// Begin License: // Copyright (C) 2006-2008 Tobias Sargeant (tobias.sargeant@gmail.com). // All rights reserved. // // This file is part of the Carve CSG Library (http://carve-csg.com/) // // This file may be used under the terms of the GNU General Public // License version 2.0 as published by the Free Software Foundation // and appearing in the file LICENSE.GPL2 included in the packaging of // this file. // // This file is provided "AS IS" with NO WARRANTY OF ANY KIND, // INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE. // End: #pragma once #include #include #include #include #include #include #include #if defined(CARVE_DEBUG) # include #endif namespace carve { namespace geom3d { typedef carve::geom::plane<3> Plane; typedef carve::geom::ray<3> Ray; typedef carve::geom::linesegment<3> LineSegment; typedef carve::geom::vector<3> Vector; template bool fitPlane(iter_t begin, iter_t end, adapt_t adapt, Plane &plane) { Vector centroid; carve::geom::centroid(begin, end, adapt, centroid); iter_t i; Vector n = Vector::ZERO(); Vector v, z; Vector p1, p2, p3, c1, c2; if (begin == end) return false; i = begin; p1 = c1 = adapt(*i++); if (i == end) return false; p2 = c2 = adapt(*i++); if (i == end) return false; #if defined(CARVE_DEBUG) size_t N = 2; #endif while (i != end) { p3 = adapt(*i++); v = cross(p3 - p2, p1 - p2); if (v.v[largestAxis(v)]) v.negate(); n += v; p1 = p2; p2 = p3; #if defined(CARVE_DEBUG) ++N; #endif } p1 = p2; p2 = p3; p3 = c1; v = cross(p3 - p2, p1 - p2); if (v.v[largestAxis(v)]) v.negate(); n += v; p1 = p2; p2 = p3; p3 = c2; v = cross(p3 - p2, p1 - p2); if (v.v[largestAxis(v)]) v.negate(); n += v; n.normalize(); plane.N = n; plane.d = -dot(n, centroid); #if defined(CARVE_DEBUG) if (N > 3) { std::cerr << "N = " << N << " fitted distance:"; for (i = begin; i != end; ++i) { Vector p = adapt(*i); std::cerr << " {" << p << "} " << distance(plane, p); } std::cerr << std::endl; } #endif return true; } bool planeIntersection(const Plane &a, const Plane &b, Ray &r); IntersectionClass rayPlaneIntersection(const Plane &p, const Vector &v1, const Vector &v2, Vector &v, double &t); IntersectionClass lineSegmentPlaneIntersection(const Plane &p, const LineSegment &line, Vector &v); RayIntersectionClass rayRayIntersection(const Ray &r1, const Ray &r2, Vector &v1, Vector &v2, double &mu1, double &mu2); // test whether point d is above, below or on the plane formed by the triangle a,b,c. // return: +ve = d is below a,b,c // -ve = d is above a,b,c // 0 = d is on a,b,c static inline double orient3d(const carve::geom3d::Vector &a, const carve::geom3d::Vector &b, const carve::geom3d::Vector &c, const carve::geom3d::Vector &d) { return dotcross((a - d), (b - d), (c - d)); } // Volume of a tetrahedron described by 4 points. Will be // positive if the anticlockwise normal of a,b,c is oriented out // of the tetrahedron. // // see: http://mathworld.wolfram.com/Tetrahedron.html inline double tetrahedronVolume(const Vector &a, const Vector &b, const Vector &c, const Vector &d) { return dotcross((a - d), (b - d), (c - d)) / 6.0; } /** * \brief Determine whether p is internal to the wedge defined by * the area between the planes defined by a,b,c and a,b,d * angle abc, where ab is the apex of the angle. * * @param[in] a * @param[in] b * @param[in] c * @param[in] d * @param[in] p * * @return true, if p is contained in the wedge defined by the * area between the planes defined by a,b,c and * a,b,d. If the wedge is reflex, p is considered to * be contained if it lies on either plane. Acute * wdges do not contain p if p lies on either * plane. This is so that internalToWedge(a,b,c,d,p) = * !internalToWedge(a,b,d,c,p) */ inline bool internalToWedge(const Vector &a, const Vector &b, const Vector &c, const Vector &d, const Vector &p) { bool reflex = (c < d) ? orient3d(a, b, c, d) >= 0.0 : orient3d(a, b, d, c) < 0.0; double d1 = orient3d(a, b, c, p); double d2 = orient3d(a, b, d, p); if (reflex) { // above a,b,c or below a,b,d (or coplanar with either) return d1 <= 0.0 || d2 >= 0.0; } else { // above a,b,c and below a,b,d return d1 < 0.0 && d2 > 0.0; } } /** * \brief Determine the ordering relationship of a and b, when * rotating around direction, starting from base. * * @param[in] adirection * @param[in] base * @param[in] a * @param[in] b * * @return * * -1, if a is ordered before b around, rotating about direction. * * 0, if a and b are equal in angle. * * +1, if a is ordered after b around, rotating about direction. */ inline int compareAngles(const Vector &direction, const Vector &base, const Vector &a, const Vector &b) { double d1 = carve::geom3d::orient3d(carve::geom::VECTOR(0,0,0), direction, a, b); double d2 = carve::geom3d::orient3d(carve::geom::VECTOR(0,0,0), direction, base, a); double d3 = carve::geom3d::orient3d(carve::geom::VECTOR(0,0,0), direction, base, b); // CASE: a and b are coplanar wrt. direction. if (d1 == 0.0) { // a and b point in the same direction. if (dot(a, b) > 0.0) { // Neither is less than the other. return 0; } // a and b point in opposite directions. double d2 = carve::geom3d::orient3d(carve::geom::VECTOR(0,0,0), direction, base, a); // * if d2 < 0.0, a is above plane(direction, base) and is less // than b. // * if d2 == 0.0 a is coplanar with plane(direction, base) and is // less than b if it points in the same direction as base. // * if d2 > 0.0, a is below plane(direction, base) and is greater // than b. if (d2 == 0.0) { return dot(a, base) > 0.0 ? -1 : +1; } if (d3 == 0.0) { return dot(b, base) > 0.0 ? +1 : -1; } if (d2 < 0.0 && d3 > 0.0) return -1; if (d2 > 0.0 && d3 < 0.0) return +1; // both a and b are to one side of plane(direction, base) - // rounding error (if a and b are truly coplanar with // direction, one should be above, and one should be below any // other plane that is not itself coplanar with // plane(direction, a|b) - which would imply d2 and d3 == 0.0). // If both are below plane(direction, base) then the one that // points in the same direction as base is greater. // If both are above plane(direction, base) then the one that // points in the same direction as base is lesser. if (d2 > 0.0) { return dot(a, base) > 0.0 ? +1 : -1; } else { return dot(a, base) > 0.0 ? -1 : +1; } } // CASE: a and b are not coplanar wrt. direction if (d2 < 0.0) { // if a is above plane(direction,base), then a is less than b if // b is below plane(direction,base) or b is above plane(direction,a) return (d3 > 0.0 || d1 < 0.0) ? -1 : +1; } else if (d2 == 0.0) { // if a is on plane(direction,base) then a is less than b if a // points in the same direction as base, or b is below // plane(direction,base) return (dot(a, base) > 0.0 || d3 > 0.0) ? -1 : +1; } else { // if a is below plane(direction,base), then a is less than b if b // is below plane(direction,base) and b is above plane(direction,a) return (d3 > 0.0 && d1 < 0.0) ? -1 : +1; } } // The anticlockwise angle from vector "from" to vector "to", oriented around the vector "orient". static inline double antiClockwiseAngle(const Vector &from, const Vector &to, const Vector &orient) { double dp = dot(from, to); Vector cp = cross(from, to); if (cp.isZero()) { if (dp < 0) { return M_PI; } else { return 0.0; } } else { if (dot(cp, orient) > 0.0) { return acos(dp); } else { return M_TWOPI - acos(dp); } } } static inline double antiClockwiseOrdering(const Vector &from, const Vector &to, const Vector &orient) { double dp = dot(from, to); Vector cp = cross(from, to); if (cp.isZero()) { if (dp < 0) { return 2.0; } else { return 0.0; } } else { if (dot(cp, orient) > 0.0) { // 1..-1 -> 0..2 return 1.0 - dp; } else { // -1..1 -> 2..4 return dp + 1.0; } } } } }