MeshLib C++ Docs
Loading...
Searching...
No Matches
MRFeatures.h
Go to the documentation of this file.
1#pragma once
2
3#include "MRMesh/MRAffineXf.h"
4#include "MRMesh/MRCone3.h"
6#include "MRMesh/MRLine3.h"
8#include "MRMesh/MRPlane3.h"
9#include "MRMesh/MRSphere.h"
10#include "MRMesh/MRVector3.h"
11
12#include <cassert>
13#include <optional>
14#include <variant>
15
16namespace MR
17{
18class FeatureObject;
19}
20
21namespace MR::Features
22{
23
24namespace Primitives
25{
26 struct Plane;
27 struct ConeSegment;
28
29 // ---
30
31 // Doubles as a point when the radius is zero.
33
34 struct Plane
35 {
36 Vector3f center;
37
38 // This must be normalized. The sign doesn't matter.
39 Vector3f normal = Vector3f( 1, 0, 0 );
40
41 // Returns an infinite line, with the center in a sane location.
42 [[nodiscard]] MRMESH_API ConeSegment intersectWithPlane( const Plane& other ) const;
43
44 // Intersects the plane with a line, returns a point (zero radius sphere).
45 // Only `center` and `dir` are used from `line` (so if `line` is a cone/cylinder, its axis is used,
46 // and the line is extended to infinity).
47 [[nodiscard]] MRMESH_API Sphere intersectWithLine( const ConeSegment& line ) const;
48
49 friend bool operator==( const Plane&, const Plane& ) = default;
50 };
51
56 {
57 // Sanity requirements:
58 // * `dir` must be normalized.
59 // * Both `positiveLength` and `negativeLength` should be non-negative. They can be infinite (both or individually).
60 // * If they are equal (both zero) or at least one of them is infinite, `positiveSideRadius` must be equal to `negativeSideRadius`.
61 // * Both `positiveSideRadius` and `negativeSideRadius` must be non-negative.
62
66 Vector3f dir;
67
72
74 float positiveLength = 0;
76 float negativeLength = 0;
77
78 // If true, the cone has no caps and no volume, and all distances (to the conical surface, that is) are positive.
79 bool hollow = false;
80
81 friend bool operator==( const ConeSegment&, const ConeSegment& ) = default;
82
83 [[nodiscard]] bool isZeroRadius() const { return positiveSideRadius == 0 && negativeSideRadius == 0; }
84 [[nodiscard]] bool isCircle() const { return positiveLength == -negativeLength && std::isfinite( positiveLength ); }
85
86 // Returns the length. Can be infinite.
87 [[nodiscard]] float length() const { return positiveLength + negativeLength; }
88
89 // Returns the center point (unlike `referencePoint`, which can actually be off-center).
90 // For half-infinite objects, returns the finite end.
91 [[nodiscard]] MRMESH_API Sphere centerPoint() const;
92
93 // Extends the object to infinity in one direction. The radius in the extended direction becomes equal to the radius in the opposite direction.
94 [[nodiscard]] MRMESH_API ConeSegment extendToInfinity( bool negative ) const;
95 // Extends the object to infinity in both directions. This is equivalent to `.extendToInfinity(false).extendToInfinity(true)`,
96 // except that calling it with `positiveSideRadius != negativeSideRadius` is illegal and triggers an assertion.
98
99 // Untruncates a truncated cone. If it's not a cone at all, returns the object unchanged and triggers an assertion.
100 [[nodiscard]] MRMESH_API ConeSegment untruncateCone() const;
101
102 // Returns a finite axis. For circles, you might want to immediately `extendToInfinity()` it.
103 [[nodiscard]] MRMESH_API ConeSegment axis() const;
104
105 // Returns a center of one of the two base circles.
106 [[nodiscard]] MRMESH_API Sphere basePoint( bool negative ) const;
107 // Returns one of the two base planes.
108 [[nodiscard]] MRMESH_API Plane basePlane( bool negative ) const;
109 // Returns one of the two base circles.
110 [[nodiscard]] MRMESH_API ConeSegment baseCircle( bool negative ) const;
111 };
112
113 using Variant = std::variant<Sphere, ConeSegment, Plane>;
114}
115
116// Those map various MR types to our primitives. Some of those are identity functions.
117
118[[nodiscard]] inline Primitives::Sphere toPrimitive( const Vector3f& point ) { return { point, 0 }; }
119[[nodiscard]] inline Primitives::Sphere toPrimitive( const Sphere3f& sphere ) { return sphere; }
120
121[[nodiscard]] inline Primitives::ConeSegment toPrimitive( const Line3f& line ) { return { .referencePoint = line.p, .dir = line.d.normalized(), .positiveLength = INFINITY, .negativeLength = INFINITY }; }
122[[nodiscard]] inline Primitives::ConeSegment toPrimitive( const LineSegm3f& segm ) { return { .referencePoint = segm.a, .dir = segm.dir().normalized(), .positiveLength = segm.length() }; }
123
124[[nodiscard]] inline Primitives::ConeSegment toPrimitive( const Cylinder3f& cyl )
125{
126 float halfLen = cyl.length / 2;
127 return{
128 .referencePoint = cyl.center(),
129 .dir = cyl.direction().normalized(),
130 .positiveSideRadius = cyl.radius, .negativeSideRadius = cyl.radius,
131 .positiveLength = halfLen, .negativeLength = halfLen,
132 };
133}
134[[nodiscard]] inline Primitives::ConeSegment toPrimitive( const Cone3f& cone )
135{
136 return{
137 .referencePoint = cone.center(),
138 .dir = cone.direction().normalized(),
139 .positiveSideRadius = std::tan( cone.angle ) * cone.height, .negativeSideRadius = 0,
140 .positiveLength = cone.height, .negativeLength = 0,
141 };
142}
143
145[[nodiscard]] MRMESH_API Primitives::ConeSegment primitiveCircle( const Vector3f& point, const Vector3f& normal, float rad );
147[[nodiscard]] MRMESH_API Primitives::ConeSegment primitiveCylinder( const Vector3f& a, const Vector3f& b, float rad );
149[[nodiscard]] MRMESH_API Primitives::ConeSegment primitiveCone( const Vector3f& a, const Vector3f& b, float rad );
150
151// Returns null if the object type is unknown. This overload ignores the parent xf.
152[[nodiscard]] MRMESH_API std::optional<Primitives::Variant> primitiveFromObject( const Object& object );
153// Returns null if the object type is unknown. This overload respects the parent's `worldXf()`.
154[[nodiscard]] MRMESH_API std::optional<Primitives::Variant> primitiveFromObjectWithWorldXf( const Object& object );
155// Can return null on some primitive configurations.
156// `infiniteExtent` is how large we make "infinite" objects. Half-infinite objects divide this by 2.
157[[nodiscard]] MRMESH_API std::shared_ptr<FeatureObject> primitiveToObject( const Primitives::Variant& primitive, float infiniteExtent );
158
159// Transform a primitive by an xf.
160// Non-uniform scaling and skewing are not supported.
161[[nodiscard]] MRMESH_API Primitives::Sphere transformPrimitive( const AffineXf3f& xf, const Primitives::Sphere& primitive );
162[[nodiscard]] MRMESH_API Primitives::Plane transformPrimitive( const AffineXf3f& xf, const Primitives::Plane& primitive );
163[[nodiscard]] MRMESH_API Primitives::ConeSegment transformPrimitive( const AffineXf3f& xf, const Primitives::ConeSegment& primitive );
164[[nodiscard]] MRMESH_API Primitives::Variant transformPrimitive( const AffineXf3f& xf, const Primitives::Variant& primitive );
165
168{
169 enum class Status
170 {
171 ok = 0,
180 notFinite,
181 };
182
184 {
186 [[nodiscard]] operator bool() const { return status == Status::ok; }
187 };
188
190 {
191 // This is a separate field because it can be negative.
192 float distance = 0;
193
196
197 [[nodiscard]] Vector3f closestPointFor( bool b ) const { return b ? closestPointB : closestPointA; }
198 };
199 // Exact distance.
201
202 // Some approximation of the distance.
203 // For planes and lines, this expects them to be mostly parallel. For everything else, it just takes the feature center.
205
207 {
208 Vector3f pointA;
209 Vector3f pointB;
210 [[nodiscard]] Vector3f pointFor( bool b ) const { return b ? pointB : pointA; }
211
212 Vector3f dirA; // Normalized.
213 Vector3f dirB; // ^
214 [[nodiscard]] Vector3f dirFor( bool b ) const { return b ? dirB : dirA; }
215
217 bool isSurfaceNormalA = false;
218 bool isSurfaceNormalB = false;
219
220 [[nodiscard]] bool isSurfaceNormalFor( bool b ) const { return b ? isSurfaceNormalB : isSurfaceNormalA; }
221
222 [[nodiscard]] MRMESH_API float computeAngleInRadians() const;
223 };
225
226 // The primitives obtained from intersecting those two.
227 std::vector<Primitives::Variant> intersections;
228
229 // Modifies the object to swap A and B;
231};
232// `MeasureResult::Status` enum to string.
233[[nodiscard]] MRMESH_API std::string_view toString( MeasureResult::Status status );
234
236namespace Traits
237{
238
239template <typename T>
240struct Unary {};
241template <>
242struct Unary<Primitives::Sphere>
243{
244 MRMESH_API std::string name( const Primitives::Sphere& prim ) const;
245};
246template <>
247struct Unary<Primitives::ConeSegment>
248{
249 MRMESH_API std::string name( const Primitives::ConeSegment& prim ) const;
250};
251template <>
252struct Unary<Primitives::Plane>
253{
254 MRMESH_API std::string name( const Primitives::Plane& prim ) const;
255};
256
257template <typename A, typename B>
258struct Binary {};
259
262template <typename A, typename B>
263concept MeasureSupportedOneWay = requires( const Binary<A, B>& t, const A& a, const B& b )
264{
265 { t.measure( a, b ) } -> std::same_as<MeasureResult>;
266};
267
268// ?? <-> Sphere
269template <>
274template <>
275struct Binary<Primitives::ConeSegment, Primitives::Sphere>
276{
278};
279template <>
280struct Binary<Primitives::Plane, Primitives::Sphere>
281{
283};
284
285// ?? <-> Cone
286template <>
287struct Binary<Primitives::ConeSegment, Primitives::ConeSegment>
288{
290};
291template <>
296
297// ?? <-> Plane
298template <>
299struct Binary<Primitives::Plane, Primitives::Plane>
300{
302};
303
304}
305
306// Get name of a `Primitives::...` class (can depend on its parameters).
307template <typename T>
308[[nodiscard]] std::string name( const T& primitive )
309{
310 return Traits::Unary<T>{}.name( primitive );
311}
312// Same but for a variant.
313[[nodiscard]] MRMESH_API std::string name( const Primitives::Variant& var );
314
315// Whether you can measure two primitives relative to one another.
316template <typename A, typename B>
318
319// Measures stuff between two primitives. (Two types from `Primitives::...`.)
320template <typename A, typename B>
322[[nodiscard]] MeasureResult measure( const A& a, const B& b )
323{
325 {
326 MeasureResult ret = Traits::Binary<A, B>{}.measure( a, b );
327
328 for ( auto* dist : { &ret.distance, &ret.centerDistance } )
329 {
330 // Catch non-finite distance.
331 if ( *dist && ( !std::isfinite( dist->distance ) || !dist->closestPointA.isFinite() || !dist->closestPointB.isFinite() ) )
333
334 // Check that we got the correct distance here.
335 // Note that the distance is signed, so we apply `abs` to it to compare it properly.
336 if ( *dist )
337 {
338 assert( [&]{
339 float a = ( dist->closestPointB - dist->closestPointA ).length();
340 float b = std::abs( dist->distance );
341 return std::abs( a - b ) <= std::max( std::min( a, b ), 0.01f ) * 0.001f;
342 }() );
343 }
344 }
345
346 // Catch non-finite angle.
347 if ( ret.angle && ( !ret.angle.pointA.isFinite() || !ret.angle.pointB.isFinite() || !ret.angle.dirA.isFinite() || !ret.angle.dirB.isFinite() ) )
349
350 // Check that the angle normals are normalized.
351 assert( ret.angle <= ( std::abs( 1 - ret.angle.dirA.length() ) < 0.0001f ) );
352 assert( ret.angle <= ( std::abs( 1 - ret.angle.dirB.length() ) < 0.0001f ) );
353
354 return ret;
355 }
356 else
357 {
358 static_assert( Traits::MeasureSupportedOneWay<B, A>, "This should never fail." );
359 MeasureResult ret = ( measure )( b, a );
360 ret.swapObjects();
361 return ret;
362 }
363}
364// Same, but with a variant as the first argument.
365template <typename B>
366[[nodiscard]] MeasureResult measure( const Primitives::Variant& a, const B& b )
367{
368 return std::visit( [&]( const auto& elem ){ return (measure)( elem, b ); }, a );
369}
370// Same, but with a variant as the second argument.
371template <typename A>
372[[nodiscard]] MeasureResult measure( const A& a, const Primitives::Variant& b )
373{
374 return std::visit( [&]( const auto& elem ){ return (measure)( a, elem ); }, b );
375}
376// Same, but with variants as both argument.
378
379}
#define MRMESH_API
Definition MRMesh/MRMeshFwd.h:68
length
Definition MRObjectDimensionsEnum.h:14
named object in the data model
Definition MRObject.h:60
Definition MRFeatures.h:317
std::variant< Sphere, ConeSegment, Plane > Variant
Definition MRFeatures.h:113
Definition MRFeatures.h:22
std::string name(const T &primitive)
Definition MRFeatures.h:308
MRMESH_API Primitives::ConeSegment primitiveCylinder(const Vector3f &a, const Vector3f &b, float rad)
a and b are centers of the sides.
MeasureResult measure(const A &a, const B &b)
Definition MRFeatures.h:322
MRMESH_API std::shared_ptr< FeatureObject > primitiveToObject(const Primitives::Variant &primitive, float infiniteExtent)
MRMESH_API Primitives::ConeSegment primitiveCircle(const Vector3f &point, const Vector3f &normal, float rad)
normal doesn't need to be normalized.
MRMESH_API std::string_view toString(MeasureResult::Status status)
MRMESH_API Primitives::Sphere transformPrimitive(const AffineXf3f &xf, const Primitives::Sphere &primitive)
MRMESH_API Primitives::ConeSegment primitiveCone(const Vector3f &a, const Vector3f &b, float rad)
a is the center of the base, b is the pointy end.
MRMESH_API std::optional< Primitives::Variant > primitiveFromObjectWithWorldXf(const Object &object)
Primitives::Sphere toPrimitive(const Vector3f &point)
Definition MRFeatures.h:118
MRMESH_API std::optional< Primitives::Variant > primitiveFromObject(const Object &object)
Cylinder3f
Definition MRMesh/MRMeshFwd.h:283
Cone3f
Definition MRMesh/MRMeshFwd.h:288
Definition MRFeatures.h:207
Vector3f pointA
Definition MRFeatures.h:208
Vector3f dirFor(bool b) const
Definition MRFeatures.h:214
MRMESH_API float computeAngleInRadians() const
Vector3f pointB
Definition MRFeatures.h:209
Vector3f dirA
Definition MRFeatures.h:212
bool isSurfaceNormalA
Whether dir{A,B} is a surface normal or a line direction.
Definition MRFeatures.h:217
Vector3f pointFor(bool b) const
Definition MRFeatures.h:210
bool isSurfaceNormalFor(bool b) const
Definition MRFeatures.h:220
Vector3f dirB
Definition MRFeatures.h:213
bool isSurfaceNormalB
Definition MRFeatures.h:218
Definition MRFeatures.h:184
Status status
Definition MRFeatures.h:185
Definition MRFeatures.h:190
Vector3f closestPointA
Definition MRFeatures.h:194
Vector3f closestPointFor(bool b) const
Definition MRFeatures.h:197
Vector3f closestPointB
Definition MRFeatures.h:195
float distance
Definition MRFeatures.h:192
Stores the results of measuring two objects relative to one another.
Definition MRFeatures.h:168
MRMESH_API void swapObjects()
Distance centerDistance
Definition MRFeatures.h:204
Angle angle
Definition MRFeatures.h:224
Distance distance
Definition MRFeatures.h:200
Status
Definition MRFeatures.h:170
@ badRelativeLocation
Can't be computed because of how the objects are located relative to each other.
@ notImplemented
Algorithms set this if this when something isn't yet implemented.
@ notFinite
The result was not finite. This is set automatically if you return non-finite values,...
std::vector< Primitives::Variant > intersections
Definition MRFeatures.h:227
Definition MRFeatures.h:56
friend bool operator==(const ConeSegment &, const ConeSegment &)=default
float length() const
Definition MRFeatures.h:87
MRMESH_API Sphere basePoint(bool negative) const
MRMESH_API ConeSegment extendToInfinity() const
Vector3f referencePoint
Some point on the axis, but not necessarily the true center point. Use centerPoint() for that.
Definition MRFeatures.h:64
MRMESH_API ConeSegment baseCircle(bool negative) const
MRMESH_API ConeSegment untruncateCone() const
float positiveSideRadius
Cap radius in the dir direction.
Definition MRFeatures.h:69
Vector3f dir
The axis direction. Must be normalized.
Definition MRFeatures.h:66
MRMESH_API Sphere centerPoint() const
float negativeLength
Distance from the center to the cap in the direction opposite to dir.
Definition MRFeatures.h:76
MRMESH_API Plane basePlane(bool negative) const
float negativeSideRadius
Cap radius in the direction opposite to dir.
Definition MRFeatures.h:71
MRMESH_API ConeSegment axis() const
float positiveLength
Distance from the center to the cap in the dir direction.
Definition MRFeatures.h:74
bool isZeroRadius() const
Definition MRFeatures.h:83
bool hollow
Definition MRFeatures.h:79
MRMESH_API ConeSegment extendToInfinity(bool negative) const
bool isCircle() const
Definition MRFeatures.h:84
Definition MRFeatures.h:35
Vector3f center
Definition MRFeatures.h:36
MRMESH_API Sphere intersectWithLine(const ConeSegment &line) const
MRMESH_API ConeSegment intersectWithPlane(const Plane &other) const
friend bool operator==(const Plane &, const Plane &)=default
Vector3f normal
Definition MRFeatures.h:39
MRMESH_API MeasureResult measure(const Primitives::ConeSegment &a, const Primitives::ConeSegment &b) const
MRMESH_API MeasureResult measure(const Primitives::ConeSegment &a, const Primitives::Sphere &b) const
MRMESH_API MeasureResult measure(const Primitives::Plane &a, const Primitives::ConeSegment &b) const
MRMESH_API MeasureResult measure(const Primitives::Plane &a, const Primitives::Plane &b) const
MRMESH_API MeasureResult measure(const Primitives::Plane &a, const Primitives::Sphere &b) const
MRMESH_API MeasureResult measure(const Primitives::Sphere &a, const Primitives::Sphere &b) const
Definition MRFeatures.h:258
MRMESH_API std::string name(const Primitives::ConeSegment &prim) const
MRMESH_API std::string name(const Primitives::Plane &prim) const
MRMESH_API std::string name(const Primitives::Sphere &prim) const
Definition MRFeatures.h:240
Definition MRSphere.h:9
V center
Definition MRSphere.h:12