Commit Graph
233 Commits
Author SHA1 Message Date
Hakan SevenandClaude Sonnet 4.6 6fd740e38f feat: ellipse arc endpoint snap and LWPolyline arc midpoint grips
Partial ellipse arcs now populate key_vertices with both endpoints so
Endpoint snap works correctly. LWPolyline arc segments (bulge ≠ 0) get
a diamond grip at the arc midpoint; dragging it recomputes the segment
bulge via circumcircle of start/mid/end, giving interactive arc editing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 02:22:51 +03:00
Hakan SevenandClaude Sonnet 4.6 67022589df feat: render LWPolyline and Polyline2D wide strokes as solid fills
Wide polyline segments (constant_width, per-vertex start/end widths)
now appear as solid-filled quads or arc bands via HatchModel, generated
inline in synced_hatch_models(). Straight segments → trapezoid, arc
segments → arc-band polygon (inner/outer arc). MINSERT confirmed working
via acadrust explode_from_document → array_transforms().

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 02:20:28 +03:00
Hakan SevenandClaude Sonnet 4.6 c7a7022293 fix: Polyline3D spline vertex flags and AttributeEntity/Definition DXF rotation
Polyline3D tessellation now separates spline-fit curve vertices (flag 8)
from frame control points (flag 16): the curve vertices form the rendered
wire while control points are exposed as key_vertices for snap. Polylines
without spline flags are unchanged.

DXF post-load fixup now also converts AttributeEntity and AttributeDefinition
rotation from degrees to radians (group code 50 bug), matching the existing
LinearDimension fix. Attribute text rendered from DXF files now has correct
orientation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 22:12:56 +03:00
Hakan SevenandClaude Sonnet 4.6 5216c247c2 fix: Spline snap points and TextStyle backward/upside-down rendering
Spline entities now expose fit_points (or control_points when no fit
points are defined) as key_vertices, enabling Endpoint and Midpoint
snap to reach spline construction points.

TextStyle is_backward flag is applied by negating the width_factor,
producing horizontally mirrored text. is_upside_down rotates the text
180°. Both tessellate_text_ex and text_local_bounds now handle negative
width_factor without clamping it to zero.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 22:10:32 +03:00
Hakan SevenandClaude Sonnet 4.6 045ab96eab fix: resolve ByBlock color, linetype, and lineweight through INSERT chain
Block sub-entities with ByBlock color now inherit their INSERT's resolved
color instead of rendering as the default white. ByBlock linetype inherits
the INSERT's linetype pattern, and ByBlock lineweight inherits the INSERT's
effective line weight.

Implemented via render_style_for_block_sub(), called in tessellate_entity()
when expanding INSERT exploded entities.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 22:07:24 +03:00
Hakan SevenandClaude Sonnet 4.6 464bb8905f fix: INSERT Insertion snap point and Hatch boundary snap points
INSERT entities now expose their insertion point as a SnapHint::Insertion
pre-baked snap candidate, enabling OSNAP Insertion to locate block
reference origins. The cross marker is also positioned at the actual
insertion point instead of the origin.

Hatch entities now populate key_vertices from Polyline and Line boundary
edges (with correct elevation Z), enabling Endpoint and Midpoint snapping
to hatch boundary geometry.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 22:04:53 +03:00
Hakan Seven d7f2f860a2 Update image link in README.md 2026-04-26 21:59:19 +03:00
Hakan SevenandClaude Sonnet 4.6 cda8078905 docs: add acadrust integration gaps audit
Comprehensive backlog of acadrust entity fields, behaviors, and subsystems
not yet integrated into H7CAD rendering, snap, grip, and properties systems.
Covers tessellation gaps, OCS→WCS transform, ByBlock style resolution,
snap/grip point gaps, text rendering, and DXF reader unit bugs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 21:58:22 +03:00
Hakan SevenandClaude Sonnet 4.6 4504916f97 fix: resolve DXF %%x codes and render MText decoration strokes
Text/MText values now correctly handle DXF special-character sequences:
- %%d → °, %%p → ±, %%c → ⌀, %%nnn → Unicode scalar (resolved in tessellation)
- %%u / %%o toggle underline and overline strokes for TEXT entities

