package ProGAL.geom3d.volumes;
import java.awt.Color;
import ProGAL.geom3d.Plane;
import ProGAL.geom3d.Point;
import ProGAL.geom3d.Simplex;
import ProGAL.geom3d.Triangle;
import ProGAL.geom3d.Vector;
//import ProGAL.geom3d.viewer.J3DScene;
/**
* A tetrahedron is a polyhedron with four triangular faces. It is defined using
* four corner-points.
*
* @author R.Fonseca
*/
public class Tetrahedron implements Simplex, Volume {
protected Point[] corners = new Point[4];
public Tetrahedron(Point p1, Point p2, Point p3, Point p4){
corners[0] = p1;
corners[1] = p2;
corners[2] = p3;
corners[3] = p4;
}
public Tetrahedron(Point[] corners){
this.corners = corners;
}
/* returns a tetrahedron with its circumscribing circle at (0,0,0) and four corners at unit distance */
public static Tetrahedron regularTetrahedron() {
return new Tetrahedron(new Point( 1.0, 0.0, 0.0),
new Point(-1.0/3.0, Math.sqrt(8)/3.0, 0.0),
new Point(-1.0/3.0, -Math.sqrt(2)/3.0, Math.sqrt(2.0/3.0)),
new Point(-1.0/3.0, -Math.sqrt(2)/3.0, -Math.sqrt(2.0/3.0)));
}
/** Return the specified corner. Throws an error if c<0 || c>3
. */
public Point getCorner(int c){
if(c<0 || c>3) throw new IllegalArgumentException();
return corners[c];
}
/** Return all four corners */
public Point[] getCorners() {
return corners;
}
public void translate(Vector v) {
for (Point p : corners) p.translateThis(v.x(), v.y(), v.z());
}
public void translate(Point q) {
for (Point p : corners) p.translateThis(q.x(), q.y(), q.z());
}
public void translate(double x, double y, double z) {
for (Point p : corners) p.translateThis(x, y, z);
}
public void blowUp(double t) {
Point center = circumCenter();
translate(-center.x(), -center.y(), -center.z());
for (Point p : corners) p.scaleThis(t);
translate(center);
}
/** Return the specified corner-point. Throws an error if c<0 || c>3
. */
public Point getPoint(int c){
if(c<0 || c>3) throw new IllegalArgumentException();
return corners[c];
}
/** Return the 'dimension' of this object. Required by the interface Simplex. */
public int getDimension() { return 3; }
/** TODO: Comment */
public void setPoint(int c, Point point) {
if(c<0 || c>3) throw new IllegalArgumentException();
corners[c] = point;
}
public boolean overlaps(Volume vol) {
// TODO Auto-generated method stub
return false;
}
/** Get the volume of the tetrahedron. */
public double getVolume() {
Vector a = corners[3].vectorTo(corners[0]);
Vector b = corners[3].vectorTo(corners[1]);
Vector c = corners[3].vectorTo(corners[2]);
return Math.abs(a.dot(b.crossThis(c)))/6.0;
}
/** Return common triangle of 2 tetrahedra */
public Triangle getCommonTriangle(Tetrahedron t) {
Point[] common = new Point[4];
int count = 0;
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
if (corners[i].equals(t.corners[j])) {
common[count++] = corners[i];
j = 4;
}
}
}
if (count != 3) return null;
return new Triangle(common[0], common[1], common[2]);
}
/** Calculate the radius of the insphere. */
public double getInradius(){
Vector a = corners[3].vectorTo(corners[0]);
Vector b = corners[3].vectorTo(corners[1]);
Vector c = corners[3].vectorTo(corners[2]);
Vector bXc = b.cross(c);
double sixV = Math.abs(a.dot(bXc));
Vector cXa = c.crossThis(a);
Vector aXb = a.crossThis(b);
double denom = bXc.length()+cXa.length()+aXb.length()+( bXc.addThis(cXa).addThis(aXb).length() );
return sixV/denom;
}
/** Calculate the radius of the circumsphere. */
public double circumRadius(){
Vector a = corners[3].vectorTo(corners[0]);
Vector b = corners[3].vectorTo(corners[1]);
Vector c = corners[3].vectorTo(corners[2]);
Vector O = b.cross(c).multiplyThis(a.dot(a));
O.addThis(c.cross(a).multiplyThis(b.dot(b)));
O.addThis(a.cross(b).multiplyThis(c.dot(c)));
O.multiplyThis(1.0/(2*a.dot(b.crossThis(c))));
return O.length();
}
/** Find the center of the circumscribing sphere. */
public Point circumCenter(){
Vector a = corners[3].vectorTo(corners[0]);
Vector b = corners[3].vectorTo(corners[1]);
Vector c = corners[3].vectorTo(corners[2]);
Vector O = b.cross(c).multiplyThis(a.dot(a));
O.addThis(c.cross(a).multiplyThis(b.dot(b)));
O.addThis(a.cross(b).multiplyThis(c.dot(c)));
O.multiplyThis(1.0/(2*a.dot(b.crossThis(c))));
return corners[3].add(O);
}
/** Find the circumscribing sphere */
public Sphere circumSphere(){
Vector a = corners[3].vectorTo(corners[0]);
Vector b = corners[3].vectorTo(corners[1]);
Vector c = corners[3].vectorTo(corners[2]);
Vector O = b.cross(c).multiplyThis(a.dot(a));
O.addThis(c.cross(a).multiplyThis(b.dot(b)));
O.addThis(a.cross(b).multiplyThis(c.dot(c)));
O.multiplyThis(1.0/(2*a.dot(b.crossThis(c))));
return new Sphere(corners[3].add(O), O.length());
}
/** Find the center of the inscribed sphere. */
public Point incenter(){
Vector a = corners[3].vectorTo(corners[0]);
Vector b = corners[3].vectorTo(corners[1]);
Vector c = corners[3].vectorTo(corners[2]);
Vector bXc = b.cross(c);
Vector cXa = c.cross(a);
Vector aXb = a.cross(b);
double bXcLength = bXc.length();
double cXaLength = cXa.length();
double aXbLength = aXb.length();
double dLength = bXc.addThis(cXa).addThis(aXb).length();
Vector O = a.multiplyThis(bXcLength);
O.addThis(b.multiplyThis(cXaLength));
O.addThis(c.multiplyThis(aXbLength));
O.divideThis(bXcLength+cXaLength+aXbLength+dLength );
return corners[3].add(O);
}
public Point getCenter() {
Vector v = corners[0].vectorTo(corners[1]);
v.addThis(corners[0].vectorTo(corners[2]));
v.addThis(corners[0].vectorTo(corners[3]));
return corners[0].add(v.multiplyThis(0.25));
}
/** Returns true if the point p is inside this tetrahedron. */
// public boolean isInside(Point p) {
// return isBehind(p,p1,p3,p2) && isBehind(p,p0,p2,p3) && isBehind(p,p0,p3,p1) && isBehind(p,p0,p1,p2);
// TODO implement
// return false;
// }
public boolean isInside(Point p) {
Plane pl012 = new Plane(getCorner(0), getCorner(1), getCorner(2));
Plane pl013 = new Plane(getCorner(0), getCorner(1), getCorner(3));
Plane pl023 = new Plane(getCorner(0), getCorner(2), getCorner(3));
Plane pl123 = new Plane(getCorner(1), getCorner(2), getCorner(3));
return (((pl012.above(p) == 1) && (pl013.above(p) == 1) && (pl023.above(p) == 1) && (pl123.above(p) == 1)) ||
((pl012.below(p) == 1) && (pl013.below(p) == 1) && (pl023.below(p) == 1) && (pl123.below(p) == 1)));
}
/*
* returns TRUE if the tetrahedron is acute. Tetrahedron is acute if all its dihedral angles are acute (< 90___)
* added by pawel 12-11-2011
*/
public boolean isAcute() {
return ((Point.getCosDihedralAngle(corners[0], corners[1], corners[2], corners[3]) > 0.0) &&
(Point.getCosDihedralAngle(corners[0], corners[1], corners[3], corners[2]) > 0.0) &&
(Point.getCosDihedralAngle(corners[0], corners[2], corners[3], corners[1]) > 0.0) &&
(Point.getCosDihedralAngle(corners[2], corners[0], corners[1], corners[3]) > 0.0) &&
(Point.getCosDihedralAngle(corners[1], corners[0], corners[2], corners[3]) > 0.0) &&
(Point.getCosDihedralAngle(corners[1], corners[0], corners[3], corners[2]) > 0.0));
}
public Volume clone(){
return new Tetrahedron(corners[0].clone(), corners[1].clone(), corners[2].clone(), corners[3].clone());
}
/** Return a string representation of this tetrahedron. */
public String toString() {
return toString(2);
}
/** Return a string representation of this tetrahedron with dec
decimals precision */
public String toString(int dec) {
return String.format("Tetrahedron[%s,%s,%s,%s]",corners[0].toString(dec),corners[1].toString(dec),corners[2].toString(dec),corners[3].toString(dec));
}
/** Writes this tetrahedron to System.out
. */
public void toConsole() { System.out.println(toString()); }
/** Writes this tetrahedron to System.out
with dec
decimals precision. */
public void toConsole(int dec) { System.out.println(toString(dec)); }
// public void toScene(J3DScene scene) {
// for (int i = 0; i < 4; i++)
// for (int j = i+1; j < 4; j++) scene.addShape(new LSS(corners[i], corners[j], 0.01), Color.BLUE, 3);
// }
public static void main(String[] args) {
Point p1 = new Point(1,0,0);
Point p2 = new Point(1,1,0);
Point p3 = new Point(2,2,3);
Point p4 = new Point(3,4,2);
Point p5 = new Point(0,0,3);
Tetrahedron tetr = new Tetrahedron(p2, p1, p3, p4);
if (tetr.isInside(p5)) System.out.println("inside"); else System.out.println("outside");
}
}