Commit Graph
25 Commits
Author SHA1 Message Date
turbuczn 3c55e6d7e1 feat(measure): add radius and angle tools with dynamic measurement overlay
(cherry picked from commit aac44906a3)
2026-10-15 00:22:41 +03:00
Karim Jerbi 9cf9ff2c5c style: apply cargo fmt across workspace 2026-10-11 03:14:29 +01:00
Karim Jerbi 02c470aac7 feat(pdf): searchable text layer with per-style subset font embedding
Real BT/Tj text objects ride alongside the vector outlines (Tr 3,
zero visual change): runs preserved at layout time, TTF styles embedded
as allsorts subsets with ToUnicode, width-matched Tm, clip-culled.
WireModel literals moved to ..Default::default() tails.
2026-10-01 23:30:35 +01:00
ramox81andClaude Opus 5.5 7f8f8457c7 Frames: state-following ribbon list, DWF/DGN frames, image frame encoding and profile, frame-only picking
- The Frames list shows the drawing's frame state (hide / display and plot /
  display but not plot / frames vary), follows commands, undo and document
  switches, and lists the mixed state as a disabled item.
- DWFFRAME and DGNFRAME are frame variables of their own, kept in the header,
  and FRAME sets them with the others.
- Out-of-range frame values say "Requires an integer between 0 and 2." and
  ask again; a set value reports "Regenerating model.".
- IMAGEFRAME is stored in the image variables as bits (2 is written as 3) and
  a drawing without them uses the profile value, which setting IMAGEFRAME
  updates and the settings file keeps.
- Raster images, PDF underlays and wipeouts are picked on their frame only;
  underlay snap geometry is never picked.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 10:53:53 +03:00
Hakan SevenandClaude Opus 5.5 f537aff75c feat: node graph overlay; move to opencadcodec/opencadkernel/opencadgraph
The codec and kernel repositories and crates are renamed opencadcodec and
opencadkernel. Inside OCS they are depended on under short keys, so code
reads `codec::`, `kernel::` and the new `graph::` (opencadgraph, the node
graph engine). File-format and protocol spellings stay as they were: the
ACIS product id `acadrust`, the ACADRUST_* switches and the plugin
version keys `acadrust_version` / `acadrust_source`.

A button left of MCP on the command line opens a translucent node graph
over the viewport, with a palette of object, input, math, logic, list,
text and geometry nodes added by click or drag. An object node owns
drawing entities and its rows are the Properties panel's own rows for
them: the single-object page is extracted from refresh_properties as
entity_property_sections, values use the control protocol's
property_json, and writes go through set_property_value with a property
target override, so the panel, MCP and the graph share one read/write
path. A list reaching an object row makes one entity per element. Each
graph action is one undo step; the canvas pans and zooms.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 07:34:23 +03:00
Karim Jerbi 00e102607c fix(dist): respect units settings in DIST measurement output 2026-09-22 11:42:56 +01:00
PanosandClaude Opus 5 d753874fe4 feat(modify): commercial solutions keyword options for MOVE, COPY, FILLET, CHAMFER, OFFSET
MOVE: Displacement, Enter at the second point uses the base as the vector.
COPY: Displacement, mOde (Single/Multiple), Undo of the last copy.
FILLET: Undo of the last fillet (the command refreshes its entity cache
through the new CadCommand::on_document_undone hook after the host undoes
the document step). CHAMFER: Multiple and Undo. OFFSET: Erase (the offset
replaces its source), Layer (Current/Source), Undo, Exit.
POLY3D, TRACE, MIRROR and MEASUREGEOM expose the keywords they already
accepted as options, so the context menu and option buttons list them.

The pinned prompt now keeps informational bracket groups such as
"[3 objects]" and only drops real option listings when the buttons take over.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 12:05:14 +03:00
PanosandClaude Opus 5 391a74a615 perf: let one tessellation serve every background
Switching to a Layout re-tessellated the whole model — `memo_miss=186455`,
2034 ms — to arrive at geometry identical to what was already in the memo.
The only thing that differed was colour.

