brepjs 18.79.0 → 18.80.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/brepjs.cjs CHANGED
@@ -2286,10 +2286,122 @@ function solveAngle(ref, dep, angleRad) {
2286
2286
  rotation: require_threadFns.quatFromAxisAngle(Math.hypot(c[0], c[1], c[2]) < 1e-9 ? anyPerpendicular(nDep) : c, phi - angleRad)
2287
2287
  };
2288
2288
  }
2289
- /** Entity types each positioning constraint requires of (entityA, entityB). */
2289
+ /**
2290
+ * Point-point coincident/distance: place the dependent point on the reference
2291
+ * point (`extra` = 0) or at `extra` along the original separation direction.
2292
+ * If the points already coincide the direction is arbitrary (+X).
2293
+ */
2294
+ function solvePointPair(refOrigin, depOrigin, extra) {
2295
+ const sep = sub(depOrigin, refOrigin);
2296
+ const len = Math.hypot(sep[0], sep[1], sep[2]);
2297
+ return {
2298
+ position: sub(add$1(refOrigin, scale$2(len < 1e-9 ? [
2299
+ 1,
2300
+ 0,
2301
+ 0
2302
+ ] : scale$2(sep, 1 / len), extra)), depOrigin),
2303
+ rotation: IDENTITY_ROTATION
2304
+ };
2305
+ }
2306
+ /**
2307
+ * Move the dependent plane (normal `depNormal`, at the origin) so its signed
2308
+ * distance from the reference point equals `extra` (the mirror of `solvePlanePair`
2309
+ * for a point reference and a plane dependent).
2310
+ */
2311
+ function solvePlaneToPoint(depNormal, refOrigin, depOrigin, extra) {
2312
+ const n = normalize(depNormal);
2313
+ return {
2314
+ position: scale$2(n, dot(n, sub(refOrigin, depOrigin)) - extra),
2315
+ rotation: IDENTITY_ROTATION
2316
+ };
2317
+ }
2318
+ /**
2319
+ * Reference axis, dependent point: drop the point onto the axis line
2320
+ * (`extra` = 0) or place it at radial distance `extra` from the line, keeping
2321
+ * its along-axis position. A point already on the axis gets an arbitrary radial.
2322
+ */
2323
+ function solveAxisToPoint(ref, depOrigin, extra) {
2324
+ const d = normalize(ref.direction ?? [
2325
+ 0,
2326
+ 0,
2327
+ 1
2328
+ ]);
2329
+ const foot = add$1(ref.origin, scale$2(d, dot(d, sub(depOrigin, ref.origin))));
2330
+ if (extra === 0) return {
2331
+ position: sub(foot, depOrigin),
2332
+ rotation: IDENTITY_ROTATION
2333
+ };
2334
+ const radial = sub(depOrigin, foot);
2335
+ const rlen = Math.hypot(radial[0], radial[1], radial[2]);
2336
+ return {
2337
+ position: sub(add$1(foot, scale$2(rlen < 1e-9 ? anyPerpendicular(d) : scale$2(radial, 1 / rlen), extra)), depOrigin),
2338
+ rotation: IDENTITY_ROTATION
2339
+ };
2340
+ }
2341
+ /**
2342
+ * Reference point, dependent axis: translate the axis line so it passes through
2343
+ * the point (`extra` = 0) or lies at perpendicular distance `extra` from it.
2344
+ * Translation is purely perpendicular to the axis, so the line's direction and
2345
+ * along-axis parameterization are preserved.
2346
+ */
2347
+ function solvePointToAxis(refOrigin, dep, extra) {
2348
+ const d = normalize(dep.direction ?? [
2349
+ 0,
2350
+ 0,
2351
+ 1
2352
+ ]);
2353
+ const w = sub(dep.origin, refOrigin);
2354
+ const perp = sub(w, scale$2(d, dot(d, w)));
2355
+ const plen = Math.hypot(perp[0], perp[1], perp[2]);
2356
+ if (extra === 0) return {
2357
+ position: scale$2(perp, -1),
2358
+ rotation: IDENTITY_ROTATION
2359
+ };
2360
+ return {
2361
+ position: sub(scale$2(plen < 1e-9 ? anyPerpendicular(d) : scale$2(perp, 1 / plen), extra), perp),
2362
+ rotation: IDENTITY_ROTATION
2363
+ };
2364
+ }
2365
+ /**
2366
+ * Axis-axis distance: align the dependent axis parallel to the reference, then
2367
+ * offset it to perpendicular distance `extra` (parallel pin-and-spacer). With
2368
+ * `extra` = 0 the axes become collinear, matching `concentric`.
2369
+ */
2370
+ function solveAxisAxisDistance(ref, dep, extra) {
2371
+ const dRef = normalize(ref.direction ?? [
2372
+ 0,
2373
+ 0,
2374
+ 1
2375
+ ]);
2376
+ const rotation = require_threadFns.quatFromTo(normalize(dep.direction ?? [
2377
+ 0,
2378
+ 0,
2379
+ 1
2380
+ ]), dRef);
2381
+ const w = sub(require_threadFns.quatRotate(rotation, dep.origin), ref.origin);
2382
+ const perp = sub(w, scale$2(dRef, dot(dRef, w)));
2383
+ const plen = Math.hypot(perp[0], perp[1], perp[2]);
2384
+ return {
2385
+ position: sub(scale$2(plen < 1e-9 ? anyPerpendicular(dRef) : scale$2(perp, 1 / plen), extra), perp),
2386
+ rotation
2387
+ };
2388
+ }
2389
+ /**
2390
+ * Supported entity-type pairs for the translational mates (`coincident` /
2391
+ * `distance`), keyed `${entityA}-${entityB}`. Both orders are listed where the
2392
+ * solver handles them, so a user need not pre-order the entities.
2393
+ */
2394
+ var TRANSLATIONAL_PAIRS = new Set([
2395
+ "plane-plane",
2396
+ "plane-point",
2397
+ "point-plane",
2398
+ "point-point",
2399
+ "axis-axis",
2400
+ "axis-point",
2401
+ "point-axis"
2402
+ ]);
2403
+ /** Entity types the orientation/axis mates require of (entityA, entityB). */
2290
2404
  var REQUIRED_ENTITIES = {
2291
- coincident: "plane",
2292
- distance: "plane",
2293
2405
  angle: "plane",
2294
2406
  concentric: "axis"
2295
2407
  };
