Persist diagram routing with atomic edits and grouped movement

This commit is contained in:
2026-09-05 20:18:55 +03:00
parent fa096cefd3
commit 4e5a88dc4d
8 changed files with 943 additions and 3 deletions
+72
View File
@@ -657,6 +657,78 @@ pub fn validate(p: &Project) -> Vec<Issue> {
}
text(&mut out, d.id, "name", &d.name);
text(&mut out, d.id, "description", &d.description);
let mut routed_edges = BTreeSet::new();
for (index, route) in d.routes.iter().enumerate() {
if !routed_edges.insert(route.edge) {
error(
&mut out,
d.id,
"duplicate_edge_route",
"routes",
"Each relation or trace has at most one route per diagram.",
);
}
if !p.relations.contains_key(&route.edge) && !p.traces.contains_key(&route.edge) {
error(
&mut out,
d.id,
"route_edge",
"routes",
"Route edge must reference an existing relation or trace.",
);
}
if route.waypoints.len() > MAX_ROUTE_POINTS {
error(
&mut out,
d.id,
"route_point_limit",
"routes",
format!("A route supports at most {MAX_ROUTE_POINTS} pinned points."),
);
}
for (point_index, point) in route.waypoints.iter().enumerate() {
for (axis, value) in [("x", point.x), ("y", point.y)] {
if !value.is_finite() || value.abs() > MAX_COORDINATE {
error(
&mut out,
d.id,
"invalid_route_bounds",
"routes",
format!(
"Route {} point {} {axis} must be finite and bounded.",
index + 1,
point_index + 1
),
);
}
}
}
if let Some(label) = &route.label {
if !label.fraction.is_finite() || !(0. ..=1.).contains(&label.fraction) {
error(
&mut out,
d.id,
"invalid_route_fraction",
"routes",
"Label fraction must be between zero and one.",
);
}
for (axis, value) in [("x", label.offset.x), ("y", label.offset.y)] {
if !value.is_finite() || value.abs() > MAX_COORDINATE {
error(
&mut out,
d.id,
"invalid_route_bounds",
"routes",
format!(
"Route {} label offset {axis} must be finite and bounded.",
index + 1
),
);
}
}
}
}
let mut elements = BTreeSet::new();
for (key, n) in &d.nodes {
if *key != n.id {