149 Commits
Author SHA1 Message Date
Hakan ca07268c7c cargo fmt 2026-05-12 10:36:15 +03:00
Hakan Seven 048bb41aef feat(render): solid fill for PolyfaceMesh and PolygonMesh entities
PolyfaceMesh and PolygonMesh were wireframe-only. They now render with
the same flat-color solid fill as Face3D entities.

- Add fill_tris: Vec<[f32;3]> to TruckEntity and WireModel
- PolyfaceMesh: fan-triangulate each face into fill_tris
- PolygonMesh: triangulate each grid quad into fill_tris
- tessellate.rs Lines branch: apply world_offset to fill_tris
- Face3DGpu::from_wires: collect fill_tris from all wires in addition
  to Face3D key_vertices quads
- upload_face3d: pass all_wires so fill_tris are batched into one draw call
2026-05-06 23:24:26 +03:00
Hakan Seven ca9ec8f47d fix(scene): bump geometry epoch after cache assignment on file open
populate_*_from_document() each called bump_geometry(); direct cache
assignment skipped it, leaving the wire cache stale on first open.
2026-05-06 23:10:35 +03:00
Hakan Seven 2da3640b45 perf: build hatch/image/mesh caches off the UI thread
Parsing + DerivedCaches construction now runs entirely in a dedicated
OS thread inside open_path(). FileOpened handler only assigns the
pre-built caches, keeping the UI thread free during file load.

- Add build_derived_caches(): parallel hatch + mesh tessellation via rayon
- Extend Message::FileOpened / open_path return type with DerivedCaches
- Drop populate_*_from_document() calls from FileOpened handler
2026-05-06 23:04:53 +03:00
HakanandClaude Sonnet 4.6 0e5dec0948 feat: annotation scale system (CANNOSCALE, PSLTSCALE, MultiLeader, Leader)
- Scale popup dropdown replaces cycle behavior; paper-space viewport scale
  editable when a viewport is active/selected
- CANNOSCALE persisted via DXF $USERR1 (read on open, write on save)
- PSLTSCALE: linetype dash lengths scaled by viewport anno_scale so dashes
  appear uniform physical size in paper space
- MultiLeader: arrow size, dogleg length, and text height scale with
  anno_scale; leader line paths (world coords) stay fixed
- Leader: arrow size and hookline length scale with anno_scale; also
  corrects world_offset subtraction that was previously deferred to the
  TruckObject::Lines path

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-05-06 17:50:46 +03:00
Hakan SevenandClaude Sonnet 4.6 899b3f4796 feat: GPU scissor rect for paper-space viewport wires
Viewport-projected wires now clip at pixel level to their viewport boundary.

- WireModel.vp_scissor: Option<[f32; 4]> — paper-space bbox set on projection
- WireGpu.vp_scissor carries it through to the draw loop
- Pipeline.compute_wire_scissors() converts paper→NDC→pixel each frame
- render() calls set_scissor_rect() per wire, reset after viewport group
- INTEGRATION_GAPS.md: Viewport ⚠️ → ✅, all 51 gaps resolved

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-05-06 07:12:05 +03:00
Hakan SevenandClaude Sonnet 4.6 27aca41dd1 feat: plinegen + SAB parsing for Solid3D/Region/Body
LwPolyline PLINEGEN=0 (DXF default): emit SegmentedLines with NaN
separators between vertex-to-vertex segments; wire_gpu resets
arc-length to 0 after each NaN so linetype pattern restarts per vertex.
PLINEGEN=1: existing Contour path unchanged (continuous pattern).
WireModel gains plinegen field propagated through tessellate pipeline.

Solid3D/Region/Body: call SabReader::read() when parse_sat() returns
None due to binary SAB format (DWG files / newer AutoCAD exports).

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-05-06 06:55:28 +03:00
Hakan Seven d7422b9396 fix: store arc angles in radians 2026-05-03 20:14:28 +03:00
Hakan Seven 1b4c5e13e5 fix: keep open drawing when creating new file 2026-05-03 19:55:38 +03:00
Hakan SevenandClaude Sonnet 4.6 928e7df274 fix: don't add model-space world_offset to paper-space grip absolute positions
GripApply::Absolute adds world_offset to convert local-space coordinates
back to DXF space for model-space entities. Paper-space entities (viewports,
etc.) use sheet mm coordinates with no world_offset, but the grip drag code
was unconditionally adding scene.world_offset — causing grips to jump by the
full model-space centroid offset (potentially hundreds of km for UTM drawings).