`local_wires_for` already knows this: it stores the raw colour and drops
its background argument with `let _ = bg_color;`, noting that
`Batches::finalize` applies `adapt_to_bg` last, "so the cache no longer
has to rebuild on BACKGROUND / layout-switch". The tessellation memo's
guard had not been told, and still mixed the background in, so every
entry was discarded on a switch.

`finalize` now records what its colour resolution consumed — the raw
colour, the raw glyph colours, and the three flags it branches on — as
`WireModel::bg_adapt`, and `resolve_colors_for_bg` replays it for another
background. The guard drops the background, so a memo entry serves any
layout.

The resolution recomputes from the recorded raw colour rather than from
the resolved one, which makes it idempotent and therefore safe over a set
that mixes memo hits with freshly tessellated misses. Re-adapting the
resolved colour would not be safe: `adapt_to_bg` snaps a near-white colour
to pure black on a light background, and pure black comes back pure
*white* on a dark one, losing the tint. A test pins that asymmetry so the
raw colour is not optimised away later.

The contract is tested against the real `finalize` rather than a copy of
its logic: for every background pair, building under one and resolving for
the other lands exactly where building under the other would have.

`bg_adapt` is `None` for wires built anywhere else, and the resolution
skips them, so nothing outside the block cache changes colour. It is also
`None` when the resolution would be a no-op — a preserved colour with no
canvas fill looks the same under every background — which keeps the common
case at no cost.

Ordering is unchanged: the pass runs inside the builder, before
`append_scene_markers` and `apply_refedit_fade`, both of which have their
own separate background dependency.

`wires-build` gains `colors=` so the pass has a number of its own.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 18:53:16 +03:00
Hakan Seven 4209c35ba3 fix(polyline): render fill mode boundaries
Use kernel-produced band outlines when FILLMODE is off. Preserve centerline linetype stations through inserts, clipping, projection, and PDF export.
2026-08-24 22:18:44 +03:00
Hakan Seven 7293bec1b9 fix(point): preserve relative marker sizing
Keep rendering, picking, block transforms, and plotted output on the
same viewport-relative PDSIZE geometry without overloading wire width
fields.
2026-08-22 22:22:01 +03:00
Hakan Seven 44efb8fd53 feat(frame): complete frame controls
Preserve frame geometry for selection while separating viewport and plot visibility across supported reference entities.

Refs #642
2026-08-20 23:37:02 +03:00
Hakan Seven 4fd9b98da4 perf(render): instance block geometry
Share source buffers across block placements and render nested content with GPU instance transforms.\n\nChunk silhouettes and compact mesh material data so large drawings stay under device buffer limits and remain responsive.
2026-08-12 02:33:10 +03:00
Hakan Seven 657d023fb7 feat(ucs): integrate coordinate systems
Route coordinate input, geometry creation, transforms, properties,\nviews, and viewport persistence through a shared working plane.\n\nRefs #484
2026-08-04 23:17:54 +03:00
Hakan Seven b1d55c6a7e feat(view): add visual style previews
- preview render modes on hover without committing
- distinguish 2D and 3D wireframe fill and depth behavior

Refs #483
2026-08-04 21:23:25 +03:00
Hakan Seven 3e6693b93f feat(locale): localize full interface 2026-08-02 22:09:25 +03:00
Hakan SevenandClaude Fable 5 e177f92ff5 fix(xref): blocks respect their internal draw order
Xref merge now inserts entities in the source block's chain order and
carries SortEntitiesTable objects across (remapped to merged handles).
draw_depth_map values become [depth, half] so exploded block/xref fills
and wide-polyline band wires both compose child depth as
insert_depth + in-block rank x half, recursing into nested inserts with
the same divisor - bands and fills from one block interleave by the
block's internal order instead of stacking at the insert's single depth.
Band batches split per rank via WireModel.depth_override; thin wires
stay merged. The wire arena falls back to a full rebuild when a slab
carries mixed depths so refreshes don't flatten band overrides.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 15:53:28 +03:00
Hakan SevenandClaude Opus 4.8 b3016ca892 feat(polyline): smooth per-segment taper for varying-width wide polylines
A wide polyline with per-vertex varying widths rendered at its widest edge
(uniform-width ones were already exact). Draw the true taper instead.

