1611 Commits
Author SHA1 Message Date
Anshul fca04823f4 use node 24 lts (#1822)
npm@12 dropped node 25, which is an odd-numbered release and not LTS.
Required: ^22.22.2 || ^24.15.0 || >=26.0.0
2026-09-07 22:28:17 +12:00
Anshul d8de9d6f76 open the release PR against the dispatched branch (#1817)
Open the release PR against the branch it was dispatched from

gh pr create targets the repository's default branch when --base is
omitted, so dispatching Prepare release against v3.5.2-release opened a
PR into master carrying the entire branch difference. Pass
--base "$GITHUB_REF_NAME" so a backport release lands on its own branch.

Also force-push the generated branch, so a re-run after a failed attempt
replaces it rather than being rejected as non-fast-forward.
2026-09-07 21:10:33 +12:00
Emmett Lalish 7b33277245 Remove duplicate Smoothing APIs (#1814)
* almost updated scallop

* scallop working

* fixed scallop

* sharpened scallop

* removed some smoothing APIs

* fixed some tests

* tests passing

* updated bindings examples

* fix formatting

* fix more formatting
2026-09-07 12:44:38 +12:00
Anshul 2e2ed36968 Fix the release pipeline (#1810)
* Resolve the deployed commit after checkout and use that for the artifact lookup instead of github.sha.

* publish_npm.yml builds from the checked-out commit instead of downloading a master artifact

* derive version into files

* Six files that were stale at 3.5.0 are now 3.5.1.
2026-09-02 22:01:20 +12:00
ssss141414 0d9abcd72a Fix degree sine reduction on Windows ARM64 (#1809) 2026-08-27 21:25:17 +12:00
ssss141414 10ecdf6ab6 Build native Windows ARM64 wheels (#1805)
* Build native Windows ARM64 wheels

* Keep Windows ARM64 change build-only

* Keep Windows ARM64 wheel change minimal

* Limit Windows ARM64 wheel Python versions

* Fix Windows ARM64 cibuildwheel filter
2026-08-27 21:23:40 +12:00
Anshul 6178ce30a9 Versioned deploy follow-ups (#1807)
* minor fixes

* Publish master as alpha/ and document manual deploys

* remove /latest + add it to the root

* minor cleanups
2026-08-27 21:17:47 +12:00
dependabot[bot] 87f5fec565 Update nanobind requirement from <3.0.0,>=1.8.0 to >=1.8.0,<4.0.0 (#1808)
Updates the requirements on [nanobind](https://github.com/wjakob/nanobind) to permit the latest version.
- [Changelog](https://github.com/wjakob/nanobind/blob/master/docs/changelog.rst)
- [Commits](https://github.com/wjakob/nanobind/compare/v1.8.0...v3.0.0)

---
updated-dependencies:
- dependency-name: nanobind
  dependency-version: 3.0.0
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-26 16:40:25 +12:00
Anshul 37e5b64440 Fix asset paths for subdirectory deploys (#1806)
* relative should build from its path

* format
2026-08-25 07:36:11 +12:00
Anshul 15224126a4 deploy of master to master/ (#1801)
* Nightly deploy of master to master/

* Trigger on master CI instead of a nightly cron

Per review on #1801: cron would fire more often than master actually
receives commits, and still add up to 24h of latency. workflow_run on
CI completion is better on both counts.

Also pins the artifact to the triggering run via run_id, checks out the
exact commit CI validated, and skips deploying when that CI run failed.
2026-08-23 12:01:21 +12:00
Anshul d00dbe1d48 add GSoC 2026 work product summary (#1795)
* doc

* moved

* dashboard minor changes
2026-08-19 19:01:48 +12:00
Emmett Lalish 81e94c86c5 Smooth quads (#1794)
* smooth quads showing promise

* cleaned up test naming

* fixed quads

* minor updates

* fixed SDF noise

* unmark temporarily fixed halfedges

* small refactor

* cleanup

* edge collapse keeps prop verts simple now

* fixed a flat faces bug

* deflake test

* big improvement in aligning tangents
2026-08-18 17:30:40 +12:00
Zach Loafman 35dec2cfdb Confine the atomic reinterpret_cast to one shim, use std::atomic_ref at C++20 (#1791)
* Replace the atomic reinterpret_cast with an AtomicRef shim

* Scope the C++20 deprecation suppression to the shared_ptr atomics

* Move the shared_ptr atomic helpers into atomic_compat.h

* Apply review: 32-bit MSVC fallback, comment accuracy, include hygiene

* Hoist the MSVC includes back to file scope

* Address review: enumerate CI jobs, reject 32-bit MSVC

* Name the MSVC atomic backend condition once

* Correct atomic_compat's comment on what survives at C++20

* Back off to a thin C++17 cast shim; std::atomic_ref at C++20

* Drop the AtomicRef concurrency tests; they cannot detect breakage
2026-08-18 15:41:10 +12:00
Anshul 2021a3ab06 versioned documentation deploy (#1787)
* updated v2

* update previous docs

* drop the master docs push

* add parantheses
2026-08-17 22:40:02 +12:00
briansturgill d38122c5ab Added Piecad to Manifold User's List. (#1792) 2026-08-15 11:01:01 +08:00
Emmett Lalish 969b1417af Improved decimation (#1789)
* added new decimator

* fixed some bugs

* new cost, no inversion

* added global tolerance bound

* add delaunay criterion

* pseudoinverse for coplanar collapse

* small refactor

* fixed property interpolation

* avoid vertNormal, which may not exist yet

* fixed some collinear collapse problems

* fixed prop interp

* loosened MatchesTriNormals slightly

* removed RemoveDegenerates

* fix maxCost

* improved cost precision

* factored Merger

* fixed uninitialized data

* iwyu in tests

* fixed early return

* fixed short collapse ordering

* verified and updated tests

* minor test updates

* some fixes, but problems remain

* fixed visitation

* clean up short edge collapse logic

* fixed degenerates

* new verts are visited

* fixed firstNewVert bug

* efficiently recalculate face normals

* constrain property edges

* some edge swap fixes

* improved swap

* added prop test function

* fixed more properties issues

* fixed property swaps

* collapse more degenerates

* small perf improvement

* cleanup

* fix tsan
2026-08-11 12:28:09 +12:00
Zach Loafman c50001b82d Add CrossSection::EvenOdd for even-odd input fill (#1788) 2026-08-10 21:10:21 +12:00
AnshulandClaude Sonnet 4.6 ff42ddc885 Weekly benchmarks (#1758)
* support weekly benchmarks

* updated for perftest

* rem clipper

* other tests

* parse cmake output + use ember-benchmark-cases.json

* add only required STl files for ember tests

* remove ember tests+fix corrupted stl file

* shell script to py + minor changes

* mac-os

* more info of machine with linux fallback

* storage for weekly benchmarks

* rebase

* binary stl

* clean

* .

* fix ember phase capture and drop removed phases

- merge stderr into stdout in run_ember_case so man_bench diagnostic
  output is not silently dropped
- remove Assembly/Triangulation/Simplification/Sorting from
  INDEPENDENT_PHASES; their Timer::Print calls were removed in PR #1745
  and they always produced 0.0; only Intersect12 and Winding03 phases
  are still reported
- update build_ember_summary table to match the 4 remaining phases

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* revert runner

* add frontend + trigger on publish + san extended test

* fix formatting

* TEMP: allow workflow_dispatch to exercise data-publish steps for testing

Not for merge - revert before this PR lands. Only relaxing the if: gate
so a manual run can validate the folded run: > block and sanitizer arg
passing reviewers asked about, without waiting for a scheduled/release run.

* Revert "TEMP: allow workflow_dispatch to exercise data-publish steps for testing"

This reverts commit e00c5f5784eefa27255681dce4105bd45be3daa0.

* Move weekly sanitizer lane to Linux, fix artifact path, derive compiler

- Move build_sanitizer/test_sanitizer in weekly_benchmark.yml from
  macos-15 to ubuntu-24.04, matching the existing PR-triggered sanitizer
  lane in manifold.yml, since nothing about ASan/UBSan requires macOS
  here. Drops the compiler overrides and macOS-only setup steps.
- Download sanitizer build artifacts into build/ instead of the
  workspace root, so manifold_test's baked-in RPATH to libsamples can
  actually resolve. It was crashing on load with
  "Library not loaded: @rpath/libsamples.3.dylib" because the artifact
  download flattened the build/ directory away.
- Capture the sanitizer build's own cmake configure log and derive
  SANITIZER_SUMMARY_COMPILER from it at runtime instead of hardcoding
  the string, reusing system_metadata.py's existing helpers.
- Trim set -euo pipefail to set -u in the data-publish steps, since
  GitHub Actions' default bash invocation already applies -e/pipefail.
- Remove the duplicate weeklybenchmark-test job from manifold.yml in
  favor of testing weekly_benchmark.yml directly via workflow_dispatch.

* Merge dashboard pages + remove dead JS fallbacks + gtest JSON output + simplify perf summary formatting + clean up .gitignore

* Stop forcing clang-18 for sanitizer builds, use runner default (gcc)

* small changes

* -Wno-maybe-uninitialized

* review changes

* dead code

* fix cmke read

* change flags

* remove redundant flags and schema version

* /dashboard route + remove upload route + change dead and retention changes

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-02 17:14:38 +08:00
Evenedric 422ab6fcee Fixes #1781 - Manifold::Transform() does not correctly handle mirror transforms (#1784)
* Fixes #1781 - Manifold::Transform() does not correctly handle mirror transforms

* Formatting
2026-08-01 16:12:13 +12:00
Xie ZhuoyangandXie Zhuoyang d62e3b0979 Use PMR for reusable triangulator nodes (#1779)
Co-authored-by: Xie Zhuoyang <179989892+lite-tx@users.noreply.github.com>
2026-07-30 10:24:35 +12:00
Xie ZhuoyangandXie Zhuoyang f2b7582bd9 Optimize MergeMeshGL open-edge discovery (#1778)
Co-authored-by: Xie Zhuoyang <179989892+lite-tx@users.noreply.github.com>
2026-07-26 11:48:56 +08:00
Xie ZhuoyangandXie Zhuoyang cf4a15ef58 Reuse triangulator scratch buffers (#1776)
* Reuse triangulator scratch buffers

* Address triangulator review feedback

---------

Co-authored-by: Xie Zhuoyang <179989892+lite-tx@users.noreply.github.com>
2026-07-25 20:14:32 +08:00
Lars Brubaker 26fe6d8c49 Add manifold-rust (native Rust port) to Bindings & Packages (#1777)
Requested in #1774.
2026-07-24 21:32:42 -07:00
Tyler Gibbs e2adc05696 document manifoldcad global settings (#1775) 2026-07-24 21:30:57 -07:00
Anshul 895bfc230f Fix benchmark guard and add memory guard (#1762)
* fix commit

* remove cross_section flag

* minor changes

* minor changes 2

* review changes

* base_median=0 case check rem

* remove schema

* switch median to mean

* switch to macos

* more info of machine with linux fallback

* print per-size perfTest memory usage

* .

* .

* pin benchmark_guard to macos-15, drop test build, clean up JSON fields

* rem redundant code

* revert cmake bug

* memory guard

* review feedback

* updated subprocess() func

* rem mismatch logic
2026-07-11 18:55:07 +08:00
Zach Loafman 8bffb52101 Boolean2: pivot the 2D arrangement to Smith's sweep line (#1770)
* Insert 2D intersections in point order with exact constructed-vertex identity

* Unify the kd-tree axis sort into one comparator

* Add non-closing-walk counterexamples and scope the retained-graph oracle

* Document the point-order arrangement

* Add near-vertical three-edge concurrence regression

* Add failing regression test for dense near-concurrent fan

* Replace the point-order arrangement with a Smith sweep-line

* Rewrite the Boolean2 design doc for the sweep engine

* Preserve collinear vertices on vertical edges in the sweep

* Use int64_t for signed multiplicity; drop redundant test try/catch

* Re-run CI (WASM worker font-fetch CDN 429 flake)
2026-07-07 15:13:11 -07:00
Emmett Lalish 8d674676f5 Fix dividers (#1771)
* fix editor resize

* small pointer event refactor
2026-07-07 13:38:46 -07:00
Rogier Goossen e5f6201dfb Add SolidSKeleton to users list (#1768) 2026-07-02 16:52:36 +12:00
pca006132 697418b8b9 remove async free (#1766) 2026-06-26 18:15:25 +12:00
Nasser 681fec46b3 Add NASSCAD to users in README (#1765)
Add NASSCAD to users list
2026-06-25 16:34:17 +08:00
Zach LoafmanandClaude Opus 4.8 090bdaca33 Simplify the boolean2 winding and crossing-insertion pipeline (#1759)
* Simplify the boolean2 winding and crossing-insertion pipeline

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Re-disable the TinyFeatureNearCorner offset tests pending winding robustness

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* boolean2: decide segment crossings by sign, not eps-near-endpoint band

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* boolean2: re-enable TinyFeatureNearCorner offset tests

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* boolean2: document the sign-based crossing decision

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* boolean2: re-disable HostDropAtOffset4096 (non-planar at large offset)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* boolean2: decide winding at the start vertex, drop the clearance offset

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* boolean2: use la::cross for the winding perp-dot, inline RayContribution

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* boolean2: re-enable ShortEdge/InclExcl with eps*length-floor bounds

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Add a CrossSection regression test for clustered edge crossings

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* boolean2: tidy the new tests for review (named constant, headroom, messages)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* boolean2: update the clustered-crossing test for the current CrossSection API

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* boolean2: reword the perp-incidence test comment per review

Use point-to-line distance vs point-to-point proximity; drop the
along-line/inverse-projection aside Emmett flagged as not worth mentioning.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* boolean2: rework incident-edge winding to Interpolate+Shadows

Per review, replace the explicit cross(d,w)/cross(n,w) perturbation in
LeftWindingAtVertex's incident branch with the Interpolate+Shadows side
test the non-incident branch already uses (the incident edge's far vert
and the classified far vert share a y-side of P, so the side test reduces
cleanly). Validated bit-identical to the prior winding across the full
suite + corpus + a 40k scaled random/degenerate differential.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* boolean2: add a disabled guard for the macro-feature-drop corner touch

Two disjoint polygons point-touching ~2e-9 at a corner produce the same
non-closing-walk assert as the HostDrop case, but here the walk a release
build would drop is the entire ~422k-area piece, not a sub-eps tangle.
Kept disabled as the witness that the closed-walk assert cannot be
relaxed to an unconditional drop.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* boolean2: rename winding local far -> farEnd (far is reserved on MSVC/MinGW)

The Interpolate+Shadows rework named a local `far`, which MSVC and MinGW
(MXE) treat as a reserved legacy pointer qualifier, breaking those builds
while gcc/clang accepted it. Pure rename, no behavior change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* boolean2: infer eps from absolute scale and drop the local-origin recenter

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* boolean2: add disabled test for the centered sub-eps non-closing walk

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* boolean2: merge winding-split verts to close the walk

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* boolean2: add disabled test for near-coincident-corner non-closing walk

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Revert "boolean2: merge winding-split verts to close the walk"

This reverts commit cb9c1abd3cbb3b6098c4c9eba120e9d5203a1cff, keeping the
near-coincident-corner regression test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Add visible large-eps/bbox missed-crossing regression (DISABLED)

A crucible search found a near-coincident missed-crossing open walk where the
op-eps is ~17% of the bbox (eps=1.75, bbox ~10.3) - eye-resolvable, vs ~3e-12 of
the bbox in production. Same class as the disabled near-coincident-corner case.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* boolean2: split edges at incidences before crossing detection

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: describe split-first arrangement in Boolean2.md

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* boolean2: pass out-params by reference

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Enable position-inclusive-eps regression tests

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 08:34:29 +12:00
Zach LoafmanandClaude Sonnet 4.6 bf47ebccc6 CrossSection: drop FillRule/Compose, add SetTolerance/GetTolerance (#1760)
* CrossSection API break (#1707): drop FillRule/Compose, const-ref Hull, tolerance API

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* CrossSection API break (#1707): C bindings + test fixes

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* CrossSection API break (#1707): Python + WASM bindings

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* CrossSection API break (#1707): doc + example followups

- Drop stale @param fillrule from CrossSection(SimplePolygon) doc
- Replace CrossSection.compose with batch_boolean in all_apis.py
- Add get_tolerance/set_tolerance to all_apis.py

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* CrossSection API break (#1707): fix WASM JoinType::Bevel

'Bevel' was registered in the embind enum but missing from the
TypeScript type and from joinTypeToInt(), so passing 'Bevel' silently
fell through to 'Square' (0). Add it to both.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* CrossSection API break (#1707): tolerance API fixups

- Rect and Circle constructors now set tolerance_ via InferEps,
  consistent with the Polygons constructor. Previously GetTolerance()
  returned 0 for these paths.
- Hull(const SimplePolygon&): use the local sorted copy (points) for
  InferEps, not the original const-ref (pts).
- Move SetTolerance declaration to the Information doxygen group
  alongside GetTolerance.
- Add CBIND.cross_section_tolerance smoke test for
  manifold_cross_section_get/set_tolerance.
- Fix stale comment in SimplifyPostFiltersBoolean2Output referencing
  the old epsilon=1e-6 default.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* CrossSection API break (#1707): fix WASM compose/voronoi, doc Simplify default

- Remove 'compose' from crossSectionStaticFunctions in garbage-collector.ts
  (CrossSection.compose no longer exists; wrapping undefined crashed with
  "originalFn is not a function" in WASM TBB:OFF CI lanes)
- Add 'setTolerance' to crossSectionMemberFunctions (returns new CrossSection)
- Update voronoi.mjs to use CrossSection.union() instead of .compose()
- Fix stale @param epsilon / default 1e-6 in manifold-encapsulated-types.d.ts
  (parameter is now 'tolerance', default 0 uses the geometry's own tolerance)
- Add @param doc note to CrossSection::Simplify explaining that default 0
  resolves to tolerance_ (geometry-scale-derived, not a fixed 1e-6)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 21:00:22 +12:00
Zach LoafmanandClaude Opus 4.8 ecc145f477 Add boolean2 and CrossSection regression and corpus tests (#1755)
* Add boolean2 and CrossSection regression and corpus tests

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Add TinyFeatureNearCorner annihilation regression tests

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Add regression tests for the eps-only new-to-old merge

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Drop tolerance from the boolean2 tests

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Add DISABLED_ tiny-feature point-contact host-drop seeds

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Add contracts to boolean2_test validators

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Share the polygon-corpus reader and inline single-use test helpers

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Tighten offset, Simplify, and rhombi-union assertions

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Use linalg helpers instead of hand-rolled vector math

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Note the validator oracle's >eps precondition

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Assert the empty intersection in the disabled host-drop seeds

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Strengthen cross-section area oracle tests

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Guard the offset corpus test under MANIFOLD_NO_IOSTREAM

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Consolidate CrossSection test anonymous namespaces and tidy seed comments

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Move the direct-engine NonFinite test out of cross_section_test

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 19:46:48 +12:00
Zach LoafmanandClaude Opus 4.8 0a68eae6ce Make the intersection-vertex merge eps-only (#1757)
* Fix tiny-feature annihilation in the intersection-vertex merge

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Snap new-to-old intersection verts at eps scale

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Drop tolerance from the arrangement entry points

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Pin nearby-intersection clusters to their old vertex

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Revert "Pin nearby-intersection clusters to their old vertex"

This reverts commit 3e171d165e8492c072a532750bb32c2e5b261e35.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 21:37:45 +12:00
Zach LoafmanandClaude Opus 4.8 72d6137b2e Drop using directives from boolean2.cpp (#1756)
Drop using directives and declarations from boolean2.cpp

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 21:22:13 +12:00
Zach LoafmanandClaude Opus 4.8 9008e157a1 Flatten the boolean2 engine into src/ and consolidate to fewer files (#1754)
* Move boolean2 engine files up to src/; rename to boolean2_* form

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Consolidate the nine boolean2 engine headers into src/boolean2.h

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Merge the boolean2 pipeline and containment sources into their targets

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Update gen_docs, Doxyfile, docs, and format.sh for the flat boolean2 layout

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Restore the module contract comments dropped by the source merges

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Fix Boolean2.md architecture table and two merge leftovers

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 22:27:35 +12:00
Zach LoafmanandClaude Opus 4.8 db042ab153 Cut over CrossSection from Clipper2 to boolean2 (#1751)
* Wire CrossSection to the boolean2 backend

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Simplify CrossSection via vertex decimation, not merge-tolerance snap

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Address wiring review: Hull/finite guards, inline mutex, signpost, asserts

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Migrate CrossSection wiring to ApplyFillRule; Simplify decimates only

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Defer transform tolerance scaling to GetPaths; fix Simplify comment

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Tidy wiring comments to house style; use la::isfinite for guards

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Cut over CrossSection from Clipper2 to boolean2

Replace the Clipper2-backed CrossSection implementation with the in-tree boolean2 one and drop the Clipper2 dependency, the MANIFOLD_CROSS_SECTION_BACKEND selector, and its CI and packaging plumbing. The public CrossSection API is unchanged: the FillRule argument is kept but only Positive construction is supported. Samples.Sponge4's degenerate-triangle bound is raised, since boolean2's Simplify leaves more near-collinear vertices (more benign zero-area tris; topology unchanged).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Delegate single-contour CrossSection ctor to the Polygons one

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Always build CrossSection; drop the MANIFOLD_CROSS_SECTION option

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Flatten manifold::boolean2 into manifold; unify JoinType

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Make SimplifyRing O(n log n) via min-heap with lazy invalidation

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Drop stale MANIFOLD_CROSS_SECTION flag from the PR perf guard script

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Scrub Clipper2 and the removed option from the new sanitizer CI script

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 08:06:37 +12:00
Anshul 324bec7bd7 ASan+UBSan workflow lane Pt2 (#1728)
* Move gtest filter to env var

* extract sanitizer gtest filter into shared script

* set explicit ASAN options for sanitizer test lane

* reproduction on failure

* split sanitizer lane into build/test with core-vs-extended subsets, clang-18 pin, and runtime guardrails

* caches files

* add shared libs on path

* lib path added

* change name "test (core/extended subset)" to "(ASan+UBSan)"

* moved runs to scripts

* rem repro

* readme change

* removed cache as discussed with @elalish in meeting

* testing by removing LD_LIBRARY_PATH

* restore

* only keep find and remove hardcoded paths

* run path use origin cmake args

* removed lib path
2026-06-10 22:41:12 +12:00
Zach LoafmanandClaude Opus 4.8 c463210f74 Fix Windows CI on the windows-2025-vs2026 runner (bump CMake to 4.3.3) (#1752)
* Bump Windows CI to CMake 4.3.3 for the windows-2025-vs2026 runner

windows-2025 migrated to VS 2026 (VS 18 / MSVC 14.51). CMake 4.0.2 has no Visual Studio 18 2026 generator, falls back to NMake Makefiles, and rejects -A x64. That generator was added in CMake 4.2; get-cmake catalogs are version-locked to the CMake they ship, so the action tag moves too.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* TEMP: disable get-cmake cloud cache to confirm CI past the flaky restore

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Revert "TEMP: disable get-cmake cloud cache to confirm CI past the flaky restore"

The cache-off run went green, confirming the CMake 4.3.3 bump fixes Windows on the vs2026 runner. Restoring the default cloud cache.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 22:06:00 +12:00
Zach LoafmanandClaude Opus 4.8 3fe31a1d09 Add boolean2 polygon offset and containment decompose (#1749)
* Add boolean2 polygon offset and containment decompose

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Fold offset and containment decls into boolean2.h; drop tiny headers

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Make fill-rule application machine-eps; drop redundant concave V

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 21:12:29 +12:00
Zach Loafman b27d8a5e26 Add Libc++ EXTENSIVE hardened CI lane (#1737)
* Add libc++ EXTENSIVE hardened CI lane

A clang/libc++ lane built with _LIBCPP_HARDENING_MODE=EXTENSIVE so the
&v[size] class of UB (#1735) traps in CI instead of silently working at
runtime. tbb/clipper2/gtest are built from source under the same libc++
flags (USE_BUILTIN + FETCHCONTENT_SOURCE_DIR) for ABI parity with the
test binary.

* TEMP: revert #1736 to show the hardened lane catches #1735

Reintroduces the &v[size] UB that #1736 fixed so build_libcxx_hardened
goes red and demonstrates it catches the regression. Reverted once CI
red is confirmed.

* Restore #1736 fix (hardened lane confirmed red on the bug)

Reverts the temporary #1736 revert now that build_libcxx_hardened
demonstrated red on the reintroduced &v[size] UB (both TBB legs failed
at the manifold_test step). Back to green on the fixed tree.
2026-06-06 20:51:09 +08:00
Anshul ae2dbdb2fb add non-blocking PR benchmark guard in manifold.yml (base vs head perfTest) (#1680)
* init

* upload benchmark artifacts and clean worktrees

* all warning only and run count mismatch

* completely non-blocking and mean of mean run times compared

* remove redundant if: always()

* changed to per benchmark mean

* standardize perf JSON schema with run index, stdev, n_runs, and metadata

* .

* add raw files to logs and clean yml

* add EOF

* .

* move shell to scripts
2026-06-05 12:11:05 +08:00
Zach LoafmanandClaude Opus 4.7 40cf0bdaf1 Add boolean2 core implementation (#1722)
* Add boolean2 core implementation

Add the experimental Boolean2 core arrangement pipeline under src/cross_section/boolean2 and compile it behind the existing CrossSection backend selector.

The core includes polygon conversion, vertex merge, edge-pair broad phase, intersection insertion, near-vertex splitting, fixed-point cleanup, canonical sub-edge generation, winding-rule filtering, containment, and offset helpers. It also adds docs/Boolean2.md describing the design, relationship to #289, validation boundary, and staged backend plan.

This does not switch production CrossSection behavior to Boolean2. The boolean2 backend translation unit remains placeholder-only on this branch; public CrossSection wiring and behavior-level tests are staged for later PRs in #1707.

* Keep boundary-touching holes in decomposition

* Split repeated-vertex output walks

* Remove unused boolean2 edge-area helper

* Require full-ring containment in decomposition

* Format full-ring containment helper

* Remove unused edge-vertex list builder

* Include owning boolean2 headers in sources

* Remove unreachable edge-pair parallel branch

* Prune unreachable boolean2 helper paths

* Remove stale fused helper fallback

* Split epsilon-width boolean output slits

* Use operation epsilon for output slit splitting

* Name boolean2 tolerance constants

* Name remaining boolean2 heuristic constants

* Document boolean2 heuristic margins

* List boolean2 parallel policy header

* Add Boolean2 debug trace phases

* Stabilize boolean2 geometric tolerances

* Add Boolean2 architecture diagram

* Tighten Boolean2 architecture diagram labels

* Use Markdown labels in Boolean2 architecture diagram

* Set Boolean2 diagram wrapping width

* Balance Boolean2 diagram label wrapping

* Replace Boolean2 architecture diagram with table

* Inline Boolean2 CCW predicate use

* Use halfedge terminology in Boolean2 filter

* Tighten Boolean2 terminology

* Restore Boolean2 API terminology

* Use nonzero union for boolean2 set add

* Classify initial nonzero outer face locally

* Use symbolic graph order for boolean2 intersections

* Use positive winding for boolean2 Add

* Run boolean2 core as a single pass

* Remove boolean2 fixed-point iteration code

* Hide boolean2 diagnostics behind helpers

* Tighten Boolean2 core style

* Address automated code review

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Rename boolean2 remap locals to match inputVert2Merged field

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Drop seedRayCastLeastBiased voting in favor of a single ray cast

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Drop dead perturbRank and inline degenerate Shadows tie-break

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Thread propagated tolerance through to structural_remerge

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Address Boolean2 core review cleanup

* Fix fused narrow scratch references

* Remove stale Boolean2 XOR docs

* Rename Boolean2 combined narrow timing

* Address Boolean2 review follow-ups

* Remove final near-repeat extraction tolerance

* Propagate all new boolean2 intersections

* Clean up Boolean2 core dead paths

* Address Boolean2 review style pass

* Clean remaining Boolean2 casts

* Clarify Boolean2 review follow-up comments

* Use symbolic Boolean2 winding seeds

* Fix symbolic winding seed underflow

* Account for current eps in endpoint snaps

When a freshly computed intersection is compared to an existing endpoint, the endpoint may already carry propagated tolerance and the new point also carries this operation's intersection error. Use tolerance + eps for the new-to-old snap gate so near-endpoint intersections do not leave a T-junction just outside the propagated tolerance budget.

* Clarify Boolean2 review follow-ups

* Make Boolean2 fill handling Positive-only

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-05 11:00:11 +12:00
Robert Kossessa 8ba18a0417 Add Manifold::Contains(vec3) and Manifold::WindingNumber(vec3) (#1746)
* Add Manifold::Contains and Manifold::WindingNumber

Closes #1440.

Implements point-in-mesh containment testing via the Boolean winding
number kernel (Kernel02) already used by the CSG operations, giving
correct results for non-simply-connected shapes and overlapping regions
where simple ray-parity would be wrong.

Core: Manifold::Impl::PointWinding in boolean3.cpp
- Builds a minimal Impl for the query points (vertPos + zero normals)
- Pre-filters with the 3D bounding box - points outside return 0 immediately
- Queries the BVH with vec3 points, which triggers DoesOverlap(vec3) - an
  XY-projected check - so all faces with XY overlap are returned regardless
  of Z, enabling correct +Z winding accumulation above the query point
- Processes all points in a single parallel BVH traversal

Public API (include/manifold/manifold.h, src/manifold.cpp):
- int WindingNumber(vec3 point) const
- std::vector<int> WindingNumber(const std::vector<vec3>& points) const
- bool Contains(vec3 point) const
- std::vector<bool> Contains(const std::vector<vec3>& points) const

Bindings: C (scalar), Python (scalar + batch), WASM (scalar + TypeScript types)

Tests (test/measurement_test.cpp):
- ContainsTallBox: verifies XY-projected collider query (would fail with 3D Box)
- WindingWatertight: no double-counting at shared edges/vertices
- WindingHollowShell: real winding number, not just ray parity
- ContainsBatch: batch results match scalar for all points
- WindingMatchesRayCastParity: cross-checked against existing RayCast

* Fix clang-format violations and nanobind overload_cast in Python bindings

nanobind requires static_cast for overloaded member functions;
nb::overload_cast without return type doesn't resolve ambiguity.
Also wrap long comment lines to satisfy clang-format 20.

* Simplify winding number API
Remove scalar WindingNumber(vec3) and all Contains overloads from C++,
C, Python, and WASM bindings.

Rename `active` to `query2input` in PointWinding.

Plumb the
vector API through WASM instead of the previous single-point calls to
avoid per-point marshalling overhead.

* Fix clang-format-lint errors
2026-06-05 10:37:16 +12:00
pca006132 cc8a7f66d7 v3.5.1 (#1747) v3.5.1 2026-06-04 21:15:28 +08:00
Zach LoafmanandClaude Opus 4.8 5b29f45db9 Route Boolean3::Result timing through the shared phase mechanism (#1745)
* Route Boolean3::Result timing through the shared phase mechanism

Per Emmett's #1742 review: drop the four hand-placed Result Timers
(Assembly/Triangulation/Simplification/Sorting) and report through the
same per-phase timing as LevelSet/FromMesh/Smooth, so all phased ops emit
the one `phase N (file:line) = X ms` format.

The phase() lambda already brackets those spans (the 4 named regions were
just unions of consecutive phase boundaries), so it gains a `line` arg and
records via a new MANIFOLD_RECORD_PHASE_AT - an explicit-line variant, since
a lambda called from 11 sites has only its own body line. Boolean never hits
the static-factory reset that seeds `lastPhase`, so BeginPhaseTiming stamps
the baseline at Result entry.

Left alone: the intersection-stage Timers in the Boolean3 ctor (a P/Q
sub-op breakdown, not sequential phases) and the verts/tris summary.
perfPhases now drives its booleans through an ExecutionContext so the phase
lines print. Progress/cancel semantics and the release build are unchanged
(timing only records with a ctx attached, under MANIFOLD_DEBUG/_TIMING).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Give Boolean3::Result phase timing a per-op local baseline + uid

Addresses the #1745 review: the per-phase timing keyed off the shared
`ctx->lastPhase`, so when a CSG tree evaluates booleans concurrently the
parallel `Boolean3::Result` calls clobbered each other's baseline (garbage
deltas) and their phase lines interleaved with no way to tell them apart.

Move the baseline to a per-`Result` stack local (`LocalPhaseTiming`) and tag
every printed line with a per-boolean uid (`[bN]`) - so concurrent booleans
keep correct deltas and the interleaved output is splittable. The static
factories keep the ctx baseline (single-threaded, unaffected). Debug-only
(verbose >= 2, MANIFOLD_DEBUG/_TIMING); release is unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 21:59:02 +12:00
Zach LoafmanandClaude Opus 4.8 37125da090 Per-phase timing in ADVANCE_PHASE_OR_RETURN; extend perfPhases (#1742)
Per-phase timing via ADVANCE_PHASE_OR_RETURN; extend perfPhases

Bake per-phase wall-clock timing into the phase-boundary macro instead of
hand-placing Timers per op. ADVANCE_PHASE_OR_RETURN now records the elapsed
time since the previous boundary (baseline stamped in ResetForStaticFactory)
via a RecordPhase helper, printing "phase N (file:line) = X ms" under
verbose >= 2. lastPhase lives on ExecutionContext::Impl; everything is behind
MANIFOLD_DEBUG || MANIFOLD_TIMING, so release builds are unchanged.

One change covers every phased op: LevelSet (5), FromMesh ingest (7), and
Smooth (14 = ingest + tangent phases) all time automatically, with no
per-op instrumentation and no risk of drifting out of sync with the cancel/
Progress boundaries. Timing records only when a ctx is attached, so it rides
the same ExecutionContext path as Progress/cancel.

extras/perfPhases: drive LevelSet through an ExecutionContext and add
FromMesh and Smooth sections (BenchFromMesh / BenchSmooth) so the new
coverage is exercised.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 22:13:56 +12:00
Zach LoafmanandClaude Opus 4.8 760d1a04b8 Fix originalID races on shared CSG nodes (#1740) (#1741)
* Add regression tests for originalID races on shared CSG nodes (#1740)

Two ThreadSanitizer-catchable tests, both barrier-synced so all threads
first-touch one shared node at once:
- ConcurrentSharedNodeOriginalIDStable: a transformed leaf realized lazily
  by GetImpl on the const-accessor path.
- ConcurrentSharedOpNodeOriginalIDStable: an unevaluated op node evaluated
  by per-thread Manifold copies (sharing pNode_), hitting ToLeafNode's cache_.

These commit only the tests; both fail without the following fix - under the
TSAN lane they halt on the race, and the functional EXPECT catches the
originalID = -1 collapse.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Fix originalID races on shared CSG nodes (#1740)

Lazily-evaluated CSG nodes are mutated through const accessors:
CsgLeafNode::GetImpl rewrites pImpl_/transform_ to realize a pending
transform, and CsgOpNode::ToLeafNode publishes cache_. A Manifold and its
copies share the underlying node (pNode_) but each gets its own pNodeMutex_,
and GetCsgLeafNode drops that lock before GetImpl/cache_ access - so two
threads touching one shared node (e.g. OriginalID()/NumTri()/GetMeshGL(), or
evaluating a shared op node) corrupt it concurrently. The torn write leaves
triRef.meshIDs absent from meshIDtransform; IncrementMeshIDs collapses them
and GetMeshGL emits originalID = -1. Distinct from #1688 (Compose's
meshIDCounter_ double-read).

The boolean path is unaffected: it copies leaf operands (node->Transform)
before GetImpl, so it never mutates a shared node.

Fix: a per-node mutex guards CsgLeafNode's lazy realize and the const
accessors that read pImpl_/transform_ (GetImpl, Transform, GetBoundingBox,
NumVert, copy ctor). CsgOpNode reads/publishes cache_ under impl_'s existing
guard, including the cancellation path, so concurrent evaluators of a shared
op node converge on one result. Compose's raw field reads are documented as
requiring thread-local (unshared) leaves.

Under TSAN, 8 threads on a shared node go from racing to clean for both the
leaf and op-node paths. Full suite 436/436 pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 21:09:23 +12:00
Zach LoafmanandClaude Opus 4.8 e02471d211 Expose ExecutionContext factories in bindings (C/Python/WASM) (#1743)
The ctx-aware static factories (FromMeshGL / LevelSet / Smooth) had no
binding, so ops with no source Manifold to attach via WithContext - notably
MeshGL ingest - could not be observed or cancelled from the language
bindings. The ExecutionContext object and WithContext were already bound;
this adds the three factories as methods on the context, mirroring the plain
static factories.

- C: manifold_execution_context_{level_set,level_set_seq,of_meshgl,
  of_meshgl64,smooth,smooth64}. The level_set helper is generalized to run
  under an ExecutionContext; the SDF callback user-data is renamed sdf_context
  to keep it distinct from the ExecutionContext argument.
- Python: ExecutionContext.{from_mesh,level_set,smooth} mirroring the
  Manifold static-factory lambdas.
- WASM: ExecutionContext.{fromMesh,smooth,levelSet} (embind helpers in
  helpers.cpp, JS wrappers in bindings.js, .d.ts declarations).

Tests: CBIND.execution_context_factories (C), all_apis.py coverage (Python),
and an ExecutionContext-static-factories case (WASM). All build and pass
locally across the three layers.

Toward #971.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 20:50:45 +12:00
Zach LoafmanandClaude Opus 4.8 df1143e596 Thread ExecutionContext into LevelSet via ExecutionContext::LevelSet (#1739)
Adds ExecutionContext::LevelSet, an eager ctx-aware factory for
Manifold::LevelSet, so a level-set build can report Progress() and honor
Cancel(). Same shape as ExecutionContext::FromMeshGL (#1723) and Smooth
(#1734); continues the #971 eager-ops work.

- Factor the Manifold::LevelSet body into Impl::CreateLevelSet;
  Manifold::LevelSet is now a thin ctx=nullptr wrapper (behavior unchanged).
- 5 progress phases (kPhasesPerLevelSet): voxel SDF sampling, NearSurface,
  ComputeVerts, BuildTris, lumped finalize. The NearSurface hash-table resize
  loop counts once.
- Cancel: entry gate, resize-loop gate, per-chunk cancel through the four grid
  for_each_n loops, and SortGeometry(ctx) in finalize (gated before
  SetNormalsAndCoplanar, whose input must not be partial-on-cancel).

Tests: ExecutionContextLevelSet cluster (HappyPath, MatchesFactory,
CancelBeforeSampling, CancelConcurrent), trimmed to LevelSet-unique paths.

Python + wasm bindings are split into a follow-up.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 11:37:40 +12:00