use oa_core::{projection::project_diagram, routing::*, *}; fn crosses(bounds: RoutingBounds, a: Position, b: Position) -> bool { let left = bounds.position.x; let top = bounds.position.y; let right = left + bounds.size.width; let bottom = top + bounds.size.height; if a.x == b.x { a.x > left && a.x < right && a.y.min(b.y) < bottom && a.y.max(b.y) > top } else { a.y > top && a.y < bottom && a.x.min(b.x) < right && a.x.max(b.x) > left } } fn fixture(functional_first: bool) -> (Project, Id, Id, Id) { let mut project = Project::demo(); let diagram = project .diagrams .values() .find(|d| d.layer == Layer::Sa && d.projection == ProjectionKind::Allocation) .unwrap() .id; // Freeze the observed default arrangement independently of random demo // identities. The two parallel border connections are only 20 units apart. for node in project .diagrams .get_mut(&diagram) .unwrap() .nodes .values_mut() { let (x, y, width, height) = match project.elements[&node.element].alias.as_str() { "vessel_communication_service" => (60., 60., 272., 220.), "rescue_coordination_system" => (572., 60., 272., 220.), "transmit_distress_report" => (84., 124., 224., 132.), "classify_incident_and_alert_operator" => (596., 124., 224., 132.), alias => panic!("Unexpected demo placement {alias}"), }; node.position = Position { x, y }; node.size = Size { width, height }; } let old_fe = project .relations .values() .find(|r| r.alias == "validated_distress_report") .unwrap() .id; let old_boundary = project .relations .values() .find(|r| r.alias == "distress_reporting_connection") .unwrap() .id; let fe = Id::from_u128(if functional_first { 1 } else { 2 }); let boundary = Id::from_u128(if functional_first { 2 } else { 1 }); let project = execute_batch( &project, &[ Command::Reidentify(old_fe, fe), Command::Reidentify(old_boundary, boundary), ], ) .unwrap(); (project, diagram, fe, boundary) } fn stroke_collisions(project: &Project, routed: &RoutedView) -> Vec { let mut collisions = Vec::new(); for edge in routed.edges.values().filter(|e| !e.label.text.is_empty()) { for other in routed.edges.values().filter(|e| e.id != edge.id) { for segment in other.points.windows(2) { if crosses(edge.label.bounds, segment[0], segment[1]) { collisions.push(format!( "{} ({}) label {:?} crosses {} ({}) segment {:?}", edge.label.text, edge.id, edge.label.bounds, project.named(other.id), other.id, segment )); } } } } collisions } #[test] fn demo_architecture_labels_do_not_cover_earlier_or_later_connection_strokes() { for functional_first in [true, false] { let (project, diagram, _, _) = fixture(functional_first); let before = project.clone(); let d = &project.diagrams[&diagram]; let view = project_diagram(&project, d); let routed = route_diagram(&project, d, &view); let collisions = stroke_collisions(&project, &routed); assert!(collisions.is_empty(), "{collisions:#?}"); assert_eq!(project, before); assert_eq!(routed, route_diagram(&project, d, &view)); } } #[test] fn authored_overlap_is_exact_and_reset_only_repositions_automatic_text() { let (mut project, diagram, fe, _) = fixture(true); let view = project_diagram(&project, &project.diagrams[&diagram]); let automatic = route_diagram(&project, &project.diagrams[&diagram], &view); let authored = EdgeLabel { fraction: 0.5, offset: Position { x: 0., y: -18. }, }; project .diagrams .get_mut(&diagram) .unwrap() .routes .push(EdgeRoute { edge: fe, waypoints: vec![], label: Some(authored.clone()), }); let authored_snapshot = project.clone(); let explicit = route_diagram(&project, &project.diagrams[&diagram], &view); let label = &explicit.edges[&fe].label; assert_eq!(label.fraction, authored.fraction); assert_eq!( label.center, Position { x: label.anchor.x + authored.offset.x, y: label.anchor.y + authored.offset.y } ); assert!( !stroke_collisions(&project, &explicit).is_empty(), "The authored label deliberately retains the native overlap" ); assert_eq!(project, authored_snapshot); for (&id, edge) in &automatic.edges { assert_eq!(edge.points, explicit.edges[&id].points); assert_eq!(edge.pins, explicit.edges[&id].pins); assert_eq!(edge.warnings, explicit.edges[&id].warnings); } project.diagrams.get_mut(&diagram).unwrap().routes[0].label = None; let reset = route_edge_preview(&project, &project.diagrams[&diagram], &view, &explicit, fe); assert_eq!(reset.edges[&fe], automatic.edges[&fe]); for (&id, edge) in &explicit.edges { if id != fe { assert_eq!(reset.edges[&id], *edge); } } }