Commit Graph
55 Commits
Author SHA1 Message Date
Hakan SevenandClaude Opus 5.5 440adec834 fix(pdf): leave out what the export window clips away
Everything was written and only clipped at view time, so a window over a
corner of a large drawing produced a near-empty page of several MB.
Wires and hatches whose sheet bounds miss the window are now skipped.

Fixes #1568

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 20:44:08 +03:00
Hakan Seven c492539fde Merge PR #1580 from Chrisyeh1983/fix/pdf-embed-truetype-text: export TrueType text as embedded subset fonts
Conflicts with 02c470aa (invisible searchable text layer): main already
depends on allsorts 0.16 with the same features, so its line stays. Both
layers would put the text into the PDF twice, so a run drawn wholly in
faces the PDF can embed now goes out as embedded-font text and skips the
invisible layer; any other run (stroke fonts, mixed scripts) keeps its
outlines and the invisible layer. Faces only probed for such a run are
not installed. The PR's test follows layout_glyph_quads' new tuple.
2026-10-02 12:16:38 +03: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
Chris YehandClaude Opus 5.5 a8dc6534f9 fix(pdf): export TrueType text as embedded subset fonts, not triangles
PDF export filled every TrueType glyph's tessellated outline, one triangle
per triple. A stroke-heavy CJK face such as DFKai-SB tessellates into
hundreds of triangles per character, so a 28-layout schematic set
exported at 320.9 MB and viewers crawled through it. The atlas export
table was also rebuilt for every text wire, which made the same export
take 566 s.

- ttf_glyph: GlyphSource records the face, glyph id and font-unit scale a
  filled outline came from; glyph_source / fallback_source resolve the
  same face as glyph / fallback_glyph (fallback picks both in one search).
- Face::glyph_source mirrors Face::glyph; stroke glyphs (LFF, SHX, big
  font) return None and keep their polyline export.
- GlyphExport carries the char and the source (filled glyphs only, so
  TEXTFILL 0 still exports hollow outlines).
- pdf_export draws a sourced glyph as text (Tf 1 + a Tm that maps its em
  onto the SDF quad), subsets each face with allsorts after all pages
  (keeping cvt/fpgm/prep for tricky fonts), renumbers glyph ids, and
  snapshots the glyph table once per export.

Same set: 566 s / 320.9 MB -> 27 s / 1.42 MB; text is searchable and
lands on the same pixels as before.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-10-01 23:07:15 +08:00
Hakan SevenandClaude Opus 5.5 60f35e2b1d fix(color): only colour 7 swaps with the background; true colours draw as authored
Every near-white colour turned black on a light background (and near-black
white on a dark one), including true colours. A 255,255,255 solid used as a
white mask became a black block. Now only colour 7 swaps: true colours and
ACI 255 resolve a hair off pure white/black and keep their value on screen
and in plots.

Discussion #1500

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-29 06:16:18 +03:00
Hakan SevenandClaude Opus 5.5 38fe2b0b08 fix(plot): text and leader background masks plot paper-white, not black
The mask is filled with the canvas colour; plotting carried that dark
colour (or the light-to-black rule) onto the sheet.

