feat(block-editor): wire Rotation, Alignment and Flip parameter placement

Enable the existing palette rows and BPARAMETER choices. Rotation accepts a
center, picked or numeric radius, and picked or numeric default angle.
Alignment places a tangent direction from its base point. Flip places a
reflection line and state label, with native grip and state expressions.

Extend shared graph append/capacity checks for Flip's additional state node
while preserving existing subgraphs. Keep Polar/Rotation angle labels unique
in either placement order. Add editor graphics, extents and definition
reframing through the existing overlay/history/save paths.

This milestone implements native parameter placement and persistence.
Rotate/Flip action association, reference alignment/grip editing, parameter
property editing and parameter sets remain separate work.

Validation covers command and palette placement, cancellation, local/parent
undo, save/discard, native DWG/DXF objects, existing graph preservation and
the 17 research fixtures. Broader block/dock regressions and editor renders
are also checked.
This commit is contained in:
Karim Jerbi committed 2026-10-15 16:50:47 +01:00
1 parent 6dab5a7229
commit 72c762e944
19 files changed
+1483 -25

No files matched your search

+4
View File
@@ -1019,6 +1019,7 @@ pub(crate) fn selection_overlay<'a>(
pub(crate) struct AuthoringDimension {
pub lines: Vec<[Point; 2]>,
pub round_grips: Vec<Point>,
pub dimensions: Vec<[Point; 2]>,
pub labels: Vec<(Point, String)>,
pub grips: Vec<(Point, Option<Point>)>,
@@ -2410,6 +2411,9 @@ impl canvas::Program<Message> for SelectionCanvas {
..Default::default()
});
}
for point in &dimension.round_grips {
frame.fill(&canvas::Path::circle(*point, 3.5), color);
}
for (point, base) in &dimension.grips {
let marker = if let Some(base) = base {
let dx = point.x - base.x;
+21 -4
View File
@@ -192,6 +192,7 @@ fn catalog(
tab: Tab,
visibility_error: Option<&'static str>,
point_error: Option<&'static str>,
flip_error: Option<&'static str>,
) -> Element<'static, Message> {
let (entries, unavailable) = match tab {
Tab::Parameters => (
@@ -209,7 +210,9 @@ fn catalog(
let visibility = tab == Tab::Parameters && *label == "Visibility";
let point = tab == Tab::Parameters && *label == "Point";
let distance = tab == Tab::Parameters && matches!(*label, "Linear" | "Polar" | "XY");
let enabled = (visibility && visibility_error.is_none()) || ((point || distance) && point_error.is_none());
let orientation = tab == Tab::Parameters && matches!(*label,"Rotation"|"Alignment"|"Flip");
let orientation_error = if *label=="Flip" {flip_error} else {point_error};
let enabled = (orientation && orientation_error.is_none()) || (visibility && visibility_error.is_none()) || ((point || distance) && point_error.is_none());
let base = if enabled {
icons::semantic(icon, 28.0)
} else {
@@ -244,6 +247,12 @@ fn catalog(
.into_owned()
} else if distance {
crate::t!(point_error.unwrap_or("Place the base point, endpoint, and dimension label. Creates one end grip; associate an action to change geometry.")).into_owned()
} else if orientation {
crate::t!(orientation_error.unwrap_or(match *label {
"Rotation" => "Place the center, specify a radius, then the default rotation angle.",
"Alignment" => "Place the alignment base point and tangent direction.",
_ => "Place a reflection line and its flip-state label. Associate a Flip action to mirror geometry.",
})).into_owned()
} else {
unavailable.to_string()
};
@@ -254,6 +263,8 @@ fn catalog(
container(entry).id("authoring-tool-point").into()
} else if distance {
container(entry).id(match *label { "Linear" => "authoring-tool-linear", "Polar" => "authoring-tool-polar", _ => "authoring-tool-xy" }).into()
} else if orientation {
container(entry).id(match *label {"Rotation"=>"authoring-tool-rotation","Alignment"=>"authoring-tool-alignment",_=>"authoring-tool-flip"}).into()
} else {
entry
}
@@ -366,6 +377,7 @@ pub fn view(
chrome: Chrome,
visibility_error: Option<&'static str>,
point_error: Option<&'static str>,
flip_error: Option<&'static str>,
) -> Element<'static, Message> {
let tabs = column(Tab::ALL.into_iter().map(|category| {
let label = category.label();
@@ -415,9 +427,14 @@ pub fn view(
chrome,
),
row![
scrollable(catalog(selected_tab, visibility_error, point_error))
.width(Fill)
.height(Fill),
scrollable(catalog(
selected_tab,
visibility_error,
point_error,
flip_error
))
.width(Fill)
.height(Fill),
tabs
]
.spacing(4)