41 if (line.size() < 2u) {
44 double best_distance = std::numeric_limits<double>::max();
46 double travelled = 0.0;
47 for (std::size_t i = 1u; i < line.size(); ++i) {
48 const Point &a = line[i - 1u];
49 const Point &b = line[i];
50 const double dx = bg::get<0>(b) - bg::get<0>(a);
51 const double dy = bg::get<1>(b) - bg::get<1>(a);
52 const double segment = std::hypot(dx, dy);
54 double t = ((bg::get<0>(p) - bg::get<0>(a)) * dx +
55 (bg::get<1>(p) - bg::get<1>(a)) * dy) / (segment * segment);
56 t = std::clamp(t, 0.0, 1.0);
57 const Point on(bg::get<0>(a) + t * dx, bg::get<1>(a) + t * dy);
61 best_s = travelled + t * segment;
71 return Point(0.0, 0.0);
76 double travelled = 0.0;
77 for (std::size_t i = 1u; i < line.size(); ++i) {
78 const double segment =
Hypot(line[i - 1u], line[i]);
79 if (travelled + segment >= s) {
80 const double t = (segment > 0.0) ? (s - travelled) / segment : 0.0;
82 bg::get<0>(line[i - 1u]) + t * (bg::get<0>(line[i]) - bg::get<0>(line[i - 1u])),
83 bg::get<1>(line[i - 1u]) + t * (bg::get<1>(line[i]) - bg::get<1>(line[i - 1u])));
108 if (line.size() < 2u) {
111 const double total =
Length(line);
112 from = std::clamp(from, 0.0, total);
113 to = std::clamp(to, 0.0, total);
118 double travelled = 0.0;
119 for (std::size_t i = 1u; i < line.size(); ++i) {
120 const double segment =
Hypot(line[i - 1u], line[i]);
121 const double at_end = travelled + segment;
122 if (at_end > from && at_end < to) {
123 bg::append(out, line[i]);
133 if (line.size() < 2u) {
136 if (std::abs(offset) < 1e-12) {
141 for (std::size_t i = 0u; i < line.size(); ++i) {
142 const std::size_t prev = (i == 0u) ? 0u : i - 1u;
143 const std::size_t next = (i + 1u == line.size()) ? i : i + 1u;
144 const double hx_in = bg::get<0>(line[i]) - bg::get<0>(line[prev]);
145 const double hy_in = bg::get<1>(line[i]) - bg::get<1>(line[prev]);
146 const double hx_out = bg::get<0>(line[next]) - bg::get<0>(line[i]);
147 const double hy_out = bg::get<1>(line[next]) - bg::get<1>(line[i]);
148 const double a_in = std::atan2(hy_in, hx_in);
149 const double a_out = std::atan2(hy_out, hx_out);
150 const double heading = (i == 0u) ? a_out
151 : (i + 1u == line.size()) ? a_in
153 const double half_turn = (i == 0u || i + 1u == line.size())
155 const double scale = std::max(0.25, std::cos(half_turn));
156 bg::append(out,
Point(
157 bg::get<0>(line[i]) - std::sin(
heading) * offset / scale,
158 bg::get<1>(line[i]) + std::cos(
heading) * offset / scale));
181 if (bg::is_valid(out)) {
187 bg::buffer(out, buffered,
188 bg::strategy::buffer::distance_symmetric<double>(0.0),
189 bg::strategy::buffer::side_straight(),
190 bg::strategy::buffer::join_miter(),
191 bg::strategy::buffer::end_flat(),
192 bg::strategy::buffer::point_square());
193 if (!buffered.empty()) {
194 std::size_t best = 0u;
195 double best_area = -1.0;
196 for (std::size_t i = 0u; i < buffered.size(); ++i) {
197 const double area = bg::area(buffered[i]);
198 if (area > best_area) {
203 return buffered[best];
208 std::vector<Polygon>
Polygonize(
const std::vector<LineString> &edges) {
212 std::size_t from = 0u;
214 double bearing = 0.0;
218 std::map<NodeKey, std::size_t> node_ids;
219 std::vector<Point>
nodes;
220 auto node_of = [&](
const Point &p) {
221 const NodeKey key = MakeKey(p);
222 auto it = node_ids.find(key);
223 if (it != node_ids.end()) {
226 const std::size_t
id =
nodes.size();
228 node_ids.emplace(key,
id);
232 std::vector<HalfEdge> half_edges;
234 for (std::size_t i = 1u; i < edge.size(); ++i) {
235 const std::size_t a = node_of(edge[i - 1u]);
236 const std::size_t b = node_of(edge[i]);
240 const double bearing = std::atan2(bg::get<1>(
nodes[b]) - bg::get<1>(
nodes[a]),
242 half_edges.push_back(HalfEdge{a, b, bearing,
false});
243 half_edges.push_back(HalfEdge{b, a,
NormalizeAngle(bearing + M_PI),
false});
247 std::vector<std::vector<std::size_t>> outgoing(
nodes.size());
248 for (std::size_t i = 0u; i < half_edges.size(); ++i) {
249 outgoing[half_edges[i].from].push_back(i);
251 for (
auto &list : outgoing) {
252 std::sort(list.begin(), list.end(), [&](std::size_t a, std::size_t b) {
253 return half_edges[a].bearing < half_edges[b].bearing;
257 std::vector<Polygon> faces;
258 for (std::size_t start = 0u; start < half_edges.size(); ++start) {
259 if (half_edges[start].used) {
263 std::size_t current = start;
265 for (std::size_t guard = 0u; guard < half_edges.size() + 1u; ++guard) {
266 half_edges[current].used =
true;
267 bg::append(ring,
nodes[half_edges[current].from]);
269 const std::size_t
node = half_edges[current].to;
270 const double back =
NormalizeAngle(half_edges[current].bearing + M_PI);
271 const auto &list = outgoing[
node];
275 std::size_t pick = list.front();
277 for (std::size_t candidate : list) {
278 double turn =
NormalizeAngle(half_edges[candidate].bearing - back);
288 if (current == start) {
293 if (!closed || ring.size() < 3u) {
296 bg::append(ring, ring.front());
302 if (bg::area(face) > 0.0 && bg::is_valid(face)) {
303 faces.push_back(face);