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
+63 -2
View File
@@ -36,6 +36,14 @@ pub enum Command {
original: Id,
node: NodePlacement,
},
SetEdgeRoute {
diagram: Id,
route: EdgeRoute,
},
RemoveEdgeRoute {
diagram: Id,
edge: Id,
},
RelocateNode {
node: Id,
diagram: Id,
@@ -559,6 +567,28 @@ fn apply_command(
.ok_or_else(missing)? = value.clone();
Some(*owner)
}
Command::SetEdgeRoute { diagram, route } => {
let d = p.diagrams.get_mut(diagram).ok_or_else(missing)?;
if let Some(existing) = d.routes.iter_mut().find(|r| r.edge == route.edge) {
*existing = route.clone();
} else {
d.routes.push(route.clone());
}
Some(route.edge)
}
Command::RemoveEdgeRoute { diagram, edge } => {
if !p.relations.contains_key(edge) && !p.traces.contains_key(edge) {
return Err(ModelError(
"Routing requires an existing relation or trace.".into(),
));
}
p.diagrams
.get_mut(diagram)
.ok_or_else(missing)?
.routes
.retain(|route| route.edge != *edge);
Some(*edge)
}
Command::MoveNode {
diagram,
node,
@@ -570,12 +600,19 @@ fn apply_command(
} else {
vec![]
};
let d = p.diagrams.get_mut(diagram).ok_or_else(missing)?;
let n = d.nodes.get_mut(node).ok_or_else(missing)?;
let d = p.diagrams.get(diagram).ok_or_else(missing)?;
let n = d.nodes.get(node).ok_or_else(missing)?;
let delta = Position {
x: position.x - n.position.x,
y: position.y - n.position.y,
};
let deltas = std::iter::once(*node)
.chain(descendants.iter().copied())
.map(|id| (id, delta))
.collect();
let routes = crate::projection::translated_routes(p, d, &deltas);
let d = p.diagrams.get_mut(diagram).unwrap();
let n = d.nodes.get_mut(node).unwrap();
n.position = *position;
for id in descendants {
if let Some(child) = d.nodes.get_mut(&id) {
@@ -583,6 +620,11 @@ fn apply_command(
child.position.y += delta.y;
}
}
for route in routes {
if let Some(existing) = d.routes.iter_mut().find(|r| r.edge == route.edge) {
*existing = route;
}
}
Some(*node)
}
Command::Layout(diagram) => {
@@ -817,11 +859,30 @@ fn layout(p: &mut Project, diagram: Id) -> Result<(), ModelError> {
x += sizes[id].width + 240.;
row_height = row_height.max(sizes[id].height);
}
let deltas = positions
.iter()
.map(|(id, position)| {
let original = d.nodes[id].position;
(
*id,
Position {
x: position.x - original.x,
y: position.y - original.y,
},
)
})
.collect();
let routes = crate::projection::translated_routes(p, d, &deltas);
let d = p.diagrams.get_mut(&diagram).unwrap();
for (id, position) in positions {
let n = d.nodes.get_mut(&id).unwrap();
n.position = position;
n.size = sizes[&id];
}
for route in routes {
if let Some(existing) = d.routes.iter_mut().find(|r| r.edge == route.edge) {
*existing = route;
}
}
Ok(())
}