MText decoration codes (\L/\l, \O/\o, \K/\k) were previously consumed
as argument-bearing codes, silently eating the decorated text. Fixed
strip_mtext_codes to keep them as zero-width markers; tessellate_text_ex
now emits actual line strokes (underline, overline, strikethrough).
measure_mtext_chars and word_wrap skip markers so line widths are correct.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 21:57:51 +03:00
Hakan SevenandClaude Sonnet 4.6 089612584d chore: bump version to 0.1.7
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
v0.1.7
2026-04-26 17:08:32 +03:00
Hakan SevenandClaude Sonnet 4.6 db653c5a07 fix: derive dimension text rotation from axis geometry
acadrust's DXF reader does not parse group code 53 (text rotation),
so DimensionBase.text_rotation is always zero for DXF-loaded files.
Fall back to the natural axis-aligned rotation computed from geometry:
Linear dimensions use d.rotation (radians), Aligned dimensions use
atan2 of the second-first vector.  Normalize to (-π/2, π/2] so text
never appears upside-down.  Explicit non-zero overrides stored by the
DWG reader are preserved.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 17:08:12 +03:00
Hakan SevenandClaude Sonnet 4.6 19bb846c50 fix: dimension line geometry and DXF rotation unit mismatch
append_linear_dimension() applied the same perp offset to both d1 and d2,
but only d2 is guaranteed to be at the definition_point level; d1 must be
projected independently onto the dimension line, otherwise non-collinear
first/second points produce a parallelogram instead of a rectangle.

Fix: compute dim_line_pos = def.dot(perp) and project each endpoint
separately so the dimension line is always parallel to axis.

Additionally, the acadrust DXF reader stores LinearDimension.rotation from
group code 50 (degrees) without converting to radians, while DWG and our
own annotation commands store it in radians.  Add a post-load fixup in
load_file() that converts the rotation to radians for DXF files so
tessellation can call cos/sin uniformly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 16:57:23 +03:00
Hakan SevenandClaude Sonnet 4.6 2255dc5176 fix: hatch visibility in synced_hatch_models()
Standalone hatches and hatches from block inserts were rendered
regardless of entity invisible flag, layer off/frozen state, or
whether the owning INSERT belongs to the current layout block.

- Filter self.hatches by invisible, layer, and belongs_to_visible_block
- Skip INSERT entities that are invisible, on a hidden layer, or outside
  the current layout block before exploding their contents
- Skip individual hatches produced by explode that are invisible or on
  a hidden layer

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 16:43:37 +03:00
Hakan SevenandClaude Sonnet 4.6 52c830e477 perf: Arc-cache paper_canvas_wires() to eliminate O(N) clone on navigation
paper_canvas_wires() now returns Arc<Vec<WireModel>> backed by
paper_canvas_cache (epoch-keyed). Pan/zoom frames hit the cache and
return a pointer copy; only geometry changes rebuild the wire list.
Completes the paper-canvas caching chain (L+M): sheet cache →
projected-per-viewport cache → full canvas Arc cache.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 16:30:21 +03:00
Hakan SevenandClaude Sonnet 4.6 9a1211842d perf: cache paper-canvas tessellation and viewport projections (Option L)
Add two epoch-keyed caches to eliminate per-frame work in paper-space layouts:

paper_sheet_cache — caches wires_for_block(layout_block) as Arc<Vec<WireModel>>.
Previously paper_sheet_wires() re-tessellated all paper-space entities (title
block, annotations, viewport borders) on every mouse-move frame.  Now it is an
O(1) Arc clone.  entity_wires_arc() reuses the same cache so a geometry-change
frame triggers only one tessellation pass even when both the GPU renderer and
the Iced canvas are active.

paper_projected_cache — caches projected+clipped wires per viewport handle.
viewport_content_wires() now uses model_wires_for_viewport_arc() for the
tessellation step (already O(1) via viewport_wire_cache) and stores the
projection+clip result keyed by (vp_handle, epoch).  Navigation frames
(pan/zoom on the paper sheet) are O(N_viewports) Arc/slice copies instead of
O(N_entities × N_points × N_viewports) tessellate+project work.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 16:21:45 +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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
2026-04-26 11:55:08 +03:00
Hakan SevenandClaude Sonnet 4.6 81db3c37d0 perf: cache hatch/wipeout/image/mesh models per geometry epoch (F+G+H)
Add Arc<Vec<...>>-backed epoch caches for hatches, wipeouts, images, and
meshes — the same pattern used for wires. build_primitive() now returns
an O(1) Arc refcount bump on navigation frames instead of rebuilding
every collection from scratch.

Also wrap ImageModel.pixels in Arc<Vec<u8>> so cloning an ImageModel is
O(1) regardless of image resolution.

