Selection inside sketches: double-click a curve selects the chain joined to it end to end (sketch.chain; shared or coincident ends), dimensions and constraint glyphs are selectable like other entities (Sel::SketchConstraint: Ctrl/Shift toggle, window and crossing boxes take them, Delete removes them with the rest), boxes leave out the origin, and the selection filter has sketch curves, points, dimensions and constraints
Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01AS5Tn3xieewUSg3uxrMVdL
This commit is contained in:
1 parent
f7703ca720
commit
d2068e07a3
12 files changed
+195
-52
No files matched your search
@@ -65,7 +65,7 @@ fn apply(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let mut bodies: Vec<String> = Vec::new();
|
||||
for x in &items {
|
||||
match x {
|
||||
Sel::SketchCurve { id } | Sel::SketchPoint { id } => entities.push(id.clone()),
|
||||
Sel::SketchCurve { id } | Sel::SketchPoint { id } | Sel::SketchConstraint { id } => entities.push(id.clone()),
|
||||
Sel::Feature { id } => {
|
||||
if s.doc.feature(*id).is_none() {
|
||||
return Err(bad(cmd, format!("no feature {id}")));
|
||||
|
||||
@@ -163,6 +163,10 @@ pub static COMMANDS: &[CommandSpec] = &[
|
||||
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",
|
||||
),
|
||||
CommandSpec::new("sketch.chain", "Chain", chain_cmd)
|
||||
.enabled(in_sketch)
|
||||
.noundo()
|
||||
.params("curve: id: the curves joined to it end to end (what a double-click selects)"),
|
||||
CommandSpec::new("sketch.solve", "Solve Sketch", solve_cmd)
|
||||
.enabled(in_sketch)
|
||||
.noundo()
|
||||
@@ -1660,6 +1664,14 @@ fn sketch_options(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
Ok(json!({"options": v, "sketch": info}))
|
||||
}
|
||||
|
||||
fn chain_cmd(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let cmd = "sketch.chain";
|
||||
let id = target_sketch(s, p, cmd)?;
|
||||
let sk = s.doc.sketch(id)?;
|
||||
let c = curve_ref(sk, p.get("curve"), cmd, "curve")?;
|
||||
Ok(json!({"curves": ids_of(sk, &sk.chain(c))}))
|
||||
}
|
||||
|
||||
fn solve_cmd(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let id = target_sketch(s, p, "sketch.solve")?;
|
||||
let st = s.model.state();
|
||||
|
||||
@@ -526,3 +526,41 @@ fn fully_constrained_feedback_is_per_entity() {
|
||||
assert!(lines.iter().filter(|l| l.1 == crate::view::colors::SKETCH_FIXED).count() >= 4);
|
||||
assert!(lines.iter().any(|l| l.1 == crate::view::colors::SKETCH));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
// Selection inside sketches
|
||||
|
||||
#[test]
|
||||
fn chain_follows_shared_and_coincident_ends() {
|
||||
let mut s = new_sketch();
|
||||
run(&mut s, "ShapeRectangleTwoPoint", json!({"p0": [0, 0], "p1": [20, 10]}));
|
||||
run(&mut s, "DrawPolyline", json!({"points": [[40, 0], [50, 0]]}));
|
||||
run(&mut s, "DrawPolyline", json!({"points": [[50.5, 0], [60, 5]]}));
|
||||
run(&mut s, "CircleCenterRadius", json!({"center": [80, 0], "radius": 3}));
|
||||
let chain = |s: &mut Session, c: &str| -> Vec<String> {
|
||||
let v = run(s, "sketch.chain", json!({"curve": c}));
|
||||
v["curves"].as_array().map(|a| a.iter().filter_map(|x| x.as_str().map(str::to_string)).collect()).unwrap_or_default()
|
||||
};
|
||||
assert_eq!(chain(&mut s, "l3"), ["l3", "l1", "l2", "l4"]);
|
||||
assert_eq!(chain(&mut s, "l5"), ["l5"]);
|
||||
assert_eq!(chain(&mut s, "c1"), ["c1"]);
|
||||
// Joined by a coincident constraint, the two lines are one chain.
|
||||
run(&mut s, "ConstraintCoincident", json!({"a": "l5.end", "b": "l6.start"}));
|
||||
assert_eq!(chain(&mut s, "l6"), ["l6", "l5"]);
|
||||
assert!(s.execute("sketch.chain", &json!({"curve": "nope"})).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn constraints_and_dimensions_can_be_selected_and_deleted_together() {
|
||||
let mut s = new_sketch();
|
||||
run(&mut s, "DrawPolyline", json!({"points": [[0, 0], [30, 0]]}));
|
||||
run(&mut s, "ConstraintHorizontalVertical", json!({"line": "l1"}));
|
||||
run(&mut s, "SketchDimension", json!({"entities": ["l1"], "value": 30}));
|
||||
let ids: Vec<String> = sketch(&s).constraints.iter().map(|c| c.id.clone()).collect();
|
||||
assert_eq!(ids.len(), 2);
|
||||
let items: Vec<Value> = ids.iter().map(|id| json!({"type": "sketch_constraint", "id": id})).collect();
|
||||
run(&mut s, "select.set", json!({ "items": items }));
|
||||
assert_eq!(s.selection.len(), 2);
|
||||
run(&mut s, "selection.delete", json!({"items": items}));
|
||||
assert!(sketch(&s).constraints.is_empty());
|
||||
}
|
||||
@@ -94,6 +94,10 @@ pub enum Sel {
|
||||
SketchPoint {
|
||||
id: String,
|
||||
},
|
||||
/// A constraint or dimension (by id) of the active sketch.
|
||||
SketchConstraint {
|
||||
id: String,
|
||||
},
|
||||
/// A closed profile of a sketch (by index).
|
||||
Profile {
|
||||
sketch: u64,
|
||||
|
||||
@@ -772,6 +772,60 @@ impl Sketch {
|
||||
out
|
||||
}
|
||||
|
||||
/// The curves joined end to end with curve `ci` (through shared or coincident end points),
|
||||
/// `ci` first, then the others in sketch order: what a double-click selects.
|
||||
pub fn chain(&self, ci: usize) -> Vec<usize> {
|
||||
if ci >= self.curves.len() {
|
||||
return Vec::new();
|
||||
}
|
||||
// Points made one by coincident constraints.
|
||||
let mut root: Vec<usize> = (0..self.points.len()).collect();
|
||||
fn find(r: &mut [usize], mut i: usize) -> usize {
|
||||
while let Some(&p) = r.get(i) {
|
||||
if p == i {
|
||||
break;
|
||||
}
|
||||
i = p;
|
||||
}
|
||||
i
|
||||
}
|
||||
for c in &self.constraints {
|
||||
if let ConstraintKind::Coincident { p, q } = c.kind {
|
||||
let (a, b) = (find(&mut root, p), find(&mut root, q));
|
||||
if let Some(x) = root.get_mut(a) {
|
||||
*x = b;
|
||||
}
|
||||
}
|
||||
}
|
||||
let ends: Vec<Vec<usize>> =
|
||||
self.curves.iter().map(|c| c.kind.ends().map(|(a, b)| vec![find(&mut root, a), find(&mut root, b)]).unwrap_or_default()).collect();
|
||||
let mut seen = vec![false; self.curves.len()];
|
||||
let mut stack = vec![ci];
|
||||
let mut out = Vec::new();
|
||||
while let Some(i) = stack.pop() {
|
||||
if seen.get(i).copied().unwrap_or(true) {
|
||||
continue;
|
||||
}
|
||||
if let Some(s) = seen.get_mut(i) {
|
||||
*s = true;
|
||||
}
|
||||
out.push(i);
|
||||
let mine = ends.get(i).cloned().unwrap_or_default();
|
||||
for (j, e) in ends.iter().enumerate() {
|
||||
if !seen.get(j).copied().unwrap_or(true) && e.iter().any(|p| mine.contains(p)) {
|
||||
stack.push(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
let first = out.first().copied();
|
||||
out.sort_unstable();
|
||||
if let Some(f) = first {
|
||||
out.retain(|x| *x != f);
|
||||
out.insert(0, f);
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
/// Is curve `c` held in place: fixed or projected (linked).
|
||||
pub fn curve_locked(&self, c: usize) -> bool {
|
||||
self.curves.get(c).is_some_and(|c| c.fixed || c.link.is_some())
|
||||
|
||||
@@ -204,7 +204,7 @@ pub fn handle(app: &mut SolveApp, ctx: &egui::Context, req: &ControlRequest) ->
|
||||
"dialog": app.dialog.as_ref().map(|d| d.inputs.iter().map(|i| json!({"label": i.label, "items": i.items})).collect::<Vec<_>>()),
|
||||
"hover": app.viewport.hover.as_ref().map(|h| format!("{h:?}")),
|
||||
"dimensions": crate::dim_view::drawn().into_iter().map(|(id, p)| json!({"id": id, "x": p.x, "y": p.y})).collect::<Vec<_>>(),
|
||||
"dimension_selected": crate::dim_view::selected(),
|
||||
"dimension_selected": crate::dim_view::selected(app),
|
||||
"dimension_editing": crate::dim_view::editing(),
|
||||
})),
|
||||
"ui.sketchToScreen" => {
|
||||
|
||||
@@ -31,7 +31,7 @@ fn is_empty(p: &Value) -> bool {
|
||||
/// Delete what is selected (the Delete key). A selected sketch dimension or constraint glyph
|
||||
/// goes first, as before.
|
||||
pub fn delete_selection(app: &mut SolveApp) {
|
||||
if crate::dim_view::delete_selected(app) || crate::sketch_tools::delete_glyph(app) {
|
||||
if crate::sketch_tools::delete_glyph(app) {
|
||||
return;
|
||||
}
|
||||
request(app, selection_params(app));
|
||||
|
||||
@@ -255,7 +255,7 @@ fn fits(a: Accept, s: &Sel) -> bool {
|
||||
Sel::Axis { .. } => a & AXES != 0,
|
||||
Sel::SketchCurve { .. } => a & (AXES | CURVES) != 0,
|
||||
Sel::Vertex { .. } => a & selection::VERTICES != 0,
|
||||
Sel::Feature { .. } | Sel::SketchPoint { .. } => false,
|
||||
Sel::Feature { .. } | Sel::SketchPoint { .. } | Sel::SketchConstraint { .. } => false,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ use std::cell::RefCell;
|
||||
|
||||
use egui::{FontId, Pos2, Rect, Shape, Stroke, vec2};
|
||||
use serde_json::json;
|
||||
use solvecraft_engine::Sel;
|
||||
use solvecraft_engine::geom::Vec2;
|
||||
use solvecraft_engine::sketch::{ConstraintKind, DimFrame, chain_centres, default_text, dim_frame, dim_layout};
|
||||
|
||||
@@ -44,7 +45,6 @@ struct Edit {
|
||||
|
||||
thread_local! {
|
||||
static DRAWN: RefCell<Vec<Drawn>> = const { RefCell::new(Vec::new()) };
|
||||
static SELECTED: RefCell<Option<String>> = const { RefCell::new(None) };
|
||||
/// A text being dragged: dimension id, undo depth at the start, grab offset (sketch).
|
||||
static DRAG: RefCell<Option<(String, usize, Vec2)>> = const { RefCell::new(None) };
|
||||
static EDIT: RefCell<Option<Edit>> = const { RefCell::new(None) };
|
||||
@@ -74,36 +74,20 @@ pub fn hit(pos: Pos2) -> Option<String> {
|
||||
})
|
||||
}
|
||||
|
||||
/// The selected dimension (constraint id).
|
||||
pub fn selected() -> Option<String> {
|
||||
SELECTED.with(|s| s.borrow().clone())
|
||||
/// The selected dimensions (constraint ids; they are in the session's selection).
|
||||
pub fn selected(app: &SolveApp) -> Vec<String> {
|
||||
let dims: Vec<String> = DRAWN.with(|d| d.borrow().iter().map(|x| x.id.clone()).collect());
|
||||
app.session
|
||||
.selection
|
||||
.iter()
|
||||
.filter_map(|s| if let Sel::SketchConstraint { id } = s { Some(id.clone()) } else { None })
|
||||
.filter(|id| dims.contains(id))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// A click without a tool: select the dimension under it (true: the click was used). A click
|
||||
/// elsewhere drops the dimension selection.
|
||||
pub fn click(app: &mut SolveApp, pos: Pos2) -> bool {
|
||||
if app.session.active_sketch.is_none() {
|
||||
return false;
|
||||
}
|
||||
let h = hit(pos);
|
||||
SELECTED.with(|s| *s.borrow_mut() = h.clone());
|
||||
if let Some(id) = &h {
|
||||
let _ = app.run("select.clear", json!({}));
|
||||
app.set_status(format!("Dimension {id} selected: drag to move, double-click to edit, Delete removes it"), false);
|
||||
}
|
||||
h.is_some()
|
||||
}
|
||||
|
||||
/// Drop the dimension selection (true when there was one).
|
||||
pub fn deselect() -> bool {
|
||||
SELECTED.with(|s| s.borrow_mut().take()).is_some()
|
||||
}
|
||||
|
||||
/// Delete the selected dimension (true when one was selected).
|
||||
pub fn delete_selected(app: &mut SolveApp) -> bool {
|
||||
let Some(id) = SELECTED.with(|s| s.borrow_mut().take()) else { return false };
|
||||
let _ = app.run("sketch.delete", json!({"entities": [id]}));
|
||||
true
|
||||
/// Dimension text boxes drawn last frame (for box selection).
|
||||
pub fn text_rects() -> Vec<(String, Rect)> {
|
||||
DRAWN.with(|d| d.borrow().iter().map(|x| (x.id.clone(), x.text)).collect())
|
||||
}
|
||||
|
||||
/// A double-click on a dimension opens its value for editing (true: it was on one).
|
||||
@@ -114,7 +98,6 @@ pub fn double_click(app: &mut SolveApp, pos: Pos2) -> bool {
|
||||
}) else {
|
||||
return false;
|
||||
};
|
||||
SELECTED.with(|s| *s.borrow_mut() = Some(d.id.clone()));
|
||||
let Some(param) = d.param else {
|
||||
app.set_status("A driven dimension measures the sketch; it has no value to edit", false);
|
||||
return true;
|
||||
@@ -132,7 +115,6 @@ pub fn drag_start(app: &SolveApp, press: Pos2, press_sketch: Vec2) -> bool {
|
||||
}) else {
|
||||
return false;
|
||||
};
|
||||
SELECTED.with(|s| *s.borrow_mut() = Some(d.id.clone()));
|
||||
DRAG.with(|g| *g.borrow_mut() = Some((d.id, app.session.undo.len(), d.at - press_sketch)));
|
||||
true
|
||||
}
|
||||
@@ -181,7 +163,6 @@ pub fn show(app: &mut SolveApp, ui: &egui::Ui, painter: &egui::Painter, proj: &P
|
||||
let st = app.session.model.state();
|
||||
let Some(ss) = app.session.active_sketch.and_then(|s| st.sketch(s)) else {
|
||||
DRAWN.with(|d| d.borrow_mut().clear());
|
||||
SELECTED.with(|s| *s.borrow_mut() = None);
|
||||
EDIT.with(|e| *e.borrow_mut() = None);
|
||||
return;
|
||||
};
|
||||
@@ -191,11 +172,7 @@ pub fn show(app: &mut SolveApp, ui: &egui::Ui, painter: &egui::Painter, proj: &P
|
||||
return;
|
||||
}
|
||||
let tk = Tokens::get();
|
||||
let sel = selected();
|
||||
// A selected dimension that was deleted (undo, another command) is forgotten.
|
||||
if sel.as_ref().is_some_and(|id| !sk.constraints.iter().any(|c| &c.id == id)) {
|
||||
SELECTED.with(|s| *s.borrow_mut() = None);
|
||||
}
|
||||
let is_sel = |id: &str| app.session.selection.iter().any(|s| matches!(s, Sel::SketchConstraint { id: x } if x == id));
|
||||
let hover = ui.input(|i| i.pointer.hover_pos()).filter(|p| proj.rect.contains(*p)).and_then(hit);
|
||||
let to = |p: Vec2| proj.to_screen(ss.plane.to_world(p));
|
||||
let font = FontId::proportional(12.0);
|
||||
@@ -217,7 +194,7 @@ pub fn show(app: &mut SolveApp, ui: &egui::Ui, painter: &egui::Painter, proj: &P
|
||||
let failing = ss.report.failing.contains(&c.id);
|
||||
let col = if failing {
|
||||
tk.error
|
||||
} else if sel.as_deref() == Some(c.id.as_str()) || hover.as_deref() == Some(c.id.as_str()) {
|
||||
} else if is_sel(&c.id) || hover.as_deref() == Some(c.id.as_str()) {
|
||||
tk.accent
|
||||
} else if c.driven {
|
||||
tk.dimension_driven
|
||||
|
||||
@@ -147,6 +147,16 @@ pub fn refine_snap(
|
||||
})
|
||||
}
|
||||
|
||||
/// The constraint glyph under a screen position (its constraint id).
|
||||
pub fn glyph_at(pos: Pos2) -> Option<String> {
|
||||
GLYPHS.with(|g| g.borrow().iter().find(|(_, c)| c.distance(pos) <= 7.0).map(|(id, _)| id.clone()))
|
||||
}
|
||||
|
||||
/// Constraint glyphs drawn last frame: (constraint id, screen centre).
|
||||
pub fn glyph_positions() -> Vec<(String, Pos2)> {
|
||||
GLYPHS.with(|g| g.borrow().clone())
|
||||
}
|
||||
|
||||
/// A click on a constraint glyph selects it (true: the click was used).
|
||||
pub fn click_glyph(app: &mut SolveApp, _proj: &Proj, pos: Pos2) -> bool {
|
||||
if app.session.active_sketch.is_none() {
|
||||
@@ -731,7 +741,8 @@ fn glyphs(app: &SolveApp, painter: &egui::Painter, proj: &Proj) {
|
||||
}
|
||||
placed.push(sp);
|
||||
let failing = ss.report.failing.contains(&c.id);
|
||||
let selected = picked.as_deref() == Some(c.id.as_str());
|
||||
let selected = picked.as_deref() == Some(c.id.as_str())
|
||||
|| app.session.selection.iter().any(|s| matches!(s, solvecraft_engine::Sel::SketchConstraint { id } if *id == c.id));
|
||||
let col = if failing {
|
||||
tk.error
|
||||
} else if selected {
|
||||
|
||||
@@ -364,7 +364,16 @@ fn selection_filter(app: &mut SolveApp, ui: &mut egui::Ui) {
|
||||
let t = Tokens::get();
|
||||
ui.label(egui::RichText::new("Selection filter").color(t.text_dim).size(11.0));
|
||||
ui.checkbox(&mut app.ui.pick_bodies, "Select bodies (a face picks its body)");
|
||||
for (k, label) in [("faces", "Faces"), ("edges", "Edges"), ("vertices", "Vertices"), ("sketch", "Sketch geometry and profiles")] {
|
||||
for (k, label) in [
|
||||
("faces", "Faces"),
|
||||
("edges", "Edges"),
|
||||
("vertices", "Vertices"),
|
||||
("sketch", "Sketch geometry and profiles"),
|
||||
("sketch_curves", "Sketch curves"),
|
||||
("sketch_points", "Sketch points"),
|
||||
("dimensions", "Sketch dimensions"),
|
||||
("constraints", "Sketch constraints"),
|
||||
] {
|
||||
let mut on = !app.ui.pick_off.iter().any(|x| x == k);
|
||||
if ui.checkbox(&mut on, label).changed() {
|
||||
app.ui.pick_off.retain(|x| x != k);
|
||||
@@ -417,7 +426,6 @@ pub fn shortcuts(app: &mut SolveApp, ctx: &egui::Context) {
|
||||
crate::tools::finish(app);
|
||||
} else if app.dialog.is_some() {
|
||||
crate::dialogs::cancel(app);
|
||||
} else if crate::dim_view::deselect() {
|
||||
} else if !app.session.selection.is_empty() || app.session.active_sketch.is_none() {
|
||||
let _ = app.run("select.clear", json!({}));
|
||||
} else {
|
||||
|
||||
@@ -672,8 +672,9 @@ pub fn candidate(app: &SolveApp, hits: &[Hit]) -> Option<(Hit, Sel)> {
|
||||
let off = |k: &str| app.ui.pick_off.iter().any(|x| x == k);
|
||||
hits.iter()
|
||||
.filter(|h| match h {
|
||||
Hit::SketchCurve { sketch, .. } => app.session.active_sketch == Some(*sketch) && !off("sketch"),
|
||||
Hit::SketchPoint { .. } | Hit::Profile { .. } => !off("sketch"),
|
||||
Hit::SketchCurve { sketch, .. } => app.session.active_sketch == Some(*sketch) && !off("sketch") && !off("sketch_curves"),
|
||||
Hit::SketchPoint { .. } => !off("sketch") && !off("sketch_points"),
|
||||
Hit::Profile { .. } => !off("sketch"),
|
||||
Hit::Vertex { .. } => !off("vertices") && !app.ui.pick_bodies,
|
||||
Hit::Edge { .. } => !off("edges") && !app.ui.pick_bodies,
|
||||
Hit::Face { .. } => !off("faces") || app.ui.pick_bodies,
|
||||
@@ -1132,13 +1133,32 @@ pub fn show(app: &mut SolveApp, ui: &mut egui::Ui) {
|
||||
app.dialog = Some(d);
|
||||
}
|
||||
} else {
|
||||
if !crate::dim_view::click(app, p) && !crate::sketch_tools::click_glyph(app, &proj, p) {
|
||||
select(app, cand.and_then(|c| c.1), add);
|
||||
// Dimensions and constraint glyphs sit on top of the geometry.
|
||||
let off = |k: &str| app.ui.pick_off.iter().any(|x| x == k);
|
||||
let sketching = app.session.active_sketch.is_some();
|
||||
let on_dim = crate::dim_view::hit(p).filter(|_| sketching && !off("dimensions"));
|
||||
let on_glyph = crate::sketch_tools::glyph_at(p).filter(|_| sketching && !off("constraints"));
|
||||
match on_dim.or(on_glyph) {
|
||||
Some(id) => select(app, Some(Sel::SketchConstraint { id }), add),
|
||||
None => select(app, cand.and_then(|c| c.1), add),
|
||||
}
|
||||
}
|
||||
}
|
||||
// Double-click a dimension: edit its value in place.
|
||||
let dim_edit = resp.double_clicked() && app.tool.is_none() && app.dialog.is_none() && crate::dim_view::double_click(app, p);
|
||||
// Double-click a sketch curve: select the chain joined to it end to end.
|
||||
if resp.double_clicked()
|
||||
&& !dim_edit
|
||||
&& app.tool.is_none()
|
||||
&& app.dialog.is_none()
|
||||
&& let Some(Hit::SketchCurve { sketch, id, .. }) = app.viewport.hover.clone()
|
||||
&& app.session.active_sketch == Some(sketch)
|
||||
&& let Ok(v) = app.session.execute("sketch.chain", &json!({ "curve": id }))
|
||||
{
|
||||
let items: Vec<Sel> =
|
||||
v["curves"].as_array().into_iter().flatten().filter_map(|c| c.as_str()).map(|id| Sel::SketchCurve { id: id.into() }).collect();
|
||||
let _ = app.run("select.set", json!({ "items": items, "add": add }));
|
||||
}
|
||||
// Double-click an edge: select the edges that continue it smoothly (the loop).
|
||||
if resp.double_clicked()
|
||||
&& !dim_edit
|
||||
@@ -1302,17 +1322,36 @@ fn box_select(app: &mut SolveApp, proj: &Proj, bx: BoxSel, add: bool) {
|
||||
if app.dialog.is_none()
|
||||
&& let Some(ss) = app.session.active_sketch.and_then(|sid| st.sketch(sid))
|
||||
{
|
||||
for (i, c) in ss.sketch.curves.iter().enumerate() {
|
||||
// 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 {
|
||||
continue;
|
||||
}
|
||||
let pts: Vec<Vec3> = ss.sketch.segs(i).iter().flat_map(|sg| sg.polyline(0.05)).map(|q| ss.plane.to_world(q)).collect();
|
||||
if bx.takes(&to2(&pts)) {
|
||||
out.push(Sel::SketchCurve { id: c.id.clone() });
|
||||
}
|
||||
}
|
||||
for p in &ss.sketch.points {
|
||||
if bx.takes(&to2(&[ss.plane.to_world(p.pos)])) {
|
||||
for (i, p) in ss.sketch.points.iter().enumerate().filter(|_| !off("sketch") && !off("sketch_points")) {
|
||||
if i != 0 && solvecraft_engine::view::sketch_point_visible(&ss.sketch, i) && bx.takes(&to2(&[ss.plane.to_world(p.pos)])) {
|
||||
out.push(Sel::SketchPoint { id: p.id.clone() });
|
||||
}
|
||||
}
|
||||
if !off("dimensions") {
|
||||
for (id, r) in crate::dim_view::text_rects() {
|
||||
if bx.takes(&[r.left_top(), r.right_top(), r.right_bottom(), r.left_bottom()]) {
|
||||
out.push(Sel::SketchConstraint { id });
|
||||
}
|
||||
}
|
||||
}
|
||||
if !off("constraints") {
|
||||
for (id, c) in crate::sketch_tools::glyph_positions() {
|
||||
if bx.takes(&[c]) {
|
||||
out.push(Sel::SketchConstraint { id });
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let accept = app.dialog.as_ref().and_then(|d| d.active_input()).map(|i| i.accept).unwrap_or(BODIES);
|
||||
for b in &st.bodies {
|
||||
|
||||
Reference in new issue
Block a user