📄 area.java
字号:
do { segments.add(v); v = v.next; } while (v != path); } paths = weilerAtherton(segments); deleteRedundantPaths(paths); } /** * Clears the Area object, creating an empty area. */ public void reset() { solids = new Vector(); holes = new Vector(); } /** * Returns whether this area encloses any area. * @return true if the object encloses any area. */ public boolean isEmpty() { if (solids.size() == 0) return true; double totalArea = 0; for (int i = 0; i < solids.size(); i++) totalArea += Math.abs(((Segment) solids.elementAt(i)).getSignedArea()); for (int i = 0; i < holes.size(); i++) totalArea -= Math.abs(((Segment) holes.elementAt(i)).getSignedArea()); if (totalArea <= EPSILON) return true; return false; } /** * Determines whether the Area consists entirely of line segments * @return true if the Area lines-only, false otherwise */ public boolean isPolygonal() { for (int i = 0; i < holes.size(); i++) if (! ((Segment) holes.elementAt(i)).isPolygonal()) return false; for (int i = 0; i < solids.size(); i++) if (! ((Segment) solids.elementAt(i)).isPolygonal()) return false; return true; } /** * Determines if the Area is rectangular.<P> * * This is strictly qualified. An area is considered rectangular if:<BR> * <li>It consists of a single polygonal path.<BR> * <li>It is oriented parallel/perpendicular to the xy axis<BR> * <li>It must be exactly rectangular, i.e. small errors induced by * transformations may cause a false result, although the area is * visibly rectangular.<P> * @return true if the above criteria are met, false otherwise */ public boolean isRectangular() { if (isEmpty()) return true; if (holes.size() != 0 || solids.size() != 1) return false; Segment path = (Segment) solids.elementAt(0); if (! path.isPolygonal()) return false; int nCorners = 0; Segment s = path; do { Segment s2 = s.next; double d1 = (s.P2.getX() - s.P1.getX())*(s2.P2.getX() - s2.P1.getX())/ ((s.P1.distance(s.P2)) * (s2.P1.distance(s2.P2))); double d2 = (s.P2.getY() - s.P1.getY())*(s2.P2.getY() - s2.P1.getY())/ ((s.P1.distance(s.P2)) * (s2.P1.distance(s2.P2))); double dotproduct = d1 + d2; // For some reason, only rectangles on the XY axis count. if (d1 != 0 && d2 != 0) return false; if (Math.abs(dotproduct) == 0) // 90 degree angle nCorners++; else if ((Math.abs(1.0 - dotproduct) > 0)) // 0 degree angle? return false; // if not, return false s = s.next; } while (s != path); return nCorners == 4; } /** * Returns whether the Area consists of more than one simple * (non self-intersecting) subpath. * * @return true if the Area consists of none or one simple subpath, * false otherwise. */ public boolean isSingular() { return (holes.size() == 0 && solids.size() <= 1); } /** * Returns the bounding box of the Area.<P> Unlike the CubicCurve2D and * QuadraticCurve2D classes, this method will return the tightest possible * bounding box, evaluating the extreme points of each curved segment.<P> * @return the bounding box */ public Rectangle2D getBounds2D() { if (solids.size() == 0) return new Rectangle2D.Double(0.0, 0.0, 0.0, 0.0); double xmin; double xmax; double ymin; double ymax; xmin = xmax = ((Segment) solids.elementAt(0)).P1.getX(); ymin = ymax = ((Segment) solids.elementAt(0)).P1.getY(); for (int path = 0; path < solids.size(); path++) { Rectangle2D r = ((Segment) solids.elementAt(path)).getPathBounds(); xmin = Math.min(r.getMinX(), xmin); ymin = Math.min(r.getMinY(), ymin); xmax = Math.max(r.getMaxX(), xmax); ymax = Math.max(r.getMaxY(), ymax); } return (new Rectangle2D.Double(xmin, ymin, (xmax - xmin), (ymax - ymin))); } /** * Returns the bounds of this object in Rectangle format. * Please note that this may lead to loss of precision. * * @return The bounds. * @see #getBounds2D() */ public Rectangle getBounds() { return getBounds2D().getBounds(); } /** * Create a new area of the same run-time type with the same contents as * this one. * * @return the clone */ public Object clone() { try { Area clone = new Area(); for (int i = 0; i < solids.size(); i++) clone.solids.add(((Segment) solids.elementAt(i)).cloneSegmentList()); for (int i = 0; i < holes.size(); i++) clone.holes.add(((Segment) holes.elementAt(i)).cloneSegmentList()); return clone; } catch (CloneNotSupportedException e) { throw (Error) new InternalError().initCause(e); // Impossible } } /** * Compares two Areas. * * @param area the area to compare against this area (<code>null</code> * permitted). * @return <code>true</code> if the areas are equal, and <code>false</code> * otherwise. */ public boolean equals(Area area) { if (area == null) return false; if (! getBounds2D().equals(area.getBounds2D())) return false; if (solids.size() != area.solids.size() || holes.size() != area.holes.size()) return false; Vector pathA = new Vector(); pathA.addAll(solids); pathA.addAll(holes); Vector pathB = new Vector(); pathB.addAll(area.solids); pathB.addAll(area.holes); int nPaths = pathA.size(); boolean[][] match = new boolean[2][nPaths]; for (int i = 0; i < nPaths; i++) { for (int j = 0; j < nPaths; j++) { Segment p1 = (Segment) pathA.elementAt(i); Segment p2 = (Segment) pathB.elementAt(j); if (! match[0][i] && ! match[1][j]) if (p1.pathEquals(p2)) match[0][i] = match[1][j] = true; } } boolean result = true; for (int i = 0; i < nPaths; i++) result = result && match[0][i] && match[1][i]; return result; } /** * Transforms this area by the AffineTransform at. * * @param at the transform. */ public void transform(AffineTransform at) { for (int i = 0; i < solids.size(); i++) ((Segment) solids.elementAt(i)).transformSegmentList(at); for (int i = 0; i < holes.size(); i++) ((Segment) holes.elementAt(i)).transformSegmentList(at); // Note that the orientation is not invariant under inversion if ((at.getType() & AffineTransform.TYPE_FLIP) != 0) { setDirection(holes, false); setDirection(solids, true); } } /** * Returns a new Area equal to this one, transformed * by the AffineTransform at. * @param at the transform. * @return the transformed area * @throws NullPointerException if <code>at</code> is <code>null</code>. */ public Area createTransformedArea(AffineTransform at) { Area a = (Area) clone(); a.transform(at); return a; } /** * Determines if the point (x,y) is contained within this Area. * * @param x the x-coordinate of the point. * @param y the y-coordinate of the point. * @return true if the point is contained, false otherwise. */ public boolean contains(double x, double y) { int n = 0; for (int i = 0; i < solids.size(); i++) if (((Segment) solids.elementAt(i)).contains(x, y)) n++; for (int i = 0; i < holes.size(); i++) if (((Segment) holes.elementAt(i)).contains(x, y)) n--; return (n != 0); } /** * Determines if the Point2D p is contained within this Area. * * @param p the point. * @return <code>true</code> if the point is contained, <code>false</code> * otherwise. * @throws NullPointerException if <code>p</code> is <code>null</code>. */ public boolean contains(Point2D p) { return contains(p.getX(), p.getY()); } /** * Determines if the rectangle specified by (x,y) as the upper-left * and with width w and height h is completely contained within this Area, * returns false otherwise.<P> * * This method should always produce the correct results, unlike for other * classes in geom. * * @param x the x-coordinate of the rectangle. * @param y the y-coordinate of the rectangle. * @param w the width of the the rectangle. * @param h the height of the rectangle. * @return <code>true</code> if the rectangle is considered contained */ public boolean contains(double x, double y, double w, double h) { LineSegment[] l = new LineSegment[4]; l[0] = new LineSegment(x, y, x + w, y); l[1] = new LineSegment(x, y + h, x + w, y + h); l[2] = new LineSegment(x, y, x, y + h); l[3] = new LineSegment(x + w, y, x + w, y + h); // Since every segment in the area must a contour // between inside/outside segments, ANY intersection // will mean the rectangle is not entirely contained. for (int i = 0; i < 4; i++) { for (int path = 0; path < solids.size(); path++) { Segment v; Segment start; start = v = (Segment) solids.elementAt(path); do { if (l[i].hasIntersections(v)) return false; v = v.next; } while (v != start); } for (int path = 0; path < holes.size(); path++) { Segment v; Segment start; start = v = (Segment) holes.elementAt(path); do { if (l[i].hasIntersections(v)) return false; v = v.next; } while (v != start); } } // Is any point inside? if (! contains(x, y)) return false; // Final hoop: Is the rectangle non-intersecting and inside, // but encloses a hole? Rectangle2D r = new Rectangle2D.Double(x, y, w, h); for (int path = 0; path < holes.size(); path++) if (! ((Segment) holes.elementAt(path)).isSegmentOutside(r)) return false; return true; } /** * Determines if the Rectangle2D specified by r is completely contained * within this Area, returns false otherwise.<P> * * This method should always produce the correct results, unlike for other * classes in geom. * * @param r the rectangle. * @return <code>true</code> if the rectangle is considered contained * * @throws NullPointerException if <code>r</code> is <code>null</code>. */ public boolean contains(Rectangle2D r) { return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight()); } /** * Determines if the rectangle specified by (x,y) as the upper-left * and with width w and height h intersects any part of this Area. * * @param x the x-coordinate for the rectangle. * @param y the y-coordinate for the rectangle. * @param w the width of the rectangle. * @param h the height of the rectangle. * @return <code>true</code> if the rectangle intersects the area, * <code>false</code> otherwise. */ public boolean intersects(double x, double y, double w, double h) { if (solids.size() == 0) return false; LineSegment[] l = new LineSegment[4]; l[0] = new LineSegment(x, y, x + w, y); l[1] = new LineSegment(x, y + h, x + w, y + h); l[2] = new LineSegment(x, y, x, y + h); l[3] = new LineSegment(x + w, y, x + w, y + h); // Return true on any intersection for (int i = 0; i < 4; i++) { for (int path = 0; path < solids.size(); path++) { Segment v; Segment start; start = v = (Segment) solids.elementAt(path); do { if (l[i].hasIntersections(v)) return true; v = v.next; } while (v != start); } for (int path = 0; path < holes.size(); path++) { Segment v; Segment start; start = v = (Segment) holes.elementAt(path); do { if (l[i].hasIntersections(v)) return true; v = v.next; } while (v != start); } } // Non-intersecting, Is any point inside? if (contains(x + w * 0.5, y + h * 0.5)) return true; // What if the rectangle encloses the whole shape? Point2D p = ((Segment) solids.elementAt(0)).getMidPoint(); if ((new Rectangle2D.Double(x, y, w, h)).contains(p)) return true; return false; } /** * Determines if the Rectangle2D specified by r intersects any * part of this Area. * @param r the rectangle to test intersection with (<code>null</code> * not permitted). * @return <code>true</code> if the rectangle intersects the area, * <code>false</code> otherwise. * @throws NullPointerException if <code>r</code> is <code>null</code>. */ public boolean intersects(Rectangle2D r) { return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight()); } /** * Returns a PathIterator object defining the contour of this Area, * transformed by at. * * @param at the transform. * @return A path iterator. */ public PathIterator getPathIterator(AffineTransform at) { return (new AreaIterator(at)); } /** * Returns a flattened PathIterator object defining the contour of this * Area, transformed by at and with a defined flatness. * * @param at the transform. * @param flatness the flatness. * @return A path iterator. */ public PathIterator getPathIterator(AffineTransform at, double flatness) { return new FlatteningPathIterator(getPathIterator(at), flatness); } //--------------------------------------------------------------------- // Non-public methods and classes /** * Private pathiterator object. */ private class AreaIterator implements PathIterator { private Vector segments; private int index; private AffineTransform at; // Simple compound type for segments class IteratorSegment { int type; double[] coords; IteratorSegment() { coords = new double[6]; } } /** * The contructor here does most of the work, * creates a vector of IteratorSegments, which can * readily be returned */ public AreaIterator(AffineTransform at) { this.at = at; index = 0; segments = new Vector(); Vector allpaths = new Vector(); allpaths.addAll(solids); allpaths.addAll(holes);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -