@swf2es/render 0.0.0-stage → 0.1.1

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 (143) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +17 -2
  3. package/dist/commands.d.ts +67 -0
  4. package/dist/commands.d.ts.map +1 -0
  5. package/dist/commands.js +93 -0
  6. package/dist/commands.js.map +1 -0
  7. package/dist/filter-plan.d.ts +110 -0
  8. package/dist/filter-plan.d.ts.map +1 -0
  9. package/dist/filter-plan.js +480 -0
  10. package/dist/filter-plan.js.map +1 -0
  11. package/dist/filter-shaders.d.ts +63 -0
  12. package/dist/filter-shaders.d.ts.map +1 -0
  13. package/dist/filter-shaders.js +350 -0
  14. package/dist/filter-shaders.js.map +1 -0
  15. package/dist/filters.d.ts +107 -0
  16. package/dist/filters.d.ts.map +1 -0
  17. package/dist/filters.js +187 -0
  18. package/dist/filters.js.map +1 -0
  19. package/dist/geometry.d.ts +83 -0
  20. package/dist/geometry.d.ts.map +1 -0
  21. package/dist/geometry.js +93 -0
  22. package/dist/geometry.js.map +1 -0
  23. package/dist/index.d.ts +11 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +13 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/keeps.d.ts +17 -0
  28. package/dist/keeps.d.ts.map +1 -0
  29. package/dist/keeps.js +17 -0
  30. package/dist/keeps.js.map +1 -0
  31. package/dist/kernel-shaders.d.ts +74 -0
  32. package/dist/kernel-shaders.d.ts.map +1 -0
  33. package/dist/kernel-shaders.js +323 -0
  34. package/dist/kernel-shaders.js.map +1 -0
  35. package/dist/measure.d.ts +56 -0
  36. package/dist/measure.d.ts.map +1 -0
  37. package/dist/measure.js +287 -0
  38. package/dist/measure.js.map +1 -0
  39. package/dist/plan.d.ts +14 -0
  40. package/dist/plan.d.ts.map +1 -0
  41. package/dist/plan.js +247 -0
  42. package/dist/plan.js.map +1 -0
  43. package/dist/registry.d.ts +49 -0
  44. package/dist/registry.d.ts.map +1 -0
  45. package/dist/registry.js +87 -0
  46. package/dist/registry.js.map +1 -0
  47. package/dist/rows.d.ts +39 -0
  48. package/dist/rows.d.ts.map +1 -0
  49. package/dist/rows.js +60 -0
  50. package/dist/rows.js.map +1 -0
  51. package/dist/scene.d.ts +179 -0
  52. package/dist/scene.d.ts.map +1 -0
  53. package/dist/scene.js +265 -0
  54. package/dist/scene.js.map +1 -0
  55. package/dist/shelves.d.ts +23 -0
  56. package/dist/shelves.d.ts.map +1 -0
  57. package/dist/shelves.js +63 -0
  58. package/dist/shelves.js.map +1 -0
  59. package/dist/textures.d.ts +72 -0
  60. package/dist/textures.d.ts.map +1 -0
  61. package/dist/textures.js +160 -0
  62. package/dist/textures.js.map +1 -0
  63. package/dist/webgl/atlas.d.ts +63 -0
  64. package/dist/webgl/atlas.d.ts.map +1 -0
  65. package/dist/webgl/atlas.js +328 -0
  66. package/dist/webgl/atlas.js.map +1 -0
  67. package/dist/webgl/backend.d.ts +141 -0
  68. package/dist/webgl/backend.d.ts.map +1 -0
  69. package/dist/webgl/backend.js +659 -0
  70. package/dist/webgl/backend.js.map +1 -0
  71. package/dist/webgl/composite.d.ts +101 -0
  72. package/dist/webgl/composite.d.ts.map +1 -0
  73. package/dist/webgl/composite.js +293 -0
  74. package/dist/webgl/composite.js.map +1 -0
  75. package/dist/webgl/draws.d.ts +213 -0
  76. package/dist/webgl/draws.d.ts.map +1 -0
  77. package/dist/webgl/draws.js +714 -0
  78. package/dist/webgl/draws.js.map +1 -0
  79. package/dist/webgl/filters.d.ts +107 -0
  80. package/dist/webgl/filters.d.ts.map +1 -0
  81. package/dist/webgl/filters.js +351 -0
  82. package/dist/webgl/filters.js.map +1 -0
  83. package/dist/webgl/gpu-textures.d.ts +44 -0
  84. package/dist/webgl/gpu-textures.d.ts.map +1 -0
  85. package/dist/webgl/gpu-textures.js +178 -0
  86. package/dist/webgl/gpu-textures.js.map +1 -0
  87. package/dist/webgl/holds.d.ts +97 -0
  88. package/dist/webgl/holds.d.ts.map +1 -0
  89. package/dist/webgl/holds.js +453 -0
  90. package/dist/webgl/holds.js.map +1 -0
  91. package/dist/webgl/index.d.ts +2 -0
  92. package/dist/webgl/index.d.ts.map +1 -0
  93. package/dist/webgl/index.js +4 -0
  94. package/dist/webgl/index.js.map +1 -0
  95. package/dist/webgl/kernels.d.ts +64 -0
  96. package/dist/webgl/kernels.d.ts.map +1 -0
  97. package/dist/webgl/kernels.js +160 -0
  98. package/dist/webgl/kernels.js.map +1 -0
  99. package/dist/webgl/pool.d.ts +71 -0
  100. package/dist/webgl/pool.d.ts.map +1 -0
  101. package/dist/webgl/pool.js +192 -0
  102. package/dist/webgl/pool.js.map +1 -0
  103. package/dist/webgl/programs.d.ts +130 -0
  104. package/dist/webgl/programs.d.ts.map +1 -0
  105. package/dist/webgl/programs.js +582 -0
  106. package/dist/webgl/programs.js.map +1 -0
  107. package/dist/webgl/stage.d.ts +67 -0
  108. package/dist/webgl/stage.d.ts.map +1 -0
  109. package/dist/webgl/stage.js +173 -0
  110. package/dist/webgl/stage.js.map +1 -0
  111. package/dist/webgl/targets.d.ts +172 -0
  112. package/dist/webgl/targets.d.ts.map +1 -0
  113. package/dist/webgl/targets.js +323 -0
  114. package/dist/webgl/targets.js.map +1 -0
  115. package/package.json +35 -3
  116. package/src/commands.ts +98 -0
  117. package/src/filter-plan.ts +755 -0
  118. package/src/filter-shaders.ts +360 -0
  119. package/src/filters.ts +220 -0
  120. package/src/geometry.ts +143 -0
  121. package/src/index.ts +87 -0
  122. package/src/keeps.ts +21 -0
  123. package/src/kernel-shaders.ts +375 -0
  124. package/src/measure.ts +400 -0
  125. package/src/plan.ts +331 -0
  126. package/src/registry.ts +105 -0
  127. package/src/rows.ts +93 -0
  128. package/src/scene.ts +297 -0
  129. package/src/shelves.ts +70 -0
  130. package/src/textures.ts +198 -0
  131. package/src/webgl/atlas.ts +370 -0
  132. package/src/webgl/backend.ts +973 -0
  133. package/src/webgl/composite.ts +372 -0
  134. package/src/webgl/draws.ts +902 -0
  135. package/src/webgl/filters.ts +502 -0
  136. package/src/webgl/gpu-textures.ts +253 -0
  137. package/src/webgl/holds.ts +624 -0
  138. package/src/webgl/index.ts +3 -0
  139. package/src/webgl/kernels.ts +205 -0
  140. package/src/webgl/pool.ts +245 -0
  141. package/src/webgl/programs.ts +620 -0
  142. package/src/webgl/stage.ts +214 -0
  143. package/src/webgl/targets.ts +464 -0
