Newer
Older
piccolo2d.java / extras / src / main / java / edu / umd / cs / piccolox / util / LineShape.java
/*
 * Copyright (c) 2008, Piccolo2D project, http://piccolo2d.org
 * Copyright (c) 1998-2008, University of Maryland
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided
 * that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this list of conditions
 * and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
 * and the following disclaimer in the documentation and/or other materials provided with the
 * distribution.
 *
 * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its
 * contributors may be used to endorse or promote products derived from this software without specific
 * prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package edu.umd.cs.piccolox.util;

import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;

public class LineShape implements Shape, MutablePoints {
    private MutablePoints points;
    private Rectangle2D bounds = new Rectangle2D.Double();

    public LineShape(MutablePoints points) {
        setPoints(points);
    }

    public void setPoints(MutablePoints points) {
        if (points == null) {
            points = new XYArray();
        }
        this.points = points;
    }

    // from Points

    public int getPointCount() {
        return points.getPointCount();
    }

    public double getX(int i) {
        return points.getX(i);
    }

    public double getY(int i) {
        return points.getY(i);
    }

    public Point2D getPoint(int i, Point2D dst) {
        return points.getPoint(i, dst);
    }

    public Rectangle2D getBounds(Rectangle2D dst) {
        points.getBounds(dst);
        return dst;
    }

    // from MutablePoints

    public void updateBounds() {
        bounds.setRect(0.0d, 0.0d, 0.0d, 0.0d);
        points.getBounds(bounds);
    }

    public void setPoint(int i, double x, double y) {
        points.setPoint(i, x, y);
        updateBounds();
    }

    public void addPoint(int pos, double x, double y) {
        points.addPoint(pos, x, y);
        updateBounds();
    }

    public void removePoints(int pos, int num) {
        points.removePoints(pos, num);
        updateBounds();
    }

    public void transformPoints(AffineTransform trans) {
        XYArray newPoints = new XYArray(points.getPointCount());
        newPoints.appendPoints(points);
        newPoints.transformPoints(trans);
        points = newPoints;
    }

    //

    public Rectangle getBounds() {
        return new Rectangle((int) bounds.getX(), (int) bounds.getY(), (int) bounds.getWidth(), (int) bounds
                .getHeight());
    }

    public Rectangle2D getBounds2D() {
        return bounds;
    }

    public static boolean contains(double x, double y, double x1, double y1, double x2, double y2, boolean min,
            boolean max, double d) {
        double dx = x2 - x1, dy = y2 - y1;
        double dx2 = dx * dx, dy2 = dy * dy;
        double p;
        if (dx != 0) {
            p = (((x - x1) / dx) + ((dy * (y - y1)) / dx2)) / (1 + (dy2 / dx2));
        }
        else if (dy != 0) {
            p = (((y - y1) / dy) + ((dx * (x - x1)) / dy2)) / (1 + (dx2 / dy2));
        }
        else {
            return false;
        }
        if (max && p > 1.0) {
            return false;
        }
        else if (min && p < 0.0) {
            return false;
        }
        dx = (p * dx) + x1 - x;
        dy = (p * dy) + y1 - y;
        double len = dx * dx + dy * dy;
        return (len < d);
    }

    public boolean contains(double x, double y, double d) {
        double x1, y1, x2, y2;
        if (points.getPointCount() == 0) {
            return false;
        }
        x2 = points.getX(0);
        y2 = points.getX(0);
        for (int i = 0; i < points.getPointCount(); i++) {
            x1 = x2;
            y1 = y2;
            x2 = points.getX(i);
            y2 = points.getX(i);
            if (contains(x, y, x1, y1, x2, y2, false, false, d)) {
                return true;
            }
        }
        return false;
    }

    public boolean contains(double x, double y) {
        return contains(x, y, 2.0d);
    }

    public boolean contains(Point2D p) {
        return contains(p.getX(), p.getY());
    }

    public static boolean intersects(double x1, double y1, double x2, double y2, double x3, double y3, double x4,
            double y4, boolean min1, boolean max1, boolean min2, boolean max2) {
        double dx1 = x2 - x1, dy1 = y2 - y1, dx2 = x4 - x3, dy2 = y4 - y3;
        double d, p2, p1;

        if (dy1 != 0.0) {
            d = dx1 / dy1;
            p2 = (x3 - x1 + (d * (y1 - y3))) / ((d * dy2) - dx2);
            p1 = (dy2 * p2 + y3 - y1) / dy1;
        }
        else if (dy2 != 0.0) {
            d = dx2 / dy2;
            p1 = (x1 - x3 + (d * (y3 - y1))) / ((d * dy1) - dx1);
            p2 = (dy1 * p1 + y1 - y3) / dy2;
        }
        else if (dx1 != 0.0) {
            d = dy1 / dx1;
            p2 = (y3 - y1 + (d * (x1 - x3))) / ((d * dx2) - dy2);
            p1 = (dx2 * p2 + x3 - x1) / dx1;
        }
        else if (dx2 != 0.0) {
            d = dy2 / dx2;
            p1 = (y1 - y3 + (d * (x3 - x1))) / ((d * dx1) - dy1);
            p2 = (dx1 * p1 + x1 - x3) / dx2;
        }
        else {
            return false;
        }
        return (((!min1) || (p1 >= 0.0)) && ((!max1) || (p1 <= 1.0)) && ((!min2) || (p2 >= 0.0)) && ((!max2) || (p2 <= 1.0)));
    }

    public boolean intersects(double x, double y, double w, double h) {
        double x1, y1, x2, y2;
        if (points.getPointCount() == 0) {
            return false;
        }
        x2 = points.getX(0);
        y2 = points.getY(0);
        for (int i = 0; i < points.getPointCount(); i++) {
            x1 = x2;
            y1 = y2;
            x2 = points.getX(i);
            y2 = points.getY(i);
            if (intersects(x, y, x + w, y, x1, y1, x2, y2, true, true, true, true)
                    || intersects(x + w, y, x + w, y + h, x1, y1, x2, y2, true, true, true, true)
                    || intersects(x + w, y + h, x, y + h, x1, y1, x2, y2, true, true, true, true)
                    || intersects(x, y + h, x, y, x1, y1, x2, y2, true, true, true, true)) {
                return true;
            }
        }
        return false;
    }

    public boolean intersects(Rectangle2D r) {
        return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight());
    }

    public boolean contains(double x, double y, double w, double h) {
        return contains(x, y) && contains(x + w, y) && contains(x, y + h) && contains(x + w, y + h);
    }

    public boolean contains(Rectangle2D r) {
        return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());
    }

    //

    //

    public PathIterator getPathIterator(AffineTransform at) {
        return new LinePathIterator(points, at);
    }

    public PathIterator getPathIterator(AffineTransform at, double flatness) {
        return new LinePathIterator(points, at);
    }

    private static class LinePathIterator implements PathIterator {

        private Points points;
        private AffineTransform trans;
        private int i = 0;

        public LinePathIterator(Points points, AffineTransform trans) {
            this.points = points;
            this.trans = trans;
        }

        public int getWindingRule() {
            return GeneralPath.WIND_EVEN_ODD;
        }

        public boolean isDone() {
            return i >= points.getPointCount();
        }

        public void next() {
            i++;
        }

        private Point2D tempPoint = new Point2D.Double();

        private void currentSegment() {
            tempPoint.setLocation(points.getX(i), points.getY(i));
            if (trans != null) {
                trans.transform(tempPoint, tempPoint);
            }
        }

        public int currentSegment(float[] coords) {
            currentSegment();
            coords[0] = (float) tempPoint.getX();
            coords[1] = (float) tempPoint.getY();
            return (i == 0 ? PathIterator.SEG_MOVETO : PathIterator.SEG_LINETO);
        }

        public int currentSegment(double[] coords) {
            currentSegment();
            coords[0] = tempPoint.getX();
            coords[1] = tempPoint.getY();
            return (i == 0 ? PathIterator.SEG_MOVETO : PathIterator.SEG_LINETO);
        }
    }
}