brepjs-verify 0.4.0 → 0.13.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.
@@ -261,7 +261,7 @@ function shapeTypeOf(brep, s) {
261
261
  return "Unknown";
262
262
  }
263
263
  function runChecks(brep, shape) {
264
- const { isSolid, isShape3D, isFace, measureVolume, measureArea, getBounds, validSolid, isOk } = brep;
264
+ const { isSolid, isShape3D, isFace, measureVolumeProps, measureArea, getBounds, getFaces, getEdges, getWires, getVertices, getSolids, getShells, isManifoldShell, validSolid, isOk } = brep;
265
265
  const r = emptyReport();
266
266
  r.shapeType = shapeTypeOf(brep, shape);
267
267
  if (isSolid(shape)) {
@@ -278,19 +278,41 @@ function runChecks(brep, shape) {
278
278
  });
279
279
  }
280
280
  r.checks.push(validCheck);
281
+ } else {
282
+ const solids = getSolids(shape);
283
+ if (solids.length > 0) {
284
+ const failures = [];
285
+ solids.forEach((s, i) => {
286
+ const v = validSolid(s);
287
+ if (!isOk(v)) failures.push(`body ${i}: ${v.error}`);
288
+ });
289
+ const bodiesCheck = {
290
+ name: "allBodiesValid",
291
+ passed: failures.length === 0
292
+ };
293
+ if (failures.length > 0) {
294
+ bodiesCheck.detail = `${failures.length}/${solids.length} bodies invalid — ${failures.join("; ")}`;
295
+ r.errorInfos.push({
296
+ message: `allBodiesValid: ${bodiesCheck.detail}`,
297
+ code: VALIDITY_FAILURE_CODE
298
+ });
299
+ }
300
+ r.checks.push(bodiesCheck);
301
+ }
281
302
  }