Now world_offset is only applied when current_layout == "Model".

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-05-01 21:19:09 +03:00
Hakan SevenandClaude Sonnet 4.6 7b7622910c fix: apply world_offset when committing interactively created entities
Commands receive click coordinates in local space (DXF coords minus
world_offset) for camera precision. Without correction, entities were
placed world_offset away from the clicked position in existing drawings
that have a non-zero centroid (e.g. UTM-coordinate drawings).

Re-add world_offset via acadrust::Transform::from_translation (f64
precision) in commit_entity before the entity is inserted into the
DXF document. Paper-space entities are exempt as they use sheet coords.

Fixes #11

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-05-01 21:00:27 +03:00
Hakan SevenandClaude Sonnet 4.6 7e8fc8ef88 feat: replace rfd Save As with native iced file chooser
Implements a full in-app file browser window for Save As, replacing the
rfd dialog that could not expose the selected filter index on Linux.
The new chooser includes directory navigation, a scrollable file list,
filename input, and a format drop-down (DWG/DXF × 2018/2013/2010/2007/2004/2000/R14).

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-05-01 16:20:38 +03:00
Hakan SevenandClaude Sonnet 4.6 9cd8eff77f fix: replace broken version filters with custom Save As dialog
rfd does not expose the selected filter index, so the old DWG/DXF version
filters had no effect — every file was always saved as AC1032 (R2018).

Replace with a single custom iced Save As window that shows:
- File name text input
- Save-in folder input + Browse button (rfd folder picker)
- Format dropdown: DWG/DXF × 2018/2013/2010/2007/2004/2000/R14
- Save / Cancel buttons

The selected format is passed to save_as_version() which temporarily sets
doc.version before writing, so DwgWriter/DxfWriter emit the correct file
format. Also works in the unsaved-changes Save flow.

Closes #9

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-05-01 16:12:32 +03:00
Hakan SevenandClaude Sonnet 4.6 01943c5b8d fix: correct paper-space grip projection and MTEXT attachment point rendering
- Grip points in paper space now use the 2-D linear to_px transform
  (tx/ty/ortho_size) instead of the 3-D view_proj matrix, matching the
  coordinate system used by PaperCanvas
- world_offset is no longer subtracted from paper-space entity grip
  positions — paper-space coordinates are already unoffset (same rule
  as wire tessellation in wires_for_block)
- MTEXT v_offset corrected for all attachment points: CXF glyphs sit
  on the baseline and extend upward, so Top attachment needs v_offset=-h,
  Bottom needs (n_lines-1)*line_h, Middle is the midpoint of the two

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-05-01 12:16:46 +03:00
Hakan SevenandClaude Sonnet 4.6 fc0aa8698b feat: show Save/Discard/Cancel dialog before closing unsaved tabs or quitting
Opens a floating OS window when the user tries to close a dirty tab or
quit with unsaved changes. The main window stays alive until the dialog
is resolved — Cancel aborts the close entirely. For quit with multiple
dirty tabs the dialog loops through each one individually.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-05-01 11:31:11 +03:00
Hakan SevenandClaude Sonnet 4.6 fad7b16d5f fix: correct PageSetup paper size when layout origin is non-zero
paper_limits() can return a non-zero origin (e.g. when min_limits has
negative margin values).  PageSetup was using x1/y1 directly instead of
(x1-x0)/(y1-y0), giving wrong dimensions in those cases.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-05-01 10:52:25 +03:00
Hakan SevenandClaude Sonnet 4.6 05dd23a8dc fix: increment camera_generation on orbit/pan/zoom to trigger dirty sync
Direct camera.orbit/pan/zoom calls in update.rs bypassed camera_generation
so the Tick handler never detected a change and never wrote the camera back
to the document. Add camera_generation += 1 after each navigation call.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-05-01 07:16:58 +03:00
Hakan SevenandClaude Sonnet 4.6 8f90a68930 feat: restore and persist saved camera position per layout
- On file open and layout tab switch, restore the camera from the
  document's saved view (*Active VPort for model space, sheet viewport
  for paper space) instead of fitting to wire bounds.
