package cc.sketchchair.triangulate; import java.util.*; /** * class to perform Delaunay triangulation. * @author gregsaul * */ public class Delaunay { class Edge { Triangle left; Triangle right; Vertex start; Vertex end; boolean constrained; boolean flipped; int index; Edge(Vertex v0, Vertex v1, boolean c) { start = v0; end = v1; constrained = c; start.edges.add(this); end.edges.add(this); } public boolean contains(Vertex v) { return (v == start || v == end); } public void delete() { start.remove(this); end.remove(this); } public Vertex get_common_vertex(Edge edge) { if (edge.contains(start)) return start; if (edge.contains(end)) return end; return null; } public void remove(Triangle triangle) { if (left == triangle) left = null; if (right == triangle) right = null; } public void set_left(Triangle triangle) { // if (left != null) LOGGE("Error in Edge.set_left"); left = triangle; } public void set_right(Triangle triangle) { // if (right != null) System.out.println("Error in Edge.set_right"); right = triangle; } } public class Triangle { Edge[] edges = new Edge[3]; Triangle(Edge e0, Edge e1, Edge e2) { edges[0] = e0; edges[1] = e1; edges[2] = e2; if (e1.contains(e0.end)) e0.set_left(this); else e0.set_right(this); if (e2.contains(e1.end)) e1.set_left(this); else e1.set_right(this); if (e0.contains(e2.end)) e2.set_left(this); else e2.set_right(this); } public void delete() { edges[0].remove(this); edges[1].remove(this); edges[2].remove(this); } public Edge get_the_other_edge(Vertex v) { if (!edges[0].contains(v)) return edges[0]; if (!edges[1].contains(v)) return edges[1]; if (!edges[2].contains(v)) return edges[2]; return null; } public Vertex get_the_other_vertex(Edge edge) { if (edge == edges[0]) return edges[1].get_common_vertex(edges[2]); if (edge == edges[1]) return edges[2].get_common_vertex(edges[0]); if (edge == edges[2]) return edges[0].get_common_vertex(edges[1]); return null; } public Vertex get_vertex(int i) { if (i == 0) return edges[1].get_common_vertex(edges[2]); if (i == 1) return edges[2].get_common_vertex(edges[0]); if (i == 2) return edges[0].get_common_vertex(edges[1]); return null; } } public class Vertex extends Vector2D { ArrayList edges = new ArrayList(); int index; Vertex(Vector2D v, int index) { x = v.x; y = v.y; this.index = index; } public int getIndex() { return this.index; } public void remove(Edge edge) { edges.remove(edge); } public Vector2D Vector2D() { return new Vector2D(x, y); } } // return {delaunay_edges, delaunay_triangles} public static ArrayList triangulate(ArrayList loop) { return (new Delaunay()).triangulate_main(loop); } public ArrayList triangles; public ArrayList vertexs; public ArrayList edges; public Vertex baseVertex; public Vertex prevVertex; public Edge prevEdge; Vertex latest_vertex; public Edge add(Edge edge) { edges.add(edge); return edge; } public Triangle add(Triangle triangle) { triangles.add(triangle); return triangle; } public void add_vertex(Vertex v) { Edge edge0 = add(new Edge(v, baseVertex, false)); Edge edge1 = add(new Edge(prevVertex, v, true)); Triangle triangle = add(new Triangle(prevEdge, edge1, edge0)); latest_vertex = v; clearFlipped(); recursiveFlip(prevEdge, null); clearFlipped(); for (int i = 0; i < edges.size(); i++) { Edge edge = (Edge) edges.get(i); //if (double_flipped(edge)){ recursiveFlip(edge, null); clearFlipped(); //} } prevVertex = v; prevEdge = edge0; } public void clearFlipped() { for (int i = 0; i < edges.size(); i++) ((Edge) edges.get(i)).flipped = false; } public void delete(Edge edge) { edges.remove(edge); edge.delete(); } public void delete(Triangle triangle) { triangles.remove(triangle); triangle.delete(); } public boolean double_flipped(Edge edge) { if (edge.flipped) return false; if (edge.constrained) return false; Triangle triangle0 = edge.left; Triangle triangle1 = edge.right; if (triangle0 == null || triangle1 == null) return false; Vertex v0 = triangle0.get_the_other_vertex(edge); Vertex v1 = triangle1.get_the_other_vertex(edge); return (same_side(edge.start, edge.end, v0, v1));// &&same_side(v0, v1, edge.start, edge.end)); } public double get_angle(Vertex v0, Vertex v1, Vertex v2) { Vector2D vec0 = new Vector2D(v1, v0); Vector2D vec1 = new Vector2D(v1, v2); return vec0.get_relative_angle(vec1); } public double min(double a, double b, double c, double d) { return Math.min(Math.min(a, b), Math.min(c, d)); } public void recursiveFlip(Edge edge, Edge fixed) { if (edge.constrained) return; Triangle triangle0 = edge.left; Triangle triangle1 = edge.right; if (triangle0 == null || triangle1 == null) return; Vertex v0 = triangle0.get_the_other_vertex(edge); Vertex v1 = triangle1.get_the_other_vertex(edge); if (should_flip(edge, v0, v1)) { Edge edge0s = triangle0.get_the_other_edge(edge.end); Edge edge0e = triangle0.get_the_other_edge(edge.start); Edge edge1s = triangle1.get_the_other_edge(edge.end); Edge edge1e = triangle1.get_the_other_edge(edge.start); delete(triangle0); delete(triangle1); delete(edge); Edge flippeEdge = add(new Edge(v0, v1, false)); flippeEdge.flipped = true; triangle0 = add(new Triangle(flippeEdge, edge1e, edge0e)); triangle1 = add(new Triangle(flippeEdge, edge0s, edge1s)); if (edge0s != fixed) recursiveFlip(edge0s, edge); if (edge0e != fixed) recursiveFlip(edge0e, edge); if (edge1s != fixed) recursiveFlip(edge1s, edge); if (edge1e != fixed) recursiveFlip(edge1e, edge); } } public boolean same_side(Vertex start, Vertex end, Vertex v0, Vertex v1) { Vector2D axis = new Vector2D(start, end); Vector2D vec0 = new Vector2D(start, v0); Vector2D vec1 = new Vector2D(start, v1); return (axis.cross_product(vec0) * axis.cross_product(vec1) > 0); } public boolean should_flip(Edge edge, Vertex v0, Vertex v1) { if (edge.flipped) return false; if (v0.x == v1.x && v0.y == v1.y) return false; if (same_side(edge.start, edge.end, v0, v1)) { return true; } if (same_side(v0, v1, edge.start, edge.end)) return false; double originalMinimumAngle = min(get_angle(edge.start, edge.end, v0), get_angle(edge.end, edge.start, v0), get_angle(edge.start, edge.end, v1), get_angle(edge.end, edge.start, v1)); double flippedMinimumAngle = min(get_angle(edge.start, v0, v1), get_angle(edge.start, v1, v0), get_angle(edge.end, v0, v1), get_angle(edge.end, v1, v0)); return (flippedMinimumAngle > originalMinimumAngle); } public ArrayList triangulate_main(ArrayList loop) { triangles = new ArrayList(); edges = new ArrayList(); vertexs = new ArrayList(); Vertex v0 = new Vertex((Vector2D) loop.get(0), 0); Vertex v1 = new Vertex((Vector2D) loop.get(1), 1); Vertex v2 = new Vertex((Vector2D) loop.get(2), 2); Edge e0 = add(new Edge(v0, v1, true)); Edge e1 = add(new Edge(v1, v2, true)); Edge e2 = add(new Edge(v2, v0, false)); Triangle t0 = add(new Triangle(e0, e1, e2)); vertexs.add(v0); vertexs.add(v1); vertexs.add(v2); baseVertex = v0; prevVertex = v2; prevEdge = e2; for (int i = 3; i < loop.size(); i++) { Vertex v = new Vertex((Vector2D) loop.get(i), i); add_vertex(v); vertexs.add(v); } // ArrayList delaunay_edges = new ArrayList(); // for(int i=0; i<edges.size(); i++){ // Edge edge = (Edge) edges.get(i); // edge.index = i; // int[] delaunay_edge = {edge.start.index, edge.end.index}; // delaunay_edges.add(delaunay_edge); // } // // ArrayList delaunay_triangles = new ArrayList(); // for(int i=0; i<triangles.size(); i++){ // Triangle triangle = (Triangle) triangles.get(i); // int[] delaunay_triangle = {triangle.edges[0].index, triangle.edges[1].index, triangle.edges[2].index}; // delaunay_triangles.add(delaunay_triangle); // } // // ArrayList[] result = {delaunay_edges, delaunay_triangles}; // return result; ArrayList delaunay_triangles = new ArrayList(); for (int i = 0; i < triangles.size(); i++) { Triangle triangle = (Triangle) triangles.get(i); int[] delaunay_triangle = { triangle.edges[0].get_common_vertex(triangle.edges[1]).index, triangle.edges[1].get_common_vertex(triangle.edges[2]).index, triangle.edges[2].get_common_vertex(triangle.edges[0]).index }; //int[] delaunay_triangle = {triangle.edges[0].index, triangle.edges[1].index, triangle.edges[2].index}; delaunay_triangles.add(delaunay_triangle); } return delaunay_triangles; } }