eyeling 1.16.3 → 1.16.5

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 (61) hide show
  1. package/HANDBOOK.md +153 -0
  2. package/README.md +0 -1
  3. package/examples/auroracare.n3 +528 -0
  4. package/examples/control-system.n3 +223 -47
  5. package/examples/delfour.n3 +409 -0
  6. package/examples/gps.n3 +144 -53
  7. package/examples/ill-formed-literals.n3 +195 -0
  8. package/examples/output/auroracare.n3 +118 -0
  9. package/examples/output/control-system.n3 +24 -1
  10. package/examples/output/delfour.n3 +40 -0
  11. package/examples/output/gps.n3 +14 -2
  12. package/examples/output/ill-formed-literals.n3 +27 -0
  13. package/examples/output/parcellocker.n3 +15 -0
  14. package/examples/parcellocker.n3 +164 -0
  15. package/eyeling.js +16 -1
  16. package/lib/engine.js +14 -1
  17. package/lib/prelude.js +2 -0
  18. package/package.json +2 -3
  19. package/arctifacts/README.md +0 -59
  20. package/arctifacts/ackermann.html +0 -678
  21. package/arctifacts/auroracare.html +0 -1297
  22. package/arctifacts/bike-trip.html +0 -752
  23. package/arctifacts/binomial-theorem.html +0 -631
  24. package/arctifacts/bmi.html +0 -511
  25. package/arctifacts/building-performance.html +0 -750
  26. package/arctifacts/clinical-care.html +0 -726
  27. package/arctifacts/collatz.html +0 -403
  28. package/arctifacts/complex.html +0 -321
  29. package/arctifacts/control-system.html +0 -482
  30. package/arctifacts/delfour.html +0 -849
  31. package/arctifacts/earthquake-epicenter.html +0 -982
  32. package/arctifacts/eco-route.html +0 -662
  33. package/arctifacts/euclid-infinitude.html +0 -564
  34. package/arctifacts/euler-identity.html +0 -667
  35. package/arctifacts/exoplanet-transit.html +0 -1000
  36. package/arctifacts/faltings-theorem.html +0 -1046
  37. package/arctifacts/fibonacci.html +0 -299
  38. package/arctifacts/fundamental-theorem-arithmetic.html +0 -398
  39. package/arctifacts/godel-numbering.html +0 -743
  40. package/arctifacts/gps-bike.html +0 -759
  41. package/arctifacts/gps-clinical-bench.html +0 -792
  42. package/arctifacts/graph-french.html +0 -449
  43. package/arctifacts/grass-molecular.html +0 -592
  44. package/arctifacts/group-theory.html +0 -740
  45. package/arctifacts/health-info.html +0 -833
  46. package/arctifacts/kaprekar-constant.html +0 -576
  47. package/arctifacts/lee.html +0 -805
  48. package/arctifacts/linked-lists.html +0 -502
  49. package/arctifacts/lldm.html +0 -612
  50. package/arctifacts/matrix-multiplication.html +0 -502
  51. package/arctifacts/matrix.html +0 -651
  52. package/arctifacts/newton-raphson.html +0 -944
  53. package/arctifacts/peano-factorial.html +0 -456
  54. package/arctifacts/pi.html +0 -363
  55. package/arctifacts/polynomial.html +0 -646
  56. package/arctifacts/prime.html +0 -366
  57. package/arctifacts/pythagorean-theorem.html +0 -468
  58. package/arctifacts/rest-path.html +0 -469
  59. package/arctifacts/roots-of-unity.html +0 -363
  60. package/arctifacts/turing.html +0 -409
  61. package/arctifacts/wind-turbines.html +0 -726
