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 <noreply@anthropic.com>
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.
- 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 <noreply@anthropic.com>
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).
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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
- 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 <noreply@anthropic.com>
PageSetup panel:
- Paper size presets (A4/A3/A2/A1/A0 portrait+landscape, Letter)
- Plot area selector: Layout vs Extents
- Center-on-page toggle
- Plot offset X/Y fields
- Rotation picker: 0° / 90° / 180° / 270°
PlotSettings (acadrust object):
- PageSetupCommit finds or creates a PlotSettings object in the document
- Writes paper size, plot type, centering, offset, rotation to it
- DXF round-trip: PlotSettings persisted to file
PDF export:
- Reads PlotSettings for the active layout
- Applies centering (auto-computes offset from wire extents)
- Applies offset X/Y from PlotSettings
- Applies rotation via PDF CTM transformation matrix
- Swaps paper dimensions for 90°/270° rotation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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 <noreply@anthropic.com>
- Add printpdf 0.9.1 dependency for native PDF generation.
- New src/io/pdf_export.rs: converts paper-space WireModels to a PDF
page (per-wire stroke color, line weight in Pt, NaN-split segments).
White/near-white colors are inverted to black for print output.
- Add PlotExport / PlotExportPath messages: opens a save-file dialog
and writes a .pdf next to the drawing file.
- Add PageSetupOpen/Close/WidthEdit/HeightEdit/Commit messages: a
centered modal panel lets the user enter paper width and height (mm);
on commit the Layout object's min/max_limits are updated in-place.
- Register PRINT / PLOT / EXPORT and PAGESETUP (PS) commands in
dispatch_command; fix MVIEW error message to English.
- Layout ribbon gains a "Plot" group with Page Setup and Export PDF
ribbon buttons.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>