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_dim(&mut self, cmd: &str, i: usize) -> Option<Task<Message>> {
|
|
|
|
|
|
match cmd {
|
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
|
|
|
|
"DIMALIGNED" => {
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
use crate::modules::annotate::aligned_dim::AlignedDimensionCommand;
|
|
|
|
|
|
let cmd = AlignedDimensionCommand::new();
|
|
|
|
|
|
self.command_line.push_info(&cmd.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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
|
|
|
|
"DIMDIAMETER" => {
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
use crate::modules::annotate::diameter_dim::DiameterDimensionCommand;
|
|
|
|
|
|
let cmd = DiameterDimensionCommand::new();
|
|
|
|
|
|
self.command_line.push_info(&cmd.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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
|
|
|
|
"DIMLINEAR" => {
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
use crate::modules::annotate::linear_dim::LinearDimensionCommand;
|
|
|
|
|
|
let new_cmd = LinearDimensionCommand::new();
|
|
|
|
|
|
self.command_line.push_info(&new_cmd.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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
|
|
|
|
"DIMRADIUS" => {
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
use crate::modules::annotate::radius_dim::RadiusDimensionCommand;
|
|
|
|
|
|
let new_cmd = RadiusDimensionCommand::new();
|
|
|
|
|
|
self.command_line.push_info(&new_cmd.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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
|
|
|
|
"DIMANGULAR" => {
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
use crate::modules::annotate::angular_dim::AngularDimensionCommand;
|
|
|
|
|
|
let new_cmd = AngularDimensionCommand::new();
|
|
|
|
|
|
self.command_line.push_info(&new_cmd.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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
|
|
|
|
"DIMORDINATE" => {
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
use crate::modules::annotate::ordinate_dim::OrdinateDimCommand;
|
|
|
|
|
|
let new_cmd = OrdinateDimCommand::new();
|
|
|
|
|
|
self.command_line.push_info(&new_cmd.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
// LEADER draws a leader line with an annotation; QLEADER is the same
|
|
|
|
|
|
// quick leader-plus-annotation operation.
|
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
|
|
|
|
"LEADER" | "QLEADER" => {
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
use crate::modules::annotate::leader_cmd::LeaderCommand;
|
2026-08-03 16:43:37 +03:00
|
|
|
|
let defaults = crate::scene::creation_style::current_dimension_defaults(
|
|
|
|
|
|
&self.tabs[i].scene.document,
|
|
|
|
|
|
);
|
|
|
|
|
|
let multiplier = self.tabs[i].scene.creation_annotation_multiplier();
|
|
|
|
|
|
let new_cmd = LeaderCommand::with_defaults(defaults, multiplier);
|
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));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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
|
|
|
|
// JUSTIFYTEXT <option> — change the justification of selected text and
|
|
|
|
|
|
// multiline-text objects.
|
2026-07-16 14:37:10 +03:00
|
|
|
|
"JUSTIFYTEXT" => {
|
|
|
|
|
|
use crate::command::SelectThenKeywordCommand;
|
|
|
|
|
|
let has_sel = !self.tabs[i].scene.selected_entities().is_empty();
|
|
|
|
|
|
let c = SelectThenKeywordCommand::new(
|
|
|
|
|
|
"JUSTIFYTEXT",
|
|
|
|
|
|
"JUSTIFYTEXT [Left / Center / Right / Middle / Aligned / Fit]:",
|
|
|
|
|
|
vec![
|
|
|
|
|
|
("Left", "LEFT", None),
|
|
|
|
|
|
("Center", "CENTER", None),
|
|
|
|
|
|
("Right", "RIGHT", None),
|
|
|
|
|
|
("Middle", "MIDDLE", None),
|
|
|
|
|
|
("Aligned", "ALIGN", None),
|
|
|
|
|
|
("Fit", "FIT", None),
|
|
|
|
|
|
],
|
|
|
|
|
|
has_sel,
|
|
|
|
|
|
);
|
|
|
|
|
|
self.command_line.push_info(&c.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(c));
|
|
|
|
|
|
}
|
|
|
|
|
|
cmd if cmd.starts_with("JUSTIFYTEXT ") => {
|
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 opt = cmd.split_whitespace().nth(1).unwrap_or("").to_uppercase();
|
|
|
|
|
|
let handles: Vec<acadrust::Handle> = self.tabs[i]
|
|
|
|
|
|
.scene
|
|
|
|
|
|
.selected_entities()
|
|
|
|
|
|
.iter()
|
|
|
|
|
|
.map(|(h, _)| *h)
|
|
|
|
|
|
.collect();
|
|
|
|
|
|
if handles.is_empty() {
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_error(crate::t!("JUSTIFYTEXT: select text objects first.").as_ref());
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
return Some(Task::none());
|
|
|
|
|
|
}
|
|
|
|
|
|
if opt.is_empty() {
|
|
|
|
|
|
self.command_line.push_info(
|
|
|
|
|
|
"Usage: JUSTIFYTEXT <Left|Center|Right|Middle|TL|TC|TR|ML|MC|MR|BL|BC|BR>",
|
|
|
|
|
|
);
|
|
|
|
|
|
return Some(Task::none());
|
|
|
|
|
|
}
|
2026-07-03 09:51:34 +03:00
|
|
|
|
use acadrust::entities::{
|
|
|
|
|
|
AttachmentPoint as AP, TextHorizontalAlignment as TH,
|
|
|
|
|
|
TextVerticalAlignment as TV,
|
|
|
|
|
|
};
|
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 text_align = match opt.as_str() {
|
|
|
|
|
|
"L" | "LEFT" | "TL" | "ML" | "BL" => Some(TH::Left),
|
|
|
|
|
|
"C" | "CENTER" | "TC" | "BC" => Some(TH::Center),
|
|
|
|
|
|
"R" | "RIGHT" | "TR" | "MR" | "BR" => Some(TH::Right),
|
|
|
|
|
|
"M" | "MIDDLE" | "MC" => Some(TH::Middle),
|
|
|
|
|
|
"A" | "ALIGN" | "ALIGNED" => Some(TH::Aligned),
|
|
|
|
|
|
"F" | "FIT" => Some(TH::Fit),
|
|
|
|
|
|
_ => None,
|
|
|
|
|
|
};
|
2026-07-03 09:51:34 +03:00
|
|
|
|
// Vertical band for single-line TEXT. Bare L/C/R (and Aligned/Fit
|
|
|
|
|
|
// and the special Middle) keep the baseline; the T*/M*/B* codes
|
|
|
|
|
|
// pin the top/middle/bottom of the text box.
|
|
|
|
|
|
let text_valign = match opt.as_str() {
|
|
|
|
|
|
"TL" | "TC" | "TR" => Some(TV::Top),
|
|
|
|
|
|
"ML" | "MC" | "MR" => Some(TV::Middle),
|
|
|
|
|
|
"BL" | "BC" | "BR" => Some(TV::Bottom),
|
|
|
|
|
|
_ => None,
|
|
|
|
|
|
};
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
let mtext_ap = match opt.as_str() {
|
|
|
|
|
|
"TL" => Some(AP::TopLeft),
|
|
|
|
|
|
"TC" => Some(AP::TopCenter),
|
|
|
|
|
|
"TR" => Some(AP::TopRight),
|
|
|
|
|
|
"ML" | "L" | "LEFT" => Some(AP::MiddleLeft),
|
|
|
|
|
|
"MC" | "M" | "MIDDLE" | "C" | "CENTER" => Some(AP::MiddleCenter),
|
|
|
|
|
|
"MR" | "R" | "RIGHT" => Some(AP::MiddleRight),
|
|
|
|
|
|
"BL" => Some(AP::BottomLeft),
|
|
|
|
|
|
"BC" => Some(AP::BottomCenter),
|
|
|
|
|
|
"BR" => Some(AP::BottomRight),
|
|
|
|
|
|
_ => None,
|
|
|
|
|
|
};
|
|
|
|
|
|
if text_align.is_none() && mtext_ap.is_none() {
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_error(crate::t!("JUSTIFYTEXT: unknown justification option.").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, "JUSTIFYTEXT");
|
|
|
|
|
|
let mut n = 0usize;
|
|
|
|
|
|
for h in &handles {
|
|
|
|
|
|
if let Some(e) = self.tabs[i]
|
|
|
|
|
|
.scene
|
|
|
|
|
|
.document
|
|
|
|
|
|
.entities_mut()
|
|
|
|
|
|
.find(|e| e.common().handle == *h)
|
|
|
|
|
|
{
|
|
|
|
|
|
match e {
|
|
|
|
|
|
acadrust::EntityType::Text(t) => {
|
2026-07-03 09:51:34 +03:00
|
|
|
|
let mut changed = false;
|
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 let Some(a) = text_align {
|
|
|
|
|
|
t.horizontal_alignment = a;
|
2026-07-03 09:51:34 +03:00
|
|
|
|
changed = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
if let Some(v) = text_valign {
|
|
|
|
|
|
t.vertical_alignment = v;
|
|
|
|
|
|
changed = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
if changed {
|
|
|
|
|
|
crate::entities::text::sync_text_alignment_point(t);
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
n += 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
acadrust::EntityType::MText(m) => {
|
|
|
|
|
|
if let Some(a) = mtext_ap {
|
|
|
|
|
|
m.attachment_point = a;
|
|
|
|
|
|
n += 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
_ => {}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
self.tabs[i].dirty = true;
|
2026-07-28 17:16:27 +03:00
|
|
|
|
let changes: Vec<_> = handles
|
|
|
|
|
|
.into_iter()
|
|
|
|
|
|
.map(|handle| (handle, crate::scene::ChangeKind::Modified))
|
|
|
|
|
|
.collect();
|
|
|
|
|
|
self.tabs[i].scene.bump_entities(&changes);
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_output(crate::tf!("JUSTIFYTEXT: updated {n} text object(s).").as_ref());
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TCASE <Upper|Lower|Sentence|Title> — change the case of the text in
|
|
|
|
|
|
// selected text and multiline-text objects.
|
2026-07-16 14:37:10 +03:00
|
|
|
|
"TCASE" => {
|
|
|
|
|
|
use crate::command::SelectThenKeywordCommand;
|
|
|
|
|
|
let has_sel = !self.tabs[i].scene.selected_entities().is_empty();
|
|
|
|
|
|
let c = SelectThenKeywordCommand::new(
|
|
|
|
|
|
"TCASE",
|
|
|
|
|
|
"TCASE [Upper / Lower / Sentence / Title]:",
|
|
|
|
|
|
vec![
|
|
|
|
|
|
("Upper", "UPPER", None),
|
|
|
|
|
|
("Lower", "LOWER", None),
|
|
|
|
|
|
("Sentence", "SENTENCE", None),
|
|
|
|
|
|
("Title", "TITLE", None),
|
|
|
|
|
|
],
|
|
|
|
|
|
has_sel,
|
|
|
|
|
|
);
|
|
|
|
|
|
self.command_line.push_info(&c.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(c));
|
|
|
|
|
|
}
|
|
|
|
|
|
cmd if cmd.starts_with("TCASE ") => {
|
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 opt = cmd.split_whitespace().nth(1).unwrap_or("").to_uppercase();
|
|
|
|
|
|
let handles: Vec<acadrust::Handle> = self.tabs[i]
|
|
|
|
|
|
.scene
|
|
|
|
|
|
.selected_entities()
|
|
|
|
|
|
.iter()
|
|
|
|
|
|
.map(|(h, _)| *h)
|
|
|
|
|
|
.collect();
|
|
|
|
|
|
if handles.is_empty() {
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_error(crate::t!("TCASE: select text objects first.").as_ref());
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
return Some(Task::none());
|
|
|
|
|
|
}
|
|
|
|
|
|
if !matches!(
|
|
|
|
|
|
opt.as_str(),
|
|
|
|
|
|
"U" | "UPPER"
|
|
|
|
|
|
| "UPPERCASE"
|
|
|
|
|
|
| "L"
|
|
|
|
|
|
| "LOWER"
|
|
|
|
|
|
| "LOWERCASE"
|
|
|
|
|
|
| "S"
|
|
|
|
|
|
| "SENTENCE"
|
|
|
|
|
|
| "T"
|
|
|
|
|
|
| "TITLE"
|
|
|
|
|
|
) {
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_info(crate::t!("Usage: TCASE <Upper|Lower|Sentence|Title>").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());
|
|
|
|
|
|
}
|
|
|
|
|
|
let conv = move |s: &str| -> String {
|
|
|
|
|
|
match opt.as_str() {
|
|
|
|
|
|
"U" | "UPPER" | "UPPERCASE" => s.to_uppercase(),
|
|
|
|
|
|
"L" | "LOWER" | "LOWERCASE" => s.to_lowercase(),
|
|
|
|
|
|
"S" | "SENTENCE" => sentence_case(s),
|
|
|
|
|
|
_ => title_case(s),
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
self.push_undo_snapshot(i, "TCASE");
|
|
|
|
|
|
let mut n = 0usize;
|
|
|
|
|
|
for h in &handles {
|
|
|
|
|
|
if let Some(e) = self.tabs[i]
|
|
|
|
|
|
.scene
|
|
|
|
|
|
.document
|
|
|
|
|
|
.entities_mut()
|
|
|
|
|
|
.find(|e| e.common().handle == *h)
|
|
|
|
|
|
{
|
|
|
|
|
|
match e {
|
|
|
|
|
|
acadrust::EntityType::Text(t) => {
|
|
|
|
|
|
t.value = conv(&t.value);
|
|
|
|
|
|
n += 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
acadrust::EntityType::MText(m) => {
|
|
|
|
|
|
m.value = conv(&m.value);
|
|
|
|
|
|
n += 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
_ => {}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
self.tabs[i].dirty = true;
|
2026-07-28 17:16:27 +03:00
|
|
|
|
let changes: Vec<_> = handles
|
|
|
|
|
|
.into_iter()
|
|
|
|
|
|
.map(|handle| (handle, crate::scene::ChangeKind::Modified))
|
|
|
|
|
|
.collect();
|
|
|
|
|
|
self.tabs[i].scene.bump_entities(&changes);
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_output(crate::tf!("TCASE: updated {n} text object(s).").as_ref());
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TEXTMASK — place a wipeout mask sized to each selected text object's
|
|
|
|
|
|
// extent and bring the text in front, so the mask hides whatever is
|
|
|
|
|
|
// underneath while the text stays readable. (Same world-XY coordinate
|
|
|
|
|
|
// space the WIPEOUT command uses.)
|
2026-07-16 14:37:10 +03:00
|
|
|
|
"TEXTMASK" => {
|
|
|
|
|
|
use crate::command::SelectThenValueCommand;
|
|
|
|
|
|
let has_sel = !self.tabs[i].scene.selected_entities().is_empty();
|
|
|
|
|
|
let c = SelectThenValueCommand::new(
|
|
|
|
|
|
"TEXTMASK",
|
|
|
|
|
|
"TEXTMASK press Enter to mask the selected text:",
|
|
|
|
|
|
has_sel,
|
|
|
|
|
|
);
|
|
|
|
|
|
self.command_line.push_info(&c.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(c));
|
|
|
|
|
|
}
|
|
|
|
|
|
cmd if cmd.starts_with("TEXTMASK ") => {
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
let handles: Vec<acadrust::Handle> = self.tabs[i]
|
|
|
|
|
|
.scene
|
|
|
|
|
|
.selected_entities()
|
|
|
|
|
|
.iter()
|
|
|
|
|
|
.filter(|(_, e)| {
|
|
|
|
|
|
matches!(
|
|
|
|
|
|
e,
|
|
|
|
|
|
acadrust::EntityType::Text(_) | acadrust::EntityType::MText(_)
|
|
|
|
|
|
)
|
|
|
|
|
|
})
|
|
|
|
|
|
.map(|(h, _)| *h)
|
|
|
|
|
|
.collect();
|
|
|
|
|
|
if handles.is_empty() {
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_error(crate::t!("TEXTMASK: select text objects first.").as_ref());
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
return Some(Task::none());
|
|
|
|
|
|
}
|
|
|
|
|
|
self.push_undo_snapshot(i, "TEXTMASK");
|
|
|
|
|
|
let mut n = 0usize;
|
|
|
|
|
|
for h in &handles {
|
|
|
|
|
|
let wires = self.tabs[i].scene.wire_models_for(&[*h]);
|
|
|
|
|
|
let (mut min, mut max) = ([f32::MAX; 3], [f32::MIN; 3]);
|
|
|
|
|
|
for w in &wires {
|
|
|
|
|
|
for p in &w.points {
|
|
|
|
|
|
for k in 0..3 {
|
|
|
|
|
|
min[k] = min[k].min(p[k]);
|
|
|
|
|
|
max[k] = max[k].max(p[k]);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if min[0] > max[0] {
|
|
|
|
|
|
continue; // no geometry for this object
|
|
|
|
|
|
}
|
|
|
|
|
|
let pad = ((max[1] - min[1]) * 0.15).max(0.0);
|
|
|
|
|
|
let c1 = acadrust::types::Vector3::new(
|
|
|
|
|
|
(min[0] - pad) as f64,
|
|
|
|
|
|
(min[1] - pad) as f64,
|
|
|
|
|
|
min[2] as f64,
|
|
|
|
|
|
);
|
|
|
|
|
|
let c2 = acadrust::types::Vector3::new(
|
|
|
|
|
|
(max[0] + pad) as f64,
|
|
|
|
|
|
(max[1] + pad) as f64,
|
|
|
|
|
|
min[2] as f64,
|
|
|
|
|
|
);
|
|
|
|
|
|
self.tabs[i]
|
|
|
|
|
|
.scene
|
|
|
|
|
|
.add_entity_clone(acadrust::EntityType::Wipeout(
|
|
|
|
|
|
acadrust::entities::Wipeout::from_corners(c1, c2),
|
|
|
|
|
|
));
|
|
|
|
|
|
n += 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
// Draw the text in front of its newly-added masks.
|
2026-07-28 17:35:21 +03:00
|
|
|
|
self.tabs[i]
|
|
|
|
|
|
.scene
|
|
|
|
|
|
.replace_selection(handles.iter().cloned().collect());
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
self.tabs[i].dirty = true;
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_output(crate::tf!("TEXTMASK: masked {n} text object(s).").as_ref());
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
return self.dispatch_view("DRAWORDER FRONT", i);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// (text width fit)
|
|
|
|
|
|
// TEXTFIT <width> — adjust the width factor of selected single-line
|
|
|
|
|
|
// text so its rendered width matches the target.
|
2026-07-16 14:37:10 +03:00
|
|
|
|
"TEXTFIT" => {
|
|
|
|
|
|
use crate::command::SelectThenValueCommand;
|
|
|
|
|
|
let has_sel = !self.tabs[i].scene.selected_entities().is_empty();
|
|
|
|
|
|
let c =
|
|
|
|
|
|
SelectThenValueCommand::new("TEXTFIT", "TEXTFIT target width:", has_sel);
|
|
|
|
|
|
self.command_line.push_info(&c.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(c));
|
|
|
|
|
|
}
|
|
|
|
|
|
cmd if cmd.starts_with("TEXTFIT ") => {
|
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 target: f64 = match cmd.split_whitespace().nth(1).and_then(|s| s.parse().ok()) {
|
|
|
|
|
|
Some(v) if v > 0.0 => v,
|
|
|
|
|
|
_ => {
|
|
|
|
|
|
self.command_line.push_info(
|
|
|
|
|
|
"Usage: TEXTFIT <target width> (fits selected text to that width)",
|
|
|
|
|
|
);
|
|
|
|
|
|
return Some(Task::none());
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
let handles: Vec<acadrust::Handle> = self.tabs[i]
|
|
|
|
|
|
.scene
|
|
|
|
|
|
.selected_entities()
|
|
|
|
|
|
.iter()
|
|
|
|
|
|
.filter(|(_, e)| matches!(e, acadrust::EntityType::Text(_)))
|
|
|
|
|
|
.map(|(h, _)| *h)
|
|
|
|
|
|
.collect();
|
|
|
|
|
|
if handles.is_empty() {
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_error(crate::t!("TEXTFIT: select single-line text first.").as_ref());
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
return Some(Task::none());
|
|
|
|
|
|
}
|
|
|
|
|
|
self.push_undo_snapshot(i, "TEXTFIT");
|
|
|
|
|
|
let mut n = 0usize;
|
|
|
|
|
|
for h in &handles {
|
|
|
|
|
|
let wires = self.tabs[i].scene.wire_models_for(&[*h]);
|
|
|
|
|
|
let (mut minx, mut maxx) = (f32::MAX, f32::MIN);
|
|
|
|
|
|
for w in &wires {
|
|
|
|
|
|
for p in &w.points {
|
|
|
|
|
|
minx = minx.min(p[0]);
|
|
|
|
|
|
maxx = maxx.max(p[0]);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
let cur_w = (maxx - minx) as f64;
|
|
|
|
|
|
if cur_w <= 1e-9 {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
if let Some(acadrust::EntityType::Text(t)) = self.tabs[i]
|
|
|
|
|
|
.scene
|
|
|
|
|
|
.document
|
|
|
|
|
|
.entities_mut()
|
|
|
|
|
|
.find(|e| e.common().handle == *h)
|
|
|
|
|
|
{
|
|
|
|
|
|
let old = if t.width_factor.abs() > 1e-9 {
|
|
|
|
|
|
t.width_factor
|
|
|
|
|
|
} else {
|
|
|
|
|
|
1.0
|
|
|
|
|
|
};
|
|
|
|
|
|
t.width_factor = old * target / cur_w;
|
|
|
|
|
|
n += 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
self.tabs[i].dirty = true;
|
2026-07-28 17:16:27 +03:00
|
|
|
|
let changes: Vec<_> = handles
|
|
|
|
|
|
.into_iter()
|
|
|
|
|
|
.map(|handle| (handle, crate::scene::ChangeKind::Modified))
|
|
|
|
|
|
.collect();
|
|
|
|
|
|
self.tabs[i].scene.bump_entities(&changes);
|
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
|
|
|
|
"TEXTFIT: fitted {n} text object(s) to width {target}."
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// (text sequential numbering)
|
|
|
|
|
|
// TCOUNT [start] — prefix selected single-line text with sequential
|
|
|
|
|
|
// numbers in reading order (top-to-bottom, then left-to-right).
|
2026-07-16 14:37:10 +03:00
|
|
|
|
"TCOUNT" => {
|
2026-08-07 19:50:11 -03:00
|
|
|
|
use crate::command::TCountCommand;
|
|
|
|
|
|
|
2026-07-16 14:37:10 +03:00
|
|
|
|
let has_sel = !self.tabs[i].scene.selected_entities().is_empty();
|
2026-08-07 19:50:11 -03:00
|
|
|
|
let c = TCountCommand::new(has_sel);
|
|
|
|
|
|
|
2026-07-16 14:37:10 +03:00
|
|
|
|
self.command_line.push_info(&c.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(c));
|
|
|
|
|
|
}
|
|
|
|
|
|
cmd if cmd.starts_with("TCOUNT ") => {
|
2026-08-07 19:50:11 -03:00
|
|
|
|
let mut args = cmd.split_whitespace().skip(1);
|
|
|
|
|
|
|
|
|
|
|
|
let start: i64 = args
|
|
|
|
|
|
.next()
|
|
|
|
|
|
.and_then(|s| s.parse().ok())
|
|
|
|
|
|
.unwrap_or(1);
|
|
|
|
|
|
|
|
|
|
|
|
let increment: i64 = args
|
|
|
|
|
|
.next()
|
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
|
|
|
|
.and_then(|s| s.parse().ok())
|
|
|
|
|
|
.unwrap_or(1);
|
2026-08-07 19:50:11 -03:00
|
|
|
|
|
|
|
|
|
|
let placement = args
|
|
|
|
|
|
.next()
|
|
|
|
|
|
.unwrap_or("O")
|
|
|
|
|
|
.to_uppercase();
|
|
|
|
|
|
|
|
|
|
|
|
let placement = match placement.as_str() {
|
|
|
|
|
|
"O" | "OVERWRITE" => "O",
|
|
|
|
|
|
"P" | "PREFIX" => "P",
|
|
|
|
|
|
"S" | "SUFFIX" => "S",
|
|
|
|
|
|
_ => {
|
|
|
|
|
|
self.command_line.push_error(
|
2026-08-08 03:19:54 +03:00
|
|
|
|
crate::t!("TCOUNT: placement must be Overwrite, Prefix, or Suffix.")
|
|
|
|
|
|
.as_ref(),
|
2026-08-07 19:50:11 -03:00
|
|
|
|
);
|
|
|
|
|
|
return Some(Task::none());
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
let mut texts: Vec<(acadrust::Handle, f64, f64)> = self.tabs[i]
|
|
|
|
|
|
.scene
|
|
|
|
|
|
.selected_entities()
|
|
|
|
|
|
.iter()
|
|
|
|
|
|
.filter_map(|(h, e)| match e {
|
|
|
|
|
|
acadrust::EntityType::Text(t) => {
|
|
|
|
|
|
Some((*h, t.insertion_point.x, t.insertion_point.y))
|
|
|
|
|
|
}
|
|
|
|
|
|
_ => None,
|
|
|
|
|
|
})
|
|
|
|
|
|
.collect();
|
2026-08-07 19:50:11 -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
|
|
|
|
if texts.is_empty() {
|
|
|
|
|
|
self.command_line
|
2026-08-08 03:19:54 +03:00
|
|
|
|
.push_error(crate::t!("TCOUNT: select single-line text first.").as_ref());
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
return Some(Task::none());
|
|
|
|
|
|
}
|
2026-08-07 19:50:11 -03:00
|
|
|
|
|
|
|
|
|
|
// Keep the existing reading order:
|
|
|
|
|
|
// higher Y first, then smaller X.
|
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
|
|
|
|
texts.sort_by(|a, b| {
|
|
|
|
|
|
b.2.partial_cmp(&a.2)
|
|
|
|
|
|
.unwrap_or(std::cmp::Ordering::Equal)
|
2026-08-07 19:50:11 -03:00
|
|
|
|
.then(
|
|
|
|
|
|
a.1.partial_cmp(&b.1)
|
|
|
|
|
|
.unwrap_or(std::cmp::Ordering::Equal),
|
|
|
|
|
|
)
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
});
|
2026-08-07 19:50:11 -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
|
|
|
|
self.push_undo_snapshot(i, "TCOUNT");
|
2026-08-07 19:50:11 -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
|
|
|
|
let mut num = start;
|
2026-08-07 19:50:11 -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
|
|
|
|
for (h, _, _) in &texts {
|
|
|
|
|
|
if let Some(acadrust::EntityType::Text(t)) = self.tabs[i]
|
|
|
|
|
|
.scene
|
|
|
|
|
|
.document
|
|
|
|
|
|
.entities_mut()
|
|
|
|
|
|
.find(|e| e.common().handle == *h)
|
|
|
|
|
|
{
|
2026-08-07 19:50:11 -03:00
|
|
|
|
t.value = match placement {
|
|
|
|
|
|
"P" => format!("{num} {}", t.value),
|
|
|
|
|
|
"S" => format!("{} {num}", t.value),
|
|
|
|
|
|
_ => num.to_string(),
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
num += increment;
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-08-07 19:50:11 -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
|
|
|
|
self.tabs[i].dirty = true;
|
2026-08-07 19:50:11 -03:00
|
|
|
|
|
2026-07-28 17:16:27 +03:00
|
|
|
|
let changes: Vec<_> = texts
|
|
|
|
|
|
.iter()
|
2026-08-07 19:50:11 -03:00
|
|
|
|
.map(|(handle, _, _)| {
|
|
|
|
|
|
(*handle, crate::scene::ChangeKind::Modified)
|
|
|
|
|
|
})
|
2026-07-28 17:16:27 +03:00
|
|
|
|
.collect();
|
2026-08-07 19:50:11 -03:00
|
|
|
|
|
2026-07-28 17:16:27 +03:00
|
|
|
|
self.tabs[i].scene.bump_entities(&changes);
|
2026-08-07 19:50:11 -03:00
|
|
|
|
|
2026-08-08 03:19:54 +03:00
|
|
|
|
let placement_name = crate::t!(match placement {
|
2026-08-07 19:50:11 -03:00
|
|
|
|
"P" => "Prefix",
|
|
|
|
|
|
"S" => "Suffix",
|
|
|
|
|
|
_ => "Overwrite",
|
2026-08-08 03:19:54 +03:00
|
|
|
|
});
|
|
|
|
|
|
let n = texts.len();
|
|
|
|
|
|
self.command_line.push_output(
|
|
|
|
|
|
crate::tf!(
|
|
|
|
|
|
"TCOUNT: numbered {n} text object(s) from {start} by {increment} ({placement_name})."
|
|
|
|
|
|
)
|
|
|
|
|
|
.as_ref(),
|
|
|
|
|
|
);
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
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
|
|
|
|
"MLEADER" => {
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
use crate::modules::annotate::mleader_cmd::MLeaderCommand;
|
2026-08-03 16:43:37 +03:00
|
|
|
|
let name = self.tabs[i]
|
|
|
|
|
|
.scene
|
|
|
|
|
|
.document
|
|
|
|
|
|
.header
|
|
|
|
|
|
.current_mleader_style_name
|
|
|
|
|
|
.clone();
|
|
|
|
|
|
let style = self.tabs[i]
|
|
|
|
|
|
.scene
|
|
|
|
|
|
.document
|
|
|
|
|
|
.objects
|
|
|
|
|
|
.iter()
|
|
|
|
|
|
.find_map(|(handle, object)| match object {
|
|
|
|
|
|
acadrust::objects::ObjectType::MultiLeaderStyle(style)
|
|
|
|
|
|
if style.name.eq_ignore_ascii_case(&name) =>
|
|
|
|
|
|
{
|
|
|
|
|
|
let mut style = style.clone();
|
|
|
|
|
|
style.handle = *handle;
|
|
|
|
|
|
Some(style)
|
|
|
|
|
|
}
|
|
|
|
|
|
_ => None,
|
|
|
|
|
|
});
|
|
|
|
|
|
let multiplier = self.tabs[i].scene.creation_annotation_multiplier();
|
|
|
|
|
|
let new_cmd = style.map_or_else(MLeaderCommand::new, |style| {
|
|
|
|
|
|
MLeaderCommand::with_style(style, multiplier)
|
|
|
|
|
|
});
|
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));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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
|
|
|
|
"TOLERANCE" => {
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
use crate::modules::annotate::tolerance_cmd::ToleranceCommand;
|
|
|
|
|
|
let cmd = ToleranceCommand::new();
|
|
|
|
|
|
self.command_line.push_info(&cmd.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
"TABLE" => {
|
|
|
|
|
|
use crate::modules::annotate::table_cmd::TableCommand;
|
2026-08-03 16:43:37 +03:00
|
|
|
|
let name = self.tabs[i]
|
|
|
|
|
|
.scene
|
|
|
|
|
|
.document
|
|
|
|
|
|
.header
|
|
|
|
|
|
.current_table_style_name
|
|
|
|
|
|
.clone();
|
|
|
|
|
|
let style = self.tabs[i]
|
|
|
|
|
|
.scene
|
|
|
|
|
|
.document
|
|
|
|
|
|
.objects
|
|
|
|
|
|
.iter()
|
|
|
|
|
|
.find_map(|(handle, object)| match object {
|
|
|
|
|
|
acadrust::objects::ObjectType::TableStyle(style)
|
|
|
|
|
|
if style.name.eq_ignore_ascii_case(&name) =>
|
|
|
|
|
|
{
|
|
|
|
|
|
Some((*handle, style.clone()))
|
|
|
|
|
|
}
|
|
|
|
|
|
_ => None,
|
|
|
|
|
|
});
|
|
|
|
|
|
let multiplier = self.tabs[i].scene.creation_annotation_multiplier();
|
|
|
|
|
|
let cmd = style.as_ref().map_or_else(TableCommand::new, |(handle, style)| {
|
|
|
|
|
|
TableCommand::with_style(*handle, style, multiplier)
|
|
|
|
|
|
});
|
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(&cmd.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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
|
|
|
|
"DIMCONTINUE" => {
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
use crate::modules::annotate::dim_continue::DimContinueCommand;
|
2026-06-27 17:47:50 +03:00
|
|
|
|
let cmd = if let Some((p1, p2, dp, rot, trot)) =
|
|
|
|
|
|
find_last_linear_dim(&self.tabs[i].scene)
|
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-06-27 17:47:50 +03:00
|
|
|
|
DimContinueCommand::from_base(p1, p2, dp, rot, trot)
|
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 {
|
|
|
|
|
|
DimContinueCommand::new()
|
|
|
|
|
|
};
|
|
|
|
|
|
self.command_line.push_info(&cmd.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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
|
|
|
|
"DIMBASELINE" => {
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
use crate::modules::annotate::dim_baseline::DimBaselineCommand;
|
2026-06-27 17:47:50 +03:00
|
|
|
|
let cmd = if let Some((p1, p2, dp, rot, trot)) =
|
|
|
|
|
|
find_last_linear_dim(&self.tabs[i].scene)
|
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 doc = &self.tabs[i].scene.document;
|
|
|
|
|
|
let dimdli = doc
|
|
|
|
|
|
.dim_styles
|
|
|
|
|
|
.iter()
|
|
|
|
|
|
.find(|s| {
|
|
|
|
|
|
s.name
|
|
|
|
|
|
.eq_ignore_ascii_case(&doc.header.current_dimstyle_name)
|
|
|
|
|
|
})
|
|
|
|
|
|
.map(|s| s.dimdli as f32)
|
|
|
|
|
|
.unwrap_or(1.5);
|
2026-06-27 17:47:50 +03:00
|
|
|
|
DimBaselineCommand::from_base(p1, p2, dp, rot, trot, dimdli)
|
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 {
|
|
|
|
|
|
DimBaselineCommand::new()
|
|
|
|
|
|
};
|
|
|
|
|
|
self.command_line.push_info(&cmd.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
"QDIM" => {
|
|
|
|
|
|
use crate::modules::annotate::qdim::QdimCommand;
|
|
|
|
|
|
let cmd = QdimCommand::new();
|
|
|
|
|
|
self.command_line.push_info(&cmd.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-27 17:56:01 +03:00
|
|
|
|
// QDIM second stage: the front-end relaunched with the picked
|
|
|
|
|
|
// dimension-line point and the gathered geometry now selected. Build
|
|
|
|
|
|
// a continuous chain of linear dimensions across the endpoints.
|
|
|
|
|
|
cmd if cmd.starts_with("QDIM_PLACE ") => {
|
fix(precision): keep typed coordinates exact in f64 (#311)
Typed values were quantized to the f32 grid before reaching the
entity (e.g. a length of 2000.8 committed as 2000.8020), worsening
with magnitude. The interaction point layer was all glam::Vec3 (f32).
Two-layer fix:
1. Coordinate backbone -> f64. last_point, last_cursor_world,
dyn_anchor, dyn_ref become DVec3; UcsXform transforms, parse_coord,
the ucs helper fns, dyn_resolve_point, the OTRACK typed-distance
paths, command-line coordinate entry and the coordinate readout all
compute in f64. Screen/GPU/alignment stays f32 (snap engine, render
geometry, projection, UCS-icon/viewcube drawing) with casts only at
those boundaries.
2. Per-command committed storage -> DVec3. on_point already received a
DVec3, but many command impls narrowed it back to f32 (let pt =
pt.as_vec3(); Vec<Vec3> fields) and re-widened at build, quantizing
the committed coordinate. Migrated PLINE, SPLINE, MLINE, LENGTHEN,
INSERT, 3D primitives, every dimension/leader/mleader, ray, revcloud,
wipeout, attdef, table, tolerance, array center, mview, plot_window,
plus the QDIM_PLACE handler and qdim_collect_points. Preview,
hit-test and rubber-band paths keep f32 at the boundary.
Hatch manual boundaries are left for a follow-up: HatchModel.boundary
is structurally Arc<Vec<[f32;2]>> and needs a model-level widen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 21:26:40 +03:00
|
|
|
|
let nums: Vec<f64> = cmd
|
2026-06-27 17:56:01 +03:00
|
|
|
|
.split_whitespace()
|
|
|
|
|
|
.skip(1)
|
|
|
|
|
|
.filter_map(|s| s.parse().ok())
|
|
|
|
|
|
.collect();
|
|
|
|
|
|
if nums.len() < 3 {
|
|
|
|
|
|
return Some(Task::none());
|
|
|
|
|
|
}
|
2026-08-04 23:17:54 +03:00
|
|
|
|
let plane = if self.tabs[i].editing_model_space() {
|
|
|
|
|
|
self.tabs[i].ucs_xform().working_plane()
|
|
|
|
|
|
} else {
|
|
|
|
|
|
crate::command::WorkingPlane::default()
|
|
|
|
|
|
};
|
|
|
|
|
|
let place = plane.to_local(glam::DVec3::new(nums[0], nums[1], nums[2]));
|
2026-06-27 17:56:01 +03:00
|
|
|
|
let handles: Vec<acadrust::Handle> = self.tabs[i]
|
|
|
|
|
|
.scene
|
|
|
|
|
|
.selected_entities()
|
|
|
|
|
|
.iter()
|
|
|
|
|
|
.map(|(h, _)| *h)
|
|
|
|
|
|
.collect();
|
fix(precision): keep typed coordinates exact in f64 (#311)
Typed values were quantized to the f32 grid before reaching the
entity (e.g. a length of 2000.8 committed as 2000.8020), worsening
with magnitude. The interaction point layer was all glam::Vec3 (f32).
Two-layer fix:
1. Coordinate backbone -> f64. last_point, last_cursor_world,
dyn_anchor, dyn_ref become DVec3; UcsXform transforms, parse_coord,
the ucs helper fns, dyn_resolve_point, the OTRACK typed-distance
paths, command-line coordinate entry and the coordinate readout all
compute in f64. Screen/GPU/alignment stays f32 (snap engine, render
geometry, projection, UCS-icon/viewcube drawing) with casts only at
those boundaries.
2. Per-command committed storage -> DVec3. on_point already received a
DVec3, but many command impls narrowed it back to f32 (let pt =
pt.as_vec3(); Vec<Vec3> fields) and re-widened at build, quantizing
the committed coordinate. Migrated PLINE, SPLINE, MLINE, LENGTHEN,
INSERT, 3D primitives, every dimension/leader/mleader, ray, revcloud,
wipeout, attdef, table, tolerance, array center, mview, plot_window,
plus the QDIM_PLACE handler and qdim_collect_points. Preview,
hit-test and rubber-band paths keep f32 at the boundary.
Hatch manual boundaries are left for a follow-up: HatchModel.boundary
is structurally Arc<Vec<[f32;2]>> and needs a model-level widen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 21:26:40 +03:00
|
|
|
|
let mut pts: Vec<glam::DVec3> = Vec::new();
|
2026-06-27 17:56:01 +03:00
|
|
|
|
for h in &handles {
|
|
|
|
|
|
if let Some(e) = self.tabs[i].scene.document.get_entity(*h) {
|
|
|
|
|
|
qdim_collect_points(e, &mut pts);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-08-04 23:17:54 +03:00
|
|
|
|
for point in &mut pts {
|
|
|
|
|
|
*point = plane.to_local(*point);
|
|
|
|
|
|
}
|
2026-06-27 17:56:01 +03:00
|
|
|
|
if pts.len() < 2 {
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_error(crate::t!("QDIM: no dimensionable endpoints in the selection.").as_ref());
|
2026-06-27 17:56:01 +03:00
|
|
|
|
return Some(Task::none());
|
|
|
|
|
|
}
|
|
|
|
|
|
// Choose the dimension axis from the points' spread: a wider X
|
|
|
|
|
|
// span dimensions horizontally (ordered by X), else vertically.
|
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 (minx, maxx) = pts
|
|
|
|
|
|
.iter()
|
fix(precision): keep typed coordinates exact in f64 (#311)
Typed values were quantized to the f32 grid before reaching the
entity (e.g. a length of 2000.8 committed as 2000.8020), worsening
with magnitude. The interaction point layer was all glam::Vec3 (f32).
Two-layer fix:
1. Coordinate backbone -> f64. last_point, last_cursor_world,
dyn_anchor, dyn_ref become DVec3; UcsXform transforms, parse_coord,
the ucs helper fns, dyn_resolve_point, the OTRACK typed-distance
paths, command-line coordinate entry and the coordinate readout all
compute in f64. Screen/GPU/alignment stays f32 (snap engine, render
geometry, projection, UCS-icon/viewcube drawing) with casts only at
those boundaries.
2. Per-command committed storage -> DVec3. on_point already received a
DVec3, but many command impls narrowed it back to f32 (let pt =
pt.as_vec3(); Vec<Vec3> fields) and re-widened at build, quantizing
the committed coordinate. Migrated PLINE, SPLINE, MLINE, LENGTHEN,
INSERT, 3D primitives, every dimension/leader/mleader, ray, revcloud,
wipeout, attdef, table, tolerance, array center, mview, plot_window,
plus the QDIM_PLACE handler and qdim_collect_points. Preview,
hit-test and rubber-band paths keep f32 at the boundary.
Hatch manual boundaries are left for a follow-up: HatchModel.boundary
is structurally Arc<Vec<[f32;2]>> and needs a model-level widen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 21:26:40 +03:00
|
|
|
|
.fold((f64::MAX, f64::MIN), |(a, b), p| (a.min(p.x), b.max(p.x)));
|
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 (miny, maxy) = pts
|
|
|
|
|
|
.iter()
|
fix(precision): keep typed coordinates exact in f64 (#311)
Typed values were quantized to the f32 grid before reaching the
entity (e.g. a length of 2000.8 committed as 2000.8020), worsening
with magnitude. The interaction point layer was all glam::Vec3 (f32).
Two-layer fix:
1. Coordinate backbone -> f64. last_point, last_cursor_world,
dyn_anchor, dyn_ref become DVec3; UcsXform transforms, parse_coord,
the ucs helper fns, dyn_resolve_point, the OTRACK typed-distance
paths, command-line coordinate entry and the coordinate readout all
compute in f64. Screen/GPU/alignment stays f32 (snap engine, render
geometry, projection, UCS-icon/viewcube drawing) with casts only at
those boundaries.
2. Per-command committed storage -> DVec3. on_point already received a
DVec3, but many command impls narrowed it back to f32 (let pt =
pt.as_vec3(); Vec<Vec3> fields) and re-widened at build, quantizing
the committed coordinate. Migrated PLINE, SPLINE, MLINE, LENGTHEN,
INSERT, 3D primitives, every dimension/leader/mleader, ray, revcloud,
wipeout, attdef, table, tolerance, array center, mview, plot_window,
plus the QDIM_PLACE handler and qdim_collect_points. Preview,
hit-test and rubber-band paths keep f32 at the boundary.
Hatch manual boundaries are left for a follow-up: HatchModel.boundary
is structurally Arc<Vec<[f32;2]>> and needs a model-level widen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 21:26:40 +03:00
|
|
|
|
.fold((f64::MAX, f64::MIN), |(a, b), p| (a.min(p.y), b.max(p.y)));
|
2026-06-27 17:56:01 +03:00
|
|
|
|
let horizontal = (maxx - minx) >= (maxy - miny);
|
|
|
|
|
|
if horizontal {
|
|
|
|
|
|
pts.sort_by(|a, b| a.x.total_cmp(&b.x));
|
|
|
|
|
|
pts.dedup_by(|a, b| (a.x - b.x).abs() < 1e-4);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
pts.sort_by(|a, b| a.y.total_cmp(&b.y));
|
|
|
|
|
|
pts.dedup_by(|a, b| (a.y - b.y).abs() < 1e-4);
|
|
|
|
|
|
}
|
|
|
|
|
|
if pts.len() < 2 {
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_error(crate::t!("QDIM: endpoints collapse to a single position.").as_ref());
|
2026-06-27 17:56:01 +03:00
|
|
|
|
return Some(Task::none());
|
|
|
|
|
|
}
|
|
|
|
|
|
self.push_undo_snapshot(i, "QDIM");
|
fix(precision): keep typed coordinates exact in f64 (#311)
Typed values were quantized to the f32 grid before reaching the
entity (e.g. a length of 2000.8 committed as 2000.8020), worsening
with magnitude. The interaction point layer was all glam::Vec3 (f32).
Two-layer fix:
1. Coordinate backbone -> f64. last_point, last_cursor_world,
dyn_anchor, dyn_ref become DVec3; UcsXform transforms, parse_coord,
the ucs helper fns, dyn_resolve_point, the OTRACK typed-distance
paths, command-line coordinate entry and the coordinate readout all
compute in f64. Screen/GPU/alignment stays f32 (snap engine, render
geometry, projection, UCS-icon/viewcube drawing) with casts only at
those boundaries.
2. Per-command committed storage -> DVec3. on_point already received a
DVec3, but many command impls narrowed it back to f32 (let pt =
pt.as_vec3(); Vec<Vec3> fields) and re-widened at build, quantizing
the committed coordinate. Migrated PLINE, SPLINE, MLINE, LENGTHEN,
INSERT, 3D primitives, every dimension/leader/mleader, ray, revcloud,
wipeout, attdef, table, tolerance, array center, mview, plot_window,
plus the QDIM_PLACE handler and qdim_collect_points. Preview,
hit-test and rubber-band paths keep f32 at the boundary.
Hatch manual boundaries are left for a follow-up: HatchModel.boundary
is structurally Arc<Vec<[f32;2]>> and needs a model-level widen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 21:26:40 +03:00
|
|
|
|
let v = |p: glam::DVec3| {
|
|
|
|
|
|
acadrust::types::Vector3::new(p.x, p.y, p.z)
|
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-27 17:56:01 +03:00
|
|
|
|
let mut made = 0usize;
|
|
|
|
|
|
for w in pts.windows(2) {
|
|
|
|
|
|
let (p1, p2) = (w[0], w[1]);
|
|
|
|
|
|
let mut dim = acadrust::entities::DimensionLinear::new(v(p1), v(p2));
|
|
|
|
|
|
dim.rotation = if horizontal {
|
|
|
|
|
|
0.0
|
|
|
|
|
|
} else {
|
|
|
|
|
|
std::f64::consts::FRAC_PI_2
|
|
|
|
|
|
};
|
|
|
|
|
|
// Dim line passes through the picked perpendicular position.
|
|
|
|
|
|
let def = if horizontal {
|
fix(precision): keep typed coordinates exact in f64 (#311)
Typed values were quantized to the f32 grid before reaching the
entity (e.g. a length of 2000.8 committed as 2000.8020), worsening
with magnitude. The interaction point layer was all glam::Vec3 (f32).
Two-layer fix:
1. Coordinate backbone -> f64. last_point, last_cursor_world,
dyn_anchor, dyn_ref become DVec3; UcsXform transforms, parse_coord,
the ucs helper fns, dyn_resolve_point, the OTRACK typed-distance
paths, command-line coordinate entry and the coordinate readout all
compute in f64. Screen/GPU/alignment stays f32 (snap engine, render
geometry, projection, UCS-icon/viewcube drawing) with casts only at
those boundaries.
2. Per-command committed storage -> DVec3. on_point already received a
DVec3, but many command impls narrowed it back to f32 (let pt =
pt.as_vec3(); Vec<Vec3> fields) and re-widened at build, quantizing
the committed coordinate. Migrated PLINE, SPLINE, MLINE, LENGTHEN,
INSERT, 3D primitives, every dimension/leader/mleader, ray, revcloud,
wipeout, attdef, table, tolerance, array center, mview, plot_window,
plus the QDIM_PLACE handler and qdim_collect_points. Preview,
hit-test and rubber-band paths keep f32 at the boundary.
Hatch manual boundaries are left for a follow-up: HatchModel.boundary
is structurally Arc<Vec<[f32;2]>> and needs a model-level widen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 21:26:40 +03:00
|
|
|
|
glam::DVec3::new((p1.x + p2.x) * 0.5, place.y, 0.0)
|
2026-06-27 17:56:01 +03:00
|
|
|
|
} else {
|
fix(precision): keep typed coordinates exact in f64 (#311)
Typed values were quantized to the f32 grid before reaching the
entity (e.g. a length of 2000.8 committed as 2000.8020), worsening
with magnitude. The interaction point layer was all glam::Vec3 (f32).
Two-layer fix:
1. Coordinate backbone -> f64. last_point, last_cursor_world,
dyn_anchor, dyn_ref become DVec3; UcsXform transforms, parse_coord,
the ucs helper fns, dyn_resolve_point, the OTRACK typed-distance
paths, command-line coordinate entry and the coordinate readout all
compute in f64. Screen/GPU/alignment stays f32 (snap engine, render
geometry, projection, UCS-icon/viewcube drawing) with casts only at
those boundaries.
2. Per-command committed storage -> DVec3. on_point already received a
DVec3, but many command impls narrowed it back to f32 (let pt =
pt.as_vec3(); Vec<Vec3> fields) and re-widened at build, quantizing
the committed coordinate. Migrated PLINE, SPLINE, MLINE, LENGTHEN,
INSERT, 3D primitives, every dimension/leader/mleader, ray, revcloud,
wipeout, attdef, table, tolerance, array center, mview, plot_window,
plus the QDIM_PLACE handler and qdim_collect_points. Preview,
hit-test and rubber-band paths keep f32 at the boundary.
Hatch manual boundaries are left for a follow-up: HatchModel.boundary
is structurally Arc<Vec<[f32;2]>> and needs a model-level widen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 21:26:40 +03:00
|
|
|
|
glam::DVec3::new(place.x, (p1.y + p2.y) * 0.5, 0.0)
|
2026-06-27 17:56:01 +03:00
|
|
|
|
};
|
|
|
|
|
|
dim.definition_point = v(def);
|
|
|
|
|
|
dim.base.definition_point = v(def);
|
|
|
|
|
|
dim.base.actual_measurement = dim.measurement();
|
2026-08-04 23:17:54 +03:00
|
|
|
|
let entity = acadrust::EntityType::Dimension(
|
2026-06-27 17:56:01 +03:00
|
|
|
|
acadrust::entities::Dimension::Linear(dim),
|
2026-08-04 23:17:54 +03:00
|
|
|
|
);
|
|
|
|
|
|
self.commit_entity(plane.place_entity(entity));
|
2026-06-27 17:56:01 +03:00
|
|
|
|
made += 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
self.tabs[i].dirty = true;
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_output(crate::tf!("QDIM {made} dimensions created.").as_ref());
|
2026-06-27 17:56:01 +03:00
|
|
|
|
return Some(Task::none());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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
|
|
|
|
"DIMEDIT" => {
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
use crate::modules::annotate::dimedit::DimEditCommand;
|
|
|
|
|
|
let cmd = DimEditCommand::new();
|
|
|
|
|
|
self.command_line.push_info(&cmd.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
"DIMTEDIT" | "DIMTED" => {
|
|
|
|
|
|
use crate::modules::annotate::dimtedit::DimTeditCommand;
|
|
|
|
|
|
let cmd = DimTeditCommand::new();
|
|
|
|
|
|
self.command_line.push_info(&cmd.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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
|
|
|
|
"DIMBREAK" => {
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
use crate::modules::annotate::dimbreak::DimBreakCommand;
|
|
|
|
|
|
let cmd = DimBreakCommand::new();
|
|
|
|
|
|
self.command_line.push_info(&cmd.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
"DIMSPACE" | "DSPACE" => {
|
|
|
|
|
|
use crate::modules::annotate::dimspace::DimSpaceCommand;
|
|
|
|
|
|
let cmd = DimSpaceCommand::new();
|
|
|
|
|
|
self.command_line.push_info(&cmd.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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
|
|
|
|
"DIMJOGLINE" => {
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
use crate::modules::annotate::dimjogline::DimJogLineCommand;
|
|
|
|
|
|
let cmd = DimJogLineCommand::new();
|
|
|
|
|
|
self.command_line.push_info(&cmd.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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
|
|
|
|
"MLEADERADD" => {
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
use crate::modules::annotate::mleader_edit::MLeaderAddCommand;
|
|
|
|
|
|
let cmd = MLeaderAddCommand::new();
|
|
|
|
|
|
self.command_line.push_info(&cmd.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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
|
|
|
|
"MLEADERREMOVE" => {
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
use crate::modules::annotate::mleader_edit::MLeaderRemoveCommand;
|
|
|
|
|
|
let cmd = MLeaderRemoveCommand::new();
|
|
|
|
|
|
self.command_line.push_info(&cmd.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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
|
|
|
|
"MLEADERALIGN" => {
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
use crate::modules::annotate::mleader_edit::MLeaderAlignCommand;
|
|
|
|
|
|
let cmd = MLeaderAlignCommand::new();
|
|
|
|
|
|
self.command_line.push_info(&cmd.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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
|
|
|
|
"MLEADERCOLLECT" => {
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
use crate::modules::annotate::mleader_edit::MLeaderCollectCommand;
|
|
|
|
|
|
let cmd = MLeaderCollectCommand::new();
|
|
|
|
|
|
self.command_line.push_info(&cmd.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-29 23:03:06 +03:00
|
|
|
|
"ZOOM EXTENTS ALL" | "ZOOM EXTENTS ALL VIEWPORTS" | "ZEA" => {
|
|
|
|
|
|
self.tabs[i].scene.fit_all_model_viewports();
|
2026-08-02 22:09:25 +03:00
|
|
|
|
self.command_line.push_output(crate::t!("Zoom Extents — All Viewports").as_ref());
|
2026-07-29 23:03:06 +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
|
|
|
|
"ZOOM EXTENTS" | "ZOOMEXTENTS" | "ZE" => {
|
|
|
|
|
|
self.tabs[i].scene.fit_all();
|
2026-08-02 22:09:25 +03:00
|
|
|
|
self.command_line.push_output(crate::t!("Zoom Extents").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
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
"ZOOM IN" | "ZI" => {
|
|
|
|
|
|
self.tabs[i].scene.zoom_camera(1.0 / 1.5);
|
2026-08-02 22:09:25 +03:00
|
|
|
|
self.command_line.push_output(crate::t!("Zoom In").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
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
"ZOOM OUT" | "ZO" => {
|
|
|
|
|
|
self.tabs[i].scene.zoom_camera(1.5);
|
2026-08-02 22:09:25 +03:00
|
|
|
|
self.command_line.push_output(crate::t!("Zoom Out").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
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-27 16:15:28 +03:00
|
|
|
|
// ZOOM ALL — fit the configured drawing limits.
|
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
|
|
|
|
"ZOOM ALL" | "ZOOM A" | "ZA" => {
|
2026-07-27 16:15:28 +03:00
|
|
|
|
self.tabs[i].scene.fit_all_with_limits();
|
2026-08-02 22:09:25 +03:00
|
|
|
|
self.command_line.push_output(crate::t!("Zoom All").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
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ZOOM SCALE — set zoom factor (e.g. "ZOOM SCALE 2" or "ZS 0.5")
|
|
|
|
|
|
cmd if cmd.starts_with("ZOOM SCALE ") || cmd.starts_with("ZS ") => {
|
|
|
|
|
|
let rest = cmd
|
|
|
|
|
|
.split_once(' ')
|
|
|
|
|
|
.and_then(|(_, r)| r.split_once(' ').map(|(_, v)| v).or(Some(r)))
|
|
|
|
|
|
.unwrap_or("1");
|
|
|
|
|
|
if let Ok(factor) = rest.trim().parse::<f32>() {
|
|
|
|
|
|
if factor > 0.0 {
|
|
|
|
|
|
self.tabs[i].scene.zoom_camera(1.0 / factor);
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_output(crate::tf!("Zoom Scale ×{factor:.3}").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(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
|
|
|
|
"PLOTWINDOW" => {
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
use crate::modules::view::plot_window::PlotWindowCommand;
|
|
|
|
|
|
let cmd = PlotWindowCommand::new();
|
|
|
|
|
|
self.command_line.push_info(&cmd.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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
|
|
|
|
"QUICKPRINT" => {
|
2026-07-09 12:57:35 +03:00
|
|
|
|
use crate::modules::view::quick_print::QuickPrintCommand;
|
|
|
|
|
|
let cmd = QuickPrintCommand::new();
|
|
|
|
|
|
self.command_line.push_info(&cmd.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-21 23:18:18 +03:00
|
|
|
|
// Bare ZOOM prompts with the mode options as clickable keywords;
|
|
|
|
|
|
// the sub-keyword forms (ZOOM EXTENTS / WINDOW / …) run directly.
|
|
|
|
|
|
"ZOOM" => {
|
|
|
|
|
|
use crate::command::KeywordCommand;
|
|
|
|
|
|
let c = KeywordCommand::new(
|
|
|
|
|
|
"ZOOM",
|
2026-07-29 23:03:06 +03:00
|
|
|
|
"ZOOM [Window / Extents / Extents All / All / In / Out / Scale]:",
|
2026-07-21 23:18:18 +03:00
|
|
|
|
vec![
|
|
|
|
|
|
("Window", "WINDOW", None),
|
|
|
|
|
|
("Extents", "EXTENTS", None),
|
2026-07-29 23:03:06 +03:00
|
|
|
|
("Extents All", "EXTENTS ALL", None),
|
2026-07-21 23:18:18 +03:00
|
|
|
|
("All", "ALL", None),
|
|
|
|
|
|
("In", "IN", None),
|
|
|
|
|
|
("Out", "OUT", None),
|
|
|
|
|
|
("Scale", "SCALE", Some("ZOOM scale factor (e.g. 2 or 0.5):")),
|
|
|
|
|
|
],
|
|
|
|
|
|
);
|
|
|
|
|
|
self.command_line.push_info(&c.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(c));
|
|
|
|
|
|
}
|
|
|
|
|
|
"ZOOM WINDOW" | "ZOOM W" | "ZW" => {
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
use crate::modules::view::zoom_window::ZoomWindowCommand;
|
|
|
|
|
|
let new_cmd = ZoomWindowCommand::new();
|
|
|
|
|
|
self.command_line.push_info(&new_cmd.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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
|
|
|
|
"STRETCH" => {
|
2026-07-21 22:35:35 +03:00
|
|
|
|
// With a prior selection the window only marks which points
|
|
|
|
|
|
// move. With none, the crossing window itself selects the
|
|
|
|
|
|
// objects — one round instead of a separate object-selection
|
|
|
|
|
|
// step (#338); the command emits StretchWindow for the host
|
|
|
|
|
|
// to resolve.
|
|
|
|
|
|
use crate::modules::draw::modify::stretch::StretchCommand;
|
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 handles: Vec<_> = self.tabs[i]
|
|
|
|
|
|
.scene
|
|
|
|
|
|
.selected_entities()
|
|
|
|
|
|
.into_iter()
|
|
|
|
|
|
.map(|(h, _)| h)
|
|
|
|
|
|
.collect();
|
2026-07-21 22:35:35 +03:00
|
|
|
|
let wires = if handles.is_empty() {
|
|
|
|
|
|
Vec::new()
|
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-07-21 22:35:35 +03:00
|
|
|
|
self.tabs[i].scene.wire_models_for(&handles)
|
|
|
|
|
|
};
|
|
|
|
|
|
let new_cmd = StretchCommand::new(handles, wires);
|
|
|
|
|
|
self.command_line.push_info(&new_cmd.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
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
|
|
|
|
"FILLET" => {
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
use crate::modules::draw::modify::fillet::FilletCommand;
|
|
|
|
|
|
let entities: Vec<_> = self.tabs[i]
|
|
|
|
|
|
.scene
|
|
|
|
|
|
.entity_wires()
|
|
|
|
|
|
.iter()
|
|
|
|
|
|
.filter_map(|w| {
|
|
|
|
|
|
let h = Scene::handle_from_wire_name(&w.name)?;
|
|
|
|
|
|
self.tabs[i]
|
|
|
|
|
|
.scene
|
|
|
|
|
|
.document
|
|
|
|
|
|
.get_entity(h)
|
|
|
|
|
|
.cloned()
|
|
|
|
|
|
.map(|e| (h, e))
|
|
|
|
|
|
})
|
|
|
|
|
|
.collect();
|
|
|
|
|
|
let all_entities: Vec<_> = entities.into_iter().map(|(_, e)| e).collect();
|
|
|
|
|
|
let new_cmd = FilletCommand::new(
|
|
|
|
|
|
crate::modules::draw::defaults::get_fillet_radius(),
|
|
|
|
|
|
all_entities,
|
|
|
|
|
|
);
|
|
|
|
|
|
self.command_line.push_info(&new_cmd.prompt());
|
2026-07-29 21:21:26 +03:00
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
"BLEND" | "BLE" => {
|
|
|
|
|
|
use crate::modules::draw::modify::blend::BlendCommand;
|
|
|
|
|
|
let all_entities: Vec<_> = self.tabs[i]
|
|
|
|
|
|
.scene
|
|
|
|
|
|
.entity_wires()
|
|
|
|
|
|
.iter()
|
|
|
|
|
|
.filter_map(|wire| {
|
|
|
|
|
|
let handle = Scene::handle_from_wire_name(&wire.name)?;
|
|
|
|
|
|
self.tabs[i].scene.document.get_entity(handle).cloned()
|
|
|
|
|
|
})
|
|
|
|
|
|
.collect();
|
|
|
|
|
|
let new_cmd = BlendCommand::new(all_entities);
|
|
|
|
|
|
self.command_line.push_info(&new_cmd.prompt());
|
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].active_cmd = Some(Box::new(new_cmd));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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
|
|
|
|
"ARRAY" | "ARRAYRECT" => {
|
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 handles: Vec<_> = self.tabs[i]
|
|
|
|
|
|
.scene
|
|
|
|
|
|
.selected_entities()
|
|
|
|
|
|
.into_iter()
|
|
|
|
|
|
.map(|(h, _)| h)
|
|
|
|
|
|
.collect();
|
|
|
|
|
|
if handles.is_empty() {
|
|
|
|
|
|
use crate::modules::draw::select::SelectObjectsCommand;
|
|
|
|
|
|
let cmd = SelectObjectsCommand::new("ARRAYRECT");
|
|
|
|
|
|
self.command_line.push_info(&cmd.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
|
|
|
|
|
} else {
|
|
|
|
|
|
use crate::modules::draw::modify::array::ArrayRectCommand;
|
|
|
|
|
|
let wires = self.tabs[i].scene.wire_models_for(&handles);
|
|
|
|
|
|
let new_cmd = ArrayRectCommand::new(handles, wires);
|
|
|
|
|
|
self.command_line.push_info(&new_cmd.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
"ARRAYPOLAR" => {
|
|
|
|
|
|
let handles: Vec<_> = self.tabs[i]
|
|
|
|
|
|
.scene
|
|
|
|
|
|
.selected_entities()
|
|
|
|
|
|
.into_iter()
|
|
|
|
|
|
.map(|(h, _)| h)
|
|
|
|
|
|
.collect();
|
|
|
|
|
|
if handles.is_empty() {
|
|
|
|
|
|
use crate::modules::draw::select::SelectObjectsCommand;
|
|
|
|
|
|
let cmd = SelectObjectsCommand::new("ARRAYPOLAR");
|
|
|
|
|
|
self.command_line.push_info(&cmd.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
|
|
|
|
|
} else {
|
|
|
|
|
|
use crate::modules::draw::modify::array::ArrayPolarCommand;
|
|
|
|
|
|
let wires = self.tabs[i].scene.wire_models_for(&handles);
|
|
|
|
|
|
let new_cmd = ArrayPolarCommand::new(handles, wires);
|
|
|
|
|
|
self.command_line.push_info(&new_cmd.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
"ARRAYPATH" => {
|
|
|
|
|
|
let handles: Vec<_> = self.tabs[i]
|
|
|
|
|
|
.scene
|
|
|
|
|
|
.selected_entities()
|
|
|
|
|
|
.into_iter()
|
|
|
|
|
|
.map(|(h, _)| h)
|
|
|
|
|
|
.collect();
|
|
|
|
|
|
if handles.is_empty() {
|
|
|
|
|
|
use crate::modules::draw::select::SelectObjectsCommand;
|
|
|
|
|
|
let cmd = SelectObjectsCommand::new("ARRAYPATH");
|
|
|
|
|
|
self.command_line.push_info(&cmd.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
|
|
|
|
|
} else {
|
|
|
|
|
|
use crate::modules::draw::modify::array::ArrayPathCommand;
|
|
|
|
|
|
let wires = self.tabs[i].scene.wire_models_for(&handles);
|
|
|
|
|
|
let all_entities: Vec<_> = self.tabs[i]
|
|
|
|
|
|
.scene
|
|
|
|
|
|
.entity_wires()
|
|
|
|
|
|
.iter()
|
|
|
|
|
|
.filter_map(|w| {
|
|
|
|
|
|
let h = Scene::handle_from_wire_name(&w.name)?;
|
|
|
|
|
|
self.tabs[i].scene.document.get_entity(h).cloned()
|
|
|
|
|
|
})
|
|
|
|
|
|
.collect();
|
|
|
|
|
|
let new_cmd = ArrayPathCommand::new(handles, wires, all_entities);
|
|
|
|
|
|
self.command_line.push_info(&new_cmd.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
"ARRAY3D" | "3DARRAY" => {
|
|
|
|
|
|
let handles: Vec<_> = self.tabs[i]
|
|
|
|
|
|
.scene
|
|
|
|
|
|
.selected_entities()
|
|
|
|
|
|
.into_iter()
|
|
|
|
|
|
.map(|(h, _)| h)
|
|
|
|
|
|
.collect();
|
|
|
|
|
|
if handles.is_empty() {
|
|
|
|
|
|
use crate::modules::draw::select::SelectObjectsCommand;
|
|
|
|
|
|
let cmd = SelectObjectsCommand::new("ARRAY3D");
|
|
|
|
|
|
self.command_line.push_info(&cmd.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
|
|
|
|
|
} else {
|
|
|
|
|
|
use crate::modules::draw::modify::array::Array3DCommand;
|
|
|
|
|
|
let new_cmd = Array3DCommand::new(handles);
|
|
|
|
|
|
self.command_line.push_info(&new_cmd.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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
|
|
|
|
"CHAMFER" => {
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
use crate::modules::draw::modify::fillet::ChamferCommand;
|
|
|
|
|
|
let entities: Vec<_> = self.tabs[i]
|
|
|
|
|
|
.scene
|
|
|
|
|
|
.entity_wires()
|
|
|
|
|
|
.iter()
|
|
|
|
|
|
.filter_map(|w| {
|
|
|
|
|
|
let h = Scene::handle_from_wire_name(&w.name)?;
|
|
|
|
|
|
self.tabs[i]
|
|
|
|
|
|
.scene
|
|
|
|
|
|
.document
|
|
|
|
|
|
.get_entity(h)
|
|
|
|
|
|
.cloned()
|
|
|
|
|
|
.map(|e| (h, e))
|
|
|
|
|
|
})
|
|
|
|
|
|
.collect();
|
|
|
|
|
|
let all_entities: Vec<_> = entities.into_iter().map(|(_, e)| e).collect();
|
|
|
|
|
|
let new_cmd = ChamferCommand::new(
|
|
|
|
|
|
crate::modules::draw::defaults::get_chamfer_dist1(),
|
|
|
|
|
|
all_entities,
|
|
|
|
|
|
);
|
|
|
|
|
|
self.command_line.push_info(&new_cmd.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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
|
|
|
|
"EXPLODE" => {
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
use crate::modules::draw::modify::explode::explode_entity;
|
|
|
|
|
|
let entities: Vec<_> = self.tabs[i].scene.selected_entities().into_iter().collect();
|
|
|
|
|
|
if entities.is_empty() {
|
|
|
|
|
|
use crate::modules::draw::select::SelectObjectsCommand;
|
|
|
|
|
|
let cmd = SelectObjectsCommand::new("EXPLODE");
|
|
|
|
|
|
self.command_line.push_info(&cmd.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
|
|
|
|
|
} else {
|
|
|
|
|
|
let replacements: Vec<(acadrust::Handle, Vec<acadrust::EntityType>)> = entities
|
|
|
|
|
|
.iter()
|
|
|
|
|
|
.filter_map(|(h, e)| {
|
|
|
|
|
|
let pieces = explode_entity(e, &self.tabs[i].scene.document);
|
|
|
|
|
|
if pieces.is_empty() {
|
|
|
|
|
|
None
|
|
|
|
|
|
} else {
|
|
|
|
|
|
Some((*h, pieces))
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
.collect();
|
|
|
|
|
|
let exploded = replacements.len();
|
|
|
|
|
|
if exploded > 0 {
|
|
|
|
|
|
self.push_undo_snapshot(i, "EXPLODE");
|
|
|
|
|
|
}
|
|
|
|
|
|
for (handle, pieces) in replacements {
|
|
|
|
|
|
self.tabs[i].scene.erase_entities(&[handle]);
|
|
|
|
|
|
for piece in pieces {
|
|
|
|
|
|
self.tabs[i].scene.add_entity(piece);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if exploded > 0 {
|
|
|
|
|
|
self.tabs[i].dirty = true;
|
|
|
|
|
|
self.refresh_properties();
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_output(crate::tf!("{exploded} object(s) exploded.").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_info(crate::t!("EXPLODE: no explodable objects selected.").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(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
|
|
|
|
"OFFSET" => {
|
2026-07-21 11:20:04 +03:00
|
|
|
|
use crate::modules::draw::modify::offset::{is_offsettable, OffsetCommand};
|
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 all_entities: Vec<_> = self.tabs[i]
|
|
|
|
|
|
.scene
|
|
|
|
|
|
.entity_wires()
|
|
|
|
|
|
.iter()
|
|
|
|
|
|
.filter_map(|w| {
|
|
|
|
|
|
let h = Scene::handle_from_wire_name(&w.name)?;
|
|
|
|
|
|
self.tabs[i].scene.document.get_entity(h).cloned()
|
|
|
|
|
|
})
|
|
|
|
|
|
.collect();
|
2026-07-21 11:20:04 +03:00
|
|
|
|
// Pick-first (#422): with offsettable objects already selected,
|
|
|
|
|
|
// skip the pick step and go straight to distance / side.
|
|
|
|
|
|
let preselected: Vec<_> = self.tabs[i]
|
|
|
|
|
|
.scene
|
|
|
|
|
|
.selected_entities()
|
|
|
|
|
|
.into_iter()
|
|
|
|
|
|
.map(|(_, e)| e.clone())
|
|
|
|
|
|
.filter(is_offsettable)
|
|
|
|
|
|
.collect();
|
|
|
|
|
|
let new_cmd = if preselected.is_empty() {
|
|
|
|
|
|
OffsetCommand::new(all_entities)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
OffsetCommand::with_selection(all_entities, preselected)
|
|
|
|
|
|
};
|
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));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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
|
|
|
|
"TRIM" => {
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
use crate::modules::draw::modify::trim::TrimCommand;
|
|
|
|
|
|
let entities: Vec<_> = self.tabs[i]
|
|
|
|
|
|
.scene
|
|
|
|
|
|
.entity_wires()
|
|
|
|
|
|
.iter()
|
|
|
|
|
|
.filter_map(|w| {
|
|
|
|
|
|
let h = Scene::handle_from_wire_name(&w.name)?;
|
|
|
|
|
|
self.tabs[i]
|
|
|
|
|
|
.scene
|
|
|
|
|
|
.document
|
|
|
|
|
|
.get_entity(h)
|
|
|
|
|
|
.cloned()
|
|
|
|
|
|
.map(|e| (h, e))
|
|
|
|
|
|
})
|
|
|
|
|
|
.collect();
|
|
|
|
|
|
let all_entities: Vec<_> = entities.into_iter().map(|(_, e)| e).collect();
|
|
|
|
|
|
let new_cmd = TrimCommand::new(all_entities);
|
|
|
|
|
|
self.command_line.push_info(&new_cmd.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-09 16:26:24 +03:00
|
|
|
|
"EXTRIM" => {
|
|
|
|
|
|
use crate::modules::draw::modify::trim::ExtrimCommand;
|
|
|
|
|
|
let all: Vec<_> = self.tabs[i]
|
|
|
|
|
|
.scene
|
|
|
|
|
|
.entity_wires()
|
|
|
|
|
|
.iter()
|
|
|
|
|
|
.filter_map(|w| {
|
|
|
|
|
|
let h = Scene::handle_from_wire_name(&w.name)?;
|
|
|
|
|
|
self.tabs[i].scene.document.get_entity(h).cloned().map(|e| (h, e))
|
|
|
|
|
|
})
|
|
|
|
|
|
.collect();
|
|
|
|
|
|
let new_cmd = ExtrimCommand::new(all);
|
|
|
|
|
|
self.command_line.push_info(&new_cmd.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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
|
|
|
|
"EXTEND" => {
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
use crate::modules::draw::modify::trim::ExtendCommand;
|
|
|
|
|
|
let entities: Vec<_> = self.tabs[i]
|
|
|
|
|
|
.scene
|
|
|
|
|
|
.entity_wires()
|
|
|
|
|
|
.iter()
|
|
|
|
|
|
.filter_map(|w| {
|
|
|
|
|
|
let h = Scene::handle_from_wire_name(&w.name)?;
|
|
|
|
|
|
self.tabs[i]
|
|
|
|
|
|
.scene
|
|
|
|
|
|
.document
|
|
|
|
|
|
.get_entity(h)
|
|
|
|
|
|
.cloned()
|
|
|
|
|
|
.map(|e| (h, e))
|
|
|
|
|
|
})
|
|
|
|
|
|
.collect();
|
|
|
|
|
|
let all_entities: Vec<_> = entities.into_iter().map(|(_, e)| e).collect();
|
|
|
|
|
|
let new_cmd = ExtendCommand::new(all_entities);
|
|
|
|
|
|
self.command_line.push_info(&new_cmd.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
// DIMJOGGED <DJO> — create a jogged radius dimension on the selected
|
|
|
|
|
|
// arc/circle: a standard radius dim carrying an OCS_JOGGED XData marker
|
|
|
|
|
|
// that the geometry generator renders with a foreshortened zig-zag.
|
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
|
|
|
|
"DIMJOGGED" | "DIMJOG" => {
|
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
|
|
|
|
use acadrust::entities::{Dimension, DimensionRadius};
|
|
|
|
|
|
use acadrust::types::Vector3;
|
|
|
|
|
|
use acadrust::xdata::{ExtendedDataRecord, XDataValue};
|
|
|
|
|
|
let found =
|
|
|
|
|
|
self.tabs[i]
|
|
|
|
|
|
.scene
|
|
|
|
|
|
.selected_entities()
|
|
|
|
|
|
.iter()
|
|
|
|
|
|
.find_map(|(_, e)| match e {
|
|
|
|
|
|
acadrust::EntityType::Arc(a) => Some((a.center, a.radius)),
|
|
|
|
|
|
acadrust::EntityType::Circle(c) => Some((c.center, c.radius)),
|
|
|
|
|
|
_ => None,
|
|
|
|
|
|
});
|
|
|
|
|
|
let Some((center, radius)) = found else {
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_error(crate::t!("DIMJOGGED: select an arc or circle first.").as_ref());
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
return None;
|
|
|
|
|
|
};
|
|
|
|
|
|
if radius <= 0.0 {
|
2026-08-02 22:09:25 +03:00
|
|
|
|
self.command_line.push_error(crate::t!("DIMJOGGED: invalid radius.").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 None;
|
|
|
|
|
|
}
|
|
|
|
|
|
let k = std::f64::consts::FRAC_1_SQRT_2;
|
|
|
|
|
|
let arc_point =
|
|
|
|
|
|
Vector3::new(center.x + radius * k, center.y + radius * k, center.z);
|
|
|
|
|
|
let mut dim = DimensionRadius::new(center, arc_point);
|
|
|
|
|
|
dim.base.common.layer = self.tabs[i].active_layer.clone();
|
|
|
|
|
|
let mut rec = ExtendedDataRecord::new("OCS_JOGGED");
|
|
|
|
|
|
rec.add_value(XDataValue::String("1".to_string()));
|
|
|
|
|
|
dim.base.common.extended_data.add_record(rec);
|
|
|
|
|
|
self.push_undo_snapshot(i, "DIMJOGGED");
|
|
|
|
|
|
self.tabs[i]
|
|
|
|
|
|
.scene
|
|
|
|
|
|
.add_entity(acadrust::EntityType::Dimension(Dimension::Radius(dim)));
|
|
|
|
|
|
self.tabs[i].dirty = true;
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_output(crate::t!("DIMJOGGED: created a jogged radius dimension.").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
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ARCTEXT <text> — lay the text out as one Text entity per character
|
|
|
|
|
|
// along the selected arc, each rotated to follow the tangent.
|
2026-07-16 14:37:10 +03:00
|
|
|
|
"ARCTEXT" => {
|
|
|
|
|
|
use crate::command::SelectThenValueCommand;
|
|
|
|
|
|
let has_sel = !self.tabs[i].scene.selected_entities().is_empty();
|
|
|
|
|
|
let c = SelectThenValueCommand::new(
|
|
|
|
|
|
"ARCTEXT",
|
|
|
|
|
|
"ARCTEXT text to place along the arc:",
|
|
|
|
|
|
has_sel,
|
|
|
|
|
|
);
|
|
|
|
|
|
self.command_line.push_info(&c.prompt());
|
|
|
|
|
|
self.tabs[i].active_cmd = Some(Box::new(c));
|
|
|
|
|
|
}
|
|
|
|
|
|
cmd if cmd.starts_with("ARCTEXT ") => {
|
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
|
|
|
|
use acadrust::entities::Text;
|
|
|
|
|
|
use acadrust::types::Vector3;
|
|
|
|
|
|
let text = cmd.strip_prefix("ARCTEXT").unwrap_or("").trim().to_string();
|
|
|
|
|
|
if text.is_empty() {
|
|
|
|
|
|
self.command_line.push_info(
|
|
|
|
|
|
"Usage: ARCTEXT <text> (select an arc first; the text follows it)",
|
|
|
|
|
|
);
|
|
|
|
|
|
return None;
|
|
|
|
|
|
}
|
|
|
|
|
|
let arc =
|
|
|
|
|
|
self.tabs[i]
|
|
|
|
|
|
.scene
|
|
|
|
|
|
.selected_entities()
|
|
|
|
|
|
.iter()
|
|
|
|
|
|
.find_map(|(_, e)| match e {
|
|
|
|
|
|
acadrust::EntityType::Arc(a) => Some(a.clone()),
|
|
|
|
|
|
_ => None,
|
|
|
|
|
|
});
|
|
|
|
|
|
let Some(arc) = arc else {
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_error(crate::t!("ARCTEXT: select an arc first.").as_ref());
|
feat(commands): expand standard CAD command coverage
Integrate a large batch of standard CAD commands and fix several
name/behaviour mismatches, with no product references in code.
3D solids (reusing the truck B-rep pipeline):
- SLICE, INTERFERE, PRESSPULL, POLYSOLID, PYRAMID, SECTION
- 3DROTATE, 3DMIRROR, 3DALIGN (cached-solid transforms)
Annotation / text:
- ARCTEXT, SPLINEFIT (Catmull-Rom to Bezier), DIMJOGGED, OBJECTSCALE
(XData markers honoured by the existing geometry-gen / tessellator)
Blocks / misc:
- NCOPY (block-content extraction via explode_entity)
- OPTIONS, SYNCPVIEWPORTS, THICKEN, plus earlier toggles, aliases,
system variables, and layer/style/view commands
POINTCLOUD/RECAP and UNDERLAYLAYERS/UOSNAP are intentionally left out:
they require a proprietary binary / PDF-layer parser, a GPU point
renderer, and real test files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 08:23:58 +03:00
|
|
|
|
return None;
|
|
|
|
|
|
};
|
|
|
|
|
|
let chars: Vec<char> = text.chars().filter(|c| !c.is_control()).collect();
|
|
|
|
|
|
if chars.is_empty() {
|
2026-08-02 22:09:25 +03:00
|
|
|
|
self.command_line.push_error(crate::t!("ARCTEXT: no printable text.").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 None;
|
|
|
|
|
|
}
|
|
|
|
|
|
let n = chars.len();
|
|
|
|
|
|
let mut span = arc.end_angle - arc.start_angle;
|
|
|
|
|
|
if span <= 0.0 {
|
|
|
|
|
|
span += std::f64::consts::TAU;
|
|
|
|
|
|
}
|
|
|
|
|
|
let height = (arc.radius * 0.12).max(0.1);
|
|
|
|
|
|
self.push_undo_snapshot(i, "ARCTEXT");
|
|
|
|
|
|
for (k, ch) in chars.iter().enumerate() {
|
|
|
|
|
|
let ang = arc.start_angle + span * (k as f64 + 0.5) / n as f64;
|
|
|
|
|
|
let pos = Vector3::new(
|
|
|
|
|
|
arc.center.x + arc.radius * ang.cos(),
|
|
|
|
|
|
arc.center.y + arc.radius * ang.sin(),
|
|
|
|
|
|
arc.center.z,
|
|
|
|
|
|
);
|
|
|
|
|
|
let t = Text::with_value(ch.to_string(), pos)
|
|
|
|
|
|
.with_height(height)
|
|
|
|
|
|
.with_rotation(ang + std::f64::consts::FRAC_PI_2);
|
|
|
|
|
|
self.tabs[i].scene.add_entity(acadrust::EntityType::Text(t));
|
|
|
|
|
|
}
|
|
|
|
|
|
self.tabs[i].dirty = true;
|
|
|
|
|
|
self.command_line
|
2026-08-02 22:09:25 +03:00
|
|
|
|
.push_output(crate::tf!("ARCTEXT: placed {n} character(s) along the arc.").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
|
|
|
|
_ => return None,
|
|
|
|
|
|
}
|
|
|
|
|
|
Some(self.finish_dispatch(cmd))
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Find the last placed linear or aligned dimension in the document.
|
|
|
|
|
|
/// Returns `(first_point, second_point, definition_point, rotation_rad)` in world-space.
|
|
|
|
|
|
fn find_last_linear_dim(
|
|
|
|
|
|
scene: &crate::scene::Scene,
|
2026-06-27 17:47:50 +03:00
|
|
|
|
) -> Option<(glam::Vec3, glam::Vec3, glam::Vec3, f64, f64)> {
|
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::entities::Dimension;
|
|
|
|
|
|
let mut best_handle: u64 = 0;
|
2026-06-27 17:47:50 +03:00
|
|
|
|
let mut result: Option<(glam::Vec3, glam::Vec3, glam::Vec3, f64, f64)> = None;
|
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:51:29 +03:00
|
|
|
|
|
|
|
|
|
|
for entity in scene.document.entities() {
|
|
|
|
|
|
if let acadrust::EntityType::Dimension(dim) = entity {
|
|
|
|
|
|
let h = entity.common().handle.value();
|
|
|
|
|
|
if h <= best_handle {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
let item = match dim {
|
|
|
|
|
|
Dimension::Linear(d) => {
|
|
|
|
|
|
let p1 = glam::Vec3::new(
|
|
|
|
|
|
d.first_point.x as f32,
|
|
|
|
|
|
d.first_point.y as f32,
|
|
|
|
|
|
d.first_point.z as f32,
|
|
|
|
|
|
);
|
|
|
|
|
|
let p2 = glam::Vec3::new(
|
|
|
|
|
|
d.second_point.x as f32,
|
|
|
|
|
|
d.second_point.y as f32,
|
|
|
|
|
|
d.second_point.z as f32,
|
|
|
|
|
|
);
|
|
|
|
|
|
let dp = glam::Vec3::new(
|
|
|
|
|
|
d.base.definition_point.x as f32,
|
|
|
|
|
|
d.base.definition_point.y as f32,
|
|
|
|
|
|
d.base.definition_point.z as f32,
|
|
|
|
|
|
);
|
2026-06-27 17:47:50 +03:00
|
|
|
|
Some((p1, p2, dp, d.rotation, d.base.text_rotation))
|
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
|
|
|
|
}
|
|
|
|
|
|
Dimension::Aligned(d) => {
|
|
|
|
|
|
let p1 = glam::Vec3::new(
|
|
|
|
|
|
d.first_point.x as f32,
|
|
|
|
|
|
d.first_point.y as f32,
|
|
|
|
|
|
d.first_point.z as f32,
|
|
|
|
|
|
);
|
|
|
|
|
|
let p2 = glam::Vec3::new(
|
|
|
|
|
|
d.second_point.x as f32,
|
|
|
|
|
|
d.second_point.y as f32,
|
|
|
|
|
|
d.second_point.z as f32,
|
|
|
|
|
|
);
|
|
|
|
|
|
let dp = glam::Vec3::new(
|
|
|
|
|
|
d.base.definition_point.x as f32,
|
|
|
|
|
|
d.base.definition_point.y as f32,
|
|
|
|
|
|
d.base.definition_point.z as f32,
|
|
|
|
|
|
);
|
|
|
|
|
|
let dx = (d.second_point.x - d.first_point.x) as f32;
|
|
|
|
|
|
let dy = (d.second_point.y - d.first_point.y) as f32;
|
|
|
|
|
|
let rot = dy.atan2(dx) as f64;
|
2026-06-27 17:47:50 +03:00
|
|
|
|
Some((p1, p2, dp, rot, d.base.text_rotation))
|
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,
|
|
|
|
|
|
};
|
|
|
|
|
|
if let Some(data) = item {
|
|
|
|
|
|
best_handle = h;
|
|
|
|
|
|
result = Some(data);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
result
|
|
|
|
|
|
}
|
2026-06-27 17:56:01 +03:00
|
|
|
|
|
|
|
|
|
|
/// Collect candidate dimension endpoints from an entity for QDIM — line ends,
|
|
|
|
|
|
/// polyline vertices, arc endpoints — in world space.
|
fix(precision): keep typed coordinates exact in f64 (#311)
Typed values were quantized to the f32 grid before reaching the
entity (e.g. a length of 2000.8 committed as 2000.8020), worsening
with magnitude. The interaction point layer was all glam::Vec3 (f32).
Two-layer fix:
1. Coordinate backbone -> f64. last_point, last_cursor_world,
dyn_anchor, dyn_ref become DVec3; UcsXform transforms, parse_coord,
the ucs helper fns, dyn_resolve_point, the OTRACK typed-distance
paths, command-line coordinate entry and the coordinate readout all
compute in f64. Screen/GPU/alignment stays f32 (snap engine, render
geometry, projection, UCS-icon/viewcube drawing) with casts only at
those boundaries.
2. Per-command committed storage -> DVec3. on_point already received a
DVec3, but many command impls narrowed it back to f32 (let pt =
pt.as_vec3(); Vec<Vec3> fields) and re-widened at build, quantizing
the committed coordinate. Migrated PLINE, SPLINE, MLINE, LENGTHEN,
INSERT, 3D primitives, every dimension/leader/mleader, ray, revcloud,
wipeout, attdef, table, tolerance, array center, mview, plot_window,
plus the QDIM_PLACE handler and qdim_collect_points. Preview,
hit-test and rubber-band paths keep f32 at the boundary.
Hatch manual boundaries are left for a follow-up: HatchModel.boundary
is structurally Arc<Vec<[f32;2]>> and needs a model-level widen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 21:26:40 +03:00
|
|
|
|
fn qdim_collect_points(e: &acadrust::EntityType, out: &mut Vec<glam::DVec3>) {
|
2026-06-27 17:56:01 +03:00
|
|
|
|
use acadrust::EntityType as ET;
|
fix(precision): keep typed coordinates exact in f64 (#311)
Typed values were quantized to the f32 grid before reaching the
entity (e.g. a length of 2000.8 committed as 2000.8020), worsening
with magnitude. The interaction point layer was all glam::Vec3 (f32).
Two-layer fix:
1. Coordinate backbone -> f64. last_point, last_cursor_world,
dyn_anchor, dyn_ref become DVec3; UcsXform transforms, parse_coord,
the ucs helper fns, dyn_resolve_point, the OTRACK typed-distance
paths, command-line coordinate entry and the coordinate readout all
compute in f64. Screen/GPU/alignment stays f32 (snap engine, render
geometry, projection, UCS-icon/viewcube drawing) with casts only at
those boundaries.
2. Per-command committed storage -> DVec3. on_point already received a
DVec3, but many command impls narrowed it back to f32 (let pt =
pt.as_vec3(); Vec<Vec3> fields) and re-widened at build, quantizing
the committed coordinate. Migrated PLINE, SPLINE, MLINE, LENGTHEN,
INSERT, 3D primitives, every dimension/leader/mleader, ray, revcloud,
wipeout, attdef, table, tolerance, array center, mview, plot_window,
plus the QDIM_PLACE handler and qdim_collect_points. Preview,
hit-test and rubber-band paths keep f32 at the boundary.
Hatch manual boundaries are left for a follow-up: HatchModel.boundary
is structurally Arc<Vec<[f32;2]>> and needs a model-level widen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 21:26:40 +03:00
|
|
|
|
let p = |v: &acadrust::types::Vector3| glam::DVec3::new(v.x, v.y, v.z);
|
2026-08-04 23:17:54 +03:00
|
|
|
|
let ocs = |point: (f64, f64, f64), normal: acadrust::types::Vector3| {
|
|
|
|
|
|
let world = crate::scene::view::transform::ocs_point_to_wcs(
|
|
|
|
|
|
point,
|
|
|
|
|
|
(normal.x, normal.y, normal.z),
|
|
|
|
|
|
);
|
|
|
|
|
|
glam::DVec3::new(world.0, world.1, world.2)
|
|
|
|
|
|
};
|
2026-06-27 17:56:01 +03:00
|
|
|
|
match e {
|
|
|
|
|
|
ET::Line(l) => {
|
|
|
|
|
|
out.push(p(&l.start));
|
|
|
|
|
|
out.push(p(&l.end));
|
|
|
|
|
|
}
|
|
|
|
|
|
ET::LwPolyline(pl) => {
|
|
|
|
|
|
for v in &pl.vertices {
|
2026-08-04 23:17:54 +03:00
|
|
|
|
out.push(ocs((v.location.x, v.location.y, pl.elevation), pl.normal));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
ET::Polyline2D(pl) => {
|
|
|
|
|
|
for v in &pl.vertices {
|
|
|
|
|
|
out.push(ocs((v.location.x, v.location.y, pl.elevation), pl.normal));
|
2026-06-27 17:56:01 +03:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
ET::Polyline(pl) => {
|
|
|
|
|
|
for v in &pl.vertices {
|
|
|
|
|
|
out.push(p(&v.location));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
ET::Arc(a) => {
|
|
|
|
|
|
for &ang in &[a.start_angle, a.end_angle] {
|
2026-08-04 23:17:54 +03:00
|
|
|
|
out.push(ocs(
|
|
|
|
|
|
(
|
|
|
|
|
|
a.center.x + a.radius * ang.cos(),
|
|
|
|
|
|
a.center.y + a.radius * ang.sin(),
|
|
|
|
|
|
a.center.z,
|
|
|
|
|
|
),
|
|
|
|
|
|
a.normal,
|
2026-06-27 17:56:01 +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
|
|
|
|
|
|
|
|
|
|
// ── TCASE helpers ──────────────────────────────────────────────────────────
|
|
|
|
|
|
fn sentence_case(s: &str) -> String {
|
|
|
|
|
|
let mut c = s.chars();
|
|
|
|
|
|
match c.next() {
|
|
|
|
|
|
Some(first) => first.to_uppercase().collect::<String>() + &c.as_str().to_lowercase(),
|
|
|
|
|
|
None => String::new(),
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn title_case(s: &str) -> String {
|
|
|
|
|
|
s.split(' ')
|
|
|
|
|
|
.map(|w| {
|
|
|
|
|
|
let mut c = w.chars();
|
|
|
|
|
|
match c.next() {
|
|
|
|
|
|
Some(f) => f.to_uppercase().collect::<String>() + &c.as_str().to_lowercase(),
|
|
|
|
|
|
None => String::new(),
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
|
|
.join(" ")
|
|
|
|
|
|
}
|