载入中...
搜索中...
未找到
RoadNetworkEditTarget.cpp
浏览该文件的文档.
2#include "procgen/editing/RoadNetworkEditTargetInternal.inc"
3
6
7#include <algorithm>
8#include <cmath>
9#include <limits>
10#include <utility>
11
12namespace eve::procgen_editing {
13namespace detail {
14
15const EditorValue* field(const EditorValue& value, const char* name) {
16 const auto* object = value.getIf<EditorValue::Object>();
17 if (!object) return nullptr;
18 const auto found = object->find(name);
19 return found == object->end() ? nullptr : &found->second;
20}
21
22bool readId(const EditorValue& value, const char* name, std::uint32_t& output) {
23 const auto* item = field(value, name);
24 const auto* id = item ? item->getIf<std::int64_t>() : nullptr;
25 if (!id || *id <= 0 || *id > std::numeric_limits<std::uint32_t>::max()) return false;
26 output = static_cast<std::uint32_t>(*id);
27 return true;
28}
29
30bool readNumber(const EditorValue& value, const char* name, float& output) {
31 const auto* item = field(value, name);
32 const auto* number = item ? item->getIf<double>() : nullptr;
33 if (!number || !std::isfinite(*number) || *number < -std::numeric_limits<float>::max() ||
34 *number > std::numeric_limits<float>::max())
35 return false;
36 output = static_cast<float>(*number);
37 return true;
38}
39
40bool readOptionalNumber(const EditorValue& value, const char* name, float& output) {
41 return field(value, name) == nullptr || readNumber(value, name, output);
42}
43
44bool readOptionalBool(const EditorValue& value, const char* name, bool& output) {
45 const auto* item = field(value, name);
46 if (!item) return true;
47 const auto* boolean = item->getIf<bool>();
48 if (!boolean) return false;
49 output = *boolean;
50 return true;
51}
52
53bool readSmallInt(const EditorValue& value, const char* name, int minimum, int maximum, int& output);
54
57 float maximumHeightDelta) {
58 struct ParameterSample {
59 float parameter = 0.f;
61 };
62 auto sampleEdge = [](const procgen::road::RoadEdge& edge) -> Result<std::vector<ParameterSample>> {
64 auto kind = path.setKindResult("catmullRom");
65 if (!kind.ok()) return Result<std::vector<ParameterSample>>::failure(kind.status());
66 path.setClosed(false);
67 for (const auto& point : edge.controlPoints) {
68 procgen::SplinePoint splinePoint;
69 splinePoint.x = point.x;
70 splinePoint.y = point.y;
71 splinePoint.z = point.z;
72 auto added = path.addPointResult(splinePoint);
73 if (!added.ok()) return Result<std::vector<ParameterSample>>::failure(added.status());
74 }
75 const int intervals = std::min(4096, std::max(32, path.segmentCount() * 24));
76 std::vector<ParameterSample> samples;
77 samples.reserve(static_cast<std::size_t>(intervals + 1));
78 for (int i = 0; i <= intervals; ++i) {
79 const float parameter = static_cast<float>(i) / static_cast<float>(intervals);
80 auto evaluated = path.evaluateResult(parameter);
81 if (!evaluated.ok()) return Result<std::vector<ParameterSample>>::failure(evaluated.status());
82 samples.push_back({parameter, {evaluated.value().x, evaluated.value().y, evaluated.value().z}});
83 }
84 return Result<std::vector<ParameterSample>>::success(std::move(samples));
85 };
86 auto firstSamples = sampleEdge(first);
87 if (!firstSamples.ok()) return Result<RoadIntersection>::failure(firstSamples.status());
88 auto secondSamples = sampleEdge(second);
89 if (!secondSamples.ok()) return Result<RoadIntersection>::failure(secondSamples.status());
90
91 std::vector<RoadIntersection> crossings;
92 constexpr float epsilon = 1e-5f;
93 const auto cross2 = [](float ax, float az, float bx, float bz) { return ax * bz - az * bx; };
94 for (std::size_t i = 0; i + 1 < firstSamples.value().size(); ++i) {
95 const auto& a = firstSamples.value()[i].point;
96 const auto& b = firstSamples.value()[i + 1].point;
97 const float rx = b.x - a.x, rz = b.z - a.z;
98 for (std::size_t j = 0; j + 1 < secondSamples.value().size(); ++j) {
99 const auto& c = secondSamples.value()[j].point;
100 const auto& d = secondSamples.value()[j + 1].point;
101 const float sx = d.x - c.x, sz = d.z - c.z;
102 const float denominator = cross2(rx, rz, sx, sz);
103 if (std::fabs(denominator) <= epsilon) continue;
104 const float qx = c.x - a.x, qz = c.z - a.z;
105 const float t = cross2(qx, qz, sx, sz) / denominator;
106 const float u = cross2(qx, qz, rx, rz) / denominator;
107 if (t < -epsilon || t > 1.f + epsilon || u < -epsilon || u > 1.f + epsilon) continue;
108 const float clampedT = std::clamp(t, 0.f, 1.f), clampedU = std::clamp(u, 0.f, 1.f);
109 const float x = a.x + rx * clampedT, z = a.z + rz * clampedT;
110 const float firstParameter = firstSamples.value()[i].parameter +
111 (firstSamples.value()[i + 1].parameter - firstSamples.value()[i].parameter) * clampedT;
112 const float secondParameter = secondSamples.value()[j].parameter +
113 (secondSamples.value()[j + 1].parameter - secondSamples.value()[j].parameter) * clampedU;
114 if (firstParameter <= epsilon || firstParameter >= 1.f - epsilon || secondParameter <= epsilon ||
115 secondParameter >= 1.f - epsilon)
116 continue;
117 const float firstY = a.y + (b.y - a.y) * clampedT;
118 const float secondY = c.y + (d.y - c.y) * clampedU;
119 if (std::fabs(firstY - secondY) > maximumHeightDelta) continue;
120 const bool duplicate = std::any_of(crossings.begin(), crossings.end(), [&](const auto& crossing) {
121 return std::fabs(crossing.mergedPoint.x - x) <= 1e-3f &&
122 std::fabs(crossing.mergedPoint.z - z) <= 1e-3f;
123 });
124 if (!duplicate) crossings.push_back({1, firstParameter, secondParameter, {x, firstY, z},
125 {x, secondY, z}, {x, (firstY + secondY) * 0.5f, z}});
126 }
127 }
128 if (crossings.size() == 1) return Result<RoadIntersection>::success(crossings.front());
129 RoadIntersection result;
130 result.count = crossings.empty() ? 0 : 2;
132}
133
134EditorValue encodeNode(std::uint32_t id, float x, float y, float z) {
135 return EditorValue::Object{{"id", std::int64_t{id}}, {"x", x}, {"y", y}, {"z", z}};
136}
137
138EditorValue encodeNodeRadius(std::uint32_t id, float junctionRadius) {
139 return EditorValue::Object{{"id", std::int64_t{id}}, {"junctionRadius", junctionRadius}};
140}
141
142bool decodeNodeRadius(const EditorValue& value, std::uint32_t& id, float& junctionRadius) {
143 return readId(value, "id", id) && readNumber(value, "junctionRadius", junctionRadius);
144}
145
147 return EditorValue::Object{{"id", std::int64_t{id}},
148 {"junctionControl", std::int64_t{static_cast<int>(control)}}};
149}
150
151bool decodeNodeControl(const EditorValue& value, std::uint32_t& id,
153 int encoded = 0;
154 if (!readId(value, "id", id) || !readSmallInt(value, "junctionControl", 0, 3, encoded)) return false;
155 control = static_cast<procgen::road::RoadJunctionControl>(encoded);
156 return true;
157}
158
159EditorValue encodeEdge(std::uint32_t id, const std::vector<procgen::road::RoadControlPoint>& points) {
160 EditorValue::Array encoded;
161 encoded.reserve(points.size());
162 for (const auto& point : points)
163 encoded.emplace_back(EditorValue::Array{EditorValue(point.x), EditorValue(point.y), EditorValue(point.z)});
164 return EditorValue::Object{{"id", std::int64_t{id}}, {"points", std::move(encoded)}};
165}
166
167EditorValue encodeStyle(std::uint32_t id, const procgen::road::RoadStyle& style) {
168 return EditorValue::Object{{"id", std::int64_t{id}},
169 {"laneWidth", style.laneWidth},
170 {"curbWidth", style.curbWidth},
171 {"curbHeight", style.curbHeight},
172 {"sidewalkWidth", style.sidewalkWidth},
173 {"sidewalkHeight", style.sidewalkHeight},
174 {"deckThickness", style.deckThickness},
175 {"pierWidth", style.pierWidth},
176 {"pierDepth", style.pierDepth},
177 {"pierSpacing", style.pierSpacing},
178 {"pierClearance", style.pierClearance},
179 {"markingWidth", style.markingWidth},
180 {"dashLength", style.dashLength},
181 {"dashGap", style.dashGap},
182 {"uvMeters", style.uvMeters},
183 {"speedLimitMps", style.speedLimitMps},
184 {"trafficPriority", std::int64_t{style.trafficPriority}},
185 {"sideObjectStartOffset", style.sideObjectStartOffset},
186 {"sideObjectEndOffset", style.sideObjectEndOffset},
187 {"sideObjectsLeft", style.sideObjectsLeft},
188 {"sideObjectsRight", style.sideObjectsRight}};
189}
190
191bool decodeStyle(const EditorValue& value, std::uint32_t& id, procgen::road::RoadStyle& style) {
192 return readId(value, "id", id) && readNumber(value, "laneWidth", style.laneWidth) &&
193 readNumber(value, "curbWidth", style.curbWidth) && readNumber(value, "curbHeight", style.curbHeight) &&
194 readNumber(value, "sidewalkWidth", style.sidewalkWidth) &&
195 readNumber(value, "sidewalkHeight", style.sidewalkHeight) &&
196 readNumber(value, "deckThickness", style.deckThickness) && readNumber(value, "pierWidth", style.pierWidth) &&
197 readNumber(value, "pierDepth", style.pierDepth) && readNumber(value, "pierSpacing", style.pierSpacing) &&
198 readNumber(value, "pierClearance", style.pierClearance) &&
199 readNumber(value, "markingWidth", style.markingWidth) && readNumber(value, "dashLength", style.dashLength) &&
200 readNumber(value, "dashGap", style.dashGap) && readNumber(value, "uvMeters", style.uvMeters) &&
201 readNumber(value, "speedLimitMps", style.speedLimitMps) &&
202 readSmallInt(value, "trafficPriority", 0, 255, style.trafficPriority) &&
203 readOptionalNumber(value, "sideObjectStartOffset", style.sideObjectStartOffset) &&
204 readOptionalNumber(value, "sideObjectEndOffset", style.sideObjectEndOffset) &&
205 readOptionalBool(value, "sideObjectsLeft", style.sideObjectsLeft) &&
206 readOptionalBool(value, "sideObjectsRight", style.sideObjectsRight);
207}
208
209bool decodeEdge(const EditorValue& value, std::uint32_t& id, std::vector<procgen::road::RoadControlPoint>& points) {
210 if (!readId(value, "id", id)) return false;
211 const auto* item = field(value, "points");
212 const auto* array = item ? item->getIf<EditorValue::Array>() : nullptr;
213 if (!array || array->size() < 2 || array->size() > 4096) return false;
214 points.clear();
215 points.reserve(array->size());
216 for (const auto& encoded : *array) {
217 const auto* xyz = encoded.getIf<EditorValue::Array>();
218 if (!xyz || xyz->size() != 3) return false;
219 float values[3]{};
220 for (int i = 0; i < 3; ++i) {
221 const auto* number = (*xyz)[static_cast<std::size_t>(i)].getIf<double>();
222 if (!number || !std::isfinite(*number) || std::fabs(*number) > std::numeric_limits<float>::max())
223 return false;
224 values[i] = static_cast<float>(*number);
225 }
226 points.push_back({values[0], values[1], values[2]});
227 }
228 return true;
229}
230
232 return EditorValue::Object{{"id", std::int64_t{node.id}},
233 {"x", node.x},
234 {"y", node.y},
235 {"z", node.z},
236 {"junctionRadius", node.junctionRadius},
237 {"junctionControl", std::int64_t{static_cast<int>(node.junctionControl)}}};
238}
239
241 if (!readId(value, "id", node.id) || !readNumber(value, "x", node.x) || !readNumber(value, "y", node.y) ||
242 !readNumber(value, "z", node.z) || !readNumber(value, "junctionRadius", node.junctionRadius))
243 return false;
244 const auto* encoded = field(value, "junctionControl");
245 if (!encoded) {
247 return true;
248 }
249 int control = 0;
250 if (!readSmallInt(value, "junctionControl", 0, 3, control)) return false;
251 node.junctionControl = static_cast<procgen::road::RoadJunctionControl>(control);
252 return true;
253}
254
255bool readSmallInt(const EditorValue& value, const char* name, int minimum, int maximum, int& output) {
256 const auto* item = field(value, name);
257 const auto* number = item ? item->getIf<std::int64_t>() : nullptr;
258 if (!number || *number < minimum || *number > maximum) return false;
259 output = static_cast<int>(*number);
260 return true;
261}
262
264 return EditorValue::Object{{"inEdge", std::int64_t{link.inEdge}},
265 {"inLane", std::int64_t{link.inLane}},
266 {"outEdge", std::int64_t{link.outEdge}},
267 {"outLane", std::int64_t{link.outLane}},
268 {"inDirection", std::int64_t{static_cast<int>(link.inDirection)}},
269 {"outDirection", std::int64_t{static_cast<int>(link.outDirection)}}};
270}
271
273 int inDirection = 0, outDirection = 0;
274 if (!readId(value, "inEdge", link.inEdge) || !readSmallInt(value, "inLane", 0, 7, link.inLane) ||
275 !readId(value, "outEdge", link.outEdge) || !readSmallInt(value, "outLane", 0, 7, link.outLane) ||
276 !readSmallInt(value, "inDirection", 0, 1, inDirection) ||
277 !readSmallInt(value, "outDirection", 0, 1, outDirection))
278 return false;
279 link.inDirection = static_cast<procgen::road::RoadLaneDirection>(inDirection);
280 link.outDirection = static_cast<procgen::road::RoadLaneDirection>(outDirection);
281 return true;
282}
283
285 auto object = *encodeLink(link).getIf<EditorValue::Object>();
286 object["restoreActive"] = restoreActive;
287 return object;
288}
289
291 const auto* encoded = field(value, "restoreActive");
292 const auto* flag = encoded ? encoded->getIf<bool>() : nullptr;
293 return flag && decodeLink(value, link) && (restoreActive = *flag, true);
294}
295
297 return left.inEdge == right.inEdge && left.inLane == right.inLane && left.outEdge == right.outEdge &&
298 left.outLane == right.outLane && left.inDirection == right.inDirection &&
299 left.outDirection == right.outDirection;
300}
301
302EditorValue encodeLinks(const std::vector<procgen::road::RoadLaneConnection>& links) {
303 EditorValue::Array encoded;
304 encoded.reserve(links.size());
305 for (const auto& link : links) encoded.push_back(encodeLink(link));
306 return EditorValue::Object{{"links", std::move(encoded)}};
307}
308
309bool decodeLinks(const EditorValue& value, std::vector<procgen::road::RoadLaneConnection>& links) {
310 const auto* item = field(value, "links");
311 const auto* array = item ? item->getIf<EditorValue::Array>() : nullptr;
312 if (!array || array->size() > 65536u) return false;
313 links.clear();
314 links.reserve(array->size());
315 for (const auto& encoded : *array) {
317 if (!decodeLink(encoded, link)) return false;
318 links.push_back(link);
319 }
320 return true;
321}
322
323EditorValue encodeLaneCounts(std::uint32_t edgeId, int lanesForward, int lanesBackward,
324 const std::vector<procgen::road::RoadLaneConnection>& restoreLinks,
325 const std::vector<procgen::road::RoadLaneConnection>& restoreBlockedLinks) {
326 EditorValue::Array links;
327 links.reserve(restoreLinks.size());
328 for (const auto& link : restoreLinks) links.push_back(encodeLink(link));
329 return EditorValue::Object{{"id", std::int64_t{edgeId}},
330 {"lanesForward", std::int64_t{lanesForward}},
331 {"lanesBackward", std::int64_t{lanesBackward}},
332 {"restoreLinks", std::move(links)},
333 {"restoreBlockedLinks", encodeLinks(restoreBlockedLinks)}};
334}
335
336bool decodeLaneCounts(const EditorValue& value, std::uint32_t& edgeId, int& lanesForward, int& lanesBackward,
337 std::vector<procgen::road::RoadLaneConnection>& restoreLinks,
338 std::vector<procgen::road::RoadLaneConnection>& restoreBlockedLinks) {
339 if (!readId(value, "id", edgeId) || !readSmallInt(value, "lanesForward", 0, 8, lanesForward) ||
340 !readSmallInt(value, "lanesBackward", 0, 8, lanesBackward))
341 return false;
342 const auto* encoded = field(value, "restoreLinks");
343 const auto* links = encoded ? encoded->getIf<EditorValue::Array>() : nullptr;
344 if (!links || links->size() > 65536u) return false;
345 restoreLinks.clear();
346 restoreLinks.reserve(links->size());
347 for (const auto& entry : *links) {
349 if (!decodeLink(entry, link)) return false;
350 restoreLinks.push_back(link);
351 }
352 const auto* blocked = field(value, "restoreBlockedLinks");
353 return blocked && decodeLinks(*blocked, restoreBlockedLinks);
354}
355
357 std::uint32_t edgeId, bool fromEndpoint, std::uint32_t nodeId,
358 const std::vector<procgen::road::RoadLaneConnection>& restoreLinks,
359 const std::vector<procgen::road::RoadLaneConnection>& restoreBlockedLinks) {
360 return EditorValue::Object{{"id", std::int64_t{edgeId}},
361 {"fromEndpoint", fromEndpoint},
362 {"nodeId", std::int64_t{nodeId}},
363 {"restoreLinks", encodeLinks(restoreLinks)},
364 {"restoreBlockedLinks", encodeLinks(restoreBlockedLinks)}};
365}
366
367bool decodeReconnectEndpoint(EditorValue const& value, std::uint32_t& edgeId, bool& fromEndpoint,
368 std::uint32_t& nodeId,
369 std::vector<procgen::road::RoadLaneConnection>& restoreLinks,
370 std::vector<procgen::road::RoadLaneConnection>& restoreBlockedLinks) {
371 const auto* endpointValue = field(value, "fromEndpoint");
372 const auto* endpoint = endpointValue ? endpointValue->getIf<bool>() : nullptr;
373 const auto* links = field(value, "restoreLinks");
374 const auto* blocked = field(value, "restoreBlockedLinks");
375 return endpoint && links && blocked && readId(value, "id", edgeId) && readId(value, "nodeId", nodeId) &&
376 decodeLinks(*links, restoreLinks) && decodeLinks(*blocked, restoreBlockedLinks) &&
377 (fromEndpoint = *endpoint, true);
378}
379
380EditorValue encodeDetachEndpoint(std::uint32_t edgeId, bool fromEndpoint, std::uint32_t nodeId) {
381 return EditorValue::Object{{"id", std::int64_t{edgeId}},
382 {"fromEndpoint", fromEndpoint},
383 {"nodeId", std::int64_t{nodeId}}};
384}
385
386bool decodeDetachEndpoint(const EditorValue& value, std::uint32_t& edgeId, bool& fromEndpoint,
387 std::uint32_t& nodeId) {
388 const auto* endpointValue = field(value, "fromEndpoint");
389 const auto* endpoint = endpointValue ? endpointValue->getIf<bool>() : nullptr;
390 return endpoint && readId(value, "id", edgeId) && readId(value, "nodeId", nodeId) &&
391 (fromEndpoint = *endpoint, true);
392}
393
395 std::uint32_t edgeId, bool fromEndpoint, std::uint32_t oldNodeId, std::uint32_t detachedNodeId,
396 const std::vector<procgen::road::RoadLaneConnection>& restoreLinks,
397 const std::vector<procgen::road::RoadLaneConnection>& restoreBlockedLinks) {
398 return EditorValue::Object{{"id", std::int64_t{edgeId}},
399 {"fromEndpoint", fromEndpoint},
400 {"oldNodeId", std::int64_t{oldNodeId}},
401 {"detachedNodeId", std::int64_t{detachedNodeId}},
402 {"restoreLinks", encodeLinks(restoreLinks)},
403 {"restoreBlockedLinks", encodeLinks(restoreBlockedLinks)}};
404}
405
406bool decodeReattachEndpoint(const EditorValue& value, std::uint32_t& edgeId, bool& fromEndpoint,
407 std::uint32_t& oldNodeId, std::uint32_t& detachedNodeId,
408 std::vector<procgen::road::RoadLaneConnection>& restoreLinks,
409 std::vector<procgen::road::RoadLaneConnection>& restoreBlockedLinks) {
410 const auto* endpointValue = field(value, "fromEndpoint");
411 const auto* endpoint = endpointValue ? endpointValue->getIf<bool>() : nullptr;
412 const auto* links = field(value, "restoreLinks");
413 const auto* blocked = field(value, "restoreBlockedLinks");
414 return endpoint && links && blocked && readId(value, "id", edgeId) &&
415 readId(value, "oldNodeId", oldNodeId) && readId(value, "detachedNodeId", detachedNodeId) &&
416 decodeLinks(*links, restoreLinks) && decodeLinks(*blocked, restoreBlockedLinks) &&
417 (fromEndpoint = *endpoint, true);
418}
419
420EditorValue encodeSplit(std::uint32_t edgeId, std::size_t controlPointIndex, float junctionRadius,
422 return EditorValue::Object{{"id", std::int64_t{edgeId}},
423 {"controlPointIndex", std::int64_t{static_cast<std::int64_t>(controlPointIndex)}},
424 {"junctionRadius", junctionRadius},
425 {"nodeId", std::int64_t{result.nodeId}},
426 {"secondEdgeId", std::int64_t{result.secondEdgeId}}};
427}
428
429bool decodeSplit(const EditorValue& value, std::uint32_t& edgeId, std::size_t& controlPointIndex,
430 float& junctionRadius, std::uint32_t& nodeId, std::uint32_t& secondEdgeId) {
431 const auto* indexValue = field(value, "controlPointIndex");
432 const auto* index = indexValue ? indexValue->getIf<std::int64_t>() : nullptr;
433 if (!readId(value, "id", edgeId) || !index || *index <= 0 || *index > 4095 ||
434 !readNumber(value, "junctionRadius", junctionRadius) || !readId(value, "nodeId", nodeId) ||
435 !readId(value, "secondEdgeId", secondEdgeId))
436 return false;
437 controlPointIndex = static_cast<std::size_t>(*index);
438 return true;
439}
440
442 float junctionRadius, const procgen::road::RoadEdgeSplitResult& result) {
443 return EditorValue::Object{{"id", std::int64_t{edgeId}},
444 {"x", position.x},
445 {"y", position.y},
446 {"z", position.z},
447 {"maxDistance", maxDistance},
448 {"junctionRadius", junctionRadius},
449 {"nodeId", std::int64_t{result.nodeId}},
450 {"secondEdgeId", std::int64_t{result.secondEdgeId}}};
451}
452
453bool decodePositionSplit(const EditorValue& value, std::uint32_t& edgeId,
454 procgen::road::RoadControlPoint& position, float& maxDistance, float& junctionRadius,
455 std::uint32_t& nodeId, std::uint32_t& secondEdgeId) {
456 return readId(value, "id", edgeId) && readNumber(value, "x", position.x) &&
457 readNumber(value, "y", position.y) && readNumber(value, "z", position.z) &&
458 readNumber(value, "maxDistance", maxDistance) &&
459 readNumber(value, "junctionRadius", junctionRadius) && readId(value, "nodeId", nodeId) &&
460 readId(value, "secondEdgeId", secondEdgeId);
461}
462
463EditorValue encodeMergeNodes(std::uint32_t keepNodeId, std::uint32_t removeNodeId, float maxDistance) {
464 return EditorValue::Object{{"keepNodeId", std::int64_t{keepNodeId}},
465 {"removeNodeId", std::int64_t{removeNodeId}},
466 {"maxDistance", maxDistance}};
467}
468
469bool decodeMergeNodes(const EditorValue& value, std::uint32_t& keepNodeId, std::uint32_t& removeNodeId,
470 float& maxDistance) {
471 return readId(value, "keepNodeId", keepNodeId) && readId(value, "removeNodeId", removeNodeId) &&
472 readNumber(value, "maxDistance", maxDistance);
473}
474
476 const std::vector<procgen::road::RoadEdge>& affectedEdges,
477 const std::vector<procgen::road::RoadLaneConnection>& affectedLinks,
478 const std::vector<procgen::road::RoadLaneConnection>& affectedBlockedLinks) {
480 edges.reserve(affectedEdges.size());
481 for (const auto& edge : affectedEdges) edges.push_back(encodeEdgeRecord(edge, {}));
482 EditorValue::Array links;
483 links.reserve(affectedLinks.size());
484 for (const auto& link : affectedLinks) links.push_back(encodeLink(link));
485 EditorValue::Array blockedLinks;
486 blockedLinks.reserve(affectedBlockedLinks.size());
487 for (const auto& link : affectedBlockedLinks) blockedLinks.push_back(encodeLink(link));
488 return EditorValue::Object{{"node", encodeNodeRecord(removedNode)},
489 {"edges", std::move(edges)},
490 {"laneLinks", std::move(links)},
491 {"blockedLaneLinks", std::move(blockedLinks)}};
492}
493
495 std::vector<procgen::road::RoadEdge>& affectedEdges,
496 std::vector<procgen::road::RoadLaneConnection>& affectedLinks,
497 std::vector<procgen::road::RoadLaneConnection>& affectedBlockedLinks) {
498 const auto* nodeValue = field(value, "node");
499 const auto* edgesValue = field(value, "edges");
500 const auto* linksValue = field(value, "laneLinks");
501 const auto* blockedValue = field(value, "blockedLaneLinks");
502 const auto* edges = edgesValue ? edgesValue->getIf<EditorValue::Array>() : nullptr;
503 const auto* links = linksValue ? linksValue->getIf<EditorValue::Array>() : nullptr;
504 const auto* blocked = blockedValue ? blockedValue->getIf<EditorValue::Array>() : nullptr;
505 if (!nodeValue || !edges || !links || !blocked || edges->size() > 65536u || links->size() > 262144u ||
506 blocked->size() > 262144u ||
507 !decodeNodeRecord(*nodeValue, removedNode))
508 return false;
509 affectedEdges.clear();
510 affectedEdges.reserve(edges->size());
511 for (const auto& encoded : *edges) {
513 std::vector<procgen::road::RoadLaneConnection> embeddedLinks;
514 if (!decodeEdgeRecord(encoded, edge, embeddedLinks) || !embeddedLinks.empty()) return false;
515 affectedEdges.push_back(std::move(edge));
516 }
517 affectedLinks.clear();
518 affectedLinks.reserve(links->size());
519 for (const auto& encoded : *links) {
521 if (!decodeLink(encoded, link)) return false;
522 affectedLinks.push_back(link);
523 }
524 affectedBlockedLinks.clear();
525 affectedBlockedLinks.reserve(blocked->size());
526 for (const auto& encoded : *blocked) {
528 if (!decodeLink(encoded, link)) return false;
529 affectedBlockedLinks.push_back(link);
530 }
531 return true;
532}
533
535 const std::vector<procgen::road::RoadLaneConnection>& links,
536 const std::vector<procgen::road::RoadLaneConnection>& blockedLinks,
537 std::uint32_t nodeId, std::uint32_t secondEdgeId) {
538 return EditorValue::Object{{"nodeId", std::int64_t{nodeId}},
539 {"secondEdgeId", std::int64_t{secondEdgeId}},
540 {"edge", encodeEdgeRecord(edge, links, blockedLinks)}};
541}
542
544 std::vector<procgen::road::RoadLaneConnection>& links,
545 std::vector<procgen::road::RoadLaneConnection>& blockedLinks,
546 std::uint32_t& nodeId, std::uint32_t& secondEdgeId) {
547 const auto* encodedEdge = field(value, "edge");
548 return readId(value, "nodeId", nodeId) && readId(value, "secondEdgeId", secondEdgeId) && encodedEdge &&
549 decodeEdgeRecord(*encodedEdge, edge, links, &blockedLinks);
550}
551
553 float maxDistance, float junctionRadius,
555 const procgen::road::RoadEdge& branch) {
556 auto object = *encodePositionSplit(edgeId, position, maxDistance, junctionRadius, split)
558 object["branch"] = encodeEdgeRecord(branch, {});
559 return object;
560}
561
562bool decodeConnectAtPosition(const EditorValue& value, std::uint32_t& edgeId,
563 procgen::road::RoadControlPoint& position, float& maxDistance, float& junctionRadius,
564 std::uint32_t& nodeId, std::uint32_t& secondEdgeId,
565 procgen::road::RoadEdge& branch) {
566 const auto* branchValue = field(value, "branch");
567 std::vector<procgen::road::RoadLaneConnection> embeddedLinks;
568 return decodePositionSplit(value, edgeId, position, maxDistance, junctionRadius, nodeId, secondEdgeId) &&
569 branchValue && decodeEdgeRecord(*branchValue, branch, embeddedLinks) && embeddedLinks.empty();
570}
571
573 const std::vector<procgen::road::RoadLaneConnection>& originalLinks,
574 const std::vector<procgen::road::RoadLaneConnection>& originalBlockedLinks,
575 std::uint32_t nodeId, std::uint32_t secondEdgeId,
576 std::uint32_t branchEdgeId) {
577 auto object = *encodeUnsplit(original, originalLinks, originalBlockedLinks, nodeId, secondEdgeId)
579 object["branchEdgeId"] = std::int64_t{branchEdgeId};
580 return object;
581}
582
584 std::vector<procgen::road::RoadLaneConnection>& originalLinks,
585 std::vector<procgen::road::RoadLaneConnection>& originalBlockedLinks,
586 std::uint32_t& nodeId, std::uint32_t& secondEdgeId,
587 std::uint32_t& branchEdgeId) {
588 return decodeUnsplit(value, original, originalLinks, originalBlockedLinks, nodeId, secondEdgeId) &&
589 readId(value, "branchEdgeId", branchEdgeId);
590}
591
592EditorValue encodeIntersectionConnect(std::uint32_t firstEdgeId, std::uint32_t secondEdgeId,
593 float firstParameter, float secondParameter,
594 procgen::road::RoadControlPoint mergedPoint, float maximumHeightDelta,
595 float junctionRadius, const procgen::road::RoadEdgeSplitResult& firstSplit,
596 const procgen::road::RoadEdgeSplitResult& secondSplit) {
597 return EditorValue::Object{
598 {"firstEdgeId", std::int64_t{firstEdgeId}},
599 {"secondEdgeId", std::int64_t{secondEdgeId}},
600 {"firstParameter", firstParameter},
601 {"secondParameter", secondParameter},
602 {"mergedPoint", EditorValue::Array{mergedPoint.x, mergedPoint.y, mergedPoint.z}},
603 {"maximumHeightDelta", maximumHeightDelta},
604 {"junctionRadius", junctionRadius},
605 {"firstNodeId", std::int64_t{firstSplit.nodeId}},
606 {"firstSecondEdgeId", std::int64_t{firstSplit.secondEdgeId}},
607 {"secondNodeId", std::int64_t{secondSplit.nodeId}},
608 {"secondSecondEdgeId", std::int64_t{secondSplit.secondEdgeId}}};
609}
610
612 const auto* item = field(value, name);
613 const auto* values = item ? item->getIf<EditorValue::Array>() : nullptr;
614 if (!values || values->size() != 3) return false;
615 const auto* x = (*values)[0].getIf<double>();
616 const auto* y = (*values)[1].getIf<double>();
617 const auto* z = (*values)[2].getIf<double>();
618 if (!x || !y || !z || !std::isfinite(*x) || !std::isfinite(*y) || !std::isfinite(*z)) return false;
619 point = {static_cast<float>(*x), static_cast<float>(*y), static_cast<float>(*z)};
620 return true;
621}
622
623bool decodeIntersectionConnect(const EditorValue& value, std::uint32_t& firstEdgeId,
624 std::uint32_t& secondEdgeId, float& firstParameter, float& secondParameter,
625 procgen::road::RoadControlPoint& mergedPoint, float& maximumHeightDelta,
626 float& junctionRadius, std::uint32_t& firstNodeId,
627 std::uint32_t& firstSecondEdgeId, std::uint32_t& secondNodeId,
628 std::uint32_t& secondSecondEdgeId) {
629 return readId(value, "firstEdgeId", firstEdgeId) && readId(value, "secondEdgeId", secondEdgeId) &&
630 readNumber(value, "firstParameter", firstParameter) &&
631 readNumber(value, "secondParameter", secondParameter) &&
632 decodePoint(value, "mergedPoint", mergedPoint) &&
633 readNumber(value, "maximumHeightDelta", maximumHeightDelta) &&
634 readNumber(value, "junctionRadius", junctionRadius) && readId(value, "firstNodeId", firstNodeId) &&
635 readId(value, "firstSecondEdgeId", firstSecondEdgeId) &&
636 readId(value, "secondNodeId", secondNodeId) &&
637 readId(value, "secondSecondEdgeId", secondSecondEdgeId);
638}
639
642 const std::vector<procgen::road::RoadLaneConnection>& links,
643 const std::vector<procgen::road::RoadLaneConnection>& blockedLinks,
644 std::uint32_t sharedNodeId, std::uint32_t firstSecondEdgeId,
645 std::uint32_t secondSecondEdgeId) {
646 EditorValue::Array encodedLinks;
647 encodedLinks.reserve(links.size());
648 for (const auto& link : links) encodedLinks.push_back(encodeLink(link));
649 EditorValue::Array encodedBlockedLinks;
650 encodedBlockedLinks.reserve(blockedLinks.size());
651 for (const auto& link : blockedLinks) encodedBlockedLinks.push_back(encodeLink(link));
652 return EditorValue::Object{{"first", encodeEdgeRecord(first, {})},
653 {"second", encodeEdgeRecord(second, {})},
654 {"laneLinks", std::move(encodedLinks)},
655 {"blockedLaneLinks", std::move(encodedBlockedLinks)},
656 {"sharedNodeId", std::int64_t{sharedNodeId}},
657 {"firstSecondEdgeId", std::int64_t{firstSecondEdgeId}},
658 {"secondSecondEdgeId", std::int64_t{secondSecondEdgeId}}};
659}
660
663 std::vector<procgen::road::RoadLaneConnection>& links,
664 std::vector<procgen::road::RoadLaneConnection>& blockedLinks,
665 std::uint32_t& sharedNodeId, std::uint32_t& firstSecondEdgeId,
666 std::uint32_t& secondSecondEdgeId) {
667 const auto* firstValue = field(value, "first");
668 const auto* secondValue = field(value, "second");
669 const auto* linksValue = field(value, "laneLinks");
670 const auto* blockedValue = field(value, "blockedLaneLinks");
671 const auto* encodedLinks = linksValue ? linksValue->getIf<EditorValue::Array>() : nullptr;
672 const auto* encodedBlockedLinks = blockedValue ? blockedValue->getIf<EditorValue::Array>() : nullptr;
673 std::vector<procgen::road::RoadLaneConnection> embedded;
674 if (!firstValue || !secondValue || !encodedLinks || !encodedBlockedLinks || encodedLinks->size() > 262144u ||
675 encodedBlockedLinks->size() > 262144u ||
676 !decodeEdgeRecord(*firstValue, first, embedded) || !embedded.empty() ||
677 !decodeEdgeRecord(*secondValue, second, embedded) || !embedded.empty() ||
678 !readId(value, "sharedNodeId", sharedNodeId) ||
679 !readId(value, "firstSecondEdgeId", firstSecondEdgeId) ||
680 !readId(value, "secondSecondEdgeId", secondSecondEdgeId))
681 return false;
682 links.clear();
683 links.reserve(encodedLinks->size());
684 for (const auto& encoded : *encodedLinks) {
686 if (!decodeLink(encoded, link)) return false;
687 links.push_back(link);
688 }
689 blockedLinks.clear();
690 blockedLinks.reserve(encodedBlockedLinks->size());
691 for (const auto& encoded : *encodedBlockedLinks) {
693 if (!decodeLink(encoded, link)) return false;
694 blockedLinks.push_back(link);
695 }
696 return true;
697}
698
700 const std::vector<procgen::road::RoadLaneConnection>& links,
701 const std::vector<procgen::road::RoadLaneConnection>& blockedLinks) {
702 auto object = *encodeEdge(edge.id, edge.controlPoints).getIf<EditorValue::Object>();
703 object["from"] = std::int64_t{edge.from};
704 object["to"] = std::int64_t{edge.to};
705 object["lanesForward"] = std::int64_t{edge.lanesForward};
706 object["lanesBackward"] = std::int64_t{edge.lanesBackward};
707 object["style"] = encodeStyle(edge.id, edge.style);
708 EditorValue::Array encodedLinks;
709 encodedLinks.reserve(links.size());
710 for (const auto& link : links) encodedLinks.push_back(encodeLink(link));
711 object["laneLinks"] = std::move(encodedLinks);
712 EditorValue::Array encodedBlocked;
713 encodedBlocked.reserve(blockedLinks.size());
714 for (const auto& link : blockedLinks) encodedBlocked.push_back(encodeLink(link));
715 object["blockedLaneLinks"] = std::move(encodedBlocked);
716 return object;
717}
718
720 std::vector<procgen::road::RoadLaneConnection>& links,
721 std::vector<procgen::road::RoadLaneConnection>* blockedLinks) {
722 if (!decodeEdge(value, edge.id, edge.controlPoints) || !readId(value, "from", edge.from) ||
723 !readId(value, "to", edge.to) || !readSmallInt(value, "lanesForward", 0, 8, edge.lanesForward) ||
724 !readSmallInt(value, "lanesBackward", 0, 8, edge.lanesBackward))
725 return false;
726 const auto* encodedStyle = field(value, "style");
727 std::uint32_t styleEdgeId = 0;
728 if (!encodedStyle || !decodeStyle(*encodedStyle, styleEdgeId, edge.style) || styleEdgeId != edge.id) return false;
729 const auto* encodedLinks = field(value, "laneLinks");
730 const auto* array = encodedLinks ? encodedLinks->getIf<EditorValue::Array>() : nullptr;
731 const auto* encodedBlocked = field(value, "blockedLaneLinks");
732 const auto* blockedArray = encodedBlocked ? encodedBlocked->getIf<EditorValue::Array>() : nullptr;
733 if (!array || array->size() > 65536 || (blockedArray && blockedArray->size() > 65536) ||
734 (!blockedLinks && blockedArray && !blockedArray->empty()))
735 return false;
736 links.clear();
737 links.reserve(array->size());
738 for (const auto& encoded : *array) {
740 if (!decodeLink(encoded, link)) return false;
741 links.push_back(link);
742 }
743 if (blockedLinks) {
744 blockedLinks->clear();
745 if (blockedArray) {
746 blockedLinks->reserve(blockedArray->size());
747 for (const auto& encoded : *blockedArray) {
749 if (!decodeLink(encoded, link)) return false;
750 blockedLinks->push_back(link);
751 }
752 }
753 }
754 return true;
755}
756
759 const double dx = static_cast<double>(to.x) - from.x;
760 const double dy = static_cast<double>(to.y) - from.y;
761 const double dz = static_cast<double>(to.z) - from.z;
762 const double length = std::sqrt(dx * dx + dy * dy + dz * dz);
764 line.id = std::move(id);
765 line.kind = "line";
766 line.position = {from.x, from.y, from.z};
767 line.color = {0.25, 0.75, 1.0, 0.9};
768 line.length = length;
769 line.dashed = true;
770 if (length > 0.0) line.direction = {dx / length, dy / length, dz / length};
771 return line;
772}
773
774} // namespace detail
775
776using namespace detail;
777
778RoadNetworkEditTarget::RoadNetworkEditTarget(std::string id, procgen::road::RoadNetwork& network)
779 : id_(std::move(id)), network_(&network) {}
780
781std::unique_ptr<RoadNetworkEditTarget> RoadNetworkEditTarget::createOwned(std::string id) {
782 return std::unique_ptr<RoadNetworkEditTarget>(
783 new RoadNetworkEditTarget(std::move(id), std::make_unique<procgen::road::RoadNetwork>()));
784}
785
786RoadNetworkEditTarget::RoadNetworkEditTarget(std::string id, std::unique_ptr<procgen::road::RoadNetwork> candidate)
787 : id_(std::move(id)), candidate_(std::move(candidate)), network_(candidate_.get()) {}
788
793
799
800editing::GizmoSnapshot RoadNetworkEditTarget::gizmo(std::size_t maximumPrimitives, bool showLabels) const {
801 editing::GizmoSnapshot snapshot;
802 snapshot.target = id_;
803 snapshot.targetRevision = revision();
804
805 std::size_t required = network_->nodes().size() * (showLabels ? 2u : 1u);
806 for (const auto& edge : network_->edges()) {
807 if (edge.controlPoints.size() >= 2) required += edge.controlPoints.size() - 1;
808 if (edge.controlPoints.size() > 2) required += edge.controlPoints.size() - 2;
809 }
810 if (maximumPrimitives == 0 || required > maximumPrimitives) {
811 snapshot.status = EditorStatus::Rejected;
812 snapshot.diagnostics.push_back(editing::ruleDiagnostic(
814 editing::DiagnosticSeverity::Error, "Road overlay exceeds the primitive budget"));
815 return snapshot;
816 }
817
818 snapshot.primitives.reserve(required);
819 for (const auto& node : network_->nodes()) {
820 const auto identity = std::to_string(node.id);
822 handle.id = "road.node." + identity;
823 handle.kind = "sphere";
824 handle.position = {node.x, node.y, node.z};
825 handle.color = {1.0, 0.65, 0.15, 1.0};
826 handle.radius = 0.45;
827 snapshot.primitives.push_back(std::move(handle));
828 if (showLabels) {
830 label.id = "road.node-label." + identity;
831 label.kind = "text";
832 label.position = {node.x, static_cast<double>(node.y) + 0.7, node.z};
833 label.color = {1.0, 0.9, 0.55, 1.0};
834 label.text = "Node " + identity;
835 snapshot.primitives.push_back(std::move(label));
836 }
837 }
838 for (const auto& edge : network_->edges()) {
839 const auto edgeIdentity = std::to_string(edge.id);
840 for (std::size_t i = 1; i < edge.controlPoints.size(); ++i) {
841 snapshot.primitives.push_back(linePrimitive("road.edge." + edgeIdentity + ".segment." +
842 std::to_string(i - 1),
843 edge.controlPoints[i - 1], edge.controlPoints[i]));
844 }
845 for (std::size_t i = 1; i + 1 < edge.controlPoints.size(); ++i) {
846 const auto& point = edge.controlPoints[i];
848 handle.id = "road.edge." + edgeIdentity + ".control." + std::to_string(i);
849 handle.kind = "sphere";
850 handle.position = {point.x, point.y, point.z};
851 handle.color = {0.2, 0.8, 1.0, 1.0};
852 handle.radius = 0.3;
853 snapshot.primitives.push_back(std::move(handle));
854 }
855 }
856 snapshot.status = EditorStatus::Applied;
857 return snapshot;
858}
859
862 nodes.reserve(network_->nodes().size());
863 for (const auto& node : network_->nodes()) nodes.push_back(encodeNodeRecord(node));
864
866 edges.reserve(network_->edges().size());
867 for (const auto& edge : network_->edges()) edges.push_back(encodeEdgeRecord(edge, {}));
868
869 EditorValue::Array laneLinks;
870 laneLinks.reserve(network_->laneLinks().size());
871 for (const auto& link : network_->laneLinks()) laneLinks.push_back(encodeLink(link));
872 EditorValue::Array blockedLaneLinks;
873 blockedLaneLinks.reserve(network_->blockedLaneLinks().size());
874 for (const auto& link : network_->blockedLaneLinks()) blockedLaneLinks.push_back(encodeLink(link));
875
876 return EditorValue::Object{{"schema", "eve.procgen.roadNetwork"},
877 {"schemaVersion", std::int64_t{1}},
878 {"nodes", std::move(nodes)},
879 {"edges", std::move(edges)},
880 {"laneLinks", std::move(laneLinks)},
881 {"blockedLaneLinks", std::move(blockedLaneLinks)}};
882}
883
885 const auto* schemaValue = field(snapshot, "schema");
886 const auto* versionValue = field(snapshot, "schemaVersion");
887 const auto* nodesValue = field(snapshot, "nodes");
888 const auto* edgesValue = field(snapshot, "edges");
889 const auto* linksValue = field(snapshot, "laneLinks");
890 const auto* blockedValue = field(snapshot, "blockedLaneLinks");
891 const auto* schema = schemaValue ? schemaValue->getIf<std::string>() : nullptr;
892 const auto* version = versionValue ? versionValue->getIf<std::int64_t>() : nullptr;
893 const auto* nodes = nodesValue ? nodesValue->getIf<EditorValue::Array>() : nullptr;
894 const auto* edges = edgesValue ? edgesValue->getIf<EditorValue::Array>() : nullptr;
895 const auto* links = linksValue ? linksValue->getIf<EditorValue::Array>() : nullptr;
896 const auto* blocked = blockedValue ? blockedValue->getIf<EditorValue::Array>() : nullptr;
897 if (!schema || *schema != "eve.procgen.roadNetwork" || !version || *version != 1 || !nodes || !edges ||
898 !links)
899 return reject<void>("editor.road.invalid-snapshot", "Road snapshot schema is unsupported",
900 EditorStatus::Unsupported);
901 constexpr std::size_t maximumNodes = 65536u, maximumEdges = 65536u, maximumLinks = 262144u;
902 if (nodes->size() > maximumNodes || edges->size() > maximumEdges || links->size() > maximumLinks ||
903 (blocked && blocked->size() > maximumLinks))
904 return reject<void>("editor.road.snapshot-budget", "Road snapshot exceeds the graph budget");
905
907 for (const auto& encoded : *nodes) {
909 if (!decodeNodeRecord(encoded, node))
910 return reject<void>("editor.road.invalid-snapshot-node", "Road snapshot contains an invalid node");
911 auto restored = candidate.restoreNode(node);
912 if (!restored.ok()) return roadFailure<void>(restored.status());
913 }
914 for (const auto& encoded : *edges) {
916 std::vector<procgen::road::RoadLaneConnection> embeddedLinks;
917 if (!decodeEdgeRecord(encoded, edge, embeddedLinks) || !embeddedLinks.empty())
918 return reject<void>("editor.road.invalid-snapshot-edge", "Road snapshot contains an invalid edge");
919 auto restored = candidate.restoreEdge(std::move(edge));
920 if (!restored.ok()) return roadFailure<void>(restored.status());
921 }
922 for (const auto& encoded : *links) {
924 if (!decodeLink(encoded, link))
925 return reject<void>("editor.road.invalid-snapshot-link", "Road snapshot contains an invalid lane link");
926 auto restored = candidate.addLaneLink(link);
927 if (!restored.ok()) return roadFailure<void>(restored.status());
928 }
929 if (blocked) {
930 for (const auto& encoded : *blocked) {
932 if (!decodeLink(encoded, link))
933 return reject<void>("editor.road.invalid-snapshot-block",
934 "Road snapshot contains an invalid blocked lane link");
935 auto restored = candidate.blockLaneLink(link);
936 if (!restored.ok()) return roadFailure<void>(restored.status());
937 if (restored.value())
938 return reject<void>("editor.road.invalid-snapshot-block",
939 "Road snapshot marks the same lane link active and blocked");
940 }
941 }
942 auto valid = candidate.validate();
943 if (!valid.ok()) return roadFailure<void>(valid.status());
944 *network_ = std::move(candidate);
945 dirty_.include(0, 0);
946 return editing::applied();
947}
948
949std::unique_ptr<editing::IDomainOperationTarget> RoadNetworkEditTarget::cloneDomainState() const {
950 return std::unique_ptr<editing::IDomainOperationTarget>(
951 new RoadNetworkEditTarget(id_, std::make_unique<procgen::road::RoadNetwork>(*network_)));
952}
953
955 std::unique_ptr<editing::IDomainOperationTarget> candidate) {
956 auto* road = dynamic_cast<RoadNetworkEditTarget*>(candidate.get());
957 if (!road || road->id_ != id_ || !road->candidate_)
958 return reject<void>("editor.road.candidate", "Road transaction candidate does not match",
959 EditorStatus::Conflict);
960 auto valid = road->network_->validate();
961 if (!valid.ok()) return roadFailure<void>(valid.status());
962 *network_ = std::move(*road->candidate_);
963 dirty_.include(0, 0);
964 return editing::applied();
965}
966
967} // namespace eve::procgen_editing
double value
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
std::string output
std::string from
int bz
Definition CaveMesh.cpp:114
int ax
Definition CaveMesh.cpp:113
int bx
Definition CaveMesh.cpp:114
float length
Definition CaveMesh.cpp:94
bool split
Definition CaveMesh.cpp:123
int az
Definition CaveMesh.cpp:113
std::string label
std::string nodeId
std::map< std::string, Var > values
float maximum[3]
float minimum[3]
float u
Definition Grass.cpp:233
HexVec3 left
HexVec3 right
std::int32_t second
std::int32_t c
std::int32_t first
HexCoordinates to
Cell the unit walks towards on this segment.
Definition HexUnits.cpp:64
TokenKind kind
std::array< float, 3 > position
bool required
std::string name
bool valid
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
std::vector< BvhNode > nodes
std::string path
Definition PlayHost.cpp:110
std::string id
Definition PlayHost.cpp:108
std::shared_ptr< const std::vector< glm::vec2 > > points
PrimitiveHandle handle
float d
int detail
float t
const RoadNode * node
const RoadEdge * edge
double number
bool found
bool boolean
float dz
float dy
float dx
Json object
uint32_t index
std::vector< int > edges
glm::vec3 point
float qx
float qz
Move-only operation result carrying either a value or Status.
Definition Result.h:155
static Result success(T value)
Construct a successful result owning value.
Definition Result.h:164
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
UUID-backed identifier adapter for legacy textual boundaries.
Definition Identity.h:314
eve::OptionalRef< C > capability()
Query a capability through its interface-owned stable identity.
Optional capability exposing a deterministic, persistence-safe editing snapshot.
static CapabilityId editingCapabilityId()
Stable capability identity shared by every editable domain target.
Deterministic owning value tree shared by authoring hosts.
std::vector< Value > Array
std::map< std::string, Value > Object
const T * getIf() const
Return an immediately borrowed typed value.
Owning multi-segment 3D spline with deterministic bounded sampling.
Definition SplinePath.h:91
Result< void > setKindResult(std::string_view kind)
Select linear, catmullRom, quadraticBezier, or cubic bezier interpolation.
Owning directed road graph with lane connectivity.
Definition RoadNetwork.h:19
const std::vector< RoadEdge > & edges() const noexcept
Edges.
Result< void > addLaneLink(RoadLaneConnection link)
Register one legal turn inside a shared junction node.
Result< void > restoreNode(RoadNode node)
Restore a node with an explicit stable id for undo/import paths.
Result< void > validate() const
Validate graph, endpoint and lane-link invariants without mutation.
Result< bool > blockLaneLink(RoadLaneConnection link)
Persistently forbid one exact legal lane connection and remove it if currently active.
const std::vector< RoadLaneConnection > & laneLinks() const noexcept
Lane links.
const std::vector< RoadLaneConnection > & blockedLaneLinks() const noexcept
Borrow deterministic persistent turn prohibitions until the next network mutation.
Result< void > restoreEdge(RoadEdge edge)
Restore an edge with an explicit stable id for undo/import paths.
const std::vector< RoadNode > & nodes() const noexcept
Nodes.
Transaction target that edits stable RoadNetwork node/edge ids.
EVENGINE_API_ORCHESTRATION EditorResult< void > loadSnapshot(const EditorValue &snapshot)
Atomically replace the network from a validated version-one snapshot.
EVENGINE_API_ORCHESTRATION EditorResult< void > commitDomainState(std::unique_ptr< editing::IDomainOperationTarget > candidate) override
EVENGINE_API_ORCHESTRATION editing::TargetDescriptor describe() const override
Describe the target for tools and automation.
EVENGINE_API_ORCHESTRATION std::unique_ptr< editing::IDomainOperationTarget > cloneDomainState() const override
Clone the complete target state into an owning detached candidate.
std::uint64_t revision() const override
Revision.
EVENGINE_API_ORCHESTRATION EditorValue snapshotValue() const override
Serialize the complete editable network as eve.procgen.roadNetwork schema version 1.
EVENGINE_API_ORCHESTRATION void * queryCapability(const editing::CapabilityId &capability) override
Query an optional stable capability.
EVENGINE_API_ORCHESTRATION editing::GizmoSnapshot gizmo(std::size_t maximumPrimitives=4096, bool showLabels=true) const
Build a renderer-neutral authoring overlay from stable road identities.
static EVENGINE_API_ORCHESTRATION std::unique_ptr< RoadNetworkEditTarget > createOwned(std::string id)
Create an editor-owned empty road target for automation/composition hosts.
EVENGINE_API_ORCHESTRATION RoadNetworkEditTarget(std::string id, procgen::road::RoadNetwork &network)
Bind an editor target to an externally owned road network.
editing::TargetId targetId() const override
Target id.
Result< T > applied(T value, std::vector< Diagnostic > diagnostics={})
Construct an Applied result with an owning payload and optional diagnostics.
Diagnostic ruleDiagnostic(eve::DiagnosticCode code, RuleId rule, DiagnosticSeverity severity, std::string message)
Build a common diagnostic carrying an open editing rule identity.
RoadJunctionControl
Authoritative traffic-control policy applied to every approach of one junction node.
Definition RoadTypes.h:43
@ Uncontrolled
Edge trafficPriority resolves right-of-way without a mandatory stop.
RoadLaneDirection
Travel direction of a lane relative to the authored edge centerline.
Definition RoadTypes.h:79
EditorValue encodeReconnectEndpoint(std::uint32_t edgeId, bool fromEndpoint, std::uint32_t nodeId, const std::vector< procgen::road::RoadLaneConnection > &restoreLinks, const std::vector< procgen::road::RoadLaneConnection > &restoreBlockedLinks)
EditorValue encodeNode(std::uint32_t id, float x, float y, float z)
bool decodeEdge(const EditorValue &value, std::uint32_t &id, std::vector< procgen::road::RoadControlPoint > &points)
editing::GizmoPrimitive linePrimitive(std::string id, const procgen::road::RoadControlPoint &from, const procgen::road::RoadControlPoint &to)
bool decodeReattachEndpoint(const EditorValue &value, std::uint32_t &edgeId, bool &fromEndpoint, std::uint32_t &oldNodeId, std::uint32_t &detachedNodeId, std::vector< procgen::road::RoadLaneConnection > &restoreLinks, std::vector< procgen::road::RoadLaneConnection > &restoreBlockedLinks)
bool decodeDisconnectAtPosition(const EditorValue &value, procgen::road::RoadEdge &original, std::vector< procgen::road::RoadLaneConnection > &originalLinks, std::vector< procgen::road::RoadLaneConnection > &originalBlockedLinks, std::uint32_t &nodeId, std::uint32_t &secondEdgeId, std::uint32_t &branchEdgeId)
EditorValue encodeIntersectionConnect(std::uint32_t firstEdgeId, std::uint32_t secondEdgeId, float firstParameter, float secondParameter, procgen::road::RoadControlPoint mergedPoint, float maximumHeightDelta, float junctionRadius, const procgen::road::RoadEdgeSplitResult &firstSplit, const procgen::road::RoadEdgeSplitResult &secondSplit)
bool decodeDetachEndpoint(const EditorValue &value, std::uint32_t &edgeId, bool &fromEndpoint, std::uint32_t &nodeId)
bool readOptionalBool(const EditorValue &value, const char *name, bool &output)
EditorValue encodeSplit(std::uint32_t edgeId, std::size_t controlPointIndex, float junctionRadius, const procgen::road::RoadEdgeSplitResult &result)
EditorValue encodeDisconnectAtPosition(const procgen::road::RoadEdge &original, const std::vector< procgen::road::RoadLaneConnection > &originalLinks, const std::vector< procgen::road::RoadLaneConnection > &originalBlockedLinks, std::uint32_t nodeId, std::uint32_t secondEdgeId, std::uint32_t branchEdgeId)
bool decodeMergeNodes(const EditorValue &value, std::uint32_t &keepNodeId, std::uint32_t &removeNodeId, float &maxDistance)
bool decodeNodeRecord(const EditorValue &value, procgen::road::RoadNode &node)
bool readOptionalNumber(const EditorValue &value, const char *name, float &output)
bool decodeLink(const EditorValue &value, procgen::road::RoadLaneConnection &link)
bool decodeStyle(const EditorValue &value, std::uint32_t &id, procgen::road::RoadStyle &style)
EditorValue encodeUnmergeNodes(const procgen::road::RoadNode &removedNode, const std::vector< procgen::road::RoadEdge > &affectedEdges, const std::vector< procgen::road::RoadLaneConnection > &affectedLinks, const std::vector< procgen::road::RoadLaneConnection > &affectedBlockedLinks)
EditorValue encodeConnectAtPosition(std::uint32_t edgeId, procgen::road::RoadControlPoint position, float maxDistance, float junctionRadius, const procgen::road::RoadEdgeSplitResult &split, const procgen::road::RoadEdge &branch)
EditorValue encodeLink(const procgen::road::RoadLaneConnection &link)
bool readId(const EditorValue &value, const char *name, std::uint32_t &output)
bool decodeLinks(const EditorValue &value, std::vector< procgen::road::RoadLaneConnection > &links)
bool decodePoint(const EditorValue &value, const char *name, procgen::road::RoadControlPoint &point)
bool sameLink(const procgen::road::RoadLaneConnection &left, const procgen::road::RoadLaneConnection &right)
EditorValue encodeIntersectionDisconnect(const procgen::road::RoadEdge &first, const procgen::road::RoadEdge &second, const std::vector< procgen::road::RoadLaneConnection > &links, const std::vector< procgen::road::RoadLaneConnection > &blockedLinks, std::uint32_t sharedNodeId, std::uint32_t firstSecondEdgeId, std::uint32_t secondSecondEdgeId)
const EditorValue * field(const EditorValue &value, const char *name)
EditorValue encodeMergeNodes(std::uint32_t keepNodeId, std::uint32_t removeNodeId, float maxDistance)
EditorValue encodeEdge(std::uint32_t id, const std::vector< procgen::road::RoadControlPoint > &points)
bool decodePositionSplit(const EditorValue &value, std::uint32_t &edgeId, procgen::road::RoadControlPoint &position, float &maxDistance, float &junctionRadius, std::uint32_t &nodeId, std::uint32_t &secondEdgeId)
EditorValue encodeNodeRadius(std::uint32_t id, float junctionRadius)
bool decodeUnmergeNodes(const EditorValue &value, procgen::road::RoadNode &removedNode, std::vector< procgen::road::RoadEdge > &affectedEdges, std::vector< procgen::road::RoadLaneConnection > &affectedLinks, std::vector< procgen::road::RoadLaneConnection > &affectedBlockedLinks)
bool decodeNodeRadius(const EditorValue &value, std::uint32_t &id, float &junctionRadius)
bool decodeLaneCounts(const EditorValue &value, std::uint32_t &edgeId, int &lanesForward, int &lanesBackward, std::vector< procgen::road::RoadLaneConnection > &restoreLinks, std::vector< procgen::road::RoadLaneConnection > &restoreBlockedLinks)
bool decodeUnsplit(const EditorValue &value, procgen::road::RoadEdge &edge, std::vector< procgen::road::RoadLaneConnection > &links, std::vector< procgen::road::RoadLaneConnection > &blockedLinks, std::uint32_t &nodeId, std::uint32_t &secondEdgeId)
bool readNumber(const EditorValue &value, const char *name, float &output)
EditorValue encodeEdgeRecord(const procgen::road::RoadEdge &edge, const std::vector< procgen::road::RoadLaneConnection > &links, const std::vector< procgen::road::RoadLaneConnection > &blockedLinks)
EditorValue encodeNodeControl(std::uint32_t id, procgen::road::RoadJunctionControl control)
bool decodeIntersectionDisconnect(const EditorValue &value, procgen::road::RoadEdge &first, procgen::road::RoadEdge &second, std::vector< procgen::road::RoadLaneConnection > &links, std::vector< procgen::road::RoadLaneConnection > &blockedLinks, std::uint32_t &sharedNodeId, std::uint32_t &firstSecondEdgeId, std::uint32_t &secondSecondEdgeId)
EditorValue encodeUnsplit(const procgen::road::RoadEdge &edge, const std::vector< procgen::road::RoadLaneConnection > &links, const std::vector< procgen::road::RoadLaneConnection > &blockedLinks, std::uint32_t nodeId, std::uint32_t secondEdgeId)
bool decodeIntersectionConnect(const EditorValue &value, std::uint32_t &firstEdgeId, std::uint32_t &secondEdgeId, float &firstParameter, float &secondParameter, procgen::road::RoadControlPoint &mergedPoint, float &maximumHeightDelta, float &junctionRadius, std::uint32_t &firstNodeId, std::uint32_t &firstSecondEdgeId, std::uint32_t &secondNodeId, std::uint32_t &secondSecondEdgeId)
EditorValue encodeDetachEndpoint(std::uint32_t edgeId, bool fromEndpoint, std::uint32_t nodeId)
bool decodeConnectAtPosition(const EditorValue &value, std::uint32_t &edgeId, procgen::road::RoadControlPoint &position, float &maxDistance, float &junctionRadius, std::uint32_t &nodeId, std::uint32_t &secondEdgeId, procgen::road::RoadEdge &branch)
bool decodeBlockedLink(const EditorValue &value, procgen::road::RoadLaneConnection &link, bool &restoreActive)
bool decodeReconnectEndpoint(EditorValue const &value, std::uint32_t &edgeId, bool &fromEndpoint, std::uint32_t &nodeId, std::vector< procgen::road::RoadLaneConnection > &restoreLinks, std::vector< procgen::road::RoadLaneConnection > &restoreBlockedLinks)
bool readSmallInt(const EditorValue &value, const char *name, int minimum, int maximum, int &output)
EditorValue encodeLinks(const std::vector< procgen::road::RoadLaneConnection > &links)
bool decodeSplit(const EditorValue &value, std::uint32_t &edgeId, std::size_t &controlPointIndex, float &junctionRadius, std::uint32_t &nodeId, std::uint32_t &secondEdgeId)
bool decodeEdgeRecord(const EditorValue &value, procgen::road::RoadEdge &edge, std::vector< procgen::road::RoadLaneConnection > &links, std::vector< procgen::road::RoadLaneConnection > *blockedLinks)
EditorValue encodeReattachEndpoint(std::uint32_t edgeId, bool fromEndpoint, std::uint32_t oldNodeId, std::uint32_t detachedNodeId, const std::vector< procgen::road::RoadLaneConnection > &restoreLinks, const std::vector< procgen::road::RoadLaneConnection > &restoreBlockedLinks)
Result< RoadIntersection > findUniqueInteriorIntersection(const procgen::road::RoadEdge &first, const procgen::road::RoadEdge &second, float maximumHeightDelta)
bool decodeNodeControl(const EditorValue &value, std::uint32_t &id, procgen::road::RoadJunctionControl &control)
EditorValue encodeLaneCounts(std::uint32_t edgeId, int lanesForward, int lanesBackward, const std::vector< procgen::road::RoadLaneConnection > &restoreLinks, const std::vector< procgen::road::RoadLaneConnection > &restoreBlockedLinks)
EditorValue encodeBlockedLink(const procgen::road::RoadLaneConnection &link, bool restoreActive)
EditorValue encodePositionSplit(std::uint32_t edgeId, procgen::road::RoadControlPoint position, float maxDistance, float junctionRadius, const procgen::road::RoadEdgeSplitResult &result)
EditorValue encodeStyle(std::uint32_t id, const procgen::road::RoadStyle &style)
EditorValue encodeNodeRecord(const procgen::road::RoadNode &node)
void include(int x, int y)
Include.
Renderer-neutral wire primitive for authoring viewport overlays.
Immutable revision-tagged overlay snapshot consumed by any viewport renderer.
std::vector< GizmoPrimitive > primitives
std::vector< Diagnostic > diagnostics
Stable discovery metadata exposed by every editable target.
One spline anchor with incoming and outgoing handle offsets.
Definition SplinePath.h:15
One authored control point on a road centerline (world space, Y-up).
Definition RoadTypes.h:12
Stable identities produced by one atomic edge split.
Definition RoadTypes.h:72
std::uint32_t secondEdgeId
Newly allocated continuation edge.
Definition RoadTypes.h:75
Directed centerline edge with optional reverse lanes.
Definition RoadTypes.h:61
One legal lane-to-lane connection inside a junction.
Definition RoadTypes.h:90
Junction node owned by a road network.
Definition RoadTypes.h:51
Cross-section and structural style for one road edge.
Definition RoadTypes.h:19
bool sideObjectsRight
Emit anchors on the authored centerline's right side.
Definition RoadTypes.h:39
float sideObjectEndOffset
Empty distance after the last side-object anchor.
Definition RoadTypes.h:37
float speedLimitMps
Navigation speed limit in metres per second.
Definition RoadTypes.h:34
float uvMeters
World-space metres per texture repeat.
Definition RoadTypes.h:33
float curbWidth
Jersey-barrier thickness.
Definition RoadTypes.h:21
bool sideObjectsLeft
Emit anchors on the authored centerline's left side.
Definition RoadTypes.h:38
float curbHeight
Raised barrier height (readable asphalt channel).
Definition RoadTypes.h:22
float sideObjectStartOffset
Empty distance before the first side-object anchor.
Definition RoadTypes.h:36
int trafficPriority
Higher incoming-road values win uncontrolled junction priority.
Definition RoadTypes.h:35