@explorable-viz/fluid 0.12.2 → 0.12.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (236) hide show
  1. package/dist/fluid/shared/fluid.mjs +478 -438
  2. package/dist/fluid/shared/load-figure.js +3193 -2969
  3. package/package.json +12 -26
  4. package/script/install-website.sh +27 -9
  5. package/web/lib/CodeMirror.svelte +19 -0
  6. package/web/lib/DataPane.svelte +46 -0
  7. package/web/lib/Figure.svelte +38 -0
  8. package/web/lib/FigureSrc.svelte +35 -0
  9. package/web/lib/Grid.svelte +32 -0
  10. package/web/lib/index.js +5 -0
  11. package/web/lib/view-styles.css +262 -0
  12. package/website/article/eslint.config.js +45 -0
  13. package/website/article/package.json +46 -0
  14. package/website/article/src/app.d.ts +13 -0
  15. package/website/article/src/app.html +12 -0
  16. package/website/article/src/lib/assets/css/styles.css +245 -0
  17. package/website/article/src/lib/assets/image/bristol.png +0 -0
  18. package/website/article/src/lib/assets/image/fluid.png +0 -0
  19. package/website/article/src/lib/assets/image/iccs.png +0 -0
  20. package/website/article/src/lib/assets/image/schmidt.png +0 -0
  21. package/website/article/src/lib/assets/image/turing.jpg +0 -0
  22. package/website/article/src/lib/config/nav.ts +7 -0
  23. package/website/article/src/lib/config/supporters.ts +27 -0
  24. package/website/article/src/routes/+layout.svelte +15 -0
  25. package/website/article/src/routes/+layout.ts +1 -0
  26. package/website/article/{index.html → src/routes/+page.svelte} +8 -17
  27. package/website/article/src/routes/convolution/+page.svelte +62 -0
  28. package/website/article/src/routes/energy-scatter/+page.svelte +34 -0
  29. package/website/article/src/routes/methane/+page.svelte +35 -0
  30. package/website/article/src/routes/moving-average/+page.svelte +57 -0
  31. package/website/article/src/routes/non-renewables/+page.svelte +40 -0
  32. package/website/article/src/routes/renewables-linked/+page.svelte +34 -0
  33. package/website/article/src/routes/scigen-1805.02474v1-10/+page.svelte +40 -0
  34. package/website/article/static/fluid/lib/graphics.fld +274 -0
  35. package/website/article/static/fluid/lib/matrix.fld +40 -0
  36. package/website/article/static/fluid/lib/prelude.fld +348 -0
  37. package/website/article/static/fluid/lib/stats.fld +83 -0
  38. package/website/article/svelte.config.js +13 -0
  39. package/website/article/test.mjs +66 -11
  40. package/website/article/tsconfig.json +20 -0
  41. package/website/article/vite.config.ts +6 -0
  42. package/.spago/aff/v7.1.0/.editorconfig +0 -13
  43. package/.spago/aff/v7.1.0/.eslintrc.json +0 -28
  44. package/.spago/aff/v7.1.0/.gitignore +0 -14
  45. package/.spago/aff/v7.1.0/.tidyrc.json +0 -10
  46. package/.spago/affjax/v13.0.0/.editorconfig +0 -13
  47. package/.spago/affjax/v13.0.0/.eslintrc.json +0 -28
  48. package/.spago/affjax/v13.0.0/.gitignore +0 -14
  49. package/.spago/affjax/v13.0.0/.tidyrc.json +0 -10
  50. package/.spago/affjax-web/v1.0.0/.editorconfig +0 -13
  51. package/.spago/affjax-web/v1.0.0/.eslintrc.json +0 -30
  52. package/.spago/affjax-web/v1.0.0/.gitignore +0 -14
  53. package/.spago/affjax-web/v1.0.0/.tidyrc.json +0 -10
  54. package/.spago/argonaut-codecs/v9.1.0/.editorconfig +0 -13
  55. package/.spago/argonaut-codecs/v9.1.0/.gitignore +0 -9
  56. package/.spago/argonaut-codecs/v9.1.0/.tidyrc.json +0 -10
  57. package/.spago/argonaut-core/v7.0.0/.editorconfig +0 -13
  58. package/.spago/argonaut-core/v7.0.0/.eslintrc.json +0 -29
  59. package/.spago/argonaut-core/v7.0.0/.gitignore +0 -14
  60. package/.spago/argonaut-core/v7.0.0/.tidyrc.json +0 -10
  61. package/.spago/arraybuffer-types/v3.0.2/.editorconfig +0 -13
  62. package/.spago/arraybuffer-types/v3.0.2/.gitignore +0 -9
  63. package/.spago/arraybuffer-types/v3.0.2/.tidyrc.json +0 -10
  64. package/.spago/arrays/v7.2.1/.eslintrc.json +0 -26
  65. package/.spago/arrays/v7.2.1/.gitignore +0 -9
  66. package/.spago/assert/v6.0.0/.eslintrc.json +0 -26
  67. package/.spago/assert/v6.0.0/.gitignore +0 -8
  68. package/.spago/bifunctors/v6.0.0/.gitignore +0 -7
  69. package/.spago/catenable-lists/v7.0.0/.gitignore +0 -7
  70. package/.spago/console/v6.1.0/.eslintrc.json +0 -30
  71. package/.spago/console/v6.1.0/.gitignore +0 -9
  72. package/.spago/const/v6.0.0/.gitignore +0 -7
  73. package/.spago/contravariant/v6.0.0/.gitignore +0 -7
  74. package/.spago/control/v6.0.0/.gitignore +0 -7
  75. package/.spago/datetime/v6.1.0/.eslintrc.json +0 -26
  76. package/.spago/datetime/v6.1.0/.gitignore +0 -8
  77. package/.spago/debug/v6.0.2/.eslintrc.json +0 -36
  78. package/.spago/debug/v6.0.2/.gitignore +0 -8
  79. package/.spago/distributive/v6.0.0/.gitignore +0 -7
  80. package/.spago/effect/v4.0.0/.eslintrc.json +0 -26
  81. package/.spago/effect/v4.0.0/.gitignore +0 -8
  82. package/.spago/either/v6.1.0/.gitignore +0 -7
  83. package/.spago/enums/v6.0.1/.gitignore +0 -7
  84. package/.spago/exceptions/v6.0.0/.eslintrc.json +0 -26
  85. package/.spago/exceptions/v6.0.0/.gitignore +0 -8
  86. package/.spago/exists/v6.0.0/.gitignore +0 -7
  87. package/.spago/filterable/v5.0.0/.gitignore +0 -11
  88. package/.spago/foldable-traversable/v6.0.0/.eslintrc.json +0 -26
  89. package/.spago/foldable-traversable/v6.0.0/.gitignore +0 -8
  90. package/.spago/foreign/v7.0.0/.eslintrc.json +0 -26
  91. package/.spago/foreign/v7.0.0/.gitignore +0 -8
  92. package/.spago/foreign-object/v4.1.0/.eslintrc.json +0 -26
  93. package/.spago/foreign-object/v4.1.0/.gitignore +0 -8
  94. package/.spago/form-urlencoded/v7.0.0/.editorconfig +0 -13
  95. package/.spago/form-urlencoded/v7.0.0/.gitignore +0 -9
  96. package/.spago/form-urlencoded/v7.0.0/.tidyrc.json +0 -10
  97. package/.spago/free/v7.1.0/.gitignore +0 -8
  98. package/.spago/functions/v6.0.0/.eslintrc.json +0 -26
  99. package/.spago/functions/v6.0.0/.gitignore +0 -8
  100. package/.spago/functors/v5.0.0/.gitignore +0 -7
  101. package/.spago/gen/v4.0.0/.gitignore +0 -8
  102. package/.spago/graphs/v8.1.0/.gitignore +0 -7
  103. package/.spago/http-methods/v6.0.0/.editorconfig +0 -13
  104. package/.spago/http-methods/v6.0.0/.gitignore +0 -9
  105. package/.spago/http-methods/v6.0.0/.tidyrc.json +0 -10
  106. package/.spago/identity/v6.0.0/.gitignore +0 -7
  107. package/.spago/integers/v6.0.0/.eslintrc.json +0 -26
  108. package/.spago/integers/v6.0.0/.gitignore +0 -8
  109. package/.spago/invariant/v6.0.0/.gitignore +0 -7
  110. package/.spago/js-date/v8.0.0/.editorconfig +0 -13
  111. package/.spago/js-date/v8.0.0/.eslintrc.json +0 -29
  112. package/.spago/js-date/v8.0.0/.gitignore +0 -14
  113. package/.spago/js-date/v8.0.0/.tidyrc.json +0 -10
  114. package/.spago/js-uri/v3.1.0/.eslintrc.json +0 -30
  115. package/.spago/js-uri/v3.1.0/.gitignore +0 -14
  116. package/.spago/js-uri/v3.1.0/.tidyrc.json +0 -10
  117. package/.spago/lazy/v6.0.0/.eslintrc.json +0 -26
  118. package/.spago/lazy/v6.0.0/.gitignore +0 -8
  119. package/.spago/lcg/v4.0.0/.gitignore +0 -8
  120. package/.spago/lists/v7.0.0/.gitignore +0 -7
  121. package/.spago/maybe/v6.0.0/.gitignore +0 -7
  122. package/.spago/media-types/v6.0.0/.editorconfig +0 -13
  123. package/.spago/media-types/v6.0.0/.gitignore +0 -9
  124. package/.spago/media-types/v6.0.0/.tidyrc.json +0 -10
  125. package/.spago/newtype/v5.0.0/.gitignore +0 -7
  126. package/.spago/node-buffer/v9.0.0/.eslintrc.json +0 -26
  127. package/.spago/node-buffer/v9.0.0/.gitignore +0 -8
  128. package/.spago/node-fs/v9.1.0/.eslintrc.json +0 -29
  129. package/.spago/node-fs/v9.1.0/.gitignore +0 -9
  130. package/.spago/node-path/v5.0.0/.eslintrc.json +0 -26
  131. package/.spago/node-path/v5.0.0/.gitignore +0 -8
  132. package/.spago/node-process/v11.2.0/.eslintrc.json +0 -29
  133. package/.spago/node-process/v11.2.0/.gitignore +0 -8
  134. package/.spago/node-streams/v9.0.0/.eslintrc.json +0 -29
  135. package/.spago/node-streams/v9.0.0/.gitignore +0 -8
  136. package/.spago/nonempty/v7.0.0/.gitignore +0 -7
  137. package/.spago/now/v6.0.0/.editorconfig +0 -13
  138. package/.spago/now/v6.0.0/.eslintrc.json +0 -29
  139. package/.spago/now/v6.0.0/.gitignore +0 -14
  140. package/.spago/now/v6.0.0/.tidyrc.json +0 -10
  141. package/.spago/nullable/v6.0.0/.editorconfig +0 -13
  142. package/.spago/nullable/v6.0.0/.eslintrc.json +0 -29
  143. package/.spago/nullable/v6.0.0/.gitignore +0 -14
  144. package/.spago/nullable/v6.0.0/.tidyrc.json +0 -10
  145. package/.spago/optparse/v6.0.0/.gitignore +0 -8
  146. package/.spago/optparse/v6.0.0/.npmrc +0 -1
  147. package/.spago/ordered-collections/v3.1.1/.gitignore +0 -8
  148. package/.spago/orders/v6.0.0/.gitignore +0 -7
  149. package/.spago/parallel/v7.0.0/.gitignore +0 -7
  150. package/.spago/parsing/v10.3.1/.editorconfig +0 -13
  151. package/.spago/parsing/v10.3.1/.gitignore +0 -12
  152. package/.spago/parsing/v10.3.1/.tidyoperators +0 -231
  153. package/.spago/parsing/v10.3.1/.tidyrc.json +0 -10
  154. package/.spago/partial/v4.0.0/.eslintrc.json +0 -26
  155. package/.spago/partial/v4.0.0/.gitignore +0 -8
  156. package/.spago/posix-types/v6.0.0/.gitignore +0 -7
  157. package/.spago/prelude/v6.0.1/.eslintrc.json +0 -26
  158. package/.spago/prelude/v6.0.1/.gitignore +0 -8
  159. package/.spago/profunctor/v6.0.0/.gitignore +0 -7
  160. package/.spago/psci-support/v6.0.0/.eslintrc.json +0 -28
  161. package/.spago/psci-support/v6.0.0/.gitignore +0 -8
  162. package/.spago/quickcheck/v8.0.1/.eslintrc.json +0 -26
  163. package/.spago/quickcheck/v8.0.1/.gitignore +0 -8
  164. package/.spago/random/v6.0.0/.eslintrc.json +0 -26
  165. package/.spago/random/v6.0.0/.gitignore +0 -8
  166. package/.spago/record/v4.0.0/.eslintrc.json +0 -26
  167. package/.spago/record/v4.0.0/.gitignore +0 -8
  168. package/.spago/refs/v6.0.0/.eslintrc.json +0 -26
  169. package/.spago/refs/v6.0.0/.gitignore +0 -8
  170. package/.spago/st/v6.2.0/.eslintrc.json +0 -26
  171. package/.spago/st/v6.2.0/.gitignore +0 -8
  172. package/.spago/strings/v6.0.1/.eslintrc.json +0 -26
  173. package/.spago/strings/v6.0.1/.gitignore +0 -8
  174. package/.spago/tailrec/v6.1.0/.gitignore +0 -7
  175. package/.spago/transformers/v6.0.0/.gitignore +0 -7
  176. package/.spago/tuples/v7.0.0/.gitignore +0 -7
  177. package/.spago/type-equality/v4.0.1/.gitignore +0 -7
  178. package/.spago/typelevel-prelude/v7.0.0/.gitignore +0 -8
  179. package/.spago/unfoldable/v6.0.0/.eslintrc.json +0 -26
  180. package/.spago/unfoldable/v6.0.0/.gitignore +0 -8
  181. package/.spago/unicode/v6.0.0/.editorconfig +0 -13
  182. package/.spago/unicode/v6.0.0/.gitattributes +0 -2
  183. package/.spago/unicode/v6.0.0/.gitignore +0 -19
  184. package/.spago/unicode/v6.0.0/.tidyrc.json +0 -10
  185. package/.spago/unsafe-coerce/v6.0.0/.eslintrc.json +0 -26
  186. package/.spago/unsafe-coerce/v6.0.0/.gitignore +0 -8
  187. package/.spago/web-dom/v6.0.0/.eslintrc.json +0 -29
  188. package/.spago/web-dom/v6.0.0/.gitignore +0 -9
  189. package/.spago/web-events/v4.0.0/.eslintrc.json +0 -29
  190. package/.spago/web-events/v4.0.0/.gitignore +0 -8
  191. package/.spago/web-file/v4.0.0/.eslintrc.json +0 -29
  192. package/.spago/web-file/v4.0.0/.gitignore +0 -8
  193. package/.spago/web-html/v4.1.0/.eslintrc.json +0 -29
  194. package/.spago/web-html/v4.1.0/.gitignore +0 -8
  195. package/.spago/web-storage/v5.0.0/.eslintrc.json +0 -29
  196. package/.spago/web-storage/v5.0.0/.gitignore +0 -8
  197. package/.spago/web-xhr/v5.0.1/.eslintrc.json +0 -29
  198. package/.spago/web-xhr/v5.0.1/.gitignore +0 -8
  199. package/LICENSE +0 -21
  200. package/README.md +0 -95
  201. package/dist/fluid/shared/website-test.js +0 -41
  202. package/dist/fluid/shared/webtest-lib.js +0 -300023
  203. package/script/bundle-website.sh +0 -52
  204. package/script/util/clean.sh +0 -5
  205. package/script/util/lisp-case.sh +0 -10
  206. package/website/article/convolution/index.html +0 -97
  207. package/website/article/css/styles.css +0 -325
  208. package/website/article/css/view-styles.css +0 -205
  209. package/website/article/energy-scatter/index.html +0 -47
  210. package/website/article/methane/index.html +0 -46
  211. package/website/article/moving-average/index.html +0 -64
  212. package/website/article/non-renewables/index.html +0 -52
  213. package/website/article/renewables-linked/index.html +0 -46
  214. package/website/article/scigen-1805.02474v1-10/index.html +0 -54
  215. package/website/article/shared/util.js +0 -15
  216. /package/website/article/{favicon.ico → src/lib/assets/image/favicon.ico} +0 -0
  217. /package/website/article/{dataset → static/dataset}/SciGen/1805.02474v1-10.json +0 -0
  218. /package/website/article/{dataset → static/dataset}/methane-emissions.json +0 -0
  219. /package/website/article/{dataset → static/dataset}/non-renewables.json +0 -0
  220. /package/website/article/{dataset → static/dataset}/renewable-new.json +0 -0
  221. /package/website/article/{dataset → static/dataset}/renewable.json +0 -0
  222. /package/website/article/{fluid → static/fluid}/1805.02474v1-10.fld +0 -0
  223. /package/website/article/{fluid → static/fluid}/bar-chart-line-chart.fld +0 -0
  224. /package/website/article/{fluid → static/fluid}/convolution/emboss.fld +0 -0
  225. /package/website/article/{fluid → static/fluid}/convolution/testImage.fld +0 -0
  226. /package/website/article/{fluid → static/fluid}/convolution.fld +0 -0
  227. /package/website/article/{fluid → static/fluid}/dataset/scigen/_1805_02474v1_10.fld +0 -0
  228. /package/website/article/{fluid → static/fluid}/energyscatter.fld +0 -0
  229. /package/website/article/{fluid → static/fluid}/methane.fld +0 -0
  230. /package/website/article/{fluid → static/fluid}/methane_data.fld +0 -0
  231. /package/website/article/{fluid → static/fluid}/moving-average.fld +0 -0
  232. /package/website/article/{fluid → static/fluid}/non-renewables.fld +0 -0
  233. /package/website/article/{fluid → static/fluid}/nonRenewables.fld +0 -0
  234. /package/website/article/{fluid → static/fluid}/renewables.fld +0 -0
  235. /package/website/article/{fluid → static/fluid}/scigen.fld +0 -0
  236. /package/website/article/{fluid → static/fluid}/util.fld +0 -0
