Commit Graph
45 Commits
Author SHA1 Message Date
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 1c0520e4e4 fix: correct CPU projection axes to match GPU look_at_rh convention
viewport_content_wires() computed view_right via world_z × view_direction
(cross-product). This gives the OPPOSITE sign to the right vector that
look_at_rh produces for views with a Y component (FRONT/BACK views):

  GPU look_at_rh:  screen_right = f × up = (−vd) × (rot*Y) = rot*X
  Old CPU formula: screen_right = Z × vd  ← opposite sign for FRONT/BACK

The mismatch caused the paper-space projection to appear mirror-flipped
after panning/rotating in MSPACE.

Fix: use camera_for_viewport() to obtain the exact same rotation
quaternion the GPU uses, then derive both axes from it:
  view_right = rotation * X
  view_up    = rotation * Y

Also replaces the perspective depth dot-product (mp · vd) with
mp · (rotation*Z) so the depth direction is consistent too.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-23 09:28:54 +03:00
Hakan SevenandClaude Sonnet 4.6 2c5405aa49 fix: correct MSPACE orbit and pan direction
orbit_active_viewport was writing cam.rotation*Z directly to
view_direction, but yaw_pitch_to_quat(y,p)*Z has its Y component
negated relative to the snap convention: (cos(p)*sin(y), -cos(p)*cos(y),
sin(p)) vs (cos(p)*sin(y), +cos(p)*cos(y), sin(p)).  This caused
camera_for_viewport to reconstruct yaw = π - original_yaw, making every
orbit appear reversed.  Fix: negate Y when writing view_direction back.

pan_active_viewport was using the paper-space camera to convert screen
pixels to model-space delta.  This gives wrong pan axes for tilted
MSPACE views (e.g. front/side views where cam_up is Z, not Y).  Fix:
use camera_for_viewport so the pan axes match the 3-D view orientation,
and update all three view_target components (X/Y/Z) for tilted views.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-23 09:10:55 +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 62fbaf5da4 fix: hide block definition entities from viewport
Block-definition geometry (entities stored inside named blocks) was
leaking into the viewport when DXF files omit the owner-handle group
code (330) on block-content entities.

The fix tightens `belongs_to_visible_block`: when `owner_handle` is
null, the current layout block-record's `entity_handles` list is used
as the authoritative allow-list before falling back to the older
"not-in-any-other-block" heuristic.  This ensures block definitions
are never rendered directly, only when referenced via an INSERT.

Bumps version to 0.1.4.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-22 22:03:32 +03:00
Hakan SevenandClaude Sonnet 4.6 e2f5715de8 fix: correct arc sweep direction for reversed normals and mirrored blocks
- Arc rendering now respects the normal vector: when normal.z < 0 the
  midpoint is placed on the clockwise side so the curve sweeps the right way
- Removed spurious .to_radians() / .to_degrees() round-trips in arc.rs
  (angles are already stored in the unit the trig functions expect)
- apply_grip and apply_transform use consistent angle units
- normalize_insert_entity no longer converts arc angles; the old conversion
  was only needed by the now-removed .to_radians() call in to_truck
- Added fix_mirrored_arc: swaps arc start/end angles when the INSERT has a
  mirrored scale (x_scale * y_scale < 0) so exploded arcs curve correctly

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-22 21:48:21 +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 5cf7a19ca8 feat: Region and Body entity tessellation + render
Refactors solid3d_tess to share tessellate_sat() across Solid3D,
Region (2D planar ACIS body), and Body (3D ACIS body).
populate_meshes_from_document() and add_entity() now tessellate
all three entity types into GPU MeshModels when ACIS SAT data
is available.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-08 18:37:24 +03:00
Hakan SevenandClaude Sonnet 4.6 85f9b930a2 feat: Layout Manager GUI (LAYOUTMANAGER / LAYOUTPANEL command)
Panel overlay showing all layouts with their active status.
Supports: select, rename, new, delete, reorder (◀▶), set current.
Also adds Scene::swap_layout_order() for tab-order resequencing.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-08 18:28:30 +03:00
Hakan SevenandClaude Sonnet 4.6 549ae38459 feat: BOX, SPHERE, CYLINDER primitives + EXTRUDE / REVOLVE commands
BOX, SPHERE, and CYLINDER build truck topology solids and tessellate
them into MeshModels stored alongside a Solid3D placeholder entity so
the GPU mesh pipeline renders them immediately.