@@ -2299,21 +2411,51 @@ var POSITIONING_TYPES = new Set([
2299
2411
  "angle",
2300
2412
  "concentric"
2301
2413
  ]);
2414
+ /** Whether a positioning mate's entity pair is solvable. */
2415
+ function isSupportedPair(type, a, b) {
2416
+ const required = REQUIRED_ENTITIES[type];
2417
+ if (required) return a === required && b === required;
2418
+ return TRANSLATIONAL_PAIRS.has(`${a}-${b}`);
2419
+ }
2420
+ /**
2421
+ * Solve a `coincident` (extra = 0) or `distance` (extra = value) mate for any
2422
+ * supported entity-type pair. `ref` is already in world space; the dependent is
2423
+ * at the origin. Returns null only for an unsupported pair (filtered out
2424
+ * upstream by `isSupportedPair`).
2425
+ */
2426
+ function solveTranslational(ref, dep, extra) {
2427
+ switch (`${ref.type}-${dep.type}`) {
2428
+ case "plane-plane":
2429
+ case "plane-point": return solvePlanePair(ref, dep, extra);
2430
+ case "point-plane": return solvePlaneToPoint(dep.normal ?? [
2431
+ 0,
2432
+ 0,
2433
+ 1
2434
+ ], ref.origin, dep.origin, extra);
2435
+ case "point-point": return solvePointPair(ref.origin, dep.origin, extra);
2436
+ case "axis-axis": return extra === 0 ? solveConcentric(ref, dep) : solveAxisAxisDistance(ref, dep, extra);
2437
+ case "axis-point": return solveAxisToPoint(ref, dep.origin, extra);
2438
+ case "point-axis": return solvePointToAxis(ref.origin, dep, extra);
2439
+ default: return null;
2440
+ }
2441
+ }
2302
2442
  /** Dispatch a positioning mate to its solver. `ref` is already in world space. */
2303
2443
  function solveMate(c, ref, dep) {
2304
2444
  switch (c.type) {
2305
2445
  case "concentric": return solveConcentric(ref, dep);
2306
2446
  case "angle": return solveAngle(ref, dep, (c.value ?? 0) * Math.PI / 180);
2307
- case "distance": return solvePlanePair(ref, dep, c.value ?? 0);
2308
- default: return solvePlanePair(ref, dep, 0);
2447
+ case "distance": return solveTranslational(ref, dep, c.value ?? 0) ?? solvePlanePair(ref, dep, c.value ?? 0);
2448
+ default: return solveTranslational(ref, dep, 0) ?? solvePlanePair(ref, dep, 0);
2309
2449
  }
2310
2450
  }