@@ -0,0 +1,274 @@
1
+ # type Colour = Str
2
+ # type Colours = List Colour
3
+ # type Cat = Str
4
+
5
+ # Group has location (0, 0) because it doesn't interfere with positioning of its children.
6
+ # GraphicsElement -> Point
7
+ def coords(Group(gs)): Point(0, 0)
8
+ def coords(Rect(x, y, _, _, _)): Point(x, y)
9
+ def coords(String(x, y, _, _, _)): Point(x, y)
10
+ def coords(Viewport(x, y, _, _, _, _, _, _, _)): Point(x, y)
11
+
12
+ # GraphicsElement -> Float
13
+ def get_x(g):
14
+ def Point(x, _): coords(g)
15
+ x
16
+
17
+ # GraphicsElement -> Float
18
+ def get_y(g):
19
+ def Point(_, y): coords(g)
20
+ y
21
+
22
+ # Want some kind of typeclass mechanism plus record accessors/updaters.
23
+ # Float -> GraphicsElement -> GraphicsElement
24
+ def set_x(x, Group(gs)):
25
+ error("Group has immutable coordinates")
26
+ def set_x(x, Rect(_, y, w, h, fill)):
27
+ Rect(x, y, w, h, fill)
28
+ def set_x(x, String(_, y, str, anchor, baseline)):
29
+ String(x, y, str, anchor, baseline)
30
+ def set_x(x, Viewport(_, y, w, h, fill, margin, scale, translate, g)):
31
+ Viewport(x, y, w, h, fill, margin, scale, translate, g)
32
+
33
+ # (Point, Point) -> Point
34
+ def dimensions2((Point(x1, y1), Point(x2, y2))):
35
+ Point(max(x1, x2), max(y1, y2))
36
+
37
+ # For Group, dimensions are relative to implicit coords of (0, 0), since a Group's children are effectively
38
+ # positioned relative to parent of Group. For Polymarker, will probably have to ignore the markers themselves,
39
+ # since they are scale-invariant.
40
+ # GraphicsElement -> Point
41
+ def dimensions(Group(gs)):
42
+ foldl(curry(dimensions2), Point(0, 0), map(coords_op, gs))
43
+ def dimensions(Polyline(ps, _, _)):
44
+ foldl(curry(dimensions2), Point(0, 0), ps)
45
+ def dimensions(Rect(_, _, w, h, _)): Point(w, h)
46
+ def dimensions(String(_, _, _, _, _)): Point(0, 0)
47
+ def dimensions(Viewport(_, _, w, h, _, _, _, _, _)): Point(w, h)
48
+
49
+ def coords_op(g):
50
+ def (Point(x, y), Point(w, h)):
51
+ prod(coords, dimensions, g)
52
+ Point(x + w, y + h)
53
+
54
+ # GraphicsElement -> Float
55
+ def width(g):
56
+ def Point(w, _): dimensions(g)
57
+ w
58
+
59
+ # GraphicsElement -> Float
60
+ def height(g):
61
+ def Point(_, h): dimensions(g)
62
+ h
63
+
64
+ # Float -> Float -> List GraphicsElement -> List GraphicsElement
65
+ def space_right(z, sep, gs):
66
+ zip_with(set_x, iterate(length(gs), (+)(sep), z), gs)
67
+
68
+ # Bake colour decisions into the library for the time being. Provide two palettes, so we can have two
69
+ # different sets of categorical values (e.g. countries and energy types). Palettes from colorbrewer2.org
70
+ def colours1: [
71
+ "#66c2a5",
72
+ "#a6d854",
73
+ "#ffd92f",
74
+ "#e5c494",
75
+ "#fc8d62",
76
+ "#b3b3b3",
77
+ "#8da0cb",
78
+ "#e78ac3"
79
+ ]
80
+
81
+ def colours2: [
82
+ "#e41a1c",
83
+ "#377eb8",
84
+ "#4daf4a",
85
+ "#984ea3",
86
+ "#ff7f00",
87
+ "#ffff33",
88
+ "#a65628",
89
+ "#f781bf"
90
+ ]
91
+
92
+ # Compositionality principle: child coords/dimensions are always expressed directly using parent reference
93
+ # frame, to avoid depending on content of child, and so are not themselves scaled. Polyline can't be scaled
94
+ # directly because it inherits its frame of reference from its parent. For Viewport, margin will shrink the
95
+ # available area, possibly to zero, at which point nothing will be rendered.
96
+ # Float -> GraphicsElement -> GraphicsElement
97
+ def scale_to_width(w, Rect(x, y, _, h, fill)):
98
+ Rect(x, y, w, h, fill)
99
+ def scale_to_width(w, Viewport(x, y, w0, h, fill, margin, Scale(x_scale, y_scale), translate, g)):
100
+ def scale: Scale((x_scale * w) / w0, y_scale)
101
+ Viewport(x, y, w, h, fill, margin, scale, translate, g)
102
+
103
+ # Float -> List GraphicsElement -> List GraphicsElement
104
+ def stack_right(sep, gs):
105
+ map(scale_to_width(1 - sep), space_right(sep / 2, 1, gs))
106
+
107
+ # Float -> List GraphicsElement -> GraphicsElement
108
+ def group_right(sep, gs):
109
+ Viewport(0, 0, length(gs), maximum(map(height, gs)), "none", 0, Scale(1, 1), Translate(0, 0), Group(stack_right(sep, gs)))
110
+
111
+ # Heuristic saying how often to place a tick on an axis of length n.
112
+ # Float -> Float
113
+ def tick_every(n):
114
+ def m: floor(log_base(10, n))
115
+
116
+ if n <= 2 * 10 ** m: 2 * 10 ** (m - 1)
117
+ else: 10 ** m
118
+
119
+ def axis_stroke_width: 0.5
120
+ def axis_colour: "black"
121
+ def background_colour: "white"
122
+ def default_margin: 24
123
+ def marker_radius: 3.5
124
+ def tick_length: 4
125
+
126
+ # Helpers for axis functions.
127
+ # Orient -> Colour -> Float -> GraphicsElement
128
+ def tick(Horiz, colour, len):
129
+ Line(Point(0, 0), Point(0, 0 - len), colour, axis_stroke_width)
130
+ def tick(Vert, colour, len):
131
+ Line(Point(0, 0), Point(0 - len, 0), colour, axis_stroke_width)
132
+
133
+ # Orient -> Float -> Float -> Str -> GraphicsElement
134
+ def label(Horiz, x, distance, str):
135
+ String(x, (0 - distance) - 4, str, "middle", "hanging")
136
+ def label(Vert, x, distance, str):
137
+ String(0 - distance, x, str, "end", "central")
138
+
139
+ # Orient -> Colour -> Float -> Str -> GraphicsElement
140
+ def labelled_tick(orient, colour, len, str):
141
+ Group([
142
+ tick(orient, colour, len),
143
+ label(orient, 0, len, str)
144
+ ])
145
+
146
+ # Orient -> Float -> Float -> Point
147
+ def mk_point(Horiz, x, y): Point(y, x)
148
+ def mk_point(Vert, x, y): Point(x, y)
149
+
150
+ # x is position of this axis on the other axis. Returns axis and position of last tick.
151
+ # Orient -> Float -> Float -> Float -> GraphicsElement
152
+ def axis(orient, x, start, end):
153
+ def tick_sp: tick_every(end - start)
154
+ def first_tick: ceiling_to_nearest(start, tick_sp)
155
+ def last_tick: ceiling_to_nearest(end, tick_sp)
156
+ def n: floor((end - first_tick) / tick_sp) + 1
157
+ def ys: iterate(n, (+)(tick_sp), first_tick)
158
+ def ys:
159
+ if first_tick > start: start :| ys
160
+ else: ys
161
+ def ys:
162
+ if last_tick > end: concat2(ys, [last_tick])
163
+ else: ys
164
+ def ps: map(mk_point(orient, x), ys)
165
+ def ax:
166
+ Group([
167
+ Line(head(ps), last(ps), axis_colour, axis_stroke_width),
168
+ Polymarkers(ps, flip(map, ys, compose(labelled_tick(orient, axis_colour, tick_length), num_to_str)))
169
+ ])
170
+
171
+ (ax, last_tick)
172
+
173
+ # x is position of this axis on the other axis.
174
+ # Orient -> Float -> List Cat -> GraphicsElement
175
+ def cat_axis(orient, x, cat_values):
176
+ def ys: iterate(length(cat_values) + 1, (+)(1), 0)
177
+ def ps: map(mk_point(orient, x), ys)
178
+
179
+ Group([
180
+ Line(head(ps), last(ps), axis_colour, axis_stroke_width),
181
+ Polymarkers(tail(ps), map(const(tick(orient, axis_colour, tick_length)), cat_values)),
182
+ Polymarkers(flip(map, tail(ps), lambda Point(x, y): Point(x - 0.5, y)), map(label(orient, -0.5, 0), cat_values))
183
+ ])
184
+
185
+ # Float -> Float -> Float -> Float -> List GraphicsElement -> GraphicsElement
186
+ def viewport(x_start, x_finish, y_finish, margin, gs):
187
+ Viewport(0, 0, x_finish - x_start, y_finish, background_colour, margin, Scale(1, 1), Translate(0 - x_start, 0), Group(gs))
188
+
189
+ # Plot a map of x values to lists of (categorical value, y value) pairs. Importantly, assume all data is uniform
190
+ # (categorical keys are the same for each x value and are ordered the same each time).
191
+ # Bool -> Colours -> Float -> List (Float, List (Cat, Float)) -> GraphicsElement
192
+ def line_chart(with_axes, colours, x_start, data):
193
+ def xs: map(fst, data)
194
+ def n_cat: length(snd(head(data)))
195
+
196
+ # (Int, Colour) -> GraphicsElement
197
+ def plot((n, colour)):
198
+ def ps:
199
+ map(lambda (x, kvs): Point(x, snd(nth(n, kvs))), data)
200
+
201
+ Group([
202
+ Polyline(ps, colour, 1),
203
+ Polymarkers(ps, repeat(length(ps), Circle(0, 0, marker_radius, colour)))
204
+ ])
205
+
206
+ # List GraphicsElement
207
+ def lines:
208
+ zip_with(curry(plot), iterate(n_cat, (+)(1), 0), colours)
209
+ def x_finish: last(xs)
210
+ def y_finish:
211
+ maximum(flip(map, data, lambda (_, kvs): maximum(map(snd, kvs))))
212
+
213
+ if with_axes:
214
+ def (x_axis, x_finish): axis(Horiz, 0, x_start, x_finish)
215
+ def (y_axis, y_finish_): axis(Vert, x_start, 0, y_finish)
216
+ viewport(x_start, x_finish, y_finish_, default_margin, x_axis :| y_axis :| lines)
217
+ else:
218
+ viewport(x_start, x_finish, y_finish, 0, lines)
219
+
220
+ # Plot a chart of categorical values on the x-axis and renderings of the corresponding a-value on the y-axis.
221
+ # (Colours -> List a -> GraphicsElement) -> Bool -> Colours -> Float -> List (Cat, a) -> GraphicsElement
222
+ def categorical_chart(plot_value, with_axes, colours, sep, data):
223
+ def gs:
224
+ stack_right(sep, plot_value(colours, map(snd, data)))
225
+ def w: length(gs)
226
+ def h: maximum(map(height, gs))
227
+
228
+ if with_axes:
229
+ def x_axis: cat_axis(Horiz, 0, map(fst, data))
230
+ def (y_axis, h_): axis(Vert, 0, 0, h)
231
+ viewport(0, w, h_, default_margin, concat2(gs, [x_axis, y_axis]))
232
+ else:
233
+ viewport(0, w, h, 0, gs)
234
+
235
+ # Colours -> List a -> GraphicsElement
236
+ def rects(colours, ns):
237
+ zip_with(lambda colour, n: Rect(0, 0, 1, n, colour), colours, ns)
238
+
239
+ # First component of data (categorical value) currently ignored; values just mapped positionally to colors.
240
+ # Can we use Group instead of Viewport here?
241
+ # Colours -> List (a, Num) -> GraphicsElement
242
+ def stacked_bar(colours, ns):
243
+ def heights: map(snd, ns)
244
+ def subtotals: scanl1((+), 0, heights)
245
+ def dims: zip(0 :| subtotals, heights)
246
+ def rects:
247
+ map(lambda ((y, height), colour): Rect(0, y, 1, height, colour), zip(dims, colours))
248
+
249
+ Viewport(0, 0, 1, last(subtotals), "none", 0, Scale(1, 1), Translate(0, 0), Group(rects))
250
+
251
+ # Bool -> Colours -> Float -> List (a, Float) -> GraphicsElement
252
+ def bar_chart:
253
+ categorical_chart(rects)
254
+
255
+ # For each categorical value of type a, plot a bar chart for the corresponding b-indexed data.
256
+ # Bool -> Colours -> Float -> List (a, List (b, Float)) -> GraphicsElement
257
+ def grouped_bar_chart:
258
+ categorical_chart(compose(map, flip(bar_chart(False), 0)))
259
+
260
+ # See stacked_bar for strong (unjustified) assumption about uniformity of data.
261
+ # Bool -> Colours -> Num -> List (a, List (b, Num)) -> GraphicsElement
262
+ def stacked_bar_chart:
263
+ categorical_chart(compose(map, stacked_bar))
264
+
265
+ # Bit of a hack, but how text fits into our model is a bit unclear at the moment.
266
+ # Str -> GraphicsElement -> GraphicsElement
267
+ def caption(str, Viewport(x, y, w, h, fill, margin, scale, translate, g)):
268
+ def g_:
269
+ Group([
270
+ String(x + w / 2, -2, str, "middle", "hanging"),
271
+ Viewport(0, 0, w, h, fill, margin, scale, translate, g)
272
+ ])
273
+
274
+ Viewport(x, y, w, h, background_colour, default_margin / 2 + 4, Scale(1, 1), Translate(0, 0), g_)
@@ -0,0 +1,40 @@
1
+ def zero(m, n, image):
2
+ def (m_size, n_size): dims(image)
3
+ if m >= 0 and m < m_size and n >= 0 and n < n_size: image ! (m, n)
4
+ else: 0
5
+
6
+ def wrap(m, n, image):
7
+ def (m_size, n_size): dims(image)
8
+ image ! (m % m_size, n % n_size)
9
+
10
+ def extend(m, n, image):
11
+ def (m_size, n_size): dims(image)
12
+ def m_:
13
+ min(max(m, 0), m_size - 1)
14
+ def n_:
15
+ min(max(n, 0), n_size - 1)
16
+ image ! (m_, n_)
17
+
18
+ def matrix_sum(matr):
19
+ def (m, n): dims(matr)
20
+ foldl((+), 0, [matr ! (i, j) for (i, j) in range2d((0, 0), (m, n))])
21
+
22
+ def convolve(image, kernel, lookup):
23
+ def ((m, n), (i, j)):
24
+ (dims(image), dims(kernel))
25
+ def (half_i, half_j):
26
+ (i |quot| 2, j |quot| 2)
27
+ def area: i * j
28
+ def interMatrix(m_, n_):
29
+ [| lookup(m_ + i_ - half_i, n_ + j_ - half_j, image) * kernel ! (i_, j_) for (i_, j_) in (i, j) |]
30
+
31
+ [| matrix_sum(interMatrix(m_, n_)) |quot| area for (m_, n_) in (m, n) |]
32
+
33
+ def mat_mul(a, b):
34
+ def ((m, n), (i, j)): (dims(a), dims(b))
35
+
36
+ if not(n == i):
37
+ error("Dimensions don't line up")
38
+ else:
39
+ @doc(f"""Intermediate matrix""")
40
+ [| sum([a!(i_, k) * b!(k, j_) for k in range(0, n)]) for (i_, j_) in (m, j) |]
@@ -0,0 +1,348 @@
1
+ # "Num" throughout means (Int + Float).
2
+
3
+ # To resolve in new semantics: expected dependencies don't arise for and/or unless they make a fresh value.
4
+ # Bool -> Bool
5
+ def and(False, y): False
6
+ def and(True, True): True
7
+ def and(True, False): False
8
+
9
+ # Bool -> Bool
10
+ def or(True, y): True
11
+ def or(False, True): True
12
+ def or(False, False): False
13
+
14
+ # Bool -> Bool
15
+ def not(True): False
16
+ def not(False): True
17
+
18
+ # Int -> Int -> Ordering
19
+ def compare(x, y):
20
+ if x > y: GT
21
+ else:
22
+ if x < y: LT
23
+ else: EQ
24
+
25
+ # (b -> b -> c) -> (a -> b) -> a -> a -> c
26
+ def on(bin_op, prop, x, y):
27
+ bin_op(prop(x), prop(y))
28
+
29
+ # Num -> Num
30
+ def negate: (-)(0)
31
+
32
+ # Log of x in base y.
33
+ # Float -> Float -> Float
34
+ def log_base(x, y): log(y) / log(x)
35
+
36
+ # Float -> Float -> Float
37
+ def ceiling_to_nearest(n, m): ceiling(n / m) * m
38
+
39
+ # (b -> c) -> (a -> b) -> a -> c
40
+ # Want infix <<<
41
+ def compose(f, g, x): f(g(x))
42
+
43
+ # ((a, b) -> c) -> a -> b -> c
44
+ def curry(f, x, y): f((x, y))
45
+
46
+ # (a -> b -> c) -> (a, b) -> c
47
+ def uncurry(f, (x, y)): f(x, y)
48
+
49
+ # a -> b -> a
50
+ def const(x, _): x
51
+
52
+ # (a -> b) -> (a, c) -> (b, c)
53
+ def first(f, (a, c)): (f(a), c)
54
+
55
+ # (a, b) -> b
56
+ def snd((_, y)): y
57
+
58
+ # (a -> b) -> (c, a) -> (c, b)
59
+ def second(f, (c, a)): (c, f(a))
60
+
61
+ # (a -> b -> c) -> b -> a -> c
62
+ def flip(f, x, y): f(y, x)
63
+
64
+ # (a, b) -> a
65
+ def fst((x, _)): x
66
+
67
+ # a -> a
68
+ def id(x): x
69
+
70
+ # (a -> b) -> (a -> c) -> a -> (b, c)
71
+ # Want infix &&&
72
+ def prod(f, g, x): (f(x), g(x))
73
+
74
+ # (a, b) -> (b, a)
75
+ def swap((a, b)): (b, a)
76
+
77
+ # List a -> a
78
+ def head([]):
79
+ error("Can't take head of empty list")
80
+ def head(x :| _): x
81
+
82
+ # List a -> List a
83
+ def tail([]):
84
+ error("Can't take tail of empty list")
85
+ def tail(_ :| xs): xs
86
+
87
+ # Eq a => a -> List a -> Bool
88
+ def elem(x, []): False
89
+ def elem(x, y :| xs):
90
+ x == y or elem(x, xs)
91
+
92
+ # (a -> Bool) -> List a -> Option a
93
+ def find(p, []): None
94
+ def find(p, x :| xs):
95
+ if p(x): Some(x)
96
+ else: find(p, xs)
97
+
98
+ # String -> String -> List Dict -> Option Dict
99
+ def find_with_key(k, v, rs):
100
+ find(lambda y: y[k] == v, rs)
101
+
102
+ # Option a -> a
103
+ def from_some(None): error("Expected Some!")
104
+ def from_some(Some(x)): x
105
+
106
+ # Option a -> a -> a
107
+ def from_option(None, y): y
108
+ def from_option(Some(x), _): x
109
+
110
+ # (a -> Bool) -> List a -> List a
111
+ def filter(p, []): []
112
+ def filter(p, x :| xs):
113
+ def ys: filter(p, xs)
114
+
115
+ if p(x): x :| ys
116
+ else: ys
117
+
118
+ # (a -> Option b) -> List a -> List b
119
+ def filter_map(p, []): []
120
+ def filter_map(p, x :| xs):
121
+ match p(x):
122
+ case None: filter_map(f, xs)
123
+ case Some(y):
124
+ y :| filter_map(f, xs)
125
+
126
+ # Equivalent to but more efficient than length ∘ filter(p)
127
+ def count_if(p):
128
+ def f(n, x):
129
+ if p(x): n + 1
130
+ else: n
131
+ foldl(f, 0)
132
+
133
+ # Split list into longest initial segment whose elements satisfy a predicate, and the rest.
134
+ # (a -> Bool) -> List a -> { init :: List a, rest :: List a }
135
+ def span(p, Nil): { init: Nil, rest: Nil }
136
+ def span(p, x :| xs_):
137
+ if p(x):
138
+ def { init: ys, rest: zs }: span(p, xs_)
139
+ { init: x :| ys, rest: zs }
140
+ else:
141
+ { init: Nil, rest: x :| xs_ }
142
+
143
+ # Each returned list is non-empty.
144
+ # (a -> a -> Bool) -> List a -> List (List a)
145
+ def group_by(eq, Nil): Nil
146
+ def group_by(eq, x :| xs):
147
+ def { init: ys, rest: zs }: span(eq(x), xs)
148
+ (x :| ys) :| group_by(eq, zs)
149
+
150
+ # (a -> b -> a) -> a -> List b -> a
151
+ def foldl(op, z, []): z
152
+ def foldl(op, z, x :| xs):
153
+ foldl(op, op(z, x), xs)
154
+
155
+ # (a -> b -> a) -> List b -> a
156
+ def foldl1(op, x :| xs): foldl(op, x, xs)
157
+
158
+ # (a -> b -> b) -> b -> List a -> b
159
+ def foldr(op, z, []): z
160
+ def foldr(op, z, x :| xs):
161
+ op(x, foldr(op, z, xs))
162
+
163
+ # (a -> b -> b) -> List a -> b
164
+ def foldr1(op, [x]): x
165
+ def foldr1(op, x :| y :| xs):
166
+ op(x, foldr1(op, y :| xs))
167
+
168
+ # (a -> b -> a) -> a -> List b -> List a
169
+ def scanl1(op, z, xs):
170
+ def go(x, continue, acc):
171
+ def next: op(acc, x)
172
+ next :| continue(next)
173
+ foldr(go, const([]), xs, z)
174
+
175
+ # (a -> b -> a) -> a -> List b -> List a
176
+ def scanl(op, z, xs):
177
+ z :| scanl1(op, z, xs)
178
+
179
+ # (a -> b) -> List a -> List b
180
+ def map(f, []): []
181
+ def map(f, x :| xs): f(x) :| map(f, xs)
182
+
183
+ # (List a, List a) -> List a
184
+ def append(([], ys)): ys
185
+ def append((x :| xs, ys)):
186
+ x :| append((xs, ys))
187
+
188
+ # List a -> List -> List a
189
+ # Want infix ++
190
+ def concat2([], ys): ys
191
+ def concat2(x :| xs, ys):
192
+ x :| concat2(xs, ys)
193
+
194
+ # List (List a) -> List a
195
+ def concat: foldr(concat2, [])
196
+
197
+ # (a -> List b) -> List a -> List b
198
+ def concat_map(f, xs): concat(map(f, xs))
199
+
200
+ # List String -> String
201
+ def join: foldr((++), "")
202
+
203
+ # List a -> a -> List a
204
+ def intersperse([], _): []
205
+ def intersperse([x], _): [x]
206
+ def intersperse(x :| y :| ys, sep):
207
+ x :| sep :| intersperse(y :| ys, sep)
208
+
209
+ # Int -> (a -> a) -> a -> List a
210
+ def iterate(n, f, z):
211
+ if n == 0: []
212
+ else:
213
+ z :| map(f, iterate(n - 1, f, z))
214
+
215
+ # List Num -> Num
216
+ def sum: foldr((+), 0)
217
+
218
+ # List Num -> Num
219
+ def mean(xs):
220
+ def (s, n): foldl(lambda (s, n), x: (s + x, n + 1), (0, 0), xs)
221
+ s / n
222
+
223
+ # List a -> a
224
+ def last([x]): x
225
+ def last(x :| y :| ys): last(y :| ys)
226
+
227
+ # List a -> Int
228
+ def length([]): 0
229
+ def length(_ :| xs): 1 + length(xs)
230
+
231
+ # List a -> List a
232
+ def reverse([]): []
233
+ def reverse(x :| xs):
234
+ append((reverse(xs), [x]))
235
+
236
+ # Int -> a -> List a
237
+ def repeat: flip(iterate, id)
238
+
239
+ # Int -> List a -> List a
240
+ def take(n, xs):
241
+ if n <= 0: []
242
+ else:
243
+ match xs:
244
+ case []: []
245
+ case x :| xs:
246
+ x :| take(n - 1, xs)
247
+
248
+ # Int -> List a -> List a
249
+ def drop(n, xs):
250
+ if n <= 0: xs
251
+ else:
252
+ match xs:
253
+ case []: []
254
+ case _ :| xs: drop(n - 1, xs)
255
+
256
+ # Int -> List a -> List a
257
+ def last_n(n, xs):
258
+ foldl(compose(const, drop(1)), xs, drop(n, xs))
259
+
260
+ # Expects non-negative integer as first argument and non-empty list as second argument.
261
+ # Int -> List a -> a
262
+ def nth(n, x :| xs):
263
+ if n == 0: x
264
+ else: nth(n - 1, xs)
265
+
266
+ # Int -> Int -> List (List Int) -> Int
267
+ def nth2(i, j, xss):
268
+ nth(j, nth(i, xss))
269
+
270
+ # Partial; requires k to be in the map.
271
+ # Int -> List (Int, b) -> b
272
+ def lookup(k, []):
273
+ error("Key not found in map")
274
+ def lookup(k, (k_, v) :| kvs):
275
+ if k == k_: v
276
+ else: lookup(k, kvs)
277
+
278
+ # Int -> Int -> Int
279
+ def max(n, m):
280
+ if n > m: n
281
+ else: m
282
+
283
+ # Int -> Int -> Int
284
+ def min(n, m):
285
+ if n < m: n
286
+ else: m
287
+
288
+ # List Int -> Int
289
+ def maximum: foldr1(max)
290
+
291
+ # List Int -> Int
292
+ def minimum: foldr1(min)
293
+
294
+ # List (a, b) -> (List a, List b)
295
+ def unzip([]): ([], [])
296
+ def unzip((x, y) :| zs):
297
+ def (xs, ys): unzip(zs)
298
+ (x :| xs, y :| ys)
299
+
300
+ # (a -> b -> c) -> List a -> List b -> List c
301
+ def zip_with(op, [], ys): []
302
+ def zip_with(op, x :| xs, []): []
303
+ def zip_with(op, x :| xs, y :| ys):
304
+ op(x, y) :| zip_with(op, xs, ys)
305
+
306
+ # List a -> List b -> List (a, b)
307
+ def zip: zip_with(curry(id))
308
+
309
+ # Int -> Int -> List Int
310
+ def range(n, m):
311
+ if n < m: n :| range(n + 1, m)
312
+ else: []
313
+
314
+ # (Int, Int) -> (Int, Int) -> List (Int, Int)
315
+ def range2d((m1, n1), (m2, n2)):
316
+ [(i1, i2) for i1 in range(m1, m2) for i2 in range(n1, n2)]
317
+
318
+ # Int -> Int -> Int
319
+ def abs(x, y):
320
+ if x - y < 0: negate(x - y)
321
+ else: x - y
322
+
323
+ # Eq a => [a] -> [a]
324
+ def nub(xs):
325
+ def nub_([], _): []
326
+ def nub_(x :| xs, ys):
327
+ if x |elem| ys:
328
+ nub_(xs, ys)
329
+ else:
330
+ x :| nub_(xs, x :| ys)
331
+
332
+ nub_(xs, [])
333
+
334
+ # Int -> Int -> [a] -> [a]
335
+ def slice(begin, end, xs):
336
+ take(end - begin, drop(begin, xs))
337
+
338
+ # (a -> Boolean) -> List a -> (List a, List a)
339
+ def split_on(p, data):
340
+ def go(fls, trs, []):
341
+ (reverse(fls), reverse(trs))
342
+ def go(fls, trs, x :| xs):
343
+ if p(x):
344
+ go(fls, x :| trs, xs)
345
+ else:
346
+ go(x :| fls, trs, xs)
347
+
348
+ go([], [], data)