@ironm00n/pyret-lang 0.0.5 → 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/package.json +1 -1
- package/src/arr/trove/charts-util.arr +71 -0
- package/src/arr/trove/charts.arr +321 -296
- 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
package/src/arr/trove/charts.arr
CHANGED
|
@@ -4,16 +4,21 @@ provide:
|
|
|
4
4
|
from-list,
|
|
5
5
|
type DataSeries,
|
|
6
6
|
type ChartWindow,
|
|
7
|
-
data StackType,
|
|
8
|
-
data TrendlineType,
|
|
9
|
-
data PointShape
|
|
10
7
|
end
|
|
11
8
|
|
|
12
9
|
import global as G
|
|
13
10
|
import base as B
|
|
14
11
|
include lists
|
|
15
12
|
include option
|
|
16
|
-
|
|
13
|
+
include charts-util
|
|
14
|
+
import charts-util as CU
|
|
15
|
+
provide from CU:
|
|
16
|
+
*,
|
|
17
|
+
type *,
|
|
18
|
+
data *
|
|
19
|
+
end
|
|
20
|
+
import image-structs as IS
|
|
21
|
+
import image as I
|
|
17
22
|
import internal-image-untyped as IM
|
|
18
23
|
import sets as S
|
|
19
24
|
import charts-lib as CL
|
|
@@ -22,6 +27,7 @@ import string-dict as SD
|
|
|
22
27
|
import valueskeleton as VS
|
|
23
28
|
import statistics as ST
|
|
24
29
|
import color as C
|
|
30
|
+
import error as ERR
|
|
25
31
|
import render-error-display as RED
|
|
26
32
|
|
|
27
33
|
################################################################################
|
|
@@ -48,45 +54,6 @@ end
|
|
|
48
54
|
data AxisData:
|
|
49
55
|
| axis-data(axisTop :: Number, axisBottom :: Number, ticks :: RawArray<Pointer>)
|
|
50
56
|
end
|
|
51
|
-
data StackType:
|
|
52
|
-
| absolute
|
|
53
|
-
| relative
|
|
54
|
-
| percent
|
|
55
|
-
| grouped
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
fun check-positive-degree(v :: Number) -> Boolean block:
|
|
59
|
-
when v < 0:
|
|
60
|
-
raise("degree: degree must be non-negative")
|
|
61
|
-
end
|
|
62
|
-
true
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
data TrendlineType:
|
|
66
|
-
| no-trendline
|
|
67
|
-
| linear
|
|
68
|
-
| exponential
|
|
69
|
-
| polynomial(degree :: NumInteger%(check-positive-degree))
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
fun check-positive-sides(v :: Number) -> Boolean block:
|
|
73
|
-
when v < 0:
|
|
74
|
-
raise("regular-polygon-shape: number of sides must be non-negative")
|
|
75
|
-
end
|
|
76
|
-
true
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
fun check-valid-dent(v :: Number) -> Boolean block:
|
|
80
|
-
when (v < 0) or (v > 1):
|
|
81
|
-
raise("regular-polygon-shape: dent must be between 0 and 1")
|
|
82
|
-
end
|
|
83
|
-
true
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
data PointShape:
|
|
87
|
-
| circle-shape
|
|
88
|
-
| regular-polygon-shape(sides :: NumInteger%(check-positive-sides), dent :: Number%(check-valid-dent))
|
|
89
|
-
end
|
|
90
57
|
|
|
91
58
|
################################################################################
|
|
92
59
|
# HELPERS
|
|
@@ -147,7 +114,7 @@ fun table2-to-list<A>(table :: RawArray<RawArray<A>>) -> List<List<A>>:
|
|
|
147
114
|
end
|
|
148
115
|
|
|
149
116
|
fun get-vs-from-img(s :: String, raw-img :: IM.Image) -> VS.ValueSkeleton:
|
|
150
|
-
|
|
117
|
+
IS.color(190, 190, 190, 0.75)
|
|
151
118
|
^ IM.text-font(s, 72, _, "", "modern", "normal", "bold", false)
|
|
152
119
|
^ IM.overlay-align("center", "bottom", _, raw-img)
|
|
153
120
|
^ VS.vs-value
|
|
@@ -187,7 +154,7 @@ fun num-to-scientific(base :: Number) -> (Number -> SciNumber) block:
|
|
|
187
154
|
Currently only works with bases > 1.
|
|
188
155
|
```
|
|
189
156
|
when base <= 1:
|
|
190
|
-
raise("Num-to-scientific: Only defined on bases > 1")
|
|
157
|
+
raise(ERR.message-exception("Num-to-scientific: Only defined on bases > 1"))
|
|
191
158
|
end
|
|
192
159
|
|
|
193
160
|
fun convert(n :: Number):
|
|
@@ -222,7 +189,7 @@ fun string-to-stacktype(s :: String) -> StackType:
|
|
|
222
189
|
| string-equal(s, 'absolute') then: absolute
|
|
223
190
|
| string-equal(s, 'percent') then: percent
|
|
224
191
|
| string-equal(s, 'relative') then: relative
|
|
225
|
-
| otherwise: raise('type must be absolute, relative, percent, or none')
|
|
192
|
+
| otherwise: raise(ERR.message-exception('type must be absolute, relative, percent, or none'))
|
|
226
193
|
end
|
|
227
194
|
end
|
|
228
195
|
|
|
@@ -289,46 +256,46 @@ type BoxData = {
|
|
|
289
256
|
}
|
|
290
257
|
|
|
291
258
|
fun get-box-data(label :: String, lst :: List<Number>) -> BoxData:
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
259
|
+
n = lst.length()
|
|
260
|
+
shadow lst = lst.sort()
|
|
261
|
+
median = ST.median(lst)
|
|
262
|
+
{first-quartile; third-quartile} = if num-modulo(n, 2) == 0:
|
|
263
|
+
splitted = lst.split-at(n / 2)
|
|
264
|
+
{ST.median(splitted.prefix); ST.median(splitted.suffix)}
|
|
265
|
+
else:
|
|
266
|
+
splitted = lst.split-at((n - 1) / 2)
|
|
267
|
+
{ST.median(splitted.prefix); ST.median(splitted.suffix.rest)}
|
|
268
|
+
end
|
|
269
|
+
iqr = third-quartile - first-quartile
|
|
270
|
+
high-outliers = for filter(shadow n from lst):
|
|
271
|
+
n > (third-quartile + (1.5 * iqr))
|
|
272
|
+
end
|
|
273
|
+
low-outliers = for filter(shadow n from lst):
|
|
274
|
+
n < (first-quartile - (1.5 * iqr))
|
|
275
|
+
end
|
|
276
|
+
min-val = lst.first
|
|
277
|
+
max-val = lst.last()
|
|
278
|
+
low-whisker = lst.get(low-outliers.length())
|
|
279
|
+
high-whisker = lst.get(n - high-outliers.length() - 1)
|
|
280
|
+
{
|
|
281
|
+
label: label,
|
|
282
|
+
max-val: max-val,
|
|
283
|
+
min-val: min-val,
|
|
284
|
+
first-quartile: first-quartile,
|
|
285
|
+
median: median,
|
|
286
|
+
third-quartile: third-quartile,
|
|
287
|
+
high-whisker: high-whisker,
|
|
288
|
+
low-whisker: low-whisker,
|
|
289
|
+
high-outliers: builtins.raw-array-from-list(high-outliers),
|
|
290
|
+
low-outliers: builtins.raw-array-from-list(low-outliers)
|
|
291
|
+
}
|
|
325
292
|
end
|
|
326
293
|
|
|
327
294
|
################################################################################
|
|
328
295
|
# METHODS
|
|
329
296
|
################################################################################
|
|
330
297
|
|
|
331
|
-
color-method = method(self, color ::
|
|
298
|
+
color-method = method(self, color :: IS.Color):
|
|
332
299
|
self.constr()(self.obj.{color: some(color)})
|
|
333
300
|
end
|
|
334
301
|
|
|
@@ -337,30 +304,30 @@ color-list-method = method(self, colors :: CL.LoC):
|
|
|
337
304
|
| empty => self.constr()(self.obj.{colors: none})
|
|
338
305
|
| link(_, _) =>
|
|
339
306
|
block:
|
|
340
|
-
each({(c ::
|
|
307
|
+
each({(c :: IS.Color): c}, colors)
|
|
341
308
|
self.constr()(self.obj.{colors: some(colors ^ builtins.raw-array-from-list)})
|
|
342
309
|
end
|
|
343
310
|
end
|
|
344
311
|
end
|
|
345
312
|
|
|
346
|
-
pointer-color-method = method(self, color ::
|
|
313
|
+
pointer-color-method = method(self, color :: IS.Color):
|
|
347
314
|
self.constr()(self.obj.{pointer-color: some(color)})
|
|
348
315
|
end
|
|
349
316
|
|
|
350
|
-
interval-color-method = method(self, color ::
|
|
317
|
+
interval-color-method = method(self, color :: IS.Color):
|
|
351
318
|
self.constr()(self.obj.{default-interval-color: some(color)})
|
|
352
319
|
end
|
|
353
320
|
|
|
354
321
|
line-width-method = method(self, lineWidth :: Number) block:
|
|
355
322
|
when lineWidth < 0:
|
|
356
|
-
raise("line-width: Line Width must be non-negative")
|
|
323
|
+
raise(ERR.message-exception("line-width: Line Width must be non-negative"))
|
|
357
324
|
end
|
|
358
325
|
self.constr()(self.obj.{lineWidth: lineWidth})
|
|
359
326
|
end
|
|
360
327
|
|
|
361
328
|
style-method = method(self, style :: String) block:
|
|
362
329
|
when not(string-equal(style, "sticks")) and not(string-equal(style, "bars")) and not(string-equal(style, "boxes")):
|
|
363
|
-
raise("style: must be either sticks, bars, or boxes")
|
|
330
|
+
raise(ERR.message-exception("style: must be either sticks, bars, or boxes"))
|
|
364
331
|
end
|
|
365
332
|
self.constr()(self.obj.{style: style})
|
|
366
333
|
end
|
|
@@ -374,7 +341,7 @@ end
|
|
|
374
341
|
labels-method = method(self, labels :: CL.LoS):
|
|
375
342
|
block:
|
|
376
343
|
when self.obj!ps.length() <> labels.length():
|
|
377
|
-
raise('plot: xs and labels should have the same length')
|
|
344
|
+
raise(ERR.message-exception('plot: xs and labels should have the same length'))
|
|
378
345
|
end
|
|
379
346
|
self.constr()(self.obj.{ps: map2({(arr, label): raw-array-set(arr, 2, label)}, self.obj!ps, labels)})
|
|
380
347
|
end
|
|
@@ -383,7 +350,7 @@ end
|
|
|
383
350
|
image-labels-method = method(self, images :: CL.LoI):
|
|
384
351
|
block:
|
|
385
352
|
when self.obj!ps.length() <> images.length():
|
|
386
|
-
raise('plot: xs and images should have the same length')
|
|
353
|
+
raise(ERR.message-exception('plot: xs and images should have the same length'))
|
|
387
354
|
end
|
|
388
355
|
self.constr()(self.obj.{ps: map2({(arr, image): raw-array-set(arr, 3, image)}, self.obj!ps, images)})
|
|
389
356
|
end
|
|
@@ -391,11 +358,11 @@ end
|
|
|
391
358
|
|
|
392
359
|
explode-method = method(self, offsets :: CL.LoN) block:
|
|
393
360
|
when raw-array-length(self.obj!tab) <> offsets.length():
|
|
394
|
-
raise('exploding-pie-chart: labels and offsets should have the same length')
|
|
361
|
+
raise(ERR.message-exception('exploding-pie-chart: labels and offsets should have the same length'))
|
|
395
362
|
end
|
|
396
363
|
for each(offset from offsets):
|
|
397
364
|
when (offset < 0) or (offset > 1):
|
|
398
|
-
raise('exploding-pie-chart: offset must be between 0 and 1')
|
|
365
|
+
raise(ERR.message-exception('exploding-pie-chart: offset must be between 0 and 1'))
|
|
399
366
|
end
|
|
400
367
|
end
|
|
401
368
|
self.constr()(self.obj.{tab: raw-array-from-list(map2({(arr, offset): raw-array-set(arr, 2, offset)}, raw-array-to-list(self.obj!tab), offsets))})
|
|
@@ -403,17 +370,17 @@ end
|
|
|
403
370
|
|
|
404
371
|
histogram-label-method = method(self, labels :: CL.LoS) block:
|
|
405
372
|
when raw-array-length(self.obj!tab) <> labels.length():
|
|
406
|
-
raise('histogram: xs and labels should have the same length')
|
|
373
|
+
raise(ERR.message-exception('histogram: xs and labels should have the same length'))
|
|
407
374
|
end
|
|
408
375
|
self.constr()(self.obj.{tab: raw-array-from-list(map2({(arr, label): raw-array-set(arr, 0, label)}, raw-array-to-list(self.obj!tab), labels))})
|
|
409
376
|
end
|
|
410
377
|
|
|
411
378
|
box-labels-method = method(self, labels :: CL.LoS) block:
|
|
412
379
|
when labels.length() <> self.obj!values.length():
|
|
413
|
-
raise('labeled-box-plot: labels and values should have the same length')
|
|
380
|
+
raise(ERR.message-exception('labeled-box-plot: labels and values should have the same length'))
|
|
414
381
|
end
|
|
415
382
|
when labels.length() == 0:
|
|
416
|
-
raise('labeled-box-plot: expect at least one box')
|
|
383
|
+
raise(ERR.message-exception('labeled-box-plot: expect at least one box'))
|
|
417
384
|
end
|
|
418
385
|
self.constr()(self.obj.{tab: map2(get-box-data, labels, self.obj!values) ^ builtins.raw-array-from-list,})
|
|
419
386
|
end
|
|
@@ -423,7 +390,7 @@ threeD-method = method(self, threeD :: Boolean):
|
|
|
423
390
|
end
|
|
424
391
|
|
|
425
392
|
piehole-method = method(self, piehole :: Number):
|
|
426
|
-
if (piehole < 0) or (piehole > 1): raise("piehole: Value must be between 0 and 1")
|
|
393
|
+
if (piehole < 0) or (piehole > 1): raise(ERR.message-exception("piehole: Value must be between 0 and 1"))
|
|
427
394
|
else: self.constr()(self.obj.{piehole: piehole})
|
|
428
395
|
end
|
|
429
396
|
end
|
|
@@ -434,7 +401,7 @@ end
|
|
|
434
401
|
|
|
435
402
|
collapse-threshold-method = method(self, collapseThreshold :: Number) block:
|
|
436
403
|
when (collapseThreshold < 0) or (collapseThreshold > 1):
|
|
437
|
-
raise("collapse-threshold: Threshold must be between 0 and 1")
|
|
404
|
+
raise(ERR.message-exception("collapse-threshold: Threshold must be between 0 and 1"))
|
|
438
405
|
end
|
|
439
406
|
self.constr()(self.obj.{collapseThreshold: collapseThreshold})
|
|
440
407
|
end
|
|
@@ -442,26 +409,26 @@ end
|
|
|
442
409
|
trendline-type-method = method(self, trendlineType :: TrendlineType):
|
|
443
410
|
cases (TrendlineType) trendlineType:
|
|
444
411
|
| no-trendline => self.constr()(self.obj.{trendlineType: none})
|
|
445
|
-
| linear => self.constr()(self.obj.{trendlineType: some("poly"), trendlineDegree: 1})
|
|
446
|
-
| exponential => self.constr()(self.obj.{trendlineType: some("exp")})
|
|
447
|
-
| polynomial(degree) => self.constr()(self.obj.{trendlineType: some("poly"), trendlineDegree: degree})
|
|
412
|
+
| tl-linear => self.constr()(self.obj.{trendlineType: some("poly"), trendlineDegree: 1})
|
|
413
|
+
| tl-exponential => self.constr()(self.obj.{trendlineType: some("exp")})
|
|
414
|
+
| tl-polynomial(degree) => self.constr()(self.obj.{trendlineType: some("poly"), trendlineDegree: degree})
|
|
448
415
|
end
|
|
449
416
|
|
|
450
417
|
end
|
|
451
418
|
|
|
452
|
-
trendline-color-method = method(self, color ::
|
|
419
|
+
trendline-color-method = method(self, color :: IS.Color):
|
|
453
420
|
self.constr()(self.obj.{trendlineColor: some(color)})
|
|
454
421
|
end
|
|
455
422
|
|
|
456
423
|
trendline-width-method = method(self, lineWidth :: Number) block:
|
|
457
424
|
when lineWidth < 0:
|
|
458
|
-
raise("trendline-width: Trendline Width must be non-negative")
|
|
425
|
+
raise(ERR.message-exception("trendline-width: Trendline Width must be non-negative"))
|
|
459
426
|
end
|
|
460
427
|
self.constr()(self.obj.{trendlineWidth: lineWidth})
|
|
461
428
|
end
|
|
462
429
|
|
|
463
430
|
trendline-opacity-method = method(self, opacity :: Number):
|
|
464
|
-
if (opacity < 0) or (opacity > 1): raise("Trendline opacity: Value must be between 0 and 1")
|
|
431
|
+
if (opacity < 0) or (opacity > 1): raise(ERR.message-exception("Trendline opacity: Value must be between 0 and 1"))
|
|
465
432
|
else: self.constr()(self.obj.{trendlineOpacity: opacity})
|
|
466
433
|
end
|
|
467
434
|
end
|
|
@@ -472,7 +439,7 @@ end
|
|
|
472
439
|
|
|
473
440
|
dashed-line-style-method = method(self, dashed-line-style :: CL.LoNi) block:
|
|
474
441
|
when any({(n): n < 0}, dashed-line-style):
|
|
475
|
-
raise("Dashed Line Style: Values must be non-negative")
|
|
442
|
+
raise(ERR.message-exception("Dashed Line Style: Values must be non-negative"))
|
|
476
443
|
end
|
|
477
444
|
self.constr()(self.obj.{dashedLine: true, dashlineStyle: raw-array-from-list(dashed-line-style)})
|
|
478
445
|
end
|
|
@@ -488,18 +455,18 @@ select-multiple-method = method(self, multiple :: Boolean):
|
|
|
488
455
|
self.constr()(self.obj.{multiple: multiple})
|
|
489
456
|
end
|
|
490
457
|
|
|
491
|
-
background-color-method = method(self, color ::
|
|
458
|
+
background-color-method = method(self, color :: IS.Color):
|
|
492
459
|
self.constr()(self.obj.{backgroundColor: some(color)})
|
|
493
460
|
end
|
|
494
461
|
|
|
495
462
|
background-border-method = method(self, border-size :: Number) block:
|
|
496
463
|
when border-size < 0:
|
|
497
|
-
raise("border-size: Border Size must be non-negative")
|
|
464
|
+
raise(ERR.message-exception("border-size: Border Size must be non-negative"))
|
|
498
465
|
end
|
|
499
466
|
self.constr()(self.obj.{borderSize: border-size})
|
|
500
467
|
end
|
|
501
468
|
|
|
502
|
-
border-color-method = method(self, border-color ::
|
|
469
|
+
border-color-method = method(self, border-color :: IS.Color):
|
|
503
470
|
self.constr()(self.obj.{borderColor: some(border-color)})
|
|
504
471
|
end
|
|
505
472
|
|
|
@@ -515,24 +482,24 @@ show-grid-lines-method = method(self, is-showing :: Boolean):
|
|
|
515
482
|
self.constr()(self.obj.{show-grid-lines: is-showing})
|
|
516
483
|
end
|
|
517
484
|
|
|
518
|
-
gridlines-color-method = method(self, color ::
|
|
485
|
+
gridlines-color-method = method(self, color :: IS.Color):
|
|
519
486
|
self.constr()(self.obj.{show-grid-lines: true, gridlineColor: some(color)})
|
|
520
487
|
end
|
|
521
488
|
|
|
522
|
-
minor-gridlines-color-method = method(self, color ::
|
|
489
|
+
minor-gridlines-color-method = method(self, color :: IS.Color):
|
|
523
490
|
self.constr()(self.obj.{show-minor-grid-lines: true, minorGridlineColor: some(color)})
|
|
524
491
|
end
|
|
525
492
|
|
|
526
493
|
gridlines-min-spacing-method = method(self, minspacing :: Number) block:
|
|
527
494
|
when minspacing < 0:
|
|
528
|
-
raise("gridlines-minspacing: Min spacing must be non-negative")
|
|
495
|
+
raise(ERR.message-exception("gridlines-minspacing: Min spacing must be non-negative"))
|
|
529
496
|
end
|
|
530
497
|
self.constr()(self.obj.{gridlineMinspacing: some(minspacing)})
|
|
531
498
|
end
|
|
532
499
|
|
|
533
500
|
minor-gridlines-min-spacing-method = method(self, minspacing :: Number) block:
|
|
534
501
|
when minspacing < 0:
|
|
535
|
-
raise("minor-gridlines-minspacing: Min spacing must be non-negative")
|
|
502
|
+
raise(ERR.message-exception("minor-gridlines-minspacing: Min spacing must be non-negative"))
|
|
536
503
|
end
|
|
537
504
|
self.constr()(self.obj.{show-minor-grid-lines: true, minorGridlineMinspacing: minspacing})
|
|
538
505
|
end
|
|
@@ -545,6 +512,14 @@ y-axis-method = method(self, y-axis :: String):
|
|
|
545
512
|
self.constr()(self.obj.{y-axis: y-axis})
|
|
546
513
|
end
|
|
547
514
|
|
|
515
|
+
x-axis-type-method = method(self, x-axis-type :: AxisType):
|
|
516
|
+
self.constr()(self.obj.{x-axis-type: x-axis-type})
|
|
517
|
+
end
|
|
518
|
+
|
|
519
|
+
y-axis-type-method = method(self, y-axis-type :: AxisType):
|
|
520
|
+
self.constr()(self.obj.{y-axis-type: y-axis-type})
|
|
521
|
+
end
|
|
522
|
+
|
|
548
523
|
x-min-method = method(self, x-min :: Number):
|
|
549
524
|
self.constr()(self.obj.{x-min: some(x-min)})
|
|
550
525
|
end
|
|
@@ -648,10 +623,10 @@ axis-pointer-method = method(self,
|
|
|
648
623
|
|
|
649
624
|
# Edge Case Error Checking
|
|
650
625
|
when not(distinctTVLen == TVLen):
|
|
651
|
-
raise('add-pointers: pointers cannot overlap')
|
|
626
|
+
raise(ERR.message-exception('add-pointers: pointers cannot overlap'))
|
|
652
627
|
end
|
|
653
628
|
when not(TVLen == TLLen):
|
|
654
|
-
raise('add-pointers: pointers values and names should have the same length')
|
|
629
|
+
raise(ERR.message-exception('add-pointers: pointers values and names should have the same length'))
|
|
655
630
|
end
|
|
656
631
|
|
|
657
632
|
ticks = fold2({(acc, e1, e2): link(pointer(e1, e2), acc)}, empty, tickLabels, tickValues)
|
|
@@ -697,7 +672,7 @@ end
|
|
|
697
672
|
format-axis-data-method = method(self, format-func :: (Number -> String)):
|
|
698
673
|
cases (Option) self.obj!axisdata:
|
|
699
674
|
| none =>
|
|
700
|
-
raise("Axis properties initialized improperly. Please report as a bug!")
|
|
675
|
+
raise(ERR.message-exception("Axis properties initialized improperly. Please report as a bug!"))
|
|
701
676
|
| some(ad) =>
|
|
702
677
|
ad-tick-list = ad.ticks ^ raw-array-to-list
|
|
703
678
|
new-ticks = map({(p): pointer(format-func(p.value), p.value)}, ad-tick-list) ^ builtins.raw-array-from-list
|
|
@@ -756,7 +731,7 @@ stacking-type-method = method(self, stack-type :: StackType):
|
|
|
756
731
|
{max-positive-height; max-negative-height} =
|
|
757
732
|
multi-prep-axis(grouped, value-lists)
|
|
758
733
|
new-self.make-axis(max-positive-height, max-negative-height)
|
|
759
|
-
| otherwise: raise('stacking-type: type must be absolute, relative, percent, or grouped')
|
|
734
|
+
| otherwise: raise(ERR.message-exception('stacking-type: type must be absolute, relative, percent, or grouped'))
|
|
760
735
|
end
|
|
761
736
|
end
|
|
762
737
|
|
|
@@ -766,10 +741,10 @@ annotations-method = method(self,
|
|
|
766
741
|
expected-length = raw-array-length(self.obj.annotations)
|
|
767
742
|
given-length = annotations.length()
|
|
768
743
|
when given-length <> expected-length:
|
|
769
|
-
raise("annotations: input dimensions mismatch. Expected length "
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
744
|
+
raise(ERR.message-exception("annotations: input dimensions mismatch. Expected length "
|
|
745
|
+
+ num-to-string(expected-length)
|
|
746
|
+
+ ", received "
|
|
747
|
+
+ num-to-string(given-length)))
|
|
773
748
|
end
|
|
774
749
|
|
|
775
750
|
block:
|
|
@@ -786,12 +761,12 @@ annotations-method = method(self,
|
|
|
786
761
|
shadow expected-length = raw-array-length(expected)
|
|
787
762
|
shadow given-length = given.length()
|
|
788
763
|
when given-length <> expected-length:
|
|
789
|
-
raise("annotations: length mismatch on row "
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
764
|
+
raise(ERR.message-exception("annotations: length mismatch on row "
|
|
765
|
+
+ num-to-string(index)
|
|
766
|
+
+ ". Expected "
|
|
767
|
+
+ num-to-string(expected-length)
|
|
768
|
+
+ ", received "
|
|
769
|
+
+ num-to-string(given-length)))
|
|
795
770
|
end
|
|
796
771
|
end
|
|
797
772
|
end
|
|
@@ -808,10 +783,10 @@ intervals-method = method(self, intervals :: CL.LoLoLoN) block:
|
|
|
808
783
|
expected-length = raw-array-length(self.obj.intervals)
|
|
809
784
|
given-length = intervals.length()
|
|
810
785
|
when given-length <> expected-length:
|
|
811
|
-
raise("intervals: input dimensions mismatch. Expected length "
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
786
|
+
raise(ERR.message-exception("intervals: input dimensions mismatch. Expected length "
|
|
787
|
+
+ num-to-string(expected-length)
|
|
788
|
+
+ ", received "
|
|
789
|
+
+ num-to-string(given-length)))
|
|
815
790
|
end
|
|
816
791
|
block:
|
|
817
792
|
each({(l :: List<List<Number>>):
|
|
@@ -823,12 +798,12 @@ intervals-method = method(self, intervals :: CL.LoLoLoN) block:
|
|
|
823
798
|
shadow expected-length = raw-array-length(expected)
|
|
824
799
|
shadow given-length = given.length()
|
|
825
800
|
when given-length <> expected-length:
|
|
826
|
-
raise("intervals: length mismatch on row "
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
801
|
+
raise(ERR.message-exception("intervals: length mismatch on row "
|
|
802
|
+
+ num-to-string(index)
|
|
803
|
+
+ ". Expected "
|
|
804
|
+
+ num-to-string(expected-length)
|
|
805
|
+
+ ", received "
|
|
806
|
+
+ num-to-string(given-length)))
|
|
832
807
|
end
|
|
833
808
|
end
|
|
834
809
|
end
|
|
@@ -854,10 +829,10 @@ error-bars-method = method(self, errors :: CL.LoLoLoN) block:
|
|
|
854
829
|
expected-length = raw-array-length(self.obj.intervals)
|
|
855
830
|
given-length = errors.length()
|
|
856
831
|
when given-length <> expected-length:
|
|
857
|
-
raise("error-bars: input dimensions mismatch. Expected length "
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
832
|
+
raise(ERR.message-exception("error-bars: input dimensions mismatch. Expected length "
|
|
833
|
+
+ num-to-string(expected-length)
|
|
834
|
+
+ ", received "
|
|
835
|
+
+ num-to-string(given-length)))
|
|
861
836
|
end
|
|
862
837
|
block:
|
|
863
838
|
each({(l :: List<List<Number>>):
|
|
@@ -871,26 +846,26 @@ error-bars-method = method(self, errors :: CL.LoLoLoN) block:
|
|
|
871
846
|
shadow given-length = given.length()
|
|
872
847
|
row-str = num-to-string(index)
|
|
873
848
|
when given-length <> expected-length:
|
|
874
|
-
raise("error-bars: length mismatch on row " + row-str
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
849
|
+
raise(ERR.message-exception("error-bars: length mismatch on row " + row-str
|
|
850
|
+
+ ". Expected "
|
|
851
|
+
+ num-to-string(expected-length)
|
|
852
|
+
+ ", received "
|
|
853
|
+
+ num-to-string(given-length)))
|
|
879
854
|
end
|
|
880
855
|
for each2(pair from given, column from range(0, given.length())):
|
|
881
856
|
block:
|
|
882
857
|
col-str = num-to-string(column)
|
|
883
858
|
when pair.length() <> 2:
|
|
884
|
-
raise("error-bars: on row " + row-str + " column " + col-str
|
|
885
|
-
|
|
859
|
+
raise(ERR.message-exception("error-bars: on row " + row-str + " column " + col-str
|
|
860
|
+
+ ", 2 intervals must be given."))
|
|
886
861
|
end
|
|
887
862
|
when pair.get(0) > 0:
|
|
888
|
-
raise("error-bars: on row " + row-str + " column " + col-str
|
|
889
|
-
|
|
863
|
+
raise(ERR.message-exception("error-bars: on row " + row-str + " column " + col-str
|
|
864
|
+
+ ", first pair must be non-positive."))
|
|
890
865
|
end
|
|
891
866
|
when pair.get(1) < 0:
|
|
892
|
-
raise("error-bars: on row " + row-str + " column " + col-str
|
|
893
|
-
|
|
867
|
+
raise(ERR.message-exception("error-bars: on row " + row-str + " column " + col-str
|
|
868
|
+
+ ", second pair must be non-negative."))
|
|
894
869
|
end
|
|
895
870
|
end
|
|
896
871
|
end
|
|
@@ -916,10 +891,10 @@ single-error-bars-method = method(self, errors :: CL.LoLoN) block:
|
|
|
916
891
|
expected-length = raw-array-length(self.obj.intervals)
|
|
917
892
|
given-length = errors.length()
|
|
918
893
|
when given-length <> expected-length:
|
|
919
|
-
raise("error-bars: input dimensions mismatch. Expected length "
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
894
|
+
raise(ERR.message-exception("error-bars: input dimensions mismatch. Expected length "
|
|
895
|
+
+ num-to-string(expected-length)
|
|
896
|
+
+ ", received "
|
|
897
|
+
+ num-to-string(given-length)))
|
|
923
898
|
end
|
|
924
899
|
block:
|
|
925
900
|
each({(l :: List<Number>):
|
|
@@ -931,17 +906,17 @@ single-error-bars-method = method(self, errors :: CL.LoLoN) block:
|
|
|
931
906
|
block:
|
|
932
907
|
row-str = num-to-string(index)
|
|
933
908
|
when given.length() <> 2:
|
|
934
|
-
raise("error-bars: on row " + row-str
|
|
935
|
-
|
|
936
|
-
|
|
909
|
+
raise(ERR.message-exception("error-bars: on row " + row-str
|
|
910
|
+
+ ", 2 intervals must be given (received "
|
|
911
|
+
+ num-to-string(given.length()) + ")."))
|
|
937
912
|
end
|
|
938
913
|
when given.get(0) > 0:
|
|
939
|
-
raise("error-bars: on row " + row-str
|
|
940
|
-
|
|
914
|
+
raise(ERR.message-exception("error-bars: on row " + row-str
|
|
915
|
+
+ ", first pair must be non-positive."))
|
|
941
916
|
end
|
|
942
917
|
when given.get(1) < 0:
|
|
943
|
-
raise("error-bars: on row " + row-str
|
|
944
|
-
|
|
918
|
+
raise(ERR.message-exception("error-bars: on row " + row-str
|
|
919
|
+
+ ", second pair must be non-negative."))
|
|
945
920
|
end
|
|
946
921
|
end
|
|
947
922
|
end
|
|
@@ -968,6 +943,13 @@ horizontal-method = method(self, b :: Boolean):
|
|
|
968
943
|
self.constr()(self.obj.{horizontal: b})
|
|
969
944
|
end
|
|
970
945
|
|
|
946
|
+
bandwidth-method = method(self, width :: Number) block:
|
|
947
|
+
when (width <= 0) or (width > 1):
|
|
948
|
+
raise(ERR.message-exception("bandwidth: must be greater than zero and at most 1, given " + num-to-string(width)))
|
|
949
|
+
end
|
|
950
|
+
self.constr()(self.obj.{bandwidth: width})
|
|
951
|
+
end
|
|
952
|
+
|
|
971
953
|
################################################################################
|
|
972
954
|
# BOUNDING BOX
|
|
973
955
|
################################################################################
|
|
@@ -1053,7 +1035,7 @@ type BoxChartSeries = {
|
|
|
1053
1035
|
tab :: TableIntern,
|
|
1054
1036
|
height :: Number,
|
|
1055
1037
|
horizontal :: Boolean,
|
|
1056
|
-
color :: Option<
|
|
1038
|
+
color :: Option<IS.Color>,
|
|
1057
1039
|
}
|
|
1058
1040
|
|
|
1059
1041
|
default-box-plot-series = {
|
|
@@ -1064,7 +1046,7 @@ default-box-plot-series = {
|
|
|
1064
1046
|
|
|
1065
1047
|
type PieChartSeries = {
|
|
1066
1048
|
tab :: TableIntern,
|
|
1067
|
-
colors :: Option<RawArray<
|
|
1049
|
+
colors :: Option<RawArray<IS.Color>>,
|
|
1068
1050
|
threeD :: Boolean,
|
|
1069
1051
|
piehole :: Number,
|
|
1070
1052
|
startingAngle :: Number,
|
|
@@ -1082,14 +1064,15 @@ default-pie-chart-series = {
|
|
|
1082
1064
|
type BarChartSeries = {
|
|
1083
1065
|
tab :: TableIntern,
|
|
1084
1066
|
axisdata :: Option<AxisData>,
|
|
1085
|
-
color :: Option<
|
|
1086
|
-
colors :: Option<RawArray<
|
|
1067
|
+
color :: Option<IS.Color>,
|
|
1068
|
+
colors :: Option<RawArray<IS.Color>>,
|
|
1087
1069
|
pointers :: Option<RawArray<Pointer>>,
|
|
1088
|
-
pointer-color :: Option<
|
|
1070
|
+
pointer-color :: Option<IS.Color>,
|
|
1089
1071
|
horizontal :: Boolean,
|
|
1072
|
+
bandwidth :: Number,
|
|
1090
1073
|
annotations :: RawArray<RawArray<Option<String>>>,
|
|
1091
1074
|
intervals :: RawArray<RawArray<RawArray<Number>>>,
|
|
1092
|
-
default-interval-color :: Option<
|
|
1075
|
+
default-interval-color :: Option<IS.Color>,
|
|
1093
1076
|
}
|
|
1094
1077
|
|
|
1095
1078
|
default-bar-chart-series = {
|
|
@@ -1098,7 +1081,8 @@ default-bar-chart-series = {
|
|
|
1098
1081
|
pointers: none,
|
|
1099
1082
|
pointer-color: none,
|
|
1100
1083
|
axisdata: none,
|
|
1101
|
-
horizontal: false,
|
|
1084
|
+
horizontal: false,
|
|
1085
|
+
bandwidth: 0.8,
|
|
1102
1086
|
default-interval-color: none,
|
|
1103
1087
|
}
|
|
1104
1088
|
|
|
@@ -1107,13 +1091,14 @@ type MultiBarChartSeries = {
|
|
|
1107
1091
|
axisdata :: Option<AxisData>,
|
|
1108
1092
|
legends :: RawArray<String>,
|
|
1109
1093
|
is-stacked :: String,
|
|
1110
|
-
colors :: Option<RawArray<
|
|
1094
|
+
colors :: Option<RawArray<IS.Color>>,
|
|
1111
1095
|
pointers :: Option<RawArray<Pointer>>,
|
|
1112
|
-
pointer-color :: Option<
|
|
1096
|
+
pointer-color :: Option<IS.Color>,
|
|
1113
1097
|
horizontal :: Boolean,
|
|
1098
|
+
bandwidth :: Number,
|
|
1114
1099
|
annotations :: RawArray<RawArray<Option<String>>>,
|
|
1115
1100
|
intervals :: RawArray<RawArray<RawArray<Number>>>,
|
|
1116
|
-
default-interval-color :: Option<
|
|
1101
|
+
default-interval-color :: Option<IS.Color>
|
|
1117
1102
|
}
|
|
1118
1103
|
|
|
1119
1104
|
default-multi-bar-chart-series = {
|
|
@@ -1122,7 +1107,8 @@ default-multi-bar-chart-series = {
|
|
|
1122
1107
|
pointers: none,
|
|
1123
1108
|
pointer-color: none,
|
|
1124
1109
|
axisdata: none,
|
|
1125
|
-
horizontal: false,
|
|
1110
|
+
horizontal: false,
|
|
1111
|
+
bandwidth: 0.8,
|
|
1126
1112
|
default-interval-color: none
|
|
1127
1113
|
}
|
|
1128
1114
|
|
|
@@ -1131,7 +1117,7 @@ type HistogramSeries = {
|
|
|
1131
1117
|
bin-width :: Option<Number>,
|
|
1132
1118
|
max-num-bins :: Option<Number>,
|
|
1133
1119
|
min-num-bins :: Option<Number>,
|
|
1134
|
-
color :: Option<
|
|
1120
|
+
color :: Option<IS.Color>,
|
|
1135
1121
|
}
|
|
1136
1122
|
|
|
1137
1123
|
default-histogram-series = {
|
|
@@ -1143,12 +1129,12 @@ default-histogram-series = {
|
|
|
1143
1129
|
|
|
1144
1130
|
type LinePlotSeries = {
|
|
1145
1131
|
ps :: List<Posn>,
|
|
1146
|
-
color :: Option<
|
|
1132
|
+
color :: Option<IS.Color>,
|
|
1147
1133
|
legend :: String,
|
|
1148
1134
|
curved :: String,
|
|
1149
1135
|
lineWidth :: Number,
|
|
1150
1136
|
trendlineType :: Option<String>,
|
|
1151
|
-
trendlineColor :: Option<
|
|
1137
|
+
trendlineColor :: Option<IS.Color>,
|
|
1152
1138
|
trendlineWidth :: Number,
|
|
1153
1139
|
trendlineOpacity :: Number,
|
|
1154
1140
|
trendlineDegree :: NumInteger,
|
|
@@ -1193,12 +1179,12 @@ type ScatterPoint = {
|
|
|
1193
1179
|
|
|
1194
1180
|
type ScatterPlotSeries = {
|
|
1195
1181
|
ps :: List<ScatterPoint>,
|
|
1196
|
-
color :: Option<
|
|
1182
|
+
color :: Option<IS.Color>,
|
|
1197
1183
|
legend :: String,
|
|
1198
1184
|
point-size :: Number,
|
|
1199
1185
|
useImageSizes :: Boolean,
|
|
1200
1186
|
trendlineType :: Option<String>,
|
|
1201
|
-
trendlineColor :: Option<
|
|
1187
|
+
trendlineColor :: Option<IS.Color>,
|
|
1202
1188
|
trendlineWidth :: Number,
|
|
1203
1189
|
trendlineOpacity :: Number,
|
|
1204
1190
|
trendlineDegree :: NumInteger,
|
|
@@ -1234,7 +1220,7 @@ type DotPoint = {
|
|
|
1234
1220
|
|
|
1235
1221
|
type DotPlotSeries = {
|
|
1236
1222
|
ps :: List<DotPoint>,
|
|
1237
|
-
color :: Option<
|
|
1223
|
+
color :: Option<IS.Color>,
|
|
1238
1224
|
legend :: String,
|
|
1239
1225
|
point-size :: Number,
|
|
1240
1226
|
useImageSizes :: Boolean,
|
|
@@ -1254,7 +1240,7 @@ type CategoricalDotPoint ={
|
|
|
1254
1240
|
|
|
1255
1241
|
type CategoricalDotPlotSeries = {
|
|
1256
1242
|
ps :: List<CategoricalDotPoint>,
|
|
1257
|
-
color :: Option<
|
|
1243
|
+
color :: Option<IS.Color>,
|
|
1258
1244
|
legend :: String
|
|
1259
1245
|
}
|
|
1260
1246
|
|
|
@@ -1272,18 +1258,18 @@ type IntervalPoint = {
|
|
|
1272
1258
|
}
|
|
1273
1259
|
type IntervalChartSeries = {
|
|
1274
1260
|
axisdata :: Option<AxisData>,
|
|
1275
|
-
color :: Option<
|
|
1261
|
+
color :: Option<IS.Color>,
|
|
1276
1262
|
pointers :: Option<RawArray<Pointer>>,
|
|
1277
|
-
pointer-color :: Option<
|
|
1263
|
+
pointer-color :: Option<IS.Color>,
|
|
1278
1264
|
point-size :: Number,
|
|
1279
1265
|
lineWidth :: Number,
|
|
1280
1266
|
stick-width :: Number,
|
|
1281
1267
|
style :: String,
|
|
1282
1268
|
horizontal :: Boolean,
|
|
1283
|
-
default-interval-color :: Option<
|
|
1269
|
+
default-interval-color :: Option<IS.Color>,
|
|
1284
1270
|
legend :: String,
|
|
1285
1271
|
trendlineType :: Option<String>,
|
|
1286
|
-
trendlineColor :: Option<
|
|
1272
|
+
trendlineColor :: Option<IS.Color>,
|
|
1287
1273
|
trendlineWidth :: Number,
|
|
1288
1274
|
trendlineOpacity :: Number,
|
|
1289
1275
|
trendlineDegree :: NumInteger,
|
|
@@ -1322,7 +1308,7 @@ default-interval-chart-series = {
|
|
|
1322
1308
|
|
|
1323
1309
|
type FunctionPlotSeries = {
|
|
1324
1310
|
f :: PlottableFunction,
|
|
1325
|
-
color :: Option<
|
|
1311
|
+
color :: Option<IS.Color>,
|
|
1326
1312
|
legend :: String,
|
|
1327
1313
|
horizontal :: Boolean,
|
|
1328
1314
|
}
|
|
@@ -1339,9 +1325,9 @@ type ChartWindowObject = {
|
|
|
1339
1325
|
title :: String,
|
|
1340
1326
|
width :: Number,
|
|
1341
1327
|
height :: Number,
|
|
1342
|
-
backgroundColor :: Option<
|
|
1328
|
+
backgroundColor :: Option<IS.Color>,
|
|
1343
1329
|
borderSize :: Number,
|
|
1344
|
-
borderColor :: Option<
|
|
1330
|
+
borderColor :: Option<IS.Color>,
|
|
1345
1331
|
render :: ( -> IM.Image)
|
|
1346
1332
|
}
|
|
1347
1333
|
|
|
@@ -1352,18 +1338,20 @@ default-chart-window-object :: ChartWindowObject = {
|
|
|
1352
1338
|
backgroundColor: none,
|
|
1353
1339
|
borderSize: 0,
|
|
1354
1340
|
borderColor: none,
|
|
1355
|
-
method render(self): raise('unimplemented') end,
|
|
1341
|
+
method render(self): raise(ERR.message-exception('unimplemented')) end,
|
|
1356
1342
|
}
|
|
1357
1343
|
|
|
1358
1344
|
type BoxChartWindowObject = {
|
|
1359
1345
|
title :: String,
|
|
1360
1346
|
width :: Number,
|
|
1361
1347
|
height :: Number,
|
|
1362
|
-
backgroundColor :: Option<
|
|
1348
|
+
backgroundColor :: Option<IS.Color>,
|
|
1363
1349
|
borderSize :: Number,
|
|
1364
|
-
borderColor :: Option<
|
|
1350
|
+
borderColor :: Option<IS.Color>,
|
|
1365
1351
|
x-axis :: String,
|
|
1366
1352
|
y-axis :: String,
|
|
1353
|
+
x-axis-type :: AxisType,
|
|
1354
|
+
y-axis-type :: AxisType,
|
|
1367
1355
|
min :: Option<Number>,
|
|
1368
1356
|
max :: Option<Number>,
|
|
1369
1357
|
render :: ( -> IM.Image),
|
|
@@ -1372,6 +1360,8 @@ type BoxChartWindowObject = {
|
|
|
1372
1360
|
default-box-plot-chart-window-object :: BoxChartWindowObject = default-chart-window-object.{
|
|
1373
1361
|
x-axis: '',
|
|
1374
1362
|
y-axis: '',
|
|
1363
|
+
x-axis-type: at-linear,
|
|
1364
|
+
y-axis-type: at-linear,
|
|
1375
1365
|
min: none,
|
|
1376
1366
|
max: none,
|
|
1377
1367
|
}
|
|
@@ -1380,9 +1370,9 @@ type PieChartWindowObject = {
|
|
|
1380
1370
|
title :: String,
|
|
1381
1371
|
width :: Number,
|
|
1382
1372
|
height :: Number,
|
|
1383
|
-
backgroundColor :: Option<
|
|
1373
|
+
backgroundColor :: Option<IS.Color>,
|
|
1384
1374
|
borderSize :: Number,
|
|
1385
|
-
borderColor :: Option<
|
|
1375
|
+
borderColor :: Option<IS.Color>,
|
|
1386
1376
|
render :: ( -> IM.Image),
|
|
1387
1377
|
}
|
|
1388
1378
|
|
|
@@ -1392,29 +1382,39 @@ type DotChartWindowObject = {
|
|
|
1392
1382
|
title :: String,
|
|
1393
1383
|
width :: Number,
|
|
1394
1384
|
height :: Number,
|
|
1395
|
-
backgroundColor :: Option<
|
|
1385
|
+
backgroundColor :: Option<IS.Color>,
|
|
1396
1386
|
borderSize :: Number,
|
|
1397
|
-
borderColor :: Option<
|
|
1387
|
+
borderColor :: Option<IS.Color>,
|
|
1398
1388
|
render :: ( -> IM.Image),
|
|
1399
1389
|
x-axis :: String,
|
|
1400
|
-
y-axis :: String
|
|
1390
|
+
y-axis :: String,
|
|
1391
|
+
x-axis-type :: AxisType,
|
|
1392
|
+
y-axis-type :: AxisType,
|
|
1393
|
+
x-min :: Option<Number>,
|
|
1394
|
+
x-max :: Option<Number>,
|
|
1401
1395
|
}
|
|
1402
1396
|
|
|
1403
1397
|
default-dot-chart-window-object :: DotChartWindowObject = default-chart-window-object.{
|
|
1404
1398
|
x-axis: '',
|
|
1405
|
-
y-axis: ''
|
|
1399
|
+
y-axis: '',
|
|
1400
|
+
x-axis-type: at-linear,
|
|
1401
|
+
y-axis-type: at-linear,
|
|
1402
|
+
x-min: none,
|
|
1403
|
+
x-max: none,
|
|
1406
1404
|
}
|
|
1407
1405
|
|
|
1408
1406
|
type BarChartWindowObject = {
|
|
1409
1407
|
title :: String,
|
|
1410
1408
|
width :: Number,
|
|
1411
1409
|
height :: Number,
|
|
1412
|
-
backgroundColor :: Option<
|
|
1410
|
+
backgroundColor :: Option<IS.Color>,
|
|
1413
1411
|
borderSize :: Number,
|
|
1414
|
-
borderColor :: Option<
|
|
1412
|
+
borderColor :: Option<IS.Color>,
|
|
1415
1413
|
render :: ( -> IM.Image),
|
|
1416
1414
|
x-axis :: String,
|
|
1417
1415
|
y-axis :: String,
|
|
1416
|
+
x-axis-type :: AxisType,
|
|
1417
|
+
y-axis-type :: AxisType,
|
|
1418
1418
|
y-min :: Option<Number>,
|
|
1419
1419
|
y-max :: Option<Number>,
|
|
1420
1420
|
}
|
|
@@ -1422,6 +1422,8 @@ type BarChartWindowObject = {
|
|
|
1422
1422
|
default-bar-chart-window-object :: BarChartWindowObject = default-chart-window-object.{
|
|
1423
1423
|
x-axis: '',
|
|
1424
1424
|
y-axis: '',
|
|
1425
|
+
x-axis-type: at-linear,
|
|
1426
|
+
y-axis-type: at-linear,
|
|
1425
1427
|
y-min: none,
|
|
1426
1428
|
y-max: none,
|
|
1427
1429
|
}
|
|
@@ -1430,12 +1432,14 @@ type IntervalChartWindowObject = {
|
|
|
1430
1432
|
title :: String,
|
|
1431
1433
|
width :: Number,
|
|
1432
1434
|
height :: Number,
|
|
1433
|
-
backgroundColor :: Option<
|
|
1435
|
+
backgroundColor :: Option<IS.Color>,
|
|
1434
1436
|
borderSize :: Number,
|
|
1435
|
-
borderColor :: Option<
|
|
1437
|
+
borderColor :: Option<IS.Color>,
|
|
1436
1438
|
render :: ( -> IM.Image),
|
|
1437
1439
|
x-axis :: String,
|
|
1438
1440
|
y-axis :: String,
|
|
1441
|
+
x-axis-type :: AxisType,
|
|
1442
|
+
y-axis-type :: AxisType,
|
|
1439
1443
|
y-min :: Option<Number>,
|
|
1440
1444
|
y-max :: Option<Number>,
|
|
1441
1445
|
}
|
|
@@ -1443,6 +1447,8 @@ type IntervalChartWindowObject = {
|
|
|
1443
1447
|
default-interval-chart-window-object :: IntervalChartWindowObject = default-chart-window-object.{
|
|
1444
1448
|
x-axis: '',
|
|
1445
1449
|
y-axis: '',
|
|
1450
|
+
x-axis-type: at-linear,
|
|
1451
|
+
y-axis-type: at-linear,
|
|
1446
1452
|
y-min: none,
|
|
1447
1453
|
y-max: none,
|
|
1448
1454
|
}
|
|
@@ -1451,12 +1457,14 @@ type HistogramChartWindowObject = {
|
|
|
1451
1457
|
title :: String,
|
|
1452
1458
|
width :: Number,
|
|
1453
1459
|
height :: Number,
|
|
1454
|
-
backgroundColor :: Option<
|
|
1460
|
+
backgroundColor :: Option<IS.Color>,
|
|
1455
1461
|
borderSize :: Number,
|
|
1456
|
-
borderColor :: Option<
|
|
1462
|
+
borderColor :: Option<IS.Color>,
|
|
1457
1463
|
render :: ( -> IM.Image),
|
|
1458
1464
|
x-axis :: String,
|
|
1459
1465
|
y-axis :: String,
|
|
1466
|
+
x-axis-type :: AxisType,
|
|
1467
|
+
y-axis-type :: AxisType,
|
|
1460
1468
|
x-min :: Option<Number>,
|
|
1461
1469
|
x-max :: Option<Number>,
|
|
1462
1470
|
y-max :: Option<Number>,
|
|
@@ -1466,6 +1474,8 @@ default-histogram-chart-window-object :: HistogramChartWindowObject =
|
|
|
1466
1474
|
default-chart-window-object.{
|
|
1467
1475
|
x-axis: '',
|
|
1468
1476
|
y-axis: '',
|
|
1477
|
+
x-axis-type: at-linear,
|
|
1478
|
+
y-axis-type: at-linear,
|
|
1469
1479
|
x-min: none,
|
|
1470
1480
|
x-max: none,
|
|
1471
1481
|
y-max: none,
|
|
@@ -1475,18 +1485,20 @@ type PlotChartWindowObject = {
|
|
|
1475
1485
|
title :: String,
|
|
1476
1486
|
width :: Number,
|
|
1477
1487
|
height :: Number,
|
|
1478
|
-
backgroundColor :: Option<
|
|
1488
|
+
backgroundColor :: Option<IS.Color>,
|
|
1479
1489
|
borderSize :: Number,
|
|
1480
|
-
borderColor :: Option<
|
|
1490
|
+
borderColor :: Option<IS.Color>,
|
|
1481
1491
|
render :: ( -> IM.Image),
|
|
1482
1492
|
show-grid-lines :: Boolean,
|
|
1483
1493
|
show-minor-grid-lines :: Boolean,
|
|
1484
|
-
gridlineColor :: Option<
|
|
1494
|
+
gridlineColor :: Option<IS.Color>,
|
|
1485
1495
|
gridlineMinspacing :: Option<Number>,
|
|
1486
|
-
minorGridlineColor :: Option<
|
|
1496
|
+
minorGridlineColor :: Option<IS.Color>,
|
|
1487
1497
|
minorGridlineMinspacing :: Number,
|
|
1488
1498
|
x-axis :: String,
|
|
1489
1499
|
y-axis :: String,
|
|
1500
|
+
x-axis-type :: AxisType,
|
|
1501
|
+
y-axis-type :: AxisType,
|
|
1490
1502
|
x-min :: Option<Number>,
|
|
1491
1503
|
x-max :: Option<Number>,
|
|
1492
1504
|
y-min :: Option<Number>,
|
|
@@ -1498,6 +1510,8 @@ type PlotChartWindowObject = {
|
|
|
1498
1510
|
default-plot-chart-window-object :: PlotChartWindowObject = default-chart-window-object.{
|
|
1499
1511
|
x-axis: '',
|
|
1500
1512
|
y-axis: '',
|
|
1513
|
+
x-axis-type: at-linear,
|
|
1514
|
+
y-axis-type: at-linear,
|
|
1501
1515
|
show-grid-lines: true,
|
|
1502
1516
|
show-minor-grid-lines: false,
|
|
1503
1517
|
x-min: none,
|
|
@@ -1535,7 +1549,7 @@ data DataSeries:
|
|
|
1535
1549
|
image-labels: image-labels-method,
|
|
1536
1550
|
method point-size(self, point-size :: Number) block:
|
|
1537
1551
|
when point-size < 0:
|
|
1538
|
-
raise("point-size: Point Size must be non-negative")
|
|
1552
|
+
raise(ERR.message-exception("point-size: Point Size must be non-negative"))
|
|
1539
1553
|
end
|
|
1540
1554
|
self.constr()(self.obj.{point-size: point-size})
|
|
1541
1555
|
end,
|
|
@@ -1557,7 +1571,7 @@ data DataSeries:
|
|
|
1557
1571
|
point-shape: pointshape-method,
|
|
1558
1572
|
method point-size(self, point-size :: Number) block:
|
|
1559
1573
|
when point-size <= 0:
|
|
1560
|
-
raise("point-size: Point Size must be positive")
|
|
1574
|
+
raise(ERR.message-exception("point-size: Point Size must be positive"))
|
|
1561
1575
|
end
|
|
1562
1576
|
self.constr()(self.obj.{point-size: point-size})
|
|
1563
1577
|
end,
|
|
@@ -1574,7 +1588,7 @@ data DataSeries:
|
|
|
1574
1588
|
labels: labels-method,
|
|
1575
1589
|
method point-size(self, point-size :: Number) block:
|
|
1576
1590
|
when point-size < 0:
|
|
1577
|
-
raise("point-size: Point Size must be non-negative")
|
|
1591
|
+
raise(ERR.message-exception("point-size: Point Size must be non-negative"))
|
|
1578
1592
|
end
|
|
1579
1593
|
self.constr()(self.obj.{point-size: point-size})
|
|
1580
1594
|
end,
|
|
@@ -1608,6 +1622,7 @@ data DataSeries:
|
|
|
1608
1622
|
is-single: true,
|
|
1609
1623
|
color: color-method,
|
|
1610
1624
|
colors: color-list-method,
|
|
1625
|
+
bandwidth: bandwidth-method,
|
|
1611
1626
|
sort: default-sort-method,
|
|
1612
1627
|
sort-by: sort-method,
|
|
1613
1628
|
sort-by-label: label-sort-method,
|
|
@@ -1640,7 +1655,7 @@ data DataSeries:
|
|
|
1640
1655
|
trendline-type: trendline-type-method,
|
|
1641
1656
|
method point-size(self, point-size :: Number) block:
|
|
1642
1657
|
when point-size < 0:
|
|
1643
|
-
raise("point-size: Point Size must be non-negative")
|
|
1658
|
+
raise(ERR.message-exception("point-size: Point Size must be non-negative"))
|
|
1644
1659
|
end
|
|
1645
1660
|
self.constr()(self.obj.{point-size: point-size})
|
|
1646
1661
|
end,
|
|
@@ -1653,6 +1668,7 @@ data DataSeries:
|
|
|
1653
1668
|
| multi-bar-chart-series(obj :: MultiBarChartSeries) with:
|
|
1654
1669
|
is-single: true,
|
|
1655
1670
|
colors: color-list-method,
|
|
1671
|
+
bandwidth: bandwidth-method,
|
|
1656
1672
|
sort: super-default-multi-sort-method,
|
|
1657
1673
|
sort-by: default-multi-sort-method,
|
|
1658
1674
|
sort-by-data: multi-sort-method,
|
|
@@ -1710,7 +1726,7 @@ end
|
|
|
1710
1726
|
|
|
1711
1727
|
fun check-chart-window(p :: ChartWindowObject) -> Nothing:
|
|
1712
1728
|
if (p.width <= 0) or (p.height <= 0):
|
|
1713
|
-
raise('render: width and height must be positive')
|
|
1729
|
+
raise(ERR.message-exception('render: width and height must be positive'))
|
|
1714
1730
|
else:
|
|
1715
1731
|
nothing
|
|
1716
1732
|
end
|
|
@@ -1723,28 +1739,40 @@ data ChartWindow:
|
|
|
1723
1739
|
constr: {(): dot-chart-window},
|
|
1724
1740
|
x-axis: x-axis-method,
|
|
1725
1741
|
y-axis: y-axis-method,
|
|
1742
|
+
x-min: x-min-method,
|
|
1743
|
+
x-max: x-max-method,
|
|
1744
|
+
x-axis-type: x-axis-type-method,
|
|
1745
|
+
y-axis-type: y-axis-type-method,
|
|
1726
1746
|
| box-plot-chart-window(obj :: BoxChartWindowObject) with:
|
|
1727
1747
|
constr: {(): box-plot-chart-window},
|
|
1728
1748
|
x-axis: x-axis-method,
|
|
1729
1749
|
y-axis: y-axis-method,
|
|
1750
|
+
x-axis-type: x-axis-type-method,
|
|
1751
|
+
y-axis-type: y-axis-type-method,
|
|
1730
1752
|
min: min-method,
|
|
1731
1753
|
max: max-method,
|
|
1732
1754
|
| bar-chart-window(obj :: BarChartWindowObject) with:
|
|
1733
1755
|
constr: {(): bar-chart-window},
|
|
1734
1756
|
x-axis: x-axis-method,
|
|
1735
1757
|
y-axis: y-axis-method,
|
|
1758
|
+
x-axis-type: x-axis-type-method,
|
|
1759
|
+
y-axis-type: y-axis-type-method,
|
|
1736
1760
|
y-min: y-min-method,
|
|
1737
1761
|
y-max: y-max-method,
|
|
1738
1762
|
| interval-chart-window(obj :: IntervalChartWindowObject) with:
|
|
1739
1763
|
constr: {(): interval-chart-window},
|
|
1740
1764
|
x-axis: x-axis-method,
|
|
1741
1765
|
y-axis: y-axis-method,
|
|
1766
|
+
x-axis-type: x-axis-type-method,
|
|
1767
|
+
y-axis-type: y-axis-type-method,
|
|
1742
1768
|
y-min: y-min-method,
|
|
1743
1769
|
y-max: y-max-method,
|
|
1744
1770
|
| histogram-chart-window(obj :: HistogramChartWindowObject) with:
|
|
1745
1771
|
constr: {(): histogram-chart-window},
|
|
1746
1772
|
x-axis: x-axis-method,
|
|
1747
1773
|
y-axis: y-axis-method,
|
|
1774
|
+
x-axis-type: x-axis-type-method,
|
|
1775
|
+
y-axis-type: y-axis-type-method,
|
|
1748
1776
|
x-min: x-min-method,
|
|
1749
1777
|
x-max: x-max-method,
|
|
1750
1778
|
y-max: y-max-method,
|
|
@@ -1758,6 +1786,8 @@ data ChartWindow:
|
|
|
1758
1786
|
# minor-gridlines-minspacing: minor-gridlines-min-spacing-method,
|
|
1759
1787
|
x-axis: x-axis-method,
|
|
1760
1788
|
y-axis: y-axis-method,
|
|
1789
|
+
x-axis-type: x-axis-type-method,
|
|
1790
|
+
y-axis-type: y-axis-type-method,
|
|
1761
1791
|
x-min: x-min-method,
|
|
1762
1792
|
x-max: x-max-method,
|
|
1763
1793
|
y-min: y-min-method,
|
|
@@ -1765,7 +1795,7 @@ data ChartWindow:
|
|
|
1765
1795
|
select-multiple: select-multiple-method,
|
|
1766
1796
|
method num-samples(self, num-samples :: Number) block:
|
|
1767
1797
|
when (num-samples <= 0) or (num-samples > 100000) or not(num-is-integer(num-samples)):
|
|
1768
|
-
raise('num-samples: value must be an ineger between 1 and 100000')
|
|
1798
|
+
raise(ERR.message-exception('num-samples: value must be an ineger between 1 and 100000'))
|
|
1769
1799
|
end
|
|
1770
1800
|
plot-chart-window(self.obj.{num-samples: num-samples})
|
|
1771
1801
|
end,
|
|
@@ -1781,6 +1811,10 @@ sharing:
|
|
|
1781
1811
|
_ = check-chart-window(self.obj)
|
|
1782
1812
|
self.obj.{interact: false}.render()
|
|
1783
1813
|
end,
|
|
1814
|
+
method get-spec(self):
|
|
1815
|
+
_ = check-chart-window(self.obj)
|
|
1816
|
+
self.obj.{interact: false, vega: true}.render()
|
|
1817
|
+
end,
|
|
1784
1818
|
method title(self, title :: String):
|
|
1785
1819
|
self.constr()(self.obj.{title: title})
|
|
1786
1820
|
end,
|
|
@@ -1807,7 +1841,7 @@ end
|
|
|
1807
1841
|
|
|
1808
1842
|
fun line-plot-from-list(xs :: CL.LoN, ys :: CL.LoN) -> DataSeries block:
|
|
1809
1843
|
when xs.length() <> ys.length():
|
|
1810
|
-
raise('line-plot: xs and ys should have the same length')
|
|
1844
|
+
raise(ERR.message-exception('line-plot: xs and ys should have the same length'))
|
|
1811
1845
|
end
|
|
1812
1846
|
xs.each(check-num)
|
|
1813
1847
|
ys.each(check-num)
|
|
@@ -1818,10 +1852,10 @@ end
|
|
|
1818
1852
|
|
|
1819
1853
|
fun labeled-line-plot-from-list(labels :: CL.LoS, xs :: CL.LoN, ys :: CL.LoN) -> DataSeries block:
|
|
1820
1854
|
when xs.length() <> ys.length():
|
|
1821
|
-
raise('labeled-line-plot: xs and ys should have the same length')
|
|
1855
|
+
raise(ERR.message-exception('labeled-line-plot: xs and ys should have the same length'))
|
|
1822
1856
|
end
|
|
1823
1857
|
when xs.length() <> labels.length():
|
|
1824
|
-
raise('labeled-line-plot: xs and labels should have the same length')
|
|
1858
|
+
raise(ERR.message-exception('labeled-line-plot: xs and labels should have the same length'))
|
|
1825
1859
|
end
|
|
1826
1860
|
xs.each(check-num)
|
|
1827
1861
|
ys.each(check-num)
|
|
@@ -1832,10 +1866,10 @@ end
|
|
|
1832
1866
|
|
|
1833
1867
|
fun image-line-plot-from-list(images :: CL.LoI, xs :: CL.LoN, ys :: CL.LoN) -> DataSeries block:
|
|
1834
1868
|
when xs.length() <> ys.length():
|
|
1835
|
-
raise('image-line-plot: xs and ys should have the same length')
|
|
1869
|
+
raise(ERR.message-exception('image-line-plot: xs and ys should have the same length'))
|
|
1836
1870
|
end
|
|
1837
1871
|
when xs.length() <> images.length():
|
|
1838
|
-
raise('image-line-plot: xs and images should have the same length')
|
|
1872
|
+
raise(ERR.message-exception('image-line-plot: xs and images should have the same length'))
|
|
1839
1873
|
end
|
|
1840
1874
|
xs.each(check-num)
|
|
1841
1875
|
ys.each(check-num)
|
|
@@ -1849,7 +1883,7 @@ fun get-scatter-point(x :: Number, y :: Number, label :: String, optimg :: Optio
|
|
|
1849
1883
|
end
|
|
1850
1884
|
fun scatter-plot-from-list(xs :: CL.LoN, ys :: CL.LoN) -> DataSeries block:
|
|
1851
1885
|
when xs.length() <> ys.length():
|
|
1852
|
-
raise('scatter-plot: xs and ys should have the same length')
|
|
1886
|
+
raise(ERR.message-exception('scatter-plot: xs and ys should have the same length'))
|
|
1853
1887
|
end
|
|
1854
1888
|
xs.each(check-num)
|
|
1855
1889
|
ys.each(check-num)
|
|
@@ -1863,10 +1897,10 @@ fun labeled-scatter-plot-from-list(
|
|
|
1863
1897
|
xs :: CL.LoN,
|
|
1864
1898
|
ys :: CL.LoN) -> DataSeries block:
|
|
1865
1899
|
when xs.length() <> ys.length():
|
|
1866
|
-
raise('labeled-scatter-plot: xs and ys should have the same length')
|
|
1900
|
+
raise(ERR.message-exception('labeled-scatter-plot: xs and ys should have the same length'))
|
|
1867
1901
|
end
|
|
1868
1902
|
when xs.length() <> labels.length():
|
|
1869
|
-
raise('labeled-scatter-plot: xs and labels should have the same length')
|
|
1903
|
+
raise(ERR.message-exception('labeled-scatter-plot: xs and labels should have the same length'))
|
|
1870
1904
|
end
|
|
1871
1905
|
xs.each(check-num)
|
|
1872
1906
|
ys.each(check-num)
|
|
@@ -1881,10 +1915,10 @@ fun image-scatter-plot-from-list(
|
|
|
1881
1915
|
xs :: CL.LoN,
|
|
1882
1916
|
ys :: CL.LoN) -> DataSeries block:
|
|
1883
1917
|
when xs.length() <> ys.length():
|
|
1884
|
-
raise('labeled-scatter-plot: xs and ys should have the same length')
|
|
1918
|
+
raise(ERR.message-exception('labeled-scatter-plot: xs and ys should have the same length'))
|
|
1885
1919
|
end
|
|
1886
1920
|
when xs.length() <> images.length():
|
|
1887
|
-
raise('labeled-scatter-plot: xs and images should have the same length')
|
|
1921
|
+
raise(ERR.message-exception('labeled-scatter-plot: xs and images should have the same length'))
|
|
1888
1922
|
end
|
|
1889
1923
|
xs.each(check-num)
|
|
1890
1924
|
ys.each(check-num)
|
|
@@ -1917,10 +1951,10 @@ fun image-bar-chart-from-list(
|
|
|
1917
1951
|
|
|
1918
1952
|
# Edge Case Error Checking
|
|
1919
1953
|
when value-length == 0:
|
|
1920
|
-
raise("bar-chart: can't have empty data")
|
|
1954
|
+
raise(ERR.message-exception("bar-chart: can't have empty data"))
|
|
1921
1955
|
end
|
|
1922
1956
|
when label-length <> value-length:
|
|
1923
|
-
raise('bar-chart: labels and values should have the same length')
|
|
1957
|
+
raise(ERR.message-exception('bar-chart: labels and values should have the same length'))
|
|
1924
1958
|
end
|
|
1925
1959
|
|
|
1926
1960
|
{max-positive-height; max-negative-height} = prep-axis(rational-values)
|
|
@@ -1946,22 +1980,22 @@ fun exploding-pie-chart-from-list(
|
|
|
1946
1980
|
value-length = values.length()
|
|
1947
1981
|
for each(value from values):
|
|
1948
1982
|
when value < 0:
|
|
1949
|
-
raise('exploding-pie-chart: values must be non-negative')
|
|
1983
|
+
raise(ERR.message-exception('exploding-pie-chart: values must be non-negative'))
|
|
1950
1984
|
end
|
|
1951
1985
|
end
|
|
1952
1986
|
when label-length <> value-length:
|
|
1953
|
-
raise('exploding-pie-chart: labels and values should have the same length')
|
|
1987
|
+
raise(ERR.message-exception('exploding-pie-chart: labels and values should have the same length'))
|
|
1954
1988
|
end
|
|
1955
1989
|
offset-length = offsets.length()
|
|
1956
1990
|
when label-length <> offset-length:
|
|
1957
|
-
raise('exploding-pie-chart: labels and offsets should have the same length')
|
|
1991
|
+
raise(ERR.message-exception('exploding-pie-chart: labels and offsets should have the same length'))
|
|
1958
1992
|
end
|
|
1959
1993
|
when label-length == 0:
|
|
1960
|
-
raise('exploding-pie-chart: need at least one data')
|
|
1994
|
+
raise(ERR.message-exception('exploding-pie-chart: need at least one data'))
|
|
1961
1995
|
end
|
|
1962
1996
|
for each(offset from offsets):
|
|
1963
1997
|
when (offset < 0) or (offset > 1):
|
|
1964
|
-
raise('exploding-pie-chart: offset must be between 0 and 1')
|
|
1998
|
+
raise(ERR.message-exception('exploding-pie-chart: offset must be between 0 and 1'))
|
|
1965
1999
|
end
|
|
1966
2000
|
end
|
|
1967
2001
|
values.each(check-num)
|
|
@@ -1981,14 +2015,14 @@ fun pie-chart-from-list(labels :: CL.LoS, values :: CL.LoN) -> DataSeries block:
|
|
|
1981
2015
|
value-length = values.length()
|
|
1982
2016
|
for each(value from values):
|
|
1983
2017
|
when value < 0:
|
|
1984
|
-
raise('pie-chart: values must be non-negative')
|
|
2018
|
+
raise(ERR.message-exception('pie-chart: values must be non-negative'))
|
|
1985
2019
|
end
|
|
1986
2020
|
end
|
|
1987
2021
|
when label-length <> value-length:
|
|
1988
|
-
raise('pie-chart: labels and values should have the same length')
|
|
2022
|
+
raise(ERR.message-exception('pie-chart: labels and values should have the same length'))
|
|
1989
2023
|
end
|
|
1990
2024
|
when label-length == 0:
|
|
1991
|
-
raise('pie-chart: need at least one data')
|
|
2025
|
+
raise(ERR.message-exception('pie-chart: need at least one data'))
|
|
1992
2026
|
end
|
|
1993
2027
|
values.each(check-num)
|
|
1994
2028
|
labels.each(check-string)
|
|
@@ -2006,14 +2040,14 @@ fun image-pie-chart-from-list(images :: CL.LoI, labels :: CL.LoS, values :: CL.L
|
|
|
2006
2040
|
value-length = values.length()
|
|
2007
2041
|
for each(value from values):
|
|
2008
2042
|
when value < 0:
|
|
2009
|
-
raise('pie-chart: values must be non-negative')
|
|
2043
|
+
raise(ERR.message-exception('pie-chart: values must be non-negative'))
|
|
2010
2044
|
end
|
|
2011
2045
|
end
|
|
2012
2046
|
when label-length <> value-length:
|
|
2013
|
-
raise('pie-chart: labels and values should have the same length')
|
|
2047
|
+
raise(ERR.message-exception('pie-chart: labels and values should have the same length'))
|
|
2014
2048
|
end
|
|
2015
2049
|
when label-length == 0:
|
|
2016
|
-
raise('pie-chart: need at least one data')
|
|
2050
|
+
raise(ERR.message-exception('pie-chart: need at least one data'))
|
|
2017
2051
|
end
|
|
2018
2052
|
images.each(check-image)
|
|
2019
2053
|
values.each(check-num)
|
|
@@ -2039,10 +2073,10 @@ fun bar-chart-from-list(labels :: CL.LoS, values :: CL.LoN) -> DataSeries block:
|
|
|
2039
2073
|
|
|
2040
2074
|
# Edge Case Error Checking
|
|
2041
2075
|
when value-length == 0:
|
|
2042
|
-
raise("bar-chart: can't have empty data")
|
|
2076
|
+
raise(ERR.message-exception("bar-chart: can't have empty data"))
|
|
2043
2077
|
end
|
|
2044
2078
|
when label-length <> value-length:
|
|
2045
|
-
raise('bar-chart: labels and values should have the same length')
|
|
2079
|
+
raise(ERR.message-exception('bar-chart: labels and values should have the same length'))
|
|
2046
2080
|
end
|
|
2047
2081
|
|
|
2048
2082
|
{max-positive-height; max-negative-height} = prep-axis(rational-values)
|
|
@@ -2068,7 +2102,7 @@ fun num-dot-chart-from-list(x-values :: CL.LoN) -> DataSeries block:
|
|
|
2068
2102
|
```
|
|
2069
2103
|
x-values.each(check-num)
|
|
2070
2104
|
when x-values.length() == 0:
|
|
2071
|
-
raise("num-dot-chart: can't have empty data")
|
|
2105
|
+
raise(ERR.message-exception("num-dot-chart: can't have empty data"))
|
|
2072
2106
|
end
|
|
2073
2107
|
default-dot-plot-series.{
|
|
2074
2108
|
ps: map3(get-dot-point, x-values, x-values.map({(_): ''}), x-values.map({(_): none})),
|
|
@@ -2082,11 +2116,11 @@ fun image-num-dot-chart-from-list(images :: CL.LoI, x-values :: CL.LoN) -> DataS
|
|
|
2082
2116
|
```
|
|
2083
2117
|
x-values.each(check-num)
|
|
2084
2118
|
when x-values.length() == 0:
|
|
2085
|
-
raise("num-dot-chart: can't have empty data")
|
|
2119
|
+
raise(ERR.message-exception("num-dot-chart: can't have empty data"))
|
|
2086
2120
|
end
|
|
2087
2121
|
images.each(check-image)
|
|
2088
2122
|
when images.length() <> x-values.length():
|
|
2089
|
-
raise("num-dot-chart: the lists of numbers and images must have the same length")
|
|
2123
|
+
raise(ERR.message-exception("num-dot-chart: the lists of numbers and images must have the same length"))
|
|
2090
2124
|
end
|
|
2091
2125
|
default-dot-plot-series.{
|
|
2092
2126
|
ps: map3(get-dot-point, x-values, x-values.map({(_): ''}), images.map(some)),
|
|
@@ -2100,11 +2134,11 @@ fun labeled-num-dot-chart-from-list(labels :: CL.LoS, x-values :: CL.LoN) -> Dat
|
|
|
2100
2134
|
```
|
|
2101
2135
|
x-values.each(check-num)
|
|
2102
2136
|
when x-values.length() == 0:
|
|
2103
|
-
raise("num-dot-chart: can't have empty data")
|
|
2137
|
+
raise(ERR.message-exception("num-dot-chart: can't have empty data"))
|
|
2104
2138
|
end
|
|
2105
2139
|
labels.each(check-string)
|
|
2106
2140
|
when labels.length() <> x-values.length():
|
|
2107
|
-
raise("num-dot-chart: the lists of numbers and labels must have the same length")
|
|
2141
|
+
raise(ERR.message-exception("num-dot-chart: the lists of numbers and labels must have the same length"))
|
|
2108
2142
|
end
|
|
2109
2143
|
default-dot-plot-series.{
|
|
2110
2144
|
ps: map3(get-dot-point, x-values, labels, x-values.map({(_): none})),
|
|
@@ -2118,7 +2152,7 @@ fun dot-chart-from-list(input-labels :: CL.LoS) -> DataSeries block:
|
|
|
2118
2152
|
|
|
2119
2153
|
# Edge Case Error Checking
|
|
2120
2154
|
when input-labels.length() == 0:
|
|
2121
|
-
raise("dot-chart: can't have empty data")
|
|
2155
|
+
raise(ERR.message-exception("dot-chart: can't have empty data"))
|
|
2122
2156
|
end
|
|
2123
2157
|
|
|
2124
2158
|
# Type Checking
|
|
@@ -2153,16 +2187,16 @@ fun grouped-bar-chart-from-list(
|
|
|
2153
2187
|
|
|
2154
2188
|
# Edge Case Error Checking
|
|
2155
2189
|
when value-length == 0:
|
|
2156
|
-
raise("grouped-bar-chart: can't have empty data")
|
|
2190
|
+
raise(ERR.message-exception("grouped-bar-chart: can't have empty data"))
|
|
2157
2191
|
end
|
|
2158
2192
|
when legend-length == 0:
|
|
2159
|
-
raise("grouped-bar-chart: can't have empty legends")
|
|
2193
|
+
raise(ERR.message-exception("grouped-bar-chart: can't have empty legends"))
|
|
2160
2194
|
end
|
|
2161
2195
|
when label-length <> value-length:
|
|
2162
|
-
raise('grouped-bar-chart: labels and values should have the same length')
|
|
2196
|
+
raise(ERR.message-exception('grouped-bar-chart: labels and values should have the same length'))
|
|
2163
2197
|
end
|
|
2164
2198
|
when any({(group): legend-length <> group.length()}, value-lists):
|
|
2165
|
-
raise('grouped-bar-chart: labels and legends should have the same length')
|
|
2199
|
+
raise(ERR.message-exception('grouped-bar-chart: labels and legends should have the same length'))
|
|
2166
2200
|
end
|
|
2167
2201
|
|
|
2168
2202
|
# Typechecking each input
|
|
@@ -2202,16 +2236,16 @@ fun stacked-bar-chart-from-list(
|
|
|
2202
2236
|
|
|
2203
2237
|
# Edge Case Error Checking
|
|
2204
2238
|
when value-length == 0:
|
|
2205
|
-
raise("stacked-bar-chart: can't have empty data")
|
|
2239
|
+
raise(ERR.message-exception("stacked-bar-chart: can't have empty data"))
|
|
2206
2240
|
end
|
|
2207
2241
|
when legend-length == 0:
|
|
2208
|
-
raise("stacked-bar-chart: can't have empty legends")
|
|
2242
|
+
raise(ERR.message-exception("stacked-bar-chart: can't have empty legends"))
|
|
2209
2243
|
end
|
|
2210
2244
|
when label-length <> value-length:
|
|
2211
|
-
raise('stacked-bar-chart: labels and values should have the same length')
|
|
2245
|
+
raise(ERR.message-exception('stacked-bar-chart: labels and values should have the same length'))
|
|
2212
2246
|
end
|
|
2213
2247
|
when any({(stack): legend-length <> stack.length()}, value-lists):
|
|
2214
|
-
raise('stacked-bar-chart: labels and legends should have the same length')
|
|
2248
|
+
raise(ERR.message-exception('stacked-bar-chart: labels and legends should have the same length'))
|
|
2215
2249
|
end
|
|
2216
2250
|
|
|
2217
2251
|
# Typechecking the input
|
|
@@ -2262,16 +2296,16 @@ fun labeled-interval-chart-from-list(
|
|
|
2262
2296
|
ys-length = ys.length()
|
|
2263
2297
|
deltas-length = deltas.length()
|
|
2264
2298
|
when xs-length <> ys-length:
|
|
2265
|
-
raise('interval-chart: xs and ys should have the same length')
|
|
2299
|
+
raise(ERR.message-exception('interval-chart: xs and ys should have the same length'))
|
|
2266
2300
|
end
|
|
2267
2301
|
when xs-length <> deltas-length:
|
|
2268
|
-
raise('interval-chart: deltas should have the same length as xs and ys')
|
|
2302
|
+
raise(ERR.message-exception('interval-chart: deltas should have the same length as xs and ys'))
|
|
2269
2303
|
end
|
|
2270
2304
|
when xs-length <> labels-length:
|
|
2271
|
-
raise('interval-chart: labels should have the same length as xs and ys')
|
|
2305
|
+
raise(ERR.message-exception('interval-chart: labels should have the same length as xs and ys'))
|
|
2272
2306
|
end
|
|
2273
2307
|
when xs-length == 0:
|
|
2274
|
-
raise('interval-chart: need at least one datum')
|
|
2308
|
+
raise(ERR.message-exception('interval-chart: need at least one datum'))
|
|
2275
2309
|
end
|
|
2276
2310
|
xs.each(check-num)
|
|
2277
2311
|
ys.each(check-num)
|
|
@@ -2303,16 +2337,16 @@ fun labeled-box-plot-from-list(
|
|
|
2303
2337
|
label-length = labels.length()
|
|
2304
2338
|
value-length = values.length()
|
|
2305
2339
|
when label-length <> value-length:
|
|
2306
|
-
raise('labeled-box-plot: labels and values should have the same length')
|
|
2340
|
+
raise(ERR.message-exception('labeled-box-plot: labels and values should have the same length'))
|
|
2307
2341
|
end
|
|
2308
2342
|
when label-length == 0:
|
|
2309
|
-
raise('labeled-box-plot: expect at least one box')
|
|
2343
|
+
raise(ERR.message-exception('labeled-box-plot: expect at least one box'))
|
|
2310
2344
|
end
|
|
2311
2345
|
values.each(_.each(check-num))
|
|
2312
2346
|
values.each(
|
|
2313
2347
|
lam(lst):
|
|
2314
2348
|
when lst.length() <= 1:
|
|
2315
|
-
raise('labeled-box-plot: the list length should be at least 2')
|
|
2349
|
+
raise(ERR.message-exception('labeled-box-plot: the list length should be at least 2'))
|
|
2316
2350
|
end
|
|
2317
2351
|
end)
|
|
2318
2352
|
labels.each(check-string)
|
|
@@ -2363,7 +2397,7 @@ fun labeled-histogram-from-list(labels :: CL.LoS, values :: CL.LoN) -> DataSerie
|
|
|
2363
2397
|
label-length = labels.length()
|
|
2364
2398
|
value-length = values.length()
|
|
2365
2399
|
when label-length <> value-length:
|
|
2366
|
-
raise('labeled-histogram: labels and values should have the same length')
|
|
2400
|
+
raise(ERR.message-exception('labeled-histogram: labels and values should have the same length'))
|
|
2367
2401
|
end
|
|
2368
2402
|
values.each(check-num)
|
|
2369
2403
|
labels.each(check-string)
|
|
@@ -2396,7 +2430,7 @@ fun check-render-x-axis(self) -> Nothing:
|
|
|
2396
2430
|
cases (Option) self.x-max:
|
|
2397
2431
|
| some(x-max) =>
|
|
2398
2432
|
if x-min >= x-max:
|
|
2399
|
-
raise("render: x-min must be strictly less than x-max")
|
|
2433
|
+
raise(ERR.message-exception("render: x-min must be strictly less than x-max"))
|
|
2400
2434
|
else:
|
|
2401
2435
|
nothing
|
|
2402
2436
|
end
|
|
@@ -2412,7 +2446,7 @@ fun check-render-y-axis(self) -> Nothing:
|
|
|
2412
2446
|
cases (Option) self.y-max:
|
|
2413
2447
|
| some(y-max) =>
|
|
2414
2448
|
if y-min >= y-max:
|
|
2415
|
-
raise("render: y-min must be strictly less than y-max")
|
|
2449
|
+
raise(ERR.message-exception("render: y-min must be strictly less than y-max"))
|
|
2416
2450
|
else:
|
|
2417
2451
|
nothing
|
|
2418
2452
|
end
|
|
@@ -2422,6 +2456,16 @@ fun check-render-y-axis(self) -> Nothing:
|
|
|
2422
2456
|
end
|
|
2423
2457
|
end
|
|
2424
2458
|
|
|
2459
|
+
fun check-data-range(min, max, vals) -> Nothing:
|
|
2460
|
+
fun too-small(v): v.value < min.or-else(v.value) end
|
|
2461
|
+
fun too-big(v): v.value > max.or-else(v.value) end
|
|
2462
|
+
if vals.any(too-small) or vals.any(too-big):
|
|
2463
|
+
raise(ERR.message-exception("render: All values must be between specified x-min and x-max bounds"))
|
|
2464
|
+
else:
|
|
2465
|
+
nothing
|
|
2466
|
+
end
|
|
2467
|
+
end
|
|
2468
|
+
|
|
2425
2469
|
fun render-chart(s :: DataSeries) -> ChartWindow:
|
|
2426
2470
|
doc: 'Render it!'
|
|
2427
2471
|
cases (DataSeries) s:
|
|
@@ -2432,6 +2476,8 @@ fun render-chart(s :: DataSeries) -> ChartWindow:
|
|
|
2432
2476
|
| dot-plot-series(obj) =>
|
|
2433
2477
|
default-dot-chart-window-object.{
|
|
2434
2478
|
method render(self) block:
|
|
2479
|
+
_ = check-render-x-axis(self)
|
|
2480
|
+
_ = check-data-range(self.x-min, self.x-max, obj.ps)
|
|
2435
2481
|
CL.dot-chart(self, obj)
|
|
2436
2482
|
end
|
|
2437
2483
|
} ^ dot-chart-window
|
|
@@ -2740,18 +2786,18 @@ end
|
|
|
2740
2786
|
fun render-charts(lst :: List<DataSeries>) -> ChartWindow block:
|
|
2741
2787
|
doc: "Draw 'em all"
|
|
2742
2788
|
cases (Option) find(_.is-single, lst):
|
|
2743
|
-
| some(v) => raise(
|
|
2744
|
-
|
|
2745
|
-
|
|
2789
|
+
| some(v) => raise(ERR.message-exception(
|
|
2790
|
+
[sprintf: "render-charts: can't draw ", v,
|
|
2791
|
+
" with `render-charts`. Use `render-chart` instead."]))
|
|
2746
2792
|
| else => nothing
|
|
2747
2793
|
end
|
|
2748
2794
|
cases (List<DataSeries>) lst:
|
|
2749
|
-
| empty => raise('render-charts: need at least one series to plot')
|
|
2795
|
+
| empty => raise(ERR.message-exception('render-charts: need at least one series to plot'))
|
|
2750
2796
|
| else => nothing
|
|
2751
2797
|
end
|
|
2752
2798
|
when lst.any({(ds): ds.obj.horizontal}) and not(lst.all({(ds): ds.obj.horizontal})):
|
|
2753
|
-
raise("render-charts: Cannot render a mix of horizontal and vertical charts. "
|
|
2754
|
-
|
|
2799
|
+
raise(ERR.message-exception("render-charts: Cannot render a mix of horizontal and vertical charts. "
|
|
2800
|
+
+ "Make sure all charts have the same direction."))
|
|
2755
2801
|
end
|
|
2756
2802
|
|
|
2757
2803
|
partitioned = partition(is-function-plot-series, lst)
|
|
@@ -2801,51 +2847,24 @@ fun render-charts(lst :: List<DataSeries>) -> ChartWindow block:
|
|
|
2801
2847
|
|
|
2802
2848
|
# shadow self = self.{y-min: y-min, y-max: y-max}
|
|
2803
2849
|
|
|
2804
|
-
|
|
2805
|
-
shadow function-plots-data = cases (Option) function-plots-data:
|
|
2806
|
-
| none => function-plots
|
|
2807
|
-
.map(generate-xy(_, self.x-min.value, self.x-max.value, self.num-samples))
|
|
2808
|
-
| some(shadow function-plots-data) => function-plots-data
|
|
2809
|
-
end
|
|
2810
|
-
|
|
2811
|
-
# scatters-arr = for map(p from scatter-plots + function-plots-data):
|
|
2812
|
-
# ps-to-arr(p.{ps: p.ps.filter(in-bound-xy(_, self))})
|
|
2813
|
-
# end ^ reverse ^ builtins.raw-array-from-list
|
|
2814
|
-
|
|
2815
|
-
# lines-arr = for map(p from line-plots):
|
|
2816
|
-
# ps-to-arr(p.{ps: line-plot-edge-cut(p.ps, self)})
|
|
2817
|
-
# end ^ reverse ^ builtins.raw-array-from-list
|
|
2818
|
-
|
|
2819
|
-
# intervals-arr = for map(p from interval-plots):
|
|
2820
|
-
# ps-to-arr(p.{ps: p.ps.filter(in-bound-xy(_, self))})
|
|
2821
|
-
# end ^ reverse ^ builtins.raw-array-from-list
|
|
2822
|
-
|
|
2823
|
-
ret = CL.plot(self, {
|
|
2850
|
+
CL.plot(self, {
|
|
2824
2851
|
scatters: builtins.raw-array-from-list(scatter-plots),
|
|
2825
2852
|
lines: builtins.raw-array-from-list(line-plots),
|
|
2826
2853
|
intervals: builtins.raw-array-from-list(interval-plots),
|
|
2827
2854
|
functions: builtins.raw-array-from-list(function-plots)
|
|
2828
2855
|
})
|
|
2829
|
-
# NOTE(Ben): THIS IS A POLYFILL FOR NOW,
|
|
2830
|
-
# until I remove the Either callback hooking mechanism altogether.
|
|
2831
|
-
if (IM.is-image(ret)): ret
|
|
2832
|
-
else:
|
|
2833
|
-
cases (E.Either<Any, IM.Image>) ret:
|
|
2834
|
-
| left(new-self) => helper(new-self, none)
|
|
2835
|
-
| right(image) => image
|
|
2836
|
-
end
|
|
2837
|
-
end
|
|
2838
|
-
end
|
|
2839
|
-
helper(self, some(empty))
|
|
2840
2856
|
end
|
|
2841
2857
|
} ^ plot-chart-window
|
|
2842
2858
|
where:
|
|
2843
|
-
|
|
2844
|
-
|
|
2859
|
+
xs = [list: 1, 3, 5, 8, 20]
|
|
2860
|
+
ys = [list: 5, 3, 8, 2, 10]
|
|
2861
|
+
p1 = from-list.function-plot(lam(x): x * x end).color(IS.red)
|
|
2862
|
+
p2 = from-list.line-plot([list: 1, 2, 3, 4], [list: 1, 4, 9, 16]).color(IS.green)
|
|
2845
2863
|
p3 = from-list.histogram([list: 1, 2, 3, 4])
|
|
2846
2864
|
p4 = from-list.line-plot(
|
|
2847
2865
|
[list: -1, 1, 2, 3, 11, 8, 9],
|
|
2848
2866
|
[list: 10, -1, 11, 9, 9, 3, 2])
|
|
2867
|
+
p5 = from-list.scatter-plot(ys, xs)
|
|
2849
2868
|
render-charts([list: p1, p2, p3]) raises ''
|
|
2850
2869
|
render-charts([list: p1, p2])
|
|
2851
2870
|
.title('quadratic function and a scatter plot')
|
|
@@ -2860,6 +2879,12 @@ where:
|
|
|
2860
2879
|
.y-min(0)
|
|
2861
2880
|
.y-max(10)
|
|
2862
2881
|
.get-image() does-not-raise
|
|
2882
|
+
render-chart(p5) does-not-raise
|
|
2883
|
+
img = render-chart(p5).get-image()
|
|
2884
|
+
img satisfies I.is-image
|
|
2885
|
+
I.image-pinhole-x(img) is I.image-width(img) / 2
|
|
2886
|
+
I.image-pinhole-y(img) is I.image-height(img) / 2
|
|
2887
|
+
render-chart(p5).get-spec() satisfies is-string
|
|
2863
2888
|
end
|
|
2864
2889
|
|
|
2865
2890
|
from-list = {
|