Package com.bbn.openmap.omGraphics.geom

Source Code of com.bbn.openmap.omGraphics.geom.BasicGeometry

// **********************************************************************
//
// <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/geom/BasicGeometry.java,v $
// $RCSfile: BasicGeometry.java,v $
// $Revision: 1.8.2.10 $
// $Date: 2007/01/30 20:26:47 $
// $Author: dietrick $
//
// **********************************************************************

package com.bbn.openmap.omGraphics.geom;

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.FlatteningPathIterator;
import java.awt.geom.GeneralPath;
import java.awt.geom.Line2D;
import java.awt.geom.PathIterator;
import java.io.Serializable;
import java.util.Hashtable;
import java.util.Map;

import com.bbn.openmap.omGraphics.OMGeometry;
import com.bbn.openmap.omGraphics.OMGraphicConstants;
import com.bbn.openmap.proj.Projection;
import com.bbn.openmap.util.Debug;

/**
* Base class implementation of OpenMap OMGeometry, the super class
* for all OMGraphics.
* <p>
*
* The geometry classes are intended to pull the object location data
* out of the OMGraphics. If you have a bunch of OMGraphics that are
* all rendered with common attributes, you can create a bunch of
* OMGeometry objects to plavce in a OMGeometryList that will render
* them all alike.
* <p>
*
* The BasicGeometry can hold attributes. Traditionally, there has
* been an appObject (Application Object) that could be set in the
* OMGeometry/OMGraphic to maintain a pointer for additional
* infomration about the shape. This has been modified so that an
* attribute Map can be maintained for the BasicGeometry to let it
* hold on to a bunch of organized attributes. To maintain backward
* compatibility, the setAppObject() and getAppObject() methods have
* been modified to manage a java.util.Map along with any Objects
* stored in the appObject. Using the setAppObject() and
* getAppObject() methods in conjunction with other attributes will
* cause that object to be stored in the attribute Map under the
* APP_OBJECT_KEY Map key.
*
* @see PolygonGeometry
* @see PolylineGeometry
* @see com.bbn.openmap.omGraphics.OMGeometryList
* @see Projection
*/
public abstract class BasicGeometry implements OMGeometry, Serializable,
        OMGraphicConstants {

    /**
     * The lineType describes the way a line will be drawn between
     * points. LINETYPE_STRAIGHT will mean the line is drawn straight
     * between the pixels of the endpoints of the line, across the
     * window. LINETYPE_GREATCIRCLE means the line will be drawn on
     * the window representing the shortest line along the land.
     * LINETYPE_RHUMB means a line will be drawn along a constant
     * bearing between the two points.
     */
    protected int lineType = LINETYPE_UNKNOWN;

    /** Flag to indicate that the object needs to be reprojected. */
    protected boolean needToRegenerate = true;

    /**
     * Space for an application to associate geometry with an
     * application object. This object can contain attribute
     * information about the geometry.
     *
     * @see #setAppObject
     * @see #getAppObject
     */
    protected Object appObject;

    /**
     * A flag to render this geometry visible.
     */
    protected boolean visible = true;

    /**
     * The Java 2D containing the Shape of the Graphic. There may be
     * several paths appended to each other, in case the graphic wraps
     * around the earth, and we need to show the other edge of the
     * graphic on the other side of the earth.
     */
    protected transient GeneralPath shape = null;

    protected static final String APP_OBJECT_KEY = "app_object_key";
    protected static final String ATT_MAP_KEY = "att_map_key";

    //////////////////////////////////////////////////////////

    /**
     * Set the line type for the graphic, which will affect how the
     * lines will be drawn. See the definition of the lineType
     * parameter. Accepts LINETYPE_RHUMB, LINETYPE_STRAIGHT and
     * LINETYPE_GREATCIRCLE. Any weird values get set to
     * LINETYPE_STRAIGHT.
     *
     * @param value the line type of the graphic.
     */
    public void setLineType(int value) {
        if (lineType == value)
            return;
        setNeedToRegenerate(true); // flag dirty

        lineType = value;
    }

    /**
     * Return the line type.
     *
     * @return the linetype - LINETYPE_RHUMB, LINETYPE_STRAIGHT,
     *         LINETYPE_GREATCIRCLE or LINETYPE_UNKNOWN.
     */
    public int getLineType() {
        return lineType;
    }

    /**
     * Return the render type.
     *
     * @return the rendertype of the object - RENDERTYPE_LATLON,
     *         RENDERTYPE_XY, RENDERTYPE_OFFSET and
     *         RENDERTYPE_UNKNOWN.
     */
    public abstract int getRenderType();

    /**
     * Sets the regenerate flag for the graphic. This flag is used to
     * determine if extra work needs to be done to prepare the object
     * for rendering.
     *
     * @param value boolean
     */
    public void setNeedToRegenerate(boolean value) {
        needToRegenerate = value;
        if (value == true) {
            shape = null;
        }
    }

    /**
     * Return the regeneration status.
     *
     * @return boolean
     */
    public boolean getNeedToRegenerate() {
        return needToRegenerate;
    }

    /**
     * Set the visibility variable. NOTE: <br>
     * This is checked by the OMGeometryList when it iterates through
     * its list for render and gesturing. It is not checked by the
     * internal OMGeometry methods, although maybe it should be...
     *
     * @param visible boolean
     */
    public void setVisible(boolean visible) {
        this.visible = visible;
    }

    /**
     * Get the visibility variable.
     *
     * @return boolean
     */
    public boolean isVisible() {
        return visible;
    }

    /**
     * Let the geometry object know it's selected. No action mandated.
     */
    public void select() {}

    /**
     * Let the geometry object know it's deselected. No action
     * mandated.
     */
    public void deselect() {}

    /**
     * Holds an application specific object for later access. This can
     * be used to associate an application object with an OMGeometry
     * for later retrieval. For instance, when the graphic is clicked
     * on, the application gets the OMGeometry object back from the
     * OMGeometryList, and can then get back to the application level
     * object through this pointer.
     * <P>
     *
     * The BasicGeometry has been updated to use an attribute Object
     * Map to hold multiple attributes. If no attributes have been
     * added, then the appObject will just hold any object passed in
     * here. If attributes have already been added, then calling this
     * method will add the object to the Map under the APP_OBJECT_KEY
     * key. getAppObject() will return the object set in this method.
     *
     * @param obj Object
     */
    public synchronized void setAppObject(Object obj) {
        setAppObject(obj, true);
    }

    /**
     * Same as setAppObject with the option for disabling the
     * attribute Map management.
     *
     * @param checkToReplaceObjWithMap if false, just sets obj to
     *        appObject.
     */
    protected synchronized void setAppObject(Object obj,
                                             boolean checkToReplaceObjWithMap) {
        if (checkToReplaceObjWithMap && checkAttributeMap()) {
            putAttribute(APP_OBJECT_KEY, obj);
        } else {
            appObject = obj;
        }
    }

    /**
     * Gets the application's object pointer. If an attribute Map
     * object is being used, returns the object stored in that map
     * under the APP_OBJECT_KEY key.
     *
     * @return Object
     */
    public synchronized Object getAppObject() {
        return getAppObject(true);
    }

    /**
     * Same as getAppObject, with the option of disabling the
     * attribute Map management.
     *
     * @param checkForObjOnMap if false, just returns the appObject.
     */
    protected synchronized Object getAppObject(boolean checkForObjOnMap) {
        if (checkForObjOnMap && checkAttributeMap()) {
            return getAttribute(APP_OBJECT_KEY);
        } else {
            return appObject;
        }
    }

    /**
     * A call used by the BasicGeometry to replace a current
     * appication object with an Object Map while also adding that
     * application object to the Map under the APP_OBJECT_KEY key
     * value.
     */
    protected void replaceAppObjectWithAttributeMap() {
        if (!checkAttributeMap()) {
            // OK, we know we need to create a Map for the attributes,
            // and place it in the appObject of this BasicGeometry.
            // So, get whatever is already there,
            Object appObj = getAppObject(false);

            // Create the new Map, set a pointer to itself so we know
            // it's the attribute Map (and not just replacing a Map
            // that someone else was using before
            Map attributes = createAttributeMap();
            attributes.put(ATT_MAP_KEY, attributes);
            setAppObject(attributes, false);

            // Now, set the old appObject if appropriate.
            if (appObj != null) {
                attributes.put(APP_OBJECT_KEY, appObj);
            }
        }
    }

    /**
     * Returns true if the appObject is a Map and if it's the
     * attribute Map, false if the appObject is something different or
     * null.
     */
    protected boolean checkAttributeMap() {
        return checkAttributeMap(getAppObject(false));
    }

    /**
     * Returns true of the Object is a Map and is pointing to itself
     * in the Map under the ATT_MAP_KEY.
     */
    protected boolean checkAttributeMap(Object obj) {
        return (obj instanceof Map && ((Map) obj).get(ATT_MAP_KEY) == obj);
    }

    /**
     * Returns a Map that is being used as an attribute holder. If a
     * Map doesn't exist, one will be created. If the current
     * appObject isn't the map, a Map will be created and the
     * appObject will be added to it under the APP_OBJECT_KEY.
     * Regardless, the attribute map will be returned from this method
     * call.
     */
    protected Map getAttributeMap() {
        // replaceAppObjectWithAttributeMap will do nothing if
        // attribute map is already set.
        replaceAppObjectWithAttributeMap();
        return (Map) getAppObject(false);
    }

    /**
     * Method to extend if you don't like Hashtables used for
     * attribute table.
     */
    protected Map createAttributeMap() {
        return new Hashtable();
    }

    /**
     * Adds a key-value pair to the attribute Map. The Map will be
     * created if it doesn't exist.
     */
    public void putAttribute(Object key, Object value) {
        if (key != null && value != null) {
            getAttributeMap().put(key, value);
        }
    }

    /**
     * Returns the object stored in a Map stored in the appObject. If
     * the appObject is a Map, the key will be passed to it even if
     * the Map isn't considered to be the 'official' attribute Map.
     */
    public Object getAttribute(Object key) {
        if (key != null) {
            Object appObj = getAppObject(false);
            if (appObj instanceof Map) {
                return ((Map) appObj).get(key);
            }
        }
        return null;
    }

    /**
     * Removes the object stored in a Map stored in the appObject. If
     * the appObject is a Map, the key will be passed to it even if
     * the Map isn't considered to be the 'official' attribute Map.
     * Returns the removed value from the Map, or null if there wasn't
     * a value for the given key.
     */
    public Object removeAttribute(Object key) {
        Object appObj = getAppObject(false);
        if (appObj instanceof Map) {
            return ((Map) appObj).remove(key);
        }
        // else
        return null;
    }

    /**
     * Removes all of the objects stored in a Map stored in the
     * appObject. If the appObject is a Map, the clear command will be
     * passed to it even if the Map isn't considered to be the
     * 'official' attribute Map.
     */
    public void clearAttributes() {
        Object appObj = getAppObject(false);
        if (appObj instanceof Map) {
            ((Map) appObj).clear();
        }
    }

    /**
     * Returns the 'official' attribute Map, null if it hasn't been
     * set.
     */
    public Map getAttributes() {
        Object appObj = getAppObject(false);
        if (checkAttributeMap(appObj)) {
            // Only returns the attribute Map if it's the official
            // version, which is marked by having a pointer to itself
            // under the ATT_MAP_KEY
            return (Map) appObj;
        }
        // else
        return null;
    }

    /**
     * Sets the 'official' attribute Map, moving any appObject that
     * isn't currently the 'official' attribute Map into the map under
     * the APP_OBJECT_KEY.
     */
    public void setAttributes(Map atts) {
       
        if (atts == null) {
            return;
        }
       
        if (!checkAttributeMap()) {
            if (appObject != null) {
                atts.put(APP_OBJECT_KEY, appObject);
            }
        } else {
            Object appObj = getAttribute(APP_OBJECT_KEY);
            if (appObj != null) {
                atts.put(APP_OBJECT_KEY, appObj);
            }
        }
        atts.put(ATT_MAP_KEY, atts);
        setAppObject(atts, false);
    }

    //////////////////////////////////////////////////////////////////////////

    /**
     * Prepare the geometry for rendering. This must be done before
     * calling <code>render()</code>! If a vector graphic has
     * lat-lon components, then we project these vertices into x-y
     * space. For raster graphics we prepare in a different fashion.
     * <p>
     * If the generate is unsuccessful, it's usually because of some
     * oversight, (for instance if <code>proj</code> is null), and
     * if debugging is enabled, a message may be output to the
     * controlling terminal.
     * <p>
     *
     * @param proj Projection
     * @return boolean true if successful, false if not.
     * @see #regenerate
     */
    public abstract boolean generate(Projection proj);

    /**
     * 
     */
    public boolean isRenderable() {
        return (!getNeedToRegenerate() && isVisible() && shape != null);
    }

    /**
     * Paint the graphic, as a filled shape.
     * <P>
     *
     * This paints the graphic into the Graphics context. This is
     * similar to <code>paint()</code> function of
     * java.awt.Components. Note that if the graphic has not been
     * generated or if it isn't visible, it will not be rendered.
     * <P>
     *
     * This method used to be abstract, but with the conversion of
     * OMGeometrys to internally represent themselves as
     * java.awt.Shape objects, it's a more generic method. If the
     * OMGeometry hasn't been updated to use Shape objects, it should
     * have its own render method.
     *
     * @param g Graphics2D context to render into.
     */
    public void fill(Graphics g) {
        if (isRenderable()) {
            ((Graphics2D) g).fill(shape);
        }
    }

    /**
     * Paint the graphic, as an outlined shape.
     * <P>
     *
     * This paints the graphic into the Graphics context. This is
     * similar to <code>paint()</code> function of
     * java.awt.Components. Note that if the graphic has not been
     * generated or if it isn't visible, it will not be rendered.
     * <P>
     *
     * This method used to be abstract, but with the conversion of
     * OMGeometrys to internally represent themselves as
     * java.awt.Shape objects, it's a more generic method. If the
     * OMGeometry hasn't been updated to use Shape objects, it should
     * have its own render method.
     *
     * @param g Graphics2D context to render into.
     */
    public void draw(Graphics g) {
        if (isRenderable()) {
            ((Graphics2D) g).draw(shape);
        }
    }

    /**
     * Return the shortest distance from the edge of a graphic to an
     * XY-point.
     * <p>
     *
     * @param x X coordinate of the point.
     * @param y Y coordinate of the point.
     * @return float distance, in pixels, from graphic to the point.
     *         Returns Float.POSITIVE_INFINITY if the graphic isn't
     *         ready (ungenerated).
     */
    public float distanceToEdge(int x, int y) {
        float distance = Float.POSITIVE_INFINITY;

        if (getNeedToRegenerate() || shape == null) {
            return distance;
        }

        PathIterator pi2 = shape.getPathIterator(null);
        FlatteningPathIterator pi = new FlatteningPathIterator(pi2, .25);
        double[] coords = new double[6];
        int count = 0;
        double startPntX = 0;
        double startPntY = 0;
        double endPntX = 0;
        double endPntY = 0;

        while (!pi.isDone()) {
            int type = pi.currentSegment(coords);
            float dist;

            if (type == PathIterator.SEG_LINETO) {
                startPntX = endPntX;
                startPntY = endPntY;
                endPntX = coords[0];
                endPntY = coords[1];

                dist = (float) Line2D.ptSegDist(startPntX,
                        startPntY,
                        endPntX,
                        endPntY,
                        (double) x,
                        (double) y);

                if (dist < distance) {
                    distance = dist;
                }

                if (Debug.debugging("omgraphicdetail")) {
                    Debug.output("Type: " + type + "(" + (count++) + "), "
                            + startPntX + ", " + startPntY + ", " + endPntX
                            + ", " + endPntY + ", " + x + ", " + y
                            + ", distance: " + distance);
                }

            } else {

                // This should be the first and last
                // condition, SEG_MOVETO and SEG_CLOSE
                startPntX = coords[0];
                startPntY = coords[1];
                endPntX = coords[0];
                endPntY = coords[1];
            }

            pi.next();
        }

        return distance;
    }

    /**
     * Return the shortest distance from the graphic to an XY-point.
     * Checks to see of the point is contained within the OMGraphic,
     * which may, or may not be the right thing for clear OMGraphics
     * or lines.
     * <p>
     *
     * This method used to be abstract, but with the conversion of
     * OMGeometrys to internally represent themselves as
     * java.awt.Shape objects, it's a more generic method. If the
     * OMGeometry hasn't been updated to use Shape objects, it should
     * have its own distance method.
     * <p>
     *
     * Calls _distance(x, y);
     *
     * @param x X coordinate of the point.
     * @param y Y coordinate of the point.
     * @return float distance, in pixels, from graphic to the point.
     *         Returns Float.POSITIVE_INFINITY if the graphic isn't
     *         ready (ungenerated).
     */
    public float distance(int x, int y) {
        return _distance(x, y);
    }

    /**
     * Return the shortest distance from the graphic to an XY-point.
     * Checks to see of the point is contained within the OMGraphic,
     * which may, or may not be the right thing for clear OMGraphics
     * or lines.
     * <p>
     *
     * _distance was added so subclasses could make this call if their
     * geometries/attributes require this action (when fill color
     * doesn't matter).
     *
     * @param x X coordinate of the point.
     * @param y Y coordinate of the point.
     * @return float distance, in pixels, from graphic to the point.
     *         Returns Float.POSITIVE_INFINITY if the graphic isn't
     *         ready (ungenerated).
     */
    protected float _distance(int x, int y) {
        float distance = Float.POSITIVE_INFINITY;

        if (getNeedToRegenerate() || shape == null) {
            return distance;
        }

        if (shape.contains((double) x, (double) y)) {
            //          if (Debug.debugging("omgraphicdetail")) {
            //              Debug.output(" contains " + x + ", " + y);
            //          }
            return 0f;
        } else {
            return distanceToEdge(x, y);
        }
    }

    /**
     * Answsers the question whether or not the OMGeometry contains
     * the given pixel point.
     * <P>
     * This method used to be abstract, but with the conversion of
     * OMGeometrys to internally represent themselves as
     * java.awt.Shape objects, it's a more generic method. If the
     * OMGeometry hasn't been updated to use Shape objects, it should
     * have its own contains method.
     * <P>
     * This method duplicates a java.awt.Shape method, with some
     * protection wrapped around it. If you have other queries for the
     * internal Shape object, just ask for it and then ask it
     * directly. This method is provided because it is the most
     * useful, used when determining if a mouse event is occuring over
     * an object on the map.
     *
     * @param x X pixel coordinate of the point.
     * @param y Y pixel coordinate of the point.
     * @return getShape().contains(x, y), false if the OMGraphic
     *         hasn't been generated yet.
     */
    public boolean contains(int x, int y) {
        Shape shape = getShape();
        boolean ret = false;

        if (shape != null) {
            ret = shape.contains((double) x, (double) y);
        }

        return ret;
    }

    /**
     * Invoke this to regenerate a "dirty" graphic. This method is a
     * wrapper around the <code>generate()</code> method. It invokes
     * <code>generate()</code> only if</code> needToRegenerate()
     * </code> on the graphic returns true. To force a graphic to be
     * generated, call <code>generate()</code> directly.
     *
     * @param proj the Projection
     * @return true if generated, false if didn't do it (maybe a
     *         problem).
     * @see #generate
     */
    public boolean regenerate(Projection proj) {
        if (proj == null) {
            return false;
        }

        if (getNeedToRegenerate()) {
            return generate(proj);
        }

        return false;
    }

    /**
     * Get the java.awt.Shape object that represents the projected
     * graphic. The array will one Shape object even if the object
     * wraps around the earth and needs to show up in more than one
     * place on the map. In conditions like that, the Shape will have
     * multiple parts.
     * <p>
     *
     * The java.awt.Shape object gives you the ability to do a little
     * spatial analysis on the graphics.
     *
     * @return java.awt.geom.GeneralPath (a java.awt.Shape object), or
     *         null if the graphic needs to be generated with the
     *         current map projection, or null if the OMGeometry
     *         hasn't been updated to use Shape objects for its
     *         internal representation.
     */
    public GeneralPath getShape() {
        return shape;
    }

    /**
     * Set the java.awt.Shape object that represents the projected
     * graphic. This Shape object should be internally generated, but
     * this method is provided to clear out the object to save memory,
     * or to allow a little customization if your requirements
     * dictate.
     * <p>
     *
     * The java.awt.Shape object gives you the ability to do a little
     * spatial analysis on the graphics.
     *
     * @param gp java.awt.geom.GeneralPath, or null if the graphic
     *        needs to be generated with the current map projection or
     *        to clear out the object being held by the OMGeometry.
     */
    public void setShape(GeneralPath gp) {
        shape = gp;
    }

    /**
     * Create a Shape object given an array of x points and y points.
     * The x points a y points should be projected. This method is
     * used by subclasses that get projected coordinates out of the
     * projection classes, and they need to build a Shape object from
     * those coordinates.
     *
     * @param xpoints projected x coordinates
     * @param ypoints projected y coordinates
     * @param isPolygon whether the points make up a polygon, or a
     *        polyline. If it's true, the Shape object returned is a
     *        Polygon. If false, the Shape returned is a GeneralPath
     *        object.
     * @return The Shape object for the points.
     */
    public static GeneralPath createShape(int xpoints[], int ypoints[],
                                          boolean isPolygon) {
        return createShape(xpoints, ypoints, 0, xpoints.length, isPolygon);
    }

    /**
     * Create a Shape object given an array of x points and y points.
     * The x points a y points should be projected. This method is
     * used by subclasses that get projected coordinates out of the
     * projection classes, and they need to build a Shape object from
     * those coordinates.
     *
     * @param xpoints projected x coordinates
     * @param ypoints projected y coordinates
     * @param startIndex the starting coordinate index in the array.
     * @param length the number of points to use from the array for
     *        the shape.
     * @param isPolygon whether the points make up a polygon, or a
     *        polyline. If it's true, the Shape object returned is a
     *        Polygon. If false, the Shape returned is a GeneralPath
     *        object.
     * @return The Shape object for the points.
     */
    public static GeneralPath createShape(int xpoints[], int ypoints[],
                                          int startIndex, int length,
                                          boolean isPolygon) {
        // used to return a Shape

        if (xpoints == null || ypoints == null) {
            return null;
        }

        if (startIndex < 0) {
            startIndex = 0;
        }

        if (length > xpoints.length - startIndex) {
            // Do as much as you can...
            length = xpoints.length - startIndex - 1;
        }

        GeneralPath path = new GeneralPath(GeneralPath.WIND_EVEN_ODD, length);

        if (length > startIndex) {
            path.moveTo(xpoints[startIndex], ypoints[startIndex]);
            for (int j = startIndex + 1; j < length; j++) {
                path.lineTo(xpoints[j], ypoints[j]);
            }

            if (isPolygon) {
                path.closePath();
            }
        }

        return path;
    }

    /**
     * Utility method that iterates over a Shape object and prints out
     * the points.
     */
    public static void describeShapeDetail(Shape shape) {
        describeShapeDetail(shape, .25);
    }

    /**
     * Utility method that iterates over a Shape object and prints out
     * the points. The flattening is used for a
     * FlatteningPathIterator, controlling the scope of the path
     * traversal.
     */
    public static void describeShapeDetail(Shape shape, double flattening) {
        PathIterator pi2 = shape.getPathIterator(null);
        FlatteningPathIterator pi = new FlatteningPathIterator(pi2, flattening);
        double[] coords = new double[6];
        int pointCount = 0;

        Debug.output(" -- start describeShapeDetail with flattening["
                + flattening + "]");
        while (!pi.isDone()) {
            int type = pi.currentSegment(coords);
            Debug.output(" Shape point [" + type + "] (" + (pointCount++)
                    + ") " + coords[0] + ", " + coords[1]);
            pi.next();
        }

        Debug.output(" -- end (" + pointCount + ")");
    }

    /**
     * Convenience method to add the coordinates to the given
     * GeneralPath. You need to close the path yourself if you want it
     * to be a polygon.
     *
     * @param toShape the GeneralPath Shape object to add the
     *        coordinates to.
     * @param xpoints horizontal pixel coordiantes.
     * @param ypoints vertical pixel coordiantes.
     * @return toShape, with coordinates appended.
     */
    public static GeneralPath appendShapeEdge(GeneralPath toShape,
                                              int xpoints[], int ypoints[]) {
        return appendShapeEdge(toShape, xpoints, ypoints, 0, xpoints.length);
    }

    /**
     * Convenience method to add the coordinates to the given
     * GeneralPath. You need to close the path yourself if you want it
     * to be a polygon.
     *
     * @param toShape the GeneralPath Shape object to add the
     *        coordinates to.
     * @param xpoints horizontal pixel coordiantes.
     * @param ypoints vertical pixel coordiantes.
     * @param startIndex the index into pixel coordinate array to
     *        start reading from.
     * @param length the number of coordinates to add.
     * @return toShape, with coordinates appended.
     */
    public static GeneralPath appendShapeEdge(GeneralPath toShape,
                                              int xpoints[], int ypoints[],
                                              int startIndex, int length) {
        return appendShapeEdge(toShape, createShape(xpoints,
                ypoints,
                startIndex,
                length,
                false));
    }

    /**
     * Convenience method to append the edge of a GeneralPath Shape to
     * another GeneralPath Shape. A PathIterator is used to figure out
     * the points to use to add to the toShape. You need to close the
     * path yourself if you want it to be a polygon. Assumes that the
     * two paths should be connected.
     *
     * @param toShape the GeneralPath Shape object to add the edge to.
     * @param addShape the GeneralPath Shape to add to the toShape.
     * @return toShape, with coordinates appended. Returns addShape if
     *         toShape was null.
     */
    public static GeneralPath appendShapeEdge(GeneralPath toShape,
                                              GeneralPath addShape) {
        return appendShapeEdge(toShape, addShape, true);
    }

    /**
     * Convenience method to append the edge of a GeneralPath Shape to
     * another GeneralPath Shape. A PathIterator is used to figure out
     * the points to use to add to the toShape. You need to close the
     * path yourself if you want it to be a polygon.
     *
     * @param toShape the GeneralPath Shape object to add the edge to.
     * @param addShape the GeneralPath Shape to add to the toShape.
     * @param lineTo specify whether the first point of the appended
     *        path is connected to the original path. True to connect.
     * @return toShape, with coordinates appended. Returns addShape if
     *         toShape was null.
     */
    public static GeneralPath appendShapeEdge(GeneralPath toShape,
                                              GeneralPath addShape,
                                              boolean lineTo) {

        boolean DEBUG = Debug.debugging("arealist");
        int pointCount = 0;

        // If both null, return null.
        if (addShape == null) {
            return toShape;
        }

        if (toShape == null) {
            return addShape;
        }

        PathIterator pi2 = addShape.getPathIterator(null);
        FlatteningPathIterator pi = new FlatteningPathIterator(pi2, .25);
        double[] coords = new double[6];

        while (!pi.isDone()) {
            int type = pi.currentSegment(coords);
            if (lineTo) {
                if (DEBUG) {
                    Debug.output(" adding point [" + type + "] ("
                            + (pointCount++) + ") " + (float) coords[0] + ", "
                            + (float) coords[1]);
                }
                toShape.lineTo((float) coords[0], (float) coords[1]);

            } else {
                if (DEBUG) {
                    Debug.output("Creating new shape, first point "
                            + (float) coords[0] + ", " + (float) coords[1]);
                }
                toShape.moveTo((float) coords[0], (float) coords[1]);
                lineTo = true;
            }
            pi.next();
        }

        if (DEBUG) {
            Debug.output(" -- end point (" + pointCount + ")");
        }

        return toShape;
    }

    /**
     * Create a general path from a point plus a height and width;
     */
    public static GeneralPath createBoxShape(int x, int y, int width, int height) {
        int[] xs = new int[4];
        int[] ys = new int[4];

        xs[0] = x;
        ys[0] = y;
        xs[1] = x + width;
        ys[1] = y;
        xs[2] = x + width;
        ys[2] = y + height;
        xs[3] = x;
        ys[3] = y + height;

        return createShape(xs, ys, true);
    }
}
TOP

Related Classes of com.bbn.openmap.omGraphics.geom.BasicGeometry

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.