mirror of
https://github.com/bherbruck/solidcraft.git
synced 2026-11-06 20:02:16 +00:00
* claim: #3034 * Pattern of features: show the direction arrow on the patterned feature (#3034) A rectangular pattern whose Object Type is Features had no drag arrow and no value box: the canvas anchor knew bodies, faces, edges and sketch geometry but not a whole feature, so it returned nothing. A picked feature now anchors at the middle of the geometry it made (its bodies, or its faces on a body later features changed). The pattern's preview stands in for the body the feature lies on, so the anchor reads the feature's geometry including replaced bodies, as a body pick reads its body.
This commit is contained in:
@@ -77,6 +77,9 @@ pub fn anchor(app: &SolveApp, d: &Dialog) -> Option<(Vec3, Option<Vec3>)> {
|
||||
(ss.plane.to_world(ss.sketch.point(ss.sketch.resolve_point(point)?)?), None)
|
||||
}
|
||||
Sel::Body { name } => (st.body(name)?.mesh().bounds().center(), None),
|
||||
// A feature picked as a whole (a pattern or mirror of features): the middle of what it
|
||||
// made, as a body sits at the middle of its box.
|
||||
Sel::Feature { id } => (crate::viewport::feature_bounds(app, *id)?.center(), None),
|
||||
Sel::Plane { name } => {
|
||||
let pl = solidcraft_engine::geom::Plane::named(name).or_else(|| {
|
||||
solidcraft_engine::view::construction_planes(&app.session).into_iter().find(|(_, n, _)| n == name).map(|(_, _, pl)| pl)
|
||||
|
||||
@@ -87,3 +87,32 @@ fn a_solid_pattern_shows_the_second_arrow_once_it_has_a_second_direction() {
|
||||
assert_eq!(after, spacing);
|
||||
assert_ne!(after2, spacing2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_pattern_of_features_puts_its_arrow_on_the_patterned_feature() {
|
||||
let mut h = Harness::new();
|
||||
h.run(&json!([
|
||||
{"call": "engine.execute", "params": {"command": "solid.box", "params": {"corner": [0, 0, 0], "length": 60, "width": 20, "height": 5, "name": "Plate"}}},
|
||||
{"call": "engine.execute", "params": {"command": "solid.box", "params": {"corner": [8, 6, 5], "length": 8, "width": 8, "height": 6, "operation": "join", "name": "Boss"}}},
|
||||
{"call": "engine.execute", "params": {"command": "solid.pattern.rectangular", "params": {"features": ["Boss"], "dir1": [1, 0, 0], "count1": 2, "spacing1": 20, "name": "Pattern"}}},
|
||||
{"call": "ui.view", "params": {"view": "iso"}},
|
||||
{"call": "ui.editFeature", "params": {"feature": "Pattern"}}
|
||||
]))
|
||||
.unwrap();
|
||||
h.settle();
|
||||
let d = h.app.dialog.as_ref().expect("the pattern dialog");
|
||||
assert!(matches!(d.inputs.first().and_then(|i| i.items.first()), Some(solidcraft_engine::Sel::Feature { .. })), "{:?}", d.inputs);
|
||||
// The arrow starts at the middle of the boss and points along X, the pattern's direction.
|
||||
let (base, dir) = crate::canvas::anchor(&h.app, d).expect("the pattern has an anchor");
|
||||
assert!(base.dist(solidcraft_engine::geom::Vec3::new(12.0, 10.0, 8.0)) < 1e-6, "{base:?}");
|
||||
assert!(dir.is_some_and(|v| v.dist(solidcraft_engine::geom::Vec3::new(1.0, 0.0, 0.0)) < 1e-9), "{dir:?}");
|
||||
let Some(tip) = handle_at("arrow") else { panic!("no arrow on a pattern of features") };
|
||||
let from = h.call("ui.worldToScreen", json!({"points": [[12, 10, 8], [32, 10, 8]]}))["result"].clone();
|
||||
let at = |i: usize| egui::pos2(from[i][0].as_f64().unwrap() as f32, from[i][1].as_f64().unwrap() as f32);
|
||||
let (b, x) = (at(0), at(1));
|
||||
// The tip lies off the base, on the screen line of the boss's +X direction.
|
||||
let (ux, uy) = (x.x - b.x, x.y - b.y);
|
||||
let (vx, vy) = (tip.x - b.x, tip.y - b.y);
|
||||
assert!(vx * ux + vy * uy > 0.0, "tip {tip:?} base {b:?}");
|
||||
assert!((vx * uy - vy * ux).abs() / (ux * ux + uy * uy).sqrt() < 3.0, "tip {tip:?} off the X line through {b:?}");
|
||||
}
|
||||
|
||||
@@ -1801,6 +1801,12 @@ pub fn section_hatch(app: &SolveApp) -> bool {
|
||||
/// Do not expand these faces into surface patches: neighbouring faces can belong to another
|
||||
/// feature even when they share a surface.
|
||||
fn feature_geometry(app: &SolveApp, id: u64) -> Vec<Sel> {
|
||||
feature_parts(app, id, true)
|
||||
}
|
||||
|
||||
/// A feature's geometry as [`feature_geometry`] finds it; `shown_only` leaves out hidden bodies
|
||||
/// and bodies a dialog's preview stands in for.
|
||||
fn feature_parts(app: &SolveApp, id: u64, shown_only: bool) -> Vec<Sel> {
|
||||
if !app.session.model.result(id).is_some_and(|r| !r.skipped && r.error.is_none()) {
|
||||
return Vec::new();
|
||||
}
|
||||
@@ -1808,7 +1814,7 @@ fn feature_geometry(app: &SolveApp, id: u64) -> Vec<Sel> {
|
||||
let prefix = format!("F{id}:");
|
||||
let mut out = Vec::new();
|
||||
for b in &st.bodies {
|
||||
if !app.session.body_visible(&b.name) || app.preview.effective_replaced().contains(&b.name) {
|
||||
if shown_only && (!app.session.body_visible(&b.name) || app.preview.effective_replaced().contains(&b.name)) {
|
||||
continue;
|
||||
}
|
||||
if b.feature == id {
|
||||
@@ -1849,9 +1855,20 @@ fn displayed_selection(app: &SolveApp, item: &Sel) -> Vec<Sel> {
|
||||
/// The box around what is selected (bodies, faces, edges, vertices, sketch curves and the
|
||||
/// bodies of selected features), where it is shown.
|
||||
pub fn selection_bounds(app: &SolveApp) -> Option<solidcraft_engine::geom::Aabb3> {
|
||||
bounds_of(app, app.session.selection.iter().flat_map(|item| displayed_selection(app, item)))
|
||||
}
|
||||
|
||||
/// The box around the geometry a feature made (its bodies, or its faces on bodies later features
|
||||
/// changed), also where a dialog's preview stands in for it: a pattern of the feature previews
|
||||
/// the body the feature is on. None when the feature made nothing.
|
||||
pub(crate) fn feature_bounds(app: &SolveApp, id: u64) -> Option<solidcraft_engine::geom::Aabb3> {
|
||||
bounds_of(app, feature_parts(app, id, false).into_iter())
|
||||
}
|
||||
|
||||
fn bounds_of(app: &SolveApp, items: impl Iterator<Item = Sel>) -> Option<solidcraft_engine::geom::Aabb3> {
|
||||
let st = crate::open_meshing::shown_world(app);
|
||||
let mut b = solidcraft_engine::geom::Aabb3::EMPTY;
|
||||
for s in app.session.selection.iter().flat_map(|item| displayed_selection(app, item)) {
|
||||
for s in items {
|
||||
match &s {
|
||||
Sel::Body { name } => {
|
||||
if let Some(x) = st.body(name) {
|
||||
|
||||
Reference in New Issue
Block a user