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/range.h>
18#include <lagrange/utils/safe_cast.h>
19
20#include <Eigen/Geometry>
21#include <Eigen/SVD>
22
23#include <algorithm>
24#include <numeric>
25#include <vector>
26
27namespace lagrange {
28namespace primitive {
29LAGRANGE_LEGACY_INLINE
30namespace legacy {
31
68template <typename _Scalar>
70{
71public:
72 using Scalar = _Scalar;
73 using TransformType = Eigen::Transform<Scalar, 3, Eigen::AffineCompact>;
74 using PointType = Eigen::Matrix<Scalar, 1, 3>;
75
76 virtual ~SweepPath() = default;
77
78public:
82 virtual std::unique_ptr<SweepPath<Scalar>> clone() const = 0;
83
88 virtual void initialize() = 0;
89
93 virtual bool is_closed() const = 0;
94
100 size_t get_num_samples() const { return m_samples.size(); }
101
105 void set_num_samples(size_t n)
106 {
107 la_runtime_assert(n >= 2, "At least 2 samples is necessary for sweep path!");
108 m_samples.resize(n);
109 for (size_t i = 0; i < n; i++) {
110 m_samples[i] = (Scalar)i / (Scalar)(n - 1);
111 }
112 }
113
117 const std::vector<Scalar>& get_samples() const { return m_samples; }
118
125 void set_samples(std::vector<Scalar> samples)
126 {
127 la_runtime_assert(samples.size() >= 2, "At least 2 samples is necessary for sweep path!");
128 m_samples = std::move(samples);
129 }
130
138 void add_samples(const std::vector<Scalar>& samples)
139 {
140 std::vector<Scalar> results;
141 results.reserve(m_samples.size() + samples.size());
142 std::set_union(
143 m_samples.begin(),
144 m_samples.end(),
145 samples.begin(),
146 samples.end(),
147 std::back_inserter(results));
148 m_samples = std::move(results);
149 }
150
154 const std::vector<TransformType>& get_transforms() const { return m_transforms; }
155 std::vector<TransformType>& get_transforms() { return m_transforms; }
156
160 std::vector<Scalar> get_offsets() const
161 {
162 if (has_offsets()) {
163 std::vector<Scalar> offsets;
164 offsets.reserve(get_num_samples());
165 for (auto t : m_samples) {
166 offsets.push_back(m_offset_fn(t));
167 }
168 return offsets;
169 } else {
170 return {};
171 }
172 }
173
178 Scalar get_depth_begin() const { return m_depth_begin; }
179 void set_depth_begin(Scalar depth) { m_depth_begin = depth; }
180
185 Scalar get_depth_end() const { return m_depth_end; }
186 void set_depth_end(Scalar depth) { m_depth_end = depth; }
187
192 Scalar get_twist_begin() const { return m_twist_begin; }
193 void set_twist_begin(Scalar twist) { m_twist_begin = twist; }
194
199 Scalar get_twist_end() const { return m_twist_end; }
200 void set_twist_end(Scalar twist) { m_twist_end = twist; }
201
205 Scalar get_taper_begin() const { return m_taper_begin; }
206 void set_taper_begin(Scalar taper) { m_taper_begin = taper; }
207
211 Scalar get_taper_end() const { return m_taper_end; }
212 void set_taper_end(Scalar taper) { m_taper_end = taper; }
213
218 const PointType& get_pivot() const { return m_pivot; }
219 void set_pivot(const PointType& p) { m_pivot = p; }
220
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); }
227
236 void set_normalization_transform(const TransformType& transform)
237 {
238 m_normalization = transform;
239 }
240 const TransformType& get_normalization_transform() const { return m_normalization; }
241
245 virtual bool operator==(const SweepPath<Scalar>& other) const
246 {
247 const size_t N = m_transforms.size();
248 if (N != other.m_transforms.size()) return false;
249
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;
258
259 if (m_samples != other.m_samples) {
260 return false;
261 }
262
263 if (get_offsets() != other.get_offsets()) {
264 return false;
265 }
266
267 // Note: change in normalization transform is ok.
268 return true;
269 }
270
271protected:
272 void clone_settings(SweepPath<Scalar>& other) const
273 {
274 other.set_depth_begin(get_depth_begin());
275 other.set_depth_end(get_depth_end());
276 other.set_twist_begin(get_twist_begin());
277 other.set_twist_end(get_twist_end());
278 other.set_taper_begin(get_taper_begin());
279 other.set_taper_end(get_taper_end());
280 other.set_pivot(get_pivot());
281 other.set_samples(get_samples());
282 other.set_offset_fn(m_offset_fn);
283 }
284
285protected:
286 std::vector<TransformType> m_transforms;
287 std::vector<Scalar> m_samples;
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};
296 std::function<Scalar(Scalar)> m_offset_fn;
297};
298
299
300template <typename _Scalar>
301class LinearSweepPath final : public SweepPath<_Scalar>
302{
303public:
304 using Parent = SweepPath<_Scalar>;
305 using Scalar = typename Parent::Scalar;
306
307public:
313 LinearSweepPath(Eigen::Matrix<Scalar, 1, 3> dir)
314 {
315 m_direction = dir.normalized();
317 m_direction.array().isFinite().all(),
318 "Invalid linear extrusion path direction");
320 }
321 ~LinearSweepPath() = default;
322
323public:
324 void initialize() override
325 {
326 const size_t N = Parent::get_num_samples();
327 la_runtime_assert(N >= 2, "Extrusion path must consist of at least 2 samples");
328 Parent::m_transforms.clear();
329 Parent::m_transforms.resize(N);
330
331 for (size_t i = 0; i < N; i++) {
332 const Scalar t = Parent::m_samples[i];
333 Eigen::AngleAxis<Scalar> R(
334 Parent::m_twist_begin * (1 - t) + Parent::m_twist_end * t,
335 m_direction);
336 Eigen::Transform<Scalar, 3, Eigen::AffineCompact> S;
337 S.setIdentity();
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;
340
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());
349
350 // Apply normalization transform.
351 Parent::m_transforms[i] = Parent::m_normalization.inverse() * Parent::m_transforms[i] *
352 Parent::m_normalization;
353 }
354 }
355
356 std::unique_ptr<Parent> clone() const override
357 {
358 auto r = std::make_unique<LinearSweepPath<Scalar>>(m_direction);
359 Parent::clone_settings(*r);
360 r->initialize();
361 return r;
362 }
363
364 bool is_closed() const override
365 {
366 // Linear path will never be closed.
367 return false;
368 }
369
370 bool operator==(const SweepPath<Scalar>& other) const override
371 {
372 if (const auto* other_linear = dynamic_cast<const LinearSweepPath<Scalar>*>(&other)) {
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;
376 }
377 }
378 return false;
379 }
380
381private:
382 Eigen::Matrix<Scalar, 1, 3> m_direction;
383};
384
385
395template <typename _Scalar>
396class CircularArcSweepPath final : public SweepPath<_Scalar>
397{
398public:
399 using Parent = SweepPath<_Scalar>;
400 using Scalar = typename Parent::Scalar;
401
402public:
411 CircularArcSweepPath(Scalar radius, Scalar theta)
412 : m_radius(radius)
413 , m_theta(theta)
414 {
415 // Initialize setting based on common use case.
417 Parent::set_depth_begin(0);
418 Parent::set_depth_end(2 * lagrange::internal::pi * radius);
420 m_radius >= 0,
421 "Negaative radius is not supported in CircularArcSweepPath.");
422 }
423 ~CircularArcSweepPath() = default;
424
425public:
430 void set_angle_begin(Scalar theta) { Parent::set_depth_begin(theta * m_radius); }
431
436 void set_angle_end(Scalar theta) { Parent::set_depth_end(theta * m_radius); }
437
438 void initialize() override
439 {
440 const size_t N = Parent::get_num_samples();
441 la_runtime_assert(N >= 2, "Extrusion path must consist of at least 2 samples");
442 Parent::m_transforms.clear();
443 Parent::m_transforms.resize(N);
444
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();
448
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;
451
452 for (size_t i = 0; i < N; i++) {
453 const Scalar t = Parent::m_samples[i];
454 Eigen::AngleAxis<Scalar> R(
455 Parent::m_twist_begin * (1 - t) + Parent::m_twist_end * t,
456 z_axis);
457 Eigen::Transform<Scalar, 3, Eigen::AffineCompact> S;
458 S.setIdentity();
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;
461
462 Parent::m_transforms[i].setIdentity();
463 Parent::m_transforms[i].translate(Parent::m_pivot.transpose());
464
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);
469
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());
473
474 // Apply normalization transform.
475 Parent::m_transforms[i] = Parent::m_normalization.inverse() * Parent::m_transforms[i] *
476 Parent::m_normalization;
477 }
478
479 if (is_closed()) {
480 Parent::m_transforms.back() = Parent::m_transforms.front();
481 }
482 }
483
484 std::unique_ptr<Parent> clone() const override
485 {
486 auto r = std::make_unique<CircularArcSweepPath<Scalar>>(m_radius, m_theta);
487 Parent::clone_settings(*r);
488 r->initialize();
489 return r;
490 }
491
492 bool is_closed() const override
493 {
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);
496
497 const auto angle_begin = Parent::get_depth_begin() / m_radius;
498 const auto angle_end = Parent::get_depth_end() / m_radius;
499 const Scalar angle_diff = angle_end - angle_begin;
500 const int winding = static_cast<int>(std::round(angle_diff / two_pi));
501
502 // A circular path is considered closed if the sweeping angle is
503 // a multiple of 2 * lagrange::internal::pi with error tolerance ±0.1 degrees.
504 return (winding != 0) && std::abs(angle_diff - winding * two_pi) < point_five_degree;
505 }
506
507 bool operator==(const SweepPath<Scalar>& other) const override
508 {
509 if (const auto* other_circular =
510 dynamic_cast<const CircularArcSweepPath<Scalar>*>(&other)) {
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;
515 }
516 }
517 return false;
518 }
519
520private:
521 Scalar m_radius;
522 Scalar m_theta;
523};
524
525
529template <typename _VertexArray>
530class PolylineSweepPath final : public SweepPath<typename _VertexArray::Scalar>
531{
532public:
533 using Scalar = typename _VertexArray::Scalar;
534 using VertexArray = _VertexArray;
535 using Parent = SweepPath<Scalar>;
536 using Index = Eigen::Index;
537
538public:
555 PolylineSweepPath(const VertexArray& polyline)
556 : m_polyline(polyline)
557 {
558 la_runtime_assert(polyline.cols() == 3, "Sweep path must be 3D path.");
559 la_runtime_assert(polyline.rows() > 1, "Sweep path must consist of at least 2 points!");
560
561 Index num_lines = polyline.rows() - 1;
562 m_lengths.resize(num_lines);
563 for (auto i : range<Index>(1, num_lines + 1)) {
564 m_lengths[i - 1] = (polyline.row(i) - polyline.row(i - 1)).norm();
565 }
566 const Scalar total_length = std::accumulate(m_lengths.begin(), m_lengths.end(), Scalar(0));
567
568 // Initialize some settings based on common use case.
569 Parent::set_num_samples(num_lines + 1);
570 Parent::set_depth_begin(0);
571 Parent::set_depth_end(total_length);
572 }
573 ~PolylineSweepPath() = default;
574
575public:
576 void initialize() override
577 {
578 using TransformType = Eigen::Transform<Scalar, 3, Eigen::AffineCompact>;
579 using PointType = Eigen::Matrix<Scalar, 1, 3>;
580
581 const Index N = Parent::get_num_samples();
582 const Index M = static_cast<Index>(m_polyline.rows());
583 const bool path_closed = is_polyline_closed();
584 const Index m = path_closed ? (M - 1) : M;
585 const Eigen::Matrix<Scalar, 1, 3> z_axis(0, 0, 1);
586
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,
595 z_axis);
596 Eigen::Transform<Scalar, 3, Eigen::AffineCompact> S;
597 S.setIdentity();
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;
600
601 transform.pretranslate(Parent::m_pivot.transpose());
602 transform.rotate(R); // twist
603 transform = transform * S; // taper
604 transform.translate(-Parent::m_pivot.transpose());
605 transform = Parent::m_normalization.inverse() * transform * Parent::m_normalization;
606 };
607
608 auto generate_node_transforms = [&]() {
609 std::vector<TransformType> transforms;
610 transforms.reserve(M);
611
612 // Initialize first transform.
613 transforms.push_back(TransformType::Identity());
614 update_transform(transforms.front(), 0);
615
616 Eigen::Quaternion<Scalar> rotation, I;
617 rotation.setIdentity();
618 I.setIdentity();
619 PointType translation;
620 translation.setZero();
621
622 if (path_closed) {
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);
626 }
627
628 // Compute vertex positions.
629 for (Index i = 1; i < m; i++) {
630 PointType v1 = m_polyline.row(i) - m_polyline.row(i - 1);
631 PointType v2 = v1;
632 if (path_closed || i < m - 1) {
633 v2 = m_polyline.row((i + 1) % m) - m_polyline.row(i);
634 }
635 auto half_r = Eigen::Quaternion<Scalar>::FromTwoVectors(v1, v2).slerp(0.5f, I);
636 translation += v1;
637 rotation = half_r * rotation;
638
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));
645
646 rotation = half_r * rotation;
647 }
648
649 if (path_closed) {
650 transforms.push_back(TransformType::Identity());
651 update_transform(transforms.back(), M - 1);
652 }
653
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) {
659 Matrix R, S;
660 Vector T;
661 transform.computeRotationScaling(&R, &S);
662 T = transform.translation();
663 decompositions.emplace_back(R, S, T);
664 }
665
666 return decompositions;
667 };
668
669 auto node_transforms = generate_node_transforms();
670 Parent::m_transforms.clear();
671 Parent::m_transforms.reserve(N);
672 Index curr_span = 0;
673 Scalar curr_depth = 0;
674 Scalar next_depth = m_lengths[0];
675
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;
679
680 for (Index i = 0; i < N; i++) {
681 const Scalar t = Parent::m_samples[i];
682 const Scalar d = Parent::get_depth_begin() * (1 - t) + Parent::get_depth_end() * t;
683
684 while (next_depth < d && curr_span < M - 1) {
685 curr_span++;
686 curr_depth = next_depth;
687 next_depth += m_lengths[curr_span];
688 }
689
690 Scalar tt = (d - curr_depth) / (next_depth - curr_depth);
691 tt = std::max<Scalar>(0, std::min<Scalar>(1, tt));
692
693 std::tie(R0, S0, T0) = node_transforms[curr_span];
694 std::tie(R1, S1, T1) = node_transforms[curr_span + 1];
695
696 R = Eigen::Quaternion<Scalar>(R0)
697 .slerp(tt, Eigen::Quaternion<Scalar>(R1))
698 .toRotationMatrix();
699 S = S0 * (1 - tt) + S1 * tt;
700 T = T0 * (1 - tt) + T1 * tt;
701
702 TransformType transform;
703 transform.setIdentity();
704 transform.linear() = R * S;
705 transform.translation() = T;
706 Parent::m_transforms.push_back(std::move(transform));
707 }
708
709 if (is_closed()) {
710 Parent::m_transforms.back() = Parent::m_transforms.front();
711 }
712 }
713
714 std::unique_ptr<Parent> clone() const override
715 {
716 auto r = std::make_unique<PolylineSweepPath<VertexArray>>(m_polyline);
717 Parent::clone_settings(*r);
718 r->initialize();
719 return r;
720 }
721
722 bool is_closed() const override
723 {
724 if (is_polyline_closed()) {
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;
730 }
731 return false;
732 }
733
734 bool operator==(const SweepPath<Scalar>& other) const override
735 {
736 if (const auto* other_polyline =
737 dynamic_cast<const PolylineSweepPath<VertexArray>*>(&other)) {
738 if (Parent::operator==(other)) {
739 constexpr Scalar TOL = std::numeric_limits<Scalar>::epsilon() * 100;
740 return (
741 (m_polyline - other_polyline->m_polyline).template lpNorm<Eigen::Infinity>() <
742 TOL);
743 }
744 }
745 return false;
746 }
747
748protected:
755 {
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;
759 }
760
761private:
762 VertexArray m_polyline;
763 std::vector<Scalar> m_lengths;
764};
765
766
767} // namespace legacy
768} // namespace primitive
769} // namespace lagrange
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
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.