AGX Dynamics 2.41.1.2
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( numSegments ), numIterations( 0 )
85 {
86 }
87
92 };
93
94 public:
95 using TransformCallback = std::function<void( value_type& )>;
96 using SegmentCallback = std::function<void( const Segment&, SegmentType )>;
97 using SegmentPointCallback = std::function<void( const SegmentPoint&, const SegmentPoint&, SegmentType )>;
98 using SegmentationErrorCallback = std::function<agx::Real( const PointCurve&,
99 const SegmentPoint&,
100 const SegmentPoint&,
101 SegmentType )>;
102
103 public:
107 PointCurve();
108
114 template<typename ContainerT>
115 PointCurve( const ContainerT& container );
116
123 template<typename ContainerT, typename TransformFunc>
124 PointCurve( const ContainerT& container, TransformFunc func );
125
132
139 template<typename T2>
140 void add( const T2& point );
141
149 SegmentPoint evaluate( agx::Real time ) const;
150
155
160 void transform( TransformCallback callback );
161
166 void traverse( SegmentCallback callback ) const;
167
175 void traverse( SegmentPointCallback callback, agx::Real segmentLength, agx::Real tolerance = agx::Real( 1.0E-6 ) ) const;
176
188 SegmentationErrorCallback errorFunction,
189 agx::Real maxError,
190 agx::Real segmentLengthTolerance = agx::Real( 1.0E-6 ),
191 agx::UInt maxNumIterations = 100ul ) const;
192
193 private:
197 agx::UInt findIndex( agx::Real time ) const;
198
199 private:
200 container_type m_points;
201 agx::RealVector m_time;
202 };
203
204 template<typename T>
206 {
207 }
208
209 template<typename T>
210 template<typename ContainerT>
211 PointCurve<T>::PointCurve( const ContainerT& container )
212 {
213 m_points.resize( container.size() );
214 for ( agx::UInt i = 0; i < m_points.size(); ++i )
215 m_points[ i ] = (value_type)container[ i ];
216 }
217
218 template<typename T>
219 template<typename ContainerT, typename TransformFunc>
220 PointCurve<T>::PointCurve( const ContainerT& container, TransformFunc func )
221 {
222 m_points.resize( container.size() );
223 std::transform( container.begin(), container.end(), m_points.begin(), func );
224 }
225
226 template<typename T>
227 template<typename T2>
228 void PointCurve<T>::add( const T2& point )
229 {
230 if ( !m_time.empty() )
231 m_time.clear();
232
233 m_points.push_back( (value_type)point );
234 }
235
236 template<typename T>
238 {
239 if ( m_points.size() < 2 ) {
240 LOGGER_WARNING() << "PointCurve::evaluate called with an undefined curve - number of points < 2." << LOGGER_ENDL();
241 return SegmentPoint();
242 }
243
244 if ( m_points.size() != m_time.size() ) {
245 LOGGER_WARNING() << "PointCurve::finalize must be executed before PointCurve::evaluate" << LOGGER_ENDL();
246 return SegmentPoint();
247 }
248
249 auto segment = SegmentPoint();
250 auto index = findIndex( time );
251
252 if ( index + 1 == m_points.size() ) {
253 // If time >= 1 roll back the index from the last point
254 // to enable time > 1 to be calculated beyond last point.
255 --index;
256 }
257
258 segment.begin = m_points[ index ];
259 segment.end = m_points[ index + 1 ];
260 segment.time = time;
261 segment.localTime = ( time - m_time[ index ] ) / ( m_time[ index + 1 ] - m_time[ index ] );
262 segment.point = (agx::Vec3)segment.begin + segment.localTime * ( (agx::Vec3)segment.end - (agx::Vec3)segment.begin );
263
264 return segment;
265 }
266
267 template<typename T>
269 {
270 agx::Real length = agx::Real( 0 );
271 for ( agx::UInt i = 1; i < m_points.size(); ++i )
272 length += ((agx::Vec3)m_points[ i - 1 ]).distance( (agx::Vec3)m_points[ i ] );
273
274 return length;
275 }
276
277 template<typename T>
279 {
280 m_time.resize( m_points.size(), agx::Real( 0 ) );
281
282 const auto totalLength = calculateLength();
283 if ( totalLength < agx::RealEpsilon )
284 return false;
285
286 agx::Real accumulatedTime = agx::Real( 0 );
287 m_time.front() = accumulatedTime;
288 for ( agx::UInt i = 1; i < m_points.size(); ++i ) {
289 accumulatedTime += ((agx::Vec3)m_points[ i - 1 ]).distance( (agx::Vec3)m_points[ i ] ) / totalLength;
290 m_time[ i ] = accumulatedTime;
291 }
292 m_time.back() = agx::Real( 1 );
293
294 return m_time.size() > 1;
295 }
296
297 template<typename T>
299 {
300 for ( agx::UInt i = 0; i < m_points.size(); ++i )
301 callback( m_points[ i ] );
302
303 this->finalize();
304 }
305
306 template<typename T>
307 void PointCurve<T>::traverse( typename PointCurve<T>::SegmentCallback callback ) const
308 {
309 for ( agx::UInt i = 1; i < m_points.size(); ++i )
310 callback( Segment{ m_points[ i - 1 ], m_points[ i ] },
311 i == 1 ?
313 i + 1 == m_points.size() ?
316 }
317
318 template<typename T>
320 agx::Real segmentLength,
321 agx::Real tolerance /*= agx::Real( 1.0E-6 )*/ ) const
322 {
323 const auto totalLength = calculateLength();
324 if ( totalLength < agx::RealEpsilon )
325 return;
326
327 const agx::Real dt = segmentLength / totalLength;
328 agx::Bool done = false;
329 agx::Real prevT = agx::Real( 0 );
330 auto prev = evaluate( prevT );
332 while ( !done ) {
333 agx::Real currT = prevT + dt;
334 auto curr = evaluate( currT );
335 agx::Real prevToCurrDist = prev.point.distance( curr.point );
336 while ( !agx::equivalent( prevToCurrDist, segmentLength, tolerance ) ) {
337 const agx::Real overshoot = prevToCurrDist - segmentLength;
338 currT -= overshoot / totalLength;
339 curr = evaluate( currT );
340 prevToCurrDist = prev.point.distance( curr.point );
341 }
342
343 done = currT > agx::Real( 1 ) + dt;
344 done = done ||
345 ( currT + agx::Real( 0.5 ) * dt >= agx::Real( 1 ) &&
346 curr.point.distance( (agx::Vec3)m_points.back() ) < agx::Real( 0.5 ) * segmentLength );
347
348 if ( done )
349 type = SegmentType::LAST;
350
351 callback( prev, curr, type );
352
353 if ( type == SegmentType::FIRST )
355
356 prevT = currT;
357 prev = curr;
358 }
359 }
360
361 template<typename T>
362 typename
364 typename PointCurve<T>::SegmentationErrorCallback errorFunction,
365 agx::Real maxError,
366 agx::Real segmentLengthTolerance /*= agx::Real( 1.0E-6 )*/,
367 agx::UInt maxNumIterations /*= 100ul*/ ) const
368 {
369 SegmentationResult result( numSegments );
370 if ( numSegments < 1 )
371 return result;
372
373 const auto totalLength = calculateLength();
374 if ( totalLength < agx::RealEpsilon )
375 return result;
376
377 result.segmentLength = totalLength / agx::Real( result.numSegments );
378
379 const agx::Real dl = agx::Real( 1.0E-3 ) / agx::Real( result.numSegments );
380 agx::Bool done = false;
381 SegmentationResult bestResult( numSegments );
382 const auto& self = *this;
383 while ( !done && result.numIterations < maxNumIterations ) {
384 ++result.numIterations;
385
386 agx::Real ePrev = agx::Real( 0 );
387 agx::Real eCurr = agx::Real( 0 );
388 agx::Real eNext = agx::Real( 0 );
389
390 this->traverse( [&self, &errorFunction, &ePrev]( const SegmentPoint& p1, const SegmentPoint& p2, SegmentType type )
391 {
392 ePrev += errorFunction( self, p1, p2, type );
393 }, result.segmentLength - dl, segmentLengthTolerance );
394
395 this->traverse( [&self, &errorFunction, &eCurr]( const SegmentPoint& p1, const SegmentPoint& p2, SegmentType type )
396 {
397 eCurr += errorFunction( self, p1, p2, type );
398 }, result.segmentLength, segmentLengthTolerance );
399
400 this->traverse( [&self, &errorFunction, &eNext]( const SegmentPoint& p1, const SegmentPoint& p2, SegmentType type )
401 {
402 eNext += errorFunction( self, p1, p2, type );
403 }, result.segmentLength + dl, segmentLengthTolerance );
404
405 if ( eCurr < bestResult.error ) {
406 bestResult = result;
407 bestResult.error = eCurr;
408 }
409
410 const agx::Real dr = -agx::Real( 2 ) * dl * eCurr / ( eNext - ePrev );
411 result.segmentLength += dr;
412
413 // No solution if we're approaching negative length or reach max iterations.
414 if ( result.numIterations == maxNumIterations ||
416 return bestResult;
417
418 result.error = agx::Real( 0 );
419 this->traverse( [&self, &result, &errorFunction]( const SegmentPoint& p1, const SegmentPoint& p2, SegmentType type )
420 {
421 result.error += errorFunction( self, p1, p2, type );
422 }, result.segmentLength, segmentLengthTolerance );
423
424 done = std::abs( result.error ) <= maxError;
425 }
426
427 return result;
428 }
429
430 template<typename T>
432 {
433 if ( agx::leq( time, agx::Real( 0 ) ) )
434 return 0ul;
435 else if ( agx::geq( time, agx::Real( 1 ) ) )
436 return std::max( m_points.size(), (typename container_type::size_type)1 ) - 1;
437
438 auto it = std::lower_bound( m_time.begin(), m_time.end(), time );
439 agxAssert( it != m_time.begin() );
440 agxAssert( it != m_time.end() );
441
442 return it - m_time.begin() - 1;
443 }
444}
#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:95
std::function< void(const SegmentPoint &, const SegmentPoint &, SegmentType)> SegmentPointCallback
Definition: PointCurve.h:97
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:278
std::function< void(const Segment &, SegmentType)> SegmentCallback
Definition: PointCurve.h:96
SegmentPoint evaluate(agx::Real time) const
Evaluate at given time.
Definition: PointCurve.h:237
void add(const T2 &point)
Add new point to this curve.
Definition: PointCurve.h:228
void transform(TransformCallback callback)
Transform points in this curve.
Definition: PointCurve.h:298
std::function< agx::Real(const PointCurve &, const SegmentPoint &, const SegmentPoint &, SegmentType)> SegmentationErrorCallback
Definition: PointCurve.h:101
void traverse(SegmentCallback callback) const
Traverse all segments.
PointCurve()
Default constructor.
Definition: PointCurve.h:205
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:268
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:363
#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:42
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:83