- After any zoom/pan/rotate, write the new camera position back to the
  document and mark the file dirty so it is saved with the file.
- Uses last_synced_camera_gen to avoid marking the file dirty on the
  initial camera restore.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-05-01 07:07:19 +03:00
Hakan SevenandClaude Sonnet 4.6 6a0b9169d6 fix: identify user viewports by geometry when id is 0 or negative
The acadrust DWG reader never assigns a viewport id (always 0).
Some DXF exporters use id=-1 for all viewports.  In both cases the
previous id > 1 check excluded every viewport from content rendering.

Replace is_user_viewport_id(id) with is_content_viewport(vp) that
uses a geometry heuristic for unknown ids (0 / negative):
  - id=1           → always sheet viewport (excluded)
  - id≥2           → always user viewport  (included)
  - id=0 or id<0   → sheet if center≈(0,0) AND scale≈1.0, else user

This allows DWG files and non-standard DXF files to display model
content inside their paper-space viewports and be entered via
double-click.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-30 22:06:43 +03:00
Hakan SevenandClaude Sonnet 4.6 66a73b00e1 fix: make viewports from external DXF/DWG files visible and enterable
Two root causes prevented paper-space viewports from loading correctly:

1. acadrust's ViewportStatusFlags::from_bits() maps bit 0 → is_on, but
   the real DXF/DWG spec uses bit 15 (0x8000) for "viewport on". Post-load
   fixup in io/mod.rs now corrects this for both DXF and DWG files: when
   bit 0 is clear but bit 15 is set, is_on is forced true and locked is
   derived from bit 14 (0x4000).

2. Some DXF exporters write id=-1 for all viewport entities instead of the
   standard id=1 (sheet) / id≥2 (user) convention. The is_user_viewport_id()
   helper now treats any id ≠ 0 and ≠ 1 as a user viewport, covering both
   the standard id≥2 and the non-standard id=-1 cases.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-30 21:57:02 +03:00
Hakan SevenandClaude Sonnet 4.6 8c3964305b fix: convert grip positions to local space for display and hit-test
Entity grips() return world-space coordinates (raw DXF values). After
the world_offset change the camera view_proj is in local space, so
projecting world-space grips through it gave wrong screen positions and
hit-test failures.

- refresh_selected_grips: subtract world_offset from every grip.world
  so selected_grips are stored in local (camera) space
- Grip drag: cursor/snap positions are already local space; for
  GripApply::Absolute add world_offset back before passing to entity
  so entity coordinates stay in world space. Translate deltas are
  identical in both spaces so no conversion is needed there.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-27 18:27:46 +03:00
Hakan SevenandClaude Sonnet 4.6 a2fb334966 fix: world_offset precision fix for large-coordinate DXF files (e.g. Turkish UTM)
Subtracts the DXF header extents centroid (world_offset) from all coordinates
in f64 BEFORE casting to f32. This eliminates the ~0.5 m ULP error that occurs
at 4,000,000 m UTM coordinates and caused geometry to disappear on zoom-out.

- Scene: adds world_offset field + compute_and_set_world_offset() method
- tessellate.rs: threads world_offset through all f64→f32 conversions
  (legacy_geometry, dimension_geometry, solid_wire_fallback, multileader)
- truck_tess.rs: to_local() helper already in place from prior session
- mod.rs: tessellate_entity() and entity_aabb() apply world_offset
- update.rs: compute_and_set_world_offset() called after doc load
- cmd_result.rs / solid3d_cmds.rs: pass correct offset to truck tessellators

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-27 07:41:38 +03:00
Hakan SevenandClaude Sonnet 4.6 bfd8d0411a perf: AABB snap pre-rejection via acadrust bounding_box() (Option K upgrade)
Add `aabb: [f32; 4]` field to WireModel (world-space 2-D bounding box).
In tessellate_entity() set each wire's AABB from e.as_entity().bounding_box()
via new entity_aabb() helper; Insert sub-entities each get their own AABB.
Preview/interim wires and any entity returning a zero-extent default box get
UNBOUNDED_AABB so they are never pre-rejected.

