Package com.bbn.openmap.omGraphics

Source Code of com.bbn.openmap.omGraphics.OMArrowHead

// **********************************************************************
//
// <copyright>
//
//  BBN Technologies
//  10 Moulton Street
//  Cambridge, MA 02138
//  (617) 873-8000
//
//  Copyright (C) BBNT Solutions LLC. All rights reserved.
//
// </copyright>
// **********************************************************************
//
// $Source: /cvs/distapps/openmap/src/openmap/com/bbn/openmap/omGraphics/OMArrowHead.java,v $
// $RCSfile: OMArrowHead.java,v $
// $Revision: 1.4.2.6 $
// $Date: 2005/12/08 21:11:20 $
// $Author: dietrick $
//
// **********************************************************************

package com.bbn.openmap.omGraphics;

import java.awt.BasicStroke;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.geom.GeneralPath;
import java.util.Iterator;
import java.util.Vector;

import com.bbn.openmap.proj.DrawUtil;
import com.bbn.openmap.util.Debug;

/**
* Basic implementation of arrowhead graphics. This class expects intimate
* knowledge of an OMLine, and is used to add Arrowhead shapes to the actual
* OMLine internal Shape object. You don't have to know about this class, just
* call the OMLine methods that create arrowheads and the OMLine will take care
* of the rest.
*/
public class OMArrowHead {

    public static final int ARROWHEAD_DIRECTION_FORWARD = 0;
    public static final int ARROWHEAD_DIRECTION_BACKWARD = 1;
    public static final int ARROWHEAD_DIRECTION_BOTH = 2;

    // These are base settings.
    protected static int DEFAULT_WINGTIP = 5;
    protected static int DEFAULT_WINGLENGTH = 20;

    protected Shape shape = null;
    protected int arrowDirectionType = ARROWHEAD_DIRECTION_FORWARD;
    protected int location = 100;
    protected int wingTip = 5;
    protected int wingLength = 20;

    public OMArrowHead(int arrowDirectionType, int location) {
        this(arrowDirectionType, location, DEFAULT_WINGTIP, DEFAULT_WINGLENGTH);
    }

    public OMArrowHead(int arrowDirectionType, int location, int wingtip,
            int winglength) {
        this.arrowDirectionType = arrowDirectionType;
        setLocation(location);
        this.wingTip = wingtip;
        this.wingLength = winglength;
    }

    public void generate(OMAbstractLine omal) {
        if (wingTip > 0 && wingLength > 0 && omal != null) {
            shape = createArrowHeads(arrowDirectionType,
                    location,
                    omal,
                    wingTip,
                    wingLength);
        } else {
            shape = null;
        }
    }

    public void render(Graphics g) {
        if (shape != null) {
            ((java.awt.Graphics2D) g).fill(shape);
        }
    }

    /**
     * Create an arrowhead for the provided line
     *
     * @param arrowDirectionType ARROWHEAD_DIRECTION_FORWARD for the arrowhead
     *        pointing to the last coordinate of the OMLine,
     *        ARROWHEAD_DIRECTION_BACKWARD for the arrowhead pointing to the
     *        first coordinate in the OMLine, and ARROWHEAD_DIRECTION_BOTH for
     *        the arrowhead on both ends.
     * @param location A number between 0-100, reflecting the percentage of the
     *        line traversed before placing the arrowhead. For
     *        ARROWHEAD_DIRECTION_FORWARD and a location of 100, the arrowhead
     *        will be placed all the way at the end of the line. For a location
     *        of 50, the arrowhead will be placed in the middle of the line.
     * @param line OMLine to use to place arrowhead.
     * @return the GeneralPath for the arrowhead.
     */
    public static GeneralPath createArrowHeads(int arrowDirectionType,
                                               int location, OMAbstractLine line) {
        return createArrowHeads(arrowDirectionType,
                location,
                line,
                DEFAULT_WINGTIP,
                DEFAULT_WINGLENGTH);
    }

