Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude/skills/deep-review/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ This crate tracks manifold3d upstream. Verify:
- Pinned version in `build.rs` — what version are we on? What's latest? What changed?
- ABI compatibility — have any C API function signatures changed in newer manifold3d releases?
- Deprecated functions — are we binding any C API functions that upstream has deprecated?
- **wasm32-unknown-unknown cfg-gates may need revisiting.** Grep for `target_os = "unknown"` across the workspace. Each gated FFI declaration / safe wrapper / test is gated because that C API surface postdates the shim's tested manifold pin. When the shim's tested pin moves to (or past) our host pin, those gates can be removed. Currently gated: `manifold_ray_*` (ray casting). The OBJ I/O gates are gated for a different reason (iostream patches strip them) and stay regardless.
- **wasm32-unknown-unknown cfg-gates may need revisiting.** Grep for `target_os = "unknown"` across the workspace. Each gated FFI declaration / safe wrapper / test is gated because that C API surface postdates the shim's tested manifold pin. When the shim's tested pin moves to (or past) our host pin, those gates can be removed. The OBJ I/O gates (`manifold_*_obj`) are gated for a different reason (iostream patches strip them) and stay regardless.
- New capabilities — has upstream added significant new C API surface (new types, new operations) that we're missing?
- Build system changes — has manifold3d changed its CMake structure, added/removed FetchContent deps, changed library names?
- Known upstream bugs — check manifold3d issues for bugs affecting functions we bind (especially boolean operations, MeshGL64, CrossSection offset)
Expand Down
1 change: 1 addition & 0 deletions .claude/skills/update-upstream/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ git checkout -b update-upstream-<ref>
- Otherwise, query upstream master HEAD: `gh api repos/elalish/manifold/git/refs/heads/master --jq '.object.sha'`
- Show the user: current pin (from `MANIFOLD_VERSION` in `crates/manifold-csg-sys/build.rs`), the target, and a one-line commit summary of what's between them: `gh api repos/elalish/manifold/compare/<current>...<target> --jq '{ahead_by: .ahead_by, commits: [.commits[] | {sha: .sha[0:8], message: (.commit.message | split("\n")[0])}]}'`
- Confirm before proceeding.
- **Check shim coverage.** Before committing to the target, verify `wasm-cxx-shim` supports it. Look at the latest shim release's `MANIFOLD_GIT_TAG` default (in the helper at `cmake/WasmCxxShimManifold.cmake`, or in the release notes). If the target SHA is past the shim's tested pin, flag it: either pick a target the shim already supports, or be ready to cfg-gate any new C API on `not(all(target_arch = "wasm32", target_os = "unknown"))` (the wasm-uu lane will fall back to the shim's tested pin via `wasm_cxx_shim_add_manifold()`'s default). Mismatch without gating produces wasm-uu link failures — see CLAUDE.md "Versioning" section.

### 3. Update the pin

