AGX Dynamics 2.42.1.1
Loading...
Searching...
No Matches
PointCurve.h
Go to the documentation of this file.
1/*
2Copyright 2007-2025. Algoryx Simulation AB.
3
4All AGX source code, intellectual property, documentation, sample code,
5tutorials, scene files and technical white papers, are copyrighted, proprietary
6and confidential material of Algoryx Simulation AB. You may not download, read,
7store, distribute, publish, copy or otherwise disseminate, use or expose this
8material unless having a written signed agreement with Algoryx Simulation AB, or having been
9advised so by Algoryx Simulation AB for a time limited evaluation, or having purchased a
10valid commercial license from Algoryx Simulation AB.
11
12Algoryx Simulation AB disclaims all responsibilities for loss or damage caused
13from using this software, unless otherwise stated in written agreements with
14Algoryx Simulation AB.
15*/
16
17#pragma once
18
20#include <agx/Logger.h>
21
22#include <algorithm>
23
24namespace agxUtil
25{
26 enum class SegmentType
27 {
28 FIRST,
30 LAST
31 };
32
40 template<typename T>
42 {
43 public:
44 using value_type = T;
46
47 public:
51 struct Segment
52 {
55 };
56
62 struct SegmentPoint : public Segment
63 {
64 SegmentPoint() : point(), time( -agx::Infinity ), localTime( -1 )
65 {
66 }
67
75 inline agx::Bool isValid() const { return localTime >= agx::Real( 0 ); }
76 };
77
82 {
84 : error( agx::Infinity ), segmentLength( agx::Real( -1 ) ), numSegments( 0 ), numIterations( 0 )
85 {
86 }
87
89 : error( agx::Infinity ), segmentLength( agx::Real( -1 ) ), numSegments( numSegments ), numIterations( 0 )
90 {
91 }
92
97 };
98
99 public:
100 using TransformCallback = std::function<void( value_type& )>;
101 using SegmentCallback = std::function<void( const Segment&, SegmentType )>;
102 using SegmentPointCallback = std::function<void( const SegmentPoint&, const SegmentPoint&, SegmentType )>;
103 using SegmentationErrorCallback = std::function<agx::Real( const PointCurve&,
104 const SegmentPoint&,
105 const SegmentPoint&,
106 SegmentType )>;
107
108 public:
112 PointCurve();
113
119 template<typename ContainerT>
120 PointCurve( const ContainerT& container );
121
128 template<typename ContainerT, typename TransformFunc>
129 PointCurve( const ContainerT& container, TransformFunc func );
130
135 void reserve( size_t capacity );
136
140 const container_type& getPoints() const;
141
148
155 template<typename T2>
156 void add( const T2& point );
157
165 SegmentPoint evaluate( agx::Real time ) const;
166
171
176 void transform( TransformCallback callback );
177
182 void traverse( SegmentCallback callback ) const;
183
191 void traverse( SegmentPointCallback callback, agx::Real segmentLength, agx::Real tolerance = agx::Real( 1.0E-6 ) ) const;
192
204 SegmentationErrorCallback errorFunction,
205 agx::Real maxError,
206 agx::Real segmentLengthTolerance = agx::Real( 1.0E-6 ),
207 agx::UInt maxNumIterations = 100ul ) const;
208
209 private:
213 agx::UInt findIndex( agx::Real time ) const;
214
215 private:
216 container_type m_points;
217 agx::RealVector m_time;
218 };
219
220 template<typename T>
222 {
223 }
224
225 template<typename T>
226 template<typename ContainerT>
227 PointCurve<T>::PointCurve( const ContainerT& container )
228 {
229 m_points.resize( container.size() );
230 for ( agx::UInt i = 0; i < m_points.size(); ++i )
231 m_points[ i ] = (value_type)container[ i ];
232 }
233
234 template<typename T>
235 template<typename ContainerT, typename TransformFunc>
236 PointCurve<T>::PointCurve( const ContainerT& container, TransformFunc func )
237 {
238 m_points.resize( container.size() );
239 std::transform( container.begin(), container.end(), m_points.begin(), func );
240 }
241
242 template<typename T>
243 void PointCurve<T>::reserve( size_t capacity )
244 {
245 m_points.reserve( capacity );
246 }
247
248 template<typename T>
250 {
251 return m_points;
252 }
253
254 template<typename T>
255 template<typename T2>
256 void PointCurve<T>::add( const T2& point )
257 {
258 if ( !m_time.empty() )
259 m_time.clear();
260
261 m_points.push_back( (value_type)point );
262 }
263
264 template<typename T>
266 {
267 if ( m_points.size() < 2 ) {
268 LOGGER_WARNING() << "PointCurve::evaluate called with an undefined curve - number of points < 2." << LOGGER_ENDL();
269 return SegmentPoint();
270 }
271
272 if ( m_points.size() != m_time.size() ) {
273 LOGGER_WARNING() << "PointCurve::finalize must be executed before PointCurve::evaluate" << LOGGER_ENDL();
274 return SegmentPoint();
275 }
276
277 auto segment = SegmentPoint();
278 auto index = findIndex( time );
279
280 if ( index + 1 == m_points.size() ) {
281 // If time >= 1 roll back the index from the last point
282 // to enable time > 1 to be calculated beyond last point.
283 --index;
284 }
285
286 segment.begin = m_points[ index ];
287 segment.end = m_points[ index + 1 ];
288 segment.time = time;
289 segment.localTime = ( time - m_time[ index ] ) / ( m_time[ index + 1 ] - m_time[ index ] );
290 segment.point = (agx::Vec3)segment.begin + segment.localTime * ( (agx::Vec3)segment.end - (agx::Vec3)segment.begin );
291
292 return segment;
293 }
294
295 template<typename T>
297 {
298 agx::Real length = agx::Real( 0 );
299 for ( agx::UInt i = 1; i < m_points.size(); ++i )
300 length += ((agx::Vec3)m_points[ i - 1 ]).distance( (agx::Vec3)m_points[ i ] );
301
302 return length;
303 }
304
305 template<typename T>
307 {
308 m_time.resize( m_points.size(), agx::Real( 0 ) );
309
310 const auto totalLength = calculateLength();
311 if ( totalLength < agx::RealEpsilon )
312 return false;
313
314 agx::Real accumulatedTime = agx::Real( 0 );
315 m_time.front() = accumulatedTime;
316 for ( agx::UInt i = 1; i < m_points.size(); ++i ) {
317 accumulatedTime += ((agx::Vec3)m_points[ i - 1 ]).distance( (agx::Vec3)m_points[ i ] ) / totalLength;
318 m_time[ i ] = accumulatedTime;
319 }
320 m_time.back() = agx::Real( 1 );
321
322 return m_time.size() > 1;
323 }
324
325 template<typename T>
327 {
328 for ( agx::UInt i = 0; i < m_points.size(); ++i )
329 callback( m_points[ i ] );
330
331 this->finalize();
332 }
333
334 template<typename T>
335 void PointCurve<T>::traverse( typename PointCurve<T>::SegmentCallback callback ) const
336 {
337 for ( agx::UInt i = 1; i < m_points.size(); ++i )
338 callback( Segment{ m_points[ i - 1 ], m_points[ i ] },
339 i == 1 ?
341 i + 1 == m_points.size() ?
344 }
345
346 template<typename T>
348 agx::Real segmentLength,
349 agx::Real tolerance /*= agx::Real( 1.0E-6 )*/ ) const
350 {
351 const auto totalLength = calculateLength();
352 if ( totalLength < agx::RealEpsilon )
353 return;
354
355 const agx::Real dt = segmentLength / totalLength;
356 agx::Bool done = false;
357 agx::Real prevT = agx::Real( 0 );
358 auto prev = evaluate( prevT );
360 while ( !done ) {
361 agx::Real currT = prevT + dt;
362 auto curr = evaluate( currT );
363 agx::Real prevToCurrDist = prev.point.distance( curr.point );
364 while ( !agx::equivalent( prevToCurrDist, segmentLength, tolerance ) ) {
365 const agx::Real overshoot = prevToCurrDist - segmentLength;
366 currT -= overshoot / totalLength;
367 curr = evaluate( currT );
368 prevToCurrDist = prev.point.distance( curr.point );
369 }
370
371 done = currT > agx::Real( 1 ) + dt;
372 done = done ||
373 ( currT + agx::Real( 0.5 ) * dt >= agx::Real( 1 ) &&
374 curr.point.distance( (agx::Vec3)m_points.back() ) < agx::Real( 0.5 ) * segmentLength );
375
376 if ( done )
377 type = SegmentType::LAST;
378
379 callback( prev, curr, type );
380
381 if ( type == SegmentType::FIRST )
383
384 prevT = currT;
385 prev = curr;
386 }
387 }
388
389 template<typename T>
390 typename
392 typename PointCurve<T>::SegmentationErrorCallback errorFunction,
393 agx::Real maxError,
394 agx::Real segmentLengthTolerance /*= agx::Real( 1.0E-6 )*/,
395 agx::UInt maxNumIterations /*= 100ul*/ ) const
396 {
397 SegmentationResult result( numSegments );
398 if ( numSegments < 1 )
399 return result;
400
401 const auto totalLength = calculateLength();
402 if ( totalLength < agx::RealEpsilon )
403 return result;
404
405 result.segmentLength = totalLength / agx::Real( result.numSegments );
406
407 const agx::Real dl = agx::Real( 1.0E-3 ) / agx::Real( result.numSegments );
408 agx::Bool done = false;
409 SegmentationResult bestResult( numSegments );
410 const auto& self = *this;
411 while ( !done && result.numIterations < maxNumIterations ) {
412 ++result.numIterations;
413
414 agx::Real ePrev = agx::Real( 0 );
415 agx::Real eCurr = agx::Real( 0 );
416 agx::Real eNext = agx::Real( 0 );
417
418 this->traverse( [&self, &errorFunction, &ePrev]( const SegmentPoint& p1, const SegmentPoint& p2, SegmentType type )
419 {
420 ePrev += errorFunction( self, p1, p2, type );
421 }, result.segmentLength - dl, segmentLengthTolerance );
422
423 this->traverse( [&self, &errorFunction, &eCurr]( const SegmentPoint& p1, const SegmentPoint& p2, SegmentType type )
424 {
425 eCurr += errorFunction( self, p1, p2, type );
426 }, result.segmentLength, segmentLengthTolerance );
427
428 this->traverse( [&self, &errorFunction, &eNext]( const SegmentPoint& p1, const SegmentPoint& p2, SegmentType type )
429 {
430 eNext += errorFunction( self, p1, p2, type );
431 }, result.segmentLength + dl, segmentLengthTolerance );
432
433 if ( eCurr < bestResult.error ) {
434 bestResult = result;
435 bestResult.error = eCurr;
436 }
437
438 const agx::Real dr = -agx::Real( 2 ) * dl * eCurr / ( eNext - ePrev );
439 result.segmentLength += dr;
440
441 // No solution if we're approaching negative length or reach max iterations.
442 if ( result.numIterations == maxNumIterations ||
444 return bestResult;
445
446 result.error = agx::Real( 0 );
447 this->traverse( [&self, &result, &errorFunction]( const SegmentPoint& p1, const SegmentPoint& p2, SegmentType type )
448 {
449 result.error += errorFunction( self, p1, p2, type );
450 }, result.segmentLength, segmentLengthTolerance );
451
452 done = std::abs( result.error ) <= maxError;
453 }
454
455 return result;
456 }
457
458 template<typename T>
460 {
461 if ( agx::leq( time, agx::Real( 0 ) ) )
462 return 0ul;
463 else if ( agx::geq( time, agx::Real( 1 ) ) )
464 return std::max( m_points.size(), (typename container_type::size_type)1 ) - 1;
465
466 auto it = std::lower_bound( m_time.begin(), m_time.end(), time );
467 agxAssert( it != m_time.begin() );
468 agxAssert( it != m_time.end() );
469
470 return it - m_time.begin() - 1;
471 }
472}
#define LOGGER_WARNING()
Definition: Logger.h:23
#define LOGGER_ENDL()
Definition: Logger.h:27
Utility class curve defined by a set of points.
Definition: PointCurve.h:42
std::function< void(value_type &)> TransformCallback
Definition: PointCurve.h:100
std::function< void(const SegmentPoint &, const SegmentPoint &, SegmentType)> SegmentPointCallback
Definition: PointCurve.h:102
void reserve(size_t capacity)
Reserves containers to given capacity.
Definition: PointCurve.h:243
agx::Bool finalize()
When all points has been added this method has to be called to collect curve data for efficient manip...
Definition: PointCurve.h:306
std::function< void(const Segment &, SegmentType)> SegmentCallback
Definition: PointCurve.h:101
SegmentPoint evaluate(agx::Real time) const
Evaluate at given time.
Definition: PointCurve.h:265
void add(const T2 &point)
Add new point to this curve.
Definition: PointCurve.h:256
void transform(TransformCallback callback)
Transform points in this curve.
Definition: PointCurve.h:326
std::function< agx::Real(const PointCurve &, const SegmentPoint &, const SegmentPoint &, SegmentType)> SegmentationErrorCallback
Definition: PointCurve.h:106
void traverse(SegmentCallback callback) const
Traverse all segments.
PointCurve()
Default constructor.
Definition: PointCurve.h:221
const container_type & getPoints() const
Definition: PointCurve.h:249
void traverse(SegmentPointCallback callback, agx::Real segmentLength, agx::Real tolerance=agx::Real(1.0E-6)) const
Traverses this curve and invokes callback with a segment of a given segment length.
agx::Real calculateLength() const
Definition: PointCurve.h:296
SegmentationResult findSegmentLength(agx::UInt numSegments, SegmentationErrorCallback errorFunction, agx::Real maxError, agx::Real segmentLengthTolerance=agx::Real(1.0E-6), agx::UInt maxNumIterations=100ul) const
Uses Newton Raphson to minimize the error while dividing this curve into segments.
Definition: PointCurve.h:391
#define agxAssert(expr)
Definition: debug.h:143
The agxUtil namespace contain classes and methods for utility functionality.
The agx namespace contains the dynamics/math part of the AGX Dynamics API.
Vec3T< Real > Vec3
The object holding 3 dimensional vectors and providing basic arithmetic.
Definition: agx/Vec3.h:36
bool Bool
Definition: Integer.h:40
bool geq(double a, double b, double eps=(double) AGX_EQUIVALENT_EPSILON)
Definition: Math.h:381
AGXCORE_EXPORT const Real REAL_SQRT_EPSILON
AGXCORE_EXPORT const Real RealEpsilon
uint64_t UInt
Definition: Integer.h:27
double Real
Definition: Real.h:41
bool leq(double a, double b, double eps=(double) AGX_EQUIVALENT_EPSILON)
Definition: Math.h:369
AGXPHYSICS_EXPORT agx::Bool equivalent(const agx::AddedMassInteraction::Matrix6x6 &lhs, const agx::AddedMassInteraction::Matrix6x6 &rhs, agx::Real eps=agx::RealEpsilon)
Segment point with current curve segment and a point on that segment with local and global time.
Definition: PointCurve.h:63
agx::Real localTime
The point's local time on the curve.
Definition: PointCurve.h:70
agx::Real time
The point's global time on the curve.
Definition: PointCurve.h:69
agx::Vec3 point
Point on segment.
Definition: PointCurve.h:68
Segment data with begin and end point.
Definition: PointCurve.h:52
Result data of segmentation, PointCurve::findSegmentLength.
Definition: PointCurve.h:82
SegmentationResult(agx::UInt numSegments)
Definition: PointCurve.h:88