Replace the chord-sphere heuristic in Snapper::wire_in_range() with a proper
AABB vs snap-circle overlap test.  The new check is four scalar comparisons and
correctly handles closed curves (circles, arcs) which the chord-sphere approach
had to special-case.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-26 12:21:51 +03:00
Hakan SevenandClaude Sonnet 4.6 2da9301a2b perf: viewport wire cache, Arc hit_test_wires, snap world-space pre-rejection (I+J+K)
Option I: add per-viewport Arc wire cache so paper-space viewport rendering
no longer re-tessellates model content every frame.

Option J: hit_test_wires() returns Arc<Vec<WireModel>> — O(1) pointer copy
in model space instead of a full Vec clone on every mouse move during commands.
paper_canvas_hatches/wipeouts() now route through the epoch caches added in F.

Option K: add world_snap_r + wire_in_range() pre-check to Snapper::snap().
All Endpoint/Midpoint/Nearest/Perpendicular/Intersection loops skip wires
whose chord sphere is outside the snap circle using O(1) scalar comparisons,
eliminating O(entities × vertices) matrix multiply cost when zoomed in.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-26 11:55:08 +03:00
Hakan SevenandClaude Sonnet 4.6 d1ecaf4695 perf: eliminate per-frame re-tessellation and GPU re-upload in large files
Add two complementary render caches that make mouse navigation essentially
free regardless of file size:

Option A – Wire tessellation cache: entity_wires_arc() caches the
tessellated Vec<WireModel> keyed by geometry_epoch and returns an Arc,
so navigation frames (no preview wires) incur zero Vec clones and zero
tessellation work. Primitive::wires changed to Arc<Vec<WireModel>>.

Option B – GPU buffer cache: Pipeline stores cached_epoch; prepare()
skips all upload_wires/hatches/images/meshes calls when the epoch is
unchanged, writing only the 192-byte camera uniform instead.

geometry_epoch is a process-wide AtomicU64 incremented by every Scene
mutation (add/erase entity, selection, layer visibility, layout switch,
populate_*, grip, transform, copy, clear, preview wires). Using a global
counter rather than a per-scene counter prevents two tabs that happen to
have the same local bump count from sharing a cached_epoch value, which
was causing the first file's geometry to remain on screen after opening a
second file.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-26 02:13:31 +03:00
Hakan SevenandClaude Sonnet 4.6 6b4bc1b832 fix: paper background no longer resizes when a viewport is added
White paper fill was calculated from entity bounding boxes (paper_entity_extents),
so adding a viewport caused the paper to visually shrink to the viewport's bounds.
Now paper_limits() (layout min/max limits) is used directly, and the unused
paper_entity_extents() function is removed.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-24 12:02:33 +03:00
Hakan SevenandClaude Sonnet 4.6 3ec7bf7065 refactor: deduplicate entity type name mapping
Make scene::properties::entity_type_name the single source of truth.
entity_type_label in helpers.rs now delegates to it instead of
maintaining a separate match list.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-24 01:44:33 +03:00
Hakan SevenandClaude Sonnet 4.6 2ee1c8e196 fix: entity_type_label/key in helpers.rs cover all EntityType variants
The Properties panel header was still showing "Entity" for unrecognized
types because helpers.rs had its own incomplete match. Both functions now
enumerate every acadrust EntityType variant explicitly.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-24 01:43:13 +03:00
Hakan SevenandClaude Sonnet 4.6 5f3182fa9a fix: NAVVCUBE and UCSICON commands now properly toggle visibility
NAVVCUBE: propagate show_viewcube flag through ViewportPane and the wgpu
Primitive so the ViewCube pipeline skips upload/render when hidden.
UCSICON: bare command (no subcommand) now toggles current state instead
of printing usage info.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-24 01:31:52 +03:00
Hakan SevenandClaude Sonnet 4.6 1ec53a4730 feat: view tab widget toggle commands and unified SVG icon color scheme
Integrate NAVVCUBE, NAVBAR, PROPERTIES, FILETAB, LAYOUTTAB, TOOLPALETTES
and SHEETSET commands to toggle UI widgets at runtime. Add 15 new SVG icons
for view module tools previously using glyph fallbacks. Restyle all SVG
icons to use the established two-color scheme: #e0e0e0 for passive geometry,
#4cc9f0 for active/accent elements.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-24 01:23:06 +03:00
Hakan SevenandClaude Sonnet 4.6 a6592cc9bc fix: ViewCube hover, glow, centroids, corner geometry and snap angles
- Hide OS crosshair and drawn crosshair when cursor is over ViewCube area
- Enhanced hover glow in WGSL shader (blue tint + brightness boost)
- Fixed region_centroids() Y-axis sign for all FRONT/BACK-related regions
- Swapped corner geometry assignments (TPF↔TBK, BTF↔BBK) to match
  intended FRONT/BACK semantics (sy=-1 = FRONT, sy=+1 = BACK)
