/** * Convenience method for changing a single control point of a node. * * @param nodeIndex The index of the node. * @param ctrlIndex Either C0_MASK, C1_MASK or C2_MASK. * @param p The control point. The coordinates will be cloned. */ public void set(int nodeIndex, int ctrlIndex, Point2D.Double p) { Node c = get(nodeIndex); c.x[ctrlIndex] = p.x; c.y[ctrlIndex] = p.y; }
/** * Return the index of the node that is the furthest away from the center **/ public int indexOfOutermostNode() { if (outer == -1) { Point2D.Double ctr = getCenter(); outer = 0; double dist = 0; for (int i = 0, n = size(); i < n; i++) { Node cp = get(i); double d = Geom.length2(ctr.x, ctr.y, cp.x[0], cp.y[0]); if (d > dist) { dist = d; outer = i; } } } return outer; }
/** * Convenience method for getting a single control point of a node. * * @param nodeIndex The index of the node. * @param ctrlIndex Either C0_MASK, C1_MASK or C2_MASK. * @return Returns a clone of the control point. */ public Point2D.Double get(int nodeIndex, int ctrlIndex) { Node c = get(nodeIndex); return new Point2D.Double( c.x[ctrlIndex], c.y[ctrlIndex]); }
/** * Gets the point coordinate of a control point. */ public Point2D.Double getPoint(int index, int coord) { return path.get(index).getControlPoint(coord); }
/** * Gets a control point. */ public BezierPath.Node getNode(int index) { return (BezierPath.Node) path.get(index).clone(); }
/** * Sets the point coordinate of control point 0 at the specified node. */ public void setPoint(int index, Point2D.Double p) { BezierPath.Node node = path.get(index); double dx = p.x - node.x[0]; double dy = p.y - node.y[0]; for (int i = 0; i < node.x.length; i++) { node.x[i] += dx; node.y[i] += dy; } invalidate(); }
/** * Convenience method for getting the point coordinate of * the first control point of the specified node. */ public Point2D.Double getPoint(int index) { return path.get(index).getControlPoint(0); }
/** * Finds a control point index. * Returns -1 if no control point could be found. * FIXME - Move this to BezierPath */ public int findNode(Point2D.Double p) { BezierPath tp = path; for (int i = 0; i < tp.size(); i++) { BezierPath.Node p2 = tp.get(i); if (p2.x[0] == p.x && p2.y[0] == p.y) { return i; } } return -1; }
/** * Creates a polygon/polyline array of the bezier path which only includes * the C0 control points of the bezier nodes. * <p> * If the bezier path is closed, the array describes a polygon. * If the bezier path is open, the array describes a polyline. * <p> * @return Point array. */ public Point2D.Double[] toPolygonArray() { Point2D.Double[] points = new Point2D.Double[size()]; for (int i = 0, n = size(); i < n; i++) { points[i] = new Point2D.Double(get(i).x[0], get(i).y[0]); } return points; }
public Point2D.Double getOutermostPoint() { return path.get(path.indexOfOutermostNode()).getControlPoint(0); }
/** Creates a deep copy of the BezierPath. */ @Override public BezierPath clone() { BezierPath that = (BezierPath) super.clone(); for (int i = 0, n = this.size(); i < n; i++) { that.set(i, (Node) this.get(i).clone()); } return that; }
/** * Splits the segment at the given Point2D.Double if a segment was hit. * @return the index of the segment or -1 if no segment was hit. */ public int splitSegment(Point2D.Double split, double tolerance) { int i = findSegment(split, tolerance); int nextI = (i + 1) % size(); if (i != -1) { if ((get(i).mask & C2_MASK) == C2_MASK && (get(nextI).mask & C1_MASK) == 0) { // quadto add(i + 1, new Node(C2_MASK, split, split, split)); } else if ((get(i).mask & C2_MASK) == 0 && (get(nextI).mask & C1_MASK) == C1_MASK) { // quadto add(i + 1, new Node(C1_MASK, split, split, split)); } else if ((get(i).mask & C2_MASK) == C2_MASK && (get(nextI).mask & C1_MASK) == C1_MASK) { // cubicto add(i + 1, new Node(C1_MASK | C2_MASK, split, split, split)); } else { // lineto add(i + 1, new Node(split)); } } return i + 1; }
/** * Sets the point coordinate of a control point. */ public void setPoint(int index, int coord, Point2D.Double p) { BezierPath.Node cp = new BezierPath.Node(path.get(index)); cp.setControlPoint(coord, p); setNode(index, cp); }
/** * Joins two segments into one if the given Point2D.Double hits a node * of the bezier path. * @return the index of the joined segment or -1 if no segment was joined. */ public int joinSegments(Point2D.Double join, double tolerance) { for (int i = 0; i < size(); i++) { Node p = get(i); if (Geom.length(p.x[0], p.y[0], join.x, join.y) < tolerance) { remove(i); return i; } } return -1; }
protected void drawCaps(Graphics2D g) { if (getNodeCount() > 1) { if (get(START_DECORATION) != null) { BezierPath cp = getCappedPath(); Point2D.Double p1 = path.get(0, 0); Point2D.Double p2 = cp.get(0, 0); if (p2.equals(p1)) { p2 = path.get(1, 0); } get(START_DECORATION).draw(g, this, p1, p2); } if (get(END_DECORATION) != null) { BezierPath cp = getCappedPath(); Point2D.Double p1 = path.get(path.size() - 1, 0); Point2D.Double p2 = cp.get(path.size() - 1, 0); if (p2.equals(p1)) { p2 = path.get(path.size() - 2, 0); } get(END_DECORATION).draw(g, this, p1, p2); } } }
/** * Sets all values of this bezier path to that bezier path, so that this * path becomes identical to that path. */ public void setTo(BezierPath that) { while (that.size() < size()) { remove(size() - 1); } for (int i = 0, n = size(); i < n; i++) { get(i).setTo(that.get(i)); } while (size() < that.size()) { add((Node) that.get(size()).clone()); } }
/** * Adds the curve to the bezier path. * * @param bezCurve * @param bezierPath */ private static void addCurveTo(Point2D.Double[] bezCurve, BezierPath bezierPath, double errorSquared, boolean connectsCorners) { BezierPath.Node lastNode = bezierPath.get(bezierPath.size() - 1); double error = Math.sqrt(errorSquared); if (connectsCorners && Geom.lineContainsPoint(lastNode.x[0], lastNode.y[0], bezCurve[3].x, bezCurve[3].y, bezCurve[1].x, bezCurve[1].y, error) && Geom.lineContainsPoint(lastNode.x[0], lastNode.y[0], bezCurve[3].x, bezCurve[3].y, bezCurve[2].x, bezCurve[2].y, error)) { bezierPath.lineTo( bezCurve[3].x, bezCurve[3].y); } else { bezierPath.curveTo( bezCurve[1].x, bezCurve[1].y, bezCurve[2].x, bezCurve[2].y, bezCurve[3].x, bezCurve[3].y); } }
/** * Adds a (at least) linear 'curve' to the bezier path. * <p> * If the previous node has no C2 control point the line will be straight * (linear), otherwise the line will be quadratic. * <p> * This is a convenience method for adding a node with a single control * point C0. * <p> * The bezier path must already have at least one node. */ public void lineTo(double x1, double y1) { if (size() == 0) { throw new IllegalPathStateException("lineTo only allowed when not empty"); } get(size() - 1).keepColinear = false; add(new Node(x1, y1)); }
/** * Adds a cubic curve to the bezier path. * <p> * This is a convenience method for adding a node with control point C0 and * C1 (incoming curve) to the bezier path, and also specifying the control * point C2 (outgoing curve) of the previous node. * <p> * The bezier path must already have at least one node. */ public void curveTo(double x1, double y1, double x2, double y2, double x3, double y3) { if (size() == 0) { throw new IllegalPathStateException("curveTo only allowed when not empty"); } Node lastPoint = get(size() - 1); lastPoint.mask |= C2_MASK; lastPoint.x[2] = x1; lastPoint.y[2] = y1; if ((lastPoint.mask & C1C2_MASK) == C1C2_MASK) { lastPoint.keepColinear = Math.abs( Geom.angle(lastPoint.x[0], lastPoint.y[0], lastPoint.x[1], lastPoint.y[1]) - Geom.angle(lastPoint.x[2], lastPoint.y[2], lastPoint.x[0], lastPoint.y[0])) < 0.001; } add(new Node(C1_MASK, x3, y3, x2, y2, x3, y3)); }