Fixes #1072

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-28 19:01:56 +03:00
Hakan SevenandClaude Opus 5.5 ac8a9c38db fix(plot): true-color hatch fills plot in their own colour, not black
Fixes #1417

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-28 18:29:00 +03:00
ramox81andClaude Opus 5.5 5ca7becd97 Raster images: transparency off draws pixels opaque; TRANSPARENCY command
An image's transparency flag decides whether transparent pixels show what
is behind the image or draw in their stored colour: the image pass and PDF
plots now follow it (it was ignored, so every PNG alpha showed through).
TRANSPARENCY selects images and asks for the mode, defaulting to the first
image's current one.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-28 11:23:19 +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 <[email protected]>
2026-09-25 07:34:23 +03:00
huanldandClaude Opus 5.5 279a87418e feat(automation): entity, block, publishing and REST operations from the fork
The operations docs/automation/API-SPEC.md (PR #1383) describes, brought
over from huaninstratech/OpenCADStudio so the specification no longer runs
ahead of the code:

- entity CRUD: entities_create, entities_delete, entities_transform,
  entities_copy_to, and `where` filters on query
- blocks and export: block_define, block_delete, a normalised wblock
- publishing: layout_create, page_setup_set, plot (per_page included)
- metadata: sysvar, xdata_set / xdata_get, file_identity
- collections: group_create, selection_set_save / load
- control plane: batch, events, view_focus, close
- the REST transport: `--http <port>`, the /api/v1 routes and their
  OpenAPI 3 description
- person-in-the-loop picks: user_select and getpoint, hosted by the GUI
  over the same REST routes, with on-screen pending signals

Taken from the fork's commits b7a7ed80, 69396b42, 64f1cf6a, c3aafc8e,
bb5a7bfa, 52317a5c, 50d293ba, b90e194f, 517c6397, 53a5c4c9, a49ac045,
37de5a46, a78b1822, 6d9aed2a, 33f522da, 5b5cafe2, 02a7e78d, 94df40dc,
fd5bc14d, 485fac61 and 2c5fc994.

Left out on purpose: 37de5a46 also removed the donation surface (the
Patreon fetch, the donation prompt, the Start-page supporters rail, the
DONATE command and its locale strings) and replaced the About dialog's call
to action; that is the fork's own product decision and stays out. The
fork's version bump and its unrelated IFC/BIM work are not part of this.

Adapted while porting: the REST server and the GUI bridge are desktop-only
and a template start is refused in the web build, so the wasm target still
compiles; the two plot tests read the PDF through the shared stream helper,
because exported streams are Flate-compressed; the benches set the new
crosshair field; comments and docs describe each operation in its own
terms rather than by another product's API names.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-24 13:41:07 +03:00
Chris YehandClaude Opus 5 4826cd7989 fix(pdf): Flate-compress the exported PDF streams
printpdf 0.9's `PdfSaveOptions::optimize` is a no-op — its `doc.compress()`
call is commented out — so every page's content stream (megabytes of vector
operators for a CAD sheet) was written uncompressed: a 16-sheet drawing set
exported as 280 MB. Serialise through `printpdf::to_lopdf_doc` and run
lopdf's `compress()` before writing; the same export is now 49 MB and
readers see ordinary FlateDecode streams.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-09-21 22:02:13 +08:00
huanld 36d5c4db79 fix(render): compose PLOTSTYLE wire-fill depth like the wire pipeline
When a display plot style maps an ACI to a fill pattern, wire fill
triangles are re-emitted as PLOTSTYLE hatches. Those took
depth_override raw — a per-block child label for block wires and 0.0
for top-level ones — while everything else in the hatch/wipeout pass
sorts at the composed depths[insert] + label * half. The fill could
land outside its host's band and get buried under its own block's
masks (same shape as the block-text bug).

Both the display and PDF paths now travel through wire_draw_depth /
the PlotWire's composed depth, and the display cache key rides the
depth generation so a DRAWORDER edit re-ranks the fills without
needing a wire rebuild.
2026-09-18 08:38:35 +07:00
huanld 28efbffd57 feat: embed images (OLE2FRAME), print pipeline fixes, plot prefs, community fonts
- IMAGEEMBED: pack raster images into OLE2FRAME entities so a .dwg is
  self-contained; 'Embed Image' entry in the XREF attach menu; control/MCP
  op embed_image for external tools; dxf_name for OLE2FRAME queries.
- Scene: populate_images_from_document now rebuilds OLE2FRAME and PDF
  underlay models too (they used to vanish after any XREF op).
- Printing: add PRINTER_ENUM_NETWORK pass when listing printers; GDI
  spooler fallback (hayro raster at 300 DPI + StretchDIBits) for hosts
  whose PDF handler registers no print verb; DEVMODE orientation with a
  90-degree raster rotation fallback for v4 drivers; fix the fit-scale
  precedence bug that printed blank pages.
