Selection feedback and origin: hover and selected highlights (theme tokens), origin planes/axes/point at a fixed screen size with browser eye toggles, click-to-pick sketch planes and faces with camera animation (Finish Sketch returns), multi-select dialog inputs with pre-selection, tangent chains and face-to-edges, window/crossing box selection, shell/draft/mirror/hole dialogs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AS5Tn3xieewUSg3uxrMVdL
This commit is contained in:
1 parent
059909fb05
commit
97e73b0935
20 files changed
+1907
-518
No files matched your search
@@ -99,6 +99,19 @@ pub enum Sel {
|
||||
sketch: u64,
|
||||
index: usize,
|
||||
},
|
||||
/// An origin plane (XY, XZ, YZ) or a construction plane (by feature name).
|
||||
Plane {
|
||||
name: String,
|
||||
},
|
||||
/// An origin axis (X, Y, Z).
|
||||
Axis {
|
||||
name: String,
|
||||
},
|
||||
/// A body vertex at a point.
|
||||
Vertex {
|
||||
body: String,
|
||||
point: Vec3,
|
||||
},
|
||||
}
|
||||
|
||||
/// One undo step.
|
||||
|
||||
@@ -312,3 +312,27 @@ fn sample_design_builds() {
|
||||
let v = m["total"]["volume_mm3"].as_f64().unwrap();
|
||||
assert!(rel(v, plate + boss - bore) < 1e-3, "{v} vs {}", plate + boss - bore);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sample_edges_chain_and_faces() {
|
||||
let mut s = Session::default();
|
||||
s.run_script(&crate::sample::script()).unwrap();
|
||||
let st = s.model.state();
|
||||
let m = st.bodies[0].mesh();
|
||||
let e = (0..m.edges.len()).find(|i| m.edges[*i].iter().all(|p| (p.z - 8.0).abs() < 1e-6 && p.y.abs() < 1e-6)).expect("front top edge");
|
||||
let chain = m.tangent_chain(e, 2f64.to_radians());
|
||||
// The plate's top outline: 4 lines and 4 corner arcs (a boolean may leave an edge twice,
|
||||
// once per face; count distinct ones).
|
||||
let mut mids: Vec<solvecraft_geom::Vec3> = Vec::new();
|
||||
for i in &chain {
|
||||
let p = &m.edges[*i];
|
||||
let mid = p[p.len() / 2];
|
||||
assert!(p.iter().all(|q| (q.z - 8.0).abs() < 1e-6), "{p:?}");
|
||||
if !mids.iter().any(|q| q.dist(mid) < 1e-6) {
|
||||
mids.push(mid);
|
||||
}
|
||||
}
|
||||
assert_eq!(mids.len(), 8, "{chain:?}");
|
||||
// Every edge bounds a face, and the top face has the outline and the hole rims.
|
||||
assert!(m.edge_faces.iter().all(|f| !f.is_empty()));
|
||||
}
|
||||
@@ -143,3 +143,17 @@ pub fn home_camera(s: &Session) -> Camera {
|
||||
}
|
||||
c
|
||||
}
|
||||
|
||||
/// Construction planes of the timeline: (feature id, name, plane).
|
||||
pub fn construction_planes(s: &Session) -> Vec<(u64, String, Plane)> {
|
||||
use solvecraft_doc::FeatureKind;
|
||||
let (vals, _) = s.doc.param_values();
|
||||
s.doc
|
||||
.features
|
||||
.iter()
|
||||
.filter_map(|f| match &f.kind {
|
||||
FeatureKind::ConstructionPlane { plane } if !f.suppressed => s.doc.resolve_plane(&vals, plane, 0).ok().map(|p| (f.id, f.name.clone(), p)),
|
||||
_ => None,
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
Reference in new issue
Block a user