/*
* Copyright (c) 2010, Frederik Vanhoutte This library is free software; you can
* redistribute it and/or modify it under the terms of the GNU Lesser General
* Public License as published by the Free Software Foundation; either version
* 2.1 of the License, or (at your option) any later version.
* http://creativecommons.org/licenses/LGPL/2.1/ This library is distributed in
* the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
* the GNU Lesser General Public License for more details. You should have
* received a copy of the GNU Lesser General Public License along with this
* library; if not, write to the Free Software Foundation, Inc., 51 Franklin St,
* Fifth Floor, Boston, MA 02110-1301 USA
*/
package wblut.geom;
import wblut.WB_Epsilon;
// TODO: Auto-generated Javadoc
/**
* 3D Triangle.
*/
public class WB_ExplicitTriangle2D implements WB_Triangle2D {
/** First point. */
protected WB_Point2d p1;
/** Second point. */
protected WB_Point2d p2;
/** Third point. */
protected WB_Point2d p3;
/** Length of side a. */
protected double a;
/** Length of side b. */
protected double b;
/** Length of side c. */
protected double c;
/** Cosine of angle A. */
protected double cosA;
/** Cosine of angle B. */
protected double cosB;
/** Cosine of angle C. */
protected double cosC;
/** Is triangle degenerate?. */
protected boolean degenerate;
/**
* Instantiates a new WB_Triangle2D. No copies are made.
*
* @param p1 first point
* @param p2 second point
* @param p3 third point
*/
public WB_ExplicitTriangle2D(final WB_Point2d p1, final WB_Point2d p2, final WB_Point2d p3) {
this.p1 = p1;
this.p2 = p2;
this.p3 = p3;
update();
}
/**
* Instantiates a new WB_Triangle2D.
*
* @param p1 first point
* @param p2 second point
* @param p3 third point
* @param copy copy points?
*/
public WB_ExplicitTriangle2D(final WB_Point2d p1, final WB_Point2d p2,
final WB_Point2d p3, final boolean copy) {
if (!copy) {
this.p1 = p1;
this.p2 = p2;
this.p3 = p3;
} else {
this.p1 = p1.get();
this.p2 = p2.get();
this.p3 = p3.get();
}
update();
}
/**
* Update side lengths and corner angles.
*/
public void update() {
a = WB_Distance2D.distance(p2, p3);
b = WB_Distance2D.distance(p1, p3);
c = WB_Distance2D.distance(p1, p2);
cosA = ((p2.x - p1.x) * (p3.x - p1.x) + (p2.y - p1.y) * (p3.y - p1.y))
/ (b * c);
cosB = ((p1.x - p2.x) * (p3.x - p2.x) + (p1.y - p2.y) * (p3.y - p2.y))
/ (a * c);
cosC = ((p2.x - p3.x) * (p1.x - p3.x) + (p2.y - p3.y) * (p1.y - p3.y))
/ (a * b);
degenerate = WB_Epsilon.isZeroSq(WB_Distance2D.sqDistanceToLine(p1, p2,
p3));
}
/**
* Get circumcircle.
*
* @return circumcircle
*/
public WB_Circle getCircumcircle() {
final WB_Circle result = new WB_Circle();
if (!degenerate) {
result.setRadius(a
* b
* c
/ Math.sqrt(2 * a * a * b * b + 2 * b * b * c * c + 2 * a
* a * c * c - a * a * a * a - b * b * b * b - c * c
* c * c));
final double bx = p2.x - p1.x;
final double by = p2.y - p1.y;
final double cx = p3.x - p1.x;
final double cy = p3.y - p1.y;
double d = 2 * (bx * cy - by * cx);
if (WB_Epsilon.isZero(d)) {
return null;
}
d = 1.0 / d;
final double b2 = bx * bx + by * by;
final double c2 = cx * cx + cy * cy;
final double x = (cy * b2 - by * c2) * d;
final double y = (bx * c2 - cx * b2) * d;
result.setCenter(x + p1.x, y + p1.y);
return result;
}
return null;
}
/**
* Get incircle.
*
* @return incircle
*/
public WB_Circle getIncircle() {
final WB_Circle result = new WB_Circle();
if (!degenerate) {
final double abc = a + b + c;
result.setRadius(0.5 * Math.sqrt(((b + c - a) * (c + a - b) * (a
+ b - c))
/ abc));
final WB_Point2d ta = p1.multAndCopy(a);
final WB_Point2d tb = p2.multAndCopy(b);
final WB_Point2d tc = p3.multAndCopy(c);
tc.add(ta).add(tb).div(abc);
result.setCenter(tc);
return result;
}
return null;
}
/**
* Get incenter.
*
* @return incenter
*/
public WB_Point2d getIncenter() {
return getPointFromTrilinear(1, 1, 1);
}
/**
* Get centroid.
*
* @return centroid
*/
public WB_Point2d getCentroid() {
return getPointFromTrilinear(b * c, c * a, a * b);
}
/**
* Get circumcenter.
*
* @return circumcenter
*/
public WB_Point2d getCircumcenter() {
return getPointFromTrilinear(cosA, cosB, cosC);
}
/**
* Get orthocenter.
*
* @return orthocenter
*/
public WB_Point2d getOrthocenter() {
final double a2 = a * a;
final double b2 = b * b;
final double c2 = c * c;
return getPointFromBarycentric((a2 + b2 - c2) * (a2 - b2 + c2), (a2
+ b2 - c2)
* (-a2 + b2 + c2), (a2 - b2 + c2) * (-a2 + b2 + c2));
}
/**
* Get point from trilinear coordinates.
*
* @param x the x
* @param y the y
* @param z the z
* @return point
*/
public WB_Point2d getPointFromTrilinear(final double x, final double y,
final double z) {
if (!degenerate) {
final double abc = a * x + b * y + c * z;
final WB_Point2d ea = p2.subAndCopy(p3);
final WB_Point2d eb = p1.subAndCopy(p3);
ea.mult(b * y);
eb.mult(a * x);
ea.add(eb);
ea.div(abc);
ea.add(p3);
return ea;
}
return null;
}
/**
* Get point from barycentric coordinates.
*
* @param x the x
* @param y the y
* @param z the z
* @return point
*/
public WB_Point2d getPointFromBarycentric(final double x, final double y,
final double z) {
if (!degenerate) {
return getPointFromTrilinear(x / a, y / b, z / c);
}
return null;
}
/**
* Barycentric coordinates of point.
*
* @param p point
* @return barycentric coordinates as WB_XYZ
*/
public WB_Point3d getBarycentric(final WB_Point2d p) {
final double m = (p3.x - p1.x) * (p2.y - p1.y) - (p3.y - p1.y)
* (p2.x - p1.x);
double nu, nv, ood;
nu = twiceSignedTriArea2D(p.x, p.y, p2.x, p2.y, p3.x, p3.y);
nv = twiceSignedTriArea2D(p.x, p.y, p3.x, p3.y, p1.x, p1.y);
ood = -1.0 / m;
nu *= ood;
nv *= ood;
return new WB_Point3d(nu, nv, 1 - nu - nv);
}
/**
* Check if points p1 and p2 lie on same side of line A-B.
*
* @param p1 the p1
* @param p2 the p2
* @param A the a
* @param B the b
* @return true, false
*/
public static boolean sameSide2D(final WB_Point2d p1, final WB_Point2d p2,
final WB_Point2d A, final WB_Point2d B) {
final WB_Point2d t1 = B.get().sub(A);
final WB_Point2d t2 = p1.get().sub(A);
final WB_Point2d t3 = p2.get().sub(A);
final double ct2 = t1.x * t2.y - t1.y * t2.x;
final double ct3 = t1.x * t3.y - t1.y * t3.x;
if (ct2 * ct3 >= WB_Epsilon.EPSILON) {
return true;
}
return false;
}
/**
* Check if point p lies in triangle A-B-C.
*
* @param p the p
* @param A the a
* @param B the b
* @param C the c
* @return true, false
*/
public static boolean pointInTriangle2D(final WB_Point2d p, final WB_Point2d A,
final WB_Point2d B, final WB_Point2d C) {
if (WB_Epsilon.isZeroSq(WB_Distance2D.sqDistanceToLine(A, B, C))) {
return false;
}
if (sameSide2D(p, A, B, C) && sameSide2D(p, B, A, C)
&& sameSide2D(p, C, A, B)) {
return true;
}
return false;
}
/*
* public static boolean pointInTriangle2D(final WB_XY p, final WB_XY A,
* final WB_XY B, final WB_XY C) { final WB_XY e0 = B.subAndCopy(A); final
* WB_XY n0 = new WB_XY(e0.y, -e0.x); final double sign = e0.y * (C.x - A.x)
* - e0.x * (C.y - A.y); if (sign * n0.dot(p.subAndCopy(A)) <
* WB_Epsilon.EPSILON) { return false; } final WB_XY e1 = C.subAndCopy(B);
* final WB_XY n1 = new WB_XY(e1.y, -e1.x); if (sign *
* n1.dot(p.subAndCopy(B)) < WB_Epsilon.EPSILON) { return false; } final
* WB_XY e2 = A.subAndCopy(C); final WB_XY n2 = new WB_XY(e2.y, -e2.x); if
* (sign * n2.dot(p.subAndCopy(C)) < WB_Epsilon.EPSILON) { return false; }
* return true; }
*/
/**
* Point in triangle2 d.
*
* @param p the p
* @param T the t
* @return true, if successful
*/
public static boolean pointInTriangle2D(final WB_Point2d p,
final WB_ExplicitTriangle2D T) {
return pointInTriangle2D(p, T.p1, T.p2, T.p3);
}
/**
* Check if point p lies in triangle A-B-C using barycentric coordinates.
*
* @param p the p
* @param A the a
* @param B the b
* @param C the c
* @return true, false
*/
public static boolean pointInTriangleBary2D(final WB_Point2d p, final WB_Point2d A,
final WB_Point2d B, final WB_Point2d C) {
if (p == A) {
return false;
}
if (p == B) {
return false;
}
if (p == C) {
return false;
}
if (WB_Epsilon.isZeroSq(WB_Distance2D.sqDistanceToLine(A, B, C))) {
return false;
}
// Compute vectors
final WB_Point2d v0 = C.get().sub(A);
final WB_Point2d v1 = B.get().sub(A);
final WB_Point2d v2 = p.get().sub(A);
// Compute dot products
final double dot00 = v0.dot(v0);
final double dot01 = v0.dot(v1);
final double dot02 = v0.dot(v2);
final double dot11 = v1.dot(v1);
final double dot12 = v1.dot(v2);
// Compute barycentric coordinates
final double invDenom = 1.0 / (dot00 * dot11 - dot01 * dot01);
final double u = (dot11 * dot02 - dot01 * dot12) * invDenom;
final double v = (dot00 * dot12 - dot01 * dot02) * invDenom;
// Check if point is in triangle
return (u > WB_Epsilon.EPSILON) && (v > WB_Epsilon.EPSILON)
&& (u + v < 1 - WB_Epsilon.EPSILON);
}
/**
* Point in triangle bary2 d.
*
* @param p the p
* @param T the t
* @return true, if successful
*/
public static boolean pointInTriangleBary2D(final WB_Point2d p,
final WB_ExplicitTriangle2D T) {
return pointInTriangleBary2D(p, T.p1, T.p2, T.p3);
}
/**
* Twice signed tri area2 d.
*
* @param p1 the p1
* @param p2 the p2
* @param p3 the p3
* @return the double
*/
public static double twiceSignedTriArea2D(final WB_Point2d p1, final WB_Point2d p2,
final WB_Point2d p3) {
return (p1.x - p3.x) * (p2.y - p3.y) - (p1.y - p3.y) * (p2.x - p3.x);
}
/**
* Twice signed tri area2 d.
*
* @param x1 the x1
* @param y1 the y1
* @param x2 the x2
* @param y2 the y2
* @param x3 the x3
* @param y3 the y3
* @return the double
*/
public static double twiceSignedTriArea2D(final double x1, final double y1,
final double x2, final double y2, final double x3, final double y3) {
return (x1 - x2) * (y2 - y3) - (x2 - x3) * (y1 - y2);
}
/*
* (non-Javadoc)
* @see wblut.geom.WB_Triangle2D#getCenter()
*/
public WB_Point2d getCenter() {
// TODO Auto-generated method stub
return null;
}
/*
* (non-Javadoc)
* @see wblut.geom.WB_Triangle2D#p1()
*/
public WB_Point2d p1() {
return p1;
}
/*
* (non-Javadoc)
* @see wblut.geom.WB_Triangle2D#p2()
*/
public WB_Point2d p2() {
return p2;
}
/*
* (non-Javadoc)
* @see wblut.geom.WB_Triangle2D#p3()
*/
public WB_Point2d p3() {
return p3;
}
}