2311
2451
  /**
2312
2452
  * Solve assembly constraints analytically.
2313
2453
  *
2314
- * Handles: fixed, coincident/distance (plane-plane), concentric (axis-axis), and
2315
- * angle (plane-plane orientation). For a positioning mate, entityA is the
2316
- * reference and entityB the dependent. Chain roots (nodes never positioned by a
2454
+ * Handles: fixed, concentric (axis-axis), angle (plane-plane orientation), and
2455
+ * coincident/distance for any supported entity-type pair (plane-plane,
2456
+ * plane-point, point-point, axis-axis, axis-point, and both point orders see
2457
+ * `TRANSLATIONAL_PAIRS`). For a positioning mate, entityA is the reference and
2458
+ * entityB the dependent. Chain roots (nodes never positioned by a
2317
2459
  * mate) and explicit `fixed` nodes anchor at the origin; constraints then resolve
2318
2460
  * in topological order — each places its dependent against the reference's solved
2319
2461
  * pose (rotation included), so multi-body chains compose. Returns
@@ -2340,9 +2482,10 @@ function solveConstraints(nodes, constraints) {
2340
2482
  const pending = [];
2341
2483
  for (const c of positioning) {
2342
2484
  if (!c.entityA || !c.entityB) continue;
2343
- const required = REQUIRED_ENTITIES[c.type];
2344
- if (c.entityA.entity.type !== required || c.entityB.entity.type !== required) {
2345
- unsupported.push(`${c.type}(${c.entityA.entity.type}-${c.entityB.entity.type})`);
2485
+ const a = c.entityA.entity.type;
2486
+ const b = c.entityB.entity.type;
2487
+ if (!isSupportedPair(c.type, a, b)) {
2488
+ unsupported.push(`${c.type}(${a}-${b})`);
2346
2489
  continue;
2347
2490
  }
2348
2491
  pending.push(c);
package/dist/brepjs.js CHANGED
@@ -2297,10 +2297,122 @@ function solveAngle(ref, dep, angleRad) {
2297
2297
  rotation: quatFromAxisAngle(Math.hypot(c[0], c[1], c[2]) < 1e-9 ? anyPerpendicular(nDep) : c, phi - angleRad)
2298
2298
  };
2299
2299
  }
2300
- /** Entity types each positioning constraint requires of (entityA, entityB). */
2300
+ /**
2301
+ * Point-point coincident/distance: place the dependent point on the reference
2302
+ * point (`extra` = 0) or at `extra` along the original separation direction.
2303
+ * If the points already coincide the direction is arbitrary (+X).
2304
+ */
2305
+ function solvePointPair(refOrigin, depOrigin, extra) {
2306
+ const sep = sub(depOrigin, refOrigin);
2307
+ const len = Math.hypot(sep[0], sep[1], sep[2]);
2308
+ return {
2309
+ position: sub(add$1(refOrigin, scale$2(len < 1e-9 ? [
2310
+ 1,
2311
+ 0,
2312
+ 0
2313
+ ] : scale$2(sep, 1 / len), extra)), depOrigin),
2314
+ rotation: IDENTITY_ROTATION
2315
+ };
2316
+ }
2317
+ /**
2318
+ * Move the dependent plane (normal `depNormal`, at the origin) so its signed
2319
+ * distance from the reference point equals `extra` (the mirror of `solvePlanePair`
2320
+ * for a point reference and a plane dependent).
2321
+ */
2322
+ function solvePlaneToPoint(depNormal, refOrigin, depOrigin, extra) {
2323
+ const n = normalize(depNormal);
2324
+ return {
2325
+ position: scale$2(n, dot(n, sub(refOrigin, depOrigin)) - extra),
2326
+ rotation: IDENTITY_ROTATION
2327
+ };
2328
+ }
2329
+ /**
2330
+ * Reference axis, dependent point: drop the point onto the axis line
2331
+ * (`extra` = 0) or place it at radial distance `extra` from the line, keeping
2332
+ * its along-axis position. A point already on the axis gets an arbitrary radial.
2333
+ */
2334
+ function solveAxisToPoint(ref, depOrigin, extra) {
2335
+ const d = normalize(ref.direction ?? [
2336
+ 0,
2337
+ 0,
2338
+ 1
2339
+ ]);
2340
+ const foot = add$1(ref.origin, scale$2(d, dot(d, sub(depOrigin, ref.origin))));
2341
+ if (extra === 0) return {
2342
+ position: sub(foot, depOrigin),
2343
+ rotation: IDENTITY_ROTATION
2344
+ };
2345
+ const radial = sub(depOrigin, foot);
2346
+ const rlen = Math.hypot(radial[0], radial[1], radial[2]);
2347
+ return {
2348
+ position: sub(add$1(foot, scale$2(rlen < 1e-9 ? anyPerpendicular(d) : scale$2(radial, 1 / rlen), extra)), depOrigin),
2349
+ rotation: IDENTITY_ROTATION
2350
+ };
2351
+ }
2352
+ /**
2353
+ * Reference point, dependent axis: translate the axis line so it passes through
2354
+ * the point (`extra` = 0) or lies at perpendicular distance `extra` from it.
2355
+ * Translation is purely perpendicular to the axis, so the line's direction and
2356
+ * along-axis parameterization are preserved.
2357
+ */
2358
+ function solvePointToAxis(refOrigin, dep, extra) {
2359
+ const d = normalize(dep.direction ?? [
2360
+ 0,
2361
+ 0,
2362
+ 1
2363
+ ]);
2364
+ const w = sub(dep.origin, refOrigin);
2365
+ const perp = sub(w, scale$2(d, dot(d, w)));
2366
+ const plen = Math.hypot(perp[0], perp[1], perp[2]);
2367
+ if (extra === 0) return {
2368
+ position: scale$2(perp, -1),
2369
+ rotation: IDENTITY_ROTATION
2370
+ };
2371
+ return {
2372
+ position: sub(scale$2(plen < 1e-9 ? anyPerpendicular(d) : scale$2(perp, 1 / plen), extra), perp),
2373
+ rotation: IDENTITY_ROTATION
2374
+ };
2375
+ }
2376
+ /**
2377
+ * Axis-axis distance: align the dependent axis parallel to the reference, then
2378
+ * offset it to perpendicular distance `extra` (parallel pin-and-spacer). With
2379
+ * `extra` = 0 the axes become collinear, matching `concentric`.
2380
+ */
2381
+ function solveAxisAxisDistance(ref, dep, extra) {
2382
+ const dRef = normalize(ref.direction ?? [
2383
+ 0,
2384
+ 0,
2385
+ 1
2386
+ ]);
2387
+ const rotation = quatFromTo(normalize(dep.direction ?? [
2388
+ 0,
2389
+ 0,
2390
+ 1
2391
+ ]), dRef);
2392
+ const w = sub(quatRotate(rotation, dep.origin), ref.origin);
2393
+ const perp = sub(w, scale$2(dRef, dot(dRef, w)));
2394
+ const plen = Math.hypot(perp[0], perp[1], perp[2]);
2395
+ return {
2396
+ position: sub(scale$2(plen < 1e-9 ? anyPerpendicular(dRef) : scale$2(perp, 1 / plen), extra), perp),
2397
+ rotation
2398
+ };
2399
+ }
2400
+ /**
2401
+ * Supported entity-type pairs for the translational mates (`coincident` /
2402
+ * `distance`), keyed `${entityA}-${entityB}`. Both orders are listed where the
2403
+ * solver handles them, so a user need not pre-order the entities.
2404
+ */
2405
+ var TRANSLATIONAL_PAIRS = new Set([
2406
+ "plane-plane",
2407
+ "plane-point",
2408
+ "point-plane",
2409
+ "point-point",
2410
+ "axis-axis",
2411
+ "axis-point",
2412
+ "point-axis"
2413
+ ]);
2414
+ /** Entity types the orientation/axis mates require of (entityA, entityB). */
2301
2415
  var REQUIRED_ENTITIES = {
2302
- coincident: "plane",
2303
- distance: "plane",
2304
2416
  angle: "plane",
2305
2417
  concentric: "axis"
2306
2418
  };