    /**
     * Create an arrowhead for the provided line
     *
     * @param arrowDirectionType ARROWHEAD_DIRECTION_FORWARD for the arrowhead
     *        pointing to the last coordinate of the OMLine,
     *        ARROWHEAD_DIRECTION_BACKWARD for the arrowhead pointing to the
     *        first coordinate in the OMLine, and ARROWHEAD_DIRECTION_BOTH for
     *        the arrowhead on both ends.
     * @param location A number between 0-100, reflecting the percentage of the
     *        line traversed before placing the arrowhead. For
     *        ARROWHEAD_DIRECTION_FORWARD and a location of 100, the arrowhead
     *        will be placed all the way at the end of the line. For a location
     *        of 50, the arrowhead will be placed in the middle of the line.
     * @param line OMLine to use to place arrowhead.
     * @param wingTip Number of pixels to push the side of the arrowhead away
     *        from the line.
     * @param wingLength Number of pixels reflecting the arrowhead length.
     * @return the GeneralPath for the arrowhead.
     */
    public static GeneralPath createArrowHeads(int arrowDirectionType,
                                               int location,
                                               OMAbstractLine line,
                                               int wingTip, int wingLength) {

        Point[] locPoints = locateArrowHeads(arrowDirectionType, location, line);

        if (locPoints == null) {
            return null;
        }

        Stroke stroke = line.getStroke();
        float lineWidth = 1f;
        if (stroke instanceof BasicStroke) {
            lineWidth = ((BasicStroke) stroke).getLineWidth();
            wingTip += lineWidth;
            wingLength += lineWidth * 2;
        }

        GeneralPath shape = createArrowHead(locPoints[0],
                locPoints[1],
                wingTip,
                wingLength);

        int numLocPoints = locPoints.length;
        for (int i = 2; i < numLocPoints - 1; i += 2) {
            shape.append(createArrowHead(locPoints[i],
                    locPoints[i + 1],
                    wingTip,
                    wingLength), false);
        }

        return shape;
    }

    public static void addArrowHeads(int arrowDirectionType, int location,
                                     OMAbstractLine line) {

        Shape arrowHeads = createArrowHeads(arrowDirectionType, location, line);
        if (arrowHeads != null) {
            line.getShape().append(arrowHeads, false);
        }
    }

    protected static GeneralPath createArrowHead(Point from, Point to,
                                                 int wingTip, int wingLength) {
        int dx = to.x - from.x;
        int dy = to.y - from.y;

        int dd = (int) DrawUtil.distance(to.x, to.y, from.x, from.y);

        if (dd < 6)
            dd = 6;

        int[] xpts = new int[3];
        int[] ypts = new int[3];
        xpts[0] = (int) (to.x + (dy * (wingTip) - dx * wingLength) / dd);
        ypts[0] = (int) (to.y + (dx * (-wingTip) - dy * wingLength) / dd);
        xpts[1] = (int) (to.x);
        ypts[1] = (int) (to.y);
        xpts[2] = (int) (to.x + (dy * (-wingTip) - dx * wingLength) / dd);
        ypts[2] = (int) (to.y + (dx * (wingTip) - dy * wingLength) / dd);

        return OMGraphic.createShape(xpts, ypts, true);
    }