- PDF export dialogs on Windows use the async file dialog so the native
  overwrite confirmation no longer deadlocks the UI thread.
- Plot dialog: persist the chosen printer (saved on selection) and the
  picked plot window across sessions; page setups with no device no
  longer reset the output device.
- Community fonts: detect unresolvable .shx references on open and offer
  a download from the repository's fonts/ folder (contribution guide in
  fonts/README.md); custom font source URL setting for intranet font
  servers; downloaded fonts resolve from the per-user fonts directory.
2026-09-16 16:42:21 +07:00
Christopher Overbeck c93cc17592 Include bitmap images in PDF plots, preview, and printing
Include decoded IMAGE/OLE content in the shared PDF plotting path,
with clipping, transforms, transparency, draw order, and resource reuse.
Use named page content for export and printing.

Fixes #1266
2026-09-14 17:07:57 -05:00
ramox81 6272722baa Carry hatch origin through creation and pattern editing 2026-09-11 13:40:40 +03:00
Hakan Seven 5b663bfe38 Fix issues found while integrating contributor PRs 2026-09-08 12:14:28 +03:00
JonathanandCopilot 92ac3f60e5 Fix crash when overwriting an existing PDF file
Use atomic file operations (write to temp file, then rename) instead of
direct File::create() to avoid crashes when the target PDF is locked by
another process (e.g., open in PDF reader). The new write_pdf_atomically()
function ensures the file is either fully written or left unchanged, with
a clear error message if the file is in use.

Fixes #1062

Co-authored-by: Copilot <[email protected]>
2026-09-08 09:04:52 +02:00
Hakan Seven d66e89ca19 Complete application localization across all supported languages 2026-09-07 01:05:02 +03:00
Hakan Seven 8c61d89b1e fix(layout): complete paperspace integration
Apply each layout's embedded page setup and plot style consistently across display, PDF, printing, and multi-layout output.\n\nRefs #844
2026-08-26 08:58:44 +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
Karim Jerbi 7215faae44 feat(properties): make inherited properties editable, align dropdowns, and switch transparency to EditChoice
Enable in-place editing of inherited properties in the Properties panel:
- Transparency: Switch to PropValue::EditChoice with ByLayer and ByBlock presets to allow direct typing of any numeric percentage value (0..90%) without combo_box autocomplete interference.
- Select-all-on-click: Integrate EditChoice text inputs into build_field_key_map with dedicated widget IDs, preserving click-to-select-all across editable fields.
- Material: Provide bounded preset choices (ByLayer, ByBlock, Global, and active custom material) to avoid scanning large drawing dictionaries.
- Plot style: Implement mode-aware behavior with interactive Choice in STB mode and ReadOnlyWithTooltip in CTB mode.
- Read-only tooltips: Introduce PropValue::ReadOnlyWithTooltip to provide contextual explanations for locked fields while preserving mouse text selection and clipboard copying.
- Dropdown alignment & unified borders: Fix EditChoice internal input/caret background clipping so the container's 1px neutral/active border surrounds the entire control identically to Lineweight and text inputs.
- Document defaults & aggregation: Synchronize document header defaults when editing with No selection, and aggregate heterogeneous multi-selections (*VARIES*).
2026-08-23 23:20:34 +01:00
Hakan Seven 339d9afcfe Merge pull request #857 from ramox81/codex/align-point-behavior
Align point command, properties, and display behavior
2026-08-22 22:58:42 +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 e5b84ab8de fix(plot): honor wipeout draw order 2026-08-22 15:33:43 +03:00
Hakan Seven f6b9d6514e feat(plot): add Print All workflow 2026-08-04 11:29:11 +03:00
Hakan Seven 6c62daf260 fix(plot): match PDF output to viewport
Refs #618
2026-08-01 20:05:38 +03:00
Hakan Seven 380cc4c9a8 fix(plot): align layout output with viewport
Keep nested model hatches, PAT origins and dots, and Layout lineweight scaling consistent across screen and vector output.\n\nRefs #618
2026-08-01 19:29:57 +03:00
Hakan Seven f0c1be0db1 fix(plot): correct layout hatch output
Keep Layout output on the selected physical sheet and preserve hatch lineweights and plot styles through paper, viewport, and nested block paths.\n\nRefs #607
2026-07-31 23:08:41 +03:00
Hakan Seven 325cd2474c feat(plot): complete plot configuration
- unify Model and Paper Space plot area behavior
- add CTB discovery, read/write, and bundled defaults
- apply scale, render mode, and output options consistently
2026-07-31 13:41:34 +03:00
Hakan Seven bd1b5b4293 feat(plot): complete plot workflow 2026-07-31 12:06:12 +03:00
Hakan Seven eb0861cf6e fix(plot): align paper space output
Use the same area pipeline as model space while keeping Layout bound to the physical sheet and printable margins. Share scale, rotation, clipping, and viewport fills across preview, PDF, and printer output.
2026-07-31 11:16:14 +03:00
Hakan Seven 99dbc8254d fix(plot): parent PDF save dialog
Remove the temporary EXPORTPDF <path> fallback now that native save requests are tied to the main window.\n\nCloses #537
2026-07-28 19:23:29 +03:00
Hakan Seven bca65600cd fix: stabilize web loading and paper rendering 2026-07-25 21:34:29 +03:00
Hakan Seven ece5598372 fix: correct viewport stretch and SHX plots
Anchor the opposite viewport corner during grip and STRETCH resizing.
Match plotted stroke-font pen widths to the SDF atlas unless CTB overrides.

