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_view(&mut self, cmd: &str, i: usize) -> Option<Task<Message>> {
|
|
|
|
|
|
match cmd {
|
|
|
|
|
|
"DONATE" => {
|
2026-08-02 22:09:25 +03:00
|
|
|
|
self.command_line.push_info(crate::t!("Opening Patreon page...").as_ref());
|
2026-07-30 21:07:31 +03:00
|
|
|
|
return Some(crate::sys::open_url(
|
|
|
|
|
|
"https://patreon.com/HakanSeven12",
|
|
|
|
|
|
self.main_window,
|
|
|
|
|
|
));
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
"WEBVERSION" => {
|
2026-08-02 22:09:25 +03:00
|
|
|
|
self.command_line.push_info(crate::t!("Opening OCS Web...").as_ref());
|
2026-07-30 21:07:31 +03:00
|
|
|
|
return Some(crate::sys::open_url(
|
|
|
|
|
|
"https://hakanseven12.github.io/OpenCADStudio/",
|
|
|
|
|
|
self.main_window,
|
|
|
|
|
|
));
|
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-07-11 10:02:08 +03:00
|
|
|
|
"HELP" => {
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_info(crate::t!("Opening OCS Discussions for help and questions...").as_ref());
|
2026-07-30 21:07:31 +03:00
|
|
|
|
return Some(crate::sys::open_url(
|
|
|
|
|
|
"https://github.com/HakanSeven12/OpenCADStudio/discussions",
|
|
|
|
|
|
self.main_window,
|
|
|
|
|
|
));
|
2026-07-11 10:02:08 +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
|
|
|
|
// ── DWGPROPS — print round-trip-only HeaderVariables ─────────
|
|
|
|
|
|
// No UI dialog for these yet; the command surfaces them so
|
|
|
|
|
|
// users can confirm the values that the parser populated and
|
|
|
|
|
|
// the writer will round-trip on save.
|
|
|
|
|
|
"DWGPROPS" | "DWGPROP" => {
|
|
|
|
|
|
let i = self.active_tab;
|
|
|
|
|
|
let h = &self.tabs[i].scene.document.header;
|
|
|
|
|
|
let path_label = self.tabs[i]
|
|
|
|
|
|
.current_path
|
|
|
|
|
|
.as_ref()
|
|
|
|
|
|
.map(|p| p.display().to_string())
|
|
|
|
|
|
.unwrap_or_else(|| "(unsaved)".to_string());
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_output(crate::tf!("Drawing: {}", path_label).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
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_output(crate::tf!(" Created (Julian): {:.6}", h.create_date_julian).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
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_output(crate::tf!(" Updated (Julian): {:.6}", h.update_date_julian).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
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_output(crate::tf!(" Total edit time: {:.4}", h.total_editing_time).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
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_output(crate::tf!(" User elapsed: {:.4}", h.user_elapsed_time).as_ref());
|
|
|
|
|
|
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
|
|
|
|
" Last saved by: {}",
|
|
|
|
|
|
if h.last_saved_by.is_empty() {
|
|
|
|
|
|
"(unknown)"
|
|
|
|
|
|
} else {
|
|
|
|
|
|
&h.last_saved_by
|
|
|
|
|
|
}
|
2026-08-02 22:09:25 +03:00
|
|
|
|
).as_ref());
|
|
|
|
|
|
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
|
|
|
|
" Fingerprint GUID: {}",
|
|
|
|
|
|
if h.fingerprint_guid.is_empty() {
|
|
|
|
|
|
"(none)"
|
|
|
|
|
|
} else {
|
|
|
|
|
|
&h.fingerprint_guid
|
|
|
|
|
|
}
|
2026-08-02 22:09:25 +03:00
|
|
|
|
).as_ref());
|
|
|
|
|
|
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
|
|
|
|
" Version GUID: {}",
|
|
|
|
|
|
if h.version_guid.is_empty() {
|
|
|
|
|
|
"(none)"
|
|
|
|
|
|
} else {
|
|
|
|
|
|
&h.version_guid
|
|
|
|
|
|
}
|
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
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_output(crate::tf!(" Code page: {}", h.code_page).as_ref());
|
|
|
|
|
|
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
|
|
|
|
" Menu name: {}",
|
|
|
|
|
|
if h.menu_name.is_empty() {
|
|
|
|
|
|
"(none)"
|
|
|
|
|
|
} else {
|
|
|
|
|
|
&h.menu_name
|
|
|
|
|
|
}
|
2026-08-02 22:09:25 +03:00
|
|
|
|
).as_ref());
|
|
|
|
|
|
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
|
|
|
|
" Hyperlink base: {}",
|
|
|
|
|
|
if h.hyperlink_base.is_empty() {
|
|
|
|
|
|
"(none)"
|
|
|
|
|
|
} else {
|
|
|
|
|
|
&h.hyperlink_base
|
|
|
|
|
|
}
|
2026-08-02 22:09:25 +03:00
|
|
|
|
).as_ref());
|
|
|
|
|
|
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
|
|
|
|
" Project name: {}",
|
|
|
|
|
|
if h.project_name.is_empty() {
|
|
|
|
|
|
"(none)"
|
|
|
|
|
|
} else {
|
|
|
|
|
|
&h.project_name
|
|
|
|
|
|
}
|
2026-08-02 22:09:25 +03:00
|
|
|
|
).as_ref());
|
|
|
|
|
|
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
|
|
|
|
" Stylesheet: {}",
|
|
|
|
|
|
if h.stylesheet.is_empty() {
|
|
|
|
|
|
"(none)"
|
|
|
|
|
|
} else {
|
|
|
|
|
|
&h.stylesheet
|
|
|
|
|
|
}
|
2026-08-02 22:09:25 +03:00
|
|
|
|
).as_ref());
|
|
|
|
|
|
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
|
|
|
|
" Required versions: {:#018x}",
|
|
|
|
|
|
h.required_versions
|
2026-08-02 22:09:25 +03:00
|
|
|
|
).as_ref());
|
|
|
|
|
|
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
|
|
|
|
" Measurement: {} ({})",
|
|
|
|
|
|
h.measurement,
|
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 h.measurement == 1 {
|
|
|
|
|
|
"Metric"
|
|
|
|
|
|
} else {
|
|
|
|
|
|
"Imperial"
|
|
|
|
|
|
}
|
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
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_output(crate::tf!(" Proxy graphics: {}", h.proxy_graphics).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_output(crate::tf!(" Tree depth: {}", h.tree_depth).as_ref());
|
|
|
|
|
|
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
|
|
|
|
" User vars (int): {} {} {} {} {}",
|
|
|
|
|
|
h.user_int1, h.user_int2, h.user_int3, h.user_int4, h.user_int5
|
2026-08-02 22:09:25 +03:00
|
|
|
|
).as_ref());
|
|
|
|
|
|
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
|
|
|
|
" User vars (real): {:.6} {:.6} {:.6} {:.6} {:.6}",
|
|
|
|
|
|
h.user_real1, h.user_real2, h.user_real3, h.user_real4, h.user_real5
|
2026-08-02 22:09:25 +03:00
|
|
|
|
).as_ref());
|
|
|
|
|
|
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
|
|
|
|
" User timer: {}",
|
|
|
|
|
|
if h.user_timer { "On" } else { "Off" }
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Edit a USERI1..USERI5 / USERR1..USERR5 slot. Lets the user
|
|
|
|
|
|
// store drawing-scoped scalars (and save them through round-trip)
|
|
|
|
|
|
// even though we don't have a LISP / DIESEL runtime yet.
|
|
|
|
|
|
// USERI 1 42 → header.user_int1 = 42
|
|
|
|
|
|
// USERR 3 1.5e-3 → header.user_real3 = 0.0015
|
2026-07-16 13:14:37 +03:00
|
|
|
|
"USERI" | "USERR" => {
|
|
|
|
|
|
use crate::command::UserRegCommand;
|
|
|
|
|
|
let name = if cmd == "USERR" { "USERR" } else { "USERI" };
|
|
|
|
|
|
let c = UserRegCommand::new(name);
|
|
|
|
|
|
self.command_line.push_info(&c.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(c));
|
|
|
|
|
|
}
|
|
|
|
|
|
cmd if cmd.starts_with("USERI ") || cmd.starts_with("USERR ") => {
|
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 is_real = cmd.starts_with("USERR");
|
|
|
|
|
|
let rest = if is_real {
|
|
|
|
|
|
cmd.trim_start_matches("USERR").trim()
|
|
|
|
|
|
} else {
|
|
|
|
|
|
cmd.trim_start_matches("USERI").trim()
|
|
|
|
|
|
};
|
|
|
|
|
|
let parts: Vec<&str> = rest.splitn(2, ' ').collect();
|
|
|
|
|
|
let slot: Option<usize> = parts.first().and_then(|s| s.parse().ok());
|
|
|
|
|
|
let value = parts.get(1).copied().unwrap_or("").trim();
|
|
|
|
|
|
let i = self.active_tab;
|
|
|
|
|
|
let h = &mut self.tabs[i].scene.document.header;
|
|
|
|
|
|
match (slot, value, is_real) {
|
|
|
|
|
|
(Some(n @ 1..=5), v, true) => {
|
|
|
|
|
|
if let Ok(val) = v.parse::<f64>() {
|
|
|
|
|
|
match n {
|
|
|
|
|
|
1 => h.user_real1 = val,
|
|
|
|
|
|
2 => h.user_real2 = val,
|
|
|
|
|
|
3 => h.user_real3 = val,
|
|
|
|
|
|
4 => h.user_real4 = val,
|
|
|
|
|
|
_ => h.user_real5 = val,
|
|
|
|
|
|
}
|
|
|
|
|
|
self.tabs[i].dirty = true;
|
2026-08-02 22:09:25 +03:00
|
|
|
|
self.command_line.push_output(crate::tf!("USERR{n} = {val}").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 {
|
2026-08-02 22:09:25 +03:00
|
|
|
|
self.command_line.push_info(crate::t!("Usage: USERR <1-5> <real>").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
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
(Some(n @ 1..=5), v, false) => {
|
|
|
|
|
|
if let Ok(val) = v.parse::<i16>() {
|
|
|
|
|
|
match n {
|
|
|
|
|
|
1 => h.user_int1 = val,
|
|
|
|
|
|
2 => h.user_int2 = val,
|
|
|
|
|
|
3 => h.user_int3 = val,
|
|
|
|
|
|
4 => h.user_int4 = val,
|
|
|
|
|
|
_ => h.user_int5 = val,
|
|
|
|
|
|
}
|
|
|
|
|
|
self.tabs[i].dirty = true;
|
2026-08-02 22:09:25 +03:00
|
|
|
|
self.command_line.push_output(crate::tf!("USERI{n} = {val}").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 {
|
2026-08-02 22:09:25 +03:00
|
|
|
|
self.command_line.push_info(crate::t!("Usage: USERI <1-5> <integer>").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: USERI <1-5> <int> | USERR <1-5> <real>").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
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
"REPORT" => {
|
|
|
|
|
|
// Pre-fill the GitHub issue body with version + platform so
|
|
|
|
|
|
// reports arrive with the basics already filled in.
|
|
|
|
|
|
let body = format!(
|
|
|
|
|
|
"<!-- Describe the issue and the steps to reproduce it. -->\n\n\n\
|
|
|
|
|
|
---\n- Open CAD Studio: v{}\n- Platform: {}\n",
|
|
|
|
|
|
env!("CARGO_PKG_VERSION"),
|
|
|
|
|
|
crate::sys::platform_info(),
|
|
|
|
|
|
);
|
|
|
|
|
|
let url = format!(
|
|
|
|
|
|
"https://github.com/HakanSeven12/OpenCADStudio/issues/new?body={}",
|
|
|
|
|
|
crate::sys::percent_encode(&body)
|
|
|
|
|
|
);
|
2026-08-02 22:09:25 +03:00
|
|
|
|
self.command_line.push_info(crate::t!("Opening feedback page...").as_ref());
|
2026-07-30 21:07:31 +03:00
|
|
|
|
return Some(crate::sys::open_url(&url, self.main_window));
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
"ABOUT" => {
|
|
|
|
|
|
return Some(Task::done(Message::AboutOpen));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
"PLUGINS" | "PLUGINMANAGER" => {
|
|
|
|
|
|
return Some(Task::done(Message::PluginManagerOpen));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
"CHANGELOG" => {
|
2026-08-02 22:09:25 +03:00
|
|
|
|
self.command_line.push_info(crate::t!("Opening release notes...").as_ref());
|
2026-07-30 21:07:31 +03:00
|
|
|
|
return Some(crate::sys::open_url(
|
|
|
|
|
|
"https://github.com/HakanSeven12/OpenCADStudio/releases",
|
|
|
|
|
|
self.main_window,
|
|
|
|
|
|
));
|
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(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
|
|
|
|
// ── ALIASEDIT — command-alias editor ───────────────────────────
|
|
|
|
|
|
// Opens the command-alias table editor (ocad.pgp). Keyboard
|
|
|
|
|
|
// key-bindings are edited separately via CUI (below).
|
|
|
|
|
|
"ALIASEDIT" => {
|
|
|
|
|
|
return Some(Task::done(Message::AliasEditorOpen));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ── CUI — keyboard shortcut / key-binding editor ───────────────
|
|
|
|
|
|
"CUI" => {
|
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::ShortcutsPanelOpen));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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
|
|
|
|
// CUIEXPORT <path> — write the keyboard-shortcut customizations
|
|
|
|
|
|
// (the drawing-independent CUI data) to a plain "KEY COMMAND" file.
|
2026-07-16 14:40:11 +03:00
|
|
|
|
"CUIEXPORT" => {
|
|
|
|
|
|
use crate::command::ValuePromptCommand;
|
|
|
|
|
|
let c = ValuePromptCommand::new("CUIEXPORT", "CUIEXPORT file to save shortcuts to:");
|
|
|
|
|
|
self.command_line.push_info(&c.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(c));
|
|
|
|
|
|
}
|
|
|
|
|
|
cmd if cmd.starts_with("CUIEXPORT ") => {
|
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("CUIEXPORT").trim();
|
|
|
|
|
|
if path.is_empty() {
|
|
|
|
|
|
self.command_line.push_info(
|
|
|
|
|
|
"Usage: CUIEXPORT <path> — save the keyboard shortcuts to a file.",
|
|
|
|
|
|
);
|
|
|
|
|
|
return Some(Task::none());
|
|
|
|
|
|
}
|
|
|
|
|
|
let mut keys: Vec<(&String, &String)> = self.shortcut_overrides.iter().collect();
|
|
|
|
|
|
keys.sort_by(|a, b| a.0.cmp(b.0));
|
|
|
|
|
|
let text: String = keys.iter().map(|(k, v)| format!("{k} {v}\n")).collect();
|
|
|
|
|
|
let count = self.shortcut_overrides.len();
|
|
|
|
|
|
match std::fs::write(path, text) {
|
2026-08-02 22:09:25 +03:00
|
|
|
|
Ok(()) => 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
|
|
|
|
"CUIEXPORT: wrote {count} shortcut(s) to \"{path}\"."
|
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!("CUIEXPORT: cannot write \"{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
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// CUIIMPORT / CUILOAD <path> — load shortcut customizations from a
|
|
|
|
|
|
// "KEY COMMAND" file (lines starting with # are ignored).
|
2026-07-16 14:40:11 +03:00
|
|
|
|
"CUIIMPORT" | "CUILOAD" => {
|
|
|
|
|
|
use crate::command::ValuePromptCommand;
|
|
|
|
|
|
let c = ValuePromptCommand::new("CUIIMPORT", "CUIIMPORT shortcuts file to load:");
|
|
|
|
|
|
self.command_line.push_info(&c.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(c));
|
|
|
|
|
|
}
|
|
|
|
|
|
cmd if cmd.starts_with("CUIIMPORT ") || cmd.starts_with("CUILOAD ") => {
|
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("CUIIMPORT")
|
|
|
|
|
|
.trim_start_matches("CUILOAD")
|
|
|
|
|
|
.trim();
|
|
|
|
|
|
if path.is_empty() {
|
|
|
|
|
|
self.command_line.push_info(
|
|
|
|
|
|
"Usage: CUIIMPORT <path> — load keyboard shortcuts from a file.",
|
|
|
|
|
|
);
|
|
|
|
|
|
return Some(Task::none());
|
|
|
|
|
|
}
|
|
|
|
|
|
match std::fs::read_to_string(path) {
|
|
|
|
|
|
Ok(text) => {
|
|
|
|
|
|
let mut n = 0usize;
|
|
|
|
|
|
for line in text.lines() {
|
|
|
|
|
|
let line = line.trim();
|
|
|
|
|
|
if line.is_empty() || line.starts_with('#') {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
if let Some((k, v)) = line.split_once(char::is_whitespace) {
|
|
|
|
|
|
self.shortcut_overrides
|
|
|
|
|
|
.insert(k.trim().to_uppercase(), v.trim().to_uppercase());
|
|
|
|
|
|
n += 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
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
|
|
|
|
"CUIIMPORT: loaded {n} shortcut(s) from \"{path}\"."
|
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!("CUIIMPORT: 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
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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
|
|
|
|
// ── Keyboard Shortcuts panel ──────────────────────────────────
|
|
|
|
|
|
cmd if cmd == "SHORTCUTS" || cmd.starts_with("SHORTCUTS ") => {
|
|
|
|
|
|
let raw_rest = cmd.trim_start_matches("SHORTCUTS").trim();
|
|
|
|
|
|
let parts: Vec<&str> = raw_rest.splitn(3, ' ').collect();
|
|
|
|
|
|
let sub = parts.first().map(|s| s.to_uppercase()).unwrap_or_default();
|
|
|
|
|
|
match sub.as_str() {
|
|
|
|
|
|
"" | "LIST" | "?" => {
|
|
|
|
|
|
return Some(Task::done(Message::ShortcutsPanelOpen));
|
|
|
|
|
|
}
|
|
|
|
|
|
"SET" | "S" => {
|
|
|
|
|
|
// SHORTCUTS SET <key> <command>
|
|
|
|
|
|
// e.g. SHORTCUTS SET CTRL+D DIST
|
|
|
|
|
|
let key = parts.get(1).map(|s| s.to_uppercase()).unwrap_or_default();
|
|
|
|
|
|
let cmd_str = parts.get(2).map(|s| s.to_uppercase()).unwrap_or_default();
|
|
|
|
|
|
if key.is_empty() || cmd_str.is_empty() {
|
2026-08-02 22:09:25 +03:00
|
|
|
|
self.command_line.push_error(crate::t!("Usage: SHORTCUTS SET <key> <command> e.g. SHORTCUTS SET CTRL+D DIST").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.shortcut_overrides.insert(key.clone(), cmd_str.clone());
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_output(crate::tf!("Shortcut set: {key} → {cmd_str}").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" | "DELETE" | "REMOVE" => {
|
|
|
|
|
|
let key = parts.get(1).map(|s| s.to_uppercase()).unwrap_or_default();
|
|
|
|
|
|
if key.is_empty() {
|
2026-08-02 22:09:25 +03:00
|
|
|
|
self.command_line.push_error(crate::t!("Usage: SHORTCUTS CLEAR <key>").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 self.shortcut_overrides.remove(&key).is_some() {
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_output(crate::tf!("Shortcut '{key}' removed.").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
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_error(crate::tf!("Shortcut '{key}' not found.").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: SHORTCUTS LIST | SET <key> <cmd> | CLEAR <key>").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
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ── Color Scheme / Theme selector ─────────────────────────────
|
|
|
|
|
|
cmd if cmd == "COLORSCHEME" || cmd.starts_with("COLORSCHEME ") => {
|
|
|
|
|
|
use iced::Theme;
|
|
|
|
|
|
let sub = cmd
|
|
|
|
|
|
.split_once(' ')
|
|
|
|
|
|
.map(|(_, r)| r.trim())
|
|
|
|
|
|
.unwrap_or("")
|
|
|
|
|
|
.to_uppercase();
|
|
|
|
|
|
// Map name to Theme variant.
|
|
|
|
|
|
let theme: Option<Theme> = match sub.as_str() {
|
|
|
|
|
|
"DARK" => Some(Theme::Dark),
|
|
|
|
|
|
"LIGHT" => Some(Theme::Light),
|
|
|
|
|
|
"DRACULA" => Some(Theme::Dracula),
|
|
|
|
|
|
"NORD" => Some(Theme::Nord),
|
|
|
|
|
|
"SOLARIZED_LIGHT" | "SOLARIZEDLIGHT" => Some(Theme::SolarizedLight),
|
|
|
|
|
|
"SOLARIZED_DARK" | "SOLARIZEDDARK" => Some(Theme::SolarizedDark),
|
|
|
|
|
|
"GRUVBOX_LIGHT" | "GRUVBOXLIGHT" => Some(Theme::GruvboxLight),
|
|
|
|
|
|
"GRUVBOX_DARK" | "GRUVBOXDARK" => Some(Theme::GruvboxDark),
|
|
|
|
|
|
"TOKYONIGHT" | "TOKYO_NIGHT" => Some(Theme::TokyoNight),
|
|
|
|
|
|
"TOKYONIGHTSTORM" | "TOKYO_NIGHT_STORM" => Some(Theme::TokyoNightStorm),
|
|
|
|
|
|
"TOKYONIGHTLIGHT" | "TOKYO_NIGHT_LIGHT" => Some(Theme::TokyoNightLight),
|
|
|
|
|
|
"KANAGAWAWAVE" | "KANAGAWA_WAVE" => Some(Theme::KanagawaWave),
|
|
|
|
|
|
"KANAGAWADRAGON" | "KANAGAWA_DRAGON" => Some(Theme::KanagawaDragon),
|
|
|
|
|
|
"KANAGAWALOTUS" | "KANAGAWA_LOTUS" => Some(Theme::KanagawaLotus),
|
|
|
|
|
|
"MOONFLY" => Some(Theme::Moonfly),
|
|
|
|
|
|
"NIGHTFLY" => Some(Theme::Nightfly),
|
|
|
|
|
|
"OXOCARBON" => Some(Theme::Oxocarbon),
|
|
|
|
|
|
"FERRA" => Some(Theme::Ferra),
|
|
|
|
|
|
"" | "LIST" | "?" => {
|
|
|
|
|
|
self.command_line.push_output(
|
|
|
|
|
|
"Available themes: DARK LIGHT DRACULA NORD SOLARIZED_LIGHT SOLARIZED_DARK \
|
|
|
|
|
|
GRUVBOX_LIGHT GRUVBOX_DARK TOKYONIGHT TOKYONIGHTSTORM TOKYONIGHTLIGHT \
|
|
|
|
|
|
KANAGAWAWAVE KANAGAWADRAGON KANAGAWALOTUS MOONFLY NIGHTFLY OXOCARBON FERRA"
|
|
|
|
|
|
);
|
|
|
|
|
|
return Some(Task::none());
|
|
|
|
|
|
}
|
|
|
|
|
|
_ => {
|
2026-08-02 22:09:25 +03:00
|
|
|
|
self.command_line.push_error(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
|
|
|
|
"COLORSCHEME: unknown theme '{}'. Type COLORSCHEME LIST for options.",
|
|
|
|
|
|
sub
|
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
|
|
|
|
return Some(Task::none());
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
if let Some(t) = theme {
|
|
|
|
|
|
let name = format!("{:?}", t);
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_output(crate::tf!("Color scheme set to '{name}'.").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
|
|
|
|
return Some(Task::done(Message::SetTheme(t)));
|
|
|
|
|
|
}
|
|
|
|
|
|
return Some(Task::none());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ── Layout Manager GUI ─────────────────────────────────────────
|
|
|
|
|
|
"LAYOUTMANAGER" | "LAYOUTPANEL" => {
|
|
|
|
|
|
return Some(Task::done(Message::LayoutManagerOpen));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ── Layout / viewport ──────────────────────────────────────────
|
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
|
|
|
|
"MVIEW" => {
|
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 self.tabs[i].scene.current_layout == "Model" {
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_error(crate::t!("MVIEW: switch to a paper space layout 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 {
|
|
|
|
|
|
use crate::modules::layout::mview::MviewCommand;
|
2026-07-27 19:28:36 +03:00
|
|
|
|
let scene = &self.tabs[i].scene;
|
|
|
|
|
|
let layout = scene.current_layout.clone();
|
|
|
|
|
|
let paper_bounds = scene
|
|
|
|
|
|
.printable_area_limits()
|
|
|
|
|
|
.or_else(|| scene.paper_limits())
|
|
|
|
|
|
.unwrap_or(((0.0, 0.0), (297.0, 210.0)));
|
|
|
|
|
|
let views = scene.document.views.iter().cloned().collect();
|
|
|
|
|
|
let new_cmd = MviewCommand::new(layout, paper_bounds, views);
|
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.push_info(&new_cmd.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ── MSPACE / PSPACE ───────────────────────────────────────────
|
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
|
|
|
|
"MSPACE" => {
|
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::MspaceCommand));
|
|
|
|
|
|
}
|
|
|
|
|
|
"PSPACE" => {
|
|
|
|
|
|
return Some(Task::done(Message::PspaceCommand));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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
|
|
|
|
// ── Viewport arrangement shortcuts ────────────────────────────
|
|
|
|
|
|
// Tile the model viewports into preset splits. Each delegates to the
|
|
|
|
|
|
// matching VPORTS configuration so the Model/paper handling stays in
|
|
|
|
|
|
// one place.
|
|
|
|
|
|
"HORIZONTAL" => return self.dispatch_view("VPORTS 2H", i),
|
|
|
|
|
|
"VERTICAL" => return self.dispatch_view("VPORTS 2V", i),
|
|
|
|
|
|
"VPJOIN" => return self.dispatch_view("VPORTS SINGLE", i),
|
|
|
|
|
|
"CASCADE" => return self.dispatch_view("VPORTS 4", 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
|
|
|
|
// ── VPORTS — list or create preset viewport configurations ────
|
|
|
|
|
|
cmd if cmd == "VPORTS" || cmd.starts_with("VPORTS ") => {
|
|
|
|
|
|
let sub = cmd.split_whitespace().nth(1).unwrap_or("").to_uppercase();
|
|
|
|
|
|
let scene = &self.tabs[i].scene;
|
|
|
|
|
|
if scene.current_layout == "Model" {
|
|
|
|
|
|
// Bare VPORTS → ask for the configuration interactively;
|
|
|
|
|
|
// the next command-line entry supplies it.
|
|
|
|
|
|
if sub.is_empty() {
|
|
|
|
|
|
self.awaiting_vports = true;
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_info(crate::t!("VPORTS Configuration [SIngle/2H/2V/4]:").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
|
|
|
|
return Some(self.focus_cmd_input());
|
|
|
|
|
|
}
|
2026-06-27 11:50:11 +03:00
|
|
|
|
// Model space: split the tiled viewport layout via pane_grid.
|
|
|
|
|
|
use iced::widget::pane_grid::{Axis, Configuration as C};
|
|
|
|
|
|
let split = |axis, a, b| C::Split {
|
|
|
|
|
|
axis,
|
|
|
|
|
|
ratio: 0.5,
|
|
|
|
|
|
a: Box::new(a),
|
|
|
|
|
|
b: Box::new(b),
|
|
|
|
|
|
};
|
|
|
|
|
|
let config: Option<(C<usize>, usize)> = match sub.as_str() {
|
|
|
|
|
|
"SINGLE" | "SI" | "1" => Some((C::Pane(0), 1)),
|
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
|
|
|
|
"2H" | "2" => Some((split(Axis::Horizontal, C::Pane(0), C::Pane(1)), 2)),
|
2026-06-27 11:50:11 +03:00
|
|
|
|
"2V" => Some((split(Axis::Vertical, C::Pane(0), C::Pane(1)), 2)),
|
|
|
|
|
|
"4" => Some((
|
|
|
|
|
|
split(
|
|
|
|
|
|
Axis::Vertical,
|
|
|
|
|
|
split(Axis::Horizontal, C::Pane(0), C::Pane(2)),
|
|
|
|
|
|
split(Axis::Horizontal, C::Pane(1), C::Pane(3)),
|
|
|
|
|
|
),
|
|
|
|
|
|
4,
|
|
|
|
|
|
)),
|
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
|
|
|
|
_ => None,
|
|
|
|
|
|
};
|
2026-06-27 11:50:11 +03:00
|
|
|
|
match config {
|
|
|
|
|
|
Some((config, n)) => {
|
|
|
|
|
|
self.tabs[i].scene.set_model_panes(config);
|
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].scene.camera_generation += 1;
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_output(crate::tf!("VPORTS: {n} viewport(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
|
|
|
|
}
|
|
|
|
|
|
None => {
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_error(crate::t!("VPORTS: use SINGLE | 2H | 2V | 4.").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 sub.is_empty() {
|
|
|
|
|
|
// ── List existing viewports ──────────────────────────
|
|
|
|
|
|
let layout_block = scene.current_layout_block_handle_pub();
|
|
|
|
|
|
let viewports: Vec<_> = scene
|
|
|
|
|
|
.document
|
|
|
|
|
|
.entities()
|
|
|
|
|
|
.filter_map(|e| {
|
|
|
|
|
|
if let acadrust::EntityType::Viewport(vp) = e {
|
|
|
|
|
|
if vp.id > 1 && vp.common.owner_handle == layout_block {
|
|
|
|
|
|
Some((
|
|
|
|
|
|
vp.id,
|
|
|
|
|
|
vp.center.clone(),
|
|
|
|
|
|
vp.width,
|
|
|
|
|
|
vp.height,
|
|
|
|
|
|
crate::scene::vp_effective_scale(
|
|
|
|
|
|
vp.custom_scale,
|
|
|
|
|
|
vp.view_height,
|
|
|
|
|
|
vp.height,
|
|
|
|
|
|
),
|
|
|
|
|
|
vp.status.is_on,
|
|
|
|
|
|
vp.status.locked,
|
|
|
|
|
|
))
|
|
|
|
|
|
} else {
|
|
|
|
|
|
None
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
None
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
.collect();
|
|
|
|
|
|
if viewports.is_empty() {
|
2026-08-02 22:09:25 +03:00
|
|
|
|
self.command_line.push_info(crate::t!("No viewports. Use MVIEW to create one, or VPORTS 2H / 2V / 4 / SINGLE.").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 {
|
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
|
|
|
|
"{} viewport(s) in layout \"{}\":",
|
|
|
|
|
|
viewports.len(),
|
|
|
|
|
|
scene.current_layout
|
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 (id, center, w, h, scale, is_on, locked) in &viewports {
|
|
|
|
|
|
let state = match (is_on, locked) {
|
|
|
|
|
|
(true, true) => "On, Locked",
|
|
|
|
|
|
(true, false) => "On",
|
|
|
|
|
|
(false, _) => "Off",
|
|
|
|
|
|
};
|
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
|
|
|
|
" VP #{id}: {w:.1}×{h:.1} @ ({:.1},{:.1}) scale={scale:.4} [{state}]",
|
|
|
|
|
|
center.x, center.y
|
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 {
|
|
|
|
|
|
// ── Preset viewport layout ───────────────────────────
|
|
|
|
|
|
// Determine paper dimensions from PlotSettings (fallback A4 landscape).
|
|
|
|
|
|
let layout_name = scene.current_layout.clone();
|
|
|
|
|
|
let (paper_w, paper_h) = {
|
|
|
|
|
|
use acadrust::objects::ObjectType;
|
|
|
|
|
|
let mut pw = 297.0_f64;
|
|
|
|
|
|
let mut ph = 210.0_f64;
|
|
|
|
|
|
for (_, obj) in &scene.document.objects {
|
|
|
|
|
|
if let ObjectType::PlotSettings(ps) = obj {
|
|
|
|
|
|
if ps.page_name == layout_name && ps.paper_width > 0.0 {
|
|
|
|
|
|
pw = ps.paper_width;
|
|
|
|
|
|
ph = ps.paper_height;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
(pw, ph)
|
|
|
|
|
|
};
|
|
|
|
|
|
let margin = 5.0_f64; // mm margin around the usable area
|
|
|
|
|
|
let uw = paper_w - 2.0 * margin; // usable width
|
|
|
|
|
|
let uh = paper_h - 2.0 * margin; // usable height
|
|
|
|
|
|
// Collect rectangle specs: (cx, cz, w, h) in mm
|
|
|
|
|
|
let rects: Vec<(f64, f64, f64, f64)> = match sub.as_str() {
|
|
|
|
|
|
"2H" => {
|
|
|
|
|
|
// Two viewports side by side (horizontal split)
|
|
|
|
|
|
let vw = (uw - 2.0) / 2.0;
|
|
|
|
|
|
vec![
|
|
|
|
|
|
(margin + vw / 2.0, margin + uh / 2.0, vw, uh),
|
|
|
|
|
|
(margin + vw + 2.0 + vw / 2.0, margin + uh / 2.0, vw, uh),
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
"2V" => {
|
|
|
|
|
|
// Two viewports stacked (vertical split)
|
|
|
|
|
|
let vh = (uh - 2.0) / 2.0;
|
|
|
|
|
|
vec![
|
|
|
|
|
|
(margin + uw / 2.0, margin + vh + 2.0 + vh / 2.0, uw, vh),
|
|
|
|
|
|
(margin + uw / 2.0, margin + vh / 2.0, uw, vh),
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
"4" => {
|
|
|
|
|
|
// Four equal viewports (2×2 grid)
|
|
|
|
|
|
let vw = (uw - 2.0) / 2.0;
|
|
|
|
|
|
let vh = (uh - 2.0) / 2.0;
|
|
|
|
|
|
vec![
|
|
|
|
|
|
(margin + vw / 2.0, margin + vh + 2.0 + vh / 2.0, vw, vh),
|
|
|
|
|
|
(
|
|
|
|
|
|
margin + vw + 2.0 + vw / 2.0,
|
|
|
|
|
|
margin + vh + 2.0 + vh / 2.0,
|
|
|
|
|
|
vw,
|
|
|
|
|
|
vh,
|
|
|
|
|
|
),
|
|
|
|
|
|
(margin + vw / 2.0, margin + vh / 2.0, vw, vh),
|
|
|
|
|
|
(margin + vw + 2.0 + vw / 2.0, margin + vh / 2.0, vw, vh),
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
"SINGLE" | "1" => {
|
|
|
|
|
|
// Single full-page viewport
|
|
|
|
|
|
vec![(margin + uw / 2.0, margin + uh / 2.0, uw, uh)]
|
|
|
|
|
|
}
|
|
|
|
|
|
_ => {
|
|
|
|
|
|
self.command_line.push_error(
|
|
|
|
|
|
"VPORTS: unknown option. Use VPORTS 2H | 2V | 4 | SINGLE",
|
|
|
|
|
|
);
|
|
|
|
|
|
vec![]
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
if !rects.is_empty() {
|
|
|
|
|
|
// Remove existing user viewports in this layout first.
|
|
|
|
|
|
let layout_block = self.tabs[i].scene.current_layout_block_handle_pub();
|
|
|
|
|
|
let to_erase: Vec<acadrust::Handle> = self.tabs[i]
|
|
|
|
|
|
.scene
|
|
|
|
|
|
.document
|
|
|
|
|
|
.entities()
|
|
|
|
|
|
.filter_map(|e| {
|
|
|
|
|
|
if let acadrust::EntityType::Viewport(vp) = e {
|
|
|
|
|
|
if vp.id > 1 && vp.common.owner_handle == layout_block {
|
|
|
|
|
|
Some(vp.common.handle)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
None
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
None
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
.collect();
|
|
|
|
|
|
self.push_undo_snapshot(i, "VPORTS");
|
|
|
|
|
|
self.tabs[i].scene.erase_entities(&to_erase);
|
|
|
|
|
|
// Create new viewports.
|
|
|
|
|
|
for (cx, cz, w, h) in &rects {
|
|
|
|
|
|
let mut vp = acadrust::entities::Viewport::new();
|
|
|
|
|
|
vp.center = acadrust::types::Vector3::new(*cx, 0.0, *cz);
|
|
|
|
|
|
vp.width = *w;
|
|
|
|
|
|
vp.height = *h;
|
|
|
|
|
|
vp.id = 2; // commit_entity will assign unique IDs
|
|
|
|
|
|
match self.tabs[i].scene.document.add_entity_to_layout(
|
|
|
|
|
|
acadrust::EntityType::Viewport(vp),
|
|
|
|
|
|
&layout_name,
|
|
|
|
|
|
) {
|
|
|
|
|
|
Ok(handle) => {
|
|
|
|
|
|
self.tabs[i].scene.auto_fit_viewport(handle);
|
|
|
|
|
|
}
|
|
|
|
|
|
Err(e) => {
|
2026-08-02 22:09:25 +03:00
|
|
|
|
self.command_line.push_error(crate::tf!("VPORTS: {e}").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
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// Re-assign unique IDs (1 + existing max per viewport).
|
|
|
|
|
|
let layout_block2 = self.tabs[i].scene.current_layout_block_handle_pub();
|
|
|
|
|
|
let mut id_counter = 2_i16;
|
|
|
|
|
|
let handles: Vec<acadrust::Handle> = self.tabs[i]
|
|
|
|
|
|
.scene
|
|
|
|
|
|
.document
|
|
|
|
|
|
.entities()
|
|
|
|
|
|
.filter_map(|e| {
|
|
|
|
|
|
if let acadrust::EntityType::Viewport(vp) = e {
|
|
|
|
|
|
if vp.id >= 2 && vp.common.owner_handle == layout_block2 {
|
|
|
|
|
|
Some(vp.common.handle)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
None
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
None
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
.collect();
|
|
|
|
|
|
for h in handles {
|
|
|
|
|
|
if let Some(acadrust::EntityType::Viewport(vp)) =
|
|
|
|
|
|
self.tabs[i].scene.document.get_entity_mut(h)
|
|
|
|
|
|
{
|
|
|
|
|
|
vp.id = id_counter;
|
|
|
|
|
|
id_counter += 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
|
|
|
|
"VPORTS: created {} viewport(s) [{}].",
|
|
|
|
|
|
rects.len(),
|
|
|
|
|
|
sub
|
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
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ── VPLAYER — per-viewport layer freeze/thaw ──────────────────
|
|
|
|
|
|
"VPLAYER" => {
|
|
|
|
|
|
let scene = &self.tabs[i].scene;
|
|
|
|
|
|
if scene.current_layout == "Model" {
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_error(crate::t!("VPLAYER: switch to a paper space layout 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 if scene.active_viewport.is_none() {
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_error(crate::t!("VPLAYER: enter a viewport first (double-click or MS).").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 {
|
|
|
|
|
|
use crate::modules::layout::vplayer::VplayerCommand;
|
|
|
|
|
|
let vp_handle = scene.active_viewport.unwrap();
|
|
|
|
|
|
// Collect current frozen layer names for display.
|
|
|
|
|
|
let frozen_names: Vec<String> = {
|
|
|
|
|
|
if let Some(acadrust::EntityType::Viewport(vp)) =
|
|
|
|
|
|
scene.document.get_entity(vp_handle)
|
|
|
|
|
|
{
|
|
|
|
|
|
vp.frozen_layers
|
|
|
|
|
|
.iter()
|
|
|
|
|
|
.filter_map(|h| {
|
|
|
|
|
|
scene
|
|
|
|
|
|
.document
|
|
|
|
|
|
.layers
|
|
|
|
|
|
.iter()
|
|
|
|
|
|
.find(|l| l.handle == *h)
|
|
|
|
|
|
.map(|l| l.name.clone())
|
|
|
|
|
|
})
|
|
|
|
|
|
.collect()
|
|
|
|
|
|
} else {
|
|
|
|
|
|
vec![]
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
if frozen_names.is_empty() {
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_info(crate::t!("VPLAYER: no frozen layers in active viewport.").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 {
|
2026-08-02 22:09:25 +03:00
|
|
|
|
self.command_line.push_info(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
|
|
|
|
"VPLAYER: frozen layers: {}",
|
|
|
|
|
|
frozen_names.join(", ")
|
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
|
|
|
|
}
|
|
|
|
|
|
let new_cmd = VplayerCommand::new(vp_handle);
|
|
|
|
|
|
self.command_line.push_info(&new_cmd.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ── Draw Order ────────────────────────────────────────────────
|
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
|
|
|
|
// TEXTTOFRONT / TEXTTOBACK — move every text and dimension object to
|
|
|
|
|
|
// the front (or back) of the draw order via the DRAWORDER machinery.
|
|
|
|
|
|
"TEXTTOFRONT" | "TEXTTOBACK" => {
|
|
|
|
|
|
let to_front = cmd.ends_with("FRONT");
|
|
|
|
|
|
let handles: rustc_hash::FxHashSet<acadrust::Handle> = self.tabs[i]
|
|
|
|
|
|
.scene
|
|
|
|
|
|
.document
|
|
|
|
|
|
.entities()
|
|
|
|
|
|
.filter(|e| {
|
|
|
|
|
|
matches!(
|
|
|
|
|
|
e,
|
|
|
|
|
|
acadrust::EntityType::Text(_)
|
|
|
|
|
|
| acadrust::EntityType::MText(_)
|
|
|
|
|
|
| acadrust::EntityType::Dimension(_)
|
|
|
|
|
|
)
|
|
|
|
|
|
})
|
|
|
|
|
|
.map(|e| e.common().handle)
|
|
|
|
|
|
.collect();
|
|
|
|
|
|
if handles.is_empty() {
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_info(crate::tf!("{cmd}: no text or dimension objects.").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-07-28 17:35:21 +03:00
|
|
|
|
self.tabs[i].scene.replace_selection(handles);
|
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 self.dispatch_view(
|
|
|
|
|
|
if to_front {
|
|
|
|
|
|
"DRAWORDER FRONT"
|
|
|
|
|
|
} else {
|
|
|
|
|
|
"DRAWORDER BACK"
|
|
|
|
|
|
},
|
|
|
|
|
|
i,
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-16 13:55:49 +03:00
|
|
|
|
"DRAWORDER" => {
|
|
|
|
|
|
use crate::command::SelectThenKeywordCommand;
|
|
|
|
|
|
let has_sel = !self.tabs[i].scene.selected_entities().is_empty();
|
|
|
|
|
|
let c = SelectThenKeywordCommand::new(
|
|
|
|
|
|
"DRAWORDER",
|
|
|
|
|
|
"DRAWORDER [Front / Back] (Above/Under <handle> by typing):",
|
|
|
|
|
|
vec![("Front", "FRONT", None), ("Back", "BACK", None)],
|
|
|
|
|
|
has_sel,
|
|
|
|
|
|
);
|
|
|
|
|
|
self.command_line.push_info(&c.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(c));
|
|
|
|
|
|
}
|
|
|
|
|
|
cmd if cmd.starts_with("DRAWORDER ") => {
|
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::objects::{ObjectType, SortEntitiesTable};
|
|
|
|
|
|
let parts: Vec<&str> = cmd.split_whitespace().collect();
|
|
|
|
|
|
let option = parts.get(1).unwrap_or(&"").to_uppercase();
|
|
|
|
|
|
let i = self.active_tab;
|
|
|
|
|
|
let selected: Vec<acadrust::Handle> = self.tabs[i]
|
|
|
|
|
|
.scene
|
|
|
|
|
|
.selected_entities()
|
|
|
|
|
|
.iter()
|
|
|
|
|
|
.map(|(h, _)| *h)
|
|
|
|
|
|
.collect();
|
|
|
|
|
|
if selected.is_empty() {
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_error(crate::t!("DRAWORDER: 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 {
|
|
|
|
|
|
// Parse relative target handle for ABOVE/UNDER.
|
|
|
|
|
|
let relative_target: Option<(bool, acadrust::Handle)> = match option.as_str() {
|
|
|
|
|
|
"A" | "ABOVE" => {
|
|
|
|
|
|
let h_val = parts.get(2).and_then(|s| u64::from_str_radix(s, 16).ok());
|
|
|
|
|
|
h_val.map(|v| (true, acadrust::Handle::new(v)))
|
|
|
|
|
|
}
|
|
|
|
|
|
"U" | "UNDER" | "BELOW" => {
|
|
|
|
|
|
let h_val = parts.get(2).and_then(|s| u64::from_str_radix(s, 16).ok());
|
|
|
|
|
|
h_val.map(|v| (false, acadrust::Handle::new(v)))
|
|
|
|
|
|
}
|
|
|
|
|
|
_ => None,
|
|
|
|
|
|
};
|
|
|
|
|
|
let to_front_opt = match option.as_str() {
|
|
|
|
|
|
"F" | "FRONT" => Some(true),
|
|
|
|
|
|
"B" | "BACK" => Some(false),
|
|
|
|
|
|
_ => None,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
if relative_target.is_some() || to_front_opt.is_some() {
|
|
|
|
|
|
self.push_undo_snapshot(i, "DRAWORDER");
|
|
|
|
|
|
let block_handle = self.tabs[i].scene.current_layout_block_handle_pub();
|
|
|
|
|
|
|
|
|
|
|
|
// For FRONT/BACK, anchor the new sort handle to the
|
|
|
|
|
|
// block's current effective draw-order range so the moved
|
|
|
|
|
|
// entities land strictly above/below every sibling —
|
|
|
|
|
|
// including ones not yet in the table, which sort by
|
|
|
|
|
|
// their own handle. (min_eff, max_eff) over siblings.
|
|
|
|
|
|
let fb_baseline: Option<(u64, u64)> = if to_front_opt.is_some() {
|
|
|
|
|
|
let selected_set: rustc_hash::FxHashSet<u64> =
|
|
|
|
|
|
selected.iter().map(|h| h.value()).collect();
|
|
|
|
|
|
let doc_ref = &self.tabs[i].scene.document;
|
|
|
|
|
|
let overrides: rustc_hash::FxHashMap<u64, u64> = doc_ref
|
|
|
|
|
|
.objects
|
|
|
|
|
|
.values()
|
|
|
|
|
|
.find_map(|obj| {
|
|
|
|
|
|
if let ObjectType::SortEntitiesTable(t) = obj {
|
|
|
|
|
|
if t.block_owner_handle == block_handle {
|
|
|
|
|
|
return Some(
|
|
|
|
|
|
t.entries()
|
|
|
|
|
|
.map(|e| {
|
|
|
|
|
|
(
|
|
|
|
|
|
e.entity_handle.value(),
|
|
|
|
|
|
e.sort_handle.value(),
|
|
|
|
|
|
)
|
|
|
|
|
|
})
|
|
|
|
|
|
.collect(),
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
None
|
|
|
|
|
|
})
|
|
|
|
|
|
.unwrap_or_default();
|
|
|
|
|
|
let mut max_eff = 0u64;
|
|
|
|
|
|
let mut min_eff = u64::MAX;
|
|
|
|
|
|
for e in doc_ref.entities() {
|
|
|
|
|
|
let c = e.common();
|
|
|
|
|
|
let hv = c.handle.value();
|
|
|
|
|
|
if selected_set.contains(&hv) {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
if c.owner_handle != block_handle && !c.owner_handle.is_null() {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
let eff = overrides.get(&hv).copied().unwrap_or(hv);
|
|
|
|
|
|
max_eff = max_eff.max(eff);
|
|
|
|
|
|
min_eff = min_eff.min(eff);
|
|
|
|
|
|
}
|
|
|
|
|
|
if min_eff == u64::MAX {
|
|
|
|
|
|
min_eff = 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
Some((min_eff, max_eff))
|
|
|
|
|
|
} else {
|
|
|
|
|
|
None
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
let doc = &mut self.tabs[i].scene.document;
|
|
|
|
|
|
let table_handle = doc.objects.iter().find_map(|(h, obj)| {
|
|
|
|
|
|
if let ObjectType::SortEntitiesTable(t) = obj {
|
|
|
|
|
|
if t.block_owner_handle == block_handle {
|
|
|
|
|
|
Some(*h)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
None
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
None
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
let get_or_create =
|
|
|
|
|
|
|doc: &mut acadrust::CadDocument, block_handle| -> acadrust::Handle {
|
|
|
|
|
|
if let Some(th) = doc.objects.iter().find_map(|(h, obj)| {
|
|
|
|
|
|
if let ObjectType::SortEntitiesTable(t) = obj {
|
|
|
|
|
|
if t.block_owner_handle == block_handle {
|
|
|
|
|
|
Some(*h)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
None
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
None
|
|
|
|
|
|
}
|
|
|
|
|
|
}) {
|
|
|
|
|
|
th
|
|
|
|
|
|
} else {
|
|
|
|
|
|
let nh = acadrust::Handle::new(doc.next_handle());
|
|
|
|
|
|
let mut table = SortEntitiesTable::for_block(block_handle);
|
|
|
|
|
|
table.handle = nh;
|
|
|
|
|
|
doc.objects.insert(nh, ObjectType::SortEntitiesTable(table));
|
|
|
|
|
|
nh
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
let th = table_handle.unwrap_or_else(|| {
|
|
|
|
|
|
let nh = acadrust::Handle::new(doc.next_handle());
|
|
|
|
|
|
let mut table = SortEntitiesTable::for_block(block_handle);
|
|
|
|
|
|
table.handle = nh;
|
|
|
|
|
|
doc.objects.insert(nh, ObjectType::SortEntitiesTable(table));
|
|
|
|
|
|
nh
|
|
|
|
|
|
});
|
|
|
|
|
|
let _ = get_or_create; // suppress unused warning
|
|
|
|
|
|
if let Some(ObjectType::SortEntitiesTable(table)) = doc.objects.get_mut(&th)
|
|
|
|
|
|
{
|
|
|
|
|
|
if let Some((above, target)) = relative_target {
|
|
|
|
|
|
// move_above/move_below read the target's sort
|
|
|
|
|
|
// handle from the table and no-op when it is
|
|
|
|
|
|
// absent. A reference object that was never
|
|
|
|
|
|
// reordered isn't in the table yet, so seed it
|
|
|
|
|
|
// with its own handle as the implicit sort key.
|
|
|
|
|
|
if !table.contains(target) {
|
|
|
|
|
|
table.add_entry(target, target);
|
|
|
|
|
|
}
|
|
|
|
|
|
for h in &selected {
|
|
|
|
|
|
if above {
|
|
|
|
|
|
table.move_above(*h, target);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
table.move_below(*h, target);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
let rel = if above { "above" } else { "below" };
|
2026-08-02 22:09:25 +03:00
|
|
|
|
self.command_line.push_info(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
|
|
|
|
"DRAWORDER: moved {} entities {} {:x}.",
|
|
|
|
|
|
selected.len(),
|
|
|
|
|
|
rel,
|
|
|
|
|
|
target.value()
|
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 if let Some(to_front) = to_front_opt {
|
|
|
|
|
|
let (min_eff, max_eff) = fb_baseline.unwrap_or((1, 0));
|
|
|
|
|
|
for (k, h) in selected.iter().enumerate() {
|
|
|
|
|
|
let sort = if to_front {
|
|
|
|
|
|
max_eff.saturating_add(1 + k as u64)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
min_eff.saturating_sub(1 + k as u64).max(1)
|
|
|
|
|
|
};
|
|
|
|
|
|
table.add_entry(*h, acadrust::Handle::new(sort));
|
|
|
|
|
|
}
|
|
|
|
|
|
let dir = if to_front { "front" } else { "back" };
|
2026-08-02 22:09:25 +03:00
|
|
|
|
self.command_line.push_info(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
|
|
|
|
"DRAWORDER: moved {} entities to {}.",
|
|
|
|
|
|
selected.len(),
|
|
|
|
|
|
dir
|
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
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// Sort order lives in SortEntitiesTable, which the
|
|
|
|
|
|
// render-side `sort_cache` rebuilds per geometry epoch.
|
|
|
|
|
|
// Bump it so the new draw order shows immediately
|
|
|
|
|
|
// instead of waiting for an unrelated geometry change.
|
2026-07-28 17:16:27 +03:00
|
|
|
|
// Draw order changes submission order only; all
|
|
|
|
|
|
// per-entity tessellation remains valid.
|
|
|
|
|
|
self.tabs[i].scene.bump_geometry_no_blocks();
|
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].dirty = true;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
self.command_line.push_info(
|
2026-08-02 22:09:25 +03:00
|
|
|
|
crate::t!("Usage: DRAWORDER F|FRONT | B|BACK | A|ABOVE <handle> | U|UNDER <handle>").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(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
|
|
|
|
// SYNCPVIEWPORTS — copy the first selected viewport's display settings
|
|
|
|
|
|
// (view direction/target, scale, snap/grid, frozen layers) to the rest.
|
|
|
|
|
|
"SYNCPVIEWPORTS" | "VPSYNC" => {
|
|
|
|
|
|
let vps: Vec<acadrust::Handle> = self.tabs[i]
|
|
|
|
|
|
.scene
|
|
|
|
|
|
.selected_entities()
|
|
|
|
|
|
.iter()
|
|
|
|
|
|
.filter(|(_, e)| matches!(e, acadrust::EntityType::Viewport(_)))
|
|
|
|
|
|
.map(|(h, _)| *h)
|
|
|
|
|
|
.collect();
|
|
|
|
|
|
if vps.len() < 2 {
|
|
|
|
|
|
self.command_line.push_error(
|
|
|
|
|
|
"SYNCPVIEWPORTS: select two or more viewports (the first is the master).",
|
|
|
|
|
|
);
|
|
|
|
|
|
return Some(Task::none());
|
|
|
|
|
|
}
|
|
|
|
|
|
let src = match self.tabs[i].scene.document.get_entity(vps[0]) {
|
|
|
|
|
|
Some(acadrust::EntityType::Viewport(vp)) => vp.clone(),
|
|
|
|
|
|
_ => {
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_error(crate::t!("SYNCPVIEWPORTS: master is not a viewport.").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, "SYNCPVIEWPORTS");
|
|
|
|
|
|
let mut n = 0usize;
|
|
|
|
|
|
for h in &vps[1..] {
|
|
|
|
|
|
if let Some(acadrust::EntityType::Viewport(vp)) =
|
|
|
|
|
|
self.tabs[i].scene.document.get_entity_mut(*h)
|
|
|
|
|
|
{
|
|
|
|
|
|
vp.view_direction = src.view_direction;
|
|
|
|
|
|
vp.view_target = src.view_target;
|
|
|
|
|
|
vp.view_height = src.view_height;
|
|
|
|
|
|
vp.snap_base = src.snap_base;
|
|
|
|
|
|
vp.snap_spacing = src.snap_spacing;
|
|
|
|
|
|
vp.snap_angle = src.snap_angle;
|
|
|
|
|
|
vp.grid_spacing = src.grid_spacing;
|
|
|
|
|
|
vp.grid_major = src.grid_major;
|
|
|
|
|
|
vp.frozen_layers = src.frozen_layers.clone();
|
|
|
|
|
|
n += 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
|
"SYNCPVIEWPORTS: synced {n} viewport(s) to the master."
|
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
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-29 09:04:18 +03:00
|
|
|
|
// HIDE — hidden-line view of the active viewport.
|
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
|
|
|
|
"HIDE" => {
|
2026-06-29 09:04:18 +03:00
|
|
|
|
return Some(Task::done(Message::SetRenderMode(
|
|
|
|
|
|
acadrust::entities::ViewportRenderMode::HiddenLine,
|
|
|
|
|
|
)));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// VISUALSTYLES <name> — apply a built-in visual style to the active
|
|
|
|
|
|
// viewport via its render mode (the style-definition manager dialog
|
|
|
|
|
|
// is not modelled; this applies the standard styles).
|
|
|
|
|
|
cmd if cmd == "VISUALSTYLES" || cmd.starts_with("VISUALSTYLES ") => {
|
|
|
|
|
|
use acadrust::entities::ViewportRenderMode as VRM;
|
|
|
|
|
|
let name = cmd.strip_prefix("VISUALSTYLES").unwrap_or("").trim().to_uppercase();
|
|
|
|
|
|
let mode = match name.as_str() {
|
|
|
|
|
|
"2DWIREFRAME" | "2D" => Some(VRM::Wireframe2D),
|
|
|
|
|
|
"3DWIREFRAME" | "WIREFRAME" | "3D" => Some(VRM::Wireframe3D),
|
|
|
|
|
|
"HIDDEN" | "HIDDENLINE" => Some(VRM::HiddenLine),
|
|
|
|
|
|
"FLAT" | "FLATSHADED" => Some(VRM::FlatShaded),
|
|
|
|
|
|
"REALISTIC" | "SHADED" | "GOURAUD" => Some(VRM::GouraudShaded),
|
|
|
|
|
|
"CONCEPTUAL" | "SHADEDWITHEDGES" => Some(VRM::GouraudShadedWithEdges),
|
|
|
|
|
|
_ => None,
|
|
|
|
|
|
};
|
|
|
|
|
|
match mode {
|
|
|
|
|
|
Some(m) => return Some(Task::done(Message::SetRenderMode(m))),
|
|
|
|
|
|
None => self.command_line.push_info(
|
|
|
|
|
|
"VISUALSTYLES <2DWIREFRAME|3DWIREFRAME|HIDDEN|REALISTIC|CONCEPTUAL|SHADED>",
|
|
|
|
|
|
),
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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))
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ── Draw Order: interactive reference-object pick ──────────────────────────
|
|
|
|
|
|
|
|
|
|
|
|
/// Moves a captured selection above or below a reference object the user
|
|
|
|
|
|
/// picks in the viewport. On pick it relaunches `DRAWORDER A|U <handle>`
|
|
|
|
|
|
/// with the captured handles reinstalled as the selection, so the existing
|
|
|
|
|
|
/// command path performs the actual reorder.
|
|
|
|
|
|
pub(crate) struct DrawOrderRefCommand {
|
|
|
|
|
|
to_move: Vec<acadrust::Handle>,
|
|
|
|
|
|
above: bool,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl DrawOrderRefCommand {
|
|
|
|
|
|
pub(crate) fn new(to_move: Vec<acadrust::Handle>, above: bool) -> Self {
|
|
|
|
|
|
Self { to_move, above }
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl CadCommand for DrawOrderRefCommand {
|
|
|
|
|
|
fn name(&self) -> &'static str {
|
|
|
|
|
|
"DRAWORDER"
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn prompt(&self) -> String {
|
|
|
|
|
|
if self.above {
|
2026-08-02 22:09:25 +03:00
|
|
|
|
crate::t!("DRAWORDER Select reference object (move selection above):").into_owned()
|
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 {
|
2026-08-02 22:09:25 +03:00
|
|
|
|
crate::t!("DRAWORDER Select reference object (move selection under):").into_owned()
|
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
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn needs_entity_pick(&self) -> bool {
|
|
|
|
|
|
true
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn on_entity_pick(
|
|
|
|
|
|
&mut self,
|
|
|
|
|
|
handle: acadrust::Handle,
|
|
|
|
|
|
_pt: glam::DVec3,
|
|
|
|
|
|
) -> crate::command::CmdResult {
|
|
|
|
|
|
if handle.is_null() {
|
|
|
|
|
|
return crate::command::CmdResult::NeedPoint;
|
|
|
|
|
|
}
|
|
|
|
|
|
let opt = if self.above { "A" } else { "U" };
|
|
|
|
|
|
let cmd = format!("DRAWORDER {} {:x}", opt, handle.value());
|
|
|
|
|
|
crate::command::CmdResult::Relaunch(cmd, std::mem::take(&mut self.to_move))
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn on_point(&mut self, _pt: glam::DVec3) -> crate::command::CmdResult {
|
|
|
|
|
|
crate::command::CmdResult::NeedPoint
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn on_enter(&mut self) -> crate::command::CmdResult {
|
|
|
|
|
|
crate::command::CmdResult::Cancel
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|