Java source code of 'jhplot.jadraw.JaArcObject'

/*

 * Title: japlot.jaxodraw
 * Description: Graphical user interface for drawing Feynman diagrams
 * @author Daniele Binosi 
 * @author Lukas Theussl  

 Copyright (C) 2003-2006,  Daniele Binosi and Lukas Theussl

 See the file LICENSE in the source distribution home directory
 for a full copy of the GPL (GNU General Public License).

    This file is part of japlot.jaxodraw.

    japlot.jaxodraw 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.

    japlot.jaxodraw is distributed in the hope that it will be useful,
    but 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.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA


 */
package jhplot.jadraw;


import japlot.jaxodraw.JaxoArcOptionsPanel;
import japlot.jaxodraw.JaxoColor;

import java.awt.BasicStroke;
import java.awt.Color;
import org.freehep.graphics2d.VectorGraphics;
import java.awt.geom.AffineTransform;
import java.awt.geom.Arc2D;
import java.awt.geom.GeneralPath;
import java.awt.geom.Point2D;

import japlot.jaxodraw.*;

/** The mother of all arc objects. */
public abstract class JaArcObject extends JaObject {
    /** The points defining this arc object.*/
    private int x1;

    /** The points defining this arc object.*/
    private int x2;

    /** The points defining this arc object.*/
    private int y1;

    /** The points defining this arc object.*/
    private int y2;

    /** The stroke of this arc object.*/
    private float stroke;

    /** The dash size of the arc object (only for ghost and scalar arcs).*/
    private float dash;

    /** The frequency of the arc object (only for photon and gluon arcs).*/
    private float freq;

    /** The starting angle of this arc object.*/
    private int stangle;

    /** A boolean variable that indicates whether to draw an arrow on
     * this arc object or not (not used for photon and gluon arcs).
     */
    private boolean arrow;

    /** A boolean variable that indicates whether the arrow should be
     * flipped with respect to the initial orientation or not
     * (only used for fermion ghost and scalar arcs).
     */
    private boolean flip;

    /** A boolean variable that indicates wether this arc object
     * has an arrow at the end (true) or in the mid (false); used only
     * for fermionic arcs.
     */
    private boolean end;

    /** The amplitude for this arc object. */
    private int amp;

    /** A boolean variable that indicates whether this arc should be drawn
     * with double lines.
     */
    private boolean doubleLine;

    /** The separation of the double lines. */
    private float dlSeparation;
    private boolean isRes = false;

    /** Constructor: sets the width, height, relative width and height,
     * to a default value of JaObject.INIT_SIZE.
     */
    public JaArcObject() {
        setRelw(INIT_SIZE);
        setRelh(INIT_SIZE);


      setAmp(-JaxoPrefs.getIntPref(JaxoPrefs.PREF_AMPLITUDE));
      setFreq(20.0f);
      setColor(JaxoColor.getColor(JaxoPrefs.getPref(
                        JaxoPrefs.PREF_COLOR)));
      setStroke(JaxoPrefs.getFloatPref(JaxoPrefs.PREF_LINEWIDTH));
      setDoubleLine(false);
      setDLSeparation(JaxoPrefs.getFloatPref(
                  JaxoPrefs.PREF_DLSEP));




    }

    // Bean getter and setter methods

    /** Sets the resizing property of this arc.
     * @param ir True if this arc id being resized.
     */
    public final void setIsResizing(boolean ir) {
        this.isRes = ir;
    }

    /** Returns the resizing property of this arc.
     * @return  True if this arc id being resized.
     */
    public final boolean isResizing() {
        return isRes;
    }

    /** Sets the x coordinate of the first click point of this arc.
     * @param newx1 The x coordinate of the first click point of this arc.
     */
    public final void setX1(int newx1) {
        this.x1 = newx1;
    }

    /** Returns the x coordinate of the first click point of this arc.
    * @return The x coordinate of the first click point of this arc.
    */
    public final int getX1() {
        return x1;
    }

    /** Sets the x coordinate of the second click point of this arc.
     * @param newx2 The x coordinate of the second click point of this arc.
     */
    public final void setX2(int newx2) {
        this.x2 = newx2;
    }

