--- /dev/null
+/*******************************************************************************\r
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
+ * in Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ * VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.g2d.utils;\r
+\r
+import java.awt.Shape;\r
+import java.awt.geom.CubicCurve2D;\r
+import java.awt.geom.Line2D;\r
+import java.awt.geom.Path2D;\r
+import java.awt.geom.PathIterator;\r
+import java.awt.geom.Point2D;\r
+import java.awt.geom.QuadCurve2D;\r
+import java.util.ArrayList;\r
+import java.util.Collection;\r
+import java.util.Iterator;\r
+import java.util.List;\r
+\r
+/**\r
+ * Path Utils.\r
+ * <p>\r
+ * A line segment (linear, quadratic or cubic bezier) is described\r
+ * with a double array. The length of the array describes its degree (4,6,8).\r
+ * The first 2 elements define start point and last 2 the end point.\r
+ * Points in the middle are bezier control points.\r
+ * \r
+ * @See {@link GeometryUtils} for more geometry related utilities\r
+ * @author Toni Kalajainen\r
+ */\r
+public class PathUtils2 {\r
+\r
+ /**\r
+ * Get tangent of an bezier\r
+ * @param lineSegment bezier of n degrees\r
+ * @param degree 1..3 \r
+ * @param t 0..1\r
+ * @return unit vector\r
+ */\r
+ public static Point2D getLineTangent(Shape line, double t, Point2D pt)\r
+ {\r
+ double x=0, y=0;\r
+ if (line instanceof Line2D)\r
+ {\r
+ Line2D l = (Line2D) line;\r
+ x = l.getX1() - l.getX2();\r
+ y = l.getY1() - l.getY2();\r
+ } else if (line instanceof QuadCurve2D) {\r
+ QuadCurve2D l = (QuadCurve2D) line;\r
+ double p0x = l.getX1();\r
+ double p0y = l.getY1();\r
+ double p1x = l.getCtrlX();\r
+ double p1y = l.getCtrlY();\r
+ double p2x = l.getX2();\r
+ double p2y = l.getY2(); \r
+ x = 2*t*(p0x - 2*p1x + p2x) + 2*(-p0x + p1x);\r
+ y = 2*t*(p0y - 2*p1y + p2y) + 2*(-p0y + p1y);\r
+ \r
+ } else if (line instanceof CubicCurve2D) {\r
+ CubicCurve2D l = (CubicCurve2D) line;\r
+ double p0x = l.getX1();\r
+ double p0y = l.getY1();\r
+ double p1x = l.getCtrlX1();\r
+ double p1y = l.getCtrlY1();\r
+ double p2x = l.getCtrlX2();\r
+ double p2y = l.getCtrlY2();\r
+ double p3x = l.getX2();\r
+ double p3y = l.getY2(); \r
+ x = 3*(1-t)*(1-t)*(p1x-p2x) + 3*(p2x-p1x)*2*t*(1-t) + 3*(p3x-p0x)*t*t;\r
+ y = 3*(1-t)*(1-t)*(p1y-p0y) + 3*(p2y-p1y)*2*t*(1-t) + 3*(p3y-p2y)*t*t;\r
+ } else throw new IllegalArgumentException();\r
+ \r
+ return new Point2D.Double(x, y);\r
+ }\r
+ \r
+ /**\r
+ * \r
+ * @param lineSegment\r
+ * @param t 0..1\r
+ * @return\r
+ */\r
+ public static Point2D getLinePos(Shape line, double t, Point2D pt)\r
+ { \r
+ assert(line!=null);\r
+ double x=0, y=0;\r
+ if (pt==null) pt = new Point2D.Double();\r
+ \r
+ if (line instanceof Line2D) {\r
+ Line2D l = (Line2D) line;\r
+ double p0x = l.getX1();\r
+ double p0y = l.getY1();\r
+ double p1x = l.getX2();\r
+ double p1y = l.getY2();\r
+ \r
+ x = p0x*(1-t) + t*p1x;\r
+ y = p0y*(1-t) + t*p1y;\r
+ } else \r
+ if (line instanceof QuadCurve2D) {\r
+ QuadCurve2D l = (QuadCurve2D) line;\r
+ double p0x = l.getX1();\r
+ double p0y = l.getY1();\r
+ double p1x = l.getCtrlX();\r
+ double p1y = l.getCtrlY();\r
+ double p2x = l.getX2();\r
+ double p2y = l.getY2(); \r
+ \r
+ double c2x = p0x-2*p1x+p2x;\r
+ double c2y = p0y-2*p1y+p2y;\r
+ \r
+ double c1x = -2*p0x+2*p1x;\r
+ double c1y = -2*p0y+2*p1y;\r
+ \r
+ double c0x = p0x;\r
+ double c0y = p0y;\r
+ \r
+ x = t*t*c2x+t*c1x+c0x;\r
+ y = t*t*c2y+t*c1y+c0y;\r
+ } else if (line instanceof CubicCurve2D) {\r
+ CubicCurve2D l = (CubicCurve2D) line;\r
+ double p0x = l.getX1();\r
+ double p0y = l.getY1();\r
+ double p1x = l.getCtrlX1();\r
+ double p1y = l.getCtrlY1();\r
+ double p2x = l.getCtrlX2();\r
+ double p2y = l.getCtrlY2();\r
+ double p3x = l.getX2();\r
+ double p3y = l.getY2(); \r
+\r
+ x = (1-t)*(1-t)*(1-t)*p0x + 3*t*(1-t)*(1-t)*p1x + 3*t*t*(1-t)*p2x + t*t*t*p3x;\r
+ y = (1-t)*(1-t)*(1-t)*p0y + 3*t*(1-t)*(1-t)*p1y + 3*t*t*(1-t)*p2y + t*t*t*p3y;\r
+ } else throw new IllegalArgumentException();\r
+ pt.setLocation(x, y);\r
+ \r
+ return pt;\r
+ }\r
+ \r
+ public static double getLineLength(Shape line)\r
+ {\r
+ if (line instanceof Line2D) {\r
+ Line2D l = (Line2D) line;\r
+ double dx = l.getX2() - l.getX1();\r
+ double dy = l.getY2() - l.getY1();\r
+ return Math.sqrt(dx*dx+dy*dy);\r
+ }\r
+ \r
+ // Quick'n'dirty approximation \r
+ // TODO Replace with accurate value\r
+ double result = 0;\r
+ Point2D prevPos = getLinePos(line, 0.0, null); \r
+ for (int i=0; i<10; i++)\r
+ {\r
+ double t = (double)(i+1)/10;\r
+ Point2D pos = getLinePos(line, t, null);\r
+ result += pos.distance(prevPos);\r
+ prevPos.setLocation(pos);\r
+ }\r
+ return result;\r
+ }\r
+ \r
+ public static int getLineDegree(Shape line)\r
+ {\r
+ if (line instanceof Line2D) return 1;\r
+ if (line instanceof QuadCurve2D) return 2;\r
+ if (line instanceof CubicCurve2D) return 3;\r
+ throw new IllegalArgumentException(line+" is not a shape");\r
+ }\r
+ \r
+ \r
+ public static double[] toArray(Shape line)\r
+ {\r
+ if (line instanceof Line2D) {\r
+ Line2D l = (Line2D) line;\r
+ return new double[] {l.getX1(), l.getY1(), l.getX2(), l.getY2()};\r
+ }\r
+ if (line instanceof QuadCurve2D) {\r
+ QuadCurve2D l = (QuadCurve2D) line;\r
+ return new double[] {l.getX1(), l.getY1(), l.getCtrlX(), l.getCtrlY(), l.getX2(), l.getY2()};\r
+ }\r
+ if (line instanceof CubicCurve2D) {\r
+ CubicCurve2D l = (CubicCurve2D) line;\r
+ return new double[] {l.getX1(), l.getY1(), l.getCtrlX1(), l.getCtrlY1(), l.getCtrlX2(), l.getCtrlY2(), l.getX2(), l.getY2()}; \r
+ }\r
+ throw new IllegalArgumentException(line+" is not a shape"); \r
+ }\r
+ \r
+ public static Shape toShape(double dada[])\r
+ {\r
+ if (dada.length==4) \r
+ return new Line2D.Double(dada[0], dada[1], dada[2], dada[3]);\r
+ if (dada.length==6) \r
+ return new QuadCurve2D.Double(dada[0], dada[1], dada[2], dada[3], dada[4], dada[5]);\r
+ if (dada.length==8) \r
+ return new CubicCurve2D.Double(dada[0], dada[1], dada[2], dada[3], dada[4], dada[5], dada[6], dada[7]);\r
+ throw new IllegalArgumentException();\r
+ }\r
+\r
+ public static void interpolatePaths(List<Shape> l1, List<Shape> l2, double t, Collection<Shape> result)\r
+ {\r
+ if (l1.size()>l2.size()) {\r
+ List<Shape> l_ = l1;\r
+ l1 = l2;\r
+ l2 = l_;\r
+ t = 1-t;\r
+ }\r
+ \r
+ int div = l2.size() / l1.size();\r
+ int mod = l2.size() % l1.size();\r
+ \r
+ int rightIndex = 0;\r
+ for (int i=0; i<l1.size(); i++) {\r
+ int rightCounterParts = i<l1.size()-1 ? (i<mod?div+1:div) : div; \r
+ int leftIndex = i;\r
+ Shape leftShape = l1.get(leftIndex); \r
+ for (int j=0; j<rightCounterParts; j++)\r
+ {\r
+ Shape rightShape = l2.get(rightIndex++);\r
+ double inv = 1/(double)rightCounterParts;\r
+ double t0 = j*inv;\r
+ double t1 = (j+1)*inv;\r
+ Shape leftShapePart = subdiv(leftShape, t0, t1); \r
+ Shape intpShape = interpolateLine(leftShapePart, rightShape, t);\r
+ result.add( intpShape );\r
+ }\r
+ }\r
+ }\r
+ \r
+ /**\r
+ * Interpolate two paths\r
+ * @param path1\r
+ * @param path2\r
+ * @param t phase 0..1, 0==path1, 1==path2\r
+ * @return\r
+ */\r
+ public static Path2D interpolatePaths(PathIterator path1, PathIterator path2, double t)\r
+ {\r
+ ArrayList<Shape> l1 = new ArrayList<Shape>();\r
+ ArrayList<Shape> l2 = new ArrayList<Shape>(); \r
+ toShapes(path1, l1);\r
+ toShapes(path2, l2);\r
+ ArrayList<Shape> result = new ArrayList<Shape>();\r
+ interpolatePaths(l1, l2, t, result);\r
+ Path2D p = new Path2D.Double();\r
+ appedToPath(p, result);\r
+ return p;\r
+ }\r
+\r
+ public static Path2D interpolatePaths(Path2D path1, Path2D path2, double t)\r
+ {\r
+ return interpolatePaths(path1.getPathIterator(null), path2.getPathIterator(null), t);\r
+ }\r
+ \r
+ public static Shape interpolateLine(Shape l1, Shape l2, double t)\r
+ {\r
+ assert(t>=0 && t<=1);\r
+ if (t==0) return l1;\r
+ if (t==1) return l2;\r
+ \r
+ double[] a1 = toArray(l1);\r
+ double[] a2 = toArray(l2);\r
+ double[] res = PathUtils.interpolateLineSegment(a1, a2, t);\r
+ return toShape(res);\r
+ }\r
+ \r
+ public static void toShapes(PathIterator pi, Collection<Shape> result)\r
+ {\r
+ Iterator<Shape> i = toShapeIterator(pi);\r
+ while (i.hasNext()) {\r
+ Shape segment = i.next();\r
+ result.add(segment);\r
+ }\r
+ }\r
+ \r
+ /**\r
+ * Returns an iterator that splits path into line shapes\r
+ * @param pi path iterator\r
+ * @return line segment iterator\r
+ */\r
+ public static Iterator<Shape> toShapeIterator(final PathIterator pi)\r
+ {\r
+ return new PathIteratorToShapeIterator(pi);\r
+ }\r
+ \r
+ private static class PathIteratorToShapeIterator implements Iterator<Shape>\r
+ {\r
+ final PathIterator pi;\r
+ double lineTo[] = new double[6];\r
+ double startPos[] = new double[2];\r
+ double from[] = new double[2];\r
+ int degree = 0;\r
+ PathIteratorToShapeIterator(PathIterator pi) {\r
+ this.pi = pi;\r
+ while(!pi.isDone()) {\r
+ int type = pi.currentSegment(lineTo);\r
+ pi.next();\r
+ if (type == PathIterator.SEG_MOVETO) {\r
+ startPos[0] = from[0] = lineTo[0];\r
+ startPos[1] = from[1] = lineTo[1];\r
+ }\r
+ if (type == PathIterator.SEG_CLOSE) {\r
+ type = PathIterator.SEG_LINETO;\r
+ lineTo[0] = startPos[0];\r
+ lineTo[1] = startPos[1];\r
+ }\r
+ if (type>=PathIterator.SEG_LINETO && type<=PathIterator.SEG_CUBICTO)\r
+ {\r
+ degree = type;\r
+ // from == xx\r
+ break;\r
+ }\r
+ }\r
+ } \r
+ @Override\r
+ public boolean hasNext() {\r
+ return degree>0;\r
+ }\r
+ @Override\r
+ public Shape next() {\r
+ if (degree==0) return null;\r
+ Shape res = null;\r
+ if (degree==1) \r
+ res = new Line2D.Double(from[0], from[1], lineTo[0], lineTo[1]);\r
+ else if (degree==2) \r
+ res = new QuadCurve2D.Double(from[0], from[1], lineTo[0], lineTo[1], lineTo[2], lineTo[3]);\r
+ else if (degree==3) \r
+ res = new CubicCurve2D.Double(from[0], from[1], lineTo[0], lineTo[1], lineTo[2], lineTo[3], lineTo[4], lineTo[5]);\r
+ else throw new IllegalArgumentException();\r
+ // traverse path iterator until end or until next segment is known\r
+ degree = 0;\r
+ from[0] = lineTo[0];\r
+ from[1] = lineTo[1];\r
+ while(!pi.isDone()) {\r
+ int type = pi.currentSegment(lineTo);\r
+ pi.next();\r
+ if (type == PathIterator.SEG_MOVETO) {\r
+ startPos[0] = from[0] = lineTo[0];\r
+ startPos[1] = from[1] = lineTo[1];\r
+ }\r
+ if (type == PathIterator.SEG_CLOSE) {\r
+ type = PathIterator.SEG_LINETO;\r
+ lineTo[0] = startPos[0];\r
+ lineTo[1] = startPos[1];\r
+ }\r
+ if (type>=PathIterator.SEG_LINETO && type<=PathIterator.SEG_CUBICTO)\r
+ {\r
+ degree = type; \r
+ break;\r
+ }\r
+ }\r
+ return res;\r
+ }\r
+ @Override\r
+ public void remove() {\r
+ throw new UnsupportedOperationException();\r
+ }\r
+ }\r
+\r
+ \r
+ \r
+ \r
+ public static Shape subdiv_takeLeft(Shape line, double t)\r
+ {\r
+ if (t==1) return line;\r
+ if (line instanceof Line2D) {\r
+ Line2D l = (Line2D) line;\r
+ double p0x = l.getX1();\r
+ double p0y = l.getY1();\r
+ double p1x = l.getX2();\r
+ double p1y = l.getY2(); \r
+ double p1x_ = p0x*(1-t) + p1x*t;\r
+ double p1y_ = p0y*(1-t) + p1y*t;\r
+ \r
+ return new Line2D.Double( p0x, p0y, p1x_, p1y_ );\r
+ }\r
+ \r
+ if (line instanceof QuadCurve2D) {\r
+ QuadCurve2D l = (QuadCurve2D) line;\r
+ double p0x = l.getX1();\r
+ double p0y = l.getY1();\r
+ double p1x = l.getCtrlX();\r
+ double p1y = l.getCtrlY(); \r
+ double p2x = l.getX2();\r
+ double p2y = l.getY2(); \r
+ double p1x_ = p0x*(1-t) + p1x*t;\r
+ double p1y_ = p0y*(1-t) + p1y*t;\r
+ \r
+ double q0x = p0x*(1-t) + p1x*t;\r
+ double q0y = p0y*(1-t) + p1y*t;\r
+\r
+ double q1x = p1x*(1-t) + p2x*t;\r
+ double q1y = p1y*(1-t) + p2y*t;\r
+ \r
+ double p2x_ = q0x*(1-t) + q1x*t;\r
+ double p2y_ = q0y*(1-t) + q1y*t;\r
+ \r
+ return new QuadCurve2D.Double( p0x, p0y, p1x_, p1y_, p2x_, p2y_ ); \r
+ }\r
+\r
+ if (line instanceof CubicCurve2D) {\r
+ CubicCurve2D l = (CubicCurve2D) line;\r
+ double p0x = l.getX1();\r
+ double p0y = l.getY1();\r
+ double p1x = l.getCtrlX1();\r
+ double p1y = l.getCtrlY1(); \r
+ double p2x = l.getCtrlX2();\r
+ double p2y = l.getCtrlY2(); \r
+ double p3x = l.getX2();\r
+ double p3y = l.getY2(); \r
+ double p1x_ = p0x*(1-t) + p1x*t;\r
+ double p1y_ = p0y*(1-t) + p1y*t;\r
+ \r
+ double q0x = p0x*(1-t) + p1x*t;\r
+ double q0y = p0y*(1-t) + p1y*t;\r
+\r
+ double q1x = p1x*(1-t) + p2x*t;\r
+ double q1y = p1y*(1-t) + p2y*t;\r
+ \r
+ double p2x_ = q0x*(1-t) + q1x*t;\r
+ double p2y_ = q0y*(1-t) + q1y*t;\r
+ \r
+ double q2x = p2x*(1-t) + p3x*t;\r
+ double q2y = p2y*(1-t) + p3y*t;\r
+\r
+ double r0x = q0x*(1-t) + q1x*t;\r
+ double r0y = q0y*(1-t) + q1y*t;\r
+ \r
+ double r1x = q1x*(1-t) + q2x*t;\r
+ double r1y = q1y*(1-t) + q2y*t;\r
+ \r
+ double p3x_ = r0x*(1-t) + r1x*t;\r
+ double p3y_ = r0y*(1-t) + r1y*t; \r
+ \r
+ return new CubicCurve2D.Double(p0x, p0y, p1x_, p1y_, p2x_, p2y_, p3x_, p3y_); \r
+ }\r
+\r
+ throw new IllegalArgumentException();\r
+ }\r
+ \r
+ public static Shape subdiv_takeRight(Shape line, double t)\r
+ {\r
+ if (t==0) return line;\r
+ if (line instanceof Line2D) {\r
+ Line2D l = (Line2D) line;\r
+ double p0x = l.getX1();\r
+ double p0y = l.getY1();\r
+ double p1x = l.getX2();\r
+ double p1y = l.getY2(); \r
+ double p0x_ = p0x*(1-t) + p1x*t;\r
+ double p0y_ = p0y*(1-t) + p1y*t;\r
+ \r
+ return new Line2D.Double( p0x_, p0y_, p1x, p1y );\r
+ }\r
+ \r
+ if (line instanceof QuadCurve2D) {\r
+ QuadCurve2D l = (QuadCurve2D) line; \r
+ double p0x = l.getX1();\r
+ double p0y = l.getY1();\r
+ double p1x = l.getCtrlX();\r
+ double p1y = l.getCtrlY(); \r
+ double p2x = l.getX2();\r
+ double p2y = l.getY2(); \r
+\r
+ double q0x = p0x*(1-t) + p1x*t;\r
+ double q0y = p0y*(1-t) + p1y*t;\r
+\r
+ double q1x = p1x*(1-t) + p2x*t;\r
+ double q1y = p1y*(1-t) + p2y*t;\r
+ \r
+ double p2x_ = q0x*(1-t) + q1x*t;\r
+ double p2y_ = q0y*(1-t) + q1y*t;\r
+ \r
+ return new QuadCurve2D.Double( p2x_, p2y_, q1x, q1y, p2x, p2y ); \r
+ } \r
+\r
+ \r
+ if (line instanceof CubicCurve2D) {\r
+ CubicCurve2D l = (CubicCurve2D) line;\r
+ \r
+ double p0x = l.getX1();\r
+ double p0y = l.getY1();\r
+ double p1x = l.getCtrlX1();\r
+ double p1y = l.getCtrlY1(); \r
+ double p2x = l.getCtrlX2();\r
+ double p2y = l.getCtrlY2(); \r
+ double p3x = l.getX2();\r
+ double p3y = l.getY2();\r
+ \r
+ double q0x = p0x*(1-t) + p1x*t;\r
+ double q0y = p0y*(1-t) + p1y*t;\r
+\r
+ double q1x = p1x*(1-t) + p2x*t;\r
+ double q1y = p1y*(1-t) + p2y*t;\r
+ \r
+ double q2x = p2x*(1-t) + p3x*t;\r
+ double q2y = p2y*(1-t) + p3y*t;\r
+\r
+ double r0x = q0x*(1-t) + q1x*t;\r
+ double r0y = q0y*(1-t) + q1y*t;\r
+ \r
+ double r1x = q1x*(1-t) + q2x*t;\r
+ double r1y = q1y*(1-t) + q2y*t;\r
+ \r
+ double p3x_ = r0x*(1-t) + r1x*t;\r
+ double p3y_ = r0y*(1-t) + r1y*t; \r
+ \r
+ return new CubicCurve2D.Double( p3x_, p3y_, r1x, r1y, q2x, q2y, p3x, p3y ); \r
+ } \r
+ \r
+ return null;\r
+ }\r
+ \r
+\r
+\r
+ /**\r
+ * Crops line segment into a smaller line segment\r
+ * @param line line segment\r
+ * @param t0 begin t\r
+ * @param t1 end t\r
+ * @return cropped line segment\r
+ */\r
+ public static Shape subdiv(Shape line, double t0, double t1)\r
+ {\r
+ if (t0==0 && t1==1) return line;\r
+ Shape temp = subdiv_takeLeft(line, t1);\r
+ return subdiv_takeRight(temp, t0/t1);\r
+ }\r
+ \r
+ public static void appedToPath(Path2D p, Shape ... shapes)\r
+ {\r
+ for (Shape s : shapes)\r
+ {\r
+ Point2D cur = p.getCurrentPoint();\r
+ if (s instanceof Line2D) {\r
+ Line2D l = (Line2D) s;\r
+ if (cur.getX()!=l.getX1() || cur.getY()!=l.getY1())\r
+ p.moveTo(l.getX1(), l.getY1());\r
+ p.lineTo(l.getX2(), l.getY2());\r
+ } else if (s instanceof QuadCurve2D) {\r
+ QuadCurve2D l = (QuadCurve2D) s;\r
+ if (cur.getX()!=l.getX1() || cur.getY()!=l.getY1())\r
+ p.moveTo(l.getX1(), l.getY1());\r
+ p.quadTo(l.getCtrlX(), l.getCtrlY(), l.getX2(), l.getY2());\r
+ } else if (s instanceof CubicCurve2D) {\r
+ CubicCurve2D l = (CubicCurve2D) s;\r
+ if (cur.getX()!=l.getX1() || cur.getY()!=l.getY1())\r
+ p.moveTo(l.getX1(), l.getY1());\r
+ p.curveTo(l.getCtrlX1(), l.getCtrlY1(), l.getCtrlX2(), l.getCtrlY2(), l.getX2(), l.getY2());\r
+ } throw new IllegalArgumentException();\r
+ }\r
+ }\r
+ public static void appedToPath(Path2D p, Collection<Shape> shapes)\r
+ {\r
+ for (Shape s : shapes)\r
+ {\r
+ Point2D cur = p.getCurrentPoint();\r
+ if (s instanceof Line2D) {\r
+ Line2D l = (Line2D) s;\r
+ if (cur==null || cur.getX()!=l.getX1() || cur.getY()!=l.getY1())\r
+ p.moveTo(l.getX1(), l.getY1());\r
+ p.lineTo(l.getX2(), l.getY2());\r
+ } else if (s instanceof QuadCurve2D) {\r
+ QuadCurve2D l = (QuadCurve2D) s;\r
+ if (cur==null || cur.getX()!=l.getX1() || cur.getY()!=l.getY1())\r
+ p.moveTo(l.getX1(), l.getY1());\r
+ p.quadTo(l.getCtrlX(), l.getCtrlY(), l.getX2(), l.getY2());\r
+ } else if (s instanceof CubicCurve2D) {\r
+ CubicCurve2D l = (CubicCurve2D) s;\r
+ if (cur==null || cur.getX()!=l.getX1() || cur.getY()!=l.getY1())\r
+ p.moveTo(l.getX1(), l.getY1());\r
+ p.curveTo(l.getCtrlX1(), l.getCtrlY1(), l.getCtrlX2(), l.getCtrlY2(), l.getX2(), l.getY2());\r
+ } else throw new IllegalArgumentException();\r
+ }\r
+ }\r
+ \r
+\r
+ public static void main(String[] args) {\r
+ \r
+ }\r
+ \r
+ \r
+}\r