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