    /** Returns the x coordinate of the second click point of this arc.
     * @return The x coordinate of the second click point of this arc.
     */
    public final int getX2() {
        return x2;
    }

    /** Sets the y coordinate of the first click point of this arc.
     * @param newy1 The y coordinate of the first click point of this arc.
     */
    public final void setY1(int newy1) {
        this.y1 = newy1;
    }

    /** Returns the y coordinate of the first click point of this arc.
    * @return The y coordinate of the first click point of this arc.
    */
    public final int getY1() {
        return y1;
    }

    /** Sets the y coordinate of the second click point of this arc.
     * @param newy2 The y coordinate of the second click point of this arc.
     */
    public final void setY2(int newy2) {
        this.y2 = newy2;
    }

    /** Returns the y coordinate of the second click point of this arc.
     * @return The y coordinate of the second click point of this arc.
     */
    public final int getY2() {
        return y2;
    }

    /** Sets the points of this arc.
     * @param sx1 The x coordinate of the first click point of this arc.
     * @param sy1 The y coordinate of the first click point of this arc.
     * @param sx2 The x coordinate of the second click point of this arc.
     * @param sy2 The y coordinate of the second click point of this arc.
     * @param sx3 The x coordinate of the third click point of this arc.
     * @param sy3 The y coordinate of the third click point of this arc.
     */
    public final void setArcPts(int sx1, int sy1, int sx2, int sy2, int sx3,
        int sy3) {
        setX1(sx1);
        setY1(sy1);
        setX2(sx2);
        setY2(sy2);
        setX(sx3);
        setY(sy3);
    }

    /** Returns the stroke property of this arc object.
     * @return The stroke property of this arc object.
     */
    public final float getStroke() {
        return stroke;
    }

    /** Sets the stroke property of this arc object.
     * @param newStroke The stroke property of this arc object.
     */
    public final void setStroke(float newStroke) {
        this.stroke = newStroke;
    }

    /** Returns the dash property of this arc object.
     * @return  The dash property of this arc object.
     */
    public final float getDash() {
        return dash;
    }

    /** Sets the dash property of this arc object.
     * @param newDash The dash property of this arc object.
     */
    public final void setDash(float newDash) {
        this.dash = newDash;
    }

    /** Returns the freq property of this arc object.
     * @return The freq property of this arc object.
     */
    public final float getFreq() {
        return freq;
    }

    /** Sets the freq property of this arc object.
     * @param newFreq The freq property of this arc object.
     */
    public final void setFreq(float newFreq) {
        this.freq = newFreq;
    }

    /** Returns the amp property of this arc object.
     * @return The amp property of this arc object.
     */
    public final int getAmp() {
        return amp;
    }

    /** Sets the amp property of this arc object.
     * @param newAmp The amp property of this arc object.
     */
    public final void setAmp(int newAmp) {
        this.amp = newAmp;
    }

    /** Returns the stangle property of this arc object.
     * @return The stangle property of this arc object.
     */
    public final int getStangle() {
        return stangle;
    }

    /** Sets the stangle property of this arc object.
     * @param newStangle The stangle property of this arc object.
     */
    public final void setStangle(int newStangle) {
        this.stangle = newStangle;
    }

    /** Determines whether the arrow property of this arc object is set or not.
     * @return The arrow property of this arc object.
     */
    public final boolean isArrow() {
        return arrow;
    }

    /** Returns the arrow property of this arc object.
     * @return The arrow property  of this arc object.
     */
    public final boolean getArrow() {
        return arrow;
    }

    /** Sets the arrow property of this arc object.
     * @param arr The arrow property  of this arc object.
     */
    public final void setArrow(boolean arr) {
        this.arrow = arr;
    }

    /** Determines whether the end property of this line object is set or not.
     * @return The end property of this line object.
     */
    public final boolean isEnd() {
        return end;
    }

    /** Returns the end property of this line object.
     * @return The end property of this line object.
     */
    public final boolean getEnd() {
        return end;
    }

    /** Sets the end property of this line object.
     * @param mid The end property of this line object.
     */
    public final void setEnd(boolean mid) {
        this.end = mid;
    }

    /**Determines whether the flip property of this arc object is set or not.
     * @return The flip property  of this arc object.
     */
    public final boolean isFlip() {
        return flip;
    }

