83df984d1f2270e5927e369764d0be6d02dd3432
[anneal.git] / src / Geom.java
1 import java.awt.*;
2 import java.util.*;
3 import java.awt.event.*;
4 import javax.swing.*;
5 import javax.media.opengl.*;
6 import javax.media.opengl.glu.*;
7 import edu.wlu.cs.levy.CG.KDTree;
8
9 public class Geom implements Iterable<Geom.T> {
10
11     private KDTree kd = new KDTree(3);
12
13     public static float EPSILON = (float)0.0001;
14     public static Random random = new Random();
15
16     private HashMap<P,V>  ps = new HashMap<P,V>();
17     public  HashSet<E>    es = new HashSet<E>();
18     public  ArrayList<T>  ts = new ArrayList<T>();
19
20     public Iterator<T> iterator() { return ts.iterator(); }
21
22     public P origin() { return new P(0, 0, 0); }
23
24     public Geom score_against = null;
25     public double score = 0;
26     public float score() { return (float)score; }
27
28     public float rescore() {
29         int num = 0;
30         double dist = 0;
31         HashSet<V> done = new HashSet<V>();
32         for(T t : ts)
33             for(V p : new V[] { t.p1(), t.p2(), t.p3() }) {
34                 if (done.contains(p)) continue;
35                 done.add(p);
36                 p.rescore();
37             }
38         for(T t : ts)
39             for(V p : new V[] { t.p1(), t.p2(), t.p3() })
40                 p.kdremove();
41         kd = new KDTree(3);
42         for(T t : ts)
43             for(V p : new V[] { t.p1(), t.p2(), t.p3() })
44                 p.kdinsert();
45         return (float)(dist/num);
46     }
47
48     public void transform(M m) {
49         ArrayList<V> set = new ArrayList<V>();
50         set.addAll(ps.values());
51         for(V v : set) v.transform(m);
52     }
53
54     public Vec diagonal() {
55         float min_x = Float.MAX_VALUE;
56         float min_y = Float.MAX_VALUE;
57         float min_z = Float.MAX_VALUE;
58         float max_x = Float.MIN_VALUE;
59         float max_y = Float.MIN_VALUE;
60         float max_z = Float.MIN_VALUE;
61         for(P p : ps.keySet()) {
62             if (p.x < min_x) min_x = p.x;
63             if (p.y < min_y) min_y = p.y;
64             if (p.z < min_z) min_z = p.z;
65             if (p.x > max_x) max_x = p.x;
66             if (p.y > max_y) max_y = p.y;
67             if (p.z > max_z) max_z = p.z;
68         }
69         return new Vec(max_x - min_x, max_y - min_y, max_z - min_z);
70     }
71
72     public P centroid() {
73         float min_x = Float.MAX_VALUE;
74         float min_y = Float.MAX_VALUE;
75         float min_z = Float.MAX_VALUE;
76         float max_x = Float.MIN_VALUE;
77         float max_y = Float.MIN_VALUE;
78         float max_z = Float.MIN_VALUE;
79         for(P p : ps.keySet()) {
80             if (p.x < min_x) min_x = p.x;
81             if (p.y < min_y) min_y = p.y;
82             if (p.z < min_z) min_z = p.z;
83             if (p.x > max_x) max_x = p.x;
84             if (p.y > max_y) max_y = p.y;
85             if (p.z > max_z) max_z = p.z;
86         }
87         return new P((float)(max_x + min_x)/2,
88                      (float)(max_y + min_y)/2,
89                      (float)(max_z + min_z)/2);
90     }
91
92     public T newT(V p12, V p23, V p31, Vec norm) {
93         Vec norm2 = p31.p.minus(p12.p).cross(p23.p.minus(p12.p));
94         float dot = norm.dot(norm2);
95         //if (Math.abs(dot) < EPSILON) throw new Error("dot products within epsilon of each other: "+norm+" "+norm2);
96         if (dot < 0) { V p = p12; p12=p23; p23 = p; }
97         return newT(p12, p23, p31);
98     }
99
100     public float volume() {
101         double total = 0;
102         for(T t : ts) {
103             double area = t.area();
104             Vec origin_to_centroid = new Vec(new P(0, 0, 0), t.centroid());
105             boolean facingAway = t.norm().dot(origin_to_centroid) > 0;
106             double height = Math.abs(t.norm().dot(origin_to_centroid));
107             total += ((facingAway ? 1 : -1) * area * height) / 3.0;
108         }
109         return (float)total;
110     }
111
112     public V nearest(P p) {
113         Object[] results;
114         try { results = kd.nearest(new double[]{p.x,p.y,p.z},1); } catch (Exception e) { throw new Error(e); }
115         return (V)results[0];
116     }
117
118     public T newT(V p1, V p2, V p3) {
119         E e12 = p1.makeE(p2);
120         E e23 = p2.makeE(p3);
121         E e31 = p3.makeE(p1);
122         while(e12.next != e23 || e23.next != e31 || e31.next != e12) {
123             e12.makeAdjacent(e23);
124             e23.makeAdjacent(e31);
125             e31.makeAdjacent(e12);
126         }
127         T ret = e12.makeT();
128         if (e12.t == null) throw new Error();
129         if (e23.t == null) throw new Error();
130         if (e31.t == null) throw new Error();
131         return ret;
132     }
133
134     public final class V {
135         public P p;
136         public V(P p) {
137             this.p = p;
138             if (ps.get(p) != null) throw new Error();
139             ps.put(this.p, this);
140         }
141         public int hashCode() {
142             throw new Error();
143         }
144         public void kdremove() {
145             if (!inserted) return;
146             inserted = false;
147             try { kd.delete(new double[]{p.x,p.y,p.z}); } catch (Exception e) { }
148         }
149         public void kdinsert() {
150             if (inserted) return;
151             inserted = true;
152             try { kd.insert(new double[]{p.x,p.y,p.z},this); } catch (Exception e) { throw new Error(e); }
153         }
154
155         public float score() { return oldscore; }
156         public void unscore() {
157             if (watch == null) return;
158             watch.watch_x -= p.x;
159             watch.watch_y -= p.y;
160             watch.watch_z -= p.z;
161             watch.watch_count--;
162             if (watch.watch_count==0) {
163                 watch.watch_x = 0;
164                 watch.watch_y = 0;
165                 watch.watch_z = 0;
166             }
167             watch = null;
168         }
169         public V partner() { return watch==null ? this : watch; }
170         public V watchback() { return watch_count==0 ? partner() :
171                 new P(watch_x/watch_count, watch_y/watch_count, watch_z/watch_count).register(); }
172         public void rescore() {
173             if (score_against == null) return;
174
175             score -= oldscore;
176             oldscore = 0;
177
178             if (watch != null) unscore();
179             V po = this;
180             if (watch == null) {
181                 watch = score_against.nearest(po.p);
182
183                 // don't attract to vertices that face the other way
184                 if (watch.norm().dot(norm()) < 0) {
185                     watch = null;
186                 } else {
187                     watch.watch_x += po.p.x;
188                     watch.watch_y += po.p.y;
189                     watch.watch_z += po.p.z;
190                     watch.watch_count++;
191                 }
192             }
193
194             double s1, s2;
195             if (watch_count==0) s1 = 0;
196             else                s1 = p.distance(watch_x/watch_count, watch_y/watch_count, watch_z/watch_count);
197             s2 = watch==null ? 0 : po.p.distance(watch.p);
198             oldscore = (float)(s1 + s2);
199             score += oldscore;
200         }
201
202
203         /** does NOT update bound pairs! */
204         public boolean transform(M m) {
205             // FIXME: screws up kdtree 
206             // FIXME: screws up hashmap
207             unscore();
208             try {
209                 if (ps.get(this.p)==null) throw new Error();
210                 ps.remove(this.p);
211                 float newx = m.a*p.x + m.b*p.y + m.c*p.z + m.d;
212                 float newy = m.e*p.x + m.f*p.y + m.g*p.z + m.h;
213                 float newz = m.i*p.x + m.j*p.y + m.k*p.z + m.l;
214                 this.p = new P(newx, newy, newz);
215                 // FIXME: what if we move onto exactly where another point is?
216                 ps.put(this.p,(V)this);
217             } catch (Exception e) {
218                 throw new RuntimeException(e);
219             }
220             rescore();
221             boolean good = true;
222             /*
223             for(T t : ts) {
224                 for(E e = this.e; ;) {
225                     if (e.intersects(t)) { good = false; break; }
226                     e = e.pair.next;
227                     if (e == this.e) break;
228                 }
229             }
230             */
231             /*
232                 if (t==this.t) continue;
233                 if (this.intersects(t)) good = false;
234             }
235             */
236             return good;
237         }
238         public boolean move(Vec v) {
239             M m = new M(v);
240             V p = this;
241             boolean good = true;
242             do {
243                 good &= p.transform(m);
244                 v = v.times(binding); // bleh wrong
245                 p = p.bound_to;
246             } while (p != this);
247             return good;
248         }
249
250         public E makeE(V p2) {
251             E e = getE(p2);
252             if (e != null) return e;
253             e = p2.getE(this);
254             if (this.e == null && p2.e == null) return this.e = new E(this, p2);
255             if (this.e == null && p2.e != null) return p2.makeE(this).pair;
256             return new E(getFreeIncident(), p2);
257         }
258
259         public E getFreeIncident() {
260             E ret = getFreeIncident(e, e);
261             if (ret != null) return ret;
262             ret = getFreeIncident(e.pair.next, e.pair.next);
263             if (ret == null) throw new Error("unable to find free incident to " + this);
264             return ret;
265         }
266
267         public E getFreeIncident(E start, E before) {
268             E e = start;
269             do {
270                 if (e.pair.p2 == this && e.pair.t == null && e.pair.next.t == null) return e.pair;
271                 e = e.pair.next;
272             } while(e != before);
273             return null;
274         }
275
276         public E getE(V p2) {
277             E e = this.e;
278             do {
279                 if (e==null) return null;
280                 if (e.p1 == this && e.p2 == p2) return e;
281                 e = e.pair.next;
282             } while (e!=this.e);
283             return null;
284         }
285
286         public boolean isBoundTo(V p) {
287             V px = p;
288             do {
289                 if (px==this) return true;
290                 px = px.bound_to;
291             } while(px != p);
292             return false;
293         }
294
295         public void unbind() { bound_to = this; binding = new M(); }
296         public void bind(V p) { bind(p, new M()); }
297         public void bind(V p, M binding) {
298             if (isBoundTo(p)) return;
299             V temp_bound_to = p.bound_to;
300             M temp_binding = p.binding;
301             p.bound_to = this.bound_to;
302             p.binding = binding.times(this.binding); // FIXME: may have order wrong here
303             this.bound_to = temp_bound_to;
304             this.binding = temp_binding.times(temp_binding); // FIXME: may have order wrong here
305         }
306         public Vec norm() {
307             Vec norm = new Vec(0, 0, 0);
308             E e = this.e;
309             do {
310                 if (e.t != null) norm = norm.plus(e.t.norm().times((float)e.prev.angle()));
311                 e = e.pair.next;
312             } while(e != this.e);
313             return norm.norm();
314         }
315         V bound_to = this;
316
317         int watch_count;
318         float watch_x;
319         float watch_y;
320         float watch_z;
321         V watch;
322         E e;                // some edge *leaving* this point
323         M binding = new M();
324         float oldscore = 0;
325         boolean inserted = false;
326     }
327
328     public class P {
329         float x, y, z;
330         public P(double x, double y, double z) { this((float)x, (float)y, (float)z); }
331         public P(float x, float y, float z) { this.x = x; this.y = y; this.z = z; }
332         public float distance(P p) { return distance(p.x, p.y, p.z); }
333         public float distance(float ox, float oy, float oz) { return (float)Math.sqrt((x-ox)*(x-ox)+(y-oy)*(y-oy)+(z-oz)*(z-oz)); }
334         public V register() { V v = ps.get(this); return v==null ? new V(this) : v; }
335         public P times(M m) { return m.times(this); }
336         public Vec minus(P p) { return new Vec(x-p.x, y-p.y, z-p.z); }
337         public P plus(Vec v) { return new P(x+v.x, y+v.y, z+v.z); }
338         public boolean equals(Object o) { return o!=null && (o instanceof P) && ((P)o).x==x && ((P)o).y==y && ((P)o).z==z; }
339         public void glVertex(GL gl) { _glVertex(gl); }
340         private void _glVertex(GL gl) { gl.glVertex3f(x, y, z); }
341         public String toString() { return "("+x+","+y+","+z+")"; }
342         // FIXME: moving a point alters its hashCode
343         public int hashCode() {
344             return
345                 Float.floatToIntBits(x) ^
346                 Float.floatToIntBits(y) ^
347                 Float.floatToIntBits(z);
348         }
349     }
350
351     /** vector in 3-space */
352     public final class Vec {
353         public final float x, y, z;
354         public Vec(double x, double y, double z) { this((float)x, (float)y, (float)z); }
355         public Vec(float x, float y, float z) { this.x = x; this.y = y; this.z = z; }
356         public Vec(P p1, P p2) { this(p2.x-p1.x, p2.y-p1.y, p2.z-p1.z); }
357         public Vec cross(Vec v) { return new Vec(y*v.z-z*v.y, z*v.x-x*v.z, x*v.y-y*v.x); }
358         public Vec plus(Vec v) { return new Vec(x+v.x, y+v.y, z+v.z); }
359         public Vec norm() { return mag()==0 ? this : div(mag()); }
360         public Vec times(M m) { return m.apply(this); }
361         public float mag() { return (float)Math.sqrt(x*x+y*y+z*z); }
362         public float dot(Vec v) { return x*v.x + y*v.y + z*v.z; }
363         public Vec times(float mag) { return new Vec(x*mag, y*mag, z*mag); }
364         public Vec div(float mag) { return new Vec(x/mag, y/mag, z/mag); }
365         public String toString() { return "<"+x+","+y+","+z+">"; }
366     }
367
368     public class BindingGroup {
369         public HashSet<E> es = new HashSet<E>();
370         public BindingGroup() { }
371         public BindingGroup(E e) {
372             es.add(e);
373         }
374         public void add(E e) {
375             if (e.bg != null) { merge(e.bg); return; }
376             es.add(e);
377             e.bg = this;
378         }
379         public void merge(BindingGroup bg) {
380             for(E e : bg.es) {
381                 e.bg = null;
382                 add(e);
383             }
384         }
385     }
386
387     /** [UNIQUE] an edge */
388     public final class E implements Comparable<E> {
389
390         public boolean intersects(T t) {
391             double A0=t.p1().p.x, A1=t.p1().p.y, A2=t.p1().p.z;
392             double B0=t.p2().p.x, B1=t.p2().p.y, B2=t.p2().p.z;
393             double C0=t.p3().p.x, C1=t.p3().p.y, C2=t.p3().p.z;
394             double j0=p1.p.x, j1=p1.p.y, j2=p1.p.z;
395             double k0=p2.p.x, k1=p2.p.y, k2=p2.p.z;
396             double J0, J1, J2;
397             double K0, K1, K2;
398             double i0, i1, i2;
399             double a0, a1, a2;
400             double b0, b1, b2;
401             double c0, c1, c2;
402             double in_det;
403             double R00, R01, R02, R03,
404                 R10, R11, R12, R13,
405                 R20, R21, R22, R23,
406                 R30, R31, R32, R33;
407
408
409             /* a = B - A */
410             a0 = B0 - A0; 
411             a1 = B1 - A1; 
412             a2 = B2 - A2;
413             /* b = C - B */
414             b0 = C0 - A0;
415             b1 = C1 - A1;
416             b2 = C2 - A2;
417             /* c = a &times; b */
418             c0 = a1 * b2 - a2 * b1;
419             c1 = a2 * b0 - a0 * b2;
420             c2 = a0 * b1 - a1 * b0;
421  
422             /* M^(-1) = (1/det(M)) * adj(M) */
423             in_det = 1 / (c0 * c0 + c1 * c1 + c2 * c2);
424             R00 = (b1 * c2 - b2 * c1) * in_det;
425             R01 = (b2 * c0 - b0 * c2) * in_det;
426             R02 = (b0 * c1 - b1 * c0) * in_det;
427             R10 = (c1 * a2 - c2 * a1) * in_det;
428             R11 = (c2 * a0 - c0 * a2) * in_det;
429             R12 = (c0 * a1 - c1 * a0) * in_det;
430             R20 = (c0) * in_det;
431             R21 = (c1) * in_det;
432             R22 = (c2) * in_det;
433   
434             /* O = M^(-1) * A */
435             R03 = -(R00 * A0 + R01 * A1 + R02 * A2);
436             R13 = -(R10 * A0 + R11 * A1 + R12 * A2);
437             R23 = -(R20 * A0 + R21 * A1 + R22 * A2);
438  
439             /* fill in last row of 4x4 matrix */
440             R30 = R31 = R32 = 0;
441             R33 = 1;
442   
443             J2 = R20 * j0 + R21 * j1 + R22 * j2 + R23;
444             K2 = R20 * k0 + R21 * k1 + R22 * k2 + R23;
445             if (J2 * K2 >= 0) return false;
446
447             J0 = R00 * j0 + R01 * j1 + R02 * j2 + R03;
448             K0 = R00 * k0 + R01 * k1 + R02 * k2 + R03;
449             i0 = J0 + J2 * ((K0 - J0) / (J2 - K2));
450             if (i0 < 0 || i0 > 1) return false;
451   
452             J1 = R10 * j0 + R11 * j1 + R12 * j2 + R13;
453             K1 = R10 * k0 + R11 * k1 + R12 * k2 + R13;
454             i1 = J1 + J2 * ((K1 - J1) / (J2 - K2));
455             if (i1 < 0 || i1 > 1 || i0 + i1 > 1) return false;
456
457             return true;            
458         }
459
460         public int compareTo(E e) {
461             return e.length() > length() ? 1 : -1;
462         }
463
464         public final V p1, p2;
465         T t;     // triangle to our "left"
466         E prev;  // previous half-edge
467         E next;  // next half-edge
468         E pair;  // partner half-edge
469
470
471         public BindingGroup bg = new BindingGroup(this);
472
473         public void bind(E e) { bind(e, new M()); }
474         public void bind(E e, M m) { e.bg.add(this); }
475
476         public void dobind() {
477             if (bg==null) return;
478             for(E ex : bg.es) {
479                 if (ex==this) continue;
480                 p1.bind(ex.p1);
481                 p2.bind(ex.p2);
482             }
483         }
484
485         boolean shattered = false;
486         public V shatter() { return shatter(midpoint().register(), null, null); }
487         public V shatter(V mid, BindingGroup bg1, BindingGroup bg2) {
488             if (shattered) return mid;
489             shattered = true;
490
491             V r = next.p2;
492             E next = this.next;
493             E prev = this.prev;
494
495             if (bg1==null) bg1 = new BindingGroup();
496             if (bg2==null) bg2 = new BindingGroup();
497             for(E e : bg.es) e.shatter(e.midpoint().register(), bg1, bg2);
498             pair.shatter();
499             destroy();
500
501             newT(r, p1, mid);
502             newT(r, mid, p2);
503             bg1.add(p1.getE(mid));
504             bg2.add(mid.getE(p2));
505             return mid;
506         }
507
508         public boolean destroyed = false;
509         public void destroy() {
510             if (destroyed) return;
511             destroyed = true;
512             pair.destroyed = true;
513             if (next.t != null) next.t.destroy();
514             if (prev.t != null) prev.t.destroy();
515             if (pair.next.t != null) ts.remove(pair.next.t);
516             if (pair.prev.t != null) ts.remove(pair.prev.t);
517             next.t = null;
518             prev.t = null;
519             pair.next.t = null;
520             pair.prev.t = null;
521             this.bg = null;
522             pair.bg = null;
523             pair.prev.next = next;
524             next.prev = pair.prev;
525             prev.next = pair.next;
526             pair.next = prev;
527             if (p1.e == this) p1.e = prev.next;
528             if (pair.p1.e == pair) pair.p1.e = pair.prev.next;
529             es.remove(this);
530             es.remove(pair);
531             avgedge -= this.length();
532             avgedge -= pair.length();
533             numedges--;
534             numedges--;
535         }
536
537         private void sync() {
538             this.prev.next = this;
539             this.next.prev = this;
540             this.pair.pair = this;
541             if (this.next.p1 != p2) throw new Error();
542             if (this.prev.p2 != p1) throw new Error();
543             if (this.p1.e == null) this.p1.e = this;
544             es.add(this);
545             if (!added) {
546                 added = true;
547                 numedges++;
548                 avgedge += length();
549             }
550         }
551         private boolean added = false;
552
553         public T makeT() { return t==null ? (t = new T(this)) : t; }
554
555         /** angle between this half-edge and the next */
556         public double angle() {
557             Vec v1 = next.p2.p.minus(p2.p);
558             Vec v2 = this.p1.p.minus(p2.p);
559             return Math.acos(v1.norm().dot(v2.norm()));
560         }
561
562         public void makeAdjacent(E e) {
563             if (this.next == e) return;
564             if (p2 != e.p1) throw new Error("cannot make adjacent -- no shared vertex");
565             if (t != null || e.t != null) throw new Error("cannot make adjacent -- edges not both free");
566
567             E freeIncident = p2.getFreeIncident(e, this);
568
569             e.prev.next = freeIncident.next;
570             freeIncident.next.prev = e.prev;
571
572             freeIncident.next = this.next;
573             this.next.prev = freeIncident;
574             
575             this.next = e;
576             e.prev = this;
577
578             sync();
579             freeIncident.sync();
580         }
581
582         /** creates an isolated edge out in the middle of space */
583         public E(V p1, V p2) {
584             if (p1==p2) throw new Error("attempt to create edge with single vertex: " + p1);
585             this.p1 = p1;
586             this.p2 = p2;
587             this.prev = this.next = this.pair = new E(this, this, this);
588             sync();
589         }
590
591         /** adds a new half-edge from prev.p2 to p2 */
592         public E(E prev, V p2) {
593             this.p1 = prev.p2;
594             this.p2 = p2;
595             this.prev = prev;
596             if (p2.getE(p1) != null) throw new Error();
597             if (p2.e==null) {
598                 this.next = this.pair = new E(this, this, prev.next);
599             } else {
600                 E q = p2.getFreeIncident();
601                 this.next = q.next;
602                 this.next.prev = this;
603                 E z = prev.next;
604                 this.prev.next = this;
605                 this.pair = new E(q, this, z);
606             }
607             sync();
608         }
609
610         /** adds a new half-edge to the mesh with a given predecessor, successor, and pair */
611         public E(E prev, E pair, E next) {
612             this.p1 = prev.p2;
613             this.p2 = next.p1;
614             this.prev = prev;
615             this.next = next;
616             this.pair = pair;
617             sync();
618         }
619         public P midpoint() { return new P((p1.p.x+p2.p.x)/2, (p1.p.y+p2.p.y)/2, (p1.p.z+p2.p.z)/2); }
620         public boolean has(V v) { return v==p1 || v==p2; }
621         public float length() { return p1.p.minus(p2.p).mag(); }
622         public String toString() { return p1+"->"+p2; }
623     }
624
625     /** [UNIQUE] a triangle (face) */
626     public final class T {
627         public final E e1;
628         public final int color;
629
630         public void destroy() {
631             ts.remove(this);
632         }
633
634         public V nearest(P p) {
635             float d1 = p1().p.distance(p);
636             float d2 = p2().p.distance(p);
637             float d3 = p3().p.distance(p);
638             if (d1 < d2 && d1 < d3) return p1();
639             if (d2 < d3) return p2();
640             return p3();
641         }
642
643         T(E e1) {
644             this.e1 = e1;
645             E e2 = e1.next;
646             E e3 = e2.next;
647             if (e1==e2     || e1==e3) throw new Error();
648             if (e3.next!=e1) throw new Error();
649             if (e1.t!=null || e2.t!=null || e3.t!=null)
650                 throw new Error("non-manifold surface or disagreeing normals");
651             e1.t = this;
652             e1.next.t = this;
653             e1.next.next.t = this;
654
655             // FIXME: check for sealed/watertight surface once construction is complete (and infer normal(s)?)
656
657             int color = Math.abs(random.nextInt());
658             while(true) {
659                 color = color % 4;
660                 if (e1().pair.t != null && color == e1().pair.t.color) { color++; continue; }
661                 if (e2().pair.t != null && color == e2().pair.t.color) { color++; continue; }
662                 if (e3().pair.t != null && color == e3().pair.t.color) { color++; continue; }
663                 break;
664             }
665
666             // FIXME unnecssary
667             ts.add(this);
668             p1().kdinsert();
669             p2().kdinsert();
670             p3().kdinsert();
671
672             this.color = color;
673         }
674         public V p1() { return e1.p1; }
675         public V p2() { return e1.p2; }
676         public V p3() { return e1.next.p2; }
677         public E e1() { return e1; }
678         public E e2() { return e1.next; }
679         public E e3() { return e1.prev; }
680         public Vec norm() { return p2().p.minus(p1().p).cross(p3().p.minus(p1().p)).norm(); }
681         public boolean hasE(E e) { return e1==e || e1.next==e || e1.prev==e; }
682         public boolean has(V v) { return p1()==v || p2()==v || p3()==v; }
683
684         public float area() {
685             return (float)Math.abs(0.5 * e1().length() * new Vec(p1().p, p2().p).norm().dot(new Vec(p2().p, p3().p)));
686         }
687
688         public void glVertices(GL gl) {
689             p1().p.glVertex(gl);
690             p2().p.glVertex(gl);
691             p3().p.glVertex(gl);
692         }
693
694         public P centroid() { return new P((p1().p.x+p2().p.x+p3().p.x)/3,
695                                           (p1().p.y+p2().p.y+p3().p.y)/3, 
696                                           (p1().p.z+p2().p.z+p3().p.z)/3); }
697         public float diameter() {
698             // FIXME: what is this supposed to be?
699             return Math.max(Math.max(e1().length(), e2().length()), e3().length()) / 2;
700         }
701
702
703     }
704
705
706     /** matrix */
707     public class M {
708         //
709         //  [ a b c d ]   [ x ]
710         //  [ e f g h ]   [ y ]
711         //  [ i j k l ]   [ z ]
712         //  [ 0 0 0 1 ]   [ 1 ]
713         //
714         public final float a, b, c, d, e, f, g, h, i, j, k, l;
715         public M() { this(1); }
716         public M(float scale) {
717             a = f = k = scale;
718             l = h = d = e = b = i = c = j = g = 0;            
719         }
720         public M(float scalex, float scaley, float scalez) {
721             a = scalex;
722             f = scaley;
723             k = scalez;
724             l = h = d = e = b = i = c = j = g = 0;            
725         }
726         public M(Vec translate) {
727             d = translate.x; h = translate.y; l = translate.z;
728             a = f = k = 1;
729             b = c = e = g = i = j = 0;
730         }
731         public M(float a, float b, float c, float d, float e, float f, float g, float h, float i, float j, float k, float l) {
732             this.a = a; this.b = b; this.c = c; this.d = d; this.e = e; this.f = f; this.g = g; this.h = h; this.i = i;
733             this.j = j; this.k = k; this.l = l;
734         }
735         public M times(float x) {
736             return new M(a*x, b*x, c*x, d*x, e*x, f*x, g*x, h*x, i*x, j*x, k*x, l*x);
737         }
738         public M(Vec axis, float angle) {
739             double q = Math.cos(angle);
740             double s = Math.sin(angle);
741             double t = 1.0 - q;
742             a = (float)(q + axis.x*axis.x*t);
743             f = (float)(q + axis.y*axis.y*t);
744             k = (float)(q + axis.z*axis.z*t);
745             double tmp1 = axis.x*axis.y*t;
746             double tmp2 = axis.z*s;
747             e = (float)(tmp1 + tmp2);
748             b = (float)(tmp1 - tmp2);
749             tmp1 = axis.x*axis.z*t;
750             tmp2 = axis.y*s;
751             i = (float)(tmp1 - tmp2);
752             c = (float)(tmp1 + tmp2);
753             tmp1 = axis.y*axis.z*t;
754             tmp2 = axis.x*s;
755             j = (float)(tmp1 + tmp2);
756             g = (float)(tmp1 - tmp2);
757             d = h = l = 0;
758         }
759         public P times(P p) {
760             return new P(a*p.x + b*p.y + c*p.z + d,
761                         e*p.x + f*p.y + g*p.z + h,
762                         i*p.x + j*p.y + k*p.z + l);
763         }
764         public P apply(P p) { return p; }
765         public Vec apply(Vec v) { return v; }
766         public M invert() { return this; }
767         public M times(M m) { return this; }
768     }
769
770     public void unbind() {
771
772         for(Geom.T t : this) {
773             t.p1().unbind();
774             t.p2().unbind();
775             t.p3().unbind();
776         }
777
778     }
779     public void bind() {
780         for(Geom.T t : this) {
781             t.e1().dobind();
782             t.e2().dobind();
783             t.e3().dobind();
784         }
785     }
786     public int numedges = 0;
787     public float avgedge = 0;
788 }