Java source code of 'jhplot.jadraw.JaGlLoop'

/*

 * 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 java.awt.BasicStroke;
import java.awt.Dimension;
import org.freehep.graphics2d.VectorGraphics;
import java.awt.geom.Arc2D;
import java.awt.geom.FlatteningPathIterator;
import java.awt.geom.GeneralPath;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;


/**
 * Gluon loop
 *
 */

public class JaGlLoop extends JaLoopObject {
    /**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	/** Constructor: just calls super(). */
    public JaGlLoop() {
        super();
    }

    /** Returns an exact copy of this JaGlLoop.
     * @return A copy of this JaGlLoop.
     */
    public final JaObject copy() {
        JaGlLoop temp = (JaGlLoop) copy(new JaGlLoop());
        temp.setAmp(getAmp());
        temp.setFreq(getFreq());

        return temp;
    }

    /** Returns true if all serializable variables of this JaObject
     *  and those of the specified one are equal.
     * @param comp A JaObject to compare with.
     * @return True if the objects are equal, false otherwise.
     */
    public final boolean isCopy(JaObject comp) {
        boolean isCopy = false;

        if (comp instanceof JaGlLoop) {
            if (super.isCopy(comp)
                    && (((JaGlLoop) comp).getAmp() == getAmp())
                    && (((JaGlLoop) comp).getFreq() == getFreq())) {
                isCopy = true;
            }
        }

        return isCopy;
    }

    /** The method that draws this JaGlLoop.
     * @param g2 The graphics context where the JaGlLoop has to be drawn.
     * @param drawToScreen A boolean variable that indicates whether
     * the drawing is done on the screen or somewhere else. This is used
     * for exporting/printing, where the object handles should not be painted,
     * even if they are visible on the screen.
     */
    public final void jaxoDraw(VectorGraphics g2, boolean drawToScreen) {
        g2.setColor(getColor());
        g2.setStroke(new BasicStroke(getStroke()));

        GeneralPath gp = getGeneralPath();
        gp.reset();

        float boh = 0.6f;
        int angamp = 180;

        if (getAmp() < 0) {
            angamp = -180;
        }

        int a = Math.abs(getAmp());
        int n;
        float ts;
        double length =
            Math.sqrt(((double) getWidth() * (double) getWidth())
                + ((double) getHeight() * (double) getHeight()));

        if (length < 5) {
            length = 0;
        }

        length = 2 * length * Math.PI;

        n = (int) (length / getFreq() / boh);
        ts = (float) (length / boh / n);

        Arc2D arc = new Arc2D.Float();
        if (getDoubleLine()) {
            for (int i = 0; i < n; i++) {
                arc.setArc(i * ts * boh, (-a / 2) + getDLSeparation(), ts, a,
                    180, angamp, Arc2D.OPEN);
                gp.append(arc, true);
                arc.setArc(i * ts * boh, (-a / 2) - getDLSeparation(), ts, a,
                    180, angamp, Arc2D.OPEN);
                gp.append(arc, false);
                arc.setArc((i + 1) * ts * boh, (-a / 2) + getDLSeparation(),
                    ts * (1 - boh), a, 0, angamp, Arc2D.OPEN);
                gp.append(arc, true);
                arc.setArc((i + 1) * ts * boh, (-a / 2) - getDLSeparation(),
                    ts * (1 - boh), a, 0, angamp, Arc2D.OPEN);
                gp.append(arc, false);
            }
        } else {
            for (int i = 0; i < n; i++) {
                arc.setArc(i * ts * boh, -a / 2, ts, a, 180, angamp, Arc2D.OPEN);
                gp.append(arc, true);
                arc.setArc((i + 1) * ts * boh, -a / 2, ts * (1 - boh), a, 0,
                    angamp, Arc2D.OPEN);
                gp.append(arc, true);
            }
        }

        gp = conformalGP(gp, length, 2 * Math.PI);

        gp = transl(gp, 1 / Math.PI);
        g2.draw(gp);

        Rectangle2D bb = gp.getBounds2D();

        double[] bbox =
        {bb.getMinX(), bb.getMinY(), bb.getMaxX(), bb.getMaxY()};
        setBoundingBox(bbox);
    }