    /** Returns the flip property  of this arc object.
     * @return The flip property  of this arc object.
     */
    public final boolean getFlip() {
        return flip;
    }

    /** Sets the  flip property of this arc object.
     * @param newFlip The flip property of this arc object.
     */
    public final void setFlip(boolean newFlip) {
        this.flip = newFlip;
    }

    /** Sets the double line property.
     * @param dline The double line boolean variable.
     */
    public final void setDoubleLine(boolean dline) {
        this.doubleLine = dline;
    }

    /** Returns the double line property.
     * @return The double line boolean variable of this object.
     */
    public final boolean getDoubleLine() {
        return doubleLine;
    }

    /** Sets the double line separation property.
     * @param dlsep The double line separation to be set.
     */
    public final void setDLSeparation(float dlsep) {
        this.dlSeparation = dlsep;
    }

    /** Returns the double line separation of this object.
     * @return The double line separation.
     */
    public final float getDLSeparation() {
        return dlSeparation;
    }

    /** Draws a visual aid during the dragging of an arc object.
     * @param g2 The graphics context to draw to.
     */
    public final void drawVisualAid(VectorGraphics g2) {
        float ax1 = (float) getX1();
        float ay1 = (float) getY1();
        float ax2 = (float) getX2();
        float ay2 = (float) getY2();
        float ax3 = (float) getX();
        float ay3 = (float) getY();

        GeneralPath gp = new GeneralPath();
        gp.moveTo(ax1, ay1);
        gp.lineTo(ax2, ay2);
        gp.lineTo(ax3, ay3);
        gp.closePath();

        g2.setColor(JaxoColor.RED);
        g2.setStroke(new BasicStroke(1.0f));

        g2.draw(gp);
    }

    /** Draws the handles of this JaxoArc.
     * @param g2 The corresponding graphics context.
     */
    public final void drawHandles(VectorGraphics g2) {
        int ax1 = getX1();
        int ay1 = getY1();
        int ax2 = getX2();
        int ay2 = getY2();
        int ax3 = getX();
        int ay3 = getY();

        g2.setColor(JaxoColor.RED);
        g2.setStroke(new BasicStroke(1.0f));

        g2.drawRect(ax1 - (LENGTH / 2), ay1 - (LENGTH / 2), LENGTH, LENGTH);
        g2.drawRect(ax2 - (LENGTH / 2), ay2 - (LENGTH / 2), LENGTH, LENGTH);
        g2.drawRect(ax3 - (LENGTH / 2), ay3 - (LENGTH / 2), LENGTH, LENGTH);

        if (isMarked()) {
            g2.setColor(JaxoColor.GRAYSCALE150);
            g2.fillRect(ax1 - (LENGTH / 2) + 1, ay1 - (LENGTH / 2) + 1,
                LENGTH - 1, LENGTH - 1);
            g2.fillRect(ax2 - (LENGTH / 2) + 1, ay2 - (LENGTH / 2) + 1,
                LENGTH - 1, LENGTH - 1);
            g2.fillRect(ax3 - (LENGTH / 2) + 1, ay3 - (LENGTH / 2) + 1,
                LENGTH - 1, LENGTH - 1);
        }
    }