Closes #435
2026-07-24 19:26:10 +03:00
Hakan SevenandClaude Fable 5 e78ad7884d feat(dialogs): reopen file pickers in the last-used directory
Every rfd dialog result funnels through sys::handle_path, which now
records the picked file's folder (persisted to the config dir), and all
pickers are built via sys::file_dialog(), seeded with that folder.
Save-As keeps the current tab's own directory as the first choice and
falls back to the remembered one.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-21 09:27:44 +03:00
Hakan SevenandClaude Opus 4.8 298bfd4803 feat(pdf): stroke the wide-polyline band at its world width
The shader-band change stopped emitting a hatch fill for wide polylines, so
PDF export — which drew the band from that HatchModel — lost it and plotted
only the thin centre-line.

Stroke the centre-line wire at the band width instead. In `build_pdf`, a wire
carrying `world_width` sets the pen to `world_width * MM_TO_PT` with no
`/ scale`: a lineweight is absolute on paper (hence the divide), but a band is
drawing-unit geometry and must scale with the plot. The CTM's scale then
renders the band at `world_width * scale` on the sheet, matching the geometry,
and the linetype dash pattern strokes it dashed. Overrides any CTB pen weight
(the width is geometry, not a lineweight).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-20 14:59:41 +03:00
Hakan Seven 00639db678 test(export): drop the flaky SDF-text position assertion
sdf_text_emits_vector_ops_at_the_run_position laid glyphs into the process-
global text atlas, released the lock, then re-read the atlas in emit_text.
Another test growing or resetting the atlas in between shifted the uv keys,
so the lookup missed and the assert saw zero polylines — ~1 in 5 parallel
runs. Its own comment already noted it couldn't catch the one thing worth
catching (a mirrored corner mapping); the size-based tests and text_gpu's
vertex-order tests cover what remains. Not worth a global test mutex.
2026-07-16 11:19:39 +03:00
Hakan SevenandClaude Opus 4.8 f452ae9211 fix(export): keep plot coordinates in f64 until they cancel
A UTM drawing plotted deformed — both geometry and text — while the same
drawing near the origin came out clean. Every pass of the exporter narrowed
an absolute world coordinate to f32 before the offset had a chance to cancel
it, and f32 resolution at UTM is ~0.03 m on easting and ~0.5 m on northing,
so the whole plot snapped onto that grid.

