14#include <lagrange/internal/constants.h>
15#include <lagrange/legacy/inline.h>
16#include <lagrange/utils/assert.h>
17#include <lagrange/utils/build.h>
18#include <lagrange/utils/range.h>
19#include <lagrange/utils/safe_cast.h>
21#include <Eigen/Geometry>
69template <
typename _Scalar>
73 using Scalar = _Scalar;
74 using TransformType = Eigen::Transform<Scalar, 3, Eigen::AffineCompact>;
75 using PointType = Eigen::Matrix<Scalar, 1, 3>;
83 virtual std::unique_ptr<SweepPath<Scalar>>
clone()
const = 0;
110 for (
size_t i = 0; i < n; i++) {
111 m_samples[i] = (Scalar)i / (Scalar)(n - 1);
128 la_runtime_assert(samples.size() >= 2,
"At least 2 samples is necessary for sweep path!");
141 std::vector<Scalar> results;
142 results.reserve(
m_samples.size() + samples.size());
148 std::back_inserter(results));
155 const std::vector<TransformType>&
get_transforms()
const {
return m_transforms; }
156 std::vector<TransformType>&
get_transforms() {
return m_transforms; }
164 std::vector<Scalar> offsets;
167 offsets.push_back(m_offset_fn(t));
180 void set_depth_begin(
Scalar depth) { m_depth_begin = depth; }
187 void set_depth_end(
Scalar depth) { m_depth_end = depth; }
194 void set_twist_begin(
Scalar twist) { m_twist_begin = twist; }
201 void set_twist_end(
Scalar twist) { m_twist_end = twist; }
207 void set_taper_begin(
Scalar taper) { m_taper_begin = taper; }
213 void set_taper_end(
Scalar taper) { m_taper_end = taper; }
220 void set_pivot(
const PointType& p) { m_pivot = p; }
226 void set_offset_fn(std::function<Scalar(Scalar)> fn) { m_offset_fn = std::move(fn); }
227 bool has_offsets()
const {
return bool(m_offset_fn); }
239 m_normalization = transform;
241 const TransformType& get_normalization_transform()
const {
return m_normalization; }
248 const size_t N = m_transforms.size();
249 if (N != other.m_transforms.size())
return false;
251 constexpr Scalar TOL = std::numeric_limits<Scalar>::epsilon() * 100;
252 if ((m_pivot - other.m_pivot).norm() > TOL)
return false;
253 if (std::abs(m_depth_begin - other.m_depth_begin) > TOL)
return false;
254 if (std::abs(m_depth_end - other.m_depth_end) > TOL)
return false;
255 if (std::abs(m_twist_begin - other.m_twist_begin) > TOL)
return false;
256 if (std::abs(m_twist_end - other.m_twist_end) > TOL)
return false;
257 if (std::abs(m_taper_begin - other.m_taper_begin) > TOL)
return false;
258 if (std::abs(m_taper_end - other.m_taper_end) > TOL)
return false;
287 std::vector<TransformType> m_transforms;
289 TransformType m_normalization = TransformType::Identity();
290 Scalar m_depth_begin = 0;
291 Scalar m_depth_end = 1;
292 Scalar m_twist_begin = 0;
293 Scalar m_twist_end = 0;
294 Scalar m_taper_begin = 1;
295 Scalar m_taper_end = 1;
296 PointType m_pivot{0, 0, 0};
301template <
typename _Scalar>
306 using Scalar =
typename Parent::Scalar;
316 m_direction = dir.normalized();
318 m_direction.array().isFinite().all(),
319 "Invalid linear extrusion path direction");
329 Parent::m_transforms.clear();
330 Parent::m_transforms.resize(N);
332 for (
size_t i = 0; i < N; i++) {
334 Eigen::AngleAxis<Scalar> R(
335 Parent::m_twist_begin * (1 - t) + Parent::m_twist_end * t,
337 Eigen::Transform<Scalar, 3, Eigen::AffineCompact> S;
339 S(0, 0) = (1 - t) * Parent::m_taper_begin + t * Parent::m_taper_end;
340 S(1, 1) = (1 - t) * Parent::m_taper_begin + t * Parent::m_taper_end;
342 Parent::m_transforms[i].setIdentity();
343 Parent::m_transforms[i].translate(Parent::m_pivot.transpose());
344 Parent::m_transforms[i].translate(
345 m_direction.transpose() *
346 (Parent::m_depth_begin * (1 - t) + Parent::m_depth_end * t));
347 Parent::m_transforms[i].rotate(R);
348 Parent::m_transforms[i] = Parent::m_transforms[i] * S;
349 Parent::m_transforms[i].translate(-Parent::m_pivot.transpose());
352 Parent::m_transforms[i] = Parent::m_normalization.inverse() * Parent::m_transforms[i] *
353 Parent::m_normalization;
357 std::unique_ptr<Parent>
clone()
const override
359 auto r = std::make_unique<LinearSweepPath<Scalar>>(m_direction);
360 Parent::clone_settings(*r);
373#if LAGRANGE_TARGET_FEATURE(RTTI)
375 if (Parent::operator==(other)) {
376 constexpr Scalar TOL = std::numeric_limits<Scalar>::epsilon() * 100;
377 return (m_direction - other_linear->m_direction).norm() < TOL;
387 Eigen::Matrix<Scalar, 1, 3> m_direction;
400template <
typename _Scalar>
405 using Scalar =
typename Parent::Scalar;
422 Parent::set_depth_begin(0);
423 Parent::set_depth_end(2 * lagrange::internal::pi * radius);
426 "Negaative radius is not supported in CircularArcSweepPath.");
441 void set_angle_end(Scalar theta) { Parent::set_depth_end(theta * m_radius); }
447 Parent::m_transforms.clear();
448 Parent::m_transforms.resize(N);
450 Eigen::Matrix<Scalar, 1, 3> radial_dir(std::cos(m_theta), std::sin(m_theta), 0);
451 Eigen::Matrix<Scalar, 1, 3> z_axis(0, 0, 1);
452 Eigen::Matrix<Scalar, 1, 3> normal = z_axis.cross(radial_dir).normalized();
454 const Scalar angle_begin = (m_radius == 0) ? 0 : Parent::m_depth_begin / m_radius;
455 const Scalar angle_end = (m_radius == 0) ? 0 : Parent::m_depth_end / m_radius;
457 for (
size_t i = 0; i < N; i++) {
459 Eigen::AngleAxis<Scalar> R(
460 Parent::m_twist_begin * (1 - t) + Parent::m_twist_end * t,
462 Eigen::Transform<Scalar, 3, Eigen::AffineCompact> S;
464 S(0, 0) = (1 - t) * Parent::m_taper_begin + t * Parent::m_taper_end;
465 S(1, 1) = (1 - t) * Parent::m_taper_begin + t * Parent::m_taper_end;
467 Parent::m_transforms[i].setIdentity();
468 Parent::m_transforms[i].translate(Parent::m_pivot.transpose());
470 Parent::m_transforms[i].translate(radial_dir.transpose() * m_radius);
471 Eigen::AngleAxis<Scalar> R2(angle_begin * (1 - t) + angle_end * t, normal);
472 Parent::m_transforms[i].rotate(R2);
473 Parent::m_transforms[i].translate(-radial_dir.transpose() * m_radius);
475 Parent::m_transforms[i].rotate(R);
476 Parent::m_transforms[i] = Parent::m_transforms[i] * S;
477 Parent::m_transforms[i].translate(-Parent::m_pivot.transpose());
480 Parent::m_transforms[i] = Parent::m_normalization.inverse() * Parent::m_transforms[i] *
481 Parent::m_normalization;
485 Parent::m_transforms.back() = Parent::m_transforms.front();
489 std::unique_ptr<Parent>
clone()
const override
491 auto r = std::make_unique<CircularArcSweepPath<Scalar>>(m_radius, m_theta);
492 Parent::clone_settings(*r);
499 constexpr Scalar two_pi =
static_cast<Scalar
>(lagrange::internal::pi * 2);
500 constexpr Scalar point_five_degree =
static_cast<Scalar
>(lagrange::internal::pi / 360);
504 const Scalar angle_diff = angle_end - angle_begin;
505 const int winding =
static_cast<int>(std::round(angle_diff / two_pi));
509 return (winding != 0) && std::abs(angle_diff - winding * two_pi) < point_five_degree;
514#if LAGRANGE_TARGET_FEATURE(RTTI)
515 if (
const auto* other_circular =
517 if (Parent::operator==(other)) {
518 constexpr Scalar TOL = std::numeric_limits<Scalar>::epsilon() * 100;
519 return std::abs(m_radius - other_circular->m_radius) < TOL &&
520 std::abs(m_theta - other_circular->m_theta) < TOL;
538template <
typename _VertexArray>
542 using Scalar =
typename _VertexArray::Scalar;
543 using VertexArray = _VertexArray;
545 using Index = Eigen::Index;
565 : m_polyline(polyline)
568 la_runtime_assert(polyline.rows() > 1,
"Sweep path must consist of at least 2 points!");
570 Index num_lines = polyline.rows() - 1;
571 m_lengths.resize(num_lines);
573 m_lengths[i - 1] = (polyline.row(i) - polyline.row(i - 1)).norm();
575 const Scalar total_length = std::accumulate(m_lengths.begin(), m_lengths.end(), Scalar(0));
579 Parent::set_depth_begin(0);
580 Parent::set_depth_end(total_length);
587 using TransformType = Eigen::Transform<Scalar, 3, Eigen::AffineCompact>;
588 using PointType = Eigen::Matrix<Scalar, 1, 3>;
591 const Index M =
static_cast<Index
>(m_polyline.rows());
593 const Index m = path_closed ? (M - 1) : M;
594 const Eigen::Matrix<Scalar, 1, 3> z_axis(0, 0, 1);
600 auto update_transform = [&](TransformType& transform, Index i) {
601 const Scalar t = (Scalar)i / (Scalar)(M - 1);
602 Eigen::AngleAxis<Scalar> R(
603 Parent::m_twist_begin * (1 - t) + Parent::m_twist_end * t,
605 Eigen::Transform<Scalar, 3, Eigen::AffineCompact> S;
607 S(0, 0) = (1 - t) * Parent::m_taper_begin + t * Parent::m_taper_end;
608 S(1, 1) = (1 - t) * Parent::m_taper_begin + t * Parent::m_taper_end;
610 transform.pretranslate(Parent::m_pivot.transpose());
612 transform = transform * S;
613 transform.translate(-Parent::m_pivot.transpose());
614 transform = Parent::m_normalization.inverse() * transform * Parent::m_normalization;
617 auto generate_node_transforms = [&]() {
618 std::vector<TransformType> transforms;
619 transforms.reserve(M);
622 transforms.push_back(TransformType::Identity());
623 update_transform(transforms.front(), 0);
625 Eigen::Quaternion<Scalar> rotation, I;
626 rotation.setIdentity();
628 PointType translation;
629 translation.setZero();
632 PointType v1 = m_polyline.row(0) - m_polyline.row(m - 1);
633 PointType v2 = m_polyline.row(1) - m_polyline.row(0);
634 rotation = Eigen::Quaternion<Scalar>::FromTwoVectors(v1, v2).slerp(0.5f, I);
638 for (Index i = 1; i < m; i++) {
639 PointType v1 = m_polyline.row(i) - m_polyline.row(i - 1);
641 if (path_closed || i < m - 1) {
642 v2 = m_polyline.row((i + 1) % m) - m_polyline.row(i);
644 auto half_r = Eigen::Quaternion<Scalar>::FromTwoVectors(v1, v2).slerp(0.5f, I);
646 rotation = half_r * rotation;
648 TransformType transform;
649 transform.setIdentity();
650 transform.translate(translation.transpose());
651 transform.rotate(rotation);
652 update_transform(transform, i);
653 transforms.push_back(std::move(transform));
655 rotation = half_r * rotation;
659 transforms.push_back(TransformType::Identity());
660 update_transform(transforms.back(), M - 1);
663 using Matrix = Eigen::Matrix<Scalar, 3, 3>;
664 using Vector = Eigen::Matrix<Scalar, 3, 1>;
665 std::vector<std::tuple<Matrix, Matrix, Vector>> decompositions;
666 decompositions.reserve(M);
667 for (
const auto& transform : transforms) {
670 transform.computeRotationScaling(&R, &S);
671 T = transform.translation();
672 decompositions.emplace_back(R, S, T);
675 return decompositions;
678 auto node_transforms = generate_node_transforms();
679 Parent::m_transforms.clear();
680 Parent::m_transforms.reserve(N);
682 Scalar curr_depth = 0;
683 Scalar next_depth = m_lengths[0];
685 Eigen::Matrix<Scalar, 3, 3> R0, R1, R;
686 Eigen::Matrix<Scalar, 3, 3> S0, S1, S;
687 Eigen::Matrix<Scalar, 3, 1> T0, T1, T;
689 for (Index i = 0; i < N; i++) {
693 while (next_depth < d && curr_span < M - 1) {
695 curr_depth = next_depth;
696 next_depth += m_lengths[curr_span];
699 Scalar tt = (d - curr_depth) / (next_depth - curr_depth);
700 tt = std::max<Scalar>(0, std::min<Scalar>(1, tt));
702 std::tie(R0, S0, T0) = node_transforms[curr_span];
703 std::tie(R1, S1, T1) = node_transforms[curr_span + 1];
705 R = Eigen::Quaternion<Scalar>(R0)
706 .slerp(tt, Eigen::Quaternion<Scalar>(R1))
708 S = S0 * (1 - tt) + S1 * tt;
709 T = T0 * (1 - tt) + T1 * tt;
711 TransformType transform;
712 transform.setIdentity();
713 transform.linear() = R * S;
714 transform.translation() = T;
715 Parent::m_transforms.push_back(std::move(transform));
719 Parent::m_transforms.back() = Parent::m_transforms.front();
723 std::unique_ptr<Parent>
clone()
const override
725 auto r = std::make_unique<PolylineSweepPath<VertexArray>>(m_polyline);
726 Parent::clone_settings(*r);
734 constexpr Scalar TOL = std::numeric_limits<Scalar>::epsilon() * 100;
735 const Scalar total_length =
736 std::accumulate(m_lengths.begin(), m_lengths.end(), Scalar(0));
737 return std::abs(Parent::m_depth_begin) < TOL &&
738 std::abs(Parent::m_depth_end - total_length) < TOL;
745#if LAGRANGE_TARGET_FEATURE(RTTI)
746 if (
const auto* other_polyline =
748 if (Parent::operator==(other)) {
749 constexpr Scalar TOL = std::numeric_limits<Scalar>::epsilon() * 100;
751 (m_polyline - other_polyline->m_polyline).template lpNorm<Eigen::Infinity>() <
769 constexpr Scalar TOL = std::numeric_limits<Scalar>::epsilon() * 100;
770 const Index M =
static_cast<Index
>(m_polyline.rows());
771 return (m_polyline.row(0) - m_polyline.row(M - 1)).norm() < TOL;
775 VertexArray m_polyline;
776 std::vector<Scalar> m_lengths;
In addition to the member function provided in SweepPath, CircularArcSweepPath also provide the follo...
Definition SweepPath.h:402
bool operator==(const SweepPath< Scalar > &other) const override
Check if two sweep paths are the same.
Definition SweepPath.h:512
void set_angle_begin(Scalar theta)
Set starting sweeping angle.
Definition SweepPath.h:435
void initialize() override
Generate transformation matrices based on the setting provided.
Definition SweepPath.h:443
CircularArcSweepPath(Scalar radius, Scalar theta)
Circular arc path constructor.
Definition SweepPath.h:416
bool is_closed() const override
Whether the sweep path is closed.
Definition SweepPath.h:497
void set_angle_end(Scalar theta)
Set ending sweeping angle.
Definition SweepPath.h:441
std::unique_ptr< Parent > clone() const override
Create a deep copy of itself.
Definition SweepPath.h:489
Definition SweepPath.h:303
bool operator==(const SweepPath< Scalar > &other) const override
Check if two sweep paths are the same.
Definition SweepPath.h:371
void initialize() override
Generate transformation matrices based on the setting provided.
Definition SweepPath.h:325
LinearSweepPath(Eigen::Matrix< Scalar, 1, 3 > dir)
Constructor.
Definition SweepPath.h:314
bool is_closed() const override
Whether the sweep path is closed.
Definition SweepPath.h:365
std::unique_ptr< Parent > clone() const override
Create a deep copy of itself.
Definition SweepPath.h:357
Create sweep path based on a polyline.
Definition SweepPath.h:540
bool operator==(const SweepPath< Scalar > &other) const override
Check if two sweep paths are the same.
Definition SweepPath.h:743
void initialize() override
Generate transformation matrices based on the setting provided.
Definition SweepPath.h:585
bool is_closed() const override
Whether the sweep path is closed.
Definition SweepPath.h:731
PolylineSweepPath(const VertexArray &polyline)
Polyline sweep path constructor.
Definition SweepPath.h:564
std::unique_ptr< Parent > clone() const override
Create a deep copy of itself.
Definition SweepPath.h:723
bool is_polyline_closed() const
Definition SweepPath.h:767
Abstract base class for sweep path.
Definition SweepPath.h:71
Scalar get_taper_end() const
Scaling factor at the beginning of the sweep path.
Definition SweepPath.h:212
void set_num_samples(size_t n)
Set the number of samples for uniform sampling of the sweeping path.
Definition SweepPath.h:106
size_t get_num_samples() const
The number of samples used to sample along the sweeping path.
Definition SweepPath.h:101
void set_samples(std::vector< Scalar > samples)
Set the sample points.
Definition SweepPath.h:126
Scalar get_depth_begin() const
Start sweeping at certain depth along the path.
Definition SweepPath.h:179
Scalar get_twist_begin() const
Twisting angle at the beginning of the sweep path.
Definition SweepPath.h:193
void add_samples(const std::vector< Scalar > &samples)
Add samples to the existing samples.
Definition SweepPath.h:139
const PointType & get_pivot() const
Twisting and tapering are all with respect to a pivot point.
Definition SweepPath.h:219
virtual std::unique_ptr< SweepPath< Scalar > > clone() const =0
Create a deep copy of itself.
Scalar get_depth_end() const
Stop sweeping at certain depth along the path.
Definition SweepPath.h:186
void set_offset_fn(std::function< Scalar(Scalar)> fn)
Offset function provides a mapping from the relative depth (from 0 to 1) to a normal offset amount (m...
Definition SweepPath.h:226
virtual void initialize()=0
Generate transformation matrices based on the setting provided.
const std::vector< TransformType > & get_transforms() const
Retrieve the transforms generated using initialize().
Definition SweepPath.h:155
virtual bool operator==(const SweepPath< Scalar > &other) const
Check if two sweep paths are the same.
Definition SweepPath.h:246
std::vector< Scalar > m_samples
Definition SweepPath.h:288
Scalar get_taper_begin() const
Scaling factor at the beginning of the sweep path.
Definition SweepPath.h:206
std::vector< Scalar > get_offsets() const
Retrieve the sampled normal offsets.
Definition SweepPath.h:161
Scalar get_twist_end() const
Twisting angle at the end of the sweep path.
Definition SweepPath.h:200
const std::vector< Scalar > & get_samples() const
Samples are always in ascending order going from 0 to 1.
Definition SweepPath.h:118
virtual bool is_closed() const =0
Whether the sweep path is closed.
void set_normalization_transform(const TransformType &transform)
Sometimes one may want to sweep a normalized profile curve, and update normalization from time to tim...
Definition SweepPath.h:237
@ Scalar
Mesh attribute must have exactly 1 channel.
Definition AttributeFwd.h:56
Eigen::Matrix< Scalar, Eigen::Dynamic, 1 > Vector
Type alias for one-dimensional column Eigen vectors.
Definition views.h:79
#define la_runtime_assert(...)
Runtime assertion check.
Definition assert.h:174
internal::Range< Index > range(Index end)
Returns an iterable object representing the range [0, end).
Definition range.h:176
Main namespace for Lagrange.