001: import java.awt.Graphics2D; 002: import java.awt.geom.Point2D; 003: import java.awt.geom.Rectangle2D; 004: import java.awt.geom.RectangularShape; 005: import java.io.IOException; 006: import java.io.ObjectInputStream; 007: import java.io.ObjectOutputStream; 008: 009: /** 010: A node that has a rectangular shape. 011: */ 012: public abstract class RectangularNode implements Node 013: { 014: public Object clone() 015: { 016: try 017: { 018: RectangularNode cloned = (RectangularNode) super.clone(); 019: cloned.bounds = (Rectangle2D) bounds.clone(); 020: return cloned; 021: } 022: catch (CloneNotSupportedException exception) 023: { 024: return null; 025: } 026: } 027: 028: public void translate(double dx, double dy) 029: { 030: bounds.setFrame(bounds.getX() + dx, 031: bounds.getY() + dy, 032: bounds.getWidth(), 033: bounds.getHeight()); 034: } 035: 036: public boolean contains(Point2D p) 037: { 038: return bounds.contains(p); 039: } 040: 041: public Rectangle2D getBounds() 042: { 043: return (Rectangle2D) bounds.clone(); 044: } 045: 046: public void setBounds(Rectangle2D newBounds) 047: { 048: bounds = newBounds; 049: } 050: 051: public Point2D getConnectionPoint(Point2D aPoint) 052: { 053: double slope = bounds.getHeight() / bounds.getWidth(); 054: double x = bounds.getCenterX(); 055: double y = bounds.getCenterY(); 056: double ex = aPoint.getX() - x; 057: double ey = aPoint.getY() - y; 058: 059: if (ex != 0 && -slope <= ey / ex && ey / ex <= slope) 060: { 061: // intersects at left or right boundary 062: if (ex > 0) 063: { 064: x = bounds.getMaxX(); 065: y += (bounds.getWidth() / 2) * ey / ex; 066: } 067: else 068: { 069: x = bounds.getX(); 070: y -= (bounds.getWidth() / 2) * ey / ex; 071: } 072: } 073: else if (ey != 0) 074: { 075: // intersects at top or bottom 076: if (ey > 0) 077: { 078: x += (bounds.getHeight() / 2) * ex / ey; 079: y = bounds.getMaxY(); 080: } 081: else 082: { 083: x -= (bounds.getHeight() / 2) * ex / ey; 084: y = bounds.getY(); 085: } 086: } 087: return new Point2D.Double(x, y); 088: } 089: 090: private void writeObject(ObjectOutputStream out) 091: throws IOException 092: { 093: out.defaultWriteObject(); 094: writeRectangularShape(out, bounds); 095: } 096: 097: /** 098: A helper method to overcome the problem that the 2D shapes 099: aren't serializable. It writes x, y, width and height 100: to the stream. 101: @param out the stream 102: @param s the shape 103: */ 104: private static void writeRectangularShape( 105: ObjectOutputStream out, 106: RectangularShape s) 107: throws IOException 108: { 109: out.writeDouble(s.getX()); 110: out.writeDouble(s.getY()); 111: out.writeDouble(s.getWidth()); 112: out.writeDouble(s.getHeight()); 113: } 114: 115: private void readObject(ObjectInputStream in) 116: throws IOException, ClassNotFoundException 117: { 118: in.defaultReadObject(); 119: bounds = new Rectangle2D.Double(); 120: readRectangularShape(in, bounds); 121: } 122: 123: /** 124: A helper method to overcome the problem that the 2D shapes 125: aren't serializable. It reads x, y, width and height 126: from the stream. 127: @param in the stream 128: @param s the shape whose frame is set from the stream values 129: */ 130: private static void readRectangularShape(ObjectInputStream in, 131: RectangularShape s) 132: throws IOException 133: { 134: double x = in.readDouble(); 135: double y = in.readDouble(); 136: double width = in.readDouble(); 137: double height = in.readDouble(); 138: s.setFrame(x, y, width, height); 139: } 140: 141: private transient Rectangle2D bounds; 142: }