Skip to content

Commit cd3b976

Browse files
committed
Add class for comparing angles to points in a clockwise fashion
1 parent fa9f62f commit cd3b976

File tree

3 files changed

+148
-14
lines changed

3 files changed

+148
-14
lines changed

src/core/geometry/qgsinternalgeometryengine.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -711,6 +711,7 @@ QgsGeometry QgsInternalGeometryEngine::densifyByDistance( double distance ) cons
711711
}
712712
}
713713

714+
///@cond PRIVATE
714715
//
715716
// QgsLineSegmentDistanceComparer
716717
//
@@ -764,6 +765,45 @@ bool QgsLineSegmentDistanceComparer::operator()( QgsLineSegment2D ab, QgsLineSeg
764765
}
765766
}
766767

768+
//
769+
// QgsClockwiseAngleComparer
770+
//
771+
772+
bool QgsClockwiseAngleComparer::operator()( const QgsPointXY &a, const QgsPointXY &b ) const
773+
{
774+
const bool aIsLeft = a.x() < mVertex.x();
775+
const bool bIsLeft = b.x() < mVertex.x();
776+
if ( aIsLeft != bIsLeft )
777+
return bIsLeft;
778+
779+
if ( qgsDoubleNear( a.x(), mVertex.x() ) && qgsDoubleNear( b.x(), mVertex.x() ) )
780+
{
781+
if ( a.y() >= mVertex.y() || b.y() >= mVertex.y() )
782+
{
783+
return b.y() < a.y();
784+
}
785+
else
786+
{
787+
return a.y() < b.y();
788+
}
789+
}
790+
else
791+
{
792+
const QgsVector oa = a - mVertex;
793+
const QgsVector ob = b - mVertex;
794+
const double det = oa.crossProduct( ob );
795+
if ( qgsDoubleNear( det, 0.0 ) )
796+
{
797+
return oa.lengthSquared() < ob.lengthSquared();
798+
}
799+
else
800+
{
801+
return det < 0;
802+
}
803+
}
804+
}
805+
806+
///@endcond PRIVATE
767807

768808
//
769809
// QgsRay2D

src/core/geometry/qgsinternalgeometryengine.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,8 @@ class CORE_EXPORT QgsRay2D
149149
* Assumes: (1) the line segments are intersected by some ray from the origin
150150
* (2) the line segments do not intersect except at their endpoints
151151
* (3) no line segment is collinear with the origin
152+
* \ingroup core
153+
* \since QGIS 3.2
152154
*/
153155
class CORE_EXPORT QgsLineSegmentDistanceComparer
154156
{
@@ -177,6 +179,29 @@ class CORE_EXPORT QgsLineSegmentDistanceComparer
177179

178180
};
179181

182+
183+
// adapted for QGIS geometry classes from original work at https://github.com/trylock/visibility by trylock
184+
185+
/**
186+
* Compares angles from an origin to points clockwise, starting at the positive y-axis.
187+
* \ingroup core
188+
* \since QGIS 3.2
189+
*/
190+
class CORE_EXPORT QgsClockwiseAngleComparer
191+
{
192+
public:
193+
explicit QgsClockwiseAngleComparer( const QgsPointXY &origin )
194+
: mVertex( origin )
195+
{}
196+
197+
bool operator()( const QgsPointXY &a, const QgsPointXY &b ) const;
198+
199+
private:
200+
201+
QgsPointXY mVertex;
202+
203+
};
204+
180205
///@endcond PRIVATE
181206

182207
#endif // QGSINTERNALGEOMETRYENGINE_H

tests/src/core/testqgsinternalgeometryengine.cpp

Lines changed: 83 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@ class TestQgsInternalGeometryEngine : public QObject
3030
void init();// will be called before each testfunction is executed.
3131
void cleanup();// will be called after every testfunction.
3232
void ray();
33-
void lineSegmentDistanceComparer();
33+
void testLineSegmentDistanceComparer_data();
34+
void testLineSegmentDistanceComparer();
35+
void clockwiseAngleComparer();
3436

3537
};
3638

@@ -94,24 +96,69 @@ void TestQgsInternalGeometryEngine::ray()
9496
QCOMPARE( intersect.x(), 2.0 );
9597
QCOMPARE( intersect.y(), 6.0 );
9698

