@faicad/faijs 0.18.3 → 0.22.2

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.
Files changed (241) hide show
  1. package/dist/api/api-namespace.d.ts.map +1 -1
  2. package/dist/api/api-namespace.js +4 -1
  3. package/dist/api/api-namespace.js.map +1 -1
  4. package/dist/api/brep-mirror/joinery-brep.d.ts +1 -1
  5. package/dist/api/brep-mirror/joinery-brep.d.ts.map +1 -1
  6. package/dist/api/brep-mirror/joinery-brep.js +22 -20
  7. package/dist/api/brep-mirror/joinery-brep.js.map +1 -1
  8. package/dist/api/generated/script-face-manifest.d.ts +4 -0
  9. package/dist/api/generated/script-face-manifest.d.ts.map +1 -1
  10. package/dist/api/generated/script-face-manifest.js.map +1 -1
  11. package/dist/api/index.d.ts +4 -0
  12. package/dist/api/index.d.ts.map +1 -1
  13. package/dist/api/index.js +3 -0
  14. package/dist/api/index.js.map +1 -1
  15. package/dist/api/knurl.d.ts.map +1 -1
  16. package/dist/api/knurl.js +1 -0
  17. package/dist/api/knurl.js.map +1 -1
  18. package/dist/api/primitives.d.ts +4 -3
  19. package/dist/api/primitives.d.ts.map +1 -1
  20. package/dist/api/primitives.js +9 -3
  21. package/dist/api/primitives.js.map +1 -1
  22. package/dist/api/profile.d.ts +34 -5
  23. package/dist/api/profile.d.ts.map +1 -1
  24. package/dist/api/profile.js +168 -186
  25. package/dist/api/profile.js.map +1 -1
  26. package/dist/api/punch-hole.d.ts +77 -0
  27. package/dist/api/punch-hole.d.ts.map +1 -0
  28. package/dist/api/punch-hole.js +207 -0
  29. package/dist/api/punch-hole.js.map +1 -0
  30. package/dist/api/sketch-on-face.d.ts +85 -0
  31. package/dist/api/sketch-on-face.d.ts.map +1 -0
  32. package/dist/api/sketch-on-face.js +136 -0
  33. package/dist/api/sketch-on-face.js.map +1 -0
  34. package/dist/api/sketch-on-plane.d.ts +87 -0
  35. package/dist/api/sketch-on-plane.d.ts.map +1 -0
  36. package/dist/api/sketch-on-plane.js +106 -0
  37. package/dist/api/sketch-on-plane.js.map +1 -0
  38. package/dist/api/surface/arg-spec.d.ts +8 -0
  39. package/dist/api/surface/arg-spec.d.ts.map +1 -1
  40. package/dist/api/surface/arg-spec.js.map +1 -1
  41. package/dist/api/transform.d.ts +5 -5
  42. package/dist/api/transform.d.ts.map +1 -1
  43. package/dist/api/transform.js +12 -10
  44. package/dist/api/transform.js.map +1 -1
  45. package/dist/api/xml-dom.d.ts +27 -0
  46. package/dist/api/xml-dom.d.ts.map +1 -0
  47. package/dist/api/xml-dom.js +45 -0
  48. package/dist/api/xml-dom.js.map +1 -0
  49. package/dist/boolean/csg-backend.d.ts +6 -6
  50. package/dist/boolean/csg-backend.d.ts.map +1 -1
  51. package/dist/boolean/csg-backend.js +9 -9
  52. package/dist/boolean/csg-backend.js.map +1 -1
  53. package/dist/boolean/csg-core.d.ts +10 -10
  54. package/dist/boolean/csg-core.d.ts.map +1 -1
  55. package/dist/boolean/csg-core.js +24 -24
  56. package/dist/boolean/csg-core.js.map +1 -1
  57. package/dist/boolean/extrude-helpers.d.ts +8 -8
  58. package/dist/boolean/extrude-helpers.d.ts.map +1 -1
  59. package/dist/boolean/extrude-helpers.js +13 -13
  60. package/dist/boolean/extrude-helpers.js.map +1 -1
  61. package/dist/brep/brep-ops.d.ts +24 -10
  62. package/dist/brep/brep-ops.d.ts.map +1 -1
  63. package/dist/brep/brep-ops.js +46 -18
  64. package/dist/brep/brep-ops.js.map +1 -1
  65. package/dist/brep/brep-topology.d.ts.map +1 -1
  66. package/dist/brep/brep-topology.js +3 -1
  67. package/dist/brep/brep-topology.js.map +1 -1
  68. package/dist/brep/export/export-model.d.ts +79 -0
  69. package/dist/brep/export/export-model.d.ts.map +1 -0
  70. package/dist/brep/export/export-model.js +335 -0
  71. package/dist/brep/export/export-model.js.map +1 -0
  72. package/dist/brep/primitives-brep.d.ts.map +1 -1
  73. package/dist/brep/primitives-brep.js +3 -1
  74. package/dist/brep/primitives-brep.js.map +1 -1
  75. package/dist/brepkit-kernel/brepkitKernel.d.ts.map +1 -1
  76. package/dist/brepkit-kernel/brepkitKernel.js +4 -2
  77. package/dist/brepkit-kernel/brepkitKernel.js.map +1 -1
  78. package/dist/browser-host/csg-worker-protocol.d.ts +3 -3
  79. package/dist/browser-host/csg-worker-protocol.d.ts.map +1 -1
  80. package/dist/browser-host/csg-worker-protocol.js.map +1 -1
  81. package/dist/browser-host/csg-worker.js +3 -3
  82. package/dist/browser-host/csg-worker.js.map +1 -1
  83. package/dist/browser-host/inline-csg-backend.d.ts +3 -3
  84. package/dist/browser-host/inline-csg-backend.d.ts.map +1 -1
  85. package/dist/browser-host/inline-csg-backend.js +3 -3
  86. package/dist/browser-host/inline-csg-backend.js.map +1 -1
  87. package/dist/browser-host/worker-csg-backend.d.ts +3 -3
  88. package/dist/browser-host/worker-csg-backend.d.ts.map +1 -1
  89. package/dist/browser-host/worker-csg-backend.js.map +1 -1
  90. package/dist/cad-runtime/direct-executor.d.ts +4 -0
  91. package/dist/cad-runtime/direct-executor.d.ts.map +1 -1
  92. package/dist/cad-runtime/direct-executor.js +17 -1
  93. package/dist/cad-runtime/direct-executor.js.map +1 -1
  94. package/dist/cad-runtime/ports.d.ts +3 -3
  95. package/dist/cad-runtime/ports.d.ts.map +1 -1
  96. package/dist/cad-runtime/ports.js.map +1 -1
  97. package/dist/cad-runtime/runtime.d.ts +24 -1
  98. package/dist/cad-runtime/runtime.d.ts.map +1 -1
  99. package/dist/cad-runtime/runtime.js +53 -3
  100. package/dist/cad-runtime/runtime.js.map +1 -1
  101. package/dist/define-op.d.ts +22 -0
  102. package/dist/define-op.d.ts.map +1 -1
  103. package/dist/define-op.js +15 -0
  104. package/dist/define-op.js.map +1 -1
  105. package/dist/env-agnostic.d.ts +7 -7
  106. package/dist/env-agnostic.d.ts.map +1 -1
  107. package/dist/env-agnostic.js +3 -3
  108. package/dist/env-agnostic.js.map +1 -1
  109. package/dist/geometry2d/adapt.d.ts +46 -0
  110. package/dist/geometry2d/adapt.d.ts.map +1 -0
  111. package/dist/geometry2d/adapt.js +41 -0
  112. package/dist/geometry2d/adapt.js.map +1 -0
  113. package/dist/geometry2d/bbox2d.d.ts +82 -0
  114. package/dist/geometry2d/bbox2d.d.ts.map +1 -0
  115. package/dist/geometry2d/bbox2d.js +123 -0
  116. package/dist/geometry2d/bbox2d.js.map +1 -0
  117. package/dist/geometry2d/blueprint-sketcher.d.ts +27 -0
  118. package/dist/geometry2d/blueprint-sketcher.d.ts.map +1 -0
  119. package/dist/geometry2d/blueprint-sketcher.js +29 -0
  120. package/dist/geometry2d/blueprint-sketcher.js.map +1 -0
  121. package/dist/geometry2d/blueprint.d.ts +102 -0
  122. package/dist/geometry2d/blueprint.d.ts.map +1 -0
  123. package/dist/geometry2d/blueprint.js +225 -0
  124. package/dist/geometry2d/blueprint.js.map +1 -0
  125. package/dist/geometry2d/blueprints.d.ts +58 -0
  126. package/dist/geometry2d/blueprints.d.ts.map +1 -0
  127. package/dist/geometry2d/blueprints.js +72 -0
  128. package/dist/geometry2d/blueprints.js.map +1 -0
  129. package/dist/geometry2d/bridge/lift-on-plane.d.ts +50 -0
  130. package/dist/geometry2d/bridge/lift-on-plane.d.ts.map +1 -0
  131. package/dist/geometry2d/bridge/lift-on-plane.js +78 -0
  132. package/dist/geometry2d/bridge/lift-on-plane.js.map +1 -0
  133. package/dist/geometry2d/bridge/on-surface.d.ts +98 -0
  134. package/dist/geometry2d/bridge/on-surface.d.ts.map +1 -0
  135. package/dist/geometry2d/bridge/on-surface.js +94 -0
  136. package/dist/geometry2d/bridge/on-surface.js.map +1 -0
  137. package/dist/geometry2d/bridge/plane.d.ts +69 -0
  138. package/dist/geometry2d/bridge/plane.d.ts.map +1 -0
  139. package/dist/geometry2d/bridge/plane.js +96 -0
  140. package/dist/geometry2d/bridge/plane.js.map +1 -0
  141. package/dist/geometry2d/canned-blueprints.d.ts +44 -0
  142. package/dist/geometry2d/canned-blueprints.d.ts.map +1 -0
  143. package/dist/geometry2d/canned-blueprints.js +94 -0
  144. package/dist/geometry2d/canned-blueprints.js.map +1 -0
  145. package/dist/geometry2d/compound-blueprint.d.ts +72 -0
  146. package/dist/geometry2d/compound-blueprint.d.ts.map +1 -0
  147. package/dist/geometry2d/compound-blueprint.js +95 -0
  148. package/dist/geometry2d/compound-blueprint.js.map +1 -0
  149. package/dist/geometry2d/curve2d.d.ts +298 -0
  150. package/dist/geometry2d/curve2d.d.ts.map +1 -0
  151. package/dist/geometry2d/curve2d.js +1110 -0
  152. package/dist/geometry2d/curve2d.js.map +1 -0
  153. package/dist/geometry2d/index.d.ts +22 -0
  154. package/dist/geometry2d/index.d.ts.map +1 -0
  155. package/dist/geometry2d/index.js +21 -0
  156. package/dist/geometry2d/index.js.map +1 -0
  157. package/dist/geometry2d/organise.d.ts +28 -0
  158. package/dist/geometry2d/organise.d.ts.map +1 -0
  159. package/dist/geometry2d/organise.js +108 -0
  160. package/dist/geometry2d/organise.js.map +1 -0
  161. package/dist/geometry2d/pen-sketcher.d.ts +365 -0
  162. package/dist/geometry2d/pen-sketcher.d.ts.map +1 -0
  163. package/dist/geometry2d/pen-sketcher.js +520 -0
  164. package/dist/geometry2d/pen-sketcher.js.map +1 -0
  165. package/dist/geometry2d/point.d.ts +44 -0
  166. package/dist/geometry2d/point.d.ts.map +1 -0
  167. package/dist/geometry2d/point.js +57 -0
  168. package/dist/geometry2d/point.js.map +1 -0
  169. package/dist/geometry2d/svg-ellipse.d.ts +74 -0
  170. package/dist/geometry2d/svg-ellipse.d.ts.map +1 -0
  171. package/dist/geometry2d/svg-ellipse.js +163 -0
  172. package/dist/geometry2d/svg-ellipse.js.map +1 -0
  173. package/dist/lang/code-to-args.d.ts +29 -3
  174. package/dist/lang/code-to-args.d.ts.map +1 -1
  175. package/dist/lang/code-to-args.js +41 -2
  176. package/dist/lang/code-to-args.js.map +1 -1
  177. package/dist/lang/codegen.d.ts +45 -1
  178. package/dist/lang/codegen.d.ts.map +1 -1
  179. package/dist/lang/codegen.js +135 -6
  180. package/dist/lang/codegen.js.map +1 -1
  181. package/dist/lang/host-arg.d.ts +6 -0
  182. package/dist/lang/host-arg.d.ts.map +1 -1
  183. package/dist/lang/host-arg.js.map +1 -1
  184. package/dist/lang/metadata-extractor.d.ts +25 -0
  185. package/dist/lang/metadata-extractor.d.ts.map +1 -1
  186. package/dist/lang/metadata-extractor.js +289 -25
  187. package/dist/lang/metadata-extractor.js.map +1 -1
  188. package/dist/lang/parse-error.d.ts +7 -1
  189. package/dist/lang/parse-error.d.ts.map +1 -1
  190. package/dist/lang/parse-error.js.map +1 -1
  191. package/dist/lang/security-scanner.d.ts +9 -1
  192. package/dist/lang/security-scanner.d.ts.map +1 -1
  193. package/dist/lang/security-scanner.js +38 -1
  194. package/dist/lang/security-scanner.js.map +1 -1
  195. package/dist/lang/symbol-table.generated.d.ts +3 -0
  196. package/dist/lang/symbol-table.generated.d.ts.map +1 -1
  197. package/dist/lang/symbol-table.generated.js +3 -0
  198. package/dist/lang/symbol-table.generated.js.map +1 -1
  199. package/dist/mesh/io.d.ts +62 -9
  200. package/dist/mesh/io.d.ts.map +1 -1
  201. package/dist/mesh/io.js +124 -17
  202. package/dist/mesh/io.js.map +1 -1
  203. package/dist/mesh/threemf-loader.d.ts +15 -0
  204. package/dist/mesh/threemf-loader.d.ts.map +1 -0
  205. package/dist/mesh/threemf-loader.js +163 -0
  206. package/dist/mesh/threemf-loader.js.map +1 -0
  207. package/dist/mesh/transform.d.ts +2 -2
  208. package/dist/mesh/transform.d.ts.map +1 -1
  209. package/dist/mesh/transform.js +3 -3
  210. package/dist/mesh/transform.js.map +1 -1
  211. package/dist/mesh/types.d.ts +2 -2
  212. package/dist/mesh/types.d.ts.map +1 -1
  213. package/dist/mesh/types.js.map +1 -1
  214. package/dist/node-host/cli.d.ts.map +1 -1
  215. package/dist/node-host/cli.js +53 -26
  216. package/dist/node-host/cli.js.map +1 -1
  217. package/dist/occt-kernel/occtKernel.d.ts.map +1 -1
  218. package/dist/occt-kernel/occtKernel.js +4 -2
  219. package/dist/occt-kernel/occtKernel.js.map +1 -1
  220. package/dist/occt-kernel/topologyExt.d.ts.map +1 -1
  221. package/dist/occt-kernel/topologyExt.js +4 -1
  222. package/dist/occt-kernel/topologyExt.js.map +1 -1
  223. package/dist/runtime-state.d.ts +18 -0
  224. package/dist/runtime-state.d.ts.map +1 -1
  225. package/dist/runtime-state.js +23 -0
  226. package/dist/runtime-state.js.map +1 -1
  227. package/dist/sdk.d.ts +1 -1
  228. package/dist/sdk.d.ts.map +1 -1
  229. package/dist/sdk.js +1 -1
  230. package/dist/sdk.js.map +1 -1
  231. package/dist/tolerance.d.ts +38 -0
  232. package/dist/tolerance.d.ts.map +1 -0
  233. package/dist/tolerance.js +40 -0
  234. package/dist/tolerance.js.map +1 -0
  235. package/dist/topology/naming/score.js +3 -3
  236. package/dist/topology/naming/score.js.map +1 -1
  237. package/dist/units.d.ts +248 -0
  238. package/dist/units.d.ts.map +1 -0
  239. package/dist/units.js +397 -0
  240. package/dist/units.js.map +1 -0
  241. package/package.json +19 -2
