@willcgage/module-schematic 0.79.0 → 0.81.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
@@ -1528,8 +1528,97 @@ var FLEX_TRACK_PARTS = [
1528
1528
  }
1529
1529
  }
1530
1530
  ];
1531
+ var FAST_TRACKS_N_ME55 = [
1532
+ // [kind, N, angle°, divergingR, defaultLength, minLength, substitutionR]
1533
+ ["turnout", 4, 14.04, 8, 4.57, 3.43, 11],
1534
+ ["turnout", 4.5, 12.53, 11, 5, 3.9, 14],
1535
+ ["turnout", 5, 11.31, 14, 5.39, 3.92, 17],
1536
+ ["turnout", 6, 9.46, 23, 6.26, 4.3, 24],
1537
+ ["turnout", 7, 8.13, 27, 7.4, 5.44, 34],
1538
+ ["turnout", 8, 7.13, 36, 8, 5.93, 42],
1539
+ ["turnout", 9, 6.34, 50, 8.38, 6.42, 57],
1540
+ ["turnout", 10, 5.71, 64, 9.15, 6.8, 70],
1541
+ ["turnout", 12, 4.76, 82, 10.34, 7.95, 90],
1542
+ ["wye", 4, 14.04, 23, 4.63, 3.48, 27],
1543
+ ["wye", 5, 11.31, 25, 5.23, 3.81, 30],
1544
+ ["wye", 6, 9.46, 35, 5.99, 4.3, 47],
1545
+ ["wye", 8, 7.13, 70, 8.22, 5.93, 94],
1546
+ ["wye", 10, 5.71, 89, 9.47, 6.97, 130]
1547
+ ].map(([kind, n, deg, divR, dflt, min, subR]) => {
1548
+ const code = kind === "wye" ? "y" : "t";
1549
+ const spec = "handlaidtrack.com Detailed Specifications, read 2026-07-26";
1550
+ const manufacturer = "manufacturer";
1551
+ return {
1552
+ id: `fast-tracks-n-me55-${code}-${n}`,
1553
+ manufacturer: "Fast Tracks",
1554
+ line: "Code 55",
1555
+ scale: "N",
1556
+ // Named for what the OWNER has, not what Fast Tracks sell. They sell a jig;
1557
+ // the person picking this in the editor is holding the turnout they built
1558
+ // on it. `buildable` and the AF-… part number carry the fixture fact.
1559
+ name: `#${n} ${kind === "wye" ? "Wye" : "Turnout"}`,
1560
+ kind,
1561
+ partNumbers: { single: `AF-N-${code.toUpperCase()}-${n}-ME55` },
1562
+ frogNumber: n,
1563
+ buildable: true,
1564
+ actualAngle: {
1565
+ deg,
1566
+ source: manufacturer,
1567
+ note: `${spec}. Matches atan(1/${n}) to the published digits \u2014 Fast Tracks build to TRUE frog ratios, unlike Atlas's sectional angles.`
1568
+ },
1569
+ divergingRadius: { inches: divR, source: manufacturer, note: spec },
1570
+ overallLength: {
1571
+ inches: dflt,
1572
+ source: manufacturer,
1573
+ note: `${spec}. The DEFAULT length \u2014 this is a fixture, so the builder chooses.`
1574
+ },
1575
+ minimumLength: { inches: min, source: manufacturer, note: spec },
1576
+ substitutionRadius: { inches: subR, source: manufacturer, note: spec }
1577
+ };
1578
+ });
1579
+ var FAST_TRACKS_N_ME55_CROSSOVERS = [
1580
+ // [N, angle°, secondFrog°, defaultLength, minLength, trackSpacing]
1581
+ [6, 9.46, 19, 10.07, 9.31, 1.09],
1582
+ [8, 7.13, 14.3, 13.61, 13.07, 1.09]
1583
+ ].map(([n, deg, second, dflt, min, spacing]) => {
1584
+ const spec = "handlaidtrack.com Detailed Specifications, read 2026-07-26";
1585
+ const manufacturer = "manufacturer";
1586
+ return {
1587
+ id: `fast-tracks-n-me55-c-${n}`,
1588
+ manufacturer: "Fast Tracks",
1589
+ line: "Code 55",
1590
+ scale: "N",
1591
+ name: `#${n} Crossover`,
1592
+ kind: "crossover",
1593
+ partNumbers: { single: `AF-N-C-${n}-ME55` },
1594
+ frogNumber: n,
1595
+ buildable: true,
1596
+ actualAngle: { deg, source: manufacturer, note: `${spec}. atan(1/${n}) exactly.` },
1597
+ secondaryFrogAngle: {
1598
+ deg: second,
1599
+ source: manufacturer,
1600
+ note: `${spec}. The diamond of a scissors crossover \u2014 2 \xD7 ${deg}\xB0 within the published rounding.`
1601
+ },
1602
+ overallLength: {
1603
+ inches: dflt,
1604
+ source: manufacturer,
1605
+ note: `${spec}. The DEFAULT length of the whole assembly \u2014 a fixture, so the builder chooses.`
1606
+ },
1607
+ minimumLength: { inches: min, source: manufacturer, note: spec },
1608
+ trackSpacing: {
1609
+ inches: spacing,
1610
+ source: manufacturer,
1611
+ note: `${spec}. \u26A0\uFE0F Free-moN \xA72.0 requires ${FREEMO_TRACK_SPACING_INCHES}\u2033 \u2014 this fixture is built to ${spacing}\u2033, ${(FREEMO_TRACK_SPACING_INCHES - spacing).toFixed(3)}\u2033 tighter, and cannot be built to another spacing.`
1612
+ }
1613
+ };
1614
+ });
1531
1615
  var DEFAULT_FLEX_PART_ID = "atlas-c55-n-flex";