- Corrected all snap_angles: yaw_pitch_to_quat(0,0) places eye at -Y
  (FRONT view), so FRONT=0 and BACK=PI — cascaded through edges/corners

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-23 19:11:19 +03:00
Hakan SevenandClaude Sonnet 4.6 578dd51af4 fix: set A4 landscape limits on initial Layout1 at document creation
acadrust initialises every Layout with 12×9 imperial defaults; override
all non-Model layouts to 297×210mm in new_drawing() so the first paper
tab opens at A4 size, consistent with layouts added later.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-23 14:23:38 +03:00
Hakan SevenandClaude Sonnet 4.6 f63858876d fix: align white paper fill with entity borders
- paper_entity_extents() computes the bounding box of actual DXF entities
  so the white fill always matches the drawn title-block / frame borders,
  regardless of whether Layout min/max_limits match entity positions
- Falls back to paper_limits() (→ A4 297×210) when layout has no entities
- Removed paper_boundary_wire: the white fill now acts as the visual paper
  edge so the near-white wire (invisible on white) was redundant
- LayoutCreate sets limits to A4 landscape immediately after add_layout()
  to override acadrust's imperial default (12×9)

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-23 14:21:03 +03:00
Hakan SevenandClaude Sonnet 4.6 0a291c877b feat: A4 landscape default paper, white paper area on dark desk
- paper_limits() falls back to A4 landscape (297×210mm) even when no
  Layout object exists in the document
- paper_bg_color default changed to white [1,1,1,1] (paper surface color)
- PaperCanvas::draw() fills canvas with dark desk color first, then draws
  the paper area rectangle in paper_bg_color so the sheet is clearly
  visible against the desk background
- view.rs container background uses the fixed desk color instead of
  paper_bg_color so it no longer conflicts with the paper-surface color
- BACKGROUND RESET for paper space now resets to white instead of dark gray
- Removed unused PaperSheet variant, paper_sheet() constructor,
  build_paper_sheet_primitive() and associated match arms (dead code
  after 2D canvas replacement)

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-23 14:05:07 +03:00
Hakan SevenandClaude Sonnet 4.6 fcc29de469 feat: inline MSPACE viewport overlay on 2D paper canvas
- viewport_screen_rect now mirrors PaperCanvas::draw()'s camera-based
  to_px transform instead of using a fixed paper-limits scale, so the
  3D overlay lands exactly over the drawn viewport border at any zoom/pan

- blue border frame (stack layer above the shader) makes the active
  viewport boundary always visible even when it fills the canvas

- space-mode pill in MSPACE now dispatches LayoutSwitch("Model") so
  clicking it jumps to full Model Space instead of just exiting to PSPACE

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-23 13:57:34 +03:00
Hakan SevenandClaude Sonnet 4.6 459b525ffe feat: replace paper-space shader with 2D Iced canvas widget
Paper space is now rendered as a 2D vector canvas (iced::widget::canvas)
instead of a 3D shader widget.  This allows direct interaction with
paper-space entities (title blocks, viewport borders, annotations)
without needing to enter MSPACE first.

