@horizon36596/zenith-core 0.1.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.
Files changed (244) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE +18 -0
  3. package/README.md +16 -0
  4. package/dist/canonicalize.d.ts +25 -0
  5. package/dist/canonicalize.d.ts.map +1 -0
  6. package/dist/canonicalize.js +199 -0
  7. package/dist/canonicalize.js.map +1 -0
  8. package/dist/check.d.ts +26 -0
  9. package/dist/check.d.ts.map +1 -0
  10. package/dist/check.js +231 -0
  11. package/dist/check.js.map +1 -0
  12. package/dist/checks/heading.d.ts +17 -0
  13. package/dist/checks/heading.d.ts.map +1 -0
  14. package/dist/checks/heading.js +78 -0
  15. package/dist/checks/heading.js.map +1 -0
  16. package/dist/checks/intake.d.ts +28 -0
  17. package/dist/checks/intake.d.ts.map +1 -0
  18. package/dist/checks/intake.js +131 -0
  19. package/dist/checks/intake.js.map +1 -0
  20. package/dist/checks/motion.d.ts +28 -0
  21. package/dist/checks/motion.d.ts.map +1 -0
  22. package/dist/checks/motion.js +252 -0
  23. package/dist/checks/motion.js.map +1 -0
  24. package/dist/checks/schedule.d.ts +29 -0
  25. package/dist/checks/schedule.d.ts.map +1 -0
  26. package/dist/checks/schedule.js +166 -0
  27. package/dist/checks/schedule.js.map +1 -0
  28. package/dist/checks/season.d.ts +6 -0
  29. package/dist/checks/season.d.ts.map +1 -0
  30. package/dist/checks/season.js +126 -0
  31. package/dist/checks/season.js.map +1 -0
  32. package/dist/continuity.d.ts +38 -0
  33. package/dist/continuity.d.ts.map +1 -0
  34. package/dist/continuity.js +69 -0
  35. package/dist/continuity.js.map +1 -0
  36. package/dist/diff.d.ts +37 -0
  37. package/dist/diff.d.ts.map +1 -0
  38. package/dist/diff.js +258 -0
  39. package/dist/diff.js.map +1 -0
  40. package/dist/edit/command.d.ts +4 -0
  41. package/dist/edit/command.d.ts.map +1 -0
  42. package/dist/edit/command.js +16 -0
  43. package/dist/edit/command.js.map +1 -0
  44. package/dist/edit/errors.d.ts +9 -0
  45. package/dist/edit/errors.d.ts.map +1 -0
  46. package/dist/edit/errors.js +12 -0
  47. package/dist/edit/errors.js.map +1 -0
  48. package/dist/edit/fields.d.ts +40 -0
  49. package/dist/edit/fields.d.ts.map +1 -0
  50. package/dist/edit/fields.js +150 -0
  51. package/dist/edit/fields.js.map +1 -0
  52. package/dist/edit/finish.d.ts +15 -0
  53. package/dist/edit/finish.d.ts.map +1 -0
  54. package/dist/edit/finish.js +36 -0
  55. package/dist/edit/finish.js.map +1 -0
  56. package/dist/edit/groups.d.ts +66 -0
  57. package/dist/edit/groups.d.ts.map +1 -0
  58. package/dist/edit/groups.js +277 -0
  59. package/dist/edit/groups.js.map +1 -0
  60. package/dist/edit/ids.d.ts +39 -0
  61. package/dist/edit/ids.d.ts.map +1 -0
  62. package/dist/edit/ids.js +76 -0
  63. package/dist/edit/ids.js.map +1 -0
  64. package/dist/edit/index.d.ts +22 -0
  65. package/dist/edit/index.d.ts.map +1 -0
  66. package/dist/edit/index.js +22 -0
  67. package/dist/edit/index.js.map +1 -0
  68. package/dist/edit/meta.d.ts +15 -0
  69. package/dist/edit/meta.d.ts.map +1 -0
  70. package/dist/edit/meta.js +13 -0
  71. package/dist/edit/meta.js.map +1 -0
  72. package/dist/edit/path.d.ts +36 -0
  73. package/dist/edit/path.d.ts.map +1 -0
  74. package/dist/edit/path.js +135 -0
  75. package/dist/edit/path.js.map +1 -0
  76. package/dist/edit/steps.d.ts +53 -0
  77. package/dist/edit/steps.d.ts.map +1 -0
  78. package/dist/edit/steps.js +0 -0
  79. package/dist/edit/steps.js.map +1 -0
  80. package/dist/edit/template.d.ts +18 -0
  81. package/dist/edit/template.d.ts.map +1 -0
  82. package/dist/edit/template.js +30 -0
  83. package/dist/edit/template.js.map +1 -0
  84. package/dist/edit/timing.d.ts +11 -0
  85. package/dist/edit/timing.d.ts.map +1 -0
  86. package/dist/edit/timing.js +23 -0
  87. package/dist/edit/timing.js.map +1 -0
  88. package/dist/edit/tree.d.ts +32 -0
  89. package/dist/edit/tree.d.ts.map +1 -0
  90. package/dist/edit/tree.js +90 -0
  91. package/dist/edit/tree.js.map +1 -0
  92. package/dist/estimate.d.ts +48 -0
  93. package/dist/estimate.d.ts.map +1 -0
  94. package/dist/estimate.js +443 -0
  95. package/dist/estimate.js.map +1 -0
  96. package/dist/findingHelp.d.ts +18 -0
  97. package/dist/findingHelp.d.ts.map +1 -0
  98. package/dist/findingHelp.js +114 -0
  99. package/dist/findingHelp.js.map +1 -0
  100. package/dist/fix.d.ts +21 -0
  101. package/dist/fix.d.ts.map +1 -0
  102. package/dist/fix.js +180 -0
  103. package/dist/fix.js.map +1 -0
  104. package/dist/footprint.d.ts +33 -0
  105. package/dist/footprint.d.ts.map +1 -0
  106. package/dist/footprint.js +71 -0
  107. package/dist/footprint.js.map +1 -0
  108. package/dist/geometry/angle.d.ts +13 -0
  109. package/dist/geometry/angle.d.ts.map +1 -0
  110. package/dist/geometry/angle.js +21 -0
  111. package/dist/geometry/angle.js.map +1 -0
  112. package/dist/geometry/curve.d.ts +24 -0
  113. package/dist/geometry/curve.d.ts.map +1 -0
  114. package/dist/geometry/curve.js +78 -0
  115. package/dist/geometry/curve.js.map +1 -0
  116. package/dist/geometry/path.d.ts +29 -0
  117. package/dist/geometry/path.d.ts.map +1 -0
  118. package/dist/geometry/path.js +61 -0
  119. package/dist/geometry/path.js.map +1 -0
  120. package/dist/geometry/quadrature.d.ts +13 -0
  121. package/dist/geometry/quadrature.d.ts.map +1 -0
  122. package/dist/geometry/quadrature.js +57 -0
  123. package/dist/geometry/quadrature.js.map +1 -0
  124. package/dist/geometry/sat.d.ts +40 -0
  125. package/dist/geometry/sat.d.ts.map +1 -0
  126. package/dist/geometry/sat.js +86 -0
  127. package/dist/geometry/sat.js.map +1 -0
  128. package/dist/geometry/vec.d.ts +27 -0
  129. package/dist/geometry/vec.d.ts.map +1 -0
  130. package/dist/geometry/vec.js +30 -0
  131. package/dist/geometry/vec.js.map +1 -0
  132. package/dist/heading.d.ts +77 -0
  133. package/dist/heading.d.ts.map +1 -0
  134. package/dist/heading.js +164 -0
  135. package/dist/heading.js.map +1 -0
  136. package/dist/headingRanges.d.ts +48 -0
  137. package/dist/headingRanges.d.ts.map +1 -0
  138. package/dist/headingRanges.js +132 -0
  139. package/dist/headingRanges.js.map +1 -0
  140. package/dist/index.d.ts +41 -0
  141. package/dist/index.d.ts.map +1 -0
  142. package/dist/index.js +41 -0
  143. package/dist/index.js.map +1 -0
  144. package/dist/kinematics.d.ts +63 -0
  145. package/dist/kinematics.d.ts.map +1 -0
  146. package/dist/kinematics.js +71 -0
  147. package/dist/kinematics.js.map +1 -0
  148. package/dist/ledger.d.ts +49 -0
  149. package/dist/ledger.d.ts.map +1 -0
  150. package/dist/ledger.js +136 -0
  151. package/dist/ledger.js.map +1 -0
  152. package/dist/load.d.ts +12 -0
  153. package/dist/load.d.ts.map +1 -0
  154. package/dist/load.js +12 -0
  155. package/dist/load.js.map +1 -0
  156. package/dist/mirror.d.ts +81 -0
  157. package/dist/mirror.d.ts.map +1 -0
  158. package/dist/mirror.js +282 -0
  159. package/dist/mirror.js.map +1 -0
  160. package/dist/plan.d.ts +29 -0
  161. package/dist/plan.d.ts.map +1 -0
  162. package/dist/plan.js +159 -0
  163. package/dist/plan.js.map +1 -0
  164. package/dist/prBody.d.ts +24 -0
  165. package/dist/prBody.d.ts.map +1 -0
  166. package/dist/prBody.js +112 -0
  167. package/dist/prBody.js.map +1 -0
  168. package/dist/provenance.d.ts +21 -0
  169. package/dist/provenance.d.ts.map +1 -0
  170. package/dist/provenance.js +46 -0
  171. package/dist/provenance.js.map +1 -0
  172. package/dist/render.d.ts +18 -0
  173. package/dist/render.d.ts.map +1 -0
  174. package/dist/render.js +464 -0
  175. package/dist/render.js.map +1 -0
  176. package/dist/resolve.d.ts +39 -0
  177. package/dist/resolve.d.ts.map +1 -0
  178. package/dist/resolve.js +172 -0
  179. package/dist/resolve.js.map +1 -0
  180. package/dist/season.d.ts +65 -0
  181. package/dist/season.d.ts.map +1 -0
  182. package/dist/season.js +16 -0
  183. package/dist/season.js.map +1 -0
  184. package/dist/sim/commands.d.ts +97 -0
  185. package/dist/sim/commands.d.ts.map +1 -0
  186. package/dist/sim/commands.js +185 -0
  187. package/dist/sim/commands.js.map +1 -0
  188. package/dist/sim/curves.d.ts +37 -0
  189. package/dist/sim/curves.d.ts.map +1 -0
  190. package/dist/sim/curves.js +187 -0
  191. package/dist/sim/curves.js.map +1 -0
  192. package/dist/sim/follower.d.ts +124 -0
  193. package/dist/sim/follower.d.ts.map +1 -0
  194. package/dist/sim/follower.js +472 -0
  195. package/dist/sim/follower.js.map +1 -0
  196. package/dist/sim/ideal.d.ts +103 -0
  197. package/dist/sim/ideal.d.ts.map +1 -0
  198. package/dist/sim/ideal.js +393 -0
  199. package/dist/sim/ideal.js.map +1 -0
  200. package/dist/sim/index.d.ts +11 -0
  201. package/dist/sim/index.d.ts.map +1 -0
  202. package/dist/sim/index.js +10 -0
  203. package/dist/sim/index.js.map +1 -0
  204. package/dist/sim/params.d.ts +91 -0
  205. package/dist/sim/params.d.ts.map +1 -0
  206. package/dist/sim/params.js +195 -0
  207. package/dist/sim/params.js.map +1 -0
  208. package/dist/sim/pedroPath.d.ts +41 -0
  209. package/dist/sim/pedroPath.d.ts.map +1 -0
  210. package/dist/sim/pedroPath.js +191 -0
  211. package/dist/sim/pedroPath.js.map +1 -0
  212. package/dist/sim/plant.d.ts +59 -0
  213. package/dist/sim/plant.d.ts.map +1 -0
  214. package/dist/sim/plant.js +128 -0
  215. package/dist/sim/plant.js.map +1 -0
  216. package/dist/sim/residuals.d.ts +36 -0
  217. package/dist/sim/residuals.d.ts.map +1 -0
  218. package/dist/sim/residuals.js +86 -0
  219. package/dist/sim/residuals.js.map +1 -0
  220. package/dist/sim/simulate.d.ts +27 -0
  221. package/dist/sim/simulate.d.ts.map +1 -0
  222. package/dist/sim/simulate.js +924 -0
  223. package/dist/sim/simulate.js.map +1 -0
  224. package/dist/sim/types.d.ts +209 -0
  225. package/dist/sim/types.d.ts.map +1 -0
  226. package/dist/sim/types.js +2 -0
  227. package/dist/sim/types.js.map +1 -0
  228. package/dist/tokens.d.ts +93 -0
  229. package/dist/tokens.d.ts.map +1 -0
  230. package/dist/tokens.js +93 -0
  231. package/dist/tokens.js.map +1 -0
  232. package/dist/trace.d.ts +89 -0
  233. package/dist/trace.d.ts.map +1 -0
  234. package/dist/trace.js +187 -0
  235. package/dist/trace.js.map +1 -0
  236. package/dist/types.d.ts +219 -0
  237. package/dist/types.d.ts.map +1 -0
  238. package/dist/types.js +2 -0
  239. package/dist/types.js.map +1 -0
  240. package/dist/unimplemented.d.ts +8 -0
  241. package/dist/unimplemented.d.ts.map +1 -0
  242. package/dist/unimplemented.js +11 -0
  243. package/dist/unimplemented.js.map +1 -0
  244. package/package.json +59 -0
