@willcgage/module-schematic 0.109.0 → 0.111.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
@@ -3149,8 +3149,12 @@ function graphToDoc(pieces, input) {
3149
3149
  for (const t of tracks) {
3150
3150
  if (t.role === "main") continue;
3151
3151
  const ends = turnouts.filter((sw) => sw.divergeTrack === t.id);
3152
- if (ends.length >= 2 && new Set(ends.map((sw) => sw.onTrack)).size >= 2)
3153
- t.role = "crossover";
3152
+ if (!(ends.length >= 2 && new Set(ends.map((sw) => sw.onTrack)).size >= 2)) continue;
3153
+ t.role = "crossover";
3154
+ const at = ends.map((sw) => sw.pos);
3155
+ t.fromPos = round(Math.min(...at));
3156
+ t.toPos = round(Math.max(...at));
3157
+ t.lane = main2Lane || 1;
3154
3158
  }
3155
3159
  const place = (a, what) => {
3156
3160
  if (!a) return null;
@@ -3417,7 +3421,12 @@ function docToGraph(doc, answers = {}, library = BUILT_IN_TRACK_PARTS) {
3417
3421
  const ends = [branch?.fromPos, branch?.toPos].filter(
3418
3422
  (n) => typeof n === "number" && Number.isFinite(n)
3419
3423
  );
3420
- const divergeFarPos = ends.length ? ends.reduce((a, b) => Math.abs(b - t.pos) > Math.abs(a - t.pos) ? b : a) : void 0;
3424
+ const divergeFarPos = (
3425
+ // ⭐ A crossover half diverges TOWARD its partner. Its diverging track is a
3426
+ // MAIN, whose ends are the whole module, so the ordinary rule has nothing
3427
+ // useful to sign and would point it away from the turnout it feeds.
3428
+ divergeFarOverride.get(t.id) ?? (ends.length ? ends.reduce((a, b) => Math.abs(b - t.pos) > Math.abs(a - t.pos) ? b : a) : void 0)
3429
+ );
3421
3430
  const facing = turnoutFacing({
3422
3431
  pos: t.pos,
3423
3432
  divergeFarPos,
@@ -3534,6 +3543,7 @@ function docToGraph(doc, answers = {}, library = BUILT_IN_TRACK_PARTS) {
3534
3543
  fromPos: a.x0,
3535
3544
  toPos: a.x0 + a.geom.lengthInches
3536
3545
  }));
3546
+ const mainIdsAll = new Set(mains.map((m) => m.id));
3537
3547
  const attachment = /* @__PURE__ */ new Map();
3538
3548
  for (const t of turnouts) {
3539
3549
  const m = mains.find((x) => x.id === t.divergeTrack && x.id !== main.id);
@@ -3542,11 +3552,34 @@ function docToGraph(doc, answers = {}, library = BUILT_IN_TRACK_PARTS) {
3542
3552
  const to = m.toPos ?? mainLen;
3543
3553
  if (from > 0 && Math.abs(t.pos - from) < 0.01) attachment.set(m.id, { sw: t, at: "from" });
3544
3554
  else if (Math.abs(t.pos - to) < 0.01) attachment.set(m.id, { sw: t, at: "to" });
3545
- else
3546
- notLaid.push({
3547
- id: m.id,
3548
- why: `${t.name || t.id} joins ${m.id} at ${t.pos}\u2033, which is neither end of it \u2014 two mains joined partway along are a crossover, and this document has no connector track between them to lay one from`
3555
+ }
3556
+ const mainToMainPairs = [];
3557
+ {
3558
+ const isMain = (id) => mainIdsAll.has(id);
3559
+ const used = /* @__PURE__ */ new Set();
3560
+ for (const t of turnouts) {
3561
+ if (used.has(t.id) || !isMain(t.onTrack) || !isMain(t.divergeTrack)) continue;
3562
+ if (t.onTrack === t.divergeTrack) continue;
3563
+ const partner = turnouts.filter(
3564
+ (u) => !used.has(u.id) && u.id !== t.id && u.onTrack === t.divergeTrack && u.divergeTrack === t.onTrack
3565
+ ).sort((x, y) => Math.abs(x.pos - t.pos) - Math.abs(y.pos - t.pos))[0];
3566
+ if (!partner) continue;
3567
+ used.add(t.id);
3568
+ used.add(partner.id);
3569
+ mainToMainPairs.push({
3570
+ a: t,
3571
+ b: partner,
3572
+ pairKey: [t.onTrack, t.divergeTrack].sort().join("\u2194")
3549
3573
  });
3574
+ }
3575
+ }
3576
+ const crossingsBetween = /* @__PURE__ */ new Map();
3577
+ for (const p of mainToMainPairs)
3578
+ crossingsBetween.set(p.pairKey, (crossingsBetween.get(p.pairKey) ?? 0) + 1);
3579
+ const divergeFarOverride = /* @__PURE__ */ new Map();
3580
+ for (const { a, b } of mainToMainPairs) {
3581
+ divergeFarOverride.set(a.id, b.pos);
3582
+ divergeFarOverride.set(b.id, a.pos);
3550
3583
  }
3551
3584
  for (const m of mains) {
3552
3585
  if (attachment.get(m.id)?.at === "from") continue;
@@ -3601,6 +3634,36 @@ function docToGraph(doc, answers = {}, library = BUILT_IN_TRACK_PARTS) {
3601
3634
  done.add(m.id);
3602
3635
  }
3603
3636
  for (const id of assemblyTracks) done.add(id);
3637
+ for (const [i, pair] of mainToMainPairs.entries()) {
3638
+ const la = laidTurnouts.get(pair.a.id);
3639
+ const lb = laidTurnouts.get(pair.b.id);
3640
+ if (!la || !lb) continue;
3641
+ const ja = jointAt(la.piece, la.divergeId);
3642
+ const jb = jointAt(lb.piece, lb.divergeId);
3643
+ if (!ja || !jb) continue;
3644
+ const dx = jb.x - ja.x;
3645
+ const dy = jb.y - ja.y;
3646
+ const len = Math.hypot(dx, dy);
3647
+ if (!(len > 1e-6)) {
3648
+ notLaid.push({
3649
+ id: pair.a.divergeTrack,
3650
+ why: `${pair.a.name || pair.a.id} and ${pair.b.name || pair.b.id} meet at the same point, so there is no track between them to lay`
3651
+ });
3652
+ continue;
3653
+ }
3654
+ pieces.push({
3655
+ id: `xc-${i}`,
3656
+ partId: flexId,
3657
+ x: ja.x,
3658
+ y: ja.y,
3659
+ rotationDeg: Math.atan2(dy, dx) * 180 / Math.PI,
3660
+ lengthInches: r3(len)
3661
+ });
3662
+ if ((crossingsBetween.get(pair.pairKey) ?? 1) > 1)
3663
+ warnings.push(
3664
+ `${pair.a.name || pair.a.id} and ${pair.b.name || pair.b.id} are two of four turnouts crossing between the same pair of mains \u2014 a double crossover. They are laid as separate turnouts with a ${len.toFixed(1)}\u2033 piece of track between them, which is what the document describes. If it is really a one-piece double crossover, name the product on a connector track and it will be laid as the single assembly it is.`
3665
+ );
3666
+ }
3604
3667
  let progressed = true;
3605
3668
  while (progressed) {
3606
3669
  progressed = false;