/* * Copyright (C) 2014 Alfons Wirtz * website www.freerouting.net * * This program 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. * * This program 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 at <http://www.gnu.org/licenses/> * for more details. * * PlanarDelaunayTriangulation.java * * Created on 8. Januar 2005, 10:12 */ package datastructures; import java.util.Collection; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.TreeSet; import geometry.planar.Point; import geometry.planar.IntPoint; import geometry.planar.Side; import geometry.planar.Limits; /** * Creates a Delaunay triangulation in the plane for the input objects. * The objects in the input list must implement the interface PlanarDelaunayTriangulation.Storable, * which consists of the the method get_triangulation_corners(). * The result can be read by the funktion get_edge_lines(). * The algorithm is from Chapter 9.3. of the book Computational Geometry, Algorithms and Applications * from M. de Berg, M. van Kreveld, M Overmars and O Schwarzkopf. * * @author Alfons Wirtz */ public class PlanarDelaunayTriangulation { /** Creates a new instance of PlanarDelaunayTriangulation from p_object_list. */ public PlanarDelaunayTriangulation(Collection<PlanarDelaunayTriangulation.Storable> p_object_list) { List<Corner> corner_list = new LinkedList<Corner>(); for (PlanarDelaunayTriangulation.Storable curr_object : p_object_list) { Point[] curr_corners = curr_object.get_triangulation_corners(); for (Point curr_corner : curr_corners) { corner_list.add(new Corner(curr_object, curr_corner)); } } // create a random permutation of the corners. // use a fixed seed to get reproducable result random_generator.setSeed(seed); Collections.shuffle(corner_list, random_generator); // create a big triangle contaning all corners in the list to start with. int bounding_coor = Limits.CRIT_INT; Corner[] bounding_corners = new Corner[3]; bounding_corners[0] = new Corner(null, new IntPoint(bounding_coor, 0)); bounding_corners[1] = new Corner(null, new IntPoint(0, bounding_coor)); bounding_corners[2] = new Corner(null, new IntPoint(-bounding_coor, -bounding_coor)); Edge [] edge_lines = new Edge[3]; for (int i = 0; i < 2; ++i) { edge_lines[i] = new Edge(bounding_corners[i], bounding_corners[i+1]); } edge_lines[2] = new Edge(bounding_corners[2], bounding_corners[0]); Triangle start_triangle = new Triangle(edge_lines, null); // Set the left triangle of the edge lines to start_triangle. // The right triangles remains null. for (Edge curr_edge : edge_lines) { curr_edge.set_left_triangle(start_triangle); } // Initialize the search graph. this.search_graph = new TriangleGraph(start_triangle); this.degenerate_edges = new LinkedList<Edge>(); // Insert the corners in the corner list into the search graph. for (Corner curr_corner : corner_list) { Triangle triangle_to_split = this.search_graph.position_locate(curr_corner); this.split(triangle_to_split, curr_corner); } } /** * Returns all edge lines of the result of the Delaunay Triangulation. */ public Collection<ResultEdge> get_edge_lines() { Collection<ResultEdge> result = new LinkedList<ResultEdge>(); for (Edge curr_edge : this.degenerate_edges) { result.add(new ResultEdge(curr_edge.start_corner.coor, curr_edge.start_corner.object, curr_edge.end_corner.coor, curr_edge.end_corner.object)); } if (this.search_graph.anchor != null) { Set<Edge> result_edges = new TreeSet<Edge>(); this.search_graph.anchor.get_leaf_edges(result_edges); for (Edge curr_edge : result_edges) { result.add(new ResultEdge(curr_edge.start_corner.coor, curr_edge.start_corner.object, curr_edge.end_corner.coor, curr_edge.end_corner.object)); } } return result; } /** * Splits p_triangle into 3 new triangles at p_corner, if p_corner lies in the interiour. * If p_corner lies on the border, p_triangle and the corresponding neighbour * are split into 2 new triangles each at p_corner. * If p_corner lies outside this triangle or on a corner, nothing is split. * In this case the function returns false. */ private boolean split(Triangle p_triangle, Corner p_corner) { // check, if p_corner is in the interiour of this triangle or // if p_corner is contained in an edge line. Edge containing_edge = null; for (int i = 0; i < 3; ++i) { Edge curr_edge = p_triangle.edge_lines[i]; Side curr_side; if (curr_edge.left_triangle == p_triangle) { curr_side = p_corner.side_of(curr_edge.start_corner, curr_edge.end_corner); } else { curr_side = p_corner.side_of(curr_edge.end_corner, curr_edge.start_corner); } if (curr_side == Side.ON_THE_RIGHT) { // p_corner is outside this triangle System.out.println("PlanarDelaunayTriangulation.split: p_corner is outside"); return false; } else if (curr_side == Side.COLLINEAR) { if (containing_edge != null) { // p_corner is equal to a corner of this triangle Corner common_corner = curr_edge.common_corner(containing_edge); if (common_corner == null) { System.out.println("PlanarDelaunayTriangulation.split: common corner expected"); return false; } if (p_corner.object == common_corner.object) { return false; } this.degenerate_edges.add(new Edge(p_corner, common_corner)); return true; } containing_edge = curr_edge; } } if (containing_edge == null) { // split p_triangle into 3 new triangles by adding edges from // the corners of p_triangle to p_corner. Triangle[] new_triangles = p_triangle.split_at_inner_point(p_corner); if (new_triangles == null) { return false; } for (Triangle curr_triangle : new_triangles) { this.search_graph.insert(curr_triangle, p_triangle); } for (int i = 0; i < 3; ++i) { legalize_edge(p_corner, p_triangle.edge_lines[i]); } } else { // split this triangle and the neighbour triangle into 4 new triangles by adding edges from // the corners of the triangles to p_corner. Triangle neighbour_to_split = containing_edge.other_neighbour(p_triangle); Triangle[] new_triangles = p_triangle.split_at_border_point(p_corner, neighbour_to_split); if (new_triangles == null) { return false; } // There are exact four new triangles with the first 2 dividing p_triangle and // the last 2 dividing neighbour_to_split. this.search_graph.insert(new_triangles[0], p_triangle); this.search_graph.insert(new_triangles[1], p_triangle); this.search_graph.insert(new_triangles[2], neighbour_to_split); this.search_graph.insert(new_triangles[3], neighbour_to_split); for (int i = 0; i < 3; ++i) { Edge curr_edge = p_triangle.edge_lines[i]; if (curr_edge != containing_edge) { legalize_edge(p_corner, curr_edge); } } for (int i = 0; i < 3; ++i) { Edge curr_edge = neighbour_to_split.edge_lines[i]; if (curr_edge != containing_edge) { legalize_edge(p_corner, curr_edge); } } } return true; } /** * Flips p_edge, if it is no legal edge of the Delaunay Triangulation. * p_corner is the last inserted corner of the triangulation * Return true, if the triangulation was changed. */ private boolean legalize_edge(Corner p_corner, Edge p_edge) { if (p_edge.is_legal()) { return false; } Triangle triangle_to_change; if (p_edge.left_triangle.opposite_corner(p_edge) == p_corner) { triangle_to_change = p_edge.right_triangle; } else if (p_edge.right_triangle.opposite_corner(p_edge) == p_corner) { triangle_to_change = p_edge.left_triangle; } else { System.out.println("PlanarDelaunayTriangulation.legalize_edge: edge lines inconsistant"); return false; } Edge flipped_edge = p_edge.flip(); // Update the search graph. this.search_graph.insert(flipped_edge.left_triangle, p_edge.left_triangle); this.search_graph.insert(flipped_edge.right_triangle, p_edge.left_triangle); this.search_graph.insert(flipped_edge.left_triangle, p_edge.right_triangle); this.search_graph.insert(flipped_edge.right_triangle, p_edge.right_triangle); // Call this function recursively for the other edge lines of triangle_to_change. for (int i = 0; i < 3; ++i) { Edge curr_edge = triangle_to_change.edge_lines[i]; if (curr_edge != p_edge) { legalize_edge(p_corner, curr_edge); } } return true; } /** * Checks the consistancy of the triangles in this triagulation. * Used for debugging purposes. */ public boolean validate() { boolean result = this.search_graph.anchor.validate(); if (result == true) { System.out.println("Delauny triangulation check passed ok"); } else { System.out.println("Delauny triangulation check has detected problems"); } return result; } /** * Creates a new unique edge id number. */ private int new_edge_id_no() { ++this.last_edge_id_no; return this.last_edge_id_no; } /** * The structure for seaching the triangle containing a given input corner. */ private final TriangleGraph search_graph; /** * This list contain the edges of the trinangulation, where the start corner and end corner are equal. */ private Collection<Edge> degenerate_edges; /** * id numbers are for implementing an ordering on the Edges so that they can be used in a set for example */ private int last_edge_id_no = 0; /** * Randum generatur to shuffle the input corners. * A fixed seed is used to make the results reproduceble. */ static private int seed = 99; static private java.util.Random random_generator = new java.util.Random(seed); /** * Interface with funktionality required for objects to be used * in a planar triangulation. */ public interface Storable { /** * Returns an array of corners, which can be used in a planar triangulation. */ geometry.planar.Point[] get_triangulation_corners(); } /** * Describes a line segment in the result of the Delaunay Triangulation. */ public static class ResultEdge { private ResultEdge(Point p_start_point, PlanarDelaunayTriangulation.Storable p_start_object, Point p_end_point, PlanarDelaunayTriangulation.Storable p_end_object) { start_point = p_start_point; start_object = p_start_object; end_point = p_end_point; end_object = p_end_object; } /** The start point of the line segment */ public final Point start_point; /** The object at the start point of the line segment */ public final PlanarDelaunayTriangulation.Storable start_object; /** The end point of the line segment */ public final Point end_point; /** The object at the end point of the line segment */ public final PlanarDelaunayTriangulation.Storable end_object; } /** * Contains a corner point together with the objects this corner belongs to. */ private static class Corner { public Corner(PlanarDelaunayTriangulation.Storable p_object, Point p_coor) { object = p_object; coor = p_coor; } /** * The function returns * Side.ON_THE_LEFT, if this corner is on the left of the line from p_1 to p_2; * Side.ON_THE_RIGHT, if this corner is on the right of the line from p_1 to p_2; * and Side.COLLINEAR, if this corner is collinear with p_1 and p_2. */ public Side side_of(Corner p_1, Corner p_2) { return this.coor.side_of(p_1.coor, p_2.coor); } public final PlanarDelaunayTriangulation.Storable object; public final Point coor; } /** * Describes an edge between two triangles in the triangulation. * The unique id_nos are for making edges comparable. */ private class Edge implements Comparable<Edge> { public Edge(Corner p_start_corner, Corner p_end_corner) { start_corner = p_start_corner; end_corner = p_end_corner; id_no = new_edge_id_no(); } public int compareTo(Edge p_other) { return (this.id_no - p_other.id_no); } public void set_left_triangle(Triangle p_triangle) { left_triangle = p_triangle; } public Triangle get_left_triangle() { return left_triangle; } public void set_right_triangle(Triangle p_triangle) { right_triangle = p_triangle; } public Triangle get_right_triangle() { return right_triangle; } /** * Returns the common corner of this edge and p_other, * or null, if no commen corner exists. */ public Corner common_corner(Edge p_other) { Corner result = null; if (p_other.start_corner.equals(this.start_corner) || p_other.end_corner.equals(this.start_corner)) { result = this.start_corner; } else if (p_other.start_corner.equals(this.end_corner) || p_other.end_corner.equals(this.end_corner)) { result = this.end_corner; } return result; } /** * Returns the neighbour triangle of this edge, which is different from p_triangle. * If p_triangle is not a neighbour of this edge, null is returned. */ public Triangle other_neighbour( Triangle p_triangle) { Triangle result; if (p_triangle == this.left_triangle) { result = this.right_triangle; } else if (p_triangle == this.right_triangle) { result = this.left_triangle; } else { System.out.println("Edge.other_neighbour: inconsistant neigbour triangle"); result = null; } return result; } /** * Returns true, if this is a legal edge of the Delaunay Triangulation. */ public boolean is_legal() { if (this.left_triangle == null || this.right_triangle == null) { return true; } Corner left_opposite_corner = this.left_triangle.opposite_corner(this); Corner right_opposite_corner = this.right_triangle.opposite_corner(this); boolean inside_circle = right_opposite_corner.coor.to_float().inside_circle( this.start_corner.coor.to_float(),left_opposite_corner.coor.to_float(), this.end_corner.coor.to_float()); return !inside_circle; } /** * Flips this edge line to the edge line between the opposite corners * of the adjacent triangles. * Returns the new constructed Edge. */ public Edge flip() { // Create the flipped edge, so that the start corner of this edge is on the left // and the end corner of this edge on the right. Edge flipped_edge = new Edge(this.right_triangle.opposite_corner(this), this.left_triangle.opposite_corner(this)); Triangle first_parent = this.left_triangle; // Calculate the index of this edge line in the left and right adjacent triangles. int left_index = -1; int right_index = -1; for (int i = 0; i < 3; ++i) { if (this.left_triangle.edge_lines[i] == this) { left_index = i; } if (this.right_triangle.edge_lines[i] == this) { right_index = i; } } if (left_index < 0 || right_index < 0) { System.out.println("Edge.flip: edge line inconsistant"); return null; } Edge left_prev_edge = this.left_triangle.edge_lines[(left_index + 2) % 3]; Edge left_next_edge = this.left_triangle.edge_lines[(left_index + 1) % 3]; Edge right_prev_edge = this.right_triangle.edge_lines[(right_index + 2) % 3]; Edge right_next_edge = this.right_triangle.edge_lines[(right_index + 1) % 3]; // Create the left triangle of the flipped edge. Edge [] curr_edge_lines = new Edge[3]; curr_edge_lines[0] = flipped_edge; curr_edge_lines[1] = left_prev_edge; curr_edge_lines[2] = right_next_edge; Triangle new_left_triangle = new Triangle(curr_edge_lines, first_parent); flipped_edge.left_triangle = new_left_triangle; if (left_prev_edge.left_triangle == this.left_triangle) { left_prev_edge.left_triangle = new_left_triangle; } else { left_prev_edge.right_triangle = new_left_triangle; } if (right_next_edge.left_triangle == this.right_triangle) { right_next_edge.left_triangle = new_left_triangle; } else { right_next_edge.right_triangle = new_left_triangle; } // Create the right triangle of the flipped edge. curr_edge_lines = new Edge[3]; curr_edge_lines[0] = flipped_edge; curr_edge_lines[1] = right_prev_edge; curr_edge_lines[2] = left_next_edge; Triangle new_right_triangle = new Triangle(curr_edge_lines, first_parent); flipped_edge.right_triangle = new_right_triangle; if (right_prev_edge.left_triangle == this.right_triangle) { right_prev_edge.left_triangle = new_right_triangle; } else { right_prev_edge.right_triangle = new_right_triangle; } if (left_next_edge.left_triangle == this.left_triangle) { left_next_edge.left_triangle = new_right_triangle; } else { left_next_edge.right_triangle = new_right_triangle; } return flipped_edge; } /** * Checks the consistancy of this edge in its database. * Used for debugging purposes. */ public boolean validate() { boolean result = true; if (this.left_triangle == null) { if (this.start_corner.object != null || this.end_corner.object != null) { System.out.println("Edge.validate: left triangle may be null only for bounding edges"); result = false; } } else { // check if the left triangle contains this edge boolean found = false; for (int i = 0; i < 3; ++i) { if (left_triangle.edge_lines[i] == this) { found = true; break; } } if (!found) { System.out.println("Edge.validate: left triangle does not contain this edge"); result = false; } } if (this.right_triangle == null) { if (this.start_corner.object != null || this.end_corner.object != null) { System.out.println("Edge.validate: right triangle may be null only for bounding edges"); result = false; } } else { // check if the left triangle contains this edge boolean found = false; for (int i = 0; i < 3; ++i) { if (right_triangle.edge_lines[i] == this) { found = true; break; } } if (!found) { System.out.println("Edge.validate: right triangle does not contain this edge"); result = false; } } return result; } public final Corner start_corner; public final Corner end_corner; /** The triangle on the left side of this edge. */ private Triangle left_triangle = null; /** The triangle on the right side of this edge. */ private Triangle right_triangle = null; /** The unique id number of this triangle. */ private final int id_no; } /** * Describes a triangle in the triagulation. edge_lines ia an array of dimension 3. * The edge lines arec sorted in counter clock sense around the border of this triangle. * The list children points to the children of this triangle, when used as a node in the * search graph. */ private class Triangle { public Triangle(Edge[] p_edge_lines, Triangle p_first_parent) { this.edge_lines = p_edge_lines; // create an empty list for the children. this.children = new LinkedList<Triangle>(); this.first_parent = p_first_parent; } /** * Returns true, if this triangle node is a leaf, * and false, if it is an inner node. */ public boolean is_leaf() { return this.children.isEmpty(); } /** * Gets the corner with index p_no. */ public Corner get_corner(int p_no) { if (p_no < 0 || p_no >= 3) { System.out.println("Triangle.get_corner: p_no out of range"); return null; } Edge curr_edge = edge_lines[p_no]; Corner result; if (curr_edge.left_triangle == this) { result = curr_edge.start_corner; } else if (curr_edge.right_triangle == this) { result = curr_edge.end_corner; } else { System.out.println("Triangle.get_corner: inconsistant edge lines"); result = null; } return result; } /** * Calculates the opposite corner of this triangle to p_edge_line. * Returns null, if p_edge_line is nor an edge line of this triangle. */ public Corner opposite_corner(Edge p_edge_line) { int edge_line_no = -1; for (int i = 0; i < 3; ++i) { if (this.edge_lines[i] == p_edge_line) { edge_line_no = i; break; } } if (edge_line_no < 0) { System.out.println("Triangle.opposite_corner: p_edge_line not found"); return null; } Edge next_edge = this.edge_lines[(edge_line_no + 1)% 3]; Corner result; if (next_edge.left_triangle == this) { result = next_edge.end_corner; } else { result = next_edge.start_corner; } return result; } /** * Checks if p_point is inside or on the border of this triangle. */ public boolean contains(Corner p_corner) { if (this.is_on_the_left_of_edge_line == null) { System.out.println("Triangle.contains: array is_on_the_left_of_edge_line not initialized"); return false; } for (int i = 0; i < 3; ++i) { Edge curr_edge = this.edge_lines[i]; Side curr_side = p_corner.side_of(curr_edge.start_corner, curr_edge.end_corner); if (this.is_on_the_left_of_edge_line[i]) // checking curr_edge.left_triangle == this instead will not work, if this triangle is an inner node. { if (curr_side == Side.ON_THE_RIGHT) { return false; } } else { if (curr_side == Side.ON_THE_LEFT) { return false; } } } return true; } /** * Puts the edges of all leafs below this node into the list p_result_edges */ public void get_leaf_edges(Set<Edge> p_result_edges) { if (this.is_leaf()) { for (int i = 0; i < 3; ++i) { Edge curr_edge = this.edge_lines[i]; if (curr_edge.start_corner.object != null && curr_edge.end_corner.object != null) { // Skip edges containing a bounding corner. p_result_edges.add(curr_edge); } } } else { for (Triangle curr_child : this.children) { if (curr_child.first_parent == this) // to prevent traversing nodes more than once { curr_child.get_leaf_edges(p_result_edges); } } } } /** Split this triangle into 3 new triangles by adding edges from * the corners of this triangle to p_corner, * p_corner has to be located in the interiour of this triangle. */ public Triangle[] split_at_inner_point(Corner p_corner) { Triangle [] new_triangles = new Triangle[3]; Edge[] new_edges = new Edge[3]; for (int i = 0; i < 3; ++i) { new_edges[i] = new Edge(this.get_corner(i), p_corner); } // construct the 3 new triangles. Edge [] curr_edge_lines = new Edge[3]; curr_edge_lines[0] = this.edge_lines[0]; curr_edge_lines[1] = new Edge(this.get_corner(1), p_corner); curr_edge_lines[2] = new Edge(p_corner, this.get_corner(0)); new_triangles[0] = new Triangle(curr_edge_lines, this); curr_edge_lines = new Edge[3]; curr_edge_lines[0] = this.edge_lines[1]; curr_edge_lines[1] = new Edge(this.get_corner(2), p_corner); curr_edge_lines[2] = new_triangles[0].edge_lines[1]; new_triangles[1] = new Triangle(curr_edge_lines, this); curr_edge_lines = new Edge[3]; curr_edge_lines[0] = this.edge_lines[2]; curr_edge_lines[1] = new_triangles[0].edge_lines[2]; curr_edge_lines[2] = new_triangles[1].edge_lines[1]; new_triangles[2] = new Triangle(curr_edge_lines, this); // Set the new neigbour triangles of the edge lines. for(int i = 0; i < 3; ++i) { Edge curr_edge = new_triangles[i].edge_lines[0]; if (curr_edge.get_left_triangle() == this) { curr_edge.set_left_triangle(new_triangles[i]); } else { curr_edge.set_right_triangle(new_triangles[i]); } // The other neighbour triangle remains valid. } Edge curr_edge = new_triangles[0].edge_lines[1]; curr_edge.set_left_triangle(new_triangles[0]); curr_edge.set_right_triangle(new_triangles[1]); curr_edge = new_triangles[1].edge_lines[1]; curr_edge.set_left_triangle(new_triangles[1]); curr_edge.set_right_triangle(new_triangles[2]); curr_edge = new_triangles[2].edge_lines[1]; curr_edge.set_left_triangle(new_triangles[0]); curr_edge.set_right_triangle(new_triangles[2]); return new_triangles; } /** * Split this triangle and p_neighbour_to_split into 4 new triangles by adding edges from * the corners of the triangles to p_corner. * p_corner is assumed to be loacated on the common edge line of this triangle and p_neigbour_to_split. * If that is not true, the function returns null. * The first 2 result triangles are from splitting this triangle, * and the last 2 result triangles are from splitting p_neighbour_to_split. */ public Triangle[] split_at_border_point(Corner p_corner, Triangle p_neighbour_to_split) { Triangle[] new_triangles = new Triangle[4]; // look for the triangle edge of this and the neighbour triangle containing p_point; int this_touching_edge_no = -1; int neigbbour_touching_edge_no = -1; Edge touching_edge = null; Edge other_touching_edge = null; for (int i = 0; i < 3; ++i) { Edge curr_edge = this.edge_lines[i]; if (p_corner.side_of(curr_edge.start_corner, curr_edge.end_corner) == Side.COLLINEAR) { this_touching_edge_no = i; touching_edge = curr_edge; } curr_edge = p_neighbour_to_split.edge_lines[i]; if (p_corner.side_of(curr_edge.start_corner, curr_edge.end_corner) == Side.COLLINEAR) { neigbbour_touching_edge_no = i; other_touching_edge = curr_edge; } } if (this_touching_edge_no < 0 || neigbbour_touching_edge_no < 0) { System.out.println("Triangle.split_at_border_point: touching edge not found"); return null; } if (touching_edge != other_touching_edge) { System.out.println("Triangle.split_at_border_point: edges inconsistent"); return null; } Edge first_common_new_edge; Edge second_common_new_edge; // Construct the new edge lines that 2 split triangles of this triangle // will be on the left side of the new common touching edges. if (this == touching_edge.left_triangle) { first_common_new_edge = new Edge(touching_edge.start_corner, p_corner); second_common_new_edge = new Edge(p_corner, touching_edge.end_corner); } else { first_common_new_edge = new Edge(touching_edge.end_corner, p_corner); second_common_new_edge = new Edge( p_corner, touching_edge.start_corner); } // Construct the first split triangle of this triangle. Edge prev_edge = this.edge_lines[(this_touching_edge_no + 2) % 3]; Edge this_splitting_edge; // construct the splitting edge line of this triangle, so that the first split // triangle lies on the left side, and the second split triangle on the right side. if (this == prev_edge.left_triangle) { this_splitting_edge = new Edge(p_corner, prev_edge.start_corner); } else { this_splitting_edge = new Edge(p_corner, prev_edge.end_corner); } Edge[] curr_edge_lines = new Edge[3]; curr_edge_lines[0] = prev_edge; curr_edge_lines[1] = first_common_new_edge; curr_edge_lines[2] = this_splitting_edge; new_triangles[0] = new Triangle(curr_edge_lines, this); if (this == prev_edge.left_triangle) { prev_edge.set_left_triangle(new_triangles[0]); } else { prev_edge.set_right_triangle(new_triangles[0]); } first_common_new_edge.set_left_triangle(new_triangles[0]); this_splitting_edge.set_left_triangle(new_triangles[0]); // Construct the second split triangle of this triangle. Edge next_edge = this.edge_lines[(this_touching_edge_no + 1) % 3]; curr_edge_lines = new Edge[3]; curr_edge_lines[0] = this_splitting_edge; curr_edge_lines[1] = second_common_new_edge; curr_edge_lines[2] = next_edge; new_triangles[1] = new Triangle(curr_edge_lines, this); this_splitting_edge.set_right_triangle(new_triangles[1]); second_common_new_edge.set_left_triangle(new_triangles[1]); if (this == next_edge.left_triangle) { next_edge.set_left_triangle(new_triangles[1]); } else { next_edge.set_right_triangle(new_triangles[1]); } // construct the first split triangle of p_neighbour_to_split next_edge = p_neighbour_to_split.edge_lines[(neigbbour_touching_edge_no + 1) % 3]; Edge neighbour_splitting_edge; // construct the splitting edge line of p_neighbour_to_split, so that the first split // triangle lies on the left side, and the second split triangle on the right side. if (p_neighbour_to_split == next_edge.left_triangle) { neighbour_splitting_edge = new Edge(next_edge.end_corner, p_corner); } else { neighbour_splitting_edge = new Edge(next_edge.start_corner, p_corner); } curr_edge_lines = new Edge[3]; curr_edge_lines[0] = neighbour_splitting_edge; curr_edge_lines[1] = first_common_new_edge; curr_edge_lines[2] = next_edge; new_triangles[2] = new Triangle(curr_edge_lines, p_neighbour_to_split); neighbour_splitting_edge.set_left_triangle(new_triangles[2]); first_common_new_edge.set_right_triangle(new_triangles[2]); if (p_neighbour_to_split == next_edge.left_triangle) { next_edge.set_left_triangle(new_triangles[2]); } else { next_edge.set_right_triangle(new_triangles[2]); } // construct the second split triangle of p_neighbour_to_split prev_edge = p_neighbour_to_split.edge_lines[(neigbbour_touching_edge_no + 2) % 3]; curr_edge_lines = new Edge[3]; curr_edge_lines[0] = prev_edge; curr_edge_lines[1] = second_common_new_edge; curr_edge_lines[2] = neighbour_splitting_edge; new_triangles[3] = new Triangle(curr_edge_lines, p_neighbour_to_split); if (p_neighbour_to_split == prev_edge.left_triangle) { prev_edge.set_left_triangle(new_triangles[3]); } else { prev_edge.set_right_triangle(new_triangles[3]); } second_common_new_edge.set_right_triangle(new_triangles[3]); neighbour_splitting_edge.set_right_triangle(new_triangles[3]); return new_triangles; } /** * Checks the consistancy of this triangle and its children. * Used for debugging purposes. */ public boolean validate() { boolean result = true; if (this.is_leaf()) { Edge prev_edge = this.edge_lines[2]; for (int i = 0; i < 3; ++i) { Edge curr_edge = this.edge_lines[i]; if (!curr_edge.validate()) { result = false; } // Check, if the ens corner of the previous line equals to the start corner of this line. Corner prev_end_corner; if (prev_edge.left_triangle == this) { prev_end_corner = prev_edge.end_corner; } else { prev_end_corner = prev_edge.start_corner; } Corner curr_start_corner; if (curr_edge.left_triangle == this) { curr_start_corner = curr_edge.start_corner; } else if (curr_edge.right_triangle == this) { curr_start_corner = curr_edge.end_corner; } else { System.out.println("Triangle.validate: edge inconsistent"); return false; } if (curr_start_corner != prev_end_corner) { System.out.println("Triangle.validate: corner inconsistent"); result = false; } prev_edge = curr_edge; } } else { for (Triangle curr_child: this.children) { if (curr_child.first_parent == this) // to avoid traversing nodes more than once. { curr_child.validate(); } } } return result; } /** * Must be done as long as this triangle node is a leaf and after for all its edge lines * the left_triangle or the right_triangle reference is set to this triangle. */ private void initialize_is_on_the_left_of_edge_line_array() { if (this.is_on_the_left_of_edge_line != null) { return; // already initialized } this.is_on_the_left_of_edge_line = new boolean[3]; for (int i = 0; i < 3; ++i) { this.is_on_the_left_of_edge_line[i] = (this.edge_lines[i].left_triangle == this); } } /** The 3 edge lines of this triangle sorted in counter clock sense around the border. */ private final Edge[] edge_lines; /** * Indicates, if this triangle is on the left of the i-th edge line for i = 0 to 2. * Must be set, if this triagngle is an inner node * because left_triangle and right_triangle of edge lines point only to leaf nodes. */ private boolean[] is_on_the_left_of_edge_line = null; /** The children of this triangle when used as a node in the triangle search graph. */ private Collection<Triangle> children; /** * Triangles resulting from an edge flip have 2 parents, all other triangles have 1 parent. * first parent is used when traversing the graph sequentially to avoid * visiting children nodes more than once. */ private final Triangle first_parent; } /** * Directed acyclic graph for finding the triangle containing a search point p. * The leaves contain the trianngles of the current triangulation. * The internal nodes are triangles, that were part of the triangulationn at some earlier stage, * but have been replaced their children. */ private static class TriangleGraph { public TriangleGraph(Triangle p_triangle) { if (p_triangle != null) { insert(p_triangle, null); } else { this.anchor = null; } } public void insert(Triangle p_triangle, Triangle p_parent) { p_triangle.initialize_is_on_the_left_of_edge_line_array(); if (p_parent == null) { anchor = p_triangle; } else { p_parent.children.add(p_triangle); } } /** * Search for the leaf triangle containing p_corner. * It will not be unique, if p_corner lies on a triangle edge. */ public Triangle position_locate(Corner p_corner) { if (this.anchor == null) { return null; } if (this.anchor.children.isEmpty()) { return this.anchor; } for (Triangle curr_child : this.anchor.children) { Triangle result = position_locate_reku(p_corner, curr_child); if (result != null) { return result; } } System.out.println("TriangleGraph.position_locate: containing triangle not found"); return null; } /** * Recursive part of position_locate. */ private Triangle position_locate_reku(Corner p_corner, Triangle p_triangle) { if (!p_triangle.contains(p_corner)) { return null; } if (p_triangle.is_leaf()) { return p_triangle; } for (Triangle curr_child : p_triangle.children) { Triangle result = position_locate_reku(p_corner, curr_child); if (result != null) { return result; } } System.out.println("TriangleGraph.position_locate_reku: containing triangle not found"); return null; } private Triangle anchor = null; } }