@@ -0,0 +1,443 @@
1
+ import { evaluateEstimateExpression, parseEstimateExpression, UNKNOWN_ESTIMATE, } from "@horizon36596/zenith-schema";
2
+ import { wrapAngle } from "./geometry/angle.js";
3
+ import { decelInPerS2, lateralAccelLimitInPerS2, robotLimits, velocityCapInPerS, } from "./kinematics.js";
4
+ import { containsPath } from "./resolve.js";
5
+ /**
6
+ * The estimate's time model.
7
+ *
8
+ * Pedro v3 is a geometric follower with no trajectory, so there is no profile to read: this is a
9
+ * kinematic model that states its assumptions (`estimate().explain`) and is calibrated against
10
+ * recorded runs in `07`. Pure and deterministic; the same files always give the same seconds.
11
+ */
12
+ /** Speeds below this are treated as a standstill, in in/s. */
13
+ const STOPPED_IN_PER_S = 1e-6;
14
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
15
+ /** A finite difference over a sampled series that copes with the two ends and with repeats. */
16
+ function slope(values, positions, index) {
17
+ const previous = index === 0 ? index : index - 1;
18
+ const next = index === values.length - 1 ? index : index + 1;
19
+ const ds = positions[next] - positions[previous];
20
+ if (Math.abs(ds) < 1e-9)
21
+ return 0;
22
+ return wrapAngle(values[next] - values[previous]) / ds;
23
+ }
24
+ /**
25
+ * Builds the per-sample caps of `03` section 3 steps 1, 4 and 5: the direction-dependent velocity
26
+ * ellipse, the heading-rate limit and the curvature cap.
27
+ *
28
+ * `headingAt` lets the caller ask what the same geometry would cost driven nose-first, which is how
29
+ * the strafe cost in seconds is worked out.
30
+ */
31
+ function profileSamples(step, geometry, limits, speedFraction, headings) {
32
+ const positions = step.samples.map((sample) => sample.sIn);
33
+ const travel = step.samples.map((sample) => {
34
+ const tangent = geometry.tangentAtDistance(sample.sIn);
35
+ return Math.atan2(tangent.yIn, tangent.xIn);
36
+ });
37
+ return step.samples.map((sample, index) => {
38
+ const phiRad = wrapAngle(travel[index] - headings[index]);
39
+ let cap = speedFraction * velocityCapInPerS(limits, phiRad);
40
+ const headingRatePerIn = Math.abs(slope(headings, positions, index));
41
+ if (headingRatePerIn > 1e-9) {
42
+ cap = Math.min(cap, limits.maxAngularVelRadPerS / headingRatePerIn);
43
+ }
44
+ const curvaturePerIn = Math.abs(slope(travel, positions, index));
45
+ if (curvaturePerIn > 1e-9) {
46
+ cap = Math.min(cap, Math.sqrt(lateralAccelLimitInPerS2(limits) / curvaturePerIn));
47
+ }
48
+ return { sIn: sample.sIn, phiRad, capInPerS: cap, decelInPerS2: decelInPerS2(limits, phiRad) };
49
+ });
50
+ }
51
+ /**
52
+ * The two-pass profile of `03` section 3 step 6: take the minimum of the caps, raise it forwards
53
+ * under `accel` from the entry velocity, lower it backwards under the direction's deceleration into
54
+ * the exit velocity, then integrate `ds / v`.
55
+ */
56
+ export function integrateProfile(samples, limits, entryVelocityInPerS, exitVelocityInPerS) {
57
+ const count = samples.length;
58
+ if (count === 0) {
59
+ return { seconds: 0, strafeFraction: 0, exitVelocityInPerS: 0, timeAtSampleS: [] };
60
+ }
61
+ if (count === 1) {
62
+ return {
63
+ seconds: 0,
64
+ strafeFraction: 0,
65
+ exitVelocityInPerS: entryVelocityInPerS,
66
+ timeAtSampleS: [0],
67
+ };
68
+ }
69
+ const velocities = samples.map((sample) => sample.capInPerS);
70
+ velocities[0] = Math.min(velocities[0], entryVelocityInPerS);
71
+ for (let i = 1; i < count; i += 1) {
72
+ const ds = samples[i].sIn - samples[i - 1].sIn;
73
+ const reachable = Math.sqrt(velocities[i - 1] ** 2 + 2 * limits.accelInPerS2 * Math.max(ds, 0));
74
+ velocities[i] = Math.min(velocities[i], reachable);
75
+ }
76
+ velocities[count - 1] = Math.min(velocities[count - 1], exitVelocityInPerS);
77
+ for (let i = count - 2; i >= 0; i -= 1) {
78
+ const ds = samples[i + 1].sIn - samples[i].sIn;
79
+ const stoppable = Math.sqrt(velocities[i + 1] ** 2 +
80
+ 2 * samples[i].decelInPerS2 * Math.max(ds, 0));
81
+ velocities[i] = Math.min(velocities[i], stoppable);
82
+ }
83
+ let seconds = 0;
84
+ let sidewaysIn = 0;
85
+ let totalIn = 0;
86
+ const timeAtSampleS = [0];
87
+ for (let i = 1; i < count; i += 1) {
88
+ const before = samples[i - 1];
89
+ const after = samples[i];
90
+ const ds = after.sIn - before.sIn;
91
+ if (ds <= 0) {
92
+ timeAtSampleS.push(seconds);
93
+ continue;
94
+ }
95
+ const mean = (velocities[i - 1] + velocities[i]) / 2;
96
+ seconds += mean <= STOPPED_IN_PER_S ? 0 : ds / mean;
97
+ timeAtSampleS.push(seconds);
98
+ totalIn += ds;
99
+ // Each interval is attributed half to the sample at each of its ends, so a leg that is
100
+ // sideways throughout scores exactly 1 and a nose-first leg exactly 0.
101
+ for (const sample of [before, after]) {
102
+ if (Math.abs(Math.sin(sample.phiRad)) > Math.abs(Math.cos(sample.phiRad))) {
103
+ sidewaysIn += ds / 2;
104
+ }
105
+ }
106
+ }
107
+ return {
108
+ seconds,
109
+ // Clamped because the halves are summed in floating point: a leg that is sideways throughout
110
+ // must read exactly 1, not 1.0000000000000002.
111
+ strafeFraction: totalIn === 0 ? 0 : Math.min(1, Math.max(0, sidewaysIn / totalIn)),
112
+ exitVelocityInPerS: velocities[count - 1],
113
+ timeAtSampleS,
114
+ };
115
+ }
116
+ /** The numbers a command's `estimateS` expression may name: its arguments, resolved to seconds. */
117
+ export function commandScope(spec, args, robot) {
118
+ const durations = namedDurations(robot);
119
+ const scope = {};
120
+ const put = (name, raw) => {
121
+ if (raw === undefined)
122
+ return;
123
+ if (typeof raw === "number")
124
+ scope[name] = raw;
125
+ else if (typeof raw === "boolean")
126
+ scope[name] = raw ? 1 : 0;
127
+ else {
128
+ // An enum argument stands for a duration the robot file tabulates, which is how
129
+ // `0.6 + count * cadence` reads `shooter.cadenceS.rapid` (site/docs/file-format.md).
130
+ const seconds = durations.get(raw);
131
+ if (seconds !== undefined)
132
+ scope[name] = seconds;
133
+ }
134
+ };
135
+ for (const [name, param] of Object.entries(spec?.params ?? {})) {
136
+ const fromArgs = args?.[name];
137
+ put(name, fromArgs ?? ("default" in param ? param.default : undefined));
138
+ }
139
+ for (const [name, raw] of Object.entries(args ?? {})) {
140
+ if (!(name in scope))
141
+ put(name, raw);
142
+ }
143
+ return scope;
144
+ }
145
+ /** Every `name: seconds` pair the robot file tabulates under `shooter`, such as the shot cadences. */
146
+ function namedDurations(robot) {
147
+ const durations = new Map();
148
+ const shooter = robot.shooter;
149
+ if (!isRecord(shooter))
150
+ return durations;
151
+ for (const table of Object.values(shooter)) {
152
+ if (!isRecord(table))
153
+ continue;
154
+ for (const [name, value] of Object.entries(table)) {
155
+ if (name === "value" || name === "provenance")
156
+ continue;
157
+ if (typeof value === "number")
158
+ durations.set(name, value);
159
+ }
160
+ }
161
+ return durations;
162
+ }
163
+ /** The seconds a command step takes, or null when its registry entry says `"unknown"`. */
164
+ export function commandSeconds(name, args, robot) {
165
+ const spec = robot.commands.find((command) => command.name === name);
166
+ if (spec === undefined)
167
+ return null;
168
+ if (spec.estimateS.trim() === UNKNOWN_ESTIMATE)
169
+ return null;
170
+ let expression;
171
+ try {
172
+ expression = parseEstimateExpression(spec.estimateS);
173
+ }
174
+ catch {
175
+ return null;
176
+ }
177
+ const seconds = evaluateEstimateExpression(expression, commandScope(spec, args, robot));
178
+ return seconds === null || !Number.isFinite(seconds) ? null : Math.max(seconds, 0);
179
+ }
180
+ /** True when the registry marks this command as one the robot stands still for. */
181
+ export const isStationary = (robot, name) => robot.commands.find((command) => command.name === name)?.stationary === true;
182
+ /**
183
+ * Per-step `{nominalS, lowS, highS, strafeFraction, strafeCostS, unknown}` and the total with its
184
+ * band, from the model of `03` section 3. Deterministic and pure.
185
+ */
186
+ export function estimate(plan, robot) {
187
+ const limits = robotLimits(robot);
188
+ const assumptions = explain(plan, robot, limits);
189
+ const byStepId = {};
190
+ const band = (seconds) => seconds === null
191
+ ? { lowS: null, highS: null }
192
+ : {
193
+ lowS: seconds * (1 - limits.bandFraction),
194
+ highS: seconds * (1 + limits.bandFraction),
195
+ };
196
+ const record = (row) => {
197
+ byStepId[row.stepId] = row;
198
+ return row;
199
+ };
200
+ const blank = (step, seconds, carry, exitVelocityInPerS) => {
201
+ carry.velocityInPerS = exitVelocityInPerS;
202
+ return record({
203
+ stepId: step.id,
204
+ kind: step.kind,
205
+ nominalS: seconds,
206
+ ...band(seconds),
207
+ strafeFraction: null,
208
+ strafeCostS: null,
209
+ unknown: seconds === null,
210
+ exitVelocityInPerS,
211
+ });
212
+ };
213
+ const estimatePath = (step, carry) => {
214
+ const geometry = step.geometry;
215
+ if (geometry === undefined || step.samples.length < 2) {
216
+ return blank(step, limits.settleS, carry, 0);
217
+ }
218
+ const pathStep = step.step;
219
+ const speedFraction = pathStep.speedFraction ?? limits.defaultPathSpeedFraction;
220
+ const headings = step.samples.map((sample) => sample.pose.headingRad);
221
+ // The follower holds the end pose when `holdEnd` is set, so the step brakes to a stop; when it
222
+ // does not, the backward pass leaves whatever the caps allow and the next step carries it on.
223
+ const exitCap = limits.holdEnd ? 0 : Infinity;
224
+ const planned = integrateProfile(profileSamples(step, geometry, limits, speedFraction, headings), limits, carry.velocityInPerS, exitCap);
225
+ // The same geometry driven nose-first, which is what "make tangent" would produce and so what
226
+ // the strafing on this leg actually costs (`03` section 5).
227
+ const tangentHeadings = step.samples.map((sample) => {
228
+ const tangent = geometry.tangentAtDistance(sample.sIn);
229
+ return Math.atan2(tangent.yIn, tangent.xIn);
230
+ });
231
+ const tangent = integrateProfile(profileSamples(step, geometry, limits, speedFraction, tangentHeadings), limits, carry.velocityInPerS, exitCap);
232
+ const nominalS = planned.seconds + limits.settleS;
233
+ carry.velocityInPerS = planned.exitVelocityInPerS;
234
+ // A step with an endCondition stops when a sensor says so, which can only be sooner than
235
+ // driving the whole path: the seconds stay as the upper bound they are, and `unknown` says the
236
+ // number is a bound rather than a prediction, so the timeline hatches it and the time budget
237
+ // does not read the total as firm.
238
+ const endsOnCondition = pathStep.endCondition !== undefined;
239
+ return record({
240
+ stepId: step.id,
241
+ kind: step.kind,
242
+ nominalS,
243
+ ...band(nominalS),
244
+ strafeFraction: planned.strafeFraction,
245
+ strafeCostS: Math.max(planned.seconds - tangent.seconds, 0),
246
+ unknown: endsOnCondition,
247
+ exitVelocityInPerS: planned.exitVelocityInPerS,
248
+ timeAtSampleS: planned.timeAtSampleS,
249
+ });
250
+ };
251
+ const estimateStep = (step, carry) => {
252
+ switch (step.kind) {
253
+ case "path":
254
+ return estimatePath(step, carry);
255
+ case "command": {
256
+ const call = step.step;
257
+ const seconds = commandSeconds(call.name, call.args, robot);
258
+ // A command the robot stands still for leaves it stopped; one that runs while the drive
259
+ // keeps going (`setIntake`) leaves the carried velocity alone (`03` section 3 step 3).
260
+ const stationary = isStationary(robot, call.name);
261
+ return blank(step, seconds, carry, stationary ? 0 : carry.velocityInPerS);
262
+ }
263
+ case "wait": {
264
+ const wait = step.step;
265
+ return blank(step, wait.seconds ?? null, carry, 0);
266
+ }
267
+ case "sequence":
268
+ return estimateSequenceGroup(step, carry);
269
+ case "parallel":
270
+ return estimateParallel(step, carry);
271
+ case "branch":
272
+ return estimateBranch(step, carry);
273
+ }
274
+ };
275
+ /**
276
+ * A sequence costs the sum of its members, and the speed carries through it exactly as it does
277
+ * through the top-level list: a path inside it accelerates from wherever the step before the
278
+ * sequence left the robot, and the step after the sequence starts from its last member's exit.
279
+ *
280
+ * A member with no predictable duration counts as zero, as it does in the routine's total, and
281
+ * marks the sequence `unknown`; the sum is null only when no member is known at all.
282
+ */
283
+ const estimateSequenceGroup = (step, carry) => {
284
+ const children = (step.children ?? []).map((child) => estimateStep(child, carry));
285
+ const known = children.filter((child) => child.nominalS !== null);
286
+ const nominalS = children.length > 0 && known.length === 0
287
+ ? null
288
+ : known.reduce((sum, child) => sum + child.nominalS, 0);
289
+ return record({
290
+ stepId: step.id,
291
+ kind: step.kind,
292
+ nominalS,
293
+ ...band(nominalS),
294
+ strafeFraction: null,
295
+ strafeCostS: null,
296
+ unknown: children.some((child) => child.unknown),
297
+ exitVelocityInPerS: carry.velocityInPerS,
298
+ children,
299
+ });
300
+ };
301
+ /**
302
+ * Members that run at the same time. Each one starts from the speed the group was entered at and
303
+ * gets its own carry: two paths in a `parallel` both begin where the robot was and neither hands
304
+ * its exit speed to the other, which is the same reading CONTINUITY takes of their start poses.
305
+ */
306
+ const estimateConcurrent = (children, entry) => children.map((child) => estimateStep(child, { velocityInPerS: entry.velocityInPerS }));
307
+ /** Steps that run one after another, threading one carry, seeded from `entry`. */
308
+ const estimateSequence = (children, entry) => {
309
+ const inner = { velocityInPerS: entry.velocityInPerS };
310
+ return children.map((child) => estimateStep(child, inner));
311
+ };
312
+ const estimateParallel = (step, carry) => {
313
+ const group = step.step;
314
+ const children = estimateConcurrent(step.children ?? [], carry);
315
+ const known = children.filter((child) => child.nominalS !== null).map((child) => child.nominalS);
316
+ const anyUnknown = children.some((child) => child.unknown);
317
+ let nominalS;
318
+ let unknown = anyUnknown;
319
+ if (group.mode === "deadline") {
320
+ const deadline = children.find((child) => child.stepId === group.deadline);
321
+ if (deadline === undefined) {
322
+ // The group names no child that exists, which `check` raises a SCHEMA error for. Cost it
323
+ // like `all` rather than returning null: the members still take the time they take, and a
324
+ // null here would quietly subtract it from the routine total the time budget is judged on.
325
+ nominalS = known.length === 0 ? null : Math.max(...known);
326
+ unknown = true;
327
+ }
328
+ else {
329
+ nominalS = deadline.nominalS;
330
+ unknown = deadline.unknown;
331
+ }
332
+ }
333
+ else if (group.mode === "race") {
334
+ // The first child to finish ends the group, so a known child is an upper bound and an
335
+ // unknown one could still come in under it.
336
+ nominalS = known.length === 0 ? null : Math.min(...known);
337
+ }
338
+ else {
339
+ nominalS = known.length === 0 ? null : Math.max(...known);
340
+ }
341
+ // The velocity carries through a parallel group from whichever child drives (`03` §3.3).
342
+ const mover = children.find((child) => child.stepId === group.deadline && containsPath(child)) ??
343
+ [...children].reverse().find(containsPath);
344
+ const exitVelocityInPerS = mover?.exitVelocityInPerS ?? carry.velocityInPerS;
345
+ carry.velocityInPerS = exitVelocityInPerS;
346
+ return record({
347
+ stepId: step.id,
348
+ kind: step.kind,
349
+ nominalS,
350
+ ...band(nominalS),
351
+ strafeFraction: null,
352
+ strafeCostS: null,
353
+ unknown,
354
+ exitVelocityInPerS,
355
+ children,
356
+ });
357
+ };
358
+ const estimateBranch = (step, carry) => {
359
+ const branch = step.step;
360
+ const planned = step.children ?? [];
361
+ const thenCount = branch.then.length;
362
+ // Only one arm ever runs, so the `else` side starts from the entry speed as well, not from
363
+ // wherever the `then` side happened to finish.
364
+ const thenSide = estimateSequence(planned.slice(0, thenCount), carry);
365
+ const elseSide = estimateSequence(planned.slice(thenCount), carry);
366
+ const children = [...thenSide, ...elseSide];
367
+ const total = (rows) => rows.some((row) => row.nominalS === null)
368
+ ? null
369
+ : rows.reduce((sum, row) => sum + row.nominalS, 0);
370
+ const thenS = total(thenSide);
371
+ const elseS = elseSide.length === 0 ? 0 : total(elseSide);
372
+ // Nobody can know which way a condition goes at planning time, so the branch costs the longer
373
+ // side: a time budget that assumed the short one would be a promise the routine cannot keep.
374
+ const nominalS = thenS === null || elseS === null ? null : Math.max(thenS, elseS);
375
+ const exitVelocityInPerS = thenSide[thenSide.length - 1]?.exitVelocityInPerS ?? carry.velocityInPerS;
376
+ carry.velocityInPerS = exitVelocityInPerS;
377
+ return record({
378
+ stepId: step.id,
379
+ kind: step.kind,
380
+ nominalS,
381
+ ...band(nominalS),
382
+ strafeFraction: null,
383
+ strafeCostS: null,
384
+ unknown: children.some((child) => child.unknown),
385
+ exitVelocityInPerS,
386
+ children,
387
+ });
388
+ };
389
+ const carry = { velocityInPerS: 0 };
390
+ const steps = plan.steps.map((step) => estimateStep(step, carry));
391
+ const hasUnknown = steps.some((step) => step.unknown);
392
+ const nominalS = steps.reduce((sum, step) => sum + (step.nominalS ?? 0), 0);
393
+ return {
394
+ steps,
395
+ byStepId,
396
+ nominalS,
397
+ lowS: nominalS * (1 - limits.bandFraction),
398
+ highS: nominalS * (1 + limits.bandFraction),
399
+ hasUnknown,
400
+ bandFraction: limits.bandFraction,
401
+ assumptions,
402
+ explain: assumptions,
403
+ };
404
+ }
405
+ /** The sentences `zenith estimate --explain` prints: every assumption behind the numbers above. */
406
+ function explain(plan, robot, limits) {
407
+ const round = (value, digits = 1) => value.toFixed(digits);
408
+ const percent = (value) => `${round(value * 100, 0)} %`;
409
+ const lines = [];
410
+ lines.push(`Pedro v3 is a geometric follower with no trajectory to read, so these seconds come from a kinematic model of ${robot.name}, not from the follower.`);
411
+ lines.push(`The speed cap in a travel direction is the ellipse through ${round(limits.maxForwardVelInPerS)} in/s nose-on and ${round(limits.maxStrafeVelInPerS)} in/s sideways, scaled by the step's speedFraction (default ${round(limits.defaultPathSpeedFraction, 2)}).`);
412
+ lines.push(`Braking uses the same ellipse between ${round(limits.forwardDecelInPerS2)} in/s² forwards and ${round(limits.strafeDecelInPerS2)} in/s² sideways, which is why a sideways leg costs far more than the speed ratio suggests: stopping from full strafe takes ${round((limits.maxStrafeVelInPerS * limits.maxStrafeVelInPerS) / (2 * limits.strafeDecelInPerS2))} in against ${round((limits.maxForwardVelInPerS * limits.maxForwardVelInPerS) / (2 * limits.forwardDecelInPerS2))} in nose-first.`);
413
+ lines.push(`Acceleration is a flat ${round(limits.accelInPerS2)} in/s², applied from the velocity the previous step ended at; a stationary command and a wait both leave the robot at rest.`);
414
+ lines.push(limits.holdEnd
415
+ ? "The follower holds the end pose, so every path step is braked to a standstill before the next one starts."
416
+ : "The follower does not hold the end pose, so a path step hands its remaining speed to the step after it.");
417
+ lines.push(`A sample is slowed until the heading the mode demands fits inside ${round(limits.maxAngularVelRadPerS, 2)} rad/s.`);
418
+ lines.push(`Cornering is capped at sqrt(a · R) with a taken as the sideways deceleration, ${round(limits.strafeDecelInPerS2)} in/s². That is a proxy for how hard this drivetrain holds a curve, not a measurement.`);
419
+ lines.push(`Each path step adds ${round(limits.settleS, 2)} s of settle time for the follower to declare the end constraints met.`);
420
+ lines.push(`A command step is timed by its estimateS expression in robot.json; one that reads "unknown" is shown hatched and left out of the total, so the total is a lower bound.`);
421
+ lines.push("A sequence costs the sum of its steps, with the speed carried through it; a parallel group costs the longest of its children under `all`, the shortest under `race`, and its deadline step under `deadline`; a branch costs the longer of its two sides.");
422
+ lines.push(`The band is nominal ± ${percent(limits.bandFraction)}${robot.kinematics.calibration === undefined ? ", the uncalibrated default until a recorded run narrows it" : `, fitted from a recorded run (${robot.kinematics.calibration.provenance ?? "no provenance recorded"})`}.`);
423
+ const periodS = plan.field.periods?.autoS;
424
+ if (periodS !== undefined) {
425
+ lines.push(`The autonomous period on this field is ${round(periodS, 0)} s.`);
426
+ }
427
+ return lines;
428
+ }
429
+ /**
430
+ * Which way an estimate's total is a bound, when it is one. A step with no predictable duration
431
+ * counts as zero seconds, so the routine takes at least the total ("lower"). A step that ends on a
432
+ * condition is timed as if it drove its whole path, so the routine takes at most the total
433
+ * ("upper"). When both kinds are present the total is only a lower bound on the known part, and
434
+ * "lower" is reported because it is the claim that stays true. The renderer, the editor and the
435
+ * pull-request body all read this one rule.
436
+ */
437
+ export function totalBound(estimateResult) {
438
+ if (!estimateResult.hasUnknown)
439
+ return null;
440
+ const rows = Object.values(estimateResult.byStepId);
441
+ return rows.some((row) => row.unknown && row.nominalS === null) ? "lower" : "upper";
442
+ }
443
+ //# sourceMappingURL=estimate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"estimate.js","sourceRoot":"","sources":["../src/estimate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import type { FindingCode } from "./types.js";
2
+ /** Plain-language help for one finding code: a short name, what it means, and how to fix it. */
3
+ export interface FindingHelp {
4
+ /** A few words, for a chip or a heading. */
5
+ title: string;
6
+ /** What the finding is telling you, for someone new to FTC pathing. */
7
+ means: string;
8
+ /** What to change, in the editor's own words. */
9
+ fix: string;
10
+ }
11
+ /**
12
+ * What each finding code means and how to fix it, written for someone who has never planned an
13
+ * FTC autonomous before (site/docs/editor.md). The editor shows it behind the
14
+ * code chip, `zenith validate --explain` prints it under each finding, and the MCP server returns
15
+ * it with findings, so all three say the same thing.
16
+ */
17
+ export declare const FINDING_HELP: Readonly<Record<FindingCode, FindingHelp>>;
18
+ //# sourceMappingURL=findingHelp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"findingHelp.d.ts","sourceRoot":"","sources":["../src/findingHelp.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,gGAAgG;AAChG,MAAM,WAAW,WAAW;IAC1B,4CAA4C;IAC5C,KAAK,EAAE,MAAM,CAAC;IACd,uEAAuE;IACvE,KAAK,EAAE,MAAM,CAAC;IACd,iDAAiD;IACjD,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;;;GAKG;AACH,eAAO,MAAM,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,WAAW,EAAE,WAAW,CAAC,CA0GnE,CAAC"}
@@ -0,0 +1,114 @@
1
+ /**
2
+ * What each finding code means and how to fix it, written for someone who has never planned an
3
+ * FTC autonomous before (site/docs/editor.md). The editor shows it behind the
4
+ * code chip, `zenith validate --explain` prints it under each finding, and the MCP server returns
5
+ * it with findings, so all three say the same thing.
6
+ */
7
+ export const FINDING_HELP = {
8
+ SCHEMA: {
9
+ title: "File problem",
10
+ means: "Something in the file is not allowed, such as a misspelled command, a missing value or a waypoint name that does not exist. The robot would refuse to load this file.",
11
+ fix: "Read the message for the exact key that is wrong. Pick the name from the list the editor offers, or add the missing value.",
12
+ },
13
+ CONTINUITY: {
14
+ title: "Path jumps",
15
+ means: "A path starts somewhere other than where the robot is, so the robot would have to teleport. The gap is measured from where the previous step ended.",
16
+ fix: "Set the path's start to \"current\" so it begins wherever the robot is. Or drag its first point onto the end of the step before it.",
17
+ },
18
+ HEADING_MISSING: {
19
+ title: "No heading",
20
+ means: "A path does not say which way the robot should face while it drives. Pedro, the path follower, stops the auto with an error when this is missing.",
21
+ fix: "Choose a heading mode in the path's inspector. Tangent, which points the nose along the path, is the usual choice.",
22
+ },
23
+ HEADING: {
24
+ title: "Heading differs on the robot",
25
+ means: "The robot will not face the way this path asks: a turn of more than half a circle in one piece goes the short way round, and a face-point offset is not used yet.",
26
+ fix: "Split the path so no piece turns more than half a circle, or set the heading at a waypoint in between.",
27
+ },
28
+ HEADING_RANGES: {
29
+ title: "Heading ranges do not fit",
30
+ means: "A piecewise heading must split its path into ranges that run from start to end in order, with no gap and no overlap. Pedro stops the auto when they do not.",
31
+ fix: "Drag the range boundaries so each range starts where the one before it ends, the first at 0 and the last at 1.",
32
+ },
33
+ PERIMETER: {
34
+ title: "Leaves the field",
35
+ means: "Part of the robot, its body or an intake mouth, would go past the field wall. A real robot would hit the wall instead.",
36
+ fix: "Move the point away from the wall until the outline stays inside. Snapping to the wall places the robot flush against it.",
37
+ },
38
+ STRUCTURE: {
39
+ title: "Hits a field element",
40
+ means: "The robot's outline overlaps something solid on the field, such as the hive or a flower base. The robot would crash into it.",
41
+ fix: "Move the path around the element, or add a point to bend it clear. The finding marks where the overlap is deepest.",
42
+ },
43
+ KEEPOUT: {
44
+ title: "Enters a no-go zone",
45
+ means: "The robot drives into an area the game rules restrict during the autonomous period. That can cost a penalty.",
46
+ fix: "Reroute the path so the outline stays out of the shaded zone. The zone's rule is named in the message.",
47
+ },
48
+ START_ILLEGAL: {
49
+ title: "Start not allowed",
50
+ means: "The starting pose breaks a game rule, for example not touching a wall or starting in the wrong half. The referees would not let the match start like this.",
51
+ fix: "Drag the start pose to a legal spot, usually flush against your own alliance's wall. Wall snap helps.",
52
+ },
53
+ LEGAL_APPROACH: {
54
+ title: "Shot from an illegal spot",
55
+ means: "The robot shoots from a place the rules do not allow for the target it is aiming at. The scoring would not count or would draw a penalty.",
56
+ fix: "Move the shooting pose to the allowed side of the target. The one-click fix moves it to the nearest legal spot.",
57
+ },
58
+ TURRET_RANGE: {
59
+ title: "Turret cannot aim",
60
+ means: "At this shooting pose the target is outside the angle the turret can turn to. The robot would shoot in the wrong direction.",
61
+ fix: "Turn the robot so the target is closer to straight ahead, or move the shooting pose.",
62
+ },
63
+ CAPACITY: {
64
+ title: "Holds too many",
65
+ means: "The plan has the robot holding more game pieces than it can carry or the rules allow. Something would fall out or draw a penalty.",
66
+ fix: "Shoot or drop pieces before collecting more, or collect fewer. The ledger panel shows what the robot holds after each step.",
67
+ },
68
+ EMPTY_SHOT: {
69
+ title: "Shooting nothing",
70
+ means: "A shoot step runs when the plan says the robot holds no pieces. It wastes time in the autonomous period.",
71
+ fix: "Collect before shooting, or remove the shot. Check the ledger panel to see when the robot is empty.",
72
+ },
73
+ STRAFE_FRACTION: {
74
+ title: "Driving sideways",
75
+ means: "A lot of this path is driven sideways, which on mecanum wheels is slower and stops less sharply than driving forward. The message says how many seconds it costs.",
76
+ fix: "Use the fix that points the nose along the path, or turn during the previous stop. Keep sideways driving for short, deliberate moves.",
77
+ },
78
+ MOUTH_LEADING: {
79
+ title: "Intake not in front",
80
+ means: "The intake is running but its mouth is not facing the way the robot drives. The robot's body would push game pieces away instead of picking them up.",
81
+ fix: "Choose a heading that puts the running mouth in front, for example tangent for a front mouth or reversed tangent for a back one.",
82
+ },
83
+ SWEEP_SPEED: {
84
+ title: "Intaking too fast",
85
+ means: "The robot drives quickly while its intake runs. Pieces get knocked away rather than collected at that speed.",
86
+ fix: "Lower this path's speed to the sweep speed the robot file sets. The one-click fix does it for you.",
87
+ },
88
+ TIME_BUDGET: {
89
+ title: "Over the time limit",
90
+ means: "The whole routine may take longer than the autonomous period. Anything after the buzzer does not happen.",
91
+ fix: "Cut or shorten steps, drive faster where it is safe, or move slow steps later. The timeline shows which steps take the longest.",
92
+ },
93
+ TIMEOUT_TIGHT: {
94
+ title: "Timeout too short",
95
+ means: "This step's timeout is close to how long it is expected to take. A slightly slow run would be cut off before it finishes.",
96
+ fix: "Raise the step's timeout to at least a little more than its estimate. The estimate is shown beside the step.",
97
+ },
98
+ STATIONARY_MARKER: {
99
+ title: "Stop-only command while moving",
100
+ means: "A command that needs the robot to stand still is set to fire in the middle of a path. It would run while the robot is still driving.",
101
+ fix: "Make it its own step after the path instead of a marker on it.",
102
+ },
103
+ MOVES_ROBOT: {
104
+ title: "Start after a moving command",
105
+ means: "The step before this one moves the robot by itself, so nobody knows exactly where it ends. This path assumes a fixed starting point anyway.",
106
+ fix: "Set this path's start to \"current\" so it begins wherever the robot really is.",
107
+ },
108
+ PROVENANCE: {
109
+ title: "Where did this come from?",
110
+ means: "A pose has no note saying where its numbers came from, such as measured on the field or set in the editor. This is only a reminder, not an error.",
111
+ fix: "Nothing is required. Measure the pose on the field or save it as a named waypoint to record where it came from.",
112
+ },
113
+ };
114
+ //# sourceMappingURL=findingHelp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"findingHelp.js","sourceRoot":"","sources":["../src/findingHelp.ts"],"names":[],"mappings":"AAYA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,YAAY,GAA+C;IACtE,MAAM,EAAE;QACN,KAAK,EAAE,cAAc;QACrB,KAAK,EAAE,uKAAuK;QAC9K,GAAG,EAAE,4HAA4H;KAClI;IACD,UAAU,EAAE;QACV,KAAK,EAAE,YAAY;QACnB,KAAK,EAAE,qJAAqJ;QAC5J,GAAG,EAAE,qIAAqI;KAC3I;IACD,eAAe,EAAE;QACf,KAAK,EAAE,YAAY;QACnB,KAAK,EAAE,mJAAmJ;QAC1J,GAAG,EAAE,oHAAoH;KAC1H;IACD,OAAO,EAAE;QACP,KAAK,EAAE,8BAA8B;QACrC,KAAK,EAAE,mKAAmK;QAC1K,GAAG,EAAE,wGAAwG;KAC9G;IACD,cAAc,EAAE;QACd,KAAK,EAAE,2BAA2B;QAClC,KAAK,EAAE,6JAA6J;QACpK,GAAG,EAAE,gHAAgH;KACtH;IACD,SAAS,EAAE;QACT,KAAK,EAAE,kBAAkB;QACzB,KAAK,EAAE,wHAAwH;QAC/H,GAAG,EAAE,2HAA2H;KACjI;IACD,SAAS,EAAE;QACT,KAAK,EAAE,sBAAsB;QAC7B,KAAK,EAAE,8HAA8H;QACrI,GAAG,EAAE,oHAAoH;KAC1H;IACD,OAAO,EAAE;QACP,KAAK,EAAE,qBAAqB;QAC5B,KAAK,EAAE,8GAA8G;QACrH,GAAG,EAAE,wGAAwG;KAC9G;IACD,aAAa,EAAE;QACb,KAAK,EAAE,mBAAmB;QAC1B,KAAK,EAAE,4JAA4J;QACnK,GAAG,EAAE,uGAAuG;KAC7G;IACD,cAAc,EAAE;QACd,KAAK,EAAE,2BAA2B;QAClC,KAAK,EAAE,2IAA2I;QAClJ,GAAG,EAAE,iHAAiH;KACvH;IACD,YAAY,EAAE;QACZ,KAAK,EAAE,mBAAmB;QAC1B,KAAK,EAAE,6HAA6H;QACpI,GAAG,EAAE,sFAAsF;KAC5F;IACD,QAAQ,EAAE;QACR,KAAK,EAAE,gBAAgB;QACvB,KAAK,EAAE,mIAAmI;QAC1I,GAAG,EAAE,6HAA6H;KACnI;IACD,UAAU,EAAE;QACV,KAAK,EAAE,kBAAkB;QACzB,KAAK,EAAE,0GAA0G;QACjH,GAAG,EAAE,qGAAqG;KAC3G;IACD,eAAe,EAAE;QACf,KAAK,EAAE,kBAAkB;QACzB,KAAK,EAAE,mKAAmK;QAC1K,GAAG,EAAE,uIAAuI;KAC7I;IACD,aAAa,EAAE;QACb,KAAK,EAAE,qBAAqB;QAC5B,KAAK,EAAE,sJAAsJ;QAC7J,GAAG,EAAE,kIAAkI;KACxI;IACD,WAAW,EAAE;QACX,KAAK,EAAE,mBAAmB;QAC1B,KAAK,EAAE,8GAA8G;QACrH,GAAG,EAAE,oGAAoG;KAC1G;IACD,WAAW,EAAE;QACX,KAAK,EAAE,qBAAqB;QAC5B,KAAK,EAAE,0GAA0G;QACjH,GAAG,EAAE,iIAAiI;KACvI;IACD,aAAa,EAAE;QACb,KAAK,EAAE,mBAAmB;QAC1B,KAAK,EAAE,2HAA2H;QAClI,GAAG,EAAE,8GAA8G;KACpH;IACD,iBAAiB,EAAE;QACjB,KAAK,EAAE,gCAAgC;QACvC,KAAK,EAAE,sIAAsI;QAC7I,GAAG,EAAE,gEAAgE;KACtE;IACD,WAAW,EAAE;QACX,KAAK,EAAE,8BAA8B;QACrC,KAAK,EAAE,6IAA6I;QACpJ,GAAG,EAAE,iFAAiF;KACvF;IACD,UAAU,EAAE;QACV,KAAK,EAAE,2BAA2B;QAClC,KAAK,EAAE,mJAAmJ;QAC1J,GAAG,EAAE,iHAAiH;KACvH;CACF,CAAC"}
package/dist/fix.d.ts ADDED
@@ -0,0 +1,21 @@
1
+ import { type Auto } from "@horizon36596/zenith-schema";
2
+ import type { Fix } from "./types.js";
3
+ /** de Casteljau subdivision: the control polygons of the two halves of a Bezier split at `u`. */
4
+ export declare function splitControlPolygon(points: readonly {
5
+ xIn: number;
6
+ yIn: number;
7
+ }[], u: number): {
8
+ left: {
9
+ xIn: number;
10
+ yIn: number;
11
+ }[];
12
+ right: {
13
+ xIn: number;
14
+ yIn: number;
15
+ }[];
16
+ };
17
+ /** Applies one structured fix to a document and hands back a new one. Pure. */
18
+ export declare function applyFix(auto: Auto, fix: Fix): Auto;
19
+ /** Applies several fixes in order. Each one sees the document the one before it produced. */
20
+ export declare const applyFixes: (auto: Auto, fixes: readonly Fix[]) => Auto;
21
+ //# sourceMappingURL=fix.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fix.d.ts","sourceRoot":"","sources":["../src/fix.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,IAAI,EAKV,MAAM,6BAA6B,CAAC;AAErC,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,YAAY,CAAC;AAkFtC,iGAAiG;AACjG,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,SAAS;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,EAAE,EAC/C,CAAC,EAAE,MAAM,GACR;IAAE,IAAI,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAAC,KAAK,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CAAE,CAgBjF;AAmDD,+EAA+E;AAC/E,wBAAgB,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,GAAG,IAAI,CAuEnD;AAED,6FAA6F;AAC7F,eAAO,MAAM,UAAU,GAAI,MAAM,IAAI,EAAE,OAAO,SAAS,GAAG,EAAE,KAAG,IACD,CAAC"}