Changes:
- src/scene/paper_canvas.rs: new PaperCanvas<'a> canvas::Program that
  renders paper wires (with linetype dashes), solid/gradient hatch fills,
  and wipeout backgrounds using the paper camera's orthographic transform.
- Scene: three new public helpers (paper_canvas_wires, paper_canvas_hatches,
  paper_canvas_wipeouts) feed the canvas with the same data the old shader
  used, including inactive viewport projections and interim/preview wires.
- view.rs: paper_canvas_view() now wraps PaperCanvas instead of
  shader(ViewportPane::paper_sheet(…)).  The PaperViewportPane 3D shader
  overlay for the active MSPACE viewport is unchanged.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-23 09:48:53 +03:00
Hakan SevenandClaude Sonnet 4.6 c2df63c4cc feat: add clickable space-mode button to status bar
Replaces the static MODEL/LAYOUT pill with an interactive button that
shows and toggles the current editing context:

  Model tab     → "MODEL"  (non-clickable, informational)
  Layout PSPACE → "PAPER"  (click → MspaceCommand: enter MSPACE)
  Layout MSPACE → "MODEL"  (click → ExitViewport: return to PSPACE)

Highlights in blue when in MSPACE to signal active model editing.
Mirrors the AutoCAD status-bar convention for MODEL/PAPER switching.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-23 09:18:01 +03:00
Hakan SevenandClaude Sonnet 4.6 f176f7c80a fix: correct ViewCubeSnap and MSPACE orbit to write viewport entity
- Add orbit_active_viewport(), snap_active_viewport_to_angles(),
  active_view_rotation_mat(), active_viewport_yaw_pitch() helpers so
  navigation gestures modify the DXF viewport entity's view_direction
  instead of the paper-space camera.
- Fix "already there → flip opposite" check: the previous code used
  rot * Vec4::W (always (0,0,0,1) for a rotation matrix); now reads
  (yaw, pitch) directly from camera_for_viewport() via the new helper.
- MSPACE right-drag orbit early-returns after writing the viewport
  entity, preventing the paper-space camera from also being modified.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-23 08:53:30 +03:00
Hakan SevenandClaude Sonnet 4.6 97dbd80c93 feat: render active MSPACE viewport as a true 3D widget in paper space
When a paper-space viewport is activated (double-click / MSPACE command),
it is now rendered through its own dedicated PaperViewportPipeline rather
than the CPU-projection approach used for inactive viewports.

Architecture:
- PaperSheet widget (full-canvas): renders paper entities + CPU-projected
  content of all viewports *except* the active one.
- PaperViewportPane widget (Fixed w×h, positioned at viewport screen rect):
  renders the active viewport with a true 3D camera derived from the
  viewport's view_direction/view_target/view_height.

PaperViewportPipeline is a newtype of Pipeline.  Having a distinct type
gives it its own Iced storage entry (keyed by TypeId), preventing the
shared prepare() overwrite that broke the earlier per-viewport approach.

Changes:
- render.rs: PaperViewportPipeline, PaperViewportPrimitive newtypes;
  build_active_viewport_primitive(); build_paper_sheet_primitive() now
  excludes active_viewport from CPU projection.
- viewport_pane.rs: PaperViewportPane<'a> with shader::Program impl.
- mod.rs: viewport_content_wires() gains exclude_vp parameter;
  viewport_screen_rect() is no longer dead code.
- view.rs: paper_canvas_view() overlays PaperViewportPane when MSPACE
  is active.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-23 03:19:15 +03:00
Hakan SevenandClaude Sonnet 4.6 d1d4bb3d02 fix: restore paper-space viewport rendering as single full-canvas widget
Iced 0.14 batches all shader prepare() calls before any render() calls.
Multiple ViewportPane widgets sharing the same Pipeline type overwrite
each other's GPU buffers, causing the PaperSheet layer (full-canvas) to
render the last viewport's model content — appearing full-screen.

