Commit graph

996 commits

Author SHA1 Message Date
Hakan Seven
e507e6615b chore(release): v0.6.5
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 20:16:02 +03:00
Hakan Seven
e39c4b4424 fix(clipboard): refresh blocks/meshes on PASTEORIG and PASTEBLOCK
PASTECLIP recreated missing block definitions, tessellated pasted solids,
and refreshed the layer panel after a paste; PASTEORIG and PASTEBLOCK did
not. A block pasted through either of those rendered empty because its
definition was never recreated in the target drawing.

Both paths now call merge_clipboard_blocks (so referenced/nested block
definitions exist) and populate_meshes_from_document (so pasted ACIS
solids tessellate); PASTEORIG also refreshes the layer panel.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 19:50:58 +03:00
Hakan Seven
5291a3fb82 fix(text): keep text anchors in f64 and honour space advance
Single-line TEXT and ATTRIB/ATTDEF cast the insertion/alignment point to
f32 before computing the stroke origin. At large coordinates (UTM ~4.5M)
the f32 ulp is ~0.5, snapping text baselines onto a coarse grid so
adjacent rows collapse onto each other and overlap. Keep the anchor in
f64 throughout.

Horizontal alignment now uses the pen advance box [0, advance] instead of
the inked min_x/max_x, so leading/trailing spaces keep their width and
space-aligned columns no longer shift. text_local_bounds returns
TextLocalBounds { ink_min, ink_max, advance }.

TABLE laid out its whole grid in an f32 world frame; switch to a local
f32 frame plus an f64 base added at the widening step, fixing cell-text
baseline snap and the i32 overflow in the border-dedup keys.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 18:47:45 +03:00
Hakan Seven
33314a5889 feat(ucs): orient text-bearing annotations to the active UCS
New annotations now place their text and reading geometry along the
active UCS X axis (identity = world), so they read square to the user's
coordinate system instead of world-horizontal:

- TEXT / MTEXT: rotation defaults to the UCS X angle
  (DocumentTab::ucs_rotation_angle, model space only).
- Linear dimensions: measure along the UCS axis the span is nearest, and
  bake the UCS X angle as the text rotation so the value reads square
  even under a style that forces horizontal text. DIMCONTINUE / DIMBASELINE
  derive their axis from the base dim's rotation (any angle), not a
  world H/V test.
- LEADER: the MText annotation rotates to the UCS, and the hookline runs
  along the leader's horizontal_direction (set to UCS X), used by the
  renderer instead of world horizontal.
- MLEADER: context.text_direction = UCS X drives both the text rotation
  and, in the renderer, the text side and dogleg/landing direction.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 18:18:35 +03:00
Hakan Seven
0c2a97e17e feat(ucs): build axis-aligned shapes in the active UCS
Commands that construct geometry with implicit axis directions ignored
the UCS, so a rectangle drawn under a rotated UCS came out world-aligned.
Give commands the UCS and build square to it:

- CadCommand::set_ucs(Mat4) (UCS→wire affine, default no-op); the host
  pushes the current UCS via App::push_ucs_to_cmd before each point /
  preview / text dispatch (DocumentTab::ucs_wire_affine, identity outside
  model space). New axis-aligned commands just override set_ucs.
- RECT (two-corner) and RECT_CEN (center) place their derived corners on
  the UCS axes (ucs_box_corners / ucs_box_around_center).
- ARRAYRECT row/column offsets run along the UCS axes.

RECT_ROT, POLYGON, and ELLIPSE already take their orientation from picked
points, so they need no change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 17:30:42 +03:00
Hakan Seven
d63602161f fix(ucs): resolve typed dynamic-input values in the active UCS
The dynamic-input boxes displayed deltas/angle in the UCS, but
dyn_resolve_point still resolved the typed (and live) values on the world
axes, so committing a typed coordinate ignored the UCS. It now rotates
the cursor delta into the UCS, interprets the typed cartesian/polar
values in that frame, and maps the relative result back to world via
UcsXform::vec_to_wcs — display and commit now agree. Absolute first-point
fields (no base) stay world; identity UCS is unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 11:33:55 +03:00
Hakan Seven
481adba66b fix(ucs): grid axis lines and grid snap follow the active UCS
Two leftovers from the UCS grid work:
- The coloured X/Y/Z axis lines (draw_axes) were drawn at the world
  origin along world axes; they now run through the UCS origin along the
  UCS axes, matching the grid lines.
