checkpoint
[anneal.git] / src / edu / berkeley / qfat / Mesh.java
1 package edu.berkeley.qfat;
2 import java.awt.*;
3 import java.util.*;
4 import java.awt.event.*;
5 import javax.swing.*;
6 import javax.media.opengl.*;
7 import javax.media.opengl.glu.*;
8 import edu.berkeley.qfat.geom.*;
9 import edu.berkeley.qfat.geom.HasBindingGroup;
10 import edu.wlu.cs.levy.CG.KDTree;
11 import edu.berkeley.qfat.geom.Point;
12 import com.infomatiq.jsi.IntProcedure;
13
14 public class Mesh implements Iterable<Mesh.T> {
15
16     public static final float EPSILON = (float)0.0001;
17     public static final Random random = new Random();
18
19     private RTree<T>         triangles = new RTree<T>();
20     private PointSet<Vertex> vertices  = new PointSet<Vertex>();
21
22     public boolean immutableVertices;
23     public Mesh    error_against      = null;
24     public double  error              = 0;
25
26     public Mesh(boolean immutableVertices) { this.immutableVertices = immutableVertices; }
27
28     public void makeVerticesImmutable() { this.immutableVertices = true; }
29     public float error() { return (float)error; }
30
31     public int size() { return vertices.size(); }
32     public Iterable<Vertex> vertices() { return vertices; }
33     public Iterator<T> iterator() { return triangles.iterator(); }
34
35     public void rebindPoints() {
36         // unbind all points
37         for(Mesh.T t : this) {
38             t.v1().unbind();
39             t.v2().unbind();
40             t.v3().unbind();
41         }
42         // ask edges to re-implement their bindings
43         for(Mesh.T t : this) {
44             t.e1().dobind();
45             t.e2().dobind();
46             t.e3().dobind();
47         }
48         System.out.println("rebound!");
49     }
50
51     public void transform(Matrix m) {
52         ArrayList<Vertex> set = new ArrayList<Vertex>();
53         for(Vertex v : vertices) set.add(v);
54         for(Vertex v : set) v.transform(m.times(v.p), true, null);
55         for(Vertex v : set) v.goodp = v.p;
56     }
57
58     public void rebuild() { /*vertices.rebuild();*/ }
59     public Vec diagonal() { return vertices.diagonal(); }
60     public Point centroid() { return vertices.centroid(); }
61     public Vertex nearest(Point p) { return vertices.nearest(p); }
62
63     /** compute the volume of the mesh */
64     public float volume() {
65         double total = 0;
66         for(T t : this) {
67             double area = t.area();
68             Vec origin_to_centroid = new Vec(new Point(0, 0, 0), t.centroid());
69             boolean facingAway = t.norm().dot(origin_to_centroid) > 0;
70             double height = Math.abs(t.norm().dot(origin_to_centroid));
71             total += ((facingAway ? 1 : -1) * area * height) / 3.0;
72         }
73         return (float)total;
74     }
75
76
77     // Vertexices //////////////////////////////////////////////////////////////////////////////
78
79     /** a vertex in the mesh */
80     public final class Vertex extends HasQuadric implements Visitor {
81         public Point p, goodp;
82         public Point oldp;
83         E e;                // some edge *leaving* this point
84
85         private boolean illegal = false;
86
87         public boolean visible = false;
88
89         public Point getPoint() { return p; }
90         public float error() { return olderror; }
91
92         private Vertex(Point p) {
93             this.p = p;
94             this.goodp = p;
95             this.oldp = p;
96             if (vertices.get(p) != null) throw new Error();
97             vertices.add(this);
98         }
99
100         public void reinsert() {
101             vertices.remove(this);
102             vertices.add(this);
103             for(E e = this.e; e!=null; e=e.pair.next==this.e?null:e.pair.next) e.t.reinsert();
104         }
105
106         public float olderror = 0;
107         public void setError(float nerror) {
108             error -= olderror;
109             olderror = nerror;
110             error += olderror;
111         }
112
113         public float averageTriangleArea() {
114             int count = 0;
115             float ret = 0;
116             for(E e = this.e; e!=null; e=e.pair.next==this.e?null:e.pair.next) {
117                 ret += e.t.area();
118                 count++;
119             }
120             return ret/count;
121         }
122         public float averageEdgeLength() {
123             int count = 0;
124             float ret = 0;
125             for(E e = this.e; e!=null; e=e.pair.next==this.e?null:e.pair.next) {
126                 ret += e.length();
127                 count++;
128             }
129             return ret/count;
130         }
131
132         public Matrix _recomputeFundamentalQuadric() {
133             Matrix m = Matrix.ZERO;
134             int count = 0;
135             for(E e = this.e; e!=null; e=e.pair.next==this.e?null:e.pair.next) {
136                 m = m.plus(e.t.norm().fundamentalQuadric(e.t.centroid()));
137                 count++;
138             }
139             if (count > 0) {
140                 m = m.plus(norm().fundamentalQuadric(this.p).times(count));
141                 count *= 2;
142             }
143             return m.times(1/(float)count);
144         }
145
146         public HasQuadric nearest() { return error_against==null ? null : error_against.vertices.nearest(p, this); }
147         public void computeError() {
148             if (error_against==null) return;
149             float nerror =
150                 nearest_in_other_mesh != null
151                 ? nearest_in_other_mesh.fundamentalQuadric().preAndPostMultiply(p)
152                 : nearest().fundamentalQuadric().preAndPostMultiply(p);
153             if (quadric_count != 0)
154                 nerror = (nerror + quadric.preAndPostMultiply(p))/(quadric_count+1);
155
156             if (!immutableVertices && quadric_count == 0) {
157                 //nerror = Math.max(nerror, 0.4f);
158                 //nerror *= 2;
159             }
160             //System.out.println(nerror);
161             for(E e = this.e; e!=null; e=e.pair.next==this.e?null:e.pair.next) {
162                 double ang = e.dihedralAngle();
163                 if (ang > Math.PI) throw new Error();
164                 if (ang < -Math.PI) throw new Error();
165                 float minangle = (float)(Math.PI * 0.8);
166                 //nerror += ((ang / Math.PI)*(ang/Math.PI)) * e.length() * 0.05;
167
168                 nerror += (1-e.t.quality())*0.0001;
169                 if (ang > minangle) nerror += (ang - minangle);
170
171                 //System.out.println(((ang / Math.PI)*(ang/Math.PI)) * 0.000001);
172                 /*
173                 if (e.t.aspect() < 0.2) {
174                     nerror += (0.2-e.t.aspect()) * 10;
175                 }
176                 */
177             }
178             if (!immutableVertices) {
179                 Vertex n = (Vertex)nearest();
180                 float d = norm().dot(n.norm());
181                 if (d > 1 || d < -1) throw new Error();
182                 if (d >= 0) {
183                     nerror *= (2.0f - d);
184                 } else {
185                     nerror += 0.0003 * (2.0f + d);
186                     nerror *= (2.0f + d);
187                 }
188             }
189
190             setError(nerror);
191         }
192
193         public boolean move(Matrix m, boolean ignoreProblems) {
194
195             boolean good = true;
196
197             //     t1' = M * t1
198             //     t2' = t2.getMatrix(t1) * t1'
199             //     t2' = t2.getMatrix(t1) * M * t1
200             //     t1 =     t1.getMatrix(t2) * t2
201             // M * t1 = M * t1.getMatrix(t2) * t2
202
203             if (bindingGroup!=null && this != bindingGroup.getMaster()) {
204                 Matrix v = getBindingMatrix(bindingGroup.getMaster());
205                 return ((Vertex)bindingGroup.getMaster()).move(v.inverse().times(m).times(v), ignoreProblems);
206             }
207
208             if (bindingGroup != null) {
209                 Matrix m2 = null;
210                 for(int i=0; i<20 && !m.equals(m2); i++) {
211                     m2 = m.times(getConstraint());
212                     //System.out.println(m.minus(m2));
213                 }
214                 if (!m.equals(m2)) return true;
215             }
216             ok = false;
217             Point op = this.p;
218             Point pt = m.times(this.p);
219             for(Vertex v : (Iterable<Vertex>)getBoundPeers()) {
220                 Point pt2 = v.getBindingMatrix(this).times(pt);
221                 /*
222                 if (Math.abs( v.p.minus(pt2).mag() / pt.minus(op).mag() ) > 5)
223                     throw new Error(v.p+" "+pt2+"\n"+op+" "+pt+"\n"+v.getBindingMatrix(this));
224                 if (Math.abs( v.p.minus(pt2).mag() / pt.minus(op).mag() ) < 1/5) throw new Error();
225                 */
226                 good &= v.transform(pt2, ignoreProblems, v.getBindingMatrix(this));
227             }
228
229             if (!good && !ignoreProblems) {
230                 for(Vertex v : (Iterable<Vertex>)getBoundPeers()) 
231                     v.transform(v.oldp, true, null);
232             }
233
234             for(Vertex v : (Iterable<Vertex>)getBoundPeers())
235                 v.recomputeFundamentalQuadricIfNeighborChanged();
236             for(Vertex v : (Iterable<Vertex>)getBoundPeers())
237                 v.reComputeErrorAround();
238             ok = true;
239             return good;
240         }
241         public boolean ok = true;
242
243         /** does NOT update bound pairs! */
244         private boolean transform(Point newp, boolean ignoreProblems, Matrix yes) {
245             this.oldp = this.p;
246             if (immutableVertices) throw new Error();
247
248             unApplyQuadricToNeighbor();
249
250
251             boolean illegalbefore = illegal;
252             illegal = false;
253             /*
254             if (this.p.minus(newp).mag() > 0.1 && !ignoreProblems) {
255                 try {
256                     throw new Exception(""+this.p.minus(newp).mag()+" "+ignoreProblems+" "+yes);
257                 } catch(Exception e) {
258                     e.printStackTrace();
259                 }
260                 illegal = true;
261             }
262             */
263
264             this.p = newp;
265             reinsert();
266             applyQuadricToNeighbor();
267
268             if (!ignoreProblems) {
269                 checkLegality();
270             }
271             for(E e = this.e; e!=null; e=e.pair.next==this.e?null:e.pair.next)
272                 e.p2.quadricStale = true;
273             return !illegal || (illegalbefore && illegal);
274         } 
275
276         public void checkLegality() {
277             /*
278             for(E e = this.e; e!=null; e=e.pair.next==this.e?null:e.pair.next) {
279                 if (Math.abs(e.dihedralAngle()) > (Math.PI * 0.9) ||
280                     Math.abs(e.next.dihedralAngle()) > (Math.PI * 0.9)) illegal = true;
281                 if (e.t.aspect() < 0.2) illegal = true;
282             }
283             */
284             if (!illegal) triangles.range(oldp, this.p, (Visitor<T>)this);
285         }
286
287         public void reComputeErrorAround() {
288             reComputeError();
289             if (nearest_in_other_mesh != null)
290                 nearest_in_other_mesh.reComputeError();
291             for(E e = this.e; e!=null; e=e.pair.next==this.e?null:e.pair.next)
292                 e.p2.reComputeError();
293         }
294
295         public boolean visit(Object o) {
296             if (o instanceof Vertex)
297                 return ((Vertex)o).e != null && ((Vertex)o).norm().dot(Vertex.this.norm()) >= 0;
298             T t = (T)o;
299             if (illegal) return false;
300             for(E e = Vertex.this.e; e!=null; e=e.pair.next==Vertex.this.e?null:e.pair.next) {
301                 if (!t.has(e.p1) && !t.has(e.p2) && e.intersects(t)) { illegal = true; }
302                 if (e.t != null) {
303                     if (!e.t.has(t.e1().p1) && !e.t.has(t.e1().p2) && t.e1().intersects(e.t)) { illegal = true; }
304                     if (!e.t.has(t.e2().p1) && !e.t.has(t.e2().p2) && t.e2().intersects(e.t)) { illegal = true; }
305                     if (!e.t.has(t.e3().p1) && !e.t.has(t.e3().p2) && t.e3().intersects(e.t)) { illegal = true; }
306                 }
307             }
308             return !illegal;
309         }
310
311         public E getFreeIncident() {
312             E ret = getFreeIncident(e, e);
313             if (ret != null) return ret;
314             for(E e = this.e; e!=null; e=e.pair.next==this.e?null:e.pair.next)
315                 System.out.println(e + " " + e.t);
316             throw new Error("unable to find free incident to " + this);
317         }
318
319         public E getFreeIncident(E start, E before) {
320             for(E e = start; e!=null; e=e.pair.next==before?null:e.pair.next)
321                 if (e.pair.p2 == this && e.pair.t == null && e.pair.next.t == null)
322                     return e.pair;
323             return null;
324         }
325
326         public E getE(Point p2) {
327             Vertex v = vertices.get(p2);
328             if (v==null) return null;
329             return getE(v);
330         }
331         public E getE(Vertex p2) {
332             for(E e = this.e; e!=null; e=e.pair.next==this.e?null:e.pair.next)
333                 if (e.p1 == this && e.p2 == p2) return e;
334             return null;
335         }
336
337         private void glNormal(GL gl) {
338             Vec norm = norm();
339             gl.glNormal3f(norm.x, norm.y, norm.z);
340         }
341         public Vec norm() {
342             Vec norm = new Vec(0, 0, 0);
343             for(E e = this.e; e!=null; e=e.pair.next==this.e?null:e.pair.next)
344                 if (e.t != null)
345                     norm = norm.plus(e.t.norm().times((float)e.prev.angle()));
346             return norm.norm();
347         }
348
349         public void bindTo(Vertex p) { bindTo(Matrix.ONE, p); }
350     }
351
352
353     /** [UNIQUE] an edge */
354     public final class E extends HasBindingGroup implements Comparable<E> {
355
356         public final Vertex p1, p2;
357         T t;     // triangle to our "left"
358         E prev;  // previous half-edge
359         E next;  // next half-edge
360         E pair;  // partner half-edge
361         boolean shattered = false;
362
363         public boolean intersects(T t) { return t.intersects(p1.p, p2.p); }
364
365         public void bindingGroupChanged(edu.berkeley.qfat.geom.BindingGroup newBindingGroup_) {
366
367             edu.berkeley.qfat.geom.BindingGroup<E> newBindingGroup =
368                 (edu.berkeley.qfat.geom.BindingGroup<E>)newBindingGroup_;
369             if (newBindingGroup==null) return;
370             if (this==newBindingGroup.getMaster()) return;
371             HashSet<E> nbg = new HashSet<E>();
372             for(E eother : (Iterable<E>)newBindingGroup) nbg.add(eother);
373             for(E eother : nbg) {
374                 if (next==null || prev==null) continue;
375                 if (eother.next==null || eother.prev==null) continue;
376
377                 if (next.isBoundTo(eother.pair.prev.pair) && !prev.isBoundTo(eother.pair.next.pair))
378                     prev.bindTo(next.getBindingMatrix(eother.pair.prev.pair), eother.pair.next.pair);
379                 if (!next.isBoundTo(eother.pair.prev.pair) && prev.isBoundTo(eother.pair.next.pair))
380                     next.bindTo(prev.getBindingMatrix(eother.pair.next.pair), eother.pair.prev.pair);
381
382                 /*
383                 if (next.isBoundTo(eother.prev) && !prev.isBoundTo(eother.next))
384                     prev.bindTo(next.getBindingMatrix(eother.prev), eother.next);
385                 if (!next.isBoundTo(eother.prev) && prev.isBoundTo(eother.next))
386                     next.bindTo(prev.getBindingMatrix(eother.next), eother.prev);
387                 */
388                 if (next.isBoundTo(eother.next) && !prev.isBoundTo(eother.prev))
389                     prev.bindTo(next.getBindingMatrix(eother.next), eother.prev);
390                 if (!next.isBoundTo(eother.next) && prev.isBoundTo(eother.prev))
391                     next.bindTo(prev.getBindingMatrix(eother.prev), eother.next);
392             }
393
394         }
395
396         public float stretchRatio() {
397             Vertex nearest = error_against.nearest(midpoint());
398             float nearest_distance = midpoint().distance(nearest.p);
399             float other_distance =
400                 (p1.p.distance(error_against.nearest(p1.p).p)+
401                  p2.p.distance(error_against.nearest(p2.p).p))/2;
402             return nearest_distance/other_distance;
403         }
404         public float comparator() {
405             return length();
406         }
407         public int compareTo(E e) {
408             return e.comparator() > comparator() ? 1 : -1;
409         }
410         public void bindEdge(E e, Matrix m) {
411             _bindEdge(e, m);
412             pair._bindEdge(e.pair, m);
413         }
414         public void _bindEdge(E e, Matrix m) {
415             e = e.pair;
416             /*
417             //assumes edges are identical length at binding time
418             Vec reflectionPlaneNormal = e.p2.p.minus(e.p1.p).norm();
419             float a = reflectionPlaneNormal.x;
420             float b = reflectionPlaneNormal.y;
421             float c = reflectionPlaneNormal.z;
422             Matrix reflectionMatrix =
423                 new Matrix( 1-2*a*a,  -2*a*b,  -2*a*c, 0,
424                             -2*a*b,  1-2*b*b,  -2*b*c, 0,
425                             -2*a*c,   -2*b*c, 1-2*c*c, 0,
426                             0,       0,       0,       1);
427             m = m.times(Matrix.translate(e.midpoint().minus(Point.ORIGIN))
428                         .times(reflectionMatrix)
429                         .times(Matrix.translate(Point.ORIGIN.minus(e.midpoint()))));
430             System.out.println(reflectionPlaneNormal);
431             System.out.println("  " + p1.p + " " + m.times(e.p1.p));
432             System.out.println("  " + p2.p + " " + m.times(e.p2.p));
433             */
434             /*
435             if (m.times(e.p1.p).minus(p1.p).mag() > EPSILON) throw new Error();
436             if (m.times(e.p2.p).minus(p2.p).mag() > EPSILON) throw new Error();
437             */
438             this.bindTo(m, e);
439         }
440         
441         public void dobind() {
442             for(E e : (Iterable<E>)getBoundPeers()) {
443                 if (e==this) continue;
444                 p1.bindTo(getBindingMatrix(e), e.p1);
445                 p2.bindTo(getBindingMatrix(e), e.p2);
446                 e.p1.setConstraint(getConstraint());
447                 e.p2.setConstraint(getConstraint());
448             }
449         }
450
451         public Point shatter() {
452             if (shattered || destroyed) return null;
453             shattered = true;
454             E first = null;
455             E firste = null;
456             E firstx = null;
457             E firstq = null;
458             for(E e : (Iterable<E>)getBoundPeers()) {
459                 E enext = e.next;
460                 E eprev = e.prev;
461                 E pnext = e.pair.next;
462                 E pprev = e.pair.prev;
463                 Point mid = e.midpoint();
464                 Vertex r = e.next.p2;
465                 Vertex l = e.pair.next.p2;
466                 if (!e.destroyed) {
467                     e.destroy();
468                     e.pair.destroy();
469                     newT(r.p, e.p1.p, mid,    null, 0);
470                     newT(r.p, mid,    e.p2.p, null, 0);
471                     newT(l.p, mid,    e.p1.p, null, 0);
472                     newT(l.p, e.p2.p, mid,    null, 0);
473                 }
474             }
475             for(E e : (Iterable<E>)getBoundPeers()) {
476                 Point mid = e.midpoint();
477                 if (first==null) {
478                     first = e.p1.getE(mid);
479                     firste = e;
480                     firstx = e.pair;
481                     firstq = e.p2.getE(mid).pair;
482                     continue;
483                 }
484                 e.p1.getE(mid).          bindTo(e.getBindingMatrix(firste), first);
485                 e.p1.getE(mid).pair.     bindTo(e.getBindingMatrix(firste), first.pair);
486                 e.p2.getE(mid).pair.     bindTo(e.getBindingMatrix(firste), firstq);
487                 e.p2.getE(mid).pair.pair.bindTo(e.getBindingMatrix(firste), firstq.pair);
488             }
489             /*
490             first.setConstraint(firste.getConstraint());
491             firstq.setConstraint(firste.getConstraint());
492             */
493             return null;
494         }
495
496         public boolean destroyed = false;
497         public void destroy() {
498             if (destroyed) return;
499             destroyed = true;
500             pair.destroyed = true;
501
502             if (t != null) t.destroy();
503             t = null;
504
505             if (pair.t != null) pair.t.destroy();
506             pair.t = null;
507
508             if (next.t != null) next.t.destroy();
509             if (prev.t != null) prev.t.destroy();
510             next.t = null;
511             prev.t = null;
512
513             if (pair.next.t != null) pair.next.t.destroy();
514             if (pair.prev.t != null) pair.next.t.destroy();
515             pair.next.t = null;
516             pair.prev.t = null;
517
518             pair.prev.next = next;
519             next.prev = pair.prev;
520             prev.next = pair.next;
521             pair.next = prev;
522             if (p1.e == this) p1.e = prev.next;
523             if (pair.p1.e == pair) pair.p1.e = pair.prev.next;
524         }
525
526         private void sync() {
527             this.prev.next = this;
528             this.next.prev = this;
529             this.pair.pair = this;
530             if (this.next.p1 != p2) throw new Error();
531             if (this.prev.p2 != p1) throw new Error();
532             if (this.p1.e == null) this.p1.e = this;
533             if (!added) added = true;
534         }
535         private boolean added = false;
536
537         public T makeT(int colorclass) { return t==null ? (t = new T(this, colorclass)) : t; }
538
539         public double dihedralAngle() {
540             Vec v1 = t.norm().times(-1);
541             Vec v2 = pair.t.norm().times(-1);
542             double prod = v1.norm().dot(v2.norm());
543             prod = Math.min(1,prod);
544             prod = Math.max(-1,prod);
545             double ret = Math.acos(prod);
546             if (Double.isNaN(ret)) throw new Error("nan! " + prod);
547             return ret;
548         }
549
550         /** angle between this half-edge and the next */
551         public double angle() {
552             Vec v1 = next.p2.p.minus(p2.p);
553             Vec v2 = this.p1.p.minus(p2.p);
554             return Math.acos(v1.norm().dot(v2.norm()));
555         }
556
557         public void makeAdjacent(E e) {
558             if (this.next == e) return;
559             if (p2 != e.p1) throw new Error("cannot make adjacent -- no shared vertex");
560             if (t != null || e.t != null) throw new Error("cannot make adjacent -- edges not both free ");
561
562             E freeIncident = p2.getFreeIncident(e, this);
563
564             e.prev.next = freeIncident.next;
565             freeIncident.next.prev = e.prev;
566
567             freeIncident.next = this.next;
568             this.next.prev = freeIncident;
569             
570             this.next = e;
571             e.prev = this;
572
573             sync();
574             freeIncident.sync();
575         }
576
577         /** creates an isolated edge out in the middle of space */
578         public E(Point p1, Point p2) {
579             if (vertices.get(p1) != null) throw new Error();
580             if (vertices.get(p2) != null) throw new Error();
581             this.p1 = new Vertex(p1);
582             this.p2 = new Vertex(p2);
583             this.prev = this.next = this.pair = new E(this, this, this);
584             this.p1.e = this;
585             this.p2.e = this.pair;
586             sync();
587         }
588
589         /** adds a new half-edge from prev.p2 to p2 */
590         public E(E prev, Point p) {
591             Vertex p2;
592             p2 = vertices.get(p);
593             if (p2 == null) p2 = new Vertex(p);
594             this.p1 = prev.p2;
595             this.p2 = p2;
596             this.prev = prev;
597             if (p2.getE(p1) != null) throw new Error();
598             if (p2.e==null) {
599                 this.next = this.pair = new E(this, this, prev.next);
600             } else {
601                 E q = p2.getFreeIncident();
602                 this.next = q.next;
603                 this.next.prev = this;
604                 E z = prev.next;
605                 this.prev.next = this;
606                 this.pair = new E(q, this, z);
607             }
608             if (p2.e==null) p2.e = this.pair;
609             sync();
610         }
611
612         /** adds a new half-edge to the mesh with a given predecessor, successor, and pair */
613         public E(E prev, E pair, E next) {
614             this.p1 = prev.p2;
615             this.p2 = next.p1;
616             this.prev = prev;
617             this.next = next;
618             this.pair = pair;
619             sync();
620         }
621         public Point midpoint() { return new Point((p1.p.x+p2.p.x)/2, (p1.p.y+p2.p.y)/2, (p1.p.z+p2.p.z)/2); }
622         public boolean has(Vertex v) { return v==p1 || v==p2; }
623         public float length() { return p1.p.minus(p2.p).mag(); }
624         public String toString() { return p1+"->"+p2; }
625
626     }
627
628     public E makeE(Point p1, Point p2) {
629         Vertex v1 = vertices.get(p1);
630         Vertex v2 = vertices.get(p2);
631         if (v1 != null && v2 != null) {
632             E e = v1.getE(v2);
633             if (e != null) return e;
634             e = v2.getE(v1);
635             if (e != null) return e;
636         }
637         if (v1 != null) return new E(v1.getFreeIncident(), p2);
638         if (v2 != null) return new E(v2.getFreeIncident(), p1).pair;
639         return new E(p1, p2);
640     }
641     public boolean coalesce = false;
642     private static float round(float f) {
643         return Math.round(f*1000)/1000f;
644     }
645     public T newT(Point p1, Point p2, Point p3, Vec norm, int colorclass) {
646         if (coalesce) {
647
648             for(Vertex v : vertices) { if (p1.distance(v.p) < EPSILON) { p1 = v.p; break; } }
649             for(Vertex v : vertices) { if (p2.distance(v.p) < EPSILON) { p2 = v.p; break; } }
650             for(Vertex v : vertices) { if (p3.distance(v.p) < EPSILON) { p3 = v.p; break; } }
651             /*
652             p1 = new Point(round(p1.x), round(p1.y), round(p1.z));
653             p2 = new Point(round(p2.x), round(p2.y), round(p2.z));
654             p3 = new Point(round(p3.x), round(p3.y), round(p3.z));
655             */
656         }
657         if (norm != null) {
658             Vec norm2 = p3.minus(p1).cross(p2.minus(p1));
659             float dot = norm.dot(norm2);
660             //if (Math.abs(dot) < EPointSILON) throw new Error("dot products within evertsilon of each other: "+norm+" "+norm2);
661             if (dot < 0) { Point p = p1; p1=p2; p2 = p; }
662         }
663         E e12 = makeE(p1, p2);
664         E e23 = makeE(p2, p3);
665         E e31 = makeE(p3, p1);
666         while(e12.next != e23 || e23.next != e31 || e31.next != e12) {
667             e12.makeAdjacent(e23);
668             e23.makeAdjacent(e31);
669             e31.makeAdjacent(e12);
670         }
671         T ret = e12.makeT(colorclass);
672         if (e12.t == null) throw new Error();
673         if (e23.t == null) throw new Error();
674         if (e31.t == null) throw new Error();
675         return ret;
676     }
677
678     private int max_serial = 0;
679     /** [UNIQUE] a triangle (face) */
680     public final class T extends Triangle {
681         public final E e1;
682         public final int color;
683         public final int colorclass;
684
685         public final int serial = max_serial++;
686         public boolean occluded;
687
688         T(E e1, int colorclass) {
689             this.e1 = e1;
690             E e2 = e1.next;
691             E e3 = e2.next;
692             if (e1==e2 || e1==e3) throw new Error();
693             if (e3.next!=e1) throw new Error();
694             if (e1.t!=null || e2.t!=null || e3.t!=null) throw new Error("non-manifold surface or disagreeing normals");
695             e1.t = this;
696             e1.next.t = this;
697             e1.next.next.t = this;
698
699             // FIXME: check for sealed/watertight surface once construction is complete (and infer normal(s)?)
700
701             int color = Math.abs(random.nextInt());
702             while(true) {
703                 color = color % 4;
704                 if (e1().pair.t != null && color == e1().pair.t.color) { color++; continue; }
705                 if (e2().pair.t != null && color == e2().pair.t.color) { color++; continue; }
706                 if (e3().pair.t != null && color == e3().pair.t.color) { color++; continue; }
707                 break;
708             }
709             this.color = color;
710             this.colorclass = colorclass;
711             triangles.add(this);
712         }
713         public E e1() { return e1; }
714         public E e2() { return e1.next; }
715         public E e3() { return e1.prev; }
716         public Vertex v1() { return e1.p1; }
717         public Vertex v2() { return e1.p2; }
718         public Vertex v3() { return e1.next.p2; }
719         public Point p1() { return e1.p1.p; }
720         public Point p2() { return e1.p2.p; }
721         public Point p3() { return e1.next.p2.p; }
722         public boolean hasE(E e) { return e1==e || e1.next==e || e1.prev==e; }
723         public boolean has(Vertex v) { return v1()==v || v2()==v || v3()==v; }
724
725         public void removeFromRTree() { triangles.remove(this); }
726         public void addToRTree() { triangles.insert(this); }
727         public void destroy() { triangles.remove(this); }
728         public void reinsert() { triangles.remove(this); triangles.add(this); }
729
730         public boolean shouldBeDrawn() {
731
732             if (e1().bindingGroupSize() <= 1) return false;
733             if (e2().bindingGroupSize() <= 1) return false;
734             if (e3().bindingGroupSize() <= 1) return false;
735
736             return true;
737         }
738
739         public void glTriangle(GL gl, Matrix m) {
740             gl.glPushName(serial);
741             gl.glBegin(GL.GL_TRIANGLES);
742             glVertices(gl, m);
743             gl.glEnd();
744             gl.glPopName();
745         }
746
747         /** issue gl.glVertex() for each of the triangle's points */
748         public void glVertices(GL gl, Matrix m) {
749             if (!shouldBeDrawn()) return;
750             super.glVertices(gl, m);
751         }
752     }
753 }