@@ -0,0 +1,207 @@
1
+ import { getBrepApi } from '../brep/handle-bridge.js';
2
+ import { solidToShape } from '../brep/brep-ops.js';
3
+ import { fromBrep, brepOf } from '../shape.js';
4
+ import { getOcctKernel } from '../occt-kernel/occtKernel.js';
5
+ import { defineOp } from '../sdk.js';
6
+ import { TopoRefError } from '../topology/naming/index.js';
7
+ import { resolveFaceHandle } from './sketch-on-face.js';
8
+ import { makeUvMap, curves2dBounds } from '../geometry2d/bridge/on-surface.js';
9
+ import { evaluateCurve2d } from '../geometry2d/curve2d.js';
10
+ import { profileSegToCurve } from '../geometry2d/adapt.js';
11
+ import { assertProfileParams } from './profile.js';
12
+ /** Extra length past the host extent so a through-hole clears the opposite side. */
13
+ const THROUGH_MARGIN = 10;
14
+ /**
15
+ * Depth of the punch extrusion: the explicit `height`, or the host's bounding-box
16
+ * diagonal (+ margin) for a through hole.
17
+ * @param kernel - the BREP engine surface.
18
+ * @param solid - the host solid handle.
19
+ * @param height - the blind depth, or null for through.
20
+ * @returns the positive extrude length.
21
+ */
22
+ function punchDepth(kernel, solid, height) {
23
+ if (height !== null && height !== undefined)
24
+ return height;
25
+ const box = kernel.getBoundingBox(solid);
26
+ const diag = Math.hypot(box.xmax - box.xmin, box.ymax - box.ymin, box.zmax - box.zmin);
27
+ return diag + THROUGH_MARGIN;
28
+ }
29
+ /**
30
+ * Map a contour's sampled 2D points onto the host face's UV space, evaluate the
31
+ * surface there, and return a clean, coplanar polygon (sample points projected
32
+ * onto the host's plane through its normal). Rebuilding a straight-edged planar
33
+ * face is deliberate: extruding the on-surface bridge's wire-derived face (a
34
+ * near-coplanar shell, z varies by ~1e-7) collapses its OCCT volume to ~0 — see
35
+ * GOTCHA `punch-hole` (E4).
36
+ * @param kernel - the BREP engine surface.
37
+ * @param host - the host face handle.
38
+ * @param mode - the face scale mode.
39
+ * @param uv - the UV mapping for this contour.
40
+ * @param anchor - an in-plane point of the host face (for the projection plane).
41
+ * @param n - the host face outward normal (unit).
42
+ * @param contour - the 2D contour to punch.
43
+ * @returns a flat polygon of 3D points (each approximately in the host plane).
44
+ */
45
+ function buildPolygon3d(kernel, host, uv, anchor, n, contour) {
46
+ const pts = [];
47
+ // Dedupe consecutive coincident samples across the contour.
48
+ const push = (p) => {
49
+ const last = pts[pts.length - 1];
50
+ if (last && Math.abs(last.x - p.x) < 1e-7 && Math.abs(last.y - p.y) < 1e-7 && Math.abs(last.z - p.z) < 1e-7)
51
+ return;
52
+ pts.push(p);
53
+ };
54
+ for (const seg of contour.segments) {
55
+ const c = profileSegToCurve(seg);
56
+ for (let k = 0; k <= 12; k++) {
57
+ const [x, y] = evaluateCurve2d(c, k / 12);
58
+ const [u, v] = uv(x, y);
59
+ const raw = kernel.pointOnSurface(host, u, v);
60
+ // Project onto the host face plane (through `anchor` + `n`) for exact coplanarity.
61
+ const d = (raw.x - anchor.x) * n.x + (raw.y - anchor.y) * n.y + (raw.z - anchor.z) * n.z;
62
+ push({ x: raw.x - n.x * d, y: raw.y - n.y * d, z: raw.z - n.z * d });
63
+ }
64
+ }
65
+ // Close the loop: drop a trailing point equal to the first.
66
+ if (pts.length > 1) {
67
+ const a = pts[0];
68
+ const b = pts[pts.length - 1];
69
+ if (Math.abs(a.x - b.x) < 1e-7 && Math.abs(a.y - b.y) < 1e-7 && Math.abs(a.z - b.z) < 1e-7)
70
+ pts.pop();
71
+ }
72
+ return pts;
73
+ }
74
+ /**
75
+ * Build and cut the punch solid for every placed profile on the host face and
76
+ * Boolean-cut it from `solid`, returning the holed solid handle.
77
+ * @param kernel - the BREP engine surface.
78
+ * @param solid - the host solid handle.
79
+ * @param params - the punch parameters.
80
+ * @returns the holed solid handle.
81
+ */
82
+ export function punchSolidOf(kernel, solid, params) {
83
+ const host = resolveFaceHandle(kernel, params.on, params.face);
84
+ const mode = params.scaleMode ?? 'original';
85
+ const depth = punchDepth(kernel, solid, params.height ?? null);
86
+ const draft = params.draftAngle ?? 0;
87
+ const draftRad = (draft * Math.PI) / 180;
88
+ // Punch inward = opposite the host face outward normal.
89
+ const n0 = kernel.surfaceNormal(host, 0, 0);
90
+ const nlen = Math.hypot(n0.x, n0.y, n0.z) || 1;
91
+ const n = { x: n0.x / nlen, y: n0.y / nlen, z: n0.z / nlen };
92
+ // In-plane anchor via the host face's UV center.
93
+ const ub = kernel.uvBounds(host);
94
+ const uMid = (ub.uMin + ub.uMax) / 2;
95
+ const vMid = (ub.vMin + ub.vMax) / 2;
96
+ const anchor = kernel.pointOnSurface(host, uMid, vMid);
97
+ const bounds = ub;
98
+ let result = solid;
99
+ for (const contour of params.contours) {
100
+ const curves = contour.segments.map((s) => profileSegToCurve(s));
101
+ const uv = makeUvMap(mode, curves2dBounds(curves), bounds);
102
+ const mouth = buildPolygon3d(kernel, host, uv, anchor, n, contour);
103
+ if (mouth.length < 3)
104
+ throw new Error('punchHole: contour is degenerate (fewer than 3 distinct points)');
105
+ // Mouth wire on the host plane.
106
+ const mouthEdges = [];
107
+ for (let i = 0; i < mouth.length; i++) {
108
+ const a = mouth[i];
109
+ const b = mouth[(i + 1) % mouth.length];
110
+ mouthEdges.push(kernel.makeLineEdge(a, b));
111
+ }
112
+ let punch;
113
+ if (draft !== 0) {
114
+ // Drafted hole = a tapered frustum: the mouth wire at the host plane and an
115
+ // end wire (recessed by `depth*tan(draftAngle)` toward the contour centre)
116
+ // at the punch depth, lofted together via occt. Verified: occt `loft` of two
117
+ // parallel-planar rect wires reproduces the analytic frustum volume exactly.
118
+ const inset = depth * Math.tan(draftRad);
119
+ // Uniform wall recess: each wall recedes by `inset` = depth·tan(draftAngle)
120
+ // measured *perpendicular* to that wall. For a homothetic scale about the
121
+ // centroid by factor s, the perpendicular recess of an edge at distance h
122
+ // from the centroid is h·(1−s); so choose s = 1 − inset/h̄ with h̄ the mean
123
+ // centroid→edge distance (h̄ = the half-side for a square/rect ⇒ walls land
124
+ // at exactly draftAngle).
125
+ const cx = mouth.reduce((s, p) => s + p.x, 0) / mouth.length;
126
+ const cy = mouth.reduce((s, p) => s + p.y, 0) / mouth.length;
127
+ const cz = mouth.reduce((s, p) => s + p.z, 0) / mouth.length;
128
+ let edgeSum = 0;
129
+ for (let i = 0; i < mouth.length; i++) {
130
+ const a = mouth[i];
131
+ const b = mouth[(i + 1) % mouth.length];
132
+ // centroid → edge midpoint projection in the host plane
133
+ const mx = (a.x + b.x) / 2 - cx;
134
+ const my = (a.y + b.y) / 2 - cy;
135
+ const mz = (a.z + b.z) / 2 - cz;
136
+ edgeSum += Math.hypot(mx, my, mz);
137
+ }
138
+ const hBar = edgeSum / mouth.length;
139
+ if (!Number.isFinite(hBar) || hBar <= 1e-9 || inset >= hBar * 0.999) {
140
+ throw new Error(`E_PUNCH_DRAFT_OVER_TAPER: draftAngle ${draft}° inset ${inset.toFixed(3)} must be < half-width ${hBar.toFixed(3)} at depth ${depth}`);
141
+ }
142
+ const s = 1 - inset / hBar;
143
+ const endPoints = mouth.map((p) => ({
144
+ x: cx + (p.x - cx) * s - n.x * depth,
145
+ y: cy + (p.y - cy) * s - n.y * depth,
146
+ z: cz + (p.z - cz) * s - n.z * depth,
147
+ }));
148
+ const endEdges = endPoints.map((p, i) => {
149
+ const a = endPoints[i];
150
+ const b = endPoints[(i + 1) % endPoints.length];
151
+ return kernel.makeLineEdge(a, b);
152
+ });
153
+ const k = getOcctKernel();
154
+ punch = k.loft([kernel.makeWire(mouthEdges), kernel.makeWire(endEdges)], true, true);
155
+ }
156
+ else {
157
+ const prismFace = kernel.makeFace(kernel.makeWire(mouthEdges));
158
+ punch = kernel.extrude(prismFace, -n.x * depth, -n.y * depth, -n.z * depth);
159
+ }
160
+ try {
161
+ result = kernel.cut(result, punch);
162
+ }
163
+ finally {
164
+ kernel.release(punch);
165
+ }
166
+ }
167
+ return result;
168
+ }
169
+ /**
170
+ * Build a Shape: the host solid with the profile(s) punched out of `params.on`.
171
+ * @param kernel - the BREP engine surface.
172
+ * @param params - the punch parameters.
173
+ * @returns the holed Shape.
174
+ */
175
+ export function buildPunchHole(kernel, params) {
176
+ const solid = brepOf(params.on);
177
+ if (!solid) {
178
+ throw new TopoRefError('E_TOPO_MESH_UNSUPPORTED', 'face', 'punchHole: target `on` has no BREP solid');
179
+ }
180
+ const holed = punchSolidOf(kernel, solid, params);
181
+ return fromBrep(solidToShape(kernel, holed), { solid: holed });
182
+ }
183
+ /**
184
+ * `cad.punchHole`: cut a face-placed 2D profile out of a solid.
185
+ * @group 创建
186
+ * @inputs 1
187
+ * @async false
188
+ * @qual ok
189
+ * @name punchHole
190
+ * @returns Shape with the profile punched out of `on`.
191
+ * @param params.contours - ordered 2D closed contours (the hole profile).type:any[] required:true
192
+ * @param params.on - the target solid to punch.type:Shape required:true
193
+ * @param params.face - host face: 1-based ordinal or `cad.faceRef(on, n)`.type:any required:true
194
+ * @param params.height - blind depth along the face inward normal (null/absent = through).type:number|object required:false
195
+ * @param params.draftAngle - wall taper in degrees (default 0 = straight; built as an occt loft frustum).type:number required:false
196
+ * @param params.scaleMode - UV mapping: 'original' | 'bounds' | 'native'.type:string required:false
197
+ */
198
+ export const punchHole = defineOp({
199
+ capabilities: ['directEdit'],
200
+ engines: ['occt'],
201
+ brep(params) {
202
+ assertProfileParams(params);
203
+ return buildPunchHole(getBrepApi(), params);
204
+ },
205
+ naming: { kind: 'construct', newFaces: { via: 'explicit', vocab: [] } },
206
+ });
207
+ //# sourceMappingURL=punch-hole.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"punch-hole.js","sourceRoot":"","sources":["../../src/api/punch-hole.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @platform occt — 本文件 import occt-kernel(getOcctKernel:draft-angle punch\n * 的 frustum loft 走原生 occt `loft`,适配器层无此能力)。\n *\n * `cad.punchHole`: cut a face-placed 2D profile out of a solid (E4 pocket/through\n * hole with optional taper).\n *\n * The hole profile is a set of closed 2D contours placed on a target face of the\n * solid (the same placement step as `cad.sketchOnFace`). Each placed contour is\n * extruded from the face *into* the part along the face inward normal, and the\n * resulting prism is the \"punch\" tool; a Boolean `cut` removes it from the host —\n * a profile-based pocket (`height` given) or through-hole (`height: null`).\n *\n * v1 scope: the punch is a straight linear prism, or — when `draftAngle > 0` — a\n * **tapered frustum** whose walls recede by `draftAngle` over the punch depth\n * (built as an occt `loft` between a mouth wire in the host plane and an inset\n * end wire at depth `depth·tan(draftAngle)`). The op is a composition over the\n * E3 on-surface placement + `kernel.extrude`/occt `loft` + `kernel.cut` and\n * declares `engines: ['occt']` (surface sampling + Boolean cut + draft loft need\n * the BREP platform).\n * @module\n */\nimport type { Shape } from '../mesh/types'\nimport type { BrepEngineApi } from '../brep/engine/primitives'\nimport type { BrepHandle } from '../brep/engine/types'\nimport { getBrepApi } from '../brep/handle-bridge'\nimport { solidToShape } from '../brep/brep-ops'\nimport { fromBrep, brepOf } from '../shape'\nimport { getOcctKernel } from '../occt-kernel/occtKernel'\nimport { defineOp } from '../sdk'\nimport type { Provenance } from '../topology/naming/lineage'\nimport { TopoRefError } from '../topology/naming'\nimport { resolveFaceHandle, type FaceSelector } from './sketch-on-face'\nimport { makeUvMap, curves2dBounds, type UvMap } from '../geometry2d/bridge/on-surface'\nimport { evaluateCurve2d } from '../geometry2d/curve2d'\nimport { profileSegToCurve } from '../geometry2d/adapt'\nimport { assertProfileParams, type ProfileLoop } from './profile'\n\n/** Parameters for `cad.punchHole`. */\nexport interface PunchHoleParams {\n /** Ordered 2D closed contour(s) describing the hole profile (same shape as `cad.profile`). */\n contours: ProfileLoop[]\n /** The target solid on which the hole is punched. */\n on: Shape\n /** The host face: a 1-based ordinal or a `cad.faceRef(on, n)` result. */\n face: FaceSelector\n /** Removal depth along the face inward normal. `null`/absent = through hole. */\n height?: number | null\n /** Taper on the hole walls (degrees; default 0 = straight wall). Built as a frustum via occt loft. */\n draftAngle?: number\n /** UV mapping mode for contour placement (default `'original'`). */\n scaleMode?: 'original' | 'bounds' | 'native'\n}\n\n/** Extra length past the host extent so a through-hole clears the opposite side. */\nconst THROUGH_MARGIN = 10\n\n/**\n * Depth of the punch extrusion: the explicit `height`, or the host's bounding-box\n * diagonal (+ margin) for a through hole.\n * @param kernel - the BREP engine surface.\n * @param solid - the host solid handle.\n * @param height - the blind depth, or null for through.\n * @returns the positive extrude length.\n */\nfunction punchDepth(kernel: BrepEngineApi, solid: BrepHandle, height: number | null | undefined): number {\n if (height !== null && height !== undefined) return height\n const box = kernel.getBoundingBox(solid)\n const diag = Math.hypot(box.xmax - box.xmin, box.ymax - box.ymin, box.zmax - box.zmin)\n return diag + THROUGH_MARGIN\n}\n\n/**\n * Map a contour's sampled 2D points onto the host face's UV space, evaluate the\n * surface there, and return a clean, coplanar polygon (sample points projected\n * onto the host's plane through its normal). Rebuilding a straight-edged planar\n * face is deliberate: extruding the on-surface bridge's wire-derived face (a\n * near-coplanar shell, z varies by ~1e-7) collapses its OCCT volume to ~0 — see\n * GOTCHA `punch-hole` (E4).\n * @param kernel - the BREP engine surface.\n * @param host - the host face handle.\n * @param mode - the face scale mode.\n * @param uv - the UV mapping for this contour.\n * @param anchor - an in-plane point of the host face (for the projection plane).\n * @param n - the host face outward normal (unit).\n * @param contour - the 2D contour to punch.\n * @returns a flat polygon of 3D points (each approximately in the host plane).\n */\nfunction buildPolygon3d(\n kernel: BrepEngineApi,\n host: BrepHandle,\n uv: UvMap,\n anchor: { x: number; y: number; z: number },\n n: { x: number; y: number; z: number },\n contour: ProfileLoop,\n): { x: number; y: number; z: number }[] {\n const pts: { x: number; y: number; z: number }[] = []\n // Dedupe consecutive coincident samples across the contour.\n const push = (p: { x: number; y: number; z: number }) => {\n const last = pts[pts.length - 1]\n if (last && Math.abs(last.x - p.x) < 1e-7 && Math.abs(last.y - p.y) < 1e-7 && Math.abs(last.z - p.z) < 1e-7) return\n pts.push(p)\n }\n for (const seg of contour.segments) {\n const c = profileSegToCurve(seg as never)\n for (let k = 0; k <= 12; k++) {\n const [x, y] = evaluateCurve2d(c, k / 12)\n const [u, v] = uv(x, y)\n const raw = kernel.pointOnSurface(host, u, v)\n // Project onto the host face plane (through `anchor` + `n`) for exact coplanarity.\n const d = (raw.x - anchor.x) * n.x + (raw.y - anchor.y) * n.y + (raw.z - anchor.z) * n.z\n push({ x: raw.x - n.x * d, y: raw.y - n.y * d, z: raw.z - n.z * d })\n }\n }\n // Close the loop: drop a trailing point equal to the first.\n if (pts.length > 1) {\n const a = pts[0]!\n const b = pts[pts.length - 1]!\n if (Math.abs(a.x - b.x) < 1e-7 && Math.abs(a.y - b.y) < 1e-7 && Math.abs(a.z - b.z) < 1e-7) pts.pop()\n }\n return pts\n}\n\n/**\n * Build and cut the punch solid for every placed profile on the host face and\n * Boolean-cut it from `solid`, returning the holed solid handle.\n * @param kernel - the BREP engine surface.\n * @param solid - the host solid handle.\n * @param params - the punch parameters.\n * @returns the holed solid handle.\n */\nexport function punchSolidOf(kernel: BrepEngineApi, solid: BrepHandle, params: PunchHoleParams): BrepHandle {\n const host = resolveFaceHandle(kernel, params.on, params.face)\n const mode = params.scaleMode ?? 'original'\n const depth = punchDepth(kernel, solid, params.height ?? null)\n\n const draft = params.draftAngle ?? 0\n const draftRad = (draft * Math.PI) / 180\n\n // Punch inward = opposite the host face outward normal.\n const n0 = kernel.surfaceNormal(host, 0, 0)\n const nlen = Math.hypot(n0.x, n0.y, n0.z) || 1\n const n = { x: n0.x / nlen, y: n0.y / nlen, z: n0.z / nlen }\n\n // In-plane anchor via the host face's UV center.\n const ub = kernel.uvBounds(host)\n const uMid = (ub.uMin + ub.uMax) / 2\n const vMid = (ub.vMin + ub.vMax) / 2\n const anchor = kernel.pointOnSurface(host, uMid, vMid)\n\n const bounds = ub\n let result = solid\n for (const contour of params.contours) {\n const curves = contour.segments.map((s) => profileSegToCurve(s as never))\n const uv = makeUvMap(mode, curves2dBounds(curves), bounds)\n const mouth = buildPolygon3d(kernel, host, uv, anchor, n, contour)\n if (mouth.length < 3) throw new Error('punchHole: contour is degenerate (fewer than 3 distinct points)')\n\n // Mouth wire on the host plane.\n const mouthEdges = [] as BrepHandle[]\n for (let i = 0; i < mouth.length; i++) {\n const a = mouth[i]!\n const b = mouth[(i + 1) % mouth.length]!\n mouthEdges.push(kernel.makeLineEdge(a, b) as never)\n }\n\n let punch: BrepHandle\n if (draft !== 0) {\n // Drafted hole = a tapered frustum: the mouth wire at the host plane and an\n // end wire (recessed by `depth*tan(draftAngle)` toward the contour centre)\n // at the punch depth, lofted together via occt. Verified: occt `loft` of two\n // parallel-planar rect wires reproduces the analytic frustum volume exactly.\n const inset = depth * Math.tan(draftRad)\n // Uniform wall recess: each wall recedes by `inset` = depth·tan(draftAngle)\n // measured *perpendicular* to that wall. For a homothetic scale about the\n // centroid by factor s, the perpendicular recess of an edge at distance h\n // from the centroid is h·(1−s); so choose s = 1 − inset/h̄ with h̄ the mean\n // centroid→edge distance (h̄ = the half-side for a square/rect ⇒ walls land\n // at exactly draftAngle).\n const cx = mouth.reduce((s, p) => s + p.x, 0) / mouth.length\n const cy = mouth.reduce((s, p) => s + p.y, 0) / mouth.length\n const cz = mouth.reduce((s, p) => s + p.z, 0) / mouth.length\n let edgeSum = 0\n for (let i = 0; i < mouth.length; i++) {\n const a = mouth[i]!\n const b = mouth[(i + 1) % mouth.length]!\n // centroid → edge midpoint projection in the host plane\n const mx = (a.x + b.x) / 2 - cx\n const my = (a.y + b.y) / 2 - cy\n const mz = (a.z + b.z) / 2 - cz\n edgeSum += Math.hypot(mx, my, mz)\n }\n const hBar = edgeSum / mouth.length\n if (!Number.isFinite(hBar) || hBar <= 1e-9 || inset >= hBar * 0.999) {\n throw new Error(\n `E_PUNCH_DRAFT_OVER_TAPER: draftAngle ${draft}° inset ${inset.toFixed(3)} must be < half-width ${hBar.toFixed(3)} at depth ${depth}`,\n )\n }\n const s = 1 - inset / hBar\n const endPoints = mouth.map((p) => ({\n x: cx + (p.x - cx) * s - n.x * depth,\n y: cy + (p.y - cy) * s - n.y * depth,\n z: cz + (p.z - cz) * s - n.z * depth,\n }))\n const endEdges = endPoints.map((p, i) => {\n const a = endPoints[i]!\n const b = endPoints[(i + 1) % endPoints.length]!\n return kernel.makeLineEdge(a, b) as never\n }) as BrepHandle[]\n const k = getOcctKernel()\n punch = k.loft(\n [kernel.makeWire(mouthEdges), kernel.makeWire(endEdges)] as unknown as never[],\n true,\n true,\n ) as unknown as BrepHandle\n } else {\n const prismFace = kernel.makeFace(kernel.makeWire(mouthEdges))\n punch = kernel.extrude(prismFace, -n.x * depth, -n.y * depth, -n.z * depth)\n }\n try {\n result = kernel.cut(result, punch)\n } finally {\n kernel.release(punch)\n }\n }\n return result\n}\n\n/**\n * Build a Shape: the host solid with the profile(s) punched out of `params.on`.\n * @param kernel - the BREP engine surface.\n * @param params - the punch parameters.\n * @returns the holed Shape.\n */\nexport function buildPunchHole(kernel: BrepEngineApi, params: PunchHoleParams): Shape {\n const solid = brepOf(params.on) as BrepHandle | undefined\n if (!solid) {\n throw new TopoRefError('E_TOPO_MESH_UNSUPPORTED', 'face', 'punchHole: target `on` has no BREP solid')\n }\n const holed = punchSolidOf(kernel, solid, params)\n return fromBrep(solidToShape(kernel, holed), { solid: holed })\n}\n\n/**\n * `cad.punchHole`: cut a face-placed 2D profile out of a solid.\n * @group 创建\n * @inputs 1\n * @async false\n * @qual ok\n * @name punchHole\n * @returns Shape with the profile punched out of `on`.\n * @param params.contours - ordered 2D closed contours (the hole profile).type:any[] required:true\n * @param params.on - the target solid to punch.type:Shape required:true\n * @param params.face - host face: 1-based ordinal or `cad.faceRef(on, n)`.type:any required:true\n * @param params.height - blind depth along the face inward normal (null/absent = through).type:number|object required:false\n * @param params.draftAngle - wall taper in degrees (default 0 = straight; built as an occt loft frustum).type:number required:false\n * @param params.scaleMode - UV mapping: 'original' | 'bounds' | 'native'.type:string required:false\n */\nexport const punchHole = defineOp({\n capabilities: ['directEdit'],\n engines: ['occt'],\n brep(params: Record<string, unknown>) {\n assertProfileParams(params)\n return buildPunchHole(getBrepApi(), params as unknown as PunchHoleParams)\n },\n naming: { kind: 'construct', newFaces: { via: 'explicit', vocab: [] } } as Provenance,\n})"]}
@@ -0,0 +1,85 @@
1
+ /**
2
+ * `cad.sketchOnFace`: place 2D contours onto the UV parameter space of an
3
+ * existing face (E3 placement).
4
+ *
5
+ * Unlike `sketchOnPlane` (which lifts a contour onto a whole plane *frame*),
6
+ * `sketchOnFace` lays the contour on the surface of a specific face of a target
7
+ * solid. Every 2D curve is sampled, the samples mapped into the face's `(u,v)`
8
+ * domain under a scale mode (`original` / `bounds` / `native`), the surface
9
+ * evaluated at those UVs, and a curve edge interpolated through the resulting
10
+ * 3D points (see `geometry2d/bridge/on-surface.ts`). The placed wire then
11
+ * becomes a face (`as:'face'`) ready to be extruded along the face normal, or
12
+ * is returned raw (`as:'wire'`).
13
+ *
14
+ * v1 scope: the on-surface primitive is surface-type-independent, but the result
15
+ * face is built with `makeFace` on the interpolated wire, so it is exact for
16
+ * planar faces; embedding the wire into a genuinely curved host face
17
+ * (`fixWireOnFace`) is a follow-up. The op declares `engines: ['occt']`
18
+ * (surface sampling needs the BREP platform).
19
+ * @module
20
+ */
21
+ import type { Shape } from '../mesh/types.js';
22
+ import type { BrepEngineApi } from '../brep/engine/primitives.js';
23
+ import type { BrepHandle } from '../brep/engine/types.js';
24
+ import { type FaceTopoRef } from '../topology/naming/index.js';
25
+ import { type FaceScaleMode } from '../geometry2d/bridge/on-surface.js';
26
+ import { type ProfileLoop } from './profile.js';
27
+ /** A face selector on a target solid: a 1-based ordinal (FreeCAD `FaceN`) or a `cad.faceRef` result. */
28
+ export type FaceSelector = number | FaceTopoRef;
29
+ /** Contour loops + the target face for `cad.sketchOnFace`. */
30
+ export interface SketchOnFaceParams {
31
+ /** Ordered 2D closed contours (same shape as `cad.profile` / `cad.sketchOnPlane`). */
32
+ contours: ProfileLoop[];
33
+ /** The target solid on which the host face lives. */
34
+ on: Shape;
35
+ /** The host face: a 1-based face ordinal, or a `cad.faceRef(on, n)` result. */
36
+ face: FaceSelector;
37
+ /** How 2D coords map onto the face UV space: `'original'` (identity) | `'bounds'`/`'native'` (affine-fit). */
38
+ scaleMode?: FaceScaleMode;
39
+ /** Result shape: `'face'` (default) builds a face on the contour; `'wire'` returns the outer-loop wire only. */
40
+ as?: 'face' | 'wire';
41
+ }
42
+ /**
43
+ * Resolve a face on `on` to its live BREP handle (1-based ordinal or `cad.faceRef`).
44
+ * @param kernel - the BREP engine surface.
45
+ * @param on - the target solid shape.
46
+ * @param selector - the face ordinal or `cad.faceRef` result.
47
+ * @returns the live face handle.
48
+ */
49
+ export declare function resolveFaceHandle(kernel: BrepEngineApi, on: Shape, selector: FaceSelector): BrepHandle;
50
+ /**
51
+ * Place `contours` onto the UV space of the host face and return one BREP face
52
+ * per classified contour (each outer + holes folded into a single face). Shared
53
+ * by `cad.sketchOnFace` (wrap as a Shape) and `cad.punchHole` (extrude → cut).
54
+ * @param kernel - the BREP engine surface.
55
+ * @param host - the resolved live host face handle.
56
+ * @param mode - the face-scale (UV mapping) mode.
57
+ * @param contours - ordered 2D closed contour loops to place.
58
+ * @returns the BREP faces built on the host face (one per organised entry).
59
+ */
60
+ export declare function placeContoursOnFace(kernel: BrepEngineApi, host: BrepHandle, mode: FaceScaleMode, contours: ProfileLoop[]): BrepHandle[];
61
+ /**
62
+ * Build a 3D Shape from closed contours placed on the host face.
63
+ * @param kernel - the BREP engine surface.
64
+ * @param params - contour loops + target solid + face + scale mode.
65
+ * @returns Shape on the target face (face, or the outer wire when `as:'wire'`).
66
+ */
67
+ export declare function buildSketchOnFaceWith(kernel: BrepEngineApi, params: SketchOnFaceParams): Shape;
68
+ /**
69
+ * `cad.sketchOnFace`: place 2D contours on a face of a solid and construct a Shape.
70
+ * @group 创建
71
+ * @inputs 0
72
+ * @async false
73
+ * @qual ok
74
+ * @name sketchOnFace
75
+ * @returns Shape on the target face (face, or a wire curve when `as:'wire'`).
76
+ * @param params.contours - ordered 2D closed contours (same shape as `cad.profile`).type:any[] required:true
77
+ * @param params.on - the target solid on which the face lives.type:Shape required:true
78
+ * @param params.face - the host face: a 1-based ordinal or a `cad.faceRef(on, n)` result.type:any required:true
79
+ * @param params.scaleMode - UV mapping: 'original' (identity) | 'bounds'/'native' (affine-fit).type:string required:false
80
+ * @param params.as - `'face'` (default) or `'wire'` (outer loop only).type:string required:false
81
+ */
82
+ export declare const sketchOnFace: ((params: Record<string, unknown>) => Promise<Shape>) & {
83
+ __faijs__dualOp?: import("../define-op.js").DualOpMeta;
84
+ };
85
+ //# sourceMappingURL=sketch-on-face.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sketch-on-face.d.ts","sourceRoot":"","sources":["../../src/api/sketch-on-face.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAA;AAC1C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAA;AAC9D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AAMtD,OAAO,EAAgC,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAKnF,OAAO,EAAsB,KAAK,aAAa,EAAE,MAAM,iCAAiC,CAAA;AAExF,OAAO,EAAuB,KAAK,WAAW,EAAE,MAAM,WAAW,CAAA;AAEjE,wGAAwG;AACxG,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,WAAW,CAAA;AAE/C,8DAA8D;AAC9D,MAAM,WAAW,kBAAkB;IACjC,sFAAsF;IACtF,QAAQ,EAAE,WAAW,EAAE,CAAA;IACvB,qDAAqD;IACrD,EAAE,EAAE,KAAK,CAAA;IACT,+EAA+E;IAC/E,IAAI,EAAE,YAAY,CAAA;IAClB,8GAA8G;IAC9G,SAAS,CAAC,EAAE,aAAa,CAAA;IACzB,gHAAgH;IAChH,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;CACrB;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,aAAa,EAAE,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,YAAY,GAAG,UAAU,CA0BtG;AA6BD;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,aAAa,EACrB,IAAI,EAAE,UAAU,EAChB,IAAI,EAAE,aAAa,EACnB,QAAQ,EAAE,WAAW,EAAE,GACtB,UAAU,EAAE,CAMd;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,kBAAkB,GAAG,KAAK,CAyB9F;AAED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,YAAY;;CAQvB,CAAA"}
@@ -0,0 +1,136 @@
1
+ import { getBrepApi } from '../brep/handle-bridge.js';
2
+ import { solidToShape } from '../brep/brep-ops.js';
3
+ import { fromBrep, fromBrepCurve, brepOf } from '../shape.js';
4
+ import { defineOp } from '../sdk.js';
5
+ import { TopoRefError, resolveTopoRef } from '../topology/naming/index.js';
6
+ import { buildEdgeResolutionContext } from './topo-resolve.js';
7
+ import { Blueprint } from '../geometry2d/blueprint.js';
8
+ import { CompoundBlueprint } from '../geometry2d/compound-blueprint.js';
9
+ import { organiseBlueprints } from '../geometry2d/organise.js';
10
+ import { assembleWireOnFace } from '../geometry2d/bridge/on-surface.js';
11
+ import { profileSegToCurve } from '../geometry2d/adapt.js';
12
+ import { assertProfileParams } from './profile.js';
13
+ /**
14
+ * Resolve a face on `on` to its live BREP handle (1-based ordinal or `cad.faceRef`).
15
+ * @param kernel - the BREP engine surface.
16
+ * @param on - the target solid shape.
17
+ * @param selector - the face ordinal or `cad.faceRef` result.
18
+ * @returns the live face handle.
19
+ */
20
+ export function resolveFaceHandle(kernel, on, selector) {
21
+ const solid = brepOf(on);
22
+ if (!solid) {
23
+ throw new TopoRefError('E_TOPO_MESH_UNSUPPORTED', 'face', 'sketchOnFace: target `on` has no BREP solid');
24
+ }
25
+ if (typeof selector === 'number') {
26
+ const faces = kernel.getSubShapes(solid, 'face');
27
+ if (!Number.isInteger(selector) || selector < 1 || selector > faces.length) {
28
+ throw new TopoRefError('E_TOPO_NOT_FOUND', 'face', `sketchOnFace: face ordinal ${selector} out of range [1, ${faces.length}]`);
29
+ }
30
+ return faces[selector - 1];
31
+ }
32
+ if (selector.kind !== 'face') {
33
+ throw new TopoRefError('E_TOPO_NOT_FOUND', 'face', 'sketchOnFace: selector must be a face ordinal or a face ref');
34
+ }
35
+ const ctx = buildEdgeResolutionContext(kernel, on);
36
+ if (!ctx)
37
+ throw new TopoRefError('E_TOPO_NOT_FOUND', 'face', 'sketchOnFace: input has no naming context');
38
+ const resolved = resolveTopoRef(selector, ctx);
39
+ if (resolved.handle === undefined) {
40
+ throw new TopoRefError('E_TOPO_NOT_FOUND', 'face', 'sketchOnFace: face ref resolved to no live handle');
41
+ }
42
+ return resolved.handle;
43
+ }
44
+ /**
45
+ * Make the placed face inherit the host face's outward orientation: surface
46
+ * normals must point the same way, otherwise the resulting face — and any prism
47
+ * extruded from it — carries a reversed shell whose OCCT volume collapses to ~0.
48
+ * @param kernel - the BREP engine surface.
49
+ * @param host - the host face handle (its normal is the reference).
50
+ * @param face - the face built on the host surface.
51
+ * @returns a handle to the (possibly re-oriented) face.
52
+ */
53
+ function orientLikeHost(kernel, host, face) {
54
+ const a = kernel.surfaceNormal(host, 0, 0);
55
+ const b = kernel.surfaceNormal(face, 0, 0);
56
+ const dot = a.x * b.x + a.y * b.y + a.z * b.z;
57
+ return dot < 0 ? kernel.reverseShape(face) : face;
58
+ }
59
+ /** Build one face (outer + optional holes) on the host face for an organised contour entry. */
60
+ function buildOnFace(kernel, host, mode, entry) {
61
+ if (entry instanceof CompoundBlueprint) {
62
+ let face = orientLikeHost(kernel, host, kernel.makeFace(assembleWireOnFace(kernel, host, mode, entry.blueprints[0]).wire));
63
+ const holes = entry.blueprints.slice(1).map((h) => assembleWireOnFace(kernel, host, mode, h).wire);
64
+ if (holes.length > 0)
65
+ face = kernel.addHolesInFace(face, holes);
66
+ return face;
67
+ }
68
+ return orientLikeHost(kernel, host, kernel.makeFace(assembleWireOnFace(kernel, host, mode, entry).wire));
69
+ }
70
+ /**
71
+ * Place `contours` onto the UV space of the host face and return one BREP face
72
+ * per classified contour (each outer + holes folded into a single face). Shared
73
+ * by `cad.sketchOnFace` (wrap as a Shape) and `cad.punchHole` (extrude → cut).
74
+ * @param kernel - the BREP engine surface.
75
+ * @param host - the resolved live host face handle.
76
+ * @param mode - the face-scale (UV mapping) mode.
77
+ * @param contours - ordered 2D closed contour loops to place.
78
+ * @returns the BREP faces built on the host face (one per organised entry).
79
+ */
80
+ export function placeContoursOnFace(kernel, host, mode, contours) {
81
+ const blueprints = contours.map((loop) => new Blueprint(loop.segments.map((s) => profileSegToCurve(s))));
82
+ const organised = organiseBlueprints(blueprints);
83
+ return organised.blueprints.map((entry) => buildOnFace(kernel, host, mode, entry));
84
+ }
85
+ /**
86
+ * Build a 3D Shape from closed contours placed on the host face.
87
+ * @param kernel - the BREP engine surface.
88
+ * @param params - contour loops + target solid + face + scale mode.
89
+ * @returns Shape on the target face (face, or the outer wire when `as:'wire'`).
90
+ */
91
+ export function buildSketchOnFaceWith(kernel, params) {
92
+ const host = resolveFaceHandle(kernel, params.on, params.face);
93
+ const mode = params.scaleMode ?? 'original';
94
+ const blueprints = params.contours.map((loop) => new Blueprint(loop.segments.map((s) => profileSegToCurve(s))));
95
+ const organised = organiseBlueprints(blueprints);
96
+ if (params.as === 'wire') {
97
+ const outer = organised.blueprints[0] instanceof CompoundBlueprint
98
+ ? organised.blueprints[0].blueprints[0]
99
+ : organised.blueprints[0];
100
+ const { wire } = assembleWireOnFace(kernel, host, mode, outer);
101
+ return fromBrepCurve(solidToShape(kernel, wire), { solid: wire });
102
+ }
103
+ const faces = placeContoursOnFace(kernel, host, mode, params.contours);
104
+ if (faces.length === 0)
105
+ throw new Error('sketchOnFace: no contours to place');
106
+ if (faces.length === 1) {
107
+ const f = faces[0];
108
+ return fromBrep(solidToShape(kernel, f), { solid: f });
109
+ }
110
+ const compound = kernel.makeCompound(faces);
111
+ return fromBrep(solidToShape(kernel, compound), { solid: compound });
112
+ }
113
+ /**
114
+ * `cad.sketchOnFace`: place 2D contours on a face of a solid and construct a Shape.
115
+ * @group 创建
116
+ * @inputs 0
117
+ * @async false
118
+ * @qual ok
119
+ * @name sketchOnFace
120
+ * @returns Shape on the target face (face, or a wire curve when `as:'wire'`).
121
+ * @param params.contours - ordered 2D closed contours (same shape as `cad.profile`).type:any[] required:true
122
+ * @param params.on - the target solid on which the face lives.type:Shape required:true
123
+ * @param params.face - the host face: a 1-based ordinal or a `cad.faceRef(on, n)` result.type:any required:true
124
+ * @param params.scaleMode - UV mapping: 'original' (identity) | 'bounds'/'native' (affine-fit).type:string required:false
125
+ * @param params.as - `'face'` (default) or `'wire'` (outer loop only).type:string required:false
126
+ */
127
+ export const sketchOnFace = defineOp({
128
+ capabilities: ['directEdit'],
129
+ engines: ['occt'],
130
+ brep(params) {
131
+ assertProfileParams(params);
132
+ return buildSketchOnFaceWith(getBrepApi(), params);
133
+ },
134
+ naming: { kind: 'construct', newFaces: { via: 'explicit', vocab: [] } },
135
+ });
136
+ //# sourceMappingURL=sketch-on-face.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sketch-on-face.js","sourceRoot":"","sources":["../../src/api/sketch-on-face.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `cad.sketchOnFace`: place 2D contours onto the UV parameter space of an\n * existing face (E3 placement).\n *\n * Unlike `sketchOnPlane` (which lifts a contour onto a whole plane *frame*),\n * `sketchOnFace` lays the contour on the surface of a specific face of a target\n * solid. Every 2D curve is sampled, the samples mapped into the face's `(u,v)`\n * domain under a scale mode (`original` / `bounds` / `native`), the surface\n * evaluated at those UVs, and a curve edge interpolated through the resulting\n * 3D points (see `geometry2d/bridge/on-surface.ts`). The placed wire then\n * becomes a face (`as:'face'`) ready to be extruded along the face normal, or\n * is returned raw (`as:'wire'`).\n *\n * v1 scope: the on-surface primitive is surface-type-independent, but the result\n * face is built with `makeFace` on the interpolated wire, so it is exact for\n * planar faces; embedding the wire into a genuinely curved host face\n * (`fixWireOnFace`) is a follow-up. The op declares `engines: ['occt']`\n * (surface sampling needs the BREP platform).\n * @module\n */\nimport type { Shape } from '../mesh/types'\nimport type { BrepEngineApi } from '../brep/engine/primitives'\nimport type { BrepHandle } from '../brep/engine/types'\nimport { getBrepApi } from '../brep/handle-bridge'\nimport { solidToShape } from '../brep/brep-ops'\nimport { fromBrep, fromBrepCurve, brepOf } from '../shape'\nimport { defineOp } from '../sdk'\nimport type { Provenance } from '../topology/naming/lineage'\nimport { TopoRefError, resolveTopoRef, type FaceTopoRef } from '../topology/naming'\nimport { buildEdgeResolutionContext } from './topo-resolve'\nimport { Blueprint } from '../geometry2d/blueprint'\nimport { CompoundBlueprint } from '../geometry2d/compound-blueprint'\nimport { organiseBlueprints } from '../geometry2d/organise'\nimport { assembleWireOnFace, type FaceScaleMode } from '../geometry2d/bridge/on-surface'\nimport { profileSegToCurve, type ProfileSegLike } from '../geometry2d/adapt'\nimport { assertProfileParams, type ProfileLoop } from './profile'\n\n/** A face selector on a target solid: a 1-based ordinal (FreeCAD `FaceN`) or a `cad.faceRef` result. */\nexport type FaceSelector = number | FaceTopoRef\n\n/** Contour loops + the target face for `cad.sketchOnFace`. */\nexport interface SketchOnFaceParams {\n /** Ordered 2D closed contours (same shape as `cad.profile` / `cad.sketchOnPlane`). */\n contours: ProfileLoop[]\n /** The target solid on which the host face lives. */\n on: Shape\n /** The host face: a 1-based face ordinal, or a `cad.faceRef(on, n)` result. */\n face: FaceSelector\n /** How 2D coords map onto the face UV space: `'original'` (identity) | `'bounds'`/`'native'` (affine-fit). */\n scaleMode?: FaceScaleMode\n /** Result shape: `'face'` (default) builds a face on the contour; `'wire'` returns the outer-loop wire only. */\n as?: 'face' | 'wire'\n}\n\n/**\n * Resolve a face on `on` to its live BREP handle (1-based ordinal or `cad.faceRef`).\n * @param kernel - the BREP engine surface.\n * @param on - the target solid shape.\n * @param selector - the face ordinal or `cad.faceRef` result.\n * @returns the live face handle.\n */\nexport function resolveFaceHandle(kernel: BrepEngineApi, on: Shape, selector: FaceSelector): BrepHandle {\n const solid = brepOf(on) as BrepHandle | undefined\n if (!solid) {\n throw new TopoRefError('E_TOPO_MESH_UNSUPPORTED', 'face', 'sketchOnFace: target `on` has no BREP solid')\n }\n if (typeof selector === 'number') {\n const faces = kernel.getSubShapes(solid, 'face')\n if (!Number.isInteger(selector) || selector < 1 || selector > faces.length) {\n throw new TopoRefError(\n 'E_TOPO_NOT_FOUND',\n 'face',\n `sketchOnFace: face ordinal ${selector} out of range [1, ${faces.length}]`,\n )\n }\n return faces[selector - 1] as BrepHandle\n }\n if (selector.kind !== 'face') {\n throw new TopoRefError('E_TOPO_NOT_FOUND', 'face', 'sketchOnFace: selector must be a face ordinal or a face ref')\n }\n const ctx = buildEdgeResolutionContext(kernel, on as object)\n if (!ctx) throw new TopoRefError('E_TOPO_NOT_FOUND', 'face', 'sketchOnFace: input has no naming context')\n const resolved = resolveTopoRef(selector, ctx)\n if (resolved.handle === undefined) {\n throw new TopoRefError('E_TOPO_NOT_FOUND', 'face', 'sketchOnFace: face ref resolved to no live handle')\n }\n return resolved.handle as BrepHandle\n}\n\n/**\n * Make the placed face inherit the host face's outward orientation: surface\n * normals must point the same way, otherwise the resulting face — and any prism\n * extruded from it — carries a reversed shell whose OCCT volume collapses to ~0.\n * @param kernel - the BREP engine surface.\n * @param host - the host face handle (its normal is the reference).\n * @param face - the face built on the host surface.\n * @returns a handle to the (possibly re-oriented) face.\n */\nfunction orientLikeHost(kernel: BrepEngineApi, host: BrepHandle, face: BrepHandle): BrepHandle {\n const a = kernel.surfaceNormal(host, 0, 0)\n const b = kernel.surfaceNormal(face, 0, 0)\n const dot = a.x * b.x + a.y * b.y + a.z * b.z\n return dot < 0 ? kernel.reverseShape(face) : face\n}\n\n/** Build one face (outer + optional holes) on the host face for an organised contour entry. */\nfunction buildOnFace(kernel: BrepEngineApi, host: BrepHandle, mode: FaceScaleMode, entry: Blueprint | CompoundBlueprint): BrepHandle {\n if (entry instanceof CompoundBlueprint) {\n let face = orientLikeHost(kernel, host, kernel.makeFace(assembleWireOnFace(kernel, host, mode, entry.blueprints[0]!).wire))\n const holes = entry.blueprints.slice(1).map((h) => assembleWireOnFace(kernel, host, mode, h).wire as never)\n if (holes.length > 0) face = kernel.addHolesInFace(face, holes)\n return face\n }\n return orientLikeHost(kernel, host, kernel.makeFace(assembleWireOnFace(kernel, host, mode, entry).wire))\n}\n\n/**\n * Place `contours` onto the UV space of the host face and return one BREP face\n * per classified contour (each outer + holes folded into a single face). Shared\n * by `cad.sketchOnFace` (wrap as a Shape) and `cad.punchHole` (extrude → cut).\n * @param kernel - the BREP engine surface.\n * @param host - the resolved live host face handle.\n * @param mode - the face-scale (UV mapping) mode.\n * @param contours - ordered 2D closed contour loops to place.\n * @returns the BREP faces built on the host face (one per organised entry).\n */\nexport function placeContoursOnFace(\n kernel: BrepEngineApi,\n host: BrepHandle,\n mode: FaceScaleMode,\n contours: ProfileLoop[],\n): BrepHandle[] {\n const blueprints = contours.map(\n (loop) => new Blueprint(loop.segments.map((s) => profileSegToCurve(s as ProfileSegLike))),\n )\n const organised = organiseBlueprints(blueprints)\n return organised.blueprints.map((entry) => buildOnFace(kernel, host, mode, entry))\n}\n\n/**\n * Build a 3D Shape from closed contours placed on the host face.\n * @param kernel - the BREP engine surface.\n * @param params - contour loops + target solid + face + scale mode.\n * @returns Shape on the target face (face, or the outer wire when `as:'wire'`).\n */\nexport function buildSketchOnFaceWith(kernel: BrepEngineApi, params: SketchOnFaceParams): Shape {\n const host = resolveFaceHandle(kernel, params.on, params.face)\n const mode = params.scaleMode ?? 'original'\n const blueprints = params.contours.map(\n (loop) => new Blueprint(loop.segments.map((s) => profileSegToCurve(s as ProfileSegLike))),\n )\n const organised = organiseBlueprints(blueprints)\n\n if (params.as === 'wire') {\n const outer =\n organised.blueprints[0] instanceof CompoundBlueprint\n ? organised.blueprints[0].blueprints[0]!\n : (organised.blueprints[0] as Blueprint)\n const { wire } = assembleWireOnFace(kernel, host, mode, outer)\n return fromBrepCurve(solidToShape(kernel, wire), { solid: wire })\n }\n\n const faces = placeContoursOnFace(kernel, host, mode, params.contours)\n if (faces.length === 0) throw new Error('sketchOnFace: no contours to place')\n if (faces.length === 1) {\n const f = faces[0]!\n return fromBrep(solidToShape(kernel, f), { solid: f })\n }\n const compound = kernel.makeCompound(faces)\n return fromBrep(solidToShape(kernel, compound), { solid: compound })\n}\n\n/**\n * `cad.sketchOnFace`: place 2D contours on a face of a solid and construct a Shape.\n * @group 创建\n * @inputs 0\n * @async false\n * @qual ok\n * @name sketchOnFace\n * @returns Shape on the target face (face, or a wire curve when `as:'wire'`).\n * @param params.contours - ordered 2D closed contours (same shape as `cad.profile`).type:any[] required:true\n * @param params.on - the target solid on which the face lives.type:Shape required:true\n * @param params.face - the host face: a 1-based ordinal or a `cad.faceRef(on, n)` result.type:any required:true\n * @param params.scaleMode - UV mapping: 'original' (identity) | 'bounds'/'native' (affine-fit).type:string required:false\n * @param params.as - `'face'` (default) or `'wire'` (outer loop only).type:string required:false\n */\nexport const sketchOnFace = defineOp({\n capabilities: ['directEdit'],\n engines: ['occt'],\n brep(params: Record<string, unknown>) {\n assertProfileParams(params)\n return buildSketchOnFaceWith(getBrepApi(), params as unknown as SketchOnFaceParams)\n },\n naming: { kind: 'construct', newFaces: { via: 'explicit', vocab: [] } } as Provenance,\n})"]}
@@ -0,0 +1,87 @@
1
+ /**
2
+ * `cad.sketchOnPlane`: lift 2D contours onto an arbitrary plane (E2 placement).
3
+ *
4
+ * This is the shared "placement" step of the plan's unified 3D pipeline: any 2D
5
+ * profile source (profile / draw / sketch) that describes contours as
6
+ * `ProfileLoop`s can be placed here, then extruded along the plane normal. The
7
+ * plane is a named plane (`'XY'` / `'XZ'` / `'top'` …) or an explicit frame
8
+ * `{ origin, normal, xAxis }`. The plane lift runs on the occt platform surface
9
+ * via the geometry2d bridge; the op declares `engines: ['occt']`.
10
+ * @module
11
+ */
12
+ import type { Shape } from '../mesh/types.js';
13
+ import type { BrepEngineApi } from '../brep/engine/primitives.js';
14
+ import { Blueprint } from '../geometry2d/blueprint.js';
15
+ import { type Plane, type Vec3Input } from '../geometry2d/bridge/plane.js';
16
+ import { type DrawnContours, type ProfileLoop } from './profile.js';
17
+ /** A plane given by a named plane and/or an explicit frame (coords accept both array and `{x,y,z}`). */
18
+ export interface PlaneSpec {
19
+ /** Named plane (`'XY'` / `'XZ'` / `'YZ'` / `'front'` / `'back'` / `'left'` / …). */
20
+ name?: string;
21
+ /** The plane origin (default [0,0,0]; array `[x,y,z]` or `{x,y,z}`). */
22
+ origin?: Vec3Input;
23
+ /** Explicit normal (used with an explicit frame; array or `{x,y,z}`). */
24
+ normal?: Vec3Input;
25
+ /** Explicit in-plane X axis (auto-derived when omitted; array or `{x,y,z}`). */
26
+ xAxis?: Vec3Input;
27
+ }
28
+ /** Contour loops + a target plane (+ the result mode) for `cad.sketchOnPlane`. */
29
+ export interface SketchOnPlaneParams {
30
+ /**
31
+ * Contour source: segment loops (`{segments: […]}`) or already-drawn contours
32
+ * (a `cad.draw` product, or an array of them). The drawn form is what makes the
33
+ * "draw → place → extrude" pipeline real — see {@link DrawnContours}.
34
+ */
35
+ contours: ProfileLoop[] | DrawnContours;
36
+ /** Named plane or explicit `{ origin, normal, xAxis }` frame. */
37
+ plane: PlaneSpec;
38
+ /** Result shape: 'face' (default) builds a planar face; 'wire' returns the outer-loop wire only. */
39
+ as?: 'face' | 'wire';
40
+ }
41
+ /**
42
+ * Resolve a `PlaneSpec` to a plane frame. A named plane wins over an explicit
43
+ * frame; with neither, the XY plane is used.
44
+ * @param planeSpec - the user-facing plane spec (named plane and/or explicit frame).
45
+ * @param fallbackOrigin - the origin used when `planeSpec.origin` is absent (default [0,0,0]).
46
+ * @returns the resolved plane frame.
47
+ */
48
+ export declare function resolvePlane(planeSpec: PlaneSpec, fallbackOrigin?: Vec3Input): Plane;
49
+ /**
50
+ * Build a 3D Shape (planar face, or outer-loop wire when `as:'wire'`) from a set
51
+ * of classified `Blueprint` contours on a plane. This is the shared placement
52
+ * core for every 2D profile source (`cad.profile` / `cad.draw` / sketch): it
53
+ * organises the closed contours (holes vs islands), assembles edge wires on the
54
+ * plane via the geometry2d bridge, and wraps them as a BREP face — ready to be
55
+ * extruded along the plane normal.
56
+ * @param kernel - the BREP engine surface.
57
+ * @param plane - the resolved target plane frame.
58
+ * @param blueprints - ordered closed contours as `Blueprint`s (draw output).
59
+ * @param as - `'face'` (default) or `'wire'` (outer loop only).
60
+ * @returns Shape on the target plane (face, or a wire curve when `as:'wire'`).
61
+ */
62
+ export declare function buildShapeFromBlueprints(kernel: BrepEngineApi, plane: Plane, blueprints: Blueprint[], as?: 'face' | 'wire'): Shape;
63
+ /**
64
+ * Build the 2D→3D placement: normalize the contour source (segment loops or a
65
+ * drawn contour) to a classified Blueprint set, assemble a wire on the plane,
66
+ * then wrap as a face (or the outer wire).
67
+ * @param kernel - the BREP engine surface.
68
+ * @param params - contour source + plane + mode.
69
+ * @returns Shape on the target plane (face, or a wire curve when `as:'wire'`).
70
+ */
71
+ export declare function buildSketchOnPlaneWith(kernel: BrepEngineApi, params: SketchOnPlaneParams): Shape;
72
+ /**
73
+ * `cad.sketchOnPlane`: place 2D contours on a plane and construct a Shape.
74
+ * @group 创建
75
+ * @inputs 0
76
+ * @async false
77
+ * @qual ok
78
+ * @name sketchOnPlane
79
+ * @returns Shape on the target plane (face, or a wire curve when `as:'wire'`).
80
+ * @param params.contours - ordered 2D contours (same shape as `cad.profile`: segment loops, or a `cad.draw` product).type:any required:true
81
+ * @param params.plane - named plane (`'XY'` / `'XZ'`…) or `{ origin, normal, xAxis }`.type:any required:true
82
+ * @param params.as - `'face'` (default) or `'wire'` (outer loop only).type:string required:false
83
+ */
84
+ export declare const sketchOnPlane: ((params: Record<string, unknown>) => Promise<Shape>) & {
85
+ __faijs__dualOp?: import("../define-op.js").DualOpMeta;
86
+ };
87
+ //# sourceMappingURL=sketch-on-plane.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sketch-on-plane.d.ts","sourceRoot":"","sources":["../../src/api/sketch-on-plane.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAA;AAC1C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAA;AAM9D,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AAGnD,OAAO,EAAiC,KAAK,KAAK,EAAE,KAAK,SAAS,EAAE,MAAM,4BAA4B,CAAA;AAEtG,OAAO,EAA4C,KAAK,aAAa,EAAE,KAAK,WAAW,EAAE,MAAM,WAAW,CAAA;AAE1G,wGAAwG;AACxG,MAAM,WAAW,SAAS;IACxB,oFAAoF;IACpF,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,wEAAwE;IACxE,MAAM,CAAC,EAAE,SAAS,CAAA;IAClB,yEAAyE;IACzE,MAAM,CAAC,EAAE,SAAS,CAAA;IAClB,gFAAgF;IAChF,KAAK,CAAC,EAAE,SAAS,CAAA;CAClB;AAED,kFAAkF;AAClF,MAAM,WAAW,mBAAmB;IAClC;;;;OAIG;IACH,QAAQ,EAAE,WAAW,EAAE,GAAG,aAAa,CAAA;IACvC,iEAAiE;IACjE,KAAK,EAAE,SAAS,CAAA;IAChB,oGAAoG;IACpG,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;CACrB;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,SAAS,EAAE,SAAS,EAAE,cAAc,GAAE,SAAqB,GAAG,KAAK,CAK/F;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,aAAa,EACrB,KAAK,EAAE,KAAK,EACZ,UAAU,EAAE,SAAS,EAAE,EACvB,EAAE,GAAE,MAAM,GAAG,MAAe,GAC3B,KAAK,CAgCP;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,mBAAmB,GAAG,KAAK,CAQhG;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,aAAa;;CAQxB,CAAA"}