@@ -2310,21 +2422,51 @@ var POSITIONING_TYPES = new Set([
2310
2422
  "angle",
2311
2423
  "concentric"
2312
2424
  ]);
2425
+ /** Whether a positioning mate's entity pair is solvable. */
2426
+ function isSupportedPair(type, a, b) {
2427
+ const required = REQUIRED_ENTITIES[type];
2428
+ if (required) return a === required && b === required;
2429
+ return TRANSLATIONAL_PAIRS.has(`${a}-${b}`);
2430
+ }
2431
+ /**
2432
+ * Solve a `coincident` (extra = 0) or `distance` (extra = value) mate for any
2433
+ * supported entity-type pair. `ref` is already in world space; the dependent is
2434
+ * at the origin. Returns null only for an unsupported pair (filtered out
2435
+ * upstream by `isSupportedPair`).
2436
+ */
2437
+ function solveTranslational(ref, dep, extra) {
2438
+ switch (`${ref.type}-${dep.type}`) {
2439
+ case "plane-plane":
2440
+ case "plane-point": return solvePlanePair(ref, dep, extra);
2441
+ case "point-plane": return solvePlaneToPoint(dep.normal ?? [
2442
+ 0,
2443
+ 0,
2444
+ 1
2445
+ ], ref.origin, dep.origin, extra);
2446
+ case "point-point": return solvePointPair(ref.origin, dep.origin, extra);
2447
+ case "axis-axis": return extra === 0 ? solveConcentric(ref, dep) : solveAxisAxisDistance(ref, dep, extra);
2448
+ case "axis-point": return solveAxisToPoint(ref, dep.origin, extra);
2449
+ case "point-axis": return solvePointToAxis(ref.origin, dep, extra);
2450
+ default: return null;
2451
+ }
2452
+ }
2313
2453
  /** Dispatch a positioning mate to its solver. `ref` is already in world space. */