    // Returns the parameters for the arc calculated from the three points
    // introduced by the user.
    public final double[] getArcParameters() {
        double[] params = {0, 0, 0, 0, 0};

        double ax1 = (double) getX1();
        double ay1 = (double) getY1();
        double ax2 = (double) getX2();
        double ay2 = (double) getY2();
        double ax3 = (double) getX();
        double ay3 = (double) getY();

        double a = getA(ax1, ay1, ax2, ay2, ax3, ay3);
        double b = getB(ax1, ay1, ax2, ay2, ax3, ay3);
        double c = getC(ax1, ay1, ax2, ay2, ax3, ay3);

        setRelw((int) Math.round(ax3 - ax1));
        setRelh((int) Math.round(ay3 - ay1));

        double radius = Math.sqrt((((a * a) / 4) + ((b * b) / 4)) - c);
        double cordlength =
            Math.sqrt(Math.pow(ax1 - ax3, 2.d) + Math.pow(ay1 - ay3, 2.d));
        double alpha = 2 * Math.asin(cordlength / (2 * radius));
        double theta = Math.atan2(ay3 + (b / 2.d), ax3 + (a / 2.d));

        double[] arcp = {0, 0};

        if (((ax1 <= ax2) && (ax2 <= ax3) && (ay2 <= ay3) && (ay3 <= ay1))
                || (
                    (ax3 <= ax1) && (ax1 <= ax2) && (ay1 <= ay2)
                    && (ay2 <= ay3)
                )
                || (
                    (ax2 <= ax3) && (ax3 < ax1) && (ay3 <= ay1)
                    && (ay1 <= ay2)
                )
                || (
                    (ax1 <= ax2) && (ax2 <= ax3) && (ay2 <= ay1)
                    && (ay1 < ay3)
                )
                || (
                    (ax3 < ax1) && (ax1 <= ax2) && (ay1 <= ay3) && (
                        ay3 < ay2
                    )
                )
                || (
                    (ax2 <= ax3) && (ax3 < ax1) && (ay3 <= ay2) && (
                        ay2 < ay1
                    )
                )
                || (
                    (ax1 <= ax2) && (ax2 < ax3) && (ay3 <= ay2) && (
                        ay2 < ay1
                    )
                )
                || (
                    (ax2 <= ax3) && (ax3 <= ax1) && (ay1 < ay3) && (
                        ay3 < ay2
                    )
                )
                || (
                    (ax3 <= ax1) && (ax1 < ax2) && (ay2 <= ay1) && (
                        ay1 < ay3
                    )
                )
                || (
                    (ax2 < ax1) && (ax1 <= ax3) && (ay3 <= ay2)
                    && (ay2 <= ay1)
                )
                || (
                    (ax3 < ax2) && (ax2 < ax1) && (ay1 <= ay3) && (
                        ay3 <= ay2
                    )
                )
                || (
                    (ax1 < ax3) && (ax3 < ax2) && (ay2 <= ay1) && (
                        ay1 <= ay3
                    )
                )
                || (
                    (ax2 < ax1) && (ax1 < ax3) && (ay2 < ay3) && (ay3 <= ay1)
                )
                || (
                    (ax1 < ax3) && (ax3 <= ax2) && (ay1 < ay2) && (ay2 < ay3)
                )
                || (
                    (ax3 <= ax2) && (ax2 <= ax1) && (ay3 < ay1)
                    && (ay1 <= ay2)
                )
                || (
                    (ax2 < ax1) && (ax1 <= ax3) && (ay2 < ay1) && (ay1 < ay3)
                )
                || ((ax3 < ax2) && (ax2 < ax1) && (ay3 < ay2) && (ay2 < ay1))
                || (
                    (ax1 <= ax3) && (ax3 < ax2) && (ay1 < ay3) && (
                        ay3 <= ay2
                    )
                )
                || (
                    (ax3 <= ax1) && (ax1 < ax2) && (ay3 < ay1) && (
                        ay1 <= ay2
                    )
                )) {
            theta = Math.atan2(ay3 + (b / 2.d), ax3 + (a / 2.d));
            arcp = testArc(radius, -a / 2.d, -b / 2.d, alpha, theta, ax1, ay1);
        } else if ((
                    (ax3 <= ax2) && (ax2 <= ax1) && (ay2 <= ay1)
                    && (ay1 <= ay3)
                )
                || (
                    (ax1 <= ax3) && (ax3 <= ax2) && (ay3 <= ay2)
                    && (ay2 <= ay1)
                )
                || (
                    (ax2 <= ax1) && (ax1 < ax3) && (ay1 <= ay3)
                    && (ay3 <= ay2)
                )
                || (
                    (ax3 <= ax2) && (ax2 <= ax1) && (ay2 <= ay3)
                    && (ay3 < ay1)
                )
                || (
                    (ax1 < ax3) && (ax3 <= ax2) && (ay3 <= ay1) && (
                        ay1 < ay2
                    )
                )
                || (
                    (ax2 <= ax1) && (ax1 < ax3) && (ay1 <= ay2) && (
                        ay2 < ay3
                    )
                )
                || (
                    (ax3 <= ax2) && (ax2 < ax1) && (ay1 <= ay2) && (
                        ay2 < ay3
                    )
                )
                || (
                    (ax2 <= ax1) && (ax1 <= ax3) && (ay3 < ay1) && (
                        ay1 < ay2
                    )
                )
                || (
                    (ax1 <= ax3) && (ax3 < ax2) && (ay2 <= ay3) && (
                        ay3 < ay1
                    )
                )
                || (
                    (ax2 < ax3) && (ax3 <= ax1) && (ay1 <= ay2)
                    && (ay2 <= ay3)
                )
                || (
                    (ax1 < ax2) && (ax2 < ax3) && (ay3 <= ay1) && (
                        ay1 <= ay2
                    )
                )
                || (
                    (ax3 < ax1) && (ax1 < ax2) && (ay2 <= ay3) && (
                        ay3 <= ay1
                    )
                )
                || (
                    (ax2 < ax3) && (ax3 < ax1) && (ay2 < ay1) && (ay1 <= ay3)
                )
                || (
                    (ax3 < ax1) && (ax1 <= ax2) && (ay3 < ay2) && (ay2 < ay1)
                )
                || (
                    (ax1 <= ax2) && (ax2 <= ax3) && (ay1 < ay3)
                    && (ay3 <= ay2)
                )
                || (
                    (ax2 < ax3) && (ax3 <= ax1) && (ay2 < ay3) && (ay3 < ay1)
                )
                || ((ax1 < ax2) && (ax2 < ax3) && (ay1 < ay2) && (ay2 < ay3))) {
            theta = Math.atan2(ay1 + (b / 2.d), ax1 + (a / 2.d));
            arcp = testArc(radius, -a / 2.d, -b / 2.d, alpha, theta, ax3, ay3);
        }

        params[0] = -a / 2.d;
        params[1] = -b / 2.d;
        params[2] = radius;
        params[3] = arcp[0];
        params[4] = arcp[1];

        return params;
    }