1532
- var BUILT_IN_TRACK_PARTS = [...ATLAS_CODE55_N, ...FLEX_TRACK_PARTS];
1616
+ var BUILT_IN_TRACK_PARTS = [
1617
+ ...ATLAS_CODE55_N,
1618
+ ...FAST_TRACKS_N_ME55,
1619
+ ...FAST_TRACKS_N_ME55_CROSSOVERS,
1620
+ ...FLEX_TRACK_PARTS
1621
+ ];
1533
1622
  function flexParts(library = BUILT_IN_TRACK_PARTS) {
1534
1623
  return library.filter((p) => p.kind === "flex");
1535
1624
  }
@@ -1656,9 +1745,14 @@ function partExtentForSize(size, library = BUILT_IN_TRACK_PARTS) {
1656
1745
  function turnoutPartForSize(size, library = BUILT_IN_TRACK_PARTS) {
1657
1746
  const turnouts = library.filter((p) => p.kind === "turnout" && p.frogNumber != null);
1658
1747
  if (!turnouts.length) return null;
1659
- return turnouts.reduce(
1660
- (best, p) => Math.abs(p.frogNumber - size) < Math.abs(best.frogNumber - size) ? p : best
1661
- );
1748
+ const dist = (p) => Math.abs(p.frogNumber - size);
1749
+ return turnouts.reduce((best, p) => {
1750
+ const d = dist(p);
1751
+ const bd = dist(best);
1752
+ if (d !== bd) return d < bd ? p : best;
1753
+ if (!partExtent(best) && partExtent(p)) return p;
1754
+ return best;
1755
+ });
1662
1756
  }
1663
1757
  function measuredLeadPoints(library) {
1664
1758
  return library.filter(
@@ -1666,8 +1760,10 @@ function measuredLeadPoints(library) {
1666
1760
  ).map((p) => ({ n: p.frogNumber, lead: p.lead.inches })).sort((a, b) => a.n - b.n);
1667
1761
  }
1668
1762
  function leadInchesForSize(size, library = BUILT_IN_TRACK_PARTS) {
1669
- const part = turnoutPartForSize(size, library);
1670
- if (part?.lead && part.frogNumber === size) return part.lead.inches;
1763
+ const exact = library.find(
1764
+ (p) => p.kind === "turnout" && p.frogNumber === size && p.lead != null
1765
+ );
1766
+ if (exact) return exact.lead.inches;
1671
1767
  const pts = measuredLeadPoints(library);
1672
1768
  if (!pts.length) return size * TURNOUT_LEAD_INCHES_PER_FROG;
1673
1769
  if (pts.length === 1) return size / pts[0].n * pts[0].lead;
@@ -1844,6 +1940,19 @@ function storedPartToTrackPart(row) {
1844
1940
  if (overall) part.overallLength = overall;
1845
1941
  const diverging = dim(row.divergingLengthInches, row.divergingLengthSource);
1846
1942
  if (diverging) part.divergingLength = diverging;
1943
+ const minimum = dim(row.minimumLengthInches, row.minimumLengthSource);
1944
+ if (minimum) part.minimumLength = minimum;
1945
+ const substitution = dim(row.substitutionRadiusInches, row.substitutionRadiusSource);
1946
+ if (substitution) part.substitutionRadius = substitution;
1947
+ const spacing = dim(row.trackSpacingInches, row.trackSpacingSource);
1948
+ if (spacing) part.trackSpacing = spacing;
1949
+ if (typeof row.secondaryFrogAngleDeg === "number")
1950
+ part.secondaryFrogAngle = {
1951
+ deg: row.secondaryFrogAngleDeg,
1952
+ source: asSource(row.secondaryFrogAngleSource),
1953
+ ...note ? { note } : {}
1954
+ };
1955
+ if (row.buildable) part.buildable = true;
1847
1956
  if (lead) part.lead = lead;
1848
1957
  const outer = dim(row.outerRadiusInches, row.radiusSource);
1849
1958
  const inner = dim(row.innerRadiusInches, row.radiusSource);
@@ -2545,6 +2654,8 @@ exports.CODE55_RAIL_HEIGHT_INCHES = CODE55_RAIL_HEIGHT_INCHES;
2545
2654
  exports.DEFAULT_FLEX_PART_ID = DEFAULT_FLEX_PART_ID;
2546
2655
  exports.ENDPLATE_FASCIA_CLEAR_INCHES = ENDPLATE_FASCIA_CLEAR_INCHES;
2547
2656
  exports.ENDPLATE_LEAD_INCHES = ENDPLATE_LEAD_INCHES;
2657
+ exports.FAST_TRACKS_N_ME55 = FAST_TRACKS_N_ME55;
2658
+ exports.FAST_TRACKS_N_ME55_CROSSOVERS = FAST_TRACKS_N_ME55_CROSSOVERS;
2548
2659
  exports.FLEX_TRACK_PARTS = FLEX_TRACK_PARTS;
2549
2660
  exports.FREEMO_ENDPLATE_TRACK_FASCIA_CLEARANCE_INCHES = FREEMO_ENDPLATE_TRACK_FASCIA_CLEARANCE_INCHES;
2550
2661
  exports.FREEMO_ENDPLATE_WIDTH_MIN_INCHES = FREEMO_ENDPLATE_WIDTH_MIN_INCHES;