2314
2454
  function solveMate(c, ref, dep) {
2315
2455
  switch (c.type) {
2316
2456
  case "concentric": return solveConcentric(ref, dep);
2317
2457
  case "angle": return solveAngle(ref, dep, (c.value ?? 0) * Math.PI / 180);
2318
- case "distance": return solvePlanePair(ref, dep, c.value ?? 0);
2319
- default: return solvePlanePair(ref, dep, 0);
2458
+ case "distance": return solveTranslational(ref, dep, c.value ?? 0) ?? solvePlanePair(ref, dep, c.value ?? 0);
2459
+ default: return solveTranslational(ref, dep, 0) ?? solvePlanePair(ref, dep, 0);
2320
2460
  }
2321
2461
  }
2322
2462
  /**
2323
2463
  * Solve assembly constraints analytically.
2324
2464
  *
2325
- * Handles: fixed, coincident/distance (plane-plane), concentric (axis-axis), and
2326
- * angle (plane-plane orientation). For a positioning mate, entityA is the
2327
- * reference and entityB the dependent. Chain roots (nodes never positioned by a
2465
+ * Handles: fixed, concentric (axis-axis), angle (plane-plane orientation), and
2466
+ * coincident/distance for any supported entity-type pair (plane-plane,
2467
+ * plane-point, point-point, axis-axis, axis-point, and both point orders see
2468
+ * `TRANSLATIONAL_PAIRS`). For a positioning mate, entityA is the reference and
2469
+ * entityB the dependent. Chain roots (nodes never positioned by a
2328
2470
  * mate) and explicit `fixed` nodes anchor at the origin; constraints then resolve
2329
2471
  * in topological order — each places its dependent against the reference's solved
2330
2472
  * pose (rotation included), so multi-body chains compose. Returns
@@ -2351,9 +2493,10 @@ function solveConstraints(nodes, constraints) {
2351
2493
  const pending = [];
2352
2494
  for (const c of positioning) {
2353
2495
  if (!c.entityA || !c.entityB) continue;
2354
- const required = REQUIRED_ENTITIES[c.type];
2355
- if (c.entityA.entity.type !== required || c.entityB.entity.type !== required) {
2356
- unsupported.push(`${c.type}(${c.entityA.entity.type}-${c.entityB.entity.type})`);
2496
+ const a = c.entityA.entity.type;
2497
+ const b = c.entityB.entity.type;
2498
+ if (!isSupportedPair(c.type, a, b)) {
2499
+ unsupported.push(`${c.type}(${a}-${b})`);
2357
2500
  continue;
2358
2501
  }
2359
2502
  pending.push(c);
@@ -34,9 +34,11 @@ export interface SolverResult {
34
34
  /**
35
35
  * Solve assembly constraints analytically.
36
36
  *
37
- * Handles: fixed, coincident/distance (plane-plane), concentric (axis-axis), and
38
- * angle (plane-plane orientation). For a positioning mate, entityA is the
39
- * reference and entityB the dependent. Chain roots (nodes never positioned by a
37
+ * Handles: fixed, concentric (axis-axis), angle (plane-plane orientation), and
38
+ * coincident/distance for any supported entity-type pair (plane-plane,
39
+ * plane-point, point-point, axis-axis, axis-point, and both point orders see
40
+ * `TRANSLATIONAL_PAIRS`). For a positioning mate, entityA is the reference and
41
+ * entityB the dependent. Chain roots (nodes never positioned by a
40
42
  * mate) and explicit `fixed` nodes anchor at the origin; constraints then resolve
41
43
  * in topological order — each places its dependent against the reference's solved
42
44
  * pose (rotation included), so multi-body chains compose. Returns
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brepjs",
3
- "version": "18.79.0",
3
+ "version": "18.80.0",
4
4
  "description": "Web CAD library with pluggable geometry kernel",
5
5
  "keywords": [
6
6
  "cad",