@willcgage/module-schematic 0.30.0 → 0.32.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/index.cjs CHANGED
@@ -35,12 +35,26 @@ function moduleSections(doc) {
35
35
  };
36
36
  });
37
37
  }
38
- function sectionFootprints(doc) {
39
- return moduleSections(doc).filter((sec) => sec.outline).map((sec) => ({
40
- id: sec.id,
41
- ...sec.name ? { name: sec.name } : {},
42
- outline: sampleBenchworkOutline(sec.outline)
43
- }));
38
+ function sectionFootprints(doc, derive) {
39
+ const spans = derive ? sectionSpans(doc) : [];
40
+ const spanOf = new Map(spans.map((sp) => [sp.id, sp]));
41
+ return moduleSections(doc).map((sec) => {
42
+ const name = sec.name ? { name: sec.name } : {};
43
+ if (sec.outline)
44
+ return { id: sec.id, ...name, outline: sampleBenchworkOutline(sec.outline), derived: false };
45
+ const sp = spanOf.get(sec.id);
46
+ if (!sp || !derive) return null;
47
+ const band = sectionBand(
48
+ derive.centerline,
49
+ sp.fromPos,
50
+ sp.toPos,
51
+ derive.widthA,
52
+ derive.widthB,
53
+ derive.offsetA,
54
+ derive.offsetB
55
+ );
56
+ return band.length >= 3 ? { id: sec.id, ...name, outline: band, derived: true } : null;
57
+ }).filter((x) => x !== null);
44
58
  }
45
59
  function sampleBenchworkOutline(pts, segsPerArc = 20) {
46
60
  const n = pts.length;
@@ -256,6 +270,136 @@ function benchworkBand(center, widthA = FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES
256
270
  }));
257
271
  return [...left, ...right.reverse()];
258
272
  }
