Skip to content

Commit 5a274e4

Browse files
committed
add isPointOnPlane function and add its unit tests
1 parent 65b6af0 commit 5a274e4

File tree

2 files changed

+88
-13
lines changed

2 files changed

+88
-13
lines changed

Sofa/framework/Geometry/src/sofa/geometry/Triangle.h

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -176,43 +176,73 @@ struct Triangle
176176
}
177177

178178
}
179-
180-
179+
180+
/**
181+
* @brief Test if input point is on the plane defined by the Triangle (n0, n1, n2)
182+
* @tparam Node iterable container
183+
* @tparam T scalar
184+
* @param p0: position of the point to test
185+
* @param n0, n1, n2: nodes of the triangle
186+
* @return bool result if point is on the plane of the triangle.
187+
*/
188+
template<typename Node,
189+
typename T = std::decay_t<decltype(*std::begin(std::declval<Node>()))>,
190+
typename = std::enable_if_t<std::is_scalar_v<T>>
191+
>
192+
[[nodiscard]]
193+
static constexpr bool isPointOnPlane(const Node& p0, const Node& n0, const Node& n1, const Node& n2)
194+
{
195+
if constexpr (std::is_same_v<Node, sofa::type::Vec<3, T>>)
196+
{
197+
const auto normal = Triangle::normal(n0, n1, n2);
198+
const auto normalNorm2 = sofa::type::dot(normal, normal);
199+
if (normalNorm2 > std::numeric_limits<T>::epsilon())
200+
{
201+
const auto d = sofa::type::dot(p0 - n0, normal);
202+
if (d * d / normalNorm2 > std::numeric_limits<T>::epsilon())
203+
return false;
204+
}
205+
206+
return true;
207+
}
208+
else
209+
{
210+
// all points are trivially in the same plane
211+
return true;
212+
}
213+
}
214+
181215
/**
182216
* @brief Test if input point is inside Triangle (n0, n1, n2) using Triangle @sa getBarycentricCoordinates . The point is inside the Triangle if and only if Those coordinates are all positive.
183217
* @tparam Node iterable container
184218
* @tparam T scalar
185219
* @param p0: position of the point to test
186220
* @param n0, n1, n2: nodes of the triangle
187221
* @param output parameter: sofa::type::Vec<3, T> barycentric coordinates of the input point in Triangle
222+
* @param assumePointIsOnPlane: optional bool to avoid testing if the point is on the plane defined by the triangle
188223
* @return bool result if point is inside Triangle.
189224
*/
190225
template<typename Node,
191226
typename T = std::decay_t<decltype(*std::begin(std::declval<Node>()))>,
192227
typename = std::enable_if_t<std::is_scalar_v<T>>
193228
>
194-
static constexpr bool isPointInTriangle(const Node& p0, const Node& n0, const Node& n1, const Node& n2, sofa::type::Vec<3, T>& baryCoefs, bool assumePointIsInPlane = true)
229+
[[nodiscard]]
230+
static constexpr bool isPointInTriangle(const Node& p0, const Node& n0, const Node& n1, const Node& n2, sofa::type::Vec<3, T>& baryCoefs, bool assumePointIsOnPlane = true)
195231
{
196232
baryCoefs = Triangle::getBarycentricCoordinates(p0, n0, n1, n2);
197233

198234
// In 3D, check if the point is in the plane of the triangle
199235
if constexpr (std::is_same_v<Node, sofa::type::Vec<3, T>>)
200236
{
201-
if(!assumePointIsInPlane)
237+
if(!assumePointIsOnPlane)
202238
{
203-
const auto normal = Triangle::normal(n0, n1, n2);
204-
const auto normalNorm2 = sofa::type::dot(normal, normal);
205-
if (normalNorm2 > std::numeric_limits<T>::epsilon())
206-
{
207-
const auto d = sofa::type::dot(p0 - n0, normal);
208-
if (d * d / normalNorm2 > std::numeric_limits<T>::epsilon())
209-
return false;
210-
}
239+
if(!isPointOnPlane(p0, n0, n1, n2))
240+
return false;
211241
}
212242
}
213243
else
214244
{
215-
SOFA_UNUSED(assumePointIsInPlane);
245+
SOFA_UNUSED(assumePointIsOnPlane);
216246
}
217247

218248
for (int i = 0; i < 3; ++i)

Sofa/framework/Geometry/test/Triangle_test.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,51 @@ TYPED_TEST(GeometryVec3DTriangle_test, getBarycentricCoordinates)
374374
}
375375

376376

377+
TYPED_TEST(GeometryVec3DTriangle_test, isPointOnPlane)
378+
{
379+
// Triangle in the plane z = x (normal is proportional to (-1, 0, 1))
380+
const TypeParam a{ 0., 0., 0. };
381+
const TypeParam b{ 2., 0., 2. };
382+
const TypeParam c{ 0., 2., 0. };
383+
384+
// point on the plane, inside the triangle
385+
EXPECT_TRUE(sofa::geometry::Triangle::isPointOnPlane(TypeParam{ 0.5, 0.5, 0.5 }, a, b, c));
386+
387+
// point on vertex
388+
EXPECT_TRUE(sofa::geometry::Triangle::isPointOnPlane(a, a, b, c));
389+
EXPECT_TRUE(sofa::geometry::Triangle::isPointOnPlane(b, a, b, c));
390+
EXPECT_TRUE(sofa::geometry::Triangle::isPointOnPlane(c, a, b, c));
391+
392+
// point on edge
393+
EXPECT_TRUE(sofa::geometry::Triangle::isPointOnPlane(TypeParam{ 1., 0., 1. }, a, b, c));
394+
395+
// point on the plane but outside the triangle
396+
EXPECT_TRUE(sofa::geometry::Triangle::isPointOnPlane(TypeParam{ 3., 0., 3. }, a, b, c));
397+
EXPECT_TRUE(sofa::geometry::Triangle::isPointOnPlane(TypeParam{ -5., 10., -5. }, a, b, c));
398+
399+
// point off the plane
400+
EXPECT_FALSE(sofa::geometry::Triangle::isPointOnPlane(TypeParam{ 1., 0.2, 0.2 }, a, b, c));
401+
EXPECT_FALSE(sofa::geometry::Triangle::isPointOnPlane(TypeParam{ 0., 0., 5. }, a, b, c));
402+
EXPECT_FALSE(sofa::geometry::Triangle::isPointOnPlane(TypeParam{ 1., 1., 0. }, a, b, c));
403+
404+
// degenerate (collinear) triangle: always returns true
405+
const TypeParam d{ 1., 0., 1. };
406+
EXPECT_TRUE(sofa::geometry::Triangle::isPointOnPlane(TypeParam{ 99., 99., 99. }, a, b, d));
407+
}
408+
409+
410+
TYPED_TEST(GeometryVec2DTriangle_test, isPointOnPlane)
411+
{
412+
// In 2D, all points are trivially on the same plane
413+
const TypeParam a{ 0., 0. };
414+
const TypeParam b{ 2., 0. };
415+
const TypeParam c{ 2., 2. };
416+
417+
EXPECT_TRUE(sofa::geometry::Triangle::isPointOnPlane(TypeParam{ 1., 1. }, a, b, c));
418+
EXPECT_TRUE(sofa::geometry::Triangle::isPointOnPlane(TypeParam{ 99., 99. }, a, b, c));
419+
}
420+
421+
377422
TEST(GeometryTriangle_test, rayIntersectionVec3)
378423
{
379424
const sofa::type::Vec3 a{ 0., 3., 0. };

0 commit comments

Comments
 (0)