Skip to content

Commit

Permalink
Add class for comparing distance from two line segments to an origin
Browse files Browse the repository at this point in the history
  • Loading branch information
nyalldawson committed Apr 23, 2018
1 parent 6e6ddbb commit fa9f62f
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 0 deletions.
58 changes: 58 additions & 0 deletions src/core/geometry/qgsinternalgeometryengine.cpp
Expand Up @@ -711,6 +711,64 @@ QgsGeometry QgsInternalGeometryEngine::densifyByDistance( double distance ) cons
}
}

//
// QgsLineSegmentDistanceComparer
//

// adapted for QGIS geometry classes from original work at https://github.com/trylock/visibility by trylock
bool QgsLineSegmentDistanceComparer::operator()( QgsLineSegment2D ab, QgsLineSegment2D cd ) const
{
Q_ASSERT_X( ab.pointLeftOfLine( mOrigin ) != 0,
"line_segment_dist_comparer",
"AB must not be collinear with the origin." );
Q_ASSERT_X( cd.pointLeftOfLine( mOrigin ) != 0,
"line_segment_dist_comparer",
"CD must not be collinear with the origin." );

// flip the segments so that if there are common endpoints,
// they will be the segment's start points
if ( ab.end() == cd.start() || ab.end() == cd.end() )
ab.reverse();
if ( ab.start() == cd.end() )
cd.reverse();

// cases with common endpoints
if ( ab.start() == cd.start() )
{
const int oad = QgsGeometryUtils::leftOfLine( cd.endX(), cd.endY(), mOrigin.x(), mOrigin.y(), ab.startX(), ab.startY() );
const int oab = ab.pointLeftOfLine( mOrigin );
if ( ab.end() == cd.end() || oad != oab )
return false;
else
return ab.pointLeftOfLine( cd.end() ) != oab;
}
else
{
// cases without common endpoints
const int cda = cd.pointLeftOfLine( ab.start() );
const int cdb = cd.pointLeftOfLine( ab.end() );
if ( cdb == 0 && cda == 0 )
{
return mOrigin.sqrDist( ab.start() ) < mOrigin.sqrDist( cd.start() );
}
else if ( cda == cdb || cda == 0 || cdb == 0 )
{
const int cdo = cd.pointLeftOfLine( mOrigin );
return cdo == cda || cdo == cdb;
}
else
{
const int abo = ab.pointLeftOfLine( mOrigin );
return abo != ab.pointLeftOfLine( cd.start() );
}
}
}


//
// QgsRay2D
//

bool QgsRay2D::intersects( const QgsLineSegment2D &segment, QgsPointXY &intersectPoint ) const
{
const QgsVector ao = origin - segment.start();
Expand Down
40 changes: 40 additions & 0 deletions src/core/geometry/qgsinternalgeometryengine.h
Expand Up @@ -110,6 +110,7 @@ class QgsInternalGeometryEngine
* A 2D ray which extends from an origin point to an infinite distance in a given direction.
* \ingroup core
* \since QGIS 3.2
* \note not available in Python bindings
*/
class CORE_EXPORT QgsRay2D
{
Expand Down Expand Up @@ -139,4 +140,43 @@ class CORE_EXPORT QgsRay2D
QgsVector direction;
};

///@cond PRIVATE

// adapted for QGIS geometry classes from original work at https://github.com/trylock/visibility by trylock

/**
* Compares two line segments based on their distance from a given point
* Assumes: (1) the line segments are intersected by some ray from the origin
* (2) the line segments do not intersect except at their endpoints
* (3) no line segment is collinear with the origin
*/
class CORE_EXPORT QgsLineSegmentDistanceComparer
{
public:

/**
* Constructor for QgsLineSegmentDistanceComparer, comparing points
* to the specified \a origin point.
*/
explicit QgsLineSegmentDistanceComparer( const QgsPointXY &origin )
: mOrigin( origin )
{}

/**
* Checks whether the line segment \a ab is closer to the origin than the
* line segment \a cd.
* \param ab line segment: left hand side of the comparison operator
* \param cd line segment: right hand side of the comparison operator
* \returns true if ab < cd (ab is closer than cd) to origin
*/
bool operator()( QgsLineSegment2D ab, QgsLineSegment2D cd ) const;

private:

QgsPointXY mOrigin;

};

///@endcond PRIVATE

#endif // QGSINTERNALGEOMETRYENGINE_H
29 changes: 29 additions & 0 deletions tests/src/core/testqgsinternalgeometryengine.cpp
Expand Up @@ -30,6 +30,7 @@ class TestQgsInternalGeometryEngine : public QObject
void init();// will be called before each testfunction is executed.
void cleanup();// will be called after every testfunction.
void ray();
void lineSegmentDistanceComparer();

};

