18 auto orthogonal = segment.direction().
orthogonal();
19 auto line =
Line(point,point+orthogonal);
20 auto intersection = segment.intersection(line);
22 return intersection.value();
23 return segment.p().distance(point) < segment.q().distance(point)? segment.p():segment.q();
35 std::array<std::pair<Vec2DReal,Vec2DReal>,4> points {{
41 return *std::ranges::min_element(points,[](
const auto & left,
const auto & right){
42 const auto & [lFrom,lTo] = left;
43 const auto & [rFrom,rTo] = right;
44 return lFrom.distance(lTo) < rFrom.distance(rTo);
50class ClosestPointsResult;
56class PolygonReference{
58 friend class ClosestPointsResult;
64 PolygonReference():type(Type::NONE){}
65 PolygonReference(Type type):type(type){}
67 constexpr bool inline isNone() const noexcept {
68 return this->type==Type::NONE;
71 static inline PolygonReference THIS() {
72 return PolygonReference(Type::THIS);
75 static PolygonReference OTHER() {
76 return PolygonReference(Type::OTHER);
79 constexpr bool inline operator<(
const PolygonReference & other)
const noexcept {
80 return type < other.type;
83 constexpr bool inline operator==(
const PolygonReference & other)
const noexcept {
84 return type == other.type;
87 PolygonReference other() const noexcept {
88 if(type == Type::THIS)
89 return PolygonReference::OTHER();
91 return PolygonReference::THIS();
102template<
bool isXOrdered>
104 constexpr static LexicographicOrder xOrdering {};
105 constexpr static YLexicographicOrder yOrdering{};
106 Segment<fishnet::math::DEFAULT_FLOATING_POINT> segment;
107 PolygonReference polygonRef;
109 PolygonPoint(Segment<fishnet::math::DEFAULT_FLOATING_POINT> && segment, PolygonReference polygonReference)
110 :segment(
std::move(segment)),polygonRef(polygonReference){}
112 constexpr const auto & getPoint() const noexcept {
116 constexpr const auto & getSegment() const noexcept {
120 constexpr bool inline operator<(
const PolygonPoint& other )
const noexcept {
121 if(polygonRef==other.polygonRef){
122 if constexpr(isXOrdered)
123 return xOrdering(this->getPoint(),other.getPoint());
125 return yOrdering(this->getPoint(),other.getPoint());
127 return polygonRef < other.polygonRef;
137class ClosestPointsResult{
139 bool foundPointOfOtherPolygon =
false;
140 PolygonReference polyRef;
144 size_t thisCounter = 0;
145 size_t otherCounter = 0;
148 ClosestPointsResult(
const Vec2DReal & thisInitial,
const Vec2DReal & otherInitial,
size_t thisCounter,
size_t otherCounter)
149 :thisPoint(thisInitial),otherPoint(otherInitial),thisCounter(thisCounter),otherCounter(otherCounter),minDistance(thisInitial.distance(otherInitial)){}
151 bool startSearch(PolygonReference polygonRef){
152 if(foundPointOfOtherPolygon){
155 [[unlikely]]
if(polyRef.isNone()){
156 polyRef = polygonRef;
158 if(polyRef != polygonRef){
159 foundPointOfOtherPolygon =
true;
161 return foundPointOfOtherPolygon;
171 void inline visit(
const PolygonPoint<O> & point)
noexcept {
172 if(point.polygonRef == PolygonReference::THIS())
186 const Vec2DReal & getNeighbour(
const PolygonPoint<O> & point)
const noexcept {
187 if(point.polygonRef == PolygonReference::THIS()){
194template<
bool isXOrdered>
195using PolygonPointSweepLine = SweepLine<PolygonPoint<isXOrdered>,ClosestPointsResult,std::less<PolygonPoint<isXOrdered>>,
false,std::less<fishnet::math::DEFAULT_NUMERIC>>;
197template<
bool isXOrdered>
198struct PolygonSegmentSweepEvent :
public PolygonPointSweepLine<isXOrdered>::InsertEvent {
200 PolygonSegmentSweepEvent(
const PolygonPoint<isXOrdered> & point):PolygonPointSweepLine<isXOrdered>::
InsertEvent(point){}
203 if constexpr(isXOrdered)
204 return this->
obj->getPoint().x;
206 return this->
obj->getPoint().y;
217 bool inline stop(PolygonPointSweepLine<isXOrdered> & sweepLine, ClosestPointsResult & status)
const {
218 if constexpr(isXOrdered)
219 return (status.thisCounter==0 || status.otherCounter==0) && this->
obj->segment.leftEndpoint().x - status.minDistance > status.getNeighbour(*this->
obj).x;
221 return (status.thisCounter==0 || status.otherCounter==0) && this->
obj->segment.lowerEndpoint().x - status.minDistance > status.getNeighbour(*this->
obj).x;
230 PolygonPoint<isXOrdered> lowerBoundQuery(ClosestPointsResult & status)
const noexcept {
231 if constexpr(isXOrdered)
232 return PolygonPoint<isXOrdered>(Segment<fishnet::math::DEFAULT_FLOATING_POINT>(this->
obj->segment.leftEndpoint() -
Vec2DReal(status.minDistance,0),this->obj->getPoint()),this->
obj->polygonRef.other());
234 return PolygonPoint<isXOrdered>(Segment<fishnet::math::DEFAULT_FLOATING_POINT>(this->
obj->segment.lowerEndpoint() -
Vec2DReal(0,status.minDistance),this->obj->getPoint()),this->obj->polygonRef.other());
243 PolygonPoint<isXOrdered> upperBoundQuery(ClosestPointsResult & status)
const noexcept {
244 if constexpr(isXOrdered)
245 return PolygonPoint<isXOrdered>({this->
obj->segment.rightEndpoint() +
Vec2DReal(status.minDistance,0),this->
obj->getPoint()},this->
obj->polygonRef.other());
247 return PolygonPoint<isXOrdered>({this->
obj->segment.upperEndpoint() +
Vec2DReal(0,status.minDistance),this->obj->getPoint()},this->
obj->polygonRef.other());
250 virtual void process(PolygonPointSweepLine<isXOrdered> & sweepLine, ClosestPointsResult & status)
const {
251 const auto & polygonPoint = *this->
obj;
252 const auto & point = polygonPoint.getPoint();
253 const auto & segment = polygonPoint.getSegment();
254 sweepLine.addSLS(this->
obj);
255 status.visit(polygonPoint);
256 if(stop(sweepLine,status)){
258 while(sweepLine.getEventQueue().size() > 1)
259 sweepLine.getEventQueue().pop();
262 if(not status.startSearch(polygonPoint.polygonRef))
264 auto leftQuery = lowerBoundQuery(status);
265 auto rightQuery = upperBoundQuery(status);
266 auto lowerBound = sweepLine.lowerBound(leftQuery);
267 auto upperBound = sweepLine.upperBound(rightQuery);
268 auto & sweepLineStatus = sweepLine.getSLS();
269 for(
auto it= lowerBound; it != upperBound;){
270 const auto & currentSegment = (*it)->getSegment();
272 if constexpr(isXOrdered){
273 if(currentSegment.rightEndpoint().x + status.minDistance < point.x){
274 it = sweepLineStatus.erase(it);
278 if(currentSegment.upperEndpoint().y + status.minDistance < point.y){
279 it = sweepLineStatus.erase(it);
284 auto distance = l.distance(r);
285 if(distance < status.minDistance){
286 status.minDistance = distance;
288 if(polygonPoint.polygonRef == PolygonReference::THIS()){
289 status.thisPoint = l;
290 status.otherPoint = r;
292 status.thisPoint = r;
293 status.otherPoint = l;
310static std::pair<Vec2DReal,Vec2DReal> closestPointsSweep(SegmentRange
auto && lhs, SegmentRange
auto && rhs)
noexcept {
311 auto lInit =
Vec2DReal((*std::ranges::begin(lhs)).p());
312 auto rInit =
Vec2DReal((*std::ranges::begin(rhs)).p());
313 PolygonPointSweepLine<xSweep> sweepLine;
316 std::vector<PolygonPoint<xSweep>> segments;
317 std::ranges::for_each(lhs,[&segments,&status](
const auto & segmentLeft){
318 if constexpr(xSweep){
319 segments.push_back(PolygonPoint<xSweep>(Segment<fishnet::math::DEFAULT_FLOATING_POINT>(
Vec2DReal(segmentLeft.leftEndpoint()),
Vec2DReal(segmentLeft.rightEndpoint())),PolygonReference::THIS()));
320 segments.push_back(PolygonPoint<xSweep>(Segment<fishnet::math::DEFAULT_FLOATING_POINT>(
Vec2DReal(segmentLeft.rightEndpoint()),
Vec2DReal(segmentLeft.leftEndpoint())),PolygonReference::THIS()));
322 segments.push_back(PolygonPoint<xSweep>(Segment<fishnet::math::DEFAULT_FLOATING_POINT>(
Vec2DReal(segmentLeft.lowerEndpoint()),
Vec2DReal(segmentLeft.upperEndpoint())),PolygonReference::THIS()));
323 segments.push_back(PolygonPoint<xSweep>(Segment<fishnet::math::DEFAULT_FLOATING_POINT>(
Vec2DReal(segmentLeft.upperEndpoint()),
Vec2DReal(segmentLeft.lowerEndpoint())),PolygonReference::THIS()));
325 status.thisCounter+=2;
327 std::ranges::for_each(rhs,[&segments,&status](
const auto & segmentRight){
328 if constexpr(xSweep){
329 segments.push_back(PolygonPoint<xSweep>(Segment<fishnet::math::DEFAULT_FLOATING_POINT>(
Vec2DReal(segmentRight.leftEndpoint()),
Vec2DReal(segmentRight.rightEndpoint())),PolygonReference::OTHER()));
330 segments.push_back(PolygonPoint<xSweep>(Segment<fishnet::math::DEFAULT_FLOATING_POINT>(
Vec2DReal(segmentRight.rightEndpoint()),
Vec2DReal(segmentRight.leftEndpoint())),PolygonReference::OTHER()));
332 segments.push_back(PolygonPoint<xSweep>(Segment<fishnet::math::DEFAULT_FLOATING_POINT>(
Vec2DReal(segmentRight.lowerEndpoint()),
Vec2DReal(segmentRight.upperEndpoint())),PolygonReference::OTHER()));
333 segments.push_back(PolygonPoint<xSweep>(Segment<fishnet::math::DEFAULT_FLOATING_POINT>(
Vec2DReal(segmentRight.upperEndpoint()),
Vec2DReal(segmentRight.lowerEndpoint())),PolygonReference::OTHER()));
335 status.otherCounter+=2;
337 std::ranges::for_each(segments,[&sweepLine](
const auto & segment){
338 sweepLine.addEvent(std::make_unique<PolygonSegmentSweepEvent<xSweep>>(segment));
340 auto result = sweepLine.sweep(status);
341 return {result.thisPoint,result.otherPoint};
345static std::pair<Vec2DReal,Vec2DReal> closestPointsSweep(SegmentRange
auto && lhs, SegmentRange
auto && rhs)
noexcept {
346 auto lInit =
Vec2DReal((*std::ranges::begin(lhs)).p());
347 auto rInit =
Vec2DReal((*std::ranges::begin(rhs)).p());
348 auto dirVector = rInit - lInit;
349 if(fabs(dirVector.x) > fabs(dirVector.y)){
350 return closestPointsSweep<true>(lhs,rhs);
352 return closestPointsSweep<false>(lhs,rhs);
363static std::pair<Vec2DReal,Vec2DReal> closestPointsBruteForce(SegmentRange
auto && lhs,SegmentRange
auto && rhs)
noexcept {
367 auto leftPoints = lhs | std::views::transform([](
const auto & s){
return s.p();});
368 auto rightPoints = rhs | std::views::transform([](
const auto & s){
return s.p();});
369 for(
const auto & s : lhs){
370 for(
const auto & p : rightPoints){
372 if(pointOnSegment.distance(p) < minDistance){
373 bestLeft = pointOnSegment;
375 minDistance = pointOnSegment.distance(p);
379 for(
const auto & s : rhs){
380 for(
const auto & p: leftPoints){
382 if(pointOnSegment.distance(p) < minDistance){
384 bestRight = pointOnSegment;
385 minDistance = pointOnSegment.distance(p);
389 return std::make_pair(bestLeft,bestRight);
396struct DefaultDistanceFunction{
397 template<
typename T,
typename U>
399 return lhs.distance(rhs);
410static std::pair<Vec2DReal,Vec2DReal>
closestPoints( SegmentRange
auto && lhs, SegmentRange
auto && rhs)
noexcept {
411 constexpr static size_t SWEEP_LINE_THRESHOLD = 10000;
413 return __impl::closestPointsSweep(lhs,rhs);
415 return __impl::closestPointsBruteForce(lhs,rhs);
427 return __impl::closestPoints(lhs.getSegments(),rhs.getSegments());
437template <Shape S, Shape T,
typename = std::enable_if_t<IPolygon<S> || IPolygon<T>>>
438static std::pair<Vec2DReal,Vec2DReal>
closestPoints(
const S & lhs,
const T & rhs)
noexcept {
450 return __impl::closestPoints(
451 lhs.getPolygons()|std::views::transform([](
const auto & polygon){return polygon.getBoundary().getSegments();}) | std::views::join,
452 rhs.getPolygons()|std::views::transform([](
const auto & polygon){return polygon.getBoundary().getSegments();}) | std::views::join
467 return distanceFunction(l,r);
478 return shapeDistance(lhs,rhs,__impl::DefaultDistanceFunction());
Implementation of a line.
Definition Line.hpp:14
virtual fishnet::math::DEFAULT_NUMERIC eventPoint() const noexcept=0
const Input * obj
Definition SweepLine.hpp:38
virtual void process(SweepLine< Input, Output, SLSLess, InsertEventsFirst, EventQueueGreater > &sweepLine, Output &output) const =0
Implementation of a two-dimensional vector.
Definition Vec2D.hpp:16
constexpr Vec2D< T > orthogonal() const noexcept
Definition Vec2D.hpp:150
Interface for a multi-polygon A multi-polygon is a collection of polygons.
Definition IMultiPolygon.hpp:93
Interface for a point.
Definition IPoint.hpp:92
Interface for a ring A ring is a closed loop of segments, forming a boundary of a shape.
Definition IRing.hpp:143
Interface for a segment A segment is formed between two points p and q, containing only the points on...
Definition ISegment.hpp:105
A geometry enclosing an area: a ring, a polygon or a multi-polygon.
Definition IGeometry.hpp:23
Definition FunctionalConcepts.hpp:15
Definition BoundingBoxWrapper.hpp:5
static std::pair< Vec2DReal, Vec2DReal > closestPoints(ISegment auto const &lhs, ISegment auto const &rhs) noexcept
Compute the pair of nearest points for two segments.
Definition PolygonDistance.hpp:34
Vec2D< fishnet::math::DEFAULT_FLOATING_POINT > Vec2DReal
Definition Vec2D.hpp:220
static fishnet::math::DEFAULT_FLOATING_POINT shapeDistance(Shape auto const &lhs, Shape auto const &rhs, util::BiFunction< Vec2DReal, Vec2DReal, fishnet::math::DEFAULT_FLOATING_POINT > auto const &distanceFunction) noexcept
Compute the distance between two shapes, with custom distance function.
Definition PolygonDistance.hpp:465
static Vec2DReal closestPointOnSegment(ISegment auto const &segment, IPoint auto const &point) noexcept
Compute the point on the segment, closest to the query point.
Definition PolygonDistance.hpp:17
double DEFAULT_NUMERIC
Definition NumericConcepts.hpp:13
double DEFAULT_FLOATING_POINT
Definition NumericConcepts.hpp:12
constexpr size_t size(std::ranges::range auto &&range) noexcept
Definition CollectionConcepts.hpp:50
InsertEvent(const Input &obj)
Definition SweepLine.hpp:57