@codincod/ornament 0.0.0-stage → 0.1.0
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/LICENSE +661 -0
- package/README.md +52 -2
- package/package.json +35 -3
- package/src/biome.ts +157 -0
- package/src/caustics.ts +81 -0
- package/src/census.ts +262 -0
- package/src/cephalopods.ts +1041 -0
- package/src/cloud.ts +308 -0
- package/src/crags.ts +974 -0
- package/src/drift.ts +435 -0
- package/src/eels.ts +667 -0
- package/src/fish_shape.ts +410 -0
- package/src/flora.ts +1955 -0
- package/src/flow_field.ts +491 -0
- package/src/jellies.ts +1794 -0
- package/src/nemos.ts +606 -0
- package/src/paint/bed.ts +231 -0
- package/src/paint/frame.ts +204 -0
- package/src/paint/life.ts +611 -0
- package/src/paint/light.ts +132 -0
- package/src/paint/pen.ts +225 -0
- package/src/paint/rock.ts +325 -0
- package/src/paint/sea.ts +809 -0
- package/src/paint/shade.ts +109 -0
- package/src/paint/sky.ts +377 -0
- package/src/paint/specimen.ts +300 -0
- package/src/paint/trace.ts +164 -0
- package/src/passers.ts +730 -0
- package/src/perlin.ts +168 -0
- package/src/plenty.ts +65 -0
- package/src/puffers.ts +510 -0
- package/src/rays.ts +225 -0
- package/src/reef.ts +945 -0
- package/src/relics.ts +451 -0
- package/src/seabed.ts +525 -0
- package/src/shoal.ts +1658 -0
- package/src/sizes.ts +86 -0
- package/src/sun.ts +500 -0
- package/src/swarm.ts +587 -0
- package/src/visitors.ts +1936 -0
- package/src/walkers.ts +861 -0
package/src/paint/bed.ts
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The ground, and everything rooted in it.
|
|
3
|
+
*
|
|
4
|
+
* The sand, the hills standing behind it, the reef's mound, the wall at the
|
|
5
|
+
* back, and every plant growing on any of them. It is the one part of the
|
|
6
|
+
* picture that does not move under its own steam: a plant leans on the current
|
|
7
|
+
* and nothing here goes anywhere.
|
|
8
|
+
*
|
|
9
|
+
* ## Why a renderer on a card does not use this
|
|
10
|
+
*
|
|
11
|
+
* Because it can do better. A plant is a short tree of limbs, so a renderer
|
|
12
|
+
* with a vertex shader takes the shape once at rest and bends it on the card,
|
|
13
|
+
* and a frame after that costs two numbers a plant. `flora.ts` hands out
|
|
14
|
+
* `madeOf` for exactly that, and the desktop's renderer reads it.
|
|
15
|
+
*
|
|
16
|
+
* This is the other bargain, and it is the right one for a processor: take the
|
|
17
|
+
* points the plant has already cut for itself and stroke them. `flora.ts` only
|
|
18
|
+
* cuts a plant when the water has moved it far enough to be worth redrawing, so
|
|
19
|
+
* the tolerance it was given is what decides how much of a frame this costs.
|
|
20
|
+
*
|
|
21
|
+
* Both draw the same bed. What differs is where the bending happens.
|
|
22
|
+
*/
|
|
23
|
+
import type { Caustics } from "../caustics.ts"
|
|
24
|
+
import type { Flora } from "../flora.ts"
|
|
25
|
+
import type { Reef } from "../reef.ts"
|
|
26
|
+
import type { Seabed } from "../seabed.ts"
|
|
27
|
+
import { type Pen, type Point, TONE } from "./pen.ts"
|
|
28
|
+
import { far, floored } from "./rock.ts"
|
|
29
|
+
import type { Box } from "./sky.ts"
|
|
30
|
+
import { trace } from "./trace.ts"
|
|
31
|
+
|
|
32
|
+
/** How dark each of these may be at the front of the picture. */
|
|
33
|
+
const FLORA_INK = 0.33
|
|
34
|
+
const SAND_INK = 0.19
|
|
35
|
+
const STONE_INK = 0.25
|
|
36
|
+
|
|
37
|
+
/** How thick a blade is against the strand it grows off. */
|
|
38
|
+
const BLADE_GIRTH = 0.8
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* How stout a strand has to be before its blades are drawn thinner than it.
|
|
42
|
+
*
|
|
43
|
+
* Under this the difference between the two widths is less than the width of a
|
|
44
|
+
* line, so the whole plant is drawn at one weight and nothing is lost. Over it
|
|
45
|
+
* the strand reads as the thing the blades hang off.
|
|
46
|
+
*/
|
|
47
|
+
const BLADE_SPLIT = 0.5
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Where each band of ground sorts.
|
|
51
|
+
*
|
|
52
|
+
* Ground is on a scale of its own, behind everything that swims, and the bands
|
|
53
|
+
* are kept a fifth of a lane apart so a hill never crosses the sand in front of
|
|
54
|
+
* it. The sand is flat rather than a function of distance because there is only
|
|
55
|
+
* one of it and it is the nearest thing in the picture that is not rock.
|
|
56
|
+
*/
|
|
57
|
+
const LANE = { cliff: -3.4, hill: -3, mound: -2.6, sand: -2 }
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* The water itself, which everything else is drawn into.
|
|
61
|
+
*
|
|
62
|
+
* Behind every band of ground and behind the murk, so it is the one thing in
|
|
63
|
+
* the picture with nothing at all in front of it. Its tone is the wall, which
|
|
64
|
+
* is whatever the surface underneath the water happens to be showing: a
|
|
65
|
+
* desktop's own wallpaper, or a page's own background.
|
|
66
|
+
*/
|
|
67
|
+
export function paintWater(pen: Pen, box: Box): void {
|
|
68
|
+
pen.fill(
|
|
69
|
+
[
|
|
70
|
+
[
|
|
71
|
+
{ x: 0, y: 0 },
|
|
72
|
+
{ x: box.width, y: 0 },
|
|
73
|
+
{ x: box.width, y: box.height },
|
|
74
|
+
{ x: 0, y: box.height },
|
|
75
|
+
],
|
|
76
|
+
],
|
|
77
|
+
{ lane: -9, tone: TONE.wall, weight: 0 },
|
|
78
|
+
)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* The ground at every distance it has, each band shut under the box.
|
|
83
|
+
*
|
|
84
|
+
* Far first, near last, which is the order they are drawn in as well as the
|
|
85
|
+
* order they stand in: a band closed off downwards covers everything behind it
|
|
86
|
+
* from its own ridge to the bottom edge, so drawing the near sand first would
|
|
87
|
+
* leave every hill behind it painted over its face.
|
|
88
|
+
*/
|
|
89
|
+
export function paintGround(pen: Pen, seabed: Seabed, reef: Reef | null, box: Box): void {
|
|
90
|
+
for (const cliff of seabed.cliffs) {
|
|
91
|
+
pen.fill([floored(cliff.ridge, box)], {
|
|
92
|
+
lane: LANE.cliff + cliff.depth,
|
|
93
|
+
shade: box.height * 0.5,
|
|
94
|
+
weight: far(SAND_INK, cliff.depth),
|
|
95
|
+
})
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
for (const band of seabed.ranges) {
|
|
99
|
+
pen.fill([floored(band.ridge, box)], {
|
|
100
|
+
lane: LANE.hill + band.depth,
|
|
101
|
+
weight: far(SAND_INK, band.depth),
|
|
102
|
+
})
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (reef) {
|
|
106
|
+
pen.fill([floored(reef.crest, box)], {
|
|
107
|
+
lane: LANE.mound + reef.depth,
|
|
108
|
+
weight: far(STONE_INK, reef.depth),
|
|
109
|
+
})
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
pen.fill([floored(seabed.ridge, box)], { lane: LANE.sand, weight: SAND_INK })
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Every plant, at the distance it grew at.
|
|
117
|
+
*
|
|
118
|
+
* A plant is a strand and whatever hangs off it, both handed over as points the
|
|
119
|
+
* plant cut for itself, and a coral is a stencil that never moves. Which of the
|
|
120
|
+
* two a plant is, is its own business and not read here beyond that: anything
|
|
121
|
+
* carrying twigs is drawn as twigs and anything else is drawn as lines.
|
|
122
|
+
*/
|
|
123
|
+
/**
|
|
124
|
+
* How bright the light on the ground gets, and the least of it at night.
|
|
125
|
+
*
|
|
126
|
+
* Faint. This palette is one colour and its shadow, so a bright web on the sand
|
|
127
|
+
* stops being water and starts being decoration at a threshold well under where
|
|
128
|
+
* instinct puts it. What it is worth is that the ground is no longer one flat
|
|
129
|
+
* tone: a ridge with light moving over it has a shape, and a ridge without has
|
|
130
|
+
* an outline.
|
|
131
|
+
*/
|
|
132
|
+
const CAUSTIC_LIGHT = 0.32
|
|
133
|
+
|
|
134
|
+
const MOON_CAUSTIC = 0.22
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* How wide one patch of it is against the box, how far apart they are put along
|
|
138
|
+
* a ridge, and how flat.
|
|
139
|
+
*
|
|
140
|
+
* Laid as a run of soft round lights rather than as a line, because a line has
|
|
141
|
+
* one strength for the whole of its length and the entire point of a caustic is
|
|
142
|
+
* that it does not. Overlapping blobs at their own strengths are what a bright
|
|
143
|
+
* band with a dark end is made of here.
|
|
144
|
+
*/
|
|
145
|
+
const CAUSTIC_REACH = 0.026
|
|
146
|
+
|
|
147
|
+
const CAUSTIC_STEP = 0.011
|
|
148
|
+
|
|
149
|
+
const CAUSTIC_FLAT = 0.42
|
|
150
|
+
|
|
151
|
+
/** Under this there is nothing to see and a drawing would be a drawing wasted. */
|
|
152
|
+
const CAUSTIC_SEEN = 0.06
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* The light on the ground, ridge by ridge.
|
|
156
|
+
*
|
|
157
|
+
* Walked along the outlines the bed already has rather than laid over the box,
|
|
158
|
+
* since the only ground a reader can see is the top of each band and that is
|
|
159
|
+
* exactly what a ridge is. Each one is asked at its own distance, so the bands
|
|
160
|
+
* are lit differently and the bed keeps the depth it is drawn with.
|
|
161
|
+
*/
|
|
162
|
+
export function paintCaustics(
|
|
163
|
+
pen: Pen,
|
|
164
|
+
seabed: Seabed,
|
|
165
|
+
caustics: Caustics,
|
|
166
|
+
box: Box,
|
|
167
|
+
daylight: number,
|
|
168
|
+
): void {
|
|
169
|
+
const overcast = MOON_CAUSTIC + (1 - MOON_CAUSTIC) * daylight
|
|
170
|
+
const reach = box.width * CAUSTIC_REACH
|
|
171
|
+
const apart = Math.max(1, box.width * CAUSTIC_STEP)
|
|
172
|
+
|
|
173
|
+
const along = (ridge: readonly Point[], depth: number, lane: number): void => {
|
|
174
|
+
if (ridge.length < 2) return
|
|
175
|
+
|
|
176
|
+
let last = Number.NEGATIVE_INFINITY
|
|
177
|
+
for (const point of ridge) {
|
|
178
|
+
if (point.x - last < apart) continue
|
|
179
|
+
last = point.x
|
|
180
|
+
|
|
181
|
+
const on = caustics.at(point.x / box.width, depth)
|
|
182
|
+
const lit = CAUSTIC_LIGHT * on * overcast * far(1, depth)
|
|
183
|
+
if (lit <= CAUSTIC_SEEN * CAUSTIC_LIGHT) continue
|
|
184
|
+
|
|
185
|
+
pen.light(point.x, point.y, {
|
|
186
|
+
alpha: lit,
|
|
187
|
+
fall: 1.7,
|
|
188
|
+
lane,
|
|
189
|
+
thin: CAUSTIC_FLAT,
|
|
190
|
+
tone: TONE.sun,
|
|
191
|
+
width: reach,
|
|
192
|
+
})
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
for (const cliff of seabed.cliffs) along(cliff.ridge, cliff.depth, LANE.cliff + cliff.depth)
|
|
197
|
+
for (const band of seabed.ranges) along(band.ridge, band.depth, LANE.hill + band.depth)
|
|
198
|
+
along(seabed.ridge, 1, LANE.sand)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export function paintFlora(pen: Pen, flora: Flora): void {
|
|
202
|
+
for (const plant of flora.plants) {
|
|
203
|
+
const weight = far(FLORA_INK, plant.depth)
|
|
204
|
+
const mark = { lane: plant.depth, shade: plant.y, weight }
|
|
205
|
+
|
|
206
|
+
if (plant.twigs.length > 0) {
|
|
207
|
+
const spot = { scale: plant.scale, x: plant.x, y: plant.y }
|
|
208
|
+
for (const twig of plant.twigs) {
|
|
209
|
+
pen.line(trace(twig.d), { ...mark, width: twig.width * plant.scale }, spot)
|
|
210
|
+
}
|
|
211
|
+
continue
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (plant.points.length < 2 && plant.blades.length === 0) continue
|
|
215
|
+
|
|
216
|
+
// The strand and its limbs at the plant's own weight, and the blades at a
|
|
217
|
+
// share of it where the plant is stout enough for the two to read apart.
|
|
218
|
+
const parted = plant.girth * (1 - BLADE_GIRTH) >= BLADE_SPLIT
|
|
219
|
+
const strand: Point[][] = plant.points.length > 1 ? [plant.points] : []
|
|
220
|
+
|
|
221
|
+
if (parted) {
|
|
222
|
+
if (strand.length) pen.line(strand, { ...mark, width: plant.girth })
|
|
223
|
+
if (plant.blades.length) {
|
|
224
|
+
pen.line(plant.blades, { ...mark, width: plant.girth * BLADE_GIRTH })
|
|
225
|
+
}
|
|
226
|
+
continue
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
pen.line([...strand, ...plant.blades], { ...mark, width: plant.girth })
|
|
230
|
+
}
|
|
231
|
+
}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A frame of the water, read back.
|
|
3
|
+
*
|
|
4
|
+
* `pen.ts` writes drawings into a block of numbers and this walks them out
|
|
5
|
+
* again. It exists because there is more than one renderer: a canvas walks the
|
|
6
|
+
* frame with a path, a graphics card cuts it into triangles, and both of them
|
|
7
|
+
* are reading the same block, so the one place that knows what the numbers mean
|
|
8
|
+
* should be a file rather than a habit.
|
|
9
|
+
*
|
|
10
|
+
* ## Nothing is allocated
|
|
11
|
+
*
|
|
12
|
+
* A busy sea is a few thousand drawings a frame at sixty frames a second, and
|
|
13
|
+
* an object per drawing is a few hundred thousand objects a second for the
|
|
14
|
+
* collector to take away again. So a reader is one object whose fields are
|
|
15
|
+
* rewritten as it walks, and points stay where they are: what a renderer is
|
|
16
|
+
* handed is where in the block a part starts and how many points it has.
|
|
17
|
+
*
|
|
18
|
+
* Read a field while the reader is standing on the drawing it belongs to. Keep
|
|
19
|
+
* it and it is the next drawing's answer.
|
|
20
|
+
*/
|
|
21
|
+
import { FORM, OWN, SPENT, TONE } from "./pen.ts"
|
|
22
|
+
|
|
23
|
+
export { FORM, OWN, SPENT, TONE }
|
|
24
|
+
|
|
25
|
+
/** How many numbers stand in front of the count. */
|
|
26
|
+
const HEAD = 6
|
|
27
|
+
|
|
28
|
+
/** How many numbers one drawing spends before its points. */
|
|
29
|
+
const MARK = 12
|
|
30
|
+
|
|
31
|
+
export interface Held {
|
|
32
|
+
/** How light it is above the water, 0 through 1. */
|
|
33
|
+
daylight: number
|
|
34
|
+
/** How much bigger than its box the scene is drawn. */
|
|
35
|
+
overscan: number
|
|
36
|
+
/** How far the frame has wandered, in px. */
|
|
37
|
+
swayX: number
|
|
38
|
+
swayY: number
|
|
39
|
+
/** How far it has rolled, in radians. */
|
|
40
|
+
tilt: number
|
|
41
|
+
/** Which frame of grain this is. */
|
|
42
|
+
turn: number
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Where a box is pointed at the frame, for a window on a water bigger than it.
|
|
47
|
+
*
|
|
48
|
+
* A scene draws itself at its own size and is held with a hand's own wander,
|
|
49
|
+
* which is the header above. A plate is the other case: the water is larger
|
|
50
|
+
* than the box, the box is pointed at one animal in it, and the animal is
|
|
51
|
+
* magnified until it fills the frame. Both are the same drawing seen from a
|
|
52
|
+
* different place, which is why this is a camera rather than a second kind of
|
|
53
|
+
* frame.
|
|
54
|
+
*/
|
|
55
|
+
export interface View {
|
|
56
|
+
/** How far in, 1 being the water's own size. */
|
|
57
|
+
scale: number
|
|
58
|
+
/** The point of the water to put in the middle of the box. */
|
|
59
|
+
x: number
|
|
60
|
+
y: number
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Where the camera is holding the frame, off the front of the block. */
|
|
64
|
+
export function held(floats: Float32Array): Held {
|
|
65
|
+
return {
|
|
66
|
+
daylight: floats[5] ?? 1,
|
|
67
|
+
overscan: floats[3] ?? 1,
|
|
68
|
+
swayX: floats[0] ?? 0,
|
|
69
|
+
swayY: floats[1] ?? 0,
|
|
70
|
+
tilt: floats[2] ?? 0,
|
|
71
|
+
turn: floats[4] ?? 0,
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* One drawing at a time, in the order they were made.
|
|
77
|
+
*
|
|
78
|
+
* The order is the picture. Depth sorts anything solid, but two lights over one
|
|
79
|
+
* another are a painting rather than a stack of distances, so a renderer that
|
|
80
|
+
* shuffles this is a renderer drawing a different scene.
|
|
81
|
+
*/
|
|
82
|
+
export class Reader {
|
|
83
|
+
/** How many drawings are in this frame. */
|
|
84
|
+
readonly count: number
|
|
85
|
+
|
|
86
|
+
form = 0
|
|
87
|
+
alpha = 1
|
|
88
|
+
fall = 1
|
|
89
|
+
fadeSpan = 0
|
|
90
|
+
fadeTop = 0
|
|
91
|
+
lane = 0
|
|
92
|
+
shade = OWN
|
|
93
|
+
soft = 0
|
|
94
|
+
thin = 1
|
|
95
|
+
tone = 0
|
|
96
|
+
weight = 0
|
|
97
|
+
width = 0
|
|
98
|
+
|
|
99
|
+
/** How many pieces this drawing is in. */
|
|
100
|
+
parts = 0
|
|
101
|
+
|
|
102
|
+
/** The block being walked, for a renderer reading points straight out of it. */
|
|
103
|
+
readonly floats: Float32Array
|
|
104
|
+
|
|
105
|
+
private at = 0
|
|
106
|
+
private read = 0
|
|
107
|
+
private mark = 0
|
|
108
|
+
|
|
109
|
+
constructor(floats: Float32Array) {
|
|
110
|
+
this.floats = floats
|
|
111
|
+
this.count = floats[HEAD] ?? 0
|
|
112
|
+
this.at = HEAD + 1
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** Whether the drawing is a round light rather than a shape. */
|
|
116
|
+
get round(): boolean {
|
|
117
|
+
return this.form === FORM.light
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** Whether it ends on the light's own curve rather than on an exponent. */
|
|
121
|
+
get spent(): boolean {
|
|
122
|
+
return (this.tone & SPENT) !== 0
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** Which colour it is mixed from, with the flags taken off. */
|
|
126
|
+
get hue(): number {
|
|
127
|
+
return this.tone & 0xff
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Stand on the next drawing, or say there is not one.
|
|
132
|
+
*
|
|
133
|
+
* The points are stepped over here rather than by the caller, so a renderer
|
|
134
|
+
* that skips a drawing it cannot use does not have to know how long it was.
|
|
135
|
+
*/
|
|
136
|
+
next(): boolean {
|
|
137
|
+
if (this.read >= this.count) return false
|
|
138
|
+
this.read++
|
|
139
|
+
this.at = this.stand(this.at)
|
|
140
|
+
return true
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** Where the drawing being stood on begins, for a caller keeping the place. */
|
|
144
|
+
get offset(): number {
|
|
145
|
+
return this.mark
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Stand on the drawing at a given place, and say where the next one begins.
|
|
150
|
+
*
|
|
151
|
+
* A renderer that has to put the frame in another order reads it twice: once
|
|
152
|
+
* to note where each drawing is and how far back it stands, and again in the
|
|
153
|
+
* order it decided on. Both passes are this.
|
|
154
|
+
*/
|
|
155
|
+
stand(from: number): number {
|
|
156
|
+
const floats = this.floats
|
|
157
|
+
let at = from
|
|
158
|
+
this.mark = from
|
|
159
|
+
|
|
160
|
+
this.form = floats[at] ?? 0
|
|
161
|
+
this.tone = floats[at + 1] ?? 0
|
|
162
|
+
this.weight = floats[at + 2] ?? 0
|
|
163
|
+
this.shade = floats[at + 3] ?? OWN
|
|
164
|
+
this.alpha = floats[at + 4] ?? 1
|
|
165
|
+
this.lane = floats[at + 5] ?? 0
|
|
166
|
+
this.width = floats[at + 6] ?? 0
|
|
167
|
+
this.fall = floats[at + 7] ?? 1
|
|
168
|
+
this.fadeTop = floats[at + 8] ?? 0
|
|
169
|
+
this.fadeSpan = floats[at + 9] ?? 0
|
|
170
|
+
this.thin = floats[at + 10] ?? 1
|
|
171
|
+
this.soft = floats[at + 11] ?? 0
|
|
172
|
+
|
|
173
|
+
at += MARK
|
|
174
|
+
this.parts = floats[at++] ?? 0
|
|
175
|
+
this.first = at
|
|
176
|
+
|
|
177
|
+
for (let part = 0; part < this.parts; part++) {
|
|
178
|
+
at += 1 + 2 * (floats[at] ?? 0)
|
|
179
|
+
}
|
|
180
|
+
return at
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/** Where this drawing's first part begins. */
|
|
184
|
+
first = 0
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Every piece of this drawing, as where its points start and how many.
|
|
188
|
+
*
|
|
189
|
+
* Several pieces rather than one, because an animal is not one outline: a
|
|
190
|
+
* fish is a body, a tail swung off its joint and a fin rooted inside the
|
|
191
|
+
* back, and they are meant to overlap. Filled as one silhouette they are the
|
|
192
|
+
* animal; filled one after another, every overlap shows as a seam.
|
|
193
|
+
*/
|
|
194
|
+
each(walk: (at: number, points: number) => void): void {
|
|
195
|
+
const floats = this.floats
|
|
196
|
+
let at = this.first
|
|
197
|
+
|
|
198
|
+
for (let part = 0; part < this.parts; part++) {
|
|
199
|
+
const points = floats[at] ?? 0
|
|
200
|
+
walk(at + 1, points)
|
|
201
|
+
at += 1 + 2 * points
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|