forked from 1b-agent/opencadstudio-upstream
cargo fmt
This commit is contained in:
1 parent
468cbde5e1
commit
ca07268c7c
204 files changed
+13997
-5525
No files matched your search
@@ -1,5 +1,10 @@
|
||||
use crate::modules::{IconKind, ModuleEvent, ToolDef};
|
||||
pub const ICON: IconKind = IconKind::Svg(include_bytes!("../../../assets/icons/attdef.svg"));
|
||||
pub fn tool() -> ToolDef {
|
||||
ToolDef { id: "ATTDEF", label: "Define\nAttributes", icon: ICON, event: ModuleEvent::Command("ATTDEF".to_string()) }
|
||||
ToolDef {
|
||||
id: "ATTDEF",
|
||||
label: "Define\nAttributes",
|
||||
icon: ICON,
|
||||
event: ModuleEvent::Command("ATTDEF".to_string()),
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,10 @@
|
||||
use crate::modules::{IconKind, ModuleEvent, ToolDef};
|
||||
pub const ICON: IconKind = IconKind::Svg(include_bytes!("../../../assets/icons/attedit.svg"));
|
||||
pub fn tool() -> ToolDef {
|
||||
ToolDef { id: "ATTEDIT", label: "Edit\nAttribute", icon: ICON, event: ModuleEvent::Command("ATTEDIT".to_string()) }
|
||||
ToolDef {
|
||||
id: "ATTEDIT",
|
||||
label: "Edit\nAttribute",
|
||||
icon: ICON,
|
||||
event: ModuleEvent::Command("ATTEDIT".to_string()),
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,10 @@
|
||||
use crate::modules::{IconKind, ModuleEvent, ToolDef};
|
||||
pub const ICON: IconKind = IconKind::Svg(include_bytes!("../../../assets/icons/attman.svg"));
|
||||
pub fn tool() -> ToolDef {
|
||||
ToolDef { id: "ATTMAN", label: "Manage", icon: ICON, event: ModuleEvent::Command("ATTMAN".to_string()) }
|
||||
ToolDef {
|
||||
id: "ATTMAN",
|
||||
label: "Manage",
|
||||
icon: ICON,
|
||||
event: ModuleEvent::Command("ATTMAN".to_string()),
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,10 @@
|
||||
use crate::modules::{IconKind, ModuleEvent, ToolDef};
|
||||
pub const ICON: IconKind = IconKind::Svg(include_bytes!("../../../assets/icons/attsync.svg"));
|
||||
pub fn tool() -> ToolDef {
|
||||
ToolDef { id: "ATTSYNC", label: "Synchronize", icon: ICON, event: ModuleEvent::Command("ATTSYNC".to_string()) }
|
||||
ToolDef {
|
||||
id: "ATTSYNC",
|
||||
label: "Synchronize",
|
||||
icon: ICON,
|
||||
event: ModuleEvent::Command("ATTSYNC".to_string()),
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,10 @@
|
||||
use crate::modules::{IconKind, ModuleEvent, ToolDef};
|
||||
pub const ICON: IconKind = IconKind::Svg(include_bytes!("../../../assets/icons/base_point.svg"));
|
||||
pub fn tool() -> ToolDef {
|
||||
ToolDef { id: "BASE", label: "Set Base\nPoint", icon: ICON, event: ModuleEvent::Command("BASE".to_string()) }
|
||||
ToolDef {
|
||||
id: "BASE",
|
||||
label: "Set Base\nPoint",
|
||||
icon: ICON,
|
||||
event: ModuleEvent::Command("BASE".to_string()),
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,11 @@
|
||||
use crate::modules::{IconKind, ModuleEvent, ToolDef};
|
||||
pub const ICON: IconKind = IconKind::Svg(include_bytes!("../../../assets/icons/content_browser.svg"));
|
||||
pub const ICON: IconKind =
|
||||
IconKind::Svg(include_bytes!("../../../assets/icons/content_browser.svg"));
|
||||
pub fn tool() -> ToolDef {
|
||||
ToolDef { id: "CONTENTBROWSER", label: "Content\nBrowser", icon: ICON, event: ModuleEvent::Command("CONTENTBROWSER".to_string()) }
|
||||
ToolDef {
|
||||
id: "CONTENTBROWSER",
|
||||
label: "Content\nBrowser",
|
||||
icon: ICON,
|
||||
event: ModuleEvent::Command("CONTENTBROWSER".to_string()),
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,10 @@
|
||||
use crate::modules::{IconKind, ModuleEvent, ToolDef};
|
||||
pub const ICON: IconKind = IconKind::Svg(include_bytes!("../../../assets/icons/design_center.svg"));
|
||||
pub fn tool() -> ToolDef {
|
||||
ToolDef { id: "ADCENTER", label: "Design\nCenter", icon: ICON, event: ModuleEvent::Command("ADCENTER".to_string()) }
|
||||
ToolDef {
|
||||
id: "ADCENTER",
|
||||
label: "Design\nCenter",
|
||||
icon: ICON,
|
||||
event: ModuleEvent::Command("ADCENTER".to_string()),
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,10 @@
|
||||
use crate::modules::{IconKind, ModuleEvent, ToolDef};
|
||||
pub const ICON: IconKind = IconKind::Svg(include_bytes!("../../../assets/icons/edit_block.svg"));
|
||||
pub fn tool() -> ToolDef {
|
||||
ToolDef { id: "BEDIT", label: "Edit Block", icon: ICON, event: ModuleEvent::Command("BEDIT".to_string()) }
|
||||
ToolDef {
|
||||
id: "BEDIT",
|
||||
label: "Edit Block",
|
||||
icon: ICON,
|
||||
event: ModuleEvent::Command("BEDIT".to_string()),
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,9 @@ pub fn tool() -> ToolDef {
|
||||
|
||||
enum Step {
|
||||
Name,
|
||||
Point { name: String },
|
||||
Point {
|
||||
name: String,
|
||||
},
|
||||
/// Attribute filling: prompt the user tag by tag.
|
||||
FillAttr {
|
||||
/// Attribute definitions: (tag, prompt_text, default_value).
|
||||
@@ -70,7 +72,11 @@ impl CadCommand for InsertBlockCommand {
|
||||
} else {
|
||||
format!(" <{default}>")
|
||||
};
|
||||
let prompt_text = if prompt.is_empty() { tag.as_str() } else { prompt.as_str() };
|
||||
let prompt_text = if prompt.is_empty() {
|
||||
tag.as_str()
|
||||
} else {
|
||||
prompt.as_str()
|
||||
};
|
||||
format!("INSERT {prompt_text}{default_hint}:")
|
||||
} else {
|
||||
"INSERT Filling attributes...".into()
|
||||
@@ -132,11 +138,17 @@ impl CadCommand for InsertBlockCommand {
|
||||
}
|
||||
|
||||
fn attreq_set_attdefs(&mut self, attdefs: Vec<(String, String, String)>) {
|
||||
self.step = Step::FillAttr { attdefs, idx: 0, values: vec![] };
|
||||
self.step = Step::FillAttr {
|
||||
attdefs,
|
||||
idx: 0,
|
||||
values: vec![],
|
||||
};
|
||||
}
|
||||
|
||||
fn attreq_take_insert(&mut self) -> Option<acadrust::EntityType> {
|
||||
self.pending_insert.take().map(|ins| EntityType::Insert(ins))
|
||||
self.pending_insert
|
||||
.take()
|
||||
.map(|ins| EntityType::Insert(ins))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,9 +164,18 @@ impl InsertBlockCommand {
|
||||
_ => return CmdResult::Cancel,
|
||||
};
|
||||
|
||||
let value = if text.trim().is_empty() { default } else { text.trim().to_string() };
|
||||
let value = if text.trim().is_empty() {
|
||||
default
|
||||
} else {
|
||||
text.trim().to_string()
|
||||
};
|
||||
|
||||
if let Step::FillAttr { ref mut values, ref mut idx, .. } = self.step {
|
||||
if let Step::FillAttr {
|
||||
ref mut values,
|
||||
ref mut idx,
|
||||
..
|
||||
} = self.step
|
||||
{
|
||||
values.push((tag, value));
|
||||
*idx = next_idx;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
use crate::modules::{IconKind, ModuleEvent, ToolDef};
|
||||
pub const ICON: IconKind = IconKind::Svg(include_bytes!("../../../assets/icons/landxml.svg"));
|
||||
pub fn tool() -> ToolDef {
|
||||
ToolDef { id: "LANDXMLIMPORT", label: "Land\nXML", icon: ICON, event: ModuleEvent::Command("LANDXMLIMPORT".to_string()) }
|
||||
ToolDef {
|
||||
id: "LANDXMLIMPORT",
|
||||
label: "Land\nXML",
|
||||
icon: ICON,
|
||||
event: ModuleEvent::Command("LANDXMLIMPORT".to_string()),
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,8 @@ mod attedit;
|
||||
mod attman;
|
||||
mod attsync;
|
||||
mod base_point;
|
||||
pub(crate) mod create_block;
|
||||
mod content_browser;
|
||||
pub(crate) mod create_block;
|
||||
mod design_center;
|
||||
mod edit_block;
|
||||
pub(crate) mod insert_block;
|
||||
@@ -19,8 +19,8 @@ pub(crate) mod solid3d_cmds;
|
||||
mod underlay_frames;
|
||||
mod underlay_layers;
|
||||
pub(crate) mod wblock;
|
||||
pub(crate) mod xattach;
|
||||
mod xadjust;
|
||||
pub(crate) mod xattach;
|
||||
mod xclip;
|
||||
|
||||
use crate::modules::{CadModule, IconKind, RibbonGroup, RibbonItem};
|
||||
@@ -47,11 +47,31 @@ impl CadModule for InsertModule {
|
||||
RibbonItem::Tool(underlay_layers::tool()),
|
||||
RibbonItem::Dropdown {
|
||||
id: "FRAMES_DROPDOWN",
|
||||
icon: IconKind::Svg(include_bytes!("../../../assets/icons/underlay_frames.svg")),
|
||||
icon: IconKind::Svg(include_bytes!(
|
||||
"../../../assets/icons/underlay_frames.svg"
|
||||
)),
|
||||
items: vec![
|
||||
("FRAMES0", "Frames Off", IconKind::Svg(include_bytes!("../../../assets/icons/underlay_frames.svg"))),
|
||||
("FRAMES1", "Frames On", IconKind::Svg(include_bytes!("../../../assets/icons/underlay_frames.svg"))),
|
||||
("FRAMES2", "Frames & Print",IconKind::Svg(include_bytes!("../../../assets/icons/underlay_frames.svg"))),
|
||||
(
|
||||
"FRAMES0",
|
||||
"Frames Off",
|
||||
IconKind::Svg(include_bytes!(
|
||||
"../../../assets/icons/underlay_frames.svg"
|
||||
)),
|
||||
),
|
||||
(
|
||||
"FRAMES1",
|
||||
"Frames On",
|
||||
IconKind::Svg(include_bytes!(
|
||||
"../../../assets/icons/underlay_frames.svg"
|
||||
)),
|
||||
),
|
||||
(
|
||||
"FRAMES2",
|
||||
"Frames & Print",
|
||||
IconKind::Svg(include_bytes!(
|
||||
"../../../assets/icons/underlay_frames.svg"
|
||||
)),
|
||||
),
|
||||
],
|
||||
default: "FRAMES1",
|
||||
},
|
||||
@@ -61,9 +81,7 @@ impl CadModule for InsertModule {
|
||||
// ── Point Cloud ───────────────────────────────────────────────────
|
||||
RibbonGroup {
|
||||
title: "Point Cloud",
|
||||
tools: vec![
|
||||
RibbonItem::LargeTool(pc_attach::tool()),
|
||||
],
|
||||
tools: vec![RibbonItem::LargeTool(pc_attach::tool())],
|
||||
},
|
||||
// ── Block ─────────────────────────────────────────────────────────
|
||||
RibbonGroup {
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
use crate::modules::{IconKind, ModuleEvent, ToolDef};
|
||||
pub const ICON: IconKind = IconKind::Svg(include_bytes!("../../../assets/icons/mview_block.svg"));
|
||||
pub fn tool() -> ToolDef {
|
||||
ToolDef { id: "BLOCKPALETTE", label: "Multi-View\nBlock", icon: ICON, event: ModuleEvent::Command("BLOCKPALETTE".to_string()) }
|
||||
ToolDef {
|
||||
id: "BLOCKPALETTE",
|
||||
label: "Multi-View\nBlock",
|
||||
icon: ICON,
|
||||
event: ModuleEvent::Command("BLOCKPALETTE".to_string()),
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,10 @@
|
||||
use crate::modules::{IconKind, ModuleEvent, ToolDef};
|
||||
pub const ICON: IconKind = IconKind::Svg(include_bytes!("../../../assets/icons/pc_attach.svg"));
|
||||
pub fn tool() -> ToolDef {
|
||||
ToolDef { id: "POINTCLOUDATTACH", label: "Attach", icon: ICON, event: ModuleEvent::Command("POINTCLOUDATTACH".to_string()) }
|
||||
ToolDef {
|
||||
id: "POINTCLOUDATTACH",
|
||||
label: "Attach",
|
||||
icon: ICON,
|
||||
event: ModuleEvent::Command("POINTCLOUDATTACH".to_string()),
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,11 @@
|
||||
use crate::modules::{IconKind, ModuleEvent, ToolDef};
|
||||
pub const ICON: IconKind = IconKind::Svg(include_bytes!("../../../assets/icons/snap_underlays.svg"));
|
||||
pub const ICON: IconKind =
|
||||
IconKind::Svg(include_bytes!("../../../assets/icons/snap_underlays.svg"));
|
||||
pub fn tool() -> ToolDef {
|
||||
ToolDef { id: "UOSNAP", label: "Snap to\nUnderlays", icon: ICON, event: ModuleEvent::Command("UOSNAP".to_string()) }
|
||||
ToolDef {
|
||||
id: "UOSNAP",
|
||||
label: "Snap to\nUnderlays",
|
||||
icon: ICON,
|
||||
event: ModuleEvent::Command("UOSNAP".to_string()),
|
||||
}
|
||||
}
|
||||
@@ -26,9 +26,15 @@ use crate::scene::truck_tess;
|
||||
|
||||
fn solid_to_mesh(solid: &truck_modeling::Solid, color: [f32; 4], name: &str) -> Option<MeshModel> {
|
||||
match truck_tess::tessellate_solid(solid, [0.0; 3]) {
|
||||
truck_tess::TruckTessResult::Mesh { verts, normals, indices } => Some(MeshModel {
|
||||
truck_tess::TruckTessResult::Mesh {
|
||||
verts,
|
||||
normals,
|
||||
indices,
|
||||
} => Some(MeshModel {
|
||||
name: name.to_string(),
|
||||
verts, normals, indices,
|
||||
verts,
|
||||
normals,
|
||||
indices,
|
||||
color,
|
||||
selected: false,
|
||||
}),
|
||||
@@ -38,9 +44,15 @@ fn solid_to_mesh(solid: &truck_modeling::Solid, color: [f32; 4], name: &str) ->
|
||||
|
||||
fn shell_to_mesh(shell: &truck_modeling::Shell, color: [f32; 4], name: &str) -> Option<MeshModel> {
|
||||
match truck_tess::tessellate_shell(shell, [0.0; 3]) {
|
||||
truck_tess::TruckTessResult::Mesh { verts, normals, indices } => Some(MeshModel {
|
||||
truck_tess::TruckTessResult::Mesh {
|
||||
verts,
|
||||
normals,
|
||||
indices,
|
||||
} => Some(MeshModel {
|
||||
name: name.to_string(),
|
||||
verts, normals, indices,
|
||||
verts,
|
||||
normals,
|
||||
indices,
|
||||
color,
|
||||
selected: false,
|
||||
}),
|
||||
@@ -58,36 +70,67 @@ pub struct BoxCommand {
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
enum BoxStep { Corner1, Corner2, Height }
|
||||
enum BoxStep {
|
||||
Corner1,
|
||||
Corner2,
|
||||
Height,
|
||||
}
|
||||
|
||||
impl BoxCommand {
|
||||
pub fn new(color: [f32; 4]) -> Self {
|
||||
Self { step: BoxStep::Corner1, p1: Vec3::ZERO, p2: Vec3::ZERO, color }
|
||||
Self {
|
||||
step: BoxStep::Corner1,
|
||||
p1: Vec3::ZERO,
|
||||
p2: Vec3::ZERO,
|
||||
color,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CadCommand for BoxCommand {
|
||||
fn name(&self) -> &'static str { "BOX" }
|
||||
fn name(&self) -> &'static str {
|
||||
"BOX"
|
||||
}
|
||||
fn prompt(&self) -> String {
|
||||
match self.step {
|
||||
BoxStep::Corner1 => "BOX First corner:".into(),
|
||||
BoxStep::Corner2 => "BOX Opposite corner (XY):".into(),
|
||||
BoxStep::Height => "BOX Height:".into(),
|
||||
BoxStep::Height => "BOX Height:".into(),
|
||||
}
|
||||
}
|
||||
fn on_point(&mut self, pt: Vec3) -> CmdResult {
|
||||
match self.step {
|
||||
BoxStep::Corner1 => { self.p1 = pt; self.step = BoxStep::Corner2; CmdResult::NeedPoint }
|
||||
BoxStep::Corner2 => { self.p2 = pt; self.step = BoxStep::Height; CmdResult::NeedPoint }
|
||||
BoxStep::Height => commit_box(self.p1, self.p2, (pt.y - self.p1.y).abs().max(1e-4), self.color),
|
||||
BoxStep::Corner1 => {
|
||||
self.p1 = pt;
|
||||
self.step = BoxStep::Corner2;
|
||||
CmdResult::NeedPoint
|
||||
}
|
||||
BoxStep::Corner2 => {
|
||||
self.p2 = pt;
|
||||
self.step = BoxStep::Height;
|
||||
CmdResult::NeedPoint
|
||||
}
|
||||
BoxStep::Height => commit_box(
|
||||
self.p1,
|
||||
self.p2,
|
||||
(pt.y - self.p1.y).abs().max(1e-4),
|
||||
self.color,
|
||||
),
|
||||
}
|
||||
}
|
||||
fn wants_text_input(&self) -> bool { self.step == BoxStep::Height }
|
||||
fn wants_text_input(&self) -> bool {
|
||||
self.step == BoxStep::Height
|
||||
}
|
||||
fn on_text_input(&mut self, text: &str) -> Option<CmdResult> {
|
||||
text.trim().parse::<f32>().ok().filter(|&h| h.abs() > 1e-6)
|
||||
text.trim()
|
||||
.parse::<f32>()
|
||||
.ok()
|
||||
.filter(|&h| h.abs() > 1e-6)
|
||||
.map(|h| commit_box(self.p1, self.p2, h.abs(), self.color))
|
||||
}
|
||||
fn on_enter(&mut self) -> CmdResult { CmdResult::Cancel }
|
||||
fn on_enter(&mut self) -> CmdResult {
|
||||
CmdResult::Cancel
|
||||
}
|
||||
}
|
||||
|
||||
fn commit_box(p1: Vec3, p2: Vec3, height: f32, color: [f32; 4]) -> CmdResult {
|
||||
@@ -97,7 +140,7 @@ fn commit_box(p1: Vec3, p2: Vec3, height: f32, color: [f32; 4]) -> CmdResult {
|
||||
let x1 = p1.x.max(p2.x) as f64;
|
||||
let y1 = p1.z.max(p2.z) as f64;
|
||||
let z0 = p1.y as f64;
|
||||
let h = height as f64;
|
||||
let h = height as f64;
|
||||
|
||||
// Build face at z=z0, then sweep to z0+h.
|
||||
let v00 = builder::vertex(Point3::new(x0, y0, z0));
|
||||
@@ -115,7 +158,9 @@ fn commit_box(p1: Vec3, p2: Vec3, height: f32, color: [f32; 4]) -> CmdResult {
|
||||
};
|
||||
// tsweep on Face → Solid
|
||||
let solid = builder::tsweep(&face, TruckVec3::new(0.0, 0.0, h));
|
||||
CmdResult::CommitSolid3D { mesh_fn: Box::new(move |name| solid_to_mesh(&solid, color, &name)) }
|
||||
CmdResult::CommitSolid3D {
|
||||
mesh_fn: Box::new(move |name| solid_to_mesh(&solid, color, &name)),
|
||||
}
|
||||
}
|
||||
|
||||
// ── SPHERE command ─────────────────────────────────────────────────────────
|
||||
@@ -127,16 +172,25 @@ pub struct SphereCommand {
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
enum SphereStep { Center, Radius }
|
||||
enum SphereStep {
|
||||
Center,
|
||||
Radius,
|
||||
}
|
||||
|
||||
impl SphereCommand {
|
||||
pub fn new(color: [f32; 4]) -> Self {
|
||||
Self { step: SphereStep::Center, center: Vec3::ZERO, color }
|
||||
Self {
|
||||
step: SphereStep::Center,
|
||||
center: Vec3::ZERO,
|
||||
color,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CadCommand for SphereCommand {
|
||||
fn name(&self) -> &'static str { "SPHERE" }
|
||||
fn name(&self) -> &'static str {
|
||||
"SPHERE"
|
||||
}
|
||||
fn prompt(&self) -> String {
|
||||
match self.step {
|
||||
SphereStep::Center => "SPHERE Center:".into(),
|
||||
@@ -145,35 +199,50 @@ impl CadCommand for SphereCommand {
|
||||
}
|
||||
fn on_point(&mut self, pt: Vec3) -> CmdResult {
|
||||
match self.step {
|
||||
SphereStep::Center => { self.center = pt; self.step = SphereStep::Radius; CmdResult::NeedPoint }
|
||||
SphereStep::Radius => commit_sphere(self.center, (pt - self.center).length(), self.color),
|
||||
SphereStep::Center => {
|
||||
self.center = pt;
|
||||
self.step = SphereStep::Radius;
|
||||
CmdResult::NeedPoint
|
||||
}
|
||||
SphereStep::Radius => {
|
||||
commit_sphere(self.center, (pt - self.center).length(), self.color)
|
||||
}
|
||||
}
|
||||
}
|
||||
fn wants_text_input(&self) -> bool { self.step == SphereStep::Radius }
|
||||
fn wants_text_input(&self) -> bool {
|
||||
self.step == SphereStep::Radius
|
||||
}
|
||||
fn on_text_input(&mut self, text: &str) -> Option<CmdResult> {
|
||||
text.trim().parse::<f32>().ok().filter(|&r| r > 1e-6)
|
||||
text.trim()
|
||||
.parse::<f32>()
|
||||
.ok()
|
||||
.filter(|&r| r > 1e-6)
|
||||
.map(|r| commit_sphere(self.center, r, self.color))
|
||||
}
|
||||
fn on_enter(&mut self) -> CmdResult { CmdResult::Cancel }
|
||||
fn on_enter(&mut self) -> CmdResult {
|
||||
CmdResult::Cancel
|
||||
}
|
||||
}
|
||||
|
||||
fn commit_sphere(center: Vec3, radius: f32, color: [f32; 4]) -> CmdResult {
|
||||
let cx = center.x as f64;
|
||||
let cy = center.z as f64;
|
||||
let cz = center.y as f64;
|
||||
let r = radius as f64;
|
||||
let r = radius as f64;
|
||||
|
||||
// Build a half-circle arc wire from north pole to south pole (XZ plane).
|
||||
let north = builder::vertex(Point3::new(cx, cy, cz + r));
|
||||
let south = builder::vertex(Point3::new(cx, cy, cz - r));
|
||||
let east = Point3::new(cx + r, cy, cz);
|
||||
let arc = builder::circle_arc(&north, &south, east);
|
||||
let east = Point3::new(cx + r, cy, cz);
|
||||
let arc = builder::circle_arc(&north, &south, east);
|
||||
// rsweep the wire around the Z axis for a full revolution → Shell.
|
||||
let wire: truck_modeling::Wire = std::iter::once(arc).collect();
|
||||
let axis_pt = Point3::new(cx, cy, cz);
|
||||
let axis = TruckVec3::new(0.0, 0.0, 1.0);
|
||||
let shell = builder::rsweep(&wire, axis_pt, axis, Rad(std::f64::consts::TAU));
|
||||
CmdResult::CommitSolid3D { mesh_fn: Box::new(move |name| shell_to_mesh(&shell, color, &name)) }
|
||||
let axis = TruckVec3::new(0.0, 0.0, 1.0);
|
||||
let shell = builder::rsweep(&wire, axis_pt, axis, Rad(std::f64::consts::TAU));
|
||||
CmdResult::CommitSolid3D {
|
||||
mesh_fn: Box::new(move |name| shell_to_mesh(&shell, color, &name)),
|
||||
}
|
||||
}
|
||||
|
||||
// ── CYLINDER command ───────────────────────────────────────────────────────
|
||||
@@ -186,16 +255,27 @@ pub struct CylinderCommand {
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
enum CylStep { Center, Radius, Height }
|
||||
enum CylStep {
|
||||
Center,
|
||||
Radius,
|
||||
Height,
|
||||
}
|
||||
|
||||
impl CylinderCommand {
|
||||
pub fn new(color: [f32; 4]) -> Self {
|
||||
Self { step: CylStep::Center, center: Vec3::ZERO, radius: 1.0, color }
|
||||
Self {
|
||||
step: CylStep::Center,
|
||||
center: Vec3::ZERO,
|
||||
radius: 1.0,
|
||||
color,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CadCommand for CylinderCommand {
|
||||
fn name(&self) -> &'static str { "CYLINDER" }
|
||||
fn name(&self) -> &'static str {
|
||||
"CYLINDER"
|
||||
}
|
||||
fn prompt(&self) -> String {
|
||||
match self.step {
|
||||
CylStep::Center => "CYLINDER Center of base:".into(),
|
||||
@@ -205,37 +285,54 @@ impl CadCommand for CylinderCommand {
|
||||
}
|
||||
fn on_point(&mut self, pt: Vec3) -> CmdResult {
|
||||
match self.step {
|
||||
CylStep::Center => { self.center = pt; self.step = CylStep::Radius; CmdResult::NeedPoint }
|
||||
CylStep::Center => {
|
||||
self.center = pt;
|
||||
self.step = CylStep::Radius;
|
||||
CmdResult::NeedPoint
|
||||
}
|
||||
CylStep::Radius => {
|
||||
self.radius = (pt - self.center).length().max(1e-4);
|
||||
self.step = CylStep::Height;
|
||||
CmdResult::NeedPoint
|
||||
}
|
||||
CylStep::Height => commit_cylinder(self.center, self.radius, (pt.y - self.center.y).abs().max(1e-4), self.color),
|
||||
CylStep::Height => commit_cylinder(
|
||||
self.center,
|
||||
self.radius,
|
||||
(pt.y - self.center.y).abs().max(1e-4),
|
||||
self.color,
|
||||
),
|
||||
}
|
||||
}
|
||||
fn wants_text_input(&self) -> bool { matches!(self.step, CylStep::Radius | CylStep::Height) }
|
||||
fn wants_text_input(&self) -> bool {
|
||||
matches!(self.step, CylStep::Radius | CylStep::Height)
|
||||
}
|
||||
fn on_text_input(&mut self, text: &str) -> Option<CmdResult> {
|
||||
let v = text.trim().parse::<f32>().ok().filter(|&v| v > 1e-6)?;
|
||||
match self.step {
|
||||
CylStep::Radius => { self.radius = v; self.step = CylStep::Height; Some(CmdResult::NeedPoint) }
|
||||
CylStep::Radius => {
|
||||
self.radius = v;
|
||||
self.step = CylStep::Height;
|
||||
Some(CmdResult::NeedPoint)
|
||||
}
|
||||
CylStep::Height => Some(commit_cylinder(self.center, self.radius, v, self.color)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
fn on_enter(&mut self) -> CmdResult { CmdResult::Cancel }
|
||||
fn on_enter(&mut self) -> CmdResult {
|
||||
CmdResult::Cancel
|
||||
}
|
||||
}
|
||||
|
||||
fn commit_cylinder(center: Vec3, radius: f32, height: f32, color: [f32; 4]) -> CmdResult {
|
||||
let cx = center.x as f64;
|
||||
let cy = center.z as f64;
|
||||
let cz = center.y as f64;
|
||||
let r = radius as f64;
|
||||
let h = height as f64;
|
||||
let r = radius as f64;
|
||||
let h = height as f64;
|
||||
|
||||
// Build circle face at z=cz, sweep upward.
|
||||
let right = builder::vertex(Point3::new(cx + r, cy, cz));
|
||||
let left = builder::vertex(Point3::new(cx - r, cy, cz));
|
||||
let left = builder::vertex(Point3::new(cx - r, cy, cz));
|
||||
let top_t = Point3::new(cx, cy + r, cz);
|
||||
let bot_t = Point3::new(cx, cy - r, cz);
|
||||
let upper = builder::circle_arc(&right, &left, top_t);
|
||||
@@ -246,7 +343,9 @@ fn commit_cylinder(center: Vec3, radius: f32, height: f32, color: [f32; 4]) -> C
|
||||
Err(_) => return CmdResult::Cancel,
|
||||
};
|
||||
let solid = builder::tsweep(&face, TruckVec3::new(0.0, 0.0, h));
|
||||
CmdResult::CommitSolid3D { mesh_fn: Box::new(move |name| solid_to_mesh(&solid, color, &name)) }
|
||||
CmdResult::CommitSolid3D {
|
||||
mesh_fn: Box::new(move |name| solid_to_mesh(&solid, color, &name)),
|
||||
}
|
||||
}
|
||||
|
||||
// ── EXTRUDE command ────────────────────────────────────────────────────────
|
||||
@@ -258,41 +357,69 @@ pub struct ExtrudeCommand {
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
enum ExtrudeStep { Pick, Height }
|
||||
enum ExtrudeStep {
|
||||
Pick,
|
||||
Height,
|
||||
}
|
||||
|
||||
impl ExtrudeCommand {
|
||||
pub fn new(color: [f32; 4]) -> Self {
|
||||
Self { step: ExtrudeStep::Pick, target_handle: acadrust::Handle::NULL, color }
|
||||
Self {
|
||||
step: ExtrudeStep::Pick,
|
||||
target_handle: acadrust::Handle::NULL,
|
||||
color,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CadCommand for ExtrudeCommand {
|
||||
fn name(&self) -> &'static str { "EXTRUDE" }
|
||||
fn name(&self) -> &'static str {
|
||||
"EXTRUDE"
|
||||
}
|
||||
fn prompt(&self) -> String {
|
||||
match self.step {
|
||||
ExtrudeStep::Pick => "EXTRUDE Select closed profile (Circle, LwPolyline…):".into(),
|
||||
ExtrudeStep::Pick => "EXTRUDE Select closed profile (Circle, LwPolyline…):".into(),
|
||||
ExtrudeStep::Height => "EXTRUDE Height:".into(),
|
||||
}
|
||||
}
|
||||
fn needs_entity_pick(&self) -> bool { self.step == ExtrudeStep::Pick }
|
||||
fn needs_entity_pick(&self) -> bool {
|
||||
self.step == ExtrudeStep::Pick
|
||||
}
|
||||
fn on_entity_pick(&mut self, handle: acadrust::Handle, _pt: Vec3) -> CmdResult {
|
||||
if handle.is_null() { return CmdResult::NeedPoint; }
|
||||
if handle.is_null() {
|
||||
return CmdResult::NeedPoint;
|
||||
}
|
||||
self.target_handle = handle;
|
||||
self.step = ExtrudeStep::Height;
|
||||
CmdResult::NeedPoint
|
||||
}
|
||||
fn on_point(&mut self, pt: Vec3) -> CmdResult {
|
||||
if self.step == ExtrudeStep::Height {
|
||||
return CmdResult::ExtrudeEntity { handle: self.target_handle, height: pt.y.abs().max(1e-4), color: self.color };
|
||||
return CmdResult::ExtrudeEntity {
|
||||
handle: self.target_handle,
|
||||
height: pt.y.abs().max(1e-4),
|
||||
color: self.color,
|
||||
};
|
||||
}
|
||||
CmdResult::NeedPoint
|
||||
}
|
||||
fn wants_text_input(&self) -> bool { self.step == ExtrudeStep::Height }
|
||||
fn wants_text_input(&self) -> bool {
|
||||
self.step == ExtrudeStep::Height
|
||||
}
|
||||
fn on_text_input(&mut self, text: &str) -> Option<CmdResult> {
|
||||
text.trim().parse::<f32>().ok().filter(|&h| h.abs() > 1e-6)
|
||||
.map(|h| CmdResult::ExtrudeEntity { handle: self.target_handle, height: h.abs(), color: self.color })
|
||||
text.trim()
|
||||
.parse::<f32>()
|
||||
.ok()
|
||||
.filter(|&h| h.abs() > 1e-6)
|
||||
.map(|h| CmdResult::ExtrudeEntity {
|
||||
handle: self.target_handle,
|
||||
height: h.abs(),
|
||||
color: self.color,
|
||||
})
|
||||
}
|
||||
fn on_enter(&mut self) -> CmdResult {
|
||||
CmdResult::Cancel
|
||||
}
|
||||
fn on_enter(&mut self) -> CmdResult { CmdResult::Cancel }
|
||||
}
|
||||
|
||||
// ── REVOLVE command ────────────────────────────────────────────────────────
|
||||
@@ -306,7 +433,12 @@ pub struct RevolveCommand {
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
enum RevolveStep { Pick, AxisStart, AxisEnd, Angle }
|
||||
enum RevolveStep {
|
||||
Pick,
|
||||
AxisStart,
|
||||
AxisEnd,
|
||||
Angle,
|
||||
}
|
||||
|
||||
impl RevolveCommand {
|
||||
pub fn new(color: [f32; 4]) -> Self {
|
||||
@@ -321,38 +453,64 @@ impl RevolveCommand {
|
||||
}
|
||||
|
||||
impl CadCommand for RevolveCommand {
|
||||
fn name(&self) -> &'static str { "REVOLVE" }
|
||||
fn name(&self) -> &'static str {
|
||||
"REVOLVE"
|
||||
}
|
||||
fn prompt(&self) -> String {
|
||||
match self.step {
|
||||
RevolveStep::Pick => "REVOLVE Select profile:".into(),
|
||||
RevolveStep::Pick => "REVOLVE Select profile:".into(),
|
||||
RevolveStep::AxisStart => "REVOLVE Axis start point:".into(),
|
||||
RevolveStep::AxisEnd => "REVOLVE Axis end point:".into(),
|
||||
RevolveStep::Angle => "REVOLVE Angle of revolution <360>:".into(),
|
||||
RevolveStep::AxisEnd => "REVOLVE Axis end point:".into(),
|
||||
RevolveStep::Angle => "REVOLVE Angle of revolution <360>:".into(),
|
||||
}
|
||||
}
|
||||
fn needs_entity_pick(&self) -> bool { self.step == RevolveStep::Pick }
|
||||
fn needs_entity_pick(&self) -> bool {
|
||||
self.step == RevolveStep::Pick
|
||||
}
|
||||
fn on_entity_pick(&mut self, handle: acadrust::Handle, _pt: Vec3) -> CmdResult {
|
||||
if handle.is_null() { return CmdResult::NeedPoint; }
|
||||
if handle.is_null() {
|
||||
return CmdResult::NeedPoint;
|
||||
}
|
||||
self.target_handle = handle;
|
||||
self.step = RevolveStep::AxisStart;
|
||||
CmdResult::NeedPoint
|
||||
}
|
||||
fn on_point(&mut self, pt: Vec3) -> CmdResult {
|
||||
match self.step {
|
||||
RevolveStep::AxisStart => { self.axis_start = pt; self.step = RevolveStep::AxisEnd; CmdResult::NeedPoint }
|
||||
RevolveStep::AxisEnd => { self.axis_end = pt; self.step = RevolveStep::Angle; CmdResult::NeedPoint }
|
||||
RevolveStep::Angle => self.make_revolve(360.0),
|
||||
RevolveStep::AxisStart => {
|
||||
self.axis_start = pt;
|
||||
self.step = RevolveStep::AxisEnd;
|
||||
CmdResult::NeedPoint
|
||||
}
|
||||
RevolveStep::AxisEnd => {
|
||||
self.axis_end = pt;
|
||||
self.step = RevolveStep::Angle;
|
||||
CmdResult::NeedPoint
|
||||
}
|
||||
RevolveStep::Angle => self.make_revolve(360.0),
|
||||
_ => CmdResult::NeedPoint,
|
||||
}
|
||||
}
|
||||
fn wants_text_input(&self) -> bool { self.step == RevolveStep::Angle }
|
||||
fn wants_text_input(&self) -> bool {
|
||||
self.step == RevolveStep::Angle
|
||||
}
|
||||
fn on_text_input(&mut self, text: &str) -> Option<CmdResult> {
|
||||
let angle = if text.trim().is_empty() { 360.0f32 }
|
||||
else { text.trim().parse::<f32>().ok().filter(|&a| a.abs() > 1e-3)? };
|
||||
let angle = if text.trim().is_empty() {
|
||||
360.0f32
|
||||
} else {
|
||||
text.trim()
|
||||
.parse::<f32>()
|
||||
.ok()
|
||||
.filter(|&a| a.abs() > 1e-3)?
|
||||
};
|
||||
Some(self.make_revolve(angle.abs()))
|
||||
}
|
||||
fn on_enter(&mut self) -> CmdResult {
|
||||
if self.step == RevolveStep::Angle { self.make_revolve(360.0) } else { CmdResult::Cancel }
|
||||
if self.step == RevolveStep::Angle {
|
||||
self.make_revolve(360.0)
|
||||
} else {
|
||||
CmdResult::Cancel
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -377,42 +535,57 @@ pub struct SweepCommand {
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
enum SweepStep { PickProfile, PickPath }
|
||||
enum SweepStep {
|
||||
PickProfile,
|
||||
PickPath,
|
||||
}
|
||||
|
||||
impl SweepCommand {
|
||||
pub fn new(color: [f32; 4]) -> Self {
|
||||
Self { step: SweepStep::PickProfile, profile_handle: acadrust::Handle::NULL, color }
|
||||
Self {
|
||||
step: SweepStep::PickProfile,
|
||||
profile_handle: acadrust::Handle::NULL,
|
||||
color,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CadCommand for SweepCommand {
|
||||
fn name(&self) -> &'static str { "SWEEP" }
|
||||
fn name(&self) -> &'static str {
|
||||
"SWEEP"
|
||||
}
|
||||
fn prompt(&self) -> String {
|
||||
match self.step {
|
||||
SweepStep::PickProfile => "SWEEP Select profile to sweep:".into(),
|
||||
SweepStep::PickPath => "SWEEP Select path (Line, Arc, LwPolyline):".into(),
|
||||
SweepStep::PickPath => "SWEEP Select path (Line, Arc, LwPolyline):".into(),
|
||||
}
|
||||
}
|
||||
fn needs_entity_pick(&self) -> bool { true }
|
||||
fn needs_entity_pick(&self) -> bool {
|
||||
true
|
||||
}
|
||||
fn on_entity_pick(&mut self, handle: acadrust::Handle, _pt: Vec3) -> CmdResult {
|
||||
if handle.is_null() { return CmdResult::NeedPoint; }
|
||||
if handle.is_null() {
|
||||
return CmdResult::NeedPoint;
|
||||
}
|
||||
match self.step {
|
||||
SweepStep::PickProfile => {
|
||||
self.profile_handle = handle;
|
||||
self.step = SweepStep::PickPath;
|
||||
CmdResult::NeedPoint
|
||||
}
|
||||
SweepStep::PickPath => {
|
||||
CmdResult::SweepEntity {
|
||||
profile_handle: self.profile_handle,
|
||||
path_handle: handle,
|
||||
color: self.color,
|
||||
}
|
||||
}
|
||||
SweepStep::PickPath => CmdResult::SweepEntity {
|
||||
profile_handle: self.profile_handle,
|
||||
path_handle: handle,
|
||||
color: self.color,
|
||||
},
|
||||
}
|
||||
}
|
||||
fn on_point(&mut self, _pt: Vec3) -> CmdResult { CmdResult::NeedPoint }
|
||||
fn on_enter(&mut self) -> CmdResult { CmdResult::Cancel }
|
||||
fn on_point(&mut self, _pt: Vec3) -> CmdResult {
|
||||
CmdResult::NeedPoint
|
||||
}
|
||||
fn on_enter(&mut self) -> CmdResult {
|
||||
CmdResult::Cancel
|
||||
}
|
||||
}
|
||||
|
||||
// ── LOFT command ───────────────────────────────────────────────────────────
|
||||
@@ -424,36 +597,57 @@ pub struct LoftCommand {
|
||||
|
||||
impl LoftCommand {
|
||||
pub fn new(color: [f32; 4]) -> Self {
|
||||
Self { profiles: Vec::new(), color }
|
||||
Self {
|
||||
profiles: Vec::new(),
|
||||
color,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CadCommand for LoftCommand {
|
||||
fn name(&self) -> &'static str { "LOFT" }
|
||||
fn name(&self) -> &'static str {
|
||||
"LOFT"
|
||||
}
|
||||
fn prompt(&self) -> String {
|
||||
if self.profiles.is_empty() {
|
||||
"LOFT Select first cross-section:".into()
|
||||
} else {
|
||||
format!("LOFT Select next cross-section ({} selected, Enter to finish):", self.profiles.len())
|
||||
format!(
|
||||
"LOFT Select next cross-section ({} selected, Enter to finish):",
|
||||
self.profiles.len()
|
||||
)
|
||||
}
|
||||
}
|
||||
fn needs_entity_pick(&self) -> bool { true }
|
||||
fn needs_entity_pick(&self) -> bool {
|
||||
true
|
||||
}
|
||||
fn on_entity_pick(&mut self, handle: acadrust::Handle, _pt: Vec3) -> CmdResult {
|
||||
if handle.is_null() { return CmdResult::NeedPoint; }
|
||||
if handle.is_null() {
|
||||
return CmdResult::NeedPoint;
|
||||
}
|
||||
// Avoid duplicate picks.
|
||||
if !self.profiles.contains(&handle) {
|
||||
self.profiles.push(handle);
|
||||
}
|
||||
CmdResult::NeedPoint
|
||||
}
|
||||
fn on_point(&mut self, _pt: Vec3) -> CmdResult { CmdResult::NeedPoint }
|
||||
fn wants_text_input(&self) -> bool { self.profiles.len() >= 2 }
|
||||
fn on_text_input(&mut self, _text: &str) -> Option<CmdResult> { None }
|
||||
fn on_point(&mut self, _pt: Vec3) -> CmdResult {
|
||||
CmdResult::NeedPoint
|
||||
}
|
||||
fn wants_text_input(&self) -> bool {
|
||||
self.profiles.len() >= 2
|
||||
}
|
||||
fn on_text_input(&mut self, _text: &str) -> Option<CmdResult> {
|
||||
None
|
||||
}
|
||||
fn on_enter(&mut self) -> CmdResult {
|
||||
if self.profiles.len() < 2 {
|
||||
CmdResult::Cancel
|
||||
} else {
|
||||
CmdResult::LoftEntities { handles: self.profiles.clone(), color: self.color }
|
||||
CmdResult::LoftEntities {
|
||||
handles: self.profiles.clone(),
|
||||
color: self.color,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
use crate::modules::{IconKind, ModuleEvent, ToolDef};
|
||||
#[allow(dead_code)]
|
||||
pub const ICON: IconKind = IconKind::Svg(include_bytes!("../../../assets/icons/underlay_frames.svg"));
|
||||
pub const ICON: IconKind =
|
||||
IconKind::Svg(include_bytes!("../../../assets/icons/underlay_frames.svg"));
|
||||
#[allow(dead_code)]
|
||||
pub fn tool() -> ToolDef {
|
||||
ToolDef { id: "FRAMES", label: "Frames", icon: ICON, event: ModuleEvent::Command("FRAMES".to_string()) }
|
||||
ToolDef {
|
||||
id: "FRAMES",
|
||||
label: "Frames",
|
||||
icon: ICON,
|
||||
event: ModuleEvent::Command("FRAMES".to_string()),
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,11 @@
|
||||
use crate::modules::{IconKind, ModuleEvent, ToolDef};
|
||||
pub const ICON: IconKind = IconKind::Svg(include_bytes!("../../../assets/icons/underlay_layers.svg"));
|
||||
pub const ICON: IconKind =
|
||||
IconKind::Svg(include_bytes!("../../../assets/icons/underlay_layers.svg"));
|
||||
pub fn tool() -> ToolDef {
|
||||
ToolDef { id: "UNDERLAYLAYERS", label: "Underlay\nLayers", icon: ICON, event: ModuleEvent::Command("UNDERLAYLAYERS".to_string()) }
|
||||
ToolDef {
|
||||
id: "UNDERLAYLAYERS",
|
||||
label: "Underlay\nLayers",
|
||||
icon: ICON,
|
||||
event: ModuleEvent::Command("UNDERLAYLAYERS".to_string()),
|
||||
}
|
||||
}
|
||||
@@ -22,10 +22,7 @@ pub fn tool() -> ToolDef {
|
||||
/// extracted into model space.
|
||||
///
|
||||
/// Returns `Err` if the block is not found or has no entities.
|
||||
pub fn extract_block_to_doc(
|
||||
src: &CadDocument,
|
||||
block_name: &str,
|
||||
) -> Result<CadDocument, String> {
|
||||
pub fn extract_block_to_doc(src: &CadDocument, block_name: &str) -> Result<CadDocument, String> {
|
||||
let br = src
|
||||
.block_records
|
||||
.get(block_name)
|
||||
@@ -62,7 +59,9 @@ pub fn extract_block_to_doc(
|
||||
}
|
||||
|
||||
if out.entities().count() == 0 {
|
||||
return Err(format!("Block \"{block_name}\" produced no exportable entities."));
|
||||
return Err(format!(
|
||||
"Block \"{block_name}\" produced no exportable entities."
|
||||
));
|
||||
}
|
||||
|
||||
Ok(out)
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
use crate::modules::{IconKind, ModuleEvent, ToolDef};
|
||||
pub const ICON: IconKind = IconKind::Svg(include_bytes!("../../../assets/icons/xadjust.svg"));
|
||||
pub fn tool() -> ToolDef {
|
||||
ToolDef { id: "ADJUST", label: "Adjust", icon: ICON, event: ModuleEvent::Command("ADJUST".to_string()) }
|
||||
ToolDef {
|
||||
id: "ADJUST",
|
||||
label: "Adjust",
|
||||
icon: ICON,
|
||||
event: ModuleEvent::Command("ADJUST".to_string()),
|
||||
}
|
||||
}
|
||||
@@ -98,7 +98,10 @@ impl CadCommand for XAttachCommand {
|
||||
fn on_point(&mut self, pt: Vec3) -> CmdResult {
|
||||
match &self.step {
|
||||
Step::FilePath => CmdResult::NeedPoint,
|
||||
Step::InsertionPoint { path: _, block_name } => {
|
||||
Step::InsertionPoint {
|
||||
path: _,
|
||||
block_name,
|
||||
} => {
|
||||
// We return the INSERT entity; the command handler in
|
||||
// commands.rs will call `prepare_xref_block` on the scene
|
||||
// before committing.
|
||||
@@ -163,7 +166,9 @@ pub fn prepare_xref_block(scene: &mut Scene, path: &str) -> String {
|
||||
// Create BLOCK entity.
|
||||
let b = Block::new(&block_name, Vector3::zero()).with_xref_path(path);
|
||||
let _ = scene.document.add_entity(EntityType::Block(b));
|
||||
let _ = scene.document.add_entity(EntityType::BlockEnd(BlockEnd::new()));
|
||||
let _ = scene
|
||||
.document
|
||||
.add_entity(EntityType::BlockEnd(BlockEnd::new()));
|
||||
|
||||
// Resolve the XREF content immediately.
|
||||
let path_buf = std::path::PathBuf::from(path);
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
use crate::modules::{IconKind, ModuleEvent, ToolDef};
|
||||
pub const ICON: IconKind = IconKind::Svg(include_bytes!("../../../assets/icons/xclip.svg"));
|
||||
pub fn tool() -> ToolDef {
|
||||
ToolDef { id: "XCLIP", label: "Clip", icon: ICON, event: ModuleEvent::Command("XCLIP".to_string()) }
|
||||
ToolDef {
|
||||
id: "XCLIP",
|
||||
label: "Clip",
|
||||
icon: ICON,
|
||||
event: ModuleEvent::Command("XCLIP".to_string()),
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user