Commit Graph
138 Commits
Author SHA1 Message Date
Karim Jerbi 9cf9ff2c5c style: apply cargo fmt across workspace 2026-10-11 03:14:29 +01:00
Hakan SevenandClaude Opus 5.5 ff6e2a6613 review(#1660, #1663): associative sweep scan, arc angles, viewport off bit
- refresh_associative_sweeps runs on every edit and scanned the xdata of
  every entity; it now returns at once when no associative swept surface
  exists (its application is not registered) and looks at surfaces only.
- An arc's end angle was raised by TAU in a loop, which never ends on a
  huge angle from a damaged file; the sweep is taken with rem_euclid.
- The sweep DELOBJ test expected surface sweeps to delete their sources,
  which #1663 changed on purpose: an associative surface keeps them.
- codec c9ffcac (opencadcodec #103) adds an explicit viewport-off bit: the
  viewport on state is read with is_on() and set with turn_on()/turn_off()
  everywhere, so a viewport the file marks off shows off and can be turned
  back on from Properties.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-08 14:56:30 +03:00
Hakan Seven 711799371a Merge pull request #1627 from ramox81/codex/plotdate-refresh
Fields: evaluation events, UPDATEFIELD, FIELDEVAL, per-field shading and PlotDate redraw.

# Conflicts:
#	Cargo.lock
#	src/entities/field.rs
2026-10-06 20:42:01 +03:00
Hakan SevenandClaude Opus 5.5 4f0040b542 Cargo: kernel e13f37f, graph f8bbf40; hole/boss edge blends, local fillets, sphere-cylinder booleans; DWT in web open dialog
- Fillet/chamfer hole and boss rims, edges of non-convex bodies, and a
  chamfer next to a fillet (and the reverse).
- Fillet after an extruded PRESSPULL: booleans now join coplanar faces.
- Union/subtract/intersect of a sphere with an off-axis cylinder.
- Web open dialog lists drawing templates.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-06 20:08:42 +03:00
Hakan SevenandClaude Opus 5.5 1eae36e726 Cargo: codec 76d2a1c, kernel e652b4d, graph 0545fa9; text styles, templates, solid edges
- Text styles keep their TrueType typeface through save and reopen (codec
  reads and writes the STYLE record's ACAD extended data) (#1633)
- Unnamed shape-file entries no longer show in text style lists (#1634)
- Save As offers DWT 2018; NEW asks for the template to start from, while
  Ctrl+N and the quick-access New run QNEW with the default template (#1635)
- An edge next to an earlier fillet of the same radius rounds together with
  it (kernel); the solid stays drawn and pickable while edges are chosen
  (#1625, #1107)
- A full cache rebuild drops the B-rep cache too, so an undone fillet does
  not come back with the next edge operation (#1107)
- Selected solids in shaded styles highlight only their visible edges (#1031)
- VIEW ISO / SWISO / SEISO / NEISO / NWISO snap to the isometric corners

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-06 18:32:43 +03:00
Hakan SevenandClaude Opus 5.5 178c7dff41 fix: open-issue sweep — drawing, plot, snap, layout and UI fixes
Fixes:
- Font prompt no longer lists bundled ltypeshp/qcadshp shape files (#1626)
- Wide polylines step at a discontinuous segment width; PEDIT marks the
  current vertex; keyword steps refresh the command preview (#1628)
- Radius-0 fillet drops the zero-length segment it left, so the corner
  fillets again (#1422)
- FILLET Trim / No trim option (#1558)
- True-colour wires plot in their own colour; colour picker stores the
  value it shows (#1621)
- Model tab keeps its page setup; "Save changes to layout" in Model (#1620)
- QNEW default template in Options > Files; .dwt in the open dialog
  (#1134, #1113)
- GRIDUNIT / GRIDMODE / PERSPECTIVE variables (#1618, #1524)
- PTYPE (#1433)
- ZOOM nX / nXP (#1465)
- CHSPACE between model and paper space through a viewport (#1428)
- Moving a dimension keeps its automatic text placement
- Shell icon; "&&" button labels (#1554)
- LINETYPE Load from a .lin file (#1588)
- XLINE / RAY hide the crosshair arms (#1437)
- Dynamic-input boxes keep clear of the crosshair and each other (#1546)
- Snap marker colour setting, dark default on light canvases
  (#1497, #1229, #1128)
- Dimension style precision as a 0.00 pattern dropdown (#1426)
- Node graph nodes left of the origin no longer clip away (#1592)
- Collapse button in the command history header (#1094)
- Freeze-in-current-viewport toggle in the ribbon layer list (#1466)
- Geometric Center object snap for closed polylines (#1369)

New strings translated in all 21 locales.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-06 15:59:30 +03:00
ramox81andClaude Opus 5.5 1c242eb489 Fields: stored values on redraw, evaluation events, per-field shading
MTEXT draws its stored text instead of re-evaluating fields on every
redraw. Fields update on their evaluation events as the reference does:
open (1), save (2, not autosave), plot (4) and REGEN/REGENALL (16), each
masked with the drawing's FIELDEVAL (variable dictionary, 0-31, with the
reference's prompt and message), and the new UPDATEFIELD command
(selection, attributes of selected block references, "N field(s)
found." / "N field(s) updated."). A Date field (on demand only) keeps
its value until UPDATEFIELD. The FIELDDISPLAY gray box now covers each
field's own glyphs, one box per line it spans, instead of the whole
text. Codec moved to the update_fields / field_spans revision.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-06 11:38:01 +03:00
ramox81andClaude Opus 5.5 0a45e59c2f Sheet sets: approved dialogs, sheet status, view categories, model views, navigation fields
Remove Sheets confirmation, Open in drawing editor, Select Layout as Sheet
Template for subsets that prompt, Import Layouts as Sheets with status and
prefix, Rename & Renumber options with Previous/Next and real layout/file
renames, New Subset folder hierarchy and publish setting, subset properties
rows as the reference shows them, sheet status badges (SSMSHEETSTATUS /
SSMPOLLTIME), Sheet Views by category with the View Category dialog, Model
Views locations, Field dialog SheetSet/SheetView navigation, and .dwt/.dws
read as DWG.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-05 12:08:29 +03:00
Karim Jerbi 7d1fca000b fix(io): materialize missing BLOCK/ENDBLK sentinels on load
The DXF reader keeps marker identities on the block record but never materializes the entities, so block_entity_handle/block_end_handle dangle after every DXF load (reopened baked *D dimension blocks hit this every time). Re-create missing sentinels at load finalize, reusing the file handle when free and minting otherwise. Idempotent and DWG-safe: loads whose markers already resolve are untouched. Pinned by dimlinear_survives_save_reload_with_baked_geometry.
2026-10-05 00:07:21 +01:00
Hakan SevenandClaude Opus 5.5 a21aef2a11 fix(field): bound the Field dialog's automation indices; ASCII browse button
#1585's field_dialog action stored any index it was given, and the dialog
indexes its category, case, size, named-type, formula, precision and plot
scale lists with them, so an out-of-range value panicked while drawing.
They are checked against the list lengths now. The hyperlink browse
button drew a glyph (…) that is tofu without the font, and two of the
new local_header_dates calls were mis-indented.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-02 14:17:48 +03:00
ramox81andClaude Opus 5.5 a735b10b34 feat(field): Formula, Hyperlink and plot fields; local-time field dates, file times and display name; header dates on save
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-02 12:38:49 +03:00
Karim Jerbi be8a7b95c4 fix(io): cap line reads on the serve and MCP transports
Bug:
The --serve automation loop (stdin/socket) and the MCP stdio loop read
input with unbounded read_line/lines. A single line over 64 MiB - a
huge paste or a hostile client - grows the buffer until the allocator
aborts, the same class of failure as the HTTP path, on the non-HTTP
transports.

Reproduce:
1. Start --serve on a socket (or the MCP server on stdio).
2. Send one 65 MiB line with no terminating newline anywhere in it.
3. Before: RSS grows to the full line, then "memory allocation failed"
   (or a multi-second stall while the line is copied).
4. After: the read fails at the cap and the existing EOF arm closes the
   connection/stdio promptly.

Fix:
New shared src/io/line_read.rs module: lines_capped(reader, 64 MiB)
yields Err for an over-cap line, which the serve and MCP loops already
treat as EOF/close. rest.rs was refactored onto the same helpers so the
cap logic lives in one place. A regression test streams a 65 MiB line
at the serve socket and asserts the connection ends without a response.
2026-09-30 19:15:49 +01:00
Hakan SevenandClaude Opus 5.5 593f7ca3e9 fix(linetype): catalog complex linetypes keep their shapes and text
The loader added every catalog linetype to the drawing with its [shape]
and ["text"] elements stripped, and the renderer reads the drawing's
own definition first — so BATTING, ZIGZAG, PLANT, POINT1/2, DRAINAGE,
FENCELINE, TRACKS, GAS_LINE, HOT_WATER_SUPPLY drew as plain dashes (a
near-solid line), and a saved file carried no shapes either.

- populate_document_from_source now stores each shape / text on the
  element it follows, with a shape-file STYLE (empty name, one per file)
  and the named text style; values keep their written decimals.
- One table maps shape names to numbers per shape file: the standard
  ltypeshp 130–134, the bundled catalog's own beyond them. The renderer
  resolves stored numbers through it instead of a hard-coded list that
  ignored which file a number belonged to.
- A drawing's copy stripped to exactly the catalog's dashes (as this
  loader saved them) gets its shapes and text back on open.
- Opening completes the linetypes before the geometry is prepared, so
  the first view already draws them.

Fixes #665

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-30 14:38:53 +03:00
Hakan SevenandClaude Opus 5.5 399b5b2c95 fix(save): a drawing saved on an SMB share no longer comes out Hidden
The dot-named temp file is stored Hidden by Samba and the attribute
followed it through the replace. Clear it unless the drawing was
already hidden.

Fixes #1414

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-28 18:30:27 +03:00
Hakan SevenandClaude Opus 5.5 8c9b485b6c refactor(io): R13/R14 plot flags come from the codec now
opencadcodec a35f43e reads R13/R14 LAYER records as plottable, the root
cause #1507 worked around on load; drop the host-side correction. Its
tests stay and now cover the codec fix end to end. They wrote one temp
file per version and ran in parallel, so each test now gets its own.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-28 10:16:19 +03:00
ysdirector 29a252de81 fix(io): R13/R14 layers come back plottable so plots are not blank
The LAYER record only grew a plot flag in R2000: R13/R14 carry frozen,
off, frozen-in-new-viewport and locked as four separate bits and nothing
else, so the reader has no bit to read and leaves `is_plottable` at the
`false` it starts from. Every layer of such a drawing then read as "don't
plot", the plot pass dropped every wire, and PLOT, the preview and
QUICKPRINT all rendered a blank sheet while the drawing still looked right
on screen — display and plot visibility are separate flags, so nothing
about the drawing itself looked wrong.

Restore what the format means by default — a layer plots unless a file
says otherwise — for the two versions that carry the four-bit record, on
both load entry points (bytes and path). R2000 and later keep the flag the
file stores, so a genuinely unplottable layer (DEFPOINTS) stays unplottable.

Tests: an R14 DWG written and read back keeps every layer plottable, the
path-based entry point does the same for R13, a modern file keeps the flag
it stored, and an R14 drawing tessellating to wires that are all
plot_visible locks the whole chain that used to blank the page.
2026-09-28 00:36:24 +08:00
thaile-gt a5468bb8f7 fix(io): normalize missing dimension arrow block references on save 2026-09-27 11:14:36 +07: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
ramox81 c1ecb0edaf Merge remote-tracking branch 'origin/main' into codex/pdf-attach-parity
# Conflicts:
#	locales/ar-SA/opencadstudio.ftl
#	locales/bg-BG/opencadstudio.ftl
#	locales/cs-CZ/opencadstudio.ftl
#	locales/de-DE/opencadstudio.ftl
#	locales/el-GR/opencadstudio.ftl
#	locales/en-US/opencadstudio.ftl
#	locales/es-ES/opencadstudio.ftl
#	locales/fi-FI/opencadstudio.ftl
#	locales/fr-FR/opencadstudio.ftl
#	locales/hi-IN/opencadstudio.ftl
#	locales/hu-HU/opencadstudio.ftl
#	locales/it-IT/opencadstudio.ftl
#	locales/ja-JP/opencadstudio.ftl
#	locales/ko-KR/opencadstudio.ftl
#	locales/nl-NL/opencadstudio.ftl
#	locales/pl-PL/opencadstudio.ftl
#	locales/pt-BR/opencadstudio.ftl
#	locales/ru-RU/opencadstudio.ftl
#	locales/tr-TR/opencadstudio.ftl
#	locales/zh-CN/opencadstudio.ftl
#	locales/zh-TW/opencadstudio.ftl
#	src/app/control/actions.rs
#	src/app/mod.rs
#	src/app/update/mod.rs
#	src/app/view/modal.rs
#	src/io/xref.rs
#	src/ui/window/mod.rs
2026-09-24 19:38:48 +03:00
ramox81andClaude Opus 5.5 3f6bdf8c59 PDF underlays: attach dialog with pages, underlay tab, layers, import settings and file import, unload on the definition
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-24 18:31:40 +03:00
Luis MartinezandClaude Opus 5.5 dddf60d79e Add audited, versioned MCP save workflow (#1452)
- `--export ... --target-version`, and explicit target_format /
  target_version on automation and protocol saves; a conversion that
  would drop unsupported records is refused unless allow_lossy is set.
- `audit` read op: dangling layers/blocks, duplicate handles, non-finite
  bounds, serialization, lossy records and the source DXF handle graph.
- `save_verified`: writes the requested version from the same snapshot a
  normal save writes, reopens it, checks the version and the entity
  manifest, audits the ASCII DXF handle graph and returns a SHA-256.
- ZOOM EXTENTS includes SDF text glyphs.
- Live MCP acceptance script and an optional reference-console audit
  runner under docs/automation.

Squashed from the PR with review fixes: merged with the DWT save path,
the save snapshot, the tool lists added since, and vendor-neutral
harness names; the dated verification record is left out.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-24 16:49:11 +03:00
Hakan SevenandClaude Fable 5.1 8845095432 fix: DXF attribute rotation converted once; plugin layers refuse unknown linetypes
- The acadrust DXF reader has converted ATTRIB/ATTDEF rotation to radians
  since f226110 (in every codec rev OCS pinned from 2026-08-08 on), while
  fix_dxf_dimension_rotations still applied to_radians a second time on
  load. Only Shape needs the conversion here. Surfaced by #1391's hunk.
- Shape-file text styles are found by their is_shape_file flag instead of
  an empty name (also from #1391).
- #1393's add_layer/modify_layer wrote an unresolved linetype name into
  the LAYER record; a name the drawing (plus the standard set) cannot
  supply is now refused, through one resolve_linetype helper.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-20 20:18:18 +03:00
Hakan Seven 7204d4f98d Merge pull request #1380 from huaninstratech/pr/io-locked-drawings
fix(io): open locked, shrinking and cloud-placeholder drawings without crashing
2026-09-19 23:06:51 +03:00
Hakan Seven f20c823ffe Merge pull request #1360 from HakanSeven12/plot/b4-ctb-leniency
fix(plot-style): load plot style tables written by other applications

# Conflicts:
#	src/app/update/file.rs
#	src/ui/window/plot.rs
2026-09-19 23:00:19 +03:00
Hakan Seven 996412c405 Merge pull request #1359 from HakanSeven12/plot/b4-windows-printer-media
feat(plot): Windows printer media and printable areas

# Conflicts:
#	locales/ar-SA/opencadstudio.ftl
#	locales/bg-BG/opencadstudio.ftl
#	locales/cs-CZ/opencadstudio.ftl
#	locales/de-DE/opencadstudio.ftl
#	locales/el-GR/opencadstudio.ftl
#	locales/en-US/opencadstudio.ftl
#	locales/es-ES/opencadstudio.ftl
#	locales/fi-FI/opencadstudio.ftl
#	locales/fr-FR/opencadstudio.ftl
#	locales/hi-IN/opencadstudio.ftl
#	locales/hu-HU/opencadstudio.ftl
#	locales/it-IT/opencadstudio.ftl
#	locales/ja-JP/opencadstudio.ftl
#	locales/ko-KR/opencadstudio.ftl
#	locales/nl-NL/opencadstudio.ftl
#	locales/pl-PL/opencadstudio.ftl
#	locales/pt-BR/opencadstudio.ftl
#	locales/ru-RU/opencadstudio.ftl
#	locales/tr-TR/opencadstudio.ftl
#	locales/zh-CN/opencadstudio.ftl
#	locales/zh-TW/opencadstudio.ftl
#	src/locale_catalog.rs
2026-09-19 23:00:18 +03:00
huanld dc23d4073a fix(io): survive locked, shrinking and cloud-placeholder drawings
Opening a drawing that another program or machine holds without read
sharing used to fail with a raw OS error, and the memory-mapped fast
path could kill the process outright: a mapped file that shrinks
mid-parse (a writer saving over it) or a cloud placeholder that cannot
page in raises an in-page exception no Rust code can catch.

Read the DWG through a guarded fast path instead: mmap when possible,
and fall back to one in-memory snapshot read — opened with the most
permissive share mode Windows offers (FILE_SHARE_READ | WRITE |
DELETE) — whenever the map fails or an I/O error surfaces mid-parse.
A snapshot can never fault: a concurrent writer just ends the read
early. Sharing violations (os error 32/33) now answer with a message
that names the file and the remedy, and OneDrive-style placeholder
attributes skip mmap entirely.
2026-09-19 16:52:24 +07:00
PanosandClaude Opus 5 7a75d430cd fix(plot-style): load plot style tables written by other applications
Tables copied from other applications into the plot styles folder were
listed but refused on selection, showing as "missing" with the reason
lost on a console: the loader failed hard on a checksum or a text length
that did not add up and on any non-UTF-8 byte — and those files carry
their descriptions in Windows-1252.

- decompress_ctb reads the file as it is: a checksum or length mismatch is
  reported and read past (a genuinely cut-off file is still an error), a
  zlib stream with a bad trailer is retried as raw deflate, and text that
  is not UTF-8 is read as Windows-1252 (pens and colours are numbers and
  unaffected; other code pages only show wrong characters in descriptions).
- PlotStyleTable carries load_warnings; every load site reports them on the
  command line, so a table that needed tolerance says so.
- load_named explains itself: a .stb name says named tables are not
  supported yet, a .ctb missing from the folder says which folder to copy
  it into (instead of "Unknown built-in plot style").
- The Plot dialog shows the reason under the Table row (style_error) —
  from a selection, from a page setup that names a table, and from the
  Load… picker, whose error used to be swallowed.
- A table picked with Load… from outside the plot styles folder is copied
  into it (unless a file of that name is already there), so it is listed
  from then on and page setups naming it resolve after a restart.

Tests: flipped checksum, text length off by one, a short payload field,
Windows-1252 descriptions, a truncated stream, .stb and missing names.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-18 19:00:28 +03:00
Hakan SevenandClaude Fable 5.1 2b69e6e94c fix: point cadkernel at upstream after #1352; unify duplicate-constraint message
- #1352 pinned cadkernel/cadkernel-constraints to ramox81's fork. Its one
  extra commit (cadkernel #67, SymmetricLineDirections) is now on
  HakanSeven12/cadkernel main as the same SHA, so the manifests and lock
  point back at the upstream repo at rev 0493b59.
- SYCONSTRAINT reports a repeated relation with the same message Fix and
  Vertical use, as an error instead of plain output.
- The knot-less spline test consumes the add_entity results.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-18 18:58:00 +03:00
Hakan Seven 9613e6edf9 Merge pull request #1351 from helenkwok/feat/web-open-from-bytes
Web control: open a drawing from bytes
2026-09-18 18:52:37 +03:00
PanosandClaude Opus 5 b8472a00b4 feat(plot): Windows printer media and printable areas
The plot dialog asks CUPS printers for their sheets and margins; on Windows
the query was a stub, so every printer showed the paper catalogue with
unverified margins. The driver is now asked the way the plot dialog expects:

- windows_media (compiled and tested everywhere): the three parallel
  DeviceCapabilities tables (ids, sizes in tenths of a millimetre, 64-WCHAR
  names) become one PrinterMedia per distinct size — a rotated or
  borderless twin folds onto its sheet, catalogue sheets keep their name,
  unknown sizes carry the driver's — with the device's margins on every
  entry and the driver's default sheet found by id or by user size.
- print_to_printer (Windows): DeviceCapabilitiesW for the tables, the
  default DEVMODE for the default sheet, one portrait printer DC for the
  unprintable margins (physical offsets and extents, per device — a
  documented approximation). The DEVMODE fetch is shared with the print
  path (with_printer_devmode).
- A direct print now asks the driver for the plot's own sheet (by id, or
  as a user-defined size), so an A3 plot prints on A3 rather than shrunk
  into the driver's default; a driver that refuses the sheet is asked for
  the orientation alone.
- The capability cache remembers printers that could not be asked, so a
  slow failing query runs once per session, not on every dialog open.
- PRINTERS <name> reports a printer's sheets, margins and default sheet.

Tests: names (NUL trim, full slot, lossy), media folding and dropping,
default by id / user size / none, DC margins, paper id lookup.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-18 18:42:03 +03:00
Helen Kwok 2a78ebe2c1 Web control: open a drawing from bytes
On the web build, {"op":"open","path":…} resolves the name against the
browser's recent-files cache, so a file that is not already there cannot be
opened by the control channel. Accept {"op":"open","name":…,
"data_base64":…} instead: the bytes go through the same parse, caching
and recovery path as a file chosen in the browser picker
(io::open_bytes_web, now shared with pick_and_load_web).

Native builds keep path-based open and reject data_base64 with
invalid_request.

Refs #1349
2026-09-18 16:43:29 +09:30
Christopher OverbeckandClaude Opus 5 37059cb0d6 Repair knot-less splines on every load path
load_file, used by headless export, xrefs and block import, finalizes a
drawing without purging, so the spline degree repair never ran there and
`--export drawing.dxf out.dwg` still hit the knot underflow. Run the repair
when any load is finalized.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-09-17 10:33:18 -05:00
Christopher OverbeckandClaude Opus 5 1ac9309f85 Guard constraint solving and DWG export against degenerate geometry
Retaining a line's length added a P2PDistance constraint, whose gradient
divides by the current distance. For a zero-length line the solver got a NaN
Jacobian and its SVD fallback never returned, freezing the editor (e.g.
ECONSTRAINT on a line drawn by clicking one point twice). Hold coincident
endpoints together with per-axis differences instead.

Control-point splines read without knots are given a degree their control
points can carry (at most n - 1) and a clamped knot vector when a drawing
loads. Generating knots for fewer points than the degree underflowed during
DWG write: a panic in debug builds, an unbounded allocation in release, and
reachable from autosave. A lone control point with no knots or fit points is
purged as corrupt.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-09-17 10:33:18 -05:00
Hakan Seven 03f4e7cb86 Merge pull request #1309 from ghost/embed-print-fonts 2026-09-17 06:57:42 +03:00
PanosandClaude Opus 5 c03a40cb33 feat(plot): plot device model with AutoCAD names and printable areas
A page setup names its plotter, and AutoCAD looks that name up in its own
configurations: `DWG To PDF.pc3` for PDF output, the spooler name for a
system printer, `None` when nothing is assigned. Drawings saved here used
to carry the dialog's label ("Save to PDF file…"), so AutoCAD reported a
missing device on every layout plotted to PDF from Open CAD Studio.

- New `io::plot_device`: `PlotDevice { None, Pdf, Printer }` with AutoCAD's
  spellings, a reader for stored names that keeps any plotter
  configuration AutoCAD wrote (an HP `.pc3`, `DWF6 ePlot.pc3` — plotted to
  PDF here but preserved for AutoCAD) and upgrades the legacy label.
- Printable areas: the PDF driver's margins per sheet variant (full bleed
  edge to edge; standard / expand insets provisional until read off
  AutoCAD's plotter configuration), and for CUPS printers the media and
  margins the queue reports over IPP (`ipptool` `media-col-database`),
  parsed once per printer and cached. Windows enumeration follows with
  native GDI printing.
- Page setups now store the device in AutoCAD's spelling and the margins
  of the chosen device/sheet, keeping the drawing's own values while
  neither changed (AutoCAD's `PAPERUPDATE` behaviour). The paper-space
  printable-area rectangle follows.
- Plot dialog: the default entry names the printer it resolves to; a
  printer that answered lists its own sheets with a note; the media query
  runs in the background and answers for a since-deselected printer are
  dropped.

Tests: device names, stored-name resolution, PDF margin table, IPP media
parsing (sizes, margins, borderless folding, default sheet), PWG names,
plus dialog ↔ layout round-trips for PDF, legacy label, preserved plotter
configuration, sheet change → margins, and printer selection.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-16 16:05:46 +03:00
PanosandClaude Opus 5 567eb90925 feat(plot): paper catalogue with AutoCAD canonical media names
AutoCAD stores a layout's sheet as the plotter driver's canonical media
name (`ISO_A4_(210.00_x_297.00_MM)`, `ANSI_full_bleed_B_(11.00_x_17.00_Inches)`),
so a drawing saved here has to carry the same spelling for AutoCAD to find
the sheet in its DWG To PDF list, and a name that arrives from AutoCAD must
resolve to its dimensions and go back out untouched.

- New `io::paper_catalog` replaces the eight-entry `paper_sizes`: ISO A/B,
  ANSI, ARCH (incl. E1) and JIS series with full-bleed / expand variants,
  Letter / Legal / Tabloid, millimetre and inch units, a parser that
  reconstructs any well-formed AutoCAD name (custom or driver-specific)
  from its embedded dimensions, driver / legacy-config aliases (`A4`,
  `Letter`, `Ledger`), and dimension matching that prefers the plain sheet.
- The Plot dialog stores the canonical name and shows the sheet as
  `ISO A4 (210 × 297 mm)`; a sheet that only exists in the drawing is
  listed first so it stays selectable.
- A sheet the user did not change keeps the drawing's own media name
  (`paper_name_for_settings`), so a Windows printer's `A4` is not rewritten
  to `ISO_A4_(…)` just by passing through the dialog.
- Drops the unused `PlotFormat` / `PlotOrientation` messages,
  `paper_label_from_dims`, and three duplicated `match "A4" | …` tables.

Tests: catalogue spelling, parser round-trip, aliases, dimension matching,
display formatting, plus dialog ↔ layout round-trips for driver-named,
catalogue and unknown sheets.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-16 16:05:46 +03: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
Hakan Seven 306d5d0957 Merge pull request #1302: Preserve valid zero-radius curves 2026-09-16 09:37:48 +03:00
Karim Jerbi f56d8a948e feat(xref): reference model with lexical paths, pathtypes, and statuses
Path normalization with stable keys, relative/absolute/none pathtypes, wildcard matching, and reference entries with loaded, stale, missing, and unreferenced states.
2026-09-15 23:56:30 +01:00
Christopher OverbeckandClaude Fable 5.1 8e14b10104 fix(io): keep zero-radius circles and arcs on open
Problem

The open guard rejects circles and arcs with a radius below 1e-10. A normal
open fails when a drawing contains one. The user gets the recovery prompt.
AutoCAD and nanoCAD open these drawings.

The check came from ace99541. Its comment says the tessellator cannot sample
a degenerate curve. That was true of truck's parameter_division. Commit
8c7ebdc9 removed truck. Commit 96be8214 made the kernel sample circles and
arcs by sweep angle with a floored segment count. The reason for the check
no longer exists.

Change

- Remove the near-zero radius check for circles and arcs.
- Keep the checks for non-finite values and radii above 1e10.
- Update the guard comments. The purge rejects parser desync. It does not
  reject degenerate geometry. Be generous in what you accept and strict in
  what you produce.
- Add tests: a zero radius passes the guard, an absurd radius does not.
- Change the automation purge test sample from radius 0 to radius 1e11.

Behavior

- Zero-radius circles and arcs load. They render as dots.
- Tessellation, hit testing, snapping, and hatch boundaries accept a zero
  radius.
- The DXF writer does not change.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-15 16:17:29 -05:00
Hakan Seven 973af87d58 fix: keep exact snapping, source drawing data and adaptive options layout
Resolve curve intersections through cadkernel, retain empty exact results, gather analytic candidates independently of tessellation, and bound material uploads. Preserve large MINSERT data while limiting rendering work; remove obsolete review narratives.
2026-09-09 13:17:30 +03:00
Felix RiestraandClaude Sonnet 5 d0025d6ddf Make automation "open" run the same finalization as a UI open
Automation's "open" op called io::load_bytes directly and installed
the result as-is, skipping everything the canonical path-based loader
(load_file_for_open / finalize_loaded_outcome) runs after parsing:
block-origin normalization for legacy non-zero-origin blocks,
relative raster/material path resolution, source_path assignment,
and the corrupt-entity purge the UI open path always applies. The
same file opened two ways behaved differently, and headless/automation
use was more exposed to malformed geometry with no filter at all.

Added io::load_bytes_finalized(path, bytes), which runs load_bytes
plus that same normalization and purge using the real path (not just
its basename, which the old code passed to load_bytes as the "name"
parameter — losing the directory needed for raster-path resolution
and source_path). automation.rs's "open" handler now uses it and
reports the purge count as a "purged" field in the response.

load_bytes itself is unchanged, since the byte round-trip tests
(sketch_persist.rs, named_parameters_persist.rs) deliberately want
the simpler byte-only path with no real filesystem path behind it.

New test constructs a document with one normal and one corrupt
(zero-radius) entity, saves it, opens it through automation, and
checks the corrupt entity was purged and source_path was set —
proving the automation path now matches the UI path.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-09 10:13:06 +02:00
Felix RiestraandClaude Sonnet 5 8bc9e83c2e Fix unbounded MINSERT allocation and scale-multiplied spacing
Two independent MINSERT (array INSERT) bugs found while auditing
render_graph.rs:

1. array_offsets() reserved and filled one Vec element per array
   instance, with row_count/column_count parsed as unchecked u16s.
   A corrupt or adversarial 65535x65535 pair requests 4,294,836,225
   instances (~96 GiB) before any GPU-side chunking limit can help,
   and before the render graph even reaches the GPU. Fixed with a
   documented instance budget (20,000) enforced at both the load-time
   corrupt-entity filter (is_entity_corrupt, matching the existing
   polyline/spline vertex-count guards) and a failsafe clamp in
   array_offsets itself, for an insert built or edited some other way.

2. insert_instance_transform composed the per-instance row/column
   offset translation *before* the INSERT's own transform, so the
   block's x/y scale multiplied the spacing along with the block
   contents — an INSERT with x-scale 2 and column spacing 10 rendered
   20 units between columns instead of 10. The pinned CAD model's own
   Insert::array_points() confirms the correct behavior: spacing
   rotates with the insert but is never scaled. Fixed by orienting the
   offset with rotation + OCS only, then translating the already-
   composed (scaled) instance transform by that world-space vector.

7 new tests cover both: pathological/reasonable instance counts at
the load-time filter and the render-graph failsafe, and instance
transforms for scaled and rotated arrays.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-09 10:12:53 +02:00
Hakan Seven 89572e5ca9 fix: keep valid small drawing curves on open 2026-09-08 22:42:49 +03:00
Hakan Seven d66e89ca19 Complete application localization across all supported languages 2026-09-07 01:05:02 +03:00
PanosandClaude Opus 5 2787dcc1a3 perf: instrument the file-open load path behind PERF
The 52 s silent stretch in a large-DWG open could not be attributed:
OpenTimings covered parse / purge / xref / caches, but the FINALIZING
phase -- prepare_open_geometry, which does the first full Model
tessellation and builds the interaction index -- was unmeasured and
reports only two progress ticks, so it was indistinguishable from parse
time.

Add finalize_ms to OpenTimings and a per-stage PERF breakdown:

  open-phases     parse / purge / xref / caches / finalize
  open-finalize   tessellation vs interaction index
  open-index      index build vs prepare_screen
  wires-build     sort cache, visible filter, block cache, classify,
                  memo hits, parallel tessellate, materialize, sort
  resident-post   scene markers, refedit fade, resident layout
  wire-upload     build_wire_buffers, which runs on the render thread

The wires-build line separates the parallel tessellate from the serial
materialize loop that deep-clones every WireModel while the memo retains
a second copy -- the leading hypothesis for both the stall and the
6.45 GB RSS.

No behaviour change. Every timer is gated on crate::perf::enabled(), so
nothing runs without PERF=1 or the in-app PERF panel. The localized
open-summary format string is left alone; changing it would rekey the
string across every locale, so the full breakdown goes to PERF instead.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-04 16:20:30 +03:00
Hakan Seven c4515cbbc1 fix(save): support autosave recovery files 2026-08-23 14:19:05 +03:00
Hakan Sevenandramox81 464971969c feat: complete freehand sketch workflow
Co-authored-by: ramox81 <[email protected]>
2026-08-21 00:17:44 +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 313349fd71 fix(io): verify saves through edit leases 2026-08-17 23:13:07 +03:00