Skip to content
Closed
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ node_modules/
# JS/TS build artifacts
dist/
main/opengeometry-three/examples-dist/
main/opengeometry-three/examples-vite/.generated/
main/opengeometry/pkg/

.DS_Store
Expand Down
103 changes: 103 additions & 0 deletions AI-DOCS/2026-03-11-sweep-joint-robustness-plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# Sweep Joint Robustness Plan

## Context

In `sweep_hilbert_profiles`, the profile remains correct in isolation, but the swept solid looks squeezed/distorted around path joints (especially around sharp turns). This is a known behavior when a sweep is built from only one section per polyline vertex and then connected linearly.

## Why this happens in the current implementation

Current sweep generation in `main/opengeometry/src/operations/sweep.rs`:

- Builds one frame per path vertex (`build_path_frames`), with a bisector tangent at interior corners.
- Places one profile ring at each path vertex.
- Connects adjacent rings with side quads.

This creates two geometric problems at corners:

1. **Corner compression ("squeeze")**: the same profile is reused at a corner where the local effective offset distance should be miter-adjusted.
2. **Asymmetric twist near joints**: frame propagation can remain numerically stable but still rotate the section in ways that look visually uneven when the path has abrupt directional changes.

## Goal

Make sweep output robust and predictable at joints while preserving backward compatibility by default.

## Proposed delivery plan

### Phase 1 — Add measurable quality gates (no behavior change)

1. Add sweep-quality tests that measure cross-section distortion around joints:
- Width/depth preservation for rectangular profiles on L-turn and zig-zag paths.
- Corner stress cases (e.g., 30°, 60°, 90°, 135°) with unequal segment lengths.
2. Add fixture-style helper(s) to compare local section extents against expected profile extents.
3. Keep current behavior as baseline; tests should initially characterize current error bands.

**Outcome:** objective pass/fail metrics before algorithm changes.

### Phase 2 — Corner-aware section generation

1. Treat each path corner as a join operation instead of a single blended frame sample.
2. Generate either:
- **dual rings at corners** (end of incoming segment + start of outgoing segment), or
- **explicit join patch** using miter/bevel/round strategy.
3. For miter joins, compute a bounded miter scale from turn angle and clamp with a configurable miter limit to avoid spikes/self-intersections.

**Outcome:** removes squeezed appearance at joints and produces deterministic geometry.

### Phase 3 — Frame robustness improvements

1. Keep parallel transport as base but add:
- Explicit handling for near-180° turns.
- Optional path-level twist minimization for closed loops.
2. Add continuity checks on normal/binormal flips between neighboring frames.

**Outcome:** reduced visual wobble and fewer frame discontinuities.

### Phase 4 — API + migration strategy

1. Introduce non-breaking sweep options (with defaults matching old behavior):
- `join_style: miter | bevel | round`
- `miter_limit: f64`
- `corner_subdivisions: usize` (for round joins)
2. Keep legacy mode as default for one release cycle; expose robust mode behind explicit options first.

**Outcome:** safe rollout with backward compatibility.

### Phase 5 — Validation + rollout

1. Regression tests:
- Topology expectations (faces/edges/vertices) across join styles.
- Geometric invariants (section size tolerance, no NaN/degenerate faces).
2. Visual verification cases in sandbox/examples:
- Hilbert path, sharp polyline turns, closed loops.
3. Document recommended defaults for user-facing UIs.

**Outcome:** production confidence and reduced support churn.

## Implementation notes (important)

- Prioritize **corner-aware ring/join generation** first; this is the primary fix for squeezed joints.
- Avoid silent behavior changes in existing call paths.
- Keep caps behavior independent of join strategy.
- Ensure robust fallbacks when a join becomes numerically degenerate.

## Local verification checklist (for implementation phase)

From `main/opengeometry/`:

- `cargo fmt`
- `cargo check`
- `cargo test operations::sweep -- --nocapture`

Add visual checks in sandbox after algorithm rollout.

## Backward-compatibility notes

- No immediate API break required.
- New options can be additive.
- Existing default sweep behavior can be preserved until robust mode is proven and promoted.

## Known caveats

- Round joins and high corner subdivision can increase face count significantly.
- Miter joins require strict limits to avoid long spikes in acute angles.
- Closed-loop orientation stabilization may require additional loop-wide correction.
99 changes: 99 additions & 0 deletions AI-DOCs/opengeometry/2026-03-04-halfedge-brep-migration-handoff.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Half-Edge BREP Migration Handoff

## What Changed

