LCOV - code coverage report
Current view: top level - AP_Math - polygon.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 88 89 98.9 %
Date: 2026-09-09 11:02:08 Functions: 7 7 100.0 %

          Line data    Source code
       1             : /*
       2             :  * polygon.cpp
       3             :  * Copyright (C) Andrew Tridgell 2011
       4             :  *
       5             :  * This file is free software: you can redistribute it and/or modify it
       6             :  * under the terms of the GNU General Public License as published by the
       7             :  * Free Software Foundation, either version 3 of the License, or
       8             :  * (at your option) any later version.
       9             :  *
      10             :  * This file is distributed in the hope that it will be useful, but
      11             :  * WITHOUT ANY WARRANTY; without even the implied warranty of
      12             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
      13             :  * See the GNU General Public License for more details.
      14             :  *
      15             :  * You should have received a copy of the GNU General Public License along
      16             :  * with this program.  If not, see <http://www.gnu.org/licenses/>.
      17             :  */
      18             : 
      19             : #include "AP_Math.h"
      20             : #include "float.h"
      21             : 
      22             : #pragma GCC optimize("O2")
      23             : 
      24             : /*
      25             :  *  The point in polygon algorithm is based on:
      26             :  *  https://wrf.ecse.rpi.edu//Research/Short_Notes/pnpoly.html
      27             :  */
      28             : 
      29             : 
      30             : /*
      31             :  *  Polygon_outside(): test for a point in a polygon
      32             :  *     Input:   P = a point,
      33             :  *              V[] = vertex points of a polygon V[n+1] with V[n]=V[0]
      34             :  *     Return:  true if P is outside the polygon
      35             :  *
      36             :  *  This does not take account of the curvature of the earth, but we
      37             :  *  expect that to be very small over the distances involved in the
      38             :  *  fence boundary
      39             :  */
      40             : template <typename T>
      41     3649514 : bool Polygon_outside(const Vector2<T> &P, const Vector2<T> *V, unsigned n)
      42             : {
      43     3649514 :     const bool complete = Polygon_complete(V, n);
      44     3649513 :     if (complete) {
      45             :         // the last point is the same as the first point; treat as if
      46             :         // the last point wasn't passed in
      47        3116 :         n--;
      48             :     }
      49             : 
      50             :     unsigned i, j;
      51             :     // step through each edge pair-wise looking for crossings:
      52             :     bool outside = true;
      53    22504129 :     for (i=0; i<n; i++) {
      54    18854616 :         j = i+1;
      55    18854616 :         if (j >= n) {
      56             :             j = 0;
      57             :         }
      58    18854616 :         if ((V[i].y > P.y) == (V[j].y > P.y)) {
      59    12655338 :             continue;
      60             :         }
      61     6199278 :         const T dx1 = P.x - V[i].x;
      62     6199278 :         const T dx2 = V[j].x - V[i].x;
      63     6199278 :         const T dy1 = P.y - V[i].y;
      64     6199278 :         const T dy2 = V[j].y - V[i].y;
      65     6199278 :         const int8_t dx1s = (dx1 < 0) ? -1 : 1;
      66     6199278 :         const int8_t dx2s = (dx2 < 0) ? -1 : 1;
      67     6199278 :         const int8_t dy1s = (dy1 < 0) ? -1 : 1;
      68     6199278 :         const int8_t dy2s = (dy2 < 0) ? -1 : 1;
      69     6199278 :         const int8_t m1 = dx1s * dy2s;
      70     6199278 :         const int8_t m2 = dx2s * dy1s;
      71             :         // we avoid the 64 bit multiplies if we can based on sign checks.
      72     6199278 :         if (dy2 < 0) {
      73     3099639 :             if (m1 > m2) {
      74      323929 :                 outside = !outside;
      75     2775710 :             } else if (m1 < m2) {
      76      143481 :                 continue;
      77             :             } else {
      78             :                 if (std::is_floating_point<T>::value) {
      79      172633 :                     if ( dx1 * dy2 > dx2 * dy1 ) {
      80      139574 :                         outside = !outside;
      81             :                     }
      82             :                 } else {
      83     2459596 :                     if ( dx1 * (int64_t)dy2 > dx2 * (int64_t)dy1 ) {
      84     1560408 :                         outside = !outside;
      85             :                     }
      86             :                 }
      87             :             }
      88             :         } else {
      89     3099639 :             if (m1 < m2) {
      90       90221 :                 outside = !outside;
      91     3009418 :             } else if (m1 > m2) {
      92     1850776 :                 continue;
      93             :             } else {
      94             :                 if (std::is_floating_point<T>::value) {
      95       47730 :                     if ( dx1 * dy2 < dx2 * dy1 ) {
      96       39020 :                         outside = !outside;
      97             :                     }
      98             :                 } else {
      99     1110912 :                     if ( dx1 * (int64_t)dy2 < dx2 * (int64_t)dy1 ) {
     100      702261 :                         outside = !outside;
     101             :                     }
     102             :                 }
     103             :             }
     104             :         }
     105             :     }
     106     3649513 :     return outside;
     107             : }
     108             : 
     109             : /*
     110             :  *  check if a polygon is complete.
     111             :  *
     112             :  *  We consider a polygon to be complete if we have at least 4 points,
     113             :  *  and the first point is the same as the last point. That is the
     114             :  *  minimum requirement for the Polygon_outside function to work
     115             :  */
     116             : template <typename T>
     117     7111341 : bool Polygon_complete(const Vector2<T> *V, unsigned n)
     118             : {
     119     7111341 :     return (n >= 4 && V[n-1] == V[0]);
     120             : }
     121             : 
     122             : // Necessary to avoid linker errors
     123             : template bool Polygon_outside<int32_t>(const Vector2l &P, const Vector2l *V, unsigned n);
     124             : template bool Polygon_complete<int32_t>(const Vector2l *V, unsigned n);
     125             : template bool Polygon_outside<float>(const Vector2f &P, const Vector2f *V, unsigned n);
     126             : template bool Polygon_complete<float>(const Vector2f *V, unsigned n);
     127             : 
     128             : /*
     129             :   determine if the polygon of N verticies defined by points V is
     130             :   intersected by a line from point p1 to point p2
     131             :   intersection argument returns the intersection closest to p1
     132             :  */
     133        7121 : bool Polygon_intersects(const Vector2f *V, unsigned N, const Vector2f &p1, const Vector2f &p2, Vector2f &intersection)
     134             : {
     135        7121 :     const bool complete = Polygon_complete(V, N);
     136        7121 :     if (complete) {
     137             :         // if the last point is the same as the first point
     138             :         // treat as if the last point wasn't passed in
     139           0 :         N--;
     140             :     }
     141             : 
     142             :     float intersect_dist_sq = FLT_MAX;
     143       65991 :     for (uint8_t i=0; i<N; i++) {
     144       58870 :         uint8_t j = i+1;
     145       58870 :         if (j >= N) {
     146             :             j = 0;
     147             :         }
     148       58870 :         const Vector2f &v1 = V[i];
     149       58870 :         const Vector2f &v2 = V[j];
     150             :         // optimisations for common cases
     151       58870 :         if (v1.x > p1.x && v2.x > p1.x && v1.x > p2.x && v2.x > p2.x) {
     152       50929 :             continue;
     153             :         }
     154       49267 :         if (v1.y > p1.y && v2.y > p1.y && v1.y > p2.y && v2.y > p2.y) {
     155       10048 :             continue;
     156             :         }
     157       39219 :         if (v1.x < p1.x && v2.x < p1.x && v1.x < p2.x && v2.x < p2.x) {
     158       16303 :             continue;
     159             :         }
     160       22916 :         if (v1.y < p1.y && v2.y < p1.y && v1.y < p2.y && v2.y < p2.y) {
     161       14975 :             continue;
     162             :         }
     163        7941 :         Vector2f intersect_tmp;
     164        7941 :         if (Vector2f::segment_intersection(v1,v2,p1,p2,intersect_tmp)) {
     165        3138 :             float dist_sq = sq(intersect_tmp.x - p1.x) + sq(intersect_tmp.y - p1.y);
     166        3138 :             if (dist_sq < intersect_dist_sq) {
     167             :                 intersect_dist_sq = dist_sq;
     168        2974 :                 intersection = intersect_tmp;
     169             :             }
     170             :         }
     171             :     }
     172        7121 :     return (intersect_dist_sq < FLT_MAX);
     173             : }
     174             : 
     175             : /*
     176             :   return the closest distance that a line from p1 to p2 comes to an
     177             :   edge of closed polygon V, defined by N points
     178             :   negative numbers indicate the line cross into the polygon with the negative size being the distance from p2 to the intersection point closest to p1
     179             :  */
     180        6828 : float Polygon_closest_distance_line(const Vector2f *V, unsigned N, const Vector2f &p1, const Vector2f &p2)
     181             : {
     182        6828 :     Vector2f intersection;
     183        6828 :     if (Polygon_intersects(V,N,p1,p2,intersection)) {
     184        2658 :         return -sqrtf(sq(intersection.x - p2.x) + sq(intersection.y - p2.y));
     185             :     }
     186             :     float closest_sq = FLT_MAX;
     187       37514 :     for (uint8_t i=0; i<N; i++) {
     188       33344 :         uint8_t j = i+1;
     189       33344 :         if (j >= N) {
     190             :             j = 0;
     191             :         }
     192       33344 :         const Vector2f &v1 = V[i];
     193       33344 :         const Vector2f &v2 = V[j];
     194             : 
     195       33344 :         float dist_sq = Vector2f::closest_distance_between_lines_squared(v1, v2, p1, p2);
     196       33344 :         if (dist_sq < closest_sq) {
     197             :             closest_sq = dist_sq;
     198             :         }
     199             :     }
     200        4170 :     return sqrtf(closest_sq);
     201             : }
     202             : 
     203             : /*
     204             :   return the closest distance that point p comes to an edge of closed
     205             :   polygon V, defined by N points
     206             :  */
     207     3454706 : bool Polygon_closest_distance_point(const Vector2f *V, unsigned N,
     208             :                                     const Vector2f &p, Vector2f &closest_vec)
     209             : {
     210             :     float closest_sq = FLT_MAX;
     211     3454706 :     if (Polygon_complete(V, N) && N > 0) {
     212          10 :         N--;   // not a polygon
     213             :     }
     214     3454706 :     if (N < 3) {
     215             :         return false;
     216             :     }
     217             : 
     218             :     Vector2f best_v(0.0f, 0.0f);
     219             : 
     220    21129201 :     for (uint32_t i = 0; i < N; i++) {
     221    17674495 :         const Vector2f &a = V[i];
     222    17674495 :         const Vector2f &b = V[(i + 1) % N];
     223             : 
     224             :         // Built-in: closest point on segment ab to p (handles v==w).
     225    17674495 :         const Vector2f q = Vector2f::closest_point(p, a, b);
     226    17674495 :         const Vector2f v = q - p;                  // vector from p to boundary
     227    17674495 :         const float vsq = v.length_squared();       // squared distance
     228             : 
     229    17674495 :         if (vsq < closest_sq) {
     230             :             closest_sq = vsq;
     231     7607780 :             best_v  = v;
     232             :         }
     233             :     }
     234             : 
     235     3454706 :     if (is_equal(closest_sq, FLT_MAX)) {
     236             :         return false;
     237             :     }
     238             : 
     239     3454706 :     closest_vec = best_v;                          // already correct direction & length
     240     3454706 :     return true;
     241             : }

Generated by: LCOV version 1.14