package delaunay_triangulation; /** * This class represents a 3D triangle in a Triangulation! * */ public class Triangle_dt { Point_dt a,b,c; Triangle_dt abnext,bcnext,canext; Circle_dt circum; int _mc=0; // modcounter for triangulation fast update. boolean halfplane=false; // true iff it is an infinite face. // public boolean visitflag; boolean _mark = false; // tag - for bfs algorithms // private static boolean visitValue=false; public static int _counter = 0, _c2=0; //public int _id; /** constructs a triangle form 3 point - store it in counterclockwised order.*/ public Triangle_dt( Point_dt A, Point_dt B, Point_dt C ) { // visitflag=visitValue; a=A; int res = C.pointLineTest(A,B); if ( (res <= Point_dt.LEFT) || (res == Point_dt.INFRONTOFA) || (res == Point_dt.BEHINDB) ) { b=B; c=C; } else { // RIGHT System.out.println("Warning, ajTriangle(A,B,C) "+ "expects points in counterclockwise order."); System.out.println(""+A+B+C); b=C; c=B; } circumcircle(); //_id = _counter++; //_counter++;_c2++; //if(_counter%10000 ==0) System.out.println("Triangle: "+_counter); } /** * creates a half plane using the segment (A,B). * @param A * @param B */ public Triangle_dt( Point_dt A, Point_dt B ) { // visitflag=visitValue; a=A; b=B; halfplane=true; // _id = _counter++; } /* protected void finalize() throws Throwable{ super.finalize(); _counter--; } */ /** * remove all pointers (for debug) */ //public void clear() { // this.abnext = null; this.bcnext=null; this.canext=null;} /** * returns true iff this triangle is actually a half plane. */ public boolean isHalfplane() {return this.halfplane;} /** * returns the first vertex of this triangle. */ public Point_dt p1() {return a;} /** * returns the second vertex of this triangle. */ public Point_dt p2() {return b;} /** * returns the 3th vertex of this triangle. */ public Point_dt p3() {return c;} /** * returns the consecutive triangle which shares this triangle p1,p2 edge. */ public Triangle_dt next_12() {return this.abnext;} /** * returns the consecutive triangle which shares this triangle p2,p3 edge. */ public Triangle_dt next_23() {return this.bcnext;} /** * returns the consecutive triangle which shares this triangle p3,p1 edge. */ public Triangle_dt next_31() {return this.canext;} /** * @return The bounding rectange between the minimum and maximum coordinates * of the triangle */ public BoundingBox getBoundingBox() { Point_dt lowerLeft, upperRight; lowerLeft = new Point_dt(Math.min(a.x(), Math.min(b.x(), c.x())), Math.min(a.y(), Math.min(b.y(), c.y()))); upperRight = new Point_dt(Math.max(a.x(), Math.max(b.x(), c.x())), Math.max(a.y(), Math.max(b.y(), c.y()))); return new BoundingBox(lowerLeft, upperRight); } void switchneighbors( Triangle_dt Old,Triangle_dt New ) { if ( abnext==Old ) abnext=New; else if ( bcnext==Old ) bcnext=New; else if ( canext==Old ) canext=New; else System.out.println( "Error, switchneighbors can't find Old." ); } Triangle_dt neighbor( Point_dt p ) { if ( a==p ) return canext; if ( b==p ) return abnext; if ( c==p ) return bcnext; System.out.println( "Error, neighbors can't find p: "+p ); return null; } /** * Returns the neighbors that shares the given corner and is not the previous triangle. * @param p The given corner * @param prevTriangle The previous triangle. * @return The neighbors that shares the given corner and is not the previous triangle. * * By: Eyal Roth & Doron Ganel. */ Triangle_dt nextNeighbor(Point_dt p, Triangle_dt prevTriangle) { Triangle_dt neighbor = null; if (a.equals(p)) { neighbor = canext; } if (b.equals(p)) { neighbor = abnext; } if (c.equals(p)) { neighbor = bcnext; } // Udi Schneider: Added a condition check for isHalfPlane. If the current // neighbor is a half plane, we also want to move to the next neighbor if (neighbor.equals(prevTriangle) || neighbor.isHalfplane()) { if (a.equals(p)) { neighbor = abnext; } if (b.equals(p)) { neighbor = bcnext; } if (c.equals(p)) { neighbor = canext; } } return neighbor; } Circle_dt circumcircle() { double u = ((a.x-b.x)*(a.x+b.x) + (a.y-b.y)*(a.y+b.y)) / 2.0f; double v = ((b.x-c.x)*(b.x+c.x) + (b.y-c.y)*(b.y+c.y)) / 2.0f; double den = (a.x-b.x)*(b.y-c.y) - (b.x-c.x)*(a.y-b.y); if ( den==0 ) // oops, degenerate case circum = new Circle_dt( a,Double.POSITIVE_INFINITY ); else { Point_dt cen = new Point_dt((u*(b.y-c.y) - v*(a.y-b.y)) / den, (v*(a.x-b.x) - u*(b.x-c.x)) / den); circum = new Circle_dt( cen,cen.distance2(a) ); } return circum; } boolean circumcircle_contains( Point_dt p ) { return circum.Radius() > circum.Center().distance2(p); } public String toString() { String res =""; //+_id+") "; res += a.toString()+b.toString(); if ( !halfplane ) res +=c.toString() ; // res +=c.toString() +" | "+abnext._id+" "+bcnext._id+" "+canext._id; return res; } /** * determinates if this triangle contains the point p. * @param p the query point * @return true iff p is not null and is inside this triangle (Note: on boundary is considered inside!!). */ public boolean contains(Point_dt p) { boolean ans = false; if(this.halfplane | p== null) return false; if (isCorner(p)) { return true; } int a12 = p.pointLineTest(a,b); int a23 = p.pointLineTest(b,c); int a31 = p.pointLineTest(c,a); if ((a12 == Point_dt.LEFT && a23 == Point_dt.LEFT && a31 == Point_dt.LEFT ) || (a12 == Point_dt.RIGHT && a23 == Point_dt.RIGHT && a31 == Point_dt.RIGHT ) || (a12 == Point_dt.ONSEGMENT ||a23 == Point_dt.ONSEGMENT || a31 == Point_dt.ONSEGMENT)) ans = true; return ans; } /** * determinates if this triangle contains the point p. * @param p the query point * @return true iff p is not null and is inside this triangle (Note: on boundary is considered outside!!). */ public boolean contains_BoundaryIsOutside(Point_dt p) { boolean ans = false; if(this.halfplane | p== null) return false; if (isCorner(p)) { return true; } int a12 = p.pointLineTest(a,b); int a23 = p.pointLineTest(b,c); int a31 = p.pointLineTest(c,a); if ((a12 == Point_dt.LEFT && a23 == Point_dt.LEFT && a31 == Point_dt.LEFT ) || (a12 == Point_dt.RIGHT && a23 == Point_dt.RIGHT && a31 == Point_dt.RIGHT )) ans = true; return ans; } /** * Checks if the given point is a corner of this triangle. * @param p The given point. * @return True iff the given point is a corner of this triangle. * * By Eyal Roth & Doron Ganel. */ public boolean isCorner(Point_dt p) { return (p.x == a.x & p.y == a.y) | (p.x == b.x & p.y == b.y)| (p.x == c.x & p.y == c.y); } //Doron public boolean fallInsideCircumcircle(Point_dt[] arrayPoints) { boolean isInside = false; Point_dt p1 = this.p1(); Point_dt p2 = this.p2(); Point_dt p3 = this.p3(); int i = 0; while(!isInside && i<arrayPoints.length) { Point_dt p = arrayPoints[i]; if(!p.equals(p1)&& !p.equals(p2) && !p.equals(p3)) { isInside = this.circumcircle_contains(p); } i++; } return isInside; } /** * compute the Z value for the X,Y values of q. <br /> * assume this triangle represent a plane --> q does NOT need to be contained * in this triangle. * * @param q query point (its Z value is ignored). * @return the Z value of this plane implies by this triangle 3 points. */ public double z_value(Point_dt q) { if(q==null || this.halfplane) throw new RuntimeException("*** ERR wrong parameters, can't approximate the z value ..***: "+q); /* incase the query point is on one of the points */ if(q.x==a.x & q.y==a.y) return a.z; if(q.x==b.x & q.y==b.y) return b.z; if(q.x==c.x & q.y==c.y) return c.z; /* * plane: aX + bY + c = Z: * 2D line: y= mX + k * */ double X=0,x0 = q.x, x1 = a.x, x2=b.x, x3=c.x; double Y=0,y0 = q.y, y1 = a.y, y2=b.y, y3=c.y; double Z=0, m01=0,k01=0,m23=0,k23=0; // 0 - regular, 1-horisintal , 2-vertical. int flag01 = 0; if(x0!=x1) { m01 = (y0-y1)/(x0-x1); k01 = y0 - m01*x0; if(m01 ==0) flag01 = 1; } else { // 2-vertical. flag01 = 2;//x01 = x0 } int flag23 = 0; if(x2!=x3) { m23 = (y2-y3)/(x2-x3); k23 = y2 - m23*x2; if(m23 ==0) flag23 = 1; } else { // 2-vertical. flag23 = 2;//x01 = x0 } if(flag01 ==2 ) { X = x0; Y = m23*X + k23; } else { if(flag23==2) { X = x2; Y = m01*X + k01; } else { // regular case X=(k23-k01)/(m01-m23); Y = m01*X+k01; } } double r = 0; if(flag23==2) {r=(y2-Y)/(y2-y3);} else {r=(x2-X)/(x2-x3);} Z = b.z + (c.z-b.z)*r; if(flag01==2) {r=(y1-y0)/(y1-Y);} else {r=(x1-x0)/(x1-X);} double qZ = a.z + (Z-a.z)*r; return qZ; } /** * compute the Z value for the X,Y values of q. * assume this triangle represent a plane --> q does NOT need to be contained * in this triangle. * * @param x x-coordinate of the query point. * @param y y-coordinate of the query point. * @return z (height) value approximation given by the triangle it falls in. * */ public double z(double x, double y) { return z_value(new Point_dt(x,y)); } /** * compute the Z value for the X,Y values of q. * assume this triangle represent a plane --> q does NOT need to be contained * in this triangle. * * @param q query point (its Z value is ignored). * @return q with updated Z value. * */ public Point_dt z(Point_dt q) { double z = z_value(q); return new Point_dt(q.x,q.y, z); } }