/** Turns a List<CubicCurve2D.Float> into a SVG Element representing a sketch of that spline. */ Element splineToSketch(SVGDocument document, List<CubicCurve2D.Float> spline) { String svgNS = SVGDOMImplementation.SVG_NAMESPACE_URI; // <g> is an SVG group // TODO: add a random(ish) rotation to the group Element group = document.createElementNS(svgNS, "g"); // For each curve in the path, draw along it using a "brush". In // our case the brush is a simple circle, but this could be changed // to something more advanced. for (CubicCurve2D.Float curve : spline) { // TODO: magic number & step in loop guard for (double i = 0.0; i <= 1.0; i += 0.01) { Point2D result = evalParametric(curve, i); // Add random jitter at some random positive or negative // distance along the unit normal to the tangent of the // curve Point2D n = vectorUnitNormal(evalParametricTangent(curve, i)); float dx = (float) ((Math.random() - 0.5) * n.getX()); float dy = (float) ((Math.random() - 0.5) * n.getY()); Element brush = document.createElementNS(svgNS, "circle"); brush.setAttribute("cx", Double.toString(result.getX() + dx)); brush.setAttribute("cy", Double.toString(result.getY() + dy)); // TODO: magic number for circle radius brush.setAttribute("r", Double.toString(1.0)); brush.setAttribute("fill", "green"); brush.setAttributeNS(null, "z-index", Integer.toString(zOrder.CONTOUR.ordinal())); group.appendChild(brush); } } return group; }
/** * Calculates the unit normal of a particular vector, where the vector is represented by the * direction and magnitude of the line segment from (0,0) to (p.x, p.y). * * @param p * @return */ private static Point2D vectorUnitNormal(Point2D p) { // if null object passed or if the passed vector has zero length if (null == p || ((0 == p.getX()) && (0 == p.getY()))) { return null; } // normalise the input "vector" // 1. get length of vector (Pythagoras) // 2. divide both x and y by this length // note: c cannot be 0 as we've already considered zero length input // vectors above. double c = Math.sqrt((p.getX() * p.getX()) + (p.getY() * p.getY())); double nvx = p.getX() / c; double nvy = p.getY() / c; // Now rotate (nvx, nvy) by 90 degrees to get the normal for the // input vector. // rx = nvx * cos (pi/2) - nvy * sin (pi/2) // ty = nvx * sin (pi/2) + nvy * cos (pi/2) // but cos (pi/2) = 0 and sin (pi/2) = 1, so this simplifies return new Point2D.Float((float) (-1 * nvy), (float) nvx); }