flowfig 0.11.0 → 0.12.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.
@@ -0,0 +1,854 @@
1
+ export const U = 40, C = U * Math.cos(Math.PI / 6), S = U / 2;
2
+ export const P = (x, y, z = 0) => [(x - y) * C, (x + y) * S - z * U];
3
+ const f = (n) => Math.round(n * 10) / 10;
4
+ const pts = (a) => a.map(([x, y]) => `${f(x)},${f(y)}`).join(' ');
5
+ const poly = (a, fill, ex = '') => `<polygon points="${pts(a)}" fill="${fill}" ${ex}/>`;
6
+ const FRONT = 4.6, WALK0 = 5.4, ROAD0 = 6.0, ROAD1 = 8.0, WALK1 = 8.6;
7
+ export const BY = 1.6;
8
+ const K = {
9
+ ground: '#E8ECE3',
10
+ lawn: '#DDE6D4',
11
+ yard: '#D3DFC8',
12
+ walk: '#F4F5F0',
13
+ seam: '#E2E5DC',
14
+ curb: '#C3C8CE',
15
+ road: '#8A919B',
16
+ dash: '#EEF0F2',
17
+ glass: '#2a3446',
18
+ glassR: '#222a39',
19
+ light: '#f2b544',
20
+ lightR: '#d99a2b',
21
+ leaf: '#7DB27A',
22
+ leafD: '#5E9660',
23
+ trunk: '#6b5a49',
24
+ ink: '#141A22',
25
+ post: '#3b4252',
26
+ ao: 'rgba(20,26,34,.11)',
27
+ };
28
+ const M = {
29
+ brick: {
30
+ l: '#c96a4b',
31
+ r: '#a4513a',
32
+ t: '#d6c0ab',
33
+ e: '#6e3424',
34
+ tex: '#b65d41',
35
+ texR: '#934733',
36
+ cap: '#f6ecdb',
37
+ capR: '#dccdb4',
38
+ fr: '#f3e8d6',
39
+ frR: '#d9cbb3',
40
+ },
41
+ stone: {
42
+ l: '#e9dcc0',
43
+ r: '#cbbd9f',
44
+ t: '#d9cfb8',
45
+ e: '#8f8064',
46
+ tex: '#d6c8a8',
47
+ texR: '#b6a888',
48
+ cap: '#fbf7ee',
49
+ capR: '#ddd3bf',
50
+ fr: '#7d7058',
51
+ frR: '#6a5e48',
52
+ },
53
+ green: {
54
+ l: '#86a581',
55
+ r: '#678663',
56
+ t: '#c3cac1',
57
+ e: '#3c533d',
58
+ tex: '#76946f',
59
+ texR: '#587553',
60
+ cap: '#eef0ea',
61
+ capR: '#cdd3ca',
62
+ fr: '#eef0ea',
63
+ frR: '#cdd3ca',
64
+ },
65
+ slate: {
66
+ l: '#434b5a',
67
+ r: '#2e3440',
68
+ t: '#5a6372',
69
+ e: '#14181e',
70
+ tex: '#515a6a',
71
+ texR: '#3a414d',
72
+ cap: '#717a8a',
73
+ capR: '#5a6271',
74
+ fr: '#6b7484',
75
+ frR: '#555d6b',
76
+ },
77
+ pale: {
78
+ l: '#e3e8ef',
79
+ r: '#c3ccd8',
80
+ t: '#d3dae4',
81
+ e: '#5d6878',
82
+ tex: '#d2d9e3',
83
+ texR: '#b3bdcb',
84
+ cap: '#f5f7fa',
85
+ capR: '#dce2ea',
86
+ fr: '#9aa5b5',
87
+ frR: '#818c9c',
88
+ },
89
+ clay: {
90
+ l: '#e7d3b4',
91
+ r: '#c9b292',
92
+ t: '#d9c8ad',
93
+ e: '#7f6a4c',
94
+ tex: '#dbc5a3',
95
+ texR: '#b9a282',
96
+ cap: '#f8f0e1',
97
+ capR: '#ddd0b9',
98
+ fr: '#f8f0e1',
99
+ frR: '#ddd0b9',
100
+ roof: '#7a4f3a',
101
+ roofB: '#5f3c2c',
102
+ },
103
+ sand: {
104
+ l: '#d8c49a',
105
+ r: '#b9a57c',
106
+ t: '#ccbf9f',
107
+ e: '#6f6043',
108
+ tex: '#cab68c',
109
+ texR: '#a9966e',
110
+ cap: '#f3ead6',
111
+ capR: '#d6cbb2',
112
+ fr: '#f3ead6',
113
+ frR: '#d6cbb2',
114
+ },
115
+ marble: {
116
+ l: '#ece8df',
117
+ r: '#cfc9bc',
118
+ t: '#dfdacf',
119
+ e: '#7e776a',
120
+ tex: '#e0dbd0',
121
+ texR: '#bdb6a8',
122
+ cap: '#faf8f3',
123
+ capR: '#ddd8cc',
124
+ fr: '#5e584d',
125
+ frR: '#4e493f',
126
+ },
127
+ concrete: {
128
+ l: '#cfd2cc',
129
+ r: '#b0b4ad',
130
+ t: '#c3c7c0',
131
+ e: '#5b6058',
132
+ tex: '#c2c6bf',
133
+ texR: '#a2a69f',
134
+ cap: '#eef0ec',
135
+ capR: '#d1d4ce',
136
+ fr: '#eef0ec',
137
+ frR: '#d1d4ce',
138
+ },
139
+ plain: {
140
+ l: '#dcd8cf',
141
+ r: '#bfbab0',
142
+ t: '#d0ccc3',
143
+ e: '#716b60',
144
+ tex: '#cfcbc1',
145
+ texR: '#b0aba1',
146
+ cap: '#f6f4ef',
147
+ capR: '#d9d5cd',
148
+ fr: '#f6f4ef',
149
+ frR: '#d9d5cd',
150
+ },
151
+ };
152
+ const quadT = (x0, x1, y0, y1, z, fill, ex = '') => poly([P(x0, y0, z), P(x1, y0, z), P(x1, y1, z), P(x0, y1, z)], fill, ex);
153
+ const quadL = (y, x0, x1, z0, z1, fill, ex = '') => poly([P(x0, y, z0), P(x1, y, z0), P(x1, y, z1), P(x0, y, z1)], fill, ex);
154
+ const quadR = (x, y0, y1, z0, z1, fill, ex = '') => poly([P(x, y0, z0), P(x, y1, z0), P(x, y1, z1), P(x, y0, z1)], fill, ex);
155
+ const edge = (c) => `stroke="${c}" stroke-opacity=".55" stroke-width="1" stroke-linejoin="round"`;
156
+ const box = (x, y, z, w, d, h, t, l, r, e) => {
157
+ const ex = e ? edge(e) : '';
158
+ return quadL(y + d, x, x + w, z, z + h, l, ex) + quadR(x + w, y, y + d, z, z + h, r, ex) + quadT(x, x + w, y, y + d, z + h, t, ex);
159
+ };
160
+ const mbox = (x, y, z, w, d, h, m) => box(x, y, z, w, d, h, m.t, m.l, m.r, m.e);
161
+ const line = (a, b, stroke, w = 1, ex = '') => `<line x1="${f(a[0])}" y1="${f(a[1])}" x2="${f(b[0])}" y2="${f(b[1])}" stroke="${stroke}" stroke-width="${w}" ${ex}/>`;
162
+ const ring = (fn, n = 28) => Array.from({ length: n }, (_, i) => fn((i / n) * Math.PI * 2));
163
+ const ellT = (cx, cy, z, r, fill, ex = '') => poly(ring((t) => P(cx + r * Math.cos(t), cy + r * Math.sin(t), z)), fill, ex);
164
+ const ao = (x, y, w, d) => quadT(x - 0.05, x + w + 0.45, y - 0.05, y + d + 0.22, 0, K.ao);
165
+ const coursesL = (Y, x0, x1, z0, z1, step, c) => {
166
+ let o = '';
167
+ for (let z = z0 + step; z < z1 - 0.02; z += step)
168
+ o += line(P(x0, Y, z), P(x1, Y, z), c, 1);
169
+ return o;
170
+ };
171
+ const coursesR = (X, y0, y1, z0, z1, step, c) => {
172
+ let o = '';
173
+ for (let z = z0 + step; z < z1 - 0.02; z += step)
174
+ o += line(P(X, y0, z), P(X, y1, z), c, 1);
175
+ return o;
176
+ };
177
+ const seamsL = (Y, x0, x1, z0, z1, step, c) => {
178
+ let o = '';
179
+ for (let x = x0 + step; x < x1 - 0.02; x += step)
180
+ o += line(P(x, Y, z0), P(x, Y, z1), c, 1);
181
+ return o;
182
+ };
183
+ const seamsR = (X, y0, y1, z0, z1, step, c) => {
184
+ let o = '';
185
+ for (let y = y0 + step; y < y1 - 0.02; y += step)
186
+ o += line(P(X, y, z0), P(X, y, z1), c, 1);
187
+ return o;
188
+ };
189
+ const cube = (x, y, z, w, d, h) => [
190
+ [x, y, z],
191
+ [x + w, y, z],
192
+ [x, y + d, z],
193
+ [x + w, y + d, z],
194
+ ].flatMap(([u, v]) => [
195
+ [u, v, z],
196
+ [u, v, z + h],
197
+ ]);
198
+ function winL(m, Y, a, b, z0, z1, lit, cols = 2, transom = true) {
199
+ const k = 0.05;
200
+ let o = quadL(Y, a, b, z0, z1, m.fr) + quadL(Y, a + k, b - k, z0 + k, z1 - k, lit ? K.light : K.glass);
201
+ for (let i = 1; i < cols; i++) {
202
+ const x = a + ((b - a) * i) / cols;
203
+ o += line(P(x, Y, z0 + k), P(x, Y, z1 - k), m.fr, 1.5);
204
+ }
205
+ if (transom) {
206
+ const z = z0 + (z1 - z0) * 0.68;
207
+ o += line(P(a + k, Y, z), P(b - k, Y, z), m.fr, 1.5);
208
+ }
209
+ return o + quadL(Y, a - 0.03, b + 0.03, z0 - 0.06, z0, m.cap);
210
+ }
211
+ function winR(m, X, a, b, z0, z1, lit, cols = 2, transom = true) {
212
+ const k = 0.05;
213
+ let o = quadR(X, a, b, z0, z1, m.frR) + quadR(X, a + k, b - k, z0 + k, z1 - k, lit ? K.lightR : K.glassR);
214
+ for (let i = 1; i < cols; i++) {
215
+ const y = a + ((b - a) * i) / cols;
216
+ o += line(P(X, y, z0 + k), P(X, y, z1 - k), m.frR, 1.5);
217
+ }
218
+ if (transom) {
219
+ const z = z0 + (z1 - z0) * 0.68;
220
+ o += line(P(X, a + k, z), P(X, b - k, z), m.frR, 1.5);
221
+ }
222
+ return o + quadR(X, a - 0.03, b + 0.03, z0 - 0.06, z0, m.capR);
223
+ }
224
+ function roofWell(x, y, w, d, h, m, k = 0.12) {
225
+ let o = quadT(x, x + w, y, y + d, h, m.cap, edge(m.e));
226
+ o += quadT(x + k, x + w - k, y + k, y + d - k, h - 0.1, m.t);
227
+ o += quadL(y + k, x + k, x + w - k, h - 0.1, h, m.tex) + quadR(x + k, y + k, y + d - k, h - 0.1, h, m.texR);
228
+ o += quadT(x, x + w, y + d - k, y + d, h, m.cap) + quadT(x + w - k, x + w, y, y + d, h, m.cap);
229
+ return o;
230
+ }
231
+ const hvac = (x, y, z, w = 0.8, d = 0.7, h = 0.35) => box(x, y, z, w, d, h, '#e4e6e9', '#cfd3d8', '#aeb4bc', '#6b717a');
232
+ const door2 = (m, Y, a, b, h) => quadL(Y, a, b, 0, h, m.fr) +
233
+ quadL(Y, a + 0.06, b - 0.06, 0, h - 0.05, K.glass) +
234
+ quadL(Y, (a + b) / 2 - 0.02, (a + b) / 2 + 0.02, 0, h - 0.05, m.fr);
235
+ const rollerL = (Y, a, b, h, frame, slat, slat2) => {
236
+ let o = quadL(Y, a - 0.05, b + 0.05, 0, h + 0.05, frame) + quadL(Y, a, b, 0, h, slat);
237
+ for (let z = 0.1; z < h - 0.05; z += 0.105)
238
+ o += quadL(Y, a, b, z, z + 0.035, slat2);
239
+ return o;
240
+ };
241
+ function house(x, y) {
242
+ const m = M.clay, w = 3, d = 3, h = 1.15, rz = 0.95, o0 = 0.16, Y = y + d, X = x + w, ym = y + d / 2;
243
+ let o = ao(x, y, w, d) + mbox(x, y, 0, w, d, h, m);
244
+ o += coursesL(Y, x, X, 0, h, 0.13, m.tex) + coursesR(X, y, Y, 0, h, 0.13, m.texR);
245
+ o += quadL(Y, x, X, 0, 0.1, m.texR) + quadR(X, y, Y, 0, 0.1, m.e, 'fill-opacity=".35"');
246
+ o +=
247
+ quadL(Y, x + 0.3, x + 0.9, 0, 0.92, '#5b3a2a') +
248
+ quadL(Y, x + 0.36, x + 0.84, 0, 0.86, '#6f4733') +
249
+ `<circle cx="${f(P(x + 0.76, Y, 0.45)[0])}" cy="${f(P(x + 0.76, Y, 0.45)[1])}" r="1.6" fill="${K.light}"/>`;
250
+ o += quadL(Y, x + 0.22, x + 0.98, 0.92, 0.98, m.cap);
251
+ o += winL(m, Y, x + 1.3, x + 2.0, 0.32, 0.86, true, 2, false) + winL(m, Y, x + 2.2, x + 2.8, 0.32, 0.86, false, 2, false);
252
+ o += winR(m, X, y + 0.45, y + 1.15, 0.32, 0.86, true, 2, false) + winR(m, X, y + 1.85, y + 2.55, 0.32, 0.86, false, 2, false);
253
+ o += poly([P(x - o0, y - o0, h), P(X + o0, y - o0, h), P(X + o0, ym, h + rz), P(x - o0, ym, h + rz)], m.roofB);
254
+ o += poly([P(X, y, h), P(X, Y, h), P(X, ym, h + rz)], m.r, edge(m.e)) + coursesR(X, y + 0.5, Y - 0.5, h, h + rz * 0.5, 0.13, m.texR);
255
+ o += winR(m, X, ym - 0.22, ym + 0.22, h + 0.18, h + 0.55, true, 1, false);
256
+ o += poly([P(x - o0, Y + o0, h), P(X + o0, Y + o0, h), P(X + o0, ym, h + rz), P(x - o0, ym, h + rz)], m.roof, edge(m.e));
257
+ for (let k = 1; k < 6; k++) {
258
+ const t = k / 6;
259
+ o += line(P(x - o0, Y + o0 - (Y + o0 - ym) * t, h + rz * t), P(X + o0, Y + o0 - (Y + o0 - ym) * t, h + rz * t), m.roofB, 1);
260
+ }
261
+ o += line(P(x - o0, ym, h + rz), P(X + o0, ym, h + rz), '#3e271c', 2);
262
+ o +=
263
+ box(x + 0.55, ym + 0.25, h + rz * 0.62, 0.36, 0.36, rz * 0.38 + 0.25, '#9c4f3a', '#8a4433', '#6f3628', '#4a241a') +
264
+ box(x + 0.5, ym + 0.2, h + rz + 0.5, 0.46, 0.46, 0.08, '#b9b0a2', '#a39a8c', '#8a8274');
265
+ return {
266
+ svg: o,
267
+ ant: [x + 2.5, ym, h + rz],
268
+ pts: [
269
+ ...cube(x - o0, y - o0, 0, w + 2 * o0, d + 2 * o0, h),
270
+ [x - o0, ym, h + rz],
271
+ [X + o0, ym, h + rz],
272
+ [x + 0.5, ym + 0.2, h + rz + 0.6],
273
+ [x + 0.96, ym + 0.66, h + rz + 0.6],
274
+ ],
275
+ };
276
+ }
277
+ function shop(x, y) {
278
+ const m = M.brick, w = 3, d = 3, h = 1.5, Y = y + d, X = x + w;
279
+ let o = ao(x, y, w, d) + mbox(x, y, 0, w, d, h, m);
280
+ o += coursesL(Y, x, X, 0, h, 0.12, m.tex) + coursesR(X, y, Y, 0, h, 0.12, m.texR);
281
+ o += quadL(Y, x, X, 0, 0.12, m.r) + quadR(X, y, Y, 0, 0.12, m.e, 'fill-opacity=".5"');
282
+ o +=
283
+ quadL(Y, x + 1.05, x + 1.95, 0, 1.0, m.fr) +
284
+ quadL(Y, x + 1.11, x + 1.89, 0, 0.94, K.glass) +
285
+ quadL(Y, x + 1.49, x + 1.51, 0, 0.94, m.fr) +
286
+ quadL(Y, x + 1.17, x + 1.83, 0.72, 0.88, K.light);
287
+ o += winL(m, Y, x + 0.2, x + 0.85, 0.3, 0.95, true, 2, false) + winL(m, Y, x + 2.15, x + 2.8, 0.3, 0.95, true, 2, false);
288
+ for (let i = 0; i < 7; i++) {
289
+ const a = x + 0.12 + i * 0.395, b = a + 0.395;
290
+ o += poly([P(a, Y, 1.14), P(b, Y, 1.14), P(b, Y + 0.38, 1.0), P(a, Y + 0.38, 1.0)], i % 2 ? '#9c4632' : '#f6ecdb', edge(m.e));
291
+ }
292
+ o += poly([P(x + 0.12, Y + 0.38, 1.0), P(x + 2.89, Y + 0.38, 1.0), P(x + 2.89, Y + 0.38, 0.94), P(x + 0.12, Y + 0.38, 0.94)], '#7c3626');
293
+ o += winR(m, X, y + 0.4, y + 1.1, 0.45, 1.0, false) + winR(m, X, y + 1.5, y + 2.2, 0.45, 1.0, true);
294
+ o += roofWell(x, y, w, d, h + 0.12, m);
295
+ o += hvac(x + 0.4, y + 0.5, h + 0.02) + hvac(x + 1.95, y + 1.75, h + 0.02, 0.45, 0.45, 0.24);
296
+ return { svg: o, ant: [x + 2.5, y + 0.7, h + 0.12], pts: cube(x, y, 0, w, d, h + 0.4) };
297
+ }
298
+ function office(x, y, floors) {
299
+ const m = M.stone, w = 3, d = 3, h = 0.04 + 1.38 * floors, Y = y + d, X = x + w;
300
+ let o = ao(x, y, w, d) + mbox(x, y, 0, w, d, h, m);
301
+ o += coursesL(Y, x, X, 0, h, 0.2, m.tex) + coursesR(X, y, Y, 0, h, 0.2, m.texR);
302
+ for (let fl = 1; fl < floors; fl++)
303
+ o += quadL(Y, x, X, fl * 1.38 - 0.04, fl * 1.38 + 0.06, m.cap) + quadR(X, y, Y, fl * 1.38 - 0.04, fl * 1.38 + 0.06, m.capR);
304
+ o += quadL(Y, x, X, 0, 0.15, m.tex) + quadR(X, y, Y, 0, 0.15, m.texR);
305
+ const lit = (k) => (k * 7) % 5 < 2;
306
+ for (let fl = 0; fl < floors; fl++)
307
+ for (let i = 0; i < 4; i++) {
308
+ const z0 = 0.38 + fl * 1.38, z1 = z0 + 0.74, a = x + 0.22 + i * 0.68;
309
+ if (!(fl === 0 && (i === 1 || i === 2)))
310
+ o += winL(m, Y, a, a + 0.48, z0, z1, lit(fl * 4 + i), 2);
311
+ const b = y + 0.22 + i * 0.68;
312
+ o += winR(m, X, b, b + 0.48, z0, z1, lit(fl * 4 + i + 3), 2);
313
+ }
314
+ o +=
315
+ quadL(Y, x + 1.0, x + 2.0, 0, 1.05, m.fr) + quadL(Y, x + 1.05, x + 1.95, 0, 1.0, K.glass) + quadL(Y, x + 1.48, x + 1.52, 0, 1.0, m.fr);
316
+ o += quadL(Y, x + 1.1, x + 1.9, 0.75, 0.95, K.light);
317
+ o += box(x + 0.85, Y, 1.08, 1.3, 0.45, 0.1, m.cap, m.cap, m.capR, m.e);
318
+ o += roofWell(x, y, w, d, h + 0.12, m);
319
+ o += hvac(x + 1.7, y + 0.4, h + 0.02, 0.9, 0.7, 0.45);
320
+ for (let i = 0; i < 3; i++)
321
+ o += quadL(y + 1.1, x + 1.8 + i * 0.25, x + 1.95 + i * 0.25, h + 0.12, h + 0.37, '#8b939f');
322
+ o += box(x + 0.4, y + 1.6, h + 0.02, 0.5, 0.5, 0.3, m.cap, m.cap, m.capR, m.e);
323
+ return { svg: o, ant: [x + 2.4, y + 2.3, h + 0.04], pts: cube(x, y, 0, w, d, h + 0.5) };
324
+ }
325
+ function depot(x, y) {
326
+ const m = M.green, w = 3, d = 2, h = 1.4, Y = y + d, X = x + w;
327
+ let o = ao(x, y, w, d) + mbox(x, y, 0, w, d, h, m);
328
+ o += seamsL(Y, x, X, 0, h, 0.12, m.tex) + seamsR(X, y, Y, 0, h, 0.12, m.texR);
329
+ o += quadL(Y, x + 0.25, x + 1.85, 0, 1.08, m.fr) + quadL(Y, x + 0.3, x + 1.8, 0, 1.03, '#1d222b');
330
+ o +=
331
+ quadL(Y, x + 2.15, x + 2.6, 0, 0.88, m.fr) +
332
+ quadL(Y, x + 2.2, x + 2.55, 0, 0.83, K.glass) +
333
+ quadL(Y, x + 2.25, x + 2.5, 0.6, 0.78, K.light);
334
+ o += winR(m, X, y + 0.35, y + 1.65, 0.55, 1.0, true, 3, false);
335
+ o += quadT(x - 0.05, X + 0.05, y - 0.05, Y + 0.05, h, m.t, edge(m.e));
336
+ for (let k = y + 0.1; k < Y; k += 0.16)
337
+ o += line(P(x - 0.05, k, h), P(X + 0.05, k, h), '#a7b0a5', 1);
338
+ o += quadL(Y + 0.05, x - 0.05, X + 0.05, h - 0.06, h, '#a7b0a5') + quadR(X + 0.05, y - 0.05, Y + 0.05, h - 0.06, h, '#8e978c');
339
+ for (let i = 0; i < 3; i++)
340
+ o += box(x + 0.4 + i * 0.85, y + 0.6, h, 0.5, 0.8, 0.14, '#e9eee8', '#dfe5dc', '#c3cbc0', m.e);
341
+ return { svg: o, ant: [x + 2.6, y + 1.7, h], pts: cube(x, y, 0, w, d, h + 0.14), depot: { x, y: Y } };
342
+ }
343
+ function shed(x, y) {
344
+ const m = M.sand, w = 3, d = 3, h = 1.35, Y = y + d, X = x + w;
345
+ let o = ao(x, y, w, d) + mbox(x, y, 0, w, d, h, m);
346
+ o += seamsL(Y, x, X, 0, h, 0.15, m.tex) + seamsR(X, y, Y, 0, h, 0.15, m.texR);
347
+ for (let i = 0; i < 3; i++) {
348
+ const a = x + 0.2 + i * 0.95;
349
+ o += box(a - 0.04, Y, 0, 0.75, 0.28, 0.32, '#8b8f96', '#9aa0a8', '#767b83', '#3b4252');
350
+ o += rollerL(Y, a, a + 0.68, 0.86, m.fr, '#4f5866', '#6a7482');
351
+ o += quadL(Y + 0.29, a + 0.02, a + 0.12, 0.12, 0.3, '#1d222b') + quadL(Y + 0.29, a + 0.56, a + 0.66, 0.12, 0.3, '#1d222b');
352
+ }
353
+ o += poly([P(x - 0.05, Y, 1.12), P(X + 0.05, Y, 1.12), P(X + 0.05, Y + 0.3, 1.06), P(x - 0.05, Y + 0.3, 1.06)], '#aab3be', edge('#5b636f'));
354
+ o += quadL(Y + 0.3, x - 0.05, X + 0.05, 1.01, 1.06, '#7d8794');
355
+ o += winR(m, X, y + 0.4, y + 2.6, 0.62, 1.05, true, 4, false);
356
+ o += roofWell(x, y, w, d, h + 0.1, m);
357
+ o += box(x + 0.35, y + 0.35, h + 0.02, 2.3, 0.5, 0.18, '#e9e4d6', '#d8d1bf', '#bdb5a2', m.e);
358
+ o += box(x + 0.35, y + 1.25, h + 0.02, 2.3, 0.5, 0.18, '#e9e4d6', '#d8d1bf', '#bdb5a2', m.e);
359
+ return { svg: o, ant: [x + 2.6, y + 2.4, h + 0.02], pts: cube(x, y, 0, w, d + 0.3, h + 0.3) };
360
+ }
361
+ function hall(x, y, m, led) {
362
+ const w = 3, d = 3, h = 1.9, Y = y + d, X = x + w;
363
+ let o = ao(x, y, w, d) + mbox(x, y, 0, w, d, h, m);
364
+ o += seamsL(Y, x, X, 0, h, 0.5, m.tex) + seamsR(X, y, Y, 0, h, 0.5, m.texR);
365
+ o += coursesL(Y, x, X, 0, h, 0.63, m.tex) + coursesR(X, y, Y, 0, h, 0.63, m.texR);
366
+ o +=
367
+ quadL(Y, x + 1.15, x + 1.85, 0, 1.15, m.fr) +
368
+ quadL(Y, x + 1.2, x + 1.8, 0, 1.1, '#1d222b') +
369
+ quadL(Y, x + 1.49, x + 1.51, 0, 1.1, m.fr);
370
+ o += quadL(Y, x + 1.92, x + 2.04, 0.5, 0.68, '#1d222b') + quadL(Y, x + 1.95, x + 2.01, 0.6, 0.65, K.light);
371
+ for (let i = 0; i < 6; i++) {
372
+ o += quadL(Y, x + 0.25, x + 0.95, 0.3 + i * 0.17, 0.37 + i * 0.17, m.e, 'fill-opacity=".7"');
373
+ o += quadL(Y, x + 2.2, x + 2.8, 0.3 + i * 0.17, 0.37 + i * 0.17, m.e, 'fill-opacity=".7"');
374
+ }
375
+ for (let i = 0; i < 4; i++) {
376
+ const b = y + 0.25 + i * 0.68;
377
+ o += quadR(X, b, b + 0.42, 0.3, 1.5, m.frR) + quadR(X, b + 0.04, b + 0.38, 0.34, 1.46, '#1a1e26');
378
+ for (let j = 0; j < 5; j++)
379
+ o += quadR(X, b + 0.08, b + 0.14, 0.45 + j * 0.22, 0.5 + j * 0.22, j === 2 && i === 1 ? K.light : led);
380
+ }
381
+ o += roofWell(x, y, w, d, h + 0.12, m);
382
+ o += box(x + 0.25, y + 0.3, h + 0.02, 0.25, 2.4, 0.08, m.cap, m.capR, m.texR);
383
+ for (const [cx, cy] of [
384
+ [x + 0.95, y + 0.55],
385
+ [x + 0.95, y + 1.65],
386
+ ]) {
387
+ o += box(cx, cy, h + 0.02, 0.95, 0.9, 0.32, '#c9ced6', '#dfe3e9', '#a9b0bb', '#3d4659');
388
+ const c = [cx + 0.475, cy + 0.45], zt = h + 0.35;
389
+ o += ellT(c[0], c[1], zt, 0.33, '#2a3142') + ellT(c[0], c[1], zt + 0.01, 0.08, '#8b9099');
390
+ for (let k = 0; k < 3; k++) {
391
+ const a = (k * 2 * Math.PI) / 3 + 0.4;
392
+ o += line(P(c[0], c[1], zt + 0.01), P(c[0] + 0.3 * Math.cos(a), c[1] + 0.3 * Math.sin(a), zt + 0.01), '#8b9099', 1.5);
393
+ }
394
+ }
395
+ return { svg: o, ant: [x + 2.5, y + 2.4, h + 0.04], pts: cube(x, y, 0, w, d, h + 0.4) };
396
+ }
397
+ function archive(x, y) {
398
+ const m = M.marble, w = 3, d = 3, h = 1.55, Y = y + d, X = x + w;
399
+ let o = ao(x, y, w, d + 0.5);
400
+ o +=
401
+ box(x - 0.1, y - 0.1, 0, w + 0.2, d + 0.6, 0.08, m.cap, m.tex, m.texR, m.e) +
402
+ box(x - 0.05, y - 0.05, 0.08, w + 0.1, d + 0.35, 0.08, m.cap, m.tex, m.texR, m.e);
403
+ o += mbox(x, y, 0.16, w, d, h - 0.16, m);
404
+ o += coursesR(X, y, Y, 0.16, h, 0.24, m.texR);
405
+ o +=
406
+ quadL(Y, x + 1.15, x + 1.85, 0.16, 1.1, m.fr) +
407
+ quadL(Y, x + 1.2, x + 1.8, 0.16, 1.05, '#3a352d') +
408
+ quadL(Y, x + 1.49, x + 1.51, 0.16, 1.05, m.fr);
409
+ for (const a of [x + 0.18, x + 0.68, x + 2.08, x + 2.58]) {
410
+ o += quadL(Y + 0.25, a, a + 0.26, 0.16, h - 0.1, '#f7f4ee') + quadL(Y + 0.25, a + 0.18, a + 0.26, 0.16, h - 0.1, '#d8d2c5');
411
+ o +=
412
+ box(a - 0.03, Y + 0.12, 0.16, 0.32, 0.16, 0.06, m.cap, m.tex, m.texR) +
413
+ box(a - 0.03, Y + 0.12, h - 0.14, 0.32, 0.16, 0.06, m.cap, m.tex, m.texR);
414
+ }
415
+ for (let i = 0; i < 4; i++) {
416
+ const b = y + 0.35 + i * 0.62;
417
+ o += quadR(X, b, b + 0.16, 0.45, 1.25, m.frR);
418
+ }
419
+ o += poly([P(x - 0.05, Y + 0.3, h), P(X + 0.05, Y + 0.3, h), P(X + 0.05, y, h), P(x - 0.05, y, h)], m.cap, edge(m.e));
420
+ o += box(x - 0.05, y, h - 0.1, w + 0.1, d + 0.3, 0.12, m.cap, m.tex, m.texR, m.e);
421
+ const pz = h + 0.02;
422
+ o +=
423
+ poly([P(x, Y + 0.3, pz), P(X, Y + 0.3, pz), P(x + w / 2, Y + 0.3, pz + 0.52)], m.l, edge(m.e)) +
424
+ poly([P(x + 0.3, Y + 0.3, pz + 0.06), P(X - 0.3, Y + 0.3, pz + 0.06), P(x + w / 2, Y + 0.3, pz + 0.4)], m.tex);
425
+ o += poly([P(X, Y + 0.3, pz), P(x + w / 2, Y + 0.3, pz + 0.52), P(x + w / 2, y + 0.2, pz + 0.52), P(X, y + 0.2, pz)], m.r, edge(m.e));
426
+ o += quadT(x + 0.15, x + w / 2 - 0.1, y + 0.4, y + 1.2, pz, m.t);
427
+ return {
428
+ svg: o,
429
+ ant: [x + 2.3, y + 0.7, h + 0.02],
430
+ pts: [...cube(x - 0.1, y - 0.1, 0, w + 0.2, d + 0.6, h + 0.1), [x + w / 2, Y + 0.3, pz + 0.52], [x + w / 2, y + 0.2, pz + 0.52]],
431
+ };
432
+ }
433
+ function works(x, y) {
434
+ const m = M.concrete, w = 3, d = 3, h = 1.25, Y = y + d, X = x + w, tz = 0.55;
435
+ let o = ao(x, y, w, d) + mbox(x, y, 0, w, d, h, m);
436
+ o += seamsL(Y, x, X, 0, h, 0.5, m.tex) + seamsR(X, y, Y, 0, h, 0.5, m.texR);
437
+ o += rollerL(Y, x + 0.3, x + 1.3, 0.95, m.fr, '#5a6b5f', '#71837a');
438
+ o += winL(m, Y, x + 1.65, x + 2.75, 0.55, 0.98, true, 3, false) + winR(m, X, y + 0.3, y + 2.7, 0.55, 0.98, true, 4, false);
439
+ const stack = (zTop) => box(x + 2.35, y + 0.25, 0, 0.36, 0.36, zTop, '#a5503b', '#93463a', '#743628', '#4a241a') +
440
+ box(x + 2.31, y + 0.21, zTop, 0.44, 0.44, 0.08, '#6b717a', '#5a6170', '#454b57');
441
+ o += quadT(x, X, y, Y, h, m.t, edge(m.e));
442
+ for (let k = 0; k < 3; k++) {
443
+ const y0 = y + k, y1 = y0 + 1;
444
+ o += poly([P(x, y0, h), P(X, y0, h), P(X, y1, h + tz), P(x, y1, h + tz)], '#8f97a1', edge('#3b4252'));
445
+ o += quadL(y1, x, X, h, h + tz, '#2a3446') + seamsL(y1, x, X, h, h + tz, 0.3, '#5d6b80');
446
+ o += poly([P(X, y0, h), P(X, y1, h), P(X, y1, h + tz)], m.r, edge(m.e));
447
+ if (k === 0)
448
+ o += stack(h + 1.75);
449
+ }
450
+ return {
451
+ svg: o,
452
+ ant: [x + 2.75, y + 2.95, h + tz],
453
+ pts: [...cube(x, y, 0, w, d, h + tz), [x + 2.31, y + 0.21, h + 1.83], [x + 2.75, y + 0.65, h + 1.83]],
454
+ };
455
+ }
456
+ function mast(x, y) {
457
+ const m = M.concrete, Y = y + 3;
458
+ let o = ao(x + 0.2, y + 1.3, 2.0, 1.7) + mbox(x + 0.2, y + 1.3, 0, 2.0, 1.7, 1.05, m);
459
+ o +=
460
+ quadL(Y, x + 0.9, x + 1.5, 0, 0.85, m.fr) +
461
+ quadL(Y, x + 0.95, x + 1.45, 0, 0.8, '#5b636f') +
462
+ quadL(Y, x + 0.4, x + 0.7, 0.5, 0.75, '#2e3440') +
463
+ seamsL(Y, x + 0.4, x + 0.7, 0.5, 0.75, 0.06, '#5b636f');
464
+ o += quadR(x + 2.2, y + 1.6, y + 2.6, 0.55, 0.8, '#2e3440') + seamsR(x + 2.2, y + 1.6, y + 2.6, 0.55, 0.8, 0.08, '#5b636f');
465
+ o += roofWell(x + 0.2, y + 1.3, 2.0, 1.7, 1.12, m, 0.08);
466
+ const b0 = [x + 1.6, y + 0.15], s0 = 1.1, top = 4.6, s1 = 0.24, c0 = b0[0] + s0 / 2, c1 = b0[1] + s0 / 2;
467
+ const leg = (sx, sy, z) => {
468
+ const s = s0 + (s1 - s0) * (z / top);
469
+ return [c0 + (sx * s) / 2, c1 + (sy * s) / 2, z];
470
+ };
471
+ const st = '#4d5666', W = (p) => P(...p);
472
+ let mo = '';
473
+ for (const [sx, sy] of [
474
+ [-1, -1],
475
+ [1, -1],
476
+ [-1, 1],
477
+ [1, 1],
478
+ ])
479
+ mo += line(W(leg(sx, sy, 0)), W(leg(sx, sy, top)), st, 2.2);
480
+ const zs = [0, 0.75, 1.5, 2.25, 3.0, 3.7, top];
481
+ for (let i = 0; i < zs.length - 1; i++) {
482
+ mo += line(W(leg(-1, 1, zs[i])), W(leg(1, 1, zs[i + 1])), st, 1.2) + line(W(leg(1, 1, zs[i])), W(leg(-1, 1, zs[i + 1])), st, 1.2);
483
+ mo += line(W(leg(1, -1, zs[i])), W(leg(1, 1, zs[i + 1])), st, 1.2) + line(W(leg(1, 1, zs[i])), W(leg(1, -1, zs[i + 1])), st, 1.2);
484
+ mo +=
485
+ line(W(leg(-1, 1, zs[i + 1])), W(leg(1, 1, zs[i + 1])), st, 1.4) + line(W(leg(1, -1, zs[i + 1])), W(leg(1, 1, zs[i + 1])), st, 1.4);
486
+ }
487
+ for (const [dx, dy] of [
488
+ [0, 0.17],
489
+ [0.17, 0],
490
+ [0.12, 0.12],
491
+ ])
492
+ mo += box(c0 + dx - 0.05, c1 + dy - 0.05, 3.55, 0.1, 0.1, 0.65, '#eef0f2', '#dfe3e8', '#b9c0ca', '#5b636f');
493
+ const dc = P(c0 + 0.15, c1 + 0.35, 2.7);
494
+ mo += `<ellipse cx="${f(dc[0] + 3)}" cy="${f(dc[1])}" rx="7" ry="10" fill="#c9ced6" stroke="#5b636f" stroke-width="1"/><ellipse cx="${f(dc[0] + 4)}" cy="${f(dc[1])}" rx="2" ry="3" fill="#5b636f"/>`;
495
+ const tp = W(leg(0, 0, top));
496
+ mo += line(tp, [tp[0], tp[1] - 14], st, 1.6) + `<circle cx="${f(tp[0])}" cy="${f(tp[1] - 15)}" r="3" fill="#D63B2F"/>`;
497
+ o = `<ellipse cx="${f(P(c0, c1)[0] + 6)}" cy="${f(P(c0, c1)[1] + 2)}" rx="34" ry="12" fill="${K.ao}"/>` + mo + o;
498
+ return {
499
+ svg: o,
500
+ ant: [c0, c1, top + 0.45],
501
+ own: true,
502
+ pts: [...cube(x + 0.2, y + 1.3, 0, 2.0, 1.7, 1.12), leg(-1, -1, 0), leg(1, 1, 0), leg(1, -1, 0), [c0, c1, top + 0.45]],
503
+ };
504
+ }
505
+ function plain(x, y) {
506
+ const m = M.plain, w = 3, d = 3, h = 1.6, Y = y + d, X = x + w;
507
+ let o = ao(x, y, w, d) + mbox(x, y, 0, w, d, h, m);
508
+ o += coursesL(Y, x, X, 0, h, 0.4, m.tex) + coursesR(X, y, Y, 0, h, 0.4, m.texR);
509
+ o += door2(m, Y, x + 1.15, x + 1.85, 0.95) + quadL(Y, x + 1.05, x + 1.95, 0.95, 1.02, '#9a9488');
510
+ o += winL(m, Y, x + 0.3, x + 0.9, 0.35, 0.85, true, 2, false) + winL(m, Y, x + 2.1, x + 2.7, 0.35, 0.85, false, 2, false);
511
+ o +=
512
+ winL(m, Y, x + 0.3, x + 0.9, 1.05, 1.42, false, 2, false) +
513
+ winL(m, Y, x + 1.2, x + 1.8, 1.05, 1.42, true, 2, false) +
514
+ winL(m, Y, x + 2.1, x + 2.7, 1.05, 1.42, false, 2, false);
515
+ for (let i = 0; i < 3; i++) {
516
+ const b = y + 0.3 + i * 0.9;
517
+ o += winR(m, X, b, b + 0.55, 0.35, 0.85, i === 1, 2, false) + winR(m, X, b, b + 0.55, 1.05, 1.42, i === 2, 2, false);
518
+ }
519
+ o += roofWell(x, y, w, d, h + 0.1, m);
520
+ o += hvac(x + 1.6, y + 0.5, h + 0.02, 0.7, 0.6, 0.3);
521
+ return { svg: o, ant: [x + 2.4, y + 2.3, h + 0.02], pts: cube(x, y, 0, w, d, h + 0.35) };
522
+ }
523
+ export const antenna = ([x, y, z]) => {
524
+ const b = P(x, y, z), t = P(x, y, z + 0.8), c = '#3b4252';
525
+ let o = `<ellipse cx="${f(b[0])}" cy="${f(b[1])}" rx="4" ry="2" fill="${c}"/>` + line(b, t, c, 2);
526
+ o += line(P(x - 0.16, y, z + 0.6), P(x + 0.16, y, z + 0.6), c, 1.6) + line(P(x, y - 0.16, z + 0.45), P(x, y + 0.16, z + 0.45), c, 1.6);
527
+ return o + `<circle cx="${f(t[0])}" cy="${f(t[1])}" r="3.4" fill="#ffffff" stroke="#3B4252" stroke-width="2"/>`;
528
+ };
529
+ const BUILD = {
530
+ person: house,
531
+ phone: house,
532
+ shop,
533
+ desk: (x, y) => office(x, y, 2),
534
+ team: (x, y) => office(x, y, 3),
535
+ truck: depot,
536
+ queue: shed,
537
+ server: (x, y) => hall(x, y, M.slate, '#6f95e3'),
538
+ cloud: (x, y) => hall(x, y, M.pale, '#3d7be0'),
539
+ cabinet: archive,
540
+ gear: works,
541
+ tower: mast,
542
+ };
543
+ function tree(x, y, s = 1, kind = 0) {
544
+ const [gx, gy] = P(x, y, 0);
545
+ let o = `<ellipse cx="${f(gx + 6 * s)}" cy="${f(gy + 2)}" rx="${f(13 * s)}" ry="${f(6 * s)}" fill="${K.ao}"/>`;
546
+ o += box(x - 0.06, y - 0.06, 0, 0.12, 0.12, 0.4 * s, K.trunk, K.trunk, '#574837');
547
+ const [cx, cy] = P(x, y, 0.4 * s);
548
+ if (kind === 0) {
549
+ const r = 15 * s;
550
+ o += `<circle cx="${f(cx)}" cy="${f(cy - r + 4)}" r="${f(r)}" fill="${K.leafD}"/>`;
551
+ o += `<circle cx="${f(cx - r * 0.2)}" cy="${f(cy - r + 4 - r * 0.2)}" r="${f(r * 0.78)}" fill="${K.leaf}"/>`;
552
+ }
553
+ else {
554
+ const h = 44 * s, w = 14 * s;
555
+ o += poly([
556
+ [cx, cy - h],
557
+ [cx + w, cy + 2],
558
+ [cx, cy + 6 * s],
559
+ ], K.leafD);
560
+ o += poly([
561
+ [cx, cy - h],
562
+ [cx, cy + 6 * s],
563
+ [cx - w, cy + 2],
564
+ ], K.leaf);
565
+ }
566
+ return o;
567
+ }
568
+ const shrub = (x, y, r) => {
569
+ const [gx, gy] = P(x, y, 0), R = r * U;
570
+ return `<ellipse cx="${f(gx + 3)}" cy="${f(gy + 1)}" rx="${f(R * 1.15)}" ry="${f(R * 0.5)}" fill="${K.ao}"/><circle cx="${f(gx)}" cy="${f(gy - R * 0.62)}" r="${f(R * 0.72)}" fill="${K.leafD}"/><circle cx="${f(gx - R * 0.16)}" cy="${f(gy - R * 0.74)}" r="${f(R * 0.52)}" fill="${K.leaf}"/>`;
571
+ };
572
+ export function hull(points) {
573
+ const p = points.slice().sort((a, b) => a[0] - b[0] || a[1] - b[1]);
574
+ const cross = (o, a, b) => (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0]);
575
+ const lo = [], up = [];
576
+ for (const q of p) {
577
+ while (lo.length >= 2 && cross(lo[lo.length - 2], lo[lo.length - 1], q) <= 0)
578
+ lo.pop();
579
+ lo.push(q);
580
+ }
581
+ for (const q of p.slice().reverse()) {
582
+ while (up.length >= 2 && cross(up[up.length - 2], up[up.length - 1], q) <= 0)
583
+ up.pop();
584
+ up.push(q);
585
+ }
586
+ return lo.slice(0, -1).concat(up.slice(0, -1));
587
+ }
588
+ export function street(looks, withAntenna, pitch, step = 0) {
589
+ const n = looks.length, X0 = -40, X1 = (n - 1) * pitch + 44;
590
+ const bends = [];
591
+ for (let i = 0, x = 0, y = 0; step && i < n; i++) {
592
+ bends.push([x, y + 7]);
593
+ if (i % 2)
594
+ x += step;
595
+ else
596
+ y += step;
597
+ }
598
+ const stack = step > 0, xs = looks.map((_, i) => (stack ? bends[i][0] - 5.4 : i * pitch)), ys = looks.map((_, i) => (stack ? bends[i][1] - 7 : 0));
599
+ let g = '';
600
+ const objs = [], lots = [];
601
+ if (stack) {
602
+ const e = bends[n - 1], roads = [[e[0], e[0] + (n % 2 ? 0 : 40), e[1], e[1] + (n % 2 ? 40 : 0)]];
603
+ bends.forEach(([x, y], i) => {
604
+ const [u, v] = bends[i + 1] ?? [x, y];
605
+ if (i < n - 1)
606
+ roads.push([x, u, y, v]);
607
+ if (!i || i % 2)
608
+ roads.push([xs[i] - 2, x, y, y]);
609
+ });
610
+ const lo = Math.min(...xs) - 40, hi = Math.max(...xs) + 44;
611
+ g = quadT(lo, hi, -40, Math.max(...ys) + 60, 0, K.lawn);
612
+ for (const [h, fill] of [
613
+ [1.6, K.walk],
614
+ [1.05, K.curb],
615
+ [1, K.road],
616
+ ])
617
+ for (const [x0, x1, y0, y1] of roads)
618
+ g += quadT(x0 - h, x1 + h, y0 - h, y1 + h, 0, fill);
619
+ for (const [x0, x1, y0, y1] of roads)
620
+ for (let u = 1.6; u < Math.max(x1 - x0, y1 - y0) - 1.6; u += 1.2) {
621
+ const v = Math.min(u + 0.6, Math.max(x1 - x0, y1 - y0) - 1.6);
622
+ g += y0 === y1 ? quadT(x0 + u, x0 + v, y0 - 0.03, y0 + 0.03, 0, K.dash) : quadT(x0 - 0.03, x0 + 0.03, y0 + u, y0 + v, 0, K.dash);
623
+ }
624
+ const clear = (x, y) => roads.every(([x0, x1, y0, y1]) => x < x0 - 2.5 || x > x1 + 2.5 || y < y0 - 2.5 || y > y1 + 2.5) &&
625
+ xs.every((lx, i) => x < lx - 1.4 || x > lx + 4.4 || y < ys[i] + BY - 1.4 || y > ys[i] + FRONT + 1.4);
626
+ xs.forEach((x, i) => {
627
+ for (const [dx, dy] of [
628
+ [-4.6, -1.6],
629
+ [7.4, -1.6],
630
+ [4.6, 4],
631
+ [-3, 9],
632
+ ])
633
+ if (clear(x + dx, ys[i] + dy))
634
+ objs.push([x + dx + ys[i] + dy, tree(x + dx, ys[i] + dy)]);
635
+ });
636
+ }
637
+ else {
638
+ g = quadT(X0, X1, -40, FRONT - 0.15, 0, K.lawn) + quadT(X0, X1, WALK1, 60, 0, K.lawn);
639
+ for (const x of xs)
640
+ for (const [a, b, c, d] of [
641
+ [-7.5, -2.5, 10.5, 15],
642
+ [3, 8, 10.8, 14.6],
643
+ [-6, -1.5, -6, 0.6],
644
+ ])
645
+ g += quadT(x + a, x + b, c, d, 0, K.yard);
646
+ }
647
+ xs.forEach((x, i) => (g += quadT(x - 0.3, x + 3.3, FRONT - 0.15 + ys[i], WALK0 + ys[i], 0, K.yard)));
648
+ const seamX = (x0, x1, y0, y1) => {
649
+ let o = '';
650
+ for (let x = Math.ceil(x0 * 2) / 2; x < x1; x += 0.5)
651
+ o += line(P(x, y0), P(x, y1), K.seam, 1);
652
+ return o;
653
+ };
654
+ if (!stack) {
655
+ g += quadT(X0, X1, WALK0, ROAD0, 0, K.walk) + seamX(X0, X1, WALK0, ROAD0);
656
+ g += quadT(X0, X1, ROAD1, WALK1, 0, K.walk) + seamX(X0, X1, ROAD1, WALK1);
657
+ g += quadT(X0, X1, ROAD0, ROAD1, 0, K.road);
658
+ g += quadT(X0, X1, ROAD0, ROAD0 + 0.05, 0, K.curb) + quadT(X0, X1, ROAD1 - 0.05, ROAD1, 0, K.curb);
659
+ for (let x = X0; x < X1; x += 1.2)
660
+ g += quadT(x, x + 0.6, 7.18, 7.24, 0, K.dash);
661
+ }
662
+ xs.forEach((x, i) => (g += quadT(x + 1.05, x + 1.95, FRONT + ys[i], WALK0 + ys[i], 0, K.walk) + seamX(x + 1.05, x + 1.95, FRONT + ys[i], WALK0 + ys[i])));
663
+ looks.forEach((look, i) => {
664
+ const x = xs[i], y = ys[i] + BY, b = ((look && BUILD[look]) || plain)(x, y), tip = [b.ant[0], b.ant[1], b.ant[2] + (b.own ? 0 : 0.8)];
665
+ if (withAntenna[i] && !b.own)
666
+ b.svg += antenna(b.ant);
667
+ const hl = hull((withAntenna[i] ? [...b.pts, tip] : b.pts).map((p) => P(...p))).map(([u, v]) => [f(u), f(v)]);
668
+ const top = hl.reduce((q, p) => (p[1] < q[1] ? p : q));
669
+ const v = P(x, y, 1.5);
670
+ const [rx, ry] = P(...tip);
671
+ lots.push({
672
+ x,
673
+ y: ys[i],
674
+ hull: hl,
675
+ tag: [f(v[0]), f(top[1])],
676
+ door: x + 1.5,
677
+ depot: b.depot ?? null,
678
+ roof: withAntenna[i] ? [f(rx), f(ry)] : null,
679
+ });
680
+ objs.push([x + 1.5 + y + 1.5, `<g class="bld" id="b-${i}">${b.svg}</g>`]);
681
+ });
682
+ for (const x of stack ? [] : xs) {
683
+ for (const [dx, y, s, k] of [
684
+ [-4.6, -1.6, 1, 0],
685
+ [7.4, -1.6, 1, 0],
686
+ [4.6, 13.2, 1, 0],
687
+ ])
688
+ objs.push([x + dx + y, tree(x + dx, y, s, k)]);
689
+ for (const [dx, r] of [
690
+ [-4.2, 0.34],
691
+ [4.2, 0.36],
692
+ ])
693
+ objs.push([x + dx + 9.2, shrub(x + dx, 9.15, r)]);
694
+ }
695
+ const pads = xs
696
+ .map((x, i) => `<g class="pad" id="pad-${i}">${quadT(x - 0.55, x + 3.55, BY - 0.55 + ys[i], BY + 3.5 + ys[i], 0, '#FBFAF3')}</g>`)
697
+ .join('');
698
+ return {
699
+ svg: g +
700
+ pads +
701
+ objs
702
+ .sort((p, q) => p[0] - q[0])
703
+ .map((o) => o[1])
704
+ .join(''),
705
+ lots,
706
+ bends,
707
+ };
708
+ }
709
+ export const LANE = 6.6, TD = 0.78, BAR_Y = 5.7, GZ = 0.8, GL = 1.2;
710
+ const BH = 0.46;
711
+ export const BED_ROWS = [LANE - 0.19, LANE + 0.19];
712
+ export const INK = { ink: '#141A22', data: '#2456D6', fail: '#B8322A', muted: '#646C79' };
713
+ const BARREL = { side: '#D9822B', shade: '#B4661D', top: '#EDAA5E', rim: '#6E3E10' };
714
+ const at = (p, inner, id = '') => `<g${id ? ` id="${id}"` : ''} transform="translate(${f(p[0])} ${f(p[1])})">${inner}</g>`;
715
+ function barrel() {
716
+ const BR = 0.17, rx = BR * Math.SQRT2 * C, ry = BR * Math.SQRT2 * S, h = BH * U, k = 0.35, sk = Math.sqrt(1 - k * k);
717
+ const T = BARREL;
718
+ let o = `<path d="M${f(-rx)},${f(-h)} L${f(-rx)},0 A${f(rx)} ${f(ry)} 0 0 0 ${f(rx)},0 L${f(rx)},${f(-h)} Z" fill="${T.side}"/>`;
719
+ o += `<path d="M${f(k * rx)},${f(-h + ry * sk)} L${f(k * rx)},${f(ry * sk)} A${f(rx)} ${f(ry)} 0 0 0 ${f(rx)},0 L${f(rx)},${f(-h)} A${f(rx)} ${f(ry)} 0 0 1 ${f(k * rx)},${f(-h + ry * sk)} Z" fill="${T.shade}"/>`;
720
+ for (const q of [0.3, 0.68])
721
+ o += `<path d="M${f(-rx)},${f(-h * q)} A${f(rx)} ${f(ry)} 0 0 0 ${f(rx)},${f(-h * q)}" fill="none" stroke="${T.rim}" stroke-opacity=".7" stroke-width="1"/>`;
722
+ o += `<path d="M${f(-rx)},${f(-h)} L${f(-rx)},0 A${f(rx)} ${f(ry)} 0 0 0 ${f(rx)},0 L${f(rx)},${f(-h)}" fill="none" stroke="${T.rim}" stroke-opacity=".8" stroke-width="1"/>`;
723
+ o += `<ellipse cx="0" cy="${f(-h)}" rx="${f(rx)}" ry="${f(ry)}" fill="${T.top}" stroke="${T.rim}" stroke-width="1"/>`;
724
+ o += `<ellipse cx="0" cy="${f(-h)}" rx="${f(rx * 0.74)}" ry="${f(ry * 0.74)}" fill="none" stroke="${T.rim}" stroke-opacity=".55" stroke-width=".8"/>`;
725
+ return o + `<ellipse cx="${f(rx * 0.38)}" cy="${f(-h - ry * 0.1)}" rx="1.6" ry="1" fill="${T.rim}"/>`;
726
+ }
727
+ function carton() {
728
+ const w = 0.48, d = 0.48, h = 0.38, x = -w / 2, y = -d / 2;
729
+ let o = box(x, y, 0, w, d, h, '#e3c48f', '#c99a5b', '#ad8046', '#6b4a22');
730
+ o +=
731
+ quadT(x + w / 2 - 0.035, x + w / 2 + 0.035, y, y + d, h, '#a07a45') +
732
+ quadL(y + d, x + w / 2 - 0.035, x + w / 2 + 0.035, h - 0.12, h, '#a07a45');
733
+ o += quadL(y + d, x + 0.06, x + 0.2, 0.08, 0.16, '#f4ecdc') + quadL(y + d, x + 0.06, x + 0.17, 0.2, 0.23, '#f4ecdc');
734
+ return o;
735
+ }
736
+ function notes() {
737
+ const w = 0.44, d = 0.28, h = 0.16, x = -w / 2, y = -d / 2;
738
+ let o = box(x, y, 0, w, d, h, '#cfe3c9', '#7fb07a', '#5f9160', '#2f5a33');
739
+ o += quadL(y + d, -0.04, 0.04, 0, h, '#f4f6fb') + quadT(-0.04, 0.04, y, y + d, h, '#f4f6fb');
740
+ for (const k of [0.05, 0.1])
741
+ o += quadL(y + d, x + 0.02, x + w - 0.02, k, k + 0.012, '#5f9160');
742
+ return o;
743
+ }
744
+ export const UNIT = { barrel: barrel(), box: carton(), notes: notes() };
745
+ export function truckArt(depot) {
746
+ const ty = LANE - TD / 2, TY = ty + TD, bedL = 1.78, bedX = 0.68, m = M.green;
747
+ const steel = ['#9aa2ae', '#7d8592', '#646c79', '#2a3142'];
748
+ const rail = (x, y, w, d, z, h) => box(x, y, z, w, d, h, steel[0], steel[1], steel[2], steel[3]);
749
+ const wheelAt = (wx) => poly(Array.from({ length: 20 }, (_, i) => {
750
+ const a = (i / 20) * Math.PI * 2;
751
+ return P(wx + 0.17 * Math.cos(a), TY + 0.01, 0.2 + 0.17 * Math.sin(a));
752
+ }), '#1b2130') +
753
+ poly(Array.from({ length: 14 }, (_, i) => {
754
+ const a = (i / 14) * Math.PI * 2;
755
+ return P(wx + 0.07 * Math.cos(a), TY + 0.02, 0.2 + 0.07 * Math.sin(a));
756
+ }), '#8b9099');
757
+ let tBack = quadT(-0.05, 2.6, ty, TY + 0.25, 0, K.ao) + box(0, ty, 0.2, 2.5, TD, 0.16, '#3a4458', '#2a3142', '#222838', '#11151c');
758
+ tBack +=
759
+ box(0, ty, 0.36, 0.64, TD, 0.72, '#a9c2a4', m.l, m.r, m.e) +
760
+ box(0.06, ty + 0.06, 1.08, 0.54, TD - 0.12, 0.05, '#c3d2bf', '#8fac8a', '#6f8c6b');
761
+ tBack +=
762
+ quadL(TY, 0.1, 0.52, 0.72, 0.98, K.glass) +
763
+ quadR(0.64, ty + 0.08, TY - 0.08, 0.5, 0.98, '#587553') +
764
+ quadL(TY, 0.08, 0.58, 0.42, 0.48, m.cap) +
765
+ quadL(TY, 0.04, 0.14, 0.52, 0.6, K.light);
766
+ tBack +=
767
+ rail(bedX, ty, bedL, 0.05, 0.43, 0.36) +
768
+ box(bedX, ty, 0.36, bedL, TD, 0.07, '#8b929d', '#6b7280', '#5a6170', '#2a3142') +
769
+ rail(bedX, ty, 0.07, TD, 0.43, 0.5);
770
+ const tailX = bedX + bedL - 0.05;
771
+ let tFront = rail(bedX, TY - 0.05, bedL, 0.05, 0.43, 0.13) + rail(tailX, ty, 0.05, TD, 0.43, 0.2);
772
+ tFront +=
773
+ quadR(tailX + 0.05, ty + 0.05, ty + 0.15, 0.48, 0.56, INK.fail) + quadR(tailX + 0.05, TY - 0.15, TY - 0.05, 0.48, 0.56, INK.fail);
774
+ tFront += [0.36, 1.64, 2.14].map(wheelAt).join('');
775
+ const rBed = 0.04, rCab = 1.86;
776
+ let rBack = quadT(-0.05, 2.6, ty, TY + 0.25, 0, K.ao) + box(0, ty, 0.2, 2.5, TD, 0.16, '#3a4458', '#2a3142', '#222838', '#11151c');
777
+ rBack +=
778
+ rail(rBed, ty, bedL, 0.05, 0.43, 0.36) +
779
+ box(rBed, ty, 0.36, bedL, TD, 0.07, '#8b929d', '#6b7280', '#5a6170', '#2a3142') +
780
+ rail(rBed, ty, 0.05, TD, 0.43, 0.2);
781
+ let rFront = rail(rBed, TY - 0.05, bedL, 0.05, 0.43, 0.13) + rail(rBed + bedL - 0.07, ty, 0.07, TD, 0.43, 0.5);
782
+ rFront +=
783
+ box(rCab, ty, 0.36, 0.64, TD, 0.72, '#a9c2a4', m.l, m.r, m.e) +
784
+ box(rCab + 0.04, ty + 0.06, 1.08, 0.54, TD - 0.12, 0.05, '#c3d2bf', '#8fac8a', '#6f8c6b');
785
+ rFront +=
786
+ quadR(rCab + 0.64, ty + 0.1, TY - 0.1, 0.72, 1.0, K.glassR) +
787
+ quadL(TY, rCab + 0.12, rCab + 0.54, 0.72, 0.98, K.glass) +
788
+ quadL(TY, rCab + 0.08, rCab + 0.58, 0.42, 0.48, m.cap) +
789
+ quadL(TY, rCab + 0.5, rCab + 0.6, 0.52, 0.6, K.light);
790
+ rFront += [0.36, 0.86, 2.14].map(wheelAt).join('');
791
+ const slotsL = [0.27, 0.69, 1.11, 1.53].map((d) => bedX + d + 0.06), slotsR = [0.27, 0.69, 1.11, 1.53].map((d) => rBed + d - 0.04);
792
+ const loadL = BED_ROWS.flatMap((y, r) => slotsL.map((x, i) => at(P(x, y, 0.43), '', 'tl' + (r * 4 + i)))).join(''), loadR = BED_ROWS.flatMap((y, r) => slotsR.map((x, i) => at(P(x, y, 0.43), '', 'tr' + (r * 4 + i)))).join('');
793
+ const outDoor = hull([depot.x + 0.2, depot.x + 1.9].flatMap((x) => [depot.y, depot.y + 6].flatMap((y) => [0, 2.5].map((z) => P(x, y, z)))))
794
+ .map((p) => p.map((v) => f(v)).join(','))
795
+ .join(' ');
796
+ const wheelY = (wy) => poly(Array.from({ length: 20 }, (_, i) => {
797
+ const a = (i / 20) * Math.PI * 2;
798
+ return P(TD + 0.01, wy + 0.17 * Math.cos(a), 0.2 + 0.17 * Math.sin(a));
799
+ }), '#1b2130') +
800
+ poly(Array.from({ length: 14 }, (_, i) => {
801
+ const a = (i / 14) * Math.PI * 2;
802
+ return P(TD + 0.02, wy + 0.07 * Math.cos(a), 0.2 + 0.07 * Math.sin(a));
803
+ }), '#8b9099');
804
+ const chassis = quadT(-0.05, TD + 0.25, -0.05, 2.6, 0, K.ao) + box(0, 0, 0.2, TD, 2.5, 0.16, '#3a4458', '#2a3142', '#222838', '#11151c');
805
+ const yBack = chassis +
806
+ rail(0, 0.04, 0.05, 1.78, 0.43, 0.36) +
807
+ box(0, 0.04, 0.36, TD, 1.78, 0.07, '#8b929d', '#6b7280', '#5a6170', '#2a3142') +
808
+ rail(0, 0.04, TD, 0.05, 0.43, 0.2);
809
+ let yFront = rail(TD - 0.05, 0.04, 0.05, 1.78, 0.43, 0.13) + rail(0, 1.75, TD, 0.07, 0.43, 0.5);
810
+ yFront +=
811
+ box(0, 1.86, 0.36, TD, 0.64, 0.72, '#a9c2a4', m.l, m.r, m.e) +
812
+ box(0.06, 1.92, 1.08, TD - 0.12, 0.52, 0.05, '#c3d2bf', '#8fac8a', '#6f8c6b');
813
+ yFront +=
814
+ quadL(2.5, 0.1, TD - 0.1, 0.72, 1.0, K.glass) +
815
+ quadR(TD, 1.95, 2.4, 0.72, 0.98, K.glassR) +
816
+ quadL(2.5, 0.08, TD - 0.08, 0.42, 0.48, m.cap);
817
+ yFront += quadL(2.5, 0.05, 0.16, 0.52, 0.6, K.light) + quadL(2.5, TD - 0.16, TD - 0.05, 0.52, 0.6, K.light);
818
+ yFront += [0.36, 0.86, 2.14].map(wheelY).join('');
819
+ let uBack = chassis + box(0, 0, 0.36, TD, 0.64, 0.72, '#a9c2a4', m.l, m.r, m.e) + quadR(TD, 0.1, 0.5, 0.72, 0.98, K.glassR);
820
+ uBack +=
821
+ rail(0, 0.68, 0.05, 1.78, 0.43, 0.36) +
822
+ box(0, 0.68, 0.36, TD, 1.78, 0.07, '#8b929d', '#6b7280', '#5a6170', '#2a3142') +
823
+ rail(0, 0.68, TD, 0.07, 0.43, 0.5);
824
+ let uFront = rail(TD - 0.05, 0.68, 0.05, 1.78, 0.43, 0.13) + rail(0, 2.41, TD, 0.05, 0.43, 0.2);
825
+ uFront += quadL(2.46, 0.05, 0.16, 0.46, 0.56, INK.fail) + quadL(2.46, TD - 0.16, TD - 0.05, 0.46, 0.56, INK.fail);
826
+ uFront += [0.36, 1.64, 2.14].map(wheelY).join('');
827
+ const loadY = (k, y0) => [0, 1]
828
+ .flatMap((r) => [0.27, 0.69, 1.11, 1.53].map((d, i) => at(P(TD / 2 - 0.19 + 0.38 * r, y0 + d, 0.43), '', k + (r * 4 + i))))
829
+ .join('');
830
+ return {
831
+ door: `<g id="depotDoor">${quadL(depot.y + 0.002, depot.x + 0.3, depot.x + 1.8, 0, 1.03, '#1d222b')}<g id="doorPanel"></g></g>`,
832
+ yard: `<clipPath id="outDoor"><polygon points="${outDoor}"/></clipPath><g clip-path="url(#outDoor)"><g id="truckY">${yBack + yFront}</g></g>`,
833
+ road: `<g id="truck" display="none"><g>${tBack}</g><g>${loadL}</g><g>${tFront}</g></g><g id="truckR" display="none"><g>${rBack}</g><g>${loadR}</g><g>${rFront}</g></g><g id="truckD" display="none">${yBack + loadY('td', 0.1) + yFront}</g><g id="truckU" display="none">${uBack + loadY('tu', 0.74) + uFront}</g>`,
834
+ slots: { L: slotsL, R: slotsR },
835
+ };
836
+ }
837
+ const screens = `<g class="scr" data-s="idle"><path d="M-6,-5 h12 M-6,0 h9 M-6,5 h7" stroke="${INK.ink}" stroke-opacity=".4" stroke-width="1.6" stroke-linecap="round"/></g>
838
+ <g class="scr" data-s="tx" display="none"><circle r="8" fill="${INK.data}"/><path d="M0,4V-4M-3.5,-.5L0,-4L3.5,-.5" stroke="#fff" stroke-width="1.8" fill="none" stroke-linecap="round" stroke-linejoin="round"/></g>
839
+ <g class="scr" data-s="in" display="none"><rect x="-7" y="-9" width="14" height="9" rx="2" fill="${INK.data}"/><path d="M-6,4 h12 M-6,8 h8" stroke="${INK.ink}" stroke-opacity=".4" stroke-width="1.6" stroke-linecap="round"/></g>`;
840
+ function phone(w, h) {
841
+ let o = `<rect x="${-w / 2}" y="${-h}" width="${w}" height="${h}" rx="${f(w * 0.2)}" fill="${INK.ink}"/>`;
842
+ o += `<rect x="${f(-w / 2 + w * 0.09)}" y="${f(-h + h * 0.12)}" width="${f(w * 0.82)}" height="${f(h * 0.76)}" rx="${f(w * 0.08)}" fill="#F4F5F0"/>`;
843
+ o += `<rect x="${f(-w * 0.12)}" y="${f(-h + h * 0.045)}" width="${f(w * 0.24)}" height="${f(h * 0.03)}" rx="1" fill="#3b4252"/>`;
844
+ return `${o}<g transform="translate(0 ${f(-h / 2)})">${screens}</g>`;
845
+ }
846
+ export const PHONE = { svg: phone(26, 46), w: 26, h: 46 };
847
+ export function gateArt(g) {
848
+ const post = (x, h) => box(x - 0.09, g.y - 0.09, 0, 0.18, 0.18, h, '#5a6372', '#434b5a', '#2e3440', '#14181e');
849
+ const b = P(g.px, g.y);
850
+ return `<g class="gt"><ellipse cx="${f(b[0] + 4)}" cy="${f(b[1] + 1)}" rx="12" ry="5" fill="${K.ao}"/>
851
+ ${post(g.ex, GZ - 0.1)}${box(g.ex - 0.13, g.y - 0.13, GZ - 0.1, 0.26, 0.26, 0.05, '#2e3440', '#2e3440', '#1d222b')}
852
+ ${post(g.px, GZ + 0.12)}${box(g.px - 0.32, g.y - 0.11, GZ - 0.14, 0.24, 0.22, 0.22, '#5a6372', '#434b5a', '#2e3440', '#14181e')}
853
+ <line class="arm" stroke-width="5" stroke-linecap="round"/><line class="armW" stroke="#ffffff" stroke-width="5" stroke-dasharray="6 6"/><circle class="hub" r="3.5" fill="#2e3440"/></g>`;
854
+ }