eyeling 1.16.3 → 1.16.4
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/README.md +0 -1
- package/package.json +2 -3
- package/arctifacts/README.md +0 -59
- package/arctifacts/ackermann.html +0 -678
- package/arctifacts/auroracare.html +0 -1297
- package/arctifacts/bike-trip.html +0 -752
- package/arctifacts/binomial-theorem.html +0 -631
- package/arctifacts/bmi.html +0 -511
- package/arctifacts/building-performance.html +0 -750
- package/arctifacts/clinical-care.html +0 -726
- package/arctifacts/collatz.html +0 -403
- package/arctifacts/complex.html +0 -321
- package/arctifacts/control-system.html +0 -482
- package/arctifacts/delfour.html +0 -849
- package/arctifacts/earthquake-epicenter.html +0 -982
- package/arctifacts/eco-route.html +0 -662
- package/arctifacts/euclid-infinitude.html +0 -564
- package/arctifacts/euler-identity.html +0 -667
- package/arctifacts/exoplanet-transit.html +0 -1000
- package/arctifacts/faltings-theorem.html +0 -1046
- package/arctifacts/fibonacci.html +0 -299
- package/arctifacts/fundamental-theorem-arithmetic.html +0 -398
- package/arctifacts/godel-numbering.html +0 -743
- package/arctifacts/gps-bike.html +0 -759
- package/arctifacts/gps-clinical-bench.html +0 -792
- package/arctifacts/graph-french.html +0 -449
- package/arctifacts/grass-molecular.html +0 -592
- package/arctifacts/group-theory.html +0 -740
- package/arctifacts/health-info.html +0 -833
- package/arctifacts/kaprekar-constant.html +0 -576
- package/arctifacts/lee.html +0 -805
- package/arctifacts/linked-lists.html +0 -502
- package/arctifacts/lldm.html +0 -612
- package/arctifacts/matrix-multiplication.html +0 -502
- package/arctifacts/matrix.html +0 -651
- package/arctifacts/newton-raphson.html +0 -944
- package/arctifacts/peano-factorial.html +0 -456
- package/arctifacts/pi.html +0 -363
- package/arctifacts/polynomial.html +0 -646
- package/arctifacts/prime.html +0 -366
- package/arctifacts/pythagorean-theorem.html +0 -468
- package/arctifacts/rest-path.html +0 -469
- package/arctifacts/roots-of-unity.html +0 -363
- package/arctifacts/turing.html +0 -409
- package/arctifacts/wind-turbines.html +0 -726
package/arctifacts/lldm.html
DELETED
|
@@ -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 (|Δ| < 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>
|