dust3d/thirdparty/carve-1.4.0/include/carve/geom3d.hpp

301 lines
9.8 KiB
C++

// 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 <carve/carve.hpp>
#include <carve/geom.hpp>
#include <math.h>
#include <carve/math_constants.hpp>
#include <vector>
#include <list>
#include <map>
#if defined(CARVE_DEBUG)
# include <iostream>
#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<typename iter_t, typename adapt_t>
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;
}
}
}
}
}