/* * This file is part of LaTeXDraw. * Copyright (c) 2005-2017 Arnaud BLOUIN * LaTeXDraw is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later version. * LaTeXDraw is distributed without any warranty; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. */ package net.sf.latexdraw.parsers.svg.path; import java.awt.geom.Arc2D; import org.eclipse.jdt.annotation.NonNull; /** * Defines the SVGPath arc segment. * @author Arnaud BLOUIN */ public class SVGPathSegArc extends SVGPathPointSeg { /** The x radius of the arc. @since 2.0 */ protected double rx; /** The y radius of the arc. @since 2.0 */ protected double ry; /** The x-axis rotation angle. @since 2.0 */ protected double angle; /** The value of the large-arc-flag parameter. */ protected boolean largeArcFlag; /** The value of the sweep-flag parameter. */ protected boolean sweepFlag; /** * The main constructor. * @param x The X-coordinate of the second point of the arc. * @param y The Y-coordinate of the second point of the arc. * @param rx The x radius of the arc. * @param ry The y radius of the arc. * @param angle The x-axis rotation angle. * @param isRelative True: the path segment is relative, false it is absolute. * @param largeArcFlag The value of the large-arc-flag parameter. * @param sweepFlag The value of the sweep-flag parameter. */ public SVGPathSegArc(final double x, final double y, final double rx, final double ry, final double angle, final boolean largeArcFlag, final boolean sweepFlag, final boolean isRelative) { super(isRelative, x, y); this.rx = rx; this.ry = ry; this.angle = angle; this.largeArcFlag = largeArcFlag; this.sweepFlag = sweepFlag; } /** * Creates a Java Arc2D corresponding to the position and the angles of the arc segment (computations based on the SVG * specification instructions concerning the build of an arc, p. 643-649). * @param x0 The X-coordinate of the initial position. * @param y0 The Y-coordinate of the initial position. * @return An Java Arc2D with double values. * @since 2.0 */ @NonNull public Arc2D getArc2D(final double x0, final double y0) { double a = getAngle(); double rx2 = getRX(); double ry2 = getRY(); final double x2 = getX(); final double y2 = getY(); final boolean laf = isLargeArcFlag(); final boolean sf = isSweepFlag(); final double dx2 = (x0-x2)/2.; final double dy2 = (y0-y2)/2.; a = Math.toRadians(a%360.); // Step 1: Compute (x1', y1') final double x1 = Math.cos(a)*dx2 + Math.sin(a)*dy2; final double y1 = -Math.sin(a)*dx2 + Math.cos(a)*dy2; // Ensure radii are large enough rx2 = Math.abs(rx2); ry2 = Math.abs(ry2); double prx = rx2*rx2; double pry = ry2*ry2; final double px1 = x1*x1; final double py1 = y1*y1; final double radiiCheck = px1/prx + py1/pry; if(radiiCheck>1) { rx2 = Math.sqrt(radiiCheck) * rx2; ry2 = Math.sqrt(radiiCheck) * ry2; prx = rx2 * rx2; pry = ry2 * ry2; } // Step 2: Compute (cx1, cy1) double sign = laf == sf ? -1 : 1; double sq = (prx * pry - prx * py1 - pry * px1) / (prx * py1 + pry * px1); sq = sq < 0 ? 0 : sq; final double coef = sign * Math.sqrt(sq); final double cx1 = coef * (rx2 * y1 / ry2); final double cy1 = coef * -(ry2 * x1 / rx2); // Step 3: Compute (cx, cy) from (cx1, cy1) final double sx2 = (x0+x2)/2.; final double sy2 = (y0+y2)/2.; final double cx = sx2 + (Math.cos(a) * cx1 - Math.sin(a) * cy1); final double cy = sy2 + (Math.sin(a) * cx1 + Math.cos(a) * cy1); // Step 4: Compute the angleStart (angle1) and the angleExtent (dangle) final double ux = (x1 - cx1) / rx2; final double uy = (y1 - cy1) / ry2; final double vx = (-x1 - cx1) / rx2; final double vy = (-y1 - cy1) / ry2; double p = ux; double n = Math.sqrt(ux * ux + uy * uy); sign = uy < 0 ? -1. : 1.; final 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 ? -1. : 1.; double angleExtent = Math.toDegrees(sign * Math.acos(p / n)); if(!sf && angleExtent > 0) angleExtent -= 360.; else if(sf && angleExtent < 0) angleExtent += 360.; return new Arc2D.Double(cx-rx2, cy-ry2, rx2*2., ry2*2., -angleStart%360., -angleExtent%360., Arc2D.OPEN); } /** * @return the rx. * @since 2.0 */ public double getRX() { return rx; } /** * @return the ry. * @since 2.0 */ public double getRY() { return ry; } /** * @return the angle. * @since 2.0 */ public double getAngle() { return angle; } /** * @return the largeArcFlag. * @since 2.0 */ public boolean isLargeArcFlag() { return largeArcFlag; } /** * @return the sweepFlag. * @since 2.0 */ public boolean isSweepFlag() { return sweepFlag; } @Override public String toString() { return String.valueOf(isRelative() ? 'a' : 'A') + ' ' + rx + ' ' + ry + ' ' + angle + ' ' + (largeArcFlag ? '1' : '0') + ' ' + (sweepFlag ? '1' : '0') + ' ' + x + ' ' + y; } }