@ironm00n/pyret-lang 0.0.4 → 0.0.6
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/Makefile +1 -1
- package/package.json +1 -1
- package/src/arr/compiler/libs.arr +3 -0
- package/src/arr/trove/charts-util.arr +71 -0
- package/src/arr/trove/charts.arr +321 -296
- package/src/js/base/js-numbers.js +59 -42
- package/src/js/trove/charts-lib.js +234 -70
- package/src/js/trove/image-lib.js +36 -34
- package/src/js/trove/load-lib.js +4 -0
- package/src/js/trove/make-image.js +1 -1
- package/src/js/trove/table.js +1 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
({
|
|
2
2
|
requires: [
|
|
3
3
|
{ 'import-type': 'builtin', 'name': 'image-lib' },
|
|
4
|
+
{ "import-type": "builtin", 'name': "charts-util" },
|
|
4
5
|
],
|
|
5
6
|
nativeRequires: [
|
|
6
7
|
'pyret-base/js/js-numbers',
|
|
@@ -19,7 +20,7 @@
|
|
|
19
20
|
'plot': "tany",
|
|
20
21
|
}
|
|
21
22
|
},
|
|
22
|
-
theModule: function (RUNTIME, NAMESPACE, uri, IMAGELIB, jsnums, vega, canvasLib) {
|
|
23
|
+
theModule: function (RUNTIME, NAMESPACE, uri, IMAGELIB, CHARTSUTILLIB, jsnums, vega, canvasLib) {
|
|
23
24
|
'use strict';
|
|
24
25
|
|
|
25
26
|
|
|
@@ -59,6 +60,7 @@
|
|
|
59
60
|
const cases = RUNTIME.ffi.cases;
|
|
60
61
|
|
|
61
62
|
var IMAGE = get(IMAGELIB, "internal");
|
|
63
|
+
var CHARTSUTIL = get(CHARTSUTILLIB, "values");
|
|
62
64
|
|
|
63
65
|
const ann = function(name, pred) {
|
|
64
66
|
return RUNTIME.makePrimitiveAnn(name, pred);
|
|
@@ -207,6 +209,28 @@
|
|
|
207
209
|
|
|
208
210
|
//////////////////////////////////////////////////////////////////////////////
|
|
209
211
|
|
|
212
|
+
const chartFontConfig = {
|
|
213
|
+
signals: [
|
|
214
|
+
{ name: 'fontSizeScale', value: 14 }
|
|
215
|
+
],
|
|
216
|
+
mark: {
|
|
217
|
+
fontSize: { signal: 'fontSizeScale' }
|
|
218
|
+
},
|
|
219
|
+
text: {
|
|
220
|
+
fontSize: { signal: 'fontSizeScale' }
|
|
221
|
+
},
|
|
222
|
+
title: {
|
|
223
|
+
fontSize: { signal: '1.2 * fontSizeScale' },
|
|
224
|
+
},
|
|
225
|
+
axis: {
|
|
226
|
+
labelFontSize: { signal: 'fontSizeScale' },
|
|
227
|
+
titleFontSize: { signal: 'fontSizeScale' },
|
|
228
|
+
},
|
|
229
|
+
legend: {
|
|
230
|
+
labelFontSize: { signal: 'fontSizeScale' },
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
|
|
210
234
|
function pieChart(globalOptions, rawData) {
|
|
211
235
|
/*
|
|
212
236
|
Note: Most of the complexity here is due to supporting the "collapsed" wedge of values,
|
|
@@ -457,6 +481,7 @@
|
|
|
457
481
|
marks,
|
|
458
482
|
legends,
|
|
459
483
|
onExit: defaultImageReturn,
|
|
484
|
+
config: chartFontConfig,
|
|
460
485
|
};
|
|
461
486
|
}
|
|
462
487
|
|
|
@@ -902,6 +927,9 @@
|
|
|
902
927
|
const background = getColorOrDefault(globalOptions['backgroundColor'], 'transparent');
|
|
903
928
|
const axesConfig = dimensions[horizontal ? 'horizontal' : 'vertical']
|
|
904
929
|
const axis = get_axis(rawData);
|
|
930
|
+
const xAxisType = globalOptions['x-axis-type'];
|
|
931
|
+
const yAxisType = globalOptions['y-axis-type'];
|
|
932
|
+
const bandwidth = toFixnum(get(rawData, 'bandwidth') || 1);
|
|
905
933
|
|
|
906
934
|
const data = [];
|
|
907
935
|
|
|
@@ -934,14 +962,15 @@
|
|
|
934
962
|
name: 'primary',
|
|
935
963
|
type: 'band',
|
|
936
964
|
range: axesConfig.primary.range,
|
|
937
|
-
domain: { data: 'table', field: 'label' }
|
|
965
|
+
domain: { data: 'table', field: 'label' },
|
|
966
|
+
padding: 1 - bandwidth,
|
|
938
967
|
},
|
|
939
968
|
{
|
|
940
969
|
name: 'secondary',
|
|
941
|
-
type: 'linear',
|
|
942
970
|
range: axesConfig.secondary.range,
|
|
943
971
|
nice: true, zero: true,
|
|
944
|
-
domain: axis ? { signal: 'extent(domain("secondaryLabels"))' } : { data: 'table', field: 'value' }
|
|
972
|
+
domain: axis ? { signal: 'extent(domain("secondaryLabels"))' } : { data: 'table', field: 'value' },
|
|
973
|
+
...(axesConfig.secondary.name === 'x' ? xAxisType : yAxisType)
|
|
945
974
|
},
|
|
946
975
|
{
|
|
947
976
|
name: 'color',
|
|
@@ -1015,6 +1044,7 @@
|
|
|
1015
1044
|
axes,
|
|
1016
1045
|
marks,
|
|
1017
1046
|
onExit: defaultImageReturn,
|
|
1047
|
+
config: chartFontConfig,
|
|
1018
1048
|
};
|
|
1019
1049
|
}
|
|
1020
1050
|
|
|
@@ -1034,6 +1064,9 @@
|
|
|
1034
1064
|
const stackType = get(rawData, 'is-stacked');
|
|
1035
1065
|
const isStacked = stackType !== 'none';
|
|
1036
1066
|
const isNotFullStacked = (stackType !== 'relative') && (stackType !== 'percent');
|
|
1067
|
+
const bandwidth = toFixnum(get(rawData, 'bandwidth') || 1);
|
|
1068
|
+
const xAxisType = globalOptions['x-axis-type'];
|
|
1069
|
+
const yAxisType = globalOptions['y-axis-type'];
|
|
1037
1070
|
|
|
1038
1071
|
const data = [];
|
|
1039
1072
|
|
|
@@ -1114,13 +1147,13 @@
|
|
|
1114
1147
|
type: 'band',
|
|
1115
1148
|
range: axesConfig.primary.range,
|
|
1116
1149
|
domain: { data: 'table', field: 'label' },
|
|
1117
|
-
padding:
|
|
1150
|
+
padding: 1 - bandwidth
|
|
1118
1151
|
};
|
|
1119
1152
|
const secondaryScale = {
|
|
1120
1153
|
name: 'secondary',
|
|
1121
|
-
type: 'linear',
|
|
1122
1154
|
range: axesConfig.secondary.range,
|
|
1123
|
-
|
|
1155
|
+
...(axesConfig.secondary.name === 'x' ? xAxisType : yAxisType),
|
|
1156
|
+
nice: true, zero: false,
|
|
1124
1157
|
domain: (axis && isNotFullStacked) ? { signal: 'extent(domain("secondaryLabels"))' } : { data: 'table', field: 'value1' }
|
|
1125
1158
|
};
|
|
1126
1159
|
const scales = [
|
|
@@ -1300,6 +1333,7 @@
|
|
|
1300
1333
|
marks,
|
|
1301
1334
|
legends,
|
|
1302
1335
|
onExit: defaultImageReturn,
|
|
1336
|
+
config: chartFontConfig,
|
|
1303
1337
|
}
|
|
1304
1338
|
}
|
|
1305
1339
|
|
|
@@ -1317,6 +1351,8 @@
|
|
|
1317
1351
|
const background = getColorOrDefault(globalOptions['backgroundColor'], 'transparent');
|
|
1318
1352
|
const min = getNumOrDefault(globalOptions['min'], undefined);
|
|
1319
1353
|
const max = getNumOrDefault(globalOptions['max'], undefined);
|
|
1354
|
+
const xAxisType = globalOptions['x-axis-type'];
|
|
1355
|
+
const yAxisType = globalOptions['y-axis-type'];
|
|
1320
1356
|
|
|
1321
1357
|
const data = [
|
|
1322
1358
|
{
|
|
@@ -1403,8 +1439,8 @@
|
|
|
1403
1439
|
},
|
|
1404
1440
|
update: {
|
|
1405
1441
|
[PC]: { scale: 'primary', field: 'label', offset: { scale: 'primary', band: 0.5 } },
|
|
1406
|
-
[S]: { scale: 'secondary', field: 'lowWhisker' },
|
|
1407
|
-
[S2]: { scale: 'secondary', field: 'highWhisker' },
|
|
1442
|
+
[S]: { scale: 'secondary', field: showOutliers ? 'lowWhisker' : 'minVal' },
|
|
1443
|
+
[S2]: { scale: 'secondary', field: showOutliers ? 'highWhisker' : 'maxVal' },
|
|
1408
1444
|
tooltip: { signal: tooltip }
|
|
1409
1445
|
},
|
|
1410
1446
|
}
|
|
@@ -1421,7 +1457,7 @@
|
|
|
1421
1457
|
update: {
|
|
1422
1458
|
[PC]: { scale: 'primary', field: 'label', offset: { scale: 'primary', band: 0.5 } },
|
|
1423
1459
|
[axesConfig.primary.range]: { scale: 'primary', band: 0.25 },
|
|
1424
|
-
[S]: { scale: 'secondary', field: 'lowWhisker' },
|
|
1460
|
+
[S]: { scale: 'secondary', field: showOutliers ? 'lowWhisker' : 'minVal' },
|
|
1425
1461
|
tooltip: { signal: tooltip }
|
|
1426
1462
|
}
|
|
1427
1463
|
}
|
|
@@ -1438,7 +1474,7 @@
|
|
|
1438
1474
|
update: {
|
|
1439
1475
|
[PC]: { scale: 'primary', field: 'label', offset: { scale: 'primary', band: 0.5 } },
|
|
1440
1476
|
[axesConfig.primary.range]: { scale: 'primary', band: 0.25 },
|
|
1441
|
-
[S]: { scale: 'secondary', field: 'highWhisker' },
|
|
1477
|
+
[S]: { scale: 'secondary', field: showOutliers ? 'highWhisker' : 'maxVal' },
|
|
1442
1478
|
tooltip: { signal: tooltip }
|
|
1443
1479
|
}
|
|
1444
1480
|
}
|
|
@@ -1534,7 +1570,7 @@
|
|
|
1534
1570
|
},
|
|
1535
1571
|
{
|
|
1536
1572
|
name: 'secondary',
|
|
1537
|
-
|
|
1573
|
+
...(axesConfig.secondary.name === 'x' ? xAxisType : yAxisType),
|
|
1538
1574
|
range: axesConfig.secondary.range,
|
|
1539
1575
|
nice: true, zero: false,
|
|
1540
1576
|
domain: [{ signal: 'minValue' },{ signal: 'maxValue' }],
|
|
@@ -1572,6 +1608,7 @@
|
|
|
1572
1608
|
axes,
|
|
1573
1609
|
marks,
|
|
1574
1610
|
onExit: defaultImageReturn,
|
|
1611
|
+
config: chartFontConfig,
|
|
1575
1612
|
};
|
|
1576
1613
|
}
|
|
1577
1614
|
|
|
@@ -1585,6 +1622,7 @@
|
|
|
1585
1622
|
const width = globalOptions['width'];
|
|
1586
1623
|
const height = globalOptions['height'];
|
|
1587
1624
|
const background = getColorOrDefault(globalOptions['backgroundColor'], 'transparent');
|
|
1625
|
+
const yAxisType = globalOptions['y-axis-type'];
|
|
1588
1626
|
|
|
1589
1627
|
const data = [
|
|
1590
1628
|
{
|
|
@@ -1714,12 +1752,14 @@
|
|
|
1714
1752
|
name: 'binScale',
|
|
1715
1753
|
type: 'linear',
|
|
1716
1754
|
range: 'width',
|
|
1717
|
-
|
|
1755
|
+
zero: false,
|
|
1756
|
+
domain: { data: 'rawTable', fields: ['bin0', 'bin1'] }
|
|
1718
1757
|
},
|
|
1719
1758
|
{
|
|
1720
1759
|
name: 'countScale',
|
|
1721
|
-
type: 'linear',
|
|
1722
1760
|
range: 'height',
|
|
1761
|
+
...yAxisType,
|
|
1762
|
+
zero: true,
|
|
1723
1763
|
domain: { data: 'rawTable', field: 'y1' }
|
|
1724
1764
|
}
|
|
1725
1765
|
];
|
|
@@ -1751,6 +1791,7 @@
|
|
|
1751
1791
|
axes,
|
|
1752
1792
|
marks,
|
|
1753
1793
|
onExit: defaultImageReturn,
|
|
1794
|
+
config: chartFontConfig,
|
|
1754
1795
|
};
|
|
1755
1796
|
}
|
|
1756
1797
|
|
|
@@ -1768,6 +1809,9 @@
|
|
|
1768
1809
|
const height = globalOptions['height'];
|
|
1769
1810
|
const xAxisLabel = globalOptions['x-axis'];
|
|
1770
1811
|
const yAxisLabel = globalOptions['y-axis'];
|
|
1812
|
+
const xMinValue = getNumOrDefault(globalOptions['x-min'], undefined);
|
|
1813
|
+
const xMaxValue = getNumOrDefault(globalOptions['x-max'], undefined);
|
|
1814
|
+
const yAxisType = globalOptions['y-axis-type'];
|
|
1771
1815
|
const background = getColorOrDefault(globalOptions['backgroundColor'], 'transparent');
|
|
1772
1816
|
|
|
1773
1817
|
const data = [
|
|
@@ -1819,18 +1863,23 @@
|
|
|
1819
1863
|
{ name: 'binSize', update: 'invert("binScale", dotSize)' },
|
|
1820
1864
|
{ name: 'actualDotSize', update: 'scale("dotScale", 0) - scale("dotScale", 1)' },
|
|
1821
1865
|
{ name: 'headspace', value: '0.25' },
|
|
1822
|
-
{ name: 'wrapMaxY', update: 'floor(domain("dotScale")[1] * (1 - headspace))' }
|
|
1866
|
+
{ name: 'wrapMaxY', update: 'floor(domain("dotScale")[1] * (1 - headspace))' },
|
|
1867
|
+
{ name: 'xMinValue', value: xMinValue },
|
|
1868
|
+
{ name: 'xMaxValue', value: xMaxValue },
|
|
1823
1869
|
];
|
|
1824
1870
|
const scales = [
|
|
1825
1871
|
{
|
|
1826
1872
|
name: 'binScale',
|
|
1827
1873
|
type: 'linear',
|
|
1874
|
+
zero: false,
|
|
1828
1875
|
range: { signal: '[0, width - dotSize / 2]' },
|
|
1829
|
-
domain: { data: 'rawTable', field: 'value' }
|
|
1876
|
+
domain: { data: 'rawTable', field: 'value' },
|
|
1877
|
+
domainMin: { signal: 'xMinValue' }, domainMax: { signal: 'xMaxValue' },
|
|
1830
1878
|
},
|
|
1831
1879
|
{
|
|
1832
1880
|
name: 'dotScale',
|
|
1833
|
-
|
|
1881
|
+
...yAxisType,
|
|
1882
|
+
zero: true,
|
|
1834
1883
|
range: { signal: '[height, dotSize / 2]' },
|
|
1835
1884
|
domain: { signal: '[0, floor(height / dotSize)]' }
|
|
1836
1885
|
}
|
|
@@ -1911,6 +1960,7 @@
|
|
|
1911
1960
|
axes,
|
|
1912
1961
|
marks,
|
|
1913
1962
|
onExit: defaultImageReturn,
|
|
1963
|
+
config: chartFontConfig,
|
|
1914
1964
|
};
|
|
1915
1965
|
}
|
|
1916
1966
|
|
|
@@ -1926,6 +1976,7 @@
|
|
|
1926
1976
|
const height = globalOptions['height'];
|
|
1927
1977
|
const xAxisLabel = globalOptions['x-axis'];
|
|
1928
1978
|
const yAxisLabel = globalOptions['y-axis'];
|
|
1979
|
+
const yAxisType = globalOptions['y-axis-type'];
|
|
1929
1980
|
const background = getColorOrDefault(globalOptions['backgroundColor'], 'transparent');
|
|
1930
1981
|
|
|
1931
1982
|
|
|
@@ -1958,8 +2009,8 @@
|
|
|
1958
2009
|
},
|
|
1959
2010
|
{
|
|
1960
2011
|
name: 'secondary',
|
|
1961
|
-
type: 'linear',
|
|
1962
2012
|
range: 'height',
|
|
2013
|
+
...yAxisType,
|
|
1963
2014
|
nice: true, zero: true,
|
|
1964
2015
|
domain: { data: 'bars', field: 'count' }
|
|
1965
2016
|
}
|
|
@@ -2028,6 +2079,7 @@
|
|
|
2028
2079
|
axes,
|
|
2029
2080
|
marks,
|
|
2030
2081
|
onExit: defaultImageReturn,
|
|
2082
|
+
config: chartFontConfig,
|
|
2031
2083
|
};
|
|
2032
2084
|
}
|
|
2033
2085
|
|
|
@@ -2107,6 +2159,8 @@
|
|
|
2107
2159
|
function scatterPlot(globalOptions, rawData, config) {
|
|
2108
2160
|
const xAxisLabel = globalOptions['x-axis'];
|
|
2109
2161
|
const yAxisLabel = globalOptions['y-axis'];
|
|
2162
|
+
const xAxisType = globalOptions['x-axis-type'];
|
|
2163
|
+
const yAxisType = globalOptions['y-axis-type'];
|
|
2110
2164
|
const prefix = config.prefix || ''
|
|
2111
2165
|
const defaultColor = config.defaultColor || default_colors[0];
|
|
2112
2166
|
const color = getColorOrDefault(get(rawData, 'color'), defaultColor);
|
|
@@ -2122,43 +2176,57 @@
|
|
|
2122
2176
|
const trendlineWidth = toFixnum(get(rawData, 'trendlineWidth'));
|
|
2123
2177
|
const trendlineOpacity = toFixnum(get(rawData, 'trendlineOpacity'));
|
|
2124
2178
|
const trendlineDegree = toFixnum(get(rawData, 'trendlineDegree'));
|
|
2125
|
-
const xMinValue = getNumOrDefault(globalOptions['x-min'], undefined);
|
|
2126
|
-
const xMaxValue = getNumOrDefault(globalOptions['x-max'], undefined);
|
|
2127
2179
|
const yMinValue = getNumOrDefault(globalOptions['y-min'], undefined);
|
|
2128
2180
|
const yMaxValue = getNumOrDefault(globalOptions['y-max'], undefined);
|
|
2181
|
+
const imageScaleFactorX = autosizeImage ? '-datum.imageWidth' : -pointSize;
|
|
2182
|
+
const imageScaleFactorY = autosizeImage ? '-datum.imageHeight' : -pointSize;
|
|
2129
2183
|
|
|
2130
2184
|
const points = RUNTIME.ffi.toArray(get(rawData, 'ps'));
|
|
2131
|
-
|
|
2185
|
+
const pointValues = points.map((p) => ({
|
|
2186
|
+
label: get(p, 'label'),
|
|
2187
|
+
x: toFixnum(get(p, 'x')),
|
|
2188
|
+
y: toFixnum(get(p, 'y')),
|
|
2189
|
+
image: cases(RUNTIME.ffi.isOption, 'Option', get(p, 'image'), {
|
|
2190
|
+
none: () => undefined,
|
|
2191
|
+
some: (opaqueImg) => imageToCanvas(opaqueImg.val)
|
|
2192
|
+
}),
|
|
2193
|
+
imageWidth: cases(RUNTIME.ffi.isOption, 'Option', get(p, 'image'), {
|
|
2194
|
+
none: () => undefined,
|
|
2195
|
+
some: (opaqueImg) => opaqueImg.val.getWidth()
|
|
2196
|
+
}),
|
|
2197
|
+
imageHeight: cases(RUNTIME.ffi.isOption, 'Option', get(p, 'image'), {
|
|
2198
|
+
none: () => undefined,
|
|
2199
|
+
some: (opaqueImg) => opaqueImg.val.getHeight()
|
|
2200
|
+
}),
|
|
2201
|
+
imageOffsetX: cases(RUNTIME.ffi.isOption, 'Option', get(p, 'image'), {
|
|
2202
|
+
none: () => undefined,
|
|
2203
|
+
some: (opaqueImg) => opaqueImg.val.getPinholeX() / opaqueImg.val.getWidth()
|
|
2204
|
+
}),
|
|
2205
|
+
imageOffsetY: cases(RUNTIME.ffi.isOption, 'Option', get(p, 'image'), {
|
|
2206
|
+
none: () => undefined,
|
|
2207
|
+
some: (opaqueImg) => opaqueImg.val.getPinholeY() / opaqueImg.val.getHeight()
|
|
2208
|
+
}),
|
|
2209
|
+
}));
|
|
2132
2210
|
const data = [
|
|
2133
2211
|
{
|
|
2134
2212
|
name: `${prefix}rawTable`,
|
|
2135
|
-
values:
|
|
2136
|
-
label: get(p, 'label'),
|
|
2137
|
-
x: toFixnum(get(p, 'x')),
|
|
2138
|
-
y: toFixnum(get(p, 'y')),
|
|
2139
|
-
image: cases(RUNTIME.ffi.isOption, 'Option', get(p, 'image'), {
|
|
2140
|
-
none: () => undefined,
|
|
2141
|
-
some: (opaqueImg) => imageToCanvas(opaqueImg.val)
|
|
2142
|
-
}),
|
|
2143
|
-
imageWidth: cases(RUNTIME.ffi.isOption, 'Option', get(p, 'image'), {
|
|
2144
|
-
none: () => undefined,
|
|
2145
|
-
some: (opaqueImg) => opaqueImg.val.getWidth()
|
|
2146
|
-
}),
|
|
2147
|
-
imageHeight: cases(RUNTIME.ffi.isOption, 'Option', get(p, 'image'), {
|
|
2148
|
-
none: () => undefined,
|
|
2149
|
-
some: (opaqueImg) => opaqueImg.val.getHeight()
|
|
2150
|
-
}),
|
|
2151
|
-
imageOffsetX: cases(RUNTIME.ffi.isOption, 'Option', get(p, 'image'), {
|
|
2152
|
-
none: () => undefined,
|
|
2153
|
-
some: (opaqueImg) => opaqueImg.val.getPinholeX() / opaqueImg.val.getWidth()
|
|
2154
|
-
}),
|
|
2155
|
-
imageOffsetY: cases(RUNTIME.ffi.isOption, 'Option', get(p, 'image'), {
|
|
2156
|
-
none: () => undefined,
|
|
2157
|
-
some: (opaqueImg) => opaqueImg.val.getPinholeY() / opaqueImg.val.getHeight()
|
|
2158
|
-
}),
|
|
2159
|
-
})),
|
|
2213
|
+
values: pointValues,
|
|
2160
2214
|
transform: []
|
|
2161
2215
|
},
|
|
2216
|
+
{
|
|
2217
|
+
name: `${prefix}tableMarkExtents`,
|
|
2218
|
+
source: `${prefix}rawTable`,
|
|
2219
|
+
transform: [
|
|
2220
|
+
{ type: 'formula', as: 'left',
|
|
2221
|
+
expr: `datum.x + (isValid(datum.image) ? datum.imageOffsetX * ${imageScaleFactorX}: ${-pointSize / 2}) / ${prefix}rawDomainUnitInPx` },
|
|
2222
|
+
{ type: 'formula', as: 'right',
|
|
2223
|
+
expr: `datum.x + (isValid(datum.image) ? (datum.imageOffsetX * ${imageScaleFactorY} - datum.imageWidth) : ${pointSize}) / ${prefix}rawDomainUnitInPx` },
|
|
2224
|
+
{ type: 'formula', as: 'top',
|
|
2225
|
+
expr: `datum.y + (isValid(datum.image) ? datum.imageOffsetY * ${imageScaleFactorY} : ${-pointSize / 2}) / ${prefix}rawRangeUnitInPx` },
|
|
2226
|
+
{ type: 'formula', as: 'bot',
|
|
2227
|
+
expr: `datum.y + (isValid(datum.image) ? (datum.imageOffsetY * ${imageScaleFactorY} - datum.imageHeight) : ${pointSize}) / ${prefix}rawRangeUnitInPx` },
|
|
2228
|
+
],
|
|
2229
|
+
},
|
|
2162
2230
|
{
|
|
2163
2231
|
name: `${prefix}table`,
|
|
2164
2232
|
source: `${prefix}rawTable`,
|
|
@@ -2171,22 +2239,87 @@
|
|
|
2171
2239
|
},
|
|
2172
2240
|
];
|
|
2173
2241
|
|
|
2174
|
-
|
|
2175
|
-
|
|
2242
|
+
// these are measured in pixels
|
|
2243
|
+
let imageOverhangX, imageOverhangY;
|
|
2244
|
+
const imageWidths = pointValues.map((v) => v.imageWidth).filter((v) => v !== undefined);
|
|
2245
|
+
const imageHeights = pointValues.map((v) => v.imageHeight).filter((v) => v !== undefined);
|
|
2246
|
+
if (imageWidths.length === 0 || autosizeImage) {
|
|
2247
|
+
imageOverhangX = pointSize;
|
|
2248
|
+
imageOverhangY = pointSize;
|
|
2249
|
+
} else {
|
|
2250
|
+
let temp;
|
|
2251
|
+
temp = computeDomain(imageWidths);
|
|
2252
|
+
imageOverhangX = temp[1] - temp[0];
|
|
2253
|
+
temp = computeDomain(imageHeights);
|
|
2254
|
+
imageOverhangY = temp[1] - temp[0];
|
|
2255
|
+
}
|
|
2256
|
+
let windowWidth = toFixnum(globalOptions['width']);
|
|
2257
|
+
let windowHeight = toFixnum(globalOptions['height']);
|
|
2258
|
+
|
|
2259
|
+
const rawDomain = computeDomain(pointValues.map((v) => v.x));
|
|
2260
|
+
const rawRange = computeDomain(pointValues.map((v) => v.y));
|
|
2261
|
+
const reducedWidth = windowWidth - imageOverhangX;
|
|
2262
|
+
const reducedHeight = windowHeight - imageOverhangY;
|
|
2263
|
+
const rawDomainUnitInPx = reducedWidth / (rawDomain[1] - rawDomain[0]);
|
|
2264
|
+
const rawRangeUnitInPx = reducedHeight / (rawRange[1] - rawRange[0]);
|
|
2265
|
+
const domain = computeDomain(pointValues.map((v) => {
|
|
2266
|
+
let left, right;
|
|
2267
|
+
if (!autosizeImage || !v.image) {
|
|
2268
|
+
let halfPointSize = pointSize / 2;
|
|
2269
|
+
let inDomainHalfPointSize = halfPointSize / rawDomainUnitInPx;
|
|
2270
|
+
return [v.x - inDomainHalfPointSize, v.x + inDomainHalfPointSize];
|
|
2271
|
+
}
|
|
2272
|
+
left = v.x - v.imageOffsetX * v.imageWidth / rawDomainUnitInPx;
|
|
2273
|
+
right = left + v.imageWidth / rawDomainUnitInPx;
|
|
2274
|
+
return [left, right];
|
|
2275
|
+
}).flat());
|
|
2276
|
+
// console.log("Raw domain", computeDomain(pointValues.map((v) => v.x)), "Point size", pointSize);
|
|
2277
|
+
// console.log({reducedWidth, imageOverhangX, rawDomainUnitInPx});
|
|
2278
|
+
// console.log("Extended domain", domain);
|
|
2279
|
+
|
|
2280
|
+
const range = computeDomain(pointValues.map((v) => {
|
|
2281
|
+
let top, bot;
|
|
2282
|
+
if (!autosizeImage || !v.image) {
|
|
2283
|
+
let halfPointSize = pointSize / 2;
|
|
2284
|
+
let inDomainHalfPointSize = halfPointSize / rawDomainUnitInPx;
|
|
2285
|
+
return [v.y - inDomainHalfPointSize, v.y + inDomainHalfPointSize];
|
|
2286
|
+
}
|
|
2287
|
+
top = v.y + v.imageOffsetY * v.imageHeight / rawDomainUnitInPx;
|
|
2288
|
+
bot = top - v.imageHeight / rawDomainUnitInPx;
|
|
2289
|
+
return [top, bot];
|
|
2290
|
+
}).flat());
|
|
2291
|
+
// console.log("Raw range", computeDomain(pointValues.map((v) => v.y)), "Point size", pointSize);
|
|
2292
|
+
// console.log("Extended range", range);
|
|
2293
|
+
|
|
2294
|
+
function unionExtents(...names) {
|
|
2295
|
+
const names0 = names.map((s) => `${s}[0]`);
|
|
2296
|
+
const names1 = names.map((s) => `${s}[1]`);
|
|
2297
|
+
return `[min(${names0.join(',')}), max(${names1.join(',')})]`
|
|
2298
|
+
}
|
|
2176
2299
|
const signals = [
|
|
2177
|
-
{ name: `${prefix}
|
|
2178
|
-
{ name: `${prefix}
|
|
2300
|
+
{ name: `${prefix}reducedWidth`, update: `width - ${imageOverhangX}` },
|
|
2301
|
+
{ name: `${prefix}reducedHeight`, update: `height - ${imageOverhangY}` },
|
|
2302
|
+
{ name: `${prefix}rawDomainUnitInPx`, update: `${prefix}reducedWidth / ${rawDomain[1] - rawDomain[0]}` },
|
|
2303
|
+
{ name: `${prefix}rawRangeUnitInPx`, update: `${prefix}reducedHeight / ${rawRange[1] - rawRange[0]}` },
|
|
2304
|
+
{ name: `${prefix}extentLeft`, update: `extent(pluck(data("${prefix}tableMarkExtents"), "left"))` },
|
|
2305
|
+
{ name: `${prefix}extentRight`, update: `extent(pluck(data("${prefix}tableMarkExtents"), "right"))` },
|
|
2306
|
+
{ name: `${prefix}extentTop`, update: `extent(pluck(data("${prefix}tableMarkExtents"), "top"))` },
|
|
2307
|
+
{ name: `${prefix}extentBot`, update: `extent(pluck(data("${prefix}tableMarkExtents"), "bot"))` },
|
|
2308
|
+
{ name: `${prefix}extentX`, update: unionExtents(`${prefix}extentLeft`, `${prefix}extentRight`) },
|
|
2309
|
+
{ name: `${prefix}extentY`, update: unionExtents(`${prefix}extentTop`, `${prefix}extentBot`) },
|
|
2179
2310
|
];
|
|
2180
2311
|
const scales = [
|
|
2181
2312
|
{ name: `${prefix}xscale`,
|
|
2182
|
-
|
|
2313
|
+
...xAxisType,
|
|
2183
2314
|
domain: { signal: `${prefix}extentX` },
|
|
2184
2315
|
range: 'width',
|
|
2316
|
+
zero: false,
|
|
2185
2317
|
nice: true },
|
|
2186
2318
|
{ name: `${prefix}yscale`,
|
|
2187
|
-
|
|
2319
|
+
...yAxisType,
|
|
2188
2320
|
domain: { signal: `${prefix}extentY` },
|
|
2189
2321
|
range: 'height',
|
|
2322
|
+
zero: false,
|
|
2190
2323
|
nice: true }
|
|
2191
2324
|
];
|
|
2192
2325
|
const marks = [];
|
|
@@ -2290,8 +2423,6 @@
|
|
|
2290
2423
|
signal: `{ title: "${legend}", Label: datum.label, x: datum.x, y: datum.y }` },
|
|
2291
2424
|
{ signal: `{ title: "${legend}", x: datum.x, y: datum.y }` },
|
|
2292
2425
|
];
|
|
2293
|
-
const imageScaleFactorX = autosizeImage ? '-datum.imageWidth' : -pointSize;
|
|
2294
|
-
const imageScaleFactorY = autosizeImage ? '-datum.imageHeight' : -pointSize;
|
|
2295
2426
|
marks.push({
|
|
2296
2427
|
type: 'image',
|
|
2297
2428
|
from: { data: `${prefix}images` },
|
|
@@ -2447,6 +2578,8 @@
|
|
|
2447
2578
|
function intervalPlot(globalOptions, rawData, config) {
|
|
2448
2579
|
const xAxisLabel = globalOptions['x-axis'];
|
|
2449
2580
|
const yAxisLabel = globalOptions['y-axis'];
|
|
2581
|
+
const xAxisType = globalOptions['x-axis-type'];
|
|
2582
|
+
const yAxisType = globalOptions['y-axis-type'];
|
|
2450
2583
|
const legend = get(rawData, 'legend') || config.legend;
|
|
2451
2584
|
const prefix = config.prefix || ''
|
|
2452
2585
|
const defaultColor = config.defaultColor || default_colors[0];
|
|
@@ -2480,7 +2613,7 @@
|
|
|
2480
2613
|
}),
|
|
2481
2614
|
})),
|
|
2482
2615
|
transform: [
|
|
2483
|
-
{ type: 'formula', as: 'yprime', expr: 'datum.y
|
|
2616
|
+
{ type: 'formula', as: 'yprime', expr: 'datum.y - datum.delta' }
|
|
2484
2617
|
]
|
|
2485
2618
|
},
|
|
2486
2619
|
{
|
|
@@ -2516,15 +2649,17 @@
|
|
|
2516
2649
|
|
|
2517
2650
|
const scales = [
|
|
2518
2651
|
{ name: `${prefix}xscale`,
|
|
2519
|
-
|
|
2652
|
+
...xAxisType,
|
|
2520
2653
|
domain: { signal: `${prefix}extentX` },
|
|
2521
2654
|
range: 'width',
|
|
2522
|
-
nice: false
|
|
2655
|
+
nice: false,
|
|
2656
|
+
zero: false },
|
|
2523
2657
|
{ name: `${prefix}yscale`,
|
|
2524
|
-
|
|
2658
|
+
...yAxisType,
|
|
2525
2659
|
domain: { signal: `${prefix}extentY` },
|
|
2526
2660
|
range: 'height',
|
|
2527
|
-
nice: false
|
|
2661
|
+
nice: false,
|
|
2662
|
+
zero: false }
|
|
2528
2663
|
];
|
|
2529
2664
|
const tooltip = {
|
|
2530
2665
|
signal: `{ title: datum.label, x: datum.x, y: datum.y, ŷ: datum.yprime, 'y - ŷ': datum.delta }`
|
|
@@ -2663,6 +2798,8 @@
|
|
|
2663
2798
|
const domain = config.domain;
|
|
2664
2799
|
const xMinValue = domain[0];
|
|
2665
2800
|
const xMaxValue = domain[1];
|
|
2801
|
+
const xAxisType = globalOptions['x-axis-type'];
|
|
2802
|
+
const yAxisType = globalOptions['y-axis-type'];
|
|
2666
2803
|
|
|
2667
2804
|
const fraction = (xMaxValue - xMinValue) / (numSamples - 1);
|
|
2668
2805
|
|
|
@@ -2676,10 +2813,11 @@
|
|
|
2676
2813
|
// from the surrounding chart context
|
|
2677
2814
|
{
|
|
2678
2815
|
name: `${prefix}yscale`,
|
|
2679
|
-
|
|
2816
|
+
...yAxisType,
|
|
2680
2817
|
domain: { signal: `${prefix}extentY` },
|
|
2681
2818
|
range: 'height',
|
|
2682
|
-
nice: true
|
|
2819
|
+
nice: true,
|
|
2820
|
+
zero: false
|
|
2683
2821
|
}
|
|
2684
2822
|
];
|
|
2685
2823
|
const tooltip = {
|
|
@@ -2775,6 +2913,8 @@
|
|
|
2775
2913
|
const numSamples = toFixnum(globalOptions['num-samples']);
|
|
2776
2914
|
const xAxisLabel = globalOptions['x-axis'];
|
|
2777
2915
|
const yAxisLabel = globalOptions['y-axis'];
|
|
2916
|
+
const xAxisType = globalOptions['x-axis-type'];
|
|
2917
|
+
const yAxisType = globalOptions['y-axis-type'];
|
|
2778
2918
|
const width = toFixnum(globalOptions['width']);
|
|
2779
2919
|
const height = toFixnum(globalOptions['height']);
|
|
2780
2920
|
const background = getColorOrDefault(globalOptions['backgroundColor'], 'transparent');
|
|
@@ -2795,10 +2935,10 @@
|
|
|
2795
2935
|
{ name: 'yscaleSignal', update: unionScaleSignal(scales.filter((s) => s.name.endsWith('yscale'))) }
|
|
2796
2936
|
);
|
|
2797
2937
|
scales.push(
|
|
2798
|
-
{ name: 'xscale', domain: { signal: 'xscaleSignal' }, range: 'width',
|
|
2799
|
-
domainMin: { signal: 'xMinValue' }, domainMax: { signal: 'xMaxValue' } },
|
|
2800
|
-
{ name: 'yscale', domain: { signal: 'yscaleSignal' }, range: 'height',
|
|
2801
|
-
domainMin: { signal: 'yMinValue' }, domainMax: { signal: 'yMaxValue' } }
|
|
2938
|
+
{ name: 'xscale', domain: { signal: 'xscaleSignal' }, range: 'width', ...xAxisType,
|
|
2939
|
+
domainMin: { signal: 'xMinValue' }, domainMax: { signal: 'xMaxValue' }, zero: false },
|
|
2940
|
+
{ name: 'yscale', domain: { signal: 'yscaleSignal' }, range: 'height', ...yAxisType,
|
|
2941
|
+
domainMin: { signal: 'yMinValue' }, domainMax: { signal: 'yMaxValue' }, zero: false }
|
|
2802
2942
|
);
|
|
2803
2943
|
|
|
2804
2944
|
// NOTE: For the axes, we're going to want to put the bar lines at the zeros
|
|
@@ -2995,7 +3135,9 @@
|
|
|
2995
3135
|
marks,
|
|
2996
3136
|
legends,
|
|
2997
3137
|
config: {
|
|
3138
|
+
...chartFontConfig,
|
|
2998
3139
|
legend: {
|
|
3140
|
+
...chartFontConfig.legend,
|
|
2999
3141
|
orient: 'bottom',
|
|
3000
3142
|
layout: { bottom: { anchor: 'middle' } },
|
|
3001
3143
|
}
|
|
@@ -3131,10 +3273,16 @@
|
|
|
3131
3273
|
}
|
|
3132
3274
|
|
|
3133
3275
|
function renderStaticImage(processed, globalOptions, rawData) {
|
|
3276
|
+
function isImageOrCanvas(v) {
|
|
3277
|
+
if (!v) return false;
|
|
3278
|
+
if (IMAGE.isImage(v)) return true;
|
|
3279
|
+
return canvasLib && canvasLib.Canvas && v instanceof canvasLib.Canvas;
|
|
3280
|
+
}
|
|
3281
|
+
const isVegaString = isTrue(globalOptions['vega']);
|
|
3134
3282
|
return RUNTIME.pauseStack(restarter => {
|
|
3135
3283
|
try {
|
|
3136
|
-
if (
|
|
3137
|
-
|
|
3284
|
+
if (isVegaString) {
|
|
3285
|
+
return restarter.resume(JSON.stringify(processed, (k, v) => isImageOrCanvas(v) ? v.ariaText : v, 2));
|
|
3138
3286
|
}
|
|
3139
3287
|
const width = toFixnum(globalOptions['width']);
|
|
3140
3288
|
const height = toFixnum(globalOptions['height']);
|
|
@@ -3159,15 +3307,20 @@
|
|
|
3159
3307
|
let height = toFixnum(globalOptions['height']);
|
|
3160
3308
|
const vegaTooltipHandler = new vegaTooltip.Handler({
|
|
3161
3309
|
formatTooltip: (value, valueToHtml, maxDepth, baseURL) => {
|
|
3162
|
-
|
|
3310
|
+
let ans;
|
|
3311
|
+
if (typeof value === 'object' && value.title instanceof Array) {
|
|
3163
3312
|
const { title, ...rest } = value;
|
|
3164
3313
|
if (title instanceof Array) {
|
|
3165
3314
|
const titleStr = `<h2>${title.map(valueToHtml).join('<br/>')}</h2>`;
|
|
3166
3315
|
const restStr = vegaTooltip.formatValue(rest, valueToHtml, maxDepth, baseURL);
|
|
3167
|
-
|
|
3316
|
+
ans = titleStr + restStr;
|
|
3168
3317
|
}
|
|
3318
|
+
} else {
|
|
3319
|
+
ans = vegaTooltip.formatValue(value, valueToHtml, maxDepth, baseURL);
|
|
3169
3320
|
}
|
|
3170
|
-
|
|
3321
|
+
ans = ans.replaceAll("<table>", "<table class=\"pyret-row\">");
|
|
3322
|
+
ans = ans.replaceAll("<td class=\"value\">", "<td class=\"value replTextOutput\"> ");
|
|
3323
|
+
return ans;
|
|
3171
3324
|
}
|
|
3172
3325
|
});
|
|
3173
3326
|
const view = new vega.View(vega.parse(processed), {
|
|
@@ -3232,10 +3385,21 @@
|
|
|
3232
3385
|
}
|
|
3233
3386
|
|
|
3234
3387
|
|
|
3388
|
+
function configScale(val) {
|
|
3389
|
+
return cases(get(CHARTSUTIL, "is-AxisType"), "AxisType", val, {
|
|
3390
|
+
'at-linear': () => ({ type: 'linear' }),
|
|
3391
|
+
'at-power': (pow) => ({ type: 'pow', exponent: toFixnum(pow) }),
|
|
3392
|
+
'at-log': (base) => ({ type: 'log', base: toFixnum(base) }),
|
|
3393
|
+
'at-symlog': (base) => ({ type: 'symlog', constant: toFixnum(base) })
|
|
3394
|
+
});
|
|
3395
|
+
}
|
|
3235
3396
|
function pyretObjToObj(globalOptions) {
|
|
3236
3397
|
const ret = {};
|
|
3237
3398
|
for (const field of RUNTIME.getFields(globalOptions)) {
|
|
3238
3399
|
ret[field] = get(globalOptions, field);
|
|
3400
|
+
if (get(CHARTSUTIL, 'is-AxisType').app(ret[field])) {
|
|
3401
|
+
ret[field] = configScale(ret[field]);
|
|
3402
|
+
}
|
|
3239
3403
|
}
|
|
3240
3404
|
return ret;
|
|
3241
3405
|
}
|