`WireModel.taper_widths` carries a per-point full band width. The wire
instance (native slim + web fat) gains per-endpoint `world_hw_a/b`, and both
wire shaders interpolate `mix(hw_a, hw_b, which_end)` so each segment's band
narrows/widens linearly — depth-tested, and the linetype dash still applies.

Alignment (widths must match the tessellated points) is solved by building
both together: a new `TruckObject::TaperedLines(points, widths)`, filled by
`common::tapered_band_points` (straight = endpoints, arc = 16 samples with the
width lerped along it). to_truck routes a varying-width polyline here —
LwPolyline before the PLINEGEN split, Polyline2D at its return — while a
uniform-width one keeps the constant-band Contour path unchanged. `world_width`
stays the widest edge as the PDF-stroke + zoom-floor fallback.

Blocks fall back to the constant-widest band (`LocalWire` carries no
taper_widths).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 16:36:06 +03:00
Hakan SevenandClaude Opus 4.8 bd1a5affd7 feat(lwpolyline): render the wide-polyline band via the wire shader
A wide polyline's flat band was drawn by the hatch pipeline: a solid fill
with only draw-order depth. It ignored the entity linetype (the width
"crushed" the dash pattern) and showed through 3-D geometry in a tilted view.

Draw the band from the polyline's own centre-line wire instead. A new
`WireModel.world_width` (drawing units) makes the wire vertex shader expand
the quad by `world_half_width / world_per_pixel` (clamped to a hairline), so
the band is sized in world units and tracks zoom. Because it is now a wire:

- the GPU dash shader dashes the band by arc-length — the linetype applies;
- it is depth-tested at the polyline elevation — no draw-order see-through;
- no CPU band triangles or per-dash geometry are generated.

`world_half_width` rides in `WireConst` (native, replacing a pad) and the web
`WireInstance` (loc 14); both wire shaders branch on it. The tessellator sets
`world_width` from the LwPolyline width on the Contour / SegmentedLines arms,
and the model-space fill loop no longer emits a hatch band for any LwPolyline.
Pick still uses the band polygon (`pick_tris`).

Blocks keep the hatch band: `LocalWire` carries no `world_width` (finalize
zeroes it), so a scaled insert still scales its band correctly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 14:50:27 +03:00
Hakan SevenandClaude Opus 4.8 626e8091f7 refactor(viewport): unify content-viewport clipping on the stencil path
Rectangular paper viewports now go through the same GPU stencil clip as
non-rectangular ones: viewport_clip_boundary_ndc returns the four rect
corners (a full-target mask) instead of nothing, so the render path has
no rect/non-rect special case — every content viewport carries a clip
boundary and draws with stencil reference 0xFF.

With a single clip path, the old per-wire / -image / -wipeout scissor
mechanism is fully dead and removed: compute_*_scissors, the
*_pixel_scissors buffers, the project_scissor helper, the per-pass
set_scissor_rect blocks, the vp_scissor field threaded through
WireModel / ImageModel / HatchModel and their GPU mirrors (WireGpu /
ImageGpu / WipeoutGpu / HatchWebGpu), from_run's scissor parameter, the
scissor grouping in build_wire_buffers, and the is_arena_eligible gate.
Net -245 lines across 54 files.

Rectangular viewports still clip exactly via their own render rectangle;
the stencil mask is a redundant full-target no-op there, kept only so
the clip code path stays uniform. XCLIP keeps its CPU wire clip.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 11:06:28 +03:00
Hakan SevenandClaude Opus 4.8 50f6196594 fix(render): flag 2D overlay fills so they show in every view mode
A SOLID's filled quad and a dimension/MultiLeader text background are flat 2-D
overlays that should draw in wireframe and shaded alike, but face3d_gpu
classified fills by `!fill_tris_low.is_empty()` — and a 2-D fill at UTM scale
carries a double-single low residual for precision just like a real 3-D mesh
face. So the arrowhead fill and the "5;8" cyan text background were routed to
the 3-D buffer and only appeared in shaded mode, vanishing back to nothing on
return to wireframe.