Fix: paper_canvas_view() now renders a single full-canvas PaperSheet
widget. build_paper_sheet_primitive() is updated to include model-space
content projected through each viewport's view matrix (via
viewport_content_wires()), restoring the original working behaviour.

ViewportPane::Paper mode and viewport_screen_rect() are kept with
#[allow(dead_code)] — a per-viewport wgpu sub-renderer that accumulates
data across frames could revive them in the future.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-23 03:02:01 +03:00
Hakan SevenandClaude Sonnet 4.6 da9fafc0f8 feat: introduce ViewportPane widget for unified model/paper rendering
- Remove Scene: shader::Program<Msg> impl; all GPU rendering now goes
  through ViewportPane which supports three modes:
    Model      — full model-space view (unchanged behaviour)
    PaperSheet — paper-space entities only, using the paper camera
    Paper      — model content through a specific viewport's own camera

- Add Scene helper methods (render.rs):
    build_primitive / build_paper_sheet_primitive / build_viewport_primitive
    update_viewcube_state / viewcube_mouse_interaction

- Add Scene helpers (mod.rs):
    paper_sheet_wires()        — paper entities without viewport projection
    camera_for_viewport()      — derive Camera from Viewport entity data
    model_wires_for_viewport() — model wires filtered by per-viewport frozen layers
    viewport_screen_rect()     — paper-space → pixel rect mapping

- Add paper_canvas_view() in view.rs:
    Stack of PaperSheet + one Paper widget per Viewport entity,
    positioned with Space offsets from viewport_screen_rect()

- Update VIEWPORT_WIDGET_PLAN.md: mark Steps 1–4 done, document remaining work

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-23 02:18:41 +03:00
Hakan SevenandClaude Sonnet 4.6 070ad1f9c5 feat: add Report/About/Changelog buttons to Support ribbon group
- REPORT opens GitHub new-issue page
- ABOUT opens a dialog window with version, OS and arch info + Copy Info button
- CHANGELOG opens GitHub releases page
- Fix Windows build: add windows-sys as target-specific dependency for PRINT command

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-22 22:22:33 +03:00
Hakan SevenandClaude Sonnet 4.6 ef988c20a3 feat: redesign Insert tab and fix all warnings
- Redesign Insert ribbon tab with 6 groups matching screenshot layout:
  Reference (Attach/Clip/Adjust large + Underlay Layers/Frames/Snap small),
  Point Cloud (Attach), Block (Multi-View Block/Insert large + Create/Edit/Base small),
  Attributes (Define/Edit large + Manage/Sync small),
  Import (Import/Land XML), Content (Content Browser/Design Center)
- Remove Primitives group and dead box_prim/clear/cylinder/sphere modules
- Fix all compiler warnings with #[allow(dead_code)] on unused-but-intentional items

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-09 12:44:31 +03:00
Hakan SevenandClaude Sonnet 4.6 bd68114bde feat: add style selector comboboxes to Annotate ribbon tab
New StyleComboGroup RibbonItem renders a dropdown combobox for selecting
text style, dimension style, multileader style, or table style. Ribbon
groups in the Annotate tab now match the reference layout: each group
has a large tool on the left and a style combobox with tool rows on the
right.

Each combobox shows the active style name, opens a scrollable item list
on click, and has an optional "Manage…" row that fires the style manager
command (STYLE / DIMSTYLE / MLEADERSTYLE / TABLESTYLE).

Style state is synced from the document on every layer refresh via
sync_ribbon_styles(). RibbonStyleChanged message propagates selections
back to the acadrust document header and the active_mleader_style field.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-09 03:38:23 +03:00
Hakan SevenandClaude Sonnet 4.6 08b9a21d31 feat: add missing Annotate tab commands and icons
New dimension commands: QDIM, DIMEDIT, DIMTEDIT, DIMBREAK, DIMSPACE,
DIMJOGLINE with full command workflows and SVG icons.

New multileader commands: MLEADERADD, MLEADERREMOVE, MLEADERALIGN,
MLEADERCOLLECT with entity injection pattern for in-place editing.

