1 #pragma once
2 
3 #include <random>
4 #include <vector>
5 #include <set>
6 
7 #include "Comparison.hpp"
8 #include "Grid.hpp"
9 #include "VoxelRayTrace.hpp"
10 
11 namespace pdal
12 {
13 
14 struct grid_error : public std::runtime_error
15 {
grid_errorpdal::grid_error16     grid_error(const std::string& s) : std::runtime_error(s)
17     {}
18 };
19 
20 
21 class GridPnp
22 {
23 public:
24     using Point = std::pair<double, double>;
25     using Ring = std::vector<Point>;
26 
27     // Initialize the point-in-poly engine by creating an overlay grid and
28     // attaching the index of each polygon edge to any cell it passes
29     // through.
30     // NOTE: This code does only minimal validation to try to make sure
31     //  that is won't hang.  Make sure your polygon is valid before
32     //  calling.  Make sure there are no self-intersections, for example.
GridPnp(const Ring & outer,const std::vector<Ring> & inners)33     GridPnp(const Ring& outer, const std::vector<Ring>& inners)
34     {
35         validateRing(outer);
36         for (const Ring& inner : inners)
37             validateRing(inner);
38 
39         calcBounds(outer);
40 
41         fillRingList(outer, inners);
42         setupGrid();
43     }
44 
45 
GridPnp(const Ring & outer)46     GridPnp(const Ring& outer)
47     {
48         validateRing(outer);
49         calcBounds(outer);
50 
51         std::vector<Ring> inners;  // no outers.
52         fillRingList(outer, inners);
53         setupGrid();
54     }
55 
inside(const Point & p) const56     bool inside(const Point& p) const
57     { return inside(p.first, p.second); }
58 
59     // Determine if a point is inside the polygon attached to this class.
inside(double x,double y) const60     bool inside(double x, double y) const
61     {
62         // Find the index of the grid cell at the position.
63         // If the position isn't in the grid, we're certainly outside.
64         XYIndex idx;
65         if (!m_grid->cellPos(x, y, idx))
66             return false;
67 
68         Cell& cell = m_grid->cell(idx);
69         // If we don't already have a reference point with computed state,
70         // do it now.
71         if (!cell.computed())
72             computeCell(cell, idx);
73 
74         // If there are no edges in the cell, the status of the cell is uniform,
75         // just return the state.
76         if (cell.empty())
77             return cell.inside();
78         return testCell(cell, x, y);
79     }
80 
81 private:
82     using XYIndex = std::pair<size_t, size_t>;
83     using Edge = std::pair<Point, Point>;
84     using RingList = Ring; // Structure is the same.
85     using EdgeId = size_t;
86 
87     enum class IntersectType
88     {
89         Cross,
90         On,
91         None
92     };
93 
94     class Cell
95     {
96     public:
Cell()97         Cell() : m_point(
98             {std::numeric_limits<double>::quiet_NaN(),
99              std::numeric_limits<double>::quiet_NaN() })
100         {}
101 
addEdge(size_t edge)102         void addEdge(size_t edge)
103             { m_edges.push_back(edge); }
empty() const104         bool empty() const
105             { return m_edges.empty(); }
setPoint(double x,double y)106         void setPoint(double x, double y)
107             { m_point = Point(x, y); }
computed() const108         bool computed() const
109             { return !std::isnan(m_point.first); }
point() const110         GridPnp::Point point() const
111             { return m_point; }
edges() const112         const std::vector<size_t>& edges() const
113             { return m_edges; }
inside() const114         bool inside() const
115             { return m_inside; }
setInside(bool inside)116         void setInside(bool inside)
117             { m_inside = inside; }
118 
119     private:
120         std::vector<size_t> m_edges;
121         bool m_inside;
122         GridPnp::Point m_point;
123     };
124 
125     class EdgeIt
126     {
127     public:
EdgeIt(const RingList & r)128         EdgeIt(const RingList& r) : m_points(r), m_id(0)
129         { skipInvalid(); }
130 
next()131         void next()
132         {
133             m_id++;
134             skipInvalid();
135         }
136 
operator *() const137         EdgeId operator * () const
138         { return m_id; }
139 
operator bool() const140         operator bool () const
141         { return m_id < m_points.size() - 1; }
142 
143     private:
skipInvalid()144         void skipInvalid()
145         {
146             while (m_id < m_points.size() - 1 &&
147                 (std::isnan(m_points[m_id].first) ||
148                  std::isnan(m_points[m_id + 1].first) ||
149                  (m_points[m_id] == m_points[m_id + 1])))
150             {
151                 m_id++;
152             }
153         }
154 
155         const RingList& m_points;
156         size_t m_id;
157     };
158 
159 
point1(EdgeId id) const160     const Point& point1(EdgeId id) const
161         { return m_rings[id]; }
point2(EdgeId id) const162     const Point& point2(EdgeId id) const
163         { return m_rings[id + 1]; }
xval(const Point & p) const164     double xval(const Point& p) const
165         { return p.first; }
yval(const Point & p) const166     double yval(const Point& p) const
167         { return p.second; }
168 
validateRing(const Ring & r) const169     void validateRing(const Ring& r) const
170     {
171         if (r.size() < 4)
172             throw grid_error("Invalid ring. Ring must consist of at least "
173                 " four points.");
174         if (r[0] != r[r.size() - 1])
175             throw grid_error("Invalid ring. First point is not equal to "
176                 "the last point.");
177     }
178 
179     // Calculate the bounding box of the polygon.  At the same time
180     // calculate the average length of the X and Y components of the
181     // polygon edges.
calcBounds(const Ring & outer)182     void calcBounds(const Ring& outer)
183     {
184         // Inialize max/min with X/Y of first point.
185         const Point& p = outer[0];
186         m_xMin = xval(p);
187         m_xMax = xval(p);
188         m_yMin = yval(p);
189         m_yMax = yval(p);
190 
191         size_t numEdges = 0;
192         // The first point is duplicated as the last, so we skip the last
193         // point when looping.
194         for (size_t id = 0; id < outer.size() - 1; ++id)
195         {
196             const Point& p1 = outer[id];
197             const Point& p2 = outer[id + 1];
198 
199             // Calculate bounding box.
200             m_xMin = (std::min)(m_xMin, xval(p1));
201             m_xMax = (std::max)(m_xMax, xval(p1));
202             m_yMin = (std::min)(m_yMin, yval(p1));
203             m_yMax = (std::max)(m_yMax, yval(p1));
204         }
205     }
206 
207 
fillRingList(const Ring & inner,const std::vector<Ring> & outers)208     void fillRingList(const Ring& inner, const std::vector<Ring>& outers)
209     {
210         double nan = std::numeric_limits<double>::quiet_NaN();
211 
212         for (size_t i = 0; i < inner.size(); ++i)
213             m_rings.push_back(inner[i]);
214         for (const Ring& r : outers)
215         {
216             // Nan is a separator between rings.
217             m_rings.push_back({nan, nan});
218             for (size_t i = 0; i < r.size(); ++i)
219                 m_rings.push_back(r[i]);
220         }
221     }
222 
223 
setupGrid()224     void setupGrid()
225     {
226         double xAvgLen;
227         double yAvgLen;
228 
229         calcAvgEdgeLen(xAvgLen, yAvgLen);
230         XYIndex gridSize = calcGridSize(xAvgLen, yAvgLen);
231         createGrid(gridSize);
232         assignEdges();
233     }
234 
235 
calcAvgEdgeLen(double & xAvgLen,double & yAvgLen)236     void calcAvgEdgeLen(double& xAvgLen, double& yAvgLen)
237     {
238         double xdist{0};
239         double ydist{0};
240         size_t numEdges{0};
241         for (EdgeIt it(m_rings); it; it.next())
242         {
243             EdgeId id = *it;
244             const Point& p1 = point1(*it);
245             const Point& p2 = point2(*it);
246 
247             // Sum the lengths of the X and Y components of the edges.
248             xdist += std::abs(xval(p2) - xval(p1));
249             ydist += std::abs(yval(p2) - yval(p1));
250             numEdges++;
251         }
252 
253         // Find the average X and Y component length.
254         xAvgLen = xdist / numEdges;
255         yAvgLen = ydist / numEdges;
256     }
257 
258     // The paper calculates an X and Y based on a) the number of edges
259     // and b) the relative length of edges in the X and Y direction.
260     // This seems fine, but it misses out on considering the common
261     // case where a polygon delineates some area of interest.  In this
262     // case there is much "empty" space in the interior of the polygon and
263     // it seems likely that most pnp tests will happen in the empty interior.
264     // So, it would seem that we'd want the grid sufficiently large to cover
265     // this case well.  That way, most pnp tests would take no work beyond
266     // an array lookup since most cells would be empty.  Lots of tradeoff,
267     // though, in preprocessing vs. actual pnp tests.  Hopefully more work
268     // can be done on this later.  My stupid way of dealing with this is
269     // to set a minimum grid size of 1000 cells.
calcGridSize(double xAvgLen,double yAvgLen) const270     XYIndex calcGridSize(double xAvgLen, double yAvgLen) const
271     {
272         // I'm setting a minimum number of cells as 1000, because, why not?
273         // m_rings isn't necessarily an exact count of edges, but it's close
274         // enough for this purpose.
275         size_t m = (std::max)((size_t)1000, m_rings.size());
276 
277         // See paper for this calc.
278         double scalex = ((m_xMax - m_xMin) * yAvgLen) /
279             ((m_yMax - m_yMin) * xAvgLen);
280         double scaley = 1 / scalex;
281         size_t mx = (size_t)std::sqrt(m * scalex);
282         size_t my = (size_t)std::sqrt(m * scaley);
283 
284         // We always round up, because why not.
285         return XYIndex(mx + 1, my + 1);
286     }
287 
288 
289     // Figure out the grid origin.
createGrid(XYIndex gridSize)290     void createGrid(XYIndex gridSize)
291     {
292         // Make the grid extend 1/2 cell beyond bounds box.
293         double boxWidth = m_xMax - m_xMin;
294         double boxHeight = m_yMax - m_yMin;
295         //
296         double cellWidth = boxWidth / (gridSize.first - 1);
297         double cellHeight = boxHeight / (gridSize.second - 1);
298         double xOrigin = m_xMin - (cellWidth / 2);
299         double yOrigin = m_yMin - (cellHeight / 2);
300 
301         m_grid.reset(new Grid<Cell>(gridSize.first, gridSize.second,
302             cellWidth, cellHeight, xOrigin, yOrigin));
303         m_xDistribution.reset(
304             new std::uniform_real_distribution<>(0, m_grid->cellWidth()));
305         m_yDistribution.reset(
306             new std::uniform_real_distribution<>(0, m_grid->cellHeight()));
307     }
308 
309 
310     // Loop through edges.  Add the edge to each cell traversed.
assignEdges()311     void assignEdges()
312     {
313         for (EdgeIt it(m_rings); it; it.next())
314         {
315             EdgeId id = *it;
316             const Point& p1 = point1(id);
317             const Point& p2 = point2(id);
318             Point origin = m_grid->origin();
319             VoxelRayTrace vrt(m_grid->cellWidth(), m_grid->cellHeight(),
320                 xval(origin), yval(origin),
321                 xval(p1), yval(p1), xval(p2), yval(p2));
322             VoxelRayTrace::CellList traversedCells = vrt.emit();
323             for (auto& c : traversedCells)
324                 m_grid->cell(XYIndex(c.first, c.second)).addEdge(id);
325         }
326     }
327 
328 
329     // Determine if a point is collinear with an edge.
pointCollinear(double x,double y,EdgeId edgeId) const330     bool pointCollinear(double x, double y, EdgeId edgeId) const
331     {
332         const Point& p1 = point1(edgeId);
333         const Point& p2 = point2(edgeId);
334         const double x1 = xval(p1);
335         const double x2 = xval(p2);
336         const double y1 = yval(p1);
337         const double y2 = yval(p2);
338 
339         // If p1 == p2, this will fail.
340 
341         // This is the same as saying slopes are equal.
342         return Comparison::closeEnough((x - x2) * (y - y1),
343             (y - y2) * (x - x1));
344     }
345 
346 
347     // Put a reference point in the cell.  Figure out if the reference point
348     // is inside the polygon.
computeCell(Cell & cell,XYIndex & pos) const349     void computeCell(Cell& cell, XYIndex& pos) const
350     {
351         generateRefPoint(cell, pos);
352         determinePointStatus(cell, pos);
353     }
354 
355 
356     // The paper uses point centers, but then has to deal with points that
357     // are "singular" (rest on a polygon edge).  But there's nothing special
358     // about the point center.  The center is just a point in a cell with
359     // a known status (inside or outside the polygon).  So we just pick a point
360     // that isn't collinear with any of the segments in the cell.  Eliminating
361     // collinearity eliminates special cases when counting crossings.
generateRefPoint(Cell & cell,XYIndex & pos) const362     void generateRefPoint(Cell& cell, XYIndex& pos) const
363     {
364         // A test point is valid if it's not collinear with any segments
365         // in the cell.
366         auto validTestPoint = [this](double x, double y, Cell& cell)
367         {
368             for (auto edgeId : cell.edges())
369                 if (pointCollinear(x, y, edgeId))
370                     return false;
371             return true;
372         };
373 
374         Grid<Cell>::Point origin = m_grid->cellOrigin(pos);
375         double x, y;
376         do
377         {
378             x = xval(origin) + (*m_xDistribution)(m_ranGen);
379             y = yval(origin) + (*m_yDistribution)(m_ranGen);
380         } while (!validTestPoint(x, y, cell));
381         cell.setPoint(x, y);
382     }
383 
384 
385     // Determine the status of a cell's reference point by drawing a segment
386     // from the reference point in the cell to the left and count crossings.
387     // Knowing the number of edge crossings and the inside/outside status
388     // of the cell determines the status of this reference point.
389     // If we're determining the status of the leftmost cell, choose a point
390     // to the left of the leftmost cell, which is guaranteed to be outside
391     // the polygon.
determinePointStatus(Cell & cell,XYIndex & pos) const392     void determinePointStatus(Cell& cell, XYIndex& pos) const
393     {
394         Point p1(cell.point());
395 
396         double x1 = xval(p1);
397         double y1 = yval(p1);
398 
399         size_t intersectCount = 0;
400         if (pos.first == 0)
401         {
402             double x2 = x1 - m_grid->cellWidth();
403             double y2 = y1;
404 
405             Edge edge{{x1, y1}, {x2, y2}};
406 
407             intersectCount = intersections(edge, cell.edges());
408         }
409         else
410         {
411             XYIndex prevPos {pos.first - 1, pos.second};
412             Cell& prevCell = m_grid->cell(prevPos);
413             if (!prevCell.computed())
414                 computeCell(prevCell, prevPos);
415             double x2 = xval(prevCell.point());
416             double y2 = yval(prevCell.point());
417 
418             Edge edge{{x1, y1}, {x2, y2}};
419 
420             // Stick the edges in the current cell and the previous cell
421             // in a set so as not to double-count.
422             std::set<size_t> edges;
423             edges.insert(cell.edges().begin(), cell.edges().end());
424             edges.insert(prevCell.edges().begin(), prevCell.edges().end());
425             intersectCount = intersections(edge, edges);
426             if (prevCell.inside())
427                 intersectCount++;
428         }
429         cell.setInside(intersectCount % 2 == 1);
430     }
431 
432 
433     // Determine the number of intersections between an edge and
434     // all edges indexes by the 'edges' list.
435     template<typename EDGES>
intersections(Edge & e1,const EDGES & edges) const436     size_t intersections(Edge& e1, const EDGES& edges) const
437     {
438         size_t isect = 0;
439         for (auto& edgeId : edges)
440         {
441             Edge e2{ point1(edgeId), point2(edgeId) };
442             if (intersects(e1, e2) != IntersectType::None)
443                 isect++;
444         }
445         return isect;
446     }
447 
448 
449     // Determine if a point in a cell is inside the polygon or outside.
450     // We're always calling a point that lies on an edge as 'inside'
451     // the polygon.
testCell(Cell & cell,double x,double y) const452     bool testCell(Cell& cell, double x, double y) const
453     {
454         Edge tester({x, y}, cell.point());
455 
456         bool inside = cell.inside();
457         for (auto edgeIdx: cell.edges())
458         {
459             Edge other{ point1(edgeIdx), point2(edgeIdx) };
460             IntersectType intersection = intersects(tester, other);
461             if (intersection == IntersectType::On)
462                 return true;
463             if (intersection == IntersectType::Cross)
464                 inside = !inside;
465         }
466         return inside;
467     }
468 
469     // Determine if two edges intersect.  Note that because of the way
470     // we've chosen reference points, the two segments should never be
471     // collinear, which eliminates some special cases.
472     //
473     // One segment endpoint lies on the other if the slope factor (t or u)
474     // is one or 0 and the other factor is between 0 and 1.
475     // This is standard math, but it's shown nicely on Stack Overflow
476     // question 563198.  The variable names map to the good response there.
intersects(Edge & e1,Edge & e2) const477     IntersectType intersects(Edge& e1, Edge& e2) const
478     {
479         using Vector = std::pair<double, double>;
480 
481         Vector p = e1.first;
482         Vector r { e1.second.first - e1.first.first,
483             e1.second.second - e1.first.second };
484         Vector q = e2.first;
485         Vector s { e2.second.first - e2.first.first,
486             e2.second.second - e2.first.second };
487 
488         // Should never be 0.
489         double rCrossS = r.first * s.second - r.second * s.first;
490         Vector pq = { q.first - p.first, q.second - p.second };
491 
492         //ABELL - This can be improved to eliminate the division
493         //  because we're testing for 1 and 0.  Later...
494         double pqCrossS = pq.first * s.second - pq.second * s.first;
495         double t = (pqCrossS / rCrossS);
496         bool tCloseEnough = Comparison::closeEnough(t, 0) ||
497             Comparison::closeEnough(t, 1);
498         bool intersect = (tCloseEnough || (t > 0 && t < 1));
499         if (!intersect)
500             return IntersectType::None;
501 
502         double pqCrossR = pq.first * r.second - pq.second * r.first;
503         double u = (pqCrossR / rCrossS);
504         bool uCloseEnough = Comparison::closeEnough(u, 0) ||
505             Comparison::closeEnough(u, 1);
506         intersect = (uCloseEnough || (u > 0 && u < 1));
507         if (intersect)
508         {
509             if (uCloseEnough || tCloseEnough)
510                 return IntersectType::On;
511             return IntersectType::Cross;
512         }
513         return IntersectType::None;
514     }
515 
516     RingList m_rings;
517     mutable std::mt19937 m_ranGen;
518     std::unique_ptr<std::uniform_real_distribution<>> m_xDistribution;
519     std::unique_ptr<std::uniform_real_distribution<>> m_yDistribution;
520     std::unique_ptr<Grid<Cell>> m_grid;
521     double m_xMin;
522     double m_xMax;
523     double m_yMin;
524     double m_yMax;
525 };
526 
527 } // namespace pdal
528