Four places did it, all now folding in f64 and narrowing only once the
result is a single-digit sheet-mm value:

- The wire pass never read `points_low` at all — the word did not appear in
  the file — so it plotted the double-single "high" half alone and dropped
  every residual.
- emit_hatch cast the f64 `world_origin` to f32, re-introducing at the last
  step exactly the quantisation the boundary-relative encoding exists to
  avoid.
- pattern_segments rasterised entirely in absolute f32. The pattern is
  anchored at world (0,0), so a hatch at UTM runs a line index k of ~1e7:
  `origin + k · step` and the `edge − line` intersections both fall apart
  there. k also becomes i64 — `as i32` saturates silently, which would
  collapse the range to a bogus run from i32::MIN rather than drop the fill.
- emit_text folded `pos + pos_low` in f32, which rounds straight back to
  `pos`: the residual the pair exists to carry was discarded at precisely
  the coordinates it was carried for.

The offsets themselves were also narrowed by the callers, so the value meant
to cancel a UTM coordinate was quantised before it could. Widen ox/oy to f64
through export_pdf, build_pdf, print_wires and the two plot-param builders;
printing shared the same path and the same bug.

Pre-existing, not from #390 — that change only routed text through the same
faulty pipe the wires were already in.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-16 10:25:28 +03:00
Hakan SevenandClaude Opus 4.8 5018963d3d fix(export): project viewport text into paper space
Review follow-ups to #390, which fixed PDF/print text for model space but
left the layout-plot path — the one issue #385 was filed from — broken.

viewport_content_wires rewrote points/snap_pts/key_vertices/aabb and then
cloned text_verts through untouched, so paper-space viewport text stayed at
model (UTM) coordinates: a dimension at model (25000, 12000) plotted its
lines onto the sheet and its glyphs kilometres off the page. A text-only
wire fared worse — TEXT/MTEXT carry no stroke points since text went
SDF-only, so it was dropped outright by the empty-clip and AABB rejections.
Project the glyphs through the same proj_abs the snap points use (via the
existing map_text_verts helper), cull per glyph on the quad centroid, count
them towards the paper AABB, and dim them like the wire colour.

Also in emit_text, which diverged from the wire pass it mirrors:
- divide the pen by `scale`, or a Fit/windowed plot prints text as
  near-invisible hairlines while its own lines stay correct;
- apply the CTB plot style, or a monochrome.ctb plot renders the lines
  black and leaves the text on its screen colour;
- reset the dash pattern, which is persistent PDF state the wire pass
  leaves set — a HIDDEN-linetype last wire printed glyph outlines dashed.

export_table's doc claimed it honoured TEXTFILL and widened the bold pen
itself; it did neither. Gate fill_tris on textfill() so hollow-on-screen
text exports hollow, carry the bold flag and widen the pen by the 1.7x the
bake uses, and resolve Face once per family instead of once per glyph (the
atlas mutex is held for that whole walk).

uv_key identifies a glyph by its tile's uv_min, but grow_height rescales
every entry's V and reset rewinds the packer, leaving already-built quads
addressing the wrong tile — garbage on screen and a silent miss in the
export table, i.e. #385 again under #347's grow-the-atlas conditions. Bump
an atlas generation on both and fold it into the tessellation memo guard so
the text re-lays-out instead. Quads built earlier in the same pass that
grows are still stale for that pass; the guard heals them on the next one.

