package ProGAL.geom3d; import java.awt.Color; import ProGAL.geom3d.complex.CTetrahedron; import ProGAL.geom3d.complex.CVertex; import ProGAL.geom3d.volumes.Cylinder; import ProGAL.geom3d.volumes.LSS; import ProGAL.geom3d.volumes.Sphere; import ProGAL.math.Constants; import ProGAL.math.Functions; /** * A plane in (x,y,z)-space represented by a point and a normal. * * Assuming that p is a point on the plane and n is the normal vector, * the half-space {q|pq__n>0} is called the 'upper halfspace' wrt. the plane * and vice versa for the 'lower halfspace'. * If the unit normal vector is n = (nx,ny,nz) and the point on the plane is p = (px,py,pz), then the plane has * equation ax + bx + cx = d where a = nx, b = ny, c = nz, and d = px*nx + py*ny + pz*nz */ public class Plane implements Shape{ /** Normal vector of the plane. */ protected Vector normal; /** Point in the plane. */ protected Point point; /** Constructs a plane with the normal vector n containing point p. */ public Plane(Point p, Vector n) { this.normal = n.normalizeThis(); this.point = p; } /** Constructs a plane with the normal vector n containing the point (0,0,0). */ public Plane(Vector n) { this.normal = n.normalizeThis(); this.point = new Point(0,0,0); } /** Constructs a plane with the normal vector n at distance d from the origin. * If d > 0 then the origin is in the half-space determined by the direction of n */ public Plane(Vector n, double d) { this.normal = n.normalizeThis(); this.point = new Point(-d*n.x(), -d*n.y(), -d*n.z()); } /** * Constructs a plane through three points using the first point as defining point. * The normal of the plane will be decided by the order of p, q and r such that if * the right hand follows the rotation from p to q to r the thumb points in the * normals direction. TODO: Test this * * An error is thrown if the points are collinear. */ public Plane(Point p, Point q, Point r) { if(Point.collinear(p, q, r)) throw new Error("Cant construct plane: Points are collinear"); normal = p.vectorTo(q).crossThis(p.vectorTo(r)).normalizeThis(); this.point = p; } /** Constructs a plane bisecting two points */ public Plane(Point p, Point q) { normal = new Vector(p, q).normalizeThis(); point = Point.getMidpoint(p, q); } private double getD(){ return -normal.x()*point.x() - normal.y()*point.y() - normal.z()*point.z(); } /** Get the point defining this plane. */ public Point getPoint(){ return point; } /** Return the normal defining this plane. */ public Vector getNormal(){ return normal; } /** Set the normal to n. */ public void setNormal(Vector n) { this.normal = n; } /** Get the projection of p onto this plane. */ public Point projectPoint(Point p) { //Daisy /* System.out.println("Plane, point = "+point.toString()); System.out.println("Plane, p = "+p.getCoord(0)+", "+p.getCoord(1)+", "+p.getCoord(2)); Vector v = point.subtract(p).toVector(); System.out.println("Plane, v = "+v.toString()); double dist = v.dot(normal); System.out.println("Plane, dist = "+dist); Point projPoint = point.subtract(normal.multiply(dist)); return projPoint;*/ //Rasmus double t = normal.x()*p.x() + normal.y()*p.y() + normal.z()*p.z() + getD(); return new Point(p.x() - normal.x()*t, p.y() - normal.y()*t, p.z() - normal.z()*t); } /** Returns 1/0/-1 if point p is above/on/below this plane. */ public int above(Point p) { double dotP = normal.dot(p.toVector()); double d = getD(); if (dotP > -d) return 1; if (dotP < -d) return -1; return 0; } /** Returns 1/0/-1 if point p is below/on/above this plane */ public int below(Point p) { return -above(p); } /** Get the distance of point p to this plane */ public double getDistance(Point p) { return Math.abs(normal.dot(p.toVector()) + getD()); } /** Get the unsigned angle between this plane and p. */ public double getUnsignedDihedralAngle(Plane p){ return Math.acos(normal.dot(p.normal)); } /** Get the intersection of a line with the plane. Returns null if the line is * parallel to plane. */ public Point getIntersection(Line line) { double denom = normal.dot(line.getDir()); if (denom==0) return null; else { Point a = line.getP(); Vector pa = point.vectorTo(a); double u = normal.dot(pa)/denom; return new Point(a.x() - u*line.dir.x(), a.y() - u*line.dir.y(), a.z() - u*line.dir.z()); } } /** Get the line-parameter of the intersection between a plane and a line. Returns infinity * if line is parallel to plane. TODO: Consider moving to Line3d * */ public double getIntersectionParameter(Line line) { double denom = normal.dot(line.getDir()); if (denom == 0) return Double.POSITIVE_INFINITY; else { Point a = line.getP(); Vector pa = point.vectorTo(a); double u = normal.dot(pa)/denom; return u; } } /** Get the intersection of a segment with the plane. Returns null if line is * parallel to plane.*/ public Point getIntersection(LineSegment sgm) { //Daisy double dist0 = normal.dot(sgm.getA().subtract(point)); double dist1 = normal.dot(sgm.getB().subtract(point)); if (dist0*dist1>0) return null; Vector x = (Vector)(sgm.getB().subtract(sgm.getA())).multiplyThis(1/sgm.getB().distance(sgm.getA())).toVector(); double cos = normal.dot(x); if (Math.abs(cos)>=Constants.EPSILON) { return sgm.getB().subtract(x.multiply(dist1/cos)); } else return null; //Rasmus /* Vector dir = sgm.getAToB(); double denom = normal.dot(dir); if (denom == 0) return null; else { Vector pa = point.vectorTo(sgm.a); double u = normal.dot(pa)/denom; if ((u < 0) || (u > 1)) return null; else return new Point(sgm.a.x() + u*dir.x(), sgm.a.y() + u*dir.y(), sgm.a.z() + u*dir.z()); }*/ } public Double getIntersectionAngle(Circle circle, Point p, Vector dir) { Vector nC = circle.getNormal(); if (nC.isParallel(normal)) return null; Plane circlePlane = new Plane(circle.getCenter(), nC); Line line = getIntersection(circlePlane); // line.toScene(scene, 0.01, Color.blue); double dist = line.getDistance(circle.getCenter()); if (dist > circle.getRadius() - Constants.EPSILON) return null; return circle.getFirstIntersection(line, p, dir); } /** Get intersection of a circle with the plane. Returns null if the plane does not intersect the circle * or if the circle is in the plane. Returns the touch point if the plane is tangent to the circle. * Otherwise returns 2 points */ public Point[] getIntersection(Circle circle) { Vector u = circle.getNormal().getOrthonormal().multiply(circle.getRadius()); return getIntersection(circle, u); } public Point[] getIntersection(Circle circle, Vector u) { Vector nC = circle.getNormal(); if (nC.isParallel(normal)) return null; Plane circlePlane = new Plane(circle.getCenter(), nC); Line line = getIntersection(circlePlane); double dist = line.getDistance(circle.getCenter()); if (dist > circle.getRadius() + Constants.EPSILON) return null; if (dist > circle.getRadius() - Constants.EPSILON) { Point intPoints[] = new Point[1]; intPoints[0] = line.orthogonalProjection(circle.getCenter()); return intPoints; } Vector v = u.clone(); nC.rotateIn(v, Math.PI/2); Vector cp = new Vector(circle.getCenter(), point); double a = u.dot(normal); double b = v.dot(normal); double c = cp.dot(normal); double r = Math.sqrt(a*a + b*b); double x = Math.atan2(b/r, a/r); // atan2(sin x, cos x) double alpha1 = Math.acos(c/r); double alpha2 = 2*Math.PI - alpha1; double t1 = alpha1 + x; double t2 = alpha2 + x; Vector[] intVectors = new Vector[2]; intVectors[0] = u.clone(); nC.rotateIn(intVectors[0], t1); intVectors[1] = u.clone(); nC.rotateIn(intVectors[1], t2); Point intPoints[] = new Point[2]; intPoints[0] = circle.getCenter().clone().add(intVectors[0]); intPoints[1] = circle.getCenter().clone().add(intVectors[1]); return intPoints; } /** returns the intersection line with another plane*/ public Line getIntersection(Plane pl) { Vector dir = normal.cross(pl.getNormal()); if (dir.isZeroVector()) return null; double h1 = normal.dot(new Vector(point)); double h2 = pl.getNormal().dot(new Vector(pl.getPoint())); double dd = normal.dot(pl.getNormal()); double denom = 1 - dd*dd; double c1 = (h1 - h2*dd)/denom; double c2 = (h2 - h1*dd)/denom; Point q = new Point(c1*normal.x() + c2*pl.getNormal().x(), c1*normal.y() + c2*pl.getNormal().y(), c1*normal.z() + c2*pl.getNormal().z()); return new Line(q, dir); } /** Get intersection of a sphere with the plane. Returns null if the plane does not intersect the sphere. * Returns a circle with radius 0 if the plane is tangent to the sphere. * Otherwise returns circle */ public Circle getIntersection(Sphere sphere) { double dist = this.getDistance(sphere.getCenter()); double rad = sphere.getRadius(); if (dist - rad > Constants.EPSILON) return null; Point center = projectPoint(sphere.getCenter()); if (dist - rad > -Constants.EPSILON) return new Circle(center, 0, null); return new Circle(center, Math.sqrt(rad*rad - center.distanceSquared(sphere.getCenter())), normal); } /** Returns the defining point for this plane. The center of a plane is not well-defined, so * to implement the shape interface the defining point is simply used. */ public Point getCenter() { return point.clone(); } private static Vector m(Vector v, double beta) { return new Vector(v.x()*Math.cos(beta) - v.y()*Math.sin(beta), v.y()*Math.cos(beta) + v.x()*Math.sin(beta), v.z()); } private static Point q(Point q0, double beta) { return new Point(q0.distance()*Math.cos(beta), q0.distance()*Math.sin(beta), q0.z()); } }