Skip to content

Commit

Permalink
move QgsCadUtils::lineCircleIntersection to QgsGeometryUtils
Browse files Browse the repository at this point in the history
also add a test
  • Loading branch information
3nids committed Jan 24, 2018
1 parent 3b0afb2 commit 6a2a626
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 54 deletions.
13 changes: 13 additions & 0 deletions python/core/geometry/qgsgeometryutils.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,19 @@ Returns the squared distance between a point and a line.
# (True, 'Point (0 0)', True)
%End

static bool lineCircleIntersection( const QgsPointXY &center, const double radius,
const QgsPointXY &linePoint1, const QgsPointXY &linePoint2,
QgsPointXY &intersection /In,Out/ );
%Docstring
lineCircleIntersection returns the closest intersection of a line and a circle
@param center the center of the circle
@param radius the radius of the circle
@param linePoint1 a first point on the line
@param linePoint2 a second point on the line
@param intersection the initial point and the returned intersection point
@return true if an intersection has been found
%End

static QgsPoint projPointOnSegment( const QgsPoint &p, const QgsPoint &s1, const QgsPoint &s2 );
%Docstring
Project the point on a segment
Expand Down
50 changes: 50 additions & 0 deletions src/core/geometry/qgsgeometryutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,56 @@ bool QgsGeometryUtils::segmentIntersection( const QgsPoint &p1, const QgsPoint &
return !( lambdaw < 0. + tolerance || lambdaw >= wl - tolerance );
}

bool QgsGeometryUtils::lineCircleIntersection( const QgsPointXY &center, const double radius,
const QgsPointXY &linePoint1, const QgsPointXY &linePoint2,
QgsPointXY &intersection )
{
// formula taken from http://mathworld.wolfram.com/Circle-LineIntersection.html

const double x1 = linePoint1.x() - center.x();
const double y1 = linePoint1.y() - center.y();
const double x2 = linePoint2.x() - center.x();
const double y2 = linePoint2.y() - center.y();
const double dx = x2 - x1;
const double dy = y2 - y1;

const double dr = std::sqrt( std::pow( dx, 2 ) + std::pow( dy, 2 ) );
const double d = x1 * y2 - x2 * y1;

const double disc = std::pow( radius, 2 ) * std::pow( dr, 2 ) - std::pow( d, 2 );

if ( disc < 0 )
{
//no intersection or tangent
return false;
}
else
{
// two solutions
const int sgnDy = dy < 0 ? -1 : 1;

const double ax = center.x() + ( d * dy + sgnDy * dx * std::sqrt( std::pow( radius, 2 ) * std::pow( dr, 2 ) - std::pow( d, 2 ) ) ) / ( std::pow( dr, 2 ) );
const double ay = center.y() + ( -d * dx + std::fabs( dy ) * std::sqrt( std::pow( radius, 2 ) * std::pow( dr, 2 ) - std::pow( d, 2 ) ) ) / ( std::pow( dr, 2 ) );
const QgsPointXY p1( ax, ay );

const double bx = center.x() + ( d * dy - sgnDy * dx * std::sqrt( std::pow( radius, 2 ) * std::pow( dr, 2 ) - std::pow( d, 2 ) ) ) / ( std::pow( dr, 2 ) );
const double by = center.y() + ( -d * dx - std::fabs( dy ) * std::sqrt( std::pow( radius, 2 ) * std::pow( dr, 2 ) - std::pow( d, 2 ) ) ) / ( std::pow( dr, 2 ) );
const QgsPointXY p2( bx, by );

// snap to nearest intersection

if ( intersection.sqrDist( p1 ) < intersection.sqrDist( p2 ) )
{
intersection.set( p1.x(), p1.y() );
}
else
{
intersection.set( p2.x(), p2.y() );
}
return true;
}
}

