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.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             for(E e_ : (Iterable<E>)e.getBoundPeers()) {
717                 if (e.v1.getPoint().distance((e.getBindingMatrix(e_).times(e_.v1.getPoint()))) > 0.01f)
718                     throw new RuntimeException("blah! " + e.v1.getPoint() + " " + e.getBindingMatrix(e_).times(e_.v1.getPoint()));
719                 if (e.v2.getPoint().distance((e.getBindingMatrix(e_).times(e_.v2.getPoint()))) > 0.01f)
720                     throw new RuntimeException("blah! " + e.v2.getPoint() + " " + e.getBindingMatrix(e_).times(e_.v2.getPoint()));
721                 if (v1.getPoint().distance(m.times(e.getBindingMatrix(e_).times(e_.v1.getPoint()))) > 0.01f)
722                     throw new RuntimeException("blah! " + v1.getPoint() + " " + m.times(e_.v1.getPoint()));
723                 if (v2.getPoint().distance(m.times(e.getBindingMatrix(e_).times(e_.v2.getPoint()))) > 0.01f)
724                     throw new RuntimeException("blah! " + v2.getPoint() + " " + m.times(e_.v2.getPoint()));
725             }
726             this.bindTo(m, e,      EPSILON);
727             this.pair.bindTo(m, e.pair, EPSILON);
728         }
729         
730         public void dobind() {
731             for(E e : (Iterable<E>)getBoundPeers()) {
732                 if (e==this) continue;
733                 v1.bindTo(getBindingMatrix(e), e.v1);
734                 v2.bindTo(getBindingMatrix(e), e.v2);
735             }
736         }
737
738         public Vertex shatter() {
739             if (shattered || destroyed) return nearest(midpoint());
740             shattered = true;
741             E first = null;
742             E firste = null;
743             E firstx = null;
744             E firstq = null;
745             for(E e : (Iterable<E>)getBoundPeers()) {
746                 E enext = e.next;
747                 E eprev = e.prev;
748                 E pnext = e.pair.next;
749                 E pprev = e.pair.prev;
750                 Point mid = e.midpoint();
751                 Vertex r = e.next.v2;
752                 Vertex l = e.pair.next.v2;
753                 if (!e.destroyed) {
754                     e.destroy();
755                     e.pair.destroy();
756                     newT(r.p, e.v1.p, mid,    null, 0);
757                     newT(r.p, mid,    e.v2.p, null, 0);
758                     newT(l.p, mid,    e.v1.p, null, 0);
759                     newT(l.p, e.v2.p, mid,    null, 0);
760                 }
761             }
762             for(E e : (Iterable<E>)getBoundPeers()) {
763                 Point mid = e.midpoint();
764                 if (first==null) {
765                     first = e.v1.getE(mid);
766                     firste = e;
767                     firstx = e.pair;
768                     firstq = e.v2.getE(mid).pair;
769                     continue;
770                 }
771                 e.v1.getE(mid).          bindTo(e.getBindingMatrix(firste), first);
772                 e.v1.getE(mid).pair.     bindTo(e.getBindingMatrix(firste), first.pair);
773                 e.v2.getE(mid).pair.     bindTo(e.getBindingMatrix(firste), firstq);
774                 e.v2.getE(mid).pair.pair.bindTo(e.getBindingMatrix(firste), firstq.pair);
775             }
776             /*
777             first.setConstraint(firste.getAffineConstraint());
778             firstq.setConstraint(firste.getAffineConstraint());
779             */
780             return nearest(midpoint());
781         }
782
783         public boolean destroyed = false;
784         public void destroy() {
785             if (destroyed) return;
786             destroyed = true;
787             pair.destroyed = true;
788
789             if (t != null) t.destroy();
790             t = null;
791
792             if (pair.t != null) pair.t.destroy();
793             pair.t = null;
794
795             if (next.t != null) next.t.destroy();
796             if (prev.t != null) prev.t.destroy();
797             next.t = null;
798             prev.t = null;
799
800             if (pair.next.t != null) pair.next.t.destroy();
801             if (pair.prev.t != null) pair.next.t.destroy();
802             pair.next.t = null;
803             pair.prev.t = null;
804
805             pair.prev.next = next;
806             next.prev = pair.prev;
807             prev.next = pair.next;
808             pair.next.prev = prev;
809
810             if (v1.e == this) v1.e = pair.next;
811             if (pair.v1.e == pair) pair.v1.e = next;
812
813             if (v2.e == this) throw new RuntimeException();
814             if (pair.v2.e == pair) throw new RuntimeException();
815
816             /*
817             next = pair;
818             prev = pair;
819             pair.next = this;
820             pair.prev = this;
821             */
822
823             /*
824             pair.prev = null;
825             pair.next = null;
826             next = null;
827             prev = null;
828             */
829
830             /*
831             sanity();
832             next.sanity();
833             prev.sanity();
834             pair.sanity();
835             */
836         }
837
838         private void sync() {
839             this.prev.next = this;
840             this.next.prev = this;
841             this.pair.pair = this;
842             if (this.next.v1 != v2) throw new Error();
843             if (this.prev.v2 != v1) throw new Error();
844             if (this.v1.e == null) this.v1.e = this;
845             if (!added) added = true;
846         }
847         private boolean added = false;
848
849         public T makeT(int colorclass) { return t==null ? (t = new T(this, colorclass)) : t; }
850
851         public double dihedralAngle() {
852             Vec v1 = t.norm().times(-1);
853             Vec v2 = pair.t.norm().times(-1);
854             double prod = v1.norm().dot(v2.norm());
855             prod = Math.min(1,prod);
856             prod = Math.max(-1,prod);
857             double ret = Math.acos(prod);
858             if (Double.isNaN(ret)) throw new Error("nan! " + prod);
859             return ret;
860         }
861
862         /** angle between this half-edge and the next */
863         public double angle() {
864             Vec v1 = next.v2.p.minus(this.v2.p);
865             Vec v2 = this.v1.p.minus(this.v2.p);
866             return Math.acos(v1.norm().dot(v2.norm()));
867         }
868
869         public Vertex getOther(Vertex v) {
870             if (this.v1 == v) return v2;
871             if (this.v2 == v) return v1;
872             throw new Error();
873         }
874
875         public void makeAdjacent(E e) {
876             if (this.next == e) return;
877             if (v2 != e.v1) throw new Error("cannot make adjacent -- no shared vertex: " + this + " " + e);
878             if (t != null || e.t != null) throw new Error("cannot make adjacent -- edges not both free " + t + " " + e.t);
879
880             E freeIncident = v2.getFreeIncident(e, this);
881
882             e.prev.next = freeIncident.next;
883             freeIncident.next.prev = e.prev;
884
885             freeIncident.next = this.next;
886             this.next.prev = freeIncident;
887             
888             this.next = e;
889             e.prev = this;
890
891             sync();
892             freeIncident.sync();
893         }
894
895         /** creates an isolated edge out in the middle of space */
896         public E(Point v1, Point v2) {
897             if (vertices.get(v1) != null) throw new Error();
898             if (vertices.get(v2) != null) throw new Error();
899             this.v1 = new Vertex(v1);
900             this.v2 = new Vertex(v2);
901             this.prev = this.next = this.pair = new E(this, this, this);
902             this.v1.e = this;
903             this.v2.e = this.pair;
904             sync();
905         }
906
907         /** adds a new half-edge from prev.v2 to v2 */
908         public E(E prev, Point p) {
909             Vertex v2;
910             v2 = vertices.get(p);
911             if (v2 == null) v2 = new Vertex(p);
912             this.v1 = prev.v2;
913             this.v2 = v2;
914             this.prev = prev;
915             if (prev.destroyed) throw new RuntimeException();
916             if (v2.getE(v1) != null) throw new Error();
917             if (v2.e==null) {
918                 this.next = this.pair = new E(this, this, prev.next);
919             } else {
920                 E q = v2.getFreeIncident();
921                 this.next = q.next;
922                 this.next.prev = this;
923                 E z = prev.next;
924                 this.prev.next = this;
925                 this.pair = new E(q, this, z);
926             }
927             if (v2.e==null) v2.e = this.pair;
928             sync();
929         }
930
931         /** adds a new half-edge to the mesh with a given predecessor, successor, and pair */
932         public E(E prev, E pair, E next) {
933             this.v1 = prev.v2;
934             this.v2 = next.v1;
935             if (prev.destroyed) throw new RuntimeException();
936             this.prev = prev;
937             this.next = next;
938             this.pair = pair;
939             sync();
940         }
941         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); }
942         public boolean has(Vertex v) { return v==v1 || v==v2; }
943         public float length() { return v1.p.minus(v2.p).mag(); }
944         public String toString() { return v1+"->"+v2; }
945
946     }
947
948     public E makeE(Point p1, Point p2) {
949         Vertex v1 = vertices.get(p1);
950         Vertex v2 = vertices.get(p2);
951         if (v1 != null && v2 != null) {
952             E e = v1.getE(v2);
953             if (e != null) return e;
954             e = v2.getE(v1);
955             if (e != null) return e;
956         }
957         if (v1 != null) return new E(v1.getFreeIncident(), p2);
958         if (v2 != null) return new E(v2.getFreeIncident(), p1).pair;
959         return new E(p1, p2);
960     }
961     public boolean coalesce = false;
962     private static float round(float f) {
963         return Math.round(f*1000)/1000f;
964     }
965     public T newT(HasPoint v1, HasPoint v2, HasPoint v3) {
966         return newT(v1.getPoint(), v2.getPoint(), v3.getPoint(), null, 0);
967     }
968     public T newT(Point v1, Point v2, Point v3, Vec norm, int colorclass) {
969         if (coalesce) {
970
971             for(Vertex v : vertices) { if (v1.distance(v.p) < EPSILON) { v1 = v.p; break; } }
972             for(Vertex v : vertices) { if (v2.distance(v.p) < EPSILON) { v2 = v.p; break; } }
973             for(Vertex v : vertices) { if (v3.distance(v.p) < EPSILON) { v3 = v.p; break; } }
974             /*
975             v1 = new Point(round(v1.x), round(v1.y), round(v1.z));
976             v2 = new Point(round(v2.x), round(v2.y), round(v2.z));
977             v3 = new Point(round(v3.x), round(v3.y), round(v3.z));
978             */
979         }
980         if (norm != null) {
981             Vec norm2 = v3.minus(v1).cross(v2.minus(v1));
982             float dot = norm.dot(norm2);
983             //if (Math.abs(dot) < EPointSILON) throw new Error("dot products within evertsilon of each other: "+norm+" "+norm2);
984             if (dot < 0) { Point p = v1; v1=v2; v2 = p; }
985         }
986         E e12 = makeE(v1, v2);
987         E e23 = makeE(v2, v3);
988         E e31 = makeE(v3, v1);
989         while(e12.next != e23 || e23.next != e31 || e31.next != e12) {
990             e12.makeAdjacent(e23);
991             e23.makeAdjacent(e31);
992             e31.makeAdjacent(e12);
993         }
994         T ret = e12.makeT(colorclass);
995         if (e12.t == null) throw new Error();
996         if (e23.t == null) throw new Error();
997         if (e31.t == null) throw new Error();
998         return ret;
999     }
1000
1001     private int max_serial = 0;
1002     /** [UNIQUE] a triangle (face) */
1003     public final class T extends Triangle {
1004         public final E e1;
1005         public final int color;
1006         public final int colorclass;
1007
1008         public boolean red = false;
1009         public boolean old = false;
1010
1011         public final int serial = max_serial++;
1012         public boolean occluded;
1013
1014         public Point shatter() {
1015             if (destroyed) return null;
1016             E e = e1();
1017             
1018             HashSet<E> forward = new HashSet<E>();
1019             HashSet<E> backward = new HashSet<E>();
1020             HashSet<E> both = new HashSet<E>();
1021
1022             for(E eb : (Iterable<E>)e.getBoundPeers()) {
1023                 if (eb==e) continue;
1024                 if (eb.next.isBoundTo(e.next) && eb.prev.isBoundTo(e.prev)) {
1025                     forward.add(eb);
1026                     both.add(eb);
1027                 }
1028                 if (eb.pair.next.pair.isBoundTo(e.prev) && eb.pair.prev.pair.isBoundTo(e.next)) {
1029                     backward.add(eb.pair);
1030                     both.add(eb.pair);
1031                 }
1032             }
1033
1034             Vertex v1 = e.t.v1();
1035             Vertex v2 = e.t.v2();
1036             Vertex v3 = e.t.v3();
1037             Point c = e.t.centroid();
1038             E e_next = e.next;
1039             E e_prev = e.prev;
1040             e.t.destroy();
1041             newT(v1, v2, c);
1042             newT(c,  v2, v3);
1043             newT(v3, v1, c);
1044
1045             // FIXME: forward too
1046             for(E ex : backward) {
1047                 Vertex v1x = ex.t.v1();
1048                 Vertex v2x = ex.t.v2();
1049                 Vertex v3x = ex.t.v3();
1050                 Point cx = ex.t.centroid();
1051                 E ex_next = ex.next;
1052                 E ex_prev = ex.prev;
1053                 ex.t.destroy();
1054                 newT(v1x, v2x, cx);
1055                 newT(cx,  v2x, v3x);
1056                 newT(v3x, v1x, cx);
1057
1058                 // FIXME: i have no idea if this is right
1059                 e.next.bindTo(e.getBindingMatrix(ex.pair), ex.prev);
1060                 e.prev.bindTo(e.getBindingMatrix(ex.pair), ex.next);
1061                 e.next.pair.bindTo(e.getBindingMatrix(ex.pair), ex.prev.pair);
1062                 e.prev.pair.bindTo(e.getBindingMatrix(ex.pair), ex.next.pair);
1063
1064                 e_next.next.bindTo(e_next.getBindingMatrix(ex_prev.pair), ex_prev.prev.pair);
1065                 e_next.prev.bindTo(e_next.getBindingMatrix(ex_prev.pair), ex_prev.next.pair);
1066
1067                 e_prev.next.bindTo(e_prev.getBindingMatrix(ex_next.pair), ex_next.prev.pair);
1068                 e_prev.prev.bindTo(e_prev.getBindingMatrix(ex_next.pair), ex_next.next.pair);
1069             }
1070
1071             /*
1072
1073             E first = null;
1074             E firste = null;
1075             E firstx = null;
1076             E firstq = null;
1077             for(E e : (Iterable<E>)getBoundPeers()) {
1078                 E enext = e.next;
1079                 E eprev = e.prev;
1080                 E pnext = e.pair.next;
1081                 E pprev = e.pair.prev;
1082                 Point mid = e.midpoint();
1083                 Vertex r = e.next.v2;
1084                 Vertex l = e.pair.next.v2;
1085                 if (!e.destroyed) {
1086                     e.destroy();
1087                     e.pair.destroy();
1088                     newT(r.p, e.v1.p, mid,    null, 0);
1089                     newT(r.p, mid,    e.v2.p, null, 0);
1090                     newT(l.p, mid,    e.v1.p, null, 0);
1091                     newT(l.p, e.v2.p, mid,    null, 0);
1092                 }
1093             }
1094             for(E e : (Iterable<E>)getBoundPeers()) {
1095                 Point mid = e.midpoint();
1096                 if (first==null) {
1097                     first = e.v1.getE(mid);
1098                     firste = e;
1099                     firstx = e.pair;
1100                     firstq = e.v2.getE(mid).pair;
1101                     continue;
1102                 }
1103                 e.v1.getE(mid).          bindTo(e.getBindingMatrix(firste), first);
1104                 e.v1.getE(mid).pair.     bindTo(e.getBindingMatrix(firste), first.pair);
1105                 e.v2.getE(mid).pair.     bindTo(e.getBindingMatrix(firste), firstq);
1106                 e.v2.getE(mid).pair.pair.bindTo(e.getBindingMatrix(firste), firstq.pair);
1107             }
1108             */
1109             /*
1110             first.setConstraint(firste.getAffineConstraint());
1111             firstq.setConstraint(firste.getAffineConstraint());
1112             */
1113             return null;
1114         }
1115
1116
1117         T(E e1, int colorclass) {
1118             this.e1 = e1;
1119             E e2 = e1.next;
1120             E e3 = e2.next;
1121             if (e1==e2 || e1==e3) throw new Error();
1122             if (e3.next!=e1) throw new Error();
1123             if (e1.t!=null || e2.t!=null || e3.t!=null) throw new Error("non-manifold surface or disagreeing normals");
1124             e1.t = this;
1125             e1.next.t = this;
1126             e1.next.next.t = this;
1127
1128             // FIXME: check for sealed/watertight surface once construction is complete (and infer normal(s)?)
1129
1130             int color = Math.abs(random.nextInt());
1131             while(true) {
1132                 color = color % 4;
1133                 if (e1().pair.t != null && color == e1().pair.t.color) { color++; continue; }
1134                 if (e2().pair.t != null && color == e2().pair.t.color) { color++; continue; }
1135                 if (e3().pair.t != null && color == e3().pair.t.color) { color++; continue; }
1136                 break;
1137             }
1138             this.color = color;
1139             this.colorclass = colorclass;
1140             triangles.add(this);
1141         }
1142         public E e1() { return e1; }
1143         public E e2() { return e1.next; }
1144         public E e3() { return e1.prev; }
1145         public Vertex v1() { return e1.v1; }
1146         public Vertex v2() { return e1.v2; }
1147         public Vertex v3() { return e1.next.v2; }
1148         public Point p1() { return e1.v1.p; }
1149         public Point p2() { return e1.v2.p; }
1150         public Point p3() { return e1.next.v2.p; }
1151         public boolean hasE(E e) { return e1==e || e1.next==e || e1.prev==e; }
1152         public boolean has(Vertex v) { return v1()==v || v2()==v || v3()==v; }
1153
1154         public Vertex getOtherVertex(E e) {
1155             if (!hasE(e)) throw new RuntimeException();
1156             if (!e.has(v1())) return v1();
1157             if (!e.has(v2())) return v2();
1158             if (!e.has(v3())) return v3();
1159             throw new RuntimeException();
1160         }
1161
1162         public void removeFromRTree() { triangles.remove(this); }
1163         public void addToRTree() { triangles.insert(this); }
1164         public void destroy() {
1165             if (e1 != null) {
1166                 e1.t = null;
1167                 e1.next.t = null;
1168                 e1.prev.t = null;
1169             }
1170             triangles.remove(this);
1171             destroyed = true;
1172         }
1173         public void reinsert() { triangles.remove(this); triangles.add(this); }
1174
1175         private boolean destroyed = false;
1176         public boolean destroyed() { return destroyed; }
1177
1178         public boolean shouldBeDrawn() {
1179             if (e1().bindingGroupUnconstrained()) return false;
1180             if (e2().bindingGroupUnconstrained()) return false;
1181             if (e3().bindingGroupUnconstrained()) return false;
1182             return true;
1183         }
1184
1185         public void glTriangle(GL gl, Matrix m) {
1186             gl.glPushName(serial);
1187             gl.glBegin(GL.GL_TRIANGLES);
1188             glVertices(gl, m);
1189             gl.glEnd();
1190             gl.glPopName();
1191         }
1192
1193         /** issue gl.glVertex() for each of the triangle's points */
1194         public void glVertices(GL gl, Matrix m) {
1195             if (!shouldBeDrawn()) return;
1196             super.glVertices(gl, m);
1197         }
1198     }
1199
1200     // Dump /////////////////////////////////////////////////////////////////////////////
1201
1202     public void dump(OutputStream os) throws IOException {
1203         PrintWriter pw = new PrintWriter(new OutputStreamWriter(os));
1204         pw.println("solid dump");
1205         for(Mesh.T t : this) {
1206             Vec normal = t.norm();
1207             pw.println("facet normal " + normal.x + " " + normal.y + " " + normal.z);
1208             pw.println("  outer loop");
1209             for(Mesh.Vertex v : new Mesh.Vertex[] { t.v1(), t.v2(), t.v3() }) {
1210                 pw.println("    vertex " + v.p.x + " " + v.p.y + " " + v.p.z);
1211             }
1212             pw.println("  endloop");
1213             pw.println("endfacet");
1214         }
1215         pw.println("endsolid dump");
1216         pw.flush();
1217     }
1218
1219 }