// This function is nearly identical to the one written for the // original port of the F3 graphics/UI library: // javafx.ui.canvas.ArcTo#addTo private void addArcTo(NGPath pgPath, Path2D path, final double x0, final double y0) { double localX = getX(); double localY = getY(); boolean localSweepFlag = isSweepFlag(); boolean localLargeArcFlag = isLargeArcFlag(); // Determine target "to" position final double xto = (isAbsolute()) ? localX : localX + x0; final double yto = (isAbsolute()) ? localY : localY + y0; // Compute the half distance between the current and the final point final double dx2 = (x0 - xto) / 2.0; final double dy2 = (y0 - yto) / 2.0; // Convert angle from degrees to radians final double xAxisRotationR = Math.toRadians(getXAxisRotation()); final double cosAngle = Math.cos(xAxisRotationR); final double sinAngle = Math.sin(xAxisRotationR); // // Step 1 : Compute (x1, y1) // final double x1 = (cosAngle * dx2 + sinAngle * dy2); final double y1 = (-sinAngle * dx2 + cosAngle * dy2); // Ensure radii are large enough double rx = Math.abs(getRadiusX()); double ry = Math.abs(getRadiusY()); double Prx = rx * rx; double Pry = ry * ry; final double Px1 = x1 * x1; final double Py1 = y1 * y1; // check that radii are large enough final double radiiCheck = Px1 / Prx + Py1 / Pry; if (radiiCheck > 1.0) { rx = Math.sqrt(radiiCheck) * rx; ry = Math.sqrt(radiiCheck) * ry; if (rx == rx && ry == ry) { /* not NANs */ } else { if (pgPath == null) { path.lineTo((float) xto, (float) yto); } else { pgPath.addLineTo((float) xto, (float) yto); } return; } Prx = rx * rx; Pry = ry * ry; } // // Step 2 : Compute (cx1, cy1) // double sign = ((localLargeArcFlag == localSweepFlag) ? -1.0 : 1.0); double sq = ((Prx * Pry) - (Prx * Py1) - (Pry * Px1)) / ((Prx * Py1) + (Pry * Px1)); sq = (sq < 0.0) ? 0.0 : sq; final double coef = (sign * Math.sqrt(sq)); final double cx1 = coef * ((rx * y1) / ry); final double cy1 = coef * -((ry * x1) / rx); // // Step 3 : Compute (cx, cy) from (cx1, cy1) // final double sx2 = (x0 + xto) / 2.0; final double sy2 = (y0 + yto) / 2.0; final double cx = sx2 + (cosAngle * cx1 - sinAngle * cy1); final double cy = sy2 + (sinAngle * cx1 + cosAngle * cy1); // // Step 4 : Compute the angleStart (angle1) and the angleExtent (dangle) // final double ux = (x1 - cx1) / rx; final double uy = (y1 - cy1) / ry; final double vx = (-x1 - cx1) / rx; final double vy = (-y1 - cy1) / ry; // Compute the angle start double n = Math.sqrt((ux * ux) + (uy * uy)); double p = ux; // (1 * ux) + (0 * uy) sign = ((uy < 0.0) ? -1.0 : 1.0); double angleStart = Math.toDegrees(sign * Math.acos(p / n)); // Compute the angle extent n = Math.sqrt((ux * ux + uy * uy) * (vx * vx + vy * vy)); p = ux * vx + uy * vy; sign = ((ux * vy - uy * vx < 0.0) ? -1.0 : 1.0); double angleExtent = Math.toDegrees(sign * Math.acos(p / n)); if (!localSweepFlag && (angleExtent > 0)) { angleExtent -= 360.0; } else if (localSweepFlag && (angleExtent < 0)) { angleExtent += 360.0; } angleExtent = angleExtent % 360; angleStart = angleStart % 360; // // We can now build the resulting Arc2D // final float arcX = (float) (cx - rx); final float arcY = (float) (cy - ry); final float arcW = (float) (rx * 2.0); final float arcH = (float) (ry * 2.0); final float arcStart = (float) -angleStart; final float arcExtent = (float) -angleExtent; if (pgPath == null) { final Arc2D arc = new Arc2D(arcX, arcY, arcW, arcH, arcStart, arcExtent, Arc2D.OPEN); BaseTransform xform = (xAxisRotationR == 0) ? null : BaseTransform.getRotateInstance(xAxisRotationR, cx, cy); PathIterator pi = arc.getPathIterator(xform); // RT-8926, append(true) converts the initial moveTo into a // lineTo which can generate huge miter joins if the segment // is small enough. So, we manually skip it here instead. pi.next(); path.append(pi, true); } else { pgPath.addArcTo(arcX, arcY, arcW, arcH, arcStart, arcExtent, (float) xAxisRotationR); } }
@Override void addTo(NGPath pgPath) { addArcTo(pgPath, null, pgPath.getCurrentX(), pgPath.getCurrentY()); }