EXTRUDE picks any closed 2D profile (Circle, LwPolyline, etc.),
attaches a planar face via try_attach_plane, and translational-sweeps
it (tsweep Face → Solid) along Z by the given height.

REVOLVE picks a profile, two axis points, and an angle (default 360°),
then rotational-sweeps the wire (rsweep) around the axis.

Three new CmdResult variants drive the operations:
  CommitSolid3D, ExtrudeEntity, RevolveEntity.

Scene::layer_color() added to retrieve the active layer's RGBA color.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-08 12:55:28 +03:00
Hakan SevenandClaude Sonnet 4.6 a773a1a1f1 feat: REFEDIT / REFCLOSE — in-place block reference editing
REFEDIT picks an INSERT, extracts the block entities into model space
with the INSERT transform applied (translate + rotate + uniform scale),
and enters an editing session.  Any normal modify command works on the
temporary entities.

REFCLOSE SAVE applies the inverse transform, replaces the block
definition, and rebuilds all derived caches (hatch/image/mesh), so
every INSERT of that block reflects the edits immediately.
REFCLOSE DISCARD removes the temp entities without changing the block.

Non-uniform-scale inserts are rejected with an error message.
RefEditSession is stored on DocumentTab; undo snapshots bracket the
begin and close operations.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-08 12:40:19 +03:00
Hakan SevenandClaude Sonnet 4.6 a99115bda5 feat: Solid3D (3DSOLID) ACIS tessellation
Parse SAT geometry directly into GPU MeshModel without going through
the truck B-rep pipeline:
  • plane-surface faces  → fan-triangulate the coedge-loop polygon
  • cone-surface faces   → parametric grid (handles cylinders and cones)
  • sphere-surface faces → UV sphere grid (GRID_U × GRID_V)
  • torus-surface faces  → UV torus grid

Scene integration:
  • add_entity(): tessellates Solid3D on insert, stores in scene.meshes
  • erase_entities(): removes mesh from scene.meshes on delete
  • populate_meshes_from_document(): rebuilds all meshes on file load
    and undo/redo (called alongside populate_hatches/images)

ROADMAP updated: Solid3D tessellation marked ✅ in sections 2.12, 14.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-07 19:11:24 +03:00
Hakan SevenandClaude Sonnet 4.6 c184535d5e fix: render wipeout fills after wire pass so they correctly mask wires
Previously wipeout solid fills were part of the hatch pass (pass 1),
drawn before wires (pass 5), so all wires appeared on top of the
wipeout. This fix moves wipeout fills into a dedicated pass 6 that
runs after wires, so the background-colored polygon correctly covers
the lines/text beneath it — matching AutoCAD behaviour.

- Scene.wipeout_models() separated from synced_hatch_models()
- Primitive.wipeout_hatches populated via wipeout_models()
- Pipeline.gpu_wipeouts + upload_wipeouts() + pass 6 after wires

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-06 23:44:36 +03:00
Hakan SevenandClaude Sonnet 4.6 efc259fdbe feat: per-viewport layer freeze columns in Layer Manager
Add VP freeze/thaw toggle columns to the layer panel when in a paper
space layout. Each viewport gets its own column (VP 1, VP 2, …) showing
a freeze icon per layer. Clicking toggles frozen_layers on the Viewport
entity and syncs back to the panel.

