@willcgage/module-schematic 0.41.0 → 0.43.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
@@ -695,7 +695,9 @@ function stateToDoc(state, recordNumber) {
695
695
  // C, D, E…
696
696
  label: b.label || `Branch ${i + 1}`,
697
697
  tracks: [{ trackId: MAIN_TRACK_ID, lane: 0, config: b.config }],
698
- at: { pos: b.pos, side: b.side }
698
+ at: { pos: b.pos, side: b.side },
699
+ kind: b.kind ?? "branch",
700
+ ...b.trackId ? { trackId: b.trackId } : {}
699
701
  }))
700
702
  ],
701
703
  state.poseOverrides
@@ -897,7 +899,9 @@ function docToState(doc, fallbackLength, moduleTracks = []) {
897
899
  label: ep.label ?? "Branch",
898
900
  pos: sc(ep.at.pos),
899
901
  side: ep.at.side === "down" ? "down" : "up",
900
- config: ep.tracks?.[0]?.config === "double" ? "double" : "single"
902
+ config: ep.tracks?.[0]?.config === "double" ? "double" : "single",
903
+ kind: ep.kind === "main" ? "main" : "branch",
904
+ trackId: ep.trackId ?? null
901
905
  })),
902
906
  poseOverrides,
903
907
  endplateWidths,
@@ -1141,6 +1145,7 @@ function moduleFeatures(doc) {
1141
1145
  const extraTracks = [];
1142
1146
  for (const t of doc.tracks) {
1143
1147
  if (t.role === "main") continue;
1148
+ if (t.role === "branch") continue;
1144
1149
  if (isCrossover(t.id)) continue;
1145
1150
  const ext = extentOf(t);
1146
1151
  if (!ext) continue;
@@ -1407,6 +1412,67 @@ function deriveEndplatePoses(geo) {
1407
1412
  }
1408
1413
  return poses;
1409
1414
  }
1415
+ var ENDPLATE_LEAD_INCHES = 4;
1416
+ var ENDPLATE_FASCIA_CLEAR_INCHES = 4;
1417
+ function endplateLead(pose, leadInches = ENDPLATE_LEAD_INCHES) {
1418
+ const inwardHeading = norm360(pose.heading + 180);
1419
+ const r = inwardHeading * DEG;
1420
+ return {
1421
+ face: { x: pose.x, y: pose.y },
1422
+ inboard: { x: pose.x + Math.cos(r) * leadInches, y: pose.y + Math.sin(r) * leadInches },
1423
+ inwardHeading
1424
+ };
1425
+ }
1426
+ function trackMeetsEndplateIssues(path, pose, opts) {
1427
+ const issues = [];
1428
+ const lead = opts?.leadInches ?? ENDPLATE_LEAD_INCHES;
1429
+ const tol = opts?.toleranceDeg ?? 5;
1430
+ if (path && path.length >= 2) {
1431
+ const seq = (opts?.end ?? "last") === "last" ? [...path].reverse() : path.slice();
1432
+ const p0 = seq[0];
1433
+ const p1 = seq[1];
1434
+ const wantIn = norm360(pose.heading + 180);
1435
+ const inHead = norm360(Math.atan2(p1.y - p0.y, p1.x - p0.x) / DEG);
1436
+ const diff = Math.abs((inHead - wantIn + 540) % 360 - 180);
1437
+ if (diff > tol)
1438
+ issues.push({
1439
+ code: "not-perpendicular",
1440
+ message: `Track must cross the endplate square (within ${tol}\xB0); it is off by ${Math.round(diff)}\xB0.`
1441
+ });
1442
+ const r = wantIn * DEG;
1443
+ const ux = Math.cos(r);
1444
+ const uy = Math.sin(r);
1445
+ let curved = false;
1446
+ let acc = 0;
1447
+ for (let i = 1; i < seq.length && acc < lead; i++) {
1448
+ const a = seq[i - 1];
1449
+ const b = seq[i];
1450
+ if (a.bulge) curved = true;
1451
+ const rx = b.x - p0.x;
1452
+ const ry = b.y - p0.y;
1453
+ const along = rx * ux + ry * uy;
1454
+ const lat = Math.abs(rx * -uy + ry * ux);
1455
+ if (along <= lead + 0.01 && lat > 0.25) curved = true;
1456
+ acc += Math.hypot(b.x - a.x, b.y - a.y);
1457
+ }
1458
+ if (curved)
1459
+ issues.push({
1460
+ code: "short-lead",
1461
+ message: `The first ${lead}\u2033 from the endplate must be straight and perpendicular.`
1462
+ });
1463
+ }
1464
+ const w = opts?.faceWidthInches;
1465
+ const off = opts?.trackOffsetInches;
1466
+ if (typeof w === "number" && w > 0 && typeof off === "number") {
1467
+ const clear = w / 2 - Math.abs(off);
1468
+ if (clear < ENDPLATE_FASCIA_CLEAR_INCHES)
1469
+ issues.push({
1470
+ code: "fascia-clearance",
1471
+ message: `Track must stay \u2265${ENDPLATE_FASCIA_CLEAR_INCHES}\u2033 from either fascia; it is ${clear.toFixed(1)}\u2033.`
1472
+ });
1473
+ }
1474
+ return issues;
1475
+ }
1410
1476
  function poseNeedsManual(geometryType) {
1411
1477
  return geometryType === "wye" || geometryType === "other";
1412
1478
  }
@@ -1420,6 +1486,8 @@ function poseOverridesFromDoc(doc) {
1420
1486
  return out;
1421
1487
  }
1422
1488
 
1489
+ exports.ENDPLATE_FASCIA_CLEAR_INCHES = ENDPLATE_FASCIA_CLEAR_INCHES;
1490
+ exports.ENDPLATE_LEAD_INCHES = ENDPLATE_LEAD_INCHES;
1423
1491
  exports.FREEMO_ENDPLATE_TRACK_FASCIA_CLEARANCE_INCHES = FREEMO_ENDPLATE_TRACK_FASCIA_CLEARANCE_INCHES;
1424
1492
  exports.FREEMO_ENDPLATE_WIDTH_MIN_INCHES = FREEMO_ENDPLATE_WIDTH_MIN_INCHES;
1425
1493
  exports.FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES = FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES;
@@ -1442,6 +1510,7 @@ exports.divergeSideForHand = divergeSideForHand;
1442
1510
  exports.docToState = docToState;
1443
1511
  exports.emptyEditorState = emptyEditorState;
1444
1512
  exports.endplateFaceSegments = endplateFaceSegments;
1513
+ exports.endplateLead = endplateLead;
1445
1514
  exports.endplateTrackOffsetFor = endplateTrackOffsetFor;
1446
1515
  exports.endplateTrackOffsetInches = endplateTrackOffsetInches;
1447
1516
  exports.endplateWidthInches = endplateWidthInches;
@@ -1475,6 +1544,7 @@ exports.sectionedEndPose = sectionedEndPose;
1475
1544
  exports.sliceCenterline = sliceCenterline;
1476
1545
  exports.stateToDoc = stateToDoc;
1477
1546
  exports.toSectionRelative = toSectionRelative;
1547
+ exports.trackMeetsEndplateIssues = trackMeetsEndplateIssues;
1478
1548
  exports.trackPath = trackPath;
1479
1549
  //# sourceMappingURL=index.cjs.map
1480
1550
  //# sourceMappingURL=index.cjs.map