mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-09 07:16:52 +00:00
- Extend vectorcraft-geom::shapes with clean-room open/pie/chord/arc ellipse and rectangle builders, per-corner radii, polygon/star corner rounding and deterministic vertex randomization, logarithmic/Archimedean spiral divergence and inner radius, and perspective/isometric 3D box face builders. - Isolate 3D Box and customizable shape parameters as a VectorCraft add-on under crates/*/src/**/addons/: - crates/tools/src/addons/box3d.rs (Box3dTool and CustomShape extras stored under Node::extras["vc-shape-tools"]) - crates/engine/src/cmd/addons/shapes.rs (addon.shapes.rectangle, addon.shapes.ellipse, addon.shapes.polygon, addon.shapes.star, addon.shapes.spiral, addon.shapes.box3d, and addon.shapes.configure) - crates/ui-egui/src/addons/shapes.rs (Object > Plug-ins > 3D Box > 3D Box Tool and context-sensitive Control bar options) - Keep crates/engine/src/cmd/create.rs, crates/tools/src/shape.rs, and crates/tools/src/draw2/family.rs byte-for-byte identical to main. - Add translations for "3D Box" and "3D Box Tool" across all 26 .tsv catalogs in crates/ui-egui/src/i18n/.
93 lines
4.7 KiB
Rust
93 lines
4.7 KiB
Rust
//! The tools of VectorCraft's built-in add-ons. They aren't in the toolbars as they ship: the All
|
|
//! Tools drawer lists them (under their add-on's heading) so they can be added, and each add-on's
|
|
//! submenu selects them. Tool gestures emit engine commands.
|
|
|
|
pub mod anchor_eraser;
|
|
pub mod box3d;
|
|
pub mod contour;
|
|
pub mod corner_tool;
|
|
pub mod enclosing_lasso;
|
|
pub mod extend_path;
|
|
pub mod measure_inspect;
|
|
pub mod parametric_shape;
|
|
pub mod shared_edge;
|
|
pub mod slide_anchor;
|
|
pub mod smart_pen;
|
|
|
|
use crate::{Tool, ToolInfo};
|
|
|
|
/// Path Precision Tools' tools, one flyout group.
|
|
pub const PATH_TOOLS: &[ToolInfo] = &[
|
|
ToolInfo { id: anchor_eraser::ID, label: "Anchor Eraser Brush", shortcut: None, icon: "dc-remove-brush" },
|
|
ToolInfo { id: extend_path::ID, label: "Extend Path Tool", shortcut: None, icon: "dc-arrow-extend" },
|
|
ToolInfo { id: measure_inspect::ID, label: "Measure & Inspect Tool", shortcut: None, icon: "ruler" },
|
|
ToolInfo { id: parametric_shape::ID, label: "Parametric Shape Tool", shortcut: None, icon: "hexagon" },
|
|
ToolInfo { id: corner_tool::ID, label: "Corner Tool", shortcut: None, icon: "dc-rounded-rect" },
|
|
ToolInfo { id: slide_anchor::ID, label: "Slide Anchor Along Path", shortcut: None, icon: "dc-anchor" },
|
|
ToolInfo { id: shared_edge::ID, label: "Shared Edge Merge Tool", shortcut: None, icon: "dc-join" },
|
|
];
|
|
|
|
/// Smart Pen's tool, a flyout group of its own.
|
|
pub const SMART_PEN: &[ToolInfo] = &[ToolInfo { id: smart_pen::ID, label: "Smart Pen Tool", shortcut: None, icon: "spline-pointer" }];
|
|
|
|
pub const CONTOUR: &[ToolInfo] = &[ToolInfo { id: contour::ID, label: "Contour Tool", shortcut: None, icon: "tool-contour" }];
|
|
|
|
pub const ENCLOSING_LASSO: &[ToolInfo] = &[ToolInfo { id: enclosing_lasso::ID, label: "Enclosing Lasso Tool", shortcut: None, icon: "tool-lasso" }];
|
|
|
|
pub const BOX_3D: &[ToolInfo] = &[ToolInfo { id: box3d::ID, label: "3D Box Tool", shortcut: None, icon: "box" }];
|
|
|
|
/// The add-ons' tool groups, after the toolbar's own ([`crate::TOOL_GROUPS`]), each with the
|
|
/// heading the All Tools drawer lists it under (its add-on's name).
|
|
const HEADED: &[(&[ToolInfo], &str)] =
|
|
&[(PATH_TOOLS, "Path Precision Tools"), (SMART_PEN, "Smart Pen"), (ENCLOSING_LASSO, "Enclosing Lasso"), (CONTOUR, "Contour"), (BOX_3D, "3D Box")];
|
|
|
|
/// The add-ons' tool groups, after the toolbar's own ([`crate::TOOL_GROUPS`]).
|
|
pub const GROUPS: &[&[ToolInfo]] = &[PATH_TOOLS, SMART_PEN, ENCLOSING_LASSO, CONTOUR, BOX_3D];
|
|
|
|
/// The heading the All Tools drawer lists tool `id` under: its add-on's name.
|
|
pub fn heading(id: &str) -> Option<&'static str> {
|
|
HEADED.iter().find(|(g, _)| g.iter().any(|t| t.id == id)).map(|(_, h)| *h)
|
|
}
|
|
|
|
/// Create an add-on tool by id (None = not one of them).
|
|
pub fn create(id: &str) -> Option<Box<dyn Tool>> {
|
|
Some(match id {
|
|
anchor_eraser::ID => Box::new(anchor_eraser::AnchorEraserTool::default()),
|
|
corner_tool::ID => Box::new(corner_tool::CornerTool::default()),
|
|
extend_path::ID => Box::new(extend_path::ExtendPathTool::default()),
|
|
measure_inspect::ID => Box::new(measure_inspect::MeasureInspectTool::default()),
|
|
parametric_shape::ID => Box::new(parametric_shape::ParametricShapeTool::default()),
|
|
shared_edge::ID => Box::new(shared_edge::SharedEdgeTool::default()),
|
|
slide_anchor::ID => Box::new(slide_anchor::SlideAnchorTool::default()),
|
|
smart_pen::ID => Box::new(smart_pen::SmartPenTool::default()),
|
|
enclosing_lasso::ID => Box::new(enclosing_lasso::EnclosingLassoTool::default()),
|
|
contour::ID => Box::new(contour::ContourTool::default()),
|
|
box3d::ID => Box::new(box3d::Box3dTool::default()),
|
|
_ => return None,
|
|
})
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn every_addon_tool_is_made_and_catalogued_but_not_in_the_toolbar() {
|
|
for t in GROUPS.iter().flat_map(|g| g.iter()) {
|
|
assert_eq!(create(t.id).map(|tool| tool.id()), Some(t.id));
|
|
assert_eq!(crate::tool_info(t.id).map(|i| i.label), Some(t.label));
|
|
assert!(crate::create(t.id).id() == t.id);
|
|
assert!(t.shortcut.is_none(), "no default shortcut");
|
|
assert!(!crate::TOOL_GROUPS.iter().any(|g| g.iter().any(|x| x.id == t.id)), "{} is not in the toolbar's groups", t.id);
|
|
}
|
|
assert!(PATH_TOOLS.iter().all(|t| heading(t.id) == Some("Path Precision Tools")));
|
|
assert_eq!(heading(smart_pen::ID), Some("Smart Pen"));
|
|
assert_eq!(heading(enclosing_lasso::ID), Some("Enclosing Lasso"));
|
|
assert_eq!(heading(contour::ID), Some("Contour"));
|
|
assert_eq!(heading(box3d::ID), Some("3D Box"));
|
|
assert_eq!(GROUPS.len(), HEADED.len());
|
|
assert!(crate::catalog::all_tools().any(|t| t.id == extend_path::ID));
|
|
assert!(create("pen").is_none() && heading("pen").is_none());
|
|
}
|
|
}
|