99+
ray = QgsRay2D( QgsPointXY( 0, 0 ), QgsVector( 1, 0 ) );
100+
QVERIFY( !ray.intersects( QgsLineSegment2D( QgsPointXY( -1, 1 ), QgsPointXY( -1, -1 ) ), intersect ) );
101+
QVERIFY( !ray.intersects( QgsLineSegment2D( QgsPointXY( -1e-3, 1 ), QgsPointXY( -1e-3, -1 ) ), intersect ) );
102+
QVERIFY( !ray.intersects( QgsLineSegment2D( QgsPointXY( -2, 0 ), QgsPointXY( -1, 0 ) ), intersect ) );
103+
QVERIFY( ray.intersects( QgsLineSegment2D( QgsPointXY( 0, 1 ), QgsPointXY( 0, -1 ) ), intersect ) );
104+
QCOMPARE( intersect.x(), 0.0 );
105+
QCOMPARE( intersect.y(), 0.0 );
106+
QVERIFY( ray.intersects( QgsLineSegment2D( QgsPointXY( -1, 0 ), QgsPointXY( 0, 0 ) ), intersect ) );
107+
QCOMPARE( intersect.x(), 0.0 );
108+
QCOMPARE( intersect.y(), 0.0 );
109+
QVERIFY( ray.intersects( QgsLineSegment2D( QgsPointXY( 0, 0 ), QgsPointXY( -1, 0 ) ), intersect ) );
110+
QCOMPARE( intersect.x(), 0.0 );
111+
QCOMPARE( intersect.y(), 0.0 );
112+
QVERIFY( ray.intersects( QgsLineSegment2D( QgsPointXY( 2, 1 ), QgsPointXY( 2, -1 ) ), intersect ) );
113+
QCOMPARE( intersect.x(), 2.0 );
114+
QCOMPARE( intersect.y(), 0.0 );
115+
QVERIFY( ray.intersects( QgsLineSegment2D( QgsPointXY( 2, 0 ), QgsPointXY( 3, 0 ) ), intersect ) );
116+
QCOMPARE( intersect.x(), 2.0 );
117+
QCOMPARE( intersect.y(), 0.0 );
118+
QVERIFY( ray.intersects( QgsLineSegment2D( QgsPointXY( 3, 0 ), QgsPointXY( 2, 0 ) ), intersect ) );
119+
QCOMPARE( intersect.x(), 2.0 );
120+
QCOMPARE( intersect.y(), 0.0 );
121+
QVERIFY( ray.intersects( QgsLineSegment2D( QgsPointXY( 1, 0 ), QgsPointXY( 1, -1 ) ), intersect ) );
122+
QCOMPARE( intersect.x(), 1.0 );
123+
QCOMPARE( intersect.y(), 0.0 );
97124
}
98125