- Grid snap rounded the cursor on the world XYZ lattice; it now rounds in
  the UCS grid frame (to-UCS, round, back-to-world) so it lands on the
  grid the user sees. The UCS origin (wire space) + rotation reach the
  snapper via DocumentTab::ucs_grid_basis; identity outside model space.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 11:27:34 +03:00
Hakan Seven
2509396ba0 fix(ucs): object snap tracking rays follow the active UCS
OTRACK alignment rays (0°/90° and polar steps, plus the polar pencil from
the last point) were built along world X/Y. They now rotate with the
active UCS via a Mat4 passed into otrack_snap (scene.viewcube_ucs_mat() —
the UCS in model space, identity in paper), so tracking lines and the
intersection lock align to the user's coordinate system like ortho,
polar, and the grid. sync_ucs_to_scene now reuses UcsXform::rotation_mat.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 11:17:55 +03:00
Hakan Seven
98142ee4af fix(ucs): snap ViewCube face views square to the active UCS
Clicking a ViewCube face rotated the gaze into the UCS frame but derived
the up vector from world X/Y/Z, so the snapped view landed world-aligned
(most visibly Top, where the UCS shares world Z and only the in-plane
roll differs). snap_to_direction now takes the UCS basis and picks its up
from the UCS axes, so a face snap lands square to the user's coordinate
system. Identity UCS reproduces the world-aligned snap.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 11:12:43 +03:00
Hakan Seven
b676874a6f feat(ucs): dynamic-input deltas and angle in the active UCS
The on-canvas dynamic-input boxes measured relative X/Y deltas and the
polar angle in world axes. They now read in the active UCS plane (via
UcsXform::vec_to_ucs on the offset-invariant delta), matching the UCS
ortho/polar constraints and coordinate readout. Identity UCS reproduces
the world-frame values; absolute first-point fields are unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 10:13:47 +03:00
Hakan Seven
d78ed60582 feat(ucs): orient the ViewCube to the active UCS
The ViewCube showed world Front/Top/Right. It now composes the camera
rotation with the active UCS (scene.viewcube_ucs, synced from the tab's
active_ucs), so its faces follow the user's coordinate system in model
space. Render uniform, hover/click hit-test, and the click→snap direction
all go through scene.viewcube_ucs_mat() so they stay in lock-step;
identity outside model space leaves paper-viewport navigation unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 10:10:47 +03:00
Hakan Seven
88bf2e57dc feat(ucs): align the model grid to the active UCS
The grid ruled along world X/Y/Z through the world origin. It now rules
along the active UCS axes through the UCS origin (passed in render/wire
space), so it lines up with the user's coordinate system. Paper space
stays plain WCS; identity UCS reproduces the old world-aligned grid.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 10:03:35 +03:00
Hakan Seven
746a2c97be feat(ucs): ortho/polar constrain in the active UCS plane
Ortho and polar drawing constraints snapped to world X/Y; under an active
UCS they now snap to the UCS axes, routed through the shared UcsXform
(identity = unchanged world-XY behaviour). Applied at all three input
sites (grip drag, command point, command preview). Typed coordinates
already flow through the converter via ucs_to_wcs / ucs_rotate_vec.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 10:00:53 +03:00
Hakan Seven
4aa1e90dcf feat(ucs): central WCS↔UCS converter; adopt file UCS, drive readout + icon
Introduce one bridge between WCS (how geometry and the file are stored)
and the active UCS (the coordinate system the user works in), so every
UCS-aware system routes through it instead of re-deriving axis math.

- UcsXform (app/helpers.rs): the converter — to_wcs/to_ucs (points),
  vec_to_wcs/vec_to_ucs (directions), axes(), is_identity(). Orthonormal
  axes, so the inverse is the transpose. The old ucs_to_wcs / ucs_rotate_vec
  free fns now delegate to it (one implementation).