package/src/measure.ts ADDED
@@ -0,0 +1,400 @@
1
+ import type { Geometries } from "./geometry.js";
2
+ import { ROW_FLOATS, type Rows } from "./rows.js";
3
+ import {
4
+ ALPHA_MASK,
5
+ DRAW,
6
+ KEPT,
7
+ LAYER_BEGIN,
8
+ LAYER_END,
9
+ MASK_BEGIN,
10
+ MASK_POP,
11
+ MASK_USE,
12
+ NO_WHOLE,
13
+ type SceneList,
14
+ WHOLE_ALIASED,
15
+ } from "./scene.js";
16
+
17
+ /**
18
+ * What measuring a frame's layers needs of its target: its size in pixels,
19
+ * its pixels a stage pixel, and its pixels a unit of its own (resolution),
20
+ * which a filtered layer is padded in.
21
+ */
22
+ export interface MeasureTarget {
23
+ width: number;
24
+ height: number;
25
+ scale: number;
26
+ resolution: number;
27
+ }
28
+
29
+ /** A hair, which keeps a padded edge on the pixel it lies on at any resolution. */
30
+ const TEXEL_SNAP = 1e-6;
31
+
32
+ // Scratch stacks, four numbers an open area: least x and y, greatest x and y, in target pixels.
33
+ const areas: number[] = [];
34
+ const clips: number[] = [];
35
+ const open: number[] = [];
36
+ // Each open layer's cut, four numbers as an area's: an alpha mask's rectangle in it (ALPHA_MASK).
37
+ const cuts: number[] = [];
38
+
39
+ /**
40
+ * Each layer's rectangle of the target (SceneList.layerRect), from what is
41
+ * drawn in it: its draws' geometries placed by their rows, a mask's own
42
+ * draws apart, what a mask clips cut to that mask's draws as it pops, and
43
+ * the rectangles of the layers in it; out to whole pixels and a pixel more
44
+ * each way, cut to the target. An alpha mask's layer is no part of the
45
+ * unfiltered layer it ends, which it cuts to its own rectangle instead, as
46
+ * nothing of that layer shows past it. What is drawn is what is measured,
47
+ * so a layer covers its draws however they came to be where they are. A
48
+ * filtered layer is measured as PixiJS's filter system measures it, which
49
+ * the filters' exactness rules were found with: cut to the target, out to
50
+ * whole pixels, then padded, not cut again; where nothing is drawn in it,
51
+ * it draws nothing.
52
+ */
53
+ export function measureLayers(
54
+ scene: SceneList,
55
+ rows: Rows,
56
+ geometries: Geometries,
57
+ target: MeasureTarget,
58
+ ): void {
59
+ if (scene.layerCount === 0) {
60
+ return;
61
+ }
62
+
63
+ areas.length = 0;
64
+ clips.length = 0;
65
+ open.length = 0;
66
+ cuts.length = 0;
67
+ pushEmpty(areas);
68
+ const k = target.scale;
69
+ const box = geometries.box;
70
+ const m = rows.data;
71
+
72
+ for (let i = 0; i < scene.length; i++) {
73
+ switch (scene.kind[i]) {
74
+ case DRAW:
75
+ placeDraw(scene, i, box, m, k, areas);
76
+ break;
77
+
78
+ case MASK_BEGIN:
79
+ // The mask's own draws, measured apart.
80
+ pushEmpty(areas);
81
+ break;
82
+ case LAYER_BEGIN:
83
+ open.push(scene.geometry[i]);
84
+ pushEmpty(areas);
85
+ cuts.push(
86
+ Number.NEGATIVE_INFINITY,
87
+ Number.NEGATIVE_INFINITY,
88
+ Number.POSITIVE_INFINITY,
89
+ Number.POSITIVE_INFINITY,
90
+ );
91
+ break;
92
+ case MASK_USE:
93
+ // The mask's draws clip what follows, measured apart till it pops.
94
+ moveTop(areas, clips);
95
+ pushEmpty(areas);
96
+ break;
97
+ case MASK_POP: {
98
+ const n = areas.length;
99
+ const c = clips.length;
100
+ const left = Math.max(areas[n - 4], clips[c - 4]);
101
+ const top = Math.max(areas[n - 3], clips[c - 3]);
102
+ const right = Math.min(areas[n - 2], clips[c - 2]);
103
+ const bottom = Math.min(areas[n - 1], clips[c - 1]);
104
+ areas.length = n - 4;
105
+ clips.length = c - 4;
106
+ if (right > left && bottom > top) {
107
+ unionTop(left, top, right, bottom);
108
+ }
109
+
110
+ break;
111
+ }
112
+
113
+ case LAYER_END: {
114
+ const n = areas.length;
115
+ const layer = open.pop() as number;
116
+ const pad = scene.pads[layer];
117
+ const c = cuts.length - 4;
118
+ if (pad < 0) {
119
+ const left = Math.max(0, Math.floor(areas[n - 4]) - 1, cuts[c]);
120
+ const top = Math.max(0, Math.floor(areas[n - 3]) - 1, cuts[c + 1]);
121
+ const right = Math.min(target.width, Math.ceil(areas[n - 2]) + 1, cuts[c + 2]);
122
+ const bottom = Math.min(target.height, Math.ceil(areas[n - 1]) + 1, cuts[c + 3]);
123
+ areas.length = n - 4;
124
+ cuts.length = c;
125
+ if (scene.geometry[i] === ALPHA_MASK) {
126
+ // Not taken into the layer it ends, which it cuts to its rectangle: to nothing, where it
127
+ // has none.
128
+ if (right > left && bottom > top) {
129
+ scene.layerRect(layer, left, top, right - left, bottom - top);
130
+ }
131
+
132
+ const p = c - 4;
133
+ if (p >= 0) {
134
+ cuts[p] = Math.max(cuts[p], left);
135
+ cuts[p + 1] = Math.max(cuts[p + 1], top);
136
+ cuts[p + 2] = Math.min(cuts[p + 2], right);
137
+ cuts[p + 3] = Math.min(cuts[p + 3], bottom);
138
+ }
139
+
140
+ break;
141
+ }
142
+
143
+ if (right > left && bottom > top) {
144
+ scene.layerRect(layer, left, top, right - left, bottom - top);
145
+ unionTop(left, top, right, bottom);
146
+ }
147
+
148
+ break;
149
+ }
150
+
151
+ // The view puts no alpha mask straight in a filtered layer, which would run its filters on
152
+ // what the mask left (cuts).
153
+ cuts.length = c;
154
+ const kept = scene.areas;
155
+ for (let k = 0; k < 4; k++) {
156
+ kept[layer * 4 + k] = areas[n - 4 + k];
157
+ }
158
+
159
+ filteredRect(
160
+ scene.areas,
161
+ layer * 4,
162
+ pad,
163
+ target,
164
+ scene.layers,
165
+ layer * 4,
166
+ scene.wholes[layer],
167
+ scene.objects,
168
+ );
169
+ areas.length = n - 4;
170
+ const r = scene.layers;
171
+ const at = layer * 4;
172
+ if (r[at + 2] > 0) {
173
+ unionTop(r[at], r[at + 1], r[at] + r[at + 2], r[at + 1] + r[at + 3]);
174
+ }
175
+
176
+ break;
177
+ }
178
+
179
+ case KEPT: {
180
+ // A kept output covers its rectangle, which its owner placed.
181
+ const r = scene.layers;
182
+ const at = scene.geometry[i] * 4;
183
+ if (r[at + 2] > 0 && r[at + 3] > 0) {
184
+ unionTop(r[at], r[at + 1], r[at] + r[at + 2], r[at + 1] + r[at + 3]);
185
+ }
186
+
187
+ break;
188
+ }
189
+ }
190
+ }
191
+ }
192
+
193
+ /**
194
+ * The rectangle a filtered layer padded by `pad` units covers, from its
195
+ * area (least x and y, greatest x and y in target pixels, from `area[at]`)
196
+ * as PixiJS's filter system has it (FilterSystem's _calculateFilterArea): in
197
+ * units, the area cut to the target, out to whole pixels at the
198
+ * resolution, or to whole stage pixels as `whole` says (SceneList.wholes),
199
+ * padded; nothing where it has no size then; its edges snapped to the
200
+ * pixels they lie on. Into `out` from `to`: x, y, width, height, of no
201
+ * size for nothing. A whole layer's object, uncut, goes into `objects` at
202
+ * `to` too (SceneList.objects).
203
+ */
204
+ export function filteredRect(
205
+ area: ArrayLike<number>,
206
+ at: number,
207
+ pad: number,
208
+ target: MeasureTarget,
209
+ out: Int32Array,
210
+ to: number,
211
+ whole = NO_WHOLE,
212
+ objects: Float64Array | null = null,
213
+ ): void {
214
+ const res = target.resolution;
215
+ let x0 = area[at] / res;
216
+ let y0 = area[at + 1] / res;
217
+ let x1 = area[at + 2] / res;
218
+ let y1 = area[at + 3] / res;
219
+ out[to] = 0;
220
+ out[to + 1] = 0;
221
+ out[to + 2] = 0;
222
+ out[to + 3] = 0;
223
+ if (!(x1 > x0 && y1 > y0)) {
224
+ return;
225
+ }
226
+
227
+ if (whole !== NO_WHOLE) {
228
+ // Flash's bitmap of the object, of whole stage pixels, whatever the stage's scale; cut to the
229
+ // target only after.
230
+ const k = target.scale;
231
+ const object = wholeBitmap(
232
+ area[at] / k,
233
+ area[at + 1] / k,
234
+ area[at + 2] / k,
235
+ area[at + 3] / k,
236
+ whole,
237
+ );
238
+ const ox0 = object[0] * k;
239
+ const oy0 = object[1] * k;
240
+ const ox1 = object[2] * k;
241
+ const oy1 = object[3] * k;
242
+ if (objects) {
243
+ objects[to] = ox0;
244
+ objects[to + 1] = oy0;
245
+ objects[to + 2] = ox1 - ox0;
246
+ objects[to + 3] = oy1 - oy0;
247
+ }
248
+
249
+ x0 = Math.max(ox0, 0) / res - pad;
250
+ y0 = Math.max(oy0, 0) / res - pad;
251
+ x1 = Math.min(ox1, target.width) / res + pad;
252
+ y1 = Math.min(oy1, target.height) / res + pad;
253
+ } else {
254
+ // Each edge moved in alone, as Bounds.fitBounds does: an area off the target turns inside out,
255
+ // and its padding may yet give it a size.
256
+ x0 = Math.floor(Math.max(x0, 0) * res) / res - pad;
257
+ y0 = Math.floor(Math.max(y0, 0) * res) / res - pad;
258
+ x1 = Math.ceil(Math.min(x1, target.width / res) * res) / res + pad;
259
+ y1 = Math.ceil(Math.min(y1, target.height / res) * res) / res + pad;
260
+ }
261
+
262
+ if (!(x1 - x0 > 0 && y1 - y0 > 0)) {
263
+ return;
264
+ }
265
+
266
+ const left = Math.floor(x0 * res + TEXEL_SNAP);
267
+ const top = Math.floor(y0 * res + TEXEL_SNAP);
268
+ out[to] = left;
269
+ out[to + 1] = top;
270
+ out[to + 2] = Math.ceil(x1 * res - TEXEL_SNAP) - left;
271
+ out[to + 3] = Math.ceil(y1 * res - TEXEL_SNAP) - top;
272
+ }
273
+
274
+ /** What wholeBitmap gives, read before it is asked again. */
275
+ const bitmap = [0, 0, 0, 0];
276
+
277
+ /**
278
+ * Flash's bitmap of an object of bounds x0, y0 to x1, y1 in stage pixels:
279
+ * the bounds out to twips, then antialiased (WHOLE_SMOOTH) the pixels its
280
+ * edges touch and one more right and down where an edge lies on one,
281
+ * aliased (WHOLE_ALIASED) those whose centres it covers.
282
+ */
283
+ export function wholeBitmap(
284
+ x0: number,
285
+ y0: number,
286
+ x1: number,
287
+ y1: number,
288
+ whole: number,
289
+ ): number[] {
290
+ x0 = Math.floor(x0 * 20 + TEXEL_SNAP) / 20;
291
+ y0 = Math.floor(y0 * 20 + TEXEL_SNAP) / 20;
292
+ x1 = Math.ceil(x1 * 20 - TEXEL_SNAP) / 20;
293
+ y1 = Math.ceil(y1 * 20 - TEXEL_SNAP) / 20;
294
+ if (whole === WHOLE_ALIASED) {
295
+ bitmap[0] = Math.ceil(x0 - 0.5);
296
+ bitmap[1] = Math.ceil(y0 - 0.5);
297
+ bitmap[2] = Math.ceil(x1 - 0.5);
298
+ bitmap[3] = Math.ceil(y1 - 0.5);
299
+ } else {
300
+ bitmap[0] = Math.floor(x0);
301
+ bitmap[1] = Math.floor(y0);
302
+ bitmap[2] = Math.floor(x1) + 1;
303
+ bitmap[3] = Math.floor(y1) + 1;
304
+ }
305
+
306
+ return bitmap;
307
+ }
308
+
309
+ /**
310
+ * The area of the target the draws from entry `from` on cover, at `scale`
311
+ * target pixels a stage pixel, masks' own draws apart and nothing cut by
312
+ * the masks: into `out`, its least x and y, then its greatest, in target
313
+ * pixels; nothing drawn leaves the least past the greatest.
314
+ */
315
+ export function drawnArea(
316
+ scene: SceneList,
317
+ rows: Rows,
318
+ geometries: Geometries,
319
+ from: number,
320
+ scale: number,
321
+ out: number[],
322
+ ): void {
323
+ out.length = 0;
324
+ pushEmpty(out);
325
+ let defining = 0;
326
+ for (let i = from; i < scene.length; i++) {
327
+ switch (scene.kind[i]) {
328
+ case DRAW:
329
+ if (defining === 0) {
330
+ placeDraw(scene, i, geometries.box, rows.data, scale, out);
331
+ }
332
+
333
+ break;
334
+ case MASK_BEGIN:
335
+ defining++;
336
+ break;
337
+ case MASK_USE:
338
+ defining--;
339
+ break;
340
+ }
341
+ }
342
+ }
343
+
344
+ /** Entry `i`'s geometry box placed by its row at `k` target pixels a stage pixel, taken into the area atop `stack`. */
345
+ function placeDraw(
346
+ scene: SceneList,
347
+ i: number,
348
+ box: Float32Array,
349
+ m: Float32Array,
350
+ k: number,
351
+ stack: number[],
352
+ ): void {
353
+ const g = scene.geometry[i] * 4;
354
+ const x0 = box[g];
355
+ const y0 = box[g + 1];
356
+ const x1 = box[g + 2];
357
+ const y1 = box[g + 3];
358
+ if (!(x1 >= x0 && y1 >= y0)) {
359
+ return;
360
+ }
361
+
362
+ const r = scene.row[i] * ROW_FLOATS;
363
+ const a = m[r] * k;
364
+ const b = m[r + 1] * k;
365
+ const c = m[r + 2] * k;
366
+ const d = m[r + 3] * k;
367
+ const tx = m[r + 4] * k;
368
+ const ty = m[r + 5] * k;
369
+ // The box's corners placed: each of the least and greatest a coordinate takes, by sign.
370
+ const n = stack.length;
371
+ stack[n - 4] = Math.min(stack[n - 4], tx + Math.min(a * x0, a * x1) + Math.min(c * y0, c * y1));
372
+ stack[n - 3] = Math.min(stack[n - 3], ty + Math.min(b * x0, b * x1) + Math.min(d * y0, d * y1));
373
+ stack[n - 2] = Math.max(stack[n - 2], tx + Math.max(a * x0, a * x1) + Math.max(c * y0, c * y1));
374
+ stack[n - 1] = Math.max(stack[n - 1], ty + Math.max(b * x0, b * x1) + Math.max(d * y0, d * y1));
375
+ }
376
+
377
+ function pushEmpty(stack: number[]): void {
378
+ stack.push(
379
+ Number.POSITIVE_INFINITY,
380
+ Number.POSITIVE_INFINITY,
381
+ Number.NEGATIVE_INFINITY,
382
+ Number.NEGATIVE_INFINITY,
383
+ );
384
+ }
385
+
386
+ /** The top area of `from` moved onto `to`. */
387
+ function moveTop(from: number[], to: number[]): void {
388
+ const n = from.length;
389
+ to.push(from[n - 4], from[n - 3], from[n - 2], from[n - 1]);
390
+ from.length = n - 4;
391
+ }
392
+
393
+ /** The open area on top grown to take in the rectangle. */
394
+ function unionTop(left: number, top: number, right: number, bottom: number): void {
395
+ const n = areas.length;
396
+ areas[n - 4] = Math.min(areas[n - 4], left);
397
+ areas[n - 3] = Math.min(areas[n - 3], top);
398
+ areas[n - 2] = Math.max(areas[n - 2], right);
399
+ areas[n - 1] = Math.max(areas[n - 1], bottom);
400
+ }