    /**
     * Create the ArrowHead objects for the lines, based on the settings. This
     * function is called while OMLine is being generated. User's don't need to
     * call this function. In fact, it assumes that generate() has been called
     * (or is being called) on the OMLine. It adds the ArrowHeads to the
     * GeneralPath Shape object.
     */
    protected static Point[] locateArrowHeads(int arrowDirection,
                                              int arrowLocation,
                                              OMAbstractLine line) {

        // NOTE: xpoints[0] refers to the original copy of the
        // xpoints, as opposed to the [1] copy, which gets used when the line
        // needs to wrap around the screen and show up on the other
        // side. Might have to think about the [1] points, and adding
        // a arrowhead there if it shows up in the future.

        if (line.xpoints == null || line.xpoints.length == 0
                || line.xpoints[0].length == 0) {
            // line doesn't know where it is...
            return null;
        }

        int pointIndex = line.xpoints[0].length - 1;
        if (Debug.debugging("arrowheads")) {
            Debug.output("createArrowHeads(): Number of points = " + pointIndex);
        }

        int drawingLinetype = OMLine.STRAIGHT_LINE; // default
        if (pointIndex > 1) {
            drawingLinetype = OMLine.CURVED_LINE;
        }

        // Used as the index for points in the xy point array to use
        // as anchors for the arrowheads
        int[] end = new int[2];
        int[] start = new int[2];
        end[0] = pointIndex;
        start[0] = 0;
        end[1] = 0;
        start[1] = pointIndex;

        // better names:
        int origEnd = pointIndex;
        int origStart = 0;

        int numArrows = 1; // default
        if (arrowDirection == OMArrowHead.ARROWHEAD_DIRECTION_BOTH) {
            numArrows = 2;
        }

        // one for the start and end of each arrowhead (there could be
        // two)
        Point sPoint1 = new Point();
        Point ePoint1 = new Point();
        Point sPoint2 = new Point();
        Point ePoint2 = new Point();

        // do we have to reverse the arrows?

        if (line instanceof OMLine) {
            OMLine omLine = (OMLine) line;
            if (omLine.arc != null && omLine.arc.getReversed() == true) {
                if (arrowDirection == OMArrowHead.ARROWHEAD_DIRECTION_FORWARD) {
                    arrowDirection = OMArrowHead.ARROWHEAD_DIRECTION_BACKWARD;
                } else if (arrowDirection == OMArrowHead.ARROWHEAD_DIRECTION_BACKWARD) {
                    arrowDirection = OMArrowHead.ARROWHEAD_DIRECTION_FORWARD;
                }
            }
        }

        Vector pointVec = new Vector();
       
        // The for loop is needed in case the projection library
        // created several projected versions of the line, those used
        // for wrapping around to the other side of the map.
        for (int lineNum = 0; lineNum < line.xpoints.length; lineNum++) {
            int[] xpoints = line.xpoints[lineNum];
            int[] ypoints = line.ypoints[lineNum];

            switch (drawingLinetype) {

            case OMLine.STRAIGHT_LINE:
                Debug.message("arrowheads",
                        "createArrowHeads(): Inside x-y space");
                int newEndX;
                int newEndY;
                int dx;
                int dy;
                float dd;

                // backwards arrow

                if (needBackwardArrow(arrowDirection)) {

                    // need to have the newEndX/Y point at the
                    // original start.

                    newEndX = xpoints[origStart];
                    newEndY = ypoints[origStart];

                    if (arrowLocation != 100) {
                        // find out where the location should be, but
                        // in
                        // reverse.
                        dx = xpoints[origStart] - xpoints[origEnd];
                        dy = ypoints[origStart] - ypoints[origEnd];
                        int offset = 0;
                        // Straight up or down
                        if (dx == 0) {
                            // doesn't matter, start and end the same
                            newEndX = xpoints[origEnd];
                            // calculate the percentage from start of line
                            offset = (int) ((float) dy * (arrowLocation / 100.0f));
                            // set the end at the begining...
                            newEndY = ypoints[origEnd] + offset;

                        } else {

                            dd = Math.abs((float) dy / (float) dx);
                            // If the line moves more x than y
                            if (Math.abs(dx) > Math.abs(dy)) {
                                // set the x
                                newEndX = xpoints[origEnd]
                                        + (int) ((float) dx * (arrowLocation / 100.0));
                                // find the y for that x and set that
                                newEndY = ypoints[origEnd];
                                offset = (int) ((float) Math.abs(xpoints[origEnd]
                                        - newEndX) * dd);

                                if (dy < 0) {
                                    newEndY -= offset;
                                } else {
                                    newEndY += offset;
                                }

                            } else {
                                // switch everything...set y end
                                newEndY = ypoints[origEnd]
                                        + (int) ((float) dy * (arrowLocation / 100.0));
                                // initialize the x to beginning
                                newEndX = xpoints[origEnd];
                                // calculate the difference x has to
                                // move based on y end
                                offset = (int) ((float) Math.abs(ypoints[origEnd]
                                        - newEndY) / dd);
                                // set the end
                                if (dx < 0) {
                                    newEndX -= offset;
                                } else {
                                    newEndX += offset;
                                }
                            }

                        }

                    } // if (arrowLocation != 100)

                    if (start[1] < 0) {
                        start[1] = 0;
                    }

                    // which point do we copy to?
                    if (numArrows == 2) {
                        // we copy the backwards arrow to
                        // sPoint2/ePoint2

                        sPoint2.x = xpoints[origEnd];
                        sPoint2.y = ypoints[origEnd];
                        ePoint2.x = newEndX;
                        ePoint2.y = newEndY;

                    } else {
                        // we copy the backwards arrow to
                        // sPoint1/ePoint1

                        sPoint1.x = xpoints[origEnd];
                        sPoint1.y = ypoints[origEnd];
                        ePoint1.x = newEndX;
                        ePoint1.y = newEndY;

                    }

                } // end if needBackwardArrow.

                if (needForwardArrow(arrowDirection)) {

                    newEndX = xpoints[origEnd];
                    newEndY = ypoints[origEnd];

                    if (arrowLocation != 100) {
                        // find out where the location should be.
                        dx = xpoints[origEnd] - xpoints[origStart];
                        dy = ypoints[origEnd] - ypoints[origStart];
                        int offset = 0;
                        // Straight up or down
                        if (dx == 0) {
                            // doesn't matter, start and end the same
                            newEndX = xpoints[origStart];
                            // calculate the percentage from start of
                            // line
                            offset = (int) ((float) dy * (arrowLocation / 100.0f));
                            // set the end at the begining...
                            newEndY = ypoints[origStart] + offset;

                        } else {

                            dd = Math.abs((float) dy / (float) dx);
                            // If the line moves more x than y
                            if (Math.abs(dx) > Math.abs(dy)) {
                                // set the x
                                newEndX = xpoints[origStart]
                                        + (int) ((float) dx * (arrowLocation / 100.0f));
                                // find the y for that x and set that
                                newEndY = ypoints[origStart];
                                offset = (int) ((float) Math.abs(xpoints[origStart]
                                        - newEndX) * dd);

                                if (dy < 0) {
                                    newEndY -= offset;
                                } else {
                                    newEndY += offset;
                                }

                            } else {
                                // switch everything...set y end
                                newEndY = ypoints[origStart]
                                        + (int) ((float) dy * (arrowLocation / 100.0));
                                // initialize the x to beginning
                                newEndX = xpoints[origStart];
                                // calculate the difference x has to
                                // move
                                // based on y end
                                offset = (int) ((float) Math.abs(ypoints[origStart]
                                        - newEndY) / dd);
                                // set the end
                                if (dx < 0) {
                                    newEndX -= offset;
                                } else {
                                    newEndX += offset;
                                }
                            }

                        }

                    } // end if (arrowLocation != 100)

                    // finally, copy the results to sPoint1/ePoint1

                    // no longer needed: if (start[0] < 0) { start[0]
                    // = 0;
                    // }

                    sPoint1.x = xpoints[origStart];
                    sPoint1.y = ypoints[origStart];
                    ePoint1.x = newEndX;
                    ePoint1.y = newEndY;

                }

                break;
            case OMLine.CURVED_LINE:
                Debug.message("arrowheads",
                        "createArrowHeads(): Curved line arrowhead");

                if (needBackwardArrow(arrowDirection)) {

                    Debug.message("arrowheads",
                            "createArrowHeads(): direction backward and");

                    // compute the backward index....
                    int bindex = pointIndex
                            - (int) ((float) pointIndex * (float) (arrowLocation / 100.0));
                    if (bindex == 0) {
                        bindex = 1;
                    }

                    if (numArrows == 2) {
                        // copy it to s/ePoint2

                        sPoint2.x = xpoints[bindex];
                        sPoint2.y = ypoints[bindex];
                        ePoint2.x = xpoints[bindex - 1];
                        ePoint2.y = ypoints[bindex - 1];
                    } else {
                        // copy it to s/ePoint1

                        sPoint1.x = xpoints[bindex];
                        sPoint1.y = ypoints[bindex];
                        ePoint1.x = xpoints[bindex - 1];
                        ePoint1.y = ypoints[bindex - 1];

                    }

                } // end if (needBackwardArrow(arrowDirection))

                if (needForwardArrow(arrowDirection)) {

                    int findex = (int) ((float) pointIndex * (float) (arrowLocation / 100.0));
                    if (findex == pointIndex) {
                        findex = findex - 1;
                    }

                    sPoint1.x = xpoints[findex];
                    sPoint1.y = ypoints[findex];
                    ePoint1.x = xpoints[findex + 1];
                    ePoint1.y = ypoints[findex + 1];

                } // end if (needForwardArrow(arrowDirection))

                break;
            } // end switch(drawingLinetype)
           
            pointVec.add(sPoint1.clone());
            pointVec.add(ePoint1.clone());
           
            if (numArrows > 1) {
                pointVec.add(sPoint2.clone());
                pointVec.add(ePoint2.clone());
            }
           
        }

        Point[] ret = new Point[pointVec.size()];
        int i = 0;
        for (Iterator it = pointVec.iterator();it.hasNext(); i++) {
            ret[i] = (Point)it.next();
        }
       
        return ret;
    }

