@willcgage/module-schematic 0.97.1 → 0.98.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
@@ -229,7 +229,7 @@ function moduleCenterline(input) {
229
229
  if (drawn) return samplePath(drawn);
230
230
  const chained = sectionedCenterline(input);
231
231
  if (chained.length >= 2) return chained;
232
- if (!input.geometryType) return [];
232
+ if (!input.geometryType && !input.hasPlacedTrack) return [];
233
233
  const L = input.lengthInches > 0 ? input.lengthInches : 24;
234
234
  const gt = input.geometryType;
235
235
  if (gt === "dead_end") return [{ x: 0, y: 0 }];
@@ -2553,6 +2553,83 @@ function fitFlexBetween(piece, others, library = BUILT_IN_TRACK_PARTS, withinInc
2553
2553
  lengthInches: Math.round(len * 1e3) / 1e3
2554
2554
  };
2555
2555
  }
2556
+ function insertIntoRun(pieces, fresh, at, library = BUILT_IN_TRACK_PARTS, withinInches = 1) {
2557
+ let host = null;
2558
+ let bestD = Infinity;
2559
+ for (const piece of pieces) {
2560
+ const part = library.find((x) => x.id === piece.partId);
2561
+ if (part?.kind !== "flex") continue;
2562
+ for (const { points } of pieceRoutePaths(piece, library))
2563
+ for (let i = 1; i < points.length; i++) {
2564
+ const d = distanceToSegment(at, points[i - 1], points[i]);
2565
+ if (d < bestD) {
2566
+ bestD = d;
2567
+ host = piece;
2568
+ }
2569
+ }
2570
+ }
2571
+ if (!host || bestD > withinInches) return null;
2572
+ const L = host.lengthInches ?? 0;
2573
+ const R = host.radiusInches;
2574
+ const steps = 200;
2575
+ let s = 0;
2576
+ let closest = Infinity;
2577
+ for (let i = 0; i <= steps; i++) {
2578
+ const t = L * i / steps;
2579
+ const local = flexRunEnd(t, R);
2580
+ const world = placeLocal(host, local.x, local.y);
2581
+ const d = Math.hypot(world.x - at.x, world.y - at.y);
2582
+ if (d < closest) {
2583
+ closest = d;
2584
+ s = t;
2585
+ }
2586
+ }
2587
+ const cutLocal = flexRunEnd(s, R);
2588
+ const cut = placeLocal(host, cutLocal.x, cutLocal.y);
2589
+ const heading = norm360(host.rotationDeg + (host.flipped ? -cutLocal.headingDeg : cutLocal.headingDeg));
2590
+ const placed = { ...fresh, x: cut.x, y: cut.y, rotationDeg: heading };
2591
+ const js = placedJoints([placed], library);
2592
+ const entry = js.find((j) => j.joint === "throat" || j.joint === "a");
2593
+ const exit = js.find((j) => j.joint === "through" || j.joint === "b");
2594
+ if (!entry || !exit) return null;
2595
+ const body = Math.hypot(exit.x - entry.x, exit.y - entry.y);
2596
+ const rest = L - s - body;
2597
+ if (s < 0 || rest < 0) return null;
2598
+ const ids = new Set(pieces.map((p) => p.id));
2599
+ let n = 1;
2600
+ while (ids.has(`${host.id}b${n}`)) n += 1;
2601
+ const tail = {
2602
+ ...host,
2603
+ id: `${host.id}b${n}`,
2604
+ x: exit.x,
2605
+ y: exit.y,
2606
+ rotationDeg: exit.headingDeg,
2607
+ lengthInches: rest
2608
+ };
2609
+ const out = pieces.map((p) => p.id === host.id ? { ...p, lengthInches: s } : p);
2610
+ const kept = out.filter((p) => p.id !== host.id || s > 0);
2611
+ return {
2612
+ pieces: [...kept, placed, ...rest > 0 ? [tail] : []],
2613
+ hostId: host.id,
2614
+ insertedId: placed.id
2615
+ };
2616
+ }
2617
+ function placeLocal(piece, x, y) {
2618
+ const rad = piece.rotationDeg * Math.PI / 180;
2619
+ const ly = piece.flipped ? -y : y;
2620
+ return {
2621
+ x: piece.x + x * Math.cos(rad) - ly * Math.sin(rad),
2622
+ y: piece.y + x * Math.sin(rad) + ly * Math.cos(rad)
2623
+ };
2624
+ }
2625
+ function distanceToSegment(p, a, b) {
2626
+ const vx = b.x - a.x;
2627
+ const vy = b.y - a.y;
2628
+ const len2 = vx * vx + vy * vy;
2629
+ if (len2 === 0) return Math.hypot(p.x - a.x, p.y - a.y);
2630
+ const t = Math.max(0, Math.min(1, ((p.x - a.x) * vx + (p.y - a.y) * vy) / len2));
2631
+ return Math.hypot(p.x - (a.x + t * vx), p.y - (a.y + t * vy));
2632
+ }
2556
2633
  var danglingDivergeWarning = (id) => `the route diverging at ${id} is not connected to anything`;
2557
2634
  var unreachedWarning = (id) => `${id} is not reachable from the endplate \u2014 nothing connects it`;
2558
2635
  function walkTrackGraph(graph, pieces, startAt, library = BUILT_IN_TRACK_PARTS) {
@@ -3630,6 +3707,7 @@ exports.graphToDoc = graphToDoc;
3630
3707
  exports.hasNoFarEndplate = hasNoFarEndplate;
3631
3708
  exports.importedPartToTrackPart = importedPartToTrackPart;
3632
3709
  exports.inchesToScaleFeet = inchesToScaleFeet;
3710
+ exports.insertIntoRun = insertIntoRun;
3633
3711
  exports.isLoopDoc = isLoopDoc;
3634
3712
  exports.isTransitionTurnout = isTransitionTurnout;
3635
3713
  exports.laneOffsetAt = laneOffsetAt;