Complete parametric ribbon and constraint tools

This commit is contained in:
Hakan Seven committed 2026-09-14 01:05:39 +03:00
1 parent f3716bb516
commit 7b4cef41f7
49 files changed
+1438 -103

No files matched your search

+98 -65
View File
@@ -16,7 +16,10 @@ pub use tools::{
colinear, concentric, equal, fixed, horizontal, normal, parallel, perpendicular, symmetric,
tangent, vertical,
};
pub use value::{angle_tool, distance_tool, AngleConstraintCommand, DistanceConstraintCommand};
pub use value::{
angle_tool, dimensional_tools, distance_tool, AngleConstraintCommand,
DistanceConstraintCommand, DistanceMode,
};
use crate::modules::{CadModule, IconKind, ModuleEvent, RibbonGroup, RibbonItem, ToolDef};
@@ -34,68 +37,107 @@ impl CadModule for ParametricModule {
fn ribbon_groups(&self) -> &[RibbonGroup] {
static GROUPS: std::sync::OnceLock<Vec<RibbonGroup>> = std::sync::OnceLock::new();
GROUPS.get_or_init(|| {
let extra_constraints = [
normal::tool(),
center_point_tool::tool(),
midpoint_tool::tool(),
point_on_curve_tool::tool(),
equal_distance_tool::tool(),
];
let command = |id: &'static str, label: &'static str, icon: &'static [u8]| ToolDef {
id, label, icon: IconKind::Svg(icon), event: ModuleEvent::Command(id.to_string()),
};
vec![
RibbonGroup {
title: "Geometry",
title: "Geometric",
tools: vec![
coincident_tool::tool().into(),
parallel::tool().into(),
tangent::tool().into(),
colinear::tool().into(),
perpendicular::tool().into(),
RibbonItem::Dropdown {
id: "PARAMETRIC_MORE",
icon: extra_constraints[0].icon,
items: extra_constraints
.iter()
.map(|tool| (tool.id, tool.label, tool.icon))
.collect(),
default: "NRCONSTRAINT",
RibbonItem::LargeTool(command(
"AUTOCONSTRAIN", "Auto Constrain",
include_bytes!("../../../assets/icons/constrain/auto.svg"),
)),
RibbonItem::ToolGrid { columns: vec![
vec![coincident_tool::tool(), parallel::tool(), tangent::tool()],
vec![
colinear::tool(), perpendicular::tool(),
command("SMOOTHCONSTRAINT", "Smooth", include_bytes!("../../../assets/icons/constrain/smooth.svg")),
],
vec![concentric::tool(), horizontal::tool(), symmetric::tool()],
vec![fixed::tool(), vertical::tool(), equal::tool()],
] },
RibbonItem::LabeledDropdown {
id: "GCVISIBILITY", label: "Show/Hide",
icon: IconKind::Svg(include_bytes!("../../../assets/icons/constrain/show.svg")),
items: vec![
("GCSHOW", "Show", IconKind::Svg(include_bytes!("../../../assets/icons/constrain/show.svg"))),
("GCHIDE", "Hide", IconKind::Svg(include_bytes!("../../../assets/icons/constrain/hide_all.svg"))),
("GCRESET", "Reset", IconKind::Svg(include_bytes!("../../../assets/icons/constrain/show.svg"))),
], default: "GCSHOW",
},
concentric::tool().into(),
horizontal::tool().into(),
symmetric::tool().into(),
fixed::tool().into(),
vertical::tool().into(),
equal::tool().into(),
RibbonItem::LabeledTool(command(
"GCSHOWALL", "Show All", include_bytes!("../../../assets/icons/constrain/show_all.svg"),
)),
RibbonItem::LabeledTool(command(
"GCHIDEALL", "Hide All", include_bytes!("../../../assets/icons/constrain/hide_all.svg"),
)),
],
},
RibbonGroup {
title: "Dimension",
title: "Dimensional",
tools: vec![
RibbonItem::LargeTool(distance_tool::tool()),
RibbonItem::LargeTool(angle_tool::tool()),
RibbonItem::LargeDropdown {
id: "DC_LINEAR_MENU", label: "Linear", icon: dimensional_tools::linear().icon,
items: [dimensional_tools::linear(), dimensional_tools::horizontal(), dimensional_tools::vertical()]
.iter().map(|tool| (tool.id, tool.label, tool.icon)).collect(),
default: "DCLINEAR",
},
RibbonItem::LargeTool(dimensional_tools::aligned()),
RibbonItem::ToolGrid { columns: vec![
vec![dimensional_tools::angular(), dimensional_tools::diameter()],
vec![dimensional_tools::radius(), dimensional_tools::convert()],
] },
RibbonItem::LabeledDropdown {
id: "DCVISIBILITY", label: "Show/Hide",
icon: IconKind::Svg(include_bytes!("../../../assets/icons/constrain/show.svg")),
items: vec![
("DCSHOW", "Show", IconKind::Svg(include_bytes!("../../../assets/icons/constrain/show.svg"))),
("DCHIDE", "Hide", IconKind::Svg(include_bytes!("../../../assets/icons/constrain/hide_all.svg"))),
], default: "DCSHOW",
},
RibbonItem::LabeledTool(command(
"DCSHOWALL", "Show All", include_bytes!("../../../assets/icons/constrain/show_all.svg"),
)),
RibbonItem::LabeledTool(command(
"DCHIDEALL", "Hide All", include_bytes!("../../../assets/icons/constrain/hide_all.svg"),
)),
],
},
RibbonGroup {
title: "Manage",
tools: vec![RibbonItem::LargeTool(ToolDef {
id: "PARAMETERS",
label: "Parameters",
icon: IconKind::Glyph("ƒ"),
event: ModuleEvent::Command("PARAMETERS".to_string()),
})],
tools: vec![
RibbonItem::LargeTool(command(
"DELCONSTRAINT", "Delete Constraints", include_bytes!("../../../assets/icons/constrain/delete.svg"),
)),
RibbonItem::LargeTool(command(
"PARAMETERS", "Parameters Manager", include_bytes!("../../../assets/icons/constrain/parameters.svg"),
)),
],
},
]
})
}
}
inventory::submit!(crate::command::CommandRegistration {
names: &[
"AUTOCONSTRAIN", "SMOOTHCONSTRAINT", "GCSHOW", "GCHIDE", "GCRESET",
"GCSHOWALL", "GCHIDEALL", "DCSHOW", "DCHIDE", "DCSHOWALL", "DCHIDEALL",
"DCCONVERT", "DELCONSTRAINT",
]
});
#[cfg(test)]
mod tests {
use super::*;
fn item_id(item: &RibbonItem) -> &'static str {
match item {
RibbonItem::Tool(tool) | RibbonItem::LargeTool(tool) => tool.id,
RibbonItem::Dropdown { id, .. } | RibbonItem::LargeDropdown { id, .. } => id,
RibbonItem::Tool(tool) | RibbonItem::LabeledTool(tool) | RibbonItem::LargeTool(tool) => tool.id,
RibbonItem::Dropdown { id, .. } | RibbonItem::LabeledDropdown { id, .. }
| RibbonItem::LargeDropdown { id, .. } => id,
RibbonItem::ToolGrid { .. } => "GRID",
_ => panic!("unexpected composite ribbon item"),
}
}
@@ -106,43 +148,34 @@ mod tests {
assert_eq!(
groups.iter().map(|group| group.title).collect::<Vec<_>>(),
["Geometry", "Dimension", "Manage"]
["Geometric", "Dimensional", "Manage"]
);
assert_eq!(
groups[0].tools.iter().map(item_id).collect::<Vec<_>>(),
[
"CCONSTRAINT",
"PCONSTRAINT",
"TCONSTRAINT",
"LCONSTRAINT",
"QCONSTRAINT",
"PARAMETRIC_MORE",
"NCONSTRAINT",
"HCONSTRAINT",
"SYCONSTRAINT",
"FXCONSTRAINT",
"VCONSTRAINT",
"ECONSTRAINT",
]
["AUTOCONSTRAIN", "GRID", "GCVISIBILITY", "GCSHOWALL", "GCHIDEALL"]
);
assert_eq!(
groups[1].tools.iter().map(item_id).collect::<Vec<_>>(),
["DCONSTRAINT", "ACONSTRAINT"]
["DC_LINEAR_MENU", "DCALIGNED", "GRID", "DCVISIBILITY", "DCSHOWALL", "DCHIDEALL"]
);
assert_eq!(item_id(&groups[2].tools[0]), "PARAMETERS");
assert_eq!(groups[2].tools.iter().map(item_id).collect::<Vec<_>>(), ["DELCONSTRAINT", "PARAMETERS"]);
let RibbonItem::Dropdown { items, .. } = &groups[0].tools[5] else {
panic!("additional geometric constraints must stay in a dropdown");
let RibbonItem::LargeDropdown { items, default, .. } = &groups[1].tools[0] else {
panic!("linear dimensional constraint must be a split dropdown");
};
assert_eq!(*default, "DCLINEAR");
assert_eq!(
items.iter().map(|(id, _, _)| *id).collect::<Vec<_>>(),
[
"NRCONSTRAINT",
"CPCONSTRAINT",
"MPCONSTRAINT",
"OCCONSTRAINT",
"EDCONSTRAINT",
]
["DCLINEAR", "DCHORIZONTAL", "DCVERTICAL"]
);
let RibbonItem::ToolGrid { columns } = &groups[0].tools[1] else { panic!("geometry grid") };
assert_eq!(columns.iter().flatten().map(|tool| tool.id).collect::<Vec<_>>(), [
"CCONSTRAINT", "PCONSTRAINT", "TCONSTRAINT", "LCONSTRAINT", "QCONSTRAINT", "SMOOTHCONSTRAINT",
"NCONSTRAINT", "HCONSTRAINT", "SYCONSTRAINT", "FXCONSTRAINT", "VCONSTRAINT", "ECONSTRAINT",
]);
let RibbonItem::ToolGrid { columns } = &groups[1].tools[2] else { panic!("dimension grid") };
assert_eq!(columns.iter().flatten().map(|tool| tool.id).collect::<Vec<_>>(), [
"DCANGULAR", "DCDIAMETER", "DCRADIUS", "DCCONVERT",
]);
}
}