Navigation frames no longer copy pixel data, tessellated hatch boundaries,
or mesh geometry — all per-frame CPU work on unchanged geometry is now
eliminated.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 11:34:52 +03:00
Hakan Seven 2bf4a348bf refactor: remove PERFORMANCE.md as optimizations are implemented 2026-04-26 02:46:22 +03:00
Hakan SevenandClaude Sonnet 4.6 432e35ded3 perf: parallel wire tessellation with Rayon (Option C)
Extract tessellate_one body into a Send-compatible free function
tessellate_entity, then drive it with rayon::into_par_iter() in
wires_for_block(). Spreads file-open and post-edit tessellation across
all CPU cores. Navigation frames are unaffected (Options A/B cache
eliminates per-frame tessellation entirely).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 02:36:34 +03:00
Hakan SevenandClaude Sonnet 4.6 9c8c9a4060 perf: cache SortEntitiesTable scan (option E)
Replace the O(objects) linear scan inside wires_for_block() with a
sort_cache keyed by geometry_epoch. On a cache miss the index is built
once by scanning all document objects; subsequent calls within the same
epoch do an O(1) HashMap lookup (block_handle → entity→sort map).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 02:17:40 +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 <noreply@anthropic.com>
2026-04-26 02:13:31 +03:00
Hakan SevenandClaude Sonnet 4.6 320312e81a fix: replace degenerate same-vertex edges with Point in lwpolyline and spline
Empty LwPolyline and single-control-point Spline fallbacks were calling
builder::line(&v, &v) with the same Rc vertex, triggering the
truck-topology panic "Two same vertices cannot construct an edge."

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 20:30:26 +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 <noreply@anthropic.com>
2026-04-24 12:02:33 +03:00
Hakan SevenandClaude Sonnet 4.6 465dcc1600 ci: include version, OS and arch in release asset filenames
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 10:13:11 +03:00
Hakan SevenandClaude Sonnet 4.6 4506cff931 ci: remove one-time upload_tag workaround from release workflow
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 10:11:28 +03:00
Hakan SevenandClaude Sonnet 4.6 135fef54c5 ci: support upload_tag input on workflow_dispatch
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 02:12:22 +03:00
Hakan SevenandClaude Sonnet 4.6 0a88d063f9 ci: add AppImage build job to release workflow
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 01:57:40 +03:00
Hakan SevenandClaude Sonnet 4.6 87eabc9372 chore: bump acadrust 0.3.3 → 0.3.4, H7CAD 0.1.5 → 0.1.6
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
v0.1.6
2026-04-24 01:51:15 +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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
2026-04-24 01:43:13 +03:00
Hakan SevenandClaude Sonnet 4.6 a5057e26b1 fix: show specific entity type name in Properties panel for all entity types
Expanded entity_type_name() to cover all EntityType variants from acadrust
instead of falling back to the generic "Entity" label.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 01:40:33 +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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
2026-04-24 01:23:06 +03:00
Hakan SevenandClaude Sonnet 4.6 6a0125e4d6 feat: 3D UCS icon with foreshortening, depth ordering and axis labels
Replace the flat fixed-length axis arrows with a proper 3D tripod:
- Axis lengths are proportional (longest fills UCS_ICON_LEN, shorter
  axes stay foreshortened) giving real depth perception as the camera
  rotates
- Draw order computed from NDC Z so back axes render behind front ones
- X / Y / Z labels rendered beyond each arrowhead tip
- All axes drawn at full opacity with solid filled arrowheads

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-23 21:54:31 +03:00
Hakan SevenandClaude Sonnet 4.6 00da80f6e5 fix: align ViewCube hit-test with widget bounds instead of full window
prepare() was passing viewport.logical_size() (full window) to
viewcube.upload(), while hit_test/hover_id used the shader widget's
bounds. With the properties panel present the widget is narrower than
the full window, so the visual cube was scaled smaller but the click
area stayed full-size — causing the mismatch that grew toward the
bottom-left corner.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-23 21:39:43 +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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
2026-04-23 09:28:54 +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 <noreply@anthropic.com>
2026-04-23 09:18:01 +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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
2026-04-23 08:53:30 +03:00
Hakan Seven 7e0e892bcc chore: remove VIEWPORT_WIDGET_PLAN.md as refactoring steps are complete 2026-04-23 03:25:15 +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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
2026-04-23 03:02:01 +03:00
Hakan SevenandClaude Sonnet 4.6 592e3da6ac docs: mark Steps 5-6 complete in ViewportPane plan
Both items were already implemented prior to this commit:
- MSPACE mouse routing (double-click/pan/zoom/Escape) was fully wired
  through the existing viewport_mouse overlay and scene helpers.
- Per-viewport frozen layer filtering uses matching Handle values from
  the same CadDocument, confirmed correct in model_wires_for_viewport().

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-23 02:47:42 +03:00