/* * Copyright (c) 2005 by L. Paul Chew. * * Permission is hereby granted, without written agreement and without * license or royalty fees, to use, copy, modify, and distribute this * software and its documentation for any purpose, subject to the following * conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ package org.openjump.core.graph.delauneySimplexInsert; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.NoSuchElementException; import java.util.Set; /** * A 2D Delaunay Triangulation (DT) with incremental site insertion. * This is not the fastest way to build a DT, but it's a reasonable way * to build the DT incrementally and it makes a nice interactive display. * There are several O(n log n) methods, but they require that either (1) * the sites are all known initially or (2) the sites are inserted in random * order. * * @author Paul Chew * * Created July 2005. Derived from an earlier, messier version. */ public class DelaunayTriangulation extends Triangulation { private Simplex mostRecent = null; // Most recently inserted triangle public boolean debug = false; // Used for debugging /** * Constructor. * All sites must fall within the initial triangle. * @param triangle the initial triangle */ public DelaunayTriangulation (Simplex triangle) { super(triangle); mostRecent = triangle; } /** * Locate the triangle with point (a Pnt) inside (or on) it. * @param point the Pnt to locate * @return triangle (Simplex<Pnt>) that holds the point; null if no such triangle */ public Simplex locate (Pnt point) { Simplex triangle = mostRecent; if (!this.contains(triangle)) triangle = null; // Try a directed walk (this works fine in 2D, but can fail in 3D) Set visited = new HashSet(); while (triangle != null) { if (visited.contains(triangle)) { // This should never happen System.out.println("Warning: Caught in a locate loop"); break; } visited.add(triangle); // Corner opposite point Pnt corner = point.isOutside((Pnt[]) triangle.toArray(new Pnt[0])); if (corner == null) return triangle; triangle = this.neighborOpposite(corner, triangle); } // No luck; try brute force System.out.println("Warning: Checking all triangles for " + point); for (Iterator it = this.iterator(); it.hasNext();) { Simplex tri = (Simplex) it.next(); if (point.isOutside((Pnt[]) tri.toArray(new Pnt[0])) == null) return tri; } // No such triangle System.out.println("Warning: No triangle holds " + point); return null; } /** * Place a new point site into the DT. * @param site the new Pnt * @return set of all new triangles created */ public Set delaunayPlace (Pnt site) { Set newTriangles = new HashSet(); Set oldTriangles = new HashSet(); Set doneSet = new HashSet(); LinkedList waitingQ = new LinkedList(); // Locate containing triangle if (debug) System.out.println("Locate"); Simplex triangle = locate(site); // Give up if no containing triangle or if site is already in DT if (triangle == null || triangle.contains(site)) return newTriangles; // Find Delaunay cavity (those triangles with site in their circumcircles) if (debug) System.out.println("Cavity"); waitingQ.add(triangle); while (!waitingQ.isEmpty()) { triangle = (Simplex) waitingQ.removeFirst(); if (site.vsCircumcircle((Pnt[]) triangle.toArray(new Pnt[0])) == 1) continue; oldTriangles.add(triangle); Iterator it = this.neighbors(triangle).iterator(); for (; it.hasNext();) { Simplex tri = (Simplex) it.next(); if (doneSet.contains(tri)) continue; doneSet.add(tri); waitingQ.add(tri); } } // Create the new triangles if (debug) System.out.println("Create"); for (Iterator it = Simplex.boundary(oldTriangles).iterator(); it.hasNext();) { Set facet = (Set) it.next(); facet.add(site); newTriangles.add(new Simplex(facet)); } // Replace old triangles with new triangles if (debug) System.out.println("Update"); this.update(oldTriangles, newTriangles); // Update mostRecent triangle if (!newTriangles.isEmpty()) mostRecent = (Simplex) newTriangles.iterator().next(); return newTriangles; } /** * Main program; used for testing. */ public static void main (String[] args) { Simplex tri = new Simplex(new Pnt[] {new Pnt(-10,10), new Pnt(10,10), new Pnt(0,-10)}); System.out.println("Triangle created: " + tri); DelaunayTriangulation dt = new DelaunayTriangulation(tri); System.out.println("DelaunayTriangulation created: " + dt); dt.delaunayPlace(new Pnt(0,0)); dt.delaunayPlace(new Pnt(1,0)); dt.delaunayPlace(new Pnt(0,1)); System.out.println("After adding 3 points, the DelaunayTriangulation is a " + dt); dt.printStuff(); } }