package com.revolsys.geometry.util; import com.revolsys.geometry.algorithm.CGAlgorithms; import com.revolsys.geometry.algorithm.HCoordinate; import com.revolsys.geometry.model.Point; import com.revolsys.geometry.model.impl.PointDoubleXY; import com.revolsys.math.Angle; public interface Triangles { /** * Computes the point at which the bisector of the angle ABC cuts the segment * AC. * * @param a * a vertex of the triangle * @param b * a vertex of the triangle * @param c * a vertex of the triangle * @return the angle bisector cut point */ static Point angleBisector(final Point a, final Point b, final Point c) { /** * Uses the fact that the lengths of the parts of the split segment are * proportional to the lengths of the adjacent triangle sides */ final double len0 = b.distancePoint(a); final double len2 = b.distancePoint(c); final double frac = len0 / (len0 + len2); final double dx = c.getX() - a.getX(); final double dy = c.getY() - a.getY(); final Point splitPt = new PointDoubleXY(a.getX() + frac * dx, a.getY() + frac * dy); return splitPt; } /** * Computes the 2D area of a triangle. The area value is always non-negative. * * @param x1 * @param y1 * @param x2 * @param y2 * @param x3 * @param y3 * @return The area. */ static double area(final double x1, final double y1, final double x2, final double y2, final double x3, final double y3) { return Math.abs(((x3 - x1) * (y2 - y1) - (x2 - x1) * (y3 - y1)) / 2); } /** * Computes the 2D area of a triangle. The area value is always non-negative. * * @param a * a vertex of the triangle * @param b * a vertex of the triangle * @param c * a vertex of the triangle * @return the area of the triangle * * @see #signedArea(Coordinate, Coordinate, Coordinate) */ static double area(final Point a, final Point b, final Point c) { final double x1 = a.getX(); final double y1 = a.getY(); final double x2 = b.getX(); final double y2 = b.getY(); final double x3 = c.getX(); final double y3 = c.getY(); return area(x1, y1, x2, y2, x3, y3); } /** * Returns twice the signed area of the triangle p1-p2-p3. * The area is positive if the triangle is oriented CCW, and negative if CW. */ static double area2(final double x1, final double y1, final double x2, final double y2, final double x3, final double y3) { return (x2 - x1) * (y3 - y1) - (x3 - x1) * (y2 - y1); } /** * Computes the 3D area of a triangle. The area value is always non-negative. * * Uses the formula 1/2 * | u x v | where u,v are the side vectors of the * triangle x is the vector cross-product * @return The area. */ static double area3D(final double x1, final double y1, final double z1, final double x2, final double y2, final double z2, final double x3, final double y3, final double z3) { // side vectors u and v final double ux = x2 - x1; final double uy = y2 - y1; final double uz = z2 - z1; final double vx = x3 - x1; final double vy = y3 - y1; final double vz = z3 - z1; // cross-product = u x v final double crossx = uy * vz - uz * vy; final double crossy = uz * vx - ux * vz; final double crossz = ux * vy - uy * vx; // tri area = 1/2 * | u x v | final double absSq = crossx * crossx + crossy * crossy + crossz * crossz; final double area3D = Math.sqrt(absSq) / 2; return area3D; } /** * Computes the 3D area of a triangle. The value computed is always * non-negative. * * @param a * a vertex of the triangle * @param b * a vertex of the triangle * @param c * a vertex of the triangle * @return the 3D area of the triangle */ static double area3D(final Point a, final Point b, final Point c) { final double x1 = a.getX(); final double x2 = b.getX(); final double z1 = a.getZ(); final double y1 = a.getY(); final double y2 = b.getY(); final double z2 = b.getZ(); final double x3 = c.getX(); final double y3 = c.getY(); final double z3 = c.getZ(); return area3D(x1, y1, z1, x2, y2, z2, x3, y3, z3); } /** * Computes the centroid (centre of mass) of a triangle. This is also the * point at which the triangle's three medians intersect (a triangle median is * the segment from a vertex of the triangle to the midpoint of the opposite * side). The centroid divides each median in a ratio of 2:1. * <p> * The centroid always lies within the triangle. * * * @param a * a vertex of the triangle * @param b * a vertex of the triangle * @param c * a vertex of the triangle * @return the centroid of the triangle */ static Point centroid(final Point a, final Point b, final Point c) { final double x = (a.getX() + b.getX() + c.getX()) / 3; final double y = (a.getY() + b.getY() + c.getY()) / 3; return new PointDoubleXY(x, y); } /** * Computes the circumcentre of a triangle. The circumcentre is the centre of * the circumcircle, the smallest circle which encloses the triangle. It is * also the common intersection point of the perpendicular bisectors of the * sides of the triangle, and is the only point which has equal distance to * all three vertices of the triangle. * <p> * The circumcentre does not necessarily lie within the triangle. For example, * the circumcentre of an obtuse isoceles triangle lies outside the triangle. * <p> * This method uses an algorithm due to J.R.Shewchuk which uses normalization * to the origin to improve the accuracy of computation. (See <i>Lecture Notes * on Geometric Robustness</i>, Jonathan Richard Shewchuk, 1999). * * @param a * a vertx of the triangle * @param b * a vertx of the triangle * @param c * a vertx of the triangle * @return the circumcentre of the triangle */ static Point circumcentre(final Point a, final Point b, final Point c) { final double cx = c.getX(); final double cy = c.getY(); final double ax = a.getX() - cx; final double ay = a.getY() - cy; final double bx = b.getX() - cx; final double by = b.getY() - cy; final double denom = 2 * det(ax, ay, bx, by); final double numx = det(ay, ax * ax + ay * ay, by, bx * bx + by * by); final double numy = det(ax, ax * ax + ay * ay, bx, bx * bx + by * by); final double ccx = cx - numx / denom; final double ccy = cy + numy / denom; return new PointDoubleXY(ccx, ccy); } /** * Computes the determinant of a 2x2 matrix. Uses standard double-precision * arithmetic, so is susceptible to round-off error. * * @param m00 * the [0,0] entry of the matrix * @param m01 * the [0,1] entry of the matrix * @param m10 * the [1,0] entry of the matrix * @param m11 * the [1,1] entry of the matrix * @return the determinant */ static double det(final double m00, final double m01, final double m10, final double m11) { return m00 * m11 - m01 * m10; } /** * Computes the incentre of a triangle. The <i>inCentre</i> of a triangle is * the point which is equidistant from the sides of the triangle. It is also * the point at which the bisectors of the triangle's angles meet. It is the * centre of the triangle's <i>incircle</i>, which is the unique circle that * is tangent to each of the triangle's three sides. * <p> * The incentre always lies within the triangle. * * @param a * a vertx of the triangle * @param b * a vertx of the triangle * @param c * a vertx of the triangle * @return the point which is the incentre of the triangle */ static Point inCentre(final Point a, final Point b, final Point c) { // the lengths of the sides, labelled by their opposite vertex final double len0 = b.distancePoint(c); final double len1 = a.distancePoint(c); final double len2 = a.distancePoint(b); final double circum = len0 + len1 + len2; final double inCentreX = (len0 * a.getX() + len1 * b.getX() + len2 * c.getX()) / circum; final double inCentreY = (len0 * a.getY() + len1 * b.getY() + len2 * c.getY()) / circum; return new PointDoubleXY(inCentreX, inCentreY); } /** * Computes the Z-value (elevation) of an XY point on a three-dimensional * plane defined by a triangle whose vertices have Z-values. The defining * triangle must not be degenerate (in other words, the triangle must enclose * a non-zero area), and must not be parallel to the Z-axis. * <p> * This method can be used to interpolate the Z-value of a point inside a * triangle (for example, of a TIN facet with elevations on the vertices). * * @param p * the point to compute the Z-value of * @param v0 * a vertex of a triangle, with a Z ordinate * @param v1 * a vertex of a triangle, with a Z ordinate * @param v2 * a vertex of a triangle, with a Z ordinate * @return the computed Z-value (elevation) of the point */ static double interpolateZ(final Point p, final Point v0, final Point v1, final Point v2) { final double x0 = v0.getX(); final double y0 = v0.getY(); final double a = v1.getX() - x0; final double b = v2.getX() - x0; final double c = v1.getY() - y0; final double d = v2.getY() - y0; final double det = a * d - b * c; final double dx = p.getX() - x0; final double dy = p.getY() - y0; final double t = (d * dx - b * dy) / det; final double u = (-c * dx + a * dy) / det; final double z = v0.getZ() + t * (v1.getZ() - v0.getZ()) + u * (v2.getZ() - v0.getZ()); return z; } /** * Tests whether a triangle is acute. A triangle is acute iff all interior * angles are acute. This is a strict test - right triangles will return * <tt>false</tt> A triangle which is not acute is either right or obtuse. * <p> * Note: this implementation is not robust for angles very close to 90 * degrees. * * @param a * a vertex of the triangle * @param b * a vertex of the triangle * @param c * a vertex of the triangle * @return true if the triangle is acute */ static boolean isAcute(final Point a, final Point b, final Point c) { if (!Angle.isAcute(a, b, c)) { return false; } if (!Angle.isAcute(b, c, a)) { return false; } if (!Angle.isAcute(c, a, b)) { return false; } return true; } /** * Computes the length of the longest side of a triangle * * @param a * a vertex of the triangle * @param b * a vertex of the triangle * @param c * a vertex of the triangle * @return the length of the longest side of the triangle */ static double longestSideLength(final Point a, final Point b, final Point c) { final double lenAB = a.distancePoint(b); final double lenBC = b.distancePoint(c); final double lenCA = c.distancePoint(a); double maxLen = lenAB; if (lenBC > maxLen) { maxLen = lenBC; } if (lenCA > maxLen) { maxLen = lenCA; } return maxLen; } /** * Computes the line which is the perpendicular bisector of the line segment * a-b. * * @param a * a point * @param b * another point * @return the perpendicular bisector, as an HCoordinate */ static HCoordinate perpendicularBisector(final Point a, final Point b) { // returns the perpendicular bisector of the line segment ab final double dx = b.getX() - a.getX(); final double dy = b.getY() - a.getY(); final HCoordinate l1 = new HCoordinate(a.getX() + dx / 2.0, a.getY() + dy / 2.0, 1.0); final HCoordinate l2 = new HCoordinate(a.getX() - dy + dx / 2.0, a.getY() + dx + dy / 2.0, 1.0); return new HCoordinate(l1, l2); } /** * Computes the signed 2D area of a triangle. The area value is positive if * the triangle is oriented CW, and negative if it is oriented CCW. * <p> * The signed area value can be used to determine point orientation, but the * implementation in this method is susceptible to round-off errors. Use * {@link CGAlgorithms#orientationIndex(Coordinate, Coordinate, Coordinate)} * for robust orientation calculation. * * @param a * a vertex of the triangle * @param b * a vertex of the triangle * @param c * a vertex of the triangle * @return the signed 2D area of the triangle * * @see CGAlgorithms#orientationIndex(Coordinate, Coordinate, Coordinate) */ static double signedArea(final Point a, final Point b, final Point c) { /** * Uses the formula 1/2 * | u x v | where u,v are the side vectors of the * triangle x is the vector cross-product For 2D vectors, this formual * simplifies to the expression below */ return ((c.getX() - a.getX()) * (b.getY() - a.getY()) - (b.getX() - a.getX()) * (c.getY() - a.getY())) / 2; } static boolean isInCircleNormalized(final double x1, final double y1, final double x2, final double y2, final double x3, final double y3, final double x, final double y) { final double adx = x1 - x; final double ady = y1 - y; final double bdx = x2 - x; final double bdy = y2 - y; final double cdx = x3 - x; final double cdy = y3 - y; final double abdet = adx * bdy - bdx * ady; final double bcdet = bdx * cdy - cdx * bdy; final double cadet = cdx * ady - adx * cdy; final double alift = adx * adx + ady * ady; final double blift = bdx * bdx + bdy * bdy; final double clift = cdx * cdx + cdy * cdy; final double disc = alift * bcdet + blift * cadet + clift * abdet; return disc > 0; } }