@faicad/faijs 0.18.3 → 0.19.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/api/api-namespace.d.ts.map +1 -1
- package/dist/api/api-namespace.js +4 -1
- package/dist/api/api-namespace.js.map +1 -1
- package/dist/api/brep-mirror/joinery-brep.d.ts +1 -1
- package/dist/api/brep-mirror/joinery-brep.d.ts.map +1 -1
- package/dist/api/brep-mirror/joinery-brep.js +19 -19
- package/dist/api/brep-mirror/joinery-brep.js.map +1 -1
- package/dist/api/index.d.ts +4 -0
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +3 -0
- package/dist/api/index.js.map +1 -1
- package/dist/api/profile.d.ts.map +1 -1
- package/dist/api/profile.js +113 -182
- package/dist/api/profile.js.map +1 -1
- package/dist/api/punch-hole.d.ts +74 -0
- package/dist/api/punch-hole.d.ts.map +1 -0
- package/dist/api/punch-hole.js +207 -0
- package/dist/api/punch-hole.js.map +1 -0
- package/dist/api/sketch-on-face.d.ts +85 -0
- package/dist/api/sketch-on-face.d.ts.map +1 -0
- package/dist/api/sketch-on-face.js +136 -0
- package/dist/api/sketch-on-face.js.map +1 -0
- package/dist/api/sketch-on-plane.d.ts +81 -0
- package/dist/api/sketch-on-plane.d.ts.map +1 -0
- package/dist/api/sketch-on-plane.js +103 -0
- package/dist/api/sketch-on-plane.js.map +1 -0
- package/dist/boolean/csg-backend.d.ts +6 -6
- package/dist/boolean/csg-backend.d.ts.map +1 -1
- package/dist/boolean/csg-backend.js +9 -9
- package/dist/boolean/csg-backend.js.map +1 -1
- package/dist/boolean/csg-core.d.ts +10 -10
- package/dist/boolean/csg-core.d.ts.map +1 -1
- package/dist/boolean/csg-core.js +24 -24
- package/dist/boolean/csg-core.js.map +1 -1
- package/dist/boolean/extrude-helpers.d.ts +8 -8
- package/dist/boolean/extrude-helpers.d.ts.map +1 -1
- package/dist/boolean/extrude-helpers.js +13 -13
- package/dist/boolean/extrude-helpers.js.map +1 -1
- package/dist/brep/brep-ops.d.ts +20 -6
- package/dist/brep/brep-ops.d.ts.map +1 -1
- package/dist/brep/brep-ops.js +38 -12
- package/dist/brep/brep-ops.js.map +1 -1
- package/dist/browser-host/csg-worker-protocol.d.ts +3 -3
- package/dist/browser-host/csg-worker-protocol.d.ts.map +1 -1
- package/dist/browser-host/csg-worker-protocol.js.map +1 -1
- package/dist/browser-host/csg-worker.js +3 -3
- package/dist/browser-host/csg-worker.js.map +1 -1
- package/dist/browser-host/inline-csg-backend.d.ts +3 -3
- package/dist/browser-host/inline-csg-backend.d.ts.map +1 -1
- package/dist/browser-host/inline-csg-backend.js +3 -3
- package/dist/browser-host/inline-csg-backend.js.map +1 -1
- package/dist/browser-host/worker-csg-backend.d.ts +3 -3
- package/dist/browser-host/worker-csg-backend.d.ts.map +1 -1
- package/dist/browser-host/worker-csg-backend.js.map +1 -1
- package/dist/cad-runtime/ports.d.ts +3 -3
- package/dist/cad-runtime/ports.d.ts.map +1 -1
- package/dist/cad-runtime/ports.js.map +1 -1
- package/dist/geometry2d/adapt.d.ts +46 -0
- package/dist/geometry2d/adapt.d.ts.map +1 -0
- package/dist/geometry2d/adapt.js +41 -0
- package/dist/geometry2d/adapt.js.map +1 -0
- package/dist/geometry2d/bbox2d.d.ts +82 -0
- package/dist/geometry2d/bbox2d.d.ts.map +1 -0
- package/dist/geometry2d/bbox2d.js +123 -0
- package/dist/geometry2d/bbox2d.js.map +1 -0
- package/dist/geometry2d/blueprint-sketcher.d.ts +27 -0
- package/dist/geometry2d/blueprint-sketcher.d.ts.map +1 -0
- package/dist/geometry2d/blueprint-sketcher.js +29 -0
- package/dist/geometry2d/blueprint-sketcher.js.map +1 -0
- package/dist/geometry2d/blueprint.d.ts +102 -0
- package/dist/geometry2d/blueprint.d.ts.map +1 -0
- package/dist/geometry2d/blueprint.js +225 -0
- package/dist/geometry2d/blueprint.js.map +1 -0
- package/dist/geometry2d/blueprints.d.ts +58 -0
- package/dist/geometry2d/blueprints.d.ts.map +1 -0
- package/dist/geometry2d/blueprints.js +72 -0
- package/dist/geometry2d/blueprints.js.map +1 -0
- package/dist/geometry2d/bridge/lift-on-plane.d.ts +50 -0
- package/dist/geometry2d/bridge/lift-on-plane.d.ts.map +1 -0
- package/dist/geometry2d/bridge/lift-on-plane.js +72 -0
- package/dist/geometry2d/bridge/lift-on-plane.js.map +1 -0
- package/dist/geometry2d/bridge/on-surface.d.ts +98 -0
- package/dist/geometry2d/bridge/on-surface.d.ts.map +1 -0
- package/dist/geometry2d/bridge/on-surface.js +94 -0
- package/dist/geometry2d/bridge/on-surface.js.map +1 -0
- package/dist/geometry2d/bridge/plane.d.ts +69 -0
- package/dist/geometry2d/bridge/plane.d.ts.map +1 -0
- package/dist/geometry2d/bridge/plane.js +96 -0
- package/dist/geometry2d/bridge/plane.js.map +1 -0
- package/dist/geometry2d/canned-blueprints.d.ts +44 -0
- package/dist/geometry2d/canned-blueprints.d.ts.map +1 -0
- package/dist/geometry2d/canned-blueprints.js +94 -0
- package/dist/geometry2d/canned-blueprints.js.map +1 -0
- package/dist/geometry2d/compound-blueprint.d.ts +72 -0
- package/dist/geometry2d/compound-blueprint.d.ts.map +1 -0
- package/dist/geometry2d/compound-blueprint.js +95 -0
- package/dist/geometry2d/compound-blueprint.js.map +1 -0
- package/dist/geometry2d/curve2d.d.ts +298 -0
- package/dist/geometry2d/curve2d.d.ts.map +1 -0
- package/dist/geometry2d/curve2d.js +1110 -0
- package/dist/geometry2d/curve2d.js.map +1 -0
- package/dist/geometry2d/index.d.ts +22 -0
- package/dist/geometry2d/index.d.ts.map +1 -0
- package/dist/geometry2d/index.js +21 -0
- package/dist/geometry2d/index.js.map +1 -0
- package/dist/geometry2d/organise.d.ts +28 -0
- package/dist/geometry2d/organise.d.ts.map +1 -0
- package/dist/geometry2d/organise.js +108 -0
- package/dist/geometry2d/organise.js.map +1 -0
- package/dist/geometry2d/pen-sketcher.d.ts +340 -0
- package/dist/geometry2d/pen-sketcher.d.ts.map +1 -0
- package/dist/geometry2d/pen-sketcher.js +477 -0
- package/dist/geometry2d/pen-sketcher.js.map +1 -0
- package/dist/geometry2d/point.d.ts +44 -0
- package/dist/geometry2d/point.d.ts.map +1 -0
- package/dist/geometry2d/point.js +57 -0
- package/dist/geometry2d/point.js.map +1 -0
- package/dist/geometry2d/svg-ellipse.d.ts +74 -0
- package/dist/geometry2d/svg-ellipse.d.ts.map +1 -0
- package/dist/geometry2d/svg-ellipse.js +163 -0
- package/dist/geometry2d/svg-ellipse.js.map +1 -0
- package/dist/lang/symbol-table.generated.d.ts +3 -0
- package/dist/lang/symbol-table.generated.d.ts.map +1 -1
- package/dist/lang/symbol-table.generated.js +3 -0
- package/dist/lang/symbol-table.generated.js.map +1 -1
- package/dist/mesh/types.d.ts +1 -1
- package/dist/mesh/types.d.ts.map +1 -1
- package/dist/mesh/types.js.map +1 -1
- package/package.json +9 -1
|
@@ -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 * `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,81 @@
|
|
|
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 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
|
+
contours: ProfileLoop[];
|
|
31
|
+
/** Named plane or explicit `{ origin, normal, xAxis }` frame. */
|
|
32
|
+
plane: PlaneSpec;
|
|
33
|
+
/** Result shape: 'face' (default) builds a planar face; 'wire' returns the outer-loop wire only. */
|
|
34
|
+
as?: 'face' | 'wire';
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Resolve a `PlaneSpec` to a plane frame. A named plane wins over an explicit
|
|
38
|
+
* frame; with neither, the XY plane is used.
|
|
39
|
+
* @param planeSpec - the user-facing plane spec (named plane and/or explicit frame).
|
|
40
|
+
* @param fallbackOrigin - the origin used when `planeSpec.origin` is absent (default [0,0,0]).
|
|
41
|
+
* @returns the resolved plane frame.
|
|
42
|
+
*/
|
|
43
|
+
export declare function resolvePlane(planeSpec: PlaneSpec, fallbackOrigin?: Vec3Input): Plane;
|
|
44
|
+
/**
|
|
45
|
+
* Build a 3D Shape (planar face, or outer-loop wire when `as:'wire'`) from a set
|
|
46
|
+
* of classified `Blueprint` contours on a plane. This is the shared placement
|
|
47
|
+
* core for every 2D profile source (`cad.profile` / `cad.draw` / sketch): it
|
|
48
|
+
* organises the closed contours (holes vs islands), assembles edge wires on the
|
|
49
|
+
* plane via the geometry2d bridge, and wraps them as a BREP face — ready to be
|
|
50
|
+
* extruded along the plane normal.
|
|
51
|
+
* @param kernel - the BREP engine surface.
|
|
52
|
+
* @param plane - the resolved target plane frame.
|
|
53
|
+
* @param blueprints - ordered closed contours as `Blueprint`s (draw output).
|
|
54
|
+
* @param as - `'face'` (default) or `'wire'` (outer loop only).
|
|
55
|
+
* @returns Shape on the target plane (face, or a wire curve when `as:'wire'`).
|
|
56
|
+
*/
|
|
57
|
+
export declare function buildShapeFromBlueprints(kernel: BrepEngineApi, plane: Plane, blueprints: Blueprint[], as?: 'face' | 'wire'): Shape;
|
|
58
|
+
/**
|
|
59
|
+
* Build the 2D→3D placement: convert contour loops to a classified Blueprint
|
|
60
|
+
* set, assemble a wire on the plane, then wrap as a face (or the outer wire).
|
|
61
|
+
* @param kernel - the BREP engine surface.
|
|
62
|
+
* @param params - contour loops + plane + mode.
|
|
63
|
+
* @returns Shape on the target plane (face, or a wire curve when `as:'wire'`).
|
|
64
|
+
*/
|
|
65
|
+
export declare function buildSketchOnPlaneWith(kernel: BrepEngineApi, params: SketchOnPlaneParams): Shape;
|
|
66
|
+
/**
|
|
67
|
+
* `cad.sketchOnPlane`: place 2D contours on a plane and construct a Shape.
|
|
68
|
+
* @group 创建
|
|
69
|
+
* @inputs 0
|
|
70
|
+
* @async false
|
|
71
|
+
* @qual ok
|
|
72
|
+
* @name sketchOnPlane
|
|
73
|
+
* @returns Shape on the target plane (face, or a wire curve when `as:'wire'`).
|
|
74
|
+
* @param params.contours - ordered 2D contours (same shape as `cad.profile`).type:any[] required:true
|
|
75
|
+
* @param params.plane - named plane (`'XY'` / `'XZ'`…) or `{ origin, normal, xAxis }`.type:any required:true
|
|
76
|
+
* @param params.as - `'face'` (default) or `'wire'` (outer loop only).type:string required:false
|
|
77
|
+
*/
|
|
78
|
+
export declare const sketchOnPlane: ((params: Record<string, unknown>) => Promise<Shape>) & {
|
|
79
|
+
__faijs__dualOp?: import("../define-op.js").DualOpMeta;
|
|
80
|
+
};
|
|
81
|
+
//# 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;AAGtG,OAAO,EAAuB,KAAK,WAAW,EAAE,MAAM,WAAW,CAAA;AAEjE,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,QAAQ,EAAE,WAAW,EAAE,CAAA;IACvB,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;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,mBAAmB,GAAG,KAAK,CAMhG;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,aAAa;;CAQxB,CAAA"}
|