@@ -195,6 +195,33 @@ bool segmentsIntersect(const Point2D& firstStart, const Point2D& firstEnd, const
195195 || pointOnSegment (firstEnd, secondStart, secondEnd);
196196}
197197
198+ bool segmentBoundsOverlap (
199+ const Point2D& firstStart,
200+ const Point2D& firstEnd,
201+ const Point2D& secondStart,
202+ const Point2D& secondEnd,
203+ double padding = 0.0 ) {
204+ const auto firstMinX = std::min (firstStart.x , firstEnd.x ) - padding;
205+ const auto firstMaxX = std::max (firstStart.x , firstEnd.x ) + padding;
206+ const auto firstMinY = std::min (firstStart.y , firstEnd.y ) - padding;
207+ const auto firstMaxY = std::max (firstStart.y , firstEnd.y ) + padding;
208+ const auto secondMinX = std::min (secondStart.x , secondEnd.x );
209+ const auto secondMaxX = std::max (secondStart.x , secondEnd.x );
210+ const auto secondMinY = std::min (secondStart.y , secondEnd.y );
211+ const auto secondMaxY = std::max (secondStart.y , secondEnd.y );
212+ return firstMinX <= secondMaxX
213+ && firstMaxX >= secondMinX
214+ && firstMinY <= secondMaxY
215+ && firstMaxY >= secondMinY;
216+ }
217+
218+ bool pointWithinSegmentBounds (const Point2D& point, const Point2D& start, const Point2D& end, double padding) {
219+ return point.x >= std::min (start.x , end.x ) - padding
220+ && point.x <= std::max (start.x , end.x ) + padding
221+ && point.y >= std::min (start.y , end.y ) - padding
222+ && point.y <= std::max (start.y , end.y ) + padding;
223+ }
224+
198225double distancePointToSegment (const Point2D& point, const Point2D& start, const Point2D& end) {
199226 return distanceBetween (point, closestPointOnSegment (point, start, end));
200227}
@@ -474,11 +501,16 @@ bool movementCrossesBarrier(const FacilityLayout2D& layout, const Point2D& from,
474501
475502 const auto & vertices = barrier.geometry .vertices ;
476503 for (std::size_t index = 0 ; index + 1 < vertices.size (); ++index) {
504+ if (!segmentBoundsOverlap (from, to, vertices[index], vertices[index + 1 ])) {
505+ continue ;
506+ }
477507 if (segmentsIntersect (from, to, vertices[index], vertices[index + 1 ])) {
478508 return true ;
479509 }
480510 }
481- if (barrier.geometry .closed && segmentsIntersect (from, to, vertices.back (), vertices.front ())) {
511+ if (barrier.geometry .closed
512+ && segmentBoundsOverlap (from, to, vertices.back (), vertices.front ())
513+ && segmentsIntersect (from, to, vertices.back (), vertices.front ())) {
482514 return true ;
483515 }
484516 if (barrier.geometry .closed && pointInRing (vertices, to)) {
@@ -513,11 +545,16 @@ bool pointHasClearance(const FacilityLayout2D& layout, const Point2D& point, dou
513545
514546 const auto & vertices = barrier.geometry .vertices ;
515547 for (std::size_t index = 0 ; index + 1 < vertices.size (); ++index) {
548+ if (!pointWithinSegmentBounds (point, vertices[index], vertices[index + 1 ], clearance)) {
549+ continue ;
550+ }
516551 if (distancePointToSegment (point, vertices[index], vertices[index + 1 ]) < clearance) {
517552 return false ;
518553 }
519554 }
520- if (barrier.geometry .closed && distancePointToSegment (point, vertices.back (), vertices.front ()) < clearance) {
555+ if (barrier.geometry .closed
556+ && pointWithinSegmentBounds (point, vertices.back (), vertices.front (), clearance)
557+ && distancePointToSegment (point, vertices.back (), vertices.front ()) < clearance) {
521558 return false ;
522559 }
523560 }
@@ -536,11 +573,16 @@ bool lineOfSightClear(const FacilityLayout2D& layout, const Point2D& from, const
536573
537574 const auto & vertices = barrier.geometry .vertices ;
538575 for (std::size_t index = 0 ; index + 1 < vertices.size (); ++index) {
576+ if (!segmentBoundsOverlap (from, to, vertices[index], vertices[index + 1 ], clearance)) {
577+ continue ;
578+ }
539579 if (segmentDistance (from, to, vertices[index], vertices[index + 1 ]) < clearance) {
540580 return false ;
541581 }
542582 }
543- if (barrier.geometry .closed && segmentDistance (from, to, vertices.back (), vertices.front ()) < clearance) {
583+ if (barrier.geometry .closed
584+ && segmentBoundsOverlap (from, to, vertices.back (), vertices.front (), clearance)
585+ && segmentDistance (from, to, vertices.back (), vertices.front ()) < clearance) {
544586 return false ;
545587 }
546588 }
@@ -578,14 +620,16 @@ bool nearlySamePoint(const Point2D& lhs, const Point2D& rhs) {
578620}
579621
580622std::vector<Point2D> buildVisibilityPath (const FacilityLayout2D& layout, const Point2D& start, const Point2D& goal, double clearance) {
581- if (lineOfSightClear (layout, start, goal, clearance)) {
582- return {goal};
583- }
584-
585623 std::vector<VisibilityNode> nodes;
586624 nodes.push_back ({.point = start});
587625 nodes.push_back ({.point = goal});
588626
627+ std::size_t candidateCapacity = nodes.size ();
628+ for (const auto & barrier : layout.barriers ) {
629+ candidateCapacity += barrier.blocksMovement ? barrier.geometry .vertices .size () * 8 : 0 ;
630+ }
631+ nodes.reserve (candidateCapacity);
632+
589633 auto addCandidate = [&](const Point2D& candidate) {
590634 if (std::any_of (nodes.begin (), nodes.end (), [&](const auto & node) {
591635 return nearlySamePoint (node.point , candidate);
@@ -718,19 +762,43 @@ std::optional<std::vector<Point2D>> buildGridPath(
718762 auto nearestVisibleCell = [&](const Point2D& point) -> std::optional<std::size_t > {
719763 std::optional<std::size_t > best;
720764 double bestDistance = std::numeric_limits<double >::infinity ();
721- for (int y = 0 ; y < height; ++y) {
722- for (int x = 0 ; x < width; ++x) {
723- const auto index = toIndex (x, y);
724- if (!walkable[index]) {
725- continue ;
765+ const auto centerX = std::clamp (static_cast <int >(std::llround ((point.x - minX) / kGridResolution )), 0 , width - 1 );
766+ const auto centerY = std::clamp (static_cast <int >(std::llround ((point.y - minY) / kGridResolution )), 0 , height - 1 );
767+ const auto maxRing = std::max ({centerX, width - 1 - centerX, centerY, height - 1 - centerY});
768+
769+ auto considerCell = [&](int x, int y) {
770+ if (x < 0 || y < 0 || x >= width || y >= height) {
771+ return ;
772+ }
773+ const auto index = toIndex (x, y);
774+ if (!walkable[index]) {
775+ return ;
776+ }
777+ const auto candidate = toPoint (x, y);
778+ const auto distance = distanceBetween (point, candidate);
779+ if (distance >= bestDistance || !lineOfSightClear (layout, point, candidate, clearance)) {
780+ return ;
781+ }
782+ best = index;
783+ bestDistance = distance;
784+ };
785+
786+ for (int ring = 0 ; ring <= maxRing; ++ring) {
787+ if (ring == 0 ) {
788+ considerCell (centerX, centerY);
789+ } else {
790+ for (int x = centerX - ring; x <= centerX + ring; ++x) {
791+ considerCell (x, centerY - ring);
792+ considerCell (x, centerY + ring);
726793 }
727- const auto candidate = toPoint (x, y);
728- const auto distance = distanceBetween (point, candidate);
729- if (distance >= bestDistance || !lineOfSightClear (layout, point, candidate, clearance)) {
730- continue ;
794+ for (int y = centerY - ring + 1 ; y <= centerY + ring - 1 ; ++y) {
795+ considerCell (centerX - ring, y);
796+ considerCell (centerX + ring, y);
731797 }
732- best = index;
733- bestDistance = distance;
798+ }
799+
800+ if (best.has_value () && static_cast <double >(std::max (0 , ring - 1 )) * kGridResolution > bestDistance) {
801+ break ;
734802 }
735803 }
736804 return best;
@@ -829,10 +897,14 @@ std::optional<std::vector<Point2D>> buildGridPath(
829897}
830898
831899std::vector<Point2D> buildPath (const FacilityLayout2D& layout, const Point2D& start, const Point2D& goal, double clearance) {
900+ if (lineOfSightClear (layout, start, goal, clearance)) {
901+ return {goal};
902+ }
903+
832904 auto path = buildVisibilityPath (layout, start, goal, clearance);
833- if (path.size () == 1 && ! lineOfSightClear (layout, start, goal, clearance) ) {
905+ if (path.size () == 1 ) {
834906 if (auto gridPath = buildGridPath (layout, start, goal, clearance); gridPath.has_value () && !gridPath->empty ()) {
835- path = *gridPath;
907+ return *gridPath;
836908 }
837909 }
838910 return path;
0 commit comments