Updated annotate ribbon: all tools exposed across Text, Dimensions,
Leaders, Tables, Markup, and Annotation Scaling groups. Added STYLE,
FIND, DIMSTYLE shortcut buttons.

Added `inject_picked_entity` to the CadCommand trait for commands that
need a cloned copy of the picked entity to modify (DIMTEDIT, MLEADERADD,
MLEADERREMOVE). Sentinel-based dispatch in cmd_result.rs handles
DIMBREAK, DIMSPACE, DIMJOGLINE, MLEADERALIGN, and MLEADERCOLLECT.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-09 03:26:47 +03:00
Hakan SevenandClaude Sonnet 4.6 fe4f9ca844 refactor: move all dialog UIs to dedicated files under src/ui/
Each dialog window (Page Setup, DimStyle, TextStyle, TableStyle,
MLineStyle, PlotStyle, Layout Manager, Shortcuts) is now a proper
window-filling layout in its own src/ui/<dialog>.rs file.

Pattern: toolbar strip at top + vertical separator + scrollable
content filling the rest — no modal card, no backdrop, no stack[].
Matches the existing Layer Manager window pattern.

Removed ~1600 lines of old overlay functions from view.rs.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-08 22:56:51 +03:00
Hakan SevenandClaude Sonnet 4.6 888095cc3e refactor: convert all overlay dialogs to separate OS windows
Every dialog panel that previously rendered as an in-viewport overlay is
now opened as its own OS window, matching the behaviour of the Layer
Properties Manager:

  • Page Setup           (520×460, non-resizable)
  • Text Style           (620×460)
  • Table Style          (620×420)
  • Multiline Style      (620×420)
  • Layout Manager       (640×320)
  • Plot Style Editor    (780×540)
  • Dimension Style Mgr  (720×560)
  • Keyboard Shortcuts   (720×520)

Changes:
  - mod.rs: replace *_open: bool fields with *_window: Option<window::Id>;
    add window IDs to OsWindowClosed tracker; add per-window title strings.
  - update.rs: Open handlers call window::open(); if already open, call
    window::gain_focus() instead of reopening. Close handlers call
    window::close(). OsWindowClosed clears all panel window IDs.
  - view.rs: each window_id is matched at the top of view() and returns
    the panel content directly. All overlay let-bindings and stack![]
    references removed.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-08 22:36:28 +03:00
Hakan SevenandClaude Sonnet 4.6 781bd186ee chore: fix all compiler warnings
- cmd_result.rs: remove unused Point3 and Rad imports (SWEEP block)
- scene/mod.rs: remove unused Solid3D struct import
- commands.rs: drop unnecessary `mut` on new_entities (REFCLOSE block)
- refedit.rs: annotate insert_handle with #[allow(dead_code)]

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-08 22:28:31 +03:00
Hakan SevenandClaude Sonnet 4.6 b24e0c5da9 feat: STEPOUT command — export 3D meshes to STEP AP203
Adds STEPOUT / STPOUT / EXPORTSTEP command that exports all tessellated
Solid3D / Region / Body meshes to an ISO 10303-21 (STEP AP203) file.

Each triangle is encoded as a minimal ADVANCED_FACE with a PLANE surface
and CLOSED_SHELL topology — sufficient for import into all major CAD systems.
A file-save dialog picks the output path; the command line reports success or
errors. Requires at least one tessellated solid in the drawing.

Closes ROADMAP 14.10.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-08 22:23:58 +03:00
Hakan SevenandClaude Sonnet 4.6 f515b556c7 feat: PRINT command — send layout to system printer via lp/lpr
The PRINT command renders the current layout to a temporary PDF (reusing
the existing PDF export pipeline) and dispatches it to the OS default
printer:
  - Linux/macOS: `lp` (CUPS) with `lpr` as fallback.
  - Windows: ShellExecute "print" verb (compiled-in, unused on Linux).

PLOT and EXPORT still open the save-file dialog as before.
Status messages are shown in the command line during the async job.

Closes ROADMAP 1.8.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-08 22:21:50 +03:00