diff --git a/src/scene/parametric_constraints.rs b/src/scene/parametric_constraints.rs index 01e8029ce..9dace36ff 100644 --- a/src/scene/parametric_constraints.rs +++ b/src/scene/parametric_constraints.rs @@ -891,6 +891,23 @@ impl ConstraintKind { } } +/// Fixed draws a padlock (`src/ui/overlay.rs`), the way the reference bar +/// does: the plain symbol for a held curve or segment, this variant when one +/// addressable point is held (white lock with the point marker). +pub(crate) const FIXED_POINT_GLYPH: &str = "F·"; + +pub(crate) fn fixed_glyph_label(constraint: &ParametricConstraint) -> &'static str { + let point = constraint + .refs + .first() + .is_some_and(|reference| reference.marker.is_some() && reference.segment_index().is_none()); + if point { + FIXED_POINT_GLYPH + } else { + ConstraintKind::Fixed.glyph_symbol() + } +} + /// The full glyph text for one constraint: its symbol, plus the driving /// value for a dimensional kind (Distance/Angle/Radius). pub(crate) fn glyph_label(constraint: &ParametricConstraint) -> String { @@ -1687,7 +1704,9 @@ impl super::Scene { }) .flat_map(|c| { let is_conflicting = set.conflicts.iter().any(|(id, _)| *id == c.id); - let label = if show_values { + let label = if c.kind == ConstraintKind::Fixed { + fixed_glyph_label(c).to_string() + } else if show_values { glyph_label(c) } else { c.kind.glyph_symbol().to_string() diff --git a/src/ui/overlay.rs b/src/ui/overlay.rs index 1aecaba9e..3a2712ce5 100644 --- a/src/ui/overlay.rs +++ b/src/ui/overlay.rs @@ -34,11 +34,16 @@ fn is_compact_coincident_glyph(label: &str) -> bool { matches!(label, "≡" | "∈") } +/// Fixed's two padlock labels (`parametric_constraints::fixed_glyph_label`). +fn is_fixed_glyph(label: &str) -> bool { + label == "F" || label == crate::scene::parametric_constraints::FIXED_POINT_GLYPH +} + fn constraint_glyph_size(label: &str) -> Size { if is_compact_coincident_glyph(label) { return Size::new(COINCIDENT_GLYPH_SIZE, COINCIDENT_GLYPH_SIZE); } - if label == "G²" { + if label == "G²" || is_fixed_glyph(label) { let side = CONSTRAINT_GLYPH_SIZE + CONSTRAINT_GLYPH_PAD_Y * 2.0; return Size::new(side, side); } @@ -94,6 +99,64 @@ fn draw_concentric_constraint_glyph( frame.stroke(&canvas::Path::circle(center, 2.15), stroke); } +/// The reference bar's padlock: white with a small orange point marker +/// when one point is held, red when a whole curve or segment is. +fn draw_fixed_constraint_glyph( + frame: &mut canvas::Frame, + center: Point, + text: Color, + badge: Color, + point_marker: bool, +) { + let lock = if point_marker { + text + } else { + Color::from_rgb8(214, 76, 76) + }; + let shackle = canvas::Path::new(|builder| { + let shackle_center = Point::new(center.x, center.y - 1.6); + for step in 0..=12 { + let angle = std::f32::consts::PI * (1.0 - step as f32 / 12.0); + let point = Point::new( + shackle_center.x + angle.cos() * 3.1, + shackle_center.y - angle.sin() * 3.1, + ); + if step == 0 { + builder.move_to(Point::new(point.x, center.y - 0.6)); + builder.line_to(point); + } else { + builder.line_to(point); + } + } + builder.line_to(Point::new(center.x + 3.1, center.y - 0.6)); + }); + frame.stroke( + &shackle, + canvas::Stroke::default().with_color(lock).with_width(1.5), + ); + let body = canvas::Path::rounded_rectangle( + Point::new(center.x - 5.0, center.y - 0.8), + Size::new(10.0, 7.6), + 1.4.into(), + ); + frame.fill(&body, lock); + frame.stroke( + &canvas::Path::line( + Point::new(center.x, center.y + 1.4), + Point::new(center.x, center.y + 4.6), + ), + canvas::Stroke::default().with_color(badge).with_width(1.3), + ); + if point_marker { + frame.stroke( + &canvas::Path::rectangle(Point::new(center.x + 4.0, center.y + 3.6), Size::new(4.6, 4.6)), + canvas::Stroke::default() + .with_color(Color::from_rgb8(224, 130, 62)) + .with_width(1.2), + ); + } +} + fn constraint_glyph_box( anchor: Point, outward: [f32; 2], @@ -2020,6 +2083,14 @@ impl canvas::Program for SelectionCanvas { draw_smooth_constraint_glyph(&mut frame, glyph_center, fg); } else if label == "◎" { draw_concentric_constraint_glyph(&mut frame, glyph_center, fg); + } else if is_fixed_glyph(label) { + draw_fixed_constraint_glyph( + &mut frame, + glyph_center, + fg, + bg, + label == crate::scene::parametric_constraints::FIXED_POINT_GLYPH, + ); } else { frame.fill_text(canvas::Text { content: label.clone(),