- scene: viewport_list() → [(handle, label, frozen_handles)]
- LayerPanel: VpCol + vp_frozen per layer, sync_with_viewports()
- Message::LayerToggleVpFreeze(layer_idx, vp_col_idx)
- Sync VP columns on layout switch, VPLAYER cmd, undo/redo, layer ops

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-06 23:41:39 +03:00
Hakan SevenandClaude Sonnet 4.6 5d0edb3a1f fix: Wipeout fill color respects BACKGROUND command override
Scene now carries bg_color / paper_bg_color fields (defaults match
previous hard-coded values). synced_hatch_models() reads these instead
of hard-coded constants, so Wipeout fills correctly use the custom
background when BACKGROUND <r> <g> <b> has been set.
BACKGROUND command syncs both DocumentTab.bg_color and scene.bg_color.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-06 23:32:11 +03:00
Hakan SevenandClaude Sonnet 4.6 4782c3bd96 feat: ImageDefinition object auto-created on RasterImage placement
When add_entity receives a RasterImage without a definition_handle,
create an ImageDefinition object in document.objects and link it.
This ensures DWG/DXF files save the IMAGEDEF section correctly so
other CAD programs can resolve image paths on reload.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-06 23:24:20 +03:00
Hakan SevenandClaude Sonnet 4.6 4312b6c3a5 feat: raster image render pipeline (IMAGE command + GPU textured quad)
- Add IMAGE / IMAGEATTACH / IM command with async file-picker (rfd)
- src/scene/image_model.rs: ImageModel with RGBA pixel data + quad corners
- src/scene/pipeline/image_gpu.rs: ImageGpu with wgpu texture, sampler, opacity uniform
- src/scene/pipeline/mod.rs: image render pipeline (pass 2, between hatches and meshes)
- src/scene/render.rs: include images in Primitive, call upload_images in prepare
- src/scene/mod.rs: images HashMap, populate_images_from_document, cache on add_entity
- src/modules/home/draw/raster_image.rs: ImageCommand (two-click: origin + width, aspect-correct preview)
- src/io/mod.rs: pick_image_file() async helper (file dialog + dimension decode)
- app: ImagePick / ImagePickResult messages, populate on file open and undo/redo

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-06 23:19:08 +03:00
Hakan SevenandClaude Sonnet 4.6 7e6de57c8d feat: Wipeout solid fill masking
Wipeout entities now render a background-colored solid fill polygon
(via the hatch GPU pipeline) that masks other hatch fills beneath them.
Fills use model space background (0.11 grey) or paper space background
(0.22/0.24/0.28). Polygon and rectangular clip boundaries both supported.
Selected wipeouts use a translucent blue highlight instead.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-06 14:46:47 +03:00
Hakan SevenandClaude Sonnet 4.6 1db3ad6a33 Feat: implement CTB/STB plot style table with PDF export integration
- src/io/plot_style.rs: PlotStyleTable and PlotStyleEntry data structures;
  CTB reader (deflate/zlib decompressed text format) with ACI→RGB and
  ACI→lineweight lookup; CTB writer (zlib compressed); LW_TABLE constant
- src/io/pdf_export.rs: accept optional PlotStyleTable; apply ACI color
  and lineweight overrides during PDF rendering
- src/scene/wire_model.rs: add `aci: u8` field to carry ACI index
- src/scene/render.rs: render_style() now returns ACI alongside RGBA
- src/scene/mod.rs + tessellate.rs: propagate aci through wire building
- app: PlotStyleLoad/Loaded/Clear messages and handlers;
  PLOTSTYLE LOAD | CLEAR | STATUS command; active_plot_style state on H7CAD
