feat(wasm): route wire offsets through join-aware builder (offsetWire2DWithJoin)#737
Conversation
Expose offsetWire2DWithJoin(wire, distance, joinType) so consumers that hold only a wire can route a corner join type (intersection / arc / chamfer) into the join-aware offset builder. Previously only the face-based offsetWireWithJoinType honored the join type; a wire-only caller had to fall back to a sharp-corner polygon offset that silently ignored the requested join. The new binding builds a planar face from the wire internally, then delegates to offset_wire_with_join. Adds the matching executeBatch dispatch op and contract tests proving the arc join produces a distinct perimeter from chamfer and that an unknown join type is rejected.
WASM Binary Size
|
Greptile SummaryAdds
Confidence Score: 4/5Safe to merge; the change is additive, tests pass, and the only gap is a missing early-validation guard on the distance parameter. The new binding follows the established pattern for wire offset operations and the logic is correct. The one gap is that crates/wasm/src/bindings/operations.rs — the missing Important Files Changed
Prompt To Fix All With AIFix the following 1 code review issue. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 1
crates/wasm/src/bindings/operations.rs:1208-1214
Missing `validate_finite` on `distance`. The binding rules require every public `#[wasm_bindgen]` method to validate numeric inputs via helpers from `error.rs`. Passing `NaN` or `±Inf` here will propagate silently into `offset_wire_with_join`, which can produce garbage geometry or a panic inside the kernel instead of a clean JS error.
```suggestion
pub fn offset_wire_2d_with_join(
&mut self,
wire: u32,
distance: f64,
join_type: &str,
) -> Result<u32, JsError> {
validate_finite(distance, "distance")?;
let wire_id = self.resolve_wire(wire)?;
```
Reviews (1): Last reviewed commit: "feat(wasm): add wire-based offsetWire2DW..." | Re-trigger Greptile |
| pub fn offset_wire_2d_with_join( | ||
| &mut self, | ||
| wire: u32, | ||
| distance: f64, | ||
| join_type: &str, | ||
| ) -> Result<u32, JsError> { | ||
| let wire_id = self.resolve_wire(wire)?; |
There was a problem hiding this comment.
Missing
validate_finite on distance. The binding rules require every public #[wasm_bindgen] method to validate numeric inputs via helpers from error.rs. Passing NaN or ±Inf here will propagate silently into offset_wire_with_join, which can produce garbage geometry or a panic inside the kernel instead of a clean JS error.
| pub fn offset_wire_2d_with_join( | |
| &mut self, | |
| wire: u32, | |
| distance: f64, | |
| join_type: &str, | |
| ) -> Result<u32, JsError> { | |
| let wire_id = self.resolve_wire(wire)?; | |
| pub fn offset_wire_2d_with_join( | |
| &mut self, | |
| wire: u32, | |
| distance: f64, | |
| join_type: &str, | |
| ) -> Result<u32, JsError> { | |
| validate_finite(distance, "distance")?; | |
| let wire_id = self.resolve_wire(wire)?; |
Prompt To Fix With AI
This is a comment left during a code review.
Path: crates/wasm/src/bindings/operations.rs
Line: 1208-1214
Comment:
Missing `validate_finite` on `distance`. The binding rules require every public `#[wasm_bindgen]` method to validate numeric inputs via helpers from `error.rs`. Passing `NaN` or `±Inf` here will propagate silently into `offset_wire_with_join`, which can produce garbage geometry or a panic inside the kernel instead of a clean JS error.
```suggestion
pub fn offset_wire_2d_with_join(
&mut self,
wire: u32,
distance: f64,
join_type: &str,
) -> Result<u32, JsError> {
validate_finite(distance, "distance")?;
let wire_id = self.resolve_wire(wire)?;
```
How can I resolve this? If you propose a fix, please make it concise.There was a problem hiding this comment.
Pull request overview
Adds a wire-only WASM entry point that preserves corner join semantics (intersection/arc/chamfer) by internally constructing a planar face from a wire and delegating to the existing join-aware offset builder. This closes an API parity gap where wire-only consumers could not select join style and effectively got sharp corners regardless of request.
Changes:
- Added
offsetWire2DWithJoin(wire, distance, joinType)binding that routes join type throughoffset_wire_with_join. - Added matching
executeBatchdispatcher op ("offsetWire2DWithJoin"). - Added contract tests (via
executeBatch) to verify join-type threading and rejection of unknown join types.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
crates/wasm/src/bindings/operations.rs |
Introduces offsetWire2DWithJoin binding and adds tests validating join behavior and error handling. |
crates/wasm/src/bindings/batch.rs |
Adds batch dispatcher support for "offsetWire2DWithJoin" with consistent argument parsing and error mapping. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
🤖 I have created a release *beep* *boop* --- ## [2.101.0](v2.100.0...v2.101.0) (2026-05-29) ### Features * **wasm:** route wire offsets through join-aware builder (offsetWire2DWithJoin) ([#737](#737)) ([cd41676](cd41676)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: brepkit[bot] <265643962+brepkit[bot]@users.noreply.github.com>
…tion/chamfer) (#1089) ## Summary Routes \`offsetWire2D\`'s \`joinType\` through the brepkit kernel's join-aware wire-offset builder instead of the sharp-corner polygon offset that silently ignored it. The brepkit adapter previously built a 2D polygon from the wire and called \`offsetPolygon2d\`, which always produces sharp/miter corners — so \`arc\`, \`intersection\`, and \`chamfer\` all collapsed to the same result. This is a developer-API parity gap. ## What changed - \`src/kernel/brepkit/modifierOps.ts\`: \`offsetWire2D\` now calls the new \`offsetWire2DWithJoin(wire, distance, joinType)\` kernel binding when available, mapping the public kind (\`arc\`/\`intersection\`/\`tangent\`; \`chamfer\` is pre-mapped to \`intersection\` upstream) to the kernel's join-type strings. Falls back to the legacy polygon path on older wasm builds. - \`src/kernel/brepkit/brepkitWasmTypes.ts\`: declares the optional \`offsetWire2DWithJoin\` binding. - \`tests/helpers/kernelDivergences.ts\`: removes the \`offsetWire2D.chamferJoin\` divergence entry (the kernel now implements the join types). - \`tests/offsetWire2D.test.ts\`: un-skips the suite and drops the reference-kernel-specific naming — it is now a normal cross-kernel test. ## Blocked on This depends on the brepkit-wasm release that ships the \`offsetWire2DWithJoin\` binding. - brepkit PR: andymai/brepkit#737 Until that wasm version is published and bumped here, CI will run the un-skipped \`tests/offsetWire2D.test.ts\` against the older wasm (which lacks the binding) and the \`arc differs from chamfer\` assertion will fail — hence this PR is kept in **draft**. Locally validated against a wasm build from PR #737 (all 9 offsetWire2D tests pass across both kernels; full suite + typecheck + lint + knip green). Do not merge until the brepkit-wasm dependency is bumped.
Summary
Adds a wire-based offset entry point, `offsetWire2DWithJoin(wire, distance, joinType)`, so consumers that hold only a wire can route a corner join type (`intersection` / `arc` / `chamfer`) into the join-aware offset builder.
Previously only the face-based `offsetWireWithJoinType` honored the join type. A wire-only caller (e.g. a 2D sketch offset that starts from a wire, not a face) had no equivalent entry point and fell back to a sharp-corner polygon offset that silently ignored the requested join. This is a developer-API parity gap: `arc` and `intersection`/`chamfer` collapsed to the same sharp result.
What changed
Validation
Downstream
Unblocks the developer-API library from honoring its `arc`/`intersection`/`chamfer` wire-offset join kinds (the adapter currently ignores the join type for wire inputs). A follow-up consumer PR (kept in draft, blocked on the next published wasm release containing this change) removes the corresponding cross-kernel skip.