root / trunk / extensions / extGraph / src / org / gvsig / fmap / algorithm / triangulation / paul_chew / Triangulation.java @ 22583
History | View | Annotate | Download (11.2 KB)
1 |
package org.gvsig.fmap.algorithm.triangulation.paul_chew; |
---|---|
2 |
|
3 |
/*
|
4 |
* Copyright (c) 2005, 2007 by L. Paul Chew.
|
5 |
*
|
6 |
* Permission is hereby granted, without written agreement and without
|
7 |
* license or royalty fees, to use, copy, modify, and distribute this
|
8 |
* software and its documentation for any purpose, subject to the following
|
9 |
* conditions:
|
10 |
*
|
11 |
* The above copyright notice and this permission notice shall be included
|
12 |
* in all copies or substantial portions of the Software.
|
13 |
*
|
14 |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
15 |
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16 |
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17 |
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18 |
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
19 |
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
20 |
* DEALINGS IN THE SOFTWARE.
|
21 |
*/
|
22 |
|
23 |
import java.util.AbstractSet; |
24 |
import java.util.ArrayList; |
25 |
import java.util.HashSet; |
26 |
import java.util.Iterator; |
27 |
import java.util.LinkedList; |
28 |
import java.util.List; |
29 |
import java.util.Queue; |
30 |
import java.util.Set; |
31 |
|
32 |
/**
|
33 |
* A 2D Delaunay Triangulation (DT) with incremental site insertion.
|
34 |
*
|
35 |
* This is not the fastest way to build a DT, but it's a reasonable way to build
|
36 |
* a DT incrementally and it makes a nice interactive display. There are several
|
37 |
* O(n log n) methods, but they require that the sites are all known initially.
|
38 |
*
|
39 |
* A Triangulation is a Set of Triangles. A Triangulation is unmodifiable as a
|
40 |
* Set; the only way to change it is to add sites (via delaunayPlace).
|
41 |
*
|
42 |
* @author Paul Chew
|
43 |
*
|
44 |
* Created July 2005. Derived from an earlier, messier version.
|
45 |
*
|
46 |
* Modified November 2007. Rewrote to use AbstractSet as parent class and to use
|
47 |
* the Graph class internally. Tried to make the DT algorithm clearer by
|
48 |
* explicitly creating a cavity. Added code needed to find a Voronoi cell.
|
49 |
*
|
50 |
*/
|
51 |
public class Triangulation extends AbstractSet<Triangle> { |
52 |
|
53 |
private Triangle mostRecent = null; // Most recently "active" triangle |
54 |
private Graph<Triangle> triGraph; // Holds triangles for navigation |
55 |
|
56 |
/**
|
57 |
* All sites must fall within the initial triangle.
|
58 |
*
|
59 |
* @param triangle the initial triangle
|
60 |
*/
|
61 |
public Triangulation (Triangle triangle) {
|
62 |
triGraph = new Graph<Triangle>();
|
63 |
triGraph.add(triangle); |
64 |
mostRecent = triangle; |
65 |
} |
66 |
|
67 |
/* The following two methods are required by AbstractSet */
|
68 |
|
69 |
@Override
|
70 |
public Iterator<Triangle> iterator () { |
71 |
return triGraph.nodeSet().iterator();
|
72 |
} |
73 |
|
74 |
@Override
|
75 |
public int size () { |
76 |
return triGraph.nodeSet().size();
|
77 |
} |
78 |
|
79 |
@Override
|
80 |
public String toString () { |
81 |
return "Triangulation with " + size() + " triangles"; |
82 |
} |
83 |
|
84 |
/**
|
85 |
* True iff triangle is a member of this triangulation.
|
86 |
* This method isn't required by AbstractSet, but it improves efficiency.
|
87 |
* @param triangle the object to check for membership
|
88 |
*/
|
89 |
public boolean contains (Object triangle) { |
90 |
return triGraph.nodeSet().contains(triangle);
|
91 |
} |
92 |
|
93 |
/**
|
94 |
* Report neighbor opposite the given vertex of triangle.
|
95 |
* @param site a vertex of triangle
|
96 |
* @param triangle we want the neighbor of this triangle
|
97 |
* @return the neighbor opposite site in triangle; null if none
|
98 |
* @throws IllegalArgumentException if site is not in this triangle
|
99 |
*/
|
100 |
public Triangle neighborOpposite (Pnt site, Triangle triangle) {
|
101 |
if (!triangle.contains(site))
|
102 |
throw new IllegalArgumentException("Bad vertex; not in triangle"); |
103 |
for (Triangle neighbor: triGraph.neighbors(triangle)) {
|
104 |
if (!neighbor.contains(site)) return neighbor; |
105 |
} |
106 |
return null; |
107 |
} |
108 |
|
109 |
/**
|
110 |
* Return the set of triangles adjacent to triangle.
|
111 |
* @param triangle the triangle to check
|
112 |
* @return the neighbors of triangle
|
113 |
*/
|
114 |
public Set<Triangle> neighbors(Triangle triangle) { |
115 |
return triGraph.neighbors(triangle);
|
116 |
} |
117 |
|
118 |
/**
|
119 |
* Report triangles surrounding site in order (cw or ccw).
|
120 |
* @param site we want the surrounding triangles for this site
|
121 |
* @param triangle a "starting" triangle that has site as a vertex
|
122 |
* @return all triangles surrounding site in order (cw or ccw)
|
123 |
* @throws IllegalArgumentException if site is not in triangle
|
124 |
*/
|
125 |
public List<Triangle> surroundingTriangles (Pnt site, Triangle triangle) { |
126 |
if (!triangle.contains(site))
|
127 |
throw new IllegalArgumentException("Site not in triangle"); |
128 |
List<Triangle> list = new ArrayList<Triangle>(); |
129 |
Triangle start = triangle; |
130 |
Pnt guide = triangle.getVertexButNot(site); // Affects cw or ccw
|
131 |
while (true) { |
132 |
list.add(triangle); |
133 |
Triangle previous = triangle; |
134 |
triangle = this.neighborOpposite(guide, triangle); // Next triangle |
135 |
guide = previous.getVertexButNot(site, guide); // Update guide
|
136 |
if (triangle == start) break; |
137 |
} |
138 |
return list;
|
139 |
} |
140 |
|
141 |
/**
|
142 |
* Locate the triangle with point inside it or on its boundary.
|
143 |
* @param point the point to locate
|
144 |
* @return the triangle that holds point; null if no such triangle
|
145 |
*/
|
146 |
public Triangle locate (Pnt point) {
|
147 |
Triangle triangle = mostRecent; |
148 |
if (!this.contains(triangle)) triangle = null; |
149 |
|
150 |
// Try a directed walk (this works fine in 2D, but can fail in 3D)
|
151 |
Set<Triangle> visited = new HashSet<Triangle>(); |
152 |
while (triangle != null) { |
153 |
if (visited.contains(triangle)) { // This should never happen |
154 |
System.out.println("Warning: Caught in a locate loop"); |
155 |
break;
|
156 |
} |
157 |
visited.add(triangle); |
158 |
// Corner opposite point
|
159 |
Pnt corner = point.isOutside(triangle.toArray(new Pnt[0])); |
160 |
if (corner == null) return triangle; |
161 |
triangle = this.neighborOpposite(corner, triangle);
|
162 |
} |
163 |
// No luck; try brute force
|
164 |
// System.out.println("Warning: Checking all triangles for " + point);
|
165 |
for (Triangle tri: this) { |
166 |
if (point.isOutside(tri.toArray(new Pnt[0])) == null) return tri; |
167 |
} |
168 |
// No such triangle
|
169 |
System.out.println("Warning: No triangle holds " + point); |
170 |
return null; |
171 |
} |
172 |
|
173 |
/**
|
174 |
* Place a new site into the DT.
|
175 |
* Nothing happens if the site matches an existing DT vertex.
|
176 |
* @param site the new Pnt
|
177 |
* @throws IllegalArgumentException if site does not lie in any triangle
|
178 |
*/
|
179 |
public void delaunayPlace (Pnt site) { |
180 |
// Uses straightforward scheme rather than best asymptotic time
|
181 |
|
182 |
// Locate containing triangle
|
183 |
Triangle triangle = locate(site); |
184 |
// Give up if no containing triangle or if site is already in DT
|
185 |
if (triangle == null) |
186 |
throw new IllegalArgumentException("No containing triangle"); |
187 |
if (triangle.contains(site)) return; |
188 |
|
189 |
// Determine the cavity and update the triangulation
|
190 |
Set<Triangle> cavity = getCavity(site, triangle);
|
191 |
// if (cavity.size() == 0)
|
192 |
// {
|
193 |
// ERROR!!!
|
194 |
// System.out.println("cavity vac?a");
|
195 |
// return;
|
196 |
// }
|
197 |
mostRecent = update(site, cavity); |
198 |
} |
199 |
|
200 |
/**
|
201 |
* Determine the cavity caused by site.
|
202 |
* @param site the site causing the cavity
|
203 |
* @param triangle the triangle containing site
|
204 |
* @return set of all triangles that have site in their circumcircle
|
205 |
*/
|
206 |
private Set<Triangle> getCavity (Pnt site, Triangle triangle) { |
207 |
Set<Triangle> encroached = new HashSet<Triangle>(); |
208 |
Queue<Triangle> toBeChecked = new LinkedList<Triangle>(); |
209 |
Set<Triangle> marked = new HashSet<Triangle>(); |
210 |
toBeChecked.add(triangle); |
211 |
marked.add(triangle); |
212 |
// encroached.add(triangle);
|
213 |
while (!toBeChecked.isEmpty()) {
|
214 |
triangle = toBeChecked.remove(); |
215 |
if (site.vsCircumcircle(triangle.toArray(new Pnt[0])) == 1) |
216 |
continue; // Site outside triangle => triangle not in cavity |
217 |
encroached.add(triangle); |
218 |
// Check the neighbors
|
219 |
for (Triangle neighbor: triGraph.neighbors(triangle)){
|
220 |
if (marked.contains(neighbor)) continue; |
221 |
marked.add(neighbor); |
222 |
toBeChecked.add(neighbor); |
223 |
} |
224 |
} |
225 |
return encroached;
|
226 |
} |
227 |
|
228 |
/**
|
229 |
* Update the triangulation by removing the cavity triangles and then
|
230 |
* filling the cavity with new triangles.
|
231 |
* @param site the site that created the cavity
|
232 |
* @param cavity the triangles with site in their circumcircle
|
233 |
* @return one of the new triangles
|
234 |
*/
|
235 |
private Triangle update (Pnt site, Set<Triangle> cavity) { |
236 |
Set<Set<Pnt>> boundary = new HashSet<Set<Pnt>>(); |
237 |
Set<Triangle> theTriangles = new HashSet<Triangle>(); |
238 |
|
239 |
// Find boundary facets and adjacent triangles
|
240 |
for (Triangle triangle: cavity) {
|
241 |
theTriangles.addAll(neighbors(triangle)); |
242 |
for (Pnt vertex: triangle) {
|
243 |
Set<Pnt> facet = triangle.facetOpposite(vertex);
|
244 |
if (boundary.contains(facet)) boundary.remove(facet);
|
245 |
else boundary.add(facet);
|
246 |
} |
247 |
} |
248 |
theTriangles.removeAll(cavity); // Adj triangles only
|
249 |
|
250 |
// Remove the cavity triangles from the triangulation
|
251 |
for (Triangle triangle: cavity) triGraph.remove(triangle);
|
252 |
|
253 |
// Build each new triangle and add it to the triangulation
|
254 |
Set<Triangle> newTriangles = new HashSet<Triangle>(); |
255 |
for (Set<Pnt> vertices: boundary) { |
256 |
vertices.add(site); |
257 |
Triangle tri = new Triangle(vertices);
|
258 |
triGraph.add(tri); |
259 |
newTriangles.add(tri); |
260 |
} |
261 |
|
262 |
// Update the graph links for each new triangle
|
263 |
theTriangles.addAll(newTriangles); // Adj triangle + new triangles
|
264 |
for (Triangle triangle: newTriangles)
|
265 |
for (Triangle other: theTriangles)
|
266 |
if (triangle.isNeighbor(other))
|
267 |
triGraph.add(triangle, other); |
268 |
|
269 |
// Return one of the new triangles
|
270 |
return newTriangles.iterator().next();
|
271 |
} |
272 |
|
273 |
/**
|
274 |
* Main program; used for testing.
|
275 |
*/
|
276 |
public static void main (String[] args) { |
277 |
Triangle tri = |
278 |
new Triangle(new Pnt(-10,10), new Pnt(10,10), new Pnt(0,-10)); |
279 |
System.out.println("Triangle created: " + tri); |
280 |
Triangulation dt = new Triangulation(tri);
|
281 |
System.out.println("DelaunayTriangulation created: " + dt); |
282 |
dt.delaunayPlace(new Pnt(0,0)); |
283 |
dt.delaunayPlace(new Pnt(1,0)); |
284 |
dt.delaunayPlace(new Pnt(0,1)); |
285 |
dt.delaunayPlace(new Pnt(0.5,0.5)); |
286 |
System.out.println("After adding 3 points, we have a " + dt); |
287 |
Triangle.moreInfo = true;
|
288 |
System.out.println("Triangles: " + dt.triGraph.nodeSet()); |
289 |
} |
290 |
|
291 |
public void printStuff() { |
292 |
System.out.println("Triangles: " + triGraph.nodeSet()); |
293 |
|
294 |
} |
295 |
} |