- flate2 added as dependency

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-06 14:37:23 +03:00
Hakan SevenandClaude Sonnet 4.6 e38aa6b611 Feat: Solid entity (2D) — render, grips, properties, transform
TruckConvertible renders the 4 perimeter edges as wireframe outline.
The scene injects a solid-fill HatchModel for the filled quad using the
existing hatch GPU pipeline (DXF Z-order: p0→p1→p3→p2).
Grippable provides 4 square corner grips; grip moves rebuild the fill model.
PropertyEditable exposes all 4 corners and thickness. Transformable handles
move/rotate/scale/mirror via the standard entity transform path.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-03 19:22:10 +03:00
Hakan SevenandClaude Sonnet 4.6 749f3b99dc Feat: hatch boundary grip editing — all edge types supported
Polyline, Line, CircularArc, EllipticArc, and Spline boundary edges
all get diamond grips at their control vertices. Dragging a grip updates
the underlying DXF boundary vertex and immediately rebuilds the GPU
hatch model via Scene::apply_grip.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-03 19:16:17 +03:00
Hakan SevenandClaude Sonnet 4.6 0aef1fc12f Feat: ROADMAP Phase 1-5 batch — trim/extend, plot scale, viewport UX, mline caps, table alignment
- TRIM/EXTEND: Ray and XLine entities supported as cutting boundaries and trim targets;
  trim results in Ray→Line/Ray, XLine→Ray/Ray or Line; hover preview added
- Plot scale override: Fit/1:1/1:2/1:5/1:10/1:20/1:50/1:100/2:1 buttons added to Page Setup
  panel; PageSetupCommit applies scale to PlotSettings
- Viewport UX: locked viewport gets orange border; selected viewport gets white border
- Named view → viewport assignment: Choice dropdown in properties panel copies
  view_target/direction/height to the viewport entity
- MLine caps: perpendicular start/end cap lines added for open (non-closed) MLines
- Wipeout polygon grips: boundary vertices become editable grips when clipping_enabled+Polygonal;
  world→pixel back-projection on drag
- Table cell alignment: CellStyle.alignment (1-9) drives left/center/right + top/middle/bottom
  text positioning; cxf::measure_text() helper added
- New commands/features: DIMCONTINUE (DCO), DIMBASELINE (DBA), TOLERANCE (TOL), TABLE,
  ATTDEF, ATTEDIT, ATTDISP, BREAKATPOINT (BAP), PLOTWINDOW (PW), MLINE (ML);
  DIMSTYLE/DIMCURRENT/CLAYER/LTSCALE/CELTSCALE/SCALETEXT/XDATA/UCSICON/REGEN/DRAWORDER extended
- PLINE arc mode: A/L/CLOSE text input, bulge computation, arc preview
- Warning cleanup: removed unused imports and unreachable patterns

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-03 15:31:05 +03:00
Hakan SevenandClaude Sonnet 4.6 1572d0c38f Feat: Phase 1.5 — viewport count in status bar + REVCLOUD command
Status bar: shows "N VP" pill (with tooltip) when in a paper layout that has
user viewports (id > 1). New Scene::viewport_count() method.

REVCLOUD: closed LwPolyline with arc bumps (bulge = 0.5) along each edge.
Click polygon corners, Enter to close into a revision cloud shape.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-02 23:52:35 +03:00
Hakan SevenandClaude Sonnet 4.6 9a1eeab53d Feat: Phase 6.4 — Draw order render pipeline via SortEntitiesTable
wires_for_block() now looks up the SortEntitiesTable for the current block.
When found, wires are sorted by sort_handle.value() so DRAWORDER F/B results
are immediately visible in the viewport (not just written to DXF).

Entities not in the sort table default to u64::MAX/2 (middle of draw order).

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-02 23:48:17 +03:00
Hakan SevenandClaude Sonnet 4.6 70a8f5bf3f Feat: ZOOM IN/OUT/WINDOW commands; ZoomToWindow CmdResult
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-02 23:28:00 +03:00
Hakan SevenandClaude Sonnet 4.6 7c3cf7accc Feat: Phase 1.2 — VIEW command (save/restore/list/delete named views)
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-02 19:19:12 +03:00
Hakan SevenandClaude Sonnet 4.6 5ad2f4a8cb Feat: Phase 3 — perspective projection and per-viewport UCS properties
- viewport_content_wires(): conditional perspective divide using
  camera_dist = view_height * lens_length / 24.0 (35mm-film equiv.);
  points behind the camera are mapped to NaN for safe clipping
