From: adam Date: Mon, 14 Feb 2005 04:18:24 +0000 (+0000) Subject: imported initial version of Mesh.java X-Git-Url: http://git.megacz.com/?p=org.ibex.core.git;a=commitdiff_plain;h=467c56cf21b99b367b3803e4283ece55c85e0d07 imported initial version of Mesh.java darcs-hash:20050214041824-5007d-e8adfd9700b3ea9ad2c17a62e36a52050b0906f2.gz --- diff --git a/src/org/ibex/graphics/Mesh.java b/src/org/ibex/graphics/Mesh.java new file mode 100644 index 0000000..43875eb --- /dev/null +++ b/src/org/ibex/graphics/Mesh.java @@ -0,0 +1,868 @@ +// Copyright 2000-2005 the Contributors, as shown in the revision logs. +// Licensed under the GNU General Public License version 2 ("the License"). +// You may not use this file except in compliance with the License. + +package org.ibex.graphics; +import java.util.*; +import java.util.collections.*; +import org.ibex.util.*; + +/** + * An incremental, adaptive, constrained Delaunay Triangulation. + * @see Kallmann, Bieri, and Thalmann: Fully Dynamic Constrained Delaunay Triangulations + */ +public final class Mesh { + + private static final double epsilon = (double)0.00000001; + private static final boolean debug = true; + + private Vector vertices = new Vector(); + private Vector triangles = new Vector(); + + public static double B = 5; + public static double Q = 0.4; + + // Utilities ////////////////////////////////////////////////////////////////////////////// + + public static double distance(double x1, double y1, double x2, double y2) { + return (double)Math.sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)); + } + public static int side(Vertex v1, Vertex v2, Vertex v3) { return side(v1, v2, v3.x, v3.y); } + public static int side(Vertex v1, Vertex v2, double x, double y) { return side(v1, v2.x, v2.y, x, y); } + public static int side(Vertex v1, double x, double y, Vertex v3) { return side(v1, x, y, v3.x, v3.y); } + public static int side(Vertex v1, double x, double y, double x2, double y2) { + double a = y-v1.y, b = v1.x-x, c = -1 * (a*v1.x+b*v1.y); + return (- (a*x2+c)/b > y2) ? -1 : (- (a*x2+c)/b < y2) ? 1 : 0; + } + + public boolean isect(Vertex v1, Vertex v2, Vertex v3, Vertex v4) { + if (v1==v3 || v1==v4 || v2==v3 || v2==v4) return false; + double a = side(v1,v2,v3); + double b = side(v1,v2,v4); + double c = side(v3,v4,v1); + double d = side(v3,v4,v2); + return a!=b && c!=d && a!=0 && b!=0 && c!=0 && d!=0; + } + + private void drawLine(PixelBuffer buf, double x1, double y1, double x2, double y2, Affine a, int color) { + buf.drawLine((int)a.multiply_px((float)x1, (float)y1), + (int)a.multiply_py((float)x1, (float)y1), + (int)a.multiply_px((float)x2, (float)y2), + (int)a.multiply_py((float)x2, (float)y2), + color); + } + public void fill(PixelBuffer buf, Affine a, int color, boolean evenOdd, boolean strokeOnly) { + for (int i=0; i good twice"); + if (t.contains(this)) { + System.out.println(t + " contains me"); + Triangle t12 = t.t12; + Triangle t23 = t.t23; + Triangle t31 = t.t31; + t.delete(vec); + vec2.addElement(newTriangle(this, t.v1, t.v2, "vertexinsertion " + this)); + vec2.addElement(newTriangle(this, t.v3, t.v2, "vertexinsertion " + this)); + vec2.addElement(newTriangle(this, t.v1, t.v3, "vertexinsertion " + this)); + fixup(vec2); + good = true; + break; + } else if (t.t23 != null && v.intersectsSegment(t.v2, t.v3)) { + System.out.println("on an edge"); + good = true; + Triangle tt = t.t23; + tt.delete(null); + t.delete(null); + Vertex va = + ((tt.v1==t.v3&&tt.v2==t.v2)||(tt.v1==t.v2&&tt.v2==t.v3)) ? tt.v3 : + ((tt.v2==t.v3&&tt.v3==t.v2)||(tt.v2==t.v2&&tt.v3==t.v3)) ? tt.v1 : + ((tt.v1==t.v3&&tt.v3==t.v2)||(tt.v1==t.v2&&tt.v3==t.v3)) ? tt.v2 : null; + vec2.addElement(newTriangle(this, t.v1, t.v3, "vertexinsertion " + this)); + vec2.addElement(newTriangle(this, t.v1, t.v2, "vertexinsertion " + this)); + vec2.addElement(newTriangle(this, t.v3, va , "vertexinsertion " + this)); + vec2.addElement(newTriangle(this, t.v2, va , "vertexinsertion " + this)); + fixup(vec2); + break; + } else if (t.t31 != null && v.intersectsSegment(t.v1, t.v3)) { + good = true; + System.out.println("on an edge"); + Triangle tt = t.t31; + tt.delete(null); + t.delete(null); + Vertex va = + ((tt.v1==t.v1&&tt.v2==t.v3)||(tt.v1==t.v3&&tt.v2==t.v1)) ? tt.v3 : + ((tt.v2==t.v1&&tt.v3==t.v3)||(tt.v2==t.v3&&tt.v3==t.v1)) ? tt.v1 : + ((tt.v1==t.v1&&tt.v3==t.v3)||(tt.v1==t.v3&&tt.v3==t.v1)) ? tt.v2 : null; + vec2.addElement(newTriangle(this, t.v2, t.v1, "vertexinsertion " + this)); + vec2.addElement(newTriangle(this, t.v2, t.v3, "vertexinsertion " + this)); + vec2.addElement(newTriangle(this, t.v1, va , "vertexinsertion " + this)); + vec2.addElement(newTriangle(this, t.v3, va , "vertexinsertion " + this)); + fixup(vec2); + break; + } else if (t.t12 != null && v.intersectsSegment(t.v1, t.v2)) { + System.out.println("on an edge"); + good = true; + Triangle tt = t.t12; + tt.delete(null); + t.delete(null); + Vertex va = + ((tt.v1==t.v1&&tt.v2==t.v2)||(tt.v1==t.v2&&tt.v2==t.v1)) ? tt.v3 : + ((tt.v2==t.v1&&tt.v3==t.v2)||(tt.v2==t.v2&&tt.v3==t.v1)) ? tt.v1 : + ((tt.v1==t.v1&&tt.v3==t.v2)||(tt.v1==t.v2&&tt.v3==t.v1)) ? tt.v2 : null; + vec2.addElement(newTriangle(this, t.v3, t.v1, "vertexinsertion " + this)); + vec2.addElement(newTriangle(this, t.v3, t.v2, "vertexinsertion " + this)); + vec2.addElement(newTriangle(this, t.v1, va , "vertexinsertion " + this)); + vec2.addElement(newTriangle(this, t.v2, va , "vertexinsertion " + this)); + fixup(vec2); + break; + } + } + if (triangles.size() > 2 && !good) + throw new Error("vertex "+this+" did not fall within a triangle or on the edge of at least two"); + } + public void fixup(Vector vec) { + while(vec.size() > 0) { + Triangle t = (Triangle)vec.lastElement(); + vec.setSize(vec.size() - 1); + if (t.deleted) continue; + Triangle to = t.v1==this ? t.t23 : t.v2==this ? t.t31 : t.v3==this ? t.t12 : null; + 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("this should not happen"); + if (to.r < this.distance(to.ccx, to.ccy)) continue; + if (t.v1==this) { + //if (t.v2.next.contains(t.v3)) continue; + //if (t.v3.next.contains(t.v2)) continue; + Vertex v = + (to.v1!=t.v2 && to.v1!=t.v3) ? to.v1 : + (to.v2!=t.v2 && to.v2!=t.v3) ? to.v2 : + (to.v3!=t.v2 && to.v3!=t.v3) ? to.v3 : null; + t.delete(null); + to.delete(null); + vec.addElement(newTriangle(this, t.v2, v, "fixup")); + vec.addElement(newTriangle(this, t.v3, v, "fixup")); + } else if (t.v2==this) { + //if (t.v3.next.contains(t.v1)) continue; + //if (t.v1.next.contains(t.v3)) continue; + Vertex v = + (to.v1!=t.v1 && to.v1!=t.v3) ? to.v1 : + (to.v2!=t.v1 && to.v2!=t.v3) ? to.v2 : + (to.v3!=t.v1 && to.v3!=t.v3) ? to.v3 : null; + t.delete(null); + to.delete(null); + vec.addElement(newTriangle(this, t.v1, v, "fixup")); + vec.addElement(newTriangle(this, t.v3, v, "fixup")); + + } else if (t.v3==this) { + //if (t.v1.next.contains(t.v2)) continue; + //if (t.v2.next.contains(t.v1)) continue; + Vertex v = + (to.v1!=t.v2 && to.v1!=t.v1) ? to.v1 : + (to.v2!=t.v2 && to.v2!=t.v1) ? to.v2 : + (to.v3!=t.v2 && to.v3!=t.v1) ? to.v3 : null; + t.delete(null); + to.delete(null); + vec.addElement(newTriangle(this, t.v2, v, "fixup")); + vec.addElement(newTriangle(this, t.v1, v, "fixup")); + } else { + throw new Error("this should not happen"); + } + System.out.println("> made a swap"); + } + } + + public boolean makeNonDegenerate(Vector vec) { + Vertex v1 = this; + for(int i2 = 0; i2 new Triangle("temp",this,v,o,true).area()) + best = o; + } + if (best==null) 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 = (Vertex)it2.next(); + if (o==x||this==x||v==x) continue; + int side_of_x = side(o,(this.x+v.x)/2,(this.y+v.y)/2,x); + int side_of_v = side(o,(this.x+v.x)/2,(this.y+v.y)/2,v); + int side_of_this = side(o,(this.x+v.x)/2,(this.y+v.y)/2,this); + if (side_of_x==side_of_this && side_of_this!=0) right.add(x); + else if (side_of_x==side_of_v && side_of_v!=0) left.add(x); + else throw new Error("impossible "+this+" "+v + " " + o + " ==> " + x); + } + boolean doit = true; + doit &= o.triangulate2(left, v, false); + doit &= o.triangulate2(right, this, false); + if (doit) { + Triangle t = newTriangle(v, this, o,"triangulate 0/0"); + if (((t.v1==this && t.v2==v)||(t.v1==v && t.v2==this)) && t.t12==null) return true; + if (((t.v3==this && t.v2==v)||(t.v3==v && t.v2==this)) && t.t23==null) return true; + if (((t.v1==this && t.v3==v)||(t.v1==v && t.v3==this)) && t.t31==null) return true; + return false; + } + return true; + } + + public int wind(Vertex v) { + if (v.next.contains(this) && next.contains(v)) return 0; + if (v.y < y || (v.y==y && v.x 0 && + y - Math.max(v1.y,v2.y) < 0 && + y - Math.min(v1.y,v2.y) > 0; + } + } + + // Triangle ////////////////////////////////////////////////////////////////////////////// + + private final class Triangle { + Triangle t12=null, t23=null, t31=null; + final Vertex v1, v2, v3; + boolean in = false; + boolean deleted = false; + boolean painted = false; + boolean special = false; + final String source; + final double ccx, ccy, r; + + 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 double area() { + double a = v1.distance(v2); + double b = v3.distance(v2); + double c = v1.distance(v3); + double s = (a+b+c)/2; + return Math.sqrt(s*(s-a)*(s-b)*(s-c)); + } + + public boolean checkSkinny() { + if (!(r / Math.min(Math.min(v1.distance(v2), v2.distance(v3)), v3.distance(v1)) > B)) return false; + System.out.println("skinny triangle " + this); + for (Iterator it = vertices.iterator(); it.hasNext();) { + Vertex v = (Vertex)it.next(); + for (Iterator nit = v.next.iterator(); nit.hasNext();) { + Vertex v2 = (Vertex)nit.next(); + double midx = (v.x+v2.x)/2; + double midy = (v.y+v2.y)/2; + if (distance(midx,midy,ccx,ccy) <= v.distance(midx,midy)) { + System.out.println(" but splitting it would encroach on a segment"); + return false; + } + } + } + newVertex(ccx, ccy); + return true; + } + + 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)v1.x, (float)v1.y), + (int)a.multiply_py((float)v1.x, (float)v1.y), + (int)a.multiply_px((float)v2.x, (float)v2.y), + (int)a.multiply_py((float)v2.x, (float)v2.y), + (int)a.multiply_px((float)v3.x, (float)v3.y), + (int)a.multiply_py((float)v3.x, (float)v3.y), + 0xff0000ff); + } else if ((evenOdd && count%2!=0) || (!evenOdd && count!=0)) { + buf.fillTriangle((int)a.multiply_px((float)v1.x, (float)v1.y), + (int)a.multiply_py((float)v1.x, (float)v1.y), + (int)a.multiply_px((float)v2.x, (float)v2.y), + (int)a.multiply_py((float)v2.x, (float)v2.y), + (int)a.multiply_px((float)v3.x, (float)v3.y), + (int)a.multiply_py((float)v3.x, (float)v3.y), + color); + } else { + buf.fillTriangle((int)a.multiply_px((float)v1.x, (float)v1.y), + (int)a.multiply_py((float)v1.x, (float)v1.y), + (int)a.multiply_px((float)v2.x, (float)v2.y), + (int)a.multiply_py((float)v2.x, (float)v2.y), + (int)a.multiply_px((float)v3.x, (float)v3.y), + (int)a.multiply_py((float)v3.x, (float)v3.y), + 0xff000000 | ((color & 0x00ffffff) >> 8)); + } + } + painted = true; + if (t12 != null && !t12.painted) { + t12.fill(buf, a, color, evenOdd, count + wind(v1, v2), strokeOnly); + drawLine(buf, (v1.x*2+v2.x)/3, (v1.y*2+v2.y)/3, (v1.x+2*v2.x)/3, (v1.y+2*v2.y)/3, a, + wind(v2,v1)==0?0xff000000:0xff0000ff); + } + if (t23 != null && !t23.painted) { + t23.fill(buf, a, color, evenOdd, count + wind(v2, v3), strokeOnly); + drawLine(buf, (v2.x*2+v3.x)/3, (v2.y*2+v3.y)/3, (v2.x+2*v3.x)/3, (v2.y+2*v3.y)/3, a, + wind(v3,v2)==0?0xff000000:0xff0000ff); + } + if (t31 != null && !t31.painted) { + t31.fill(buf, a, color, evenOdd, count + wind(v3, v1), strokeOnly); + drawLine(buf, (v1.x*2+v3.x)/3, (v1.y*2+v3.y)/3, (v1.x+2*v3.x)/3, (v1.y+2*v3.y)/3, a, + wind(v3,v1)==0?0xff000000:0xff0000ff); + } + } + public int wind(Vertex a, Vertex b) { return a.wind(b); } + 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; + double a=(v2.y-v3.y)*(v2.x-v1.x)-(v2.y-v1.y)*(v2.x-v3.x); + if (a==0) { + Vertex t = v2; v2=v3; v3=v1; v1 = t; + a=(v2.y-v3.y)*(v2.x-v1.x)-(v2.y-v1.y)*(v2.x-v3.x); + if (a==0) { + t = v2; v2=v3; v3=v1; v1 = t; + a=(v2.y-v3.y)*(v2.x-v1.x)-(v2.y-v1.y)*(v2.x-v3.x); + } + if (a==0) { + t = v2; v2=v3; v3=v1; v1 = t; + a=(v2.y-v3.y)*(v2.x-v1.x)-(v2.y-v1.y)*(v2.x-v3.x); + } + if (a==0) { + t = v1; v1=v3; v3=t; + a=(v2.y-v3.y)*(v2.x-v1.x)-(v2.y-v1.y)*(v2.x-v3.x); + } + if (a==0) { + t = v2; v2=v3; v3=v1; v1 = t; + a=(v2.y-v3.y)*(v2.x-v1.x)-(v2.y-v1.y)*(v2.x-v3.x); + } + if (a==0) { + t = v2; v2=v3; v3=v1; v1 = t; + a=(v2.y-v3.y)*(v2.x-v1.x)-(v2.y-v1.y)*(v2.x-v3.x); + } + if (a==0) { + t = v2; v2=v3; v3=v1; v1 = t; + a=(v2.y-v3.y)*(v2.x-v1.x)-(v2.y-v1.y)*(v2.x-v3.x); + } + } + if (a==0) throw new Error("a==0 for " + v1 + " " + v2 + " " + v3); + this.v1 = v1; this.v2 = v2; this.v3 = v3; + double a1=(v1.x+v2.x)*(v2.x-v1.x)+(v2.y-v1.y)*(v1.y+v2.y); + double a2=(v2.x+v3.x)*(v2.x-v3.x)+(v2.y-v3.y)*(v2.y+v3.y); + ccx=(a1*(v2.y-v3.y)-a2*(v2.y-v1.y))/a/2; + ccy=(a2*(v2.x-v1.x)-a1*(v2.x-v3.x))/a/2; + r = v1.distance(ccx, ccy); + if (fake) return; + for(int i=0; i