brepjs 18.69.2 → 18.69.3

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
@@ -2146,11 +2146,64 @@ var UNSUPPORTED_DOF = {
2146
2146
  distance: 1,
2147
2147
  angle: 1
2148
2148
  };
2149
+ var IDENTITY_ROTATION = [
2150
+ 1,
2151
+ 0,
2152
+ 0,
2153
+ 0
2154
+ ];
2155
+ function add$1(a, b) {
2156
+ return [
2157
+ a[0] + b[0],
2158
+ a[1] + b[1],
2159
+ a[2] + b[2]
2160
+ ];
2161
+ }
2162
+ function dot(a, b) {
2163
+ return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
2164
+ }
2165
+ /**
2166
+ * Position a dependent plane against an already-placed reference plane.
2167
+ *
2168
+ * The reference's solved translation is applied to its (local) entity origin
2169
+ * before measuring, so a chain composes down already-solved poses instead of
2170
+ * reading original geometry. The dependent is at the origin (a node is only
2171
+ * solved once, while unplaced), so the returned position is its absolute
2172
+ * translation. `extra` is the gap for a distance mate (0 for coincident).
2173
+ * Rotation stays identity — coincident/distance produce pure translations;
2174
+ * Phase 1 rotational constraints will extend this.
2175
+ */
2176
+ function solvePlanePair(ref, refPos, dep, extra) {
2177
+ const n = ref.normal ?? [
2178
+ 0,
2179
+ 0,
2180
+ 1
2181
+ ];
2182
+ const refOrigin = add$1(ref.origin, refPos);
2183
+ const offset = dot(n, [
2184
+ refOrigin[0] - dep.origin[0],
2185
+ refOrigin[1] - dep.origin[1],
2186
+ refOrigin[2] - dep.origin[2]
2187
+ ]) + extra;
2188
+ return {
2189
+ position: [
2190
+ offset * n[0],
2191
+ offset * n[1],
2192
+ offset * n[2]
2193
+ ],
2194
+ rotation: IDENTITY_ROTATION
2195
+ };
2196
+ }
2149
2197
  /**
2150
2198
  * Solve assembly constraints analytically.
2151
2199
  *
2152
- * Currently handles: fixed, coincident (plane-plane), distance (plane-plane).
2153
- * Returns `converged: false` with unsupported constraint details for concentric and angle.
2200
+ * Handles: fixed, coincident (plane-plane), distance (plane-plane). For a
2201
+ * positioning mate, entityA is the reference and entityB the dependent. Chain
2202
+ * roots (nodes never positioned by a mate) and explicit `fixed` nodes anchor at
2203
+ * the origin; constraints then resolve in topological order — each places its
2204
+ * dependent against the reference's solved pose, so multi-body chains compose.
2205
+ * Returns `converged: false` with unsupported details for concentric, angle,
2206
+ * non-plane pairs, and any constraint whose reference never resolves.
2154
2207
  */
2155
2208
  function solveConstraints(nodes, constraints) {
2156
2209
  const transforms = /* @__PURE__ */ new Map();
@@ -2160,69 +2213,51 @@ function solveConstraints(nodes, constraints) {
2160
2213
  0,
2161
2214
  0
2162
2215
  ],
2163
- rotation: [
2164
- 1,
2165
- 0,
2166
- 0,
2167
- 0
2168
- ]
2216
+ rotation: IDENTITY_ROTATION
2169
2217
  });
2170
2218
  const unsupported = [];
