Enable direct and menu bend editing in State diagrams
This commit is contained in:
@@ -39,6 +39,8 @@ pub enum Event {
|
||||
object: Id,
|
||||
placement: Option<Id>,
|
||||
anchor: iced::Point,
|
||||
bend: Option<usize>,
|
||||
route_segment: Option<(usize, Position)>,
|
||||
},
|
||||
CreateAt {
|
||||
context: Context,
|
||||
@@ -74,6 +76,25 @@ pub enum Event {
|
||||
placement: Id,
|
||||
attachment: Option<EdgeAttachment>,
|
||||
},
|
||||
SetBends {
|
||||
context: Context,
|
||||
relation: Id,
|
||||
waypoints: Vec<Position>,
|
||||
},
|
||||
AddBend {
|
||||
context: Context,
|
||||
relation: Id,
|
||||
segment: Option<(usize, Position)>,
|
||||
},
|
||||
RemoveBend {
|
||||
context: Context,
|
||||
relation: Id,
|
||||
index: usize,
|
||||
},
|
||||
ClearBends {
|
||||
context: Context,
|
||||
relation: Id,
|
||||
},
|
||||
}
|
||||
|
||||
impl Event {
|
||||
@@ -93,7 +114,11 @@ impl Event {
|
||||
| Self::Move { context: c, .. }
|
||||
| Self::Connect { context: c, .. }
|
||||
| Self::Reconnect { context: c, .. }
|
||||
| Self::SetAttachment { context: c, .. } => Some(*c),
|
||||
| Self::SetAttachment { context: c, .. }
|
||||
| Self::SetBends { context: c, .. }
|
||||
| Self::AddBend { context: c, .. }
|
||||
| Self::RemoveBend { context: c, .. }
|
||||
| Self::ClearBends { context: c, .. } => Some(*c),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -288,6 +313,24 @@ pub fn attachment_node(
|
||||
view.nodes.iter().find(|node| node.id == owner).cloned()
|
||||
}
|
||||
|
||||
/// Actual visible State transition geometry, never an internal row or folded proxy.
|
||||
pub fn bend_route(
|
||||
project: &Project,
|
||||
diagram: Id,
|
||||
relation: Id,
|
||||
) -> Option<oa_core::routing::RoutedEdge> {
|
||||
machine(project, diagram).ok()?;
|
||||
if project.relations.get(&relation)?.kind() != RelationKind::StateTransition {
|
||||
return None;
|
||||
}
|
||||
let stored = project.diagrams.get(&diagram)?;
|
||||
let view = projection::project_diagram(project, stored);
|
||||
let geometry = oa_core::routing::route_diagram(project, stored, &view)
|
||||
.edges
|
||||
.remove(&relation)?;
|
||||
(!geometry.custom_suspended).then_some(geometry)
|
||||
}
|
||||
|
||||
fn checked(project: &Project, plan: Plan) -> Result<Plan, ModelError> {
|
||||
prepare_command(project, &plan.command)?;
|
||||
Ok(plan)
|
||||
@@ -547,6 +590,66 @@ pub fn plan(project: &Project, current: Context, event: &Event) -> Result<Plan,
|
||||
route.set_attachment(end, attachment);
|
||||
(Command::SetEdgeRoute { diagram, route }, relation, None)
|
||||
}
|
||||
Event::SetBends { relation, .. }
|
||||
| Event::AddBend { relation, .. }
|
||||
| Event::RemoveBend { relation, .. }
|
||||
| Event::ClearBends { relation, .. } => {
|
||||
let geometry = bend_route(project, diagram, relation)
|
||||
.ok_or_else(|| error("Choose an actual visible transition route in this view."))?;
|
||||
let mut route = crate::route_edit::stored_route(&project.diagrams[&diagram], relation);
|
||||
match event {
|
||||
Event::SetBends { waypoints, .. } => {
|
||||
if waypoints.len() > 128 {
|
||||
return Err(error("A route supports at most 128 bends."));
|
||||
}
|
||||
for &point in waypoints {
|
||||
finite(point)?;
|
||||
}
|
||||
route.waypoints = waypoints.clone();
|
||||
}
|
||||
Event::AddBend { segment, .. } => {
|
||||
let (segment, point) = segment
|
||||
.or_else(|| {
|
||||
geometry
|
||||
.points
|
||||
.windows(2)
|
||||
.enumerate()
|
||||
.filter(|(index, _)| {
|
||||
*index > 0 && index + 2 < geometry.points.len()
|
||||
})
|
||||
.max_by(|(_, a), (_, b)| {
|
||||
let length = |s: &[Position]| {
|
||||
(s[1].x - s[0].x).abs() + (s[1].y - s[0].y).abs()
|
||||
};
|
||||
length(a).total_cmp(&length(b))
|
||||
})
|
||||
.map(|(index, span)| {
|
||||
(
|
||||
index,
|
||||
Position {
|
||||
x: (span[0].x + span[1].x) / 2.,
|
||||
y: (span[0].y + span[1].y) / 2.,
|
||||
},
|
||||
)
|
||||
})
|
||||
})
|
||||
.ok_or_else(|| error("This route has no interior segment for a bend."))?;
|
||||
crate::route_edit::insert_pin_on_segment(&mut route, &geometry, segment, point)
|
||||
.ok_or_else(|| {
|
||||
error("The bend segment is stale, invalid, or at capacity.")
|
||||
})?;
|
||||
}
|
||||
Event::RemoveBend { index, .. } => {
|
||||
if *index >= route.waypoints.len() {
|
||||
return Err(error("This bend no longer exists."));
|
||||
}
|
||||
route.waypoints.remove(*index);
|
||||
}
|
||||
Event::ClearBends { .. } => route.waypoints.clear(),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
(Command::SetEdgeRoute { diagram, route }, relation, None)
|
||||
}
|
||||
_ => {
|
||||
return Err(error(
|
||||
"Handle this navigation or tool request without a model command.",
|
||||
|
||||
Reference in New Issue
Block a user