    private double getA(double ax1, double ay1, double ax2, double ay2,
        double ax3, double ay3) {
        double y12 = ay1 - ay2;
        double y13 = ay1 - ay3;
        double y23 = ay2 - ay3;

        double a =
            (
                ((-y12 * ((ax3 * ax3) + (y13 * y23))) + (ax2 * ax2 * y13))
                - (ax1 * ax1 * y23)
            ) / (((ax3 * y12) + (ax1 * y23)) - (ax2 * y13));

        return a;
    }

    private double getB(double ax1, double ay1, double ax2, double ay2,
        double ax3, double ay3) {
        double y12 = ay1 - ay2;
        double y13 = ay1 - ay3;
        double y23 = ay2 - ay3;

        double b =
            (
                (
                    (
                        (
                            (ax1 * ax1 * ax2) - (ax1 * ax2 * ax2)
                            - (ax1 * ax1 * ax3) + (ax2 * ax2 * ax3)
                            + (ax1 * ax3 * ax3)
                        ) - (ax2 * ax3 * ax3) - (ax3 * ay1 * y12)
                        - (ax3 * y12 * ay2) + (ax2 * ay1 * y13)
                    ) - (ax1 * ay2 * y23) + (ax2 * y13 * ay3)
                ) - (ax1 * y23 * ay3)
            ) / (((ax3 * y12) + (ax1 * y23)) - (ax2 * y13));

        return b;
    }

    private double getC(double ax1, double ay1, double ax2, double ay2,
        double ax3, double ay3) {
        double y12 = ay1 - ay2;
        double y13 = ay1 - ay3;
        double y23 = ay2 - ay3;

        double c =
            (
                (
                    (
                        (
                            (-ax2 * ax2 * ax3 * ay1) + (
                                ax2 * ax3 * ax3 * ay1
                            ) + (ax1 * ax1 * ax3 * ay2)
                        ) - (ax1 * ax3 * ax3 * ay2) + (ax3 * ay1 * y12 * ay2)
                    ) - (ax1 * ax1 * ax2 * ay3) + (ax1 * ax2 * ax2 * ay3)
                ) - (ax2 * ay1 * y13 * ay3) + (ax1 * ay2 * y23 * ay3)
            ) / (((ax3 * y12) + (ax1 * y23)) - (ax2 * y13));

        return c;
    }

