Skip to content

Commit f289673

Browse files
authored
precise predicate segmentIntersectionOrder for 2D (#4790)
1 parent 6cebd3d commit f289673

File tree

6 files changed

+270
-1
lines changed

6 files changed

+270
-1
lines changed

source/MRMesh/MRMesh.vcxproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,7 @@
237237
<ClInclude Include="MRSeparationPoint.h" />
238238
<ClInclude Include="MRSharedThreadSafeOwner.h" />
239239
<ClInclude Include="MRSolarRadiation.h" />
240+
<ClInclude Include="MRSparsePolynomial.h" />
240241
<ClInclude Include="MRSphere.h" />
241242
<ClInclude Include="MRStacktrace.h" />
242243
<ClInclude Include="MRSurfaceLineOffset.h" />

source/MRMesh/MRMesh.vcxproj.filters

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1337,6 +1337,9 @@
13371337
<ClInclude Include="MREndMill.h">
13381338
<Filter>Source Files\Gcode</Filter>
13391339
</ClInclude>
1340+
<ClInclude Include="MRSparsePolynomial.h">
1341+
<Filter>Source Files\Math</Filter>
1342+
</ClInclude>
13401343
</ItemGroup>
13411344
<ItemGroup>
13421345
<ClCompile Include="MRParallelProgressReporter.cpp">

source/MRMesh/MRPrecisePredicates2.cpp

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,73 @@
11
#include "MRPrecisePredicates2.h"
22
#include "MRHighPrecision.h"
33
#include "MRPrecisePredicates3.h"
4+
#include "MRSparsePolynomial.h"
45

56
namespace MR
67
{
78

9+
namespace
10+
{
11+
12+
struct PointDegree
13+
{
14+
Vector2i pt;
15+
int d = 0; // degree of epsilon for pt.y
16+
};
17+
18+
std::array<PointDegree, 6> getPointDegrees( const std::array<PreciseVertCoords2, 6> & vs )
19+
{
20+
struct VertN
21+
{
22+
VertId v;
23+
int n = 0;
24+
};
25+
std::array<VertN, 6> as;
26+
for ( int i = 0; i < 6; ++i )
27+
as[i] = { vs[i].id, i };
28+
std::sort( begin( as ), end( as ), []( const auto & a, const auto & b ) { return a.v < b.v; } );
29+
30+
std::array<PointDegree, 6> res;
31+
int d = 1;
32+
for ( int i = 0; i < 6; ++i )
33+
{
34+
assert( i == 0 || as[i-1].v < as[i].v ); // no duplicate vertices are permitted
35+
const auto n = as[i].n;
36+
res[n] = { vs[n].pt, d };
37+
d *= 9;
38+
}
39+
return res;
40+
}
41+
42+
SparsePolynomial<FastInt128> ccwPoly(
43+
const PointDegree & a,
44+
const PointDegree & b,
45+
const PointDegree & c,
46+
int db ) // degree.x = degree.y * db
47+
{
48+
using Poly = SparsePolynomial<FastInt128>;
49+
50+
const Poly xx( a.pt.x - c.pt.x, a.d * db, 1, c.d * db, -1 );
51+
const Poly xy( a.pt.y - c.pt.y, a.d , 1, c.d , -1 );
52+
const Poly yx( b.pt.x - c.pt.x, b.d * db, 1, c.d * db, -1 );
53+
const Poly yy( b.pt.y - c.pt.y, b.d , 1, c.d , -1 );
54+
auto det = xx * yy;
55+
det -= xy * yx;
56+
return det;
57+
}
58+
59+
bool isPositive( const SparsePolynomial<FastInt128>& poly )
60+
{
61+
const auto & mapDegToCf = poly.get();
62+
if ( !mapDegToCf.empty() )
63+
return mapDegToCf.begin()->second > 0;
64+
65+
assert (false);
66+
return false;
67+
}
68+
69+
} // anonymous namespace
70+
871
// see https://arxiv.org/pdf/math/9410209 Table 4-i:
972
// a=(pi_i,1, pi_i,2)
1073
// b=(pi_j,1, pi_j,2)
@@ -206,6 +269,32 @@ SegmentSegmentIntersectResult doSegmentSegmentIntersect( const std::array<Precis
206269
return res;
207270
}
208271

272+
bool segmentIntersectionOrder( const std::array<PreciseVertCoords2, 6> & vs )
273+
{
274+
// s=01, sa=23, sb=45
275+
assert( doSegmentSegmentIntersect( { vs[0], vs[1], vs[2], vs[3] } ) );
276+
assert( doSegmentSegmentIntersect( { vs[0], vs[1], vs[4], vs[5] } ) );
277+
const auto ds = getPointDegrees( vs );
278+
279+
// res = ( ccw(sa,s[0])*ccw(sb,ds[1]) - ccw(sb,s[0])*ccw(sa,ds[1]) ) /
280+
// ( ccw(sa,s[0])-ccw(sa,ds[1]) ) * ( ccw(sb,s[0])-ccw(sb,ds[1]) )
281+
const auto polySaOrg = ccwPoly( ds[2], ds[3], ds[0], 3 );
282+
const auto polySaDest = ccwPoly( ds[2], ds[3], ds[1], 3 );
283+
assert( isPositive( polySaOrg ) != isPositive( polySaDest ) );
284+
285+
const auto polySbOrg = ccwPoly( ds[4], ds[5], ds[0], 3 );
286+
const auto polySbDest = ccwPoly( ds[4], ds[5], ds[1], 3 );
287+
assert( isPositive( polySbOrg ) != isPositive( polySbDest ) );
288+
289+
auto nom = polySaOrg * polySbDest;
290+
nom -= polySbOrg * polySaDest;
291+
292+
bool res = isPositive( nom );
293+
if ( isPositive( polySaOrg ) != isPositive( polySbOrg ) ) // denominator is negative
294+
res = !res;
295+
return res;
296+
}
297+
209298
Vector2i findSegmentSegmentIntersectionPrecise(
210299
const Vector2i& ai, const Vector2i& bi, const Vector2i& ci, const Vector2i& di )
211300
{

source/MRMesh/MRPrecisePredicates2.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,19 @@ struct CoordinateConverters2
7676
ConvertToFloatVector2 toFloat{};
7777
};
7878

79+
/// given line segment s=01 and two other segments sa=23, sb=45 known to intersect it, finds the order of intersection using precise predicates:
80+
/// true: s[0], s ^ sa, s ^ sb, s[1]
81+
/// false: s[0], s ^ sb, s ^ sa, s[1]
82+
[[nodiscard]] MRMESH_API bool segmentIntersectionOrder( const std::array<PreciseVertCoords2, 6> & vs );
83+
7984
/// finds intersection precise, using high precision int inside
8085
/// this function input should have intersection
8186
[[nodiscard]] MRMESH_API Vector2i findSegmentSegmentIntersectionPrecise(
8287
const Vector2i& a, const Vector2i& b, const Vector2i& c, const Vector2i& d );
8388

8489
/// finds intersection precise, using high precision int inside
8590
/// this function input should have intersection
86-
[[nodiscard]] MRMESH_API Vector2f findSegmentSegmentIntersectionPrecise(
91+
[[nodiscard]] MRMESH_API Vector2f findSegmentSegmentIntersectionPrecise(
8792
const Vector2f& a, const Vector2f& b, const Vector2f& c, const Vector2f& d,
8893
CoordinateConverters2 converters );
8994

source/MRMesh/MRSparsePolynomial.h

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
#pragma once
2+
3+
#include <map>
4+
5+
namespace MR
6+
{
7+
8+
template <typename C, typename D>
9+
class SparsePolynomial;
10+
template <typename C, typename D>
11+
SparsePolynomial<C,D> operator *( const SparsePolynomial<C,D>& a, const SparsePolynomial<C,D>& b );
12+
13+
/// The class to store a polynomial with a large number of zero coefficient (only non-zeros are stored in std::map)
14+
/// \tparam C - type of coefficients
15+
/// \tparam D - type of degrees
16+
template <typename C, typename D = int>
17+
class SparsePolynomial
18+
{
19+
public:
20+
/// constructs zero polynomial
21+
SparsePolynomial() = default;
22+
23+
/// takes existing coefficients in ownership
24+
SparsePolynomial( std::map<D, C> && );
25+
26+
/// constructs polynomial c0 + c1*x^d1
27+
SparsePolynomial( C c0, D d1, C c1 );
28+
29+
/// constructs polynomial c0 + c1*x^d1 + c2*x^d2
30+
SparsePolynomial( C c0, D d1, C c1, D d2, C c2 );
31+
32+
/// gets read-only access to all not-zero coefficients
33+
const std::map<D, C> & get() const { return map_; }
34+
35+
SparsePolynomial& operator +=( const SparsePolynomial& b );
36+
SparsePolynomial& operator -=( const SparsePolynomial& b );
37+
friend SparsePolynomial operator *<>( const SparsePolynomial& a, const SparsePolynomial& b );
38+
39+
private:
40+
std::map<D, C> map_; // degree -> not-zero coefficient
41+
};
42+
43+
template <typename C, typename D>
44+
SparsePolynomial<C,D>::SparsePolynomial( std::map<D, C> && m ) : map_( std::move( m ) )
45+
{
46+
#ifndef NDEBUG
47+
for ( const auto & [deg, cf] : map_ )
48+
assert( cf != 0 );
49+
#endif
50+
}
51+
52+
template <typename C, typename D>
53+
SparsePolynomial<C,D>::SparsePolynomial( C c0, D d1, C c1 )
54+
{
55+
assert( c1 != 0 );
56+
assert( d1 != 0 );
57+
if ( c0 != 0 )
58+
map_[D(0)] = c0;
59+
map_[d1] = c1;
60+
}
61+
62+
template <typename C, typename D>
63+
SparsePolynomial<C,D>::SparsePolynomial( C c0, D d1, C c1, D d2, C c2 )
64+
{
65+
assert( c1 != 0 );
66+
assert( d1 != 0 );
67+
assert( c2 != 0 );
68+
assert( d2 != 0 );
69+
assert( d1 != d2 );
70+
if ( c0 != 0 )
71+
map_[D(0)] = c0;
72+
map_[d1] = c1;
73+
map_[d2] = c2;
74+
}
75+
76+
template <typename C, typename D>
77+
SparsePolynomial<C,D>& SparsePolynomial<C,D>::operator +=( const SparsePolynomial& b )
78+
{
79+
for ( const auto & [degB, cfB] : b.map_ )
80+
{
81+
assert( cfB != 0 );
82+
auto [it,inserted] = map_.insert( { degB, cfB } );
83+
if ( !inserted )
84+
{
85+
const auto sum = it->second + cfB;
86+
if ( sum != 0 )
87+
it->second = sum;
88+
else
89+
map_.erase( it );
90+
}
91+
}
92+
return * this;
93+
}
94+
95+
template <typename C, typename D>
96+
SparsePolynomial<C,D>& SparsePolynomial<C,D>::operator -=( const SparsePolynomial& b )
97+
{
98+
for ( const auto & [degB, cfB] : b.map_ )
99+
{
100+
assert( cfB != 0 );
101+
auto [it,inserted] = map_.insert( { degB, -cfB } );
102+
if ( !inserted )
103+
{
104+
const auto sum = it->second - cfB;
105+
if ( sum != 0 )
106+
it->second = sum;
107+
else
108+
map_.erase( it );
109+
}
110+
}
111+
return * this;
112+
}
113+
114+
template <typename C, typename D>
115+
SparsePolynomial<C,D> operator *( const SparsePolynomial<C,D>& a, const SparsePolynomial<C,D>& b )
116+
{
117+
std::map<D,C> resMap;
118+
for ( const auto & [degA, cfA] : a.map_ )
119+
{
120+
assert( cfA != 0 );
121+
for ( const auto & [degB, cfB] : b.map_ )
122+
{
123+
assert( cfB != 0 );
124+
const auto deg = degA + degB;
125+
const auto cf = cfA * cfB;
126+
auto [it,inserted] = resMap.insert( { deg, cf } );
127+
if ( !inserted )
128+
{
129+
const auto sum = it->second + cf;
130+
if ( sum != 0 )
131+
it->second = sum;
132+
else
133+
resMap.erase( it );
134+
}
135+
}
136+
}
137+
return resMap;
138+
}
139+
140+
} //namespace MR

source/MRTest/MRPrecisePredicatesTests.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,10 @@ TEST( MRMesh, PrecisePredicates2More )
109109

110110
// segments 03 and 12 intersect one with another
111111
EXPECT_TRUE( doSegmentSegmentIntersect( { vs[0], vs[3], vs[1], vs[2] } ).doIntersect );
112+
113+
// intersection of 45 and 03 is closer to 4 than intersection of 45 and 12
114+
EXPECT_TRUE( segmentIntersectionOrder( { vs[4], vs[5], vs[0], vs[3], vs[1], vs[2] } ) );
115+
EXPECT_FALSE( segmentIntersectionOrder( { vs[5], vs[4], vs[0], vs[3], vs[1], vs[2] } ) );
112116
}
113117

114118
TEST( MRMesh, PrecisePredicates2FullDegen )
@@ -192,6 +196,33 @@ TEST( MRMesh, PrecisePredicates2InCircle2 )
192196
EXPECT_FALSE( inCircle( { vs[1],vs[4],vs[2],vs[3] } ) );
193197
}
194198

199+
TEST( MRMesh, PreciseSegmentIntersectionOrder2 )
200+
{
201+
PreciseVertCoords2 vs[6] =
202+
{
203+
// s:
204+
PreciseVertCoords2{ 0_v, Vector2i( 0, 0 ) },
205+
PreciseVertCoords2{ 1_v, Vector2i( 3, 0 ) },
206+
// sa:
207+
PreciseVertCoords2{ 2_v, Vector2i( 1,-1 ) },
208+
PreciseVertCoords2{ 3_v, Vector2i( 1, 1 ) },
209+
// sb:
210+
PreciseVertCoords2{ 5_v, Vector2i( 2,-1 ) },
211+
PreciseVertCoords2{ 6_v, Vector2i( 2, 1 ) }
212+
};
213+
214+
EXPECT_TRUE( segmentIntersectionOrder( { vs[0], vs[1], vs[2], vs[3], vs[4], vs[5] } ) );
215+
EXPECT_TRUE( segmentIntersectionOrder( { vs[0], vs[1], vs[3], vs[2], vs[4], vs[5] } ) );
216+
EXPECT_TRUE( segmentIntersectionOrder( { vs[0], vs[1], vs[3], vs[2], vs[5], vs[4] } ) );
217+
EXPECT_FALSE( segmentIntersectionOrder( { vs[1], vs[0], vs[3], vs[2], vs[5], vs[4] } ) );
218+
219+
// swapped sa and sb
220+
EXPECT_FALSE( segmentIntersectionOrder( { vs[0], vs[1], vs[4], vs[5], vs[2], vs[3] } ) );
221+
EXPECT_FALSE( segmentIntersectionOrder( { vs[0], vs[1], vs[4], vs[5], vs[3], vs[2] } ) );
222+
EXPECT_FALSE( segmentIntersectionOrder( { vs[0], vs[1], vs[5], vs[4], vs[3], vs[2] } ) );
223+
EXPECT_TRUE( segmentIntersectionOrder( { vs[1], vs[0], vs[5], vs[4], vs[3], vs[2] } ) );
224+
}
225+
195226
TEST( MRMesh, PrecisePredicates3 )
196227
{
197228
const std::array<PreciseVertCoords, 5> vs =

0 commit comments

Comments
 (0)