X-Git-Url: http://git.megacz.com/?p=org.ibex.core.git;a=blobdiff_plain;f=src%2Forg%2Fibex%2Fgraphics%2FMesh.java;fp=src%2Forg%2Fibex%2Fgraphics%2FMesh.java;h=4693b64ab2e0f944c9914196c362dc5efbbaae72;hp=12f71d792dc68660450a60d7a91b17d38c699b71;hb=531e31402f74fa636b2d9a2a367305bfd0777cdc;hpb=b0554ff79ee5f166eab7c660e5a872806df68271 diff --git a/src/org/ibex/graphics/Mesh.java b/src/org/ibex/graphics/Mesh.java index 12f71d7..4693b64 100644 --- a/src/org/ibex/graphics/Mesh.java +++ b/src/org/ibex/graphics/Mesh.java @@ -7,620 +7,861 @@ import java.util.*; import java.util.collections.*; import org.ibex.util.*; +// TODO: +// - allow edge-constraint removal +// +// ~30% of our time spent finding vertices => use a balanced quadtree +// +// - store "which curve is inside me" pointer in Triangle +// - split if two curves enter +// - go to treating Vertex as a value class (epsilon==0) +// - union() +// - subtract() +// - [??] preserve in/out-ness every time we delete() a triangle + /** - * An incremental, adaptive, constrained Delaunay Triangulation. - * @see Kallmann, Bieri, and Thalmann: Fully Dynamic Constrained Delaunay Triangulations + * An incremental, adaptive, addWeighted Delaunay Triangulation. + * @see Kallmann, Bieri, and Thalmann: Fully Dynamic AddWeighted Delaunay Triangulations */ public final class Mesh { - //#define Vertex int - - private static final float epsilon = (float)0.001; + private static final float epsilon = (float)0.0001; + private static final float epsilon2 = (float)0.001; private static final boolean debug = false; - private Vector triangles = new Vector(); - - public static float B = (float)5.0; - public static float Q = (float)0.4; + private Vector triangles = new Vector(); + private Hash edges = new Hash(); + private int numvertices = 0; + private Triangle triangle0 = null; + private Vertex vertex0 = null; + private Vertex vertex1 = null; + private Vertex start = null; + private Vertex last = null; + + public Mesh copy() { + Mesh m = new Mesh(); + m.vertex(triangle0.v(1)); + m.vertex(triangle0.v(2)); + m.vertex(triangle0.v(3)); + Object[] edges = this.edges.vals(); + for(int i=0; i= this.x.length-1) { - if (debug) Log.debug(Mesh.class, "expanding vertex arrays from " + this.x.length + " to " + this.x.length*2); - float[] newx = new float[this.x.length * 2]; - float[] newy = new float[this.y.length * 2]; - System.arraycopy(this.x, 0, newx, 0, this.x.length); - System.arraycopy(this.y, 0, newy, 0, this.y.length); - this.x = newx; - this.y = newy; - } - this.x[numvertices] = x; - this.y[numvertices] = y; - return numvertices++; + // Chain ////////////////////////////////////////////////////////////////////////////// + + public static interface Chain { + public Mesh.Chain getMeshChainParent(); + public Affine getAffine(); + public Mesh getMesh(); } - public int numvertices = 0; - public float x[] = new float[255]; - public float y[] = new float[255]; - public String s(Vertex v) { return "("+x(v)+","+y(v)+")"; } - public float x(Vertex v) { return x[v]; } - public float y(Vertex v) { return y[v]; } - - public static final Vertex NULLVERTEX = -1; - - public Vertex newVertex(float x, float y) { - Vertex ret = NULLVERTEX; - for(int k=0; k 0) { + Triangle t = iter[--numiter]; + if (t.tick >= this.tick) continue; + switch(mode) { + case ITERATE_INTERSECT: + case ITERATE_SUBTRACT: { + if (!t.in) break; + boolean oin = m.queryPoint(t.c().multiply(a)); + t.in = (mode==ITERATE_SUBTRACT) ? (t.in && !oin) : (t.in && oin); + break; } - if (!bad) { - vec.addElement(newTriangle(v,t.v1,t.v2, "hull expansion")); - good = true; + case ITERATE_ADD: { + for(int i=1; i<=3; i++) { + Edge e = t.e(i); + if (e.t1 != null && e.t1.tick >= this.tick) continue; + if (e.t2 != null && e.t2.tick >= this.tick) continue; + if (!e.locked()) continue; + if ((e.t1==null || !e.t1.in) && (e.t2==null || !e.t2.in)) continue; + Point p1 = e.v(1).multiply(a); + Point p2 = e.v(2).multiply(a); + Vertex v1=null, v2=null; + v1 = m.vertex(p1); + v2 = m.vertex(p2); + if (v1==v2) continue; + m.getEdge(v1, v2).lock(v1, 0); } + break; } - //#end } - break; + t.tick = this.tick; + for(int i=1; i<=3; i++) { + Triangle ti = t.t(i); + if (ti == null) continue; + if (ti.tick >= this.tick) continue; + iter[numiter++] = ti; + } } - if (!good) throw new Error("couldn't figure out where to put " + s(v)); - fixup(vec, v); - return v; } - public void fixup(Vector vec, Vertex v) { - while(vec.size() > 0) { - Triangle t = (Triangle)vec.lastElement(); - vec.setSize(vec.size() - 1); - if (t.deleted) continue; - //if (t.wind(t.nextVertex(v), t.prevVertex(v)) != 0) continue; // FIXME - Triangle to = t.opposingTriangle(v); - if (to==null) continue; //throw new Error("fixup invoked with a triangle which does not have me as a vertex"); - if (to.deleted) throw new Error("v should not happen"); - if (to.r < distance(v,to.ccx, to.ccy)) continue; - Vertex v2 = to.opposingVertex(t); - t.delete(); - to.delete(); - vec.addElement(newTriangle(v, t.nextVertex(v), v2, "fixup")); - vec.addElement(newTriangle(v, t.prevVertex(v), v2, "fixup")); - } + public void clipOp(Mesh m, Affine a, boolean subtract) { + seekTime = 0; + long start = System.currentTimeMillis(); + m.add(this, a); + iterateTriangles(subtract ? ITERATE_SUBTRACT : ITERATE_INTERSECT, m, a.inverse()); + float total = (float)((System.currentTimeMillis() - start)); + float seek = (float)seekTime; + if (total > 80) System.out.println("clip in " + (100 * (seek/total)) + "%"); } - // Static ////////////////////////////////////////////////////////////////////////////// - - public Triangle newTriangle(Vertex v1, Vertex v2, Vertex v3, String source) { - if (v1==v2 || v2==v3 || v3==v1) throw new Error("identical vertices"); - for(int i=0; i 0) return b>=0 ? -1 : 1; + if (c < 0) return b>=0 ? 1 : -1; + return 0; + } - public boolean triangulate(Vertex vv, HashSet orphans, Vertex v, boolean first) { - if (first) { - HashSet left = new HashSet(); - HashSet right = new HashSet(); - for(Iterator it=orphans.iterator(); it.hasNext();) { - Vertex o = ((Integer)it.next()).intValue(); - if (o==v||o==vv) continue; - if (side(vv, v, o) == -1) left.add(new Integer(o)); - else if (side(vv, v, o) == 1) right.add(new Integer(o)); - else throw new Error("impossible "+vv+" "+v + " " + o); - } - triangulate(vv, left, v, false); - triangulate(v, right, vv, false); - return false; - } - Vertex farthest = NULLVERTEX; - float dist = 0; - if (orphans.size()==0) return true; - Vertex o = ((Integer)orphans.iterator().next()).intValue(); - if (orphans.size()==1) { - Triangle t = newTriangle(vv, v, o, "triangulate2 " + v + " " + vv); - if (((t.v1==vv && t.v2==v)||(t.v1==v && t.v2==vv)) && t.t12==null) return true; - if (((t.v3==vv && t.v2==v)||(t.v3==v && t.v2==vv)) && t.t23==null) return true; - if (((t.v1==vv && t.v3==v)||(t.v1==v && t.v3==vv)) && t.t31==null) return true; - return false; - } + // Vertex ////////////////////////////////////////////////////////////////////////////// - Vertex best = NULLVERTEX; - OUTER: for(Iterator it=orphans.iterator(); it.hasNext();) { - o = ((Integer)it.next()).intValue(); - if (o==v) continue; - Triangle t = new Triangle("temp", vv, v, o, true); - for(Iterator it2=orphans.iterator(); it2.hasNext();) { - Vertex z = ((Integer)it2.next()).intValue(); - if (z==o) continue; - if (z==v) continue; - if (z==vv) continue; - if (distance(z,t.ccx,t.ccy) < t.r || t.contains(z)) continue OUTER; - } - if (best==NULLVERTEX || new Triangle("temp",vv,v,best,true).area() > new Triangle("temp",vv,v,o,true).area()) - best = o; - } - if (best==NULLVERTEX) throw new Error("notgood, #orphans = " + orphans.size()); - o = best; - HashSet left = new HashSet(); - HashSet right = new HashSet(); - for(Iterator it2=orphans.iterator(); it2.hasNext();) { - Vertex x = ((Integer)it2.next()).intValue(); - if (o==x||vv==x||v==x) continue; - int side_of_x = side(o,(x(vv)+x(v))/2,(y(vv)+y(v))/2,x); - int side_of_v = side(o,(x(vv)+x(v))/2,(y(vv)+y(v))/2,v); - int side_of_vv = side(o,(x(vv)+x(v))/2,(y(vv)+y(v))/2,vv); - if (side_of_x==side_of_vv && side_of_vv!=0) right.add(new Integer(x)); - else if (side_of_x==side_of_v && side_of_v!=0) left.add(new Integer(x)); - else throw new Error("impossible "+vv+" "+v + " " + o + " ==> " + x); + public Vertex vertex(Point p, Affine a) { return vertex(p.multiply(a)); } + public Vertex vertex(Point p) { + Vertex ret = null; + switch(numvertices) { + case 0: return (vertex0 = new Vertex(p)); + case 1: return vertex0.distance(p)<=epsilon ? vertex0 : (vertex1 = new Vertex(p)); + case 2: { + if (vertex0.distance(p)<=epsilon) return vertex0; + if (vertex1.distance(p)<=epsilon) return vertex1; + Vertex v2 = new Vertex(p); + triangle(newEdge(vertex0,vertex1), newEdge(vertex1,v2), newEdge(v2,vertex0)); + return v2; } - boolean doit = true; - doit &= triangulate(o, left, v, false); - doit &= triangulate(o, right, vv, false); - if (doit) { - Triangle t = newTriangle(v, vv, o,"triangulate 0/0"); - if (((t.v1==vv && t.v2==v)||(t.v1==v && t.v2==vv)) && t.t12==null) return true; - if (((t.v3==vv && t.v2==v)||(t.v3==v && t.v2==vv)) && t.t23==null) return true; - if (((t.v1==vv && t.v3==v)||(t.v1==v && t.v3==vv)) && t.t31==null) return true; - return false; + default: { + Triangle t = null; + t = triangle0.seek(p); + for(int i=1; i<=3; i++) + for(int j=1; j<=2; j++) + if (t != null && t.e(i).v(j).distance(p)<=epsilon) return t.e(i).v(j); + Vertex v = new Vertex(p); + if (t.e(3).intersects(p)) t.e(3).bisect(v); + else if (t.e(1).intersects(p)) t.e(1).bisect(v); + else if (t.e(2).intersects(p)) t.e(2).bisect(v); + else if (t.contains(v)) t.trisect(v); + else t.addHull(v); + return v; } - return true; } + } - public void force() { - while(true) { - boolean redo = false; - for(int k=0; k= Math.min(p1.x,p2.x) && + y <= Math.max(p1.y,p2.y) && + y >= Math.min(p1.y,p2.y); } } - public boolean force(Vertex vv) { - boolean ret = false; - OUTER: for(Iterator it=next(vv).iterator(); it.hasNext();) { - Vertex v = ((Integer)it.next()).intValue(); - for(int k=0; k 0; } + public boolean partitions(Point va, Point vb) { return side(va,vb)==-1; } + public int side(Point a, Point b) { return side(a) * side(b); } + public int side(Point a) { return Mesh.side(v(1), v(2), a); } + public boolean hasVertex(Vertex v) { return v1==v || v2==v; } + public boolean hasTriangle(Triangle t) { return t==t1 || t==t2; } + public String toString() { return v(1) + "--" + v(2); } + public void rmTriangle(Triangle t) { if (t1==t) t1 = null; else if (t2==t) t2 = null; else throw new Error(); } + public boolean convex() { return this.intersects(t1.opposingVertex(t2), t2.opposingVertex(t1)); } + + public boolean colinear(Point v) { return area(v,v1,v2)<=epsilon; } + + public boolean intersects(Point p) { + return + side(p)==0 && + p.x <= Math.max(v1.x,v2.x) && + p.x >= Math.min(v1.x,v2.x) && + p.y <= Math.max(v1.y,v2.y) && + p.y >= Math.min(v1.y,v2.y); + } + public boolean intersects(Edge e) { return intersects(e.v(1), e.v(2)); } + public boolean intersects(Point va, Point vb) { + return + !v1.equals(va) && + !v1.equals(vb) && + !v2.equals(va) && + !v2.equals(vb) && + partitions(va, vb) && + Mesh.side(va, vb, v1) * Mesh.side(va, vb, v2) == -1; + } + public Triangle opposingTriangle(Triangle t) { + if (t1 == t) return t2; + if (t2 == t) return t1; + throw new Error(); + } + public void addTriangle(Triangle tnew) { + if (t1==null) t1 = tnew; + else if (t2==null) t2 = tnew; + else throw new Error("attempted to addTriangle("+tnew+")\n t1="+t1+"\n t2="+t2); + if (t1==t2) throw new Error("same triangle can't be both sides of an edge"); + if (v1.x==v2.x) { + boolean b = side(t1.opposingVertex(this)) == side(point(Float.MAX_VALUE, v1.y)); + Triangle right = b ? t1 : t2; // right side + Triangle left = b ? t2 : t1; // left side + t1 = right; + t2 = left; + } else { + boolean b = side(t1.opposingVertex(this)) == side(point(0, Float.MAX_VALUE)); + Triangle top = b ? t1 : t2; + Triangle bottom = b ? t2 : t1; + if (v1.y==v2.y) { // horizontal + t1 = bottom; + t2 = top; + } else if (v1.x > v2.x) { // positive slope + t1 = top; + t2 = bottom; + } else { // negative slope + t1 = bottom; + t2 = top; + } } - if (!redo) break; } - } - public boolean makeNonDegenerate(Vector vec, Vertex v1) { - for(int i2 = 0; i2 " + v2); + if (tlast == t) { if (t.hasVertex(v1)) continue; throw new Error("no predecessor"); } + Edge e = t.getSharedEdge(tlast); + if (!e.convex()) continue; + if (e.v(1)==v1 || e.v(1)==v2 || e.v(2)==v1 || e.v(2)==v2) { continue; } + if (this.intersects(e.v(1))) { fracture(e.v(1)); return; } + if (this.intersects(e.v(2))) { fracture(e.v(2)); return; } + if (!this.intersects(e)) continue; + if (e.locked()) { + fracture(e); + return; + } else { + Edge eold = e = e.flip(); + t = e.t1; + if (t==null || !t.intersects(this)) t = e.t2; + told = t; + if (eold.intersects(this)) { + t = t.followVector(v1,v2); + if (t != e.t1 && t != e.t2) t = told; + continue; + } else { + told = null; + while(t.intersects(this)) { + if (t==told) break; + told = t; + t = t.followVector(v2,v1); + } + t = told; + told = null; + continue; } } } + if (t1!=null) t1.fixup(); + if (t2!=null) t2.fixup(); + } + + public void stroke(PixelBuffer buf, Affine a, int color) { + int c = debug + ? (weight() == 0 ? color : 0xffff0000) + : (weight() != 0 ? color : 0); + if (c != 0) buf.drawLine(v1.xi(a), v1.yi(a), v2.xi(a), v2.yi(a), c); + } + public Edge(Vertex v1, Vertex v2) { + boolean b = v1.y < v2.y || (v1.y==v2.y && v1.x < v2.x); + this.v1 = b ? v1 : v2; + this.v2 = b ? v2 : v1; + edges.put(v1, v2, this); + edges.put(v2, v1, this); } - return false; } // Triangle ////////////////////////////////////////////////////////////////////////////// - private final class Triangle { - Triangle t12=null, t23=null, t31=null; - final Vertex v1, v2, v3; + public Triangle triangle(Edge e1, Edge e2, Edge e3) { + float x = (e1.v(1).x+e1.v(2).x+e2.v(1).x+e2.v(2).x+e3.v(1).x+e3.v(2).x)/6; + float y = (e1.v(1).y+e1.v(2).y+e2.v(1).y+e2.v(2).y+e3.v(1).y+e3.v(2).y)/6; + Point p = point(x,y); + Triangle t = triangle0==null ? null : triangle0.seek(p); + if (t != null && + (t.contains(p) || t.intersects(p)) && + t.hasEdge(e1) && + t.hasEdge(e2) && + t.hasEdge(e3)) + return triangle0 = t; + t = new Triangle(e1, e2, e3); + if (debug) t.check(); + if (triangle0 == null) triangle0 = t; + return t; + } + + public static boolean fixing = false; + private final class Triangle implements org.ibex.arenaj.Gladiator { + + final float r2; + final Point cc; + + private Edge e1, e2, e3; // should be final =( + public int tick; + boolean in = false; - boolean deleted = false; + boolean inWasSet = false; boolean painted = false; - boolean special = false; - final String source; - final float ccx, ccy, r; - - public boolean hasEdge(Vertex a, Vertex b) { return a!=b && (a==v1||a==v2||a==v3) && (b==v1||b==v2||b==v3); } - public Vertex opposingVertex(Triangle t) { return t12==t ? v3 : t23==t ? v1 : t31==t ? v2 : NULLVERTEX; } - public Triangle opposingTriangle(Vertex v) { return v1==v ? t23 : v2==v ? t31 : v3==v ? t12 : null; } - public Vertex nextVertex(Vertex v) { return v1==v ? v2 : v2==v ? v3 : v3==v ? v1 : NULLVERTEX; } - public Vertex prevVertex(Vertex v) { return v1==v ? v3 : v2==v ? v1 : v3==v ? v2 : NULLVERTEX; } - public String toString() { return "<<"+v1+""+v2+""+v3+">>"; } - public boolean intersectsSegment(Vertex a, Vertex b) { return isect(v1,v2,a,b) || isect(v3,v2,a,b) || isect(v1,v3,a,b); } - public float area() { - float a = distance(v1,v2); - float b = distance(v2,v3); - float c = distance(v3,v1); - float s = (a+b+c)/2; - return (float)Math.sqrt(s*(s-a)*(s-b)*(s-c)); - } - - public boolean checkSkinny() { - if (!(r / Math.min(Math.min(distance(v1,v2), distance(v2,v3)), distance(v3,v1)) > B)) return false; - if (debug) Log.debug(this,"skinny triangle " + this); - for (Vertex v=0; v>"; } + + public void stroke(PixelBuffer buf, Affine a, int color) { + for(int i=1; i<=3; i++) if (in || debug) e(i).stroke(buf, a, color); + } + public Triangle fixup() { + if (!dirty) return this; + dirty = false; + for(int i=1; i<=3; i++) { + Triangle t = t(i); + if (t==null) continue; + if (t.r2 <= v(i).distance2(t.cc)) continue; + Edge e = e(i); + if (e.locked()) { t.fixup(); continue; } + return e.flip().t1.fixup(); + } + return this; + } + public void addHull(Vertex vnew) { + Edge e = e(1).hasTriangle(null) ? e(1) : e(2).hasTriangle(null) ? e(2) : e(3).hasTriangle(null) ? e(3) : null; + Triangle t = e.opposingTriangle(null), newt = null; + while (!e.partitions(vnew, t.opposingVertex(e))) { + e = e.rotateHull(true); + t = e.opposingTriangle(null); + } + Edge ea = newEdge(e.v(1), vnew); + Edge eb = newEdge(e.v(2), vnew); + newt = triangle(e, ea, eb); + if (ea.rotateHull(true) != eb) { Edge temp = ea; ea = eb; eb = temp; } + for(int i=1; i<=2; i++) + for(Edge ex = i==1?eb:ea; ;) { + e = ex.rotateHull(i==1); + t = e.opposingTriangle(null); + if (!e.partitions(vnew, t.opposingVertex(e))) break; + Edge ep = newEdge(vnew, e.unCommonVertex(ex)); + newt = triangle(e, ex, ep); + ex = ep; + } + if (newt==null) throw new Error("couldn't find a place to add a triangle for " + vnew); + newt.fixup(); + } + public Triangle seek(Point p) { + Triangle t = this; + try { + while (true) { + if (t.contains(p) || t.intersects(p)) return t; + else if (t.e(3).intersects(p)) return (t.t(3)!=null && t.t(3).contains(p)) ? t.t(3) : t; + else if (t.e(1).intersects(p)) return (t.t(1)!=null && t.t(1).contains(p)) ? t.t(1) : t; + else if (t.e(2).intersects(p)) return (t.t(2)!=null && t.t(2).contains(p)) ? t.t(2) : t; + else { + Triangle t2 = t.followVector(t.c(), p); + if (t2==null || t2==t) return t; + t = t2; } } - newVertex(ccx, ccy); - return true; + } finally { if (t!=null) triangle0 = t; } } - public void fill(PixelBuffer buf, Affine a, int color, boolean evenOdd, int count, boolean strokeOnly) { - if (painted) return; - if (deleted) throw new Error("gargh!"); - if (!strokeOnly) { - if (special) { - buf.fillTriangle((int)a.multiply_px((float)x(v1), (float)y(v1)), - (int)a.multiply_py((float)x(v1), (float)y(v1)), - (int)a.multiply_px((float)x(v2), (float)y(v2)), - (int)a.multiply_py((float)x(v2), (float)y(v2)), - (int)a.multiply_px((float)x(v3), (float)y(v3)), - (int)a.multiply_py((float)x(v3), (float)y(v3)), - 0xff0000ff); - } else if ((evenOdd && count%2!=0) || (!evenOdd && count!=0)) { - buf.fillTriangle((int)a.multiply_px((float)x(v1), (float)y(v1)), - (int)a.multiply_py((float)x(v1), (float)y(v1)), - (int)a.multiply_px((float)x(v2), (float)y(v2)), - (int)a.multiply_py((float)x(v2), (float)y(v2)), - (int)a.multiply_px((float)x(v3), (float)y(v3)), - (int)a.multiply_py((float)x(v3), (float)y(v3)), - color); - } else { - if (debug) - buf.fillTriangle((int)a.multiply_px((float)x(v1), (float)y(v1)), - (int)a.multiply_py((float)x(v1), (float)y(v1)), - (int)a.multiply_px((float)x(v2), (float)y(v2)), - (int)a.multiply_py((float)x(v2), (float)y(v2)), - (int)a.multiply_px((float)x(v3), (float)y(v3)), - (int)a.multiply_py((float)x(v3), (float)y(v3)), - 0xff000000 | ((color & 0x00ffffff) >> 8)); + // gives the triangle across the edge through which the ray v(1)-->v(2) exits this triangle + public Triangle followVector(Point p1, Point p2) { + Triangle ret = followVector2(p1, p2); + if (ret==null) return ret; + triangle0 = ret; + if (!ret.encounters(p1,p2)) return this; + return ret; + } + public Triangle followVector2(Point p1, Point p2) { + if (contains(p2) || intersects(p2) || v(1).equals(p2) || v(2).equals(p2) || v(3).equals(p2)) return this; + for(int i=1; i<=3; i++) if (!v(i).equals(p1) && v(i).intersects(p1,p2)) return followVector(v(i),p2); + Triangle t1 = t(1); + Triangle t2 = t(2); + Triangle t3 = t(3); + for(int i=1; i<=3; i++) { + int k1 = i==1?3:i==2?1:i==3?2:0; + int k2 = i==1?2:i==2?3:i==3?1:0; + int k3 = i==1?1:i==2?2:i==3?3:0; + if (v(i).equals(p1)) { + if (e(k1).partitions(v(k1),p2)) return t(k1); + if (e(k2).partitions(v(k2),p2)) return t(k2); + if (e(k3).partitions(v(k3),p2)) return t(k3); + throw new Error("bad!"); } } - painted = true; - //#repeat t12/t23/t31 v1/v2/v3 v2/v3/v1 - if (t12 != null && !t12.painted) { - t12.fill(buf, a, color, evenOdd, count + wind(v1, v2), strokeOnly); - if (debug) - drawLine(buf, (x(v1)*2+x(v2))/3, (y(v1)*2+y(v2))/3, (x(v1)+2*x(v2))/3, (y(v1)+2*y(v2))/3, a, - wind(v2,v1)==0?0xff000000:0xff0000ff); + if (!e(1).intersects(p1,p2) && !e(2).intersects(p1,p2) && !e(3).intersects(p1,p2)) + throw new Error("invoked followVector() on a Triangle which it does not encounter:\n" + + " p1=" + p1 + "\n" + + " p2=" + p2 + "\n" + + " t =" + this + " (area "+area(v(1),v(2),v(3))+")\n"); + for(int i=1; i<=3; i++) if (e(i).intersects(p1,p2) && e(i).side(v(i)) * e(i).side(p2) == -1) return t(i); + throw new Error("giving up: \n "+p1+" -> "+p2+"\n " + this); + } + + public void check() { + if (debug) { + for(int i=1; i<=3; i++) { + if (e(i).v(1) != v(1) && e(i).v(1) != v(2) && e(i).v(1) != v(3)) throw new Error("inconsistent"); + if (e(i).t1 != this && e(i).t2 != this) throw new Error("inconsistent"); + if (e(i).t1 == e(i).t2) throw new Error("same triangle on both sides of edge"); + } + if (e(1)==e(2) || e(2)==e(3) || e(3)==e(1)) throw new Error("identical edges"); + for(int i=1; i<=3; i++) { + if (t(i) != null) if (!t(i).hasEdge(e(i))) throw new Error("t1 doesn't have e(1)"); + if (t(i) != null) { + if (t(i).getSharedEdge(this) != e(i)) throw new Error("blark"); + if (!e(i).hasTriangle(t(i))) throw new Error("blark2"); + if (!e(i).hasTriangle(this)) throw new Error("blark3"); + } + } + // check that edges all join up } - //#end } - public int wind(Vertex a, Vertex b) { - if (next(a).contains(new Integer(b)) && next(b).contains(new Integer(a))) return 0; - if (y(a) < y(b) || (y(a)==y(b) && x(a)0; i--) if (e(i).isNear(v)) { e(i).bisect(v); return; } + Triangle a=null,b=null,c=null; + + boolean oldIn = in; + Edge v1v = newEdge(v(1), v); + Edge v2v = newEdge(v(2), v); + Edge v3v = newEdge(v(3), v); + Edge e1 = this.e(1); + Edge e2 = this.e(2); + Edge e3 = this.e(3); + delete(); + + a = triangle(e3, v1v, v2v); + b = triangle(e2, v1v, v3v); + c = triangle(e1, v3v, v2v); + a.in = oldIn; + b.in = oldIn; + c.in = oldIn; + a.fixup(); } - public void clear() { - if (!painted) return; - painted = false; - if (t12 != null) t12.clear(); - if (t23 != null) t23.clear(); - if (t31 != null) t31.clear(); - } - public Triangle(String source, Vertex v1, Vertex v2, Vertex v3) { this(source, v1, v2, v3, false, false); } - public Triangle(String source, Vertex v1, Vertex v2, Vertex v3, boolean fake) { this(source, v1,v2,v3,fake,false); } - public Triangle(String source, Vertex v1, Vertex v2, Vertex v3, boolean fake, boolean ignoreProblems) { - this.source = source; + + public void setIn(boolean evenOdd, int weight) { + if (inWasSet) return; + inWasSet = true; + in = (evenOdd && weight%2!=0) || (!evenOdd && weight!=0); + for(int i=1; i<=3; i++) if (t(i) != null) t(i).setIn(evenOdd, weight + e(i).weight()); + } + + public void fill(PixelBuffer buf, Affine a, Mesh.Chain clip, int color, boolean strokeOnly) { + if (painted) return; + painted = true; + if (in) buf.fillTriangle(v(1).xi(a), v(1).yi(a), v(2).xi(a), v(2).yi(a), v(3).xi(a), v(3).yi(a), color); + for(int i=1; i<=3; i++) + if (t(i) != null) { + boolean prepaint = t(i).painted; + if (debug) e(i).stroke(buf, a, color); + t(i).fill(buf, a, clip, color, strokeOnly); + } + } + + public Triangle(Edge e1, Edge e2, Edge e3) { + this.e1 = e1; + this.e2 = e2; + this.e3 = e3; + Vertex v1 = e(2).unCommonVertex(e(1)); + Vertex v2 = e(3).unCommonVertex(e(2)); + Vertex v3 = e(1).unCommonVertex(e(3)); + if (e(1).intersects(v1)) throw new Error("triangle points are colinear"); + if (e(2).intersects(v2)) throw new Error("triangle points are colinear"); + if (e(3).intersects(v3)) throw new Error("triangle points are colinear"); + e(1).addTriangle(this); + e(2).addTriangle(this); + e(3).addTriangle(this); + float a = 0; - for(int i=0; i<3; i++) { - a=(y(v2)-y(v3))*(x(v2)-x(v1))-(y(v2)-y(v1))*(x(v2)-x(v3)); - if (a!=0) break; - Vertex t = v2; v2=v3; v3=v1; v1 = t; + Q: for(int q=0; q<2; q++) { + for(int i=0; i<3; i++) { + if ((a=(v2.y-v3.y)*(v2.x-v1.x)-(v2.y-v1.y)*(v2.x-v3.x))!=0) break Q; + Vertex t = v2; v2=v3; v3=v1; v1 = t; + } + Vertex t = v2; v2=v3; v3=t; } if (a==0) throw new Error("a==0 for " + v1 + " " + v2 + " " + v3); - this.v1 = v1; this.v2 = v2; this.v3 = v3; - float a1=(x(v1)+x(v2))*(x(v2)-x(v1))+(y(v2)-y(v1))*(y(v1)+y(v2)); - float a2=(x(v2)+x(v3))*(x(v2)-x(v3))+(y(v2)-y(v3))*(y(v2)+y(v3)); - ccx=(a1*(y(v2)-y(v3))-a2*(y(v2)-y(v1)))/a/2; - ccy=(a2*(x(v2)-x(v1))-a1*(x(v2)-x(v3)))/a/2; - r = distance(v1,ccx, ccy); - if (fake) return; - for(int i=0; i y2) ? -1 : (- (a*x2+c)/b < y2) ? 1 : 0; + public void newcontour() { + if (start != null) add(start.x, start.y); + start = null; + last = null; } - public boolean isect(Vertex v1, Vertex v2, Vertex v3, Vertex v4) { - if (v1==v3 || v1==v4 || v2==v3 || v2==v4) return false; - float a = side(v1,v2,v3); - float b = side(v1,v2,v4); - float c = side(v3,v4,v1); - float d = side(v3,v4,v2); - return a!=b && c!=d && a!=0 && b!=0 && c!=0 && d!=0; + public Mesh addRect(float x, float y, float w, float h) { + if (w==0 || h==0) return this; + Vertex v1 = vertex(point(x,y)); + Vertex v2 = vertex(point(x+w,y)); + Vertex v3 = vertex(point(x+w,y+h)); + Vertex v4 = vertex(point(x,y+h)); + newEdge(v1,v2).lock(v1,1); + newEdge(v2,v3).lock(v2,1); + newEdge(v3,v4).lock(v3,1); + newEdge(v4,v1).lock(v4,1); + setIn(true); + return this; } - public boolean isect(Vertex v1, Vertex v2, Vertex v) { - return - side(v1,v2,v)==0 && - x(v) - Math.max(x(v1),x(v2)) < 0 && - x(v) - Math.min(x(v1),x(v2)) > 0 && - y(v) - Math.max(y(v1),y(v2)) < 0 && - y(v) - Math.min(y(v1),y(v2)) > 0; + public void add(float x, float y) { + Vertex vx = vertex(point(x,y)); + if (vx==last) return; + if (start==null) start = vx; + try { + if (last==null) return; + if (numvertices<3) return; + if (numvertices==3) getEdge(start, last).lock(start,1); + getEdge(last,vx).lock(last,1); + } finally { + last = vx; + } } - }