okgeometry-api 1.14.0 → 1.16.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/Mesh.js CHANGED
@@ -2159,6 +2159,151 @@ export class Mesh {
2159
2159
  const outside = readGroup();
2160
2160
  return { inside, outside };
2161
2161
  }
2162
+ /**
2163
+ * Compute all transversal crossing points of a polyline with this mesh.
2164
+ * Works on OPEN meshes (sheets) — unlike `clipPolyline`, the mesh is NOT
2165
+ * required to be a closed volume.
2166
+ *
2167
+ * Hits are deduplicated (shared-edge / shared-vertex crossings merge into
2168
+ * one, keeping the smallest triangle index) and sorted by
2169
+ * (segmentIndex, segmentT). Coplanar sliding overlaps yield no hits.
2170
+ * Segment `i` runs from point `i` to point `(i + 1) % n`; open polylines
2171
+ * have n − 1 segments, closed ones n (the wrap segment).
2172
+ *
2173
+ * @param polyline - Polyline (closedness inferred) or raw point list
2174
+ * @param closed - Treat the points as a closed loop (defaults to the
2175
+ * polyline's own closedness; required when passing a raw point list)
2176
+ * @returns Crossings with the 3D point, segment index/parameter, and
2177
+ * triangle index
2178
+ */
2179
+ intersectPolyline(polyline, closed) {
2180
+ ensureInit();
2181
+ const { flat, isClosed } = Mesh.flattenPolylineArg(polyline, closed, "Mesh.intersectPolyline");
2182
+ const buf = wasm.mesh_polyline_intersection_points(this._vertexCount, this._buffer, flat, isClosed);
2183
+ if (buf.length === 0) {
2184
+ throw new Error("Mesh.intersectPolyline failed — the mesh and polyline must be non-degenerate.");
2185
+ }
2186
+ const count = buf[0];
2187
+ const hits = [];
2188
+ for (let i = 0; i < count; i++) {
2189
+ const off = 1 + i * 6;
2190
+ hits.push({
2191
+ point: new Point(buf[off], buf[off + 1], buf[off + 2]),
2192
+ segmentIndex: buf[off + 3],
2193
+ segmentT: buf[off + 4],
2194
+ triangleIndex: buf[off + 5],
2195
+ });
2196
+ }
2197
+ return hits;
2198
+ }
2199
+ /**
2200
+ * Split a polyline into pieces at every crossing with this mesh (open
2201
+ * sheets allowed). Hit points become the shared endpoints of adjacent
2202
+ * pieces; a closed loop is opened at the first crossing (N crossings → N
2203
+ * pieces), an open polyline yields N + 1 pieces. With no crossings the
2204
+ * original polyline comes back as one piece. Pieces are NOT classified —
2205
+ * use `clipPolyline` (closed volume, containment) or
2206
+ * `clipPolylineBySheet` (oriented sheet, signed side) for classification.
2207
+ *
2208
+ * @param polyline - Polyline (closedness inferred) or raw point list
2209
+ * @param closed - Treat the points as a closed loop (defaults to the
2210
+ * polyline's own closedness; required when passing a raw point list)
2211
+ * @returns Polyline pieces in order along the input
2212
+ */
2213
+ splitPolyline(polyline, closed) {
2214
+ ensureInit();
2215
+ const { flat, isClosed } = Mesh.flattenPolylineArg(polyline, closed, "Mesh.splitPolyline");
2216
+ const buf = wasm.mesh_split_polyline(this._vertexCount, this._buffer, flat, isClosed);
2217
+ if (buf.length === 0) {
2218
+ throw new Error("Mesh.splitPolyline failed — the mesh and polyline must be non-degenerate.");
2219
+ }
2220
+ return parsePolylineBuf(buf).map(pts => new Polyline(pts));
2221
+ }
2222
+ /**
2223
+ * Clip a polyline by this mesh treated as an OPEN oriented sheet: the
2224
+ * polyline is split at every crossing and each piece is classified by the
2225
+ * SIGNED SIDE of the sheet — `inside` = in FRONT of the sheet normal,
2226
+ * `outside` = behind it (the mesh's stored winding defines the normals).
2227
+ *
2228
+ * This is side classification, NOT containment — unlike `clipPolyline`,
2229
+ * which requires a CLOSED volume: the sheet may be open, and pieces far
2230
+ * from it still classify deterministically by the nearest facet's normal.
2231
+ * Pieces lying ON the sheet default to `inside`. Adjacent same-side pieces
2232
+ * (e.g. from a touch, not a crossing) merge back into one run; closed
2233
+ * loops merge the run spanning the seam.
2234
+ *
2235
+ * @param polyline - Polyline (closedness inferred) or raw point list
2236
+ * @param closed - Treat the points as a closed loop (defaults to the
2237
+ * polyline's own closedness; required when passing a raw point list)
2238
+ */
2239
+ clipPolylineBySheet(polyline, closed) {
2240
+ ensureInit();
2241
+ const { flat, isClosed } = Mesh.flattenPolylineArg(polyline, closed, "Mesh.clipPolylineBySheet");
2242
+ const buf = wasm.mesh_clip_polyline_by_sheet(this._vertexCount, this._buffer, flat, isClosed);
2243
+ if (buf.length === 0) {
2244
+ throw new Error("Mesh.clipPolylineBySheet failed — the sheet and polyline must be non-degenerate.");
2245
+ }
2246
+ // Two concatenated polyline groups: inside first, then outside — same
2247
+ // packing as clipPolyline.
2248
+ let idx = 0;
2249
+ const readGroup = () => {
2250
+ const group = [];
2251
+ const count = buf[idx++];
2252
+ for (let i = 0; i < count; i++) {
2253
+ const n = buf[idx++];
2254
+ const points = [];
2255
+ for (let k = 0; k < n; k++) {
2256
+ points.push(new Point(buf[idx], buf[idx + 1], buf[idx + 2]));
2257
+ idx += 3;
2258
+ }
2259
+ if (points.length >= 2)
2260
+ group.push(new Polyline(points));
2261
+ }
2262
+ return group;
2263
+ };
2264
+ const inside = readGroup();
2265
+ const outside = readGroup();
2266
+ return { inside, outside };
2267
+ }
2268
+ /**
2269
+ * Flatten a Polyline-or-points argument into WASM coords + closed flag.
2270
+ *
2271
+ * Closed inputs following the duplicated-seam convention (terminal point
2272
+ * repeating the first) have the duplicate STRIPPED before crossing the
2273
+ * boundary — the kernel's closed-polyline encoding is seam-free (the wrap
2274
+ * segment is implicit), and a duplicated seam would add a zero-length wrap
2275
+ * segment / doubled seam vertex to split pieces.
2276
+ */
2277
+ static flattenPolylineArg(polyline, closed, context) {
2278
+ let pts = Array.isArray(polyline) ? polyline : polyline.points;
2279
+ if (pts.length < 2) {
2280
+ throw new Error(`${context} needs at least 2 polyline points`);
2281
+ }
2282
+ const isClosed = closed ?? (!Array.isArray(polyline) && polyline.isClosed());
2283
+ if (isClosed && pts.length > 2) {
2284
+ // Scale-relative epsilon for the duplicate-seam test.
2285
+ let maxAbs = 0;
2286
+ for (const p of pts) {
2287
+ maxAbs = Math.max(maxAbs, Math.abs(p.x), Math.abs(p.y), Math.abs(p.z));
2288
+ }
2289
+ const eps = (1 + maxAbs) * 1e-12;
2290
+ const first = pts[0];
2291
+ const last = pts[pts.length - 1];
2292
+ const dx = first.x - last.x;
2293
+ const dy = first.y - last.y;
2294
+ const dz = first.z - last.z;
2295
+ if (Math.sqrt(dx * dx + dy * dy + dz * dz) <= eps) {
2296
+ pts = pts.slice(0, -1);
2297
+ }
2298
+ }
2299
+ const flat = new Float64Array(pts.length * 3);
2300
+ for (let i = 0; i < pts.length; i++) {
2301
+ flat[i * 3] = pts[i].x;
2302
+ flat[i * 3 + 1] = pts[i].y;
2303
+ flat[i * 3 + 2] = pts[i].z;
2304
+ }
2305
+ return { flat, isClosed };
2306
+ }
2162
2307
  /**
2163
2308
  * Apply a 4x4 transformation matrix.
2164
2309
  * @param matrix - Row-major flat array of 16 numbers