@@ -1,612 +0,0 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1" />
6
- <title>Leg Length Discrepancy Measurement</title>
7
- <style>
8
- :root {
9
- --bg: #f8fafc;
10
- --fg: #0f172a;
11
- --muted: #475569;
12
- --soft: #e2e8f0;
13
- --card: #ffffff;
14
- --accent: #0ea5e9;
15
- --accent2: #ef4444;
16
- --accent3: #22c55e;
17
- --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, 'Liberation Mono', monospace;
18
- --sans:
19
- ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans',
20
- 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
21
- }
22
- html,
23
- body {
24
- height: 100%;
25
- }
26
- body {
27
- margin: 0;
28
- background: var(--bg);
29
- color: var(--fg);
30
- font: 16px/1.5 var(--sans);
31
- }
32
- .wrap {
33
- max-width: 960px;
34
- margin: 28px auto;
35
- padding: 0 16px;
36
- }
37
- header {
38
- margin-bottom: 16px;
39
- }
40
- h1 {
41
- font-size: 22px;
42
- font-weight: 700;
43
- margin: 0 0 4px;
44
- }
45
- .sub {
46
- color: var(--muted);
47
- font-size: 14px;
48
- }
49
- .card {
50
- background: var(--card);
51
- border: 1px solid var(--soft);
52
- border-radius: 14px;
53
- padding: 16px;
54
- margin: 14px 0;
55
- box-shadow: 0 6px 16px rgba(15, 23, 42, 0.06);
56
- }
57
- .inputs {
58
- display: grid;
59
- grid-template-columns: repeat(4, minmax(0, 1fr));
60
- gap: 10px;
61
- }
62
- label {
63
- font-size: 12px;
64
- color: var(--muted);
65
- display: block;
66
- margin-bottom: 6px;
67
- }
68
- input[type='number'] {
69
- width: 100%;
70
- box-sizing: border-box;
71
- background: #fff;
72
- color: var(--fg);
73
- border: 1px solid var(--soft);
74
- border-radius: 10px;
75
- padding: 8px 10px;
76
- font: 14px var(--mono);
77
- }
78
- button {
79
- appearance: none;
80
- border: 0;
81
- background: var(--accent);
82
- color: #fff;
83
- font-weight: 700;
84
- padding: 10px 14px;
85
- border-radius: 12px;
86
- cursor: pointer;
87
- }
88
- .btn-row {
89
- display: flex;
90
- gap: 10px;
91
- align-items: center;
92
- flex-wrap: wrap;
93
- margin-top: 12px;
94
- }
95
- .hint {
96
- color: var(--muted);
97
- font-size: 12px;
98
- }
99
-
100
- h2 {
101
- font-size: 18px;
102
- margin: 6px 0 10px;
103
- }
104
- pre {
105
- font: 14px/1.5 var(--mono);
106
- background: #fff;
107
- border: 1px solid var(--soft);
108
- border-radius: 12px;
109
- padding: 12px;
110
- overflow: auto;
111
- }
112
- code {
113
- font-family: var(--mono);
114
- background: #fff;
115
- border: 1px solid var(--soft);
116
- padding: 2px 6px;
117
- border-radius: 8px;
118
- }
119
- .answer {
120
- padding: 12px;
121
- border: 1px solid var(--soft);
122
- border-radius: 12px;
123
- background: #fff;
124
- }
125
- .answer pre {
126
- margin: 0;
127
- border: 0;
128
- }
129
-
130
- /* Archived image panel (no internal scrollbars) */
131
- .img-panel {
132
- position: relative;
133
- border: 1px solid var(--soft);
134
- border-radius: 12px;
135
- overflow: hidden;
136
- background: #fff;
137
- }
138
- .img-panel img {
139
- display: block;
140
- width: 100%;
141
- height: auto;
142
- }
143
- .annos {
144
- position: absolute;
145
- inset: 0;
146
- pointer-events: none;
147
- }
148
- .callout {
149
- position: absolute;
150
- background: #ffffffcc;
151
- border: 1px solid var(--soft);
152
- border-radius: 10px;
153
- padding: 8px 10px;
154
- font: 13px var(--mono);
155
- color: var(--fg);
156
- pointer-events: auto;
157
- }
158
- .callout .hd {
159
- font-weight: 700;
160
- margin-bottom: 4px;
161
- }
162
-
163
- table {
164
- width: 100%;
165
- border-collapse: collapse;
166
- font: 14px var(--mono);
167
- background: #fff;
168
- border: 1px solid var(--soft);
169
- border-radius: 12px;
170
- overflow: hidden;
171
- }
172
- th,
173
- td {
174
- padding: 8px 10px;
175
- border-bottom: 1px solid var(--soft);
176
- text-align: left;
177
- }
178
- tr:last-child td {
179
- border-bottom: 0;
180
- }
181
- .ok {
182
- color: #16a34a;
183
- font-weight: 700;
184
- }
185
- .bad {
186
- color: #dc2626;
187
- font-weight: 700;
188
- }
189
- </style>
190
- </head>
191
- <body>
192
- <div class="wrap">
193
- <header>
194
- <h1>Leg Length Discrepancy Measurement</h1>
195
- <div class="sub">
196
- Leg-Length Discrepancy Measurement (LLDM) by orthogonal projection onto pelvic baseline L1.
197
- </div>
198
- </header>
199
-
200
- <section class="card">
201
- <h2>Landmarks (cm)</h2>
202
- <div class="inputs">
203
- <div><label for="p1x">p1x</label><input id="p1x" type="number" step="0.1" value="10.1" /></div>
204
- <div><label for="p1y">p1y</label><input id="p1y" type="number" step="0.1" value="7.8" /></div>
205
- <div><label for="p2x">p2x</label><input id="p2x" type="number" step="0.1" value="45.1" /></div>
206
- <div><label for="p2y">p2y</label><input id="p2y" type="number" step="0.1" value="5.6" /></div>
207
- <div><label for="p3x">p3x</label><input id="p3x" type="number" step="0.1" value="3.6" /></div>
208
- <div><label for="p3y">p3y</label><input id="p3y" type="number" step="0.1" value="29.8" /></div>
209
- <div><label for="p4x">p4x</label><input id="p4x" type="number" step="0.1" value="54.7" /></div>
210
- <div><label for="p4y">p4y</label><input id="p4y" type="number" step="0.1" value="28.5" /></div>
211
- </div>
212
- <div class="btn-row">
213
- <button id="run">Compute</button>
214
- <span class="hint"
215
- >Rounding to 4 decimals; internal math in full precision. Slope convention:
216
- <span class="mono">cL3 = -(-1/cL1)</span>.</span
217
- >
218
- </div>
219
- </section>
220
-
221
- <section class="card">
222
- <h2>Answer</h2>
223
- <div class="answer"><pre id="answerTxt"></pre></div>
224
- </section>
225
-
226
- <section class="card">
227
- <h2>Reason why</h2>
228
- <pre id="trace"></pre>
229
- </section>
230
-
231
- <section class="card">
232
- <h2>Figure (photo with annotations)</h2>
233
- <div class="img-panel">
234
- <img
235
- src="https://web.archive.org/web/20110103134005im_/http://www.agfa.com/w3c/2002/10/medicad/op/lldmD.jpg"
236
- alt="LLDM illustration (archived Agfa page)" />
237
- <div class="annos">
238
- <div class="callout" style="top: 14px; left: 14px; max-width: 320px">
239
- <div class="hd">Computed results</div>
240
- <div class="mono" id="callNumbers">d53=?, d64=?, dCm=?</div>
241
- <div class="mono" id="callAlarm">LLDAlarm: ?</div>
242
- </div>
243
- <div class="callout" style="bottom: 14px; right: 14px; max-width: 360px">
244
- <div class="hd">Notes</div>
245
- <div>Values overlay the image. Landmarks drive the math.</div>
246
- </div>
247
- </div>
248
- </div>
249
- </section>
250
-
251
- <section class="card">
252
- <h2>Check (harness)</h2>
253
- <p class="hint">
254
- Each line: <code>p1x,p1y,p2x,p2y,p3x,p3y,p4x,p4y[,expected_dCm[,expected_alarm]]</code>. Expected columns are
255
- optional; when present they’re validated (|Δ| &lt; 0.001 cm).
256
- </p>
257
- <pre id="cases" contenteditable="true"></pre>
258
- <div style="display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin-top: 8px">
259
- <button id="runChecks">Run checks</button>
260
- <button id="addRow">Add current landmarks</button>
261
- <button id="clearRows" style="background: #64748b">Clear</button>
262
- </div>
263
- <div id="checkTableWrap" style="margin-top: 10px"></div>
264
- </section>
265
- </div>
266
-
267
- <script>
268
- (function () {
269
- const EN_DASH = '–';
270
- const fmt = (num, dec = 4, width = 7) => {
271
- const sgn = num >= 0 ? '+' : EN_DASH;
272
- const mag = Math.abs(num).toFixed(dec);
273
- return (sgn + mag).padStart(width + dec + 2, ' ');
274
- };
275
- const line1 = (buf, step, lhs, rhs) => buf.push(`Step ${String(step).padStart(2, '0')} ${lhs} = ${rhs}`);
276
- const cont = (buf, expr) => buf.push(` = ${expr}`);
277
- const sep = (buf) => buf.push('─'.repeat(76));
278
-
279
- function compute(p1x, p1y, p2x, p2y, p3x, p3y, p4x, p4y) {
280
- let step = 1,
281
- out = [];
282
- // Deltas
283
- const dx12 = p1x - p2x;
284
- const dy12 = p1y - p2y;
285
- const dy13 = p1y - p3y;
286
- const dy24 = p2y - p4y;
287
- line1(out, step, 'dx12Cm', 'p1x − p2x');
288
- cont(out, `${p1x} − ${p2x}`);
289
- cont(out, `${fmt(dx12)} cm`);
290
- step++;
291
- line1(out, step, 'dy12Cm', 'p1y − p2y');
292
- cont(out, `${p1y} − ${p2y}`);
293
- cont(out, `${fmt(dy12)} cm`);
294
- step++;
295
- line1(out, step, 'dy13Cm', 'p1y − p3y');
296
- cont(out, `${p1y} − ${p3y}`);
297
- cont(out, `${fmt(dy13)} cm`);
298
- step++;
299
- line1(out, step, 'dy24Cm', 'p2y − p4y');
300
- cont(out, `${p2y} − ${p4y}`);
301
- cont(out, `${fmt(dy24)} cm`);
302
- step++;
303
- sep(out);
304
-
305
- // Slopes
306
- const cL1 = dy12 / dx12;
307
- const dL3m = -1 / cL1;
308
- const cL3 = -dL3m;
309
- line1(out, step, 'cL1 ', 'dy12 / dx12');
310
- cont(out, `${fmt(dy12)} / ${fmt(dx12)}`);
311
- cont(out, `${fmt(cL1, 6)}`);
312
- step++;
313
- line1(out, step, 'dL3m', EN_DASH + '1 / cL1');
314
- cont(out, `${EN_DASH}1 / ${fmt(cL1, 6)}`);
315
- cont(out, `${fmt(dL3m, 4)}`);
316
- step++;
317
- line1(out, step, 'cL3 ', EN_DASH + 'dL3m');
318
- cont(out, `${EN_DASH}(${fmt(dL3m, 4)})`);
319
- cont(out, `${fmt(cL3, 4)}`);
320
- step++;
321
- sep(out);
322
-
323
- // m·x items
324
- const pL1x1 = cL1 * p1x,
325
- pL1x2 = cL1 * p2x;
326
- const pL3x3 = cL3 * p3x,
327
- pL3x4 = cL3 * p4x;
328
- line1(out, step, 'pL1x1', 'cL1 · p1x');
329
- cont(out, `${fmt(cL1, 6)} · ${p1x}`);
330
- cont(out, `${fmt(pL1x1)} cm`);
331
- step++;
332
- line1(out, step, 'pL1x2', 'cL1 · p2x');
333
- cont(out, `${fmt(cL1, 6)} · ${p2x}`);
334
- cont(out, `${fmt(pL1x2)} cm`);
335
- step++;
336
- line1(out, step, 'pL3x3', 'cL3 · p3x');
337
- cont(out, `${fmt(cL3, 4)} · ${p3x}`);
338
- cont(out, `${fmt(pL3x3)} cm`);
339
- step++;
340
- line1(out, step, 'pL3x4', 'cL3 · p4x');
341
- cont(out, `${fmt(cL3, 4)} · ${p4x}`);
342
- cont(out, `${fmt(pL3x4)} cm`);
343
- step++;
344
- sep(out);
345
-
346
- // Diagonal helpers
347
- const dd13 = pL1x1 - pL3x3,
348
- ddy13 = dd13 - dy13;
349
- const dd24 = pL1x2 - pL3x4,
350
- ddy24 = dd24 - dy24;
351
- const ddL13 = cL1 - cL3;
352
- line1(out, step, 'dd13Cm', 'pL1x1 − pL3x3');
353
- cont(out, `${fmt(pL1x1)} − ${fmt(pL3x3)}`);
354
- cont(out, `${fmt(dd13)} cm`);
355
- step++;
356
- line1(out, step, 'ddy13Cm', 'dd13Cm − dy13Cm');
357
- cont(out, `${fmt(dd13)} − (${fmt(dy13)})`);
358
- cont(out, `${fmt(ddy13)} cm`);
359
- step++;
360
- line1(out, step, 'dd24Cm', 'pL1x2 − pL3x4');
361
- cont(out, `${fmt(pL1x2)} − ${fmt(pL3x4)}`);
362
- cont(out, `${fmt(dd24)} cm`);
363
- step++;
364
- line1(out, step, 'ddy24Cm', 'dd24Cm − dy24Cm');
365
- cont(out, `${fmt(dd24)} − (${fmt(dy24)})`);
366
- cont(out, `${fmt(ddy24)} cm`);
367
- step++;
368
- line1(out, step, 'ddL13', 'cL1 − cL3');
369
- cont(out, `${fmt(cL1, 6)} − ${fmt(cL3, 4)}`);
370
- cont(out, `${fmt(ddL13)}`);
371
- step++;
372
- sep(out);
373
-
374
- // Feet p5/p6 on L1
375
- const p5x = ddy13 / ddL13;
376
- const dx51 = p5x - p1x;
377
- const p5y = cL1 * dx51 + p1y;
378
- const p6x = ddy24 / ddL13;
379
- const dx62 = p6x - p2x;
380
- const p6y = cL1 * dx62 + p2y;
381
- line1(out, step, 'p5xCm', 'ddy13Cm / ddL13');
382
- cont(out, `${fmt(ddy13)} / ${fmt(ddL13)}`);
383
- cont(out, `${fmt(p5x)} cm`);
384
- step++;
385
- line1(out, step, 'p5yCm', 'cL1·(p5x−p1x)+p1y');
386
- cont(out, `(${fmt(cL1, 6)})(${fmt(dx51)}) + ${p1y}`);
387
- cont(out, `${fmt(p5y)} cm`);
388
- step++;
389
- line1(out, step, 'p6xCm', 'ddy24Cm / ddL13');
390
- cont(out, `${fmt(ddy24)} / ${fmt(ddL13)}`);
391
- cont(out, `${fmt(p6x)} cm`);
392
- step++;
393
- line1(out, step, 'p6yCm', 'cL1·(p6x−p2x)+p2y');
394
- cont(out, `(${fmt(cL1, 6)})(${fmt(dx62)}) + ${p2y}`);
395
- cont(out, `${fmt(p6y)} cm`);
396
- step++;
397
- sep(out);
398
-
399
- // Distances
400
- const dx53 = p5x - p3x,
401
- dy53 = p5y - p3y;
402
- const dx64 = p6x - p4x,
403
- dy64 = p6y - p4y;
404
- const d53 = Math.hypot(dx53, dy53);
405
- const d64 = Math.hypot(dx64, dy64);
406
- line1(out, step, 'd53Cm', '√(dx53² + dy53²)');
407
- cont(out, `√(${fmt(dx53)}² + ${fmt(dy53)}²)`);
408
- cont(out, `${fmt(d53)} cm`);
409
- step++;
410
- line1(out, step, 'd64Cm', '√(dx64² + dy64²)');
411
- cont(out, `√(${fmt(dx64)}² + ${fmt(dy64)}²)`);
412
- cont(out, `${fmt(d64)} cm`);
413
- step++;
414
-
415
- // Discrepancy & alarm
416
- const dCm = d53 - d64;
417
- const alarm = Math.abs(dCm) > 1.25;
418
- line1(out, step, 'dCm ', 'd53 − d64');
419
- cont(out, `${d53.toFixed(4)} − ${d64.toFixed(4)}`);
420
- cont(out, `${fmt(dCm)} cm`);
421
- step++;
422
- line1(out, step, 'LLDAlarm', '|dCm| > 1.25 ?');
423
- cont(out, `|${fmt(dCm)}| > 1.25 → ${alarm}`);
424
- cont(out, `${alarm ? ' 1.000000' : ' 0.000000'}`);
425
-
426
- return {
427
- out: out.join('\n'),
428
- d: { p1x, p1y, p2x, p2y, p3x, p3y, p4x, p4y, p5x, p5y, p6x, p6y, d53, d64, dCm, alarm },
429
- };
430
- }
431
-
432
- function renderAnswer(d) {
433
- const sgn = (x) => (x >= 0 ? '+' : EN_DASH) + Math.abs(x).toFixed(4);
434
- return [
435
- 'Answer',
436
- '======',
437
- 'LLDAlarm = |dCm| > 1.25 ?',
438
- ` = |${sgn(d.dCm).padStart(10)}| > 1.25 → ${d.alarm ? 'True' : 'False'}`,
439
- ` = ${(d.alarm ? 1.0 : 0.0).toFixed(6)} # ${d.alarm ? 'Alarm raised: discrepancy exceeds ±1.25 cm' : 'No alarm: within ±1.25 cm'}`,
440
- ].join('\n');
441
- }
442
-
443
- function updateCallouts(d) {
444
- const num = (x) => (x >= 0 ? '+' : EN_DASH) + Math.abs(x).toFixed(4);
445
- document.getElementById('callNumbers').textContent =
446
- `d53=${d.d53.toFixed(4)} cm, d64=${d.d64.toFixed(4)} cm, dCm=${num(d.dCm)} cm`;
447
- document.getElementById('callAlarm').textContent = `LLDAlarm: ${d.alarm ? 'True' : 'False'}`;
448
- }
449
-
450
- function run() {
451
- const val = (id) => parseFloat(document.getElementById(id).value);
452
- const res = compute(
453
- val('p1x'),
454
- val('p1y'),
455
- val('p2x'),
456
- val('p2y'),
457
- val('p3x'),
458
- val('p3y'),
459
- val('p4x'),
460
- val('p4y'),
461
- );
462
- document.getElementById('trace').textContent = res.out;
463
- document.getElementById('answerTxt').textContent = renderAnswer(res.d);
464
- updateCallouts(res.d);
465
- }
466
-
467
- document.getElementById('run').addEventListener('click', run);
468
- run();
469
-
470
- // ---- Harness ----------------------------------------------------
471
- function parseLine(line) {
472
- const parts = line.split(/\s*,\s*/).filter(Boolean);
473
- if (parts.length < 8) return null;
474
- const nums = parts.slice(0, 8).map(parseFloat);
475
- const expDCm = parts[8] !== undefined && parts[8] !== '' ? parseFloat(parts[8]) : null;
476
- const expAlarm = parts[9] !== undefined ? /true/i.test(String(parts[9])) : null;
477
- return { nums, expDCm, expAlarm };
478
- }
479
-
480
- function runChecks() {
481
- const lines = document
482
- .getElementById('cases')
483
- .textContent.split(/\n+/)
484
- .map((s) => s.trim())
485
- .filter(Boolean);
486
- const rows = [];
487
- for (const line of lines) {
488
- const rec = parseLine(line);
489
- if (!rec) continue;
490
- const [p1x, p1y, p2x, p2y, p3x, p3y, p4x, p4y] = rec.nums;
491
- const { d } = compute(p1x, p1y, p2x, p2y, p3x, p3y, p4x, p4y);
492
- const diff = rec.expDCm == null ? null : d.dCm - rec.expDCm;
493
- const passDCm = rec.expDCm == null ? null : Math.abs(diff) < 1e-3; // within 0.001 cm
494
- const passAlarm = rec.expAlarm == null ? null : d.alarm === rec.expAlarm;
495
- rows.push({
496
- p1x,
497
- p1y,
498
- p2x,
499
- p2y,
500
- p3x,
501
- p3y,
502
- p4x,
503
- p4y,
504
- d53: d.d53,
505
- d64: d.d64,
506
- dCm: d.dCm,
507
- alarm: d.alarm,
508
- expDCm: rec.expDCm,
509
- expAlarm: rec.expAlarm,
510
- diff,
511
- passDCm,
512
- passAlarm,
513
- });
514
- }
515
- renderTable(rows);
516
- }
517
-
518
- function renderTable(rows) {
519
- const wrap = document.getElementById('checkTableWrap');
520
- if (!rows.length) {
521
- wrap.innerHTML = '<p class="hint">No rows.</p>';
522
- return;
523
- }
524
- let html =
525
- '<table style="width:100%; border-collapse:collapse; font:14px var(--mono); background:#fff; border:1px solid var(--soft)">' +
526
- '<thead><tr>' +
527
- '<th style="text-align:left;padding:8px 10px;border-bottom:1px solid var(--soft)">p1x</th>' +
528
- '<th style="text-align:left;padding:8px 10px;border-bottom:1px solid var(--soft)">p1y</th>' +
529
- '<th style="text-align:left;padding:8px 10px;border-bottom:1px solid var(--soft)">p2x</th>' +
530
- '<th style="text-align:left;padding:8px 10px;border-bottom:1px solid var(--soft)">p2y</th>' +
531
- '<th style="text-align:left;padding:8px 10px;border-bottom:1px solid var(--soft)">p3x</th>' +
532
- '<th style="text-align:left;padding:8px 10px;border-bottom:1px solid var(--soft)">p3y</th>' +
533
- '<th style="text-align:left;padding:8px 10px;border-bottom:1px solid var(--soft)">p4x</th>' +
534
- '<th style="text-align:left;padding:8px 10px;border-bottom:1px solid var(--soft)">p4y</th>' +
535
- '<th style="text-align:left;padding:8px 10px;border-bottom:1px solid var(--soft)">d53</th>' +
536
- '<th style="text-align:left;padding:8px 10px;border-bottom:1px solid var(--soft)">d64</th>' +
537
- '<th style="text-align:left;padding:8px 10px;border-bottom:1px solid var(--soft)">dCm</th>' +
538
- '<th style="text-align:left;padding:8px 10px;border-bottom:1px solid var(--soft)">|dCm|>1.25</th>' +
539
- '<th style="text-align:left;padding:8px 10px;border-bottom:1px solid var(--soft)">exp dCm</th>' +
540
- '<th style="text-align:left;padding:8px 10px;border-bottom:1px solid var(--soft)">Δ</th>' +
541
- '<th style="text-align:left;padding:8px 10px;border-bottom:1px solid var(--soft)">exp alarm</th>' +
542
- '<th style="text-align:left;padding:8px 10px;border-bottom:1px solid var(--soft)">pass?</th>' +
543
- '</tr></thead><tbody>';
544
- for (const r of rows) {
545
- const pass = (r.passDCm === null || r.passDCm) && (r.passAlarm === null || r.passAlarm);
546
- const cells = [
547
- r.p1x,
548
- r.p1y,
549
- r.p2x,
550
- r.p2y,
551
- r.p3x,
552
- r.p3y,
553
- r.p4x,
554
- r.p4y,
555
- r.d53.toFixed(4),
556
- r.d64.toFixed(4),
557
- r.dCm.toFixed(4),
558
- Math.abs(r.dCm) > 1.25,
559
- r.expDCm == null ? '' : r.expDCm,
560
- r.diff == null ? '' : r.diff.toFixed(4),
561
- r.expAlarm == null ? '' : r.expAlarm,
562
- pass
563
- ? '<span style="color:#16a34a;font-weight:700">OK</span>'
564
- : '<span style="color:#dc2626;font-weight:700">FAIL</span>',
565
- ];
566
- html +=
567
- '<tr>' +
568
- cells.map((x) => `<td style="padding:8px 10px;border-bottom:1px solid var(--soft)">${x}</td>`).join('') +
569
- '</tr>';
570
- }
571
- html += '</tbody></table>';
572
- wrap.innerHTML = html;
573
- }
574
-
575
- // Buttons
576
- document.getElementById('runChecks').addEventListener('click', runChecks);
577
- document.getElementById('addRow').addEventListener('click', () => {
578
- const id = (s) => document.getElementById(s).value;
579
- const line = [id('p1x'), id('p1y'), id('p2x'), id('p2y'), id('p3x'), id('p3y'), id('p4x'), id('p4y')].join(
580
- ',',
581
- );
582
- const pre = document.getElementById('cases');
583
- pre.textContent += (pre.textContent.endsWith('\n') ? '' : '\n') + line + '\n';
584
- });
585
- document.getElementById('clearRows').addEventListener('click', () => {
586
- document.getElementById('cases').textContent = '';
587
- document.getElementById('checkTableWrap').innerHTML = '';
588
- });
589
-
590
- // Seed a few checks and AUTO-RUN them on load
591
- (function seedAndAutoRun() {
592
- const tests = [
593
- [10.1, 7.8, 45.1, 5.6, 3.6, 29.8, 54.7, 28.5], // baseline (page)
594
- [10.1, 7.8, 45.1, 5.6, 3.6, 29.8, 54.7, 29.5], // p4y +1cm
595
- [10.1, 7.8, 45.1, 5.6, 3.6, 28.8, 54.7, 28.5], // p3y -1cm
596
- [10.1, 8.8, 45.1, 5.6, 3.6, 29.8, 54.7, 28.5], // p1y +1cm (tilt L1)
597
- [8.1, 7.8, 47.1, 5.6, 3.6, 29.8, 54.7, 28.5], // widen pelvis
598
- ];
599
- const lines =
600
- tests
601
- .map((t) => {
602
- const { d } = compute(...t);
603
- return t.join(',') + ',' + d.dCm.toFixed(4) + ',' + (Math.abs(d.dCm) > 1.25);
604
- })
605
- .join('\n') + '\n';
606
- document.getElementById('cases').textContent = lines;
607
- runChecks(); // <-- auto-run now
608
- })();
609
- })();
610
- </script>
611
- </body>
612
- </html>