    // Does a test of the arc: if the area of the triangle inscribed in the
    // arc shrinks to zero we have a singular point. If failed the
    // corresponding arc will not be drawn.
    protected final boolean testArea() {
        boolean failed = false;

        double sideA =
            Math.sqrt(Math.pow(getX1() - getX2(), 2)
                + Math.pow(getY1() - getY2(), 2));
        double sideB =
            Math.sqrt(Math.pow(getX() - getX2(), 2)
                + Math.pow(getY() - getY2(), 2));
        double sideC =
            Math.sqrt(Math.pow(getX1() - getX(), 2)
                + Math.pow(getY1() - getY(), 2));

        double semip = 0.5 * (sideA + sideB + sideC);
        double area =
            Math.sqrt(semip * (semip - sideA) * (semip - sideB) * (
                    semip - sideC
                ));

        if (area < 150.d) {
            failed = true;
        }

        return failed;
    }

    // This does a test for the initial arc parameters determined from
    // getArcParameters(); if the arc does not satisfy all the conditions,
    // actions are taken to modify the parameters until all the conditions
    // are satisfied.
    protected final double[] testArc(double r, double cx, double cy,
        double al, double th, double tx, double ty) {
        double[] newpar = {0, 0};
        double newalpha = al;
        double newtheta = th;

        // Expand the triangle in such a way to avoid problems with rounding
        // errors of the general path "contains" method (glitches are still
        // there from time to time but this is the best I can do).
        // It turned out that one extra check get rid of all the glitches!!!
        double ax1 = (double) getX1();
        double ay1 = (double) getY1();
        double ax2 = (double) getX2();
        double ay2 = (double) getY2();
        double ax3 = (double) getX();
        double ay3 = (double) getY();

        double xG = (ax1 + ax2 + ax3) / 3.d;
        double yG = (ay1 + ay2 + ay3) / 3.d;

        double fact = 1.5f; //this control the expansion factor

        ax1 = xG + (fact * (ax1 - xG));
        ay1 = yG + (fact * (ay1 - yG));
        ax2 = xG + (fact * (ax2 - xG));
        ay2 = yG + (fact * (ay2 - yG));
        ax3 = xG + (fact * (ax3 - xG));
        ay3 = yG + (fact * (ay3 - yG));

        double a = getA(ax1, ay1, ax2, ay2, ax3, ay3);
        double b = getB(ax1, ay1, ax2, ay2, ax3, ay3);
        double c = getC(ax1, ay1, ax2, ay2, ax3, ay3);

        double radius = Math.sqrt((((a * a) / 4) + ((b * b) / 4)) - c);

        Arc2D arc =
            new Arc2D.Double(-r, -r, 2 * r, 2 * r, 0, Math.toDegrees(al),
                Arc2D.OPEN);
        GeneralPath gp = new GeneralPath();
        gp.moveTo(0.f, 0.f);
        gp.append(arc, false);
        gp = newtransarc(gp, cx, cy, th, 1.f);

        if (Math.abs(gp.getCurrentPoint().getX() - tx) > 0.0001) {
            newpar[0] = (2 * Math.PI) - al;
            newpar[1] = th;

            Arc2D arc1 =
                new Arc2D.Double(-radius, -radius, 2 * radius, 2 * radius, 0,
                    Math.toDegrees(newpar[0]), Arc2D.CHORD);
            GeneralPath gp1 = new GeneralPath();
            gp1.moveTo(0.f, 0.f);
            gp1.append(arc1, false);
            gp1 = newtransarc(gp1, -a / 2.d, -b / 2.d, newpar[1], 1.f);

            Arc2D arc3 =
                new Arc2D.Double(-r, -r, 2 * r, 2 * r, 0,
                    Math.toDegrees(newpar[0]), Arc2D.OPEN);
            GeneralPath gp3 = new GeneralPath();
            gp3.moveTo(0.f, 0.f);
            gp3.append(arc3, false);
            gp3 = newtransarc(gp3, cx, cy, newpar[1], 1.f);

            if (Math.abs(gp3.getCurrentPoint().getX() - tx) > 0.001) {
                newpar[0] = al;

                return newpar;
            }

            Point2D testPoint = new Point2D.Double(getX2(), getY2());
            if (!gp1.contains(testPoint)) {
                newpar[0] = al;
                newpar[1] = Math.atan2(ty - cy, tx - cx);
            }

            return newpar;
        }

        Arc2D arc2 =
            new Arc2D.Double(-radius, -radius, 2 * radius, 2 * radius, 0,
                Math.toDegrees(al), Arc2D.CHORD);
        GeneralPath gp2 = new GeneralPath();
        gp2.moveTo(0.f, 0.f);
        gp2.append(arc2, false);
        gp2 = newtransarc(gp2, -a / 2.d, -b / 2.d, th, 1.f);

        Point2D testPoint = new Point2D.Double(getX2(), getY2());
        if (!gp2.contains(testPoint)) {
            newpar[0] = (2 * Math.PI) - al;
            newpar[1] = Math.atan2(ty - cy, tx - cx);

            return newpar;
        }

        newpar[0] = newalpha;
        newpar[1] = newtheta;

        return newpar;
    }