- Replaced the BREP core model with a half-edge-first topology schema.
- Added canonical topology entities:
- `Vertex { outgoing_halfedge }`
- `HalfEdge`
- `Edge { halfedge, twin_halfedge }`
- `Loop`
- `Face { outer_loop, inner_loops, shell_ref }`
- `Wire`
- `Shell`
- Added `BrepBuilder` for deterministic topology construction.
- Added `Brep::validate_topology() -> Result<(), BrepError>` with manifold and linkage checks.
- Reworked projection/HLR to derive visibility from half-edge adjacency.
- Migrated primitives and sweep/extrude operations to builder-based half-edge output.
- Updated wasm mutating methods to checked APIs (`Result<(), JsValue>`) for invalid topology/input states.
- Updated three integration (`shapes/sphere.ts`) to consume outline buffers from kernel instead of legacy edge endpoint fields.

## Breaking Changes

- `get_brep_serialized()` now emits the new half-edge schema.
- Removed legacy fields from serialized BREP:
- `edges[].v1`
- `edges[].v2`
- `faces[].face_indices`
- root `holes`
- root `hole_edges`
- Mutating methods such as `set_config` / `generate_geometry` now return `Result<(), JsValue>` at wasm boundaries.

## Legacy Cleanup

- Removed:
- `main/opengeometry/src/utility/geometry.rs`
- `main/opengeometry/src/primitives/cylinderOld.rs`
- Removed stale commented legacy BREP scaffolding from `main/opengeometry/src/lib.rs`.
- Replaced old extrude geometry dependency with local `Geometry` in `operations/extrude.rs`.

## Implementation Notes

- `BrepBuilder` enforces:
- canonical undirected edge mapping
- twin linking of opposite directed halfedges
- non-manifold rejection (`> 2` halfedges per edge)
- loop closure and link consistency
- Face triangulation inputs now come from `outer_loop` and `inner_loops` via `Brep::get_vertices_and_holes_by_face_id`.
- Wire primitives (`line`, `curve`, `polyline`, `arc`) are represented as `wires` and edge-backed halfedges.
- Solid primitives (`cuboid`, `cylinder`, `wedge`, `sphere`, `sweep`) assign shell topology.

## Files Added

- `main/opengeometry/src/brep/error.rs`
- `main/opengeometry/src/brep/builder.rs`
- `main/opengeometry/src/brep/loop.rs`
- `main/opengeometry/src/brep/wire.rs`
- `main/opengeometry/src/brep/shell.rs`

## Files Reworked (Core)

- `main/opengeometry/src/brep/mod.rs`
- `main/opengeometry/src/brep/vertex.rs`
- `main/opengeometry/src/brep/halfedge.rs`
- `main/opengeometry/src/brep/edge.rs`
- `main/opengeometry/src/brep/face.rs`
- `main/opengeometry/src/export/projection.rs`
- `main/opengeometry/src/operations/extrude.rs`
- `main/opengeometry/src/operations/sweep.rs`

## Files Reworked (Primitives)

- `main/opengeometry/src/primitives/line.rs`
- `main/opengeometry/src/primitives/curve.rs`
- `main/opengeometry/src/primitives/polyline.rs`
- `main/opengeometry/src/primitives/arc.rs`
- `main/opengeometry/src/primitives/rectangle.rs`
- `main/opengeometry/src/primitives/polygon.rs`
- `main/opengeometry/src/primitives/cuboid.rs`
- `main/opengeometry/src/primitives/cylinder.rs`
- `main/opengeometry/src/primitives/wedge.rs`
- `main/opengeometry/src/primitives/sphere.rs`
- `main/opengeometry/src/primitives/sweep.rs`

## Validation Run

- `cargo fmt --manifest-path main/opengeometry/Cargo.toml`
- `cargo check --manifest-path main/opengeometry/Cargo.toml`
- `cargo test --manifest-path main/opengeometry/Cargo.toml`
- `cargo test --examples --manifest-path main/opengeometry/Cargo.toml`
- `npm run build-three`

All commands above completed successfully.

## Known Caveats / Follow-ups

- Existing non-critical warnings still present in unrelated legacy code:
- `operations/windingsort.rs` (`ccw_and_flag` naming)
- `geometry/triangle.rs` (`crso` unused variable)
- Consumers that deserialize old BREP JSON must migrate to the new half-edge schema.
106 changes: 106 additions & 0 deletions AI-DOCs/opengeometry/2026-03-04-stl-export-handoff.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# STL Export Handoff (Binary STL + Three.js Validation)

## What Changed

