@willcgage/module-schematic 0.96.0 → 0.97.1

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
@@ -2019,14 +2019,14 @@ function storedPartToTrackPart(row) {
2019
2019
  source: points.source === "measured" && frog.source === "measured" ? "measured" : "derived",
2020
2020
  ...note ? { note } : {}
2021
2021
  } : dim(row.leadInches, row.leadSource);
2022
- const kind = row.kind;
2022
+ const kind = TRACK_PART_KINDS.includes(row.kind) ? row.kind : "turnout";
2023
2023
  const part = {
2024
2024
  id: row.slug,
2025
2025
  manufacturer: row.manufacturer,
2026
2026
  line: row.line,
2027
2027
  scale: "N",
2028
2028
  name: row.name,
2029
- kind: kind === "wye" || kind === "curved-turnout" || kind === "crossing" ? kind : "turnout"
2029
+ kind
2030
2030
  };
2031
2031
  const numbers = {
2032
2032
  ...row.partNumberLeft ? { left: row.partNumberLeft } : {},
@@ -2061,6 +2061,9 @@ function storedPartToTrackPart(row) {
2061
2061
  const inner = dim(row.innerRadiusInches, row.radiusSource);
2062
2062
  if (outer) part.outerRadius = outer;
2063
2063
  if (inner) part.innerRadius = inner;
2064
+ const radius = dim(row.radiusInches, row.radiusSource);
2065
+ if (radius) part.radius = radius;
2066
+ if (typeof row.arcDegrees === "number") part.arcDegrees = row.arcDegrees;
2064
2067
  if (typeof row.actualAngleDeg === "number")
2065
2068
  part.actualAngle = {
2066
2069
  deg: row.actualAngleDeg,
@@ -2174,6 +2177,13 @@ function turnoutClosure(size, opts = {}) {
2174
2177
  function partGeometryGap(part) {
2175
2178
  if (part.kind === "flex") return null;
2176
2179
  if (part.kind === "bumper") return null;
2180
+ if (part.kind === "straight")
2181
+ return part.overallLength ? null : "no overall length \u2014 a sectional straight IS its length";
2182
+ if (part.kind === "curve") {
2183
+ if (!part.radius) return "no radius \u2014 a sectional curve is a radius and an arc";
2184
+ if (part.arcDegrees == null) return "no arc \u2014 the radius alone does not say how far it turns";
2185
+ return null;
2186
+ }
2177
2187
  if (part.kind === "crossover")
2178
2188
  return "a crossover fixture builds one HALF of the assembly (piecesPerAssembly), so its published lengths describe a piece, not the finished part \u2014 the geometry of the whole crossover is not yet derivable from them";
2179
2189
  if (part.kind === "crossing") return "crossing geometry is not modelled yet";
@@ -2187,6 +2197,18 @@ function partGeometryGap(part) {
2187
2197
  }
2188
2198
  function partGeometry(part, library = BUILT_IN_TRACK_PARTS) {
2189
2199
  if (partGeometryGap(part)) return null;
2200
+ if (part.kind === "straight" || part.kind === "curve") {
2201
+ const end = part.kind === "curve" ? flexRunEnd(sectionalArcInches(part), part.radius.inches) : { x: part.overallLength.inches, y: 0, headingDeg: 0 };
2202
+ return {
2203
+ joints: [
2204
+ { id: "a", role: "throat", x: 0, y: 0, angleDeg: 180 },
2205
+ { id: "b", role: "through", x: end.x, y: end.y, angleDeg: end.headingDeg }
2206
+ ],
2207
+ routes: [["a", "b"]],
2208
+ source: (part.kind === "curve" ? part.radius : part.overallLength)?.source ?? "unverified",
2209
+ divergingEndMeasured: false
2210
+ };
2211
+ }
2190
2212
  if (part.kind === "bumper") {
2191
2213
  return {
2192
2214
  joints: [{ id: "a", role: "throat", x: 0, y: 0, angleDeg: 180 }],
@@ -2291,6 +2313,16 @@ function pieceRoutePaths(piece, library = BUILT_IN_TRACK_PARTS) {
2291
2313
  const len = part.overallLength?.inches ?? BUMPER_DRAWN_INCHES;
2292
2314
  return [{ route: ["a", "a"], points: [{ x: at2.x, y: at2.y }, place(len, 0)] }];
2293
2315
  }
2316
+ if (part.kind === "curve") {
2317
+ const a = joints.find((j) => j.joint === "a");
2318
+ const b = joints.find((j) => j.joint === "b");
2319
+ if (!a || !b) return out;
2320
+ const pts = flexRunPoints(sectionalArcInches(part), part.radius.inches).map(
2321
+ (q) => place(q.x, q.y)
2322
+ );
2323
+ pts[pts.length - 1] = { x: b.x, y: b.y };
2324
+ return [{ route: ["a", "b"], points: pts }];
2325
+ }
2294
2326
  for (const route of geo.routes) {
2295
2327
  const a = at(route[0]);
2296
2328
  const b = at(route[1]);
@@ -2354,7 +2386,22 @@ function partsPlaceable(library = BUILT_IN_TRACK_PARTS) {
2354
2386
  return { placeable, blocked };
2355
2387
  }
2356
2388
  var JOINT_SNAP_INCHES = 0.01;
2389
+ var TRACK_PART_KINDS = [
2390
+ "turnout",
2391
+ "wye",
2392
+ "curved-turnout",
2393
+ "crossover",
2394
+ "crossing",
2395
+ "flex",
2396
+ "straight",
2397
+ "curve",
2398
+ "bumper"
2399
+ ];
2357
2400
  var BUMPER_DRAWN_INCHES = 0.75;
2401
+ function sectionalArcInches(part) {
2402
+ if (part.kind !== "curve" || !part.radius || part.arcDegrees == null) return 0;
2403
+ return part.radius.inches * Math.abs(part.arcDegrees) * Math.PI / 180;
2404
+ }
2358
2405
  function flexRunEnd(lengthInches, radiusInches) {
2359
2406
  const L = lengthInches;
2360
2407
  const R = radiusInches;
@@ -2530,6 +2577,7 @@ function walkTrackGraph(graph, pieces, startAt, library = BUILT_IN_TRACK_PARTS)
2530
2577
  const piece = pieceById.get(a.piece);
2531
2578
  const part = piece ? partOf(a.piece) : void 0;
2532
2579
  if (piece && part?.kind === "flex") return piece.lengthInches ?? 0;
2580
+ if (part?.kind === "curve") return sectionalArcInches(part);
2533
2581
  }
2534
2582
  return Math.hypot(a.x - b.x, a.y - b.y);
2535
2583
  };
@@ -3541,6 +3589,7 @@ exports.N_SCALE_RATIO = N_SCALE_RATIO;
3541
3589
  exports.PINCH_EASE_INCHES = PINCH_EASE_INCHES;
3542
3590
  exports.RAIL_GAUGE_INCHES = RAIL_GAUGE_INCHES;
3543
3591
  exports.TIE_HALF_LENGTH_INCHES = TIE_HALF_LENGTH_INCHES;
3592
+ exports.TRACK_PART_KINDS = TRACK_PART_KINDS;
3544
3593
  exports.TURNOUT_LEAD_INCHES_PER_FROG = TURNOUT_LEAD_INCHES_PER_FROG;
3545
3594
  exports.WHOLE_MODULE_SECTION_ID = WHOLE_MODULE_SECTION_ID;
3546
3595
  exports.asModuleSchematic = asModuleSchematic;
@@ -3625,6 +3674,7 @@ exports.sectionFootprints = sectionFootprints;
3625
3674
  exports.sectionNeighbours = sectionNeighbours;
3626
3675
  exports.sectionSpans = sectionSpans;
3627
3676
  exports.sectionSpansOrWhole = sectionSpansOrWhole;
3677
+ exports.sectionalArcInches = sectionalArcInches;
3628
3678
  exports.sectionedCenterline = sectionedCenterline;
3629
3679
  exports.sectionedEndPose = sectionedEndPose;
3630
3680
  exports.sliceCenterline = sliceCenterline;