forked from 1b-agent/opencadstudio-upstream
refactor(ribbon): simplify draw tool registry
This commit is contained in:
1 parent
1b3efb84de
commit
acba9f87bd
17 files changed
+269
-150
No files matched your search
@@ -4,7 +4,6 @@
|
||||
use std::path::Path;
|
||||
|
||||
fn main() {
|
||||
generate_draw_panel_tools();
|
||||
let version = std::env::var("CARGO_PKG_VERSION").expect("Cargo package version");
|
||||
let parts: Vec<&str> = version.split('.').collect();
|
||||
let app_version = if parts.len() == 3 && parts[0].len() == 4
|
||||
@@ -82,27 +81,3 @@ fn main() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Each drawing button can contribute its own expression without editing a registry.
|
||||
fn generate_draw_panel_tools() {
|
||||
let manifest = std::path::PathBuf::from(std::env::var_os("CARGO_MANIFEST_DIR").expect("Cargo manifest directory"));
|
||||
let directory = manifest.join("src/ui/ribbon/draw_tools");
|
||||
println!("cargo:rerun-if-changed={}", directory.display());
|
||||
let mut files = match std::fs::read_dir(&directory) {
|
||||
Ok(entries) => entries.map(|entry| entry.expect("Draw tool directory entry").path())
|
||||
.filter(|path| path.is_file() && path.extension().is_some_and(|extension| extension == "rs"))
|
||||
.collect::<Vec<_>>(),
|
||||
Err(error) if error.kind() == std::io::ErrorKind::NotFound => Vec::new(),
|
||||
Err(error) => panic!("Cannot read Draw tool contributions: {error}"),
|
||||
};
|
||||
files.sort_by(|left, right| left.file_name().cmp(&right.file_name()));
|
||||
let mut source = String::from("const TOOLS: &[Tool] = &[\n");
|
||||
for path in files {
|
||||
println!("cargo:rerun-if-changed={}", path.display());
|
||||
// Debug string formatting produces a quoted Rust literal, including Windows escapes.
|
||||
source.push_str(&format!(" include!({:?}),\n", path.to_str().expect("UTF-8 Draw tool path")));
|
||||
}
|
||||
source.push_str("];\n");
|
||||
let output = std::path::PathBuf::from(std::env::var_os("OUT_DIR").expect("Cargo output directory"));
|
||||
std::fs::write(output.join("draw_panel_tools.rs"), source).expect("Write Draw tool registry");
|
||||
}
|
||||
@@ -4,10 +4,10 @@
|
||||
// Produces a Ray entity; repeats until Enter/Esc.
|
||||
// XLINE — infinite construction line: same two-click pattern, yields XLine.
|
||||
|
||||
use crate::t;
|
||||
use acadrust::entities::{Ray as RayEnt, XLine as XLineEnt};
|
||||
use acadrust::types::Vector3;
|
||||
use acadrust::EntityType;
|
||||
use crate::t;
|
||||
|
||||
use crate::command::{CadCommand, CmdResult};
|
||||
use crate::scene::model::wire_model::WireModel;
|
||||
@@ -226,7 +226,34 @@ impl CadCommand for XLineCommand {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn repeated_rays_and_xlines_keep_their_first_point() {
|
||||
let base = DVec3::new(2.0, 3.0, 4.0);
|
||||
let mut ray = RayCommand::new();
|
||||
let mut xline = XLineCommand::new();
|
||||
assert!(matches!(ray.on_point(base), CmdResult::NeedPoint));
|
||||
assert!(matches!(xline.on_point(base), CmdResult::NeedPoint));
|
||||
|
||||
for through in [DVec3::new(3.0, 3.0, 4.0), DVec3::new(2.0, 5.0, 4.0)] {
|
||||
let CmdResult::CommitEntity(EntityType::Ray(entity)) = ray.on_point(through) else {
|
||||
panic!("ray was not committed");
|
||||
};
|
||||
assert_eq!(entity.base_point, Vector3::new(base.x, base.y, base.z));
|
||||
|
||||
let CmdResult::CommitEntity(EntityType::XLine(entity)) = xline.on_point(through) else {
|
||||
panic!("construction line was not committed");
|
||||
};
|
||||
assert_eq!(entity.base_point, Vector3::new(base.x, base.y, base.z));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Autocomplete registry ─────────────────────────────────
|
||||
inventory::submit!(crate::command::CommandRegistration { names: &["RAY"] }); // RayCommand
|
||||
inventory::submit!(crate::command::CommandRegistration { names: &["CONSTRUCTIONLINE", "XLINE"] }); // XLineCommand
|
||||
inventory::submit!(crate::command::CommandRegistration { names: &["RAY"] }); // RayCommand
|
||||
inventory::submit!(crate::command::CommandRegistration {
|
||||
names: &["CONSTRUCTIONLINE", "XLINE"]
|
||||
}); // XLineCommand
|
||||
@@ -3,9 +3,9 @@
|
||||
// Command: SPLINE (SPL)
|
||||
// Click to add fit points. Enter (≥2 pts) → commits EntityType::Spline.
|
||||
|
||||
use crate::t;
|
||||
use acadrust::types::Vector3;
|
||||
use acadrust::{EntityType, Spline};
|
||||
use crate::t;
|
||||
|
||||
use crate::command::{CadCommand, CmdResult};
|
||||
use crate::modules::{IconKind, ModuleEvent, ToolDef};
|
||||
@@ -30,27 +30,35 @@ pub struct SplineCommand {
|
||||
|
||||
impl SplineCommand {
|
||||
pub fn new() -> Self {
|
||||
Self { pts: Vec::new(), control_vertices: false, choosing_method: false }
|
||||
Self {
|
||||
pts: Vec::new(),
|
||||
control_vertices: false,
|
||||
choosing_method: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn control_vertices() -> Self {
|
||||
Self { control_vertices: true, ..Self::new() }
|
||||
Self {
|
||||
control_vertices: true,
|
||||
..Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
fn build(&self, closed: bool) -> Option<EntityType> {
|
||||
if self.pts.len() < 2 {
|
||||
return None;
|
||||
}
|
||||
Some(EntityType::Spline(make_spline(&self.pts, closed, self.control_vertices)))
|
||||
Some(EntityType::Spline(make_spline(
|
||||
&self.pts,
|
||||
closed,
|
||||
self.control_vertices,
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
/// Store the chosen construction method directly in the persistent spline.
|
||||
fn make_spline(pts: &[DVec3], closed: bool, control_vertices: bool) -> Spline {
|
||||
let mut points: Vec<Vector3> = pts
|
||||
.iter()
|
||||
.map(|p| Vector3::new(p.x, p.y, p.z))
|
||||
.collect();
|
||||
let mut points: Vec<Vector3> = pts.iter().map(|p| Vector3::new(p.x, p.y, p.z)).collect();
|
||||
let mut spline = if control_vertices {
|
||||
if closed && pts.first() != pts.last() {
|
||||
points.push(points[0]);
|
||||
@@ -61,9 +69,13 @@ fn make_spline(pts: &[DVec3], closed: bool, control_vertices: bool) -> Spline {
|
||||
// Open uniform knot vector: degree + 1 equal knots at each endpoint.
|
||||
let knots = (0..count + degree + 1)
|
||||
.map(|index| {
|
||||
if index <= degree { 0.0 }
|
||||
else if index >= count { 1.0 }
|
||||
else { (index - degree) as f64 / spans as f64 }
|
||||
if index <= degree {
|
||||
0.0
|
||||
} else if index >= count {
|
||||
1.0
|
||||
} else {
|
||||
(index - degree) as f64 / spans as f64
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
Spline {
|
||||
@@ -103,7 +115,11 @@ fn sample_curve(pts: &[DVec3], closed: bool, control_vertices: bool) -> Vec<[f32
|
||||
|
||||
impl CadCommand for SplineCommand {
|
||||
fn name(&self) -> &'static str {
|
||||
if self.control_vertices { "SPLINECV" } else { "SPLINE" }
|
||||
if self.control_vertices {
|
||||
"SPLINECV"
|
||||
} else {
|
||||
"SPLINE"
|
||||
}
|
||||
}
|
||||
|
||||
fn prompt(&self) -> String {
|
||||
@@ -122,7 +138,10 @@ impl CadCommand for SplineCommand {
|
||||
fn options(&self) -> Vec<crate::command::CmdOption> {
|
||||
use crate::command::CmdOption;
|
||||
if self.choosing_method {
|
||||
return vec![CmdOption::new("Fit", "FIT"), CmdOption::new("Control vertices", "CV")];
|
||||
return vec![
|
||||
CmdOption::new("Fit", "FIT"),
|
||||
CmdOption::new("Control vertices", "CV"),
|
||||
];
|
||||
}
|
||||
if self.pts.is_empty() {
|
||||
return vec![CmdOption::new(t!("Method").as_ref(), "M")];
|
||||
@@ -213,6 +232,32 @@ impl CadCommand for SplineCommand {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn control_vertices_make_a_clamped_finite_spline() {
|
||||
let points = [
|
||||
DVec3::new(0.0, 0.0, 0.0),
|
||||
DVec3::new(1.0, 2.0, 0.0),
|
||||
DVec3::new(2.0, 2.0, 0.0),
|
||||
DVec3::new(3.0, 0.0, 0.0),
|
||||
];
|
||||
let spline = make_spline(&points, false, true);
|
||||
|
||||
assert_eq!(spline.degree, 3);
|
||||
assert_eq!(spline.control_points.len(), 4);
|
||||
assert_eq!(spline.knots, vec![0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0]);
|
||||
assert_eq!(spline.weights, vec![1.0; 4]);
|
||||
assert!(sample_curve(&points, false, true)
|
||||
.iter()
|
||||
.flatten()
|
||||
.all(|value| value.is_finite()));
|
||||
}
|
||||
}
|
||||
|
||||
// ── Autocomplete registry ─────────────────────────────────
|
||||
inventory::submit!(crate::command::CommandRegistration { names: &["SPLINE", "SPLINECV"] }); // SplineCommand
|
||||
inventory::submit!(crate::command::CommandRegistration {
|
||||
names: &["SPLINE", "SPLINECV"]
|
||||
}); // SplineCommand
|
||||
+180
-26
@@ -5,7 +5,10 @@ use std::time::Duration;
|
||||
use iced::widget::{button, column, container, row, scrollable, text, tooltip};
|
||||
use iced::{Element, Fill, Theme};
|
||||
|
||||
use super::widgets::{make_icon, make_tip, muted_text_style, popup_panel_style, popup_row_style, tip_style, tool_btn_style};
|
||||
use super::widgets::{
|
||||
make_icon, make_tip, muted_text_style, popup_panel_style, popup_row_style, tip_style,
|
||||
tool_btn_style,
|
||||
};
|
||||
use super::{dropdown_backdrop, position_ribbon_dropdown, Ribbon};
|
||||
use crate::app::Message;
|
||||
use crate::modules::IconKind;
|
||||
@@ -23,39 +26,158 @@ struct Tool {
|
||||
options: &'static [(&'static str, &'static str)],
|
||||
}
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/draw_panel_tools.rs"));
|
||||
const TOOLS: &[Tool] = &[
|
||||
Tool {
|
||||
command: "SPLINE",
|
||||
label: "Spline Fit",
|
||||
icon: include_bytes!("../../../assets/icons/spline.svg"),
|
||||
options: &[],
|
||||
},
|
||||
Tool {
|
||||
command: "SPLINECV",
|
||||
label: "Spline CV",
|
||||
icon: include_bytes!("../../../assets/icons/spline_cv.svg"),
|
||||
options: &[],
|
||||
},
|
||||
Tool {
|
||||
command: "XLINE",
|
||||
label: "Construction Line",
|
||||
icon: include_bytes!("../../../assets/icons/xline.svg"),
|
||||
options: &[],
|
||||
},
|
||||
Tool {
|
||||
command: "RAY",
|
||||
label: "Ray",
|
||||
icon: include_bytes!("../../../assets/icons/ray.svg"),
|
||||
options: &[],
|
||||
},
|
||||
Tool {
|
||||
command: "MULTIPOINT",
|
||||
label: "Multiple Points",
|
||||
icon: include_bytes!("../../../assets/icons/multipoint.svg"),
|
||||
options: &[
|
||||
("POINT", "Single Point"),
|
||||
("MULTIPOINT", "Multiple Points"),
|
||||
("DDPTYPE", "Point Style"),
|
||||
],
|
||||
},
|
||||
Tool {
|
||||
command: "DIVIDE",
|
||||
label: "Divide",
|
||||
icon: include_bytes!("../../../assets/icons/divide.svg"),
|
||||
options: &[],
|
||||
},
|
||||
Tool {
|
||||
command: "MEASURE",
|
||||
label: "Measure",
|
||||
icon: include_bytes!("../../../assets/icons/measure.svg"),
|
||||
options: &[],
|
||||
},
|
||||
Tool {
|
||||
command: "REGION",
|
||||
label: "Region",
|
||||
icon: include_bytes!("../../../assets/icons/region.svg"),
|
||||
options: &[],
|
||||
},
|
||||
Tool {
|
||||
command: "WIPEOUT",
|
||||
label: "Wipeout",
|
||||
icon: include_bytes!("../../../assets/icons/wipeout.svg"),
|
||||
options: &[],
|
||||
},
|
||||
Tool {
|
||||
command: "BOUNDARY",
|
||||
label: "Boundary",
|
||||
icon: include_bytes!("../../../assets/icons/boundary.svg"),
|
||||
options: &[],
|
||||
},
|
||||
Tool {
|
||||
command: "HELIX",
|
||||
label: "Helix",
|
||||
icon: include_bytes!("../../../assets/icons/helix.svg"),
|
||||
options: &[],
|
||||
},
|
||||
Tool {
|
||||
command: "DONUT",
|
||||
label: "Donut",
|
||||
icon: include_bytes!("../../../assets/icons/donut.svg"),
|
||||
options: &[],
|
||||
},
|
||||
Tool {
|
||||
command: "REVCLOUD",
|
||||
label: "Revision Cloud",
|
||||
icon: include_bytes!("../../../assets/icons/revcloud.svg"),
|
||||
options: &[
|
||||
("REVCLOUD_RECTANGULAR", "Rectangular"),
|
||||
("REVCLOUD_POLYGONAL", "Polygonal"),
|
||||
("REVCLOUD_FREEHAND", "Freehand"),
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
pub(super) fn owns_dropdown(id: &str) -> bool {
|
||||
id == PANEL_ID || TOOLS.iter().any(|tool| tool.command == id && !tool.options.is_empty())
|
||||
id == PANEL_ID
|
||||
|| TOOLS
|
||||
.iter()
|
||||
.any(|tool| tool.command == id && !tool.options.is_empty())
|
||||
}
|
||||
|
||||
pub(super) fn group_title<'a>(title: &'static str, open: &Option<String>) -> Element<'a, Message> {
|
||||
if title != "Draw" || TOOLS.is_empty() {
|
||||
return container(text(t!(title)).size(9).style(muted_text_style)).padding([1, 4]).into();
|
||||
return container(text(t!(title)).size(9).style(muted_text_style))
|
||||
.padding([1, 4])
|
||||
.into();
|
||||
}
|
||||
let expanded = open.as_deref().is_some_and(owns_dropdown);
|
||||
let arrow = if expanded { icons::themed_arrow_up(7.0) } else { icons::themed_arrow_down(7.0) };
|
||||
PosReport::new(PANEL_ID, button(row![text(t!(title)).size(9), arrow].spacing(4).align_y(iced::Center))
|
||||
let arrow = if expanded {
|
||||
icons::themed_arrow_up(7.0)
|
||||
} else {
|
||||
icons::themed_arrow_down(7.0)
|
||||
};
|
||||
PosReport::new(
|
||||
PANEL_ID,
|
||||
button(
|
||||
row![text(t!(title)).size(9), arrow]
|
||||
.spacing(4)
|
||||
.align_y(iced::Center),
|
||||
)
|
||||
.on_press(Message::ToggleRibbonDropdown(PANEL_ID.to_string()))
|
||||
.style(move |theme: &Theme, status| tool_btn_style(theme, expanded, status))
|
||||
.padding([1, 4])).into()
|
||||
.padding([1, 4]),
|
||||
)
|
||||
.into()
|
||||
}
|
||||
|
||||
fn tool_button(tool: &Tool, active: bool) -> Element<'static, Message> {
|
||||
let face = button(make_icon(IconKind::Svg(tool.icon), 23.0))
|
||||
.on_press(Message::DropdownSelectItem { dropdown_id: PANEL_ID, cmd: tool.command })
|
||||
.on_press(Message::DropdownSelectItem {
|
||||
dropdown_id: PANEL_ID,
|
||||
cmd: tool.command,
|
||||
})
|
||||
.style(move |theme: &Theme, status| tool_btn_style(theme, active, status))
|
||||
.width(if tool.options.is_empty() { CELL } else { CELL - 11.0 })
|
||||
.width(if tool.options.is_empty() {
|
||||
CELL
|
||||
} else {
|
||||
CELL - 11.0
|
||||
})
|
||||
.height(CELL)
|
||||
.padding(3);
|
||||
let tip = format!("{}\n{} {}", t!(tool.label), t!("Command:"), tool.command);
|
||||
let face: Element<'static, Message> = tooltip(face, make_tip(tip), tooltip::Position::Bottom)
|
||||
.delay(Duration::from_millis(400)).style(tip_style).into();
|
||||
.delay(Duration::from_millis(400))
|
||||
.style(tip_style)
|
||||
.into();
|
||||
if !tool.options.is_empty() {
|
||||
row![face, button(icons::themed_arrow_down(6.0))
|
||||
.on_press(Message::ToggleRibbonDropdown(tool.command.to_string()))
|
||||
.style(move |theme: &Theme, status| tool_btn_style(theme, false, status))
|
||||
.width(11).height(CELL).padding(2)].into()
|
||||
row![
|
||||
face,
|
||||
button(icons::themed_arrow_down(6.0))
|
||||
.on_press(Message::ToggleRibbonDropdown(tool.command.to_string()))
|
||||
.style(move |theme: &Theme, status| tool_btn_style(theme, false, status))
|
||||
.width(11)
|
||||
.height(CELL)
|
||||
.padding(2)
|
||||
]
|
||||
.into()
|
||||
} else {
|
||||
face
|
||||
}
|
||||
@@ -65,28 +187,60 @@ pub(super) fn overlay<'a>(ribbon: &Ribbon, id: &str, win: (f32, f32)) -> Element
|
||||
let width = (7.0 * (CELL + GAP) - GAP + 12.0).min((win.0 - 8.0).max(CELL + 12.0));
|
||||
let (_, x, anchor_top) = ribbon.dd_anchor(PANEL_ID, width, win.0);
|
||||
let anchor = crate::ui::wrap_bar::dropdown_bounds(PANEL_ID);
|
||||
let left = anchor.map_or(x, |b| b.x).clamp(0.0, (win.0 - width).max(0.0));
|
||||
let left = anchor
|
||||
.map_or(x, |b| b.x)
|
||||
.clamp(0.0, (win.0 - width).max(0.0));
|
||||
let top = anchor_top.min((win.1 - 80.0).max(0.0));
|
||||
let available_height = (win.1 - top - 4.0).max(1.0);
|
||||
let cols = (((width - 12.0 + GAP) / (CELL + GAP)).floor() as usize).clamp(1, 7);
|
||||
let option_tool = TOOLS.iter().find(|tool| tool.command == id && !tool.options.is_empty());
|
||||
let option_tool = TOOLS
|
||||
.iter()
|
||||
.find(|tool| tool.command == id && !tool.options.is_empty());
|
||||
let content_height = option_tool.map_or_else(
|
||||
|| TOOLS.len().div_ceil(cols) as f32 * (CELL + GAP) - GAP + 12.0,
|
||||
|tool| tool.options.len() as f32 * 30.0,
|
||||
);
|
||||
let contents: Element<'static, Message> = if let Some(tool) = option_tool {
|
||||
column(tool.options.iter().map(|&(cmd, label)| {
|
||||
button(text(t!(label)).size(11))
|
||||
.on_press(Message::DropdownSelectItem { dropdown_id: tool.command, cmd })
|
||||
.style(popup_row_style).width(Fill).height(30).padding([6, 10]).into()
|
||||
}).collect::<Vec<Element<'static, Message>>>()).into()
|
||||
column(
|
||||
tool.options
|
||||
.iter()
|
||||
.map(|&(cmd, label)| {
|
||||
button(text(t!(label)).size(11))
|
||||
.on_press(Message::DropdownSelectItem {
|
||||
dropdown_id: tool.command,
|
||||
cmd,
|
||||
})
|
||||
.style(popup_row_style)
|
||||
.width(Fill)
|
||||
.height(30)
|
||||
.padding([6, 10])
|
||||
.into()
|
||||
})
|
||||
.collect::<Vec<Element<'static, Message>>>(),
|
||||
)
|
||||
.into()
|
||||
} else {
|
||||
column(TOOLS.chunks(cols).map(|tools| {
|
||||
row(tools.iter().map(|tool| tool_button(tool, ribbon.active_tool.as_deref() == Some(tool.command)))
|
||||
.collect::<Vec<_>>()).spacing(GAP).into()
|
||||
}).collect::<Vec<Element<'static, Message>>>()).spacing(GAP).padding(6).into()
|
||||
column(
|
||||
TOOLS
|
||||
.chunks(cols)
|
||||
.map(|tools| {
|
||||
row(tools
|
||||
.iter()
|
||||
.map(|tool| {
|
||||
tool_button(tool, ribbon.active_tool.as_deref() == Some(tool.command))
|
||||
})
|
||||
.collect::<Vec<_>>())
|
||||
.spacing(GAP)
|
||||
.into()
|
||||
})
|
||||
.collect::<Vec<Element<'static, Message>>>(),
|
||||
)
|
||||
.spacing(GAP)
|
||||
.padding(6)
|
||||
.into()
|
||||
};
|
||||
let panel = container(scrollable(contents).height(content_height.min(available_height)))
|
||||
.width(width).style(popup_panel_style);
|
||||
.width(width)
|
||||
.style(popup_panel_style);
|
||||
dropdown_backdrop(position_ribbon_dropdown(panel.into(), false, left, top))
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
Tool {
|
||||
command: "SPLINE",
|
||||
label: "Spline Fit",
|
||||
icon: include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/assets/icons/spline.svg")),
|
||||
options: &[],
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
Tool {
|
||||
command: "SPLINECV",
|
||||
label: "Spline CV",
|
||||
icon: include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/assets/icons/spline_cv.svg")),
|
||||
options: &[],
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
Tool {
|
||||
command: "XLINE",
|
||||
label: "Construction Line",
|
||||
icon: include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/assets/icons/xline.svg")),
|
||||
options: &[],
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
Tool {
|
||||
command: "RAY",
|
||||
label: "Ray",
|
||||
icon: include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/assets/icons/ray.svg")),
|
||||
options: &[],
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
Tool {
|
||||
command: "MULTIPOINT",
|
||||
label: "Multiple Points",
|
||||
icon: include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/assets/icons/multipoint.svg")),
|
||||
options: &[("POINT", "Single Point"), ("MULTIPOINT", "Multiple Points"), ("DDPTYPE", "Point Style")],
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
Tool {
|
||||
command: "DIVIDE",
|
||||
label: "Divide",
|
||||
icon: include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/assets/icons/divide.svg")),
|
||||
options: &[],
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
Tool {
|
||||
command: "MEASURE",
|
||||
label: "Measure",
|
||||
icon: include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/assets/icons/measure.svg")),
|
||||
options: &[],
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
Tool {
|
||||
command: "REGION",
|
||||
label: "Region",
|
||||
icon: include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/assets/icons/region.svg")),
|
||||
options: &[],
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
Tool {
|
||||
command: "WIPEOUT",
|
||||
label: "Wipeout",
|
||||
icon: include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/assets/icons/wipeout.svg")),
|
||||
options: &[],
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
Tool {
|
||||
command: "BOUNDARY",
|
||||
label: "Boundary",
|
||||
icon: include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/assets/icons/boundary.svg")),
|
||||
options: &[],
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
Tool {
|
||||
command: "HELIX",
|
||||
label: "Helix",
|
||||
icon: include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/assets/icons/helix.svg")),
|
||||
options: &[],
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
Tool {
|
||||
command: "DONUT",
|
||||
label: "Donut",
|
||||
icon: include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/assets/icons/donut.svg")),
|
||||
options: &[],
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
Tool {
|
||||
command: "REVCLOUD",
|
||||
label: "Revision Cloud",
|
||||
icon: include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/assets/icons/revcloud.svg")),
|
||||
options: &[
|
||||
("REVCLOUD_RECTANGULAR", "Rectangular"),
|
||||
("REVCLOUD_POLYGONAL", "Polygonal"),
|
||||
("REVCLOUD_FREEHAND", "Freehand"),
|
||||
],
|
||||
}
|
||||
Reference in new issue
Block a user