15 descriptor.ownerModule =
"procgen_editing.road";
32 float x = 0.f,
y = 0.f,
z = 0.f,
radius = 6.f;
36 return commandError<editing::CommandPlan>(
"editor.road.command.node-add",
37 "Road node add requires finite x, y and z");
38 return plan(road->makeAddNode(
x,
y,
z,
radius));
40 if (!registered.ok())
return registered;
46 float x = 0.f,
y = 0.f,
z = 0.f;
49 return commandError<editing::CommandPlan>(
"editor.road.command.node-move",
50 "Road node move requires id and finite x, y and z");
51 return plan(road->makeMoveNode(
id,
x,
y,
z));
53 if (!registered.ok())
return registered;
55 registered = add(
"road.node.radius.set.v1",
"Set road junction radius",
62 return commandError<editing::CommandPlan>(
"editor.road.command.node-radius",
63 "Road node radius requires id and radius");
64 return plan(road->makeSetNodeJunctionRadius(
id,
radius));
66 if (!registered.ok())
return registered;
68 registered = add(
"road.node.junction-control.set.v1",
"Set road junction control",
75 return commandError<editing::CommandPlan>(
76 "editor.road.command.node-control",
77 "Road junction control requires id and junctionControl in [0,3]");
78 return plan(road->makeSetNodeJunctionControl(
81 if (!registered.ok())
return registered;
83 registered = add(
"road.node.merge.v1",
"Merge nearby road nodes",
86 std::uint32_t keepNodeId = 0, removeNodeId = 0;
87 float maxDistance = 1.f;
88 if (!road || !
readId(
request.payload,
"keepNodeId", keepNodeId) ||
92 return commandError<editing::CommandPlan>(
"editor.road.command.node-merge",
93 "Road node merge requires two node ids");
94 return plan(road->makeMergeNodes(keepNodeId, removeNodeId, maxDistance));
96 if (!registered.ok())
return registered;
98 registered = add(
"road.node.merge-and-connect.v1",
"Merge road nodes and connect turns",
101 std::uint32_t keepNodeId = 0, removeNodeId = 0;
102 float maxDistance = 1.f;
103 if (!road || !
readId(
request.payload,
"keepNodeId", keepNodeId) ||
107 return commandError<editing::CommandPlan>(
108 "editor.road.command.node-merge-connect",
109 "Road node merge and connect requires two node ids");
110 return plan(road->makeMergeNodesAndConnect(keepNodeId, removeNodeId, maxDistance));
112 if (!registered.ok())
return registered;
117 std::uint32_t
id = 0;
119 return commandError<editing::CommandPlan>(
"editor.road.command.node-remove",
120 "Road node remove requires id");
121 return plan(road->makeRemoveNode(
id));
123 if (!registered.ok())
return registered;
125 registered = add(
"road.node.connect-all-turns.v1",
"Connect all road turns",
128 std::uint32_t
id = 0;
130 return commandError<editing::CommandPlan>(
"editor.road.command.connect-turns",
131 "Connect all turns requires a node id");
132 return plan(road->makeConnectAllTurns(
id));
134 if (!registered.ok())
return registered;
139 std::uint32_t
from = 0,
to = 0, ignored = 0;
140 int forward = 2, backward = 0;
141 std::vector<procgen::road::RoadControlPoint>
points;
143 if (
const auto* encoded =
field(
request.payload,
"points")) pointPayload[
"points"] = *encoded;
150 return commandError<editing::CommandPlan>(
"editor.road.command.edge-add",
151 "Road edge add requires endpoints and at least two points");
152 return plan(road->makeAddEdge(
from,
to, std::move(
points), forward, backward));
154 if (!registered.ok())
return registered;
156 registered = add(
"road.edge.add-and-connect.v1",
"Add road edge and connect endpoints",
159 std::uint32_t
from = 0,
to = 0, ignored = 0;
160 int forward = 2, backward = 0;
161 std::vector<procgen::road::RoadControlPoint>
points;
163 if (
const auto* encoded =
field(
request.payload,
"points")) pointPayload[
"points"] = *encoded;
170 return commandError<editing::CommandPlan>(
171 "editor.road.command.edge-add-connect",
172 "Road edge add and connect requires endpoints and at least two points");
174 if (
const auto* encodedStyle =
field(
request.payload,
"style")) {
177 return commandError<editing::CommandPlan>(
178 "editor.road.command.edge-add-connect-style",
"Road edge style is invalid");
180 stylePayload[
"id"] = std::int64_t{1};
182 return commandError<editing::CommandPlan>(
183 "editor.road.command.edge-add-connect-style",
"Road edge style is invalid");
185 return plan(road->makeAddEdgeAndConnect(
from,
to, std::move(
points), forward, backward,
188 if (!registered.ok())
return registered;
190 registered = add(
"road.edge.split.v1",
"Split road edge",
193 std::uint32_t
id = 0;
200 return commandError<editing::CommandPlan>(
"editor.road.command.edge-split",
201 "Road split requires edge id and interior control-point index");
202 return plan(road->makeSplitEdge(
id,
static_cast<std::size_t
>(
index),
radius));
204 if (!registered.ok())
return registered;
206 registered = add(
"road.edge.split-at-position.v1",
"Split road edge at world position",
209 std::uint32_t
id = 0;
211 float maxDistance = 2.f,
radius = 6.f;
220 return commandError<editing::CommandPlan>(
221 "editor.road.command.edge-split-position",
222 "Road position split requires edge id and a finite world position");
223 return plan(road->makeSplitEdgeAtPosition(
id,
position, maxDistance,
radius));
225 if (!registered.ok())
return registered;
227 registered = add(
"road.node.connect-at-position.v1",
"Connect road node at world position",
232 float maxDistance = 2.f,
radius = 6.f;
233 int forward = 1, backward = 0;
247 return commandError<editing::CommandPlan>(
248 "editor.road.command.node-connect-position",
249 "Road branch connection requires source node, edge and world position");
251 radius, forward, backward));
253 if (!registered.ok())
return registered;
255 registered = add(
"road.edge.connect-intersection.v1",
"Connect crossing road edges",
258 std::uint32_t firstEdgeId = 0, secondEdgeId = 0;
259 float maximumHeightDelta = 0.5f,
radius = 6.f;
260 if (!road || !
readId(
request.payload,
"firstEdgeId", firstEdgeId) ||
266 return commandError<editing::CommandPlan>(
267 "editor.road.command.edge-intersection",
268 "Road intersection requires two edge ids and finite tolerances");
269 return plan(road->makeConnectEdgeIntersection(firstEdgeId, secondEdgeId,
270 maximumHeightDelta,
radius));
272 if (!registered.ok())
return registered;
274 registered = add(
"road.edge.lanes.set.v1",
"Set road edge lanes",
277 std::uint32_t
id = 0;
278 int forward = 0, backward = 0;
282 return commandError<editing::CommandPlan>(
"editor.road.command.edge-lanes",
283 "Road lane edit requires id and lane counts");
284 return plan(road->makeSetEdgeLaneCounts(
id, forward, backward));
286 if (!registered.ok())
return registered;
288 registered = add(
"road.edge.style.set.v1",
"Set road edge style",
291 std::uint32_t edgeId = 0;
294 return commandError<editing::CommandPlan>(
"editor.road.command.edge-style",
295 "Road edge style requires a complete style payload");
296 return plan(road->makeSetEdgeStyle(edgeId, style));
298 if (!registered.ok())
return registered;
300 registered = add(
"road.edge.control-points.set.v1",
"Set road edge control points",
303 std::uint32_t edgeId = 0;
304 std::vector<procgen::road::RoadControlPoint>
points;
306 return commandError<editing::CommandPlan>(
"editor.road.command.edge-control-points",
307 "Road edge control points require id and at least two finite points");
308 return plan(road->makeSetEdgeControlPoints(edgeId, std::move(
points)));
310 if (!registered.ok())
return registered;
312 registered = add(
"road.edge.reverse.v1",
"Reverse road edge",
315 std::uint32_t edgeId = 0;
317 return commandError<editing::CommandPlan>(
"editor.road.command.edge-reverse",
318 "Road edge reverse requires an id");
319 return plan(road->makeReverseEdge(edgeId));
321 if (!registered.ok())
return registered;
323 registered = add(
"road.edge.reconnect-endpoint.v1",
"Reconnect road edge endpoint",
326 std::uint32_t edgeId = 0,
nodeId = 0;
327 const auto* endpointValue =
field(
request.payload,
"fromEndpoint");
328 const auto* fromEndpoint = endpointValue ? endpointValue->getIf<
bool>() :
nullptr;
329 if (!road || !fromEndpoint || !
readId(
request.payload,
"id", edgeId) ||
331 return commandError<editing::CommandPlan>(
332 "editor.road.command.edge-reconnect",
333 "Road edge reconnect requires id, fromEndpoint, and nodeId");
334 return plan(road->makeReconnectEdgeEndpoint(edgeId, *fromEndpoint,
nodeId));
336 if (!registered.ok())
return registered;
338 registered = add(
"road.edge.reconnect-endpoint-and-connect.v1",
"Reconnect and connect road edge endpoint",
341 std::uint32_t edgeId = 0,
nodeId = 0;
342 const auto* endpointValue =
field(
request.payload,
"fromEndpoint");
343 const auto* fromEndpoint = endpointValue ? endpointValue->getIf<
bool>() :
nullptr;
344 if (!road || !fromEndpoint || !
readId(
request.payload,
"id", edgeId) ||
346 return commandError<editing::CommandPlan>(
347 "editor.road.command.edge-reconnect-connect",
348 "Road edge reconnect-and-connect requires id, fromEndpoint, and nodeId");
349 return plan(road->makeReconnectEdgeEndpointAndConnect(edgeId, *fromEndpoint,
nodeId));
351 if (!registered.ok())
return registered;
353 registered = add(
"road.edge.snap-endpoint.v1",
"Snap road edge endpoint to nearest node",
356 std::uint32_t edgeId = 0;
357 float maxDistance = 0.f;
358 bool connectTurns =
true;
359 const auto* endpointValue =
field(
request.payload,
"fromEndpoint");
360 const auto* fromEndpoint = endpointValue ? endpointValue->getIf<
bool>() :
nullptr;
361 const auto* connectValue =
field(
request.payload,
"connectTurns");
362 const auto* connect = connectValue ? connectValue->getIf<
bool>() :
nullptr;
363 if (!road || !fromEndpoint || (connectValue && !connect) ||
366 return commandError<editing::CommandPlan>(
367 "editor.road.command.edge-snap",
368 "Road edge snap requires id, fromEndpoint, and a finite maxDistance");
369 if (connect) connectTurns = *connect;
370 return plan(road->makeSnapEdgeEndpoint(edgeId, *fromEndpoint, maxDistance, connectTurns));
372 if (!registered.ok())
return registered;
374 registered = add(
"road.edge.detach-endpoint.v1",
"Detach road edge endpoint",
377 std::uint32_t edgeId = 0;
378 const auto* endpointValue =
field(
request.payload,
"fromEndpoint");
379 const auto* fromEndpoint = endpointValue ? endpointValue->getIf<
bool>() :
nullptr;
380 if (!road || !fromEndpoint || !
readId(
request.payload,
"id", edgeId))
381 return commandError<editing::CommandPlan>(
382 "editor.road.command.edge-detach",
383 "Road edge detach requires id and fromEndpoint");
384 return plan(road->makeDetachEdgeEndpoint(edgeId, *fromEndpoint));
386 if (!registered.ok())
return registered;
391 std::uint32_t
id = 0;
393 return commandError<editing::CommandPlan>(
"editor.road.command.edge-remove",
394 "Road edge remove requires id");
395 return plan(road->makeRemoveEdge(
id));
397 if (!registered.ok())
return registered;
404 return commandError<editing::CommandPlan>(
"editor.road.command.lane-link",
405 "Road lane-link command requires a complete directed link");
406 return plan(addLink ? road->makeAddLaneLink(link) : road->makeRemoveLaneLink(link));
408 registered = add(
"road.lane-link.add.v1",
"Add road lane link",
412 if (!registered.ok())
return registered;
413 registered = add(
"road.lane-link.remove.v1",
"Remove road lane link",
417 if (!registered.ok())
return registered;
423 return commandError<editing::CommandPlan>(
"editor.road.command.lane-link-block",
424 "Road lane-link block command requires a complete directed link");
425 return plan(block ? road->makeBlockLaneLink(link) : road->makeUnblockLaneLink(link));
427 registered = add(
"road.lane-link.block.v1",
"Block road lane link",
432 if (!registered.ok())
return registered;
433 return add(
"road.lane-link.unblock.v1",
"Unblock road lane link",