- viewport.rs: expose lens_length, ucs_per_viewport, ucs_origin,
  ucs_x_axis, ucs_y_axis as editable properties; handle all new
  fields in apply_geom_prop()

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-04-01 00:22:02 +03:00
Hakan SevenandClaude Sonnet 4.6 73b224c191 Feat: viewport display lock, extended properties, VPLAYER and VPORTS commands
- Enforce status.locked in pan_active_viewport and zoom_active_viewport:
  locked viewports now reject pan/zoom inputs entirely
- Add perspective, render_mode, shade_plot_mode, hide_plot, ucs_icon_visible
  fields to the viewport property panel with appropriate pickers/toggles
- Add VPORTS command: lists all user viewports in the current layout
  (id, size, position, scale, on/locked state) in the command line
- Add VPLAYER command: per-viewport layer freeze/thaw from the command line
  (F <layer> to freeze, T <layer> to thaw, Enter to exit)
- Add CmdResult::VpLayerUpdate variant and its handler in apply_cmd_result

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-03-31 23:52:18 +03:00
Hakan SevenandClaude Sonnet 4.6 f01e4f69be Fix: viewport line weights no longer scale with viewport zoom
line_weight_px was multiplied by the viewport scale factor, making lines
near-invisible at small scales (e.g. 1:100) and overly thick at large
scales (e.g. 5:1). Line weights represent physical pen widths on the
plotted sheet and must be constant regardless of viewport scale.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-03-31 13:17:42 +03:00
Hakan SevenandClaude Sonnet 4.6 9cee12d1c7 Feat: PSPACE/MSPACE interaction isolation + paper-space drawing
Selection, snapping and editing are now mode-aware:

PSPACE (paper layout, no active viewport)
- Only paper-space entities (viewport borders, title blocks, etc.) are
  hit-testable and selectable. Viewport content wires are invisible to
  the interaction layer.
- Drawing commands add entities to the paper-space layout block so
  annotations/title blocks land on the sheet, not in model space.

MSPACE (active viewport entered)
- Only model-space entities visible through the active viewport are
  hit-testable. Other viewports' content and paper entities are ignored.
- Drawing commands add entities to model space (correct DXF block).
- Snap results from projected viewport-content wires are converted from
  paper-space back to model-space via paper_to_model so that placed
  coordinates and command previews are consistent.

Entering/exiting MSPACE clears the current selection so no stale
cross-space selection is carried over.

Implementation:
- Scene::hit_test_wires() returns mode-filtered wires for all
  hit-tests, snaps, and selections (entity_wires() still returns
  everything for rendering and PDF export).
- viewport_content_wires() gains an Option<Handle> filter to render
  only the active viewport's content for MSPACE hit-testing.
- Scene::add_entity() routes to the paper-layout block or model-space
  block depending on current_layout / active_viewport.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-03-31 13:07:25 +03:00
Hakan SevenandClaude Sonnet 4.6 0beb589f2c Fix: viewport MSPACE activation and navigation
Four bugs prevented viewports from working like AutoCAD model-space windows:

1. Dead-code double-click block: `!is_down2` was always false because
   `is_down2 = sel.left_down` is captured while the button is still held
   (before the selection block clears it). Changed to `is_down` so the
   double-click handler actually fires.

2. Inverted pan direction: pan_active_viewport used `-=` on the delta
   returned by screen_delta_to_world, which is the same delta that cam.pan
   *adds* to its target — causing reversed drag. Changed to `+=`.

3. Zoom not cursor-centered: zoom_active_viewport now accepts an optional
   paper-space cursor position and adjusts view_target so the model point
   under the cursor stays fixed (same as cam.zoom_about_point).

4. ESC did not exit MSPACE: CommandEscape now calls ExitViewport when
   active_viewport is set and no command is running.

