package org.geogebra.common.kernel.discrete.delaunay; import org.geogebra.common.util.debug.Log; /** * 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 Log.warn("Warning, ajTriangle(A,B,C) " + "expects points in counterclockwise order." + 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 { Log.debug("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; } Log.debug("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) { 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); } @Override 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); } public Point_dt getCorner(int index) { switch (index) { case 0: return p1(); case 1: return p2(); case 2: return p3(); } return null; } }