Mercurial > hg > truffle
view src/share/tools/IdealGraphVisualizer/ControlFlowEditor/src/at/ssw/visualizer/cfg/visual/.svn/text-base/BezierWidget.java.svn-base @ 4487:aae5b3773e63
Added CFG editor from c1visualizer.
author | Thomas Wuerthinger <thomas.wuerthinger@oracle.com> |
---|---|
date | Tue, 31 Jan 2012 00:23:10 +0100 |
parents | |
children |
line wrap: on
line source
package at.ssw.visualizer.cfg.visual; import org.netbeans.api.visual.widget.ConnectionWidget; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Point; import java.awt.Rectangle; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.AffineTransform; import java.awt.geom.CubicCurve2D; import java.awt.geom.GeneralPath; import java.awt.geom.Point2D; import java.util.List; import org.netbeans.api.visual.anchor.AnchorShape; import org.netbeans.api.visual.graph.GraphScene; import org.netbeans.api.visual.widget.Scene; import org.netbeans.api.visual.widget.Widget; /** * In comparison to the default ConnectionWidget this class is able to connect * Widgets with a curve instead of a straight line sequence. Between two control * points a curve is painted as cubic bezier curve the control points are * calculated automaticaly they depend on the position of the prior- and the * following control points. * In conjunction with a suitable router the connection will be a straight line * or a curve depending on the amount and the position of the controlpoints. * Controlpoints supplied by the router, are treated as curve intersection points. * For Reflexive edges the router doesn`t need to supply controlpoints, they * get painted by this class automatically. If the router supplys more as 2 * control points for a recursive edge the edge gets painted with the default * curve approximation algorithm. */ public class BezierWidget extends ConnectionWidget { private static final double BEZIER_SCALE = 0.3; private static final double ENDPOINT_DEVIATION = 3;//curve endpoint approximation accuracy private GraphScene scene=null; public BezierWidget(Scene scene) { super(scene); } public BezierWidget(GraphScene scene) { super(scene); this.scene=scene; } private boolean isReflexive(){ return getSourceAnchor().getRelatedWidget() == getTargetAnchor().getRelatedWidget(); } @Override protected Rectangle calculateClientArea() { Rectangle bounds = null; if(this.getControlPoints().size()>0){ for(Point p : this.getControlPoints()){ if(bounds==null) bounds = new Rectangle(p); else bounds.add(p); } bounds.grow(5,5); } if(isReflexive()){ Widget related = this.getTargetAnchor().getRelatedWidget(); bounds = related.convertLocalToScene(related.getBounds()); bounds.grow(10, 10); } if(bounds==null) bounds = super.calculateClientArea(); return bounds; } //returns prefered location for an edge -1 for left and 1 for right private int edgeBalance(Widget nodeWidget) { if(scene == null) return 1; Point nodeLocation = nodeWidget.getLocation(); int left = 0, right = 0; Object node = scene.findObject(nodeWidget); for(Object e : scene.findNodeEdges(node, true, true)) {//inputedges ConnectionWidget cw = (ConnectionWidget) scene.findWidget(e); if(cw != this) { Widget targetNodeWidget = cw.getTargetAnchor().getRelatedWidget(); Point location; if(targetNodeWidget == nodeWidget) { Widget sourceNodeWidget = cw.getSourceAnchor().getRelatedWidget(); location = sourceNodeWidget.getLocation(); } else { location = targetNodeWidget.getLocation(); } if(location.x < nodeLocation.x) left++; else right++; } } if(left < right) return -1; else return 1; } /** * if the edge is reflexive its painted as a cyclic edge * if there are 2 controlpoints the connection is painted as a straight line from the source to the targetanchor * if there are more as 2 controlpoints the connection path between 2 control points is painted as bezier curve */ @Override protected void paintWidget () { List<Point> contrPoints = this.getControlPoints(); int listSize = contrPoints.size(); Graphics2D gr = getGraphics (); if (listSize <= 2) { if(isReflexive()) { //special case for reflexive connection widgets Widget related = this.getTargetAnchor().getRelatedWidget(); int position = this.edgeBalance(related); Rectangle bounds = related.convertLocalToScene(related.getBounds()); gr.setColor (getLineColor()); Point first = new Point(); Point last = new Point(); double centerX = bounds.getCenterX(); first.x = (int) (centerX + bounds.width / 4); first.y = bounds.y + bounds.height; last.x = first.x; last.y = bounds.y; gr.setStroke(this.getStroke()); double cutDistance = this.getTargetAnchorShape().getCutDistance(); double anchorAngle = Math.PI/-3.0; double cutX = Math.abs(Math.cos(anchorAngle)*cutDistance); double cutY = Math.abs(Math.sin(anchorAngle)*cutDistance); int ydiff=first.y-last.y; int endy = -ydiff; double height=bounds.getHeight(); double cy = height/4.0; double cx=bounds.getWidth()/5.0; double dcx = cx*2; GeneralPath gp = new GeneralPath(); gp.moveTo(0, 0); gp.quadTo(0, cy, cx, cy); gp.quadTo(dcx, cy, dcx, -height/2.0); gp.quadTo(dcx, endy - cy, cy, -(cy+ydiff)); gp.quadTo(cutX*1.5, endy - cy, cutX, endy-cutY); AffineTransform af = new AffineTransform(); AnchorShape anchorShape = this.getTargetAnchorShape(); if(position < 0) { first.x = (int) (centerX - bounds.width / 4); af.translate(first.x, first.y); af.scale(-1.0, 1.0); last.x = first.x; } else { af.translate(first.x, first.y); } Shape s = gp.createTransformedShape(af); gr.draw(s); if (last != null) { AffineTransform previousTransform = gr.getTransform (); gr.translate (last.x, last.y); if(position < 0) gr.rotate(Math.PI - anchorAngle); else gr.rotate (anchorAngle); anchorShape.paint (gr, false); gr.setTransform (previousTransform); } } else { super.paintWidget(); } return; } //bezier curve... GeneralPath curvePath = new GeneralPath(); Point lastControlPoint = null; double lastControlPointRotation = 0.0; Point prev = null; for (int i = 0; i < listSize - 1; i++) { Point cur = contrPoints.get(i); Point next = contrPoints.get(i + 1); Point nextnext = null; if (i < listSize - 2) { nextnext = contrPoints.get(i + 2); } double len = cur.distance(next); double scale = len * BEZIER_SCALE; Point bezierFrom = null;//first ControlPoint Point bezierTo = null;//second ControlPoint if (prev == null) { //first point curvePath.moveTo(cur.x, cur.y);//startpoint bezierFrom = cur; } else { bezierFrom = new Point(next.x - prev.x, next.y - prev.y); bezierFrom = scaleVector(bezierFrom, scale); bezierFrom.translate(cur.x, cur.y); } if (nextnext == null) {//next== last point (curve to) lastControlPoint=next; bezierTo = next;//set 2nd intermediate point to endpoint GeneralPath lastseg = this.subdivide(cur, bezierFrom, bezierTo, next); if(lastseg != null) curvePath.append(lastseg, true); break; } else { bezierTo = new Point(cur.x - nextnext.x, cur.y - nextnext.y); bezierTo = scaleVector(bezierTo, scale); bezierTo.translate(next.x, next.y); } curvePath.curveTo( bezierFrom.x, bezierFrom.y,//controlPoint1 bezierTo.x, bezierTo.y,//controlPoint2 next.x,next.y ); prev = cur; } Point2D cur = curvePath.getCurrentPoint(); Point next = lastControlPoint; lastControlPointRotation = //anchor anchorAngle Math.atan2 (cur.getY() - next.y, cur.getX() - next.x); Color previousColor = gr.getColor(); gr.setColor (getLineColor()); Stroke s = this.getStroke(); gr.setStroke(s); gr.setColor(this.getLineColor()); gr.draw(curvePath); AffineTransform previousTransform = gr.getTransform (); gr.translate (lastControlPoint.x, lastControlPoint.y); gr.rotate (lastControlPointRotation); AnchorShape targetAnchorShape = this.getTargetAnchorShape(); targetAnchorShape.paint (gr, false); gr.setTransform (previousTransform); //paint ControlPoints if enabled if (isPaintControlPoints()) { int last = listSize - 1; for (int index = 0; index <= last; index ++) { Point point = contrPoints.get (index); previousTransform = gr.getTransform (); gr.translate (point.x, point.y); if (index == 0 || index == last) getEndPointShape().paint (gr); else getControlPointShape().paint (gr); gr.setTransform (previousTransform); } } gr.setColor(previousColor); } private GeneralPath subdivide (Point b0, Point b1, Point b2, Point b3) { double cutDistance = getTargetAnchorShape().getCutDistance(); double minDistance = cutDistance - ENDPOINT_DEVIATION; /** * if the cutDistance is valid the last segment of the curve * gets reduced by subdivision until the distance of the endpoint(epDistance) * satisfys the condition (cutDistance > epDistance > (cutDistance - ENDPOINT-DEVIATION) */ if(cutDistance > minDistance && minDistance > 0 ) { GeneralPath path = new GeneralPath(); path.moveTo(b0.x, b0.y); CubicCurve2D.Double left = new CubicCurve2D.Double( b0.x, b0.y, b1.x, b1.y, b2.x, b2.y, b3.x, b3.y); CubicCurve2D right=new CubicCurve2D.Double(); left.subdivide(left, right); double distance = b3.distance(left.getP2()); //if the distance is bigger as the cutDistance the left segment is added //and the right segment is divided again while(distance>cutDistance){ path.append(left, true); right.subdivide(left, right); distance = b3.distance(left.getP2()); //if the devision removed to much the left segment is divided while(distance < minDistance) { //changes the distance to ~ (distance+distance/2) left.subdivide(left, right); distance = b3.distance(left.getP2()); } } //append the last segment with (minDistance < distance < cutDistance) //actually we should check if the a division happend, but this is very unlikly path.append(left, true); return path; } return null; } private static Point scaleVector(Point vector, double len) { double scale = Math.sqrt(vector.x * vector.x + vector.y * vector.y); if(scale==0.0) return vector; scale = len / scale; return new Point( Math.round(vector.x * (float)scale), Math.round(vector.y * (float)scale)); } }