Also added MS/MSPACE and PSPACE command aliases, and MspaceCommand /
PspaceCommand messages so MS auto-enters the first viewport.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-03-31 12:52:07 +03:00
Hakan SevenandClaude Sonnet 4.6 0b8f9bfa5b Fix: deduplicate layout tabs when opening DXF/DWG files
CadDocument::new() inserts default Layout1; DXF/DWG readers then add
the file's own Layout1 on top, producing duplicate tabs with the same
name but different handles. layout_names() now deduplicates by name,
preferring the entry with a non-null block_record (the real layout from
the file).

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-03-31 12:24:49 +03:00
Hakan SevenandClaude Sonnet 4.6 a65a09c59e Fix: layout block-record resolution for DXF files
Root cause: the DXF reader never reads group code 340 (block_record
handle) inside LAYOUT objects, so layout.block_record stays Handle::NULL
after opening any DXF file.  This caused current_layout_block_handle()
to return NULL, which made belongs_to_visible_block() return true for
every entity — all entities appeared in all layouts simultaneously.

Fix in current_layout_block_handle() — three-tier lookup:
  1. Layout.block_record (non-null) — works for DWG files.
  2. Conventional DXF name ("*Model_Space", "*Paper_Space", …) derived
     from the layout's tab_order — works for standard DXF exporters.
  3. Position-based fallback: sort paper-space block records and paper
     layouts independently, then match by index — handles exporters that
     set tab_order = 0 for all layouts.

Apply the same two-tier lookup to model_space_block_handle().

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-03-31 12:08:27 +03:00
Hakan SevenandClaude Sonnet 4.6 845770938c Fix: paper limits, Cohen-Sutherland clipping, frozen layers UI, PDF improvements
Paper limits:
- Change A4 fallback from 12×9 to 297×210 mm in paper_limits()

Context menu:
- Translate layout context menu labels to English (Rename / Delete)

Viewport clipping:
- Replace AABB-only culling with proper Cohen-Sutherland line clipping
  in viewport_content_wires(); each projected segment is now clipped at
  the exact viewport boundary instead of being included or discarded whole
- Add cs_clip() and clip_polyline_to_rect() free functions in scene/mod.rs

Frozen layers per-viewport UI:
- Inject a "Frozen Layers" EditText property in the Properties panel for
  viewport entities (layer names resolved from handles at display time)
- Handle "frozen_layers" in PropGeomCommit: parse comma-separated names,
  resolve to layer handles, update vp.frozen_layers in-place

PDF export improvements:
- Add offset_x/offset_y params to export_pdf() for model-space normalization:
  coordinates are shifted so the drawing origin maps to the page origin
- Model space export computes extents via model_space_extents() (now public)
  and adds a 5 % margin; falls back to A4 landscape for empty drawings
- Skip __paper_boundary__ wire in PDF (white background covers it)
- Map yellow (active viewport border) → black and cyan (vp border) → dark
  blue for print-friendly output

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-03-31 08:18:29 +03:00
Hakan SevenandClaude Sonnet 4.6 ace83a0953 Phase 6: Auto-fit viewport and annotation scale hints
- Add model_space_extents() to Scene: collects key_vertices from all
  model-space entities to compute an AABB.
- Add auto_fit_viewport() to Scene: sets view_target to model centroid
  and derives custom_scale / view_height so the content fits the new
  viewport with a 10% margin.
- Call auto_fit_viewport() in commit_entity() after a viewport is added
  to a paper-space layout, so newly placed viewports immediately show
  model-space content at a sensible scale.
- Scale line_weight_px by the viewport scale factor when projecting
  model-space wires into paper space, so dimension arrows and annotation
  lines appear at the correct visual thickness.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-03-31 07:33:11 +03:00
Hakan SevenandClaude Sonnet 4.6 403264d3ab Phase 5: Viewport render improvements
- 3D view direction support: model geometry projected using view_right/
  view_up vectors derived from Viewport.view_direction — enables Front,
  Side, Isometric views inside paper-space viewports
- Proper AABB clipping: replaces centroid heuristic with bounding-box
  intersection test against the exact viewport rectangle
- Per-viewport frozen layers: Viewport.frozen_layers Vec<Handle> is
  resolved to layer handles and used to skip frozen entities