99-
void TestQgsInternalGeometryEngine::lineSegmentDistanceComparer()
126+
void TestQgsInternalGeometryEngine::testLineSegmentDistanceComparer_data()
100127
{
128+
QTest::addColumn<QgsPointXY>( "origin" );
129+
QTest::addColumn<QgsPointXY>( "a" );
130+
QTest::addColumn<QgsPointXY>( "b" );
131+
QTest::addColumn<QgsPointXY>( "c" );
132+
QTest::addColumn<QgsPointXY>( "d" );
133+
134+
QTest::newRow( "a" ) << QgsPointXY( 3, 5 ) << QgsPointXY( 1, 2 ) << QgsPointXY( 3, 4 ) << QgsPointXY( 11, 2 ) << QgsPointXY( 13, 4 );
135+
QTest::newRow( "b" ) << QgsPointXY( 0, 0 ) << QgsPointXY( 1, 1 ) << QgsPointXY( 1, -1 ) << QgsPointXY( 2, 1 ) << QgsPointXY( 2, -1 );
136+
QTest::newRow( "b" ) << QgsPointXY( 0, 0 ) << QgsPointXY( 1, 1 ) << QgsPointXY( 1, -1 ) << QgsPointXY( 2, 2 ) << QgsPointXY( 2, 3 );
137+
QTest::newRow( "b" ) << QgsPointXY( 0, 0 ) << QgsPointXY( 2, 0 ) << QgsPointXY( 1, 1 ) << QgsPointXY( 2, 1 ) << QgsPointXY( 2, 0 );
138+
QTest::newRow( "b" ) << QgsPointXY( 0, 0 ) << QgsPointXY( 2, 1 ) << QgsPointXY( 2, 0 ) << QgsPointXY( 2, 0 ) << QgsPointXY( 3, 1 );
139+
}
140+
141+
void TestQgsInternalGeometryEngine::testLineSegmentDistanceComparer()
142+
{
143+
QFETCH( QgsPointXY, origin );
144+
QFETCH( QgsPointXY, a );
145+
QFETCH( QgsPointXY, b );
146+
QFETCH( QgsPointXY, c );
147+
QFETCH( QgsPointXY, d );
148+
149+
QgsLineSegmentDistanceComparer cmp( origin );
150+
QVERIFY( cmp( QgsLineSegment2D( a, b ), QgsLineSegment2D( c, d ) ) );
151+
QVERIFY( cmp( QgsLineSegment2D( b, a ), QgsLineSegment2D( c, d ) ) );
152+
QVERIFY( cmp( QgsLineSegment2D( a, b ), QgsLineSegment2D( d, c ) ) );
153+
QVERIFY( cmp( QgsLineSegment2D( b, a ), QgsLineSegment2D( d, c ) ) );
154+
155+
QVERIFY( !cmp( QgsLineSegment2D( c, d ), QgsLineSegment2D( a, b ) ) );
156+
QVERIFY( !cmp( QgsLineSegment2D( d, c ), QgsLineSegment2D( a, b ) ) );
157+
QVERIFY( !cmp( QgsLineSegment2D( c, d ), QgsLineSegment2D( b, a ) ) );
158+
QVERIFY( !cmp( QgsLineSegment2D( d, c ), QgsLineSegment2D( b, a ) ) );
159+
101160
QgsLineSegmentDistanceComparer comp( QgsPointXY( 3, 5 ) );
102161

103-
QVERIFY( comp( QgsLineSegment2D( QgsPointXY( 1, 2 ), QgsPointXY( 3, 4 ) ),
104-
QgsLineSegment2D( QgsPointXY( 11, 2 ), QgsPointXY( 13, 4 ) ) ) );
105-
QVERIFY( comp( QgsLineSegment2D( QgsPointXY( 1, 2 ), QgsPointXY( 3, 4 ) ),
106-
QgsLineSegment2D( QgsPointXY( 13, 4 ), QgsPointXY( 11, 2 ) ) ) );
107-
QVERIFY( comp( QgsLineSegment2D( QgsPointXY( 1, 2 ), QgsPointXY( 3, 4 ) ),
108-
QgsLineSegment2D( QgsPointXY( -13, 4 ), QgsPointXY( -11, 2 ) ) ) );
109-
QVERIFY( comp( QgsLineSegment2D( QgsPointXY( 1, 2 ), QgsPointXY( 3, 4 ) ),
110-
QgsLineSegment2D( QgsPointXY( -13, -4 ), QgsPointXY( -11, 2 ) ) ) );
111-
QVERIFY( comp( QgsLineSegment2D( QgsPointXY( 1, 2 ), QgsPointXY( 3, 4 ) ),
112-
QgsLineSegment2D( QgsPointXY( 11, 2 ), QgsPointXY( 13, 4 ) ) ) );
113-
QVERIFY( !comp( QgsLineSegment2D( QgsPointXY( 11, 2 ), QgsPointXY( 13, 4 ) ),
114-
QgsLineSegment2D( QgsPointXY( 1, 2 ), QgsPointXY( 3, 4 ) ) ) );
115162
QVERIFY( !comp( QgsLineSegment2D( QgsPointXY( 1, 2 ), QgsPointXY( 3, 4 ) ),
116163
QgsLineSegment2D( QgsPointXY( 5, 6 ), QgsPointXY( 8, 9 ) ) ) );
117164
QVERIFY( comp( QgsLineSegment2D( QgsPointXY( 5, 6 ), QgsPointXY( 8, 9 ) ),
@@ -124,5 +171,27 @@ void TestQgsInternalGeometryEngine::lineSegmentDistanceComparer()
124171
QgsLineSegment2D( QgsPointXY( 8, 9 ), QgsPointXY( 15, 16 ) ) ) );
125172
}
126173

174+
void TestQgsInternalGeometryEngine::clockwiseAngleComparer()
175+
{
176+
QgsClockwiseAngleComparer cmp( QgsPointXY( 0, 0 ) );
177+
QVERIFY( cmp( QgsPointXY( 0, 1 ), QgsPointXY( 1, 1 ) ) );
178+
QVERIFY( !cmp( QgsPointXY( 1, 1 ), QgsPointXY( 0, 1 ) ) );
179+
180+
QVERIFY( cmp( QgsPointXY( 1, 1 ), QgsPointXY( 1, -1 ) ) );
181+
QVERIFY( !cmp( QgsPointXY( 1, -1 ), QgsPointXY( 1, 1 ) ) );
182+
183+
QVERIFY( cmp( QgsPointXY( 1, 0 ), QgsPointXY( -1, -1 ) ) );
184+
QVERIFY( !cmp( QgsPointXY( -1, -1 ), QgsPointXY( 1, 0 ) ) );
185+
186+
QVERIFY( cmp( QgsPointXY( 0, 1 ), QgsPointXY( 0, -1 ) ) );
187+
QVERIFY( !cmp( QgsPointXY( 0, -1 ), QgsPointXY( 0, 1 ) ) );
188+
189+
QVERIFY( cmp( QgsPointXY( 1, 0 ), QgsPointXY( 2, 0 ) ) );
190+
QVERIFY( !cmp( QgsPointXY( 2, 0 ), QgsPointXY( 1, 0 ) ) );
191+
192+
QVERIFY( !cmp( QgsPointXY( 1, 0 ), QgsPointXY( 1, 0 ) ) );
193+
QVERIFY( !cmp( QgsPointXY( 0, 0 ), QgsPointXY( 0, 0 ) ) );
194+
}
195+
127196
QGSTEST_MAIN( TestQgsInternalGeometryEngine )
128197
#include "testqgsinternalgeometryengine.moc"

0 commit comments

Comments
 (0)