linny-r 1.6.2 → 1.6.3
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/package.json +1 -1
- package/static/index.html +18 -0
- package/static/linny-r.css +39 -2
- package/static/scripts/linny-r-ctrl.js +8 -1
- package/static/scripts/linny-r-gui-chart-manager.js +80 -16
- package/static/scripts/linny-r-gui-controller.js +3 -3
- package/static/scripts/linny-r-gui-equation-manager.js +19 -0
- package/static/scripts/linny-r-gui-expression-editor.js +1 -1
- package/static/scripts/linny-r-gui-monitor.js +1 -0
- package/static/scripts/linny-r-model.js +96 -50
- package/static/scripts/linny-r-vm.js +9 -8
package/package.json
CHANGED
package/static/index.html
CHANGED
@@ -1765,6 +1765,8 @@ NOTE: * and ? will be interpreted as wildcards"
|
|
1765
1765
|
<img id="eq-delete-btn" class="btn disab" src="images/delete.png"
|
1766
1766
|
title="Delete selected equation">
|
1767
1767
|
</div>
|
1768
|
+
<img id="equation-outcome" class="not-selected" src="images/outcome.png"
|
1769
|
+
title="Click to consider/ignore selected equation as experiment outcome">
|
1768
1770
|
<div id="equation-scroll-area">
|
1769
1771
|
<table id="equation-table">
|
1770
1772
|
</table>
|
@@ -2011,6 +2013,22 @@ NOTE: * and ? will be interpreted as wildcards"
|
|
2011
2013
|
</div>
|
2012
2014
|
</div>
|
2013
2015
|
|
2016
|
+
<!-- the ADD WILDCARD VARIABLES dialog allows selecting wildcard
|
2017
|
+
variables from a list to be added to the current chart -->
|
2018
|
+
<div id="add-wildcard-variables-modal" class="modal">
|
2019
|
+
<div id="add-wildcard-variables-dlg" class="inp-dlg">
|
2020
|
+
<div class="dlg-title">
|
2021
|
+
Add wildcard matches
|
2022
|
+
<img class="cancel-btn" src="images/cancel.png">
|
2023
|
+
<img class="ok-btn" src="images/ok.png">
|
2024
|
+
</div>
|
2025
|
+
<div id="add-wildcard-variables-scroll-area">
|
2026
|
+
<table id="add-wildcard-variables-table">
|
2027
|
+
</table>
|
2028
|
+
</div>
|
2029
|
+
</div>
|
2030
|
+
</div>
|
2031
|
+
|
2014
2032
|
<!-- the RENAME CHART dialog prompts for the new title for a chart -->
|
2015
2033
|
<div id="rename-chart-modal" class="modal">
|
2016
2034
|
<div id="rename-chart-dlg" class="inp-dlg">
|
package/static/linny-r.css
CHANGED
@@ -2161,7 +2161,8 @@ div.io-box {
|
|
2161
2161
|
cursor: pointer;
|
2162
2162
|
}
|
2163
2163
|
|
2164
|
-
#dataset-outcome.not-selected
|
2164
|
+
#dataset-outcome.not-selected,
|
2165
|
+
#equation-outcome.not-selected {
|
2165
2166
|
filter: saturate(0) contrast(50%) brightness(170%);
|
2166
2167
|
}
|
2167
2168
|
|
@@ -2273,10 +2274,12 @@ div.modif::before {
|
|
2273
2274
|
margin-right: 2px;
|
2274
2275
|
}
|
2275
2276
|
|
2276
|
-
div.outcome::before
|
2277
|
+
div.outcome::before,
|
2278
|
+
span.outcome::before {
|
2277
2279
|
content: ' \25C8';
|
2278
2280
|
color: #b00080;
|
2279
2281
|
margin-right: 1px;
|
2282
|
+
font-style: normal;
|
2280
2283
|
}
|
2281
2284
|
|
2282
2285
|
div.array::before {
|
@@ -2449,6 +2452,15 @@ td.equation-expression {
|
|
2449
2452
|
border-top: 1px solid Silver;
|
2450
2453
|
}
|
2451
2454
|
|
2455
|
+
#equation-outcome {
|
2456
|
+
position: absolute;
|
2457
|
+
top: 25px;
|
2458
|
+
right: 4px;
|
2459
|
+
width: 16px;
|
2460
|
+
height: 16px;
|
2461
|
+
cursor: pointer;
|
2462
|
+
}
|
2463
|
+
|
2452
2464
|
/* NOTE: Rename equation modal must be above Edit variable modal */
|
2453
2465
|
#rename-equation-modal {
|
2454
2466
|
z-index: 110;
|
@@ -3098,6 +3110,31 @@ img.v-disab {
|
|
3098
3110
|
width: calc(100% - 6px);
|
3099
3111
|
}
|
3100
3112
|
|
3113
|
+
#add-wildcard-variables-dlg {
|
3114
|
+
width: 250px;
|
3115
|
+
min-height: 42px;
|
3116
|
+
max-height: 50vh;
|
3117
|
+
}
|
3118
|
+
|
3119
|
+
#add-wildcard-variables-scroll-area {
|
3120
|
+
position: absolute;
|
3121
|
+
top: 22px;
|
3122
|
+
left: 2px;
|
3123
|
+
width: calc(100% - 4px);
|
3124
|
+
height: calc(100% - 26px);
|
3125
|
+
overflow-y: auto;
|
3126
|
+
border-top: 1px solid Silver;
|
3127
|
+
}
|
3128
|
+
|
3129
|
+
#add-wildcard-variables-table {
|
3130
|
+
width: 100%;
|
3131
|
+
border-collapse: collapse;
|
3132
|
+
line-height: 16px;
|
3133
|
+
background-color: white;
|
3134
|
+
border: 1px solid Silver;
|
3135
|
+
border-top: none;
|
3136
|
+
}
|
3137
|
+
|
3101
3138
|
/* the SENSITIVITY DIALOG displays the sensitivity analysis */
|
3102
3139
|
#sensitivity-dlg {
|
3103
3140
|
display: none;
|
@@ -834,12 +834,19 @@ class ChartManager {
|
|
834
834
|
}
|
835
835
|
|
836
836
|
resetChartVectors() {
|
837
|
-
// Reset vectors of all charts
|
837
|
+
// Reset vectors of all charts.
|
838
838
|
for(let i = 0; i < MODEL.charts.length; i++) {
|
839
839
|
MODEL.charts[i].resetVectors();
|
840
840
|
}
|
841
841
|
}
|
842
842
|
|
843
|
+
promptForWildcardIndices(chart, dsm) {
|
844
|
+
// No GUI dialog, so add *all* vectors for wildcard dataset modifier
|
845
|
+
// `dsm` as variables to `chart`.
|
846
|
+
const indices = Object.keys(dsm.expression.wildcard_vectors);
|
847
|
+
chart.addWildcardVariables(dsm, indices);
|
848
|
+
}
|
849
|
+
|
843
850
|
setRunsChart(show) {
|
844
851
|
// Indicate whether the chart manager should display a run result chart.
|
845
852
|
this.runs_chart = show;
|
@@ -131,7 +131,7 @@ class GUIChartManager extends ChartManager {
|
|
131
131
|
document.getElementById('chart-narrow-btn').addEventListener(
|
132
132
|
'click', () => CHART_MANAGER.stretchChart(-1));
|
133
133
|
|
134
|
-
// The Add variable modal
|
134
|
+
// The Add variable modal.
|
135
135
|
this.add_variable_modal = new ModalDialog('add-variable');
|
136
136
|
this.add_variable_modal.ok.addEventListener(
|
137
137
|
'click', () => CHART_MANAGER.addVariable());
|
@@ -143,7 +143,7 @@ class GUIChartManager extends ChartManager {
|
|
143
143
|
this.add_variable_modal.element('name').addEventListener(
|
144
144
|
'change', () => X_EDIT.updateAttributeSelector('add-'));
|
145
145
|
|
146
|
-
// The Edit variable modal
|
146
|
+
// The Edit variable modal.
|
147
147
|
this.variable_modal = new ModalDialog('variable');
|
148
148
|
this.variable_modal.ok.addEventListener(
|
149
149
|
'click', () => CHART_MANAGER.modifyVariable());
|
@@ -160,7 +160,7 @@ class GUIChartManager extends ChartManager {
|
|
160
160
|
'mouseleave', () => CHART_MANAGER.hidePasteColor());
|
161
161
|
document.getElementById('variable-color').addEventListener(
|
162
162
|
'click', (event) => CHART_MANAGER.copyPasteColor(event));
|
163
|
-
// NOTE:
|
163
|
+
// NOTE: Uses the color picker developed by James Daniel.
|
164
164
|
this.color_picker = new iro.ColorPicker("#color-picker", {
|
165
165
|
width: 92,
|
166
166
|
height: 92,
|
@@ -179,13 +179,20 @@ class GUIChartManager extends ChartManager {
|
|
179
179
|
CHART_MANAGER.color_picker.color.hexString;
|
180
180
|
});
|
181
181
|
|
182
|
-
// The Rename chart modal
|
182
|
+
// The Rename chart modal.
|
183
183
|
this.rename_chart_modal = new ModalDialog('rename-chart');
|
184
184
|
this.rename_chart_modal.ok.addEventListener(
|
185
185
|
'click', () => CHART_MANAGER.renameChart());
|
186
186
|
this.rename_chart_modal.cancel.addEventListener(
|
187
187
|
'click', () => CHART_MANAGER.rename_chart_modal.hide());
|
188
188
|
|
189
|
+
// The Add wildcard variables modal.
|
190
|
+
this.add_wildcard_modal = new ModalDialog('add-wildcard-variables');
|
191
|
+
this.add_wildcard_modal.ok.addEventListener(
|
192
|
+
'click', () => CHART_MANAGER.addSelectedWildcardVariables());
|
193
|
+
this.add_wildcard_modal.cancel.addEventListener(
|
194
|
+
'click', () => CHART_MANAGER.add_wildcard_modal.hide());
|
195
|
+
|
189
196
|
// Do not display the time step until cursor moves over chart
|
190
197
|
this.time_step.style.display = 'none';
|
191
198
|
document.getElementById('table-only-buttons').style.display = 'none';
|
@@ -326,16 +333,25 @@ class GUIChartManager extends ChartManager {
|
|
326
333
|
}
|
327
334
|
|
328
335
|
updateSelector() {
|
329
|
-
// Adds one option to the selector for each chart defined for the model
|
330
|
-
// NOTE:
|
336
|
+
// Adds one option to the selector for each chart defined for the model.
|
337
|
+
// NOTE: Add the "new chart" option if it is not in the list.
|
331
338
|
MODEL.addChart(this.new_chart_title);
|
332
339
|
if(this.chart_index < 0) this.chart_index = 0;
|
333
340
|
const ol = [];
|
334
341
|
for(let i = 0; i < MODEL.charts.length; i++) {
|
335
|
-
|
336
|
-
|
337
|
-
|
342
|
+
const t = MODEL.charts[i].title;
|
343
|
+
ol.push(['<option value="', i,
|
344
|
+
(i == this.chart_index ? '" selected="selected' : ''),
|
345
|
+
'">', t , '</option>'].join(''));
|
338
346
|
}
|
347
|
+
// Sort option list by chart title.
|
348
|
+
ol.sort((a, b) => {
|
349
|
+
const
|
350
|
+
re = /<option value="\d+"( selected="selected")?>(.+)<\/option>/,
|
351
|
+
ta = a.replace(re, '$2'),
|
352
|
+
tb = b.replace(re, '$2');
|
353
|
+
return UI.compareFullNames(ta, tb);
|
354
|
+
});
|
339
355
|
this.chart_selector.innerHTML = ol.join('');
|
340
356
|
}
|
341
357
|
|
@@ -379,6 +395,11 @@ class GUIChartManager extends ChartManager {
|
|
379
395
|
} else {
|
380
396
|
this.variable_index = -1;
|
381
397
|
}
|
398
|
+
// Just in case variable index has not been adjusted after some
|
399
|
+
// variables have been deleted
|
400
|
+
if(this.variable_index >= c.variables.length) {
|
401
|
+
this.variable_index = -1;
|
402
|
+
}
|
382
403
|
// Set the image of the sort type button.
|
383
404
|
if(this.variable_index >= 0) {
|
384
405
|
const
|
@@ -392,11 +413,6 @@ class GUIChartManager extends ChartManager {
|
|
392
413
|
u_btn = 'chart-variable-up ',
|
393
414
|
d_btn = 'chart-variable-down ',
|
394
415
|
ed_btns = 'chart-edit-variable chart-sort-variable chart-delete-variable ';
|
395
|
-
// Just in case variable index has not been adjusted after some
|
396
|
-
// variables have been deleted
|
397
|
-
if(this.variable_index >= c.variables.length) {
|
398
|
-
this.variable_index = -1;
|
399
|
-
}
|
400
416
|
if(this.variable_index < 0) {
|
401
417
|
UI.disableButtons(ed_btns + u_btn + d_btn);
|
402
418
|
} else {
|
@@ -713,15 +729,63 @@ class GUIChartManager extends ChartManager {
|
|
713
729
|
}
|
714
730
|
}
|
715
731
|
|
732
|
+
promptForWildcardIndices(chart, dsm) {
|
733
|
+
// Prompt modeler with list of vectors for wildcard dataset modifier
|
734
|
+
// `dsm` as variables to `chart`.
|
735
|
+
const
|
736
|
+
md = this.add_wildcard_modal,
|
737
|
+
indices = Object.keys(dsm.expression.wildcard_vectors);
|
738
|
+
// First hide the "Add variable" modal.
|
739
|
+
this.add_variable_modal.hide();
|
740
|
+
// Do not prompt for selection if there is only 1 match.
|
741
|
+
if(indices.length < 2) chart.addWildcardVariables(dsm, indices);
|
742
|
+
md.chart = chart;
|
743
|
+
md.modifier = dsm;
|
744
|
+
md.indices = indices;
|
745
|
+
const
|
746
|
+
tr = [],
|
747
|
+
dn = dsm.displayName,
|
748
|
+
tbl = md.element('table');
|
749
|
+
for(let i = 0; i < indices.length; i++) {
|
750
|
+
tr.push('<tr><td class="v-box"><div id="wcv-box-', indices[i],
|
751
|
+
'" class="box clear" onclick="UI.toggleBox(event);"></td>',
|
752
|
+
'<td class="vname">', dn.replace('??', indices[i]),
|
753
|
+
'</td></tr>');
|
754
|
+
tbl.innerHTML = tr.join('');
|
755
|
+
}
|
756
|
+
md.dialog.style.height = (22 + indices.length * 16) + 'px';
|
757
|
+
md.show();
|
758
|
+
}
|
759
|
+
|
760
|
+
addSelectedWildcardVariables() {
|
761
|
+
// Let the chart add selected wildcard matches (if any) as chart
|
762
|
+
// variables.
|
763
|
+
const
|
764
|
+
md = this.add_wildcard_modal,
|
765
|
+
c = md.chart,
|
766
|
+
dsm = md.modifier,
|
767
|
+
il = md.indices,
|
768
|
+
indices = [];
|
769
|
+
if(c && dsm && il) {
|
770
|
+
for(let i = 0; i < il.length; i++) {
|
771
|
+
if(UI.boxChecked('wcv-box-'+ il[i])) indices.push(il[i]);
|
772
|
+
}
|
773
|
+
}
|
774
|
+
if(indices.length) c.addWildcardVariables(dsm, indices);
|
775
|
+
// Always hide the dialog.
|
776
|
+
md.hide();
|
777
|
+
this.updateDialog();
|
778
|
+
}
|
779
|
+
|
716
780
|
selectVariable(vi) {
|
717
|
-
// Select variable, and edit it when double-clicked
|
781
|
+
// Select variable, and edit it when double-clicked.
|
718
782
|
const
|
719
783
|
now = Date.now(),
|
720
784
|
dt = now - this.last_time_selected;
|
721
785
|
if(vi >= 0 && this.chart_index >= 0) {
|
722
786
|
this.last_time_selected = now;
|
723
787
|
if(vi === this.variable_index) {
|
724
|
-
// Consider click to be "double" if it occurred less than 300 ms ago
|
788
|
+
// Consider click to be "double" if it occurred less than 300 ms ago.
|
725
789
|
if(dt < 300) {
|
726
790
|
this.last_time_selected = 0;
|
727
791
|
this.editVariable();
|
@@ -109,7 +109,7 @@ class GroupPropertiesDialog extends ModalDialog {
|
|
109
109
|
// input fields this means `onkeydown` events.
|
110
110
|
const fnc = (event) => {
|
111
111
|
const id = event.target.id.split('-').shift();
|
112
|
-
// NOTE:
|
112
|
+
// NOTE: Add a short delay to permit checkboxes to update their
|
113
113
|
// status first, before checking for change.
|
114
114
|
setTimeout(() => UI.modals[id].highlightModifiedFields(), 100);
|
115
115
|
};
|
@@ -819,8 +819,8 @@ class GUIController extends Controller {
|
|
819
819
|
|
820
820
|
// Add all draggable stay-on-top dialogs as controller properties.
|
821
821
|
|
822
|
-
// Make checkboxes respond to click
|
823
|
-
// NOTE:
|
822
|
+
// Make checkboxes respond to click.
|
823
|
+
// NOTE: Checkbox-specific events must be bound AFTER this general setting.
|
824
824
|
const
|
825
825
|
cbs = document.getElementsByClassName('box'),
|
826
826
|
cbf = (event) => UI.toggleBox(event);
|
@@ -54,6 +54,9 @@ class EquationManager {
|
|
54
54
|
'click', () => EQUATION_MANAGER.editEquation());
|
55
55
|
document.getElementById('eq-delete-btn').addEventListener(
|
56
56
|
'click', () => EQUATION_MANAGER.deleteEquation());
|
57
|
+
this.outcome_btn = document.getElementById('equation-outcome');
|
58
|
+
this.outcome_btn.addEventListener(
|
59
|
+
'click', () => EQUATION_MANAGER.toggleOutcome());
|
57
60
|
|
58
61
|
// Create modal dialogs
|
59
62
|
this.new_modal = new ModalDialog('new-equation');
|
@@ -134,6 +137,11 @@ class EquationManager {
|
|
134
137
|
ml = [],
|
135
138
|
msl = ed.selectorList,
|
136
139
|
sm = this.selected_modifier;
|
140
|
+
if(sm && sm.outcome_equation) {
|
141
|
+
this.outcome_btn.classList.remove('not-selected');
|
142
|
+
} else {
|
143
|
+
this.outcome_btn.classList.add('not-selected');
|
144
|
+
}
|
137
145
|
let smid = 'eqmtr';
|
138
146
|
for(let i = 0; i < msl.length; i++) {
|
139
147
|
const
|
@@ -155,6 +163,7 @@ class EquationManager {
|
|
155
163
|
(m.expression.noMethodObject ? ' no-object' : ''),
|
156
164
|
(m.expression.isStatic ? '' : ' it'), issue,
|
157
165
|
(wild ? ' wildcard' : ''), clk, ', false);"', mover, '>',
|
166
|
+
(m.outcome_equation ? '<span class="outcome"></span>' : ''),
|
158
167
|
(wild ? wildcardFormat(m.selector) : m.selector),
|
159
168
|
'</td><td class="equation-expression', issue,
|
160
169
|
(issue ? '"title="' +
|
@@ -201,6 +210,16 @@ class EquationManager {
|
|
201
210
|
this.updateDialog();
|
202
211
|
}
|
203
212
|
|
213
|
+
toggleOutcome() {
|
214
|
+
const m = this.selected_modifier;
|
215
|
+
// NOTE: Methods cannot be outcomes.
|
216
|
+
if(m && !m.selector.startsWith(':')) {
|
217
|
+
m.outcome_equation = !m.outcome_equation;
|
218
|
+
this.updateDialog();
|
219
|
+
if(!UI.hidden('experiment-dlg')) EXPERIMENT_MANAGER.updateDialog();
|
220
|
+
}
|
221
|
+
}
|
222
|
+
|
204
223
|
promptForEquation(add=false) {
|
205
224
|
this.add_to_chart = add;
|
206
225
|
this.new_modal.element('name').value = '';
|
@@ -377,7 +377,7 @@ NOTE: Grouping groups results in a single group, e.g., (1;2);(3;4;5) evaluates a
|
|
377
377
|
}
|
378
378
|
va.innerHTML = options.join('');
|
379
379
|
// NOTE: Chart Manager variable dialog is 60px wider
|
380
|
-
va.style.width = (prefix ? 'calc(100% -
|
380
|
+
va.style.width = (prefix ? 'calc(100% - 84px)' : 'calc(100% - 142px)');
|
381
381
|
return;
|
382
382
|
}
|
383
383
|
// Add "empty" as first and initial option, as it denotes "use default"
|
@@ -170,11 +170,11 @@ class LinnyRModel {
|
|
170
170
|
olist.push(ds.displayName);
|
171
171
|
}
|
172
172
|
}
|
173
|
-
//
|
173
|
+
// Also add all outcome equation selectors.
|
174
174
|
const dsm = this.equations_dataset.modifiers;
|
175
|
-
//
|
175
|
+
// Exclude selectors starting with a colon (methods) -- just in case.
|
176
176
|
for(let k in dsm) if(dsm.hasOwnProperty(k) && !k.startsWith(':')) {
|
177
|
-
olist.push(dsm[k].selector);
|
177
|
+
if(dsm[k].outcome_equation) olist.push(dsm[k].selector);
|
178
178
|
}
|
179
179
|
return olist;
|
180
180
|
}
|
@@ -1593,8 +1593,8 @@ class LinnyRModel {
|
|
1593
1593
|
nbp = note.nearby_pos;
|
1594
1594
|
if(nbp) {
|
1595
1595
|
// Adjust (x, y) so as to retain the relative position.
|
1596
|
-
note.x += nbp.node.x -
|
1597
|
-
note.y += nbp.node.y -
|
1596
|
+
note.x += nbp.node.x - nbp.oldx;
|
1597
|
+
note.y += nbp.node.y - nbp.oldy;
|
1598
1598
|
note.nearby_pos = null;
|
1599
1599
|
}
|
1600
1600
|
}
|
@@ -3155,8 +3155,8 @@ class LinnyRModel {
|
|
3155
3155
|
const
|
3156
3156
|
dm = ds.modifiers[ms],
|
3157
3157
|
n = dm.displayName;
|
3158
|
-
// Do not add if already in the list.
|
3159
|
-
if(names.indexOf(n) < 0) {
|
3158
|
+
// Do not add if already in the list, or equation is not outcome.
|
3159
|
+
if(names.indexOf(n) < 0 && (!eq || dm.outcome_equation)) {
|
3160
3160
|
// Here, too, NULL can be used as "owner chart".
|
3161
3161
|
const cv = new ChartVariable(null);
|
3162
3162
|
// NOTE: For equations, the object is the dataset modifier.
|
@@ -3169,10 +3169,17 @@ class LinnyRModel {
|
|
3169
3169
|
// Sort variables by their name.
|
3170
3170
|
vbls.sort((a, b) => UI.compareFullNames(a.displayName, b.displayName));
|
3171
3171
|
// Create a new chart as dummy, so without adding it to this model.
|
3172
|
-
const
|
3172
|
+
const
|
3173
|
+
c = new Chart(),
|
3174
|
+
wcdm = [];
|
3173
3175
|
for(let i = 0; i < vbls.length; i++) {
|
3174
3176
|
const v = vbls[i];
|
3175
|
-
|
3177
|
+
// NOTE: Prevent adding wildcard dataset modifiers more than once.
|
3178
|
+
if(wcdm.indexOf(v.object) < 0) {
|
3179
|
+
if(v.object instanceof DatasetModifier &&
|
3180
|
+
v.object.selector.indexOf('??') >= 0) wcdm.push(v.object);
|
3181
|
+
c.addVariable(v.object.displayName, v.attribute);
|
3182
|
+
}
|
3176
3183
|
}
|
3177
3184
|
// NOTE: Call `draw` with FALSE to prevent display in the chart manager.
|
3178
3185
|
c.draw(false);
|
@@ -8477,7 +8484,7 @@ class DatasetModifier {
|
|
8477
8484
|
this.dataset = dataset;
|
8478
8485
|
this.selector = selector;
|
8479
8486
|
this.expression = new Expression(dataset, selector, '');
|
8480
|
-
this.
|
8487
|
+
this.outcome_equation = false;
|
8481
8488
|
}
|
8482
8489
|
|
8483
8490
|
get type() {
|
@@ -8507,12 +8514,21 @@ class DatasetModifier {
|
|
8507
8514
|
// NOTE: For some reason, selector may become empty string, so prevent
|
8508
8515
|
// saving such unidentified modifiers.
|
8509
8516
|
if(this.selector.trim().length === 0) return '';
|
8510
|
-
|
8517
|
+
const oe = (this.outcome_equation ? ' outcome="1"' : '');
|
8518
|
+
return ['<modifier', oe, '><selector>', xmlEncoded(this.selector),
|
8511
8519
|
'</selector><expression>', xmlEncoded(this.expression.text),
|
8512
8520
|
'</expression></modifier>'].join('');
|
8513
8521
|
}
|
8514
8522
|
|
8515
8523
|
initFromXML(node) {
|
8524
|
+
// NOTE: Up to version 1.6.2, all equations were considered as
|
8525
|
+
// outcomes. To maintain upward compatibility, check for the model
|
8526
|
+
// version number.
|
8527
|
+
if(earlierVersion(MODEL.version, '1.6.3')) {
|
8528
|
+
this.outcome_equation = true;
|
8529
|
+
} else {
|
8530
|
+
this.outcome_equation = nodeParameterValue(node, 'outcome') === '1';
|
8531
|
+
}
|
8516
8532
|
this.expression.text = xmlDecoded(nodeContentByTag(node, 'expression'));
|
8517
8533
|
if(IO_CONTEXT) {
|
8518
8534
|
// Contextualize the included expression.
|
@@ -8824,12 +8840,19 @@ class Dataset {
|
|
8824
8840
|
this.max = Math.max(this.max, this.data[i]);
|
8825
8841
|
sum += this.data[i];
|
8826
8842
|
}
|
8827
|
-
|
8828
|
-
|
8829
|
-
|
8830
|
-
|
8843
|
+
// NOTE: Avoid small differences due to numerical imprecision.
|
8844
|
+
if(this.max - this.min < VM.NEAR_ZERO) {
|
8845
|
+
this.max = this.min;
|
8846
|
+
this.mean = this.min;
|
8847
|
+
this.standard_deviation = 0;
|
8848
|
+
} else {
|
8849
|
+
this.mean = sum / this.data.length;
|
8850
|
+
let sumsq = 0;
|
8851
|
+
for(let i = 0; i < this.data.length; i++) {
|
8852
|
+
sumsq += Math.pow(this.data[i] - this.mean, 2);
|
8853
|
+
}
|
8854
|
+
this.standard_deviation = Math.sqrt(sumsq / this.data.length);
|
8831
8855
|
}
|
8832
|
-
this.standard_deviation = Math.sqrt(sumsq / this.data.length);
|
8833
8856
|
}
|
8834
8857
|
|
8835
8858
|
get statisticsAsString() {
|
@@ -9081,7 +9104,6 @@ class Dataset {
|
|
9081
9104
|
for(let m in this.modifiers) if(this.modifiers.hasOwnProperty(m)) {
|
9082
9105
|
// NOTE: "empty" expressions for modifiers default to dataset default.
|
9083
9106
|
this.modifiers[m].expression.reset(this.defaultValue);
|
9084
|
-
this.modifiers[m].expression_cache = {};
|
9085
9107
|
}
|
9086
9108
|
}
|
9087
9109
|
|
@@ -9141,6 +9163,8 @@ class ChartVariable {
|
|
9141
9163
|
this.non_zero_tally = 0;
|
9142
9164
|
this.exceptions = 0;
|
9143
9165
|
this.bin_tallies = [];
|
9166
|
+
// The actual wildcard index is set for each variable that is added
|
9167
|
+
// during this "expansion".
|
9144
9168
|
this.wildcard_index = false;
|
9145
9169
|
}
|
9146
9170
|
|
@@ -9210,6 +9234,8 @@ class ChartVariable {
|
|
9210
9234
|
}
|
9211
9235
|
const xml = ['<chart-variable', (this.stacked ? ' stacked="1"' : ''),
|
9212
9236
|
(this.visible ? ' visible="1"' : ''),
|
9237
|
+
(this.wildcard_index !== false ?
|
9238
|
+
` wildcard-index="${this.wildcard_index}"` : ''),
|
9213
9239
|
` sorted="${this.sorted}"`,
|
9214
9240
|
'><object-id>', xmlEncoded(id),
|
9215
9241
|
'</object-id><attribute>', this.attribute,
|
@@ -9255,6 +9281,9 @@ class ChartVariable {
|
|
9255
9281
|
UI.warn(`No chart variable entity with ID "${id}"`);
|
9256
9282
|
return false;
|
9257
9283
|
}
|
9284
|
+
// For wildcard variables, a subset of wildcard indices may be specified.
|
9285
|
+
const wci = nodeParameterValue(node, 'wildcard-index');
|
9286
|
+
this.wildcard_index = (wci ? parseInt(wci) : false);
|
9258
9287
|
this.setProperties(
|
9259
9288
|
obj,
|
9260
9289
|
nodeContentByTag(node, 'attribute'),
|
@@ -9327,7 +9356,7 @@ class ChartVariable {
|
|
9327
9356
|
for(let t = 0; t <= t_end; t++) {
|
9328
9357
|
// Get the result, store it, and incorporate it in statistics.
|
9329
9358
|
if(!av) {
|
9330
|
-
// Undefined attribute => zero (no error)
|
9359
|
+
// Undefined attribute => zero (no error).
|
9331
9360
|
v = 0;
|
9332
9361
|
} else if(Array.isArray(av)) {
|
9333
9362
|
// Attribute value is a vector.
|
@@ -9339,19 +9368,19 @@ class ChartVariable {
|
|
9339
9368
|
// this index as context number.
|
9340
9369
|
v = av.result(t, this.wildcard_index);
|
9341
9370
|
} else {
|
9342
|
-
// Attribute value must be a number
|
9371
|
+
// Attribute value must be a number.
|
9343
9372
|
v = av;
|
9344
9373
|
}
|
9345
|
-
// Map undefined values and all errors to 0
|
9374
|
+
// Map undefined values and all errors to 0.
|
9346
9375
|
if(v < VM.MINUS_INFINITY || v > VM.PLUS_INFINITY) {
|
9347
|
-
// Do not include values for t = 0 in statistics
|
9376
|
+
// Do not include values for t = 0 in statistics.
|
9348
9377
|
if(t > 0) this.exceptions++;
|
9349
9378
|
v = 0;
|
9350
9379
|
}
|
9351
|
-
// Scale the value unless run result (these are already scaled!)
|
9380
|
+
// Scale the value unless run result (these are already scaled!).
|
9352
9381
|
if(!rr) v *= this.scale_factor;
|
9353
9382
|
this.vector.push(v);
|
9354
|
-
// Do not include values for t = 0 in statistics
|
9383
|
+
// Do not include values for t = 0 in statistics.
|
9355
9384
|
if(t > 0) {
|
9356
9385
|
if(Math.abs(v) > VM.NEAR_ZERO) {
|
9357
9386
|
this.sum += v;
|
@@ -9361,17 +9390,24 @@ class ChartVariable {
|
|
9361
9390
|
this.maximum = Math.max(this.maximum, v);
|
9362
9391
|
}
|
9363
9392
|
}
|
9364
|
-
|
9365
|
-
|
9366
|
-
|
9367
|
-
|
9368
|
-
|
9369
|
-
|
9370
|
-
//
|
9371
|
-
|
9372
|
-
|
9393
|
+
if(this.maximum - this.minimum < VM.NEAR_ZERO) {
|
9394
|
+
// Ignore minute differences.
|
9395
|
+
this.maximum = this.minimum;
|
9396
|
+
this.mean = this.minimum;
|
9397
|
+
this.variance = 0;
|
9398
|
+
} else {
|
9399
|
+
// Compute the mean.
|
9400
|
+
this.mean = this.sum / t_end;
|
9401
|
+
// Compute the variance for t=1, ..., N.
|
9402
|
+
let sumsq = 0;
|
9403
|
+
for(let t = 1; t <= t_end; t++) {
|
9404
|
+
v = this.vector[t];
|
9405
|
+
// Here, too, ignore exceptional values, and use 0 instead.
|
9406
|
+
if(v < VM.MINUS_INFINITY || v > VM.PLUS_INFINITY) v = 0;
|
9407
|
+
sumsq += Math.pow(v - this.mean, 2);
|
9408
|
+
}
|
9409
|
+
this.variance = sumsq / t_end;
|
9373
9410
|
}
|
9374
|
-
this.variance = sumsq / t_end;
|
9375
9411
|
}
|
9376
9412
|
|
9377
9413
|
tallyVector() {
|
@@ -9540,16 +9576,16 @@ class Chart {
|
|
9540
9576
|
}
|
9541
9577
|
|
9542
9578
|
addVariable(n, a) {
|
9543
|
-
//
|
9544
|
-
// is already in the variable list.
|
9579
|
+
// Add variable [entity name `n`|attribute `a`] to the chart unless
|
9580
|
+
// it is already in the variable list.
|
9545
9581
|
let dn = n + UI.OA_SEPARATOR + a;
|
9546
|
-
// Adapt display name for special cases
|
9582
|
+
// Adapt display name for special cases.
|
9547
9583
|
if(n === UI.EQUATIONS_DATASET_NAME) {
|
9548
|
-
// For equations only the attribute (modifier selector)
|
9584
|
+
// For equations only the attribute (modifier selector).
|
9549
9585
|
dn = a;
|
9550
9586
|
n = a;
|
9551
9587
|
} else if(!a) {
|
9552
|
-
// If no attribute specified (=> dataset) only the entity name
|
9588
|
+
// If no attribute specified (=> dataset) only the entity name.
|
9553
9589
|
dn = n;
|
9554
9590
|
}
|
9555
9591
|
let vi = this.variableIndexByName(dn);
|
@@ -9564,17 +9600,14 @@ class Chart {
|
|
9564
9600
|
// No equation and no attribute specified? Then assume default.
|
9565
9601
|
if(!eq && a === '') a = obj.defaultAttribute;
|
9566
9602
|
if(eq && (n.indexOf('??') >= 0 || obj.expression.isMethod)) {
|
9567
|
-
// Special case: for wildcard equations and methods,
|
9568
|
-
//
|
9569
|
-
//
|
9570
|
-
|
9571
|
-
|
9572
|
-
|
9573
|
-
|
9574
|
-
|
9575
|
-
v.setProperties(obj, dn, false, clr);
|
9576
|
-
v.wildcard_index = parseInt(indices[i]);
|
9577
|
-
this.variables.push(v);
|
9603
|
+
// Special case: for wildcard equations and methods, prompt the
|
9604
|
+
// modeler which wildcard possibilities to add UNLESS this is an
|
9605
|
+
// untitled "dummy" chart used to report outcomes.
|
9606
|
+
if(this.title) {
|
9607
|
+
CHART_MANAGER.promptForWildcardIndices(this, obj);
|
9608
|
+
} else {
|
9609
|
+
this.addWildcardVariables(obj,
|
9610
|
+
Object.keys(obj.expression.wildcard_vectors));
|
9578
9611
|
}
|
9579
9612
|
} else {
|
9580
9613
|
const v = new ChartVariable(this);
|
@@ -9583,6 +9616,19 @@ class Chart {
|
|
9583
9616
|
}
|
9584
9617
|
return this.variables.length - 1;
|
9585
9618
|
}
|
9619
|
+
|
9620
|
+
addWildcardVariables(dsm, indices) {
|
9621
|
+
// For dataset modifier `dsm`, add dummy variables for those vectors
|
9622
|
+
// in the wildcard vector set of the expression that are indicated
|
9623
|
+
// by the list `indices`.
|
9624
|
+
const dn = dsm.displayName;
|
9625
|
+
for(let i = 0; i < indices.length; i++) {
|
9626
|
+
const v = new ChartVariable(this);
|
9627
|
+
v.setProperties(dsm, dn, false, this.nextAvailableDefaultColor);
|
9628
|
+
v.wildcard_index = parseInt(indices[i]);
|
9629
|
+
this.variables.push(v);
|
9630
|
+
}
|
9631
|
+
}
|
9586
9632
|
|
9587
9633
|
addSVG(lines) {
|
9588
9634
|
// Appends a string or an array of strings to the SVG
|
@@ -10401,7 +10447,7 @@ class Chart {
|
|
10401
10447
|
if(CHART_MANAGER.drawing_chart) {
|
10402
10448
|
return '(chart statistics not calculated yet)';
|
10403
10449
|
}
|
10404
|
-
// NOTE:
|
10450
|
+
// NOTE: Unlike statistics, series data is output in columns.
|
10405
10451
|
const data = [], vbl = [], line = ['t'];
|
10406
10452
|
// First line: column labels (variable names, but time step in first column)
|
10407
10453
|
for(let i = 0; i < this.variables.length; i++) {
|
@@ -394,11 +394,12 @@ class Expression {
|
|
394
394
|
vmi[0](this, vmi[1]);
|
395
395
|
this.program_counter++;
|
396
396
|
}
|
397
|
-
// Stack should now have length 1.
|
397
|
+
// Stack should now have length 1. If not, report error unless the
|
398
|
+
// length is due to some other error.
|
398
399
|
if(this.stack.length > 1) {
|
399
|
-
v[t] = VM.OVERFLOW;
|
400
|
+
if(v[t] > VM.ERROR) v[t] = VM.OVERFLOW;
|
400
401
|
} else if(this.stack.length < 1) {
|
401
|
-
v[t] = VM.UNDERFLOW;
|
402
|
+
if(v[t] > VM.ERROR) v[t] = VM.UNDERFLOW;
|
402
403
|
} else {
|
403
404
|
v[t] = this.stack.pop();
|
404
405
|
}
|
@@ -2476,7 +2477,7 @@ class VirtualMachine {
|
|
2476
2477
|
// Return number `n` formatted so as to show 2-3 significant digits
|
2477
2478
|
// NOTE: as `n` should be a number, a warning sign will typically
|
2478
2479
|
// indicate a bug in the software.
|
2479
|
-
if(n === undefined) return '\u26A0'; // Warning sign
|
2480
|
+
if(n === undefined || isNaN(n)) return '\u26A0'; // Warning sign
|
2480
2481
|
const sv = this.specialValue(n);
|
2481
2482
|
// If `n` has a special value, return its representation.
|
2482
2483
|
if(sv[0]) return sv[1];
|
@@ -2972,10 +2973,10 @@ class VirtualMachine {
|
|
2972
2973
|
if(vcnt == 0) return '(no variables)';
|
2973
2974
|
let l = '';
|
2974
2975
|
for(let i = 0; i < vcnt; i++) {
|
2975
|
-
const
|
2976
|
-
|
2977
|
-
|
2978
|
-
|
2976
|
+
const
|
2977
|
+
obj = this.variables[i][1],
|
2978
|
+
v = 'X' + (i+1).toString().padStart(z, '0') + ' ' + obj.displayName,
|
2979
|
+
p = (obj instanceof Process && obj.pace > 1 ? ' 1/' + obj.pace : '');
|
2979
2980
|
l += v + ' [' + this.variables[i][0] + p + ']\n';
|
2980
2981
|
}
|
2981
2982
|
if(this.chunk_variables.length > 0) {
|