11constexpr double kEps = 1e-9;
13int wrapIndex(
int i,
int n) {
15 return ((i %
n) +
n) %
n;
18bool samePoint(
const Vec2&
a,
const Vec2&
b,
double eps) {
19 return std::fabs(
a.x -
b.x) <= eps && std::fabs(
a.y -
b.y) <= eps;
22bool onSegmentTol(
const Vec2&
p,
const Vec2&
a,
const Vec2&
b,
double eps) {
25 if (l2 <= eps * eps)
return samePoint(
p,
a, eps);
26 const double t =
dot(
p -
a,
ab) / l2;
27 if (t < -eps || t > 1.0 + eps)
return false;
35 const size_t n = poly.size();
36 if (
n < 3)
return 0.0;
38 for (
size_t i = 0; i <
n; ++i) {
39 const Vec2&
a = poly[i];
40 const Vec2&
b = poly[wrapIndex(
int(i) + 1,
int(
n))];
50 for (
size_t i = 1; i < pl.size(); ++i) l +=
distance(pl[i - 1], pl[i]);
55 const size_t n = poly.size();
56 if (
n == 0)
return {0, 0};
57 if (
n == 1)
return poly[0];
58 if (
n == 2)
return (poly[0] + poly[1]) * 0.5;
61 for (
size_t i = 0; i <
n; ++i) {
62 const Vec2&
p = poly[i];
63 const Vec2&
q = poly[wrapIndex(
int(i) + 1,
int(
n))];
68 if (std::fabs(
a) < 1e-12)
return poly[0];
74 for (
size_t i = 0; i < poly.size(); ++i)
p +=
distance(poly[i], poly[wrapIndex(
int(i) + 1,
int(poly.size()))]);
80 std::reverse(poly.begin(), poly.end());
88 out.reserve(poly.size());
89 for (
const Vec2&
p : poly) {
90 if (!out.empty() && samePoint(out.back(),
p, eps))
continue;
92 if (out.size() >= 2) {
93 const Vec2&
a = out[out.size() - 2];
94 const Vec2&
b = out.back();
103 while (out.size() > 3 && samePoint(out.front(), out.back(), eps)) out.pop_back();
104 if (out.size() >= 3) {
105 const Vec2&
a = out[out.size() - 1];
106 const Vec2&
b = out[0];
109 poly = std::move(out);
113 if (poly.size() < 3)
return false;
115 for (
size_t i = 0, j = poly.size() - 1; i < poly.size(); j = i++) {
116 const Vec2&
a = poly[i];
117 const Vec2&
b = poly[j];
119 const bool crosses = ((
a.y >
p.y) != (
b.y >
p.y)) && (
p.x < (
b.x -
a.x) * (
p.y -
a.y) / (
b.y -
a.y) +
a.x);
131 const double denom =
cross(
ab, cd);
132 const double eps = 1e-12;
133 if (std::fabs(denom) <= eps)
return false;
134 const double t =
cross(
ac, cd) / denom;
136 if (t < -eps || t > 1.0 + eps || u < -eps || u > 1.0 + eps)
return false;
137 if (out) *out =
a +
ab *
t;
142 for (
size_t i = 1; i < pl.size(); ++i) {
150 for (
size_t i = 1; i < pl.size(); ++i) {
151 for (
size_t j = i + 2; j < pl.size(); ++j) {
160 const size_t n = poly.size();
161 if (
n < 3)
return false;
162 for (
size_t i = 0; i <
n; ++i) {
163 const Vec2&
a = poly[i];
164 const Vec2&
b = poly[wrapIndex(
int(i) + 1,
int(
n))];
165 for (
size_t j = i + 1; j <
n; ++j) {
166 const size_t jn = wrapIndex(
int(j) + 1,
int(
n));
168 if (j == i + 1)
continue;
169 if (i == 0 && j ==
n - 1)
continue;
174 return area(poly) > 1e-12;
184 const double t = std::clamp(
dot(
p -
a,
ab) / l2, 0.0, 1.0);
185 if (out) *out =
a +
ab *
t;
190 const size_t n = poly.size();
191 if (
n == 0)
return p;
192 double best = std::numeric_limits<double>::infinity();
193 Vec2 bestP = poly[0];
196 for (
size_t i = 0; i <
n; ++i) {
197 const Vec2&
a = poly[i];
198 const Vec2&
b = poly[wrapIndex(
int(i) + 1,
int(
n))];
202 if (l2 > 1e-18)
t = std::clamp(
dot(
p -
a,
ab) / l2, 0.0, 1.0);
213 pos->edgeIndex = bestEdge;
220 const size_t n = poly.size();
221 if (
n == 0)
return {0, 0};
223 double t = std::fmod(
s, total);
224 if (
t < 0)
t += total;
226 for (
size_t i = 0; i <
n; ++i) {
227 const Vec2&
a = poly[i];
228 const Vec2&
b = poly[wrapIndex(
int(i) + 1,
int(
n))];
230 if (acc + seg >=
t || i + 1 ==
n) {
231 const double f = seg > 1e-12 ? (
t - acc) / seg : 0.0;
233 pos->edgeIndex = int(i);
234 pos->t = std::clamp(
f, 0.0, 1.0);
236 return a + (
b -
a) *
f;
248 std::vector<BoundarySample> samples;
249 const size_t n = poly.size();
250 if (
n < 3)
return samples;
251 const int k = std::max(8,
count);
253 samples.reserve(
size_t(k));
254 for (
int i = 0; i < k; ++i) {
255 const double s = double(i) * total / double(k);
258 const Vec2&
a = poly[size_t(
pos.edgeIndex)];
259 const Vec2&
b = poly[wrapIndex(
pos.edgeIndex + 1,
int(
n))];
261 samples.push_back({
p, std::atan2(
dir.y,
dir.x),
s});
269 const double c = std::clamp(
dot(nu, nv), -1.0, 1.0);
270 return std::acos(
c) * 180.0 / 3.14159265358979323846;
278 const size_t n = ring.size();
279 if (
n <= 3)
return ring;
282 std::vector<char> merged(
n, 0);
283 for (
size_t i = 0; i <
n; ++i) {
284 const Vec2& prev = ring[wrapIndex(
int(i) - 1,
int(
n))];
285 const Vec2& cur = ring[i];
286 const Vec2& next = ring[wrapIndex(
int(i) + 1,
int(
n))];
289 for (
size_t i = 0; i <
n; ++i) {
290 if (merged[i])
continue;
291 out.push_back(ring[i]);
293 if (out.size() < 3)
return ring;
295 while (out.size() > 3) {
296 const Vec2&
a = out[out.size() - 2];
297 const Vec2&
b = out.back();
298 const Vec2&
c = out[0];
303 const Vec2&
d = out[1];
305 out.erase(out.begin());
315 const size_t n = approxRing.size();
316 if (
n < 3)
return std::numeric_limits<double>::infinity();
318 std::vector<double> angles(
n);
319 double angleMean = 0.0;
320 for (
size_t i = 0; i <
n; ++i) {
321 const Vec2& prev = approxRing[wrapIndex(
int(i) - 1,
int(
n))];
322 const Vec2& cur = approxRing[i];
323 const Vec2& next = approxRing[wrapIndex(
int(i) + 1,
int(
n))];
326 const double c = std::clamp(
dot(
u,
v), -1.0, 1.0);
330 double a = std::acos(
c);
331 if (
cross(
u,
v) < 0.0)
a = 2.0 * 3.14159265358979323846 -
a;
335 angleMean /= double(
n);
336 double sideMean = 0.0;
337 std::vector<double>
sides(
n);
338 for (
size_t i = 0; i <
n; ++i) {
339 const double l =
distance(approxRing[i], approxRing[wrapIndex(
int(i) + 1,
int(
n))]);
343 sideMean /= double(
n);
344 double angleVar = 0.0;
345 double sideVar = 0.0;
346 for (
size_t i = 0; i <
n; ++i) {
347 const double da = angles[i] - angleMean;
349 const double dl =
sides[i] - sideMean;
352 angleVar /= double(
n);
353 sideVar /= double(
n);
354 if (sideMean <= 1e-12)
return std::numeric_limits<double>::infinity();
355 return gammaAngle * angleVar + gammaSide * sideVar / (sideMean * sideMean);
361Polygon boundaryChain(
const Polygon& poly,
const BoundaryPosition&
from,
const BoundaryPosition&
to,
const Vec2& pFrom,
363 const size_t n = poly.size();
365 const int startEdge =
from.edgeIndex;
366 const int endEdge =
to.edgeIndex;
367 if (startEdge == endEdge) {
370 chain.push_back(pFrom);
371 if (!(pFrom == pTo)) chain.push_back(pTo);
375 chain.push_back(pFrom);
378 e = wrapIndex(e + 1,
int(
n));
379 if (e == endEdge)
break;
380 chain.push_back(poly[
size_t(e)]);
382 chain.push_back(poly[
size_t(endEdge)]);
383 chain.push_back(pTo);
386 chain.push_back(pFrom);
387 int e = wrapIndex(startEdge + 1,
int(
n));
388 while (e != endEdge) {
389 chain.push_back(poly[
size_t(e)]);
390 e = wrapIndex(e + 1,
int(
n));
392 if (
to.t > 1e-12) chain.push_back(poly[
size_t(endEdge)]);
393 chain.push_back(pTo);
401 if (poly.size() < 3 ||
split.size() < 2)
return false;
408 Polygon chainAB = boundaryChain(poly, posA, posB, pa, pb);
409 Polygon chainBA = boundaryChain(poly, posB, posA, pb, pa);
413 for (
auto it =
split.rbegin(); it !=
split.rend(); ++it) outA.push_back(*it);
427 if (
split.size() < 2)
return false;
432 const double snapTol = 1e-4 * std::max(1.0,
perimeter(poly));
434 if (
distance(pa, pb) <= 1e-9)
return false;
436 for (
size_t i = 1; i <
split.size(); ++i) {
444 snapped.front() = pa;
447 const double areaPoly =
area(poly);
448 const double areaA =
area(
a);
449 const double areaB =
area(
b);
450 if (areaPoly <= 1e-12)
return false;
452 if (std::fabs(areaA + areaB - areaPoly) > 0.02 * areaPoly)
return false;
453 if (areaA < minHalfArea || areaB < minHalfArea)
return false;
454 const double fa = areaA / areaPoly;
455 if (fa < 0.02 || fa > 0.98)
return false;
456 if (outA) *outA = std::move(
a);
457 if (outB) *outB = std::move(
b);
458 if (fracA) *fracA = fa;
463 const size_t n = poly.size();
464 outTriangles.clear();
465 if (
n < 3)
return false;
467 std::vector<int>
idx(
n);
468 for (
size_t i = 0; i <
n; ++i)
idx[
size_t(i)] = int(i);
470 while (
idx.size() > 3 && guard++ < 100000) {
471 bool clipped =
false;
472 const size_t m =
idx.size();
473 for (
size_t i = 0; i <
m; ++i) {
474 const int iPrev =
idx[wrapIndex(
int(i) - 1,
int(
m))];
475 const int iCur =
idx[size_t(i)];
476 const int iNext =
idx[wrapIndex(
int(i) + 1,
int(
m))];
477 const Vec2&
a = poly[size_t(iPrev)];
478 const Vec2&
b = poly[size_t(iCur)];
479 const Vec2&
c = poly[size_t(iNext)];
481 if (
cross(
b -
a,
c -
b) <= 0.0)
continue;
484 for (
size_t j = 0; j <
m; ++j) {
485 const int v =
idx[size_t(j)];
486 if (
v == iPrev ||
v == iCur ||
v == iNext)
continue;
492 if (!empty)
continue;
493 outTriangles.push_back(iPrev);
494 outTriangles.push_back(iCur);
495 outTriangles.push_back(iNext);
496 idx.erase(
idx.begin() +
long(i));
501 outTriangles.clear();
505 if (
idx.size() == 3) {
506 outTriangles.push_back(
idx[0]);
507 outTriangles.push_back(
idx[1]);
508 outTriangles.push_back(
idx[2]);
510 return outTriangles.size() >= 3;
std::array< double, 10 > q
HexCoordinates to
Cell the unit walks towards on this segment.
std::map< Cell, int > best
std::vector< char > inside
Polygon approximatePolygon(const Polygon &ring)
Simplify a ring to its approximate polygon: consecutive edges with included angle > 135° are merged i...
bool validSplit(const Polygon &poly, const Polyline &split, double minHalfArea, Polygon *outA, Polygon *outB, double *fracA)
Validity of a candidate split: both halves simple, positive area, inside the original.
std::vector< Vec2 > Polyline
Open polyline (e.g. a streamline candidate or a street centerline).
double includedAngleDeg(const Vec2 &u, const Vec2 &v)
Included angle in degrees at the shared vertex between edge u (prev->shared) and v (shared->next),...
bool pointInPolygon(const Vec2 &p, const Polygon &poly)
Point-in-polygon test (ray casting; boundary counts as inside).
Vec2 pointAtBoundaryLength(const Polygon &poly, double s, BoundaryPosition *pos)
Interpolate the boundary point at arc length s in [0, perimeter).
bool splitPolygonByPolyline(const Polygon &poly, const Polyline &split, const BoundaryPosition &posA, const BoundaryPosition &posB, Polygon &outA, Polygon &outB)
Split a CCW simple polygon by a polyline whose endpoints lie on the boundary and whose interior point...
bool triangulatePolygon(const Polygon &poly, std::vector< int > &outTriangles)
Triangulate a simple polygon by ear clipping; returns CCW triangles (3*i..3*i+2).
bool polylineSelfIntersects(const Polyline &pl)
True if any two non-adjacent segments of the open polyline cross.
double closestPointOnSegment(const Vec2 &p, const Vec2 &a, const Vec2 &b, Vec2 *out)
Closest point on segment a-b; returns distance and writes out.
double distanceToSegment(const Vec2 &p, const Vec2 &a, const Vec2 &b)
Raster helper: does the pixel-center fall within eps of segment a-b?
Vec2 normalize(const Vec2 &a)
Normalize.
double dot(const Vec2 &a, const Vec2 &b)
Dot.
std::vector< Vec2 > Polygon
Closed polygon ring, stored CCW, without repeating the first point.
double cross(const Vec2 &a, const Vec2 &b)
Cross.
bool segmentsIntersect(const Vec2 &a, const Vec2 &b, const Vec2 &c, const Vec2 &d, Vec2 *out)
True if the open segments a-b and c-d properly cross; out receives the crossing.
double perimeter(const Polygon &poly)
Perimeter length of a closed ring.
double lengthSq(const Vec2 &a)
Length sq.
std::vector< BoundarySample > sampleBoundary(const Polygon &poly, int count)
Uniformly sample the polygon boundary (approx. count samples, at least 8). Samples are ordered along ...
bool polygonIsSimple(const Polygon &poly)
Simple polygon test: no self intersections among non-adjacent ring edges.
double signedArea(const Polygon &poly)
Return the raw (possibly negative) signed area of a polygon ring.
bool pointOnSegment(const Vec2 &p, const Vec2 &a, const Vec2 &b, double eps)
True if p lies on segment a-b (within tolerance).
double shapeIrregularity(const Polygon &approxRing, double gammaAngle, double gammaSide)
Shape irregularity metric of Eq. (1) over the approximate polygon: I = γ1·(1/N)·Σ(θi−θ̄)² + γ2·(1/(N·...
void cleanupRing(Polygon &poly, double eps)
Remove consecutive duplicate points (within eps) and points that create zero spikes.
double polylineLength(const Polyline &pl)
Total length of an open polyline.
bool segmentIntersectsPolyline(const Vec2 &a, const Vec2 &b, const Polyline &pl)
True if a segment crosses any segment of an open polyline (excluding shared endpoints).
Vec2 closestPointOnBoundary(const Polygon &poly, const Vec2 &p, BoundaryPosition *pos)
Closest point on the polygon boundary; writes edge index + t in [0,1].
bool ensureCCW(Polygon &poly)
Ensure the ring is CCW (positive signed area); returns whether it was flipped.
bool isCollinear(const Vec2 &prev, const Vec2 &shared, const Vec2 &next)
True when two consecutive edges are considered collinear (included angle > 135°).
Where a point sits on the polygon boundary: edge edgeIndex at parameter t in [0,1].
Minimal 2D vector used by the urban layout algorithms (paper coordinates).