feat(wasm): compile the library for wasm32 (issue #45 groundwork)

First step toward a web build: the library now compiles for
wasm32-unknown-unknown with --no-default-features.

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

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

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Hakan Seven 2026-06-16 13:01:19 +03:00
commit b2e20c95fe
12 changed files with 123 additions and 20 deletions

View file

@ -1,5 +1,21 @@
pub mod acad_to_truck;
#[cfg(feature = "solid3d")]
pub mod acis_to_truck;
/// Without `solid3d` (e.g. wasm) ACIS/SAT meshing is unavailable; the entry
/// point stays so callers compile, returning no mesh.
#[cfg(not(feature = "solid3d"))]
pub mod acis_to_truck {
use crate::scene::model::mesh_model::MeshLodSet;
use acadrust::entities::acis::SatDocument;
pub fn tessellate_sat_truck(
_sat: &SatDocument,
_name: String,
_color: [f32; 4],
_facet_res: f64,
) -> Option<MeshLodSet> {
None
}
}
pub mod truck_tess;
pub mod tessellate;
pub mod tess_util;

View file

@ -8,9 +8,11 @@
// The caller (acad_to_truck / tessellate) decides which function to call based on
// the truck topology level of the object.
#[cfg(feature = "solid3d")]
use truck_meshalgo::tessellation::{MeshableShape, MeshedShape};
use truck_modeling::base::{BoundedCurve, ParameterDivision1D};
use truck_modeling::{Edge, Shell, Solid, Vertex, Wire};
#[cfg(feature = "solid3d")]
use truck_polymesh::PolygonMesh;
@ -140,21 +142,45 @@ pub fn tessellate_wire(w: &Wire, offset: [f64; 3]) -> TruckTessResult {
// ── Shell ─────────────────────────────────────────────────────────────────
#[allow(dead_code)]
#[cfg(feature = "solid3d")]
pub fn tessellate_shell(s: &Shell, offset: [f64; 3]) -> TruckTessResult {
let meshed = s.triangulation(MESH_TOL);
polygon_to_result(meshed.to_polygon(), offset)
}
/// Without `solid3d` (e.g. wasm) there is no mesher; a shell yields no mesh.
#[cfg(not(feature = "solid3d"))]
pub fn tessellate_shell(_s: &Shell, _offset: [f64; 3]) -> TruckTessResult {
TruckTessResult::Mesh {
verts: Vec::new(),
normals: Vec::new(),
indices: Vec::new(),
}
}
// ── Solid ─────────────────────────────────────────────────────────────────
#[allow(dead_code)]
#[cfg(feature = "solid3d")]
pub fn tessellate_solid(s: &Solid, offset: [f64; 3]) -> TruckTessResult {
let meshed = s.triangulation(MESH_TOL);
polygon_to_result(meshed.to_polygon(), offset)
}
/// Without `solid3d` (e.g. wasm) there is no mesher; a solid yields no mesh.
#[allow(dead_code)]
#[cfg(not(feature = "solid3d"))]
pub fn tessellate_solid(_s: &Solid, _offset: [f64; 3]) -> TruckTessResult {
TruckTessResult::Mesh {
verts: Vec::new(),
normals: Vec::new(),
indices: Vec::new(),
}
}
// ── Internal ─────────────────────────────────────────────────────────────
#[cfg(feature = "solid3d")]
fn polygon_to_result(mesh: PolygonMesh, offset: [f64; 3]) -> TruckTessResult {
let verts: Vec<[f32; 3]> = mesh
.positions()

View file

@ -130,10 +130,12 @@ pub enum Bool {
Intersect,
}
#[cfg(feature = "solid3d")]
const BOOL_TOL: f64 = 0.05;
/// Combine two solids. `Subtract` removes `b` from `a`. Returns `None` when the
/// operation fails (e.g. the solids don't actually overlap).
#[cfg(feature = "solid3d")]
pub fn boolean(op: Bool, a: &Solid, b: &Solid) -> Option<Solid> {
match op {
Bool::Union => truck_shapeops::or(a, b, BOOL_TOL),
@ -146,6 +148,12 @@ pub fn boolean(op: Bool, a: &Solid, b: &Solid) -> Option<Solid> {
}
}
/// Without `solid3d` (e.g. wasm) there is no boolean kernel.
#[cfg(not(feature = "solid3d"))]
pub fn boolean(_op: Bool, _a: &Solid, _b: &Solid) -> Option<Solid> {
None
}
// ── Tessellation ────────────────────────────────────────────────────────────
/// Tessellate a truck `Solid` into a single-LOD `MeshLodSet` (world-space,