Mesh.java: better error for zero-length edges, and add numTriangles() method
[anneal.git] / src / edu / berkeley / qfat / Mesh.java
index f5ce535..1a96e7d 100644 (file)
@@ -1,13 +1,14 @@
 package edu.berkeley.qfat;
 import java.awt.*;
 import java.util.*;
+import java.io.*;
 import java.awt.event.*;
 import javax.swing.*;
 import javax.media.opengl.*;
 import javax.media.opengl.glu.*;
 import edu.berkeley.qfat.geom.*;
-import edu.berkeley.qfat.geom.HasBindingGroup;
 import edu.wlu.cs.levy.CG.KDTree;
+import edu.berkeley.qfat.bind.*;
 import edu.berkeley.qfat.geom.Point;
 import com.infomatiq.jsi.IntProcedure;
 
@@ -25,11 +26,12 @@ public class Mesh implements Iterable<Mesh.T> {
     public boolean option_errorNormals = false;
     public boolean option_selectable   = true;
 
-    public void render(GL gl, Matrix m) {
+    public void render(GL gl, Matrix m) { render(gl, m, false); }
+    public void render(GL gl, Matrix m, boolean noColor) {
         if (option_wireframe) {
             gl.glDisable(GL.GL_LIGHTING);
             gl.glBegin(GL.GL_LINES);
-            gl.glColor3f(1, 1, 1);
+            if (!noColor) gl.glColor3f(1, 1, 1);
             for (T t : this) {
                 // fixme used to be .goodp
                 m.times(t.e1().v1.p).glVertex(gl);
@@ -44,10 +46,11 @@ public class Mesh implements Iterable<Mesh.T> {
             return;
         }
         for(T t : this) {
-            gl.glColor4f((float)(0.25+(0.05*t.color)),
-                         (float)(0.25+(0.05*t.color)),
-                         (float)(0.75+(0.05*t.color)),
-                         (float)0.3); 
+            if (!noColor)
+                gl.glColor4f((float)(0.25+(0.05*t.color)),
+                             (float)(0.25+(0.05*t.color)),
+                             (float)(0.75+(0.05*t.color)),
+                             (float)0.3); 
             /*
             if (t.red) {
             gl.glColor4f((float)(0.75+(0.05*t.color)),
@@ -63,7 +66,8 @@ public class Mesh implements Iterable<Mesh.T> {
                 for(Mesh.Vertex p : new Mesh.Vertex[] { t.v1(), t.v2(), t.v3() }) {
                     if (p.ok) {
                         gl.glBegin(GL.GL_LINES);
-                        gl.glColor3f(1, 1, 1);
+                        if (!noColor)
+                            gl.glColor3f(1, 1, 1);
                         p.p.glVertex(gl);
                         p.p.plus(p.norm().times((float)p.error()*10)).glVertex(gl);
                         gl.glEnd();
@@ -81,6 +85,7 @@ public class Mesh implements Iterable<Mesh.T> {
     public float error() { return (float)error; }
 
     public int size() { return vertices.size(); }
+    public int numTriangles() { return triangles.size(); }
     public Iterable<Vertex> vertices() { return vertices; }
     public Iterator<T> iterator() { return triangles.iterator(); }
 
@@ -215,8 +220,19 @@ public class Mesh implements Iterable<Mesh.T> {
 
     // Vertexices //////////////////////////////////////////////////////////////////////////////
 
+
     /** a vertex in the mesh */
-    public final class Vertex extends HasQuadric implements Visitor {
+    public final class Vertex extends HasQuadric implements Visitor, HasPoint {
+        public void bindTo(Matrix bindingMatrix, HasBindingGroup other) {
+            bindTo(bindingMatrix, other, EPSILON);
+        }
+        public float getMaxX() { return getPoint().getMaxX(); }
+        public float getMinX() { return getPoint().getMinX(); }
+        public float getMaxY() { return getPoint().getMaxY(); }
+        public float getMinY() { return getPoint().getMinY(); }
+        public float getMaxZ() { return getPoint().getMaxZ(); }
+        public float getMinZ() { return getPoint().getMinZ(); }
+
         public Point p, goodp;
         public Point oldp;
         E e;                // some edge *leaving* this point
@@ -364,7 +380,7 @@ public class Mesh implements Iterable<Mesh.T> {
                 float minangle = (float)(Math.PI * 0.8);
                 //nerror += ((ang / Math.PI)*(ang/Math.PI)) * e.length() * 0.05;
 
-                nerror += (1-e.t.quality())*0.0001;
+                //nerror += (1-e.t.quality())*0.0001;
                 if (ang > minangle) nerror += (ang - minangle);
 
                 //System.out.println(((ang / Math.PI)*(ang/Math.PI)) * 0.000001);
@@ -399,27 +415,24 @@ public class Mesh implements Iterable<Mesh.T> {
             //     t1 =     t1.getMatrix(t2) * t2
             // M * t1 = M * t1.getMatrix(t2) * t2
 
+            /*
             if (bindingGroup!=null && this != bindingGroup.getMaster()) {
                 Matrix m2 = getBindingMatrix(bindingGroup.getMaster());
                 Vec v2 = m2.times(vv.plus(getPoint())).minus(m2.times(getPoint()));
                 return ((Vertex)bindingGroup.getMaster()).move(v2, ignoreProblems);
             }
+            */
 
             Point op = this.p;
-            Point pp = vv.plus(getPoint());
-            if (bindingGroup != null) {
-                /*
-                for(int i=0; i<20 ; i++) {
-                    Point v2 = getConstraint().times(pp);
-                    pp = pp.midpoint(v2);
-                    //System.out.println(m.minus(m2));
-                }
-            */
-                //pp = getConstraint().times(pp);
+            Point pt = vv.plus(getPoint());
+            Point pp = pt;
+            pt = getBindingConstraint().getProjection(pp);
+            if (pt==null) {
+                System.out.println("constraint violation: " + getBindingConstraint());
+                return false;
             }
-            //pp = pp.minus(op).norm().times(vv.mag()).plus(op);
-            ok = false;
-            Point pt = pp;
+            System.out.println(pt.minus(pp).mag() + " " + getBindingConstraint());
+
             for(Vertex v : (Iterable<Vertex>)getBoundPeers()) {
                 Point pt2 = v.getBindingMatrix(this).times(pt);
                 /*
@@ -563,6 +576,10 @@ public class Mesh implements Iterable<Mesh.T> {
     /** [UNIQUE] an edge */
     public final class E extends HasBindingGroup implements Comparable<E> {
 
+        public void bindTo(Matrix bindingMatrix, HasBindingGroup other) {
+            bindTo(bindingMatrix, other, EPSILON);
+        }
+
         public void sanity() {
             if (destroyed) return;
             if (pair!=null && (pair.v1!=v2 || pair.v2!=v1)) throw new RuntimeException();
@@ -577,6 +594,34 @@ public class Mesh implements Iterable<Mesh.T> {
         E pair;  // partner half-edge
         boolean shattered = false;
 
+
+        /** the "edge normal" -- average of the normals of the adjacent faces */
+        public Vec norm() {
+            return
+                (t==null || pair==null || pair.t==null)
+                ? null
+                : t.norm().plus(pair.t.norm()).norm();
+        }
+
+        public void glVertices(GL gl) {
+            Point p1 = v1.p;
+            Point p2 = v2.p;
+            if (t != null) {
+                p1 = p1.plus(t.centroid().minus(p1).times(0.1f));
+                p2 = p2.plus(t.centroid().minus(p2).times(0.1f));
+                p1 = p1.plus(norm().times(length() * 0.01f));
+                p2 = p2.plus(norm().times(length() * 0.01f));
+            }
+            p1.glVertex(gl);
+            p2.glVertex(gl);
+            if (t==null || pair.t==null) return;
+
+            Point atip = p2.minus(Point.ZERO).times(4).plus(p1.minus(Point.ZERO)).div(5).plus(Point.ZERO);
+            atip = (t.norm().plus(pair.t.norm())).norm().times(getSegment().length() / 5).plus(atip);
+            p2.glVertex(gl);
+            atip.glVertex(gl);
+        }
+
         public boolean intersects(T t) { return t.intersects(v1.p, v2.p); }
 
         public Segment getSegment() { return new Segment(v1.getPoint(), v2.getPoint()); }
@@ -616,33 +661,37 @@ public class Mesh implements Iterable<Mesh.T> {
 
         }
 
-        public void bindingGroupChanged(edu.berkeley.qfat.geom.BindingGroup newBindingGroup_) {
-
-            edu.berkeley.qfat.geom.BindingGroup<E> newBindingGroup =
-                (edu.berkeley.qfat.geom.BindingGroup<E>)newBindingGroup_;
-            if (newBindingGroup==null) return;
-            if (this==newBindingGroup.getMaster()) return;
+        public void bindingGroupChanged() {
             HashSet<E> nbg = new HashSet<E>();
-            for(E eother : (Iterable<E>)newBindingGroup) nbg.add(eother);
+            for(E eother : (Iterable<E>)getBoundPeers()) nbg.add(eother);
             for(E eother : nbg) {
                 if (next==null || prev==null) continue;
                 if (eother.next==null || eother.prev==null) continue;
 
-                if (next.isBoundTo(eother.pair.prev.pair) && !prev.isBoundTo(eother.pair.next.pair))
-                    prev.bindTo(next.getBindingMatrix(eother.pair.prev.pair), eother.pair.next.pair);
-                if (!next.isBoundTo(eother.pair.prev.pair) && prev.isBoundTo(eother.pair.next.pair))
-                    next.bindTo(prev.getBindingMatrix(eother.pair.next.pair), eother.pair.prev.pair);
+                Matrix m = getBindingMatrix(eother);
+                if (next.isBoundTo(eother.pair.prev.pair) &&
+                    !prev.isBoundTo(eother.pair.next.pair) &&
+                    m.equalsModuloEpsilon(next.getBindingMatrix(eother.pair.prev.pair), EPSILON))
+                    prev.bindEdge(next.getBindingMatrix(eother.pair.prev.pair), eother.pair.next.pair);
+                if (!next.isBoundTo(eother.pair.prev.pair) &&
+                    prev.isBoundTo(eother.pair.next.pair)  &&
+                    m.equalsModuloEpsilon(prev.getBindingMatrix(eother.pair.next.pair), EPSILON))
+                    next.bindEdge(prev.getBindingMatrix(eother.pair.next.pair), eother.pair.prev.pair);
+
+                //if (next.isBoundTo(eother.prev) && !prev.isBoundTo(eother.next))
+                //prev.bindTo(next.getBindingMatrix(eother.prev), eother.next);
+                //if (!next.isBoundTo(eother.prev) && prev.isBoundTo(eother.next))
+                //next.bindTo(prev.getBindingMatrix(eother.next), eother.prev);
+
+                if (next.isBoundTo(eother.next) &&
+                    !prev.isBoundTo(eother.prev) &&
+                    m.equalsModuloEpsilon(next.getBindingMatrix(eother.next), EPSILON))
+                    prev.bindEdge(next.getBindingMatrix(eother.next), eother.prev);
+                if (!next.isBoundTo(eother.next) &&
+                    prev.isBoundTo(eother.prev) &&
+                    m.equalsModuloEpsilon(prev.getBindingMatrix(eother.prev), EPSILON))
+                    next.bindEdge(prev.getBindingMatrix(eother.prev), eother.next);
 
-                /*
-                if (next.isBoundTo(eother.prev) && !prev.isBoundTo(eother.next))
-                    prev.bindTo(next.getBindingMatrix(eother.prev), eother.next);
-                if (!next.isBoundTo(eother.prev) && prev.isBoundTo(eother.next))
-                    next.bindTo(prev.getBindingMatrix(eother.next), eother.prev);
-                */
-                if (next.isBoundTo(eother.next) && !prev.isBoundTo(eother.prev))
-                    prev.bindTo(next.getBindingMatrix(eother.next), eother.prev);
-                if (!next.isBoundTo(eother.next) && prev.isBoundTo(eother.prev))
-                    next.bindTo(prev.getBindingMatrix(eother.prev), eother.next);
             }
 
         }
@@ -661,35 +710,24 @@ public class Mesh implements Iterable<Mesh.T> {
         public int compareTo(E e) {
             return e.comparator() > comparator() ? 1 : -1;
         }
-        public void bindEdge(E e, Matrix m) {
-            _bindEdge(e, m);
-            pair._bindEdge(e.pair, m);
+        public void bindEdge(Matrix m, E e) {
+            bindEdge(e, m);
         }
-        public void _bindEdge(E e, Matrix m) {
-            e = e.pair;
-            /*
-            //assumes edges are identical length at binding time
-            Vec reflectionPlaneNormal = e.v2.p.minus(e.v1.p).norm();
-            float a = reflectionPlaneNormal.x;
-            float b = reflectionPlaneNormal.y;
-            float c = reflectionPlaneNormal.z;
-            Matrix reflectionMatrix =
-                new Matrix( 1-2*a*a,  -2*a*b,  -2*a*c, 0,
-                            -2*a*b,  1-2*b*b,  -2*b*c, 0,
-                            -2*a*c,   -2*b*c, 1-2*c*c, 0,
-                            0,       0,       0,       1);
-            m = m.times(Matrix.translate(e.midpoint().minus(Point.ORIGIN))
-                        .times(reflectionMatrix)
-                        .times(Matrix.translate(Point.ORIGIN.minus(e.midpoint()))));
-            System.out.println(reflectionPlaneNormal);
-            System.out.println("  " + v1.p + " " + m.times(e.v1.p));
-            System.out.println("  " + v2.p + " " + m.times(e.v2.p));
-            */
+        public void bindEdge(E e, Matrix m) {
             /*
-            if (m.times(e.v1.p).minus(v1.p).mag() > EPSILON) throw new Error();
-            if (m.times(e.v2.p).minus(v2.p).mag() > EPSILON) throw new Error();
+            for(E e_ : (Iterable<E>)e.getBoundPeers()) {
+                if (e.v1.getPoint().distance((e.getBindingMatrix(e_).times(e_.v1.getPoint()))) > 0.01f)
+                    throw new RuntimeException("blah! " + e.v1.getPoint() + " " + e.getBindingMatrix(e_).times(e_.v1.getPoint()));
+                if (e.v2.getPoint().distance((e.getBindingMatrix(e_).times(e_.v2.getPoint()))) > 0.01f)
+                    throw new RuntimeException("blah! " + e.v2.getPoint() + " " + e.getBindingMatrix(e_).times(e_.v2.getPoint()));
+                if (v1.getPoint().distance(m.times(e.getBindingMatrix(e_).times(e_.v1.getPoint()))) > 0.01f)
+                    throw new RuntimeException("blah! " + v1.getPoint() + " " + m.times(e_.v1.getPoint()));
+                if (v2.getPoint().distance(m.times(e.getBindingMatrix(e_).times(e_.v2.getPoint()))) > 0.01f)
+                    throw new RuntimeException("blah! " + v2.getPoint() + " " + m.times(e_.v2.getPoint()));
+            }
             */
-            this.bindTo(m, e);
+            this.bindTo(m, e,      EPSILON);
+            this.pair.bindTo(m, e.pair, EPSILON);
         }
         
         public void dobind() {
@@ -697,8 +735,6 @@ public class Mesh implements Iterable<Mesh.T> {
                 if (e==this) continue;
                 v1.bindTo(getBindingMatrix(e), e.v1);
                 v2.bindTo(getBindingMatrix(e), e.v2);
-                e.v1.setConstraint(getConstraint());
-                e.v2.setConstraint(getConstraint());
             }
         }
 
@@ -741,8 +777,8 @@ public class Mesh implements Iterable<Mesh.T> {
                 e.v2.getE(mid).pair.pair.bindTo(e.getBindingMatrix(firste), firstq.pair);
             }
             /*
-            first.setConstraint(firste.getConstraint());
-            firstq.setConstraint(firste.getConstraint());
+            first.setConstraint(firste.getAffineConstraint());
+            firstq.setConstraint(firste.getAffineConstraint());
             */
             return nearest(midpoint());
         }
@@ -863,6 +899,7 @@ public class Mesh implements Iterable<Mesh.T> {
         public E(Point v1, Point v2) {
             if (vertices.get(v1) != null) throw new Error();
             if (vertices.get(v2) != null) throw new Error();
+            if (v1.equals(v2)) throw new Error("attempt to create a zero-length edge!");
             this.v1 = new Vertex(v1);
             this.v2 = new Vertex(v2);
             this.prev = this.next = this.pair = new E(this, this, this);
@@ -1074,8 +1111,8 @@ public class Mesh implements Iterable<Mesh.T> {
             }
             */
             /*
-            first.setConstraint(firste.getConstraint());
-            firstq.setConstraint(firste.getConstraint());
+            first.setConstraint(firste.getAffineConstraint());
+            firstq.setConstraint(firste.getAffineConstraint());
             */
             return null;
         }
@@ -1143,11 +1180,9 @@ public class Mesh implements Iterable<Mesh.T> {
         public boolean destroyed() { return destroyed; }
 
         public boolean shouldBeDrawn() {
-
-            if (e1().bindingGroupSize() <= 1) return false;
-            if (e2().bindingGroupSize() <= 1) return false;
-            if (e3().bindingGroupSize() <= 1) return false;
-
+            if (e1().bindingGroupUnconstrained()) return false;
+            if (e2().bindingGroupUnconstrained()) return false;
+            if (e3().bindingGroupUnconstrained()) return false;
             return true;
         }
 
@@ -1165,4 +1200,24 @@ public class Mesh implements Iterable<Mesh.T> {
             super.glVertices(gl, m);
         }
     }
+
+    // Dump /////////////////////////////////////////////////////////////////////////////
+
+    public void dump(OutputStream os) throws IOException {
+        PrintWriter pw = new PrintWriter(new OutputStreamWriter(os));
+        pw.println("solid dump");
+        for(Mesh.T t : this) {
+            Vec normal = t.norm();
+            pw.println("facet normal " + normal.x + " " + normal.y + " " + normal.z);
+            pw.println("  outer loop");
+            for(Mesh.Vertex v : new Mesh.Vertex[] { t.v1(), t.v2(), t.v3() }) {
+                pw.println("    vertex " + v.p.x + " " + v.p.y + " " + v.p.z);
+            }
+            pw.println("  endloop");
+            pw.println("endfacet");
+        }
+        pw.println("endsolid dump");
+        pw.flush();
+    }
+
 }