diff --git a/crates/doc/src/format.rs b/crates/doc/src/format.rs index ac2c185c..9f812f64 100644 --- a/crates/doc/src/format.rs +++ b/crates/doc/src/format.rs @@ -100,6 +100,8 @@ //! Under the same version, a sheet metal rule's corner relief shape can also be `tear` (no //! relief: each bend is cut straight from the bends' corner to where its flange's end //! starts); a rule that says `notch`, `round` or `rectangular` builds what it did. +//! A sketch's view options can also say `hide_construction` (the Sketch Palette's +//! Construction Geometries); a sketch that shows its construction geometry saves as before. //! A `SurfaceExtrude` feature (Surface Extrude: `sketch`, optional `profiles`, `curves` and //! `extent`, walls only) is first saved in 17; 0.8.0 would refuse the unknown feature. diff --git a/crates/engine/src/cmd/sketch.rs b/crates/engine/src/cmd/sketch.rs index 9cfc3049..39da5523 100644 --- a/crates/engine/src/cmd/sketch.rs +++ b/crates/engine/src/cmd/sketch.rs @@ -183,7 +183,7 @@ pub static COMMANDS: &[CommandSpec] = &[ .enabled(in_sketch) .params("dimension: constraint id or parameter name; at: [x,y] text centre (sketch coordinates) | reset: true (default place)"), CommandSpec::new("sketch.options", "Sketch Palette Options", sketch_options).icon("sketch").params( - "sketch?: id|name (default active); any of show_profile, show_points, show_dimensions, show_constraints, show_projected, grid, snap, slice, sketch_3d: bool. Returns them all", + "sketch?: id|name (default active); any of show_profile, show_points, show_dimensions, show_constraints, show_projected, show_construction, grid, snap, slice, sketch_3d: bool. Returns them all", ), CommandSpec::new("sketch.chain", "Chain", chain_cmd) .enabled(in_sketch) @@ -2252,6 +2252,7 @@ fn options_json(v: &solidcraft_sketch::SketchView) -> Value { "show_dimensions": !v.hide_dimensions, "show_constraints": !v.hide_constraints, "show_projected": !v.hide_projected, + "show_construction": !v.hide_construction, "grid": !v.hide_grid, "snap": !v.no_snap, "slice": v.slice, @@ -2261,7 +2262,18 @@ fn options_json(v: &solidcraft_sketch::SketchView) -> Value { fn sketch_options(s: &mut Session, p: &Value) -> Result { let cmd = "sketch.options"; - let keys = ["show_profile", "show_points", "show_dimensions", "show_constraints", "show_projected", "grid", "snap", "slice", "sketch_3d"]; + let keys = [ + "show_profile", + "show_points", + "show_dimensions", + "show_constraints", + "show_projected", + "show_construction", + "grid", + "snap", + "slice", + "sketch_3d", + ]; if let Some(o) = p.as_object() && let Some(k) = o.keys().find(|k| *k != "sketch" && !keys.contains(&k.as_str())) { @@ -2284,6 +2296,7 @@ fn sketch_options(s: &mut Session, p: &Value) -> Result { put("show_dimensions", &mut v.hide_dimensions, true); put("show_constraints", &mut v.hide_constraints, true); put("show_projected", &mut v.hide_projected, true); + put("show_construction", &mut v.hide_construction, true); put("grid", &mut v.hide_grid, true); put("snap", &mut v.no_snap, true); put("slice", &mut v.slice, false); diff --git a/crates/engine/src/cmd/sketch_ux_tests.rs b/crates/engine/src/cmd/sketch_ux_tests.rs index 450a15db..8656cfa3 100644 --- a/crates/engine/src/cmd/sketch_ux_tests.rs +++ b/crates/engine/src/cmd/sketch_ux_tests.rs @@ -482,6 +482,63 @@ fn palette_options_are_kept_with_each_sketch() { assert!(!sketch(&s).view.hide_points); } +/// Construction Geometries is a display option of one sketch, saved with it, and it changes +/// nothing about the geometry: construction curves were already outside every profile, and +/// they still are while hidden. Centerlines are a line type of their own and keep showing. A +/// design written before the option existed carries no key for it and loads with construction +/// geometry shown. +#[test] +fn hiding_construction_geometry_is_per_sketch_saved_and_leaves_profiles_and_centerlines_alone() { + use crate::view::sketch_curve_visible; + let mut s = new_sketch(); + run(&mut s, "sketch.rectangle.two_point", json!({"p0": [0, 0], "p1": [40, 30]})); + run(&mut s, "sketch.line", json!({"points": [[0, 45], [40, 45]], "connect": false, "infer": false})); + run(&mut s, "sketch.line", json!({"points": [[0, 55], [40, 55]], "connect": false, "infer": false})); + run(&mut s, "sketch.construction", json!({"curves": ["l5"], "value": true})); + run(&mut s, "sketch.centerline", json!({"curves": ["l6"], "value": true})); + let (rect, construction, centerline) = (0, 4, 5); + let profiles = run(&mut s, "sketch.inspect", json!({}))["profiles"].clone(); + assert_eq!(profiles.as_array().map(Vec::len), Some(1), "the rectangle is one profile: {profiles}"); + + assert_eq!(run(&mut s, "sketch.options", json!({}))["options"]["show_construction"], true); + let o = run(&mut s, "sketch.options", json!({"show_construction": false})); + assert_eq!(o["options"]["show_construction"], false); + let sk = sketch(&s); + assert!(sk.view.hide_construction); + assert!(!sketch_curve_visible(&sk, construction), "construction geometry is hidden"); + assert!(sketch_curve_visible(&sk, centerline), "a centerline is its own line type"); + assert!(sketch_curve_visible(&sk, rect), "ordinary geometry stays"); + // Hiding is display only: the curves, their line types and the profiles are untouched. + let after = run(&mut s, "sketch.inspect", json!({})); + assert_eq!(after["profiles"], profiles); + assert_eq!(after["curves"][construction]["construction"], true); + assert!(sketch(&s).curves[centerline].centerline, "the centerline keeps its line type"); + // Undo puts the option back. + run(&mut s, "edit.undo", json!({})); + assert!(!sketch(&s).view.hide_construction); + run(&mut s, "sketch.options", json!({"show_construction": false})); + + // Another sketch shows construction geometry; the first keeps the option after finishing. + let first = s.active_sketch.expect("sketch"); + run(&mut s, "sketch.finish", json!({})); + run(&mut s, "sketch.create", json!({"plane": "XZ"})); + assert_eq!(run(&mut s, "sketch.options", json!({}))["options"]["show_construction"], true); + assert_eq!(run(&mut s, "sketch.options", json!({"sketch": first}))["options"]["show_construction"], false); + + // Saved with the design. + let text = serde_json::to_string(s.doc.sketch(first).expect("first")).expect("json"); + assert!(text.contains("hide_construction"), "{text}"); + let back: Sketch = serde_json::from_str(&text).expect("parse"); + assert!(back.view.hide_construction); + + // An older design file has no `hide_construction` key at all: `SketchView`'s serde default + // fills it in, and construction geometry shows, the way it did before the option existed. + let older = r#"{"points":[{"id":"origin","pos":[0.0,0.0],"fixed":true}],"curves":[],"constraints":[], + "view":{"hide_projected":true,"slice":true}}"#; + let old: Sketch = serde_json::from_str(older).expect("an older sketch still loads"); + assert!(old.view.hide_projected && old.view.slice && !old.view.hide_construction); +} + // --------------------------------------------------------------------------------------------- // Dragging diff --git a/crates/engine/src/view.rs b/crates/engine/src/view.rs index 21d19cfc..7ad14fbf 100644 --- a/crates/engine/src/view.rs +++ b/crates/engine/src/view.rs @@ -107,7 +107,7 @@ pub fn sketch_lines_with_sampler( ) -> Vec<(Vec, Rgb, bool)> { let mut out = Vec::new(); for (i, c) in sk.curves.iter().enumerate() { - if c.link.is_some() && sk.view.hide_projected && !sk.is_text_curve(i) { + if !sketch_curve_visible(sk, i) { continue; } let col = if c.link.is_some() && !sk.is_text_curve(i) { @@ -151,6 +151,24 @@ pub fn sketch_lines_with_sampler( out } +/// Should sketch curve `i` be drawn and picked? The Sketch Palette's two geometry toggles each +/// hide one category, and a curve belongs to the category its colour already names in +/// [`sketch_lines_with_sampler`]: a linked curve is projected reference geometry (Projected +/// Geometries), anything else carrying the `construction` line type is construction geometry +/// (Construction Geometries). Text outlines are linked curves that the Text feature draws, so +/// they are never reference geometry and always show. Centerlines are their own line type and +/// no toggle hides them. +/// +/// Drawing and picking share this one answer so they cannot drift: geometry that is not on +/// screen is never clickable, and geometry on screen always is. +pub fn sketch_curve_visible(sk: &Sketch, i: usize) -> bool { + let Some(c) = sk.curves.get(i) else { return false }; + if c.link.is_some() && !sk.is_text_curve(i) { + return !sk.view.hide_projected; + } + !(c.construction && sk.view.hide_construction) +} + /// Should sketch point `i` be drawn? Text outlines' points are not (they are not picked or /// constrained individually). pub fn sketch_point_visible(sk: &Sketch, i: usize) -> bool { diff --git a/crates/engine/src/view_display_tests.rs b/crates/engine/src/view_display_tests.rs index 5a1808a5..4c31bfbf 100644 --- a/crates/engine/src/view_display_tests.rs +++ b/crates/engine/src/view_display_tests.rs @@ -71,6 +71,47 @@ fn sampler_keeps_style_precedence_hidden_references_text_and_disjoint_parts() { assert!(sk.link(&projected).is_some() && sk.link(&text).is_some()); } +/// Construction Geometries hides the `construction` line type and nothing else. Centerlines +/// are their own line type. A projected curve that carries the construction flag (the trace of +/// a plane or an axis) is drawn in the projected colour, so Projected Geometries owns it and +/// Construction Geometries leaves it alone. `sketch_curve_visible` is the one answer the GPU +/// viewport's picking uses as well, so nothing hidden here can still be clicked there. +#[test] +fn hiding_construction_geometry_keeps_ordinary_curves_centerlines_and_projected_traces() { + let mut sk = Sketch::new(); + let ordinary = line(&mut sk, 0.); + let construction = line(&mut sk, 1.); + sk.curves[construction].construction = true; + let centerline = line(&mut sk, 2.); + sk.curves[centerline].centerline = true; + let axis = sk.add_link(LinkKind::Project, LinkSource::Origin, &[LinkGeom::Line(Vec2::new(0., 3.), Vec2::new(2., 3.))]).unwrap(); + let trace = sk.link_curves(&axis)[0]; + sk.curves[trace].construction = true; + let determined = vec![false; sk.curves.len()]; + let drawn = |sk: &Sketch| -> Vec { + let mut seen = Vec::new(); + sketch_lines_with_sampler(sk, &Plane::XY, true, &determined, |_, i, _| { + seen.push(i); + vec![vec![Vec3::ZERO, Vec3::X]] + }); + seen + }; + assert_eq!(drawn(&sk), vec![ordinary, construction, centerline, trace]); + + sk.view.hide_construction = true; + assert_eq!(drawn(&sk), vec![ordinary, centerline, trace], "only the construction curve goes"); + for (i, want) in [(ordinary, true), (construction, false), (centerline, true), (trace, true)] { + assert_eq!(sketch_curve_visible(&sk, i), want, "curve {i}"); + } + // The projected trace answers to its own toggle, in either order. + sk.view.hide_projected = true; + assert_eq!(drawn(&sk), vec![ordinary, centerline]); + sk.view.hide_construction = false; + assert_eq!(drawn(&sk), vec![ordinary, construction, centerline]); + // A curve index that does not exist is not visible, and nothing panics asking. + assert!(!sketch_curve_visible(&sk, sk.curves.len())); +} + #[test] fn legacy_paths_match_sampler_wrapper_and_placed_wires_are_unchanged() { let mut sk = Sketch::new(); diff --git a/crates/sketch/src/model.rs b/crates/sketch/src/model.rs index 2836dcfa..7d5ee805 100644 --- a/crates/sketch/src/model.rs +++ b/crates/sketch/src/model.rs @@ -499,6 +499,11 @@ pub struct SketchView { pub hide_constraints: bool, #[serde(skip_serializing_if = "std::ops::Not::not")] pub hide_projected: bool, + /// Construction geometry (the `construction` line type) is not drawn and cannot be picked. + /// Centerlines are a line type of their own and keep showing; projected construction + /// geometry belongs to `hide_projected`, the way its colour already says it does. + #[serde(skip_serializing_if = "std::ops::Not::not")] + pub hide_construction: bool, #[serde(skip_serializing_if = "std::ops::Not::not")] pub hide_grid: bool, /// Clicks don't snap to the grid. diff --git a/crates/ui-egui/src/i18n.rs b/crates/ui-egui/src/i18n.rs index 2535d8e6..7a4b46c3 100644 --- a/crates/ui-egui/src/i18n.rs +++ b/crates/ui-egui/src/i18n.rs @@ -258,6 +258,20 @@ mod tests { assert_eq!(Locale::for_language("de").label("Points"), Locale::for_language("de").text("ui.points")); } + /// The Construction Geometries row passes its English text, like the other seven text + /// rows. "Construction" on its own is already the line-type entry `ui.construction`, so + /// the row's two-word text must reach its own entry rather than that one. + #[test] + fn the_construction_geometries_row_reaches_its_own_entry_not_the_line_type() { + let rows = [("de", "Konstruktionsgeometrien"), ("ja", "構築ジオメトリ"), ("ko", "구성 형상")]; + for (lang, want) in rows { + let locale = Locale::for_language(lang); + assert_eq!(locale.label("Construction Geometries"), want, "{lang}"); + assert_ne!(locale.label("Construction Geometries"), locale.text("ui.construction"), "{lang}"); + } + assert_eq!(Locale::for_language("en").label("Construction Geometries"), "Construction Geometries"); + } + #[test] fn templates_allow_reordering_and_literal_braces_in_values() { assert_eq!(interpolate("{second} then {first}", &[("first", "{second}"), ("second", "B")]), "B then {second}"); diff --git a/crates/ui-egui/src/locales/de.json b/crates/ui-egui/src/locales/de.json index 1d045aa6..2d6a0656 100644 --- a/crates/ui-egui/src/locales/de.json +++ b/crates/ui-egui/src/locales/de.json @@ -271,6 +271,7 @@ "ui.shell": "Hülle", "ui.show": "Einblenden", "ui.show_constraints": "Bedingungen", + "ui.show_construction_geometries": "Konstruktionsgeometrien", "ui.show_dimensions": "Maße", "ui.show_points": "Punkte anzeigen", "ui.show_profile": "Profil anzeigen", diff --git a/crates/ui-egui/src/locales/en.json b/crates/ui-egui/src/locales/en.json index cf0fc080..2d79190f 100644 --- a/crates/ui-egui/src/locales/en.json +++ b/crates/ui-egui/src/locales/en.json @@ -554,6 +554,7 @@ "ui.shell": "Shell", "ui.show": "Show", "ui.show_constraints": "Constraints", + "ui.show_construction_geometries": "Construction Geometries", "ui.show_dimensions": "Dimensions", "ui.show_points": "Show Points", "ui.show_profile": "Show Profile", diff --git a/crates/ui-egui/src/locales/ja.json b/crates/ui-egui/src/locales/ja.json index dc962a10..a563e503 100644 --- a/crates/ui-egui/src/locales/ja.json +++ b/crates/ui-egui/src/locales/ja.json @@ -554,6 +554,7 @@ "ui.shell": "シェル", "ui.show": "表示", "ui.show_constraints": "拘束", + "ui.show_construction_geometries": "構築ジオメトリ", "ui.show_dimensions": "寸法", "ui.show_points": "点を表示", "ui.show_profile": "プロファイルを表示", diff --git a/crates/ui-egui/src/locales/ko.json b/crates/ui-egui/src/locales/ko.json index 2d5aab18..b6d35c34 100644 --- a/crates/ui-egui/src/locales/ko.json +++ b/crates/ui-egui/src/locales/ko.json @@ -271,6 +271,7 @@ "ui.shell": "쉘", "ui.show": "표시", "ui.show_constraints": "구속 조건", + "ui.show_construction_geometries": "구성 형상", "ui.show_dimensions": "치수", "ui.show_points": "점 표시", "ui.show_profile": "프로파일 표시", diff --git a/crates/ui-egui/src/sketch_palette.rs b/crates/ui-egui/src/sketch_palette.rs index 1f485988..5b98103e 100644 --- a/crates/ui-egui/src/sketch_palette.rs +++ b/crates/ui-egui/src/sketch_palette.rs @@ -2,9 +2,9 @@ //! command dialog. Feature Options: the modes of the creation tool that is running, as icon //! buttons. Options: the line type of the selected curves (normal, construction, centerline), //! Look At, and the display toggles kept with the sketch (grid, snap, slice, profiles, points, -//! dimensions, constraints, projected geometry, 3D sketch); the sketch's degrees of freedom; -//! Finish Sketch. Toggles run `sketch.options`, line types `sketch.construction` / -//! `sketch.centerline`. +//! dimensions, constraints, projected geometry, construction geometry, 3D sketch); the sketch's +//! degrees of freedom; Finish Sketch. Toggles run `sketch.options`, line types +//! `sketch.construction` / `sketch.centerline`. //! //! The line type is also a drawing mode: with nothing selected, Construction (or X) makes what //! is drawn next construction geometry until it is switched back. @@ -33,7 +33,7 @@ const WIDTH: f32 = 236.0; /// The display toggles: option key and label. Plain nouns throughout, no "Show " prefix: the /// checkbox itself already says whether each one is on. /// -/// Seven of these pass their English source text directly, like the rest of this file's +/// Eight of these pass their English source text directly, like the rest of this file's /// `app.tr_label(...)` calls: `Locale::label` looks up a literal English string against every /// other English catalog entry to find its id. "Profile" and "Points" cannot do that safely: /// both already name something else under that same lookup (`SelInput::new("Profile", ...)` / @@ -56,7 +56,7 @@ const WIDTH: f32 = 236.0; /// before the id fallback ever runs). Nobody writes UI copy that looks like a dotted id, so /// this is a remote hazard, not a likely one, but it is why these two ids are spelled out here /// rather than reused anywhere else. -pub const TOGGLES: [(&str, &str); 9] = [ +pub const TOGGLES: [(&str, &str); 10] = [ ("grid", "Sketch Grid"), ("snap", "Snap"), ("slice", "Slice"), @@ -65,6 +65,7 @@ pub const TOGGLES: [(&str, &str); 9] = [ ("show_dimensions", "Dimensions"), ("show_constraints", "Constraints"), ("show_projected", "Projected Geometries"), + ("show_construction", "Construction Geometries"), ("sketch_3d", "3D Sketch"), ]; @@ -347,6 +348,7 @@ pub fn show(app: &mut SolveApp, ctx: &egui::Context) { "show_dimensions" => !opts.hide_dimensions, "show_constraints" => !opts.hide_constraints, "show_projected" => !opts.hide_projected, + "show_construction" => !opts.hide_construction, "sketch_3d" => opts.three_d, _ => false, }; diff --git a/crates/ui-egui/src/viewport.rs b/crates/ui-egui/src/viewport.rs index 52f09b06..432927d9 100644 --- a/crates/ui-egui/src/viewport.rs +++ b/crates/ui-egui/src/viewport.rs @@ -855,7 +855,7 @@ fn pick_hits(app: &SolveApp, proj: &Proj, pos: Pos2, other_sketch_points: bool, let Some(ss) = st.sketch(sid) else { continue }; let bonus = if s.active_sketch == Some(sid) { 2.0 } else { 0.0 }; for (i, c) in ss.sketch.curves.iter().enumerate() { - if c.link.is_some() && ss.sketch.view.hide_projected { + if !solidcraft_engine::view::sketch_curve_visible(&ss.sketch, i) { continue; } let straight = matches!(c.kind, CurveKind::Line { .. }); @@ -2493,7 +2493,7 @@ fn box_select(app: &mut SolveApp, proj: &Proj, bx: BoxSel, add: bool) { // What the selection filter lets through (window: wholly inside; crossing: touching). let off = |k: &str| app.ui.pick_off.iter().any(|x| x == k); for (i, c) in ss.sketch.curves.iter().enumerate().filter(|_| !off("sketch") && !off("sketch_curves")) { - if c.link.is_some() && ss.sketch.view.hide_projected { + if !solidcraft_engine::view::sketch_curve_visible(&ss.sketch, i) { continue; } let sampled = if bx.crossing { diff --git a/crates/ui-egui/tests/scenarios/s37_construction_geometries_toggle.json b/crates/ui-egui/tests/scenarios/s37_construction_geometries_toggle.json new file mode 100644 index 00000000..b2f9cb82 --- /dev/null +++ b/crates/ui-egui/tests/scenarios/s37_construction_geometries_toggle.json @@ -0,0 +1,51 @@ +[ + {"note": "The Sketch Palette's Construction Geometries row hides construction geometry while sketching. Hidden construction geometry cannot be picked either, by a click or by a box, so nothing invisible is selectable. Ordinary geometry, centerlines, sketch points and the profile are untouched."}, + {"start": "sketch.create"}, + {"click": {"plane": "XY"}}, + {"call": "engine.execute", "params": {"command": "sketch.rectangle.two_point", "params": {"p0": [0, 0], "p1": [40, 30]}}}, + {"call": "engine.execute", "params": {"command": "sketch.line", "params": {"points": [[0, 45], [40, 45]], "connect": false, "infer": false}}}, + {"call": "engine.execute", "params": {"command": "sketch.line", "params": {"points": [[0, 55], [40, 55]], "connect": false, "infer": false}}}, + {"call": "engine.execute", "params": {"command": "sketch.construction", "params": {"curves": ["l5"], "value": true}}}, + {"call": "engine.execute", "params": {"command": "sketch.centerline", "params": {"curves": ["l6"], "value": true}}}, + {"call": "ui.view", "params": {"view": "top"}}, + {"expect": {"shown": "Construction Geometries"}}, + {"expect": {"query": {"command": "sketch.options", "path": ["options", "show_construction"], "value": true}}}, + + {"note": "Shown: the construction line picks, and a box over it takes the line and its two end points."}, + {"click": {"sketch": [20, 45]}}, + {"expect": {"selection": 1}}, + {"key": "Escape"}, + {"drag": [{"sketch": [-6, 40]}, {"sketch": [46, 50]}], "steps": 10}, + {"expect": {"selection": 3}}, + {"key": "Escape"}, + {"expect": {"selection": 0}}, + + {"note": "Turn the row off from the palette itself."}, + {"widget": "Construction Geometries"}, + {"expect": {"query": {"command": "sketch.options", "path": ["options", "show_construction"], "value": false}}}, + + {"note": "Hidden: the same click reaches nothing. The same box takes only the two end points, which are sketch points and answer to the Points row, not to this one."}, + {"click": {"sketch": [20, 45]}}, + {"expect": {"selection": 0}}, + {"drag": [{"sketch": [-6, 40]}, {"sketch": [46, 50]}], "steps": 10}, + {"expect": {"selection": 2}}, + {"key": "Escape"}, + + {"note": "The centerline, the rectangle and the profile are all still there."}, + {"click": {"sketch": [20, 55]}}, + {"expect": {"selection": 1}}, + {"key": "Escape"}, + {"click": {"sketch": [20, 0]}}, + {"expect": {"selection": 1}}, + {"key": "Escape"}, + {"expect": {"query": {"command": "sketch.inspect", "path": ["curves", 4, "construction"], "value": true}}}, + {"expect": {"query": {"command": "sketch.inspect", "path": ["curves", 5, "construction"], "value": false}}}, + {"expect": {"sketch_curves": {"own": 6, "linked": 0}}}, + + {"note": "Turning it back on brings the construction line back, pickable again."}, + {"widget": "Construction Geometries"}, + {"expect": {"query": {"command": "sketch.options", "path": ["options", "show_construction"], "value": true}}}, + {"click": {"sketch": [20, 45]}}, + {"expect": {"selection": 1}}, + {"shot": "s37_construction_geometries_toggle"} +] diff --git a/docs/book/src/guide/sketching.md b/docs/book/src/guide/sketching.md index be9526e0..7346d9b2 100644 --- a/docs/book/src/guide/sketching.md +++ b/docs/book/src/guide/sketching.md @@ -31,6 +31,13 @@ fit-point and control-point splines, conics, points and text. **Construction** ( into reference geometry that doesn't form profiles. Modify tools include trim (T), extend, break, fillet, chamfer, offset (O), mirror, patterns, move/copy and scale. +The Sketch Palette's **Construction Geometries** option hides construction curves while you +work. A hidden construction curve cannot be picked by a click or by a box. Nothing invisible +is selectable. The option belongs to one sketch and is saved with the design. Centerlines are +a line type of their own and keep showing. Projected geometry that carries the construction +line type, such as the trace of a plane, answers to **Projected Geometries** instead. The +option changes display only. Construction curves were already outside every profile. + Polygon tools show their edge outline while positioning the second point, so the orientation can be checked before placement. Inscribed and circumscribed polygons retain their support circle and update the outline when the inline **Sides** value changes.