- DocumentTab::ucs_xform() builds it from the tab's active_ucs (None = WCS).
- DocumentTab::adopt_active_ucs_from_header() loads the document's saved
  current UCS (header model-space UCS) into active_ucs on open, so the file's
  coordinate system is live immediately; called from both load paths.
- Status-bar coordinate readout reports the cursor in the active UCS.
- UCS icon tripod projects the active UCS axes, so it rotates to the UCS
  instead of always showing world X/Y/Z.

First slice of routing the app's coordinate systems through the converter;
input/picking, grid, ViewCube, and the remaining readouts follow.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 09:57:39 +03:00
Hakan Seven
44d2df9361 fix(view): unify model VPORT / paper VIEWPORT view handling
Model-space tiled VPORTs and paper-space floating VIEWPORTs each carried
their own copy of the saved-view→camera math, so a fix to one drifted
from the other (the recurring grid/twist regressions). Route both, plus
the View-table and sheet-viewport paths, through one decoder and fix the
bugs that the duplication hid.

Decoder unification:
- New camera_from_view(direction, target, center, height, twist,
  world_offset): the single saved-view→Camera decoder. camera_from_vport
  (tiled), camera_for_viewport (floating, + auto-fit), and the View-entry
  / sheet-viewport apply paths all delegate to it. Tiled vs floating now
  differ only in field source and the floating auto-fit fallback.

Twist round-trips on layout switch and file save:
- camera_from_view applies roll = -twist; the write path was dropping it,
  so model→paper→model (and reload) reset the view to raw orientation.
- New Camera::roll() recovers the camera roll; sync_camera_to_document,
  write_camera_view_entry, and vport_from_camera now store -roll as the
  view twist on the *Active VPort, the OpenCADStudio_Camera_Model View
  entry, the per-tile VPorts, and the paper sheet viewport.

Rotation-aware frustum cull:
- New view_cull_aabb(): the entity-cull rectangle is built from the
  camera's right/up basis (so a twisted/rotated view's rotated rectangle
  is enclosed) instead of world X/Y axes; returns None for tilted views
  to disable the flat-XY cull rather than wrongly hide geometry. Applied
  to the model-tile and paper-viewport cull sites.

Paper viewport scale:
- The floating-viewport auto-fit overlap test summed view_target +
  view_center raw; under a twist view_center is a DCS offset, so the sum
  landed far from the real WCS centre and wrongly tripped the auto-fit,
  rendering content at the wrong zoom. It now tests the decoded
  (twist-folded, wire-space) target. The CPU projection path derives its
  scale from that same camera, locking hit-test/snap to the GPU render.

Grid enumeration:
- ViewportInstance gains grid_on; the two grid-view collectors collapse
  into one grid_views() derived from the same active_viewports() list the
  renderer uses, so the grid overlay can no longer drift from the views
  on screen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 09:35:06 +03:00
Hakan Seven
7a1988e851 fix(view): apply saved view twist as camera roll on load
Drawings authored under a rotated UCS save a view twist so they open
square. camera_from_vport ignored VPort.view_twist (roll hardcoded 0),
so such files rendered in raw world orientation and looked tilted.

