@explorable-viz/fluid 0.10.2 → 0.10.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.
@@ -1,221 +1,246 @@
1
- -- typedef Colour = Str
2
- -- typedef Colours = List Colour
3
- -- typedef Cat = Str
4
-
5
- -- Group has location (0, 0) because it doesn't interfere with positioning of its children.
6
- -- GraphicsElement -> Point
7
- let coords (Group gs) = Point 0 0;
8
- coords (Rect x y _ _ _) = Point x y;
9
- coords (String x y _ _ _) = Point x y;
10
- coords (Viewport x y _ _ _ _ _ _ _) = Point x y;
11
-
12
- -- GraphicsElement -> Float
13
- let get_x g = let Point x _ = coords g in x;
14
-
15
- -- GraphicsElement -> Float
16
- let get_y g = let Point _ y = coords g in x;
17
-
18
- -- Want some kind of typeclass mechanism plus record accessors/updaters.
19
- -- Float -> GraphicsElement -> GraphicsElement
20
- let set_x x (Group gs) = error "Group has immutable coordinates";
21
- set_x x (Rect _ y w h fill) = Rect x y w h fill;
22
- set_x x (String _ y str anchor baseline) = String x y str anchor baseline;
23
- set_x x (Viewport _ y w h fill margin scale translate g) = Viewport x y w h fill margin scale translate g;
24
-
25
- -- (Point, Point) -> Point
26
- let dimensions2 (Point x1 y1, Point x2 y2) = Point (max x1 x2) (max y1 y2);
27
-
28
- -- For Group, dimensions are relative to implicit coords of (0, 0), since a Group's children are effectively
29
- -- positioned relative to parent of Group. For Polymarker, will probably have to ignore the markers themselves,
30
- -- since they are scale-invariant.
31
- -- GraphicsElement -> Point
32
- let
33
- dimensions (Group gs) = foldl (curry dimensions2) (Point 0 0) (map (coords_op) gs);
34
- dimensions (Polyline ps _ _) = foldl (curry dimensions2) (Point 0 0) ps;
35
- dimensions (Rect _ _ w h _) = Point w h;
36
- dimensions (String _ _ _ _ _) = Point 0 0; -- treat text like markers; scale-invariant
37
- dimensions (Viewport _ _ w h _ _ _ _ _) = Point w h;
38
-
39
- coords_op g =
40
- let (Point x y, Point w h) = prod coords dimensions g in
41
- Point (x + w) (y + h);
42
-
43
- -- GraphicsElement -> Float
44
- let width g = let Point w _ = dimensions g in w;
45
-
46
- -- GraphicsElement -> Float
47
- let height g = let Point _ h = dimensions g in h;
48
-
49
- -- Float -> Float -> List GraphicsElement -> List GraphicsElement
50
- let spaceRight z sep gs =
51
- zipWith set_x (iterate (length gs) ((+) sep) z) gs;
52
-
53
- -- Bake colour decisions into the library for the time being. Provide two palettes, so we can have two
54
- -- different sets of categorical values (e.g. countries and energy types). Palettes from colorbrewer2.org.
55
- let colours1 = ["#66c2a5", "#a6d854", "#ffd92f", "#e5c494", "#fc8d62", "#b3b3b3", "#8da0cb", "#e78ac3"];
56
- let colours2 = ["#e41a1c", "#377eb8", "#4daf4a", "#984ea3", "#ff7f00", "#ffff33", "#a65628", "#f781bf"];
57
-
58
- -- Compositionality principle: child coords/dimensions are always expressed directly using parent reference
59
- -- frame, to avoid depending on content of child, and so are not themselves scaled. Polyline can't be scaled
60
- -- directly because it inherits its frame of reference from its parent. For Viewport, margin will shrink the
61
- -- available area, possibly to zero, at which point nothing will be rendered.
62
- -- Float -> GraphicsElement -> GraphicsElement
63
- let scaleToWidth w (Rect x y _ h fill) = Rect x y w h fill;
64
- scaleToWidth w (Viewport x y w0 h fill margin (Scale x_scale y_scale) translate g) =
65
- let scale = Scale (x_scale * w / w0) y_scale in
66
- Viewport x y w h fill margin scale translate g;
67
-
68
- -- Float -> List GraphicsElement -> List GraphicsElement
69
- let stackRight sep gs =
70
- map (scaleToWidth (1 - sep)) (spaceRight (sep / 2) 1 gs);
71
-
72
- -- Float -> List GraphicsElement -> GraphicsElement
73
- let groupRight sep gs =
74
- Viewport 0 0 (length gs) (maximum (map height gs)) "none" 0 (Scale 1 1) (Translate 0 0) (Group (stackRight sep gs));
75
-
76
- -- Heuristic saying how often to place a tick on an axis of length n.
77
- -- Float -> Float
78
- let tickEvery n =
79
- let m = floor (logBase 10 n) in
80
- if n <= 2 * 10 ** m
81
- then 2 * 10 ** (m - 1)
82
- else 10 ** m;
83
-
84
- let axisStrokeWidth = 0.5;
85
- axisColour = "black";
86
- backgroundColour = "white";
87
- defaultMargin = 24;
88
- markerRadius = 3.5;
89
- tickLength = 4;
90
-
91
- -- Helpers for axis functions.
92
- -- Orient -> Colour -> Float -> GraphicsElement
93
- let tick Horiz colour len = Line (Point 0 0) (Point 0 (0 - len)) colour axisStrokeWidth;
94
- tick Vert colour len = Line (Point 0 0) (Point (0 - len) 0) colour axisStrokeWidth;
95
-
96
- -- Orient -> Float -> Float -> Str -> GraphicsElement
97
- let label Horiz x distance str = String x (0 - distance - 4) str "middle" "hanging";
98
- label Vert x distance str = String (0 - distance) x str "end" "central";
99
-
100
- -- Orient -> Colour -> Float -> Str -> GraphicsElement
101
- let labelledTick orient colour len str =
102
- Group [tick orient colour len, label orient 0 len str];
103
-
104
- -- Orient -> Float -> Float -> Point
105
- let mkPoint Horiz x y = Point y x;
106
- mkPoint Vert x y = Point x y;
107
-
108
- -- x is position of this axis on the other axis. Returns axis and position of last tick.
109
- -- Orient -> Float -> Float -> Float -> GraphicsElement
110
- let axis orient x start end =
111
- let tickSp = tickEvery (end - start);
112
- firstTick = ceilingToNearest start tickSp;
113
- lastTick = ceilingToNearest end tickSp;
114
- n = floor ((end - firstTick) / tickSp) + 1;
115
- ys = iterate n ((+) tickSp) firstTick;
116
- -- avoid redundant start and end points
117
- ys = match firstTick > start as {
118
- True -> start : ys;
119
- False -> ys
120
- };
121
- ys = match lastTick > end as {
122
- True -> concat2 ys [lastTick];
123
- False -> ys
124
- };
125
- ps = map (mkPoint orient x) ys;
126
- ax = Group [
127
- Line (head ps) (last ps) axisColour axisStrokeWidth,
128
- Polymarkers ps (flip map ys (compose (labelledTick orient axisColour tickLength) numToStr))
129
- ]
130
- in (ax, lastTick);
131
-
132
- -- x is position of this axis on the other axis.
133
- -- Orient -> Float -> List Cat -> GraphicsElement
134
- let catAxis orient x catValues =
135
- let ys = iterate (length catValues + 1) ((+) 1) 0;
136
- ps = map (mkPoint orient x) ys
137
- in Group [
138
- Line (head ps) (last ps) axisColour axisStrokeWidth,
139
- Polymarkers (tail ps) (map (const (tick orient axisColour tickLength)) catValues),
140
- Polymarkers (flip map (tail ps) (fun (Point x y) -> Point (x - 0.5) y)) (map (label orient -0.5 0) catValues)
141
- ];
142
-
143
- -- Float -> Float -> Float -> Float -> List GraphicsElement -> GraphicsElement
144
- let viewport x_start x_finish y_finish margin gs =
145
- Viewport 0 0 (x_finish - x_start) y_finish backgroundColour margin
146
- (Scale 1 1) (Translate (0 - x_start) 0) (Group gs);
147
-
148
- -- Plot a map of x values to lists of (categorical value, y value) pairs. Importantly, assume all data is uniform
149
- -- (categorical keys are the same for each x value and are ordered the same each time).
150
- -- Bool -> Colours -> Float -> List (Float, List (Cat, Float)) -> GraphicsElement
151
- let lineChart withAxes colours x_start data =
152
- let xs = map fst data;
153
- nCat = length (snd (head data));
154
- -- (Int, Colour) -> GraphicsElement
155
- let plot (n, colour) =
156
- let ps = map (fun (x, kvs) -> Point x (snd (nth n kvs))) data
157
- in Group [
158
- Polyline ps colour 1,
159
- Polymarkers ps (repeat (length ps) (Circle 0 0 markerRadius colour))
160
- ];
161
- -- List GraphicsElement
162
- let lines = zipWith (curry plot) (iterate nCat ((+) 1) 0) colours;
163
- x_finish = last xs;
164
- y_finish = maximum (flip map data (fun (_, kvs) -> maximum (map snd kvs)))
165
- in match withAxes as {
166
- True ->
167
- let (x_axis, x_finish) = axis Horiz 0 x_start x_finish;
168
- (y_axis, y_finish') = axis Vert x_start 0 y_finish
169
- in viewport x_start x_finish y_finish' defaultMargin (x_axis : y_axis : lines);
170
- False -> viewport x_start x_finish y_finish 0 lines
171
- };
172
-
173
- -- Plot a chart of categorical values on the x-axis and renderings of the corresponding a-value on the y-axis.
174
- -- (Colours -> List a -> GraphicsElement) -> Bool -> Colours -> Float -> List (Cat, a) -> GraphicsElement
175
- let categoricalChart plotValue withAxes colours sep data =
176
- let gs = stackRight sep (plotValue colours (map snd data));
177
- w = length gs;
178
- h = maximum (map height gs)
179
- in match withAxes as {
180
- True ->
181
- let x_axis = catAxis Horiz 0 (map fst data);
182
- (y_axis, h') = axis Vert 0 0 h
183
- in viewport 0 w h' defaultMargin (concat2 gs [x_axis, y_axis]); -- axes on top
184
- False -> viewport 0 w h 0 gs
185
- };
186
-
187
- -- Colours -> List a -> GraphicsElement
188
- let rects colours ns =
189
- zipWith (fun colour n -> Rect 0 0 1 n colour) colours ns;
190
-
191
- -- First component of data (categorical value) currently ignored; values just mapped positionally to colors.
192
- -- Can we use Group instead of Viewport here?
193
- -- Colours -> List (a, Num) -> GraphicsElement
194
- let stackedBar colours ns =
195
- let heights = map snd ns;
196
- subtotals = scanl1 (+) 0 heights;
197
- dims = zip (0 : subtotals) heights;
198
- rects = map
199
- (fun ((y, height), colour) -> Rect 0 y 1 height colour)
200
- (zip dims colours)
201
- in Viewport 0 0 1 (last subtotals) "none" 0 (Scale 1 1) (Translate 0 0) (Group rects);
202
-
203
- -- Bool -> Colours -> Float -> List (a, Float) -> GraphicsElement
204
- let barChart = categoricalChart rects;
205
-
206
- -- For each categorical value of type a, plot a bar chart for the corresponding b-indexed data.
207
- -- Bool -> Colours -> Float -> List (a, List (b, Float)) -> GraphicsElement
208
- let groupedBarChart = categoricalChart (compose map (flip (barChart False) 0));
209
-
210
- -- See stackedBar for strong (unjustified) assumption about uniformity of data.
211
- -- Bool -> Colours -> Num -> List (a, List (b, Num)) -> GraphicsElement
212
- let stackedBarChart = categoricalChart (compose map stackedBar);
213
-
214
- -- Bit of a hack, but how text fits into our model is a bit unclear at the moment.
215
- -- Str -> GraphicsElement -> GraphicsElement
216
- let caption str (Viewport x y w h fill margin scale translate g) =
217
- let g' = Group [
218
- String (x + w / 2) -2 str "middle" "hanging",
219
- Viewport 0 0 w h fill margin scale translate g
220
- ]
221
- in Viewport x y w h backgroundColour (defaultMargin / 2 + 4) (Scale 1 1) (Translate 0 0) g';
1
+ def coords(Group(gs)): Point(0, 0)
2
+ def coords(Rect(x, y, _, _, _)): Point(x, y)
3
+ def coords(String(x, y, _, _, _)): Point(x, y)
4
+ def coords(Viewport(x, y, _, _, _, _, _, _, _)): Point(x, y);
5
+
6
+ def get_x(g):
7
+ def Point(x, _): coords(g);
8
+
9
+ x;
10
+
11
+ def get_y(g):
12
+ def Point(_, y): coords(g);
13
+
14
+ x;
15
+
16
+ def set_x(x, Group(gs)):
17
+ error("Group has immutable coordinates")
18
+ def set_x(x, Rect(_, y, w, h, fill)):
19
+ Rect(x, y, w, h, fill)
20
+ def set_x(x, String(_, y, str, anchor, baseline)):
21
+ String(x, y, str, anchor, baseline)
22
+ def set_x(x, Viewport(_, y, w, h, fill, margin, scale, translate, g)):
23
+ Viewport(x, y, w, h, fill, margin, scale, translate, g);
24
+
25
+ def dimensions2((Point(x1, y1), Point(x2, y2))):
26
+ Point(max(x1, x2), max(y1, y2));
27
+
28
+ def dimensions(Group(gs)):
29
+ foldl(curry(dimensions2), Point(0, 0), map(coords_op, gs))
30
+ def dimensions(Polyline(ps, _, _)):
31
+ foldl(curry(dimensions2), Point(0, 0), ps)
32
+ def dimensions(Rect(_, _, w, h, _)): Point(w, h)
33
+ def dimensions(String(_, _, _, _, _)): Point(0, 0)
34
+ def dimensions(Viewport(_, _, w, h, _, _, _, _, _)): Point(w, h)
35
+ def coords_op(g):
36
+ def (Point(x, y), Point(w, h)):
37
+ prod(coords, dimensions, g);
38
+
39
+ Point(x + w, y + h);
40
+
41
+ def width(g):
42
+ def Point(w, _): dimensions(g);
43
+
44
+ w;
45
+
46
+ def height(g):
47
+ def Point(_, h): dimensions(g);
48
+
49
+ h;
50
+
51
+ def spaceRight(z, sep, gs):
52
+ zipWith(set_x, iterate(length(gs), (+)(sep), z), gs);
53
+
54
+ def colours1: [
55
+ "#66c2a5",
56
+ "#a6d854",
57
+ "#ffd92f",
58
+ "#e5c494",
59
+ "#fc8d62",
60
+ "#b3b3b3",
61
+ "#8da0cb",
62
+ "#e78ac3"
63
+ ];
64
+
65
+ def colours2: [
66
+ "#e41a1c",
67
+ "#377eb8",
68
+ "#4daf4a",
69
+ "#984ea3",
70
+ "#ff7f00",
71
+ "#ffff33",
72
+ "#a65628",
73
+ "#f781bf"
74
+ ];
75
+
76
+ def scaleToWidth(w, Rect(x, y, _, h, fill)):
77
+ Rect(x, y, w, h, fill)
78
+ def scaleToWidth(w, Viewport(x, y, w0, h, fill, margin, Scale(x_scale, y_scale), translate, g)):
79
+ def scale:
80
+ Scale((x_scale * w) / w0, y_scale);
81
+
82
+ Viewport(x, y, w, h, fill, margin, scale, translate, g);
83
+
84
+ def stackRight(sep, gs):
85
+ map(scaleToWidth(1 - sep), spaceRight(sep / 2, 1, gs));
86
+
87
+ def groupRight(sep, gs):
88
+ Viewport(0, 0, length(gs), maximum(map(height, gs)), "none", 0, Scale(1, 1), Translate(0, 0), Group(stackRight(sep, gs)));
89
+
90
+ def tickEvery(n):
91
+ def m:
92
+ floor(logBase(10, n));
93
+
94
+ if n <= 2 * 10 ** m: 2 * 10 ** (m - 1)
95
+ else: 10 ** m;
96
+
97
+ def axisStrokeWidth: 0.5
98
+ def axisColour: "black"
99
+ def backgroundColour: "white"
100
+ def defaultMargin: 24
101
+ def markerRadius: 3.5
102
+ def tickLength: 4;
103
+
104
+ def tick(Horiz, colour, len):
105
+ Line(Point(0, 0), Point(0, 0 - len), colour, axisStrokeWidth)
106
+ def tick(Vert, colour, len):
107
+ Line(Point(0, 0), Point(0 - len, 0), colour, axisStrokeWidth);
108
+
109
+ def label(Horiz, x, distance, str):
110
+ String(x, (0 - distance) - 4, str, "middle", "hanging")
111
+ def label(Vert, x, distance, str):
112
+ String(0 - distance, x, str, "end", "central");
113
+
114
+ def labelledTick(orient, colour, len, str):
115
+ Group([
116
+ tick(orient, colour, len),
117
+ label(orient, 0, len, str)
118
+ ]);
119
+
120
+ def mkPoint(Horiz, x, y): Point(y, x)
121
+ def mkPoint(Vert, x, y): Point(x, y);
122
+
123
+ def axis(orient, x, start, end):
124
+ def tickSp:
125
+ tickEvery(end - start)
126
+ def firstTick:
127
+ ceilingToNearest(start, tickSp)
128
+ def lastTick:
129
+ ceilingToNearest(end, tickSp)
130
+ def n:
131
+ floor((end - firstTick) / tickSp) + 1
132
+ def ys:
133
+ iterate(n, (+)(tickSp), firstTick)
134
+ def ys:
135
+ match firstTick > start:
136
+ case True: start :| ys
137
+ case False: ys
138
+ def ys:
139
+ match lastTick > end:
140
+ case True:
141
+ concat2(ys, [lastTick])
142
+ case False: ys
143
+ def ps:
144
+ map(mkPoint(orient, x), ys)
145
+ def ax:
146
+ Group([
147
+ Line(head(ps), last(ps), axisColour, axisStrokeWidth),
148
+ Polymarkers(ps, flip(map, ys, compose(labelledTick(orient, axisColour, tickLength), numToStr)))
149
+ ]);
150
+
151
+ (ax, lastTick);
152
+
153
+ def catAxis(orient, x, catValues):
154
+ def ys:
155
+ iterate(length(catValues) + 1, (+)(1), 0)
156
+ def ps:
157
+ map(mkPoint(orient, x), ys);
158
+
159
+ Group([
160
+ Line(head(ps), last(ps), axisColour, axisStrokeWidth),
161
+ Polymarkers(tail(ps), map(const(tick(orient, axisColour, tickLength)), catValues)),
162
+ Polymarkers(flip(map, tail(ps), (lambda Point(x, y): Point(x - 0.5, y))), map(label(orient, -0.5, 0), catValues))
163
+ ]);
164
+
165
+ def viewport(x_start, x_finish, y_finish, margin, gs):
166
+ Viewport(0, 0, x_finish - x_start, y_finish, backgroundColour, margin, Scale(1, 1), Translate(0 - x_start, 0), Group(gs));
167
+
168
+ def lineChart(withAxes, colours, x_start, data):
169
+ def xs: map(fst, data)
170
+ def nCat:
171
+ length(snd(head(data)));
172
+
173
+ def plot((n, colour)):
174
+ def ps:
175
+ map((lambda (x, kvs): Point(x, snd(nth(n, kvs)))), data);
176
+
177
+ Group([
178
+ Polyline(ps, colour, 1),
179
+ Polymarkers(ps, repeat(length(ps), Circle(0, 0, markerRadius, colour)))
180
+ ]);
181
+
182
+ def lines:
183
+ zipWith(curry(plot), iterate(nCat, (+)(1), 0), colours)
184
+ def x_finish: last(xs)
185
+ def y_finish:
186
+ maximum(flip(map, data, (lambda (_, kvs): maximum(map(snd, kvs)))));
187
+
188
+ match withAxes:
189
+ case True:
190
+ def (x_axis, x_finish):
191
+ axis(Horiz, 0, x_start, x_finish)
192
+ def (y_axis, y_finish'):
193
+ axis(Vert, x_start, 0, y_finish);
194
+
195
+ viewport(x_start, x_finish, y_finish', defaultMargin, x_axis :| y_axis :| lines)
196
+ case False:
197
+ viewport(x_start, x_finish, y_finish, 0, lines);
198
+
199
+ def categoricalChart(plotValue, withAxes, colours, sep, data):
200
+ def gs:
201
+ stackRight(sep, plotValue(colours, map(snd, data)))
202
+ def w: length(gs)
203
+ def h:
204
+ maximum(map(height, gs));
205
+
206
+ match withAxes:
207
+ case True:
208
+ def x_axis:
209
+ catAxis(Horiz, 0, map(fst, data))
210
+ def (y_axis, h'): axis(Vert, 0, 0, h);
211
+
212
+ viewport(0, w, h', defaultMargin, concat2(gs, [x_axis, y_axis]))
213
+ case False:
214
+ viewport(0, w, h, 0, gs);
215
+
216
+ def rects(colours, ns):
217
+ zipWith((lambda colour, n: Rect(0, 0, 1, n, colour)), colours, ns);
218
+
219
+ def stackedBar(colours, ns):
220
+ def heights: map(snd, ns)
221
+ def subtotals:
222
+ scanl1((+), 0, heights)
223
+ def dims:
224
+ zip(0 :| subtotals, heights)
225
+ def rects:
226
+ map((lambda ((y, height), colour): Rect(0, y, 1, height, colour)), zip(dims, colours));
227
+
228
+ Viewport(0, 0, 1, last(subtotals), "none", 0, Scale(1, 1), Translate(0, 0), Group(rects));
229
+
230
+ def barChart:
231
+ categoricalChart(rects);
232
+
233
+ def groupedBarChart:
234
+ categoricalChart(compose(map, flip(barChart(False), 0)));
235
+
236
+ def stackedBarChart:
237
+ categoricalChart(compose(map, stackedBar));
238
+
239
+ def caption(str, Viewport(x, y, w, h, fill, margin, scale, translate, g)):
240
+ def g':
241
+ Group([
242
+ String(x + w / 2, -2, str, "middle", "hanging"),
243
+ Viewport(0, 0, w, h, fill, margin, scale, translate, g)
244
+ ]);
245
+
246
+ Viewport(x, y, w, h, backgroundColour, defaultMargin / 2 + 4, Scale(1, 1), Translate(0, 0), g');
@@ -1,41 +1,44 @@
1
- let zero m n image =
2
- let (m_max, n_max) = dims image
3
- in if (m >= 1) `and` (m <= m_max) `and` (n >= 1) `and` (n <= n_max)
4
- then image!(m, n)
5
- else 0;
6
-
7
- let wrap m n image =
8
- let (m_max, n_max) = dims image
9
- in image!( ((m - 1) `mod` m_max) + 1, ((n - 1) `mod` n_max) + 1);
10
-
11
- let extend m n image =
12
- let (m_max, n_max) = dims image;
13
- m' = min (max m 1) m_max;
14
- n' = min (max n 1) n_max
15
- in image!(m', n');
16
-
17
- let matrixSum matr =
18
- let (m, n) = dims matr
19
- in foldl (+) 0 [ matr!(i, j) | (i, j) <- range (1, 1) (m, n)];
20
-
21
- let convolve image kernel lookup =
22
- let ((m, n), (i, j)) = (dims image, dims kernel);
23
- (half_i, half_j) = (i `quot` 2, j `quot` 2);
24
- area = i * j
25
- in [| let interMatrix =
26
- @doc("""Intermediate matrix for element (${m'}, ${n'})""")
27
- [| let x = m' + i' - 1 - half_i;
28
- y = n' + j' - 1 - half_j in
29
- @doc("""Multiply these two elements""")
30
- lookup x y image * kernel!(i', j')
31
- | (i', j') in (i, j) |]
32
- in matrixSum interMatrix `quot` area
33
- | (m', n') in (m, n) |];
34
-
35
- let matMul a b =
36
- let ((m, n), (i, j)) = (dims a, dims b) in
37
- if not (n == i)
38
- then error "Dimensions don't line up"
39
- else @doc("""Intermediate matrix""")
40
- [| sum [ a!(i', k) * b!(k, j') | k <- enumFromTo 1 n]
41
- | (i', j') in (m, j) |];
1
+ def zero(m, n, image):
2
+ def (m_max, n_max): dims(image);
3
+
4
+ if m >= 1 |and| m <= m_max |and| n >= 1 |and| n <= n_max: image ! (m, n)
5
+ else: 0;
6
+
7
+ def wrap(m, n, image):
8
+ def (m_max, n_max): dims(image);
9
+
10
+ image ! (m - 1 |mod| m_max + 1, n - 1 |mod| n_max + 1);
11
+
12
+ def extend(m, n, image):
13
+ def (m_max, n_max): dims(image)
14
+ def m':
15
+ min(max(m, 1), m_max)
16
+ def n':
17
+ min(max(n, 1), n_max);
18
+
19
+ image ! (m', n');
20
+
21
+ def matrixSum(matr):
22
+ def (m, n): dims(matr);
23
+
24
+ foldl((+), 0, [matr ! (i, j) for (i, j) in range((1, 1), (m, n))]);
25
+
26
+ def convolve(image, kernel, lookup):
27
+ def ((m, n), (i, j)):
28
+ (dims(image), dims(kernel))
29
+ def (half_i, half_j):
30
+ (i |quot| 2, j |quot| 2)
31
+ def area: i * j;
32
+ def interMatrix(m', n'):
33
+ @doc("""Intermediate matrix for element ( ${m'} , ${n'} )""")
34
+ [| @doc("""Multiply these two elements""") lookup(((m' + i') - 1) - half_i, ((n' + j') - 1) - half_j, image) * kernel ! (i', j') for (i', j') in (i, j) |];
35
+
36
+ [| matrixSum(interMatrix(m', n')) |quot| area for (m', n') in (m, n) |];
37
+
38
+ def matMul(a, b):
39
+ def ((m, n), (i, j)): (dims(a), dims(b));
40
+
41
+ if not(n == i):
42
+ error("Dimensions don't line up")
43
+ else:
44
+ @doc("""Intermediate matrix""") [| sum([a ! (i', k) * b ! (k, j') for k in enumFromTo(1, n)]) for (i', j') in (m, j) |];