@decidables/discountable-elements 0.3.9 → 0.4.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 +22 -0
- package/README.md +62 -5
- package/lib/discountableElements.esm.js +1112 -838
- package/lib/discountableElements.esm.js.map +1 -1
- package/lib/discountableElements.esm.min.js +283 -135
- package/lib/discountableElements.esm.min.js.map +1 -1
- package/lib/discountableElements.umd.js +1112 -837
- package/lib/discountableElements.umd.js.map +1 -1
- package/lib/discountableElements.umd.min.js +283 -135
- package/lib/discountableElements.umd.min.js.map +1 -1
- package/package.json +4 -4
- package/src/components/discountable-control.js +2 -2
- package/src/components/discountable-response.js +24 -0
- package/src/components/htd-calculation.js +44 -8
- package/src/components/htd-curves.js +179 -37
- package/src/components/htd-fit-worker.js +1 -1
- package/src/components/htd-fit.js +32 -4
- package/src/components/htd-parameters.js +112 -0
- package/src/components/index.js +1 -0
- package/src/equations/adk2v.js +30 -5
- package/src/examples/human.js +110 -64
- package/src/examples/interactive.js +18 -1
- package/src/examples/model.js +89 -65
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@decidables/discountable-elements",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "discountable-elements: Web Components for visualizing Hyperbolic Temporal Discounting",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"web component",
|
|
@@ -52,8 +52,8 @@
|
|
|
52
52
|
"gulp": "^5.0.0"
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
|
-
"@decidables/decidables-elements": "^0.
|
|
56
|
-
"@decidables/discountable-math": "^0.
|
|
55
|
+
"@decidables/decidables-elements": "^0.5.0",
|
|
56
|
+
"@decidables/discountable-math": "^0.2.0",
|
|
57
57
|
"@lit-labs/motion": "^1.0.7",
|
|
58
58
|
"@observablehq/plot": "^0.6.16",
|
|
59
59
|
"bayes.js": "https://github.com/akrawitz/bayes.js.git#commit=c7b091b75f85a86b6a3965a983b31e23d9ef456f",
|
|
@@ -61,5 +61,5 @@
|
|
|
61
61
|
"lit": "^3.2.1",
|
|
62
62
|
"regenerator-runtime": "^0.14.1"
|
|
63
63
|
},
|
|
64
|
-
"gitHead": "
|
|
64
|
+
"gitHead": "0320347d03a20fb478784fe296c82f11018c276b"
|
|
65
65
|
}
|
|
@@ -145,10 +145,10 @@ export default class DiscountableControl extends DiscountableElement {
|
|
|
145
145
|
render() {
|
|
146
146
|
return html`
|
|
147
147
|
<div class="holder">
|
|
148
|
-
${this.trials
|
|
148
|
+
${this.trials != null
|
|
149
149
|
? html`<decidables-slider min="1" max="100" step="1" .value=${this.trials} @change=${this.setTrials.bind(this)} @input=${this.setTrials.bind(this)}>Trials</decidables-slider>`
|
|
150
150
|
: html``}
|
|
151
|
-
${this.duration
|
|
151
|
+
${this.duration != null
|
|
152
152
|
? html`<decidables-slider min="10" max="4000" step="10" .value=${this.duration} @change=${this.setDuration.bind(this)} @input=${this.setDuration.bind(this)}>Duration</decidables-slider>`
|
|
153
153
|
: html``}
|
|
154
154
|
${this.run || this.pause || this.reset
|
|
@@ -117,6 +117,30 @@ export default class DiscountableResponse extends DiscountableElement {
|
|
|
117
117
|
this.response = undefined;
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
+
keydown(event) {
|
|
121
|
+
if (this.state === 'waiting') {
|
|
122
|
+
if (event.key === 'ArrowLeft') {
|
|
123
|
+
this.responded('first');
|
|
124
|
+
event.preventDefault();
|
|
125
|
+
} else if (event.key === 'ArrowRight') {
|
|
126
|
+
this.responded('second');
|
|
127
|
+
event.preventDefault();
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
connectedCallback() {
|
|
133
|
+
super.connectedCallback();
|
|
134
|
+
|
|
135
|
+
window.addEventListener('keydown', this.keydown.bind(this));
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
disconnectedCallback() {
|
|
139
|
+
window.removeEventListener('keydown', this.keydown.bind(this));
|
|
140
|
+
|
|
141
|
+
super.disconnectedCallback();
|
|
142
|
+
}
|
|
143
|
+
|
|
120
144
|
static get styles() {
|
|
121
145
|
return [
|
|
122
146
|
super.styles,
|
|
@@ -59,7 +59,7 @@ export default class HTDCalculation extends HTDEquation {
|
|
|
59
59
|
this.al = 50;
|
|
60
60
|
this.dl = 40;
|
|
61
61
|
|
|
62
|
-
this.k =
|
|
62
|
+
this.k = HTDMath.k.DEFAULT;
|
|
63
63
|
|
|
64
64
|
this.alignState();
|
|
65
65
|
}
|
|
@@ -149,26 +149,62 @@ export default class HTDCalculation extends HTDEquation {
|
|
|
149
149
|
let vl;
|
|
150
150
|
let vDiff;
|
|
151
151
|
if (this.numeric) {
|
|
152
|
-
as = html`<decidables-spinner class="a as"
|
|
152
|
+
as = html`<decidables-spinner class="a as"
|
|
153
|
+
?disabled=${!this.interactive}
|
|
154
|
+
step="1"
|
|
155
|
+
.value=${this.as}
|
|
156
|
+
@input=${this.asInput.bind(this)}
|
|
157
|
+
>
|
|
153
158
|
<var class="math-var">A<sub class="subscript">ss</sub></var>
|
|
154
159
|
</decidables-spinner>`;
|
|
155
|
-
ds = html`<decidables-spinner class="d ds"
|
|
160
|
+
ds = html`<decidables-spinner class="d ds"
|
|
161
|
+
?disabled=${!this.interactive}
|
|
162
|
+
min="0"
|
|
163
|
+
step="1"
|
|
164
|
+
.value=${this.ds}
|
|
165
|
+
@input=${this.dsInput.bind(this)}
|
|
166
|
+
>
|
|
156
167
|
<var class="math-var">D<sub class="subscript">ss</sub></var>
|
|
157
168
|
</decidables-spinner>`;
|
|
158
|
-
al = html`<decidables-spinner class="a al"
|
|
169
|
+
al = html`<decidables-spinner class="a al"
|
|
170
|
+
?disabled=${!this.interactive}
|
|
171
|
+
step="1"
|
|
172
|
+
.value=${this.al}
|
|
173
|
+
@input=${this.alInput.bind(this)}
|
|
174
|
+
>
|
|
159
175
|
<var class="math-var">A<sub class="subscript">ll</sub></var>
|
|
160
176
|
</decidables-spinner>`;
|
|
161
|
-
dl = html`<decidables-spinner class="d dl"
|
|
177
|
+
dl = html`<decidables-spinner class="d dl"
|
|
178
|
+
?disabled=${!this.interactive}
|
|
179
|
+
min="0"
|
|
180
|
+
step="1"
|
|
181
|
+
.value=${this.dl}
|
|
182
|
+
@input=${this.dlInput.bind(this)}
|
|
183
|
+
>
|
|
162
184
|
<var class="math-var">D<sub class="subscript">ll</sub></var>
|
|
163
185
|
</decidables-spinner>`;
|
|
164
|
-
|
|
186
|
+
|
|
187
|
+
k = html`<decidables-spinner class="k"
|
|
188
|
+
?disabled=${!this.interactive}
|
|
189
|
+
min=${HTDMath.k.MIN}
|
|
190
|
+
max=${HTDMath.k.MAX}
|
|
191
|
+
step=${HTDMath.k.STEP}
|
|
192
|
+
.value=${+this.k.toFixed(3)}
|
|
193
|
+
@input=${this.kInput.bind(this)}
|
|
194
|
+
>
|
|
165
195
|
<var class="math-var">k</var>
|
|
166
196
|
</decidables-spinner>`;
|
|
167
197
|
|
|
168
|
-
vs = html`<decidables-spinner class="v vs"
|
|
198
|
+
vs = html`<decidables-spinner class="v vs"
|
|
199
|
+
disabled
|
|
200
|
+
.value=${+this.vs.toFixed(2)}
|
|
201
|
+
>
|
|
169
202
|
<var class="math-var">V<sub class="subscript">ss</sub></var>
|
|
170
203
|
</decidables-spinner>`;
|
|
171
|
-
vl = html`<decidables-spinner class="v vl"
|
|
204
|
+
vl = html`<decidables-spinner class="v vl"
|
|
205
|
+
disabled
|
|
206
|
+
.value=${+this.vl.toFixed(2)}
|
|
207
|
+
>
|
|
172
208
|
<var class="math-var">V<sub class="subscript">ll</sub></var>
|
|
173
209
|
</decidables-spinner>`;
|
|
174
210
|
vDiff = html`${(this.vDiff > 0)
|
|
@@ -74,18 +74,12 @@ export default class HTDCurves extends DecidablesMixinResizeable(DiscountableEle
|
|
|
74
74
|
step: 1,
|
|
75
75
|
round: Math.round,
|
|
76
76
|
},
|
|
77
|
-
discount: {
|
|
78
|
-
min: 0,
|
|
79
|
-
max: 100,
|
|
80
|
-
step: 0.001,
|
|
81
|
-
round: (k) => { return +k.toFixed(3); },
|
|
82
|
-
},
|
|
83
77
|
};
|
|
84
78
|
|
|
85
79
|
this.a = null;
|
|
86
80
|
this.d = null;
|
|
87
81
|
this.label = '';
|
|
88
|
-
this.k =
|
|
82
|
+
this.k = HTDMath.k.DEFAULT;
|
|
89
83
|
|
|
90
84
|
this.options = [
|
|
91
85
|
{
|
|
@@ -353,6 +347,19 @@ export default class HTDCurves extends DecidablesMixinResizeable(DiscountableEle
|
|
|
353
347
|
/* HACK: This gets Safari to correctly apply the filter! */
|
|
354
348
|
stroke: #0000ff;
|
|
355
349
|
}
|
|
350
|
+
|
|
351
|
+
/* Make larger targets for touch users */
|
|
352
|
+
.interactive .touch {
|
|
353
|
+
stroke: #000000;
|
|
354
|
+
stroke-opacity: 0;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
@media (pointer: coarse) {
|
|
358
|
+
.interactive .touch {
|
|
359
|
+
stroke-linecap: round;
|
|
360
|
+
stroke-width: 12;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
356
363
|
`,
|
|
357
364
|
];
|
|
358
365
|
}
|
|
@@ -549,9 +556,33 @@ export default class HTDCurves extends DecidablesMixinResizeable(DiscountableEle
|
|
|
549
556
|
const optionEnter = optionUpdate.enter().append('g')
|
|
550
557
|
.classed('option', true);
|
|
551
558
|
// Curve
|
|
552
|
-
optionEnter.append('
|
|
559
|
+
const curveEnter = optionEnter.append('g')
|
|
553
560
|
.classed('curve', true)
|
|
554
|
-
.attr('clip-path', 'url(#clip-htd-curves)')
|
|
561
|
+
.attr('clip-path', 'url(#clip-htd-curves)');
|
|
562
|
+
curveEnter.append('path')
|
|
563
|
+
.classed('path', true)
|
|
564
|
+
.attr('d', (datum) => {
|
|
565
|
+
const curve = d3.range(xScale(datum.d), xScale(0), -1).map((range) => {
|
|
566
|
+
return {
|
|
567
|
+
d: xScale.invert(range),
|
|
568
|
+
v: HTDMath.adk2v(
|
|
569
|
+
datum.a,
|
|
570
|
+
datum.d - xScale.invert(range),
|
|
571
|
+
this.k,
|
|
572
|
+
),
|
|
573
|
+
};
|
|
574
|
+
});
|
|
575
|
+
return line(curve);
|
|
576
|
+
})
|
|
577
|
+
.attr('stroke-dasharray', (datum, index, nodes) => {
|
|
578
|
+
if (datum.trial) {
|
|
579
|
+
const length = nodes[index].getTotalLength();
|
|
580
|
+
return `0,${length}`;
|
|
581
|
+
}
|
|
582
|
+
return 'none';
|
|
583
|
+
});
|
|
584
|
+
curveEnter.append('path')
|
|
585
|
+
.classed('path touch', true)
|
|
555
586
|
.attr('d', (datum) => {
|
|
556
587
|
const curve = d3.range(xScale(datum.d), xScale(0), -1).map((range) => {
|
|
557
588
|
return {
|
|
@@ -573,8 +604,23 @@ export default class HTDCurves extends DecidablesMixinResizeable(DiscountableEle
|
|
|
573
604
|
return 'none';
|
|
574
605
|
});
|
|
575
606
|
// Bar
|
|
576
|
-
optionEnter.append('
|
|
577
|
-
.classed('bar', true)
|
|
607
|
+
const barEnter = optionEnter.append('g')
|
|
608
|
+
.classed('bar', true);
|
|
609
|
+
barEnter.append('line')
|
|
610
|
+
.classed('line', true)
|
|
611
|
+
.attr('x1', (datum) => { return xScale(datum.d); })
|
|
612
|
+
.attr('x2', (datum) => { return xScale(datum.d); })
|
|
613
|
+
.attr('y1', yScale(0))
|
|
614
|
+
.attr('y2', (datum) => { return yScale(datum.a); })
|
|
615
|
+
.attr('stroke-dasharray', (datum, index, nodes) => {
|
|
616
|
+
if (datum.trial) {
|
|
617
|
+
const length = nodes[index].getTotalLength();
|
|
618
|
+
return `0,${length}`;
|
|
619
|
+
}
|
|
620
|
+
return 'none';
|
|
621
|
+
});
|
|
622
|
+
barEnter.append('line')
|
|
623
|
+
.classed('line touch', true)
|
|
578
624
|
.attr('x1', (datum) => { return xScale(datum.d); })
|
|
579
625
|
.attr('x2', (datum) => { return xScale(datum.d); })
|
|
580
626
|
.attr('y1', yScale(0))
|
|
@@ -599,7 +645,7 @@ export default class HTDCurves extends DecidablesMixinResizeable(DiscountableEle
|
|
|
599
645
|
return 1;
|
|
600
646
|
});
|
|
601
647
|
pointEnter.append('circle')
|
|
602
|
-
.classed('mark', true);
|
|
648
|
+
.classed('mark touch', true);
|
|
603
649
|
pointEnter.append('text')
|
|
604
650
|
.classed('label', true);
|
|
605
651
|
// MERGE
|
|
@@ -609,7 +655,7 @@ export default class HTDCurves extends DecidablesMixinResizeable(DiscountableEle
|
|
|
609
655
|
// Curve
|
|
610
656
|
optionMerge
|
|
611
657
|
.filter((datum, index, nodes) => {
|
|
612
|
-
return (this.interactive && !nodes[index].
|
|
658
|
+
return (this.interactive && !d3.select(nodes[index]).select('.curve').classed('interactive'));
|
|
613
659
|
})
|
|
614
660
|
.select('.curve')
|
|
615
661
|
.classed('interactive', true)
|
|
@@ -641,11 +687,11 @@ export default class HTDCurves extends DecidablesMixinResizeable(DiscountableEle
|
|
|
641
687
|
? datum.a
|
|
642
688
|
: dragV;
|
|
643
689
|
const k = HTDMath.adv2k(datum.a, d, v);
|
|
644
|
-
this.k = (k <
|
|
645
|
-
?
|
|
646
|
-
: (k >
|
|
647
|
-
?
|
|
648
|
-
:
|
|
690
|
+
this.k = (k < HTDMath.k.MIN)
|
|
691
|
+
? HTDMath.k.MIN
|
|
692
|
+
: (k > HTDMath.k.MAX)
|
|
693
|
+
? HTDMath.k.MAX
|
|
694
|
+
: k;
|
|
649
695
|
this.alignState();
|
|
650
696
|
this.requestUpdate();
|
|
651
697
|
this.dispatchEvent(new CustomEvent('htd-curves-change', {
|
|
@@ -666,26 +712,26 @@ export default class HTDCurves extends DecidablesMixinResizeable(DiscountableEle
|
|
|
666
712
|
// Keyboard interaction
|
|
667
713
|
.on('keydown', (event, datum) => {
|
|
668
714
|
if (['ArrowUp', 'ArrowDown', 'ArrowRight', 'ArrowLeft'].includes(event.key)) {
|
|
669
|
-
let
|
|
715
|
+
let {k} = this;
|
|
670
716
|
switch (event.key) {
|
|
671
717
|
case 'ArrowUp':
|
|
672
718
|
case 'ArrowLeft':
|
|
673
|
-
|
|
719
|
+
k *= event.shiftKey ? 0.95 : 0.85;
|
|
674
720
|
break;
|
|
675
721
|
case 'ArrowDown':
|
|
676
722
|
case 'ArrowRight':
|
|
677
|
-
|
|
723
|
+
k *= event.shiftKey ? (1 / 0.95) : (1 / 0.85);
|
|
678
724
|
break;
|
|
679
725
|
default:
|
|
680
726
|
// no-op
|
|
681
727
|
}
|
|
682
|
-
|
|
683
|
-
?
|
|
684
|
-
: (
|
|
685
|
-
?
|
|
686
|
-
:
|
|
687
|
-
if (
|
|
688
|
-
this.k =
|
|
728
|
+
k = (k < HTDMath.k.MIN)
|
|
729
|
+
? HTDMath.k.MIN
|
|
730
|
+
: (k > HTDMath.k.MAX)
|
|
731
|
+
? HTDMath.k.MAX
|
|
732
|
+
: k;
|
|
733
|
+
if (k !== this.k) {
|
|
734
|
+
this.k = k;
|
|
689
735
|
this.alignState();
|
|
690
736
|
this.requestUpdate();
|
|
691
737
|
this.dispatchEvent(new CustomEvent('htd-curves-change', {
|
|
@@ -705,7 +751,7 @@ export default class HTDCurves extends DecidablesMixinResizeable(DiscountableEle
|
|
|
705
751
|
// Bar
|
|
706
752
|
optionMerge
|
|
707
753
|
.filter((datum, index, nodes) => {
|
|
708
|
-
return (this.interactive && !datum.trial && !nodes[index].
|
|
754
|
+
return (this.interactive && !datum.trial && !d3.select(nodes[index]).select('.bar').classed('interactive'));
|
|
709
755
|
})
|
|
710
756
|
.select('.bar')
|
|
711
757
|
.classed('interactive', true)
|
|
@@ -793,7 +839,7 @@ export default class HTDCurves extends DecidablesMixinResizeable(DiscountableEle
|
|
|
793
839
|
// Point
|
|
794
840
|
optionMerge
|
|
795
841
|
.filter((datum, index, nodes) => {
|
|
796
|
-
return (this.interactive && !datum.trial && !nodes[index].
|
|
842
|
+
return (this.interactive && !datum.trial && !d3.select(nodes[index]).select('.point').classed('interactive'));
|
|
797
843
|
})
|
|
798
844
|
.select('.point')
|
|
799
845
|
.classed('interactive', true)
|
|
@@ -883,7 +929,7 @@ export default class HTDCurves extends DecidablesMixinResizeable(DiscountableEle
|
|
|
883
929
|
// Curve
|
|
884
930
|
optionMerge
|
|
885
931
|
.filter((datum, index, nodes) => {
|
|
886
|
-
return (!this.interactive && nodes[index].
|
|
932
|
+
return (!this.interactive && d3.select(nodes[index]).select('.curve').classed('interactive'));
|
|
887
933
|
})
|
|
888
934
|
.select('.curve')
|
|
889
935
|
.classed('interactive', false)
|
|
@@ -893,7 +939,7 @@ export default class HTDCurves extends DecidablesMixinResizeable(DiscountableEle
|
|
|
893
939
|
// Bar
|
|
894
940
|
optionMerge
|
|
895
941
|
.filter((datum, index, nodes) => {
|
|
896
|
-
return ((!this.interactive || datum.trial) && nodes[index].
|
|
942
|
+
return ((!this.interactive || datum.trial) && d3.select(nodes[index]).select('.bar').classed('interactive'));
|
|
897
943
|
})
|
|
898
944
|
.select('.bar')
|
|
899
945
|
.classed('interactive', false)
|
|
@@ -903,7 +949,7 @@ export default class HTDCurves extends DecidablesMixinResizeable(DiscountableEle
|
|
|
903
949
|
// Point
|
|
904
950
|
optionMerge
|
|
905
951
|
.filter((datum, index, nodes) => {
|
|
906
|
-
return ((!this.interactive || datum.trial) && nodes[index].
|
|
952
|
+
return ((!this.interactive || datum.trial) && d3.select(nodes[index]).select('.point').classed('interactive'));
|
|
907
953
|
})
|
|
908
954
|
.select('.point')
|
|
909
955
|
.classed('interactive', false)
|
|
@@ -917,7 +963,7 @@ export default class HTDCurves extends DecidablesMixinResizeable(DiscountableEle
|
|
|
917
963
|
.filter((datum) => {
|
|
918
964
|
return (datum.new);
|
|
919
965
|
})
|
|
920
|
-
.select('.curve').transition()
|
|
966
|
+
.select('.curve .path').transition()
|
|
921
967
|
.duration(transitionDuration)
|
|
922
968
|
.delay(transitionDuration + transitionDuration / 10)
|
|
923
969
|
.ease(d3.easeLinear)
|
|
@@ -939,12 +985,35 @@ export default class HTDCurves extends DecidablesMixinResizeable(DiscountableEle
|
|
|
939
985
|
bubbles: true,
|
|
940
986
|
}));
|
|
941
987
|
});
|
|
988
|
+
optionMerge
|
|
989
|
+
.filter((datum) => {
|
|
990
|
+
return (datum.new);
|
|
991
|
+
})
|
|
992
|
+
.select('.curve .path.touch').transition()
|
|
993
|
+
.duration(transitionDuration)
|
|
994
|
+
.delay(transitionDuration + transitionDuration / 10)
|
|
995
|
+
.ease(d3.easeLinear)
|
|
996
|
+
.attrTween('stroke-dasharray', (datum, index, nodes) => {
|
|
997
|
+
const length = nodes[index].getTotalLength();
|
|
998
|
+
return d3.interpolate(`0,${length}`, `${length},${0}`);
|
|
999
|
+
});
|
|
942
1000
|
// Bar
|
|
943
1001
|
optionMerge
|
|
944
1002
|
.filter((datum) => {
|
|
945
1003
|
return (datum.new);
|
|
946
1004
|
})
|
|
947
|
-
.select('.bar').transition()
|
|
1005
|
+
.select('.bar .line').transition()
|
|
1006
|
+
.duration(transitionDuration)
|
|
1007
|
+
.ease(d3.easeLinear)
|
|
1008
|
+
.attrTween('stroke-dasharray', (datum, index, nodes) => {
|
|
1009
|
+
const length = nodes[index].getTotalLength();
|
|
1010
|
+
return d3.interpolate(`0,${length}`, `${length},${length}`);
|
|
1011
|
+
});
|
|
1012
|
+
optionMerge
|
|
1013
|
+
.filter((datum) => {
|
|
1014
|
+
return (datum.new);
|
|
1015
|
+
})
|
|
1016
|
+
.select('.bar .line.touch').transition()
|
|
948
1017
|
.duration(transitionDuration)
|
|
949
1018
|
.ease(d3.easeLinear)
|
|
950
1019
|
.attrTween('stroke-dasharray', (datum, index, nodes) => {
|
|
@@ -963,7 +1032,7 @@ export default class HTDCurves extends DecidablesMixinResizeable(DiscountableEle
|
|
|
963
1032
|
.attrTween('opacity', () => { return d3.interpolate(0, 1); });
|
|
964
1033
|
|
|
965
1034
|
// All options
|
|
966
|
-
optionUpdate.select('.curve').transition()
|
|
1035
|
+
optionUpdate.select('.curve .path').transition()
|
|
967
1036
|
.duration(this.drag
|
|
968
1037
|
? 0
|
|
969
1038
|
: (this.firstUpdate
|
|
@@ -996,7 +1065,80 @@ export default class HTDCurves extends DecidablesMixinResizeable(DiscountableEle
|
|
|
996
1065
|
return line(curve);
|
|
997
1066
|
};
|
|
998
1067
|
});
|
|
999
|
-
optionUpdate.select('.
|
|
1068
|
+
optionUpdate.select('.curve .path.touch').transition()
|
|
1069
|
+
.duration(this.drag
|
|
1070
|
+
? 0
|
|
1071
|
+
: (this.firstUpdate
|
|
1072
|
+
? (transitionDuration * 2)
|
|
1073
|
+
: transitionDuration))
|
|
1074
|
+
.ease(d3.easeCubicOut)
|
|
1075
|
+
.attrTween('d', (datum, index, elements) => {
|
|
1076
|
+
const element = elements[index];
|
|
1077
|
+
const interpolateA = d3.interpolate(
|
|
1078
|
+
(element.a !== undefined) ? element.a : datum.a,
|
|
1079
|
+
datum.a,
|
|
1080
|
+
);
|
|
1081
|
+
const interpolateD = d3.interpolate(
|
|
1082
|
+
(element.d !== undefined) ? element.d : datum.d,
|
|
1083
|
+
datum.d,
|
|
1084
|
+
);
|
|
1085
|
+
return (time) => {
|
|
1086
|
+
element.a = interpolateA(time);
|
|
1087
|
+
element.d = interpolateD(time);
|
|
1088
|
+
const curve = d3.range(xScale(element.d), xScale(0), -1).map((range) => {
|
|
1089
|
+
return {
|
|
1090
|
+
d: xScale.invert(range),
|
|
1091
|
+
v: HTDMath.adk2v(
|
|
1092
|
+
element.a,
|
|
1093
|
+
element.d - xScale.invert(range),
|
|
1094
|
+
this.k,
|
|
1095
|
+
),
|
|
1096
|
+
};
|
|
1097
|
+
});
|
|
1098
|
+
return line(curve);
|
|
1099
|
+
};
|
|
1100
|
+
});
|
|
1101
|
+
optionUpdate.select('.bar .line').transition()
|
|
1102
|
+
.duration(this.drag
|
|
1103
|
+
? 0
|
|
1104
|
+
: (this.firstUpdate
|
|
1105
|
+
? (transitionDuration * 2)
|
|
1106
|
+
: transitionDuration))
|
|
1107
|
+
.ease(d3.easeCubicOut)
|
|
1108
|
+
.attrTween('x1', (datum, index, elements) => {
|
|
1109
|
+
const element = elements[index];
|
|
1110
|
+
const interpolateD = d3.interpolate(
|
|
1111
|
+
(element.d !== undefined) ? element.d : datum.d,
|
|
1112
|
+
datum.d,
|
|
1113
|
+
);
|
|
1114
|
+
return (time) => {
|
|
1115
|
+
element.d = interpolateD(time);
|
|
1116
|
+
return `${xScale(element.d)}`;
|
|
1117
|
+
};
|
|
1118
|
+
})
|
|
1119
|
+
.attrTween('x2', (datum, index, elements) => {
|
|
1120
|
+
const element = elements[index];
|
|
1121
|
+
const interpolateD = d3.interpolate(
|
|
1122
|
+
(element.d !== undefined) ? element.d : datum.d,
|
|
1123
|
+
datum.d,
|
|
1124
|
+
);
|
|
1125
|
+
return (time) => {
|
|
1126
|
+
element.d = interpolateD(time);
|
|
1127
|
+
return `${xScale(element.d)}`;
|
|
1128
|
+
};
|
|
1129
|
+
})
|
|
1130
|
+
.attrTween('y2', (datum, index, elements) => {
|
|
1131
|
+
const element = elements[index];
|
|
1132
|
+
const interpolateA = d3.interpolate(
|
|
1133
|
+
(element.a !== undefined) ? element.a : datum.a,
|
|
1134
|
+
datum.a,
|
|
1135
|
+
);
|
|
1136
|
+
return (time) => {
|
|
1137
|
+
element.a = interpolateA(time);
|
|
1138
|
+
return `${yScale(element.a)}`;
|
|
1139
|
+
};
|
|
1140
|
+
});
|
|
1141
|
+
optionUpdate.select('.bar .line.touch').transition()
|
|
1000
1142
|
.duration(this.drag
|
|
1001
1143
|
? 0
|
|
1002
1144
|
: (this.firstUpdate
|
|
@@ -10,7 +10,7 @@ import HTDMath from '@decidables/discountable-math';
|
|
|
10
10
|
|
|
11
11
|
self.onmessage = (event) => {
|
|
12
12
|
const params = {
|
|
13
|
-
k: {type: 'real', lower:
|
|
13
|
+
k: {type: 'real', lower: HTDMath.k.MIN, upper: HTDMath.k.MAX},
|
|
14
14
|
luce: {type: 'real', lower: 0, upper: 100},
|
|
15
15
|
};
|
|
16
16
|
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
import {html, css} from 'lit';
|
|
3
3
|
import * as Plot from '@observablehq/plot';
|
|
4
4
|
|
|
5
|
+
import HTDMath from '@decidables/discountable-math';
|
|
6
|
+
|
|
5
7
|
// Special Web Worker import for rollup-plugin-web-worker-loader
|
|
6
8
|
import HTDFitWorker from 'web-worker:./htd-fit-worker'; /* eslint-disable-line import/no-unresolved */
|
|
7
9
|
|
|
@@ -24,7 +26,7 @@ export default class HTDFit extends DiscountableElement {
|
|
|
24
26
|
constructor() {
|
|
25
27
|
super();
|
|
26
28
|
|
|
27
|
-
this.k =
|
|
29
|
+
this.k = HTDMath.k.DEFAULT;
|
|
28
30
|
|
|
29
31
|
this.choices = [];
|
|
30
32
|
this.samples = null;
|
|
@@ -136,7 +138,7 @@ export default class HTDFit extends DiscountableElement {
|
|
|
136
138
|
<div>
|
|
137
139
|
<div>After ${this.choices.length} trials:</div>
|
|
138
140
|
<div>Current:
|
|
139
|
-
<var class="math-var k">k</var> = ${this.k.toFixed(
|
|
141
|
+
<var class="math-var k">k</var> = ${this.k.toFixed(3)}
|
|
140
142
|
</div>
|
|
141
143
|
<div class="param">
|
|
142
144
|
<div class="trace k"></div>
|
|
@@ -161,7 +163,22 @@ export default class HTDFit extends DiscountableElement {
|
|
|
161
163
|
marks: [
|
|
162
164
|
Plot.rectY(
|
|
163
165
|
this.samples[param],
|
|
164
|
-
Plot.binX(
|
|
166
|
+
Plot.binX(
|
|
167
|
+
{y: 'count'},
|
|
168
|
+
{x: Plot.identity},
|
|
169
|
+
),
|
|
170
|
+
),
|
|
171
|
+
Plot.rectY(
|
|
172
|
+
this.samples[param],
|
|
173
|
+
Plot.pointerX(Plot.binX(
|
|
174
|
+
{y: 'count'},
|
|
175
|
+
{
|
|
176
|
+
x: Plot.identity,
|
|
177
|
+
stroke: 'black',
|
|
178
|
+
fill: 'white',
|
|
179
|
+
tip: true,
|
|
180
|
+
},
|
|
181
|
+
)),
|
|
165
182
|
),
|
|
166
183
|
],
|
|
167
184
|
}),
|
|
@@ -170,7 +187,7 @@ export default class HTDFit extends DiscountableElement {
|
|
|
170
187
|
this.shadowRoot.querySelector(`.trace.${param}`).replaceChildren(
|
|
171
188
|
Plot.plot({
|
|
172
189
|
title: `Traceplot of ${param}`,
|
|
173
|
-
x: {label: '
|
|
190
|
+
x: {label: 'Sample'},
|
|
174
191
|
y: {label: `${param}`},
|
|
175
192
|
width: 320,
|
|
176
193
|
height: 240,
|
|
@@ -179,6 +196,17 @@ export default class HTDFit extends DiscountableElement {
|
|
|
179
196
|
Plot.lineY(
|
|
180
197
|
this.samples[param],
|
|
181
198
|
),
|
|
199
|
+
Plot.dot(
|
|
200
|
+
this.samples[param],
|
|
201
|
+
Plot.pointer({
|
|
202
|
+
x: Plot.indexOf,
|
|
203
|
+
y: Plot.identity,
|
|
204
|
+
stroke: 'black',
|
|
205
|
+
fill: 'white',
|
|
206
|
+
r: 4,
|
|
207
|
+
tip: true,
|
|
208
|
+
}),
|
|
209
|
+
),
|
|
182
210
|
],
|
|
183
211
|
}),
|
|
184
212
|
);
|