Feed -view_twist in as the camera roll: the twist rotates world-X onto
screen-right, so the direction that becomes horizontal is its negative.
The roll rides the camera quaternion, surviving render and orbit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 08:52:20 +03:00
Hakan Seven
10e4c18ba7 chore: silence native and wasm build warnings
Gate target-specific code so neither build warns about the other's items:
native-only helpers (view, window icon, file dialogs, file association, plugin
host/guard/registry, self-update) are cfg'd to non-wasm; the wasm-only font
fallback (script_of / cjk_lang / request) is cfg'd to wasm. Drop an unused mut,
allow the few genuinely-conditional dead items (mesh_click_hit, GPU layout
helpers, parity stubs), and gate the embedded PlotSettings-unrelated dead
constants. Both targets now build clean (only the unavoidable nom/quick-xml
future-incompat dependency notice remains).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 02:03:34 +03:00
Hakan Seven
2de5c7668f chore(deps): update lock so the wasm build compiles again
A toolchain/libc shift broke the previously-locked transitive set on wasm32
(memchr 1.0.2's libc::memchr binding stopped resolving), failing the web/Pages
deploy. Re-resolve within the existing version requirements — no Cargo.toml
change needed. Native and wasm32 both build.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 01:40:26 +03:00
Hakan Seven
7fa12b757b chore(release): bump to 0.6.4
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 01:12:03 +03:00
Hakan Seven
e7352cfc24 chore(deps): bump acadrust for block header owned-handle fix
Picks up the fix that keeps a block's owned-handle list in sync with the
entities actually written, so AutoCAD no longer drops model-space geometry from
OCS-saved files.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 01:05:30 +03:00
Hakan Seven
429f3035b1 fix(layout): fully remove a deleted layout's record and references
delete_layout removed the Layout object and its entities but left the paper
space block record, the ACAD_LAYOUT dictionary entry and any standalone
PlotSettings behind. AutoCAD pairs every paper-space block record with a layout,
so the deleted tab could reappear on reopen. Also drop the block record, the
dictionary entry and the page setup so the layout is gone for good.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 00:37:58 +03:00
Hakan Seven
26479b6faa fix(grid): independent, clipped grid per viewport
Grid (and grid-snap) state was shared through the global show_grid flag, so
toggling it in model space also turned it on in paper space, and a floating
viewport's grid painted across the whole paper — and beyond the 3-D view when
zoomed in.

- Store grid/snap per view: a model tile in model space, the active floating
  viewport or the layout's sheet viewport in paper space, round-tripped via the
  viewport status flags.
- adopt_view_display on layout switch and viewport enter/exit so the live
  toggle follows the view you are in.
- Render one grid per view (paper_viewport_grid_views): the sheet over the
  paper, each floating viewport clipped to its screen rectangle.
- Clamp the grid clip to the widget so an overflowing viewport rectangle never
  paints grid outside the 3-D view.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 00:18:01 +03:00
Hakan Seven
ba834fb166 fix(layout): faithful paper-space round-trip and page setup (#156)
Builds on the acadrust viewport/PlotSettings round-trip fixes:

- Write the sheet viewport's view position into view_center (DCS) with
  view_target at the origin, matching AutoCAD; the old view_target form shifted
  the layout.
- PAGESETUP now writes paper size, rotation, origin and units onto the Layout's
  embedded PlotSettings (not just a side object), and bumps geometry, so an edit
  shows on the sheet immediately and survives a save.
- Draw the printable-area guide (paper inset by plot margins, rotation-aware) as
  a dashed rectangle, matching AutoCAD's layout view.
- Plot/PDF export falls back to the Layout's embedded PlotSettings via
  effective_plot_settings(), so a loaded file's rotation/origin/scale apply.

Bumps acadrust to pick up the viewport-status and PlotSettings round-trip fixes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 23:47:36 +03:00
Hakan Seven
9d5ee75bcc fix(layout): place sheet viewport center in the paper plane (#156)
The overall (sheet) viewport was created with its center as (x, 0, z) —
carrying the paper-height midpoint in z with y = 0. AutoCAD/TrueView read a
paper-space viewport center as (x, y), so the sheet view ended up centered at
y = 0, shifting the whole layout half a page down; activating the layout in
AutoCAD could fault on the off-plane viewport. Store the center as (x, y, 0),
matching the convention MVIEW already uses for floating viewports. Verified: a
newly created paper-space viewport no longer faults AutoCAD on layout switch.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 13:20:16 +03:00
Hakan Seven
c80cc3292c docs(commands): sync COMMANDS.md with implemented commands
Mark commands that are wired and dispatching as done: 3DARRAY, the solid
booleans (UNION/SUBTRACT/INTERSECT), the CONE/WEDGE/TORUS primitives,
LAYISO/LAYUNISO, DIST/ID/LIST inquiry, named-view VIEW, and QUIT. Refresh the
summary totals (done 141 -> 158, missing 91 -> 74).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 12:28:42 +03:00
Hakan Seven
a72df6a805 feat(command): surface one-shot commands in command-line autocomplete
File/view/layer/style/undo-redo and similar one-shot commands dispatch a
single action instead of installing an interactive CadCommand, so they had no
module to register from and never appeared in autocomplete. Add a consolidated
CommandRegistration covering all 132 user-typeable one-shots (NEW, OPEN, SAVE,
SAVEAS, PLOT, PURGE, UNDO, REDO, ZOOM, LAYER, XREF, …). Internal dispatch
tokens the user never types (REFEDIT_BEGIN, REFCLOSE_SAVE, REFCLOSE_DISCARD)
stay excluded.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 12:24:34 +03:00
Hakan Seven
5673612047 fix(command): register remaining interactive commands for autocomplete
ARRAY3D/3DARRAY, ATTEDIT/ATE, DDEDIT/ED and WIPEOUT/WO dispatched on literal
matches but had no CommandRegistration entry, so none surfaced in command-line
autocomplete. Add the inventory submit in each command's module, matching the
convention used by the other interactive commands. Audit confirms no remaining
active_cmd dispatch lacks a registration.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 12:20:36 +03:00
Hakan Seven
895937913f fix(modify): register TORIENT in the autocomplete command registry
TORIENT dispatched on a literal match but had no CommandRegistration entry,
so it never surfaced in command-line autocomplete. Add the inventory submit
like the other interactive commands.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 12:16:35 +03:00
Hakan Seven
b134aff4e3 refactor(annotate): route TEXTEDIT Mode entry through parse_texteditmode
Fold the Single/Multiple value parsing in TexteditCommand's Mode step into
the shared parse_texteditmode() helper, and report unrecognized input to
the user — the Mode step now stays active and re-prompts with
"Requires Single or Multiple." instead of silently ignoring the value.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 12:10:39 +03:00
Hakan Seven
8c48233dcf merge: TEXTEDIT command + TEXTEDITMODE sysvar (#130)
Merge PR #130 (KarimJerbi) and resolve review findings:

- Conflict (settings.rs): keep current persisted-settings layout, append
  texteditmode field.
- Conflict (command/mod.rs): keep both new CmdResult variants — main's
  live-entity ops and the PR's text-edit suspend/resume.
- Bug: invalid TEXTEDITMODE value no longer aborts via the Measurement
  arm; it now re-prompts with the error and stays active.
- Dedup: single parse_texteditmode() helper replaces the value parsing
  duplicated across commands.rs, settings.rs and textedit.rs.
- Strip trailing whitespace.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 12:05:54 +03:00
Hakan Seven
038052f5f9 feat(block): fade context, free editing and a Save/Discard toolbar in REFEDIT
In-place block edit now reads more like a dedicated editor:

- Fade everything except the edited entities toward the background (wires,
  block instances and 3D solid meshes) so the block stands out while the
  surrounding drawing stays visible for context.
- Don't leave a command active on enter — the user edits the block's geometry
  freely (move, grips, draw, erase) and finishes explicitly.
- Show the right-edge vertical toolbar with clickable Save (REFCLOSE_SAVE) and
  Discard (REFCLOSE_DISCARD) buttons while a session is active, so the edit
  can be ended without typing.

The fade clears on save/discard, restoring full brightness.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 11:46:43 +03:00
Hakan Seven
5052d2338c fix(block): bump acadrust so REFEDIT'd arcs aren't distorted
Picks up the acadrust transform_arc fix that rotates/reflects an arc's
start/end angles, so editing a rotated or mirrored block in place (and
rotating/scaling a standalone arc) keeps arcs intact.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 07:52:25 +03:00
Hakan Seven
9f447308a6 feat(block): double-click to edit a block in place (#136)
Double-clicking a block reference (its wire or its solid body) now enters
in-place block edit (REFEDIT), so its geometry can be modified and the change
reflects in every instance on REFCLOSE.

Also drop REFEDIT's "non-uniform scale not supported" restriction: the
session now carries the INSERT's full forward/inverse affine (OCS, rotation
and non-uniform / mirrored scale) and applies it via the entity transform,
instead of a uniform scale + rotate + translate sequence. Mirrored blocks
(e.g. x_scale = -1) are editable now.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 07:43:59 +03:00
Hakan Seven
65b8e85751 fix(select): highlight a block's solid body on hover/selection
The instanced block solid meshes kept the inner solid's handle as their
name, but the highlight tint matches the mesh name against the hovered /
selected handle — which for a block is the parent INSERT. So hovering or
selecting a block whose body is a solid never tinted it. Tag the instanced
meshes with the parent INSERT handle so the orange hover / blue selection
tint applies.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 07:29:18 +03:00
Hakan Seven
cd10aa0363 fix(select): click the front-most solid across top-level and block bodies
Clicking a block whose body is a solid tried top-level solid meshes first
and block-internal ones only as a fallback, so a stray solid behind the
block always won and the block couldn't be picked. Merge both into one
depth-sorted hit-test (solid_click_hit) keyed by the solid's own handle or
the parent INSERT, so the front-most body wins regardless of kind.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 07:24:31 +03:00
Hakan Seven
d1395e8f3d fix(solid): refresh a DXF SOLID's fill after move/copy
A DXF SOLID renders as a cached solid-fill hatch, but transform_entities and
copy_entities only rebuilt that cache for true HATCH entities — a SOLID fell
through and kept drawing at its old position (or a copy showed nothing).
Rebuild the solid-fill hatch for EntityType::Solid in both paths, matching
the existing grip/add/open handling.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 07:15:03 +03:00
Hakan Seven
ae9940929c fix(select): box/lasso-select blocks whose body is a solid
Box/lasso only tested top-level solid meshes, so a block whose visible body
is a solid (its mesh lives in the per-INSERT instanced cache, and it has no
wires to catch) was never picked up by a window/crossing/lasso. Add
block_mesh_box_hit / block_mesh_poly_hit that test each INSERT's instanced
block solid meshes and resolve a hit to the parent INSERT, and wire them
into the selection paths.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 07:03:56 +03:00
Hakan Seven
ae3b305f95 fix(dim): move the baked dimension block when the dimension is transformed
A dimension's final graphics are baked into a per-instance *D block, and the
render draws that block's sub-entities directly rather than the definition
points. Move/rotate/scale/mirror updated the definition points but left the
block sub-entities in place, so the dimension stayed drawn where it was.
Apply the same transform to the dimension's *D block sub-entities.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 06:55:34 +03:00
Hakan Seven
d5f1348f1b fix(3d): box/lasso-select 3D solids and refresh them after a transform
Box, window, crossing and lasso selection only tested wires and hatches, so
3D solids (which render as meshes) could never be selected that way. Add
mesh_box_hit / mesh_poly_hit that project the solid mesh and test it against
the box / lasso (window = all vertices inside, crossing = any vertex inside
or the region sitting inside the solid), and wire them into the selection
paths.

Also re-tessellate solids after a move/rotate/scale/mirror/copy: the entity
moved but its cached mesh didn't, so the solid stayed drawn at its old
position. Rebuild the mesh caches when a transformed/copied handle is an
ACIS volume.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 06:45:49 +03:00
Hakan Seven
c5969b4776 fix(clipboard): keep the paste preview on the cursor across drawings
The preview wires are tessellated in the target drawing's offset-relative
frame, but the centroid driving the preview translation was the source
drawing's, so the ghost drifted by the world_offset difference. Shift the
centroid into the target frame to match the committed paste. Zero within one
drawing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 06:32:50 +03:00
Hakan Seven
de1f1c0e24 fix(3d): move and paste ACIS solids to the right place
Solids were missing from the entity transform dispatch, so move / rotate /
scale / mirror were silently no-ops on a 3D solid (only its wires shifted)
and a pasted solid stayed at its original location. Add a Transformable impl
for Solid3D/Region/Body/Surface (delegating to acadrust, which now composes
the move into the ACIS body placement) and register them in the dispatcher.

Also make cross-drawing paste land at the cursor: the clipboard centroid is
measured in the source drawing's offset-relative frame, so correct the paste
translation by the source/target world_offset difference (zero within one
drawing). Bumps acadrust for the solid-geometry transform fixes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 02:46:56 +03:00
Hakan Seven
e45064f8a8 fix(clipboard): carry block-internal layers and refresh the layer panel
Cross-drawing paste recreated layers/linetypes/styles only for the
top-level selection, so objects inside a pasted block landed on layers the
target drawing didn't have. Scan the entities inside captured block
definitions for their dependencies too. Also refresh the layer panel after
a paste so the brought-in layers show in the manager and the dropdown.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 02:12:58 +03:00
Hakan Seven
5f3eaa621c fix(properties): recolour solid meshes on a colour/layer change
ACIS solids bake their colour into the mesh, so a property-panel or ribbon
colour (or layer) change left the solid showing its old colour — only the
wires, which resolve colour at render time, updated. Run recolor_meshes
(now covering block-definition meshes too) when property targets are
invalidated, and route the ribbon colour change through the same path.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 02:12:58 +03:00
Hakan Seven
a492ea6300 fix(clipboard): tessellate pasted ACIS solids so they render
Paste added the entities but never rebuilt the solid mesh caches, which are
only populated by populate_meshes_from_document (file open, undo, a few
commands). A pasted 3D solid — top-level or inside a recreated block
definition — therefore had no mesh and stayed invisible. Rebuild the mesh
caches after the paste so the solids show up.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 01:58:39 +03:00
Hakan Seven
31ed911714 fix(3d): place and move ACIS solids and tilted-normal blocks correctly
Bump acadrust to pick up real ACIS body transforms (so moving/copying/
rotating a 3D solid relocates its geometry) and OCS-aware INSERT placement.

- body_transform: read the SAT transform record from its first 13 float
  tokens instead of by raw index, which skipped the matrix because the
  record leads with a book-keeping pointer — so the body placement was
  silently ignored and solids never honored their stored transform.
- Insert grips: the grip lives in world space but insert_point is stored in
  the block's OCS, so grip display and grip-drag now round-trip through the
  OCS. A block whose extrusion normal isn't +Z no longer shows its grip in
  the wrong place or drags along the wrong axes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 01:52:53 +03:00
Hakan Seven
fd15dd0aac feat(select): invert selection from the right-click menu
Add an Invert Selection entry to the selection context menu (below
Select Similar): it replaces the current selection with every other
selectable object in the active layout. Candidates come from the visible
wire set, so objects on off/frozen layers stay excluded.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 21:31:05 +03:00
Hakan Seven
e562738d9e fix(clipboard): carry block definitions across drawings on paste
Copying an INSERT and pasting it into another drawing rendered nothing:
the clipboard snapshotted referenced layers/linetypes/styles but not the
block definition itself, so the pasted reference pointed at a block the
target drawing didn't have.

Capture every block an copied INSERT references — walking nested INSERTs
transitively, skipping model/paper space and xref blocks — and store the
definition's base point plus its owned entities. On paste, recreate any
of those blocks the target drawing lacks via a new define_block_raw
(entities kept in block-local coordinates), before adding the pasted
references. Same-document pastes are unaffected since the blocks already
exist.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 21:23:59 +03:00
Hakan Seven
490c0f6cf9 feat(properties): show polyline length and area
Line, Circle and Arc reported their lengths/quantities in the Geometry
section, but polylines (and rectangles, which are closed polylines) only
listed vertex count, closed flag and elevation. Add read-only Length and
Area rows: length is the true path length with arc segments measured by
their arc length, area treats the polyline as closed with a circular
segment correction per bulge.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 21:14:13 +03:00
Hakan Seven
af850421b4 fix(join): stitch touching segments into a polyline, any angle
JOIN previously only merged collinear lines or co-circular arcs into a
single Line/Arc and rejected everything else as "not co-linear" — so the
segments of a broken polyline, which meet at vertices but run at angles,
could never rejoin. It also flattened endpoints to the first segment's Z,
silently discarding height.

Rewrite the geometry pass to chain segments wherever their endpoints
touch, regardless of angle: a collinear straight run still collapses to
one Line, a planar chain becomes an LwPolyline (arcs carried as bulges),
and a chain with varying Z becomes a Polyline3D, preserving height.
Open/closed is detected from the endpoints; a selection that isn't a
single connected chain, or holds an unsupported entity / tilted arc, is
refused with a clearer message.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 21:08:23 +03:00
Hakan Seven
54ff2c97e2 chore(release): bump to 0.6.3
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 20:46:53 +03:00