Expand Down
3 changes: 2 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ The sys crate clones manifold3d (pinned to a specific commit on master, post-v3.
- **`manifold-csg-sys`** uses version `{major}.{minor}.{patch}` where major.minor tracks the upstream manifold3d version and patch >= 100 is our release number. For example, `3.4.100` tracks manifold3d v3.4.1, and `3.4.101` would be our next release against the same upstream. Patch bumps (e.g., `3.4.101` → `3.4.102`) must be semver-compatible: no removed or changed function signatures, only additions.
- **`manifold-csg`** uses standard semver (`0.1.0`, etc.) independent of the upstream version. Its `Cargo.toml` pins the sys crate version it depends on.
- When bumping the manifold3d pin in `build.rs`, the sys crate version must be updated to match (e.g., manifold3d v3.5.0 -> sys crate `3.5.100`).
- **Before bumping `MANIFOLD_VERSION`, check that `wasm-cxx-shim` supports the new pin.** The shim's helper (`wasm_cxx_shim_add_manifold()`) ships carry-patches generated against a specific manifold commit; pins past that point may not patch cleanly, and FFI declarations for any C API added in the gap will produce wasm-uu link failures. Two paths if the shim hasn't caught up: (1) wait for a shim release that pins past your target SHA, or (2) cfg-gate the new FFI surface on `not(all(target_arch = "wasm32", target_os = "unknown"))` so the wasm-uu lane stays on the shim's tested pin. The "Pin / shim follow-ups" section below covers the post-bump cleanups for path (2).
- The sys crate pins upstream to a specific commit SHA rather than a tag. This is necessary because upstream doesn't follow strict semver — minor releases can include breaking changes. Pinning to a commit gives us the same reproducibility as a tag, while allowing us to pick up post-release fixes and carry-patches between releases.
- **Version bumps**: feature PRs may (and usually should) include their own version bump so the merge is ready to publish. The `/publish` skill will bump at publish time only if no PR has done so since the last release. Note that `cargo-semver-checks` CI requires a bump whenever the PR changes the public API in a way the current version doesn't allow (e.g., a breaking change requires a minor bump pre-1.0).
- **Facade crates** (`manifold3d`, `manifold3d-sys`) always ship in lockstep with their canonical counterparts (`manifold-csg`, `manifold-csg-sys`) via `=` version pins. Bumping the canonical means bumping the facade.
Expand Down Expand Up @@ -48,7 +49,7 @@ The sys crate clones manifold3d (pinned to a specific commit on master, post-v3.

Things to revisit whenever the manifold pin moves OR `wasm-cxx-shim` cuts a new release:

- **Re-evaluate wasm32-unknown-unknown cfg-gates.** Any FFI declaration / safe wrapper / test gated on `not(all(target_arch = "wasm32", target_os = "unknown"))` exists because that surface postdates the shim's tested manifold pin. When the shim's tested pin moves up to (or past) our host pin, those gates can be dropped and the surface unified across targets. Current gated surface: `manifold_ray_*` (ray casting), `manifold_*_obj` (OBJ I/O — gated for a different reason: iostream patches strip it). Grep for `target_os = "unknown"` to enumerate.
- **Re-evaluate wasm32-unknown-unknown cfg-gates.** Any FFI declaration / safe wrapper / test gated on `not(all(target_arch = "wasm32", target_os = "unknown"))` exists because that surface postdates the shim's tested manifold pin. When the shim's tested pin moves up to (or past) our host pin, those gates can be dropped and the surface unified across targets. Current gated surface: `manifold_*_obj` (OBJ I/O — gated for a different reason: iostream patches strip it; this stays regardless). Ray casting is **no longer gated** as of shim v0.4.0-alpha.1, whose tested pin matches our host pin. Grep for `target_os = "unknown"` to enumerate.
- **Re-evaluate carry-patches.** For each patch in `crates/manifold-csg-sys/patches/` (if any), check whether it's merged upstream and included in the new pin; if so, delete it.

## Carry-patches
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ members = [
resolver = "2"

[workspace.package]
version = "0.1.7"
version = "0.1.8"
edition = "2024"
license = "Apache-2.0 OR MIT"
repository = "https://github.com/zmerlynn/manifold-csg"
Expand Down
2 changes: 1 addition & 1 deletion crates/manifold-csg-sys/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "manifold-csg-sys"
description = "Raw FFI bindings to the manifold3d C API for constructive solid geometry"
version = "3.4.106"
version = "3.4.107"
edition.workspace = true
license.workspace = true
repository.workspace = true
Expand Down
21 changes: 13 additions & 8 deletions crates/manifold-csg-sys/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,11 @@ fn main() {
// build.rs instead of a cmake file).

const WASM_CXX_SHIM_GIT: &str = "https://github.com/zmerlynn/wasm-cxx-shim.git";
const WASM_CXX_SHIM_TAG: &str = "v0.3.0";
// v0.4.0-alpha.1 pins manifold to the same commit as our host build
// (`5f95a3ac`), so the wasm-uu lane sees the same C API surface (including
// ray_cast). Will move to non-alpha v0.4.0 once upstream manifold#1690
// (the iostream gating PR the alpha vendors as a carry-patch) merges.
const WASM_CXX_SHIM_TAG: &str = "v0.4.0-alpha.1+5f95a3ac";

fn build_wasm_unknown_unknown() {
println!("cargo:rerun-if-changed=build.rs");
Expand Down Expand Up @@ -550,13 +554,14 @@ fn build_wasm_unknown_unknown() {

// ---- Stage 2: build manifold + Clipper2 via the shim's helper -------
//
// wasm-cxx-shim v0.3.0 ships `wasm_cxx_shim_add_manifold()`, which
// owns the high-change-rate parts of the integration cocktail:
// FetchContent of manifold + Clipper2 (with tested-pin defaults, the
// three carry-patches, and manifold/Clipper2 CMake options). Our
// wrapper at wasm32-uu/CMakeLists.txt sets up the consumer-side
// `-isystem` chain (libc++ headers + our `__config_site` override +
// the `<mutex>` stub) and calls the helper.
// The shim's `wasm_cxx_shim_add_manifold()` helper owns the high-
// change-rate parts of the integration cocktail: FetchContent of
// manifold + Clipper2 (with tested-pin defaults, carry-patches, and
// manifold/Clipper2 CMake options). Our wrapper at
// wasm32-uu/CMakeLists.txt sets up the consumer-side `-isystem`
// chain (libc++ headers + our `__config_site` override + the
// `<mutex>` stub) and calls the helper. See WASM_CXX_SHIM_TAG above
// for the pinned version.

let manifold_build = out_dir.join("manifold-build-wasm32-uu");

Expand Down
18 changes: 0 additions & 18 deletions crates/manifold-csg-sys/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,6 @@ pub struct ManifoldSimplePolygon {
}

/// Opaque handle to a manifold3d `RayHitVec` (vector of ray hit results).
///
/// Unavailable on `wasm32-unknown-unknown` — that lane builds against the
/// shim's tested manifold pin (v3.4.1), which predates ray casting.
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
#[repr(C)]
#[derive(Debug)]
pub struct ManifoldRayHitVec {
Expand Down Expand Up @@ -163,10 +159,6 @@ pub struct ManifoldProperties {
}

/// Result of a ray-manifold intersection test.
///
/// Unavailable on `wasm32-unknown-unknown` — see [`ManifoldRayHitVec`] for
/// the rationale.
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct ManifoldRayHit {
Expand Down Expand Up @@ -276,7 +268,6 @@ unsafe extern "C" {
pub fn manifold_box_size() -> usize;
pub fn manifold_rect_size() -> usize;
pub fn manifold_triangulation_size() -> usize;
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
pub fn manifold_ray_hit_vec_size() -> usize;

// ── Allocation ─────────────────────────────────────────────────────
Expand All @@ -292,7 +283,6 @@ unsafe extern "C" {
pub fn manifold_alloc_box() -> *mut ManifoldBox;
pub fn manifold_alloc_rect() -> *mut ManifoldRect;
pub fn manifold_alloc_triangulation() -> *mut ManifoldTriangulation;
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
pub fn manifold_alloc_ray_hit_vec() -> *mut ManifoldRayHitVec;

// ── Destruction (destruct only, does not free) ─────────────────────
Expand All @@ -308,7 +298,6 @@ unsafe extern "C" {
pub fn manifold_destruct_box(b: *mut ManifoldBox);
pub fn manifold_destruct_rect(b: *mut ManifoldRect);
pub fn manifold_destruct_triangulation(m: *mut ManifoldTriangulation);
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
pub fn manifold_destruct_ray_hit_vec(v: *mut ManifoldRayHitVec);

// ── Deletion (destruct + free) ─────────────────────────────────────
Expand All @@ -324,7 +313,6 @@ unsafe extern "C" {
pub fn manifold_delete_box(b: *mut ManifoldBox);
pub fn manifold_delete_rect(b: *mut ManifoldRect);
pub fn manifold_delete_triangulation(m: *mut ManifoldTriangulation);
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
pub fn manifold_delete_ray_hit_vec(v: *mut ManifoldRayHitVec);

// ── Polygons ───────────────────────────────────────────────────────
Expand Down Expand Up @@ -1072,12 +1060,8 @@ unsafe extern "C" {
) -> *mut ManifoldManifold;

// ── Ray casting ─────────────────────────────────────────────────────
//
// Postdates the manifold v3.4.1 pin the wasm32-uu lane builds against,
// so the whole ray-cast surface is cfg-elided on that target.

/// Cast a ray from `origin` to `end` against a manifold, returning all hits.
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
pub fn manifold_ray_cast(
mem: *mut ManifoldRayHitVec,
m: *const ManifoldManifold,
Expand All @@ -1090,11 +1074,9 @@ unsafe extern "C" {
) -> *mut ManifoldRayHitVec;

/// Number of hits in a ray hit vector.
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
pub fn manifold_ray_hit_vec_length(v: *const ManifoldRayHitVec) -> usize;

/// Get a hit by index from a ray hit vector.
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
pub fn manifold_ray_hit_vec_get(v: *const ManifoldRayHitVec, idx: usize) -> ManifoldRayHit;

// ── Bounding box ────────────────────────────────────────────────────
Expand Down
11 changes: 6 additions & 5 deletions crates/manifold-csg-sys/wasm32-uu/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,12 @@ add_compile_options(
)

# FetchContent + patches + manifold/Clipper2 options + base compile flags.
# Uses the helper's tested-pin defaults (manifold v3.4.1, Clipper2 matching
# SHA). The host build pins newer SHAs to pick up post-3.4.1 fixes; FFI
# declarations and safe wrappers for any C API surface that postdates v3.4.1
# (e.g., manifold_ray_cast) are cfg-gated off on this target so the wasm
# produced here has no unresolved imports against the helper's pin.
# Uses the helper's tested-pin default. As of shim v0.4.0-alpha.1 the
# helper's default pin matches our host pin (`5f95a3ac`), so the wasm-uu
# lane sees the same C API surface as the host build (including ray_cast).
# When the host pin moves past the helper's tested pin again, either
# override via `MANIFOLD_GIT_TAG` here or cfg-gate the new surface on
# this target — see the "Pin / shim follow-ups" section in CLAUDE.md.
wasm_cxx_shim_add_manifold()

# Force the manifold + Clipper2 archives to build by default. Without
Expand Down
2 changes: 1 addition & 1 deletion crates/manifold-csg/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ nalgebra = ["dep:nalgebra"]
unstable-wasm-uu = ["manifold-csg-sys/unstable-wasm-uu"]

[dependencies]
manifold-csg-sys = { path = "../manifold-csg-sys", version = "3.4.106", default-features = false }
manifold-csg-sys = { path = "../manifold-csg-sys", version = "3.4.107", default-features = false }
thiserror = "2"
log = "0.4"
nalgebra = { version = "0.34", optional = true }
Expand Down
2 changes: 0 additions & 2 deletions crates/manifold-csg/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ pub mod bounding_box;
pub mod cross_section;
pub mod manifold;
pub mod mesh;
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
pub mod ray;
pub mod rect;
pub mod triangulation;
Expand All @@ -38,7 +37,6 @@ pub use manifold::{
};
pub use manifold_csg_sys::ManifoldOpType as OpType;
pub use mesh::{MeshGL, MeshGL64};
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
pub use ray::RayHit;
pub use rect::Rect;
pub use triangulation::triangulate_polygons;
Expand Down
4 changes: 0 additions & 4 deletions crates/manifold-csg/src/manifold.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1231,10 +1231,6 @@ impl Manifold {
/// The ray goes from `origin` to `end`. Returns a list of [`RayHit`](crate::RayHit)
/// results, each containing the face ID, distance, hit position, and
/// surface normal.
///
/// Unavailable on `wasm32-unknown-unknown` — that lane builds against the
/// shim's tested manifold pin (v3.4.1), which predates ray casting.
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
#[must_use]
pub fn ray_cast(&self, origin: [f64; 3], end: [f64; 3]) -> Vec<crate::RayHit> {
// SAFETY: manifold_alloc_ray_hit_vec returns a valid handle.
Expand Down
6 changes: 0 additions & 6 deletions crates/manifold-csg/tests/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -799,11 +799,7 @@ fn min_gap_between_separated_cubes() {
}

// ── Ray casting tests ──────────────────────────────────────────────
//
// `Manifold::ray_cast` is unavailable on `wasm32-unknown-unknown` (the shim's
// tested manifold pin predates ray casting), so these tests are gated off.

#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
#[test]
fn ray_cast_through_cube() {
// Cube from [0,0,0] to [10,10,10].
Expand All @@ -819,7 +815,6 @@ fn ray_cast_through_cube() {
assert_relative_eq!(distances[1], 10.0, epsilon = 0.1);
}

#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
#[test]
fn ray_cast_miss() {
let cube = Manifold::cube(10.0, 10.0, 10.0, true);
Expand All @@ -828,7 +823,6 @@ fn ray_cast_miss() {
assert!(hits.is_empty());
}

#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
#[test]
fn ray_cast_hit_has_normal() {
let cube = Manifold::cube(10.0, 10.0, 10.0, false);
Expand Down
4 changes: 2 additions & 2 deletions crates/manifold3d-sys/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "manifold3d-sys"
description = "Raw FFI bindings to the manifold3d C API (facade crate — re-exports manifold-csg-sys)"
version = "3.4.106"
version = "3.4.107"
edition.workspace = true
license.workspace = true
repository.workspace = true
Expand All @@ -15,7 +15,7 @@ parallel = ["manifold-csg-sys/parallel"]
unstable-wasm-uu = ["manifold-csg-sys/unstable-wasm-uu"]

[dependencies]
manifold-csg-sys = { path = "../manifold-csg-sys", version = "=3.4.106", default-features = false }
manifold-csg-sys = { path = "../manifold-csg-sys", version = "=3.4.107", default-features = false }

[package.metadata.docs.rs]
features = ["parallel"]
Expand Down
2 changes: 1 addition & 1 deletion crates/manifold3d/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ nalgebra = ["manifold-csg/nalgebra"]
unstable-wasm-uu = ["manifold-csg/unstable-wasm-uu"]

[dependencies]
manifold-csg = { path = "../manifold-csg", version = "=0.1.7", default-features = false }
manifold-csg = { path = "../manifold-csg", version = "=0.1.8", default-features = false }

[package.metadata.docs.rs]
features = ["nalgebra", "parallel"]
Expand Down
8 changes: 4 additions & 4 deletions docs/plans/wasm-unknown-unknown.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ ecosystem (Bevy, Leptos, Yew, etc.). Supporting `wasm32-unknown-unknown`
directly unblocks those consumers.

The C++ runtime gap (`wasm32-unknown-unknown` ships no libc, no libcxx,
no libcxxabi) is filled by `wasm-cxx-shim` (pinned to v0.3.0) — a small,
no libcxxabi) is filled by `wasm-cxx-shim` (pinned to v0.4.0-alpha.1) — a small,
independently-maintained library providing exactly the C/C++ runtime
subset that manifold3d (and similar C++-via-Rust crates) need. See the
shim's [`docs/context.md`](https://github.com/zmerlynn/wasm-cxx-shim/blob/main/docs/context.md) for the design background.

Since v0.3.0 the shim ships a `wasm_cxx_shim_add_manifold()` CMake helper
Since v0.4.0-alpha.1 the shim ships a `wasm_cxx_shim_add_manifold()` CMake helper
that owns the high-change-rate parts of the integration cocktail
(FetchContent of manifold + Clipper2 with tested-pin defaults, the three
carry-patches, and manifold/Clipper2 CMake options). We invoke it from a
Expand Down Expand Up @@ -59,7 +59,7 @@ A separate `build_wasm_unknown_unknown()` function dispatched early when
Steps:

1. Sanity-check `cmake` and `clang` on PATH.
2. Clone `wasm-cxx-shim` (pinned to `v0.3.0`) into `OUT_DIR` and build its
2. Clone `wasm-cxx-shim` (pinned to `v0.4.0-alpha.1`) into `OUT_DIR` and build its
three components (libc, libm, libcxx) via cmake using the shim's own
wasm32 toolchain file.
3. cmake-configure + build manifold + Clipper2 via our wrapper at
Expand Down Expand Up @@ -210,7 +210,7 @@ mention:

- New target supported: `wasm32-unknown-unknown` (bare-wasm browser
target compatible with `wasm-bindgen`).
- Build dependency: `wasm-cxx-shim` v0.3.0 (cloned via build.rs into
- Build dependency: `wasm-cxx-shim` v0.4.0-alpha.1 (cloned via build.rs into
`OUT_DIR`; no Cargo dependency). The integration uses the shim's
`wasm_cxx_shim_add_manifold()` CMake helper, so manifold/Clipper2
pins, patches, and CMake options live upstream now.
Expand Down
Loading