@willcgage/module-schematic 0.77.0 → 0.79.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.d.cts CHANGED
@@ -1486,6 +1486,15 @@ interface TrackPart {
1486
1486
  frogOffset?: PartDimension;
1487
1487
  /** End-to-end length of the part. */
1488
1488
  overallLength?: PartDimension;
1489
+ /** Frog apex → the end of the diverging rail, measured ALONG that rail. The
1490
+ * independent cross-check on {@link frogOffset}: it must be slightly LONGER
1491
+ * than the axial `overallLength − frogOffset`, because the rail is the
1492
+ * hypotenuse of the angle it leaves at. A reading that makes that difference
1493
+ * negative means a mis-read frog — which is exactly how the 2057 wye's frog
1494
+ * was caught (see its note). Too insensitive to recover the angle from — at
1495
+ * these angles ¹⁄₃₂″ of slop swings the implied half-angle by 6° — so use it
1496
+ * to falsify a frog position, never to derive one. */
1497
+ divergingLength?: PartDimension;
1489
1498
  /** Diverging route radius (straight turnouts). */
1490
1499
  divergingRadius?: PartDimension;
1491
1500
  /** Curved turnouts: the two concentric radii. */
@@ -1520,6 +1529,15 @@ declare const CODE55_RAIL_HEIGHT_INCHES = 0.055;
1520
1529
  * #5 1¾″ 4.75″ 6.00″ 3.00000″ 1.25″ 0.2500
1521
1530
  * #7 ⁵⁄₈″ 4⁷⁄₃₂″ 6.00″ 3.59375″ 1.78125″ 0.2545
1522
1531
  * #10 ⁹⁄₁₆″ 5.50″ 8.00″ 4.93750″ 2.50″ 0.2500
1532
+ * wye points frog V overall lead past frog diverging rail
1533
+ * #2.5 1⁵⁄₈″ 4⅛″ 6.50″ 2.50000″ 2.375″ 2.4375″
1534
+ * #3.5 ¾″ 3⁵⁄₃₂″ 5.00″ 2.40625″ 1.84375″ 1.9375″
1535
+ *
1536
+ * ⚠️ THE WYES ARE NOT ON THE ÷N TREND AND MUST NOT BE INTERPOLATED WITH THE
1537
+ * TURNOUTS. lead ÷ N is 1.00 and 0.69 against the turnouts' flat 0.25, and the
1538
+ * #2.5 is the LONGER part despite the sharper frog. Every size lookup here
1539
+ * filters `kind === "turnout"`, which is what keeps them out — do not "fix"
1540
+ * that filter to include wyes.
1523
1541
  *
1524
1542
  * Every one of these is Will Gage's, off a physical part, frog taken at the V.
1525
1543
  * ⚠️ The #7 lead SUPERSEDES Steve Branton's 3⅜″ (#173) — the library's founding
@@ -1883,6 +1901,8 @@ interface StoredTrackPart {
1883
1901
  frogOffsetSource?: string | null;
1884
1902
  overallLengthInches?: number | null;
1885
1903
  overallLengthSource?: string | null;
1904
+ divergingLengthInches?: number | null;
1905
+ divergingLengthSource?: string | null;
1886
1906
  leadInches?: number | null;
1887
1907
  leadSource?: string | null;
1888
1908
  outerRadiusInches?: number | null;
@@ -1916,6 +1936,17 @@ declare function storedPartToTrackPart(row: StoredTrackPart): TrackPart;
1916
1936
  *
1917
1937
  * The built-ins remain the floor: a part nobody has stored still resolves, so
1918
1938
  * geometry keeps working with no database at all.
1939
+ *
1940
+ * ⛔ THE TRAP THIS CREATES, and it has already been sprung once. Replacement is
1941
+ * WHOLESALE, so a stored row that is merely INCOMPLETE deletes every dimension
1942
+ * the built-in had. When both Atlas wyes were measured in 0.78.0 their stored
1943
+ * rows still held null offsets from the original seed — so in production the
1944
+ * new measurements were invisible and the 2056 still carried a `derived` lead
1945
+ * the release had just retired. Nothing failed; the parts simply had no
1946
+ * dimensions, which is indistinguishable from never having measured them.
1947
+ *
1948
+ * **Measuring a built-in is therefore not finished until the stored row is
1949
+ * updated too.** Adding a dimension here and stopping ships nothing.
1919
1950
  */
1920
1951
  declare function mergeStoredParts(stored: StoredTrackPart[], library?: TrackPart[]): TrackPart[];
1921
1952
  /**
package/dist/index.d.ts CHANGED
@@ -1486,6 +1486,15 @@ interface TrackPart {
1486
1486
  frogOffset?: PartDimension;
1487
1487
  /** End-to-end length of the part. */
1488
1488
  overallLength?: PartDimension;
1489
+ /** Frog apex → the end of the diverging rail, measured ALONG that rail. The
1490
+ * independent cross-check on {@link frogOffset}: it must be slightly LONGER
1491
+ * than the axial `overallLength − frogOffset`, because the rail is the
1492
+ * hypotenuse of the angle it leaves at. A reading that makes that difference
1493
+ * negative means a mis-read frog — which is exactly how the 2057 wye's frog
1494
+ * was caught (see its note). Too insensitive to recover the angle from — at
1495
+ * these angles ¹⁄₃₂″ of slop swings the implied half-angle by 6° — so use it
1496
+ * to falsify a frog position, never to derive one. */
1497
+ divergingLength?: PartDimension;
1489
1498
  /** Diverging route radius (straight turnouts). */
1490
1499
  divergingRadius?: PartDimension;
1491
1500
  /** Curved turnouts: the two concentric radii. */
@@ -1520,6 +1529,15 @@ declare const CODE55_RAIL_HEIGHT_INCHES = 0.055;
1520
1529
  * #5 1¾″ 4.75″ 6.00″ 3.00000″ 1.25″ 0.2500
1521
1530
  * #7 ⁵⁄₈″ 4⁷⁄₃₂″ 6.00″ 3.59375″ 1.78125″ 0.2545
1522
1531
  * #10 ⁹⁄₁₆″ 5.50″ 8.00″ 4.93750″ 2.50″ 0.2500
1532
+ * wye points frog V overall lead past frog diverging rail
1533
+ * #2.5 1⁵⁄₈″ 4⅛″ 6.50″ 2.50000″ 2.375″ 2.4375″
1534
+ * #3.5 ¾″ 3⁵⁄₃₂″ 5.00″ 2.40625″ 1.84375″ 1.9375″
1535
+ *
1536
+ * ⚠️ THE WYES ARE NOT ON THE ÷N TREND AND MUST NOT BE INTERPOLATED WITH THE
1537
+ * TURNOUTS. lead ÷ N is 1.00 and 0.69 against the turnouts' flat 0.25, and the
1538
+ * #2.5 is the LONGER part despite the sharper frog. Every size lookup here
1539
+ * filters `kind === "turnout"`, which is what keeps them out — do not "fix"
1540
+ * that filter to include wyes.
1523
1541
  *
1524
1542
  * Every one of these is Will Gage's, off a physical part, frog taken at the V.
1525
1543
  * ⚠️ The #7 lead SUPERSEDES Steve Branton's 3⅜″ (#173) — the library's founding
@@ -1883,6 +1901,8 @@ interface StoredTrackPart {
1883
1901
  frogOffsetSource?: string | null;
1884
1902
  overallLengthInches?: number | null;
1885
1903
  overallLengthSource?: string | null;
1904
+ divergingLengthInches?: number | null;
1905
+ divergingLengthSource?: string | null;
1886
1906
  leadInches?: number | null;
1887
1907
  leadSource?: string | null;
1888
1908
  outerRadiusInches?: number | null;
@@ -1916,6 +1936,17 @@ declare function storedPartToTrackPart(row: StoredTrackPart): TrackPart;
1916
1936
  *
1917
1937
  * The built-ins remain the floor: a part nobody has stored still resolves, so
1918
1938
  * geometry keeps working with no database at all.
1939
+ *
1940
+ * ⛔ THE TRAP THIS CREATES, and it has already been sprung once. Replacement is
1941
+ * WHOLESALE, so a stored row that is merely INCOMPLETE deletes every dimension
1942
+ * the built-in had. When both Atlas wyes were measured in 0.78.0 their stored
1943
+ * rows still held null offsets from the original seed — so in production the
1944
+ * new measurements were invisible and the 2056 still carried a `derived` lead
1945
+ * the release had just retired. Nothing failed; the parts simply had no
1946
+ * dimensions, which is indistinguishable from never having measured them.
1947
+ *
1948
+ * **Measuring a built-in is therefore not finished until the stored row is
1949
+ * updated too.** Adding a dimension here and stopping ships nothing.
1919
1950
  */
1920
1951
  declare function mergeStoredParts(stored: StoredTrackPart[], library?: TrackPart[]): TrackPart[];
1921
1952
  /**
package/dist/index.js CHANGED
@@ -1411,9 +1411,29 @@ var ATLAS_CODE55_N = [
1411
1411
  partNumbers: { single: "2056" },
1412
1412
  frogNumber: 2.5,
1413
1413
  lead: {
1414
- inches: 2.5 * TURNOUT_LEAD_INCHES_PER_FROG,
1415
- source: "derived",
1416
- note: "scaled from the measured #7 by frog number \u2014 not measured"
1414
+ inches: 2.5,
1415
+ source: "measured",
1416
+ note: "Will Gage, physical Atlas 2056, 2026-07-26: points at 1\u2075\u2044\u2088\u2033, frog at 4\u215B\u2033 \u21D2 2.5\u2033 \u2014 the difference of the two offsets, per the rule above. \u26A0\uFE0F RETIRES a `derived` 1.205\u2033 (2.5 \xD7 TURNOUT_LEAD_INCHES_PER_FROG) \u2014 the real lead is more than DOUBLE it. Third independent refutation of the per-frog rule, and the largest: at low N it is not merely mis-sloped, it is nowhere near."
1417
+ },
1418
+ pointsOffset: {
1419
+ inches: 1.625,
1420
+ source: "measured",
1421
+ note: "Will Gage, physical Atlas 2056 \u2014 end of tie strip to point tips (1\xB9\u2070\u2044\u2081\u2086\u2033)"
1422
+ },
1423
+ frogOffset: {
1424
+ inches: 4.125,
1425
+ source: "measured",
1426
+ note: "Will Gage, physical Atlas 2056 \u2014 tie end to the apex of the V (4\xB2\u2044\u2081\u2086\u2033)"
1427
+ },
1428
+ overallLength: {
1429
+ inches: 6.5,
1430
+ source: "measured",
1431
+ note: "Will Gage, physical Atlas 2056 \u2014 end to end"
1432
+ },
1433
+ divergingLength: {
1434
+ inches: 2.4375,
1435
+ source: "measured",
1436
+ note: "Will Gage, physical Atlas 2056 \u2014 frog to the end of each diverging rail (2\u2077\u2044\u2081\u2086\u2033, both legs equal). \u2714 CROSS-CHECKS the frog: axial past-frog is 6.5 \u2212 4.125 = 2.375\u2033, and the rail is \xB9\u2044\u2081\u2086\u2033 longer than its own projection, which is the right sign and the right order of magnitude."
1417
1437
  }
1418
1438
  },
1419
1439
  {
@@ -1421,11 +1441,17 @@ var ATLAS_CODE55_N = [
1421
1441
  // wyes are different FROG NUMBERS, not a left/right pair of one part. The
1422
1442
  // 2056 half of that confirms the entry above; this is the one we didn't have.
1423
1443
  //
1424
- // No lead. The #2.5's is `derived` from a per-frog rule its own note records
1425
- // as refuted, so it rests on nothing — repeating that for a second part
1426
- // would be exactly how a number nobody checked becomes two. Without one,
1427
- // `leadInchesForSize` interpolates across the MEASURED parts at the wye's
1428
- // effective frog (3.5 × 2 = 7), which lands on the measured #7.
1444
+ // Fully measured 2026-07-26, after one reading was caught and re-read.
1445
+ //
1446
+ // ⚠️ WORKED EXAMPLE OF WHY THIS LIBRARY CROSS-CHECKS. The frog first came in
1447
+ // at 5⁵⁄₃₂″, which is PAST the part's own 5″ overall length — the apex would
1448
+ // have sat beyond the end of the moulding. Two independent lines said ~3⅛″:
1449
+ // · points ¾″ + points→frog 2⅜″ = 3.125″
1450
+ // · overall 5″ − diverging rail 1³⁰⁄₃₂″ ≈ 3.06″ axial
1451
+ // Queried, and Will re-read it as 3⁵⁄₃₂″ — a leading-digit slip, exactly what
1452
+ // the arithmetic implied. That re-read is the number recorded here; it was
1453
+ // NOT inferred. Had it been taken at face value the wye would have drawn its
1454
+ // frog off the end of itself.
1429
1455
  id: "atlas-c55-n-wye-35",
1430
1456
  manufacturer: "Atlas",
1431
1457
  line: "Code 55",
@@ -1433,7 +1459,32 @@ var ATLAS_CODE55_N = [
1433
1459
  name: "#3.5 Wye",
1434
1460
  kind: "wye",
1435
1461
  partNumbers: { single: "2057" },
1436
- frogNumber: 3.5
1462
+ frogNumber: 3.5,
1463
+ lead: {
1464
+ inches: 2.40625,
1465
+ source: "measured",
1466
+ note: "Will Gage, physical Atlas 2057, 2026-07-26: points at \xBE\u2033, frog at 3\u2075\u2044\u2083\u2082\u2033 \u21D2 2.40625\u2033 \u2014 the difference of the two offsets, per the rule above. His direct points\u2192frog reading was 2\u215C\u2033, \xB9\u2044\u2083\u2082\u2033 short; the offsets win. \u26A0\uFE0F RETIRES the reasoning in this entry's original note, which left the lead unset so `leadInchesForSize` would interpolate at the wye's effective frog (3.5 \xD7 2 = 7) and land on the measured #7's 3.59375\u2033. That substitution was 49% over."
1467
+ },
1468
+ pointsOffset: {
1469
+ inches: 0.75,
1470
+ source: "measured",
1471
+ note: "Will Gage, physical Atlas 2057 \u2014 end of tie strip to point tips (\xBE\u2033)"
1472
+ },
1473
+ frogOffset: {
1474
+ inches: 3.15625,
1475
+ source: "measured",
1476
+ note: "Will Gage, physical Atlas 2057 \u2014 tie end to the apex of the V (3\u2075\u2044\u2083\u2082\u2033). This is his RE-READ after the first reading (5\u2075\u2044\u2083\u2082\u2033) was queried for landing past the part's 5\u2033 end; see the comment above this entry."
1477
+ },
1478
+ overallLength: {
1479
+ inches: 5,
1480
+ source: "measured",
1481
+ note: "Will Gage, physical Atlas 2057 \u2014 end to end"
1482
+ },
1483
+ divergingLength: {
1484
+ inches: 1.9375,
1485
+ source: "measured",
1486
+ note: "Will Gage, physical Atlas 2057 \u2014 frog to the end of each diverging rail (1\xB3\u2070\u2044\u2083\u2082\u2033, both legs equal). \u2714 THE READING THAT CAUGHT THE BAD FROG: against the first 5\u2075\u2044\u2083\u2082\u2033 the axial past-frog is NEGATIVE, which a 1.9375\u2033 rail flatly contradicts. Against the re-read 3\u2075\u2044\u2083\u2082\u2033 it is 5 \u2212 3.15625 = 1.84375\u2033, so the rail runs \xB3\u2044\u2083\u2082\u2033 longer than its own projection \u2014 right sign, same order as the 2056's \xB9\u2044\u2081\u2086\u2033. (The two differ because this is a cos() residual on a small angle, which is why the field doc says not to read an angle back out of it.)"
1487
+ }
1437
1488
  },
1438
1489
  {
1439
1490
  id: "atlas-c55-n-curved-21-15",
@@ -1789,6 +1840,8 @@ function storedPartToTrackPart(row) {
1789
1840
  if (frog) part.frogOffset = frog;
1790
1841
  const overall = dim(row.overallLengthInches, row.overallLengthSource);
1791
1842
  if (overall) part.overallLength = overall;
1843
+ const diverging = dim(row.divergingLengthInches, row.divergingLengthSource);
1844
+ if (diverging) part.divergingLength = diverging;
1792
1845
  if (lead) part.lead = lead;
1793
1846
  const outer = dim(row.outerRadiusInches, row.radiusSource);
1794
1847
  const inner = dim(row.innerRadiusInches, row.radiusSource);