    /** Determines which handle the user has selected to move/resize/edit
     * this arc.
     * @param clickX The x coordinate where the mouse click has ocurred.
     * @param clickY The y coordinate where the mouse click has ocurred.
     * @param editmode The current edit mode.
     * @return One of the static variables SELECT_* defined in JaObject
     * that specifies the handle which the user has clicked.
     */
    public final int getGrabbedHandle(int clickX, int clickY, int editmode) {
        int ax1 = getX1();
        int ay1 = getY1();
        int ax2 = getX2();
        int ay2 = getY2();
        int ax3 = getX();
        int ay3 = getY();

        int l2 = LENGTH / 2;

        if ((editmode == MOVE) || (editmode == COPY)) {
            if ((clickX >= (ax1 - l2)) && (clickX <= (ax1 + l2))
                    && (clickY >= (ay1 - l2)) && (clickY <= (ay1 + l2))) {
                return SELECT_BODY;
            } else if ((clickX >= (ax2 - l2)) && (clickX <= (ax2 + l2))
                    && (clickY >= (ay2 - l2)) && (clickY <= (ay2 + l2))) {
                return SELECT_BODY;
            } else if ((clickX >= (ax3 - l2)) && (clickX <= (ax3 + l2))
                    && (clickY >= (ay3 - l2)) && (clickY <= (ay3 + l2))) {
                return SELECT_BODY;
            }
        } else if (editmode == RESIZE) {
            if ((clickX >= (x1 - l2)) && (clickX <= (ax1 + l2))
                    && (clickY >= (ay1 - l2)) && (clickY <= (ay1 + l2))) {
                return SELECT_P1;
            } else if ((clickX >= (ax2 - l2)) && (clickX <= (ax2 + l2))
                    && (clickY >= (ay2 - l2)) && (clickY <= (ay2 + l2))) {
                return SELECT_P2;
            } else if ((clickX >= (ax3 - l2)) && (clickX <= (ax3 + l2))
                    && (clickY >= (ay3 - l2)) && (clickY <= (ay3 + l2))) {
                return SELECT_P3;
            }
        }

        return SELECT_NONE;
    }

    /** Shifts the elements of the vector containing the three click
     * points of this arc.
     * @param deltaX The x displacement.
     * @param deltaY The y displacement.
     */
    public final void moveBy(int deltaX, int deltaY) {
        setArcPts(getX1() + deltaX, getY1() + deltaY, getX2() + deltaX,
            getY2() + deltaY, getX() + deltaX, getY() + deltaY);
    }

    /** Draws an arrow on this arc.
     * @param g2 The graphics context.
     * @param c The color of the arrow.
     * @param length The radius of this arc.
     * @param size The size of the arrow.
     * @param fl The flip property of this arc.
     * @return A GeneralPath object of the arrow.
     */
    protected final GeneralPath arcArrow(VectorGraphics g2, Color c,
        double length, float size, boolean fl) {
        g2.setColor(c);
        g2.setStroke(new BasicStroke(2.0f));

        GeneralPath gp = new GeneralPath();
        float arrowsize = (5.f * size) / 8.f;
        gp.moveTo((float) arrowsize, 0.f);
        gp.lineTo(-arrowsize, arrowsize / 1.5f);
        gp.lineTo(-arrowsize / 1.5f, 0.f);
        gp.lineTo(-arrowsize, -arrowsize / 1.5f);
        gp.closePath();

        AffineTransform at = new AffineTransform();
        at.translate(0.f, -length);

        if (!flip) {
            at.rotate(Math.toRadians(180), 0.f, 0.f);
        }

        gp.transform(at);

        return gp;
    }

