Browser: Show Dimensions for finished sketches
Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01AS5Tn3xieewUSg3uxrMVdL
This commit is contained in:
1 parent
5539de8fd4
commit
584cb7e8a0
4 files changed
+75
No files matched your search
@@ -1047,6 +1047,63 @@ fn occurrence_panel(app: &mut SolveApp, ctx: &egui::Context) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Dimensions of finished sketches whose dimensions are shown (Show Dimensions), drawn over the
|
||||
/// viewport like the sketch being edited draws its own (`crate::dim_view`), but not editable.
|
||||
pub fn dims_overlay(app: &SolveApp, ctx: &egui::Context) {
|
||||
use solvecraft_engine::geom::Vec2;
|
||||
use solvecraft_engine::sketch::{DimFrame, chain_centres, default_text, dim_frame, dim_layout};
|
||||
let Some(rect) = app.viewport.rect else { return };
|
||||
if app.ui.shown_dims.is_empty() {
|
||||
return;
|
||||
}
|
||||
let tk = Tokens::get();
|
||||
let proj = crate::viewport::projection(app, rect);
|
||||
let painter = ctx.layer_painter(egui::LayerId::background()).with_clip_rect(rect);
|
||||
let st = app.session.world_state();
|
||||
for id in app.ui.shown_dims.iter().filter(|id| app.session.active_sketch != Some(**id) && sketch_visible(app, **id)) {
|
||||
let Some(ss) = st.sketch(*id) else { continue };
|
||||
let sk = &ss.sketch;
|
||||
let to = |p: Vec2| proj.to_screen(ss.plane.to_world(p));
|
||||
// Unplaced dimensions sit away from the middle of the sketch.
|
||||
let centres = chain_centres(sk);
|
||||
let n = centres.len().max(1) as f64;
|
||||
let middle = centres.iter().fold(Vec2::ZERO, |a, p| a + *p) * (1.0 / n);
|
||||
for c in &sk.constraints {
|
||||
let Some(frame) = dim_frame(sk, &c.kind) else { continue };
|
||||
let anchor = match frame {
|
||||
DimFrame::Linear { p0, p1, .. } => (p0 + p1) * 0.5,
|
||||
DimFrame::Radial { center, .. } | DimFrame::ArcLength { center, .. } => center,
|
||||
DimFrame::Angular { vertex, .. } => vertex,
|
||||
};
|
||||
let (Some(a), Some(b)) = (to(anchor), to(anchor + Vec2::X)) else { continue };
|
||||
let px = 1.0 / f64::from(a.distance(b)).max(1e-9);
|
||||
if !px.is_finite() {
|
||||
continue;
|
||||
}
|
||||
let expr = c.param.as_ref().and_then(|p| app.session.doc.param(p)).map(|p| p.expr.as_str());
|
||||
let col = if c.driven { tk.dimension_driven } else { tk.dimension };
|
||||
let galley = painter.layout_no_wrap(crate::dim_view::label(&c.kind, expr, c.driven), FontId::proportional(11.5), col);
|
||||
let size = galley.size() + vec2(6.0, 2.0);
|
||||
let half = Vec2::new(f64::from(size.x) * 0.5 * px, f64::from(size.y) * 0.5 * px);
|
||||
let lay = dim_layout(&frame, c.text.or_else(|| default_text(&frame, middle, px)), px, half);
|
||||
for line in &lay.lines {
|
||||
painter.add(egui::Shape::line(line.iter().filter_map(|q| to(*q)).collect(), Stroke::new(1.0, col)));
|
||||
}
|
||||
for (tip, dir) in &lay.arrows {
|
||||
if let (Some(t), Some(back)) = (to(*tip), to(*tip - *dir * px)) {
|
||||
let d = (t - back).normalized();
|
||||
let n = vec2(-d.y, d.x);
|
||||
painter.add(egui::Shape::convex_polygon(vec![t, t - d * 9.0 + n * 3.0, t - d * 9.0 - n * 3.0], col, Stroke::NONE));
|
||||
}
|
||||
}
|
||||
let Some(tc) = to(lay.text) else { continue };
|
||||
let r = Rect::from_center_size(tc, size);
|
||||
painter.rect_filled(r, 2.0, tk.viewport_top.gamma_multiply(0.85));
|
||||
painter.galley(r.min + vec2(3.0, 1.0), galley, col);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Where the browser's panels open: the viewport's top left corner.
|
||||
fn panel_pos(app: &SolveApp) -> Pos2 {
|
||||
app.viewport.rect.map(|r| r.left_top() + vec2(16.0, 16.0)).unwrap_or(pos2(270.0, 140.0))
|
||||
|
||||
@@ -359,11 +359,13 @@ fn body_items(app: &SolveApp, bodies: &[String], browser: bool) -> Vec<Item> {
|
||||
fn sketch_items(app: &SolveApp, id: u64) -> Vec<Item> {
|
||||
let visible = crate::browser::sketch_visible(app, id);
|
||||
let profiles = !app.ui.hidden_profiles.contains(&id);
|
||||
let dims = app.ui.shown_dims.contains(&id);
|
||||
let editing = app.session.active_sketch == Some(id);
|
||||
vec![
|
||||
act("ui.editSketch", "Edit Sketch", "sketch").with(json!({ "sketch": id })).on(!editing),
|
||||
act("ui.redefineSketch", "Redefine Sketch Plane", "plane").with(json!({ "sketch": id })),
|
||||
act(if visible { "ui.hideSketch" } else { "ui.showSketch" }, if visible { "Hide" } else { "Show" }, "eye").with(json!({ "sketch": id })),
|
||||
act("ui.sketchDims", if dims { "Hide Dimensions" } else { "Show Dimensions" }, "dimension").with(json!({ "sketch": id })),
|
||||
act("ui.sketchProfile", if profiles { "Hide Profile" } else { "Show Profile" }, "").with(json!({ "sketch": id })),
|
||||
act("ui.rename", "Rename", "").with(json!({ "feature": id })),
|
||||
cmd(app, "FusionDeleteCommand", "Delete").key("Del").with(json!({ "features": [id.to_string()] })),
|
||||
@@ -603,6 +605,15 @@ pub fn run_item(app: &mut SolveApp, item: &Item, at: Pos2) {
|
||||
crate::browser::set_sketch_visible(app, id, item.id == "ui.showSketch");
|
||||
}
|
||||
}
|
||||
"ui.sketchDims" => {
|
||||
if let Some(id) = id_of(&p, "sketch") {
|
||||
if app.ui.shown_dims.contains(&id) {
|
||||
app.ui.shown_dims.retain(|x| *x != id);
|
||||
} else {
|
||||
app.ui.shown_dims.push(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
"ui.sketchProfile" => {
|
||||
if let Some(id) = id_of(&p, "sketch") {
|
||||
if app.ui.hidden_profiles.contains(&id) {
|
||||
@@ -895,6 +906,7 @@ pub fn show(app: &mut SolveApp, ctx: &egui::Context) {
|
||||
app.menu.pending = None;
|
||||
if target == Target::Viewport { open(app, at) } else { open_for(app, at, target) }
|
||||
}
|
||||
crate::browser::dims_overlay(app, ctx);
|
||||
track_hold(app, ctx);
|
||||
show_rename(app, ctx);
|
||||
show_props(app, ctx);
|
||||
|
||||
@@ -74,6 +74,8 @@ pub struct UiState {
|
||||
pub shown_sketches: Vec<u64>,
|
||||
/// Sketches whose closed profiles are not shaded.
|
||||
pub hidden_profiles: Vec<u64>,
|
||||
/// Finished sketches whose dimensions are shown.
|
||||
pub shown_dims: Vec<u64>,
|
||||
/// Bodies locked in the browser (no move or delete).
|
||||
pub locked_bodies: Vec<String>,
|
||||
}
|
||||
@@ -98,6 +100,7 @@ impl Default for UiState {
|
||||
hidden_sketches: Vec::new(),
|
||||
shown_sketches: Vec::new(),
|
||||
hidden_profiles: Vec::new(),
|
||||
shown_dims: Vec::new(),
|
||||
locked_bodies: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,6 +106,9 @@ fn sketch_menu_hides_and_moves_to_another_plane() {
|
||||
let vis = crate::browser::sketch_visible(&app, id);
|
||||
context_menu::run_item(&mut app, &find(&items, if vis { "Hide" } else { "Show" }), pos2(0.0, 0.0));
|
||||
assert_eq!(crate::browser::sketch_visible(&app, id), !vis);
|
||||
context_menu::run_item(&mut app, &find(&items, "Show Dimensions"), pos2(0.0, 0.0));
|
||||
assert_eq!(app.ui.shown_dims, vec![id]);
|
||||
assert!(find(&context_menu::items(&app, &t), "Hide Dimensions").enabled);
|
||||
context_menu::run_item(&mut app, &find(&items, "Edit Sketch"), pos2(0.0, 0.0));
|
||||
assert_eq!(app.session.active_sketch, Some(id));
|
||||
app.finish_sketch();
|
||||
|
||||
Reference in new issue
Block a user