What a component holds moves with its occurrence, and picks in the world land in the
component's frame:
- Sketches and construction planes show where their component is placed (free move, joint
drive, Capture Position); sketch.inspect reports sketch points in the world.
- frames.rs maps a command's world picks into the frame of the body they lie on (through the
inverse of its placement: a moved component or a pasted instance), points that make new
geometry into the active component's frame, and directions with them; instance body names
("Body1 (Pin:2)") become the component's body. Fillet/chamfer edges, shell/offset/draft
faces, face extrude/revolve, sketch on face, holes, sheet metal and plastic picks, primitives,
appearance faces, analyses. Once per command (not again for commands run by commands); the
active-frame fallback stays for commands without a table. Nothing changes while every
placement is the identity.
- component.move_bodies keeps a body where it is in the world (Document.body_offsets: from the
new component's frame to the one it was made in; renames and deleted components follow).
- The UI reads the placed model (Session::world_state) for display, picking, sketch editing and
dialogs; live previews use it once anything is placed.
Tests (engine): sketches and construction planes move (and turn) with the component; world
picks fillet, sketch, hole and make boxes in a moved component; a pick on a pasted instance;
move_bodies keeps the world place there and back; a sketch on a face follows a Move feature.
QA's s34 now runs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AS5Tn3xieewUSg3uxrMVdL
74 lines
2.3 KiB
Rust
74 lines
2.3 KiB
Rust
//! User scenarios in the real UI, headless (see `solvecraft_ui_egui::scenario`). Each JSON file
|
|
//! under `tests/scenarios/` is one workflow done with clicks, keys and drags.
|
|
|
|
use solvecraft_ui_egui::scenario::Harness;
|
|
|
|
fn run(name: &str) {
|
|
let path = format!("{}/tests/scenarios/{name}.json", env!("CARGO_MANIFEST_DIR"));
|
|
let text = std::fs::read_to_string(&path).unwrap_or_else(|e| panic!("{path}: {e}"));
|
|
let steps: serde_json::Value = serde_json::from_str(&text).unwrap_or_else(|e| panic!("{path}: {e}"));
|
|
let mut h = Harness::new();
|
|
if let Err(e) = h.run(&steps) {
|
|
panic!("{name}: {e}");
|
|
}
|
|
}
|
|
|
|
macro_rules! scenarios {
|
|
($($name:ident),* $(,)?) => {
|
|
$(#[test]
|
|
fn $name() {
|
|
run(stringify!($name));
|
|
})*
|
|
};
|
|
}
|
|
|
|
scenarios!(
|
|
s01_box_fillet_hole_shell,
|
|
s02_revolve_on_xz,
|
|
s03_sketch_on_face_cut,
|
|
s04_mirror_body,
|
|
s05_delete_and_undo,
|
|
s06_edit_early_feature,
|
|
s07_components_joint_drive,
|
|
s08_sheet_metal_flat_dxf,
|
|
s09_step_roundtrip_hole,
|
|
s10_parameters_drive_model,
|
|
s11_sketch_constraints,
|
|
s12_pattern_bodies,
|
|
s13_plastic_enclosure,
|
|
s14_appearance_export,
|
|
s15_start_page_sample_save_reopen,
|
|
s16_tabs_separate_undo,
|
|
s17_toolbox_and_visibility,
|
|
s18_3d_sketch_pipe,
|
|
s19_canvas_calibrate,
|
|
s20_section_with_joint_drive,
|
|
s21_named_views,
|
|
s26_drive_joint_typed,
|
|
s22_sheet_metal_fold,
|
|
s23_configurations,
|
|
s24_motion_study,
|
|
s25_autosave_recovery,
|
|
s27_move_triad,
|
|
s33_move_x_arrow,
|
|
s34_component_move_carries_sketches,
|
|
);
|
|
|
|
/// Scenarios written ahead of their fixes or features (the user's open bugs): run with
|
|
/// `--ignored`; each moves to the list above once its fix lands.
|
|
macro_rules! pending {
|
|
($($name:ident: $why:literal),* $(,)?) => {
|
|
$(#[test]
|
|
#[ignore = $why]
|
|
fn $name() {
|
|
run(stringify!($name));
|
|
})*
|
|
};
|
|
}
|
|
|
|
pending!(
|
|
s31_sketch_on_face_projects: "user bug: a sketch on a face projects its loops as reference (solvecraft-sketch)",
|
|
s32_cut_in_active_component: "user bug: a cut in the active component leaves others alone (solvecraft-params)",
|
|
s30_pattern_a_hole: "the dialog sends the hole as a feature; the engine's pattern of a blind hole cuts too little (one hole short)",
|
|
);
|