- Added binary STL export support in the Rust core:
- `main/opengeometry/src/export/stl.rs`
- best-effort and strict policies
- single-BREP and multi-BREP export APIs
- native file output helpers (`cfg(not(target_arch = "wasm32"))`)
- Wired STL export through `scenegraph`:
- `exportBrepToStl(...)`
- `exportSceneToStl(...)`
- `exportCurrentSceneToStl(...)`
- wasm return payload includes bytes + report metadata (`OGStlExportResult`)
- Hardened incoming serialized BREP handling:
- `addBrepEntityToScene(...)` now validates topology before storing.
- Fixed stale half-edge usage in native sandbox:
- `main/opengeometry/sandbox-native/src/main.rs` now resolves endpoints via `get_edge_endpoints(...)`.
- Added Three.js validation examples:
- Legacy page: `main/opengeometry-three/examples/stl-export.html`
- Vite page: `main/opengeometry-three/examples-vite/operations/stl-export.html`
- Vite script: `main/opengeometry-three/examples-vite/src/pages/operations-stl-export.ts`
- Vite specs index updated to include STL export card.
- Vite STL page now includes a shape dropdown (`Cuboid`, `Cylinder`, `Sphere`, `Wedge`) with per-shape parameter groups and shape-specific STL filenames (`opengeometry-<shape>.stl`).
- Vite STL controls were consolidated into a single panel to prevent control container overlap.
- Exposed STL-related wasm types from package entry:
- `main/opengeometry-three/index.ts` now re-exports `OGSceneManager` and `OGStlExportResult`.

## API Summary

- Rust core:
- `export_brep_to_stl_bytes(&Brep, &StlExportConfig) -> Result<(Vec<u8>, StlExportReport), StlExportError>`
- `export_breps_to_stl_bytes(...) -> Result<(Vec<u8>, StlExportReport), StlExportError>`
- `export_brep_to_stl_file(...)` and `export_breps_to_stl_file(...)` for native file output
- Wasm scenegraph:
- `OGSceneManager.exportBrepToStl(brep_serialized, config_json?)`
- `OGSceneManager.exportSceneToStl(scene_id, config_json?)`
- `OGSceneManager.exportCurrentSceneToStl(config_json?)`
- return type: `OGStlExportResult` with:
- `bytes: Uint8Array`
- `reportJson: string`

## Config Defaults

- Binary STL only.
- Default policy is best-effort.
- Units are preserved as-is (`scale: 1.0` by default).
- Topology validation is enabled by default (`validate_topology: true`).

## How To Test Locally

### Rust quality gates

```bash
cargo fmt --manifest-path main/opengeometry/Cargo.toml
cargo check --manifest-path main/opengeometry/Cargo.toml
cargo test --manifest-path main/opengeometry/Cargo.toml
cargo test --examples --manifest-path main/opengeometry/Cargo.toml
cargo check --target wasm32-unknown-unknown --manifest-path main/opengeometry/Cargo.toml
```

### Native sandbox check

```bash
cargo check --offline --manifest-path main/opengeometry/sandbox-native/Cargo.toml
```

Note: online `cargo check` for `sandbox-native` may fail in restricted environments that cannot reach `index.crates.io`.

### Regenerate wasm package and build examples

```bash
npm run build-core
npm --prefix main/opengeometry-three run build-example-three
```

### Run examples

- Legacy page (static host):
- `main/opengeometry-three/examples/stl-export.html`
- Vite built page:
- `main/opengeometry-three/examples-dist/operations/stl-export.html`
- Use the Shape dropdown and export button to verify shape-specific downloads (`opengeometry-cuboid.stl`, `opengeometry-cylinder.stl`, etc.).

## STL Validation Notes

- Binary STL writer uses `stl_io::write_stl` (spec-compliant binary structure).
- Unit tests cover:
- expected binary size (`84 + 50 * triangle_count`)
- triangle count consistency with STL header
- custom header injection
- best-effort skip behavior and strict failure behavior

## Backward Compatibility

- Existing primitive generation, projection, and PDF APIs remain intact.
- STL functionality is additive.
- One behavior hardening change:
- serialized BREPs added through scene manager are now topology-validated before insertion.

## Known Caveats / Follow-ups

- Existing non-critical warnings remain:
- `operations/windingsort.rs` (`ccw_and_flag` naming)
- `geometry/triangle.rs` (`crso` unused variable)
- `sandbox-native` still has pre-existing `unused_must_use` warnings in some call sites (not introduced by STL work).
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>OpenGeometry Example</title>
</head>
<body data-example-slug="operations/offset">
<div id="app"></div>
<script type="module" src="../../src/example-page.ts"></script>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>OpenGeometry Example</title>
</head>
<body data-example-slug="operations/sweep-path-profile">
<div id="app"></div>
<script type="module" src="../../src/example-page.ts"></script>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>OpenGeometry Example</title>
</head>
<body data-example-slug="operations/wall-from-offsets">
<div id="app"></div>
<script type="module" src="../../src/example-page.ts"></script>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>OpenGeometry Example</title>
</head>
<body data-example-slug="primitives/arc">
<div id="app"></div>
<script type="module" src="../../src/example-page.ts"></script>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>OpenGeometry Example</title>
</head>
<body data-example-slug="primitives/curve">
<div id="app"></div>
<script type="module" src="../../src/example-page.ts"></script>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>OpenGeometry Example</title>
</head>
<body data-example-slug="primitives/line">
<div id="app"></div>
<script type="module" src="../../src/example-page.ts"></script>
</body>
</html>
Loading
Loading