/* * Created on Nov 1, 2011 * * TODO To change the template for this generated file go to * Window - Preferences - Java - Code Style - Code Templates */ package eu.moonlight3d.math; /** * A triangle class with various helper functions as needed */ public class Triangle { /** * Result of the intersection computation between a triangle and a plane */ public static class PlaneIntersection { /** * Classification of start and end point of intersection as either * on a vertex or on an edge. */ enum EndPointClassification { Edge, Vertex } public Vector3D intersectionStart; public EndPointClassification startClassification; public Vector3D intersectionEnd; public EndPointClassification endClassification; } /** * first corner of triangle */ public Vector3D a; /** * second corner of triangle */ public Vector3D b; /** * third corner of triangle */ public Vector3D c; /** * Default constructor */ public Triangle() { a=new Vector3D(0,0,0); b=new Vector3D(0,0,0); c=new Vector3D(0,0,0); } /** * Constructor taking corners of triangle as arguments * * @param a * @param b * @param c */ public Triangle(Vector3D a, Vector3D b, Vector3D c) { this.a=a.clone(); this.b=b.clone(); this.c=c.clone(); } /** * Clone triangle. * * @return cloned triangle */ public Triangle clone() { Triangle t=new Triangle(a,b,c); return t; } /** * Build a 2d vector from the selected elements of a 3 element vector * * @param bx whether x component should be included * @param bz whether z component should be included * @param v3 original vector from which elements are extracted * @return the 2d vector with the selected components */ private Vector2D extractComponents(boolean bx, boolean bz, Vector3D v3) { double x1, x2; if(bx) { x1=v3.X1; } else { x1=v3.X2; } if(!bz) { x2=v3.X2; } else { x2=v3.X3; } return new Vector2D(x1,x2); } /** * Compute intersection between the given ray and the triangle * * @param p the intersection point (return value) * @param bary the barycentric coordinates of the intersection point (return value) * @param ray the ray to intersect * * @return true if ray intersects triangle, false otherwise */ public boolean intersectRay(Vector3D p, Vector2D bary, Ray ray) { Vector3D o=ray.start; Vector3D d=ray.direction; //calculate normal of triangle Vector3D n = getNormal(); double distance = ((a.sub(o)).multiply(n))/(d.multiply(n)); //intersection point on the plane of the triangle p = o.add(d.multiply(distance)); //project triangle on R2 with normal of the plane being the dominant axis of the triangle's normal boolean bx = false; boolean bz = false; //need most dominant component..but can be smaller than zero Vector3D nSquare=n.multiplyCompontents(n); bx = nSquare.X1 < nSquare.X3; bz = (!bx) || (nSquare.X3 < nSquare.X2); bx = bx || (nSquare.X1 < nSquare.X2); Vector2D a2 = extractComponents(bx, bz, a); Vector2D p2 = extractComponents(bx, bz, p).sub(a2); Vector2D b2 = extractComponents(bx, bz, b).sub(a2); Vector2D c2 = extractComponents(bx, bz, c).sub(a2); //calculate the barycentric coordinates bary.X1 = (p2.X2*c2.X1 - p2.X1*c2.X2) / (b2.X2*c2.X1-b2.X1*c2.X2); bary.X2 = (p2.X2*b2.X1 - p2.X1*b2.X2) / (c2.X2*b2.X1-c2.X1*b2.X2); return (bary.X1 >= 0) && (bary.X2 >= 0) && (bary.X1+bary.X2 <= 1) && (distance >= 0); } /** * get sign of distance between the given vertex and the plane given by * a point and its normal. * * @param vertex vertex to compute signed distance for * @param point point on plane * @param normal normal of plane * @return sign of distance */ private int getDistanceSign(Vector3D vertex, Vector3D point, Vector3D normal) { return (int)Math.signum(vertex.sub(point).multiply(normal)); } /** * Test for the presence of an intersection with the given plane * * @param plane plane to test against * @return true if intersection exists, false otherwise */ public boolean doesIntersect(Plane plane) { Vector3D point=plane.normal.multiply(-1*plane.distance); int sign1=getDistanceSign(a, point, plane.normal); int sign2=getDistanceSign(b, point, plane.normal); int sign3=getDistanceSign(c, point, plane.normal); // if signs differ, an intersection exists if(sign1!=sign2 || sign1!=sign3 || sign2!=sign3) { return true; } // if signs don't differ, but all distances are 0, an intersection also exists if(sign1==sign2 && sign2==sign3 && sign1==0) { return true; } // in all other cases, no intersection is possible return false; } /** * Compute a ray for the line of the intersection between this triangle * and the given triangle assuming that an intersection does exist. * * @param other triangle to compute intersection with * @return ray of intersection line or null if planes are coplanar */ public Ray getRayForFaceIntersection(Triangle other) { final double EPSILON=1e-9; Ray ray=new Ray(); Plane p1=getPlane(); Plane p2=other.getPlane(); Vector3D normal1 = p1.normal; Vector3D normal2 = p2.normal; // direction of intersection ray is perpendicular to both normals ray.direction=normal1.crossMultiply(normal2); // check that the direction is not 0 (parallel faces/ parallel normals) if (!(ray.direction.abs()EPSILON) { ray.start.X1 = 0; ray.start.X2 = (normal2.X3*p1.distance - p2.distance*normal1.X3)/(normal2.X3*normal1.X2 - normal2.X2*normal1.X3); ray.start.X3 = (p2.distance*normal1.X2 - normal2.X2*p1.distance)/(normal2.X3*normal1.X2 - normal2.X2*normal1.X3); } else if(Math.abs(ray.direction.X2)>EPSILON) { ray.start.X1 = (p2.distance*normal1.X3 - normal2.X3*p1.distance)/(normal2.X1*normal1.X3 - normal2.X3*normal1.X1); ray.start.X2 = 0; ray.start.X3 = (normal2.X1*p1.distance - p2.distance*normal1.X1)/(normal2.X1*normal1.X3 - normal2.X3*normal1.X1); } else { ray.start.X1 = (normal2.X2*p1.distance - p2.distance*normal1.X2)/(normal2.X2*normal1.X1 - normal2.X1*normal1.X2); ray.start.X2 = (p2.distance*normal1.X1 - normal2.X1*p1.distance)/(normal2.X2*normal1.X1 - normal2.X1*normal1.X2); ray.start.X3 = 0; } } else { return null; } ray.direction=ray.direction.norm(); return ray; } /** * Return triangle normal * * @return triangle normal */ public Vector3D getNormal() { return (b.sub(a)).crossMultiply(c.sub(a)); } /** * Return triangle area * * @return triangle area */ public double getArea() { return 0.5*getNormal().abs(); } /** * Return the plane in which the triangle exists * * @return plane of the triangle */ public Plane getPlane() { Plane p=new Plane(); p.fromPointAndNormal(a, getNormal()); return p; } /** * Return barycenter of triangle * * @return triangle barycenter */ public Vector3D getBarycenter() { return (a.add(b.add(c))).multiply(1/3.0); } /** * Return axis-aligned bounding box of triangle * * @return bounding box */ public AxisAlignedBoundingBox getAABB() { return new AxisAlignedBoundingBox(a).merge(b).merge(c); } /** * Invert orientation of the triangle (flip normal) */ public void invertOrientation() { Vector3D temp=a; a=b; b=temp; } }