QVector<QgsGeometryUtils::SelfIntersection> QgsGeometryUtils::getSelfIntersections( const QgsAbstractGeometry *geom, int part, int ring, double tolerance )
{
QVector<SelfIntersection> intersections;
Expand Down
13 changes: 13 additions & 0 deletions src/core/geometry/qgsgeometryutils.h
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,19 @@ class CORE_EXPORT QgsGeometryUtils
*/
static bool segmentIntersection( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &q1, const QgsPoint &q2, QgsPoint &intersectionPoint SIP_OUT, bool &isIntersection SIP_OUT, const double tolerance = 1e-8, bool acceptImproperIntersection = false );

/**
* @brief lineCircleIntersection returns the closest intersection of a line and a circle
* @param center the center of the circle
* @param radius the radius of the circle
* @param linePoint1 a first point on the line
* @param linePoint2 a second point on the line
* @param intersection the initial point and the returned intersection point
* @return true if an intersection has been found
*/
static bool lineCircleIntersection( const QgsPointXY &center, const double radius,
const QgsPointXY &linePoint1, const QgsPointXY &linePoint2,
QgsPointXY &intersection SIP_INOUT );

/**
* \brief Project the point on a segment
* \param p The point
Expand Down
58 changes: 4 additions & 54 deletions src/core/qgscadutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

#include "qgslogger.h"
#include "qgssnappingutils.h"
#include "qgsgeometryutils.h"

// tolerances for soft constraints (last values, and common angles)
// for angles, both tolerance in pixels and degrees are used for better performance
Expand All @@ -33,57 +34,6 @@ struct EdgesOnlyFilter : public QgsPointLocator::MatchFilter
/// @endcond


// TODO: move to geometry utils (if not already there)
static bool lineCircleIntersection( const QgsPointXY &center, const double radius, const QgsPointXY &edgePt0, const QgsPointXY &edgePt1, QgsPointXY &intersection )
{
// formula taken from http://mathworld.wolfram.com/Circle-LineIntersection.html

const double x1 = edgePt0.x() - center.x();
const double y1 = edgePt0.y() - center.y();
const double x2 = edgePt1.x() - center.x();
const double y2 = edgePt1.y() - center.y();
const double dx = x2 - x1;
const double dy = y2 - y1;

const double dr = std::sqrt( std::pow( dx, 2 ) + std::pow( dy, 2 ) );
const double d = x1 * y2 - x2 * y1;

const double disc = std::pow( radius, 2 ) * std::pow( dr, 2 ) - std::pow( d, 2 );

if ( disc < 0 )
{
//no intersection or tangent
return false;
}
else
{
// two solutions
const int sgnDy = dy < 0 ? -1 : 1;

const double ax = center.x() + ( d * dy + sgnDy * dx * std::sqrt( std::pow( radius, 2 ) * std::pow( dr, 2 ) - std::pow( d, 2 ) ) ) / ( std::pow( dr, 2 ) );
const double ay = center.y() + ( -d * dx + std::fabs( dy ) * std::sqrt( std::pow( radius, 2 ) * std::pow( dr, 2 ) - std::pow( d, 2 ) ) ) / ( std::pow( dr, 2 ) );
const QgsPointXY p1( ax, ay );

const double bx = center.x() + ( d * dy - sgnDy * dx * std::sqrt( std::pow( radius, 2 ) * std::pow( dr, 2 ) - std::pow( d, 2 ) ) ) / ( std::pow( dr, 2 ) );
const double by = center.y() + ( -d * dx - std::fabs( dy ) * std::sqrt( std::pow( radius, 2 ) * std::pow( dr, 2 ) - std::pow( d, 2 ) ) ) / ( std::pow( dr, 2 ) );
const QgsPointXY p2( bx, by );

// snap to nearest intersection

if ( intersection.sqrDist( p1 ) < intersection.sqrDist( p2 ) )
{
intersection.set( p1.x(), p1.y() );
}
else
{
intersection.set( p2.x(), p2.y() );
}
return true;
}
}



QgsCadUtils::AlignMapPointOutput QgsCadUtils::alignMapPoint( const QgsPointXY &originalMapPoint, const QgsCadUtils::AlignMapPointContext &ctx )
{
QgsCadUtils::AlignMapPointOutput res;
Expand Down Expand Up @@ -312,13 +262,13 @@ QgsCadUtils::AlignMapPointOutput QgsCadUtils::alignMapPoint( const QgsPointXY &o
{
QgsPointXY verticalPt0( ctx.xConstraint.value, point.y() );
QgsPointXY verticalPt1( ctx.xConstraint.value, point.y() + 1 );
res.valid &= lineCircleIntersection( previousPt, ctx.distanceConstraint.value, verticalPt0, verticalPt1, point );
res.valid &= QgsGeometryUtils::lineCircleIntersection( previousPt, ctx.distanceConstraint.value, verticalPt0, verticalPt1, point );
}
if ( ctx.yConstraint.locked )
{
QgsPointXY horizontalPt0( point.x(), ctx.yConstraint.value );
QgsPointXY horizontalPt1( point.x() + 1, ctx.yConstraint.value );
res.valid &= lineCircleIntersection( previousPt, ctx.distanceConstraint.value, horizontalPt0, horizontalPt1, point );
res.valid &= QgsGeometryUtils::lineCircleIntersection( previousPt, ctx.distanceConstraint.value, horizontalPt0, horizontalPt1, point );
}
}
else
Expand All @@ -340,7 +290,7 @@ QgsCadUtils::AlignMapPointOutput QgsCadUtils::alignMapPoint( const QgsPointXY &o
if ( edgeMatch.hasEdge() && !ctx.angleConstraint.locked )
{
// we will magnietize to the intersection of that segment and the lockedDistance !
res.valid &= lineCircleIntersection( previousPt, ctx.distanceConstraint.value, edgePt0, edgePt1, point );
res.valid &= QgsGeometryUtils::lineCircleIntersection( previousPt, ctx.distanceConstraint.value, edgePt0, edgePt1, point );
}
}
}
Expand Down
18 changes: 18 additions & 0 deletions tests/src/core/testqgsgeometryutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ class TestQgsGeometryUtils: public QObject
void testPerpendicularSegment();
void testClosestPoint();
void testSegmentIntersection();
void testLineCircleIntersection();
};


Expand Down Expand Up @@ -797,5 +798,22 @@ void TestQgsGeometryUtils::testSegmentIntersection()
QVERIFY( inter == QgsPoint( 0, 0 ) );
}

void TestQgsGeometryUtils::testLineCircleIntersection()
{
QgsPointXY center = QgsPoint( 2, 2 );
double radius = 2.0;
QgsPointXY linePoint1 = QgsPoint( 0, 2 );
QgsPointXY linePoint2 = QgsPoint( 2, 2 );
QgsPointXY intersection = QgsPoint( 3, 3 );
bool isIntersection = QgsGeometryUtils::lineCircleIntersection( center, radius, linePoint1, linePoint2, intersection );
QVERIFY( isIntersection );
QVERIFY( intersection == QgsPointXY( 4, 2 ) );

linePoint1 = QgsPoint( 5, 0 );
linePoint2 = QgsPoint( 5, 2 );
isIntersection = QgsGeometryUtils::lineCircleIntersection( center, radius, linePoint1, linePoint2, intersection );
QVERIFY( !isIntersection );
}

QGSTEST_MAIN( TestQgsGeometryUtils )
#include "testqgsgeometryutils.moc"

0 comments on commit 6a2a626

Please sign in to comment.