2026-03-22 14:56:27 +03:00
|
|
|
// MVIEW — interactive paper-space viewport creation.
|
|
|
|
|
//
|
|
|
|
|
// Two clicks define opposite corners of a new viewport rectangle.
|
|
|
|
|
// The created Viewport entity is routed to add_entity_to_layout by apply_cmd_result
|
|
|
|
|
// because we're in paper space.
|
|
|
|
|
|
|
|
|
|
use acadrust::entities::Viewport;
|
|
|
|
|
use acadrust::types::Vector3;
|
|
|
|
|
use acadrust::EntityType;
|
|
|
|
|
|
|
|
|
|
use crate::command::{CadCommand, CmdResult};
|
|
|
|
|
use crate::modules::{IconKind, ModuleEvent, ToolDef};
|
2026-06-16 11:33:46 +03:00
|
|
|
use crate::scene::model::wire_model::WireModel;
|
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
|
|
|
use glam::DVec3;
|
2026-03-22 14:56:27 +03:00
|
|
|
|
|
|
|
|
// ── Ribbon definition ─────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
pub fn tool() -> ToolDef {
|
|
|
|
|
ToolDef {
|
|
|
|
|
id: "MVIEW",
|
|
|
|
|
label: "Viewport",
|
2026-06-18 12:29:32 +03:00
|
|
|
icon: IconKind::Svg(include_bytes!("../../../assets/icons/viewport.svg")),
|
2026-03-22 14:56:27 +03:00
|
|
|
event: ModuleEvent::Command("MVIEW".to_string()),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ── Command ───────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
pub struct MviewCommand {
|
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
|
|
|
corner1: Option<DVec3>,
|
2026-03-22 14:56:27 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl MviewCommand {
|
|
|
|
|
pub fn new() -> Self {
|
|
|
|
|
Self { corner1: None }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl CadCommand for MviewCommand {
|
|
|
|
|
fn name(&self) -> &'static str {
|
|
|
|
|
"MVIEW"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn prompt(&self) -> String {
|
|
|
|
|
if self.corner1.is_none() {
|
2026-05-26 18:59:51 +03:00
|
|
|
"MVIEW Specify first corner:".to_string()
|
2026-03-22 14:56:27 +03:00
|
|
|
} else {
|
2026-05-26 18:59:51 +03:00
|
|
|
"MVIEW Specify opposite corner:".to_string()
|
2026-03-22 14:56:27 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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 on_point(&mut self, pt: DVec3) -> CmdResult {
|
2026-03-22 14:56:27 +03:00
|
|
|
if let Some(c1) = self.corner1 {
|
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 w = (pt.x - c1.x).abs();
|
|
|
|
|
let h = (pt.y - c1.y).abs();
|
2026-03-22 14:56:27 +03:00
|
|
|
if w < 1.0 || h < 1.0 {
|
|
|
|
|
return CmdResult::Cancel;
|
|
|
|
|
}
|
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 cx = (c1.x + pt.x) / 2.0;
|
|
|
|
|
let cy = (c1.y + pt.y) / 2.0;
|
|
|
|
|
let cz = c1.z;
|
2026-03-22 14:56:27 +03:00
|
|
|
|
|
|
|
|
let mut vp = Viewport::new();
|
|
|
|
|
vp.center = Vector3::new(cx, cy, cz);
|
|
|
|
|
vp.width = w;
|
|
|
|
|
vp.height = h;
|
|
|
|
|
vp.id = 2; // user viewport (id > 1)
|
|
|
|
|
|
|
|
|
|
CmdResult::CommitAndExit(EntityType::Viewport(vp))
|
|
|
|
|
} else {
|
|
|
|
|
self.corner1 = Some(pt);
|
|
|
|
|
CmdResult::NeedPoint
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn on_enter(&mut self) -> CmdResult {
|
|
|
|
|
CmdResult::Cancel
|
|
|
|
|
}
|
|
|
|
|
|
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 on_mouse_move(&mut self, pt: DVec3) -> Option<WireModel> {
|
|
|
|
|
let pt = pt.as_vec3();
|
2026-03-22 14:56:27 +03:00
|
|
|
let c1 = self.corner1?;
|
|
|
|
|
Some(WireModel {
|
2026-07-20 14:50:27 +03:00
|
|
|
world_width: 0.0,
|
2026-07-18 15:15:24 +03:00
|
|
|
fill_is_3d: false,
|
fix(pick): hit-test lineweight width and thickness walls
Selection tested a flat 8 px radius around each wire's centreline and
nothing else, so two things the user can see were not pickable.
A wire renders as a band `line_weight_px` wide, so pick now widens to that
rendered half-width via `pick_tolerance_px`, mirroring the wire shader's
`select(0.5, half_width, lwdisplay_enable)`. Behaviour is unchanged for
every standard weight (the widest, 2.11 mm, renders 7.97 px half-width —
just under the 8 px threshold); this keeps the two sides from drifting
apart if the display boost in `lineweight_to_px` ever changes.
Entities extruded by a DXF thickness draw their wall as four edges with
nothing in between, so the interior was a hole the cursor fell through.
WireModel gains `pick_tris`: triangles hit-testing treats as solid and the
renderer never uploads. Kept apart from `fill_tris` because that channel
reaches the GPU, and a wall that rendered shaded would change how every
thickness drawing looks. Ranked below `fill_tris` — those are drawn, these
are not, so where they overlap the drawn face is what the user means.
Walls are dropped when a wire is projected into a floating viewport:
`points` are reprojected and clipped into paper coords there, so cloned
model-space triangles would hit-test at a model-scale offset. Extruded
entities stay selectable by their edges, which do get projected.
The wall loop runs on every hover that misses everything else, so it
rejects on the wire's AABB first — an extruded circle is ~128 triangles
and a drawing full of thickness would otherwise project every wall on
each mouse move.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 14:58:55 +03:00
|
|
|
pick_tris: Vec::new(),
|
|
|
|
|
pick_tris_low: Vec::new(),
|
2026-07-17 01:49:18 +03:00
|
|
|
dash_from_start: false,
|
2026-07-17 02:59:11 +03:00
|
|
|
dash_align_end: None,
|
refactor(text): per-entity SDF text — all text kinds, selection/hover highlight
Rearchitect SDF text from a document-wide collector to per-entity
production during tessellation, so each entity owns its glyph quads and
composite/nested text is covered for free (no re-explosion, no per-frame
document walk).
- WireModel gains `text_verts`: glyph quads ride on the wire, so they are
cached by the tess memo, cloned on hit, and transformed by the block
expand loop exactly like `points`.
- Text arm (tessellate.rs) builds the quads when SDF is on and suppresses
the strokes; render gathers text_verts from the viewport wire set
(`gather_text_verts`, cached on wire content id) — the old collector is
removed. Covers model, dimension, table, block-internal and paper-sheet
text uniformly.
- MLEADER text (lays out via layout_mtext, not the Text arm) gets its own
SDF branch driven by the per-run GlyphRun.
- Pick box comes from the rendered glyph quads (true text extent), not
entity_aabb which mis-places the box for MTEXT; composite paths no longer
clobber the empty text wire's tight AABB (dim pick box hugs the text).
- Selection / hover highlight via a text-highlight overlay (upload_text_
highlight, keyed on selection_generation) mirroring the selected-wire
xray overlay; base glyphs stay neutral so a deselect leaves no stale tint.
- Debug: env OCS_TEXT_BOX draws each run's glyph-bounds rectangle.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 17:01:36 +03:00
|
|
|
text_verts: Vec::new(),
|
2026-03-22 14:56:27 +03:00
|
|
|
name: "mview_preview".to_string(),
|
|
|
|
|
points: vec![
|
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
|
|
|
[c1.x as f32, c1.y as f32, c1.z as f32],
|
|
|
|
|
[pt.x, c1.y as f32, c1.z as f32],
|
|
|
|
|
[pt.x, pt.y, c1.z as f32],
|
|
|
|
|
[c1.x as f32, pt.y, c1.z as f32],
|
|
|
|
|
[c1.x as f32, c1.y as f32, c1.z as f32],
|
2026-03-22 14:56:27 +03:00
|
|
|
],
|
2026-06-23 17:08:54 +03:00
|
|
|
points_low: Vec::new(),
|
2026-03-22 14:56:27 +03:00
|
|
|
color: WireModel::CYAN,
|
|
|
|
|
selected: false,
|
|
|
|
|
pattern_length: 0.0,
|
|
|
|
|
pattern: [0.0; 8],
|
|
|
|
|
line_weight_px: 1.0,
|
|
|
|
|
snap_pts: vec![],
|
|
|
|
|
tangent_geoms: vec![],
|
2026-04-06 23:19:08 +03:00
|
|
|
aci: 0,
|
2026-03-22 14:56:27 +03:00
|
|
|
key_vertices: vec![],
|
2026-04-26 12:21:51 +03:00
|
|
|
aabb: WireModel::UNBOUNDED_AABB,
|
2026-05-06 06:55:28 +03:00
|
|
|
plinegen: true,
|
2026-05-06 23:24:26 +03:00
|
|
|
fill_tris: vec![],
|
2026-06-23 18:03:40 +03:00
|
|
|
fill_tris_low: Vec::new(),
|
2026-03-22 14:56:27 +03:00
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-05-26 22:45:54 +03:00
|
|
|
|
|
|
|
|
|
|
|
|
|
// ── Autocomplete registry ─────────────────────────────────
|
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
|
|
|
inventory::submit!(crate::command::CommandRegistration { names: &["MVIEW"] }); // MviewCommand
|