WireModel now carries an explicit `fill_is_3d`: true only for a real surface
(PolyfaceMesh / PolygonMesh face, set in the tessellator), false for every flat
overlay fill. face3d_gpu classifies on the flag, so 2-D fills stay in the
always-visible buffer while keeping their low residual (and UTM precision).

Verified on budweiser: the `_BoxFilled` arrow SOLID and the dim '5;8' MTEXT
background both tessellate with fill_is_3d = false.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 15:15:24 +03:00
Hakan SevenandClaude Opus 4.8 3f060a570a fix(pick): hit-test lineweight width and thickness walls
Selection tested a flat 8 px radius around each wire's centreline and
nothing else, so two things the user can see were not pickable.

A wire renders as a band `line_weight_px` wide, so pick now widens to that
rendered half-width via `pick_tolerance_px`, mirroring the wire shader's
`select(0.5, half_width, lwdisplay_enable)`. Behaviour is unchanged for
every standard weight (the widest, 2.11 mm, renders 7.97 px half-width —
just under the 8 px threshold); this keeps the two sides from drifting
apart if the display boost in `lineweight_to_px` ever changes.

Entities extruded by a DXF thickness draw their wall as four edges with
nothing in between, so the interior was a hole the cursor fell through.
WireModel gains `pick_tris`: triangles hit-testing treats as solid and the
renderer never uploads. Kept apart from `fill_tris` because that channel
reaches the GPU, and a wall that rendered shaded would change how every
thickness drawing looks. Ranked below `fill_tris` — those are drawn, these
are not, so where they overlap the drawn face is what the user means.

Walls are dropped when a wire is projected into a floating viewport:
`points` are reprojected and clipped into paper coords there, so cloned
model-space triangles would hit-test at a model-scale offset. Extruded
entities stay selectable by their edges, which do get projected.

The wall loop runs on every hover that misses everything else, so it
rejects on the wire's AABB first — an extruded circle is ~128 triangles
and a drawing full of thickness would otherwise project every wall on
each mouse move.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 14:58:55 +03:00
Hakan SevenandClaude Opus 4.8 5bd683cf4e feat(mline): GPU-dash multiline elements with shared A-type alignment
MLINE dashed elements were fully CPU-expanded (apply_along) — one wire per
dash — to keep the parallel lines in phase. Move pure-dash elements onto the
GPU dash shader and end-align every element to the multiline's centre-line so
they share one interior phase.

- wire_distances now accumulates arc-length from the double-single (high+low)
  points, so the dash phase stays precise at UTM coordinates. f32-high-only
  deltas quantised ~0.1 there and drifted the parallel lines apart — the
  original reason MLINE dashes were CPU-side.