    /** The LaTeX command that is necessary to draw this JaGlLoop
     * using the axodraw.sty package.
     * @param scale A scale factor to translate Java coordinates to
     * LaTeX coordinates.
     * @param canvasDim The current dimension of the canvas.
     * @return The corresponding axodraw LaTeX command.
     */
    public final String latexCommand(float scale, Dimension canvasDim) {
        int canvasHeight = canvasDim.height;

        Point2D center = getLaTexCenter(scale, canvasHeight);
        float radius = getLaTexRadius(scale);
        Point2D angles = getLaTexAngles();
        float amplitude = getLaTexAmplitude(2.f * scale);
        float windings = getLaTexWindingNumber(1.0f);
        String command = "";
        String base = "\\GlueArc";

        if ((int) radius == 0) {
            command = "%";
        } else if (getDoubleLine()) {
            float offSet = getDLSeparation() / scale;

            String command1 =
                base + "(" + D_FORMAT.format(center.getX()) + ","
                + D_FORMAT.format(center.getY()) + ")" + "("
                + D_FORMAT.format(radius + offSet) + ","
                + D_FORMAT.format(angles.getX()) + ","
                + D_FORMAT.format(angles.getY()) + ")" + "{"
                + D_FORMAT.format(amplitude) + "}" + "{"
                + D_FORMAT.format(windings) + "}";

            String command2 =
                base + "(" + D_FORMAT.format(center.getX()) + ","
                + D_FORMAT.format(center.getY()) + ")" + "("
                + D_FORMAT.format(radius - offSet) + ","
                + D_FORMAT.format(angles.getX()) + ","
                + D_FORMAT.format(angles.getY()) + ")" + "{"
                + D_FORMAT.format(amplitude) + "}" + "{"
                + D_FORMAT.format(windings) + "}";

            command =
                command1.concat(command2.concat("%%JaxoDrawID:DoubleLine"
                        + "(" + D_FORMAT.format(getDLSeparation()) + ")"));
        } else {
            command =
                base + "(" + D_FORMAT.format(center.getX()) + ","
                + D_FORMAT.format(center.getY()) + ")" + "("
                + D_FORMAT.format(radius) + ","
                + D_FORMAT.format(angles.getX()) + ","
                + D_FORMAT.format(angles.getY()) + ")" + "{"
                + D_FORMAT.format(amplitude) + "}" + "{"
                + D_FORMAT.format(windings) + "}";
        }

        return command;
    }

    /////////////////////////////////////////////////////////////////////////
    //
    //   private auxiliary methods  //
    //
    /////////////////////////////////////////////////////////////////////////
    protected final Point2D getLaTexAngles() {
        Point2D anglesPoint = new Point2D.Float();
        int sAngle;
        int eAngle;
        int offset = 0;

        if (getRelw() == 0) {
            sAngle = 90;
        }

        sAngle =
            (int) Math.round((
                    Math.atan2((float) getRelh(), (float) getRelw()) * 180.f
                ) / Math.PI);

        if (((getRelw() <= 0) && (getRelh() > 0))
                || ((getRelw() >= 0) && (getRelh() < 0))) {
            sAngle = -sAngle - offset;
            eAngle = sAngle + 360;
            anglesPoint.setLocation(sAngle, eAngle);
        }

        if (((getRelw() > 0) && (getRelh() >= 0))
                || ((getRelw() < 0) && (getRelh() <= 0))) {
            sAngle = -sAngle + offset;
            eAngle = sAngle + 360;
            anglesPoint.setLocation(sAngle, eAngle);
        }

        return anglesPoint;
    }

    private float getLaTexAmplitude(float scaleFactor) {
        float a = getAmp() / scaleFactor;

        return a;
    }

    private float getLaTexWindingNumber(float scaleFactor) {
        double length =
            Math.sqrt(((double) getWidth() * (double) getWidth())
                + ((double) getHeight() * (double) getHeight()));

        float n = (float) ((length * 2 * Math.PI) / getFreq() / 0.7f);

        n = n / scaleFactor;

        return n;
    }

    private Point2D conformal(float px, float py, double length, double angle) {
        float nx =
            (float) (
                length / 2 * (
                    1
                    + (
                        Math.exp((py * angle) / length) * Math.cos((
                                px * angle
                            ) / length)
                    )
                )
            );
        float ny =
            (float) (
                -length / 2 * Math.exp((py * angle) / length) * Math.sin((
                        -px * angle
                    ) / length)
            );

        return new Point2D.Float(nx, ny);
    }

    private GeneralPath conformalGP(GeneralPath gpp, double length,
        double angle) {
        GeneralPath gp2 = new GeneralPath();
        FlatteningPathIterator pi =
            new FlatteningPathIterator(gpp.getPathIterator(null), 0.05);

        float[] segm = new float[6];
        int type;
        float corr = (1.f - (20.f / (float) length));

        gp2.moveTo(0.f, 0.f);

        while (!pi.isDone()) {
            type = pi.currentSegment(segm);

            float px1 =
                (float) conformal(segm[0], segm[1], length, angle).getX();
            float py1 =
                (float) conformal(segm[0], segm[1], length, angle).getY();
            float px2 =
                (float) conformal(segm[2], segm[3], length, angle).getX();
            float py2 =
                (float) conformal(segm[2], segm[3], length, angle).getY();
            float px3 =
                (float) conformal(segm[4], segm[5], length, angle).getX();
            float py3 =
                (float) conformal(segm[4], segm[5], length, angle).getY();

            if (type == PathIterator.SEG_MOVETO) {
                gp2.moveTo(px1, py1);
            } else if (type == PathIterator.SEG_LINETO) {
                gp2.lineTo(px1, py1);
            } else if (type == PathIterator.SEG_QUADTO) {
                px1 = (float) conformal(segm[0], corr * segm[1], length, angle)
                                  .getX();
                py1 = (float) conformal(segm[0], corr * segm[1], length, angle)
                                  .getY();
                gp2.quadTo(px1, py1, px2, py2);
            } else if (type == PathIterator.SEG_CUBICTO) {
                gp2.curveTo(px1, py1, px2, py2, px3, py3);
            } else {
                gp2.closePath();
            }

            pi.next();
        }

        return gp2;
    }
}