/* * Codeable Objects by Jennifer Jacobs is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License. * Based on a work at hero-worship.com/portfolio/codeable-objects. * * This file is part of the Codeable Objects Framework. * * Codeable Objects is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Codeable Objects is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Codeable Objects. If not, see <http://www.gnu.org/licenses/>. */ package com.pixelmaid.dresscode.drawing.math; import com.pixelmaid.dresscode.drawing.primitive2d.Drawable; import com.pixelmaid.dresscode.drawing.primitive2d.Ellipse; import com.pixelmaid.dresscode.drawing.primitive2d.Line; import com.pixelmaid.dresscode.drawing.primitive2d.Polygon; import com.pixelmaid.dresscode.drawing.datatype.DCHalfEdge; import com.pixelmaid.dresscode.drawing.datatype.DoublyConnectedEdgeList; import com.pixelmaid.dresscode.drawing.datatype.Point; import com.seisw.util.geom.Point2D; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.Set; import java.util.Vector; public class Geom { public static final double BIG = 1e+15; public static final double SMALL = 1e-15; public static Point polarToCart(double r, double theta) { double x = Math.cos(theta * (Math.PI / 180.0)) * r; double y = Math.sin(theta * (Math.PI / 180.0)) * r; /*double[] xY = new double[2]; xY[0]=x; xY[1] = y; return xY;*/ return new Point(x, y); } public static double round(double unrounded, int precision, int roundingMode) { BigDecimal bd = new BigDecimal(unrounded); BigDecimal rounded = bd.setScale(precision, roundingMode); return rounded.doubleValue(); } public static double[] cartToPolar(double x, double y) { double r = 0.0; double theta = 0.0; r = Math.sqrt((x * x) + (y * y)); int type = 0; if (x > 0 && y >= 0) type = 1; if (x > 0 && y < 0) type = 2; if (x < 0) type = 3; if (x == 0 && y > 0) type = 4; if (x == 0 && y < 0) type = 5; if (x == 0 && y == 0) type = 6; //Find theta switch (type) { case (1): theta = Math.atan(y / x) * (180.0 / Math.PI); break; case (2): theta = (Math.atan(y / x) + 2 * Math.PI) * (180.0 / Math.PI); break; case (3): theta = (Math.atan(y / x) + Math.PI) * (180.0 / Math.PI); break; case (4): theta = (Math.PI / 2.0) * (180.0 / Math.PI); break; case (5): theta = ((3 * Math.PI) / 2.0) * (180.0 / Math.PI); break; case (6): theta = 0.0; break; default: theta = 0.0; break; } double[] rTheta = new double[2]; rTheta[0] = r; rTheta[1] = theta; return rTheta; } public static Point getMidpoint(Point p1, Point p2){ double x = (p1.getX()+p2.getX())/2; double y = (p1.getY()+p2.getY())/2; return new Point(x,y); } public static Point getAveragePoint(ArrayList<Point> p){ Point average = p.get(0); for(int i=1;i<p.size();i++){ average = average.add(p.get(i)); } average.setX(average.getX()/((double)(p.size()))); average.setY(average.getY()/((double)(p.size()))); return average; } public static boolean isEven(double num){ if (num %2==0){ return true; } else{ return false; } } public static boolean isOdd(double num){ if (num %2==0){ return false; } else{ return true; } } public static double getHighestAngle(DCHalfEdge edge, Point focus){ Point target; if(edge.start.compareTo(focus)==0){ target = edge.end; } else{ target = edge.start; } //reset edge with higher point as start; //return angle of edge from start to end return Geom.cartToPolar(target.getX()-focus.getX(), target.getY()-focus.getY())[1]; } public static DCHalfEdge getIntersectedEdge(Point focus, Point direction, DCHalfEdge parentEdge, DoublyConnectedEdgeList border) { DCHalfEdge edge = new DCHalfEdge(focus, direction); double dx = direction.getX() - focus.getX(); //direction x double dy = direction.getY() - focus.getY(); //direction y DCHalfEdge borderEdge = null; //selected border edge for intersection; double[] thetas = border.getBorderPoints(focus); //degrees of all points along the border double edgeTheta = Geom.cartToPolar(dx, dy)[1];//degree of edge double selectedTheta = -1; //myParent.println("\n\n\n edgeTheta="+edgeTheta+"\n\n\n"); for (int i = 0; i < thetas.length; i++) { //iterate through all edges and look for correct point of intersection //myParent.println("edge "+i+" theta="+thetas[i]+" x="+border.edges.get(i).start.getX()+" y="+border.edges.get(i).start.getY()); int after = i + 1; if (i == thetas.length - 1) { after = 0; } if (thetas[after] > thetas[i]) {//special case where angle of previous edge is greater than angle of current edge //myParent.println("call special case"); if (edgeTheta >= thetas[after] || edgeTheta < thetas[i]) {//detects quadrant of intersection for special case if (edgeTheta > thetas[i]) { borderEdge = border.edges.get(i - 1); selectedTheta = thetas[i]; } else { borderEdge = border.edges.get(i); selectedTheta = thetas[after]; } /*myParent.println("\n\n\n edgeTheta="+edgeTheta); myParent.println("theta i="+thetas[i]); myParent.println("theta after="+thetas[after]); myParent.println("selected theta="+selectedTheta); myParent.println("start x,y,="+focus.getX()+","+focus.getY()); myParent.println("end x,y,="+direction.getX()+","+direction.getY()); myParent.println("intersection x,y,="+intersection.getX()+","+intersection.getY()+"\n\n\n");*/ parentEdge.infiniteEdge = 1; break; } } else {//otherwise all thetas should be greater than the preceeding theta if (edgeTheta <= thetas[i] && edgeTheta > thetas[after]) {//detects quadrant of intersection borderEdge = border.edges.get(i); selectedTheta = thetas[i]; break; } } } return borderEdge; } public static Point getIntersectedEdgePoint(Point focus, Point direction, DCHalfEdge parentEdge, DCHalfEdge borderEdge) { Point intersection = null; DCHalfEdge edge = new DCHalfEdge(focus, direction); double dx = direction.getX() - focus.getX(); //direction x double dy = direction.getY() - focus.getY(); //direction y edge.intersectedEdge = borderEdge; //System.out.println("intersected edge="+border.edges.indexOf(borderEdge)); intersection = findIntersectionPoint(edge, borderEdge); //myParent.println("\n\n\n selectedTheta="+selectedTheta+"\n\n\n"); return intersection; } //ray determined point in polygon (returns bool) public static DoublyConnectedEdgeList linesToDCEdgeList(ArrayList<Line> l){ DoublyConnectedEdgeList p = new DoublyConnectedEdgeList(); for(int i=0;i<l.size();i++){ p.addHalfEdge(new DCHalfEdge(l.get(i).getStart(),l.get(i).getEnd())); } return p; } /*public static LineCollection dCEdgeListToLines(DoublyConnectedEdgeList p){ LineCollection lc = new LineCollection(); ArrayList<DCHalfEdge> edges = p.edges; for(int i=0;i<edges.size();i++){ lc.addLine(edges.get(i).start.copy(),edges.get(i).end.copy()); } return lc; }*/ /* public static boolean rayPointInPolygon(Point q, LineCollection lc){ DoublyConnectedEdgeList p = linesToDCEdgeList(lc.getAllLines()); char type = rayTypePointInPolygon(q, p); if(type !='o'){ return true; } else{ return false; } } */ //ray determined point in polygon (returns bool) public static boolean rayPointInPolygon(Point q, DoublyConnectedEdgeList p){ char type = rayTypePointInPolygon(q, p); if(type !='o'){ return true; } else{ return false; } } //ray determined point in polygon (returns type) public static char rayTypePointInPolygon(Point q, DoublyConnectedEdgeList p){ double width = 500000; int i,i1; //point index; i1 = 1-1 mod n; int n = p.edges.size(); int d=2; //dimension index int Rcross = 0; //number of right edge/ray crossings int Lcross = 0; //number of left edge/ray crossings boolean Rstrad, Lstrad; //flags that indicate the edge straddles the x axis //ArrayList<CompPoint> p = new ArrayList<CompPoint>(); // for each edge in poly, see if crosses rays /*for(i=0;i<n;i++){ DCHalfEdge edge = p.edge.get(i); CompPoint newPoint = new CompPoint(edge.start.getX()-q.getX(),edge.start.getY()-q.getY()); }*/ //check if p is a vertex of the polygon for(i=0; i<n;i++){ i1 = (i+n-1)%n; DCHalfEdge edge = p.edges.get(i); if(q.compareTo(edge.start)==0||q.compareTo(edge.end)==0 ){ return 'v'; } } DCHalfEdge rEdge = new DCHalfEdge(q,new Point(width,q.getY())); DCHalfEdge lEdge = new DCHalfEdge(q,new Point(0,q.getY())); Rcross = Geom.edgeIntersectsPolygon(rEdge, p).size(); Lcross = Geom.edgeIntersectsPolygon(lEdge, p).size(); //System.out.println("Rcross = "+Rcross+",Lcross="+Lcross); /*i1=(i+n-1)%n; DCHalfEdge edge1 = p.edges.get(i1); Rstrad = (edge.start.getY()>q.getY())!=(edge1.start.getY()>q.getY()); Lstrad = (edge.start.getY()<q.getY())!=(edge1.start.getY()<q.getY()); if(Rstrad|| Lstrad){ double x = (edge.start.getX()*edge1.start.getY()-edge1.start.getX()*edge.start.getY())/(edge1.start.getY()-edge.start.getY()); if(Rstrad && x> q.getX()){ Rcross++; } if(Lstrad && x> q.getX()){ Lcross++; } } } */ // q is on an edge if L/Rcross counts are not the same parity if((Rcross % 2) != (Lcross %2)){ return 'e'; } if((Rcross %2)==1){ return 'i'; } else{ return 'o'; } } public static boolean polygonIntersect(Polygon a, Polygon b){ DoublyConnectedEdgeList dA = polyToEdgeList(a); b.setPointsAbsolute(); ArrayList<Point> p = b.getPoints(); boolean intersect = false; for(int i=0;i<p.size();i++){ intersect = rayPointInPolygon(p.get(i), dA); System.out.println("Intersect ="+intersect); } return intersect; } private static DoublyConnectedEdgeList polyToEdgeList(Polygon p){ p.setPointsAbsolute(); ArrayList<Point> pPoints = p.getPoints(); //temp polygon to be stored in master polygon DoublyConnectedEdgeList p_Poly = new DoublyConnectedEdgeList(); //add all polygon points to temp polygon for(int i=0;i<pPoints.size();i++) { int next; if(i<pPoints.size()-2){ next = i+1; } else{ next = 0; } Point start = pPoints.get(i); Point end = pPoints.get(next); DCHalfEdge e = new DCHalfEdge(start,end); p_Poly.addHalfEdge(e); } //add all polygon holes to temp polygon return p_Poly; } //uses winding number algorithim, works for irregular convex polygons public static boolean pointInComPolygon(Point testPoint, DoublyConnectedEdgeList border) { int angleCount = 0; for (int i = border.edges.size() - 1; i >= 0; i--) { DCHalfEdge edge = border.edges.get(i); if (edge.start.getY() >= edge.end.getY()) { if (leftOn(edge.start, edge.end, testPoint)) { angleCount++; } } else { if (leftOn(edge.end, edge.start, testPoint)) { angleCount--; } } } if (angleCount != 0) { return true; } else { return false; } } //determines if a given point is in a polygon defined by a doubly connected edge list //only works for regular polygons public static boolean pointInPolygon(Point testPoint, DoublyConnectedEdgeList border) { for (int i = border.edges.size() - 1; i >= 0; i--) { DCHalfEdge edge = border.edges.get(i); if (leftOn(edge.start, edge.end, testPoint)) { return false; } } return true; } //determines if a segment intersects a polygon defined by a doubly connected edge list and returns edges of intersection if they exist public static ArrayList<Line> edgeIntersectsPolygon(Line edge, ArrayList<Line> border) { ArrayList<Line> intersectedEdges = new ArrayList<Line>(); for (int i = border.size() - 1; i >= 0; i--) { Line borderEdge = border.get(i); if (lineIntersect(borderEdge.getStart(), borderEdge.getEnd(), edge.getStart(), edge.getEnd())) { intersectedEdges.add(borderEdge); } } return intersectedEdges; } //determines if a segment intersects a polygon defined by a doubly connected edge list and returns edges of intersection if they exist public static ArrayList<DCHalfEdge> edgeIntersectsPolygon(DCHalfEdge edge, DoublyConnectedEdgeList border) { ArrayList<DCHalfEdge> intersectedEdges = new ArrayList<DCHalfEdge>(); for (int i = border.edges.size() - 1; i >= 0; i--) { DCHalfEdge borderEdge = border.edges.get(i); if (lineIntersect(borderEdge.start, borderEdge.end, edge.start, edge.end)) { intersectedEdges.add(borderEdge); } } return intersectedEdges; } //detects if a line intersects an edge but does not return the point(s) of intersection public static boolean lineIntersect(Point edgeStart, Point edgeEnd, Point testPointStart, Point testPointEnd) { if (intersectProp(edgeStart, edgeEnd, testPointStart, testPointEnd)) return true; else if (between(edgeStart, edgeEnd, testPointStart) || between(edgeStart, edgeEnd, testPointEnd) || between(testPointStart, testPointEnd, edgeStart) || between(testPointStart, testPointEnd, edgeEnd)) return true; else { return false; } } public static boolean between(Point edgeStart, Point edgeEnd, Point testPoint) { if (!collinear(edgeStart, edgeEnd, testPoint)) { return false; } if (edgeStart.getX() != edgeEnd.getX()) { return (edgeStart.getX() <= testPoint.getX()) && (testPoint.getX() <= edgeEnd.getX()) || (edgeStart.getX() >= testPoint.getX()) && (testPoint.getX() >= edgeEnd.getX()); } else { return (edgeStart.getY() <= testPoint.getY()) && (testPoint.getY() <= edgeEnd.getY()) || (edgeStart.getY() >= testPoint.getY()) && (testPoint.getY() >= edgeEnd.getY()); } } public static boolean intersectProp(Point edgeStart, Point edgeEnd, Point testPointStart, Point testPointEnd) { if (collinear(edgeStart, edgeEnd, testPointStart) || collinear(edgeStart, edgeEnd, testPointEnd) || collinear(testPointStart, testPointEnd, edgeStart) || collinear(testPointStart, testPointEnd, edgeEnd)) { return false; } return Xor(left(edgeStart, edgeEnd, testPointStart), left(edgeStart, edgeEnd, testPointEnd)) && Xor(left(testPointStart, testPointEnd, edgeStart), left(testPointStart, testPointEnd, edgeEnd)); } public static boolean Xor(boolean x, boolean y) { return !x ^ !y; } public static boolean leftOn(Point edgeStart, Point edgeEnd, Point testPoint) { return area2(edgeStart, edgeEnd, testPoint) >= 0; } public static boolean left(Point edgeStart, Point edgeEnd, Point testPoint) { return area2(edgeStart, edgeEnd, testPoint) > 0; } public static boolean collinear(Point edgeStart, Point edgeEnd, Point testPoint) { return area2(edgeStart, edgeEnd, testPoint) == 0; } public static double area2(Point a, Point b, Point c) { double area = ((b.getX() - a.getX()) * (c.getY() - a.getY())) - ((c.getX() - a.getX()) * (b.getY() - a.getY())); return area; } //finds the point of intersection between two edges that are known to intersect public static Point findIntersectionPoint(DCHalfEdge edge, DCHalfEdge borderEdge) { double mx = 0; double my = 0; //System.out.println("slope of edge="+edge.getSlope()); if (Double.isInfinite(borderEdge.getSlope())) {//check to see if slope is undefined (line is vertical) mx = borderEdge.start.getX(); my = (mx * edge.getSlope()) + edge.getYIntercept(); } else if (Double.isNaN(borderEdge.getSlope())) {//check to see if slope is NaN (line is horizontal) my = borderEdge.start.getY(); if(Double.isInfinite(edge.getSlope())){ // System.out.println("slope in in horz line"); mx = edge.start.getX(); } else{ mx = (my - edge.getYIntercept()) / edge.getSlope(); } } else { if(Double.isInfinite(edge.getSlope())){ //System.out.println("slope inifinite in normal line"); mx = edge.start.getX(); my = (mx * borderEdge.getSlope()) + borderEdge.getYIntercept(); } else{ mx = (edge.getYIntercept() - borderEdge.getYIntercept()) / (borderEdge.getSlope() - edge.getSlope());//line has a slope my = (mx * edge.getSlope()) + edge.getYIntercept(); } } Point intersection = new Point(mx, my); return intersection; } //finds the point of intersection between a line and an edge public static Point findIntersectionPoint(DCHalfEdge borderEdge, Point point, double m) { double mx = 0; double my = 0; double b = m*point.getX()-point.getY(); if (Double.isInfinite(borderEdge.getSlope())) {//check to see if slope is undefined (line is vertical) mx = borderEdge.start.getX(); my = (mx * m) + b; } else if (Double.isNaN(borderEdge.getSlope())) {//check to see if slope is NaN (line is horizontal) my = borderEdge.start.getY(); mx = (my - b) / m; } else { mx = (b - borderEdge.getYIntercept()) / (borderEdge.getSlope() - m);//line has a slope my = (mx * m) + b; } Point intersection = new Point(mx, my); return intersection; } /* public static boolean ellipseEdgeIntersect(Ellipse disc, DCHalfEdge edge) { Vec2d seg_a = new Vec2d(edge.start.getX(), edge.start.getY()); Vec2d seg_b = new Vec2d(edge.end.getX(), edge.end.getY()); Point closest = closestPoint(seg_a, seg_b, disc.getOrigin()); double dist = new DCHalfEdge(closest, disc.getOrigin()).getLength(); if (dist > disc.getWidth()) { return false; } else { return true; } } */ public static double clamp(double X, double Min, double Max) { if (X > Max) X = Max; else if (X < Min) X = Min; return X; } public static Point closestPoint(Vec2d seg_a, Vec2d seg_b, Point circleOrigin) { Vec2d seg_v = seg_b.sub(seg_a); Vec2d pt_v = new Vec2d(circleOrigin.getX(), circleOrigin.getY()).sub(seg_a); Vec2d seg_v_unit = seg_v.div(seg_v.Length()); double proj = pt_v.Dot(seg_v_unit); Point closest; if (proj <= 0) { closest = new Point(seg_a.x, seg_b.x); } else if (proj >= seg_v.Length()) { closest = new Point(seg_b.x, seg_b.x); } else { Vec2d proj_v = seg_v_unit.mul(proj); Vec2d newVec = proj_v.add(seg_a); closest = new Point(newVec.x, newVec.y); } return closest; } public static double distance(Point a, Point b) { double distance = Math.sqrt(Math.pow(a.getX() - b.getX(), 2) + Math.pow(a.getY() - b.getY(), 2)); return distance; } //find area of a polygon public static double SignedPolygonArea(Polygon poly) { int i,j; double area = 0; ArrayList<Point> points = poly.getPoints(); int N = points.size(); for (i=0;i<N;i++) { j = (i + 1) % N; area += points.get(i).getX() * points.get(j).getY(); area -= points.get(i).getY() * points.get(j).getX(); } area /= 2.0; //return(area); return(area < 0 ? -area : area); //for unsigned } public static Point findCentroid(Polygon polygon) { double cx=0,cy=0; double A= SignedPolygonArea(polygon); ArrayList<Point> verticies = polygon.getPoints(); if (verticies.size()>0){ //System.out.println("signedArea="+A); Point res; int i,j; int N = verticies.size(); double factor=0; verticies.add(verticies.get(0).copy()); //System.out.println("added"); for (i=0;i<verticies.size();i++) { j = i+1; if(j>=verticies.size()){ j=0; } factor=(verticies.get(i).getX()*verticies.get(j).getY()-verticies.get(j).getX()*verticies.get(i).getY()); cx+=(verticies.get(i).getX()+verticies.get(j).getX())*factor; cy+=(verticies.get(i).getY()+verticies.get(j).getY())*factor; } A*=6.0f; factor=1/A; cx*=factor; cy*=factor; res = new Point(cx,cy); //System.out.println("centroid="+cx+","+cy); verticies.remove(verticies.size()-1); return res; } else{ return null; } } //remove duplicate vertices from a polygon (accepts a dcedge list) public static ArrayList<Point> removeDuplicateVerts(DoublyConnectedEdgeList poly){ ArrayList<Point> verticies = new ArrayList<Point>(0); for(int i=0;i<poly.edges.size();i++) { verticies.add(poly.edges.get(i).start); verticies.add(poly.edges.get(i).end); } return removeDuplicateVerts(verticies); } //remove duplicate vertices from a polygon (accepts a vector of points) public static ArrayList<Point> removeDuplicateVerts(ArrayList<Point> verticies){ //System.out.println("org#="+verticies.size()); Collection<Point> noDup = new LinkedHashSet<Point>(verticies); ArrayList<Point> newVerticies = new ArrayList<Point>(); newVerticies.addAll(noDup); //System.out.println("new#="+noDup.size()); return newVerticies; /*Vector <CompPoint> newVerts = new ArrayList<CompPoint>(0); for(int i=0;i<verticies.size();i++){ for(int j=0;j<verticies.size();j++){ if(j!=i){ System.out.println("found dup"); } } }*/ } }