forked from 1b-agent/opencadstudio-upstream
Add named parameters, UI scale setting, OPTIONS/OP dispatch, and help manual
Named parameters (docs/named_parameters_design.md, stages 3-4): - ParameterTable (src/scene/named_parameters.rs): a document-wide table of named formulas, each resolved through a shared expression evaluator with cross-parameter references, cycle detection, and division-by-zero/parse error reporting. SketchConstraint::driving_param becomes DrivingValue (Literal | Named) instead of a bare f64, resolved at solve time by sketch_solve::build_constraint via Scene::named_parameters. - Persistence (src/scene/named_parameters_persist.rs): same lazy XRecord model sketch_persist.rs already established, synced around open/save. Bumps the sketch-constraint XRecord's own format version (1 -> 2) since DrivingValue's shape doesn't line up with a version-1 blob's bytes. - PARAMETERS command and its editor (src/ui/window/named_parameters.rs): add/remove/edit rows, live validation, duplicate-name detection: Apply defines the table and re-solves every constraint scope with a named-parameter-driven constraint (command_driver.rs). Distance/Angle constraint prompts (modules/draw/constrain/value.rs) now accept either a literal or an existing parameter's name, mirroring how the constraint's own value prompt already worked. - Undo scope-down carried over from this project's own established pattern (constraint-system stage 9): the geometry an Apply moves is one undo step, but the parameter table edit itself has no undo entry yet — a documented follow-up, not a silent gap. UI scale setting (src/app/settings.rs, app/mod.rs, ui/window/options.rs): a whole-interface scale percentage (50-200%), wired directly into iced's own per-window scale_factor hook rather than per-panel font-size plumbing, so it applies immediately to every panel, dialog, and the ribbon at once. Persisted alongside the existing lineweight-display-scale setting. OPTIONS/OP command dispatch (app/commands/view.rs, app/commands/mod.rs): OPTIONS was registered for autocomplete but had no dispatch arm — the Start-tab welcome screen's own button was the only way to reach it. Found live-testing the UI-scale setting, unrelated to that feature itself. "OP" is AutoCAD's own short alias. help/: a new self-contained static user-help manual (manual.md as the authored source, tools/build_help.py splits it into the committed static HTML pages plus search index) — not yet wired into the application. 64 new/updated tests across the named-parameters and constraint-prompt changes. Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
1 parent
e2d39b049d
commit
7cddf31bcb
47 files changed
+4136
-52
No files matched your search
@@ -5,6 +5,7 @@
|
||||
// constrained neighbors, produces the expected change list, and an
|
||||
// unrelated edit doesn't touch anything (§4.3's "what does NOT trigger").
|
||||
|
||||
use OpenCADStudio::scene::named_parameters::DrivingValue;
|
||||
use OpenCADStudio::scene::sketch_constraints::{ConstraintKind, SketchRef, SketchScope};
|
||||
use OpenCADStudio::scene::{ChangeKind, Scene};
|
||||
use acadrust::entities::EntityType;
|
||||
@@ -96,7 +97,7 @@ fn distance_constraint_holds_the_target_length_after_an_unrelated_endpoint_edit(
|
||||
scene.sketch_constraint_set_mut(SketchScope::ModelSpace).add(
|
||||
ConstraintKind::Distance,
|
||||
vec![SketchRef::point(line, 0), SketchRef::point(line, 1)],
|
||||
Some(20.0),
|
||||
Some(DrivingValue::Literal(20.0)),
|
||||
);
|
||||
|
||||
set_line_end(&mut scene, line, Vector3::new(5.0, 5.0, 0.0)); // arbitrary edit, not length=20
|
||||
@@ -107,6 +108,63 @@ fn distance_constraint_holds_the_target_length_after_an_unrelated_endpoint_edit(
|
||||
assert!((len - 20.0).abs() < 1e-6, "length should have been solved to the driving value, got {len}");
|
||||
}
|
||||
|
||||
/// `named_parameters_design.md` stage 3: a `Distance` constraint's driving
|
||||
/// value can be a named-parameter reference instead of a literal, resolved
|
||||
/// through `Scene::named_parameters` at solve time.
|
||||
#[test]
|
||||
fn distance_constraint_resolves_a_named_parameter_reference() {
|
||||
let mut scene = Scene::new();
|
||||
let line = add_line(&mut scene, 0.0, 0.0, 10.0, 0.0);
|
||||
scene.named_parameters_mut().set("target_len", "20").unwrap();
|
||||
|
||||
scene.sketch_constraint_set_mut(SketchScope::ModelSpace).add(
|
||||
ConstraintKind::Distance,
|
||||
vec![SketchRef::point(line, 0), SketchRef::point(line, 1)],
|
||||
Some(DrivingValue::Named("target_len".to_string())),
|
||||
);
|
||||
|
||||
set_line_end(&mut scene, line, Vector3::new(5.0, 5.0, 0.0)); // arbitrary edit, not length=20
|
||||
scene.bump_entities(&[(line, ChangeKind::Modified)]);
|
||||
|
||||
let (start, end) = line_endpoints(&scene, line);
|
||||
let len = ((end.x - start.x).powi(2) + (end.y - start.y).powi(2)).sqrt();
|
||||
assert!((len - 20.0).abs() < 1e-6, "length should have resolved the named parameter, got {len}");
|
||||
|
||||
// Editing the parameter itself and touching the scope again should
|
||||
// ripple through, same as editing a literal driving value would.
|
||||
scene.named_parameters_mut().set("target_len", "8").unwrap();
|
||||
scene.bump_entities(&[(line, ChangeKind::Modified)]);
|
||||
let (start, end) = line_endpoints(&scene, line);
|
||||
let len = ((end.x - start.x).powi(2) + (end.y - start.y).powi(2)).sqrt();
|
||||
assert!((len - 8.0).abs() < 1e-6, "length should track the redefined parameter value, got {len}");
|
||||
}
|
||||
|
||||
/// A `driving_param` referencing a named parameter that doesn't (or no
|
||||
/// longer) exists must not panic the solve — same "skip, don't panic"
|
||||
/// contract `build_constraint`'s doc comment gives every other unbuildable
|
||||
/// constraint. The referenced line's two endpoint coordinates are still
|
||||
/// registered (via `point_ref`) even though the constraint itself can't be
|
||||
/// built, so they're just left free — the edit that triggered this solve is
|
||||
/// not undone or altered.
|
||||
#[test]
|
||||
fn distance_constraint_with_an_undefined_named_reference_is_skipped_not_panicked() {
|
||||
let mut scene = Scene::new();
|
||||
let line = add_line(&mut scene, 0.0, 0.0, 10.0, 0.0);
|
||||
|
||||
scene.sketch_constraint_set_mut(SketchScope::ModelSpace).add(
|
||||
ConstraintKind::Distance,
|
||||
vec![SketchRef::point(line, 0), SketchRef::point(line, 1)],
|
||||
Some(DrivingValue::Named("does_not_exist".to_string())),
|
||||
);
|
||||
|
||||
let edited_end = Vector3::new(5.0, 5.0, 0.0);
|
||||
set_line_end(&mut scene, line, edited_end);
|
||||
scene.bump_entities(&[(line, ChangeKind::Modified)]); // must not panic
|
||||
|
||||
let (_, end) = line_endpoints(&scene, line);
|
||||
assert_eq!(end, edited_end, "an unresolvable driving reference must leave the edit alone, not move it toward a phantom target");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tangent_constraint_between_a_line_and_a_circle_solves_to_touching() {
|
||||
let mut scene = Scene::new();
|
||||
@@ -285,8 +343,8 @@ fn two_conflicting_distance_targets_are_reported_as_conflicting() {
|
||||
let mut scene = Scene::new();
|
||||
let a = add_line(&mut scene, 0.0, 0.0, 10.0, 0.0);
|
||||
let set = scene.sketch_constraint_set_mut(SketchScope::ModelSpace);
|
||||
let first = set.add(ConstraintKind::Distance, vec![SketchRef::point(a, 0), SketchRef::point(a, 1)], Some(20.0));
|
||||
let second = set.add(ConstraintKind::Distance, vec![SketchRef::point(a, 0), SketchRef::point(a, 1)], Some(50.0));
|
||||
let first = set.add(ConstraintKind::Distance, vec![SketchRef::point(a, 0), SketchRef::point(a, 1)], Some(DrivingValue::Literal(20.0)));
|
||||
let second = set.add(ConstraintKind::Distance, vec![SketchRef::point(a, 0), SketchRef::point(a, 1)], Some(DrivingValue::Literal(50.0)));
|
||||
|
||||
scene.bump_entities(&[(a, ChangeKind::Modified)]);
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
// (save/load integration, the "lazy" persistence model) is gated on this
|
||||
// test passing — see the design doc §8 stage 2 and §7 open question 1.
|
||||
|
||||
use OpenCADStudio::scene::named_parameters::DrivingValue;
|
||||
use OpenCADStudio::scene::sketch_constraints::{ConstraintKind, SketchConstraintSet, SketchRef, SketchScope};
|
||||
use OpenCADStudio::scene::Scene;
|
||||
use acadrust::entities::EntityType;
|
||||
@@ -20,9 +21,13 @@ fn sample_set(scope: SketchScope) -> SketchConstraintSet {
|
||||
vec![SketchRef::point(Handle::new(101), 0), SketchRef::point(Handle::new(102), 1)],
|
||||
None,
|
||||
);
|
||||
set.add(ConstraintKind::Distance, vec![SketchRef::whole(Handle::new(103))], Some(25.0));
|
||||
set.add(ConstraintKind::Angle, vec![SketchRef::whole(Handle::new(104)), SketchRef::whole(Handle::new(105))], Some(45.0));
|
||||
set.add(ConstraintKind::Radius, vec![SketchRef::whole(Handle::new(106))], Some(3.5));
|
||||
set.add(ConstraintKind::Distance, vec![SketchRef::whole(Handle::new(103))], Some(DrivingValue::Literal(25.0)));
|
||||
set.add(
|
||||
ConstraintKind::Angle,
|
||||
vec![SketchRef::whole(Handle::new(104)), SketchRef::whole(Handle::new(105))],
|
||||
Some(DrivingValue::Literal(45.0)),
|
||||
);
|
||||
set.add(ConstraintKind::Radius, vec![SketchRef::whole(Handle::new(106))], Some(DrivingValue::Literal(3.5)));
|
||||
set
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user