14#include <lagrange/internal/constants.h>
15#include <lagrange/legacy/inline.h>
16#include <lagrange/utils/assert.h>
17#include <lagrange/utils/range.h>
18#include <lagrange/utils/safe_cast.h>
20#include <Eigen/Geometry>
68template <
typename _Scalar>
72 using Scalar = _Scalar;
73 using TransformType = Eigen::Transform<Scalar, 3, Eigen::AffineCompact>;
74 using PointType = Eigen::Matrix<Scalar, 1, 3>;
82 virtual std::unique_ptr<SweepPath<Scalar>>
clone()
const = 0;
109 for (
size_t i = 0; i < n; i++) {
110 m_samples[i] = (Scalar)i / (Scalar)(n - 1);
127 la_runtime_assert(samples.size() >= 2,
"At least 2 samples is necessary for sweep path!");
140 std::vector<Scalar> results;
141 results.reserve(
m_samples.size() + samples.size());
147 std::back_inserter(results));
154 const std::vector<TransformType>&
get_transforms()
const {
return m_transforms; }
155 std::vector<TransformType>&
get_transforms() {
return m_transforms; }
163 std::vector<Scalar> offsets;
166 offsets.push_back(m_offset_fn(t));
179 void set_depth_begin(
Scalar depth) { m_depth_begin = depth; }
186 void set_depth_end(
Scalar depth) { m_depth_end = depth; }
193 void set_twist_begin(
Scalar twist) { m_twist_begin = twist; }
200 void set_twist_end(
Scalar twist) { m_twist_end = twist; }
206 void set_taper_begin(
Scalar taper) { m_taper_begin = taper; }
212 void set_taper_end(
Scalar taper) { m_taper_end = taper; }
219 void set_pivot(
const PointType& p) { m_pivot = p; }
225 void set_offset_fn(std::function<Scalar(Scalar)> fn) { m_offset_fn = std::move(fn); }
226 bool has_offsets()
const {
return bool(m_offset_fn); }
238 m_normalization = transform;
240 const TransformType& get_normalization_transform()
const {
return m_normalization; }
247 const size_t N = m_transforms.size();
248 if (N != other.m_transforms.size())
return false;
250 constexpr Scalar TOL = std::numeric_limits<Scalar>::epsilon() * 100;
251 if ((m_pivot - other.m_pivot).norm() > TOL)
return false;
252 if (std::abs(m_depth_begin - other.m_depth_begin) > TOL)
return false;
253 if (std::abs(m_depth_end - other.m_depth_end) > TOL)
return false;
254 if (std::abs(m_twist_begin - other.m_twist_begin) > TOL)
return false;
255 if (std::abs(m_twist_end - other.m_twist_end) > TOL)
return false;
256 if (std::abs(m_taper_begin - other.m_taper_begin) > TOL)
return false;
257 if (std::abs(m_taper_end - other.m_taper_end) > TOL)
return false;
286 std::vector<TransformType> m_transforms;
288 TransformType m_normalization = TransformType::Identity();
289 Scalar m_depth_begin = 0;
290 Scalar m_depth_end = 1;
291 Scalar m_twist_begin = 0;
292 Scalar m_twist_end = 0;
293 Scalar m_taper_begin = 1;
294 Scalar m_taper_end = 1;
295 PointType m_pivot{0, 0, 0};
300template <
typename _Scalar>
305 using Scalar =
typename Parent::Scalar;
315 m_direction = dir.normalized();
317 m_direction.array().isFinite().all(),
318 "Invalid linear extrusion path direction");
328 Parent::m_transforms.clear();
329 Parent::m_transforms.resize(N);
331 for (
size_t i = 0; i < N; i++) {
333 Eigen::AngleAxis<Scalar> R(
334 Parent::m_twist_begin * (1 - t) + Parent::m_twist_end * t,
336 Eigen::Transform<Scalar, 3, Eigen::AffineCompact> S;
338 S(0, 0) = (1 - t) * Parent::m_taper_begin + t * Parent::m_taper_end;
339 S(1, 1) = (1 - t) * Parent::m_taper_begin + t * Parent::m_taper_end;
341 Parent::m_transforms[i].setIdentity();
342 Parent::m_transforms[i].translate(Parent::m_pivot.transpose());
343 Parent::m_transforms[i].translate(
344 m_direction.transpose() *
345 (Parent::m_depth_begin * (1 - t) + Parent::m_depth_end * t));
346 Parent::m_transforms[i].rotate(R);
347 Parent::m_transforms[i] = Parent::m_transforms[i] * S;
348 Parent::m_transforms[i].translate(-Parent::m_pivot.transpose());
351 Parent::m_transforms[i] = Parent::m_normalization.inverse() * Parent::m_transforms[i] *
352 Parent::m_normalization;
356 std::unique_ptr<Parent>
clone()
const override
358 auto r = std::make_unique<LinearSweepPath<Scalar>>(m_direction);
359 Parent::clone_settings(*r);
373 if (Parent::operator==(other)) {
374 constexpr Scalar TOL = std::numeric_limits<Scalar>::epsilon() * 100;
375 return (m_direction - other_linear->m_direction).norm() < TOL;
382 Eigen::Matrix<Scalar, 1, 3> m_direction;
395template <
typename _Scalar>
400 using Scalar =
typename Parent::Scalar;
417 Parent::set_depth_begin(0);
418 Parent::set_depth_end(2 * lagrange::internal::pi * radius);
421 "Negaative radius is not supported in CircularArcSweepPath.");
436 void set_angle_end(Scalar theta) { Parent::set_depth_end(theta * m_radius); }
442 Parent::m_transforms.clear();
443 Parent::m_transforms.resize(N);
445 Eigen::Matrix<Scalar, 1, 3> radial_dir(std::cos(m_theta), std::sin(m_theta), 0);
446 Eigen::Matrix<Scalar, 1, 3> z_axis(0, 0, 1);
447 Eigen::Matrix<Scalar, 1, 3> normal = z_axis.cross(radial_dir).normalized();
449 const Scalar angle_begin = (m_radius == 0) ? 0 : Parent::m_depth_begin / m_radius;
450 const Scalar angle_end = (m_radius == 0) ? 0 : Parent::m_depth_end / m_radius;
452 for (
size_t i = 0; i < N; i++) {
454 Eigen::AngleAxis<Scalar> R(
455 Parent::m_twist_begin * (1 - t) + Parent::m_twist_end * t,
457 Eigen::Transform<Scalar, 3, Eigen::AffineCompact> S;
459 S(0, 0) = (1 - t) * Parent::m_taper_begin + t * Parent::m_taper_end;
460 S(1, 1) = (1 - t) * Parent::m_taper_begin + t * Parent::m_taper_end;
462 Parent::m_transforms[i].setIdentity();
463 Parent::m_transforms[i].translate(Parent::m_pivot.transpose());
465 Parent::m_transforms[i].translate(radial_dir.transpose() * m_radius);
466 Eigen::AngleAxis<Scalar> R2(angle_begin * (1 - t) + angle_end * t, normal);
467 Parent::m_transforms[i].rotate(R2);
468 Parent::m_transforms[i].translate(-radial_dir.transpose() * m_radius);
470 Parent::m_transforms[i].rotate(R);
471 Parent::m_transforms[i] = Parent::m_transforms[i] * S;
472 Parent::m_transforms[i].translate(-Parent::m_pivot.transpose());
475 Parent::m_transforms[i] = Parent::m_normalization.inverse() * Parent::m_transforms[i] *
476 Parent::m_normalization;
480 Parent::m_transforms.back() = Parent::m_transforms.front();
484 std::unique_ptr<Parent>
clone()
const override
486 auto r = std::make_unique<CircularArcSweepPath<Scalar>>(m_radius, m_theta);
487 Parent::clone_settings(*r);
494 constexpr Scalar two_pi =
static_cast<Scalar
>(lagrange::internal::pi * 2);
495 constexpr Scalar point_five_degree =
static_cast<Scalar
>(lagrange::internal::pi / 360);
499 const Scalar angle_diff = angle_end - angle_begin;
500 const int winding =
static_cast<int>(std::round(angle_diff / two_pi));
504 return (winding != 0) && std::abs(angle_diff - winding * two_pi) < point_five_degree;
509 if (
const auto* other_circular =
511 if (Parent::operator==(other)) {
512 constexpr Scalar TOL = std::numeric_limits<Scalar>::epsilon() * 100;
513 return std::abs(m_radius - other_circular->m_radius) < TOL &&
514 std::abs(m_theta - other_circular->m_theta) < TOL;
529template <
typename _VertexArray>
533 using Scalar =
typename _VertexArray::Scalar;
534 using VertexArray = _VertexArray;
536 using Index = Eigen::Index;
556 : m_polyline(polyline)
559 la_runtime_assert(polyline.rows() > 1,
"Sweep path must consist of at least 2 points!");
561 Index num_lines = polyline.rows() - 1;
562 m_lengths.resize(num_lines);
564 m_lengths[i - 1] = (polyline.row(i) - polyline.row(i - 1)).norm();
566 const Scalar total_length = std::accumulate(m_lengths.begin(), m_lengths.end(), Scalar(0));
570 Parent::set_depth_begin(0);
571 Parent::set_depth_end(total_length);
578 using TransformType = Eigen::Transform<Scalar, 3, Eigen::AffineCompact>;
579 using PointType = Eigen::Matrix<Scalar, 1, 3>;
582 const Index M =
static_cast<Index
>(m_polyline.rows());
584 const Index m = path_closed ? (M - 1) : M;
585 const Eigen::Matrix<Scalar, 1, 3> z_axis(0, 0, 1);
591 auto update_transform = [&](TransformType& transform, Index i) {
592 const Scalar t = (Scalar)i / (Scalar)(M - 1);
593 Eigen::AngleAxis<Scalar> R(
594 Parent::m_twist_begin * (1 - t) + Parent::m_twist_end * t,
596 Eigen::Transform<Scalar, 3, Eigen::AffineCompact> S;
598 S(0, 0) = (1 - t) * Parent::m_taper_begin + t * Parent::m_taper_end;
599 S(1, 1) = (1 - t) * Parent::m_taper_begin + t * Parent::m_taper_end;
601 transform.pretranslate(Parent::m_pivot.transpose());
603 transform = transform * S;
604 transform.translate(-Parent::m_pivot.transpose());
605 transform = Parent::m_normalization.inverse() * transform * Parent::m_normalization;
608 auto generate_node_transforms = [&]() {
609 std::vector<TransformType> transforms;
610 transforms.reserve(M);
613 transforms.push_back(TransformType::Identity());
614 update_transform(transforms.front(), 0);
616 Eigen::Quaternion<Scalar> rotation, I;
617 rotation.setIdentity();
619 PointType translation;
620 translation.setZero();
623 PointType v1 = m_polyline.row(0) - m_polyline.row(m - 1);
624 PointType v2 = m_polyline.row(1) - m_polyline.row(0);
625 rotation = Eigen::Quaternion<Scalar>::FromTwoVectors(v1, v2).slerp(0.5f, I);
629 for (Index i = 1; i < m; i++) {
630 PointType v1 = m_polyline.row(i) - m_polyline.row(i - 1);
632 if (path_closed || i < m - 1) {
633 v2 = m_polyline.row((i + 1) % m) - m_polyline.row(i);
635 auto half_r = Eigen::Quaternion<Scalar>::FromTwoVectors(v1, v2).slerp(0.5f, I);
637 rotation = half_r * rotation;
639 TransformType transform;
640 transform.setIdentity();
641 transform.translate(translation.transpose());
642 transform.rotate(rotation);
643 update_transform(transform, i);
644 transforms.push_back(std::move(transform));
646 rotation = half_r * rotation;
650 transforms.push_back(TransformType::Identity());
651 update_transform(transforms.back(), M - 1);
654 using Matrix = Eigen::Matrix<Scalar, 3, 3>;
655 using Vector = Eigen::Matrix<Scalar, 3, 1>;
656 std::vector<std::tuple<Matrix, Matrix, Vector>> decompositions;
657 decompositions.reserve(M);
658 for (
const auto& transform : transforms) {
661 transform.computeRotationScaling(&R, &S);
662 T = transform.translation();
663 decompositions.emplace_back(R, S, T);
666 return decompositions;
669 auto node_transforms = generate_node_transforms();
670 Parent::m_transforms.clear();
671 Parent::m_transforms.reserve(N);
673 Scalar curr_depth = 0;
674 Scalar next_depth = m_lengths[0];
676 Eigen::Matrix<Scalar, 3, 3> R0, R1, R;
677 Eigen::Matrix<Scalar, 3, 3> S0, S1, S;
678 Eigen::Matrix<Scalar, 3, 1> T0, T1, T;
680 for (Index i = 0; i < N; i++) {
684 while (next_depth < d && curr_span < M - 1) {
686 curr_depth = next_depth;
687 next_depth += m_lengths[curr_span];
690 Scalar tt = (d - curr_depth) / (next_depth - curr_depth);
691 tt = std::max<Scalar>(0, std::min<Scalar>(1, tt));
693 std::tie(R0, S0, T0) = node_transforms[curr_span];
694 std::tie(R1, S1, T1) = node_transforms[curr_span + 1];
696 R = Eigen::Quaternion<Scalar>(R0)
697 .slerp(tt, Eigen::Quaternion<Scalar>(R1))
699 S = S0 * (1 - tt) + S1 * tt;
700 T = T0 * (1 - tt) + T1 * tt;
702 TransformType transform;
703 transform.setIdentity();
704 transform.linear() = R * S;
705 transform.translation() = T;
706 Parent::m_transforms.push_back(std::move(transform));
710 Parent::m_transforms.back() = Parent::m_transforms.front();
714 std::unique_ptr<Parent>
clone()
const override
716 auto r = std::make_unique<PolylineSweepPath<VertexArray>>(m_polyline);
717 Parent::clone_settings(*r);
725 constexpr Scalar TOL = std::numeric_limits<Scalar>::epsilon() * 100;
726 const Scalar total_length =
727 std::accumulate(m_lengths.begin(), m_lengths.end(), Scalar(0));
728 return std::abs(Parent::m_depth_begin) < TOL &&
729 std::abs(Parent::m_depth_end - total_length) < TOL;
736 if (
const auto* other_polyline =
738 if (Parent::operator==(other)) {
739 constexpr Scalar TOL = std::numeric_limits<Scalar>::epsilon() * 100;
741 (m_polyline - other_polyline->m_polyline).template lpNorm<Eigen::Infinity>() <
756 constexpr Scalar TOL = std::numeric_limits<Scalar>::epsilon() * 100;
757 const Index M =
static_cast<Index
>(m_polyline.rows());
758 return (m_polyline.row(0) - m_polyline.row(M - 1)).norm() < TOL;
762 VertexArray m_polyline;
763 std::vector<Scalar> m_lengths;
In addition to the member function provided in SweepPath, CircularArcSweepPath also provide the follo...
Definition SweepPath.h:397
bool operator==(const SweepPath< Scalar > &other) const override
Check if two sweep paths are the same.
Definition SweepPath.h:507
void set_angle_begin(Scalar theta)
Set starting sweeping angle.
Definition SweepPath.h:430
void initialize() override
Generate transformation matrices based on the setting provided.
Definition SweepPath.h:438
CircularArcSweepPath(Scalar radius, Scalar theta)
Circular arc path constructor.
Definition SweepPath.h:411
bool is_closed() const override
Whether the sweep path is closed.
Definition SweepPath.h:492
void set_angle_end(Scalar theta)
Set ending sweeping angle.
Definition SweepPath.h:436
std::unique_ptr< Parent > clone() const override
Create a deep copy of itself.
Definition SweepPath.h:484
Definition SweepPath.h:302
bool operator==(const SweepPath< Scalar > &other) const override
Check if two sweep paths are the same.
Definition SweepPath.h:370
void initialize() override
Generate transformation matrices based on the setting provided.
Definition SweepPath.h:324
LinearSweepPath(Eigen::Matrix< Scalar, 1, 3 > dir)
Constructor.
Definition SweepPath.h:313
bool is_closed() const override
Whether the sweep path is closed.
Definition SweepPath.h:364
std::unique_ptr< Parent > clone() const override
Create a deep copy of itself.
Definition SweepPath.h:356
Create sweep path based on a polyline.
Definition SweepPath.h:531
bool operator==(const SweepPath< Scalar > &other) const override
Check if two sweep paths are the same.
Definition SweepPath.h:734
void initialize() override
Generate transformation matrices based on the setting provided.
Definition SweepPath.h:576
bool is_closed() const override
Whether the sweep path is closed.
Definition SweepPath.h:722
PolylineSweepPath(const VertexArray &polyline)
Polyline sweep path constructor.
Definition SweepPath.h:555
std::unique_ptr< Parent > clone() const override
Create a deep copy of itself.
Definition SweepPath.h:714
bool is_polyline_closed() const
Definition SweepPath.h:754
Abstract base class for sweep path.
Definition SweepPath.h:70
Scalar get_taper_end() const
Scaling factor at the beginning of the sweep path.
Definition SweepPath.h:211
void set_num_samples(size_t n)
Set the number of samples for uniform sampling of the sweeping path.
Definition SweepPath.h:105
size_t get_num_samples() const
The number of samples used to sample along the sweeping path.
Definition SweepPath.h:100
void set_samples(std::vector< Scalar > samples)
Set the sample points.
Definition SweepPath.h:125
Scalar get_depth_begin() const
Start sweeping at certain depth along the path.
Definition SweepPath.h:178
Scalar get_twist_begin() const
Twisting angle at the beginning of the sweep path.
Definition SweepPath.h:192
void add_samples(const std::vector< Scalar > &samples)
Add samples to the existing samples.
Definition SweepPath.h:138
const PointType & get_pivot() const
Twisting and tapering are all with respect to a pivot point.
Definition SweepPath.h:218
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:185
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:225
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:154
virtual bool operator==(const SweepPath< Scalar > &other) const
Check if two sweep paths are the same.
Definition SweepPath.h:245
std::vector< Scalar > m_samples
Definition SweepPath.h:287
Scalar get_taper_begin() const
Scaling factor at the beginning of the sweep path.
Definition SweepPath.h:205
std::vector< Scalar > get_offsets() const
Retrieve the sampled normal offsets.
Definition SweepPath.h:160
Scalar get_twist_end() const
Twisting angle at the end of the sweep path.
Definition SweepPath.h:199
const std::vector< Scalar > & get_samples() const
Samples are always in ascending order going from 0 to 1.
Definition SweepPath.h:117
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:236
@ 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.