2171
- for (const c of constraints) if (c.type === "coincident" && c.entityA && c.entityB) {
2172
- const a = c.entityA;
2173
- const b = c.entityB;
2174
- if (a.entity.type === "plane" && b.entity.type === "plane") {
2175
- const aNormal = a.entity.normal ?? [
2176
- 0,
2177
- 0,
2178
- 1
2179
- ];
2180
- const aOrigin = a.entity.origin;
2181
- const bOrigin = b.entity.origin;
2182
- const dot = aNormal[0] * (aOrigin[0] - bOrigin[0]) + aNormal[1] * (aOrigin[1] - bOrigin[1]) + aNormal[2] * (aOrigin[2] - bOrigin[2]);
2183
- const pos = [
2184
- dot * aNormal[0],
2185
- dot * aNormal[1],
2186
- dot * aNormal[2]
2187
- ];
2188
- transforms.set(b.node, {
2189
- position: pos,
2190
- rotation: [
2191
- 1,
2192
- 0,
2193
- 0,
2194
- 0
2195
- ]
2196
- });
2197
- } else unsupported.push(`coincident(${a.entity.type}-${b.entity.type})`);
2198
- } else if (c.type === "distance" && c.entityA && c.entityB && c.value !== void 0) {
2199
- const a = c.entityA;
2200
- const b = c.entityB;
2201
- if (a.entity.type === "plane" && b.entity.type === "plane") {
2202
- const aNormal = a.entity.normal ?? [
2203
- 0,
2204
- 0,
2205
- 1
2206
- ];
2207
- const aOrigin = a.entity.origin;
2208
- const bOrigin = b.entity.origin;
2209
- const offset = aNormal[0] * (aOrigin[0] - bOrigin[0]) + aNormal[1] * (aOrigin[1] - bOrigin[1]) + aNormal[2] * (aOrigin[2] - bOrigin[2]) + c.value;
2210
- const pos = [
2211
- offset * aNormal[0],
2212
- offset * aNormal[1],
2213
- offset * aNormal[2]
2214
- ];
2215
- transforms.set(b.node, {
2216
- position: pos,
2217
- rotation: [
2218
- 1,
2219
+ const positioning = constraints.filter((c) => (c.type === "coincident" || c.type === "distance") && c.entityA && c.entityB);
2220
+ const dependents = /* @__PURE__ */ new Set();
2221
+ for (const c of positioning) if (c.entityB) dependents.add(c.entityB.node);
2222
+ const placed = /* @__PURE__ */ new Set();
2223
+ for (const node of nodes) if (!dependents.has(node)) placed.add(node);
2224
+ for (const c of constraints) if (c.type === "fixed" && c.entityA) placed.add(c.entityA.node);
2225
+ for (const c of constraints) if (c.type === "concentric" || c.type === "angle") unsupported.push(c.type);
2226
+ const pending = [];
2227
+ for (const c of positioning) {
2228
+ if (!c.entityA || !c.entityB) continue;
2229
+ if (c.entityA.entity.type !== "plane" || c.entityB.entity.type !== "plane") {
2230
+ unsupported.push(`${c.type}(${c.entityA.entity.type}-${c.entityB.entity.type})`);
2231
+ continue;
2232
+ }
2233
+ pending.push(c);
2234
+ }
2235
+ let progress = true;
2236
+ while (progress && pending.length > 0) {
2237
+ progress = false;
2238
+ for (let i = pending.length - 1; i >= 0; i--) {
2239
+ const c = pending[i];
2240
+ if (!c?.entityA || !c.entityB) continue;
2241
+ const ref = c.entityA;
2242
+ const dep = c.entityB;
2243
+ if (!placed.has(ref.node)) continue;
2244
+ pending.splice(i, 1);
2245
+ progress = true;
2246
+ if (placed.has(dep.node)) continue;
2247
+ const refPose = transforms.get(ref.node) ?? {
2248
+ position: [
2219
2249
  0,
2220
2250
  0,
2221
2251
  0
2222
- ]
2223
- });
2224
- } else unsupported.push(`distance(${a.entity.type}-${b.entity.type})`);
2225
- } else if (c.type === "concentric" || c.type === "angle") unsupported.push(c.type);
2252
+ ],
2253
+ rotation: IDENTITY_ROTATION
2254
+ };
2255
+ const extra = c.type === "distance" ? c.value ?? 0 : 0;
2256
+ transforms.set(dep.node, solvePlanePair(ref.entity, refPose.position, dep.entity, extra));
2257
+ placed.add(dep.node);
2258
+ }
2259
+ }
2260
+ for (const c of pending) unsupported.push(`${c.type}(unanchored)`);
2226
2261
  return {
2227
2262
  transforms,
2228
2263
  dof: unsupported.reduce((sum, type) => {
package/dist/brepjs.js CHANGED
@@ -2157,11 +2157,64 @@ var UNSUPPORTED_DOF = {
2157
2157
  distance: 1,
2158
2158
  angle: 1
2159
2159
  };
2160
+ var IDENTITY_ROTATION = [
2161
+ 1,
2162
+ 0,
2163
+ 0,
2164
+ 0
2165
+ ];
2166
+ function add$1(a, b) {
2167
+ return [
2168
+ a[0] + b[0],
2169
+ a[1] + b[1],
2170
+ a[2] + b[2]
2171
+ ];
2172
+ }
2173
+ function dot(a, b) {
2174
+ return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
2175
+ }
2176
+ /**
2177
+ * Position a dependent plane against an already-placed reference plane.
2178
+ *
2179
+ * The reference's solved translation is applied to its (local) entity origin
2180
+ * before measuring, so a chain composes down already-solved poses instead of
2181
+ * reading original geometry. The dependent is at the origin (a node is only
2182
+ * solved once, while unplaced), so the returned position is its absolute
2183
+ * translation. `extra` is the gap for a distance mate (0 for coincident).
2184
+ * Rotation stays identity — coincident/distance produce pure translations;
2185
+ * Phase 1 rotational constraints will extend this.
2186
+ */
2187
+ function solvePlanePair(ref, refPos, dep, extra) {
2188
+ const n = ref.normal ?? [
2189
+ 0,
2190
+ 0,
2191
+ 1
2192
+ ];
2193
+ const refOrigin = add$1(ref.origin, refPos);
2194
+ const offset = dot(n, [
2195
+ refOrigin[0] - dep.origin[0],
2196
+ refOrigin[1] - dep.origin[1],
2197
+ refOrigin[2] - dep.origin[2]
2198
+ ]) + extra;
2199
+ return {
2200
+ position: [
2201
+ offset * n[0],
2202
+ offset * n[1],
2203
+ offset * n[2]
2204
+ ],
2205
+ rotation: IDENTITY_ROTATION
2206
+ };
2207
+ }
2160
2208
  /**
2161
2209
  * Solve assembly constraints analytically.
2162
2210
  *
2163
- * Currently handles: fixed, coincident (plane-plane), distance (plane-plane).
2164
- * Returns `converged: false` with unsupported constraint details for concentric and angle.
2211
+ * Handles: fixed, coincident (plane-plane), distance (plane-plane). For a
2212
+ * positioning mate, entityA is the reference and entityB the dependent. Chain
2213
+ * roots (nodes never positioned by a mate) and explicit `fixed` nodes anchor at
2214
+ * the origin; constraints then resolve in topological order — each places its
2215
+ * dependent against the reference's solved pose, so multi-body chains compose.
2216
+ * Returns `converged: false` with unsupported details for concentric, angle,
2217
+ * non-plane pairs, and any constraint whose reference never resolves.
2165
2218
  */
2166
2219
  function solveConstraints(nodes, constraints) {
2167
2220
  const transforms = /* @__PURE__ */ new Map();
@@ -2171,69 +2224,51 @@ function solveConstraints(nodes, constraints) {
2171
2224
  0,
2172
2225
  0
2173
2226
  ],
2174
- rotation: [
2175
- 1,
2176
- 0,
2177
- 0,
2178
- 0
2179
- ]
2227
+ rotation: IDENTITY_ROTATION
2180
2228
  });
2181
2229
  const unsupported = [];
2182
- for (const c of constraints) if (c.type === "coincident" && c.entityA && c.entityB) {
2183
- const a = c.entityA;
2184
- const b = c.entityB;
2185
- if (a.entity.type === "plane" && b.entity.type === "plane") {
2186
- const aNormal = a.entity.normal ?? [
2187
- 0,
2188
- 0,
2189
- 1
2190
- ];
2191
- const aOrigin = a.entity.origin;
2192
- const bOrigin = b.entity.origin;
2193
- const dot = aNormal[0] * (aOrigin[0] - bOrigin[0]) + aNormal[1] * (aOrigin[1] - bOrigin[1]) + aNormal[2] * (aOrigin[2] - bOrigin[2]);
2194
- const pos = [
2195
- dot * aNormal[0],
2196
- dot * aNormal[1],
2197
- dot * aNormal[2]
2198
- ];
2199
- transforms.set(b.node, {
2200
- position: pos,
2201
- rotation: [
2202
- 1,
2203
- 0,
2204
- 0,
2205
- 0
2206
- ]
2207
- });
2208
- } else unsupported.push(`coincident(${a.entity.type}-${b.entity.type})`);
2209
- } else if (c.type === "distance" && c.entityA && c.entityB && c.value !== void 0) {
2210
- const a = c.entityA;
2211
- const b = c.entityB;
2212
- if (a.entity.type === "plane" && b.entity.type === "plane") {
2213
- const aNormal = a.entity.normal ?? [
2214
- 0,
2215
- 0,
2216
- 1
2217
- ];
2218
- const aOrigin = a.entity.origin;
2219
- const bOrigin = b.entity.origin;
2220
- const offset = aNormal[0] * (aOrigin[0] - bOrigin[0]) + aNormal[1] * (aOrigin[1] - bOrigin[1]) + aNormal[2] * (aOrigin[2] - bOrigin[2]) + c.value;
2221
- const pos = [
2222
- offset * aNormal[0],
2223
- offset * aNormal[1],
2224
- offset * aNormal[2]
2225
- ];
2226
- transforms.set(b.node, {
2227
- position: pos,
2228
- rotation: [
2229
- 1,
2230
+ const positioning = constraints.filter((c) => (c.type === "coincident" || c.type === "distance") && c.entityA && c.entityB);
2231
+ const dependents = /* @__PURE__ */ new Set();
2232
+ for (const c of positioning) if (c.entityB) dependents.add(c.entityB.node);
2233
+ const placed = /* @__PURE__ */ new Set();
2234
+ for (const node of nodes) if (!dependents.has(node)) placed.add(node);
2235
+ for (const c of constraints) if (c.type === "fixed" && c.entityA) placed.add(c.entityA.node);
2236
+ for (const c of constraints) if (c.type === "concentric" || c.type === "angle") unsupported.push(c.type);
2237
+ const pending = [];
2238
+ for (const c of positioning) {
2239
+ if (!c.entityA || !c.entityB) continue;
2240
+ if (c.entityA.entity.type !== "plane" || c.entityB.entity.type !== "plane") {
2241
+ unsupported.push(`${c.type}(${c.entityA.entity.type}-${c.entityB.entity.type})`);
2242
+ continue;
2243
+ }
2244
+ pending.push(c);
2245
+ }
2246
+ let progress = true;
2247
+ while (progress && pending.length > 0) {
2248
+ progress = false;
2249
+ for (let i = pending.length - 1; i >= 0; i--) {
2250
+ const c = pending[i];
2251
+ if (!c?.entityA || !c.entityB) continue;
2252
+ const ref = c.entityA;
2253
+ const dep = c.entityB;
2254
+ if (!placed.has(ref.node)) continue;
2255
+ pending.splice(i, 1);
2256
+ progress = true;
2257
+ if (placed.has(dep.node)) continue;
2258
+ const refPose = transforms.get(ref.node) ?? {
2259
+ position: [
2230
2260
  0,
2231
2261
  0,
2232
2262
  0
2233
- ]
2234
- });
2235
- } else unsupported.push(`distance(${a.entity.type}-${b.entity.type})`);
2236
- } else if (c.type === "concentric" || c.type === "angle") unsupported.push(c.type);
2263
+ ],
2264
+ rotation: IDENTITY_ROTATION
2265
+ };
2266
+ const extra = c.type === "distance" ? c.value ?? 0 : 0;
2267
+ transforms.set(dep.node, solvePlanePair(ref.entity, refPose.position, dep.entity, extra));
2268
+ placed.add(dep.node);
2269
+ }
2270
+ }
2271
+ for (const c of pending) unsupported.push(`${c.type}(unanchored)`);
2237
2272
  return {
2238
2273
  transforms,
2239
2274
  dof: unsupported.reduce((sum, type) => {
@@ -34,8 +34,13 @@ export interface SolverResult {
34
34
  /**
35
35
  * Solve assembly constraints analytically.
36
36
  *
37
- * Currently handles: fixed, coincident (plane-plane), distance (plane-plane).
38
- * Returns `converged: false` with unsupported constraint details for concentric and angle.
37
+ * Handles: fixed, coincident (plane-plane), distance (plane-plane). For a
38
+ * positioning mate, entityA is the reference and entityB the dependent. Chain
39
+ * roots (nodes never positioned by a mate) and explicit `fixed` nodes anchor at
40
+ * the origin; constraints then resolve in topological order — each places its
41
+ * dependent against the reference's solved pose, so multi-body chains compose.
42
+ * Returns `converged: false` with unsupported details for concentric, angle,
43
+ * non-plane pairs, and any constraint whose reference never resolves.
39
44
  */
40
45
  export declare function solveConstraints(nodes: string[], constraints: SolverConstraint[]): SolverResult;
41
46
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brepjs",
3
- "version": "18.69.2",
3
+ "version": "18.69.3",
4
4
  "description": "Web CAD library with pluggable geometry kernel",
5
5
  "keywords": [
6
6
  "cad",