/* * 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); } } }