Loading [MathJax]/extensions/TeX/AMSmath.js
Radium Engine  1.5.28
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
PolyLine.cpp
1#include <Core/Geometry/DistanceQueries.hpp>
2#include <Core/Geometry/PolyLine.hpp>
3#include <Core/Math/LinearAlgebra.hpp>
4#include <algorithm>
5#include <cmath>
6#include <limits>
7#include <memory>
8
9namespace Ra {
10namespace Core {
11namespace Geometry {
12
14 m_ptsDiff.clear();
15 m_lengths.clear();
16 CORE_ASSERT( m_pts.size() > 1, "Line must have at least two points" );
17 m_ptsDiff.reserve( m_pts.size() - 1 );
18 m_lengths.reserve( m_pts.size() - 1 );
19 Scalar len = 0;
20 for ( uint i = 0; i < m_pts.size() - 1; ++i ) {
21 m_ptsDiff.push_back( m_pts[i + 1] - m_pts[i] );
22 len += m_ptsDiff.back().norm();
23 m_lengths.push_back( len );
24 }
25}
26
27PolyLine::PolyLine( const Vector3Array& pts ) : m_pts( pts ) {
28 update();
29}
30
31void PolyLine::setPoints( const Vector3Array& pts ) {
32 m_pts = pts;
33 update();
34}
35
36Scalar PolyLine::squaredDistance( const Vector3& p ) const {
37 CORE_ASSERT( m_pts.size() > 1, "Line must have at least two points" );
38 Scalar sqDist = std::numeric_limits<Scalar>::max();
39 for ( uint i = 0; i < m_ptsDiff.size(); ++i ) {
40 sqDist = std::min( Geometry::pointToSegmentSq( p, m_pts[i], m_ptsDiff[i] ), sqDist );
41 }
42 return sqDist;
43}
44
45Scalar PolyLine::distance( const Vector3& p ) const {
46 return std::sqrt( squaredDistance( p ) );
47}
48
49Scalar PolyLine::projectOnSegment( const Vector3& p, uint segment ) const {
50 CORE_ASSERT( segment < m_ptsDiff.size(), "invalid segment index" );
51 const Scalar tSegment = Geometry::projectOnSegment( p, m_pts[segment], m_ptsDiff[segment] );
52 return getLineParameter( segment, tSegment );
53}
54
55Scalar PolyLine::project( const Vector3& p ) const {
56 CORE_ASSERT( m_pts.size() > 1, "Line must have at least two points" );
57 Scalar sqDist = std::numeric_limits<Scalar>::max();
58 uint segment = 0;
61 ts.reserve( m_ptsDiff.size() );
62 ds.reserve( m_ptsDiff.size() );
63
64 for ( uint i = 0; i < m_ptsDiff.size(); ++i ) {
65 Scalar proj = Geometry::projectOnSegment( p, m_pts[i], m_ptsDiff[i] );
66 Scalar d = ( p - ( m_pts[i] + proj * ( m_ptsDiff[i] ) ) ).squaredNorm();
67 ds.push_back( d );
68 ts.push_back( proj );
69 if ( d < sqDist ) {
70 sqDist = d;
71 segment = i;
72 }
73 }
74
75 CORE_ASSERT( segment < m_ptsDiff.size(), "Invalid index" );
76 Scalar t = ts[segment];
77 if ( t > 0 && t < 1 ) {
78 bool prev = segment > 0 && ts[segment - 1] > 0 && ts[segment - 1] < 1;
79 bool next = segment < m_ptsDiff.size() - 1 && ts[segment + 1] > 0 && ts[segment + 1] < 1;
80 if ( prev || next ) {
81 if ( prev && next ) { prev = ds[segment - 1] < ds[segment + 1]; }
82 uint i = prev ? segment - 1 : segment;
83 Vector3 ba = -m_ptsDiff[i];
84 Vector3 bc = m_ptsDiff[i + 1];
85 Vector3 bp = p - m_pts[i + 1];
86 Scalar c1 = Math::cotan( ba, bp );
87 Scalar c2 = Math::cotan( bp, bc );
88
89 Scalar t1 = getLineParameter( i, ts[i] );
90 Scalar t2 = getLineParameter( i + 1, ts[i + 1] );
91 return ( c1 * t1 + c2 * t2 ) / ( c1 + c2 );
92 }
93 }
94 return getLineParameter( segment, t );
95}
96
97Vector3 PolyLine::f( Scalar t ) const {
98 // Clamp the parameter between 0 and 1 and scale it.
99 const Scalar param = length() * Math::saturate( t );
100
101 // Try to locate the segment section where f(t) belongs.
102 uint i = 0;
103 Scalar lprev = 0.f;
104 while ( m_lengths[i] < param ) {
105 lprev = m_lengths[i];
106 ++i;
107 }
108
109 CORE_ASSERT( i < m_ptsDiff.size(), "Invalid index" );
110
111 // Now we know point f(t) is between point Pi and Pi+1;
112 return m_pts[i] + ( ( param - lprev ) / ( m_lengths[i] - lprev ) ) * m_ptsDiff[i];
113}
114
115uint PolyLine::getNearestSegment( const Vector3& p ) const {
116 CORE_ASSERT( m_pts.size() > 1, "Line must have at least two points" );
117 Scalar sqDist = std::numeric_limits<Scalar>::max();
118 uint segment = 0;
119 for ( uint i = 0; i < m_ptsDiff.size(); ++i ) {
120 Scalar proj = Geometry::projectOnSegment( p, m_pts[i], m_ptsDiff[i] );
121 Scalar d = ( p - ( m_pts[i] + proj * ( m_ptsDiff[i] ) ) ).squaredNorm();
122 if ( d < sqDist ) {
123 sqDist = d;
124 segment = i;
125 }
126 }
127
128 CORE_ASSERT( segment < m_ptsDiff.size(), "Invalid index" );
129 return segment;
130}
131
132} // namespace Geometry
133} // namespace Core
134} // namespace Ra
uint getNearestSegment(const Vector3 &p) const
Returns the index of the nearest segment.
Definition PolyLine.cpp:115
PolyLine(const Vector3Array &pt)
Create a polyline from a given set of points.
Definition PolyLine.cpp:27
Vector3 f(Scalar t) const
Definition PolyLine.cpp:97
void update()
Update the precomputed values after new points have been set.
Definition PolyLine.cpp:13
Scalar distance(const Vector3 &p) const
Return the distance between the line and a given point p.
Definition PolyLine.cpp:45
Scalar getLineParameter(uint segment, Scalar tSegment) const
Definition PolyLine.hpp:97
void setPoints(const Vector3Array &pt)
Update the points of the polyline.
Definition PolyLine.cpp:31
Scalar squaredDistance(const Vector3 &p) const
Return the squared distance between the line and a given point p.
Definition PolyLine.cpp:36
Scalar projectOnSegment(const Vector3 &p, uint segment) const
Definition PolyLine.cpp:49
Scalar project(const Vector3 &p) const
Definition PolyLine.cpp:55
Scalar length() const
Get the total length of the line .
Definition PolyLine.hpp:85
T clear(T... args)
T max(T... args)
T min(T... args)
Scalar cotan(const Vector_ &v1, const Vector_ &v2)
constexpr T saturate(T v)
Clamps the value between 0 and 1.
Definition Math.hpp:120
@ Geometry
"Geometry" render objects are those loaded using Radium::IO and generated by GeometrySystem
hepler function to manage enum as underlying types in VariableSet
Definition Cage.cpp:4
T push_back(T... args)
T reserve(T... args)
T sqrt(T... args)