@decidables/detectable-elements 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +44 -0
- package/README.md +128 -5
- package/lib/detectableElements.esm.js +742 -367
- package/lib/detectableElements.esm.js.map +1 -1
- package/lib/detectableElements.esm.min.js +215 -129
- package/lib/detectableElements.esm.min.js.map +1 -1
- package/lib/detectableElements.umd.js +744 -366
- package/lib/detectableElements.umd.js.map +1 -1
- package/lib/detectableElements.umd.min.js +220 -134
- package/lib/detectableElements.umd.min.js.map +1 -1
- package/package.json +3 -3
- package/src/components/detectable-control.js +7 -6
- package/src/components/detectable-response.js +26 -11
- package/src/components/detectable-table.js +30 -13
- package/src/components/rdk-task.js +4 -6
- package/src/components/roc-space.js +5 -7
- package/src/components/sdt-model.js +13 -15
- package/src/equations/hfa2ppv.js +121 -0
- package/src/equations/hmfacr2acc.js +1 -1
- package/src/equations/index.js +2 -0
- package/src/equations/mcr2fomr.js +121 -0
- package/src/equations/sdt-equation.js +15 -0
- package/src/examples/index.js +1 -0
- package/src/examples/multiple.js +76 -0
- package/src/examples/sdt-example.js +2 -6
- package/src/examples/unequal.js +4 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@decidables/detectable-elements",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "detectable-elements: Web Components for visualizing Signal Detection Theory",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"web component",
|
|
@@ -56,11 +56,11 @@
|
|
|
56
56
|
"gulp": "^4.0.2"
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
|
-
"@decidables/decidables-elements": "^0.
|
|
59
|
+
"@decidables/decidables-elements": "^0.3.1",
|
|
60
60
|
"@decidables/detectable-math": "^0.1.1",
|
|
61
61
|
"d3": "^7.3.0",
|
|
62
62
|
"jstat": "^1.9.5",
|
|
63
63
|
"lit": "^2.2.1"
|
|
64
64
|
},
|
|
65
|
-
"gitHead": "
|
|
65
|
+
"gitHead": "0afa544a1711ac1d34594934ab6c03064481810f"
|
|
66
66
|
}
|
|
@@ -81,7 +81,7 @@ export default class DetectableControl extends DetectableElement {
|
|
|
81
81
|
this.duration = undefined;
|
|
82
82
|
this.coherence = undefined;
|
|
83
83
|
this.payoff = undefined;
|
|
84
|
-
this.colors = ['none', 'accuracy', 'stimulus', 'response', 'outcome'];
|
|
84
|
+
this.colors = ['none', 'accuracy', 'stimulus', 'response', 'outcome', 'all'];
|
|
85
85
|
this.color = undefined;
|
|
86
86
|
this.zRoc = undefined;
|
|
87
87
|
this.run = false;
|
|
@@ -234,11 +234,12 @@ export default class DetectableControl extends DetectableElement {
|
|
|
234
234
|
? html`
|
|
235
235
|
<decidables-toggle @change=${this.chooseColor.bind(this)}>
|
|
236
236
|
<span slot="label">Emphasis</span>
|
|
237
|
-
<decidables-toggle-option name
|
|
238
|
-
<decidables-toggle-option name
|
|
239
|
-
<decidables-toggle-option name
|
|
240
|
-
<decidables-toggle-option name
|
|
241
|
-
<decidables-toggle-option name
|
|
237
|
+
<decidables-toggle-option name="toggle" value="none" ?checked=${this.color === 'none'}>None</decidables-toggle-option>
|
|
238
|
+
<decidables-toggle-option name="toggle" value="accuracy" ?checked=${this.color === 'accuracy'}>Accuracy</decidables-toggle-option>
|
|
239
|
+
<decidables-toggle-option name="toggle" value="stimulus" ?checked=${this.color === 'stimulus'}>Stimulus</decidables-toggle-option>
|
|
240
|
+
<decidables-toggle-option name="toggle" value="response" ?checked=${this.color === 'response'}>Response</decidables-toggle-option>
|
|
241
|
+
<decidables-toggle-option name="toggle" value="outcome" ?checked=${this.color === 'outcome'}>Outcome</decidables-toggle-option>
|
|
242
|
+
<decidables-toggle-option name="toggle" value="all" ?checked=${this.color === 'all'}>All</decidables-toggle-option>
|
|
242
243
|
</decidables-toggle>
|
|
243
244
|
`
|
|
244
245
|
: html``}
|
|
@@ -302,6 +302,11 @@ export default class DetectableResponse extends DetectableElement {
|
|
|
302
302
|
border: 1px solid var(---color-element-border);
|
|
303
303
|
}
|
|
304
304
|
|
|
305
|
+
:host([payoff="trial"]) .feedback,
|
|
306
|
+
:host([payoff="total"]) .feedback {
|
|
307
|
+
height: 5rem;
|
|
308
|
+
}
|
|
309
|
+
|
|
305
310
|
.feedback.h {
|
|
306
311
|
background-color: var(---color-h-light);
|
|
307
312
|
}
|
|
@@ -337,17 +342,12 @@ export default class DetectableResponse extends DetectableElement {
|
|
|
337
342
|
line-height: 1.15;
|
|
338
343
|
}
|
|
339
344
|
|
|
340
|
-
:host([payoff="trial"]) .feedback,
|
|
341
|
-
:host([payoff="total"]) .feedback {
|
|
342
|
-
height: 4rem;
|
|
343
|
-
}
|
|
344
|
-
|
|
345
345
|
/* Payoff feedback */
|
|
346
|
-
.
|
|
346
|
+
.total {
|
|
347
347
|
text-align: center;
|
|
348
348
|
}
|
|
349
349
|
|
|
350
|
-
.
|
|
350
|
+
.total .label {
|
|
351
351
|
font-weight: 600;
|
|
352
352
|
}
|
|
353
353
|
`,
|
|
@@ -355,6 +355,21 @@ export default class DetectableResponse extends DetectableElement {
|
|
|
355
355
|
}
|
|
356
356
|
|
|
357
357
|
render() {
|
|
358
|
+
const payoffFormatter = new Intl.NumberFormat('en-US', {
|
|
359
|
+
style: 'currency',
|
|
360
|
+
currency: 'USD',
|
|
361
|
+
minimumFractionDigits: 0,
|
|
362
|
+
maximumFractionDigits: 0,
|
|
363
|
+
});
|
|
364
|
+
const payoffFormat = (number) => {
|
|
365
|
+
return payoffFormatter.formatToParts(number).map(({type, value}) => {
|
|
366
|
+
if (type === 'minusSign') {
|
|
367
|
+
return '−';
|
|
368
|
+
}
|
|
369
|
+
return value;
|
|
370
|
+
}).reduce((string, part) => { return string + part; });
|
|
371
|
+
};
|
|
372
|
+
|
|
358
373
|
return html`
|
|
359
374
|
<div class="holder">
|
|
360
375
|
<div class="responses">
|
|
@@ -394,15 +409,15 @@ export default class DetectableResponse extends DetectableElement {
|
|
|
394
409
|
? html`<span class="outcome">Error</span>`
|
|
395
410
|
: html`<span class="outcome">No<br>Response</span>`
|
|
396
411
|
: ''}
|
|
397
|
-
${(this.payoff === 'trial' || this.payoff === 'total')
|
|
398
|
-
? html`<span class="payoff">${this.trialPayoff}</span>`
|
|
412
|
+
${((this.state === 'feedback') && (this.payoff === 'trial' || this.payoff === 'total'))
|
|
413
|
+
? html`<span class="payoff">${payoffFormat(this.trialPayoff)}</span>`
|
|
399
414
|
: html``}
|
|
400
415
|
</div>`
|
|
401
416
|
: html``}
|
|
402
417
|
${(this.payoff === 'total')
|
|
403
418
|
? html`
|
|
404
|
-
<div class="
|
|
405
|
-
<span class="label">Total: </span><span class="value">${this.totalPayoff}</span>
|
|
419
|
+
<div class="total">
|
|
420
|
+
<span class="label">Total: </span><span class="value">${payoffFormat(this.totalPayoff)}</span>
|
|
406
421
|
</div>`
|
|
407
422
|
: html``}
|
|
408
423
|
</div>`
|
|
@@ -118,8 +118,8 @@ export default class DetectableTable extends DetectableElement {
|
|
|
118
118
|
this.summaries = ['stimulusRates', 'responseRates', 'accuracy'];
|
|
119
119
|
this.summary = new Set();
|
|
120
120
|
|
|
121
|
-
this.colors = ['none', 'accuracy', 'stimulus', 'response', 'outcome'];
|
|
122
|
-
this.color = '
|
|
121
|
+
this.colors = ['none', 'accuracy', 'stimulus', 'response', 'outcome', 'all'];
|
|
122
|
+
this.color = 'all';
|
|
123
123
|
|
|
124
124
|
this.h = 40;
|
|
125
125
|
this.m = 60;
|
|
@@ -318,7 +318,7 @@ export default class DetectableTable extends DetectableElement {
|
|
|
318
318
|
|
|
319
319
|
/* Color schemes & Table emphasis */
|
|
320
320
|
|
|
321
|
-
/* (Default)
|
|
321
|
+
/* (Default) All color scheme */
|
|
322
322
|
.h {
|
|
323
323
|
background: var(---color-h-light);
|
|
324
324
|
border-top: 2px solid var(---color-element-emphasis);
|
|
@@ -417,6 +417,15 @@ export default class DetectableTable extends DetectableElement {
|
|
|
417
417
|
background: var(---color-element-background);
|
|
418
418
|
}
|
|
419
419
|
|
|
420
|
+
/* Outcome color scheme */
|
|
421
|
+
:host([color="outcome"]) .hr,
|
|
422
|
+
:host([color="outcome"]) .far,
|
|
423
|
+
:host([color="outcome"]) .ppv,
|
|
424
|
+
:host([color="outcome"]) .fomr,
|
|
425
|
+
:host([color="outcome"]) .acc {
|
|
426
|
+
background: var(---color-element-background);
|
|
427
|
+
}
|
|
428
|
+
|
|
420
429
|
/* No color scheme */
|
|
421
430
|
:host([color="none"]) .cr,
|
|
422
431
|
:host([color="none"]) .fa,
|
|
@@ -440,6 +449,14 @@ export default class DetectableTable extends DetectableElement {
|
|
|
440
449
|
minimumFractionDigits: 0,
|
|
441
450
|
maximumFractionDigits: 0,
|
|
442
451
|
});
|
|
452
|
+
const payoffFormat = (number) => {
|
|
453
|
+
return payoffFormatter.formatToParts(number).map(({type, value}) => {
|
|
454
|
+
if (type === 'minusSign') {
|
|
455
|
+
return '−';
|
|
456
|
+
}
|
|
457
|
+
return value;
|
|
458
|
+
}).reduce((string, part) => { return string + part; });
|
|
459
|
+
};
|
|
443
460
|
|
|
444
461
|
this.alignState();
|
|
445
462
|
let h;
|
|
@@ -455,25 +472,25 @@ export default class DetectableTable extends DetectableElement {
|
|
|
455
472
|
h = html`
|
|
456
473
|
<decidables-spinner ?disabled=${!this.interactive} min="0" .value="${this.h}" @input=${this.hInput.bind(this)}>
|
|
457
474
|
<span>Hits</span>
|
|
458
|
-
${this.payoff ? html`<span class="payoff">${
|
|
475
|
+
${this.payoff ? html`<span class="payoff">${payoffFormat(this.hPayoff)}</span>` : html``}
|
|
459
476
|
</decidables-spinner>
|
|
460
477
|
`;
|
|
461
478
|
m = html`
|
|
462
479
|
<decidables-spinner ?disabled=${!this.interactive} min="0" .value="${this.m}" @input=${this.mInput.bind(this)}>
|
|
463
480
|
<span>Misses</span>
|
|
464
|
-
${this.payoff ? html`<span class="payoff">${
|
|
481
|
+
${this.payoff ? html`<span class="payoff">${payoffFormat(this.mPayoff)}</span>` : html``}
|
|
465
482
|
</decidables-spinner>
|
|
466
483
|
`;
|
|
467
484
|
fa = html`
|
|
468
485
|
<decidables-spinner ?disabled=${!this.interactive} min="0" .value="${this.fa}" @input=${this.faInput.bind(this)}>
|
|
469
486
|
<span>False Alarms</span>
|
|
470
|
-
${this.payoff ? html`<span class="payoff">${
|
|
487
|
+
${this.payoff ? html`<span class="payoff">${payoffFormat(this.faPayoff)}</span>` : html``}
|
|
471
488
|
</decidables-spinner>
|
|
472
489
|
`;
|
|
473
490
|
cr = html`
|
|
474
491
|
<decidables-spinner ?disabled=${!this.interactive} min="0" .value="${this.cr}" @input=${this.crInput.bind(this)}>
|
|
475
492
|
<span>Correct Rejections</span>
|
|
476
|
-
${this.payoff ? html`<span class="payoff">${
|
|
493
|
+
${this.payoff ? html`<span class="payoff">${payoffFormat(this.crPayoff)}</span>` : html``}
|
|
477
494
|
</decidables-spinner>
|
|
478
495
|
`;
|
|
479
496
|
hr = html`
|
|
@@ -503,13 +520,13 @@ export default class DetectableTable extends DetectableElement {
|
|
|
503
520
|
`;
|
|
504
521
|
} else {
|
|
505
522
|
h = html`<span>Hits</span>
|
|
506
|
-
${this.payoff ? html`<span class="payoff">${
|
|
523
|
+
${this.payoff ? html`<span class="payoff">${payoffFormat(this.hPayoff)}</span>` : html``}`;
|
|
507
524
|
m = html`<span>Misses</span>
|
|
508
|
-
${this.payoff ? html`<span class="payoff">${
|
|
525
|
+
${this.payoff ? html`<span class="payoff">${payoffFormat(this.mPayoff)}</span>` : html``}`;
|
|
509
526
|
fa = html`<span>False Alarms</span>
|
|
510
|
-
${this.payoff ? html`<span class="payoff">${
|
|
527
|
+
${this.payoff ? html`<span class="payoff">${payoffFormat(this.faPayoff)}</span>` : html``}`;
|
|
511
528
|
cr = html`<span>Correct Rejections</span>
|
|
512
|
-
${this.payoff ? html`<span class="payoff">${
|
|
529
|
+
${this.payoff ? html`<span class="payoff">${payoffFormat(this.crPayoff)}</span>` : html``}`;
|
|
513
530
|
hr = html`<span>Hit Rate</span>`;
|
|
514
531
|
far = html`<span>False Alarm Rate</span>`;
|
|
515
532
|
acc = html`<span>Accuracy</span>`;
|
|
@@ -527,10 +544,10 @@ export default class DetectableTable extends DetectableElement {
|
|
|
527
544
|
</tr>
|
|
528
545
|
<tr>
|
|
529
546
|
<th class="th th-sub" scope="col">
|
|
530
|
-
|
|
547
|
+
‘Present’
|
|
531
548
|
</th>
|
|
532
549
|
<th class="th th-sub" scope="col">
|
|
533
|
-
|
|
550
|
+
‘Absent’
|
|
534
551
|
</th>
|
|
535
552
|
</tr>
|
|
536
553
|
</thead>
|
|
@@ -164,7 +164,7 @@ export default class RDKTask extends DetectableElement {
|
|
|
164
164
|
}
|
|
165
165
|
|
|
166
166
|
.dot {
|
|
167
|
-
|
|
167
|
+
r: 2px;
|
|
168
168
|
}
|
|
169
169
|
|
|
170
170
|
.dots.coherent {
|
|
@@ -188,7 +188,7 @@ export default class RDKTask extends DetectableElement {
|
|
|
188
188
|
];
|
|
189
189
|
}
|
|
190
190
|
|
|
191
|
-
render() {
|
|
191
|
+
render() { /* eslint-disable-line class-methods-use-this */
|
|
192
192
|
return html``;
|
|
193
193
|
}
|
|
194
194
|
|
|
@@ -213,8 +213,7 @@ export default class RDKTask extends DetectableElement {
|
|
|
213
213
|
super.firstUpdated(changedProperties);
|
|
214
214
|
|
|
215
215
|
// Get the width and height after initial render/update has occurred
|
|
216
|
-
|
|
217
|
-
window.setTimeout(this.getDimensions.bind(this), 0);
|
|
216
|
+
this.getDimensions();
|
|
218
217
|
}
|
|
219
218
|
|
|
220
219
|
update(changedProperties) {
|
|
@@ -556,8 +555,7 @@ export default class RDKTask extends DetectableElement {
|
|
|
556
555
|
.data((datum) => { return datum; });
|
|
557
556
|
// ENTER
|
|
558
557
|
const dotEnter = dotUpdate.enter().append('circle')
|
|
559
|
-
.classed('dot', true)
|
|
560
|
-
.attr('r', 2); /* HACK: Firefox does not support CSS SVG Geometry Properties */
|
|
558
|
+
.classed('dot', true);
|
|
561
559
|
// MERGE
|
|
562
560
|
dotEnter.merge(dotUpdate)
|
|
563
561
|
.attr('cx', (datum) => { return datum.x; })
|
|
@@ -361,13 +361,13 @@ export default class ROCSpace extends DetectableElement {
|
|
|
361
361
|
.point .circle {
|
|
362
362
|
fill: var(---color-element-emphasis);
|
|
363
363
|
|
|
364
|
-
|
|
364
|
+
r: 6px;
|
|
365
365
|
}
|
|
366
366
|
|
|
367
367
|
.point .label {
|
|
368
368
|
font-size: 0.75rem;
|
|
369
369
|
|
|
370
|
-
dominant-baseline:
|
|
370
|
+
dominant-baseline: central;
|
|
371
371
|
text-anchor: middle;
|
|
372
372
|
|
|
373
373
|
fill: var(---color-text-inverse);
|
|
@@ -376,7 +376,7 @@ export default class ROCSpace extends DetectableElement {
|
|
|
376
376
|
];
|
|
377
377
|
}
|
|
378
378
|
|
|
379
|
-
render() {
|
|
379
|
+
render() { /* eslint-disable-line class-methods-use-this */
|
|
380
380
|
return html`
|
|
381
381
|
${DetectableElement.svgFilters}
|
|
382
382
|
`;
|
|
@@ -403,8 +403,7 @@ export default class ROCSpace extends DetectableElement {
|
|
|
403
403
|
super.firstUpdated(changedProperties);
|
|
404
404
|
|
|
405
405
|
// Get the width and height after initial render/update has occurred
|
|
406
|
-
|
|
407
|
-
window.setTimeout(this.getDimensions.bind(this), 0);
|
|
406
|
+
this.getDimensions();
|
|
408
407
|
}
|
|
409
408
|
|
|
410
409
|
update(changedProperties) {
|
|
@@ -1054,8 +1053,7 @@ export default class ROCSpace extends DetectableElement {
|
|
|
1054
1053
|
const pointEnter = pointUpdate.enter().append('g')
|
|
1055
1054
|
.classed('point', true);
|
|
1056
1055
|
pointEnter.append('circle')
|
|
1057
|
-
.classed('circle', true)
|
|
1058
|
-
.attr('r', 6); /* HACK: Firefox does not support CSS SVG Geometry Properties */
|
|
1056
|
+
.classed('circle', true);
|
|
1059
1057
|
pointEnter.append('text')
|
|
1060
1058
|
.classed('label', true);
|
|
1061
1059
|
// MERGE
|
|
@@ -415,7 +415,7 @@ export default class SDTModel extends DetectableElement {
|
|
|
415
415
|
.threshold .handle {
|
|
416
416
|
fill: var(---color-element-emphasis);
|
|
417
417
|
|
|
418
|
-
|
|
418
|
+
r: 6px;
|
|
419
419
|
}
|
|
420
420
|
|
|
421
421
|
/* Make a larger target for touch users */
|
|
@@ -473,7 +473,7 @@ export default class SDTModel extends DetectableElement {
|
|
|
473
473
|
];
|
|
474
474
|
}
|
|
475
475
|
|
|
476
|
-
render() {
|
|
476
|
+
render() { /* eslint-disable-line class-methods-use-this */
|
|
477
477
|
return html`
|
|
478
478
|
${DetectableElement.svgFilters}
|
|
479
479
|
`;
|
|
@@ -517,8 +517,7 @@ export default class SDTModel extends DetectableElement {
|
|
|
517
517
|
super.firstUpdated(changedProperties);
|
|
518
518
|
|
|
519
519
|
// Get the width and height after initial render/update has occurred
|
|
520
|
-
|
|
521
|
-
window.setTimeout(this.getDimensions.bind(this), 0);
|
|
520
|
+
this.getDimensions();
|
|
522
521
|
}
|
|
523
522
|
|
|
524
523
|
update(changedProperties) {
|
|
@@ -644,7 +643,7 @@ export default class SDTModel extends DetectableElement {
|
|
|
644
643
|
})
|
|
645
644
|
.on('drag', (event, datum) => {
|
|
646
645
|
this.drag = true;
|
|
647
|
-
let muS = this.muS;
|
|
646
|
+
let muS = this.muS; /* eslint-disable-line prefer-destructuring */
|
|
648
647
|
if (this.interactive) {
|
|
649
648
|
muS = xScale.invert(event.x);
|
|
650
649
|
// Clamp Signal Curve to stay visible
|
|
@@ -654,7 +653,7 @@ export default class SDTModel extends DetectableElement {
|
|
|
654
653
|
? xScale.domain()[1]
|
|
655
654
|
: muS;
|
|
656
655
|
}
|
|
657
|
-
let hS = this.hS;
|
|
656
|
+
let hS = this.hS; /* eslint-disable-line prefer-destructuring */
|
|
658
657
|
if (this.unequal) {
|
|
659
658
|
hS = yScale.invert(event.y);
|
|
660
659
|
// Clamp Signal Curve to stay visible
|
|
@@ -850,7 +849,7 @@ export default class SDTModel extends DetectableElement {
|
|
|
850
849
|
.on('keydown', this.interactive
|
|
851
850
|
? (event) => {
|
|
852
851
|
if (['ArrowRight', 'ArrowLeft'].includes(event.key)) {
|
|
853
|
-
let muN = this.muN;
|
|
852
|
+
let muN = this.muN; /* eslint-disable-line prefer-destructuring */
|
|
854
853
|
switch (event.key) {
|
|
855
854
|
case 'ArrowRight':
|
|
856
855
|
muN += event.shiftKey ? 0.01 : 0.1;
|
|
@@ -1045,7 +1044,7 @@ export default class SDTModel extends DetectableElement {
|
|
|
1045
1044
|
.on('keydown.sensitivity', this.interactive
|
|
1046
1045
|
? (event) => {
|
|
1047
1046
|
if (['ArrowRight', 'ArrowLeft'].includes(event.key)) {
|
|
1048
|
-
let muS = this.muS;
|
|
1047
|
+
let muS = this.muS; /* eslint-disable-line prefer-destructuring */
|
|
1049
1048
|
switch (event.key) {
|
|
1050
1049
|
case 'ArrowRight':
|
|
1051
1050
|
muS += event.shiftKey ? 0.01 : 0.1;
|
|
@@ -1073,7 +1072,7 @@ export default class SDTModel extends DetectableElement {
|
|
|
1073
1072
|
.on('keydown.variance', this.unequal
|
|
1074
1073
|
? (event) => {
|
|
1075
1074
|
if (['ArrowUp', 'ArrowDown'].includes(event.key)) {
|
|
1076
|
-
let hS = this.hS;
|
|
1075
|
+
let hS = this.hS; /* eslint-disable-line prefer-destructuring */
|
|
1077
1076
|
switch (event.key) {
|
|
1078
1077
|
case 'ArrowUp':
|
|
1079
1078
|
hS += event.shiftKey ? 0.002 : 0.02;
|
|
@@ -1327,7 +1326,7 @@ export default class SDTModel extends DetectableElement {
|
|
|
1327
1326
|
);
|
|
1328
1327
|
return (time) => {
|
|
1329
1328
|
element.d = interpolateD(time);
|
|
1330
|
-
d3.select(element).text(
|
|
1329
|
+
d3.select(element).text(d3.format('.3')(element.d));
|
|
1331
1330
|
};
|
|
1332
1331
|
});
|
|
1333
1332
|
// EXIT
|
|
@@ -1385,7 +1384,7 @@ export default class SDTModel extends DetectableElement {
|
|
|
1385
1384
|
);
|
|
1386
1385
|
return (time) => {
|
|
1387
1386
|
element.c = interpolateC(time);
|
|
1388
|
-
d3.select(element).text(
|
|
1387
|
+
d3.select(element).text(d3.format('.3')(element.c));
|
|
1389
1388
|
};
|
|
1390
1389
|
});
|
|
1391
1390
|
// EXIT
|
|
@@ -1451,7 +1450,7 @@ export default class SDTModel extends DetectableElement {
|
|
|
1451
1450
|
);
|
|
1452
1451
|
return (time) => {
|
|
1453
1452
|
element.s = interpolateS(time);
|
|
1454
|
-
d3.select(element).text(
|
|
1453
|
+
d3.select(element).text(d3.format('.3')(element.s));
|
|
1455
1454
|
};
|
|
1456
1455
|
});
|
|
1457
1456
|
// EXIT
|
|
@@ -1467,8 +1466,7 @@ export default class SDTModel extends DetectableElement {
|
|
|
1467
1466
|
thresholdEnter.append('line')
|
|
1468
1467
|
.classed('line', true);
|
|
1469
1468
|
thresholdEnter.append('circle')
|
|
1470
|
-
.classed('handle', true)
|
|
1471
|
-
.attr('r', 6); /* HACK: Firefox does not support CSS SVG Geometry Properties */
|
|
1469
|
+
.classed('handle', true);
|
|
1472
1470
|
// MERGE
|
|
1473
1471
|
const thresholdMerge = thresholdEnter.merge(thresholdUpdate)
|
|
1474
1472
|
.attr('tabindex', this.interactive ? 0 : null)
|
|
@@ -1482,7 +1480,7 @@ export default class SDTModel extends DetectableElement {
|
|
|
1482
1480
|
.call(dragThreshold)
|
|
1483
1481
|
.on('keydown', (event) => {
|
|
1484
1482
|
if (['ArrowRight', 'ArrowLeft'].includes(event.key)) {
|
|
1485
|
-
let l = this.l;
|
|
1483
|
+
let l = this.l; /* eslint-disable-line prefer-destructuring */
|
|
1486
1484
|
switch (event.key) {
|
|
1487
1485
|
case 'ArrowRight':
|
|
1488
1486
|
l += event.shiftKey ? 0.01 : 0.1;
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
|
|
2
|
+
import {html} from 'lit';
|
|
3
|
+
|
|
4
|
+
import '@decidables/decidables-elements/spinner';
|
|
5
|
+
import SDTMath from '@decidables/detectable-math';
|
|
6
|
+
|
|
7
|
+
import SDTEquation from './sdt-equation';
|
|
8
|
+
|
|
9
|
+
/*
|
|
10
|
+
SDTEquationHFa2Ppv element
|
|
11
|
+
<sdt-equation-hm2hr>
|
|
12
|
+
|
|
13
|
+
Attributes:
|
|
14
|
+
Hits; Misses; Hit Rate;
|
|
15
|
+
*/
|
|
16
|
+
export default class SDTEquationHFa2Ppv extends SDTEquation {
|
|
17
|
+
static get properties() {
|
|
18
|
+
return {
|
|
19
|
+
h: {
|
|
20
|
+
attribute: 'hits',
|
|
21
|
+
type: Number,
|
|
22
|
+
reflect: true,
|
|
23
|
+
},
|
|
24
|
+
fa: {
|
|
25
|
+
attribute: 'false-alarms',
|
|
26
|
+
type: Number,
|
|
27
|
+
reflect: true,
|
|
28
|
+
},
|
|
29
|
+
ppv: {
|
|
30
|
+
attribute: false,
|
|
31
|
+
type: Number,
|
|
32
|
+
reflect: false,
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
constructor() {
|
|
38
|
+
super();
|
|
39
|
+
this.h = 0;
|
|
40
|
+
this.fa = 0;
|
|
41
|
+
this.alignState();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
alignState() {
|
|
45
|
+
this.ppv = SDTMath.hFa2Ppv(this.h, this.fa);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
sendEvent() {
|
|
49
|
+
this.dispatchEvent(new CustomEvent('sdt-equation-hfa2ppv-change', {
|
|
50
|
+
detail: {
|
|
51
|
+
h: this.h,
|
|
52
|
+
fa: this.fa,
|
|
53
|
+
ppv: this.ppv,
|
|
54
|
+
},
|
|
55
|
+
bubbles: true,
|
|
56
|
+
}));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
hInput(event) {
|
|
60
|
+
this.h = parseInt(event.target.value, 10);
|
|
61
|
+
this.alignState();
|
|
62
|
+
this.sendEvent();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
faInput(event) {
|
|
66
|
+
this.fa = parseInt(event.target.value, 10);
|
|
67
|
+
this.alignState();
|
|
68
|
+
this.sendEvent();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
render() {
|
|
72
|
+
this.alignState();
|
|
73
|
+
let h;
|
|
74
|
+
let fa;
|
|
75
|
+
let ppv;
|
|
76
|
+
if (this.numeric) {
|
|
77
|
+
h = html`
|
|
78
|
+
<decidables-spinner class="h" ?disabled=${!this.interactive} min="0" .value="${this.h}" @input=${this.hInput.bind(this)}>
|
|
79
|
+
<var>Hits</var>
|
|
80
|
+
</decidables-spinner>
|
|
81
|
+
`;
|
|
82
|
+
fa = html`
|
|
83
|
+
<decidables-spinner class="fa" ?disabled=${!this.interactive} min="0" .value="${this.fa}" @input=${this.faInput.bind(this)}>
|
|
84
|
+
<var>False Alarms</var>
|
|
85
|
+
</decidables-spinner>
|
|
86
|
+
`;
|
|
87
|
+
ppv = html`
|
|
88
|
+
<decidables-spinner class="ppv" disabled min="0" max="1" step=".001" .value="${+this.ppv.toFixed(3)}">
|
|
89
|
+
<var>Positive Predictive Value</var>
|
|
90
|
+
</decidables-spinner>
|
|
91
|
+
`;
|
|
92
|
+
} else {
|
|
93
|
+
h = html`<var class="h">Hits</var>`;
|
|
94
|
+
fa = html`<var class="fa">False Alarms</var>`;
|
|
95
|
+
ppv = html`<var class="ppv">Positive Predictive Value</var>`;
|
|
96
|
+
}
|
|
97
|
+
return html`
|
|
98
|
+
<div class="holder">
|
|
99
|
+
<table class="equation">
|
|
100
|
+
<tbody>
|
|
101
|
+
<tr>
|
|
102
|
+
<td rowspan="2">
|
|
103
|
+
${ppv}<span class="equals">=</span>
|
|
104
|
+
</td>
|
|
105
|
+
<td class="underline">
|
|
106
|
+
${h}
|
|
107
|
+
</td>
|
|
108
|
+
</tr>
|
|
109
|
+
<tr>
|
|
110
|
+
<td>
|
|
111
|
+
${h}<span class="plus">+</span>${fa}
|
|
112
|
+
</td>
|
|
113
|
+
</tr>
|
|
114
|
+
</tbody>
|
|
115
|
+
</table>
|
|
116
|
+
</div>
|
|
117
|
+
`;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
customElements.define('sdt-equation-hfa2ppv', SDTEquationHFa2Ppv);
|
|
@@ -149,7 +149,7 @@ export default class SDTEquationHMFaCr2Acc extends SDTEquation {
|
|
|
149
149
|
</tr>
|
|
150
150
|
<tr>
|
|
151
151
|
<td>
|
|
152
|
-
${h}<span class="plus">+</span>${
|
|
152
|
+
${h}<span class="plus">+</span>${cr}<span class="plus">+</span>${m}<span class="plus">+</span>${fa}
|
|
153
153
|
</td>
|
|
154
154
|
</tr>
|
|
155
155
|
</tbody>
|
package/src/equations/index.js
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
export {default as SDTEquationDC2Far} from './dc2far';
|
|
3
3
|
export {default as SDTEquationDC2Hr} from './dc2hr';
|
|
4
4
|
export {default as SDTEquationFaCr2Far} from './facr2far';
|
|
5
|
+
export {default as SDTEquationHFa2Ppv} from './hfa2ppv';
|
|
5
6
|
export {default as SDTEquationHM2Hr} from './hm2hr';
|
|
6
7
|
export {default as SDTEquationHMFaCr2Acc} from './hmfacr2acc';
|
|
7
8
|
export {default as SDTEquationHrFar2C} from './hrfar2c';
|
|
8
9
|
export {default as SDTEquationHrFar2D} from './hrfar2d';
|
|
10
|
+
export {default as SDTEquationMCr2Fomr} from './mcr2fomr';
|