- New WireModel.dash_align_end carries a shared begin/end dash length. The
  shader uses it as align_end for every parallel element (the interior phase
  depends on align_end, not the wire's own length) while align_total stays the
  element's own length, so each still ends on a dash at its own endpoint.
- MLINE tessellation derives that length once from the centre-line, routes
  pure dash/space/dot elements to the GPU pattern and text-bearing elements to
  apply_along.
- apply_along gained an "A"-type mode (shared reference length): a solid
  begin/end dash and a phased interior, so its dashes — and embedded text —
  line up with the GPU-dashed sibling elements instead of tiling from start.

Pure-dash MLINEs now cost one WireModel + pattern per element instead of N
segments; single (non-MLINE) entities keep the from-start tiling unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 02:59:11 +03:00
Hakan SevenandClaude Opus 4.8 48e6d0d553 feat(dgn-ls): dash walls from start + fix wall connector and cap identity
DET pipe walls now carry the stroke pattern (dash_from_start), which
tiles from each wall's own start vertex and skips the A-type end
alignment that was force-lighting a solid dash under the end symbol.

Two rendering fixes on top:
- Dedup the host polyline's vertices before offsetting the walls. Some
  DGN pipe polylines carry trailing duplicate end vertices; a zero-length
  segment gave offset_xy a degenerate normal, leaving that vertex
  un-offset and folding the wall down to the centre line. The two walls'
  fold segments met at the centre and drew a spurious line linking them.
- Re-key the symbol (end circle / half-ellipse) wires to the host handle
  so the whole pipe picks and selects as one entity instead of the
  symbols acting as separate phantom entities.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 01:49:18 +03:00
Hakan SevenandClaude Opus 4.8 110683f419 refactor(text): per-entity SDF text — all text kinds, selection/hover highlight
Rearchitect SDF text from a document-wide collector to per-entity
production during tessellation, so each entity owns its glyph quads and
composite/nested text is covered for free (no re-explosion, no per-frame
document walk).

- WireModel gains `text_verts`: glyph quads ride on the wire, so they are
  cached by the tess memo, cloned on hit, and transformed by the block
  expand loop exactly like `points`.
- Text arm (tessellate.rs) builds the quads when SDF is on and suppresses
  the strokes; render gathers text_verts from the viewport wire set
  (`gather_text_verts`, cached on wire content id) — the old collector is
  removed. Covers model, dimension, table, block-internal and paper-sheet
  text uniformly.
- MLEADER text (lays out via layout_mtext, not the Text arm) gets its own
  SDF branch driven by the per-run GlyphRun.
- Pick box comes from the rendered glyph quads (true text extent), not
  entity_aabb which mis-places the box for MTEXT; composite paths no longer
  clobber the empty text wire's tight AABB (dim pick box hugs the text).
- Selection / hover highlight via a text-highlight overlay (upload_text_
  highlight, keyed on selection_generation) mirroring the selected-wire
  xray overlay; base glyphs stay neutral so a deselect leaves no stale tint.
- Debug: env OCS_TEXT_BOX draws each run's glyph-bounds rectangle.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 17:01:36 +03:00
Hakan SevenandClaude Opus 4.8 0ca3954b5d feat(commands): integrate standard CAD commands and fix behavior gaps
Broad pass over the command set: integrate missing commands, wire
previously-unreachable ribbon/menu buttons, and correct name/behaviour
mismatches. Every UI command token now resolves (real handler where the
infrastructure exists, an honest "not yet implemented" status otherwise).

New commands:
- Draw: 3DPOLY, 2D solid (SO/SOLID2D), HELIX, TRACE, SKETCH, DIMCENTER
  (DCE/CENTERMARK)
- Modify/inquiry: REVERSE, MEASUREGEOM (MEA), DBLIST
- Data/manage: BASE, OVERKILL (exact-duplicate removal), AUDIT (read-only
  integrity report), SETVAR (system-variable get/set), SCRIPT (.scr runner),
  FINDNONPURGEABLE

Behaviour fixes:
- LINE and SPLINE gain Close/Undo; AREA computes area/perimeter in f64
  (correct at large coordinates)
- ATTEDIT/ATE unified so the alias and the ribbon button open the
  interactive editor; -ATTEDIT is the command-line form
- Linear/aligned/radius/diameter dimensions gain a text override (Text) and
  text angle (Angle) option
- INSERT gains Scale/Rotate options; COPY gains an Array option
- LINETYPE SET sets the current entity linetype; SETBYLAYER resets overrides
- UNDO accepts a step count

Integrations: standard dimension/block aliases (DLI/DRA/DAN, BMAKE,
EXPORTPDF, DDIM), drafting-aid and screen toggles (GRID/SNAP/POLAR/OSNAP/
DSETTINGS/CLEANSCREEN/QUICKPROPERTIES), viewport tiling
(CASCADE/HORIZONTAL/VERTICAL/VPJOIN), and customization entry points
(CUI/ALIASEDIT).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 19:27:35 +03:00