Expand Down Expand Up @@ -95,5 +96,33 @@ void TestQgsInternalGeometryEngine::ray()

}

void TestQgsInternalGeometryEngine::lineSegmentDistanceComparer()
{
QgsLineSegmentDistanceComparer comp( QgsPointXY( 3, 5 ) );

QVERIFY( comp( QgsLineSegment2D( QgsPointXY( 1, 2 ), QgsPointXY( 3, 4 ) ),
QgsLineSegment2D( QgsPointXY( 11, 2 ), QgsPointXY( 13, 4 ) ) ) );
QVERIFY( comp( QgsLineSegment2D( QgsPointXY( 1, 2 ), QgsPointXY( 3, 4 ) ),
QgsLineSegment2D( QgsPointXY( 13, 4 ), QgsPointXY( 11, 2 ) ) ) );
QVERIFY( comp( QgsLineSegment2D( QgsPointXY( 1, 2 ), QgsPointXY( 3, 4 ) ),
QgsLineSegment2D( QgsPointXY( -13, 4 ), QgsPointXY( -11, 2 ) ) ) );
QVERIFY( comp( QgsLineSegment2D( QgsPointXY( 1, 2 ), QgsPointXY( 3, 4 ) ),
QgsLineSegment2D( QgsPointXY( -13, -4 ), QgsPointXY( -11, 2 ) ) ) );
QVERIFY( comp( QgsLineSegment2D( QgsPointXY( 1, 2 ), QgsPointXY( 3, 4 ) ),
QgsLineSegment2D( QgsPointXY( 11, 2 ), QgsPointXY( 13, 4 ) ) ) );
QVERIFY( !comp( QgsLineSegment2D( QgsPointXY( 11, 2 ), QgsPointXY( 13, 4 ) ),
QgsLineSegment2D( QgsPointXY( 1, 2 ), QgsPointXY( 3, 4 ) ) ) );
QVERIFY( !comp( QgsLineSegment2D( QgsPointXY( 1, 2 ), QgsPointXY( 3, 4 ) ),
QgsLineSegment2D( QgsPointXY( 5, 6 ), QgsPointXY( 8, 9 ) ) ) );
QVERIFY( comp( QgsLineSegment2D( QgsPointXY( 5, 6 ), QgsPointXY( 8, 9 ) ),
QgsLineSegment2D( QgsPointXY( 1, 2 ), QgsPointXY( 3, 4 ) ) ) );
QVERIFY( !comp( QgsLineSegment2D( QgsPointXY( 5, 6 ), QgsPointXY( 8, 9 ) ),
QgsLineSegment2D( QgsPointXY( 8, 9 ), QgsPointXY( 13, 14 ) ) ) );
QVERIFY( !comp( QgsLineSegment2D( QgsPointXY( 8, 9 ), QgsPointXY( 13, 14 ) ),
QgsLineSegment2D( QgsPointXY( 5, 6 ), QgsPointXY( 8, 9 ) ) ) );
QVERIFY( comp( QgsLineSegment2D( QgsPointXY( 1, 4 ), QgsPointXY( 8, 9 ) ),
QgsLineSegment2D( QgsPointXY( 8, 9 ), QgsPointXY( 15, 16 ) ) ) );
}

QGSTEST_MAIN( TestQgsInternalGeometryEngine )
#include "testqgsinternalgeometryengine.moc"

0 comments on commit fa9f62f

Please sign in to comment.