Statistics
| Revision:

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
}