+ public boolean hasE(E e) { return e1==e || e2==e || e3==e; }
+ public void glVertices(GL gl) {
+ p1().glVertex(gl);
+ p2().glVertex(gl);
+ p3().glVertex(gl);
+ }
+ public P p1() { return e1.shared(e2); }
+ public P p2() { return e1.shared(e3); }
+ public P p3() { return e3.shared(e2); }
+ public P centroid() { return newP((p1().x+p2().x+p3().x)/3,
+ (p1().y+p2().y+p3().y)/3,
+ (p1().z+p2().z+p3().z)/3); }
+ public float diameter() {
+ // FIXME: what is this supposed to be?
+ return Math.max(Math.max(e1.length(), e2.length()), e3.length()) / 2;
+ }
+
+ // FIXME: ambiguity firstEdge or secondEdge?
+ /** returns the next triangle walking "around" shared vertex p */
+ public T next(P p) { return secondEdge(p).other(this); }
+
+ public E firstEdge(P p) {
+ if (p == e1.shared(e2)) return e1;
+ else if (p == e2.shared(e3)) return e2;
+ else if (p == e3.shared(e1)) return e3;
+ else throw new Error("triangle " + this + " does not own point " + p);
+ }
+
+ public E secondEdge(P p) {
+ if (p == e1.shared(e2)) return e2;
+ else if (p == e2.shared(e3)) return e3;
+ else if (p == e3.shared(e1)) return e1;
+ else throw new Error("triangle " + this + " does not own point " + p);
+ }
+
+ /** returns the angle at point p */
+ public double angle(P p) {
+ V v1 = firstEdge(p).other(p).minus(p);
+ V v2 = secondEdge(p).other(p).minus(p);
+ return Math.acos(v1.norm().dot(v2.norm()));
+ }
+