@willcgage/module-schematic 0.57.0 → 0.58.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
@@ -769,7 +769,8 @@ function stateToDoc(state, recordNumber) {
769
769
  name: t.name || void 0,
770
770
  ...t.size ? { size: t.size } : {},
771
771
  ...t.curved ? { curved: true } : {},
772
- ...t.flipped ? { flipped: true } : {}
772
+ ...t.flipped ? { flipped: true } : {},
773
+ ...t.partId ? { partId: t.partId } : {}
773
774
  })),
774
775
  ...state.crossings.length > 0 ? {
775
776
  crossings: state.crossings.map((x) => ({
@@ -957,7 +958,8 @@ function docToState(doc, fallbackLength, moduleTracks = []) {
957
958
  kind: t.kind ?? "right",
958
959
  ...t.size ? { size: t.size } : {},
959
960
  ...t.curved ? { curved: true } : {},
960
- ...t.flipped ? { flipped: true } : {}
961
+ ...t.flipped ? { flipped: true } : {},
962
+ ...t.partId ? { partId: t.partId } : {}
961
963
  })),
962
964
  controlPoints: readControlPoints(d, sc),
963
965
  industries: (d.industries ?? []).map((ind) => ({
@@ -1399,6 +1401,44 @@ function mergeImportedParts(imported, library = BUILT_IN_TRACK_PARTS, sourceName
1399
1401
  }
1400
1402
  return out;
1401
1403
  }
1404
+ function partOutlineAtFrog(part, leadInches, stepsPerCurve = 16) {
1405
+ const ends = part.ends ?? [];
1406
+ const segs = part.segments ?? [];
1407
+ if (!segs.length || ends.length < 2) return null;
1408
+ let points = ends[0];
1409
+ if (ends.length >= 3) {
1410
+ let bestPair = Infinity;
1411
+ let pairIdx = [1, 2];
1412
+ for (let i = 0; i < ends.length; i++) {
1413
+ for (let j = i + 1; j < ends.length; j++) {
1414
+ const d = Math.hypot(ends[i].x - ends[j].x, ends[i].y - ends[j].y);
1415
+ if (d < bestPair) {
1416
+ bestPair = d;
1417
+ pairIdx = [i, j];
1418
+ }
1419
+ }
1420
+ }
1421
+ const odd = ends.findIndex((_, k) => k !== pairIdx[0] && k !== pairIdx[1]);
1422
+ if (odd >= 0) points = ends[odd];
1423
+ }
1424
+ const a = points.angleDeg * Math.PI / 180;
1425
+ const ux = -Math.sin(a);
1426
+ const uy = -Math.cos(a);
1427
+ const toLocal = (p) => {
1428
+ const dx = p.x - points.x;
1429
+ const dy = p.y - points.y;
1430
+ return { x: dx * ux + dy * uy, y: dx * -uy + dy * ux };
1431
+ };
1432
+ let sign = 1;
1433
+ const offAxis = ends.filter((e) => e !== points).map(toLocal).sort((p, q) => Math.abs(q.y) - Math.abs(p.y))[0];
1434
+ if (offAxis && offAxis.y < 0) sign = -1;
1435
+ return samplePartSegments(segs, stepsPerCurve).map(
1436
+ (poly) => poly.map((p) => {
1437
+ const l = toLocal(p);
1438
+ return { x: l.x - leadInches, y: l.y * sign };
1439
+ })
1440
+ );
1441
+ }
1402
1442
  function turnoutClosure(size, opts = {}) {
1403
1443
  const N = size > 0 ? size : 6;
1404
1444
  const g = opts.gaugeInches ?? RAIL_GAUGE_INCHES;
@@ -1977,6 +2017,7 @@ exports.moduleLengthFromSections = moduleLengthFromSections;
1977
2017
  exports.moduleSections = moduleSections;
1978
2018
  exports.nextId = nextId;
1979
2019
  exports.parseXtpLibrary = parseXtpLibrary;
2020
+ exports.partOutlineAtFrog = partOutlineAtFrog;
1980
2021
  exports.poseNeedsManual = poseNeedsManual;
1981
2022
  exports.poseOverridesFromDoc = poseOverridesFromDoc;
1982
2023
  exports.remapPos = remapPos;