@willcgage/module-schematic 0.90.0 → 0.92.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
@@ -2268,8 +2268,16 @@ function pieceRoutePaths(piece, library = BUILT_IN_TRACK_PARTS) {
2268
2268
  const b = at(route[1]);
2269
2269
  if (!a || !b) continue;
2270
2270
  const ends = [{ x: a.x, y: a.y }, { x: b.x, y: b.y }];
2271
+ if (part.kind === "flex") {
2272
+ const pts2 = flexRunPoints(piece.lengthInches ?? 0, piece.radiusInches).map(
2273
+ (q) => place(q.x, q.y)
2274
+ );
2275
+ pts2[pts2.length - 1] = { x: b.x, y: b.y };
2276
+ out.push({ route, points: pts2 });
2277
+ continue;
2278
+ }
2271
2279
  const diverging = route.some((r) => r === "diverge" || r === "legA" || r === "legB");
2272
- if (!diverging || part.kind === "flex") {
2280
+ if (!diverging) {
2273
2281
  out.push({ route, points: ends });
2274
2282
  continue;
2275
2283
  }
@@ -2308,6 +2316,25 @@ function partsPlaceable(library = BUILT_IN_TRACK_PARTS) {
2308
2316
  return { placeable, blocked };
2309
2317
  }
2310
2318
  var JOINT_SNAP_INCHES = 0.01;
2319
+ function flexRunEnd(lengthInches, radiusInches) {
2320
+ const L = lengthInches;
2321
+ const R = radiusInches;
2322
+ if (!R || !Number.isFinite(R) || Math.abs(R) < 1e-6)
2323
+ return { x: L, y: 0, headingDeg: 0 };
2324
+ const theta = L / R;
2325
+ return {
2326
+ x: Math.abs(R) * Math.sin(Math.abs(theta)) * Math.sign(L || 1),
2327
+ y: R * (1 - Math.cos(theta)),
2328
+ headingDeg: theta * 180 / Math.PI
2329
+ };
2330
+ }
2331
+ function flexRunPoints(lengthInches, radiusInches, steps = 16) {
2332
+ if (!radiusInches || !Number.isFinite(radiusInches) || Math.abs(radiusInches) < 1e-6)
2333
+ return [{ x: 0, y: 0 }, { x: lengthInches, y: 0 }];
2334
+ const out = [];
2335
+ for (let i = 0; i <= steps; i++) out.push(flexRunEnd(lengthInches * i / steps, radiusInches));
2336
+ return out.map((p) => ({ x: p.x, y: p.y }));
2337
+ }
2311
2338
  function placedJoints(pieces, library = BUILT_IN_TRACK_PARTS) {
2312
2339
  const out = [];
2313
2340
  const RAD = Math.PI / 180;
@@ -2318,10 +2345,13 @@ function placedJoints(pieces, library = BUILT_IN_TRACK_PARTS) {
2318
2345
  if (!geo) continue;
2319
2346
  const c = Math.cos(p.rotationDeg * RAD);
2320
2347
  const s = Math.sin(p.rotationDeg * RAD);
2348
+ const run = part.kind === "flex" ? flexRunEnd(p.lengthInches ?? 0, p.radiusInches) : null;
2321
2349
  for (const j of geo.joints) {
2322
- const lx = part.kind === "flex" && j.id === "b" ? p.lengthInches ?? 0 : j.x;
2323
- const ly = p.flipped ? -j.y : j.y;
2324
- const h = (p.flipped ? -j.angleDeg : j.angleDeg) + p.rotationDeg;
2350
+ const far = run && j.id === "b";
2351
+ const lx = far ? run.x : j.x;
2352
+ const ly = p.flipped ? -(far ? run.y : j.y) : far ? run.y : j.y;
2353
+ const local = far ? run.headingDeg : j.angleDeg;
2354
+ const h = (p.flipped ? -local : local) + p.rotationDeg;
2325
2355
  out.push({
2326
2356
  key: `${p.id}.${j.id}`,
2327
2357
  piece: p.id,
@@ -2410,6 +2440,7 @@ function snapPiece(moving, others, library = BUILT_IN_TRACK_PARTS, withinInches
2410
2440
  to: best.t.key
2411
2441
  };
2412
2442
  }
2443
+ var danglingDivergeWarning = (id) => `the route diverging at ${id} is not connected to anything`;
2413
2444
  function walkTrackGraph(graph, pieces, startAt, library = BUILT_IN_TRACK_PARTS) {
2414
2445
  const byKey = new Map(graph.joints.map((j) => [j.key, j]));
2415
2446
  const pieceById = new Map(pieces.map((p) => [p.id, p]));
@@ -2426,10 +2457,19 @@ function walkTrackGraph(graph, pieces, startAt, library = BUILT_IN_TRACK_PARTS)
2426
2457
  const part = partOf(pid);
2427
2458
  return part ? partGeometry(part, library) : null;
2428
2459
  };
2429
- const gap = (a, b) => a && b ? Math.hypot(a.x - b.x, a.y - b.y) : 0;
2460
+ const gap = (a, b) => {
2461
+ if (!a || !b) return 0;
2462
+ if (a.piece === b.piece) {
2463
+ const piece = pieceById.get(a.piece);
2464
+ const part = piece ? partOf(a.piece) : void 0;
2465
+ if (piece && part?.kind === "flex") return piece.lengthInches ?? 0;
2466
+ }
2467
+ return Math.hypot(a.x - b.x, a.y - b.y);
2468
+ };
2430
2469
  const routes = [];
2431
2470
  const turnouts = [];
2432
2471
  const warnings = [];
2472
+ const danglingDiverges = [];
2433
2473
  const queued = /* @__PURE__ */ new Set();
2434
2474
  const pending = [];
2435
2475
  const walk = (startKey, startPos, id, bornAt) => {
@@ -2520,7 +2560,8 @@ function walkTrackGraph(graph, pieces, startAt, library = BUILT_IN_TRACK_PARTS)
2520
2560
  const b = pending.shift();
2521
2561
  const start = link.get(b.joint);
2522
2562
  if (!start) {
2523
- warnings.push(`the route diverging at ${b.from} is not connected to anything`);
2563
+ warnings.push(danglingDivergeWarning(b.from));
2564
+ danglingDiverges.push(b.from);
2524
2565
  continue;
2525
2566
  }
2526
2567
  const already = routes.find((r2) => r2.pieces.includes(byKey.get(start).piece));
@@ -2541,7 +2582,7 @@ function walkTrackGraph(graph, pieces, startAt, library = BUILT_IN_TRACK_PARTS)
2541
2582
  if (!reached.has(p.id) && !graph.unplaceable.some((u) => u.piece === p.id))
2542
2583
  warnings.push(`${p.id} is not reachable from the endplate \u2014 nothing connects it`);
2543
2584
  for (const c of graph.conflicts) warnings.push(c.reason);
2544
- return { routes, turnouts, warnings };
2585
+ return { routes, turnouts, warnings, danglingDiverges };
2545
2586
  }
2546
2587
  function resolveGraphAnchor(anchor, walk, pieces, library = BUILT_IN_TRACK_PARTS) {
2547
2588
  const piece = pieces.find((p) => p.id === anchor.piece);
@@ -2562,7 +2603,8 @@ function graphToDoc(pieces, input) {
2562
2603
  const library = input.library ?? BUILT_IN_TRACK_PARTS;
2563
2604
  const graph = buildTrackGraph(pieces, library);
2564
2605
  const walk = walkTrackGraph(graph, pieces, input.startAt, library);
2565
- const warnings = [...walk.warnings];
2606
+ const dangling = new Set(walk.danglingDiverges.map(danglingDivergeWarning));
2607
+ const warnings = walk.warnings.filter((w) => !dangling.has(w));
2566
2608
  const round = (n) => Math.round(n * 100) / 100;
2567
2609
  const base = input.base ?? {};
2568
2610
  const endplates = base.endplates && base.endplates.length ? base.endplates : [{ id: "A", label: "West" }, { id: "B", label: "East" }];
@@ -3374,6 +3416,7 @@ exports.endplateWidthInches = endplateWidthInches;
3374
3416
  exports.flexPartFor = flexPartFor;
3375
3417
  exports.flexParts = flexParts;
3376
3418
  exports.flexPieces = flexPieces;
3419
+ exports.flexRunEnd = flexRunEnd;
3377
3420
  exports.flexUsage = flexUsage;
3378
3421
  exports.frogCasting = frogCasting;
3379
3422
  exports.frogNumberFromName = frogNumberFromName;