282
303
  if (isShape3D(shape)) {
283
- const vol = measureVolume(shape);
284
- if (isOk(vol)) {
285
- r.measurements.volume = vol.value;
304
+ const volProps = measureVolumeProps(shape);
305
+ if (isOk(volProps)) {
306
+ r.measurements.volume = volProps.value.volume;
307
+ r.measurements.centerOfMass = volProps.value.centerOfMass;
286
308
  r.checks.push({
287
309
  name: "positiveVolume",
288
- passed: vol.value > 0
310
+ passed: volProps.value.volume > 0
289
311
  });
290
312
  } else pushError(r, {
291
- message: `measureVolume: ${vol.error.message}`,
292
- code: vol.error.code,
293
- suggestion: vol.error.suggestion
313
+ message: `measureVolume: ${volProps.error.message}`,
314
+ code: volProps.error.code,
315
+ suggestion: volProps.error.suggestion
294
316
  });
295
317
  }
296
318
  if (isFace(shape) || isShape3D(shape)) {
@@ -302,6 +324,18 @@ function runChecks(brep, shape) {
302
324
  } catch (e) {
303
325
  pushError(r, { message: `getBounds: ${e.message}` });
304
326
  }
327
+ try {
328
+ r.topology = {
329
+ faceCount: getFaces(shape).length,
330
+ edgeCount: getEdges(shape).length,
331
+ wireCount: getWires(shape).length,
332
+ vertexCount: getVertices(shape).length
333
+ };
334
+ } catch {}
335
+ if (r.topology) try {
336
+ const shells = getShells(shape);
337
+ if (shells.length > 0) r.topology.manifold = shells.every((s) => isManifoldShell(s));
338
+ } catch {}
305
339
  r.hints = buildHints(r);
306
340
  return r;
307
341
  }
@@ -530,6 +564,39 @@ function typecheckPart(partPath, toolDir) {
530
564
  }
531
565
  //#endregion
532
566
  //#region src/verify/runPart.ts
567
+ /** Centroid of a face group's vertices, in part (Z-up, mm) coordinates. */
568
+ function faceCentroid(m, start, count) {
569
+ let x = 0;
570
+ let y = 0;
571
+ let z = 0;
572
+ for (let i = start; i < start + count; i++) {
573
+ const vi = (m.triangles[i] ?? 0) * 3;
574
+ x += m.vertices[vi] ?? 0;
575
+ y += m.vertices[vi + 1] ?? 0;
576
+ z += m.vertices[vi + 2] ?? 0;
577
+ }
578
+ const n = count || 1;
579
+ return [
580
+ x / n,
581
+ y / n,
582
+ z / n
583
+ ];
584
+ }
585
+ function buildMaterialMap(m, spec) {
586
+ if (spec === void 0 || spec === null) return {};
587
+ if (typeof spec !== "function" && (typeof spec !== "object" || Array.isArray(spec))) return { warning: "export const materials must be a function or a material object — ignored" };
588
+ const sel = spec;
589
+ const select = typeof sel === "function" ? sel : () => sel;
590
+ const map = /* @__PURE__ */ new Map();
591
+ for (const fg of m.faceGroups) {
592
+ const mat = select({
593
+ faceId: fg.faceId,
594
+ center: faceCentroid(m, fg.start, fg.count)
595
+ });
596
+ if (mat) map.set(fg.faceId, mat);
597
+ }
598
+ return map.size > 0 ? { map } : {};
599
+ }
533
600
  async function loadPart(modulePath) {
534
601
  try {
535
602
  return await import((0, node_url.pathToFileURL)(modulePath).href);
@@ -648,7 +715,13 @@ async function runPart(modulePath, opts = {}) {
648
715
  let glb;
649
716
  let step;
650
717
  if (opts.glb) try {
651
- glb = exportGlb(mesh(shape));
718
+ const shapeMesh = mesh(shape);
719
+ const { map, warning } = buildMaterialMap(shapeMesh, mod.materials);
720
+ if (warning) pushError(result, {
721
+ message: `materials: ${warning}`,
722
+ code: "MATERIALS_IGNORED"
723
+ });
724
+ glb = map ? exportGlb(shapeMesh, { materials: map }) : exportGlb(shapeMesh);
652
725
  } catch (e) {
653
726
  pushError(result, toErrorInfo("exportGlb", e));
654
727
  }
@@ -665,7 +738,7 @@ async function runPart(modulePath, opts = {}) {
665
738
  });
666
739
  }
667
740
  //#endregion
668
- //#region \0@oxc-project+runtime@0.132.0/helpers/usingCtx.js
741
+ //#region \0@oxc-project+runtime@0.133.0/helpers/esm/usingCtx.js
669
742
  function _usingCtx() {
670
743
  var r = "function" == typeof SuppressedError ? SuppressedError : function(r, e) {
671
744
  var n = Error();
@@ -259,7 +259,7 @@ function shapeTypeOf(brep, s) {
259
259
  return "Unknown";
260
260
  }
261
261
  function runChecks(brep, shape) {
262
- const { isSolid, isShape3D, isFace, measureVolume, measureArea, getBounds, validSolid, isOk } = brep;
262
+ const { isSolid, isShape3D, isFace, measureVolumeProps, measureArea, getBounds, getFaces, getEdges, getWires, getVertices, getSolids, getShells, isManifoldShell, validSolid, isOk } = brep;
263
263
  const r = emptyReport();
264
264
  r.shapeType = shapeTypeOf(brep, shape);
265
265
  if (isSolid(shape)) {
@@ -276,19 +276,41 @@ function runChecks(brep, shape) {
276
276
  });
277
277
  }
278
278
  r.checks.push(validCheck);
279
+ } else {
280
+ const solids = getSolids(shape);
281
+ if (solids.length > 0) {
282
+ const failures = [];
283
+ solids.forEach((s, i) => {
284
+ const v = validSolid(s);
285
+ if (!isOk(v)) failures.push(`body ${i}: ${v.error}`);
286
+ });
287
+ const bodiesCheck = {
288
+ name: "allBodiesValid",
289
+ passed: failures.length === 0
290
+ };
291
+ if (failures.length > 0) {
292
+ bodiesCheck.detail = `${failures.length}/${solids.length} bodies invalid — ${failures.join("; ")}`;
293
+ r.errorInfos.push({
294
+ message: `allBodiesValid: ${bodiesCheck.detail}`,
295
+ code: VALIDITY_FAILURE_CODE
296
+ });
297
+ }
298
+ r.checks.push(bodiesCheck);
299
+ }
279
300
  }
280
301
  if (isShape3D(shape)) {
281
- const vol = measureVolume(shape);
282
- if (isOk(vol)) {
283
- r.measurements.volume = vol.value;
302
+ const volProps = measureVolumeProps(shape);
303
+ if (isOk(volProps)) {
304
+ r.measurements.volume = volProps.value.volume;
305
+ r.measurements.centerOfMass = volProps.value.centerOfMass;
284
306
  r.checks.push({
285
307
  name: "positiveVolume",
286
- passed: vol.value > 0
308
+ passed: volProps.value.volume > 0
287
309
  });
288
310
  } else pushError(r, {
289
- message: `measureVolume: ${vol.error.message}`,
290
- code: vol.error.code,
291
- suggestion: vol.error.suggestion
311
+ message: `measureVolume: ${volProps.error.message}`,
312
+ code: volProps.error.code,
313
+ suggestion: volProps.error.suggestion
292
314
  });
293
315
  }
294
316
  if (isFace(shape) || isShape3D(shape)) {
@@ -300,6 +322,18 @@ function runChecks(brep, shape) {
300
322
  } catch (e) {
301
323
  pushError(r, { message: `getBounds: ${e.message}` });
302
324
  }
325
+ try {
326
+ r.topology = {
327
+ faceCount: getFaces(shape).length,
328
+ edgeCount: getEdges(shape).length,
329
+ wireCount: getWires(shape).length,
330
+ vertexCount: getVertices(shape).length
331
+ };
332
+ } catch {}
333
+ if (r.topology) try {
334
+ const shells = getShells(shape);
335
+ if (shells.length > 0) r.topology.manifold = shells.every((s) => isManifoldShell(s));
336
+ } catch {}
303
337
  r.hints = buildHints(r);
304
338
  return r;
305
339
  }
@@ -528,6 +562,39 @@ function typecheckPart(partPath, toolDir) {
528
562
  }
529
563
  //#endregion
530
564
  //#region src/verify/runPart.ts
565
+ /** Centroid of a face group's vertices, in part (Z-up, mm) coordinates. */
566
+ function faceCentroid(m, start, count) {
567
+ let x = 0;
568
+ let y = 0;
569
+ let z = 0;
570
+ for (let i = start; i < start + count; i++) {
571
+ const vi = (m.triangles[i] ?? 0) * 3;
572
+ x += m.vertices[vi] ?? 0;
573
+ y += m.vertices[vi + 1] ?? 0;
574
+ z += m.vertices[vi + 2] ?? 0;
575
+ }
576
+ const n = count || 1;
577
+ return [
578
+ x / n,
579
+ y / n,
580
+ z / n
581
+ ];
582
+ }
583
+ function buildMaterialMap(m, spec) {
584
+ if (spec === void 0 || spec === null) return {};
585
+ if (typeof spec !== "function" && (typeof spec !== "object" || Array.isArray(spec))) return { warning: "export const materials must be a function or a material object — ignored" };
586
+ const sel = spec;
587
+ const select = typeof sel === "function" ? sel : () => sel;
588
+ const map = /* @__PURE__ */ new Map();
589
+ for (const fg of m.faceGroups) {
590
+ const mat = select({
591
+ faceId: fg.faceId,
592
+ center: faceCentroid(m, fg.start, fg.count)
593
+ });
594
+ if (mat) map.set(fg.faceId, mat);
595
+ }
596
+ return map.size > 0 ? { map } : {};
597
+ }
531
598
  async function loadPart(modulePath) {
532
599
  try {
533
600
  return await import(pathToFileURL(modulePath).href);
@@ -646,7 +713,13 @@ async function runPart(modulePath, opts = {}) {
646
713
  let glb;
647
714
  let step;
648
715
  if (opts.glb) try {
649
- glb = exportGlb(mesh(shape));
716
+ const shapeMesh = mesh(shape);
717
+ const { map, warning } = buildMaterialMap(shapeMesh, mod.materials);
718
+ if (warning) pushError(result, {
719
+ message: `materials: ${warning}`,
720
+ code: "MATERIALS_IGNORED"
721
+ });
722
+ glb = map ? exportGlb(shapeMesh, { materials: map }) : exportGlb(shapeMesh);
650
723
  } catch (e) {
651
724
  pushError(result, toErrorInfo("exportGlb", e));
652
725
  }
@@ -663,7 +736,7 @@ async function runPart(modulePath, opts = {}) {
663
736
  });
664
737
  }
665
738
  //#endregion
666
- //#region \0@oxc-project+runtime@0.132.0/helpers/usingCtx.js
739
+ //#region \0@oxc-project+runtime@0.133.0/helpers/esm/usingCtx.js
667
740
  function _usingCtx() {
668
741
  var r = "function" == typeof SuppressedError ? SuppressedError : function(r, e) {
669
742
  var n = Error();
package/dist/index.d.ts CHANGED
@@ -2,6 +2,6 @@ export { runPart, type RunPartOptions, type RunPartResult } from './verify/runPa
2
2
  export { runChecks } from './verify/checks.js';
3
3
  export { runMeasure, type MeasureReport } from './verify/measure.js';
4
4
  export { runDiff } from './verify/diff.js';
5
- export { serializeReport, emptyReport, type VerifyReport, type VerifyCheck, type VerifyMeasurements, type VerifyAssertion, type DiffReport, type BoundsDelta, } from './verify/report.js';
5
+ export { serializeReport, emptyReport, type VerifyReport, type VerifyCheck, type VerifyMeasurements, type VerifyTopology, type VerifyAssertion, type DiffReport, type BoundsDelta, } from './verify/report.js';
6
6
  export { typecheckPart, TYPECHECK_CODE, type TypecheckResult } from './verify/typecheck.js';
7
7
  export { evaluateExpected, isExpectedDims, pctDelta, DEFAULT_TOLERANCE_PCT, type ExpectedDims, type ExpectedBounds, } from './verify/expected.js';