org.dyn4j.geometry.Triangle Maven / Gradle / Ivy
/*
* Copyright (c) 2010-2020 William Bittle http://www.dyn4j.org/
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted
* provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of conditions
* and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
* and the following disclaimer in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of the copyright holder nor the names of its contributors may be used to endorse or
* promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.dyn4j.geometry;
import org.dyn4j.DataContainer;
/**
* Implementation of a Triangle {@link Convex} {@link Shape}.
*
* A {@link Triangle} must have one vertex which is not colinear with the other two.
*
* This class is provided to enhance performance of some of the methods contained in
* the {@link Convex} and {@link Shape} interfaces.
* @author William Bittle
* @version 3.2.0
* @since 1.0.0
*/
public class Triangle extends Polygon implements Convex, Wound, Shape, Transformable, DataContainer {
/**
* Full constructor.
*
* Creates a new triangle using the given points. The center will be the area
* weighted center of the points.
*
* A triangle must have 3 non-null points of which one is not colinear with the
* other two.
* @param point1 the first point
* @param point2 the second point
* @param point3 the third point
* @throws NullPointerException if point1, point2, or point3 is null
* @throws IllegalArgumentException if point1, point2, and point3 contain coincident points or has clockwise winding
*/
public Triangle(Vector2 point1, Vector2 point2, Vector2 point3) {
super(point1, point2, point3);
}
/* (non-Javadoc)
* @see org.dyn4j.geometry.Wound#toString()
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Triangle[").append(super.toString()).append("]");
return sb.toString();
}
/**
* Returns true if the point is inside the {@link Triangle}.
*
* The equation of a plane is:
*
N · (P - A) = 0
* Where A is any point on the plane.
* Create two axes ({@link Vector2}s), we will choose Vab and Vac.
* Vac = C - A
* Vab = B - A
* Where A, B, and C are the vertices of the {@link Triangle}.
* From this we can say that you can get to any point on the
* plane by going some u distance on Vac and some v distance on Vab
* where A is the origin.
* P = A + u * Vac + v * Vab
* Simplifing P - A
* Vpa = u * Vac + v * Vab
* We still need another equation to solve for u and v:
* Dot the equation by Vac to get
* Vpa · Vac = (u * Vac + v * Vab) · Vac
* Dot the equation by Vab to get the other
* Vpa · Vab = (u * Vac + v * Vab) · Vab
* Distribute out both equations
* Vpa · Vac = u * Vac · Vac + v * Vab · Vac
* Vpa · Vab = u * Vac · Vab + v * Vab · Vab
* Solving the first equation for u:
* u = (Vpa · Vac - v * Vab · Vac) / (Vac · Vac)
* Substitute one into the other:
* Vpa · Vab = (Vpa · Vac - v * Vab · Vac) / (Vac · Vac) * Vac · Vab + v * Vab · Vab
* Vpa · Vab = (Vpa · Vac / Vac · Vac) * Vac · Vab - v * (Vab · Vac / Vac · Vac) * Vac · Vab + v * Vab · Vab
* Vpa · Vab = (Vpa · Vac / Vac · Vac) * Vac · Vab + v * (Vab · Vab - (Vab · Vac / Vac · Vac) * Vac · Vab)
* v = (Vpa · Vab - (Vpa · Vac / Vac · Vac) * Vac · Vab) / (Vab · Vab - (Vab · Vac / Vac · Vac) * Vac · Vab)
* Which reduces to:
* v = ((Vpa · Vab) * (Vac · Vac) - (Vpa · Vac) * (Vac · Vab)) / ((Vab · Vab) * (Vac · Vac) - (Vab · Vac) * (Vac · Vab))
* Once v is obtained use either equation to obtain u:
* u = (v * Vab · Vab - Vpa · Vab) / Vac · Vab
* We know that the point is inside the {@link Triangle} if u and v are greater than
* zero and u + v is less than one.
* @param point world space point
* @param transform {@link Transform} the {@link Shape}'s transform
* @return boolean
*/
@Override
public boolean contains(Vector2 point, Transform transform) {
double u, v;
// put the point in local coordinates
Vector2 p = transform.getInverseTransformed(point);
// get the vertices
Vector2 p1 = this.vertices[0];
Vector2 p2 = this.vertices[1];
Vector2 p3 = this.vertices[2];
// create a vector representing edge ab
Vector2 ab = p1.to(p2);
// create a vector representing edge ac
Vector2 ac = p1.to(p3);
// create a vector from a to the point
Vector2 pa = p1.to(p);
double dot00 = ac.dot(ac);
double dot01 = ac.dot(ab);
double dot02 = ac.dot(pa);
double dot11 = ab.dot(ab);
double dot12 = ab.dot(pa);
double denominator = dot00 * dot11 - dot01 * dot01;
double invD = 1.0 / denominator;
u = (dot11 * dot02 - dot01 * dot12) * invD;
// don't bother going any farther if u is less than zero
if (u <= 0) return false;
v = (dot00 * dot12 - dot01 * dot02) * invD;
return /*u > 0 && */v > 0 && (u + v <= 1);
}
}