refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
|
|
|
|
impl OpenCADStudio {
|
|
|
|
|
|
pub(super) fn dispatch_display(&mut self, cmd: &str, i: usize) -> Option<Task<Message>> {
|
|
|
|
|
|
match cmd {
|
|
|
|
|
|
// Interactive pan: left-drag pans the view until Esc. The only pan
|
|
|
|
|
|
// path when there is no middle mouse button (trackpad / web).
|
feat(alias): centralize command aliases in user-editable ocad.pgp (#286/#288)
Command aliases (L→LINE, CC→COPYCLIP) were scattered across dispatch match
arms and CommandRegistration name arrays. Move them all into a single
user-editable file:
- assets/ocad.pgp: shipped default alias table (ALIAS,*COMMAND format),
embedded and copied to config/ocad.pgp on first launch; only that user
copy is read after. AA→AREA default closes #286.
- resolve_alias() rewrites the leading command token at the top of
dispatch_command_inner, so aliases resolve for every path (families,
plugins, Repeat menu). Args after the first space are untouched.
- Autocomplete hides alias keys and, when the whole input is an alias,
surfaces its target command as the top (highlighted) suggestion so the
highlight matches what Enter runs (AA→AREA, L→LINE).
- ALIASEDIT opens a new editor modal (add/edit/delete rows) with an Apply
button; Apply persists to ocad.pgp, closing discards unapplied edits.
- Strip alias tokens from all 8 dispatch match-arm files and 71 module
CommandRegistration arrays, keeping canonical/synonym/distinct-command
names. ocad.pgp is now the sole source: deleting or remapping an alias
there takes effect. CUI stays on the keyboard-shortcut panel.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 12:39:22 +03:00
|
|
|
|
"PAN" => {
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
self.tabs[i].pan_mode = true;
|
2026-08-06 00:21:44 +03:00
|
|
|
|
self.clear_navigation_hover(i);
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_output(crate::t!("PAN: drag with the left mouse button. Press Esc to exit.").as_ref());
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ── TABLE cell editing ─────────────────────────────────────────────
|
|
|
|
|
|
// TABLE CELL <row> <col> <text> — set text for a cell in the selected Table
|
|
|
|
|
|
cmd if cmd.starts_with("TABLE ") => {
|
|
|
|
|
|
let rest = cmd.trim_start_matches("TABLE").trim();
|
|
|
|
|
|
let sub_up = rest.split_whitespace().next().unwrap_or("").to_uppercase();
|
|
|
|
|
|
if sub_up == "CELL" {
|
|
|
|
|
|
let parts: Vec<&str> = rest.splitn(4, char::is_whitespace).collect();
|
|
|
|
|
|
// parts: ["CELL", "<row>", "<col>", "<text>"]
|
|
|
|
|
|
let row_res = parts.get(1).and_then(|s| s.parse::<usize>().ok());
|
|
|
|
|
|
let col_res = parts.get(2).and_then(|s| s.parse::<usize>().ok());
|
|
|
|
|
|
let text = parts.get(3).copied().unwrap_or("");
|
|
|
|
|
|
match (row_res, col_res) {
|
|
|
|
|
|
(Some(row), Some(col)) => {
|
|
|
|
|
|
let selected_handles: Vec<acadrust::Handle> = self.tabs[i]
|
|
|
|
|
|
.scene
|
|
|
|
|
|
.selected_entities()
|
|
|
|
|
|
.iter()
|
|
|
|
|
|
.map(|(h, _)| *h)
|
|
|
|
|
|
.collect();
|
|
|
|
|
|
let mut found = false;
|
|
|
|
|
|
for sh in &selected_handles {
|
|
|
|
|
|
if let Some(acadrust::EntityType::Table(tbl)) = self.tabs[i]
|
|
|
|
|
|
.scene
|
|
|
|
|
|
.document
|
|
|
|
|
|
.entities_mut()
|
|
|
|
|
|
.find(|e| e.common().handle == *sh)
|
|
|
|
|
|
{
|
|
|
|
|
|
if tbl.set_cell_text(row, col, text) {
|
|
|
|
|
|
found = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if found {
|
|
|
|
|
|
self.push_undo_snapshot(i, "TABLE CELL");
|
|
|
|
|
|
self.tabs[i].dirty = true;
|
2026-08-02 22:09:25 +03:00
|
|
|
|
self.command_line.push_output(crate::tf!(
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
"TABLE CELL: set [{row},{col}] = \"{text}\"."
|
2026-08-02 22:09:25 +03:00
|
|
|
|
).as_ref());
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
} else {
|
|
|
|
|
|
self.command_line.push_error(
|
2026-08-02 22:09:25 +03:00
|
|
|
|
crate::t!("TABLE CELL: select a Table entity first, or row/col out of range.").as_ref()
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
_ => {
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_info(crate::t!("Usage: TABLE CELL <row> <col> <text>").as_ref());
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
self.command_line.push_info(
|
|
|
|
|
|
"Usage: TABLE (creates new table) or TABLE CELL <row> <col> <text>",
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ── UCSICON — toggle UCS icon visibility on all viewports ────────────
|
|
|
|
|
|
// UCSICON ON — show UCS icon in all viewports
|
|
|
|
|
|
// UCSICON OFF — hide UCS icon in all viewports
|
|
|
|
|
|
// UCSICON NOORIGIN — show icon but not at origin (show at corner)
|
|
|
|
|
|
// UCSICON ORIGIN — show icon at UCS origin
|
2026-07-16 13:29:04 +03:00
|
|
|
|
"UCSICON" => {
|
|
|
|
|
|
use crate::command::KeywordCommand;
|
|
|
|
|
|
let c = KeywordCommand::new(
|
|
|
|
|
|
"UCSICON",
|
|
|
|
|
|
"UCSICON [On / Off / NoOrigin / Origin]:",
|
|
|
|
|
|
vec![
|
|
|
|
|
|
("On", "ON", None),
|
|
|
|
|
|
("Off", "OFF", None),
|
|
|
|
|
|
("NoOrigin", "NOORIGIN", None),
|
|
|
|
|
|
("Origin", "ORIGIN", None),
|
|
|
|
|
|
],
|
|
|
|
|
|
);
|
|
|
|
|
|
self.command_line.push_info(&c.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(c));
|
|
|
|
|
|
}
|
|
|
|
|
|
cmd if cmd.starts_with("UCSICON ") => {
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
let sub = cmd.split_whitespace().nth(1).unwrap_or("").to_uppercase();
|
|
|
|
|
|
match sub.as_str() {
|
|
|
|
|
|
"ON" | "OFF" | "NOORIGIN" | "ORIGIN" => {
|
|
|
|
|
|
self.push_undo_snapshot(i, "UCSICON");
|
|
|
|
|
|
let visible = sub != "OFF";
|
|
|
|
|
|
let at_origin = sub == "ORIGIN";
|
2026-06-27 01:12:45 +03:00
|
|
|
|
// Update model-space icon flags.
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
self.show_ucs_icon = visible;
|
2026-07-12 00:56:15 +01:00
|
|
|
|
self.ribbon.set_ucs_icon(visible);
|
2026-06-27 01:12:45 +03:00
|
|
|
|
if sub == "NOORIGIN" || sub == "ORIGIN" {
|
|
|
|
|
|
self.ucs_icon_at_origin = at_origin;
|
|
|
|
|
|
}
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
let mut count = 0usize;
|
|
|
|
|
|
for entity in self.tabs[i].scene.document.entities_mut() {
|
|
|
|
|
|
if let acadrust::EntityType::Viewport(vp) = entity {
|
|
|
|
|
|
vp.status.ucs_icon_visible = visible;
|
|
|
|
|
|
if sub == "NOORIGIN" || sub == "ORIGIN" {
|
|
|
|
|
|
vp.status.ucs_icon_at_origin = at_origin;
|
|
|
|
|
|
}
|
|
|
|
|
|
count += 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
self.tabs[i].dirty = true;
|
2026-08-02 22:09:25 +03:00
|
|
|
|
self.command_line.push_output(crate::tf!(
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
"UCSICON {sub}: updated {count} viewport(s) + model space."
|
2026-08-02 22:09:25 +03:00
|
|
|
|
).as_ref());
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
}
|
|
|
|
|
|
"" => {
|
|
|
|
|
|
// Bare UCSICON toggles visibility.
|
|
|
|
|
|
self.push_undo_snapshot(i, "UCSICON");
|
|
|
|
|
|
let visible = !self.show_ucs_icon;
|
|
|
|
|
|
self.show_ucs_icon = visible;
|
2026-07-12 00:56:15 +01:00
|
|
|
|
self.ribbon.set_ucs_icon(visible);
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
for entity in self.tabs[i].scene.document.entities_mut() {
|
|
|
|
|
|
if let acadrust::EntityType::Viewport(vp) = entity {
|
|
|
|
|
|
vp.status.ucs_icon_visible = visible;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
self.tabs[i].dirty = true;
|
|
|
|
|
|
let state = if visible { "ON" } else { "OFF" };
|
2026-08-02 22:09:25 +03:00
|
|
|
|
self.command_line.push_output(crate::tf!("UCSICON {state}").as_ref());
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
}
|
|
|
|
|
|
_ => {
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_info(crate::t!("Usage: UCSICON ON | OFF | NOORIGIN | ORIGIN").as_ref());
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ── NAVVCUBE — toggle ViewCube visibility ────────────────────────────
|
|
|
|
|
|
"NAVVCUBE" => {
|
|
|
|
|
|
return Some(Task::done(Message::ToggleViewCube));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-27 16:15:28 +03:00
|
|
|
|
// ── LIMITS — drawing/grid boundary for the active space ─────────────
|
|
|
|
|
|
"LIMITS" => {
|
|
|
|
|
|
use crate::modules::view::limits::LimitsCommand;
|
|
|
|
|
|
let (min, max) = self.tabs[i]
|
|
|
|
|
|
.scene
|
|
|
|
|
|
.current_drawing_limits()
|
|
|
|
|
|
.unwrap_or((glam::DVec2::ZERO, glam::DVec2::new(12.0, 9.0)));
|
|
|
|
|
|
let command = LimitsCommand::new(min, max);
|
|
|
|
|
|
self.command_line.push_info(&command.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(command));
|
|
|
|
|
|
}
|
|
|
|
|
|
"LIMITS ON" | "LIMITS OFF" => {
|
|
|
|
|
|
let enabled = cmd.ends_with("ON");
|
|
|
|
|
|
if self.tabs[i].scene.drawing_limit_check_enabled() != enabled {
|
|
|
|
|
|
self.push_undo_snapshot(i, "LIMITS");
|
|
|
|
|
|
self.tabs[i].scene.set_drawing_limit_check(enabled);
|
|
|
|
|
|
self.tabs[i].dirty = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
self.command_line.push_output(if enabled {
|
|
|
|
|
|
"Limits checking ON."
|
|
|
|
|
|
} else {
|
|
|
|
|
|
"Limits checking OFF."
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
cmd if cmd.starts_with("LIMITS SET ") => {
|
|
|
|
|
|
let tokens: Vec<&str> = cmd["LIMITS SET ".len()..].split_whitespace().collect();
|
|
|
|
|
|
let values: Result<Vec<f64>, _> =
|
|
|
|
|
|
tokens.iter().map(|value| value.parse()).collect();
|
|
|
|
|
|
let Ok(values) = values else {
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_error(crate::t!("LIMITS: four numeric coordinates required.").as_ref());
|
2026-07-27 16:15:28 +03:00
|
|
|
|
return Some(Task::none());
|
|
|
|
|
|
};
|
|
|
|
|
|
if tokens.len() != 4 || !values.iter().all(|value| value.is_finite()) {
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_error(crate::t!("LIMITS: four finite numeric coordinates required.").as_ref());
|
2026-07-27 16:15:28 +03:00
|
|
|
|
} else {
|
|
|
|
|
|
let first = glam::DVec2::new(values[0], values[1]);
|
|
|
|
|
|
let opposite = glam::DVec2::new(values[2], values[3]);
|
|
|
|
|
|
let min = first.min(opposite);
|
|
|
|
|
|
let max = first.max(opposite);
|
|
|
|
|
|
if min.x == max.x || min.y == max.y {
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_error(crate::t!("LIMITS: corners must define a non-zero area.").as_ref());
|
2026-07-27 16:15:28 +03:00
|
|
|
|
} else {
|
|
|
|
|
|
self.push_undo_snapshot(i, "LIMITS");
|
|
|
|
|
|
self.tabs[i].scene.set_current_drawing_limits(min, max);
|
|
|
|
|
|
self.tabs[i].dirty = true;
|
2026-08-02 22:09:25 +03:00
|
|
|
|
self.command_line.push_output(crate::tf!(
|
2026-07-27 16:15:28 +03:00
|
|
|
|
"Drawing limits: {:.4},{:.4} to {:.4},{:.4}.",
|
|
|
|
|
|
min.x, min.y, max.x, max.y
|
2026-08-02 22:09:25 +03:00
|
|
|
|
).as_ref());
|
2026-07-27 16:15:28 +03:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
// ── PROPERTIES — toggle Properties panel visibility ──────────────────
|
feat(alias): centralize command aliases in user-editable ocad.pgp (#286/#288)
Command aliases (L→LINE, CC→COPYCLIP) were scattered across dispatch match
arms and CommandRegistration name arrays. Move them all into a single
user-editable file:
- assets/ocad.pgp: shipped default alias table (ALIAS,*COMMAND format),
embedded and copied to config/ocad.pgp on first launch; only that user
copy is read after. AA→AREA default closes #286.
- resolve_alias() rewrites the leading command token at the top of
dispatch_command_inner, so aliases resolve for every path (families,
plugins, Repeat menu). Args after the first space are untouched.
- Autocomplete hides alias keys and, when the whole input is an alias,
surfaces its target command as the top (highlighted) suggestion so the
highlight matches what Enter runs (AA→AREA, L→LINE).
- ALIASEDIT opens a new editor modal (add/edit/delete rows) with an Apply
button; Apply persists to ocad.pgp, closing discards unapplied edits.
- Strip alias tokens from all 8 dispatch match-arm files and 71 module
CommandRegistration arrays, keeping canonical/synonym/distinct-command
names. ocad.pgp is now the sole source: deleting or remapping an alias
there takes effect. CUI stays on the keyboard-shortcut panel.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 12:39:22 +03:00
|
|
|
|
"PROPERTIES" | "PROPS" => {
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
return Some(Task::done(Message::ToggleProperties));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ── FILETAB — toggle file/document tabs ──────────────────────────────
|
|
|
|
|
|
"FILETAB" => {
|
|
|
|
|
|
return Some(Task::done(Message::ToggleFileTabs));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ── LAYOUTTAB — toggle layout/paper-space tabs ───────────────────────
|
|
|
|
|
|
"LAYOUTTAB" => {
|
|
|
|
|
|
return Some(Task::done(Message::ToggleLayoutTabs));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
feat(commands): integrate standard CAD commands and fix behavior gaps
Broad pass over the command set: integrate missing commands, wire
previously-unreachable ribbon/menu buttons, and correct name/behaviour
mismatches. Every UI command token now resolves (real handler where the
infrastructure exists, an honest "not yet implemented" status otherwise).
New commands:
- Draw: 3DPOLY, 2D solid (SO/SOLID2D), HELIX, TRACE, SKETCH, DIMCENTER
(DCE/CENTERMARK)
- Modify/inquiry: REVERSE, MEASUREGEOM (MEA), DBLIST
- Data/manage: BASE, OVERKILL (exact-duplicate removal), AUDIT (read-only
integrity report), SETVAR (system-variable get/set), SCRIPT (.scr runner),
FINDNONPURGEABLE
Behaviour fixes:
- LINE and SPLINE gain Close/Undo; AREA computes area/perimeter in f64
(correct at large coordinates)
- ATTEDIT/ATE unified so the alias and the ribbon button open the
interactive editor; -ATTEDIT is the command-line form
- Linear/aligned/radius/diameter dimensions gain a text override (Text) and
text angle (Angle) option
- INSERT gains Scale/Rotate options; COPY gains an Array option
- LINETYPE SET sets the current entity linetype; SETBYLAYER resets overrides
- UNDO accepts a step count
Integrations: standard dimension/block aliases (DLI/DRA/DAN, BMAKE,
EXPORTPDF, DDIM), drafting-aid and screen toggles (GRID/SNAP/POLAR/OSNAP/
DSETTINGS/CLEANSCREEN/QUICKPROPERTIES), viewport tiling
(CASCADE/HORIZONTAL/VERTICAL/VPJOIN), and customization entry points
(CUI/ALIASEDIT).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 19:27:35 +03:00
|
|
|
|
// ── Drafting aids — same toggles the status-bar pills drive, also
|
|
|
|
|
|
// reachable by name from the command line. ─────────────────────────
|
|
|
|
|
|
// GRID — show / hide the reference grid.
|
|
|
|
|
|
"GRID" => {
|
|
|
|
|
|
return Some(Task::done(Message::ToggleGrid));
|
|
|
|
|
|
}
|
|
|
|
|
|
// SNAP — toggle cursor snapping to the grid.
|
|
|
|
|
|
"SNAP" => {
|
|
|
|
|
|
return Some(Task::done(Message::ToggleGridSnap));
|
|
|
|
|
|
}
|
|
|
|
|
|
// POLAR — toggle polar tracking.
|
|
|
|
|
|
"POLAR" => {
|
|
|
|
|
|
return Some(Task::done(Message::TogglePolar));
|
|
|
|
|
|
}
|
feat(ui): replace app-menu logo with a quick-access file toolbar
Remove the OCS-logo button and the dropdown menu it opened; the top strip
now shows direct New / Open / Save / Save As / Print buttons ahead of the
undo/redo controls. Drop the HELP and OPTIONS commands (HELP dumped text to
the command line, OPTIONS aliased the drafting-settings popup).
The app-menu overlay is gone, so its rendering code, MENU_ITEMS, four dead
messages, and the LOGO_RED / GEAR / HELP assets are removed. The recents
list it held is the only survivor — moved into OpenCADStudio.recent_files
via a new app/recent.rs, backing the Start page's Recent Documents panel.
Quick-access icons are redrawn in a cleaner style (file-plus, folder-open,
device-floppy, file-export, printer) and tinted light so they read on the
dark strip; FOLDER / DOC keep their old glyphs for the file browser.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 09:51:49 +03:00
|
|
|
|
// DSETTINGS / OSNAP — open the drafting-settings popup, which is OCS's
|
|
|
|
|
|
// settings surface (the persisted DYN/ORTHO/POLAR/OSNAP prefs).
|
|
|
|
|
|
"DSETTINGS" | "OSNAP" => {
|
feat(commands): integrate standard CAD commands and fix behavior gaps
Broad pass over the command set: integrate missing commands, wire
previously-unreachable ribbon/menu buttons, and correct name/behaviour
mismatches. Every UI command token now resolves (real handler where the
infrastructure exists, an honest "not yet implemented" status otherwise).
New commands:
- Draw: 3DPOLY, 2D solid (SO/SOLID2D), HELIX, TRACE, SKETCH, DIMCENTER
(DCE/CENTERMARK)
- Modify/inquiry: REVERSE, MEASUREGEOM (MEA), DBLIST
- Data/manage: BASE, OVERKILL (exact-duplicate removal), AUDIT (read-only
integrity report), SETVAR (system-variable get/set), SCRIPT (.scr runner),
FINDNONPURGEABLE
Behaviour fixes:
- LINE and SPLINE gain Close/Undo; AREA computes area/perimeter in f64
(correct at large coordinates)
- ATTEDIT/ATE unified so the alias and the ribbon button open the
interactive editor; -ATTEDIT is the command-line form
- Linear/aligned/radius/diameter dimensions gain a text override (Text) and
text angle (Angle) option
- INSERT gains Scale/Rotate options; COPY gains an Array option
- LINETYPE SET sets the current entity linetype; SETBYLAYER resets overrides
- UNDO accepts a step count
Integrations: standard dimension/block aliases (DLI/DRA/DAN, BMAKE,
EXPORTPDF, DDIM), drafting-aid and screen toggles (GRID/SNAP/POLAR/OSNAP/
DSETTINGS/CLEANSCREEN/QUICKPROPERTIES), viewport tiling
(CASCADE/HORIZONTAL/VERTICAL/VPJOIN), and customization entry points
(CUI/ALIASEDIT).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 19:27:35 +03:00
|
|
|
|
return Some(Task::done(Message::ToggleSnapPopup));
|
|
|
|
|
|
}
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
// UNITS — open the drawing-units picker (linear / angular format).
|
feat(alias): centralize command aliases in user-editable ocad.pgp (#286/#288)
Command aliases (L→LINE, CC→COPYCLIP) were scattered across dispatch match
arms and CommandRegistration name arrays. Move them all into a single
user-editable file:
- assets/ocad.pgp: shipped default alias table (ALIAS,*COMMAND format),
embedded and copied to config/ocad.pgp on first launch; only that user
copy is read after. AA→AREA default closes #286.
- resolve_alias() rewrites the leading command token at the top of
dispatch_command_inner, so aliases resolve for every path (families,
plugins, Repeat menu). Args after the first space are untouched.
- Autocomplete hides alias keys and, when the whole input is an alias,
surfaces its target command as the top (highlighted) suggestion so the
highlight matches what Enter runs (AA→AREA, L→LINE).
- ALIASEDIT opens a new editor modal (add/edit/delete rows) with an Apply
button; Apply persists to ocad.pgp, closing discards unapplied edits.
- Strip alias tokens from all 8 dispatch match-arm files and 71 module
CommandRegistration arrays, keeping canonical/synonym/distinct-command
names. ocad.pgp is now the sole source: deleting or remapping an alias
there takes effect. CUI stays on the keyboard-shortcut panel.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 12:39:22 +03:00
|
|
|
|
"UNITS" | "DDUNITS" => {
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
return Some(Task::done(Message::ToggleUnitsPopup));
|
|
|
|
|
|
}
|
feat(commands): integrate standard CAD commands and fix behavior gaps
Broad pass over the command set: integrate missing commands, wire
previously-unreachable ribbon/menu buttons, and correct name/behaviour
mismatches. Every UI command token now resolves (real handler where the
infrastructure exists, an honest "not yet implemented" status otherwise).
New commands:
- Draw: 3DPOLY, 2D solid (SO/SOLID2D), HELIX, TRACE, SKETCH, DIMCENTER
(DCE/CENTERMARK)
- Modify/inquiry: REVERSE, MEASUREGEOM (MEA), DBLIST
- Data/manage: BASE, OVERKILL (exact-duplicate removal), AUDIT (read-only
integrity report), SETVAR (system-variable get/set), SCRIPT (.scr runner),
FINDNONPURGEABLE
Behaviour fixes:
- LINE and SPLINE gain Close/Undo; AREA computes area/perimeter in f64
(correct at large coordinates)
- ATTEDIT/ATE unified so the alias and the ribbon button open the
interactive editor; -ATTEDIT is the command-line form
- Linear/aligned/radius/diameter dimensions gain a text override (Text) and
text angle (Angle) option
- INSERT gains Scale/Rotate options; COPY gains an Array option
- LINETYPE SET sets the current entity linetype; SETBYLAYER resets overrides
- UNDO accepts a step count
Integrations: standard dimension/block aliases (DLI/DRA/DAN, BMAKE,
EXPORTPDF, DDIM), drafting-aid and screen toggles (GRID/SNAP/POLAR/OSNAP/
DSETTINGS/CLEANSCREEN/QUICKPROPERTIES), viewport tiling
(CASCADE/HORIZONTAL/VERTICAL/VPJOIN), and customization entry points
(CUI/ALIASEDIT).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 19:27:35 +03:00
|
|
|
|
|
|
|
|
|
|
// ── CLEANSCREEN — collapse the surrounding panels for a full canvas ──
|
|
|
|
|
|
"CLEANSCREEN" => {
|
|
|
|
|
|
return Some(Task::done(Message::ToggleCleanScreen));
|
|
|
|
|
|
}
|
|
|
|
|
|
// ── QUICKPROPERTIES — toggle the floating quick-properties readout ───
|
|
|
|
|
|
"QUICKPROPERTIES" => {
|
|
|
|
|
|
return Some(Task::done(Message::ToggleQuickProperties));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
// ── TOOLPALETTES — not yet implemented ───────────────────────────────
|
feat(alias): centralize command aliases in user-editable ocad.pgp (#286/#288)
Command aliases (L→LINE, CC→COPYCLIP) were scattered across dispatch match
arms and CommandRegistration name arrays. Move them all into a single
user-editable file:
- assets/ocad.pgp: shipped default alias table (ALIAS,*COMMAND format),
embedded and copied to config/ocad.pgp on first launch; only that user
copy is read after. AA→AREA default closes #286.
- resolve_alias() rewrites the leading command token at the top of
dispatch_command_inner, so aliases resolve for every path (families,
plugins, Repeat menu). Args after the first space are untouched.
- Autocomplete hides alias keys and, when the whole input is an alias,
surfaces its target command as the top (highlighted) suggestion so the
highlight matches what Enter runs (AA→AREA, L→LINE).
- ALIASEDIT opens a new editor modal (add/edit/delete rows) with an Apply
button; Apply persists to ocad.pgp, closing discards unapplied edits.
- Strip alias tokens from all 8 dispatch match-arm files and 71 module
CommandRegistration arrays, keeping canonical/synonym/distinct-command
names. ocad.pgp is now the sole source: deleting or remapping an alias
there takes effect. CUI stays on the keyboard-shortcut panel.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 12:39:22 +03:00
|
|
|
|
"TOOLPALETTES" => {
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_info(crate::t!("TOOLPALETTES: Tool Palettes not yet implemented.").as_ref());
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ── SHEETSET — not yet implemented ───────────────────────────────────
|
feat(alias): centralize command aliases in user-editable ocad.pgp (#286/#288)
Command aliases (L→LINE, CC→COPYCLIP) were scattered across dispatch match
arms and CommandRegistration name arrays. Move them all into a single
user-editable file:
- assets/ocad.pgp: shipped default alias table (ALIAS,*COMMAND format),
embedded and copied to config/ocad.pgp on first launch; only that user
copy is read after. AA→AREA default closes #286.
- resolve_alias() rewrites the leading command token at the top of
dispatch_command_inner, so aliases resolve for every path (families,
plugins, Repeat menu). Args after the first space are untouched.
- Autocomplete hides alias keys and, when the whole input is an alias,
surfaces its target command as the top (highlighted) suggestion so the
highlight matches what Enter runs (AA→AREA, L→LINE).
- ALIASEDIT opens a new editor modal (add/edit/delete rows) with an Apply
button; Apply persists to ocad.pgp, closing discards unapplied edits.
- Strip alias tokens from all 8 dispatch match-arm files and 71 module
CommandRegistration arrays, keeping canonical/synonym/distinct-command
names. ocad.pgp is now the sole source: deleting or remapping an alias
there takes effect. CUI stays on the keyboard-shortcut panel.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 12:39:22 +03:00
|
|
|
|
"SHEETSET" => {
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_info(crate::t!("SHEETSET: Sheet Set Manager not yet implemented.").as_ref());
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ── XDATA — read/write extended entity data ──────────────────────────
|
|
|
|
|
|
// XDATA LIST — show all xdata records on selected entities
|
|
|
|
|
|
// XDATA SET <app> <str> — append a string xdata value for <app>
|
|
|
|
|
|
// XDATA CLEAR — remove all xdata from selected entities
|
|
|
|
|
|
// XDATA CLEAR <app> — remove xdata for a specific application
|
2026-07-16 13:55:49 +03:00
|
|
|
|
"XDATA" => {
|
|
|
|
|
|
use crate::command::SelectThenKeywordCommand;
|
|
|
|
|
|
let has_sel = !self.tabs[i].scene.selected_entities().is_empty();
|
|
|
|
|
|
let c = SelectThenKeywordCommand::new(
|
|
|
|
|
|
"XDATA",
|
|
|
|
|
|
"XDATA [List / Clear] (SET <app> <value> by typing):",
|
|
|
|
|
|
vec![("List", "LIST", None), ("Clear", "CLEAR", None)],
|
|
|
|
|
|
has_sel,
|
|
|
|
|
|
);
|
|
|
|
|
|
self.command_line.push_info(&c.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(c));
|
|
|
|
|
|
}
|
|
|
|
|
|
cmd if cmd.starts_with("XDATA ") => {
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
use acadrust::xdata::{ExtendedDataRecord, XDataValue};
|
|
|
|
|
|
let rest = cmd.trim_start_matches("XDATA").trim();
|
|
|
|
|
|
let parts: Vec<&str> = rest.splitn(3, char::is_whitespace).collect();
|
|
|
|
|
|
let sub = parts.first().map(|s| s.to_uppercase()).unwrap_or_default();
|
|
|
|
|
|
let selected_handles: Vec<acadrust::Handle> = self.tabs[i]
|
|
|
|
|
|
.scene
|
|
|
|
|
|
.selected_entities()
|
|
|
|
|
|
.iter()
|
|
|
|
|
|
.map(|(h, _)| *h)
|
|
|
|
|
|
.collect();
|
|
|
|
|
|
if selected_handles.is_empty() {
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_error(crate::t!("XDATA: select entities first.").as_ref());
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
} else {
|
|
|
|
|
|
match sub.as_str() {
|
|
|
|
|
|
"LIST" | "" => {
|
|
|
|
|
|
for sh in &selected_handles {
|
|
|
|
|
|
if let Some(entity) = self.tabs[i].scene.document.get_entity(*sh) {
|
|
|
|
|
|
let xd = &entity.common().extended_data;
|
|
|
|
|
|
if xd.is_empty() {
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_output(crate::tf!(" {:x}: no xdata.", sh.value()).as_ref());
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
} else {
|
|
|
|
|
|
for rec in xd.records() {
|
2026-08-02 22:09:25 +03:00
|
|
|
|
self.command_line.push_output(crate::tf!(
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
" {:x} [{}]: {} value(s)",
|
|
|
|
|
|
sh.value(),
|
|
|
|
|
|
rec.application_name,
|
|
|
|
|
|
rec.values.len()
|
2026-08-02 22:09:25 +03:00
|
|
|
|
).as_ref());
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
for v in &rec.values {
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_output(crate::tf!(" {:?}", v).as_ref());
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
"SET" => {
|
|
|
|
|
|
let app = parts.get(1).copied().unwrap_or("OpenCADStudio");
|
|
|
|
|
|
let val = parts.get(2).copied().unwrap_or("");
|
|
|
|
|
|
self.push_undo_snapshot(i, "XDATA SET");
|
|
|
|
|
|
for sh in &selected_handles {
|
|
|
|
|
|
if let Some(entity) =
|
|
|
|
|
|
self.tabs[i].scene.document.get_entity_mut(*sh)
|
|
|
|
|
|
{
|
|
|
|
|
|
let mut rec = ExtendedDataRecord::new(app);
|
|
|
|
|
|
rec.add_value(XDataValue::String(val.to_string()));
|
|
|
|
|
|
entity.common_mut().extended_data.add_record(rec);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
self.tabs[i].dirty = true;
|
2026-08-02 22:09:25 +03:00
|
|
|
|
self.command_line.push_output(crate::tf!(
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
"XDATA: set [{app}] = \"{val}\" on {} entity/entities.",
|
|
|
|
|
|
selected_handles.len()
|
2026-08-02 22:09:25 +03:00
|
|
|
|
).as_ref());
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
}
|
|
|
|
|
|
"CLEAR" => {
|
|
|
|
|
|
let app_filter = parts.get(1).copied();
|
|
|
|
|
|
self.push_undo_snapshot(i, "XDATA CLEAR");
|
|
|
|
|
|
for sh in &selected_handles {
|
|
|
|
|
|
if let Some(entity) =
|
|
|
|
|
|
self.tabs[i].scene.document.get_entity_mut(*sh)
|
|
|
|
|
|
{
|
|
|
|
|
|
let xd = &mut entity.common_mut().extended_data;
|
|
|
|
|
|
if let Some(app) = app_filter {
|
|
|
|
|
|
// Rebuild without the matching app.
|
|
|
|
|
|
let kept: Vec<_> = xd
|
|
|
|
|
|
.records()
|
|
|
|
|
|
.iter()
|
|
|
|
|
|
.filter(|r| r.application_name != app)
|
|
|
|
|
|
.cloned()
|
|
|
|
|
|
.collect();
|
|
|
|
|
|
xd.clear();
|
|
|
|
|
|
for r in kept {
|
|
|
|
|
|
xd.add_record(r);
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
xd.clear();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
self.tabs[i].dirty = true;
|
2026-08-02 22:09:25 +03:00
|
|
|
|
self.command_line.push_output(crate::t!("XDATA: cleared.").as_ref());
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
}
|
|
|
|
|
|
_ => {
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_info(crate::t!("Usage: XDATA LIST | SET <app> <value> | CLEAR [app]").as_ref());
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// BOX / SPHERE / CYLINDER / CONE / WEDGE / TORUS are handled by the
|
|
|
|
|
|
// Model-tab primitive command above (with truck boolean caching).
|
|
|
|
|
|
|
|
|
|
|
|
// ── EXTRUDE ────────────────────────────────────────────────────
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
// PRESSPULL on a closed boundary creates a solid by extruding it to a
|
|
|
|
|
|
// height — the same operation as EXTRUDE. THICKEN turns a closed planar
|
|
|
|
|
|
// profile into a solid of the given thickness, which is also an extrude.
|
feat(alias): centralize command aliases in user-editable ocad.pgp (#286/#288)
Command aliases (L→LINE, CC→COPYCLIP) were scattered across dispatch match
arms and CommandRegistration name arrays. Move them all into a single
user-editable file:
- assets/ocad.pgp: shipped default alias table (ALIAS,*COMMAND format),
embedded and copied to config/ocad.pgp on first launch; only that user
copy is read after. AA→AREA default closes #286.
- resolve_alias() rewrites the leading command token at the top of
dispatch_command_inner, so aliases resolve for every path (families,
plugins, Repeat menu). Args after the first space are untouched.
- Autocomplete hides alias keys and, when the whole input is an alias,
surfaces its target command as the top (highlighted) suggestion so the
highlight matches what Enter runs (AA→AREA, L→LINE).
- ALIASEDIT opens a new editor modal (add/edit/delete rows) with an Apply
button; Apply persists to ocad.pgp, closing discards unapplied edits.
- Strip alias tokens from all 8 dispatch match-arm files and 71 module
CommandRegistration arrays, keeping canonical/synonym/distinct-command
names. ocad.pgp is now the sole source: deleting or remapping an alias
there takes effect. CUI stays on the keyboard-shortcut panel.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 12:39:22 +03:00
|
|
|
|
"EXTRUDE" | "PRESSPULL" | "THICKEN" => {
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
use crate::modules::insert::solid3d_cmds::ExtrudeCommand;
|
|
|
|
|
|
// If a single entity is already selected, skip the pick step.
|
|
|
|
|
|
let selected: Vec<_> = self.tabs[i].scene.selected_entities().into_iter().collect();
|
|
|
|
|
|
let color = self.tabs[i].scene.layer_color(&self.tabs[i].active_layer);
|
|
|
|
|
|
if selected.len() == 1 {
|
|
|
|
|
|
let handle = selected[0].0;
|
|
|
|
|
|
let mut cmd = ExtrudeCommand::new(color);
|
|
|
|
|
|
cmd.on_entity_pick(handle, glam::DVec3::ZERO);
|
|
|
|
|
|
self.command_line.push_info(&cmd.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
|
|
|
|
|
} else {
|
|
|
|
|
|
let cmd = ExtrudeCommand::new(color);
|
|
|
|
|
|
self.command_line.push_info(&cmd.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ── REVOLVE ────────────────────────────────────────────────────
|
feat(alias): centralize command aliases in user-editable ocad.pgp (#286/#288)
Command aliases (L→LINE, CC→COPYCLIP) were scattered across dispatch match
arms and CommandRegistration name arrays. Move them all into a single
user-editable file:
- assets/ocad.pgp: shipped default alias table (ALIAS,*COMMAND format),
embedded and copied to config/ocad.pgp on first launch; only that user
copy is read after. AA→AREA default closes #286.
- resolve_alias() rewrites the leading command token at the top of
dispatch_command_inner, so aliases resolve for every path (families,
plugins, Repeat menu). Args after the first space are untouched.
- Autocomplete hides alias keys and, when the whole input is an alias,
surfaces its target command as the top (highlighted) suggestion so the
highlight matches what Enter runs (AA→AREA, L→LINE).
- ALIASEDIT opens a new editor modal (add/edit/delete rows) with an Apply
button; Apply persists to ocad.pgp, closing discards unapplied edits.
- Strip alias tokens from all 8 dispatch match-arm files and 71 module
CommandRegistration arrays, keeping canonical/synonym/distinct-command
names. ocad.pgp is now the sole source: deleting or remapping an alias
there takes effect. CUI stays on the keyboard-shortcut panel.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 12:39:22 +03:00
|
|
|
|
"REVOLVE" => {
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
use crate::modules::insert::solid3d_cmds::RevolveCommand;
|
|
|
|
|
|
let color = self.tabs[i].scene.layer_color(&self.tabs[i].active_layer);
|
|
|
|
|
|
let cmd = RevolveCommand::new(color);
|
|
|
|
|
|
self.command_line.push_info(&cmd.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ── SWEEP ──────────────────────────────────────────────────────
|
|
|
|
|
|
"SWEEP" => {
|
|
|
|
|
|
use crate::modules::insert::solid3d_cmds::SweepCommand;
|
|
|
|
|
|
let color = self.tabs[i].scene.layer_color(&self.tabs[i].active_layer);
|
|
|
|
|
|
let cmd = SweepCommand::new(color);
|
|
|
|
|
|
self.command_line.push_info(&cmd.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ── LOFT ───────────────────────────────────────────────────────
|
|
|
|
|
|
"LOFT" => {
|
|
|
|
|
|
use crate::modules::insert::solid3d_cmds::LoftCommand;
|
|
|
|
|
|
let color = self.tabs[i].scene.layer_color(&self.tabs[i].active_layer);
|
|
|
|
|
|
let cmd = LoftCommand::new(color);
|
|
|
|
|
|
self.command_line.push_info(&cmd.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ── OBJ import ───────────────────────────────────────────────
|
|
|
|
|
|
"IMPORTOBJ" | "OBJIMPORT" => {
|
|
|
|
|
|
return Some(Task::done(Message::ObjImport));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ── STL export ────────────────────────────────────────────────
|
|
|
|
|
|
"STLOUT" | "EXPORTSTL" => {
|
|
|
|
|
|
return Some(Task::done(Message::StlExport));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// STEPOUT — export 3D meshes to STEP AP203 format
|
|
|
|
|
|
"STEPOUT" | "EXPORTSTEP" | "STPOUT" => {
|
|
|
|
|
|
return Some(Task::done(Message::StepExport));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ── Plot Style Editor GUI ─────────────────────────────────────
|
|
|
|
|
|
"PLOTSTYLEPANEL" | "PLOTSTYLEEDITOR" | "STYLESMANAGER" => {
|
|
|
|
|
|
return Some(Task::done(Message::PlotStylePanelOpen));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ── Plot / Page Setup ──────────────────────────────────────────
|
feat(plot): unified plot/print dialog, retire Page Setup
Replace the old Page Setup panel with a single OCS-styled plot/print
dialog (dropdowns for printer, paper, orientation, rotation, plot area,
scale, quality, shaded plot; checkboxes for plot options). PLOT / PRINT
/ PAGESETUP all open it.
- Printer output via lp: -d printer, -n copies, -o ColorModel=Gray,
print-quality, Resolution; Windows printto verb; list_printers via
lpstat. Preview opens a temp PDF in the OS viewer (never a save
dialog). PDF export reuses the tested pipeline.
- Window pick returns to the new dialog (was the old Page Setup) and the
clipped window plot drives PDF / printer / preview.
- Remember the user's print preferences across sessions in
config/plot.txt (printer, copies, quality, output options, area,
scale); drawing-specific fields (offset/center/rotation) seed from the
layout. Default plot area Window, scale Fit.
- Delete src/ui/window/page_setup.rs and its ModalKind / Message /
buffer state; fold on_page_setup_commit into apply_plot_page_settings.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 10:27:54 +03:00
|
|
|
|
// PLOT / PRINT open the full plot dialog (printer, paper, scale,
|
|
|
|
|
|
// options); EXPORT / EXPORTPDF stay a direct PDF export.
|
|
|
|
|
|
"PLOT" | "PRINT" => {
|
|
|
|
|
|
return Some(Task::done(Message::PlotDialogOpen));
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
}
|
2026-08-04 11:29:11 +03:00
|
|
|
|
"PRINTALL" => {
|
|
|
|
|
|
return Some(Task::done(Message::PrintAllOpen));
|
|
|
|
|
|
}
|
2026-07-28 19:23:29 +03:00
|
|
|
|
"EXPORT" | "EXPORTPDF" => {
|
|
|
|
|
|
return Some(Task::done(Message::PlotExport));
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
}
|
|
|
|
|
|
// PLOTSTYLE — load or clear CTB/STB plot style table
|
|
|
|
|
|
cmd if cmd == "PLOTSTYLE" || cmd.starts_with("PLOTSTYLE ") => {
|
|
|
|
|
|
let sub = cmd
|
|
|
|
|
|
.split_once(' ')
|
|
|
|
|
|
.map(|(_, r)| r.trim().to_uppercase())
|
|
|
|
|
|
.unwrap_or_default();
|
|
|
|
|
|
match sub.as_str() {
|
|
|
|
|
|
"CLEAR" | "NONE" => {
|
|
|
|
|
|
return Some(Task::done(Message::PlotStyleClear));
|
|
|
|
|
|
}
|
|
|
|
|
|
"" | "LOAD" => {
|
|
|
|
|
|
let active = self
|
|
|
|
|
|
.active_plot_style
|
|
|
|
|
|
.as_ref()
|
|
|
|
|
|
.map(|t| format!("Active: {}", t.name))
|
|
|
|
|
|
.unwrap_or_else(|| "No plot style loaded.".into());
|
|
|
|
|
|
self.command_line.push_info(&active);
|
|
|
|
|
|
return Some(Task::done(Message::PlotStyleLoad));
|
|
|
|
|
|
}
|
|
|
|
|
|
"?" | "STATUS" => {
|
|
|
|
|
|
let msg = self
|
|
|
|
|
|
.active_plot_style
|
|
|
|
|
|
.as_ref()
|
|
|
|
|
|
.map(|t| {
|
|
|
|
|
|
format!(
|
|
|
|
|
|
"Plot style: {} ({} color overrides)",
|
|
|
|
|
|
t.name,
|
|
|
|
|
|
t.aci_entries.iter().filter(|e| e.color.is_some()).count()
|
|
|
|
|
|
)
|
|
|
|
|
|
})
|
|
|
|
|
|
.unwrap_or_else(|| "No plot style table loaded.".into());
|
|
|
|
|
|
self.command_line.push_output(&msg);
|
|
|
|
|
|
}
|
|
|
|
|
|
_ => {
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_error(crate::t!("Usage: PLOTSTYLE [LOAD | CLEAR | STATUS]").as_ref());
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// UNDERLAY — edit properties of selected PDF/DWF/DGN underlay entities.
|
|
|
|
|
|
// Usage:
|
|
|
|
|
|
// UNDERLAY FADE <0-80>
|
|
|
|
|
|
// UNDERLAY CONTRAST <0-100>
|
|
|
|
|
|
// UNDERLAY ON | OFF
|
|
|
|
|
|
// UNDERLAY CLIP ON | OFF
|
|
|
|
|
|
// UNDERLAY MONO ON | OFF
|
2026-07-16 13:55:49 +03:00
|
|
|
|
"UNDERLAY" => {
|
|
|
|
|
|
use crate::command::SelectThenKeywordCommand;
|
|
|
|
|
|
let has_sel = !self.tabs[i].scene.selected_entities().is_empty();
|
|
|
|
|
|
let c = SelectThenKeywordCommand::new(
|
|
|
|
|
|
"UNDERLAY",
|
|
|
|
|
|
"UNDERLAY [Fade / Contrast / On / Off / Mono / Clip]:",
|
|
|
|
|
|
vec![
|
|
|
|
|
|
("Fade", "FADE", Some("UNDERLAY fade 0-100:")),
|
|
|
|
|
|
("Contrast", "CONTRAST", Some("UNDERLAY contrast 0-100:")),
|
|
|
|
|
|
("On", "ON", None),
|
|
|
|
|
|
("Off", "OFF", None),
|
|
|
|
|
|
("Mono", "MONO", Some("UNDERLAY MONO [On / Off]:")),
|
|
|
|
|
|
("Clip", "CLIP", Some("UNDERLAY CLIP [On / Off]:")),
|
|
|
|
|
|
],
|
|
|
|
|
|
has_sel,
|
|
|
|
|
|
);
|
|
|
|
|
|
self.command_line.push_info(&c.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(c));
|
|
|
|
|
|
}
|
|
|
|
|
|
cmd if cmd.starts_with("UNDERLAY ") => {
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
let sub = cmd
|
|
|
|
|
|
.split_once(' ')
|
|
|
|
|
|
.map(|(_, r)| r.trim().to_uppercase())
|
|
|
|
|
|
.unwrap_or_default();
|
|
|
|
|
|
let handles: Vec<acadrust::Handle> = self.tabs[i]
|
|
|
|
|
|
.scene
|
|
|
|
|
|
.selected_entities()
|
|
|
|
|
|
.iter()
|
|
|
|
|
|
.map(|(h, _)| *h)
|
|
|
|
|
|
.collect();
|
|
|
|
|
|
if handles.is_empty() {
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_error(crate::t!("UNDERLAY: select underlay entities first.").as_ref());
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
} else {
|
|
|
|
|
|
let parts: Vec<&str> = sub.splitn(2, char::is_whitespace).collect();
|
|
|
|
|
|
let action = parts.first().copied().unwrap_or("");
|
|
|
|
|
|
let arg = parts.get(1).copied().unwrap_or("").trim();
|
|
|
|
|
|
let mut changed = 0usize;
|
|
|
|
|
|
self.push_undo_snapshot(i, "UNDERLAY");
|
|
|
|
|
|
for h in &handles {
|
|
|
|
|
|
if let Some(acadrust::EntityType::Underlay(ul)) = self.tabs[i]
|
|
|
|
|
|
.scene
|
|
|
|
|
|
.document
|
|
|
|
|
|
.entities_mut()
|
|
|
|
|
|
.find(|e| e.common().handle == *h)
|
|
|
|
|
|
{
|
|
|
|
|
|
match action {
|
|
|
|
|
|
"FADE" => {
|
|
|
|
|
|
if let Ok(v) = arg.parse::<u8>() {
|
|
|
|
|
|
ul.set_fade(v);
|
|
|
|
|
|
changed += 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
"CONTRAST" => {
|
|
|
|
|
|
if let Ok(v) = arg.parse::<u8>() {
|
|
|
|
|
|
ul.set_contrast(v);
|
|
|
|
|
|
changed += 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
"ON" => {
|
|
|
|
|
|
ul.set_on(true);
|
|
|
|
|
|
changed += 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
"OFF" => {
|
|
|
|
|
|
ul.set_on(false);
|
|
|
|
|
|
changed += 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
"CLIP" => match arg {
|
|
|
|
|
|
"ON" => {
|
|
|
|
|
|
ul.flags |=
|
|
|
|
|
|
acadrust::entities::UnderlayDisplayFlags::CLIPPING;
|
|
|
|
|
|
changed += 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
"OFF" => {
|
|
|
|
|
|
ul.clear_clip();
|
|
|
|
|
|
changed += 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
_ => {}
|
|
|
|
|
|
},
|
|
|
|
|
|
"MONO" => match arg {
|
|
|
|
|
|
"ON" => {
|
|
|
|
|
|
ul.set_monochrome(true);
|
|
|
|
|
|
changed += 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
"OFF" => {
|
|
|
|
|
|
ul.set_monochrome(false);
|
|
|
|
|
|
changed += 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
_ => {}
|
|
|
|
|
|
},
|
|
|
|
|
|
_ => {
|
|
|
|
|
|
// No sub-command: print status.
|
2026-08-02 22:09:25 +03:00
|
|
|
|
self.command_line.push_output(crate::tf!(
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
"Underlay {:x}: fade={}, contrast={}, on={}, clip={}, mono={}",
|
|
|
|
|
|
h.value(),
|
|
|
|
|
|
ul.fade,
|
|
|
|
|
|
ul.contrast,
|
|
|
|
|
|
ul.is_on(),
|
|
|
|
|
|
ul.is_clipping(),
|
|
|
|
|
|
ul.is_monochrome(),
|
2026-08-02 22:09:25 +03:00
|
|
|
|
).as_ref());
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if changed > 0 {
|
|
|
|
|
|
self.tabs[i].dirty = true;
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_info(crate::tf!("Updated {changed} underlay(s).").as_ref());
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
} else if !action.is_empty() {
|
|
|
|
|
|
self.command_line.push_error(
|
2026-08-02 22:09:25 +03:00
|
|
|
|
crate::t!("Usage: UNDERLAY [FADE <n>|CONTRAST <n>|ON|OFF|CLIP ON|OFF|MONO ON|OFF]").as_ref()
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
feat(plot): unified plot/print dialog, retire Page Setup
Replace the old Page Setup panel with a single OCS-styled plot/print
dialog (dropdowns for printer, paper, orientation, rotation, plot area,
scale, quality, shaded plot; checkboxes for plot options). PLOT / PRINT
/ PAGESETUP all open it.
- Printer output via lp: -d printer, -n copies, -o ColorModel=Gray,
print-quality, Resolution; Windows printto verb; list_printers via
lpstat. Preview opens a temp PDF in the OS viewer (never a save
dialog). PDF export reuses the tested pipeline.
- Window pick returns to the new dialog (was the old Page Setup) and the
clipped window plot drives PDF / printer / preview.
- Remember the user's print preferences across sessions in
config/plot.txt (printer, copies, quality, output options, area,
scale); drawing-specific fields (offset/center/rotation) seed from the
layout. Default plot area Window, scale Fit.
- Delete src/ui/window/page_setup.rs and its ModalKind / Message /
buffer state; fold on_page_setup_commit into apply_plot_page_settings.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 10:27:54 +03:00
|
|
|
|
// PAGESETUP is folded into the unified plot dialog.
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
"PAGESETUP" => {
|
feat(plot): unified plot/print dialog, retire Page Setup
Replace the old Page Setup panel with a single OCS-styled plot/print
dialog (dropdowns for printer, paper, orientation, rotation, plot area,
scale, quality, shaded plot; checkboxes for plot options). PLOT / PRINT
/ PAGESETUP all open it.
- Printer output via lp: -d printer, -n copies, -o ColorModel=Gray,
print-quality, Resolution; Windows printto verb; list_printers via
lpstat. Preview opens a temp PDF in the OS viewer (never a save
dialog). PDF export reuses the tested pipeline.
- Window pick returns to the new dialog (was the old Page Setup) and the
clipped window plot drives PDF / printer / preview.
- Remember the user's print preferences across sessions in
config/plot.txt (printer, copies, quality, output options, area,
scale); drawing-specific fields (offset/center/rotation) seed from the
layout. Default plot area Window, scale Fit.
- Delete src/ui/window/page_setup.rs and its ModalKind / Message /
buffer state; fold on_page_setup_commit into apply_plot_page_settings.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 10:27:54 +03:00
|
|
|
|
return Some(Task::done(Message::PlotDialogOpen));
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
}
|
feat(commands): integrate standard CAD commands and fix behavior gaps
Broad pass over the command set: integrate missing commands, wire
previously-unreachable ribbon/menu buttons, and correct name/behaviour
mismatches. Every UI command token now resolves (real handler where the
infrastructure exists, an honest "not yet implemented" status otherwise).
New commands:
- Draw: 3DPOLY, 2D solid (SO/SOLID2D), HELIX, TRACE, SKETCH, DIMCENTER
(DCE/CENTERMARK)
- Modify/inquiry: REVERSE, MEASUREGEOM (MEA), DBLIST
- Data/manage: BASE, OVERKILL (exact-duplicate removal), AUDIT (read-only
integrity report), SETVAR (system-variable get/set), SCRIPT (.scr runner),
FINDNONPURGEABLE
Behaviour fixes:
- LINE and SPLINE gain Close/Undo; AREA computes area/perimeter in f64
(correct at large coordinates)
- ATTEDIT/ATE unified so the alias and the ribbon button open the
interactive editor; -ATTEDIT is the command-line form
- Linear/aligned/radius/diameter dimensions gain a text override (Text) and
text angle (Angle) option
- INSERT gains Scale/Rotate options; COPY gains an Array option
- LINETYPE SET sets the current entity linetype; SETBYLAYER resets overrides
- UNDO accepts a step count
Integrations: standard dimension/block aliases (DLI/DRA/DAN, BMAKE,
EXPORTPDF, DDIM), drafting-aid and screen toggles (GRID/SNAP/POLAR/OSNAP/
DSETTINGS/CLEANSCREEN/QUICKPROPERTIES), viewport tiling
(CASCADE/HORIZONTAL/VERTICAL/VPJOIN), and customization entry points
(CUI/ALIASEDIT).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 19:27:35 +03:00
|
|
|
|
|
|
|
|
|
|
// ── Recognized commands whose full implementation is pending ─────────
|
|
|
|
|
|
// These verbs are surfaced by the ribbon / menus but their feature is
|
|
|
|
|
|
// still being built. Acknowledge them with an honest status so the
|
|
|
|
|
|
// button responds instead of reporting an unknown command; each is
|
|
|
|
|
|
// replaced by its real handler as the feature lands.
|
2026-08-02 12:00:15 +03:00
|
|
|
|
// OBJECTSCALE ADD — add the active scale representation to every
|
|
|
|
|
|
// selected object that supports per-scale context data.
|
2026-07-16 13:14:37 +03:00
|
|
|
|
"OBJECTSCALE ADD" => {
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
let handles: Vec<acadrust::Handle> = self.tabs[i]
|
|
|
|
|
|
.scene
|
|
|
|
|
|
.selected_entities()
|
|
|
|
|
|
.iter()
|
|
|
|
|
|
.map(|(h, _)| *h)
|
|
|
|
|
|
.collect();
|
|
|
|
|
|
if handles.is_empty() {
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_error(crate::t!("OBJECTSCALE: select objects first.").as_ref());
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
return Some(Task::none());
|
|
|
|
|
|
}
|
|
|
|
|
|
self.push_undo_snapshot(i, "OBJECTSCALE");
|
2026-08-02 12:00:15 +03:00
|
|
|
|
let Some(scale) = self.tabs[i].scene.creation_annotation_scale_handle() else {
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_error(crate::t!("OBJECTSCALE: the active annotation scale is unavailable.").as_ref());
|
2026-08-02 12:00:15 +03:00
|
|
|
|
return Some(Task::none());
|
|
|
|
|
|
};
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
let mut n = 0usize;
|
|
|
|
|
|
for h in &handles {
|
2026-08-02 12:00:15 +03:00
|
|
|
|
if crate::scene::annotative::create_annotation_context(
|
|
|
|
|
|
&mut self.tabs[i].scene.document,
|
|
|
|
|
|
*h,
|
|
|
|
|
|
scale,
|
|
|
|
|
|
) {
|
|
|
|
|
|
crate::scene::annotative::set_entity_annotative(
|
|
|
|
|
|
&mut self.tabs[i].scene.document,
|
|
|
|
|
|
*h,
|
|
|
|
|
|
true,
|
|
|
|
|
|
);
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
n += 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-07-28 17:16:27 +03:00
|
|
|
|
let changes: Vec<_> = handles
|
|
|
|
|
|
.into_iter()
|
|
|
|
|
|
.map(|handle| (handle, crate::scene::ChangeKind::Modified))
|
|
|
|
|
|
.collect();
|
|
|
|
|
|
self.tabs[i].scene.bump_entities(&changes);
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
self.tabs[i].dirty = true;
|
2026-08-02 22:09:25 +03:00
|
|
|
|
self.command_line.push_output(crate::tf!(
|
2026-08-02 12:00:15 +03:00
|
|
|
|
"OBJECTSCALE: added the active scale to {n} object(s)."
|
2026-08-02 22:09:25 +03:00
|
|
|
|
).as_ref());
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
return Some(Task::none());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-29 09:04:18 +03:00
|
|
|
|
// HYPERLINK <url> — attach a hyperlink to the selected objects, stored
|
|
|
|
|
|
// in the standard PE_URL XData record so it round-trips in the file.
|
2026-07-16 14:37:10 +03:00
|
|
|
|
"HYPERLINK" => {
|
|
|
|
|
|
use crate::command::SelectThenValueCommand;
|
|
|
|
|
|
let has_sel = !self.tabs[i].scene.selected_entities().is_empty();
|
|
|
|
|
|
let c = SelectThenValueCommand::new(
|
|
|
|
|
|
"HYPERLINK",
|
|
|
|
|
|
"HYPERLINK URL to attach:",
|
|
|
|
|
|
has_sel,
|
|
|
|
|
|
);
|
|
|
|
|
|
self.command_line.push_info(&c.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(c));
|
|
|
|
|
|
}
|
|
|
|
|
|
cmd if cmd.starts_with("HYPERLINK ") => {
|
2026-06-29 09:04:18 +03:00
|
|
|
|
use acadrust::xdata::{ExtendedDataRecord, XDataValue};
|
|
|
|
|
|
let url = cmd.strip_prefix("HYPERLINK").unwrap_or("").trim().to_string();
|
|
|
|
|
|
if url.is_empty() {
|
2026-08-02 22:09:25 +03:00
|
|
|
|
self.command_line.push_info(crate::t!("Usage: HYPERLINK <url> (select objects first)").as_ref());
|
2026-06-29 09:04:18 +03:00
|
|
|
|
return Some(Task::none());
|
|
|
|
|
|
}
|
|
|
|
|
|
let handles: Vec<acadrust::Handle> =
|
|
|
|
|
|
self.tabs[i].scene.selected_entities().iter().map(|(h, _)| *h).collect();
|
|
|
|
|
|
if handles.is_empty() {
|
2026-08-02 22:09:25 +03:00
|
|
|
|
self.command_line.push_error(crate::t!("HYPERLINK: select objects first.").as_ref());
|
2026-06-29 09:04:18 +03:00
|
|
|
|
return Some(Task::none());
|
|
|
|
|
|
}
|
|
|
|
|
|
self.push_undo_snapshot(i, "HYPERLINK");
|
|
|
|
|
|
let mut n = 0usize;
|
|
|
|
|
|
for h in &handles {
|
|
|
|
|
|
if let Some(e) = self.tabs[i].scene.document.get_entity_mut(*h) {
|
|
|
|
|
|
let xd = &mut e.common_mut().extended_data;
|
|
|
|
|
|
let mut rec = ExtendedDataRecord::new("PE_URL");
|
|
|
|
|
|
rec.add_value(XDataValue::String(url.clone()));
|
|
|
|
|
|
xd.add_record(rec);
|
|
|
|
|
|
n += 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
self.tabs[i].dirty = true;
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_output(crate::tf!("HYPERLINK: attached to {n} object(s).").as_ref());
|
2026-06-29 09:04:18 +03:00
|
|
|
|
return Some(Task::none());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
// ADJUST — set brightness / contrast / fade on selected raster images
|
|
|
|
|
|
// ADJUST BRIGHTNESS|CONTRAST|FADE <0-100>
|
2026-07-16 13:55:49 +03:00
|
|
|
|
"ADJUST" => {
|
|
|
|
|
|
use crate::command::SelectThenKeywordCommand;
|
|
|
|
|
|
let has_sel = !self.tabs[i].scene.selected_entities().is_empty();
|
|
|
|
|
|
let c = SelectThenKeywordCommand::new(
|
|
|
|
|
|
"ADJUST",
|
|
|
|
|
|
"ADJUST [Brightness / Contrast / Fade]:",
|
|
|
|
|
|
vec![
|
|
|
|
|
|
("Brightness", "BRIGHTNESS", Some("ADJUST brightness 0-100:")),
|
|
|
|
|
|
("Contrast", "CONTRAST", Some("ADJUST contrast 0-100:")),
|
|
|
|
|
|
("Fade", "FADE", Some("ADJUST fade 0-100:")),
|
|
|
|
|
|
],
|
|
|
|
|
|
has_sel,
|
|
|
|
|
|
);
|
|
|
|
|
|
self.command_line.push_info(&c.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(c));
|
|
|
|
|
|
}
|
|
|
|
|
|
cmd if cmd.starts_with("ADJUST ") => {
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
let rest = cmd.trim_start_matches("ADJUST").trim();
|
|
|
|
|
|
let parts: Vec<&str> = rest.splitn(2, char::is_whitespace).collect();
|
|
|
|
|
|
let action = parts.first().map(|s| s.to_uppercase()).unwrap_or_default();
|
|
|
|
|
|
let arg = parts.get(1).copied().unwrap_or("").trim();
|
|
|
|
|
|
let handles: Vec<acadrust::Handle> = self.tabs[i]
|
|
|
|
|
|
.scene
|
|
|
|
|
|
.selected_entities()
|
|
|
|
|
|
.iter()
|
|
|
|
|
|
.map(|(h, _)| *h)
|
|
|
|
|
|
.collect();
|
|
|
|
|
|
if handles.is_empty() {
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_error(crate::t!("ADJUST: select raster image(s) first.").as_ref());
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
} else if action.is_empty() {
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_info(crate::t!("Usage: ADJUST BRIGHTNESS|CONTRAST|FADE <0-100>").as_ref());
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
} else if let Ok(v) = arg.parse::<u8>() {
|
|
|
|
|
|
let v = v.min(100);
|
|
|
|
|
|
self.push_undo_snapshot(i, "ADJUST");
|
|
|
|
|
|
let mut changed = 0usize;
|
2026-07-28 17:16:27 +03:00
|
|
|
|
let mut changed_handles = Vec::new();
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
for h in &handles {
|
|
|
|
|
|
if let Some(acadrust::EntityType::RasterImage(img)) = self.tabs[i]
|
|
|
|
|
|
.scene
|
|
|
|
|
|
.document
|
|
|
|
|
|
.entities_mut()
|
|
|
|
|
|
.find(|e| e.common().handle == *h)
|
|
|
|
|
|
{
|
|
|
|
|
|
match action.as_str() {
|
|
|
|
|
|
"BRIGHTNESS" => {
|
|
|
|
|
|
img.brightness = v;
|
|
|
|
|
|
changed += 1;
|
2026-07-28 17:16:27 +03:00
|
|
|
|
changed_handles.push(*h);
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
}
|
|
|
|
|
|
"CONTRAST" => {
|
|
|
|
|
|
img.contrast = v;
|
|
|
|
|
|
changed += 1;
|
2026-07-28 17:16:27 +03:00
|
|
|
|
changed_handles.push(*h);
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
}
|
|
|
|
|
|
"FADE" => {
|
|
|
|
|
|
img.fade = v;
|
|
|
|
|
|
changed += 1;
|
2026-07-28 17:16:27 +03:00
|
|
|
|
changed_handles.push(*h);
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
}
|
|
|
|
|
|
_ => {}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if changed > 0 {
|
|
|
|
|
|
self.tabs[i].dirty = true;
|
2026-07-28 17:16:27 +03:00
|
|
|
|
for &handle in &changed_handles {
|
|
|
|
|
|
self.tabs[i].scene.reseed_derived_caches(handle);
|
|
|
|
|
|
}
|
|
|
|
|
|
let changes: Vec<_> = changed_handles
|
|
|
|
|
|
.into_iter()
|
|
|
|
|
|
.map(|handle| (handle, crate::scene::ChangeKind::Modified))
|
|
|
|
|
|
.collect();
|
|
|
|
|
|
self.tabs[i].scene.bump_entities(&changes);
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_output(crate::tf!("ADJUST: {action} = {v} on {changed} image(s).").as_ref());
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
} else {
|
|
|
|
|
|
self.command_line.push_error(
|
|
|
|
|
|
"ADJUST: no raster images selected, or unknown property (use BRIGHTNESS|CONTRAST|FADE).",
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
2026-08-02 22:09:25 +03:00
|
|
|
|
self.command_line.push_error(crate::t!("ADJUST: value must be 0-100.").as_ref());
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ANNOSCALE / CANNOSCALE <ratio> — set the current annotation scale
|
|
|
|
|
|
// (e.g. 1:50, 2:1, or a plain factor). Drives annotative-object size
|
|
|
|
|
|
// in model space and is written to the drawing header.
|
2026-07-16 13:00:10 +03:00
|
|
|
|
"ANNOSCALE" | "CANNOSCALE" => {
|
|
|
|
|
|
use crate::command::ValuePromptCommand;
|
|
|
|
|
|
let c = ValuePromptCommand::new(
|
|
|
|
|
|
"ANNOSCALE",
|
|
|
|
|
|
"ANNOSCALE new annotation scale (e.g. 1:50, 2:1, or a factor):",
|
|
|
|
|
|
);
|
|
|
|
|
|
self.command_line.push_info(&c.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(c));
|
|
|
|
|
|
}
|
2026-08-02 12:00:15 +03:00
|
|
|
|
"ANNOALLVISIBLE" => {
|
|
|
|
|
|
use crate::command::ValuePromptCommand;
|
|
|
|
|
|
let c = ValuePromptCommand::new(
|
|
|
|
|
|
"ANNOALLVISIBLE",
|
|
|
|
|
|
"ANNOALLVISIBLE new value [0/1]:",
|
|
|
|
|
|
);
|
|
|
|
|
|
self.command_line.push_info(&c.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(c));
|
|
|
|
|
|
}
|
|
|
|
|
|
cmd if cmd.starts_with("ANNOALLVISIBLE ") => {
|
|
|
|
|
|
let value = cmd.split_whitespace().nth(1).unwrap_or("");
|
|
|
|
|
|
match value {
|
|
|
|
|
|
"0" | "OFF" | "FALSE" => {
|
|
|
|
|
|
self.tabs[i].scene.set_annotation_all_visible(false);
|
|
|
|
|
|
self.tabs[i].dirty = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
"1" | "ON" | "TRUE" => {
|
|
|
|
|
|
self.tabs[i].scene.set_annotation_all_visible(true);
|
|
|
|
|
|
self.tabs[i].dirty = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
_ => self
|
|
|
|
|
|
.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_error(crate::t!("ANNOALLVISIBLE: enter 0 or 1.").as_ref()),
|
2026-08-02 12:00:15 +03:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
"ANNOAUTOSCALE" => {
|
|
|
|
|
|
use crate::command::ValuePromptCommand;
|
|
|
|
|
|
let c = ValuePromptCommand::new(
|
|
|
|
|
|
"ANNOAUTOSCALE",
|
|
|
|
|
|
"ANNOAUTOSCALE new value [-4..4]:",
|
|
|
|
|
|
);
|
|
|
|
|
|
self.command_line.push_info(&c.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(c));
|
|
|
|
|
|
}
|
|
|
|
|
|
cmd if cmd.starts_with("ANNOAUTOSCALE ") => {
|
|
|
|
|
|
let value = cmd.split_whitespace().nth(1).unwrap_or("");
|
|
|
|
|
|
match value.parse::<i8>() {
|
|
|
|
|
|
Ok(mode @ -4..=4) => self.annotation_auto_scale = mode,
|
|
|
|
|
|
_ => self.command_line.push_error(
|
|
|
|
|
|
"ANNOAUTOSCALE: enter an integer from -4 through 4.",
|
|
|
|
|
|
),
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
"ANNOUPDATE" => {
|
|
|
|
|
|
let handles: Vec<_> = self.tabs[i]
|
|
|
|
|
|
.scene
|
|
|
|
|
|
.selected_entities()
|
|
|
|
|
|
.iter()
|
|
|
|
|
|
.map(|(handle, _)| *handle)
|
|
|
|
|
|
.collect();
|
|
|
|
|
|
if handles.is_empty() {
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_error(crate::t!("ANNOUPDATE: select annotation objects first.").as_ref());
|
2026-08-02 12:00:15 +03:00
|
|
|
|
return Some(Task::none());
|
|
|
|
|
|
}
|
|
|
|
|
|
self.push_undo_snapshot(i, "ANNOUPDATE");
|
|
|
|
|
|
let scale = self.tabs[i].scene.creation_annotation_scale_handle();
|
|
|
|
|
|
let mut updated = 0usize;
|
|
|
|
|
|
for handle in &handles {
|
|
|
|
|
|
if crate::scene::annotative::update_entity_from_annotation_style(
|
|
|
|
|
|
&mut self.tabs[i].scene.document,
|
|
|
|
|
|
*handle,
|
|
|
|
|
|
scale,
|
|
|
|
|
|
) {
|
|
|
|
|
|
updated += 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if updated > 0 {
|
|
|
|
|
|
let changes: Vec<_> = handles
|
|
|
|
|
|
.into_iter()
|
|
|
|
|
|
.map(|handle| (handle, crate::scene::ChangeKind::Modified))
|
|
|
|
|
|
.collect();
|
|
|
|
|
|
self.tabs[i].scene.bump_entities(&changes);
|
|
|
|
|
|
self.tabs[i].dirty = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_output(crate::tf!("ANNOUPDATE: updated {updated} object(s).").as_ref());
|
2026-08-02 12:00:15 +03:00
|
|
|
|
return Some(Task::none());
|
|
|
|
|
|
}
|
2026-07-16 13:00:10 +03:00
|
|
|
|
cmd if cmd.starts_with("ANNOSCALE ") || cmd.starts_with("CANNOSCALE ") => {
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
let arg = cmd
|
|
|
|
|
|
.split_whitespace()
|
|
|
|
|
|
.nth(1)
|
|
|
|
|
|
.unwrap_or("")
|
|
|
|
|
|
.trim()
|
|
|
|
|
|
.to_string();
|
|
|
|
|
|
if arg.is_empty() {
|
|
|
|
|
|
let name = self.tabs[i]
|
|
|
|
|
|
.scene
|
|
|
|
|
|
.document
|
|
|
|
|
|
.header
|
|
|
|
|
|
.current_annotation_scale
|
|
|
|
|
|
.clone();
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_output(crate::tf!("Current annotation scale: {name}").as_ref());
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
return Some(Task::none());
|
|
|
|
|
|
}
|
2026-08-02 12:00:15 +03:00
|
|
|
|
let previous = self.tabs[i].scene.displayed_annotation_scale_handle();
|
|
|
|
|
|
match self.tabs[i].scene.set_annotation_scale_named(&arg) {
|
|
|
|
|
|
Some(handle) => {
|
|
|
|
|
|
if self.annotation_auto_scale > 0 {
|
|
|
|
|
|
self.tabs[i].scene.add_annotation_scale_to_objects(
|
|
|
|
|
|
handle,
|
|
|
|
|
|
previous,
|
|
|
|
|
|
self.annotation_auto_scale as u8,
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
self.tabs[i].dirty = true;
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_output(crate::tf!("Annotation scale: {arg}").as_ref());
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
}
|
2026-08-02 12:00:15 +03:00
|
|
|
|
None => self
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_error(crate::t!("Usage: ANNOSCALE <ratio> e.g. 1:50, 2:1, or a factor").as_ref()),
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-13 10:18:05 +03:00
|
|
|
|
// SCALELISTEDIT — list / add / delete the drawing's annotation scales.
|
|
|
|
|
|
// SCALELISTEDIT list
|
|
|
|
|
|
// SCALELISTEDIT ADD 1:50 add (name is a paper:drawing ratio)
|
|
|
|
|
|
// SCALELISTEDIT DELETE 1:50 remove (not the current scale)
|
2026-07-16 13:29:04 +03:00
|
|
|
|
"SCALELISTEDIT" => {
|
|
|
|
|
|
use crate::command::KeywordCommand;
|
|
|
|
|
|
let c = KeywordCommand::new(
|
|
|
|
|
|
"SCALELISTEDIT",
|
|
|
|
|
|
"SCALELISTEDIT [Add / Delete]:",
|
|
|
|
|
|
vec![
|
|
|
|
|
|
("Add", "ADD", Some("SCALELISTEDIT ADD new scale ratio (e.g. 1:50):")),
|
|
|
|
|
|
("Delete", "DELETE", Some("SCALELISTEDIT DELETE scale ratio to remove:")),
|
|
|
|
|
|
],
|
|
|
|
|
|
);
|
|
|
|
|
|
self.command_line.push_info(&c.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(c));
|
|
|
|
|
|
}
|
|
|
|
|
|
cmd if cmd.starts_with("SCALELISTEDIT ") => {
|
2026-07-13 10:18:05 +03:00
|
|
|
|
let rest = cmd.trim_start_matches("SCALELISTEDIT").trim();
|
|
|
|
|
|
let mut parts = rest.splitn(2, char::is_whitespace);
|
|
|
|
|
|
let sub = parts.next().unwrap_or("").to_uppercase();
|
|
|
|
|
|
let arg = parts.next().unwrap_or("").trim();
|
|
|
|
|
|
match sub.as_str() {
|
|
|
|
|
|
"ADD" => match arg.split_once(':') {
|
|
|
|
|
|
Some((p, d)) => match (p.trim().parse::<f64>(), d.trim().parse::<f64>()) {
|
|
|
|
|
|
(Ok(paper), Ok(drawing)) if paper > 0.0 && drawing > 0.0 => {
|
|
|
|
|
|
self.push_undo_snapshot(i, "SCALELISTEDIT");
|
|
|
|
|
|
if self.tabs[i].scene.add_scale(arg, paper, drawing) {
|
|
|
|
|
|
self.tabs[i].dirty = true;
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_output(crate::tf!("Added annotation scale {arg}.").as_ref());
|
2026-07-13 10:18:05 +03:00
|
|
|
|
} else {
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_info(crate::tf!("Scale {arg} already exists.").as_ref());
|
2026-07-13 10:18:05 +03:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
_ => self
|
|
|
|
|
|
.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_error(crate::t!("SCALELISTEDIT ADD: use a ratio like 1:50.").as_ref()),
|
2026-07-13 10:18:05 +03:00
|
|
|
|
},
|
|
|
|
|
|
None => self
|
|
|
|
|
|
.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_error(crate::t!("SCALELISTEDIT ADD: use a ratio like 1:50.").as_ref()),
|
2026-07-13 10:18:05 +03:00
|
|
|
|
},
|
|
|
|
|
|
"DELETE" | "REMOVE" => {
|
|
|
|
|
|
let current = self.tabs[i]
|
|
|
|
|
|
.scene
|
|
|
|
|
|
.document
|
|
|
|
|
|
.header
|
|
|
|
|
|
.current_annotation_scale
|
|
|
|
|
|
.clone();
|
|
|
|
|
|
if arg.is_empty() {
|
2026-08-02 22:09:25 +03:00
|
|
|
|
self.command_line.push_info(crate::t!("Usage: SCALELISTEDIT DELETE <name>").as_ref());
|
2026-07-13 10:18:05 +03:00
|
|
|
|
} else if arg.eq_ignore_ascii_case(¤t) {
|
2026-08-02 22:09:25 +03:00
|
|
|
|
self.command_line.push_error(crate::tf!(
|
2026-07-13 10:18:05 +03:00
|
|
|
|
"Cannot delete the current annotation scale ({arg})."
|
2026-08-02 22:09:25 +03:00
|
|
|
|
).as_ref());
|
2026-07-13 10:18:05 +03:00
|
|
|
|
} else {
|
|
|
|
|
|
self.push_undo_snapshot(i, "SCALELISTEDIT");
|
|
|
|
|
|
if self.tabs[i].scene.remove_scale(arg) {
|
|
|
|
|
|
self.tabs[i].dirty = true;
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_output(crate::tf!("Removed annotation scale {arg}.").as_ref());
|
2026-07-13 10:18:05 +03:00
|
|
|
|
} else {
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_info(crate::tf!("No annotation scale named {arg}.").as_ref());
|
2026-07-13 10:18:05 +03:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
"" => {
|
|
|
|
|
|
let names: Vec<String> = self.tabs[i]
|
|
|
|
|
|
.scene
|
|
|
|
|
|
.scale_list()
|
|
|
|
|
|
.into_iter()
|
|
|
|
|
|
.map(|(n, _, _)| n)
|
|
|
|
|
|
.collect();
|
|
|
|
|
|
if names.is_empty() {
|
2026-08-02 22:09:25 +03:00
|
|
|
|
self.command_line.push_info(crate::t!("No annotation scales defined.").as_ref());
|
2026-07-13 10:18:05 +03:00
|
|
|
|
} else {
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_output(crate::tf!("Annotation scales: {}", names.join(", ")).as_ref());
|
2026-07-13 10:18:05 +03:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
_ => self
|
|
|
|
|
|
.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_info(crate::t!("Usage: SCALELISTEDIT [ADD 1:50 | DELETE 1:50]").as_ref()),
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-13 22:38:29 +03:00
|
|
|
|
// OBJECTSCALE — open the Annotation Object Scale dialog for the
|
|
|
|
|
|
// selected object (add / remove its per-object scale representations).
|
2026-07-16 13:14:37 +03:00
|
|
|
|
// Reachable now that the immediate "add current scale" quick action
|
|
|
|
|
|
// moved to the explicit `OBJECTSCALE ADD` keyword above.
|
2026-07-13 22:38:29 +03:00
|
|
|
|
"OBJECTSCALE" => {
|
|
|
|
|
|
return Some(Task::done(Message::AnnoObjectScaleOpen));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
// DATALINK <path.csv> — import a CSV file into a table placed at the
|
|
|
|
|
|
// origin (one-time import; a live re-reading link is future work).
|
2026-07-16 14:40:11 +03:00
|
|
|
|
"DATALINK" => {
|
|
|
|
|
|
use crate::command::ValuePromptCommand;
|
|
|
|
|
|
let c = ValuePromptCommand::new("DATALINK", "DATALINK path to the .csv file:");
|
|
|
|
|
|
self.command_line.push_info(&c.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(c));
|
|
|
|
|
|
}
|
|
|
|
|
|
cmd if cmd.starts_with("DATALINK ") => {
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
let path = cmd.trim_start_matches("DATALINK").trim();
|
|
|
|
|
|
if path.is_empty() {
|
|
|
|
|
|
self.command_line.push_info(
|
|
|
|
|
|
"Usage: DATALINK <path-to-.csv> — imports the CSV into a table at the origin.",
|
|
|
|
|
|
);
|
|
|
|
|
|
return Some(Task::none());
|
|
|
|
|
|
}
|
|
|
|
|
|
match std::fs::read_to_string(path) {
|
|
|
|
|
|
Ok(text) => {
|
|
|
|
|
|
let rows_data: Vec<Vec<String>> = text
|
|
|
|
|
|
.lines()
|
|
|
|
|
|
.filter(|l| !l.trim().is_empty())
|
|
|
|
|
|
.map(|line| line.split(',').map(|s| s.trim().to_string()).collect())
|
|
|
|
|
|
.collect();
|
|
|
|
|
|
let nrows = rows_data.len();
|
|
|
|
|
|
let ncols = rows_data.iter().map(|r| r.len()).max().unwrap_or(0);
|
|
|
|
|
|
if nrows == 0 || ncols == 0 {
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_error(crate::t!("DATALINK: the CSV file is empty.").as_ref());
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
return Some(Task::none());
|
|
|
|
|
|
}
|
|
|
|
|
|
use acadrust::entities::TableBuilder;
|
|
|
|
|
|
use acadrust::types::Vector3;
|
|
|
|
|
|
let mut table = TableBuilder::new(nrows, ncols)
|
|
|
|
|
|
.at(Vector3::new(0.0, 0.0, 0.0))
|
|
|
|
|
|
.row_height(0.5)
|
|
|
|
|
|
.column_width(2.0)
|
|
|
|
|
|
.build();
|
|
|
|
|
|
for (r, row) in rows_data.iter().enumerate() {
|
|
|
|
|
|
for (c, cell) in row.iter().enumerate() {
|
|
|
|
|
|
table.set_cell_text(r, c, cell);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
self.push_undo_snapshot(i, "DATALINK");
|
|
|
|
|
|
self.tabs[i]
|
|
|
|
|
|
.scene
|
|
|
|
|
|
.add_entity_clone(acadrust::EntityType::Table(table));
|
|
|
|
|
|
self.tabs[i].dirty = true;
|
2026-08-02 22:09:25 +03:00
|
|
|
|
self.command_line.push_output(crate::tf!(
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
"DATALINK: imported {nrows}×{ncols} cells into a table at the origin."
|
2026-08-02 22:09:25 +03:00
|
|
|
|
).as_ref());
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
}
|
|
|
|
|
|
Err(e) => {
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_error(crate::tf!("DATALINK: cannot read \"{path}\": {e}").as_ref());
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// LANDXMLIMPORT <path> — import survey points (LandXML <CgPoint>
|
|
|
|
|
|
// elements) as Point objects. Reads the coordinate text content
|
|
|
|
|
|
// (northing easting elevation) → Point at (easting, northing, elev).
|
2026-07-16 14:40:11 +03:00
|
|
|
|
"LANDXMLIMPORT" => {
|
|
|
|
|
|
use crate::command::ValuePromptCommand;
|
|
|
|
|
|
let c =
|
|
|
|
|
|
ValuePromptCommand::new("LANDXMLIMPORT", "LANDXMLIMPORT path to the .xml file:");
|
|
|
|
|
|
self.command_line.push_info(&c.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(c));
|
|
|
|
|
|
}
|
|
|
|
|
|
cmd if cmd.starts_with("LANDXMLIMPORT ") => {
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
let path = cmd.trim_start_matches("LANDXMLIMPORT").trim();
|
|
|
|
|
|
if path.is_empty() {
|
|
|
|
|
|
self.command_line.push_info(
|
|
|
|
|
|
"Usage: LANDXMLIMPORT <path-to-.xml> (imports CgPoint survey points)",
|
|
|
|
|
|
);
|
|
|
|
|
|
return Some(Task::none());
|
|
|
|
|
|
}
|
|
|
|
|
|
match std::fs::read_to_string(path) {
|
|
|
|
|
|
Ok(xml) => {
|
|
|
|
|
|
let pts = parse_landxml_cgpoints(&xml);
|
|
|
|
|
|
if pts.is_empty() {
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_info(crate::t!("LANDXMLIMPORT: no <CgPoint> survey points found.").as_ref());
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
return Some(Task::none());
|
|
|
|
|
|
}
|
|
|
|
|
|
self.push_undo_snapshot(i, "LANDXMLIMPORT");
|
|
|
|
|
|
for [x, y, z] in &pts {
|
|
|
|
|
|
let mut p = acadrust::entities::Point::new();
|
|
|
|
|
|
p.location = acadrust::types::Vector3::new(*x, *y, *z);
|
|
|
|
|
|
self.tabs[i]
|
|
|
|
|
|
.scene
|
|
|
|
|
|
.add_entity_clone(acadrust::EntityType::Point(p));
|
|
|
|
|
|
}
|
|
|
|
|
|
self.tabs[i].dirty = true;
|
2026-08-02 22:09:25 +03:00
|
|
|
|
self.command_line.push_output(crate::tf!(
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
"LANDXMLIMPORT: imported {} survey point(s). Use ZOOM EXTENTS to view.",
|
|
|
|
|
|
pts.len()
|
2026-08-02 22:09:25 +03:00
|
|
|
|
).as_ref());
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
}
|
|
|
|
|
|
Err(e) => self
|
|
|
|
|
|
.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_error(crate::tf!("LANDXMLIMPORT: cannot read \"{path}\": {e}").as_ref()),
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-13 22:38:29 +03:00
|
|
|
|
"POINTCLOUDATTACH" | "RECAP" | "SYNCPVIEWPORTS" | "UNDERLAYLAYERS"
|
2026-06-30 17:55:44 +03:00
|
|
|
|
| "UOSNAP" => {
|
feat(commands): integrate standard CAD commands and fix behavior gaps
Broad pass over the command set: integrate missing commands, wire
previously-unreachable ribbon/menu buttons, and correct name/behaviour
mismatches. Every UI command token now resolves (real handler where the
infrastructure exists, an honest "not yet implemented" status otherwise).
New commands:
- Draw: 3DPOLY, 2D solid (SO/SOLID2D), HELIX, TRACE, SKETCH, DIMCENTER
(DCE/CENTERMARK)
- Modify/inquiry: REVERSE, MEASUREGEOM (MEA), DBLIST
- Data/manage: BASE, OVERKILL (exact-duplicate removal), AUDIT (read-only
integrity report), SETVAR (system-variable get/set), SCRIPT (.scr runner),
FINDNONPURGEABLE
Behaviour fixes:
- LINE and SPLINE gain Close/Undo; AREA computes area/perimeter in f64
(correct at large coordinates)
- ATTEDIT/ATE unified so the alias and the ribbon button open the
interactive editor; -ATTEDIT is the command-line form
- Linear/aligned/radius/diameter dimensions gain a text override (Text) and
text angle (Angle) option
- INSERT gains Scale/Rotate options; COPY gains an Array option
- LINETYPE SET sets the current entity linetype; SETBYLAYER resets overrides
- UNDO accepts a step count
Integrations: standard dimension/block aliases (DLI/DRA/DAN, BMAKE,
EXPORTPDF, DDIM), drafting-aid and screen toggles (GRID/SNAP/POLAR/OSNAP/
DSETTINGS/CLEANSCREEN/QUICKPROPERTIES), viewport tiling
(CASCADE/HORIZONTAL/VERTICAL/VPJOIN), and customization entry points
(CUI/ALIASEDIT).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 19:27:35 +03:00
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_info(crate::tf!("{cmd}: not yet implemented.").as_ref());
|
feat(commands): integrate standard CAD commands and fix behavior gaps
Broad pass over the command set: integrate missing commands, wire
previously-unreachable ribbon/menu buttons, and correct name/behaviour
mismatches. Every UI command token now resolves (real handler where the
infrastructure exists, an honest "not yet implemented" status otherwise).
New commands:
- Draw: 3DPOLY, 2D solid (SO/SOLID2D), HELIX, TRACE, SKETCH, DIMCENTER
(DCE/CENTERMARK)
- Modify/inquiry: REVERSE, MEASUREGEOM (MEA), DBLIST
- Data/manage: BASE, OVERKILL (exact-duplicate removal), AUDIT (read-only
integrity report), SETVAR (system-variable get/set), SCRIPT (.scr runner),
FINDNONPURGEABLE
Behaviour fixes:
- LINE and SPLINE gain Close/Undo; AREA computes area/perimeter in f64
(correct at large coordinates)
- ATTEDIT/ATE unified so the alias and the ribbon button open the
interactive editor; -ATTEDIT is the command-line form
- Linear/aligned/radius/diameter dimensions gain a text override (Text) and
text angle (Angle) option
- INSERT gains Scale/Rotate options; COPY gains an Array option
- LINETYPE SET sets the current entity linetype; SETBYLAYER resets overrides
- UNDO accepts a step count
Integrations: standard dimension/block aliases (DLI/DRA/DAN, BMAKE,
EXPORTPDF, DDIM), drafting-aid and screen toggles (GRID/SNAP/POLAR/OSNAP/
DSETTINGS/CLEANSCREEN/QUICKPROPERTIES), viewport tiling
(CASCADE/HORIZONTAL/VERTICAL/VPJOIN), and customization entry points
(CUI/ALIASEDIT).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 19:27:35 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
_ => return None,
|
|
|
|
|
|
}
|
|
|
|
|
|
Some(self.finish_dispatch(cmd))
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
|
|
|
|
|
|
// Scan LandXML text for <CgPoint> survey points. Each element's text content is
|
|
|
|
|
|
// "northing easting elevation"; returned as [easting, northing, elevation] so it
|
|
|
|
|
|
// maps to a Point at (X=easting, Y=northing, Z=elevation). Tolerant manual scan
|
|
|
|
|
|
// (no XML dependency); handles the standard text-content form.
|
|
|
|
|
|
// (landxml cgpoint scan)
|
|
|
|
|
|
fn parse_landxml_cgpoints(xml: &str) -> Vec<[f64; 3]> {
|
|
|
|
|
|
let mut out = Vec::new();
|
|
|
|
|
|
let mut rest = xml;
|
|
|
|
|
|
while let Some(open) = rest.find("<CgPoint") {
|
|
|
|
|
|
let after = &rest[open + "<CgPoint".len()..];
|
|
|
|
|
|
// Skip the container element "<CgPoints>".
|
|
|
|
|
|
if !matches!(
|
|
|
|
|
|
after.chars().next(),
|
|
|
|
|
|
Some(' ') | Some('>') | Some('\t') | Some('\n') | Some('\r')
|
|
|
|
|
|
) {
|
|
|
|
|
|
rest = after;
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
let Some(gt) = after.find('>') else { break };
|
|
|
|
|
|
let body = &after[gt + 1..];
|
|
|
|
|
|
let Some(close) = body.find("</CgPoint>") else {
|
|
|
|
|
|
break;
|
|
|
|
|
|
};
|
|
|
|
|
|
let text = &body[..close];
|
|
|
|
|
|
let nums: Vec<f64> = text
|
|
|
|
|
|
.split_whitespace()
|
|
|
|
|
|
.filter_map(|s| s.parse().ok())
|
|
|
|
|
|
.collect();
|
|
|
|
|
|
if nums.len() >= 3 {
|
|
|
|
|
|
out.push([nums[1], nums[0], nums[2]]);
|
|
|
|
|
|
}
|
|
|
|
|
|
rest = &body[close + "</CgPoint>".len()..];
|
|
|
|
|
|
}
|
|
|
|
|
|
out
|
|
|
|
|
|
}
|