From ab67f7e8366d6ea5ce5798cb68394beed075585d Mon Sep 17 00:00:00 2001 From: adam Date: Tue, 15 Feb 2005 12:19:19 +0000 Subject: [PATCH] fixed Mesh.java, made it really fast darcs-hash:20050215121919-5007d-525fa45c4d4f5faa5731ae5dcf51adf4793780b5.gz --- src/org/ibex/core/Box.java | 6 +- src/org/ibex/graphics/Mesh.java | 1076 +++++++++++++++------------------------ 2 files changed, 422 insertions(+), 660 deletions(-) diff --git a/src/org/ibex/core/Box.java b/src/org/ibex/core/Box.java index b68480f..8c312de 100644 --- a/src/org/ibex/core/Box.java +++ b/src/org/ibex/core/Box.java @@ -244,7 +244,11 @@ public final class Box extends JS.Obj implements Callable { } // FIXME: texture } else { - if (mesh == null) mesh = new Mesh(new Polygon(path, Affine.identity())); + if (mesh == null) { + Log.warn(this, "generating mesh..."); + mesh = new Mesh(new Polygon(path, Affine.identity())); + Log.warn(this, " done generating mesh."); + } mesh.fill(buf, a, fillcolor, true, false); mesh.stroke(buf, a, strokecolor); //mesh.fill(buf, a, fillcolor, true, true); diff --git a/src/org/ibex/graphics/Mesh.java b/src/org/ibex/graphics/Mesh.java index 43875eb..12f71d7 100644 --- a/src/org/ibex/graphics/Mesh.java +++ b/src/org/ibex/graphics/Mesh.java @@ -13,79 +13,135 @@ import org.ibex.util.*; */ public final class Mesh { - private static final double epsilon = (double)0.00000001; - private static final boolean debug = true; + //#define Vertex int - private Vector vertices = new Vector(); - private Vector triangles = new Vector(); + private static final float epsilon = (float)0.001; + private static final boolean debug = false; - public static double B = 5; - public static double Q = 0.4; + private Vector triangles = new Vector(); - // Utilities ////////////////////////////////////////////////////////////////////////////// + public static float B = (float)5.0; + public static float Q = (float)0.4; - 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 static Hashtable nexter = new Hashtable(); + public static LinkedList next(Vertex v) { + LinkedList hs = (LinkedList)nexter.get(new Integer(v)); + if (hs == null) nexter.put(new Integer(v), hs = new LinkedList()); + return hs; } - 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= 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++; } - public void stroke(PixelBuffer buf, Affine a, int color) { - if (debug) - for (int i=0; i 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")); } - return ret==null ? new Vertex(x,y) : ret; } + // 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 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()) + if (best==NULLVERTEX || new Triangle("temp",vv,v,best,true).area() > new Triangle("temp",vv,v,o,true).area()) best = o; } - if (best==null) throw new Error("notgood, #orphans = " + orphans.size()); + 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 = (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); + 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); } boolean doit = true; - doit &= o.triangulate2(left, v, false); - doit &= o.triangulate2(right, this, false); + doit &= triangulate(o, left, v, false); + doit &= triangulate(o, right, vv, 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; + 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; } 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; + } + public boolean makeNonDegenerate(Vector vec, Vertex v1) { + for(int i2 = 0; i2>"; } 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 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(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"); + 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> 8)); + 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)); } } 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); - 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); + 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); } + //#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) 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; + 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 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; + } + + } -- 1.7.10.4