@codincod/ornament 0.0.0-stage → 0.2.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 +62 -2
- package/package.json +35 -3
- package/src/biome.ts +157 -0
- package/src/canvas/flat.ts +336 -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/colour.ts +86 -0
- package/src/paint/frame.ts +204 -0
- package/src/paint/ink.ts +134 -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/svg.ts +326 -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
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A drawing, walked.
|
|
3
|
+
*
|
|
4
|
+
* The ornament writes its animals and its wreckage as paths, because that is
|
|
5
|
+
* what a browser and Qt both take: a string of moves and curves. This renderer
|
|
6
|
+
* takes triangles, so somewhere between the two the curves have to become
|
|
7
|
+
* points, and here is the cheapest place for it to happen. The alternative is a
|
|
8
|
+
* path parser on the other side of the wire and a string squeezed through an
|
|
9
|
+
* array of floats to reach it.
|
|
10
|
+
*
|
|
11
|
+
* Absolute commands only, and only the four the ornament writes: a move, a
|
|
12
|
+
* line, a cubic and a quadratic, with a close. Nothing here guesses at the rest
|
|
13
|
+
* of SVG. If a drawing ever grows an arc, this will draw it as the chord and
|
|
14
|
+
* somebody will see it.
|
|
15
|
+
*/
|
|
16
|
+
import type { Point } from "./pen.ts"
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* How fine a curve is cut, as a share of how big the whole drawing is.
|
|
20
|
+
*
|
|
21
|
+
* A share rather than a length, and that is the whole of the change this file
|
|
22
|
+
* once needed. The drawings in this ornament are not written at one scale: a
|
|
23
|
+
* fish is about forty units nose to tail and an octopus is written about the
|
|
24
|
+
* origin at less than one, and both are stood at whatever size they are
|
|
25
|
+
* actually drawn. Cut to a fixed length, the fish came out smooth and the
|
|
26
|
+
* octopus came out as a heptagon with legs, and nothing said so until one was
|
|
27
|
+
* magnified on a plate.
|
|
28
|
+
*
|
|
29
|
+
* So a curve is cut against the drawing it belongs to. The steps are still
|
|
30
|
+
* counted against how far the curve actually runs, because a long sweep down a
|
|
31
|
+
* whale's back and the notch in a tail fin are the same command and want a
|
|
32
|
+
* different number of pieces.
|
|
33
|
+
*/
|
|
34
|
+
const STEP = 0.035
|
|
35
|
+
const MOST = 24
|
|
36
|
+
|
|
37
|
+
/** What one path came to, kept, since a fish is the same drawing as the fish
|
|
38
|
+
* beating beside it and there are only so many steps in a beat. */
|
|
39
|
+
const walked = new Map<string, Point[][]>()
|
|
40
|
+
|
|
41
|
+
export function trace(d: string): Point[][] {
|
|
42
|
+
const held = walked.get(d)
|
|
43
|
+
if (held) return held
|
|
44
|
+
|
|
45
|
+
const cut = walk(d)
|
|
46
|
+
walked.set(d, cut)
|
|
47
|
+
return cut
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* How big the drawing is, off the numbers in it.
|
|
52
|
+
*
|
|
53
|
+
* The furthest any coordinate gets from the origin, which is a bound on the
|
|
54
|
+
* drawing rather than its size. It is read off the string because it has to be
|
|
55
|
+
* known before the walk it decides the fineness of, and because every drawing
|
|
56
|
+
* in this ornament is written about its own middle, where the two are close
|
|
57
|
+
* enough.
|
|
58
|
+
*/
|
|
59
|
+
function span(d: string): number {
|
|
60
|
+
let widest = 0
|
|
61
|
+
const numbers = d.match(/-?\d*\.?\d+(?:e-?\d+)?/g)
|
|
62
|
+
if (!numbers) return 1
|
|
63
|
+
for (const one of numbers) widest = Math.max(widest, Math.abs(Number(one)))
|
|
64
|
+
return Math.max(widest, 0.001)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function walk(d: string): Point[][] {
|
|
68
|
+
const step = span(d) * STEP
|
|
69
|
+
const parts: Point[][] = []
|
|
70
|
+
let here: Point[] = []
|
|
71
|
+
let at = { x: 0, y: 0 }
|
|
72
|
+
let start = { x: 0, y: 0 }
|
|
73
|
+
|
|
74
|
+
const tokens = d.match(/[MLCQZmlcqz]|-?\d*\.?\d+(?:e-?\d+)?/g) ?? []
|
|
75
|
+
let read = 0
|
|
76
|
+
const number = () => Number(tokens[read++])
|
|
77
|
+
|
|
78
|
+
while (read < tokens.length) {
|
|
79
|
+
const command = tokens[read++]
|
|
80
|
+
|
|
81
|
+
if (command === "M" || command === "m") {
|
|
82
|
+
if (here.length > 1) parts.push(here)
|
|
83
|
+
here = []
|
|
84
|
+
at = { x: number(), y: number() }
|
|
85
|
+
start = at
|
|
86
|
+
here.push(at)
|
|
87
|
+
continue
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (command === "L" || command === "l") {
|
|
91
|
+
at = { x: number(), y: number() }
|
|
92
|
+
here.push(at)
|
|
93
|
+
continue
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (command === "C" || command === "c") {
|
|
97
|
+
const one = { x: number(), y: number() }
|
|
98
|
+
const two = { x: number(), y: number() }
|
|
99
|
+
const end = { x: number(), y: number() }
|
|
100
|
+
cubic(here, at, one, two, end, step)
|
|
101
|
+
at = end
|
|
102
|
+
continue
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (command === "Q" || command === "q") {
|
|
106
|
+
const hold = { x: number(), y: number() }
|
|
107
|
+
const end = { x: number(), y: number() }
|
|
108
|
+
quadratic(here, at, hold, end, step)
|
|
109
|
+
at = end
|
|
110
|
+
continue
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (command === "Z" || command === "z") {
|
|
114
|
+
if (here.length > 1) parts.push(here)
|
|
115
|
+
here = []
|
|
116
|
+
at = start
|
|
117
|
+
continue
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// A number where a command was expected is a repeat of the last one, which
|
|
121
|
+
// the ornament does not write. Skipping it is better than reading the rest
|
|
122
|
+
// of the drawing off by one.
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (here.length > 1) parts.push(here)
|
|
126
|
+
return parts
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** How many pieces a curve of this reach is worth cutting into. */
|
|
130
|
+
function pieces(reach: number, step: number): number {
|
|
131
|
+
return Math.max(2, Math.min(MOST, Math.ceil(reach / step)))
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function cubic(into: Point[], from: Point, one: Point, two: Point, end: Point, step: number): void {
|
|
135
|
+
const reach =
|
|
136
|
+
Math.hypot(one.x - from.x, one.y - from.y) +
|
|
137
|
+
Math.hypot(two.x - one.x, two.y - one.y) +
|
|
138
|
+
Math.hypot(end.x - two.x, end.y - two.y)
|
|
139
|
+
const steps = pieces(reach, step)
|
|
140
|
+
|
|
141
|
+
for (let i = 1; i <= steps; i++) {
|
|
142
|
+
const t = i / steps
|
|
143
|
+
const u = 1 - t
|
|
144
|
+
into.push({
|
|
145
|
+
x: u * u * u * from.x + 3 * u * u * t * one.x + 3 * u * t * t * two.x + t * t * t * end.x,
|
|
146
|
+
y: u * u * u * from.y + 3 * u * u * t * one.y + 3 * u * t * t * two.y + t * t * t * end.y,
|
|
147
|
+
})
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function quadratic(into: Point[], from: Point, hold: Point, end: Point, step: number): void {
|
|
152
|
+
const reach =
|
|
153
|
+
Math.hypot(hold.x - from.x, hold.y - from.y) + Math.hypot(end.x - hold.x, end.y - hold.y)
|
|
154
|
+
const steps = pieces(reach, step)
|
|
155
|
+
|
|
156
|
+
for (let i = 1; i <= steps; i++) {
|
|
157
|
+
const t = i / steps
|
|
158
|
+
const u = 1 - t
|
|
159
|
+
into.push({
|
|
160
|
+
x: u * u * from.x + 2 * u * t * hold.x + t * t * end.x,
|
|
161
|
+
y: u * u * from.y + 2 * u * t * hold.y + t * t * end.y,
|
|
162
|
+
})
|
|
163
|
+
}
|
|
164
|
+
}
|