    private static boolean needBackwardArrow(int arrowDir) {
        return (arrowDir == ARROWHEAD_DIRECTION_BACKWARD || arrowDir == ARROWHEAD_DIRECTION_BOTH);
    }

    private static boolean needForwardArrow(int arrowDir) {
        return (arrowDir == ARROWHEAD_DIRECTION_FORWARD || arrowDir == ARROWHEAD_DIRECTION_BOTH);
    }

    public int getArrowDirectionType() {
        return arrowDirectionType;
    }

    public void setArrowDirectionType(int arrowDirectionType) {
        this.arrowDirectionType = arrowDirectionType;
    }

    public int getLocation() {
        return location;
    }

    public void setLocation(int location) {
        if (location < 1)
            this.location = 1;
        else if (location > 100)
            this.location = 100;
        else
            this.location = location;
    }

    public Shape getShape() {
        return shape;
    }

    public void setShape(Shape shape) {
        this.shape = shape;
    }

    public int getWingLength() {
        return wingLength;
    }

    public void setWingLength(int wingLength) {
        this.wingLength = wingLength;
    }

    public int getWingTip() {
        return wingTip;
    }

    public void setWingTip(int wingTip) {
        this.wingTip = wingTip;
    }

}
TOP

Related Classes of com.bbn.openmap.omGraphics.OMArrowHead

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.