Lagrange
Loading...
Searching...
No Matches
SweepPath.h
1/*
2 * Copyright 2021 Adobe. All rights reserved.
3 * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License. You may obtain a copy
5 * of the License at http://www.apache.org/licenses/LICENSE-2.0
6 *
7 * Unless required by applicable law or agreed to in writing, software distributed under
8 * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9 * OF ANY KIND, either express or implied. See the License for the specific language
10 * governing permissions and limitations under the License.
11 */
12#pragma once
13
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>
20
21#include <Eigen/Geometry>
22#include <Eigen/SVD>
23
24#include <algorithm>
25#include <numeric>
26#include <vector>
27
28namespace lagrange {
29namespace primitive {
30LAGRANGE_LEGACY_INLINE
31namespace legacy {
32
69template <typename _Scalar>
71{
72public:
73 using Scalar = _Scalar;
74 using TransformType = Eigen::Transform<Scalar, 3, Eigen::AffineCompact>;
75 using PointType = Eigen::Matrix<Scalar, 1, 3>;
76
77 virtual ~SweepPath() = default;
78
79public:
83 virtual std::unique_ptr<SweepPath<Scalar>> clone() const = 0;
84
89 virtual void initialize() = 0;
90
94 virtual bool is_closed() const = 0;
95
101 size_t get_num_samples() const { return m_samples.size(); }
102
106 void set_num_samples(size_t n)
107 {
108 la_runtime_assert(n >= 2, "At least 2 samples is necessary for sweep path!");
109 m_samples.resize(n);
110 for (size_t i = 0; i < n; i++) {
111 m_samples[i] = (Scalar)i / (Scalar)(n - 1);
112 }
113 }
114
118 const std::vector<Scalar>& get_samples() const { return m_samples; }
119
126 void set_samples(std::vector<Scalar> samples)
127 {
128 la_runtime_assert(samples.size() >= 2, "At least 2 samples is necessary for sweep path!");
129 m_samples = std::move(samples);
130 }
131
139 void add_samples(const std::vector<Scalar>& samples)
140 {
141 std::vector<Scalar> results;
142 results.reserve(m_samples.size() + samples.size());
143 std::set_union(
144 m_samples.begin(),
145 m_samples.end(),
146 samples.begin(),
147 samples.end(),
148 std::back_inserter(results));
149 m_samples = std::move(results);
150 }
151
155 const std::vector<TransformType>& get_transforms() const { return m_transforms; }
156 std::vector<TransformType>& get_transforms() { return m_transforms; }
157
161 std::vector<Scalar> get_offsets() const
162 {
163 if (has_offsets()) {
164 std::vector<Scalar> offsets;
165 offsets.reserve(get_num_samples());
166 for (auto t : m_samples) {
167 offsets.push_back(m_offset_fn(t));
168 }
169 return offsets;
170 } else {
171 return {};
172 }
173 }
174
179 Scalar get_depth_begin() const { return m_depth_begin; }
180 void set_depth_begin(Scalar depth) { m_depth_begin = depth; }
181
186 Scalar get_depth_end() const { return m_depth_end; }
187 void set_depth_end(Scalar depth) { m_depth_end = depth; }
188
193 Scalar get_twist_begin() const { return m_twist_begin; }
194 void set_twist_begin(Scalar twist) { m_twist_begin = twist; }
195
200 Scalar get_twist_end() const { return m_twist_end; }
201 void set_twist_end(Scalar twist) { m_twist_end = twist; }
202
206 Scalar get_taper_begin() const { return m_taper_begin; }
207 void set_taper_begin(Scalar taper) { m_taper_begin = taper; }
208
212 Scalar get_taper_end() const { return m_taper_end; }
213 void set_taper_end(Scalar taper) { m_taper_end = taper; }
214
219 const PointType& get_pivot() const { return m_pivot; }
220 void set_pivot(const PointType& p) { m_pivot = p; }
221
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); }
228
237 void set_normalization_transform(const TransformType& transform)
238 {
239 m_normalization = transform;
240 }
241 const TransformType& get_normalization_transform() const { return m_normalization; }
242
246 virtual bool operator==(const SweepPath<Scalar>& other) const
247 {
248 const size_t N = m_transforms.size();
249 if (N != other.m_transforms.size()) return false;
250
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;
259
260 if (m_samples != other.m_samples) {
261 return false;
262 }
263
264 if (get_offsets() != other.get_offsets()) {
265 return false;
266 }
267
268 // Note: change in normalization transform is ok.
269 return true;
270 }
271
272protected:
273 void clone_settings(SweepPath<Scalar>& other) const
274 {
275 other.set_depth_begin(get_depth_begin());
276 other.set_depth_end(get_depth_end());
277 other.set_twist_begin(get_twist_begin());
278 other.set_twist_end(get_twist_end());
279 other.set_taper_begin(get_taper_begin());
280 other.set_taper_end(get_taper_end());
281 other.set_pivot(get_pivot());
282 other.set_samples(get_samples());
283 other.set_offset_fn(m_offset_fn);
284 }
285
286protected:
287 std::vector<TransformType> m_transforms;
288 std::vector<Scalar> m_samples;
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};
297 std::function<Scalar(Scalar)> m_offset_fn;
298};
299
300
301template <typename _Scalar>
302class LinearSweepPath final : public SweepPath<_Scalar>
303{
304public:
305 using Parent = SweepPath<_Scalar>;
306 using Scalar = typename Parent::Scalar;
307
308public:
314 LinearSweepPath(Eigen::Matrix<Scalar, 1, 3> dir)
315 {
316 m_direction = dir.normalized();
318 m_direction.array().isFinite().all(),
319 "Invalid linear extrusion path direction");
321 }
322 ~LinearSweepPath() = default;
323
324public:
325 void initialize() override
326 {
327 const size_t N = Parent::get_num_samples();
328 la_runtime_assert(N >= 2, "Extrusion path must consist of at least 2 samples");
329 Parent::m_transforms.clear();
330 Parent::m_transforms.resize(N);
331
332 for (size_t i = 0; i < N; i++) {
333 const Scalar t = Parent::m_samples[i];
334 Eigen::AngleAxis<Scalar> R(
335 Parent::m_twist_begin * (1 - t) + Parent::m_twist_end * t,
336 m_direction);
337 Eigen::Transform<Scalar, 3, Eigen::AffineCompact> S;
338 S.setIdentity();
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;
341
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());
350
351 // Apply normalization transform.
352 Parent::m_transforms[i] = Parent::m_normalization.inverse() * Parent::m_transforms[i] *
353 Parent::m_normalization;
354 }
355 }
356
357 std::unique_ptr<Parent> clone() const override
358 {
359 auto r = std::make_unique<LinearSweepPath<Scalar>>(m_direction);
360 Parent::clone_settings(*r);
361 r->initialize();
362 return r;
363 }
364
365 bool is_closed() const override
366 {
367 // Linear path will never be closed.
368 return false;
369 }
370
371 bool operator==(const SweepPath<Scalar>& other) const override
372 {
373#if LAGRANGE_TARGET_FEATURE(RTTI)
374 if (const auto* other_linear = dynamic_cast<const LinearSweepPath<Scalar>*>(&other)) {
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;
378 }
379 }
380#else
381 la_runtime_assert(false, "RTTI is required for comparing LinearSweepPath");
382#endif
383 return false;
384 }
385
386private:
387 Eigen::Matrix<Scalar, 1, 3> m_direction;
388};
389
390
400template <typename _Scalar>
401class CircularArcSweepPath final : public SweepPath<_Scalar>
402{
403public:
404 using Parent = SweepPath<_Scalar>;
405 using Scalar = typename Parent::Scalar;
406
407public:
416 CircularArcSweepPath(Scalar radius, Scalar theta)
417 : m_radius(radius)
418 , m_theta(theta)
419 {
420 // Initialize setting based on common use case.
422 Parent::set_depth_begin(0);
423 Parent::set_depth_end(2 * lagrange::internal::pi * radius);
425 m_radius >= 0,
426 "Negaative radius is not supported in CircularArcSweepPath.");
427 }
428 ~CircularArcSweepPath() = default;
429
430public:
435 void set_angle_begin(Scalar theta) { Parent::set_depth_begin(theta * m_radius); }
436
441 void set_angle_end(Scalar theta) { Parent::set_depth_end(theta * m_radius); }
442
443 void initialize() override
444 {
445 const size_t N = Parent::get_num_samples();
446 la_runtime_assert(N >= 2, "Extrusion path must consist of at least 2 samples");
447 Parent::m_transforms.clear();
448 Parent::m_transforms.resize(N);
449
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();
453
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;
456
457 for (size_t i = 0; i < N; i++) {
458 const Scalar t = Parent::m_samples[i];
459 Eigen::AngleAxis<Scalar> R(
460 Parent::m_twist_begin * (1 - t) + Parent::m_twist_end * t,
461 z_axis);
462 Eigen::Transform<Scalar, 3, Eigen::AffineCompact> S;
463 S.setIdentity();
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;
466
467 Parent::m_transforms[i].setIdentity();
468 Parent::m_transforms[i].translate(Parent::m_pivot.transpose());
469
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);
474
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());
478
479 // Apply normalization transform.
480 Parent::m_transforms[i] = Parent::m_normalization.inverse() * Parent::m_transforms[i] *
481 Parent::m_normalization;
482 }
483
484 if (is_closed()) {
485 Parent::m_transforms.back() = Parent::m_transforms.front();
486 }
487 }
488
489 std::unique_ptr<Parent> clone() const override
490 {
491 auto r = std::make_unique<CircularArcSweepPath<Scalar>>(m_radius, m_theta);
492 Parent::clone_settings(*r);
493 r->initialize();
494 return r;
495 }
496
497 bool is_closed() const override
498 {
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);
501
502 const auto angle_begin = Parent::get_depth_begin() / m_radius;
503 const auto angle_end = Parent::get_depth_end() / m_radius;
504 const Scalar angle_diff = angle_end - angle_begin;
505 const int winding = static_cast<int>(std::round(angle_diff / two_pi));
506
507 // A circular path is considered closed if the sweeping angle is
508 // a multiple of 2 * lagrange::internal::pi with error tolerance ±0.1 degrees.
509 return (winding != 0) && std::abs(angle_diff - winding * two_pi) < point_five_degree;
510 }
511
512 bool operator==(const SweepPath<Scalar>& other) const override
513 {
514#if LAGRANGE_TARGET_FEATURE(RTTI)
515 if (const auto* other_circular =
516 dynamic_cast<const CircularArcSweepPath<Scalar>*>(&other)) {
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;
521 }
522 }
523#else
524 la_runtime_assert(false, "RTTI is required for comparing LinearSweepPath");
525#endif
526 return false;
527 }
528
529private:
530 Scalar m_radius;
531 Scalar m_theta;
532};
533
534
538template <typename _VertexArray>
539class PolylineSweepPath final : public SweepPath<typename _VertexArray::Scalar>
540{
541public:
542 using Scalar = typename _VertexArray::Scalar;
543 using VertexArray = _VertexArray;
544 using Parent = SweepPath<Scalar>;
545 using Index = Eigen::Index;
546
547public:
564 PolylineSweepPath(const VertexArray& polyline)
565 : m_polyline(polyline)
566 {
567 la_runtime_assert(polyline.cols() == 3, "Sweep path must be 3D path.");
568 la_runtime_assert(polyline.rows() > 1, "Sweep path must consist of at least 2 points!");
569
570 Index num_lines = polyline.rows() - 1;
571 m_lengths.resize(num_lines);
572 for (auto i : range<Index>(1, num_lines + 1)) {
573 m_lengths[i - 1] = (polyline.row(i) - polyline.row(i - 1)).norm();
574 }
575 const Scalar total_length = std::accumulate(m_lengths.begin(), m_lengths.end(), Scalar(0));
576
577 // Initialize some settings based on common use case.
578 Parent::set_num_samples(num_lines + 1);
579 Parent::set_depth_begin(0);
580 Parent::set_depth_end(total_length);
581 }
582 ~PolylineSweepPath() = default;
583
584public:
585 void initialize() override
586 {
587 using TransformType = Eigen::Transform<Scalar, 3, Eigen::AffineCompact>;
588 using PointType = Eigen::Matrix<Scalar, 1, 3>;
589
590 const Index N = Parent::get_num_samples();
591 const Index M = static_cast<Index>(m_polyline.rows());
592 const bool path_closed = is_polyline_closed();
593 const Index m = path_closed ? (M - 1) : M;
594 const Eigen::Matrix<Scalar, 1, 3> z_axis(0, 0, 1);
595
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,
604 z_axis);
605 Eigen::Transform<Scalar, 3, Eigen::AffineCompact> S;
606 S.setIdentity();
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;
609
610 transform.pretranslate(Parent::m_pivot.transpose());
611 transform.rotate(R); // twist
612 transform = transform * S; // taper
613 transform.translate(-Parent::m_pivot.transpose());
614 transform = Parent::m_normalization.inverse() * transform * Parent::m_normalization;
615 };
616
617 auto generate_node_transforms = [&]() {
618 std::vector<TransformType> transforms;
619 transforms.reserve(M);
620
621 // Initialize first transform.
622 transforms.push_back(TransformType::Identity());
623 update_transform(transforms.front(), 0);
624
625 Eigen::Quaternion<Scalar> rotation, I;
626 rotation.setIdentity();
627 I.setIdentity();
628 PointType translation;
629 translation.setZero();
630
631 if (path_closed) {
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);
635 }
636
637 // Compute vertex positions.
638 for (Index i = 1; i < m; i++) {
639 PointType v1 = m_polyline.row(i) - m_polyline.row(i - 1);
640 PointType v2 = v1;
641 if (path_closed || i < m - 1) {
642 v2 = m_polyline.row((i + 1) % m) - m_polyline.row(i);
643 }
644 auto half_r = Eigen::Quaternion<Scalar>::FromTwoVectors(v1, v2).slerp(0.5f, I);
645 translation += v1;
646 rotation = half_r * rotation;
647
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));
654
655 rotation = half_r * rotation;
656 }
657
658 if (path_closed) {
659 transforms.push_back(TransformType::Identity());
660 update_transform(transforms.back(), M - 1);
661 }
662
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) {
668 Matrix R, S;
669 Vector T;
670 transform.computeRotationScaling(&R, &S);
671 T = transform.translation();
672 decompositions.emplace_back(R, S, T);
673 }
674
675 return decompositions;
676 };
677
678 auto node_transforms = generate_node_transforms();
679 Parent::m_transforms.clear();
680 Parent::m_transforms.reserve(N);
681 Index curr_span = 0;
682 Scalar curr_depth = 0;
683 Scalar next_depth = m_lengths[0];
684
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;
688
689 for (Index i = 0; i < N; i++) {
690 const Scalar t = Parent::m_samples[i];
691 const Scalar d = Parent::get_depth_begin() * (1 - t) + Parent::get_depth_end() * t;
692
693 while (next_depth < d && curr_span < M - 1) {
694 curr_span++;
695 curr_depth = next_depth;
696 next_depth += m_lengths[curr_span];
697 }
698
699 Scalar tt = (d - curr_depth) / (next_depth - curr_depth);
700 tt = std::max<Scalar>(0, std::min<Scalar>(1, tt));
701
702 std::tie(R0, S0, T0) = node_transforms[curr_span];
703 std::tie(R1, S1, T1) = node_transforms[curr_span + 1];
704
705 R = Eigen::Quaternion<Scalar>(R0)
706 .slerp(tt, Eigen::Quaternion<Scalar>(R1))
707 .toRotationMatrix();
708 S = S0 * (1 - tt) + S1 * tt;
709 T = T0 * (1 - tt) + T1 * tt;
710
711 TransformType transform;
712 transform.setIdentity();
713 transform.linear() = R * S;
714 transform.translation() = T;
715 Parent::m_transforms.push_back(std::move(transform));
716 }
717
718 if (is_closed()) {
719 Parent::m_transforms.back() = Parent::m_transforms.front();
720 }
721 }
722
723 std::unique_ptr<Parent> clone() const override
724 {
725 auto r = std::make_unique<PolylineSweepPath<VertexArray>>(m_polyline);
726 Parent::clone_settings(*r);
727 r->initialize();
728 return r;
729 }
730
731 bool is_closed() const override
732 {
733 if (is_polyline_closed()) {
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;
739 }
740 return false;
741 }
742
743 bool operator==(const SweepPath<Scalar>& other) const override
744 {
745#if LAGRANGE_TARGET_FEATURE(RTTI)
746 if (const auto* other_polyline =
747 dynamic_cast<const PolylineSweepPath<VertexArray>*>(&other)) {
748 if (Parent::operator==(other)) {
749 constexpr Scalar TOL = std::numeric_limits<Scalar>::epsilon() * 100;
750 return (
751 (m_polyline - other_polyline->m_polyline).template lpNorm<Eigen::Infinity>() <
752 TOL);
753 }
754 }
755#else
756 la_runtime_assert(false, "RTTI is required for comparing LinearSweepPath");
757#endif
758 return false;
759 }
760
761protected:
768 {
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;
772 }
773
774private:
775 VertexArray m_polyline;
776 std::vector<Scalar> m_lengths;
777};
778
779
780} // namespace legacy
781} // namespace primitive
782} // namespace lagrange
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
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.