Tests: the integration test's "some wire has text_verts" assert passed on
pre-fix code too (the exporter ignored them; the scene always carried
them), and its dimension half was satisfied by the TEXT alone — assert per
entity handle instead, and stop colliding on fixed names in the shared temp
root. The unit test's bbox came from the same quads the mapping reads, so
it held by construction; pin the run's own world box instead. That still
cannot catch a mirrored corner assignment — verified by mirroring it — so
say so rather than claim coverage the assert doesn't have.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-16 10:06:15 +03:00
Kevin GriffinandClaude Opus 4.8 6b15884444 fix(export): render SDF text as vectors in PDF/print export (#385)
Since text became SDF-only (the legacy stroke-text path was removed), all
text — standalone TEXT/MTEXT and dimension text — rendered on-screen only as
GPU glyph quads carried on `WireModel::text_verts`. The CPU PDF/print exporter
draws only wire stroke `points` and hatch fills, so it silently dropped every
glyph: dimensions and text were missing from exported PDFs and prints
(present in v0.7.6).

Reconstruct vector text in the exporter instead of resurrecting the stroke
path, so the SDF render path and its caching are untouched:

- sdf_atlas: `GlyphAtlas::export_table()` snapshots each baked glyph's vector
  geometry (outline strokes + fill triangles, glyph space) keyed by its tile
  `uv_min`, re-resolved through `Face` so it honours the current TEXTFILL.
- pdf_export: a new `emit_text` pass walks each wire's `text_verts` (one
  6-vertex quad per glyph), recovers the glyph geometry by `uv_min`, and
  affine-maps it into the quad — `DrawLine` polylines for stroke (LFF/SHX)
  fonts, filled triangles for TrueType, plus solid decoration bars. Runs under
  the same rotation/scale/clip CTM as the wires, so it covers model space,
  paper space and windowed plots, and every SDF-text kind (text, dimension,
  multileader, table, tolerance).

Text embedded in complex linetypes is SDF-only with no stroke fallback and
stays out of scope.

Tests: a unit test asserts a stroke glyph emits vector ops within the glyph's
world bbox; an integration test drives a real TEXT + linear DIMENSION through
scene.entity_wires() -> export_pdf and checks text reaches the file. Verified
bug-first: with emit_text disabled the end-to-end test fails with the reported
symptom (text adds no content).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-15 13:49:21 -07:00
sLuCHa 1a45f6bd97 fix(plot): correct hatch rendering in PDF / plot export
Hatched content — especially fills nested inside a block INSERT, such as a
title-block logo — plotted incorrectly: missing, mis-spaced, or with phantom
bars. Five related fixes make the PDF/plot output match AutoCAD.

1. Export block-internal hatch fills.
   The export collected only hatches owned directly by the layout block, so a
   hatch nested in a block INSERT was dropped and printed as bare monochrome
   outlines. The insert-explosion the viewport already does is extracted into a
   shared `exploded_insert_hatch_models()` and called from both the viewport
   (`synced_hatch_models`) and the export (`paper_canvas_hatches`), so a plot
   draws block-internal hatches identically to the screen.

