diff --git a/primitive_process/interface/internal_primitive_desc.hpp b/primitive_process/interface/internal_primitive_desc.hpp index cdac6a4..4fe69da 100644 --- a/primitive_process/interface/internal_primitive_desc.hpp +++ b/primitive_process/interface/internal_primitive_desc.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -7,8 +8,6 @@ #include #include -#include - #include "primitive_descriptor.h" // ========================================================================================================================= @@ -169,7 +168,7 @@ struct PE_API mesh { }; // CAUTION: in polyline local space, the X/Y axis should be according to local N/B directions -struct polyline { +struct PE_API polyline { std::vector vertices{}; // // for a straight line, it consists of two vertices; for a circle arc, it consists // of three vertices: start point, circle center, and a point on the circle to @@ -179,11 +178,11 @@ struct polyline { void build_as_axis(const polyline_descriptor_t&, const raw_vector3d_t&, - Eigen::Transform&, + Eigen::Transform&, aabb_t<>&); void build_as_axis(polyline_descriptor_t&&, const raw_vector3d_t&, - Eigen::Transform&, + Eigen::Transform&, aabb_t<>&); void build_as_profile(const polyline_descriptor_t&, const Eigen::Ref&, @@ -203,40 +202,39 @@ struct polyline { [[nodiscard]] std::pair calculate_closest_param( const Eigen::Ref&) const; - // return type: true for outside - [[nodiscard]] bool pmc(const Eigen::Ref&, const line_closest_param_t&) const; - [[nodiscard]] bool isEnd(const double t) const; + // return type: true for outside + [[nodiscard]] bool pmc(const Eigen::Ref&, const line_closest_param_t&) const; + [[nodiscard]] bool isEnd(const double t) const; }; struct helixline { double radius{}; double total_theta{}; double height{}; - bool is_clockwise{}; // CAUTION: here returned aabb is in helixline's local space - helixline(const helixline_descriptor_t&, Eigen::Transform&, aabb_t<>&); - helixline(helixline_descriptor_t&&, Eigen::Transform&, aabb_t<>&); + helixline(const helixline_descriptor_t&, Eigen::Transform&, aabb_t<>&); + helixline(helixline_descriptor_t&&, Eigen::Transform&, aabb_t<>&); [[nodiscard]] Eigen::Vector3d calculate_tangent(double) const; [[nodiscard]] Eigen::Vector3d calculate_normal(double) const; // CAUTION: make sure the input point is in the local space of the helixline [[nodiscard]] line_closest_param_t calculate_closest_param(const Eigen::Ref&) const; - [[nodiscard]] bool isEnd(const double t) const; + [[nodiscard]] bool isEnd(const double t) const; }; struct PE_API extrude_polyline { - Eigen::Transform axis_to_world{}; - polyline axis; - polyline profile; + Eigen::Transform axis_to_world{}; + polyline axis; + polyline profile; DEF_PRIMITIVE_COMMON_METHODS(extrude_polyline, extrude_polyline_descriptor_t) }; struct PE_API extrude_helixline { - Eigen::Transform axis_to_world{}; - helixline axis; - polyline profile; + Eigen::Transform axis_to_world{}; + helixline axis; + polyline profile; DEF_PRIMITIVE_COMMON_METHODS(extrude_helixline, extrude_helixline_descriptor_t) }; diff --git a/primitive_process/interface/primitive_descriptor.h b/primitive_process/interface/primitive_descriptor.h index 484d978..6bb0ab7 100644 --- a/primitive_process/interface/primitive_descriptor.h +++ b/primitive_process/interface/primitive_descriptor.h @@ -87,7 +87,7 @@ typedef struct { double radius; // The radius of the helix line double advance_per_round; // he advance per round of the helix line raw_vector3d_t start_direction; // The direction from axisStart to start of the helix line - bool is_clockwise; + bool is_righthanded; // {axis_start -> axis_end} as upward direction } helixline_descriptor_t; // Note : In profiles, The first polyline is outer boundary, and the ohters is internal holes diff --git a/primitive_process/src/helixline.cpp b/primitive_process/src/helixline.cpp index 326f102..06cf8ba 100644 --- a/primitive_process/src/helixline.cpp +++ b/primitive_process/src/helixline.cpp @@ -4,9 +4,9 @@ namespace internal { -helixline::helixline(const helixline_descriptor_t &desc, - Eigen::Transform &axis_to_world, - aabb_t<> &aabb) +helixline::helixline(const helixline_descriptor_t &desc, + Eigen::Transform &axis_to_world, + aabb_t<> &aabb) { Eigen::Vector3d origin, topper; vec3d_conversion(desc.axis_start, origin); @@ -14,14 +14,16 @@ helixline::helixline(const helixline_descriptor_t &desc, this->radius = desc.radius; this->height = (topper - origin).norm(); this->total_theta = this->height / desc.advance_per_round * TWO_PI; - this->is_clockwise = desc.is_clockwise; auto &matrix_handle = axis_to_world.matrix(); vec3d_conversion(desc.start_direction, matrix_handle.col(0)); matrix_handle.col(0).normalize(); matrix_handle.col(2) = (topper - origin) / this->height; matrix_handle.col(3) = origin; - matrix_handle.col(1) = matrix_handle.col(2).cross(matrix_handle.col(0)); + if (desc.is_righthanded) + matrix_handle.col(1) = matrix_handle.col(2).cross(matrix_handle.col(0)); + else + matrix_handle.col(1) = matrix_handle.col(0).cross(matrix_handle.col(2)); aabb = { Eigen::Vector3d{-this->radius, -this->radius, 0 }, @@ -29,9 +31,9 @@ helixline::helixline(const helixline_descriptor_t &desc, }; } -helixline::helixline(helixline_descriptor_t &&desc, - Eigen::Transform &axis_to_world, - aabb_t<> &aabb) +helixline::helixline(helixline_descriptor_t &&desc, + Eigen::Transform &axis_to_world, + aabb_t<> &aabb) { Eigen::Vector3d origin, topper; vec3d_conversion(std::move(desc.axis_start), origin); @@ -39,14 +41,16 @@ helixline::helixline(helixline_descriptor_t &&desc, this->radius = std::move(desc.radius); this->height = (topper - origin).norm(); this->total_theta = this->height / std::move(desc.advance_per_round) * TWO_PI; - this->is_clockwise = std::move(desc.is_clockwise); auto &matrix_handle = axis_to_world.matrix(); vec3d_conversion(std::move(desc.start_direction), matrix_handle.col(0)); matrix_handle.col(0).normalize(); matrix_handle.col(2) = (topper - origin) / this->height; matrix_handle.col(3) = origin; - matrix_handle.col(1) = matrix_handle.col(2).cross(matrix_handle.col(0)); + if (desc.is_righthanded) + matrix_handle.col(1) = matrix_handle.col(2).cross(matrix_handle.col(0)); + else + matrix_handle.col(1) = matrix_handle.col(0).cross(matrix_handle.col(2)); aabb = { Eigen::Vector3d{-this->radius, -this->radius, 0 }, @@ -183,8 +187,5 @@ helixline::helixline(helixline_descriptor_t &&desc, }; } -[[nodiscard]] bool helixline::isEnd(const double t) const -{ - return t >= 1 - EPSILON || t < EPSILON; -} +[[nodiscard]] bool helixline::isEnd(const double t) const { return t >= 1 - EPSILON || t < EPSILON; } } // namespace internal \ No newline at end of file diff --git a/primitive_process/src/polyline.cpp b/primitive_process/src/polyline.cpp index 73f4231..4e88c5c 100644 --- a/primitive_process/src/polyline.cpp +++ b/primitive_process/src/polyline.cpp @@ -1,3 +1,5 @@ +#include + #include #include "internal_primitive_desc.hpp" @@ -7,7 +9,8 @@ static inline auto manually_projection(const raw_vector3d_t & const Eigen::Ref &y_dir, const Eigen::Ref &offset) { - Eigen::Vector3d p; + Eigen::aligned_allocator allocator; + Eigen::Vector3d p; vec3d_conversion(point, p); return Eigen::Vector2d{p.dot(x_dir), p.dot(y_dir)} + offset; } @@ -24,15 +27,15 @@ static inline auto manually_projection(raw_vector3d_t && namespace internal { -void polyline::build_as_axis(const polyline_descriptor_t &desc, - const raw_vector3d_t &profile_ref_normal, - Eigen::Transform &axis_to_world, - aabb_t<> &aabb) +void polyline::build_as_axis(const polyline_descriptor_t &desc, + const raw_vector3d_t &profile_ref_normal, + Eigen::Transform &axis_to_world, + aabb_t<> &aabb) { auto &matrix_handle = axis_to_world.matrix(); - vec3d_conversion(profile_ref_normal, matrix_handle.col(1)); + vec3d_conversion(desc.reference_normal, matrix_handle.col(1)); matrix_handle.col(1).normalize(); - vec3d_conversion(desc.reference_normal, matrix_handle.col(2)); + vec3d_conversion(profile_ref_normal, matrix_handle.col(2)); matrix_handle.col(2).normalize(); matrix_handle.col(0) = matrix_handle.col(1).cross(matrix_handle.col(2)); vec3d_conversion(desc.points[0], matrix_handle.col(3)); @@ -48,15 +51,15 @@ void polyline::build_as_axis(const polyline_descriptor_t & aabb.transform(axis_to_world); } -void polyline::build_as_axis(polyline_descriptor_t &&desc, - const raw_vector3d_t &profile_ref_normal, - Eigen::Transform &axis_to_world, - aabb_t<> &aabb) +void polyline::build_as_axis(polyline_descriptor_t &&desc, + const raw_vector3d_t &profile_ref_normal, + Eigen::Transform &axis_to_world, + aabb_t<> &aabb) { auto &matrix_handle = axis_to_world.matrix(); - vec3d_conversion(profile_ref_normal, matrix_handle.col(1)); + vec3d_conversion(std::move(desc.reference_normal), matrix_handle.col(1)); matrix_handle.col(1).normalize(); - vec3d_conversion(std::move(desc.reference_normal), matrix_handle.col(2)); + vec3d_conversion(profile_ref_normal, matrix_handle.col(2)); matrix_handle.col(2).normalize(); matrix_handle.col(0) = matrix_handle.col(1).cross(matrix_handle.col(2)); vec3d_conversion(desc.points[0], matrix_handle.col(3)); @@ -84,69 +87,54 @@ void polyline::build_as_profile(const polyline_descriptor_t &desc, // collect indices info this->thetas.resize(desc.bulge_number); this->start_indices.resize(desc.bulge_number + 1, uint8_t{0}); - stl_vector line_start_indices(desc.bulge_number + 1), circle_start_indices(desc.bulge_number + 1); - if (this->start_indices.size() > 16) { - std::atomic_size_t line_start_indices_offset{0}; - std::atomic_size_t circle_start_indices_offset{0}; - std::transform_inclusive_scan(std::execution::par_unseq, - counting_iterator{}, - counting_iterator{this->start_indices.size()}, - this->start_indices.begin() + 1, - std::plus{}, - [&](size_t index) { - auto &theta = this->thetas[index]; - theta = std::atan(fabs(desc.bulges[index])) * 4; - if (theta <= EPSILON) { - const auto offset = line_start_indices_offset.fetch_add(1); - line_start_indices[offset] = index; - return uint8_t{1}; - } else { - const auto offset = circle_start_indices_offset.fetch_add(1); - return uint8_t{3}; - } - }); - } else { - size_t line_start_indices_offset{0}; - size_t circle_start_indices_offset{0}; - std::transform_inclusive_scan(counting_iterator{}, - counting_iterator{this->start_indices.size()}, - this->start_indices.begin() + 1, - std::plus{}, - [&](size_t index) { - auto &theta = this->thetas[index]; - theta = std::atan(fabs(desc.bulges[index])) * 4; - if (theta <= EPSILON) { - line_start_indices[line_start_indices_offset++] = index; - return uint8_t{1}; - } else { - circle_start_indices[circle_start_indices_offset++] = index; - return uint8_t{3}; - } - }); - } + Concurrency::concurrent_vector> circle_start_indices{}; + circle_start_indices.reserve(desc.bulge_number + 1); + std::transform_inclusive_scan(std::execution::par_unseq, + counting_iterator{}, + counting_iterator{desc.bulge_number}, + this->start_indices.begin() + 1, + std::plus{}, + [&](size_t index) { + auto &theta = this->thetas[index]; + theta = std::atan(fabs(desc.bulges[index])) * 4; + if (theta <= EPSILON) { + return uint8_t{1}; + } else { + circle_start_indices.push_back(static_cast(index)); + return uint8_t{3}; + } + }); // i.e. iff close curve, deduce loop to simple segements by copying the first vertex to the end - this->vertices.resize(this->start_indices.back() + static_cast(desc.is_close)); + this->vertices.resize(this->start_indices.back() + 1); this->start_indices.pop_back(); + circle_start_indices.shrink_to_fit(); const Eigen::Vector2d offset = {-proj_x_dir.dot(proj_origin), -proj_y_dir.dot(proj_origin)}; - // first loop: process all line segment vertices - algorithm::for_loop(size_t{}, line_start_indices.size(), [&](size_t i) { - const auto &index = line_start_indices[i]; - const auto &start_index = this->start_indices[index]; - auto &v = this->vertices[start_index]; - v = manually_projection(desc.points[index], proj_x_dir, proj_y_dir, offset); + // first loop: process all start vertices + algorithm::for_loop(size_t{}, this->start_indices.size(), [&](size_t i) { + const auto &index = this->start_indices[i]; + auto &v = this->vertices[index]; + v = manually_projection(desc.points[i], proj_x_dir, proj_y_dir, offset); aabb.extend(v); }); - if (desc.is_close) this->vertices.back() = this->vertices.front(); + // ways to handle the last vertex: + // iff close curve, deduce loop to simple segements by copying the first vertex to the end + // if not, then process the last vertex + if (desc.is_close) + this->vertices.back() = this->vertices.front(); + else { + auto &v = this->vertices.back(); + v = manually_projection(desc.points[desc.point_number - 1], proj_x_dir, proj_y_dir, offset); + aabb.extend(v); + } // second loop: process all circle vertices algorithm::for_loop(size_t{}, circle_start_indices.size(), [&](size_t i) { - const auto &index = line_start_indices[i]; - const auto &start_index = this->start_indices[index]; - const auto &bulge = desc.bulges[index]; - const auto &theta = this->thetas[index]; - this->vertices[start_index] = manually_projection(desc.points[index], proj_x_dir, proj_y_dir, offset); + const auto &index = circle_start_indices[i]; + const auto &start_index = this->start_indices[index]; + const auto &bulge = desc.bulges[index]; + const auto &theta = this->thetas[index]; const auto &a = this->vertices[start_index]; const auto &b = this->vertices[start_index + 3]; @@ -168,7 +156,6 @@ void polyline::build_as_profile(const polyline_descriptor_t &desc, else d = c + Eigen::Vector2d{-vec_ca[1], vec_ca[0]}; - aabb.extend(a); aabb.extend(c); aabb.extend(d); }); @@ -186,69 +173,54 @@ void polyline::build_as_profile(polyline_descriptor_t &&desc, // collect indices info this->thetas.resize(desc.bulge_number); this->start_indices.resize(desc.bulge_number + 1, uint8_t{0}); - stl_vector line_start_indices(desc.bulge_number + 1), circle_start_indices(desc.bulge_number + 1); - if (this->start_indices.size() > 16) { - std::atomic_size_t line_start_indices_offset{0}; - std::atomic_size_t circle_start_indices_offset{0}; - std::transform_inclusive_scan(std::execution::par_unseq, - counting_iterator{}, - counting_iterator{this->start_indices.size()}, - this->start_indices.begin() + 1, - std::plus{}, - [&](size_t index) { - auto &theta = this->thetas[index]; - theta = std::atan(fabs(desc.bulges[index])) * 4; - if (theta <= EPSILON) { - const auto offset = line_start_indices_offset.fetch_add(1); - line_start_indices[offset] = index; - return uint8_t{1}; - } else { - const auto offset = circle_start_indices_offset.fetch_add(1); - return uint8_t{3}; - } - }); - } else { - size_t line_start_indices_offset{0}; - size_t circle_start_indices_offset{0}; - std::transform_inclusive_scan(counting_iterator{}, - counting_iterator{this->start_indices.size()}, - this->start_indices.begin() + 1, - std::plus{}, - [&](size_t index) { - auto &theta = this->thetas[index]; - theta = std::atan(fabs(desc.bulges[index])) * 4; - if (theta <= EPSILON) { - line_start_indices[line_start_indices_offset++] = index; - return uint8_t{1}; - } else { - circle_start_indices[circle_start_indices_offset++] = index; - return uint8_t{3}; - } - }); - } + Concurrency::concurrent_vector> circle_start_indices{}; + circle_start_indices.reserve(desc.bulge_number + 1); + std::transform_inclusive_scan(std::execution::par_unseq, + counting_iterator{}, + counting_iterator{desc.bulge_number}, + this->start_indices.begin() + 1, + std::plus{}, + [&](size_t index) { + auto &theta = this->thetas[index]; + theta = std::atan(fabs(desc.bulges[index])) * 4; + if (theta <= EPSILON) { + return uint8_t{1}; + } else { + circle_start_indices.push_back(static_cast(index)); + return uint8_t{3}; + } + }); // i.e. iff close curve, deduce loop to simple segements by copying the first vertex to the end - this->vertices.resize(this->start_indices.back() + static_cast(desc.is_close)); + this->vertices.resize(this->start_indices.back() + 1); this->start_indices.pop_back(); + circle_start_indices.shrink_to_fit(); const Eigen::Vector2d offset = {-proj_x_dir.dot(proj_origin), -proj_y_dir.dot(proj_origin)}; - // first loop: process all line segment vertices - algorithm::for_loop(size_t{}, line_start_indices.size(), [&](size_t i) { - const auto &index = line_start_indices[i]; - const auto &start_index = this->start_indices[index]; - auto &v = this->vertices[start_index]; - v = manually_projection(std::move(desc.points[index]), proj_x_dir, proj_y_dir, offset); + // first loop: process all start vertices + algorithm::for_loop(size_t{}, this->start_indices.size(), [&](size_t i) { + const auto &index = this->start_indices[i]; + auto &v = this->vertices[index]; + v = manually_projection(std::move(desc.points[i]), proj_x_dir, proj_y_dir, offset); aabb.extend(v); }); - if (desc.is_close) this->vertices.back() = this->vertices.front(); + // ways to handle the last vertex: + // iff close curve, deduce loop to simple segements by copying the first vertex to the end + // if not, then process the last vertex + if (desc.is_close) + this->vertices.back() = this->vertices.front(); + else { + auto &v = this->vertices.back(); + v = manually_projection(std::move(desc.points[desc.point_number - 1]), proj_x_dir, proj_y_dir, offset); + aabb.extend(v); + } // second loop: process all circle vertices algorithm::for_loop(size_t{}, circle_start_indices.size(), [&](size_t i) { - const auto &index = line_start_indices[i]; - const auto &start_index = this->start_indices[index]; - const auto &bulge = desc.bulges[index]; - const auto &theta = this->thetas[index]; - this->vertices[start_index] = manually_projection(std::move(desc.points[index]), proj_x_dir, proj_y_dir, offset); + const auto &index = circle_start_indices[i]; + const auto &start_index = this->start_indices[index]; + const auto &bulge = desc.bulges[index]; + const auto &theta = this->thetas[index]; const auto &a = this->vertices[start_index]; const auto &b = this->vertices[start_index + 3]; @@ -270,7 +242,6 @@ void polyline::build_as_profile(polyline_descriptor_t &&desc, else d = c + Eigen::Vector2d{-vec_ca[1], vec_ca[0]}; - aabb.extend(a); aabb.extend(c); aabb.extend(d); }); @@ -405,16 +376,15 @@ void polyline::build_as_profile(polyline_descriptor_t &&desc, } } -[[nodiscard]] bool polyline::pmc(const Eigen::Ref&p, const line_closest_param_t& closest_param) const { - if (closest_param.is_peak_value) { - return (p - closest_param.point.topRows<2>()).dot(this->calculate_normal(closest_param.t)) < 0; - } - // the closest point is the vertex of the line - // todo: may not be robust for rare cases - return (p - closest_param.point.topRows<2>()).dot(this->calculate_normal(closest_param.t)) < 0; +[[nodiscard]] bool polyline::pmc(const Eigen::Ref &p, const line_closest_param_t &closest_param) const +{ + if (closest_param.is_peak_value) { + return (p - closest_param.point.topRows<2>()).dot(this->calculate_normal(closest_param.t)) < 0; + } + // the closest point is the vertex of the line + // todo: may not be robust for rare cases + return (p - closest_param.point.topRows<2>()).dot(this->calculate_normal(closest_param.t)) < 0; } -[[nodiscard]] bool polyline::isEnd(const double t) const { - return t >= thetas.size() - EPSILON || t < EPSILON; -} +[[nodiscard]] bool polyline::isEnd(const double t) const { return t >= thetas.size() - EPSILON || t < EPSILON; } } // namespace internal \ No newline at end of file