// File: FigAltEdge.java // Classes: FigAltEdge // Author: Kedar Patankar package edu.neu.ccs.demeter.tools.apstudio.graphedit; import java.lang.RuntimeException; import java.awt.Point; import java.awt.Color; import java.awt.Rectangle; import java.awt.Graphics; import java.awt.Polygon; import java.util.Hashtable; public class FigAltEdge extends Fig { public static final int FUDGE_FACTOR = 4; private Point _destIntersection,_sourceIntersection; private String _sourceTowards,_arrowEdge; private Point _destShaftend; double ArrowAngle=Math.PI/7.0; double ArrowShaft = 12; private double angle=0.0; private Point _bendPoint; private boolean _defaultBend; public FigAltEdge(Point x1,Point x2,Color l_color) { this(x1.x,x1.y,x2.x,x2.y,l_color,0); int left=Math.min(x1.x,x2.x); int top =Math.min(x1.y,x2.y); int length=Math.abs(x1.x-x2.x); int ht=Math.abs(x1.y-x2.y); _bendPoint=new Point(left+length/2 , top+ht/2); _defaultBend = true; } public FigAltEdge(Point x1,Point x2,Color l_color,Point p) { this(x1.x,x1.y,x2.x,x2.y,l_color,0); _bendPoint=new Point(p.x,p.y); _defaultBend = false; } public FigAltEdge(int x, int y, int r_width, int r_height, Color l_color,int handles) { super(x, y, r_width, r_height, l_color, null, handles); } public BendPoint get_bendPoint() { if(_defaultBend) return null; BendPoint bp; try{ bp = BendPoint.parse("{"+_bendPoint.x+" "+_bendPoint.y+" "+"}"); }catch(RuntimeException pe){return null;} return bp; } public void set_position(Point src, Point dst) { position().x = src.x; position().y = src.y; objectWidth = dst.x; objectHeight = dst.y; } public void drawColoredArc(Graphics g,Point s,Point d) { draw(g,s,d); Color old = g.getColor(); g.setColor(Color.blue); Point blueDot; Point x1 = _destIntersection; Point x2 = _bendPoint; int left=Math.min(x1.x,x2.x); int top =Math.min(x1.y,x2.y); int length=Math.abs(x1.x-x2.x); int ht=Math.abs(x1.y-x2.y); blueDot=new Point(left+length/2 , top+ht/2); g.fillOval(blueDot.x-5,blueDot.y-5,10,10); g.setColor(old); } public void draw (Graphics g, Color l_color,Point s,Point d) { Color old = objectLineColor; objectLineColor = l_color; draw(g,s,d); objectLineColor = old; } public void draw(Graphics g,Point sourceSize,Point destSize) { Point source=position(); Point dest=new Point(objectWidth,objectHeight); if(_defaultBend) { _destIntersection=findIntersection(source,dest,destSize); if (_destIntersection==null) { return;} _arrowEdge=_sourceTowards; _sourceIntersection=findIntersection(dest,source,sourceSize); if (_sourceIntersection==null) { return;} // if still default bend point compute new bend point for // new positions of source/destination double ratio = 0.5; double y1 = (_sourceIntersection.y - _destIntersection.y) * ratio; double x1 = (_sourceIntersection.x - _destIntersection.x) * ratio; _bendPoint=new Point((int)(_destIntersection.x + x1), (int)(_destIntersection.y + y1)); } else { _destIntersection=findIntersection(_bendPoint,dest,destSize); if (_destIntersection==null) { return;} _arrowEdge=_sourceTowards; _sourceIntersection=findIntersection(_bendPoint,source,sourceSize); if (_sourceIntersection==null) { return;} } double z = Math.sqrt( Math.pow((_bendPoint.x - _destIntersection.x),2) + Math.pow((_bendPoint.y - _destIntersection.y),2)); double ratio=ArrowShaft/z; double y1 = (_bendPoint.y - _destIntersection.y) * ratio; double x1 = (_bendPoint.x - _destIntersection.x) * ratio; _destShaftend=new Point((int)(_destIntersection.x + x1), (int)(_destIntersection.y + y1)); draw(g); } private Point findIntersection(Point source,Point dest,Point destSize) { double dX = source.x - dest.x; double dY = dest.y - source.y; double x1 = dest.x; double y1 = dest.y; double width = destSize.x/2.0; double height = destSize.y/2.0; double angle=0.0; Point intersection=null; if((dX==0) && (dY==0)) return intersection; if(dX!=0) angle=Math.atan(dY/dX); else { if(dY > 0) angle=Math.PI/2.0; else angle= -Math.PI/2.0; } double limit = Math.atan(height/width); if (dX>=0) // The right half of the rectangle { int x,y; if(Math.abs(angle)<=limit) // covers -limit<= angle <=limit { x=(int)(x1+width); y=(int)(y1-(width*dY/dX)); intersection = new Point(x,y); _sourceTowards="Right"; } else if(angle > limit) // covers angle <= 90 { x=(int)(x1+(height*dX/dY)); y=(int)(y1-height); intersection = new Point(x,y); _sourceTowards="Up"; } else // covers -90 <= angle { x=(int)(x1-(height*dX/dY)); y=(int)(y1+height); intersection = new Point(x,y); _sourceTowards="Down"; } return intersection; } else // The left half of the rectangle { int x,y; if(Math.abs(angle)<=limit) // covers -limit<= angle <=limit { x=(int)(x1-width); y=(int)(y1+(width*dY/dX)); intersection = new Point(x,y); _sourceTowards="Left"; } else if(angle > limit) // covers angle <= 90 { x=(int)(x1-(height*dX/dY)); y=(int)(y1+height); intersection = new Point(x,y); _sourceTowards="Down"; } else // covers -90 <= angle { x=(int)(x1+(height*dX/dY)); y=(int)(y1-height); intersection = new Point(x,y); _sourceTowards="Up"; } return intersection; } } /** draw this line object */ public void draw(Graphics g) { g.setColor(objectLineColor); g.drawLine(_sourceIntersection.x, _sourceIntersection.y, _bendPoint.x,_bendPoint.y); g.drawLine(_bendPoint.x, _bendPoint.y, _destIntersection.x,_destIntersection.y); Point p1=new Point(0,0); Point p2=new Point(0,0); p1=rotateLine(_destIntersection,_destShaftend,ArrowAngle); p2=rotateLine(_destIntersection,_destShaftend,-ArrowAngle); Polygon arrowHead = new Polygon(); arrowHead.addPoint(_destIntersection.x,_destIntersection.y); arrowHead.addPoint(p1.x,p1.y); arrowHead.addPoint(p2.x,p2.y); Color c=g.getColor(); g.setColor(Color.white); g.fillPolygon(arrowHead); g.setColor(c); g.drawLine(_destIntersection.x,_destIntersection.y,p1.x,p1.y); g.drawLine(_destIntersection.x,_destIntersection.y,p2.x,p2.y); g.drawLine(p1.x,p1.y,p2.x,p2.y); } private Point rotateLine(Point end1,Point end2,double angle) { int x1=(int) ( (end1.x )+ (end2.x - end1.x)*Math.cos(angle) - (end2.y - end1.y)*Math.sin(angle) ); int y1=(int) ( ( end1.y )+ (end2.x - end1.x)*Math.sin(angle) + (end2.y - end1.y)*Math.cos(angle) ); return new Point(x1,y1); } public void translate(int dx,int dy) { _bendPoint = new Point(_bendPoint.x + dx,_bendPoint.y + dy); _defaultBend = false; } /* public void dragHandle(int mX, int mY, int handle) { _bendPoint = new Point(mX,mY); _defaultBend = false; } */ /** draw this line when it is selected. */ public void drawSelected(Graphics g) { // _handleRects[0].x = _bendPoint.x - 4; // _handleRects[0].y = _bendPoint.y - 4; // drawHandles(g); } /** When this Fig is selected, the Editor should use an instance of * SelectionHandles to record that fact. */ public Selection selectionObject() { return new SelectionHandles(this); } /** Reply true iff the given point is "near" the line. Nearness * allows the user to more easily select the line with the * mouse. Needs-More-Work: I should probably have two functions * inside() which gives a strict geometric version, and near() which * is for selection by mouse clicks. */ public boolean inside(int x, int y) { return intersects(new Rectangle(x - GRIP_MARGIN, y - GRIP_MARGIN, 2 * GRIP_MARGIN, 2 * GRIP_MARGIN)); } /** Reply true if the given point is counter-clockwise from the * vector defined by the position of this line and its endpoint. This * is used as in determining intersection between lines and * rectangles. Taken from Algorithms in C by Sedgewick, page * 350. */ int checkSegmentOne(int x, int y) { Point p0 = _destIntersection; Point p1 = new Point(_bendPoint.x,_bendPoint.y); Point p2 = new Point(x, y); /* the point to test */ int dx1 = p1.x - p0.x; int dy1 = p1.y - p0.y; int dx2 = p2.x - p0.x; int dy2 = p2.y - p0.y; if (dx1*dy2 > dy1*dx2) return +1; if (dx1*dy2 < dy1*dx2) return -1; if ((dx1*dx2 < 0) || (dy1*dy2 < 0)) return -1; if ((dx1*dx1+dy1*dy1) < (dx2*dx2+dy2*dy2)) return +1; return 0; } int checkSegmentTwo(int x, int y) { Point p0 = _sourceIntersection; Point p1 = new Point(_bendPoint.x,_bendPoint.y); Point p2 = new Point(x, y); /* the point to test */ int dx1 = p1.x - p0.x; int dy1 = p1.y - p0.y; int dx2 = p2.x - p0.x; int dy2 = p2.y - p0.y; if (dx1*dy2 > dy1*dx2) return +1; if (dx1*dy2 < dy1*dx2) return -1; if ((dx1*dx2 < 0) || (dy1*dy2 < 0)) return -1; if ((dx1*dx1+dy1*dy1) < (dx2*dx2+dy2*dy2)) return +1; return 0; } public boolean intersects(Rectangle r) { if (! r.intersects(getBoundingBox())) return false; boolean isInside = checkFirst(r); if(!isInside) return checkSecond(r); else return isInside; } private boolean checkFirst(Rectangle r) { int ccw1 = checkSegmentOne(r.x, r.y); int ccw2 = checkSegmentOne(r.x, r.y + r.height); int ccw3 = checkSegmentOne(r.x + r.width, r.y); int ccw4 = checkSegmentOne(r.x + r.width, r.y + r.height); // reply true iff any of the points are on opposite sides of the // line, or if any of them are on the line return ((ccw1 == 1 || ccw2 == 1 || ccw3 == 1 || ccw4 == 1) && (ccw1 == -1 || ccw2 == -1 || ccw3 == -1 || ccw4 == -1) || ccw1 == 0 || ccw2 == 0 || ccw3 == 0 || ccw4 == 0); } private boolean checkSecond(Rectangle r) { int ccw1 = checkSegmentTwo(r.x, r.y); int ccw2 = checkSegmentTwo(r.x, r.y + r.height); int ccw3 = checkSegmentTwo(r.x + r.width, r.y); int ccw4 = checkSegmentTwo(r.x + r.width, r.y + r.height); // reply true iff any of the points are on opposite sides of the // line, or if any of them are on the line return ((ccw1 == 1 || ccw2 == 1 || ccw3 == 1 || ccw4 == 1) && (ccw1 == -1 || ccw2 == -1 || ccw3 == -1 || ccw4 == -1) || ccw1 == 0 || ccw2 == 0 || ccw3 == 0 || ccw4 == 0); } /** Reply a rect that encloses this line. I add a few pixels on * either side so that the rect will have non-zero area. */ public Rectangle oldMethod() { Point p = position(); int x1 = Math.min(p.x,objectWidth); x1=Math.min(x1,_bendPoint.x); int y1 = Math.min(p.y, objectHeight); y1=Math.min(y1,_bendPoint.y); int x2 = Math.max(p.x, objectWidth); x2 = Math.max(x2,_bendPoint.x); int y2 = Math.max(p.y, objectHeight); y2=Math.max(y2,_bendPoint.y); return new Rectangle(x1 , y1, x2 - x1, y2 - y1 ); } public Rectangle getBoundingBox() { Point p = _destIntersection; Point b = _bendPoint; Point q = _sourceIntersection; if(p==null) return oldMethod(); int x1 = Math.min(p.x,b.x); x1=Math.min(x1,q.x); int y1 = Math.min(p.y,b.y); y1=Math.min(y1,q.y); int x2 = Math.max(p.x,b.x); x2=Math.max(x2,q.x); int y2 = Math.max(p.y,b.y); y2=Math.max(y2,q.y); return new Rectangle(x1 -1 , y1-1, x2 - x1+2, y2 - y1+2 ); } } /* end class FigAltEdge */