33 #include <SketchUpAPI/model/vertex.h> 35 #include "SUAPI-CppWrapper/Geometry.hpp" 37 #include "SUAPI-CppWrapper/model/Face.hpp" 38 #include "SUAPI-CppWrapper/model/Edge.hpp" 48 Radians::Radians(
const double &rhs):
52 m_val = std::fmod(rhs, PI2);
55 m_val = PI2 - std::fmod(std::fabs(rhs), PI2);
65 Radians::operator double()
const 74 m_val = radians.m_val;
84 Radians Radians::operator-(
const double value)
const {
89 Radians Radians::operator*(
const double multiplier)
const {
90 return m_val * multiplier;
94 Radians Radians::operator/(
const double divider)
const {
95 if (std::abs(divider) < EPSILON) {
96 throw std::invalid_argument(
"CW::Radians::operator/() cannot divide by zero");
98 return m_val * divider;
105 bool Radians::operator==(
const Radians& rhs)
const {
106 return (fabs(static_cast<double>(*
this) - static_cast<double>(rhs))) < EPSILON;
109 bool Radians::operator==(
const double rhs)
const {
110 return (fabs(m_val - rhs)) < EPSILON;
114 double diff = std::abs(m_val - other.m_val);
122 bool Radians::closest(
const Radians& value) {
130 constexpr
double Vector3D::EPSILON;
132 Vector3D::Vector3D():
136 Vector3D::Vector3D(SUVector3D su_vector):
143 Vector3D::Vector3D(
double x,
double y,
double z):
144 m_vector(SUVector3D{x,y,z}),
150 Vector3D::Vector3D(
bool valid):
151 m_vector(SUVector3D{0.0,0.0,0.0}),
159 Vector3D::Vector3D(
const Edge &edge):
163 Vector3D::Vector3D(
const Vector3D &vector):
164 m_vector(vector.m_vector),
165 is_null(vector.is_null),
173 Vector3D(SUVector3D{point.x, point.y, point.z})
180 if (
this == &vector) {
186 is_null = vector.is_null;
199 Vector3D::operator SUVector3D()
const {
204 Vector3D::operator
const SUVector3D*()
const {
209 Vector3D::operator
Point3D()
const {
219 assert(!!vector && !is_null);
220 return Vector3D(m_vector.x + vector.x, m_vector.y + vector.y, m_vector.z + vector.z);
227 Vector3D Vector3D::operator-()
const {
232 assert(!!vector && !is_null);
233 return Vector3D(x - vector.x, y - vector.y, z - vector.z);
235 Vector3D Vector3D::operator*(
const double &scalar)
const {
237 return Vector3D( x * scalar, y * scalar, z * scalar);
239 Vector3D Vector3D::operator/(
const double &scalar)
const {
241 if (std::abs(scalar) < EPSILON) {
242 throw std::invalid_argument(
"CW::Vector3D::operator/() - cannot divide by zero");
244 return Vector3D( x / scalar, y / scalar, z / scalar);
251 if (!!lhs && !!rhs &&
252 std::abs(lhs.x - rhs.x) < Vector3D::EPSILON &&
253 std::abs(lhs.y - rhs.y) < Vector3D::EPSILON &&
254 std::abs(lhs.z - rhs.z) < Vector3D::EPSILON) {
277 double Vector3D::length()
const {
279 return sqrt(pow(x,2) + pow(y,2) + pow(z,2));
284 return *
this / length();
287 double Vector3D::angle(
const Vector3D& vector_b)
const {
290 double dot_product = unit().
dot(vector_b.unit());
291 if (dot_product < -1.0) {
294 else if (dot_product > 1.0) {
297 return acos(dot_product);
301 assert(!!vector2 && !is_null);
302 return (x * vector2.x) + (y * vector2.y) + (z * vector2.z);
306 assert(!!point && !is_null);
307 return (x * point.x) + (y * point.y) + (z * point.z);
312 assert(!!vector2 && !is_null);
313 return Vector3D{y * vector2.z - z * vector2.y,
314 z * vector2.x - x * vector2.z,
315 x * vector2.y - y * vector2.x};
319 if (this->length() < EPSILON || vector_b.length() < EPSILON) {
320 return Vector3D::Colinearity::UNDEFINED;
322 Vector3D combined = (*this) + vector_b;
323 double combined_length = combined.length();
324 double added_length = (*this).length() + vector_b.length();
325 if (std::fabs(added_length - combined_length) < EPSILON) {
326 return Vector3D::Colinearity::COLINEAR_PRO;
328 double subtracted_length = (*this).length() - vector_b.length();
329 if (std::fabs(subtracted_length - combined_length) < EPSILON) {
330 return Vector3D::Colinearity::COLINEAR_ANTI;
332 return Vector3D::Colinearity::NO;
338 double b_dot_b = axis.
dot(axis);
339 Vector3D a_component_b_dir = ((*this).dot(axis) * b_dot_b) * axis;
340 Vector3D a_component_b_orth = (*this) - (((*this).dot(axis) / b_dot_b) * axis);
342 double x1 = cos(angle) / a_component_b_orth.length();
343 double x2 = sin(angle) / w.length();
344 Vector3D a_component_b_orth_rot = a_component_b_orth.length() * ((x1 * a_component_b_orth) + (x2 * w));
345 return a_component_b_orth_rot + a_component_b_dir;
361 m_point(SUPoint3D{0.0,0.0,0.0}),
376 Point3D(SUPoint3D{su_vector.x, su_vector.y, su_vector.z})
384 Point3D(other.x, other.y, other.z)
386 is_null = other.is_null;
391 Point3D(SUPoint3D{vector.x, vector.y, vector.z})
395 if (
this == &point) {
401 is_null = point.is_null;
406 Point3D::operator SUPoint3D()
const {
return SUPoint3D {m_point.x, m_point.y, m_point.z}; }
409 Point3D::operator
const SUPoint3D*()
const{
414 Point3D::operator
Vector3D()
const {
return Vector3D(m_point.x, m_point.y, m_point.z); }
418 assert(!!point && !is_null);
419 return Point3D(m_point.x + point.x, m_point.y + point.y, m_point.z + point.z);
423 assert(!!vector && !is_null);
424 return Point3D(m_point.x + vector.x, m_point.y + vector.y, m_point.z + vector.z);
429 return (*
this) +
Point3D(point);
433 assert(!!point && !is_null);
434 return Vector3D(m_point.x - point.x, m_point.y - point.y, m_point.z - point.z);
438 assert(!!vector && !is_null);
439 return (*
this) -
static_cast<Point3D>(vector);
442 Point3D Point3D::operator-(
const SUPoint3D &point)
const {
444 return (*
this) -
Point3D(point);
447 Point3D Point3D::operator*(
const double &scalar)
const {
449 return Point3D(m_point.x * scalar, m_point.y * scalar, m_point.z * scalar);
452 Point3D Point3D::operator/(
const double &scalar)
const {
454 if (std::abs(scalar) < EPSILON) {
455 throw std::invalid_argument(
"Point3D::operator/: cannot divide by zero");
457 return Point3D(m_point.x / scalar, m_point.y / scalar, m_point.z / scalar);
474 if (!!lhs && !!rhs &&
475 std::abs(lhs.x - rhs.x) < Point3D::EPSILON &&
476 std::abs(lhs.y - rhs.y) < Point3D::EPSILON &&
477 std::abs(lhs.z - rhs.z) < Point3D::EPSILON) {
496 if (vector_a.length() < EPSILON || vector_b.length() < EPSILON) {
497 throw std::invalid_argument(
"CW::Point3D::intersection_between_lines() - cannot find intersection of lines with zero length");
501 Vector3D a_to_b = point_b - point_a;
502 Vector3D vec_a_cross_b = vector_a.unit().
cross(vector_b.unit());
503 Vector3D a_to_b_cross_vec_a = a_to_b.unit().
cross(vector_a.unit());
506 if (vec_a_cross_b == zero_vector) {
507 if (a_to_b_cross_vec_a == zero_vector) {
509 if (!return_colinear) {
512 bool opposite_direction;
513 if (vector_a.unit() == vector_b.unit())
514 opposite_direction =
false;
516 opposite_direction =
true;
517 double vec_a_factor_0 = a_to_b.
dot(vector_a) / vector_a.
dot(vector_a);
518 double vec_a_factor_1 = vec_a_factor_0 + (vector_b.
dot(vector_a) / vector_a.
dot(vector_a));
519 double vec_a_epsilon = Vector3D::EPSILON / vector_a.length();
520 if ((!opposite_direction && vec_a_factor_0 < vec_a_epsilon && vec_a_factor_1 > -vec_a_epsilon) ||
521 (opposite_direction && vec_a_factor_1 < vec_a_epsilon && vec_a_factor_0 > -vec_a_epsilon)) {
525 if ((!opposite_direction && vec_a_factor_0 > -vec_a_epsilon && vec_a_factor_0 < (1 + vec_a_epsilon))) {
529 if (opposite_direction && vec_a_factor_1 > -vec_a_epsilon && vec_a_factor_1 < (1 + vec_a_epsilon)) {
531 return point_b + vector_b;
543 Line3D line_a(point_a, vector_a);
544 Line3D line_b(point_b, vector_b);
545 std::pair<Point3D, Point3D> closest_points = line_a.
closest_points(line_b);
547 if (closest_points.first != closest_points.second) {
552 Point3D intersection = closest_points.first;
553 Vector3D a_to_int = intersection - point_a;
554 Vector3D b_to_int = intersection - point_b;
556 double a_direction_factor = 0.0;
557 if (a_to_int != zero_vector)
558 a_direction_factor = vector_a.unit().
dot(a_to_int.unit());
559 double b_direction_factor = 0.0;
560 if (b_to_int != zero_vector)
561 b_direction_factor = vector_b.unit().
dot(b_to_int.unit());
563 double a_factor = a_direction_factor * a_to_int.length() / vector_a.length();
564 double b_factor = b_direction_factor * b_to_int.length() / vector_b.length();
565 double a_epsilon = Vector3D::EPSILON / vector_a.length();
566 double b_epsilon = Vector3D::EPSILON / vector_b.length();
567 if (a_factor > -a_epsilon && a_factor < 1.0 + a_epsilon &&
568 b_factor > -b_epsilon && b_factor < 1.0 + b_epsilon) {
578 if (ray_b == zero_vector || vector_a == zero_vector) {
579 throw std::invalid_argument(
"CW::Point3D::ray_line_intersection() - given ray/line have zero length");
581 Vector3D a_to_b = point_b - point_a;
583 if (a_to_b == zero_vector) {
587 Vector3D vec_a_cross_b_z_comp = vector_a.unit().
cross(ray_b.unit());
588 Vector3D a_to_b_cross_vec_a_z_comp = a_to_b.unit().
cross(vector_a.unit());
590 if (vec_a_cross_b_z_comp == zero_vector) {
591 if (a_to_b_cross_vec_a_z_comp == zero_vector) {
593 if (!return_colinear) {
596 double b_to_a_factor = b_to_a.
dot(ray_b) / ray_b.
dot(ray_b);
597 double b_to_a_end_factor = b_to_a_factor + (vector_a.
dot(ray_b) / ray_b.
dot(ray_b));
598 if ((b_to_a_factor < 0.0 && b_to_a_end_factor > 0.0) || (b_to_a_factor > 0.0 && b_to_a_end_factor < 0.0)) {
602 if (b_to_a_factor > 0.0) {
603 if (b_to_a_factor < b_to_a_end_factor) {
609 return point_a + vector_a;
622 Line3D line_a(point_a, vector_a);
623 Line3D line_b(point_b, ray_b);
624 std::pair<Point3D, Point3D> closest_points = line_a.
closest_points(line_b);
626 if (closest_points.first != closest_points.second) {
631 Point3D intersection = closest_points.first;
632 Vector3D a_to_int = intersection - point_a;
633 Vector3D b_to_int = intersection - point_b;
635 double a_direction_factor = 0.0;
636 if (a_to_int != zero_vector)
637 a_direction_factor = vector_a.unit().
dot(a_to_int.unit());
638 double b_direction_factor = 0.0;
639 if (b_to_int != zero_vector)
640 b_direction_factor = ray_b.unit().
dot(b_to_int.unit());
642 double a_factor = a_direction_factor * a_to_int.length() / vector_a.length();
643 double b_factor = b_direction_factor * b_to_int.length() / ray_b.length();
644 double a_epsilon = Vector3D::EPSILON / vector_a.length();
645 double b_epsilon = Vector3D::EPSILON / ray_b.length();
646 if (a_factor > -a_epsilon && a_factor < 1.0 + a_epsilon &&
647 b_factor > -b_epsilon) {
676 Plane3D(SUPlane3D{0.0,0.0,0.0,0.0})
679 Plane3D::Plane3D(SUPlane3D plane):
681 is_null((m_plane.a == 0.0 && m_plane.b == 0.0 && m_plane.c == 0.0)),
689 Plane3D::Plane3D(
double a,
double b,
double c,
double d):
694 Plane3D::Plane3D(
const Face &face):
699 Plane3D::Plane3D(
const Plane3D &plane):
700 m_plane(plane.m_plane),
701 is_null(plane.is_null),
710 Plane3D(SUPlane3D{normal.unit().x, normal.unit().y, normal.unit().z, -normal.unit().
dot(point)})
718 Plane3D::Plane3D(
bool valid):
719 m_plane(SUPlane3D{1.0,0.0,0.0,0.0}),
736 is_null = plane.is_null;
754 if (!!lhs && !!rhs &&
755 std::abs(lhs.a - rhs.a) < Plane3D::EPSILON &&
756 std::abs(lhs.b - rhs.b) < Plane3D::EPSILON &&
757 std::abs(lhs.c - rhs.c) < Plane3D::EPSILON &&
758 std::abs(lhs.d - rhs.d) < Plane3D::EPSILON) {
774 if ((this->
normal() * this->d) == (test_plane.
normal() * test_plane.d)) {
784 const double determinant = pow(line_vector.length(), 2);
786 if (determinant < EPSILON) {
791 ((*this).normal().cross(line_vector) * plane2.d)) / determinant;
792 return Line3D(line_point, line_vector);
796 return line.intersection(*
this);
802 Vector3D unit_direction = direction.unit();
803 double combined_length =
Vector3D(this->
normal() + unit_direction).length();
804 double distance_from_plane = this->
distance(start_point);
805 if (combined_length > 1.0) {
806 if (distance_from_plane > EPSILON) {
814 if (distance_from_plane < -EPSILON) {
844 if (fabs(distance) < EPSILON) {
852 return Plane3D(a, b, c, (d - offset_by));
858 throw std::invalid_argument(
"CW::Plane3D::parallel(): given plane is null");
861 throw std::logic_error(
"CW::Plane3D::parallel(): plane is null");
872 throw std::logic_error(
"CW::Plane3D::inverse(): plane is null");
874 return Plane3D(a*-1, b*-1, c*-1, d*-1);
877 double Plane3D::angle_with(
const Plane3D& plane2)
const {
879 throw std::invalid_argument(
"CW::Plane3D::angle_with(): given plane is null");
882 throw std::logic_error(
"CW::Plane3D::angle_with(): plane is null");
889 Plane3D::operator SUPlane3D()
const {
return m_plane; }
893 if (loop_points.size() < 3) {
894 throw std::invalid_argument(
"CW::Plane3D::plane_from_loop(): not enough points given for a valid loop");
931 for (
size_t i=0; i < loop_points.size(); i++) {
933 Point3D current = loop_points[i];
934 if (i == loop_points.size() - 1) {
935 next = loop_points[0];
938 next = loop_points[i+1];
940 normal.x += (current.y - next.y) * (current.z + next.z);
941 normal.y += (current.z - next.z) * (current.x + next.x);
942 normal.z += (current.x - next.x) * (current.y + next.y);
947 return Plane3D (loop_points[0], normal.unit());
966 m_bounding_box(bounding_box)
976 BoundingBox3D::operator SUBoundingBox3D()
const {
977 return m_bounding_box;
981 return Point3D(m_bounding_box.min_point);
985 m_bounding_box.min_point = point;
989 return Point3D(m_bounding_box.max_point);
993 m_bounding_box.max_point = point;
1008 m_direction(direction.unit()),
1010 direction(m_direction)
1024 direction(m_direction)
1029 m_point(other.m_point),
1030 m_direction(other.m_direction),
1031 is_null(other.is_null),
1033 direction(m_direction)
1042 direction = line.direction;
1043 is_null = line.is_null;
1056 Point3D Line3D::intersection(
const Line3D &other_line)
const {
1058 throw std::invalid_argument(
"CW::Line3D::intersection(): given line is null");
1061 throw std::logic_error(
"CW::Line3D::intersection(): this line is null");
1064 std::pair<Point3D, Point3D> close_points = this->
closest_points(other_line);
1065 if (!close_points.first) {
1066 return close_points.first;
1068 Vector3D vector_between = close_points.first - close_points.second;
1069 double distance = vector_between.length();
1070 if (distance > EPSILON) {
1074 return close_points.first;
1080 throw std::invalid_argument(
"CW::Line3D::intersection(): given plane is null");
1083 throw std::logic_error(
"CW::Line3D::intersection(): this line is null");
1086 double numerator = (plane.a * this->point.x) + (plane.b * this->point.y) + (plane.c * this->point.z) + plane.d;
1087 double denominator = -(plane.a * this->direction.x) - (plane.b * this->direction.y) - (plane.c * this->direction.z);
1088 if (std::abs(denominator) < EPSILON) {
1092 double u = numerator / denominator;
1093 Point3D point_of_intersection = this->point + (u * this->direction);
1094 return point_of_intersection;
1100 throw std::invalid_argument(
"CW::Line3D::closest_points(): given line is null");
1103 throw std::logic_error(
"CW::Line3D::closest_points(): this line is null");
1107 double d1343,d4321,d1321,d4343,d2121;
1110 Vector3D p13 = this->point - other_line.point;
1111 Vector3D p43 = other_line.direction;
1112 if (std::abs(p43.x) < EPSILON && std::abs(p43.y) < EPSILON && std::abs(p43.z) < EPSILON) {
1114 return std::pair<Point3D,Point3D> (
Point3D(
false),
Point3D(
false));
1121 d1343 = p13.x * p43.x + p13.y * p43.y + p13.z * p43.z;
1122 d4321 = p43.x * p21.x + p43.y * p21.y + p43.z * p21.z;
1123 d1321 = p13.x * p21.x + p13.y * p21.y + p13.z * p21.z;
1124 d4343 = p43.x * p43.x + p43.y * p43.y + p43.z * p43.z;
1125 d2121 = p21.x * p21.x + p21.y * p21.y + p21.z * p21.z;
1127 denom = d2121 * d4343 - d4321 * d4321;
1128 if (std::abs(denom) < EPSILON) {
1130 return std::pair<Point3D,Point3D> (
Point3D(
false),
Point3D(
false));
1132 numer = d1343 * d4321 - d1321 * d4343;
1134 double mua = numer / denom;
1135 double mub = (d1343 + d4321 * mua) / d4343;
1136 Point3D pa = this->point + (this->direction * mua);
1137 Point3D pb = other_line.point + (other_line.direction * mub);
1138 return std::pair<Point3D,Point3D> (pa, pb);
1144 throw std::invalid_argument(
"CW::Line3D::closest_point(): given point is null");
1147 throw std::logic_error(
"CW::Line3D::closest_point(): this line is null");
1151 Vector3D start_to_point = point - m_point;
1152 double factor = start_to_point.
dot(m_direction);
1153 return m_point + (factor * m_direction);
1159 return Vector3D(point - closest_point).length();
1165 throw std::invalid_argument(
"CW::Line3D::on_line(): given point is null");
1168 throw std::logic_error(
"CW::Line3D::on_line(): this line is null");
1171 enum class OnLineFactorCoords {
1176 OnLineFactorCoords factor_val;
1178 if (this->direction.x > 0.56) {
1180 factor = (test_point.x - this->point.x) / this->direction.x;
1181 factor_val = OnLineFactorCoords::X;
1183 else if (this->direction.y > 0.56) {
1184 factor = (test_point.y - this->point.y) / this->direction.y;
1185 factor_val = OnLineFactorCoords::Y;
1188 factor = (test_point.z - this->point.z) / this->direction.z;
1189 factor_val = OnLineFactorCoords::Z;
1191 if (factor_val != OnLineFactorCoords::X) {
1192 double x_test = this->point.x + (this->direction.x * factor);
1193 if (std::abs(x_test - test_point.x) > EPSILON) {
1197 if (factor_val != OnLineFactorCoords::Y) {
1198 double y_test = this->point.y + (this->direction.y * factor);
1199 if (std::abs(y_test - test_point.y) > EPSILON) {
1203 if (factor_val != OnLineFactorCoords::Z) {
1204 double z_test = this->point.z + (this->direction.z * factor);
1205 if (std::abs(z_test - test_point.z) > EPSILON) {
1214 if (!point_a || !test_point || !point_b) {
1215 throw std::invalid_argument(
"CW::Line3D::on_line_segment(): given point is null");
1217 if (point_a == point_b) {
1218 throw std::invalid_argument(
"CW::Line3D::on_line_segment(): given points are equal");
1221 Vector3D a_to_b = point_b - point_a;
1222 Vector3D a_to_c = test_point - point_a;
1227 if (fabs(crossproduct.length()) > EPSILON) {
1232 double dotproduct = a_to_b.
dot(a_to_c);
1233 if (dotproduct < EPSILON) {
1237 double squaredlengthba = pow(a_to_b.length()+EPSILON, 2);
1238 if (dotproduct > squaredlengthba) {
1250 throw std::invalid_argument(
"CW::Line3D::parallel(): given line is null");
1253 throw std::logic_error(
"CW::Line3D::parallel(): this line is null");
1255 if (line.direction == direction) {
1258 else if (line.direction == (direction * -1)) {
1267 throw std::invalid_argument(
"CW::Line3D::parallel(): given vector is null");
1269 if (vector.length() < EPSILON) {
1270 throw std::invalid_argument(
"CW::Line3D::parallel(): given vector has zero length");
1273 throw std::logic_error(
"CW::Line3D::parallel(): this line is null");
1275 Vector3D difference = this->direction.unit() - vector.unit();
1276 double length = difference.length();
1277 if (length < EPSILON || (length - 2.0) < EPSILON) {
1287 if (lhs.m_point == rhs.m_point) {
1290 Vector3D l_to_r = rhs.m_point - lhs.m_point;
1291 if (l_to_r.
colinear(lhs.m_direction) != Vector3D::Colinearity::NO) {
Line3D intersection(const Plane3D &plane2) const
Radians operator+(const double value) const
double distance(const Point3D &point) const
static Point3D intersection_between_lines(const Point3D &point_a, const Vector3D &vector_a, const Point3D &point_b, const Vector3D &vector_b, bool return_colinear=false)
friend bool operator==(const Vector3D &lhs, const Vector3D &rhs)
static Point3D ray_line_intersection(const Point3D &point_a, const Vector3D &vector_a, const Point3D &point_b, const Vector3D &ray_b, bool return_colinear=false)
double dot(const Vector3D &vector2) const
Vector3D operator+(const Vector3D &vector) const
friend bool operator==(const Plane3D &lhs, const Plane3D &rhs)
Point3D intersection_between(const Point3D &point_a, const Point3D &point_b) const
bool coplanar(const Plane3D &test_plane) const
double difference(const Radians &other) const
Plane3D offset(double offset_by) const
static bool on_line_segment(const Point3D &point_a, const Point3D &point_b, const Point3D &test_point)
bool parallel(const Line3D &line) const
Point3D & operator=(const Point3D &point)
Point3D operator+(const Point3D &point) const
void max_point(const Point3D &point)
bool parallel(const Plane3D &plane2) const
Point3D closest_point(const Point3D &point) const
bool on_line(const Point3D &point) const
Colinearity colinear(const Vector3D &vector_b) const
static Vector3D zero_vector()
friend bool operator==(const Line3D &lhs, const Line3D &rhs)
bool on_plane(const Point3D &point) const
Vector3D cross(const Vector3D &vector2) const
double distance(const Point3D &point) const
Radians & operator=(const Radians &radians)
Vector3D rotate_about(double angle, const Vector3D &axis) const
Vector3D & operator=(const Vector3D &vector)
static Plane3D plane_from_loop(const std::vector< Point3D > &loop_points)
void min_point(const Point3D &point)
std::pair< Point3D, Point3D > closest_points(const Line3D &line) const