273
+ function sliceCenterline(center, fromPos, toPos) {
274
+ if (center.length < 2) return [];
275
+ const cum = [0];
276
+ for (let i = 1; i < center.length; i++)
277
+ cum.push(cum[i - 1] + Math.hypot(center[i].x - center[i - 1].x, center[i].y - center[i - 1].y));
278
+ const total = cum[cum.length - 1];
279
+ const a = Math.max(0, Math.min(total, Math.min(fromPos, toPos)));
280
+ const b = Math.max(0, Math.min(total, Math.max(fromPos, toPos)));
281
+ if (b - a <= 0) return [];
282
+ const at = (d) => {
283
+ for (let i = 1; i < center.length; i++) {
284
+ if (d <= cum[i] || i === center.length - 1) {
285
+ const seg = cum[i] - cum[i - 1] || 1;
286
+ const t = Math.max(0, Math.min(1, (d - cum[i - 1]) / seg));
287
+ return {
288
+ x: center[i - 1].x + (center[i].x - center[i - 1].x) * t,
289
+ y: center[i - 1].y + (center[i].y - center[i - 1].y) * t
290
+ };
291
+ }
292
+ }
293
+ return center[center.length - 1];
294
+ };
295
+ const out = [at(a)];
296
+ for (let i = 0; i < center.length; i++) {
297
+ if (cum[i] > a && cum[i] < b) out.push({ x: center[i].x, y: center[i].y });
298
+ }
299
+ out.push(at(b));
300
+ return out;
301
+ }
302
+ function sectionBand(center, fromPos, toPos, widthA = FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES, widthB = FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES, offsetA = 0, offsetB = 0) {
303
+ const slice = sliceCenterline(center, fromPos, toPos);
304
+ if (slice.length < 2) return [];
305
+ let total = 0;
306
+ for (let i = 1; i < center.length; i++)
307
+ total += Math.hypot(center[i].x - center[i - 1].x, center[i].y - center[i - 1].y);
308
+ total = total || 1;
309
+ const lo = Math.min(fromPos, toPos);
310
+ let acc = 0;
311
+ const fr = [0];
312
+ for (let i = 1; i < slice.length; i++) {
313
+ acc += Math.hypot(slice[i].x - slice[i - 1].x, slice[i].y - slice[i - 1].y);
314
+ fr.push(acc);
315
+ }
316
+ const f = fr.map((d) => Math.max(0, Math.min(1, (lo + d) / total)));
317
+ const n = centerlineNormals(slice);
318
+ const half = (i) => (widthA * (1 - f[i]) + widthB * f[i]) / 2;
319
+ const off = (i) => offsetA * (1 - f[i]) + offsetB * f[i];
320
+ const left = slice.map((p, i) => ({
321
+ x: p.x + n[i].x * (off(i) + half(i)),
322
+ y: p.y + n[i].y * (off(i) + half(i))
323
+ }));
324
+ const right = slice.map((p, i) => ({
325
+ x: p.x + n[i].x * (off(i) - half(i)),
326
+ y: p.y + n[i].y * (off(i) - half(i))
327
+ }));
328
+ return [...left, ...right.reverse()];
329
+ }
330
+ function ringEdges(pts) {
331
+ const out = [];
332
+ for (let i = 0; i < pts.length; i++) out.push([pts[i], pts[(i + 1) % pts.length]]);
333
+ return out;
334
+ }
335
+ function sharedEdgeLength(e1, e2, gap, angleDeg) {
336
+ const ux = e1[1].x - e1[0].x;
337
+ const uy = e1[1].y - e1[0].y;
338
+ const ul = Math.hypot(ux, uy);
339
+ const vx = e2[1].x - e2[0].x;
340
+ const vy = e2[1].y - e2[0].y;
341
+ const vl = Math.hypot(vx, vy);
342
+ if (ul < 1e-6 || vl < 1e-6) return 0;
343
+ const dx = ux / ul;
344
+ const dy = uy / ul;
345
+ const cross = Math.abs((dx * vy - dy * vx) / vl);
346
+ if (cross > Math.sin(angleDeg * Math.PI / 180)) return 0;
347
+ const perp = (q) => Math.abs((q.x - e1[0].x) * -dy + (q.y - e1[0].y) * dx);
348
+ if (perp(e2[0]) > gap || perp(e2[1]) > gap) return 0;
349
+ const proj = (q) => (q.x - e1[0].x) * dx + (q.y - e1[0].y) * dy;
350
+ const t1 = proj(e2[0]);
351
+ const t2 = proj(e2[1]);
352
+ return Math.max(0, Math.min(ul, Math.max(t1, t2)) - Math.max(0, Math.min(t1, t2)));
353
+ }
354
+ function sectionAdjacency(footprints, opts) {
355
+ const gap = opts?.gapInches ?? 0.5;
356
+ const angle = opts?.angleDegrees ?? 3;
357
+ const min = opts?.minOverlapInches ?? 1;
358
+ const edges = footprints.map((f) => ringEdges(f.outline));
359
+ const out = [];
360
+ for (let i = 0; i < footprints.length; i++) {
361
+ for (let j = i + 1; j < footprints.length; j++) {
362
+ let total = 0;
363
+ for (const e1 of edges[i])
364
+ for (const e2 of edges[j]) total += sharedEdgeLength(e1, e2, gap, angle);
365
+ if (total >= min)
366
+ out.push({
367
+ a: footprints[i].id,
368
+ b: footprints[j].id,
369
+ lengthInches: Math.round(total * 1e3) / 1e3
370
+ });
371
+ }
372
+ }
373
+ return out;
374
+ }
375
+ function sectionNeighbours(id, adj) {
376
+ return adj.filter((x) => x.a === id || x.b === id).map((x) => x.a === id ? x.b : x.a);
377
+ }
378
+ function sectionComponents(ids, adj) {
379
+ const parent = new Map(ids.map((id) => [id, id]));
380
+ const find = (x) => {
381
+ let r = x;
382
+ while (parent.get(r) !== r) r = parent.get(r);
383
+ while (parent.get(x) !== r) {
384
+ const nx = parent.get(x);
385
+ parent.set(x, r);
386
+ x = nx;
387
+ }
388
+ return r;
389
+ };
390
+ for (const { a, b } of adj) {
391
+ if (!parent.has(a) || !parent.has(b)) continue;
392
+ const ra = find(a);
393
+ const rb = find(b);
394
+ if (ra !== rb) parent.set(ra, rb);
395
+ }
396
+ const groups = /* @__PURE__ */ new Map();
397
+ for (const id of ids) {
398
+ const r = find(id);
399
+ groups.set(r, [...groups.get(r) ?? [], id]);
400
+ }
401
+ return [...groups.values()];
402
+ }
259
403
  function endplateFaceSegments(center, widthA = FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES, widthB = FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES, offsetA = 0, offsetB = 0) {
260
404
  if (center.length < 2) return [];
261
405
  const n = centerlineNormals(center);
@@ -273,7 +417,13 @@ function moduleFootprint(input) {
273
417
  const authored = benchworkOutline(input);
274
418
  const offA = input.endplateTrackOffsets?.["A"] ?? 0;
275
419
  const offB = input.endplateTrackOffsets?.["B"] ?? 0;
276
- const sectionOutlines = sectionFootprints(input);
420
+ const sectionOutlines = sectionFootprints(input, {
421
+ centerline,
422
+ widthA,
423
+ widthB,
424
+ offsetA: offA,
425
+ offsetB: offB
426
+ });
277
427
  return {
278
428
  centerline,
279
429
  band: benchworkBand(centerline, widthA, widthB, offA, offB),
@@ -1233,10 +1383,15 @@ exports.poseOverridesFromDoc = poseOverridesFromDoc;
1233
1383
  exports.sampleBenchworkOutline = sampleBenchworkOutline;
1234
1384
  exports.samplePath = samplePath;
1235
1385
  exports.scaleFeetToInches = scaleFeetToInches;
1386
+ exports.sectionAdjacency = sectionAdjacency;
1387
+ exports.sectionBand = sectionBand;
1236
1388
  exports.sectionBreaksFromSections = sectionBreaksFromSections;
1389
+ exports.sectionComponents = sectionComponents;
1237
1390
  exports.sectionFootprints = sectionFootprints;
1391
+ exports.sectionNeighbours = sectionNeighbours;
1238
1392
  exports.sectionSpans = sectionSpans;
1239
1393
  exports.sectionedCenterline = sectionedCenterline;
1394
+ exports.sliceCenterline = sliceCenterline;
1240
1395
  exports.stateToDoc = stateToDoc;
1241
1396
  exports.trackPath = trackPath;
1242
1397
  //# sourceMappingURL=index.cjs.map