    /** Draws arrows on a double-arc.
     * @param g2 The graphics context.
     * @param c The color of the arrow.
     * @param length The radius of this arc.
     * @param size The size of the arrow.
     * @param fl The flip property of this arc.
     * @param dlSep The separation of the double-arc.
     * @return A GeneralPath object of the arrow.
     */
    protected final GeneralPath dArcArrow(VectorGraphics g2, Color c,
        double length, float size, boolean fl, float dlSep) {
        g2.setStroke(new BasicStroke(2.0f));

        GeneralPath gp = new GeneralPath();
        float arrowsize = (5.f * size) / 8.f;
        gp.moveTo((float) arrowsize, dlSep);
        gp.lineTo(-arrowsize, (arrowsize / 1.5f) + dlSep);
        gp.lineTo(-arrowsize / 1.5f, 0.f + dlSep);
        gp.lineTo(-arrowsize, (-arrowsize / 1.5f) + dlSep);
        gp.closePath();

        AffineTransform at = new AffineTransform();
        at.translate(0.f, -length);

        if (!flip) {
            at.rotate(Math.toRadians(180), 0.f, 0.f);
        }

        gp.transform(at);

        g2.setPaint(c);
        g2.fill(gp);

        return gp;
    }

    protected final GeneralPath newtransarc(GeneralPath gp, double cx,
        double cy, double theta, double scale) {
        AffineTransform at = new AffineTransform();
        at.translate(cx, cy);
        at.rotate(theta, 0.d, 0.d);
        at.scale(scale, scale);
        gp.transform(at);

        return gp;
    }

    /** Rescales this JaArcObject by the scale factor scale.
     * keeping the point (orx, ory) fixed.
     * @param orx The x-coordinate of the fixed point.
     * @param ory The y-coordinate of the fixed point.
     * @param scale The scale parameter.
     */
    public final void rescaleObject(int orx, int ory, float scale) {
        Point2D p1 = scalePoint(orx, ory, scale, getX1(), getY1());
        Point2D p2 = scalePoint(orx, ory, scale, getX2(), getY2());
        Point2D p3 = scalePoint(orx, ory, scale, getX(), getY());
        setArcPts((int) Math.round(p1.getX()), (int) Math.round(p1.getY()),
            (int) Math.round(p2.getX()), (int) Math.round(p2.getY()),
            (int) Math.round(p3.getX()), (int) Math.round(p3.getY()));

        double[] par = this.getArcParameters();
        int namp = (int) (this.getAmp() * scale);

        int nwig = 0;
        float nfreq = 0.f;

        if (this instanceof JaPArc) {
            nwig = (int) ((2 * Math.PI * par[2]) / this.getFreq());
            nfreq = (float) ((2 * Math.PI * par[2]) / nwig);
        }

        if (this instanceof JaGlArc) {
            nwig = (int) ((2 * Math.PI * par[2]) / this.getFreq() / 0.6d);
            nfreq = (float) ((2 * Math.PI * par[2]) / nwig / 0.6d);
        }

        this.setAmp(namp);
        this.setFreq(nfreq);
    }

    /** Brings up the edit panel that allows to change the parameters of
     * this object.
     * @return True if the editing actually changed the object, false if
     * the object has not been changed.
     */
    public final boolean editPanel() {
        JaxoArcOptionsPanel arcop = new JaxoArcOptionsPanel(this);

        return arcop.hasChanged();
    }

    /** The LaTeX command that sets the width for this Arc,
     *  using the axodraw.sty package.
     * @return The corresponding LaTeX command.
     */
    public final String latexWidth() {
        String thisValue = Float.toString(getStroke() / 2.f);
        String base = "\\SetWidth{";
        String closing = "}";
        String lineWidth = base.concat(thisValue.concat(closing));

        return lineWidth;
    }
}