31e0590d6d3915b0b4c5d4b1a35a16af4538bf42
[anneal.git] / src / edu / berkeley / qfat / Mesh.java
1 package edu.berkeley.qfat;
2 import java.awt.*;
3 import java.util.*;
4 import java.io.*;
5 import java.awt.event.*;
6 import javax.swing.*;
7 import javax.media.opengl.*;
8 import javax.media.opengl.glu.*;
9 import edu.berkeley.qfat.geom.*;
10 import edu.wlu.cs.levy.CG.KDTree;
11 import edu.berkeley.qfat.bind.*;
12 import edu.berkeley.qfat.geom.Point;
13 import com.infomatiq.jsi.IntProcedure;
14
15 // EDGES RUN COUNTER-CLOCKWISE
16
17 public class Mesh implements Iterable<Mesh.T> {
18
19     public static final float EPSILON = (float)0.0001;
20     public static final Random random = new Random();
21
22     private RTree<T>         triangles = new RTree<T>();
23     private PointSet<Vertex> vertices  = new PointSet<Vertex>();
24
25     public boolean option_wireframe    = false;
26     public boolean option_errorNormals = false;
27     public boolean option_selectable   = true;
28
29     public void render(GL gl, Matrix m) { render(gl, m, false); }
30     public void render(GL gl, Matrix m, boolean noColor) {
31         if (option_wireframe) {
32             gl.glDisable(GL.GL_LIGHTING);
33             gl.glBegin(GL.GL_LINES);
34             if (!noColor) gl.glColor3f(1, 1, 1);
35             for (T t : this) {
36                 // fixme used to be .goodp
37                 m.times(t.e1().v1.p).glVertex(gl);
38                 m.times(t.e1().v2.p).glVertex(gl);
39                 m.times(t.e2().v1.p).glVertex(gl);
40                 m.times(t.e2().v2.p).glVertex(gl);
41                 m.times(t.e3().v1.p).glVertex(gl);
42                 m.times(t.e3().v2.p).glVertex(gl);
43             }
44             gl.glEnd();
45             gl.glEnable(GL.GL_LIGHTING);
46             return;
47         }
48         for(T t : this) {
49             if (!noColor)
50                 gl.glColor4f((float)(0.25+(0.05*t.color)),
51                              (float)(0.25+(0.05*t.color)),
52                              (float)(0.75+(0.05*t.color)),
53                              (float)0.3); 
54             /*
55             if (t.red) {
56             gl.glColor4f((float)(0.75+(0.05*t.color)),
57                          (float)(0.25+(0.05*t.color)),
58                          (float)(0.25+(0.05*t.color)),
59                          (float)0.3); 
60             }
61             */
62             t.glTriangle(gl, m);
63         }
64         if (option_errorNormals)
65             for(T t : this)
66                 for(Mesh.Vertex p : new Mesh.Vertex[] { t.v1(), t.v2(), t.v3() }) {
67                     if (p.ok) {
68                         gl.glBegin(GL.GL_LINES);
69                         if (!noColor)
70                             gl.glColor3f(1, 1, 1);
71                         p.p.glVertex(gl);
72                         p.p.plus(p.norm().times((float)p.error()*10)).glVertex(gl);
73                         gl.glEnd();
74                     }
75                 }
76     }
77
78     public boolean immutableVertices;
79     public Mesh    error_against      = null;
80     public double  error              = 0;
81
82     public Mesh(boolean immutableVertices) { this.immutableVertices = immutableVertices; }
83
84     public void makeVerticesImmutable() { this.immutableVertices = true; }
85     public float error() { return (float)error; }
86
87     public int size() { return vertices.size(); }
88     public Iterable<Vertex> vertices() { return vertices; }
89     public Iterator<T> iterator() { return triangles.iterator(); }
90
91     public void rebindPoints() {
92         // unbind all points
93         for(Mesh.T t : this) {
94             t.v1().unbind();
95             t.v2().unbind();
96             t.v3().unbind();
97         }
98         // ask edges to re-implement their bindings
99         for(Mesh.T t : this) {
100             t.e1().dobind();
101             t.e2().dobind();
102             t.e3().dobind();
103         }
104         System.out.println("rebound!");
105     }
106
107     public void transform(Matrix m) {
108         ArrayList<Vertex> set = new ArrayList<Vertex>();
109         for(Vertex v : vertices) set.add(v);
110         for(Vertex v : set) v.transform(m.times(v.p), true, null);
111         for(Vertex v : set) v.goodp = v.p;
112     }
113
114     public void rebuild() { /*vertices.rebuild();*/ }
115     public Vec diagonal() { return vertices.diagonal(); }
116     public Point centroid() { return vertices.centroid(); }
117     public Vertex nearest(Point p) { return vertices.nearest(p); }
118
119     /** compute the volume of the mesh */
120     public float volume() {
121         double total = 0;
122         for(T t : this) {
123             double area = t.area();
124             Vec origin_to_centroid = new Vec(new Point(0, 0, 0), t.centroid());
125             boolean facingAway = t.norm().dot(origin_to_centroid) > 0;
126             double height = Math.abs(t.norm().dot(origin_to_centroid));
127             total += ((facingAway ? 1 : -1) * area * height) / 3.0;
128         }
129         return (float)total;
130     }
131
132
133     public void subdivide() {
134         for (Vertex v : vertices()) v.original = true;
135         HashSet<E> edges = new HashSet<E>();
136         HashSet<E> flip = new HashSet<E>();
137         HashSet<T> tris = new HashSet<T>();
138         int count = 0;
139         for (T t : this) {
140             tris.add(t);
141             edges.add(t.e1());
142             edges.add(t.e2());
143             edges.add(t.e3());
144             count++;
145         }
146         System.out.println("triangles="+count);
147         count = 0;
148         for(E e : edges) {
149             if (e.destroyed || e.shattered) continue;
150             e.shatter().edge = true;
151             for(E ex : (Iterable<E>)e.getBoundPeers()) {
152                 Vertex m = nearest(ex.midpoint());
153                 m.edge = true;
154                 E e3 = ex.v1.getE(m).next;
155                 if (e3.v2.original)
156                     flip.add(e3);
157             }
158         }
159
160         int i=0;
161
162         for(E e : flip) {
163             e.flip();
164             System.out.println("flip!");
165             i++;
166         }
167
168         System.out.println("count="+count);
169
170         rebindPoints();
171         HashSet<Vertex> verts = new HashSet<Vertex>();
172         for(Vertex v : vertices()) verts.add(v);
173         for (Vertex v : verts)
174             v.clearWish();
175         for (Vertex v : verts) {
176             if (v.edge) {
177                 for(E e = v.e; e!=null; e=e.pair.next==v.e?null:e.pair.next) {
178                     if (e.v2.original) {
179                         v.wish(e.v2);
180                         v.wish(e.v2);
181                         v.wish(e.v2);
182                         v.wish(e.v2);
183                     }
184                 }
185                 for(E e = v.e; e!=null; e=e.pair.next==v.e?null:e.pair.next) {
186                     for(E e2 = e.v2.e; e2!=null; e2=e2.pair.next==e.v2.e?null:e2.pair.next) {
187                         if (e2.v2.original) {
188                             v.wish(e.v2);
189                         }
190                     }
191                 }
192             }
193         }
194         for (Vertex v : verts)
195             v.grantWish();
196         System.out.println("-------------------------------------------------------------------");
197         /*
198         for (Vertex v : verts) {
199             if (v.original) {
200                 int n=0;
201                 for(E e = v.e; e!=null; e=e.pair.next==v.e?null:e.pair.next) {
202                     n++;
203                     v.wish(e.midpoint());
204                     v.wish(e.midpoint());
205                     v.wish(e.next.pair.t.centroid());
206                 }
207                 v.avgWish();
208                 v.wishes = 3;
209                 for(int j=0; j<n-3; j++)
210                     v.wish(v.getPoint());
211             }
212         }
213         for (Vertex v : verts)
214             v.avgWish();
215         for (Vertex v : verts)
216             v.grantWish();
217         */
218     }
219
220     // Vertexices //////////////////////////////////////////////////////////////////////////////
221
222
223     /** a vertex in the mesh */
224     public final class Vertex extends HasQuadric implements Visitor, HasPoint {
225         public void bindTo(Matrix bindingMatrix, HasBindingGroup other) {
226             bindTo(bindingMatrix, other, EPSILON);
227         }
228         public float getMaxX() { return getPoint().getMaxX(); }
229         public float getMinX() { return getPoint().getMinX(); }
230         public float getMaxY() { return getPoint().getMaxY(); }
231         public float getMinY() { return getPoint().getMinY(); }
232         public float getMaxZ() { return getPoint().getMaxZ(); }
233         public float getMinZ() { return getPoint().getMinZ(); }
234
235         public Point p, goodp;
236         public Point oldp;
237         E e;                // some edge *leaving* this point
238
239         public boolean original = false;
240         public boolean edge = false;
241         public boolean face = false;
242
243         private int wishes = 0;
244         private Point wish = Point.ZERO;
245         public void clearWish() { wishes = 0; wish = Point.ZERO; }
246         public void wish(HasPoint hp) {
247             Point p = hp.getPoint();
248             wishes++;
249             wish = new Point(wish.x+p.x, wish.y+p.y, wish.z+p.z);
250         }
251         public void grantWish() {
252             for(Vertex v : (Iterable<Vertex>)getBoundPeers()) {
253                 if (v==this) continue;
254                 if (v.wishes==0) continue;
255                 Point p = this.getBindingMatrix(v).times(v.wish.minus(Point.ZERO).div(v.wishes).plus(Point.ZERO));
256                 wish = p.minus(Point.ZERO).times(v.wishes).plus(wish);
257                 wishes += v.wishes;
258                 v.clearWish();
259             }
260             if (wishes==0) return;
261             Vec d = wish.minus(Point.ZERO).div(wishes).plus(Point.ZERO).minus(getPoint());
262             move(d, false);
263             clearWish();
264         }
265         public void avgWish() {
266             if (wishes==0) return;
267             wish = wish.minus(Point.ZERO).div(wishes).plus(Point.ZERO);
268             wishes = 1;
269         }
270
271         private boolean illegal = false;
272
273         public boolean visible = false;
274
275         public Point getPoint() { return p; }
276         public float error() { return olderror; }
277
278         private Vertex(Point p) {
279             this.p = p;
280             this.goodp = p;
281             this.oldp = p;
282             if (vertices.get(p) != null) throw new Error();
283             vertices.add(this);
284         }
285
286         public void reinsert() {
287             vertices.remove(this);
288             vertices.add(this);
289             for(E e = this.e; e!=null; e=e.pair.next==this.e?null:e.pair.next) e.t.reinsert();
290         }
291
292         // the average of all adjacent points
293         public Point recenter() {
294             int count = 0;
295             Vec vec = Vec.ZERO;
296             for(E e = this.e; e!=null; e=e.pair.next==this.e?null:e.pair.next) {
297                 vec = vec.plus(e.getOther(this).getPoint().minus(Point.ZERO));
298                 count++;
299             }
300             return Point.ZERO.plus(vec.div(count));
301         }
302
303         public float olderror = 0;
304         public void setError(float nerror) {
305             error -= olderror;
306             olderror = nerror;
307             error += olderror;
308         }
309
310         /*
311         public Vertex hack(GL gl, Point mouse) {
312             double dist = Double.MAX_VALUE;
313             Vertex cur = null;
314             for(E e = this.e; e!=null; e=e.pair.next==this.e?null:e.pair.next) {
315                 Vertex v = e.getOther(this);
316                 double dist2 = v.getPoint().glProject(gl).distance(mouse);
317                 if ((cur==null || dist2 < dist) && v.visible) {
318                     dist = dist2;
319                     cur = v;
320                 }
321             }
322             return cur;
323         }
324         */
325
326         public float averageTriangleArea() {
327             int count = 0;
328             float ret = 0;
329             for(E e = this.e; e!=null; e=e.pair.next==this.e?null:e.pair.next) {
330                 ret += e.t.area();
331                 count++;
332             }
333             return ret/count;
334         }
335         public float averageEdgeLength() {
336             int count = 0;
337             float ret = 0;
338             for(E e = this.e; e!=null; e=e.pair.next==this.e?null:e.pair.next) {
339                 ret += e.length();
340                 count++;
341             }
342             return ret/count;
343         }
344
345         public Matrix _recomputeFundamentalQuadric() {
346             Matrix m = Matrix.ZERO;
347             int count = 0;
348             for(E e = this.e; e!=null; e=e.pair.next==this.e?null:e.pair.next) {
349                 m = m.plus(e.t.norm().fundamentalQuadric(e.t.centroid()));
350                 count++;
351             }
352             if (count > 0) {
353                 m = m.plus(norm().fundamentalQuadric(this.p).times(count));
354                 count *= 2;
355             }
356             return m.times(1/(float)count);
357         }
358
359         public HasQuadric nearest() { return error_against==null ? null : error_against.vertices.nearest(p, this); }
360         public void computeError() {
361             if (error_against==null) return;
362             if (nearest_in_other_mesh == null && nearest()==null) return;
363             float nerror =
364                 nearest_in_other_mesh != null
365                 ? nearest_in_other_mesh.fundamentalQuadric().preAndPostMultiply(p)
366                 : nearest().fundamentalQuadric().preAndPostMultiply(p);
367             if (quadric_count != 0)
368                 nerror = (nerror + quadric.preAndPostMultiply(p))/(quadric_count+1);
369
370             if (!immutableVertices && quadric_count == 0) {
371                 //nerror = Math.max(nerror, 0.4f);
372                 //nerror *= 2;
373             }
374             //System.out.println(nerror);
375             for(E e = this.e; e!=null; e=e.pair.next==this.e?null:e.pair.next) {
376                 double ang = e.dihedralAngle();
377                 if (ang > Math.PI) throw new Error();
378                 if (ang < -Math.PI) throw new Error();
379                 float minangle = (float)(Math.PI * 0.8);
380                 //nerror += ((ang / Math.PI)*(ang/Math.PI)) * e.length() * 0.05;
381
382                 //nerror += (1-e.t.quality())*0.0001;
383                 if (ang > minangle) nerror += (ang - minangle);
384
385                 //System.out.println(((ang / Math.PI)*(ang/Math.PI)) * 0.000001);
386                 /*
387                 if (e.t.aspect() < 0.2) {
388                     nerror += (0.2-e.t.aspect()) * 10;
389                 }
390                 */
391             }
392             if (!immutableVertices) {
393                 Vertex n = (Vertex)nearest();
394                 float d = norm().dot(n.norm());
395                 if (d > 1 || d < -1) throw new Error();
396                 if (d >= 0) {
397                     nerror *= (2.0f - d);
398                 } else {
399                     nerror += 0.0003 * (2.0f + d);
400                     nerror *= (2.0f + d);
401                 }
402             }
403
404             setError(nerror);
405         }
406
407         public boolean move(Vec vv, boolean ignoreProblems) {
408
409             boolean good = true;
410
411             //     t1' = M * t1
412             //     t2' = t2.getMatrix(t1) * t1'
413             //     t2' = t2.getMatrix(t1) * M * t1
414             //     t1 =     t1.getMatrix(t2) * t2
415             // M * t1 = M * t1.getMatrix(t2) * t2
416
417             /*
418             if (bindingGroup!=null && this != bindingGroup.getMaster()) {
419                 Matrix m2 = getBindingMatrix(bindingGroup.getMaster());
420                 Vec v2 = m2.times(vv.plus(getPoint())).minus(m2.times(getPoint()));
421                 return ((Vertex)bindingGroup.getMaster()).move(v2, ignoreProblems);
422             }
423             */
424
425             Point op = this.p;
426             Point pt = vv.plus(getPoint());
427             Point pp = pt;
428             pt = getBindingConstraint().getProjection(pp);
429             if (pt==null) {
430                 System.out.println("constraint violation: " + getBindingConstraint());
431                 return false;
432             }
433             System.out.println(pt.minus(pp).mag() + " " + getBindingConstraint());
434
435             for(Vertex v : (Iterable<Vertex>)getBoundPeers()) {
436                 Point pt2 = v.getBindingMatrix(this).times(pt);
437                 /*
438                 if (Math.abs( v.p.minus(pt2).mag() / pt.minus(op).mag() ) > 5)
439                     throw new Error(v.p+" "+pt2+"\n"+op+" "+pt+"\n"+v.getBindingMatrix(this));
440                 if (Math.abs( v.p.minus(pt2).mag() / pt.minus(op).mag() ) < 1/5) throw new Error();
441                 */
442                 good &= v.transform(pt2, ignoreProblems, v.getBindingMatrix(this));
443             }
444
445             if (!good && !ignoreProblems) {
446                 for(Vertex v : (Iterable<Vertex>)getBoundPeers()) 
447                     v.transform(v.oldp, true, null);
448             }
449
450             for(Vertex v : (Iterable<Vertex>)getBoundPeers())
451                 v.recomputeFundamentalQuadricIfNeighborChanged();
452             for(Vertex v : (Iterable<Vertex>)getBoundPeers())
453                 v.reComputeErrorAround();
454             ok = true;
455             return good;
456         }
457         public boolean ok = true;
458
459         /** does NOT update bound pairs! */
460         private boolean transform(Point newp, boolean ignoreProblems, Matrix yes) {
461             this.oldp = this.p;
462             if (immutableVertices) throw new Error();
463
464             unApplyQuadricToNeighbor();
465
466             boolean illegalbefore = illegal;
467             illegal = false;
468             /*
469             if (this.p.minus(newp).mag() > 0.1 && !ignoreProblems) {
470                 try {
471                     throw new Exception(""+this.p.minus(newp).mag()+" "+ignoreProblems+" "+yes);
472                 } catch(Exception e) {
473                     e.printStackTrace();
474                 }
475                 illegal = true;
476             }
477             */
478
479             this.p = newp;
480             reinsert();
481             applyQuadricToNeighbor();
482
483             if (!ignoreProblems) {
484                 checkLegality();
485             }
486             for(E e = this.e; e!=null; e=e.pair.next==this.e?null:e.pair.next)
487                 e.v2.quadricStale = true;
488             return !illegal || (illegalbefore && illegal);
489         } 
490
491         public void checkLegality() {
492             /*
493             for(E e = this.e; e!=null; e=e.pair.next==this.e?null:e.pair.next) {
494                 if (Math.abs(e.dihedralAngle()) > (Math.PI * 0.9) ||
495                     Math.abs(e.next.dihedralAngle()) > (Math.PI * 0.9)) illegal = true;
496                 if (e.t.aspect() < 0.2) illegal = true;
497             }
498             */
499             if (!illegal) triangles.range(oldp, this.p, (Visitor<T>)this);
500         }
501
502         public void reComputeErrorAround() {
503             reComputeError();
504             if (nearest_in_other_mesh != null)
505                 nearest_in_other_mesh.reComputeError();
506             for(E e = this.e; e!=null; e=e.pair.next==this.e?null:e.pair.next)
507                 e.v2.reComputeError();
508         }
509
510         public boolean visit(Object o) {
511             if (o instanceof Vertex)
512                 return ((Vertex)o).e != null && ((Vertex)o).norm().dot(Vertex.this.norm()) >= 0;
513             T t = (T)o;
514             if (illegal) return false;
515             for(E e = Vertex.this.e; e!=null; e=e.pair.next==Vertex.this.e?null:e.pair.next) {
516                 if (!t.has(e.v1) && !t.has(e.v2) && e.intersects(t)) { illegal = true; }
517                 if (e.t != null) {
518                     if (!e.t.has(t.e1().v1) && !e.t.has(t.e1().v2) && t.e1().intersects(e.t)) { illegal = true; }
519                     if (!e.t.has(t.e2().v1) && !e.t.has(t.e2().v2) && t.e2().intersects(e.t)) { illegal = true; }
520                     if (!e.t.has(t.e3().v1) && !e.t.has(t.e3().v2) && t.e3().intersects(e.t)) { illegal = true; }
521                 }
522             }
523             return !illegal;
524         }
525
526         public E getEdge() { return e; }
527         public E getFreeIncident() {
528             E ret = getFreeIncident(e, e);
529             if (ret != null) return ret;
530             for(E e = this.e; e!=null; e=e.pair.next==this.e?null:e.pair.next)
531                 System.out.println(e + " " + e.t);
532             throw new Error("unable to find free incident to " + this);
533         }
534
535         public E getFreeIncident(E start, E before) {
536             for(E e = start; e!=null; e=e.pair.next==before?null:e.pair.next)
537                 if (e.pair.v2 == this && e.pair.t == null && e.pair.next.t == null)
538                     return e.pair;
539             return null;
540         }
541
542         public E getE(Point v2) {
543             Vertex v = vertices.get(v2);
544             if (v==null) return null;
545             return getE(v);
546         }
547         public E getE(Vertex v2) {
548             if (this.e!=null && this!=this.e.v1 && this!=this.e.v2) throw new RuntimeException();
549             int i=0;
550             for(E e = this.e; e!=null; e=e.pair.next==this.e?null:e.pair.next) {
551                 if (e.v1 == this && e.v2 == v2) return e;
552                 i++;
553                 e.sanity();
554                 if (e.destroyed) throw new RuntimeException("fark " + i + " " + e.prev + " " + (e.prev.next==e) + " " + e.prev.destroyed);
555             }
556             return null;
557         }
558
559         private void glNormal(GL gl) {
560             Vec norm = norm();
561             gl.glNormal3f(norm.x, norm.y, norm.z);
562         }
563         public Vec norm() {
564             Vec norm = new Vec(0, 0, 0);
565             for(E e = this.e; e!=null; e=e.pair.next==this.e?null:e.pair.next)
566                 if (e.t != null)
567                     norm = norm.plus(e.t.norm().times((float)e.prev.angle()));
568             return norm.norm();
569         }
570
571         public void bindTo(Vertex p) { bindTo(Matrix.ONE, p); }
572     }
573
574
575     /** [UNIQUE] an edge */
576     public final class E extends HasBindingGroup implements Comparable<E> {
577
578         public void bindTo(Matrix bindingMatrix, HasBindingGroup other) {
579             bindTo(bindingMatrix, other, EPSILON);
580         }
581
582         public void sanity() {
583             if (destroyed) return;
584             if (pair!=null && (pair.v1!=v2 || pair.v2!=v1)) throw new RuntimeException();
585             if (next!=null && next.v1!=v2) throw new RuntimeException();
586             if (prev!=null && prev.v2!=v1) throw new RuntimeException();
587         }
588
589         public final Vertex v1, v2;
590         T t;     // triangle to our "left"
591         E prev;  // previous half-edge
592         E next;  // next half-edge
593         E pair;  // partner half-edge
594         boolean shattered = false;
595
596
597         /** the "edge normal" -- average of the normals of the adjacent faces */
598         public Vec norm() {
599             return
600                 (t==null || pair==null || pair.t==null)
601                 ? null
602                 : t.norm().plus(pair.t.norm()).norm();
603         }
604
605         public void glVertices(GL gl) {
606             Point p1 = v1.p;
607             Point p2 = v2.p;
608             if (t != null) {
609                 p1 = p1.plus(t.centroid().minus(p1).times(0.1f));
610                 p2 = p2.plus(t.centroid().minus(p2).times(0.1f));
611                 p1 = p1.plus(norm().times(length() * 0.01f));
612                 p2 = p2.plus(norm().times(length() * 0.01f));
613             }
614             p1.glVertex(gl);
615             p2.glVertex(gl);
616             if (t==null || pair.t==null) return;
617
618             Point atip = p2.minus(Point.ZERO).times(4).plus(p1.minus(Point.ZERO)).div(5).plus(Point.ZERO);
619             atip = (t.norm().plus(pair.t.norm())).norm().times(getSegment().length() / 5).plus(atip);
620             p2.glVertex(gl);
621             atip.glVertex(gl);
622         }
623
624         public boolean intersects(T t) { return t.intersects(v1.p, v2.p); }
625
626         public Segment getSegment() { return new Segment(v1.getPoint(), v2.getPoint()); }
627
628         public void flip() {
629             // FIXME: coplanarity check needed
630             if (destroyed) return;
631             for (E e : (Iterable<E>)getBoundPeers()) {
632                 if (!e.pair.isBoundTo(pair)) throw new RuntimeException("cannot flip!");
633             }
634             Vertex v1 = t.getOtherVertex(this);
635             Vertex v2 = pair.t.getOtherVertex(pair);
636             destroy();
637             pair.destroy();
638             T t1 = newT(v1, v2, this.v2);
639             T t2 = newT(v2, v1, this.v1);
640             t1.e1().sanity();
641             t1.e2().sanity();
642             t1.e3().sanity();
643             t2.e1().sanity();
644             t2.e2().sanity();
645             t2.e3().sanity();
646
647             for (E e : (Iterable<E>)getBoundPeers()) {
648                 if (e==this) continue;
649                 if (e.destroyed) continue;
650                 Vertex v1e = e.t.getOtherVertex(e);
651                 Vertex v2e = e.pair.t.getOtherVertex(e.pair);
652                 e.destroy();
653                 e.pair.destroy();
654                 if (v1e.getE(v2e)!=null) throw new RuntimeException();
655                 newT(v1e, v2e, e.v2).red = true;
656                 newT(v2e, v1e, e.v1).red = true;
657                 v2e.getE(v1e).bindTo(e.getBindingMatrix(this), v1.getE(v2));
658                 v1e.getE(v2e).bindTo(e.pair.getBindingMatrix(this.pair), v2.getE(v1));
659             }
660
661         }
662
663         public void bindingGroupChanged() {
664             HashSet<E> nbg = new HashSet<E>();
665             for(E eother : (Iterable<E>)getBoundPeers()) nbg.add(eother);
666             for(E eother : nbg) {
667                 if (next==null || prev==null) continue;
668                 if (eother.next==null || eother.prev==null) continue;
669
670                 Matrix m = getBindingMatrix(eother);
671                 if (next.isBoundTo(eother.pair.prev.pair) &&
672                     !prev.isBoundTo(eother.pair.next.pair) &&
673                     m.equalsModuloEpsilon(next.getBindingMatrix(eother.pair.prev.pair), EPSILON))
674                     prev.bindEdge(next.getBindingMatrix(eother.pair.prev.pair), eother.pair.next.pair);
675                 if (!next.isBoundTo(eother.pair.prev.pair) &&
676                     prev.isBoundTo(eother.pair.next.pair)  &&
677                     m.equalsModuloEpsilon(prev.getBindingMatrix(eother.pair.next.pair), EPSILON))
678                     next.bindEdge(prev.getBindingMatrix(eother.pair.next.pair), eother.pair.prev.pair);
679
680                 //if (next.isBoundTo(eother.prev) && !prev.isBoundTo(eother.next))
681                 //prev.bindTo(next.getBindingMatrix(eother.prev), eother.next);
682                 //if (!next.isBoundTo(eother.prev) && prev.isBoundTo(eother.next))
683                 //next.bindTo(prev.getBindingMatrix(eother.next), eother.prev);
684
685                 if (next.isBoundTo(eother.next) &&
686                     !prev.isBoundTo(eother.prev) &&
687                     m.equalsModuloEpsilon(next.getBindingMatrix(eother.next), EPSILON))
688                     prev.bindEdge(next.getBindingMatrix(eother.next), eother.prev);
689                 if (!next.isBoundTo(eother.next) &&
690                     prev.isBoundTo(eother.prev) &&
691                     m.equalsModuloEpsilon(prev.getBindingMatrix(eother.prev), EPSILON))
692                     next.bindEdge(prev.getBindingMatrix(eother.prev), eother.next);
693
694             }
695
696         }
697
698         public float stretchRatio() {
699             Vertex nearest = error_against.nearest(midpoint());
700             float nearest_distance = midpoint().distance(nearest.p);
701             float other_distance =
702                 (v1.p.distance(error_against.nearest(v1.p).p)+
703                  v2.p.distance(error_against.nearest(v2.p).p))/2;
704             return nearest_distance/other_distance;
705         }
706         public float comparator() {
707             return length();
708         }
709         public int compareTo(E e) {
710             return e.comparator() > comparator() ? 1 : -1;
711         }
712         public void bindEdge(Matrix m, E e) {
713             bindEdge(e, m);
714         }
715         public void bindEdge(E e, Matrix m) {
716             /*
717             for(E e_ : (Iterable<E>)e.getBoundPeers()) {
718                 if (e.v1.getPoint().distance((e.getBindingMatrix(e_).times(e_.v1.getPoint()))) > 0.01f)
719                     throw new RuntimeException("blah! " + e.v1.getPoint() + " " + e.getBindingMatrix(e_).times(e_.v1.getPoint()));
720                 if (e.v2.getPoint().distance((e.getBindingMatrix(e_).times(e_.v2.getPoint()))) > 0.01f)
721                     throw new RuntimeException("blah! " + e.v2.getPoint() + " " + e.getBindingMatrix(e_).times(e_.v2.getPoint()));
722                 if (v1.getPoint().distance(m.times(e.getBindingMatrix(e_).times(e_.v1.getPoint()))) > 0.01f)
723                     throw new RuntimeException("blah! " + v1.getPoint() + " " + m.times(e_.v1.getPoint()));
724                 if (v2.getPoint().distance(m.times(e.getBindingMatrix(e_).times(e_.v2.getPoint()))) > 0.01f)
725                     throw new RuntimeException("blah! " + v2.getPoint() + " " + m.times(e_.v2.getPoint()));
726             }
727             */
728             this.bindTo(m, e,      EPSILON);
729             this.pair.bindTo(m, e.pair, EPSILON);
730         }
731         
732         public void dobind() {
733             for(E e : (Iterable<E>)getBoundPeers()) {
734                 if (e==this) continue;
735                 v1.bindTo(getBindingMatrix(e), e.v1);
736                 v2.bindTo(getBindingMatrix(e), e.v2);
737             }
738         }
739
740         public Vertex shatter() {
741             if (shattered || destroyed) return nearest(midpoint());
742             shattered = true;
743             E first = null;
744             E firste = null;
745             E firstx = null;
746             E firstq = null;
747             for(E e : (Iterable<E>)getBoundPeers()) {
748                 E enext = e.next;
749                 E eprev = e.prev;
750                 E pnext = e.pair.next;
751                 E pprev = e.pair.prev;
752                 Point mid = e.midpoint();
753                 Vertex r = e.next.v2;
754                 Vertex l = e.pair.next.v2;
755                 if (!e.destroyed) {
756                     e.destroy();
757                     e.pair.destroy();
758                     newT(r.p, e.v1.p, mid,    null, 0);
759                     newT(r.p, mid,    e.v2.p, null, 0);
760                     newT(l.p, mid,    e.v1.p, null, 0);
761                     newT(l.p, e.v2.p, mid,    null, 0);
762                 }
763             }
764             for(E e : (Iterable<E>)getBoundPeers()) {
765                 Point mid = e.midpoint();
766                 if (first==null) {
767                     first = e.v1.getE(mid);
768                     firste = e;
769                     firstx = e.pair;
770                     firstq = e.v2.getE(mid).pair;
771                     continue;
772                 }
773                 e.v1.getE(mid).          bindTo(e.getBindingMatrix(firste), first);
774                 e.v1.getE(mid).pair.     bindTo(e.getBindingMatrix(firste), first.pair);
775                 e.v2.getE(mid).pair.     bindTo(e.getBindingMatrix(firste), firstq);
776                 e.v2.getE(mid).pair.pair.bindTo(e.getBindingMatrix(firste), firstq.pair);
777             }
778             /*
779             first.setConstraint(firste.getAffineConstraint());
780             firstq.setConstraint(firste.getAffineConstraint());
781             */
782             return nearest(midpoint());
783         }
784
785         public boolean destroyed = false;
786         public void destroy() {
787             if (destroyed) return;
788             destroyed = true;
789             pair.destroyed = true;
790
791             if (t != null) t.destroy();
792             t = null;
793
794             if (pair.t != null) pair.t.destroy();
795             pair.t = null;
796
797             if (next.t != null) next.t.destroy();
798             if (prev.t != null) prev.t.destroy();
799             next.t = null;
800             prev.t = null;
801
802             if (pair.next.t != null) pair.next.t.destroy();
803             if (pair.prev.t != null) pair.next.t.destroy();
804             pair.next.t = null;
805             pair.prev.t = null;
806
807             pair.prev.next = next;
808             next.prev = pair.prev;
809             prev.next = pair.next;
810             pair.next.prev = prev;
811
812             if (v1.e == this) v1.e = pair.next;
813             if (pair.v1.e == pair) pair.v1.e = next;
814
815             if (v2.e == this) throw new RuntimeException();
816             if (pair.v2.e == pair) throw new RuntimeException();
817
818             /*
819             next = pair;
820             prev = pair;
821             pair.next = this;
822             pair.prev = this;
823             */
824
825             /*
826             pair.prev = null;
827             pair.next = null;
828             next = null;
829             prev = null;
830             */
831
832             /*
833             sanity();
834             next.sanity();
835             prev.sanity();
836             pair.sanity();
837             */
838         }
839
840         private void sync() {
841             this.prev.next = this;
842             this.next.prev = this;
843             this.pair.pair = this;
844             if (this.next.v1 != v2) throw new Error();
845             if (this.prev.v2 != v1) throw new Error();
846             if (this.v1.e == null) this.v1.e = this;
847             if (!added) added = true;
848         }
849         private boolean added = false;
850
851         public T makeT(int colorclass) { return t==null ? (t = new T(this, colorclass)) : t; }
852
853         public double dihedralAngle() {
854             Vec v1 = t.norm().times(-1);
855             Vec v2 = pair.t.norm().times(-1);
856             double prod = v1.norm().dot(v2.norm());
857             prod = Math.min(1,prod);
858             prod = Math.max(-1,prod);
859             double ret = Math.acos(prod);
860             if (Double.isNaN(ret)) throw new Error("nan! " + prod);
861             return ret;
862         }
863
864         /** angle between this half-edge and the next */
865         public double angle() {
866             Vec v1 = next.v2.p.minus(this.v2.p);
867             Vec v2 = this.v1.p.minus(this.v2.p);
868             return Math.acos(v1.norm().dot(v2.norm()));
869         }
870
871         public Vertex getOther(Vertex v) {
872             if (this.v1 == v) return v2;
873             if (this.v2 == v) return v1;
874             throw new Error();
875         }
876
877         public void makeAdjacent(E e) {
878             if (this.next == e) return;
879             if (v2 != e.v1) throw new Error("cannot make adjacent -- no shared vertex: " + this + " " + e);
880             if (t != null || e.t != null) throw new Error("cannot make adjacent -- edges not both free " + t + " " + e.t);
881
882             E freeIncident = v2.getFreeIncident(e, this);
883
884             e.prev.next = freeIncident.next;
885             freeIncident.next.prev = e.prev;
886
887             freeIncident.next = this.next;
888             this.next.prev = freeIncident;
889             
890             this.next = e;
891             e.prev = this;
892
893             sync();
894             freeIncident.sync();
895         }
896
897         /** creates an isolated edge out in the middle of space */
898         public E(Point v1, Point v2) {
899             if (vertices.get(v1) != null) throw new Error();
900             if (vertices.get(v2) != null) throw new Error();
901             this.v1 = new Vertex(v1);
902             this.v2 = new Vertex(v2);
903             this.prev = this.next = this.pair = new E(this, this, this);
904             this.v1.e = this;
905             this.v2.e = this.pair;
906             sync();
907         }
908
909         /** adds a new half-edge from prev.v2 to v2 */
910         public E(E prev, Point p) {
911             Vertex v2;
912             v2 = vertices.get(p);
913             if (v2 == null) v2 = new Vertex(p);
914             this.v1 = prev.v2;
915             this.v2 = v2;
916             this.prev = prev;
917             if (prev.destroyed) throw new RuntimeException();
918             if (v2.getE(v1) != null) throw new Error();
919             if (v2.e==null) {
920                 this.next = this.pair = new E(this, this, prev.next);
921             } else {
922                 E q = v2.getFreeIncident();
923                 this.next = q.next;
924                 this.next.prev = this;
925                 E z = prev.next;
926                 this.prev.next = this;
927                 this.pair = new E(q, this, z);
928             }
929             if (v2.e==null) v2.e = this.pair;
930             sync();
931         }
932
933         /** adds a new half-edge to the mesh with a given predecessor, successor, and pair */
934         public E(E prev, E pair, E next) {
935             this.v1 = prev.v2;
936             this.v2 = next.v1;
937             if (prev.destroyed) throw new RuntimeException();
938             this.prev = prev;
939             this.next = next;
940             this.pair = pair;
941             sync();
942         }
943         public Point midpoint() { return new Point((v1.p.x+v2.p.x)/2, (v1.p.y+v2.p.y)/2, (v1.p.z+v2.p.z)/2); }
944         public boolean has(Vertex v) { return v==v1 || v==v2; }
945         public float length() { return v1.p.minus(v2.p).mag(); }
946         public String toString() { return v1+"->"+v2; }
947
948     }
949
950     public E makeE(Point p1, Point p2) {
951         Vertex v1 = vertices.get(p1);
952         Vertex v2 = vertices.get(p2);
953         if (v1 != null && v2 != null) {
954             E e = v1.getE(v2);
955             if (e != null) return e;
956             e = v2.getE(v1);
957             if (e != null) return e;
958         }
959         if (v1 != null) return new E(v1.getFreeIncident(), p2);
960         if (v2 != null) return new E(v2.getFreeIncident(), p1).pair;
961         return new E(p1, p2);
962     }
963     public boolean coalesce = false;
964     private static float round(float f) {
965         return Math.round(f*1000)/1000f;
966     }
967     public T newT(HasPoint v1, HasPoint v2, HasPoint v3) {
968         return newT(v1.getPoint(), v2.getPoint(), v3.getPoint(), null, 0);
969     }
970     public T newT(Point v1, Point v2, Point v3, Vec norm, int colorclass) {
971         if (coalesce) {
972
973             for(Vertex v : vertices) { if (v1.distance(v.p) < EPSILON) { v1 = v.p; break; } }
974             for(Vertex v : vertices) { if (v2.distance(v.p) < EPSILON) { v2 = v.p; break; } }
975             for(Vertex v : vertices) { if (v3.distance(v.p) < EPSILON) { v3 = v.p; break; } }
976             /*
977             v1 = new Point(round(v1.x), round(v1.y), round(v1.z));
978             v2 = new Point(round(v2.x), round(v2.y), round(v2.z));
979             v3 = new Point(round(v3.x), round(v3.y), round(v3.z));
980             */
981         }
982         if (norm != null) {
983             Vec norm2 = v3.minus(v1).cross(v2.minus(v1));
984             float dot = norm.dot(norm2);
985             //if (Math.abs(dot) < EPointSILON) throw new Error("dot products within evertsilon of each other: "+norm+" "+norm2);
986             if (dot < 0) { Point p = v1; v1=v2; v2 = p; }
987         }
988         E e12 = makeE(v1, v2);
989         E e23 = makeE(v2, v3);
990         E e31 = makeE(v3, v1);
991         while(e12.next != e23 || e23.next != e31 || e31.next != e12) {
992             e12.makeAdjacent(e23);
993             e23.makeAdjacent(e31);
994             e31.makeAdjacent(e12);
995         }
996         T ret = e12.makeT(colorclass);
997         if (e12.t == null) throw new Error();
998         if (e23.t == null) throw new Error();
999         if (e31.t == null) throw new Error();
1000         return ret;
1001     }
1002
1003     private int max_serial = 0;
1004     /** [UNIQUE] a triangle (face) */
1005     public final class T extends Triangle {
1006         public final E e1;
1007         public final int color;
1008         public final int colorclass;
1009
1010         public boolean red = false;
1011         public boolean old = false;
1012
1013         public final int serial = max_serial++;
1014         public boolean occluded;
1015
1016         public Point shatter() {
1017             if (destroyed) return null;
1018             E e = e1();
1019             
1020             HashSet<E> forward = new HashSet<E>();
1021             HashSet<E> backward = new HashSet<E>();
1022             HashSet<E> both = new HashSet<E>();
1023
1024             for(E eb : (Iterable<E>)e.getBoundPeers()) {
1025                 if (eb==e) continue;
1026                 if (eb.next.isBoundTo(e.next) && eb.prev.isBoundTo(e.prev)) {
1027                     forward.add(eb);
1028                     both.add(eb);
1029                 }
1030                 if (eb.pair.next.pair.isBoundTo(e.prev) && eb.pair.prev.pair.isBoundTo(e.next)) {
1031                     backward.add(eb.pair);
1032                     both.add(eb.pair);
1033                 }
1034             }
1035
1036             Vertex v1 = e.t.v1();
1037             Vertex v2 = e.t.v2();
1038             Vertex v3 = e.t.v3();
1039             Point c = e.t.centroid();
1040             E e_next = e.next;
1041             E e_prev = e.prev;
1042             e.t.destroy();
1043             newT(v1, v2, c);
1044             newT(c,  v2, v3);
1045             newT(v3, v1, c);
1046
1047             // FIXME: forward too
1048             for(E ex : backward) {
1049                 Vertex v1x = ex.t.v1();
1050                 Vertex v2x = ex.t.v2();
1051                 Vertex v3x = ex.t.v3();
1052                 Point cx = ex.t.centroid();
1053                 E ex_next = ex.next;
1054                 E ex_prev = ex.prev;
1055                 ex.t.destroy();
1056                 newT(v1x, v2x, cx);
1057                 newT(cx,  v2x, v3x);
1058                 newT(v3x, v1x, cx);
1059
1060                 // FIXME: i have no idea if this is right
1061                 e.next.bindTo(e.getBindingMatrix(ex.pair), ex.prev);
1062                 e.prev.bindTo(e.getBindingMatrix(ex.pair), ex.next);
1063                 e.next.pair.bindTo(e.getBindingMatrix(ex.pair), ex.prev.pair);
1064                 e.prev.pair.bindTo(e.getBindingMatrix(ex.pair), ex.next.pair);
1065
1066                 e_next.next.bindTo(e_next.getBindingMatrix(ex_prev.pair), ex_prev.prev.pair);
1067                 e_next.prev.bindTo(e_next.getBindingMatrix(ex_prev.pair), ex_prev.next.pair);
1068
1069                 e_prev.next.bindTo(e_prev.getBindingMatrix(ex_next.pair), ex_next.prev.pair);
1070                 e_prev.prev.bindTo(e_prev.getBindingMatrix(ex_next.pair), ex_next.next.pair);
1071             }
1072
1073             /*
1074
1075             E first = null;
1076             E firste = null;
1077             E firstx = null;
1078             E firstq = null;
1079             for(E e : (Iterable<E>)getBoundPeers()) {
1080                 E enext = e.next;
1081                 E eprev = e.prev;
1082                 E pnext = e.pair.next;
1083                 E pprev = e.pair.prev;
1084                 Point mid = e.midpoint();
1085                 Vertex r = e.next.v2;
1086                 Vertex l = e.pair.next.v2;
1087                 if (!e.destroyed) {
1088                     e.destroy();
1089                     e.pair.destroy();
1090                     newT(r.p, e.v1.p, mid,    null, 0);
1091                     newT(r.p, mid,    e.v2.p, null, 0);
1092                     newT(l.p, mid,    e.v1.p, null, 0);
1093                     newT(l.p, e.v2.p, mid,    null, 0);
1094                 }
1095             }
1096             for(E e : (Iterable<E>)getBoundPeers()) {
1097                 Point mid = e.midpoint();
1098                 if (first==null) {
1099                     first = e.v1.getE(mid);
1100                     firste = e;
1101                     firstx = e.pair;
1102                     firstq = e.v2.getE(mid).pair;
1103                     continue;
1104                 }
1105                 e.v1.getE(mid).          bindTo(e.getBindingMatrix(firste), first);
1106                 e.v1.getE(mid).pair.     bindTo(e.getBindingMatrix(firste), first.pair);
1107                 e.v2.getE(mid).pair.     bindTo(e.getBindingMatrix(firste), firstq);
1108                 e.v2.getE(mid).pair.pair.bindTo(e.getBindingMatrix(firste), firstq.pair);
1109             }
1110             */
1111             /*
1112             first.setConstraint(firste.getAffineConstraint());
1113             firstq.setConstraint(firste.getAffineConstraint());
1114             */
1115             return null;
1116         }
1117
1118
1119         T(E e1, int colorclass) {
1120             this.e1 = e1;
1121             E e2 = e1.next;
1122             E e3 = e2.next;
1123             if (e1==e2 || e1==e3) throw new Error();
1124             if (e3.next!=e1) throw new Error();
1125             if (e1.t!=null || e2.t!=null || e3.t!=null) throw new Error("non-manifold surface or disagreeing normals");
1126             e1.t = this;
1127             e1.next.t = this;
1128             e1.next.next.t = this;
1129
1130             // FIXME: check for sealed/watertight surface once construction is complete (and infer normal(s)?)
1131
1132             int color = Math.abs(random.nextInt());
1133             while(true) {
1134                 color = color % 4;
1135                 if (e1().pair.t != null && color == e1().pair.t.color) { color++; continue; }
1136                 if (e2().pair.t != null && color == e2().pair.t.color) { color++; continue; }
1137                 if (e3().pair.t != null && color == e3().pair.t.color) { color++; continue; }
1138                 break;
1139             }
1140             this.color = color;
1141             this.colorclass = colorclass;
1142             triangles.add(this);
1143         }
1144         public E e1() { return e1; }
1145         public E e2() { return e1.next; }
1146         public E e3() { return e1.prev; }
1147         public Vertex v1() { return e1.v1; }
1148         public Vertex v2() { return e1.v2; }
1149         public Vertex v3() { return e1.next.v2; }
1150         public Point p1() { return e1.v1.p; }
1151         public Point p2() { return e1.v2.p; }
1152         public Point p3() { return e1.next.v2.p; }
1153         public boolean hasE(E e) { return e1==e || e1.next==e || e1.prev==e; }
1154         public boolean has(Vertex v) { return v1()==v || v2()==v || v3()==v; }
1155
1156         public Vertex getOtherVertex(E e) {
1157             if (!hasE(e)) throw new RuntimeException();
1158             if (!e.has(v1())) return v1();
1159             if (!e.has(v2())) return v2();
1160             if (!e.has(v3())) return v3();
1161             throw new RuntimeException();
1162         }
1163
1164         public void removeFromRTree() { triangles.remove(this); }
1165         public void addToRTree() { triangles.insert(this); }
1166         public void destroy() {
1167             if (e1 != null) {
1168                 e1.t = null;
1169                 e1.next.t = null;
1170                 e1.prev.t = null;
1171             }
1172             triangles.remove(this);
1173             destroyed = true;
1174         }
1175         public void reinsert() { triangles.remove(this); triangles.add(this); }
1176
1177         private boolean destroyed = false;
1178         public boolean destroyed() { return destroyed; }
1179
1180         public boolean shouldBeDrawn() {
1181             if (e1().bindingGroupUnconstrained()) return false;
1182             if (e2().bindingGroupUnconstrained()) return false;
1183             if (e3().bindingGroupUnconstrained()) return false;
1184             return true;
1185         }
1186
1187         public void glTriangle(GL gl, Matrix m) {
1188             gl.glPushName(serial);
1189             gl.glBegin(GL.GL_TRIANGLES);
1190             glVertices(gl, m);
1191             gl.glEnd();
1192             gl.glPopName();
1193         }
1194
1195         /** issue gl.glVertex() for each of the triangle's points */
1196         public void glVertices(GL gl, Matrix m) {
1197             if (!shouldBeDrawn()) return;
1198             super.glVertices(gl, m);
1199         }
1200     }
1201
1202     // Dump /////////////////////////////////////////////////////////////////////////////
1203
1204     public void dump(OutputStream os) throws IOException {
1205         PrintWriter pw = new PrintWriter(new OutputStreamWriter(os));
1206         pw.println("solid dump");
1207         for(Mesh.T t : this) {
1208             Vec normal = t.norm();
1209             pw.println("facet normal " + normal.x + " " + normal.y + " " + normal.z);
1210             pw.println("  outer loop");
1211             for(Mesh.Vertex v : new Mesh.Vertex[] { t.v1(), t.v2(), t.v3() }) {
1212                 pw.println("    vertex " + v.p.x + " " + v.p.y + " " + v.p.z);
1213             }
1214             pw.println("  endloop");
1215             pw.println("endfacet");
1216         }
1217         pw.println("endsolid dump");
1218         pw.flush();
1219     }
1220
1221 }