okgeometry-api 1.15.0 → 1.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Brep.d.ts +95 -6
- package/dist/Brep.d.ts.map +1 -1
- package/dist/Brep.js +114 -5
- package/dist/Brep.js.map +1 -1
- package/dist/GeometryBoolean.d.ts +136 -0
- package/dist/GeometryBoolean.d.ts.map +1 -0
- package/dist/GeometryBoolean.js +632 -0
- package/dist/GeometryBoolean.js.map +1 -0
- package/dist/Mesh.d.ts +21 -1
- package/dist/Mesh.d.ts.map +1 -1
- package/dist/Mesh.js +66 -4
- package/dist/Mesh.js.map +1 -1
- package/dist/NurbsCurve.d.ts +66 -0
- package/dist/NurbsCurve.d.ts.map +1 -1
- package/dist/NurbsCurve.js +101 -0
- package/dist/NurbsCurve.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/wasm-base64.d.ts +1 -1
- package/dist/wasm-base64.d.ts.map +1 -1
- package/dist/wasm-base64.js +1 -1
- package/dist/wasm-base64.js.map +1 -1
- package/dist/wasm-bindings.d.ts +123 -1
- package/dist/wasm-bindings.d.ts.map +1 -1
- package/dist/wasm-bindings.js +193 -1
- package/dist/wasm-bindings.js.map +1 -1
- package/package.json +58 -54
- package/src/Brep.ts +147 -10
- package/src/GeometryBoolean.ts +863 -0
- package/src/Mesh.ts +3804 -3730
- package/src/NurbsCurve.ts +110 -0
- package/src/index.ts +88 -79
- package/src/wasm-base64.ts +1 -1
- package/src/wasm-bindings.d.ts +110 -1
- package/src/wasm-bindings.js +197 -1
package/package.json
CHANGED
|
@@ -1,54 +1,58 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "okgeometry-api",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Geometry engine API for AEC applications
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "dist/index.js",
|
|
7
|
-
"types": "dist/index.d.ts",
|
|
8
|
-
"exports": {
|
|
9
|
-
".": {
|
|
10
|
-
"types": "./dist/index.d.ts",
|
|
11
|
-
"import": "./dist/index.js"
|
|
12
|
-
}
|
|
13
|
-
},
|
|
14
|
-
"files": [
|
|
15
|
-
"dist/",
|
|
16
|
-
"src/"
|
|
17
|
-
],
|
|
18
|
-
"scripts": {
|
|
19
|
-
"build:wasm": "node -e \"require('fs').rmSync('wasm',{recursive:true,force:true})\" && cd .. && wasm-pack build --target web --out-dir okgeometry-api/wasm",
|
|
20
|
-
"sync:wasm-bindings": "node scripts/sync-wasm-bindings.mjs",
|
|
21
|
-
"inline-wasm": "tsx scripts/inline-wasm.ts",
|
|
22
|
-
"build": "npm run build:wasm && npm run sync:wasm-bindings && npm run inline-wasm && tsc",
|
|
23
|
-
"bench:boolean-heavy": "npm run build && tsx scripts/bench-boolean-heavy.ts",
|
|
24
|
-
"bench:boolean-ab": "npm run build && tsx scripts/bench-boolean-ab.ts",
|
|
25
|
-
"bench:boolean-batch": "npm run build && tsx scripts/bench-boolean-batch.ts",
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"smoke:brep-
|
|
30
|
-
"smoke:brep-
|
|
31
|
-
"smoke:
|
|
32
|
-
"smoke:
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
"geometry",
|
|
36
|
-
"
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
}
|
|
54
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "okgeometry-api",
|
|
3
|
+
"version": "1.17.0",
|
|
4
|
+
"description": "Geometry engine API for AEC applications - NURBS, meshes, booleans, intersections. Powered by Rust/WASM.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist/",
|
|
16
|
+
"src/"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build:wasm": "node -e \"require('fs').rmSync('wasm',{recursive:true,force:true})\" && cd .. && wasm-pack build --target web --out-dir okgeometry-api/wasm",
|
|
20
|
+
"sync:wasm-bindings": "node scripts/sync-wasm-bindings.mjs",
|
|
21
|
+
"inline-wasm": "tsx scripts/inline-wasm.ts",
|
|
22
|
+
"build": "npm run build:wasm && npm run sync:wasm-bindings && npm run inline-wasm && tsc",
|
|
23
|
+
"bench:boolean-heavy": "npm run build && tsx scripts/bench-boolean-heavy.ts",
|
|
24
|
+
"bench:boolean-ab": "npm run build && tsx scripts/bench-boolean-ab.ts",
|
|
25
|
+
"bench:boolean-batch": "npm run build && tsx scripts/bench-boolean-batch.ts",
|
|
26
|
+
"bench:boolean-matrix": "tsx scripts/bench-boolean-matrix.ts",
|
|
27
|
+
"smoke:brep-boolean": "tsx scripts/smoke-brep-boolean.ts",
|
|
28
|
+
"prepublishOnly": "npm run build",
|
|
29
|
+
"smoke:brep-parametric": "tsx scripts/smoke-brep-parametric.ts",
|
|
30
|
+
"smoke:brep-from-mesh": "tsx scripts/smoke-brep-from-mesh.ts",
|
|
31
|
+
"smoke:brep-faces": "tsx scripts/smoke-brep-faces.ts",
|
|
32
|
+
"smoke:curve-intersections": "tsx scripts/smoke-curve-intersections.ts",
|
|
33
|
+
"smoke:brep-intersections": "tsx scripts/smoke-brep-intersections.ts",
|
|
34
|
+
"smoke:brep-sheet-boolean": "tsx scripts/smoke-brep-sheet-boolean.ts",
|
|
35
|
+
"smoke:geometry-boolean": "tsx scripts/smoke-geometry-boolean.ts",
|
|
36
|
+
"smoke:curve-region": "tsx scripts/smoke-curve-region.ts"
|
|
37
|
+
},
|
|
38
|
+
"keywords": [
|
|
39
|
+
"geometry",
|
|
40
|
+
"wasm",
|
|
41
|
+
"nurbs",
|
|
42
|
+
"mesh",
|
|
43
|
+
"boolean",
|
|
44
|
+
"cad",
|
|
45
|
+
"aec",
|
|
46
|
+
"3d"
|
|
47
|
+
],
|
|
48
|
+
"author": "Mostafa El Ayoubi",
|
|
49
|
+
"license": "Proprietary License",
|
|
50
|
+
"repository": {
|
|
51
|
+
"type": "git",
|
|
52
|
+
"url": "https://www.orkestra.online"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"tsx": "^4.7.0",
|
|
56
|
+
"typescript": "^5.4.0"
|
|
57
|
+
}
|
|
58
|
+
}
|
package/src/Brep.ts
CHANGED
|
@@ -50,6 +50,21 @@ export interface BrepSplitSolidResult {
|
|
|
50
50
|
positive: Brep | null;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
/**
|
|
54
|
+
* Both sides of a sheet split ({@link Brep.splitSheetBySolid} /
|
|
55
|
+
* {@link Brep.splitSheetBySheet}), each an OPEN Brep preserving the sheet's
|
|
56
|
+
* exact trimmed faces, pcurves and edges.
|
|
57
|
+
*
|
|
58
|
+
* `inside` follows the oriented-sheet convention ("inside" = FRONT, the side
|
|
59
|
+
* a sheet's normal points toward): pieces CONTAINED in the solid cutter, or
|
|
60
|
+
* pieces in FRONT of the sheet cutter's normal. A side is null when no
|
|
61
|
+
* material lands on it.
|
|
62
|
+
*/
|
|
63
|
+
export interface BrepSheetSplitResult {
|
|
64
|
+
inside: Brep | null;
|
|
65
|
+
outside: Brep | null;
|
|
66
|
+
}
|
|
67
|
+
|
|
53
68
|
/** Validation report for a BREP body. */
|
|
54
69
|
export interface BrepValidationReport {
|
|
55
70
|
isClosed: boolean;
|
|
@@ -624,12 +639,27 @@ export class Brep {
|
|
|
624
639
|
*
|
|
625
640
|
* `options.tolerance` is the absolute geometric tolerance of the fitted
|
|
626
641
|
* intersection curves (default 1e-6).
|
|
642
|
+
*
|
|
643
|
+
* `options.join` (default false) JOINS the chained pieces into maximal
|
|
644
|
+
* curves: greedy reversal-aware endpoint chaining, exact degree elevation
|
|
645
|
+
* to the chain maximum, exact knot-vector concatenation (junction
|
|
646
|
+
* multiplicity = degree, no re-parameterization), and a per-curve
|
|
647
|
+
* verification gate — every joined curve is sampled densely and must stay
|
|
648
|
+
* within 5·tolerance of BOTH operands' surfaces, otherwise that chain's
|
|
649
|
+
* pieces come back unjoined (never a corrupted joined curve). A closed
|
|
650
|
+
* intersection loop (e.g. a plane through a cylinder) comes back as ONE
|
|
651
|
+
* closed curve with `pointAt(0)` equal to `pointAt(1)`.
|
|
627
652
|
*/
|
|
628
|
-
intersectionCurves(
|
|
653
|
+
intersectionCurves(
|
|
654
|
+
other: Brep | NurbsSurface,
|
|
655
|
+
options?: { tolerance?: number; join?: boolean },
|
|
656
|
+
): NurbsCurve[] {
|
|
629
657
|
ensureInit();
|
|
630
658
|
const rhs = other instanceof Brep ? other : Brep.fromSurface(other);
|
|
631
659
|
const tolerance = options?.tolerance ?? 1e-6;
|
|
632
|
-
const buf =
|
|
660
|
+
const buf = options?.join
|
|
661
|
+
? wasm.brep_intersection_curves_joined_op(this.json, rhs.json, tolerance)
|
|
662
|
+
: wasm.brep_intersection_curves_op(this.json, rhs.json, tolerance);
|
|
633
663
|
// An EMPTY buffer signals a kernel error (invalid BREP / pipeline
|
|
634
664
|
// failure); "no intersections" is the non-empty `[0]` packing.
|
|
635
665
|
if (buf.length < 1) {
|
|
@@ -755,14 +785,34 @@ export class Brep {
|
|
|
755
785
|
|
|
756
786
|
// ── Parametric booleans (BREP → BREP) ──
|
|
757
787
|
|
|
788
|
+
/**
|
|
789
|
+
* Resolve a parametric-boolean operand: Breps pass through; Meshes are
|
|
790
|
+
* converted via {@link Brep.fromMesh} (planar-faceted meshes only) so the
|
|
791
|
+
* EXACT pipeline can run. Curved/dense meshes fail conversion with
|
|
792
|
+
* guidance to use the tessellated union()/subtract()/intersect() instead.
|
|
793
|
+
*/
|
|
794
|
+
private static resolveParametricOperand(other: Brep | Mesh): Brep {
|
|
795
|
+
if (other instanceof Brep) return other;
|
|
796
|
+
try {
|
|
797
|
+
return Brep.fromMesh(other);
|
|
798
|
+
} catch (e) {
|
|
799
|
+
const reason = e instanceof Error ? e.message : String(e);
|
|
800
|
+
throw new Error(
|
|
801
|
+
`Parametric boolean operand mesh could not be converted to a BREP (${reason}); ` +
|
|
802
|
+
"use the tessellated union()/subtract()/intersect() for mesh operands instead",
|
|
803
|
+
);
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
|
|
758
807
|
private runParametricBoolean(
|
|
759
|
-
other: Brep,
|
|
808
|
+
other: Brep | Mesh,
|
|
760
809
|
op: "union" | "subtract" | "intersect",
|
|
761
810
|
options?: BrepParametricBooleanOptions,
|
|
762
811
|
): Brep {
|
|
763
812
|
ensureInit();
|
|
813
|
+
const rhs = Brep.resolveParametricOperand(other);
|
|
764
814
|
const tolerance = options?.tolerance ?? 1e-6;
|
|
765
|
-
const json = wasm.brep_boolean_op(this.json,
|
|
815
|
+
const json = wasm.brep_boolean_op(this.json, rhs.json, op, tolerance);
|
|
766
816
|
if (!json) throw new Error(`Brep.${op}Brep failed`);
|
|
767
817
|
if (json.startsWith('{"error"')) {
|
|
768
818
|
const parsed = JSON.parse(json) as { error: string };
|
|
@@ -776,7 +826,30 @@ export class Brep {
|
|
|
776
826
|
* trimmed-surface output. Result faces keep their parents' exact surfaces
|
|
777
827
|
* (planes/NURBS, untessellated); intersection edges are Newton-refined SSI
|
|
778
828
|
* curves stored as tolerant NURBS edges with matched pcurves on both
|
|
779
|
-
* adjacent faces.
|
|
829
|
+
* adjacent faces.
|
|
830
|
+
*
|
|
831
|
+
* Operands may be CLOSED solids or OPEN sheets — the kernel dispatches on
|
|
832
|
+
* closedness, following the oriented-sheet convention used across the API
|
|
833
|
+
* (a sheet's "inside" is the FRONT of its normal):
|
|
834
|
+
* - closed × closed — full volumetric boolean.
|
|
835
|
+
* - sheet × solid — `subtract` keeps the sheet's pieces OUTSIDE the solid,
|
|
836
|
+
* `intersect` the pieces INSIDE (open trimmed sheets).
|
|
837
|
+
* - solid × sheet — `subtract` keeps the capped half of the solid BEHIND
|
|
838
|
+
* the sheet normal, `intersect` the FRONT half (closed solids).
|
|
839
|
+
* - sheet × sheet — a sheet's material is its FRONT half-space, so
|
|
840
|
+
* `union` = ∂(matA ∪ matB): this sheet's BACK-of-other pieces joined
|
|
841
|
+
* with the other's BACK-of-this pieces into ONE open Brep stitched at
|
|
842
|
+
* the shared exact intersection edges (pieces in FRONT of the other
|
|
843
|
+
* sheet are interior to the union material and are dropped; faces the
|
|
844
|
+
* other operand never cuts are kept whole, coincident overlaps keep
|
|
845
|
+
* this sheet's copy); `subtract` keeps this sheet's BACK pieces;
|
|
846
|
+
* `intersect` its FRONT pieces.
|
|
847
|
+
* - `union` of a solid and a sheet (either order) is NOT defined and
|
|
848
|
+
* throws — trim the sheet with subtract/intersect instead.
|
|
849
|
+
*
|
|
850
|
+
* A Mesh operand is converted via {@link Brep.fromMesh} (planar-faceted
|
|
851
|
+
* meshes only); if conversion fails, use the tessellated
|
|
852
|
+
* union()/subtract()/intersect() instead.
|
|
780
853
|
*
|
|
781
854
|
* `options.tolerance` is the absolute geometric tolerance of intersection
|
|
782
855
|
* edges and coincident-face classification (default 1e-6).
|
|
@@ -785,17 +858,27 @@ export class Brep {
|
|
|
785
858
|
* cylinders touching along a line/point) is not resolved into pinch
|
|
786
859
|
* topology; transversal and coincident (coplanar) configurations are exact.
|
|
787
860
|
*/
|
|
788
|
-
unionBrep(other: Brep, options?: BrepParametricBooleanOptions): Brep {
|
|
861
|
+
unionBrep(other: Brep | Mesh, options?: BrepParametricBooleanOptions): Brep {
|
|
789
862
|
return this.runParametricBoolean(other, "union", options);
|
|
790
863
|
}
|
|
791
864
|
|
|
792
|
-
/**
|
|
793
|
-
|
|
865
|
+
/**
|
|
866
|
+
* PARAMETRIC boolean subtraction (this − other). See {@link unionBrep} for
|
|
867
|
+
* the full open/closed dispatch table: sheet − solid trims the sheet to
|
|
868
|
+
* the OUTSIDE of the solid; solid − sheet keeps the capped half BEHIND the
|
|
869
|
+
* sheet normal; sheet − sheet keeps this sheet's BACK pieces.
|
|
870
|
+
*/
|
|
871
|
+
subtractBrep(other: Brep | Mesh, options?: BrepParametricBooleanOptions): Brep {
|
|
794
872
|
return this.runParametricBoolean(other, "subtract", options);
|
|
795
873
|
}
|
|
796
874
|
|
|
797
|
-
/**
|
|
798
|
-
|
|
875
|
+
/**
|
|
876
|
+
* PARAMETRIC boolean intersection. See {@link unionBrep} for the full
|
|
877
|
+
* open/closed dispatch table: sheet ∩ solid trims the sheet to the INSIDE
|
|
878
|
+
* of the solid; solid ∩ sheet keeps the capped half in FRONT of the sheet
|
|
879
|
+
* normal; sheet ∩ sheet keeps this sheet's FRONT pieces.
|
|
880
|
+
*/
|
|
881
|
+
intersectBrep(other: Brep | Mesh, options?: BrepParametricBooleanOptions): Brep {
|
|
799
882
|
return this.runParametricBoolean(other, "intersect", options);
|
|
800
883
|
}
|
|
801
884
|
|
|
@@ -888,6 +971,60 @@ export class Brep {
|
|
|
888
971
|
return { negative: side(parsed.negative), positive: side(parsed.positive) };
|
|
889
972
|
}
|
|
890
973
|
|
|
974
|
+
/**
|
|
975
|
+
* PARAMETRIC split of this OPEN sheet by a CLOSED solid: the sheet's faces
|
|
976
|
+
* are imprinted with the exact intersection curves and every face piece is
|
|
977
|
+
* classified by containment in the solid. `inside` = the pieces contained
|
|
978
|
+
* in the solid (ON-boundary pieces count as inside), `outside` = the rest.
|
|
979
|
+
* Both sides are open Breps preserving the sheet's exact trimmed faces,
|
|
980
|
+
* pcurves and edges.
|
|
981
|
+
*
|
|
982
|
+
* The counterpart of {@link splitBySheet} with the roles swapped: use that
|
|
983
|
+
* one to split a closed solid BY a sheet (capped halves); use this one to
|
|
984
|
+
* split a sheet BY a solid (trimmed sheet pieces).
|
|
985
|
+
*/
|
|
986
|
+
splitSheetBySolid(
|
|
987
|
+
solid: Brep,
|
|
988
|
+
options?: BrepParametricBooleanOptions,
|
|
989
|
+
): BrepSheetSplitResult {
|
|
990
|
+
ensureInit();
|
|
991
|
+
const tolerance = options?.tolerance ?? 1e-6;
|
|
992
|
+
const json = wasm.brep_split_sheet_by_solid_op(this.json, solid.json, tolerance);
|
|
993
|
+
return Brep.parseSheetSplitResult(json, "Brep.splitSheetBySolid");
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
/**
|
|
997
|
+
* PARAMETRIC split of this OPEN sheet by another OPEN sheet (a sheet Brep
|
|
998
|
+
* or a NurbsSurface, converted via {@link Brep.fromSurface}): the mutual
|
|
999
|
+
* exact intersection curves are imprinted onto this sheet's faces and each
|
|
1000
|
+
* piece is assigned to the FRONT (`inside`) or BACK (`outside`) of the
|
|
1001
|
+
* other sheet's oriented normal — the same seam-vote classification as
|
|
1002
|
+
* {@link splitBySheet}. Faces the cutter never crosses classify wholesale
|
|
1003
|
+
* by their side of the cutter; coincident-overlap pieces land in `inside`.
|
|
1004
|
+
*/
|
|
1005
|
+
splitSheetBySheet(
|
|
1006
|
+
other: Brep | NurbsSurface,
|
|
1007
|
+
options?: BrepParametricBooleanOptions,
|
|
1008
|
+
): BrepSheetSplitResult {
|
|
1009
|
+
ensureInit();
|
|
1010
|
+
const cutter = other instanceof Brep ? other : Brep.fromSurface(other);
|
|
1011
|
+
const tolerance = options?.tolerance ?? 1e-6;
|
|
1012
|
+
const json = wasm.brep_split_sheet_by_sheet_op(this.json, cutter.json, tolerance);
|
|
1013
|
+
return Brep.parseSheetSplitResult(json, "Brep.splitSheetBySheet");
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
private static parseSheetSplitResult(json: string, label: string): BrepSheetSplitResult {
|
|
1017
|
+
if (!json) throw new Error(`${label} failed`);
|
|
1018
|
+
if (json.startsWith('{"error"')) {
|
|
1019
|
+
const parsed = JSON.parse(json) as { error: string };
|
|
1020
|
+
throw new Error(`${label} failed: ${parsed.error}`);
|
|
1021
|
+
}
|
|
1022
|
+
const parsed = JSON.parse(json) as { inside: unknown; outside: unknown };
|
|
1023
|
+
const side = (v: unknown): Brep | null =>
|
|
1024
|
+
v == null ? null : new Brep(JSON.stringify(v));
|
|
1025
|
+
return { inside: side(parsed.inside), outside: side(parsed.outside) };
|
|
1026
|
+
}
|
|
1027
|
+
|
|
891
1028
|
/** JSON representation (persistence). */
|
|
892
1029
|
toJson(): string {
|
|
893
1030
|
return this.json;
|