2. Honour the hatch's own stored pattern-line spacing.
   `hatch_model_from_dxf` re-derived pattern spacing from the name-matched
   catalog entry x pattern_scale, ignoring the resolved line geometry the DWG
   stores on the hatch. When a drawing was authored against a different base
   spacing (imperial 0.125 vs the catalog's metric 3.175 for ANSI31), lines
   came out up to ~25x too coarse and a dense fill collapsed to a few stray
   lines. When the hatch carries its own line geometry it is now used directly
   (identity scale/angle); the catalog path remains the fallback.

3. Fill far-from-origin pattern hatches.
   `pattern_segments` clamped the ABSOLUTE scan-line index to
   +/-MAX_LINES_PER_FAMILY. A fine-spaced hatch far from the pattern origin has
   large-magnitude indices at both ends but a small span, so the clamp inverted
   the range and emitted nothing — silently dropping the fill. Cap the line
   count (span) instead of the absolute index.

4. Skip TEXTBOX boundary paths.
   TEXTBOX boundary paths (flag bit 3) are text bounding-boxes AutoCAD derives
   for island detection; they are never drawn or filled. Treating one as a fill
   boundary painted its rectangle solid — a phantom bar. It is now skipped when
   building the fill boundary.

5. Print white/ACI-7 hatch fills black on paper.
   Hatch fills arrive adapted to the dark screen background, so a white/ACI-7
   fill would vanish white-on-white on the sheet. Mirror the wire pass:
   near-white/near-yellow -> black, near-cyan -> dark blue, matching AutoCAD's
   colour-7-on-white plotting. Wipeouts keep their paper-white mask.

Adds `tests/block_hatch_export.rs` covering: block-internal hatch reaches the
export set, stored-line spacing is honoured, far-from-origin fills are not
dropped, and TEXTBOX paths are not filled.
2026-07-06 12:24:10 +02:00
Hakan SevenandClaude Opus 4.8 b50921923f chore: post-merge cleanup for #287/#289
- Reword the two new explanatory comments to describe the behaviour on its
  own instead of citing another product.
- window_to_sheet: drop the unused window_min parameter (the caller already
  derives its coordinate offset from the returned min-corner position) and fix
  the doc comment to match what the function returns.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-06 01:22:10 +03:00
sLuCHa 363593b09f feat: plot a model-space window to PDF
Pick a rectangular window in model space with the existing PLOTWINDOW command (now enabled in model space, previously paper-space only) and export it to a PDF at a chosen ISO paper size (A4-A0), orientation and scale (fit or ratio). The window is a hard clip boundary. Adds paper-size presets, a window-to-sheet transform, scale + clip support in the PDF exporter, and Page Setup controls (format/orientation/pick window).
2026-07-05 23:13:43 +02:00
Hakan SevenandClaude Opus 4.8 c599c2ba09 fix(pdf): export linetypes and true lineweights (#155)
The internal PDF export set each wire's colour and thickness but never a
dash pattern, so every dashed / centre / dash-dot line printed solid. It
also derived thickness from the on-screen pixel weight, which carries a 2×
screen-legibility boost — so print weights were inconsistent and barely
differentiated.

Emit a PDF dash pattern per wire from its linetype run lengths (mm→pt,
real length = index of the last non-zero element + 1, 1 pt floor so a dot
still marks), resetting to solid where there is no pattern. Convert
lineweight with a factor that undoes both the 96-dpi scaling and the
display boost, so print thickness is the true physical mm.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-25 19:13:19 +03:00
Hakan SevenandClaude Opus 4.8 44fa9df278 build(web): restore the wasm build — gate native-only deps off wasm
The web build had bit-rotted: native-only features added since it last
compiled pulled crates that don't build for wasm32-unknown-unknown.

- printpdf (PDF export / printing) → native-only target; the web build gets
  stub export_pdf / print_wires. It pulls a wasm-incompatible memchr 1.0.2
  via lopdf → nom_locate, and the web has no filesystem.
- getrandom 0.3 (via ahash via acadrust) → enable its wasm_js feature and add
  the matching --cfg getrandom_backend rustflag in .cargo/config.toml.
- ocs_plugin_api "host" feature (out-of-process plugin runtime: interprocess,
  libloading, memmap2, rkyv) → native-only target; the web gets the
  dependency-free manifest/ribbon contract. The host code was already
  wasm-gated; only the Cargo wiring pulled it in unconditionally.

`trunk build --release` succeeds again.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-25 14:11:18 +03:00
Hakan SevenandClaude Opus 4.8 b2e20c95fe feat(wasm): compile the library for wasm32 (issue #45 groundwork)
First step toward a web build: the library now compiles for
wasm32-unknown-unknown with --no-default-features.

- Add a `solid3d` feature (default on) gating truck-meshalgo /
  truck-shapeops / lzma-sys. These pull vtkio → xz2 → lzma-sys (C),
  which cannot cross-compile to wasm. Without the feature the 3-D
  solid path (tessellation, booleans, ACIS import) becomes no-ops;
  2-D CAD is unaffected. Native builds keep the feature by default.
- Move native-only crates (ureq, open) to non-wasm target deps and
  add cfg shims: update-check is a no-op on wasm, and a new `sys`
  module provides open_url + handle_path (rfd FileHandle → PathBuf;
  synthesizes a name-based path on the web).

Native build + all 75 tests unchanged. Browser runtime wiring
(entry point, trunk, real file I/O) is a follow-up.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-16 13:01:19 +03:00
Hakan SevenandClaude Opus 4.8 7fe7e0230d refactor(scene): group modules into role-based subfolders
Move the 30 flat modules under src/scene/ into six folders by role:
convert (entity→geometry), text (fonts/shaping), model (render models),
pick (hit-test/selection/grips/xclip), view (camera/transform/render),
cache (block + property caches). pipeline/ is unchanged.

Full path migration — all `scene::X` references become
`scene::<group>::X`, no compatibility re-exports. render.rs items that
were `pub(super)` (parent was scene) become `pub(in crate::scene)` to
keep their original scene-wide visibility.

No behavior change; build and tests are green.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-16 11:33:46 +03:00
Hakan SevenandClaude Opus 4.7 ef1f9a08d6 chore: rename H7CAD to Open CAD Studio
Joining the OpenAEC Foundation (open-aec.com). Crate/struct become
OpenCADStudio, user-facing strings use the spaced "Open CAD Studio"
form, repo URLs point to HakanSeven12/open-cad-studio. Logo (window
icon + assets/logo.svg) redrawn with OCS initials. Drop examples/
binaries — unused.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
2026-05-23 17:16:15 +03:00
Hakan SevenandClaude Opus 4.7 30f7ad0918 feat(hatch): rasterise PAT pattern lines for paper canvas + PDF
Both the on-screen paper canvas and the PDF / print pipeline only knew how
to draw the boundary outline of pattern hatches (ANSI31, ANSI37, dotted
brick, etc.) — the actual hatch lines were a GPU-only feature, so any
paper-space view or PDF export showed a hollow polygon where there should
be ruled fill.

Add a CPU rasteriser as `HatchModel::pattern_segments()`:

- builds an edge list from the boundary, splitting at NaN sentinels and
  closing each sub-path so even-odd inside tests handle islands / holes;
- for every PAT family computes the world-space step vector
  (`(dx,dy)` rotated into the family's frame, scaled by `model.scale`)
  and the inclusive k-range bounded by the boundary AABB plus a 4096-
  line safety cap;
- intersects each line with every boundary edge, sorts the hit
  parameters, walks them in pairs (even-odd) and emits one segment per
  in/out interval. Dash patterns are walked along each clipped span with
  phase preserved via `t mod period`, matching the GPU shader's
  absolute-t dash evaluation;
- pattern origin (`family.x0 / y0`) is rotated by `angle_offset` and
  scaled the same way as `hatch_batched.wgsl`, so PAT patterns with
  non-zero origins (brick / offset herringbone) line up with the GPU
  render.

`paper_canvas::draw_hatch` and `pdf_export::emit_hatch` now route
pattern hatches through this rasteriser; solid / gradient hatches keep
their existing fast paths. The previous outline-only behaviour was a
visible regression on every layout containing a ruled fill.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
2026-05-22 06:52:20 +03:00
Hakan SevenandClaude Opus 4.7 c2f3844cb6 feat(io): export hatch + wipeout fills to PDF
The PDF / print pipeline only walked `WireModel.points` (lines), so every
hatched region disappeared the moment a sheet left H7CAD. Route the same
`paper_canvas_hatches()` / `paper_canvas_wipeouts()` lists that the
on-screen canvas consumes into `pdf_export::export_pdf` and
`print_to_printer::print_wires`, and emit each one as an
`Op::DrawPolygon` with the EvenOdd winding rule so multi-ring boundaries
(islands / holes) render correctly:

- Solid       → fill the boundary polygon in the hatch colour.
- Pattern     → stroke the outline (PAT line families stay procedural;
                follow-up work will rasterise them into segments).
- Gradient    → fill with the averaged colour, mirroring paper_canvas.

Wipeouts emit first, then hatches, then wires — same z-order as the
on-screen canvas, so wires draw on top of the fills they belong to.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
2026-05-22 06:45:36 +03:00