📄 graphlayout.java
字号:
else { // region II y2 -= hHalfBox; x2 -= hHalfBox*dx/dy; } } else { if (cp2 > 0) { // region III x2 -= wHalfBox; y2 -= wHalfBox*dy/dx; } else { // region IV y2 += hHalfBox; x2 += hHalfBox*dx/dy; } } g.drawLine (x1, y1, x2, y2); g.drawLine (x2, y2, (int)(x2-lx+wy+0.5), (int)(y2-ly-wx+.5)); g.drawLine (x2, y2, (int)(x2-lx-wy+0.5), (int)(y2-ly+wx+.5)); } } public synchronized FontMetrics getFontMetrics () { if (fm == null) { Dimension d = size (); createOffscreenArea (d); } return fm; } // intercept font settings and transfer to offscreen buffer public synchronized void setFont (Font f) { super.setFont (f); if (offg != null) { offg.setFont (f); fm = offg.getFontMetrics (); } } /* public Dimension preferredSize () { return new Dimension (400, 400); } */ /* * Selecting and dragging nodes * */ RenderedNode dragNode = null; // node being dragged, or null int dragOffsetX, dragOffsetY; // initial displacement of mouse cursor from dragged object's origin; // the object remains at this displacement throughout the drag void point (int x, int y) { Object over = pick (x, y); if (over == null) { if (tipObject != null || tip != null) { tipObject = null; tip = null; super.repaint (); } } else if (over != tipObject) { String[] tipLines = ((Tipped)over).getTip (); if (tipLines == null) { tipObject = null; tip = null; super.repaint (); } else { tipObject = over; tip = new MultiLineString (tipLines); tipWidth = tip.getWidth (fm) + 10; tipHeight = tip.getHeight (fm) + 4; tipX = Math.max (x - tipWidth/2, 0); tipY = Math.min (y + 25, offSize.height - tipHeight); super.repaint (); } } } void leave () { if (tipObject != null || tip != null) { tip = null; tipObject = null; super.repaint (); } } void click (int x, int y, boolean rightClick) { requestFocus(); Object over = pick (x, y); if (over != null) { if (over instanceof RenderedNode) { RenderedNode n = (RenderedNode)over; //selectedNode = (RenderedNode)over; //selectedEdge = null; // start dragging the node if (!n.fixed) { dragNode = n; dragNode.fixed = true; dragOffsetX = (int)dragNode.screenX - x; dragOffsetY = (int)dragNode.screenY - y; } } //else { // // over instanceof RenderedEdge // selectedNode = null; // selectedEdge = (RenderedEdge)over; //} } else if (rightClick) { // right-click over background showControlPanel (); } } void drag (int x, int y) { if (dragNode != null) { placeNodeOnScreen (dragNode, x + dragOffsetX, y + dragOffsetY); changedGraph (); } } void drop (int x, int y) { if (dragNode != null) { placeNodeOnScreen (dragNode, x + dragOffsetX, y + dragOffsetY); changedGraph (); dragNode.fixed = false; dragNode = null; } } public boolean handleEvent (Event event) { switch (event.id) { case Event.MOUSE_DOWN: click (event.x, event.y, event.metaDown()); return true; case Event.MOUSE_UP: drop (event.x, event.y); return true; case Event.MOUSE_MOVE: point (event.x, event.y); return true; case Event.MOUSE_EXIT: leave (); return true; case Event.MOUSE_DRAG: if (dragNode != null) { drag (event.x, event.y); return true; } else super.handleEvent (event); default: return super.handleEvent (event); } } /** * Find the object (Node or Edge) at position (x,y) relative to the window. * @param x X position * @param y Y position * @return topmost object under (x,y), or null if none */ public Object pick (int x, int y) { // proceed in reverse display order: nodes first, then edges for (int i=graph.sizeNodes-1; i >= 0; --i) { RenderedNode n = (RenderedNode)graph.nodes[i]; if (Math.abs (n.screenX - x) < n.width/2 && Math.abs (n.screenY - y) < n.height/2) return n; } for (int i=graph.sizeEdges-1; i>=0; --i) { RenderedEdge e = (RenderedEdge)graph.edges[i]; RenderedNode to = (RenderedNode)e.to; RenderedNode from = (RenderedNode)e.from; if (inLineSegment (x, y, (int)to.screenX, (int)to.screenY, (int)from.screenX, (int)from.screenY, 4)) return e; } return null; } boolean inLineSegment (int x, int y, int x1, int y1, int x2, int y2, int threshold) { int left, right, top, bottom; if (x1 < x2) { left = x1; right = x2; } else { left = x2; right = x1; } if (y1 < y2) { top = y1; bottom = y2; } else { top = y2; bottom = y1; } // check bounding box first if (x < left-threshold || x > right+threshold || y < top-threshold || y > bottom+threshold) { return false; } // equation for line is ax + by + c = 0 // d/sqrt(a^2+b^2) is the distance between line and point <x,y> int a = y1 - y2; int b = x2 - x1; int c = x1*y2 - x2*y1; int d = a*x + b*y + c; return (d*d <= threshold * threshold * (a*a + b*b)); } /* * Testing * public static void main (String[] args) { Frame f = new Frame (); f.addWindowListener (new WindowAdapter () { public void windowClosing (WindowEvent event) { ((Frame)event.getSource()).dispose(); } }); f.setSize (100,100); f.setLayout (new BorderLayout ()); GraphLayout g = new GraphLayout (); f.add ("Center", g); f.show (); Node last = null; for (int i=0; i<args.length; ++i) { try { Thread.sleep (200); } catch (InterruptedException e) {} RenderedNode n = new RenderedNode(); n.name = args[i]; g.addNode (n); g.graph.placeNode (n, 0, 0); if (last != null) { RenderedEdge e = new RenderedEdge (last, n); g.addEdge (e); } } } */}class GraphLayoutControlPanel extends ClosableFrame { GraphLayout gl; Checkbox automatic; Scrollbar threshold; Scrollbar restLength; Scrollbar springConstant; Scrollbar nodeCharge; TextField thresholdText; TextField restLengthText; TextField springConstantText; TextField nodeChargeText; public GraphLayoutControlPanel (GraphLayout graphLayout) { super ("Graph Layout Control Panel", true); gl = graphLayout; setLayout (new GridBagLayout ()); Constrain.add (this, automatic = new Checkbox ("Automatic layout"), Constrain.labelLike (0, 0, 2)); automatic.setState (true); Constrain.add (this, new Label ("Threshold:", Label.LEFT), Constrain.labelLike (0,1)); Constrain.add (this, thresholdText = new TextField (String.valueOf (gl.getThreshold())), Constrain.fieldLike (1,1)); Constrain.add (this, threshold = new Scrollbar (Scrollbar.HORIZONTAL, (int)gl.getThreshold(), 50, 0, 1000), Constrain.fieldLike (0,2,2)); Constrain.add (this, new Label ("Rest length:", Label.LEFT), Constrain.labelLike (0,3)); Constrain.add (this, restLengthText = new TextField (String.valueOf (gl.getRestLength())), Constrain.fieldLike (1,3)); Constrain.add (this, restLength = new Scrollbar (Scrollbar.HORIZONTAL, (int)gl.getRestLength(), 50, 0, 1000), Constrain.fieldLike (0,4,2)); Constrain.add (this, new Label ("Spring constant:", Label.LEFT), Constrain.labelLike (0,5)); Constrain.add (this, springConstantText = new TextField (String.valueOf (gl.getSpringConstant())), Constrain.fieldLike (1,5)); Constrain.add (this, springConstant = new Scrollbar (Scrollbar.HORIZONTAL, (int)gl.getSpringConstant(), 50, 0, 1000), Constrain.fieldLike (0,6,2)); Constrain.add (this, new Label ("Node charge:", Label.LEFT), Constrain.labelLike (0,7)); Constrain.add (this, nodeChargeText = new TextField (String.valueOf (Math.sqrt(gl.getNodeCharge()))), Constrain.fieldLike (1,7)); Constrain.add (this, nodeCharge = new Scrollbar (Scrollbar.HORIZONTAL, (int)(Math.sqrt(gl.getNodeCharge())), 50, 0, 1000), Constrain.fieldLike (0,8,2)); pack (); } public boolean handleEvent (Event event) { // FIX: doesn't support text entry if (event.target == automatic) gl.setAutomaticLayout (automatic.getState ()); else if (event.target == threshold) gl.setThreshold (((Integer)event.arg).intValue()); else if (event.target == restLength) gl.setRestLength (((Integer)event.arg).intValue()); else if (event.target == springConstant) gl.setSpringConstant (((Integer)event.arg).intValue()); else if (event.target == restLength) { int v = ((Integer)event.arg).intValue(); gl.setNodeCharge (v*v); } else return super.handleEvent (event); return true; }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -