@willcgage/module-schematic 0.16.0 → 0.17.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
@@ -65,6 +65,78 @@ function arcSweep(a0, a1, am) {
65
65
  const one = norm(a1);
66
66
  return m <= one ? one : one - 2 * Math.PI;
67
67
  }
68
+ var DEG_FP = Math.PI / 180;
69
+ function moduleCenterline(input) {
70
+ const L = input.lengthInches > 0 ? input.lengthInches : 24;
71
+ const gt = input.geometryType;
72
+ if (gt === "dead_end") return [{ x: 0, y: 0 }];
73
+ if (gt === "offset") return [{ x: 0, y: 0 }, { x: L, y: input.geometryOffsetInches ?? 0 }];
74
+ const turn = gt === "corner_45" ? 45 : gt === "corner_90" ? 90 : gt === "curve" ? input.geometryDegrees ?? 0 : 0;
75
+ if (turn === 0) return [{ x: 0, y: 0 }, { x: L, y: 0 }];
76
+ const t = turn * DEG_FP;
77
+ const r = L / t;
78
+ const steps = 12;
79
+ const pts = [];
80
+ for (let i = 0; i <= steps; i++) {
81
+ const a = t * i / steps;
82
+ pts.push({ x: r * Math.sin(a), y: r * (1 - Math.cos(a)) });
83
+ }
84
+ return pts;
85
+ }
86
+ function centerlineNormals(center) {
87
+ return center.map((_, i) => {
88
+ const a = center[Math.max(0, i - 1)];
89
+ const b = center[Math.min(center.length - 1, i + 1)];
90
+ let dx = b.x - a.x;
91
+ let dy = b.y - a.y;
92
+ const len = Math.hypot(dx, dy) || 1;
93
+ dx /= len;
94
+ dy /= len;
95
+ return { x: -dy, y: dx };
96
+ });
97
+ }
98
+ function centerlineFractions(center) {
99
+ const cum = [0];
100
+ for (let i = 1; i < center.length; i++)
101
+ cum.push(cum[i - 1] + Math.hypot(center[i].x - center[i - 1].x, center[i].y - center[i - 1].y));
102
+ const total = cum[cum.length - 1] || 1;
103
+ return cum.map((d) => d / total);
104
+ }
105
+ function benchworkBand(center, widthA = FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES, widthB = FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES) {
106
+ if (center.length < 2) return [];
107
+ const n = centerlineNormals(center);
108
+ const f = centerlineFractions(center);
109
+ const half = (i) => (widthA * (1 - f[i]) + widthB * f[i]) / 2;
110
+ const left = center.map((p, i) => ({ x: p.x + n[i].x * half(i), y: p.y + n[i].y * half(i) }));
111
+ const right = center.map((p, i) => ({ x: p.x - n[i].x * half(i), y: p.y - n[i].y * half(i) }));
112
+ return [...left, ...right.reverse()];
113
+ }
114
+ function endplateFaceSegments(center, widthA = FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES, widthB = FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES) {
115
+ if (center.length < 2) return [];
116
+ const n = centerlineNormals(center);
117
+ const face = (i, w) => ({
118
+ p1: { x: center[i].x + n[i].x * (w / 2), y: center[i].y + n[i].y * (w / 2) },
119
+ p2: { x: center[i].x - n[i].x * (w / 2), y: center[i].y - n[i].y * (w / 2) },
120
+ mid: { x: center[i].x, y: center[i].y }
121
+ });
122
+ return [face(0, widthA), face(center.length - 1, widthB)];
123
+ }
124
+ function moduleFootprint(input) {
125
+ const centerline = moduleCenterline(input);
126
+ const widthA = endplateWidthFor(input.endplateWidths, "A");
127
+ const widthB = endplateWidthFor(input.endplateWidths, "B");
128
+ const authored = benchworkOutline(input);
129
+ return {
130
+ centerline,
131
+ band: benchworkBand(centerline, widthA, widthB),
132
+ endplateFaces: endplateFaceSegments(centerline, widthA, widthB),
133
+ outline: authored ? sampleBenchworkOutline(authored) : null
134
+ };
135
+ }
136
+ function endplateWidthFor(widths, id) {
137
+ const w = widths?.[id];
138
+ return typeof w === "number" && w > 0 ? w : FREEMO_ENDPLATE_WIDTH_RECOMMENDED_INCHES;
139
+ }
68
140
  function isLoopDoc(doc) {
69
141
  return doc.loop === true || doc.endplates.length === 1;
70
142
  }
@@ -828,6 +900,7 @@ exports.MAIN2_TRACK_ID = MAIN2_TRACK_ID;
828
900
  exports.MAIN_TRACK_ID = MAIN_TRACK_ID;
829
901
  exports.N_SCALE_RATIO = N_SCALE_RATIO;
830
902
  exports.asModuleSchematic = asModuleSchematic;
903
+ exports.benchworkBand = benchworkBand;
831
904
  exports.benchworkOutline = benchworkOutline;
832
905
  exports.buildCrossover = buildCrossover;
833
906
  exports.buildPassingSiding = buildPassingSiding;
@@ -836,12 +909,15 @@ exports.deriveEndplatePoses = deriveEndplatePoses;
836
909
  exports.divergeSideForHand = divergeSideForHand;
837
910
  exports.docToState = docToState;
838
911
  exports.emptyEditorState = emptyEditorState;
912
+ exports.endplateFaceSegments = endplateFaceSegments;
839
913
  exports.endplateWidthInches = endplateWidthInches;
840
914
  exports.geometryTurnDegrees = geometryTurnDegrees;
841
915
  exports.inchesToScaleFeet = inchesToScaleFeet;
842
916
  exports.isLoopDoc = isLoopDoc;
843
917
  exports.isTransitionTurnout = isTransitionTurnout;
918
+ exports.moduleCenterline = moduleCenterline;
844
919
  exports.moduleFeatures = moduleFeatures;
920
+ exports.moduleFootprint = moduleFootprint;
845
921
  exports.nextId = nextId;
846
922
  exports.poseNeedsManual = poseNeedsManual;
847
923
  exports.poseOverridesFromDoc = poseOverridesFromDoc;