- view_target used consistently as the model-space pan origin
- Model wires collected per-viewport (inside the loop) so each viewport
  can apply its own layer-freeze set

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-03-31 07:28:42 +03:00
Hakan SevenandClaude Sonnet 4.6 031d2311ed Phase 4: Viewport control and properties
- Viewport ID uniqueness: commit_entity assigns max_existing_id+1
  instead of always using id=2
- Lock/On boolean toggles in Properties panel (vp_locked, vp_on)
  backed by status.locked / status.is_on fields
- Standard scale Choice picker: 1:500 to 10:1 (also syncs view_height)
- Numeric scale edit now syncs view_height when changed
- Standard view Choice picker: Top/Bottom/Front/Back/Left/Right/4×Iso
  updates view_direction vector via apply_geom_prop
- Viewport content not rendered when status.is_on is false
- current_layout_block_handle_pub() added for external access

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-03-31 07:27:03 +03:00
Hakan SevenandClaude Sonnet 4.6 523ba74c10 Phase 3: Active viewport MSPACE/PSPACE workflow
- Add active_viewport: Option<Handle> field to Scene
- Add EnterViewport(Handle) and ExitViewport messages
- Double-click on a user viewport (id > 1) enters MSPACE;
  double-click on empty area while in MSPACE exits to PSPACE
- MSPACE middle-drag pans the viewport's model-space view (view_target)
- MSPACE scroll zooms the viewport's view_height / custom_scale
- Drawing commands inside MSPACE receive model-space coordinates
  via paper_to_model() coordinate transform
- Active viewport rendered with yellow dashed border (vs. cyan solid)
- Camera screen_delta_to_world helper for MSPACE pan conversion
- LayoutSwitch clears active_viewport

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-03-31 07:24:16 +03:00
Hakan SevenandClaude Sonnet 4.6 474b38f4fb Aşama 2: Paper space görsel düzenleme
- Kağıt sınırı dikdörtgeni: paper layout'ta Layout.min/max_limits'ten
  beyaz çerçeve wire'ı oluşturulur, entity_wires()'a eklenir
- first_viewport_scale(): paper layout'taki ilk kullanıcı viewport'un ölçeğini döner
- paper_limits(): mevcut layout'un kağıt sınırlarını döner, sıfır boyutlu
  layout için varsayılan 12×9 kullanılır
- Status bar scale göstergesi: hardcoded "1:1" yerine aktif viewport ölçeği
  dinamik olarak "1:50", "2:1" vb. formatında gösterilir
- Overall viewport (ID=1) zaten gri renkte render ediliyor (tessellate_one)

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-03-31 07:17:45 +03:00
Hakan SevenandClaude Sonnet 4.6 9b14719ec5 Aşama 1: Layout yönetim UI — yeniden adlandırma, silme, context menüsü
- Layout sekme sağ-tıklamasında context menüsü (Yeniden Adlandır / Sil)
- Inline rename: sekme metin-giriş alanına dönüşür, Enter ile commit, ✕ ile iptal
- LayoutDelete: layout ve sahip olduğu tüm entity'leri kaldırır
- LayoutRename: Layout nesnesinin adını yerinde günceller
- LayoutCreate çakışma koruması: mevcut isimler arasında benzersiz ad bulur
- Escape tuşu açık rename/context menüsünü kapatır

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-03-31 07:13:36 +03:00
Hakan SevenandClaude Sonnet 4.6 bdde862597 Split large files: ribbon widgets and scene render/selection
- ribbon.rs → ribbon/mod.rs + ribbon/widgets.rs
  Extracted all rendering helpers, button styles, colour constants,
  layout constants and dropdown position calculators into widgets.rs.

- scene/mod.rs → scene/mod.rs + scene/selection.rs + scene/render.rs
  Moved SelectionState into selection.rs; moved CameraState, Primitive,
  shader::Program/Primitive impls, render_style and resolve_pattern
  into render.rs, reducing mod.rs from 1323 to ~1030 lines.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-03-22 23:07:43 +03:00
Hakan Seven d2fdac7f1e first commit 2026-03-22 14:56:27 +03:00