package org.geogebra.common.util.clipper; import java.util.ArrayList; import java.util.Collections; import org.geogebra.common.util.clipper.Point.DoublePoint; /** * A pure convenience class to avoid writing List<IntPoint> everywhere. * * @author Tobias Mahlmann * */ // public class Path extends ArrayList<LongPoint> { public class Path extends ArrayList<DoublePoint> { private static OutPt excludeOp(OutPt op) { final OutPt result = op.prev; result.next = op.next; op.next.prev = result; result.idx = 0; return result; } /** * */ private static final long serialVersionUID = -7120161578077546673L; public Path() { super(); } public Path(int cnt) { super(cnt); } public double area() { final int cnt = size(); if (cnt < 3) { return 0; } double a = 0; for (int i = 0, j = cnt - 1; i < cnt; ++i) { // a += ((double) get( j ).getX() + get( i ).getX()) * ((double) // get( j ).getY() - get( i ).getY()); a += (get(j).getX() + get(i).getX()) * (get(j).getY() - get(i).getY()); j = i; } return -a * 0.5; } public Path cleanPolygon() { return cleanPolygon(1.415); } public Path cleanPolygon(double distance) { // distance = proximity in units/pixels below which vertices will be // stripped. // Default ~= sqrt(2) so when adjacent vertices or semi-adjacent // vertices have // both x & y coords within 1 unit, then the second vertex will be // stripped. int cnt = size(); if (cnt == 0) { return new Path(); } OutPt[] outPts = new OutPt[cnt]; for (int i = 0; i < cnt; ++i) { outPts[i] = new OutPt(); } for (int i = 0; i < cnt; ++i) { outPts[i].pt = get(i); outPts[i].next = outPts[(i + 1) % cnt]; outPts[i].next.prev = outPts[i]; outPts[i].idx = 0; } final double distSqrd = distance * distance; OutPt op = outPts[0]; while (op.idx == 0 && op.next != op.prev) { if (Point.arePointsClose(op.pt, op.prev.pt, distSqrd)) { op = excludeOp(op); cnt--; } else if (Point.arePointsClose(op.prev.pt, op.next.pt, distSqrd)) { excludeOp(op.next); op = excludeOp(op); cnt -= 2; } else if (Point.slopesNearCollinear(op.prev.pt, op.pt, op.next.pt, distSqrd)) { op = excludeOp(op); cnt--; } else { op.idx = 1; op = op.next; } } if (cnt < 3) { cnt = 0; } final Path result = new Path(cnt); for (int i = 0; i < cnt; ++i) { result.add(op.pt); op = op.next; } outPts = null; return result; } /** * modified to be compatible with double */ public int isPointInPolygon(DoublePoint pt) { // returns 0 if false, +1 if true, -1 if pt ON polygon boundary // See "The Point in Polygon Problem for Arbitrary Polygons" by Hormann // & Agathos // http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.88.5498&rep=rep1&type=pdf int result = 0; final int cnt = size(); if (cnt < 3) { return 0; } DoublePoint ip = get(0); for (int i = 1; i <= cnt; ++i) { final DoublePoint ipNext = i == cnt ? get(0) : get(i); if (ipNext.getY() == pt.getY()) { if (ipNext.getX() == pt.getX() || ip.getY() == pt.getY() && ipNext.getX() > pt.getX() == ip.getX() < pt.getX()) { return -1; } } if (ip.getY() < pt.getY() != ipNext.getY() < pt.getY()) { if (ip.getX() >= pt.getX()) { if (ipNext.getX() > pt.getX()) { result = 1 - result; } else { final double d = (ip.getX() - pt.getX()) * (ipNext.getY() - pt.getY()) - (ipNext.getX() - pt.getX()) * (ip.getY() - pt.getY()); if (d == 0) { return -1; } else if (d > 0 == ipNext.getY() > ip.getY()) { result = 1 - result; } } } else { if (ipNext.getX() > pt.getX()) { final double d = (ip.getX() - pt.getX()) * (ipNext.getY() - pt.getY()) - (ipNext.getX() - pt.getX()) * (ip.getY() - pt.getY()); if (d == 0) { return -1; } else if (d > 0 == ipNext.getY() > ip.getY()) { result = 1 - result; } } } } ip = ipNext; } return result; } public boolean orientation() { return area() >= 0; } public void reverse() { Collections.reverse(this); } /** * modified to be compatible with double */ public Path translatePath(DoublePoint delta) { final Path outPath = new Path(size()); for (int i = 0; i < size(); i++) { outPath.add(new DoublePoint(get(i).getX() + delta.getX(), get(i).getY() + delta.getY())); } return outPath; } }