├── .gitignore
├── .prettierrc
├── csv.js
├── display.js
├── dist
├── index.js
├── index_cube.js
├── index_elevate.js
├── index_full.js
├── index_interactive.js
├── index_interactive_print.js
├── index_interactive_print_b.js
├── index_interactive_print_c.js
├── index_radial.js
├── index_sheet.js
└── index_symmetry.js
├── index.html
├── index.js
├── index_building.js
├── index_cube.js
├── index_elevate.js
├── index_full.js
├── index_interactive.js
├── index_interactive_print.js
├── index_interactive_print_b.js
├── index_interactive_print_c.js
├── index_radial.js
├── index_sheet.js
├── index_symmetry.js
├── package-lock.json
├── package.json
├── presets_interactive.js
├── rollup.config.js
├── simple.js
└── ui.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "printWidth": 100,
3 | "singleQuote": true
4 | }
5 |
--------------------------------------------------------------------------------
/csv.js:
--------------------------------------------------------------------------------
1 | export function generateCSV(front, left, top, scale, pal_size) {
2 | const c_front = front.map(convert);
3 | const c_left = left.map(convert).map(rot);
4 | const c_top = top.map(convert).map(rot).map(rot);
5 | const array = [...c_front, ...c_left, ...c_top];
6 |
7 | const merged = mergeEdges(array);
8 | return merged.map((a) => [
9 | a.x1 * scale,
10 | a.y1 * scale,
11 | a.z1 * scale,
12 | a.w * scale,
13 | a.h * scale,
14 | a.d * scale,
15 | a.col % pal_size,
16 | ]);
17 | }
18 |
19 | function convert(box) {
20 | let nbox = { ...box, d: box.z1 * 4 };
21 | nbox.z1 = 0;
22 | return nbox;
23 | }
24 |
25 | function rot(box) {
26 | return {
27 | x1: -box.z1,
28 | y1: box.x1,
29 | z1: -box.y1,
30 | w: -box.d,
31 | h: box.w,
32 | d: -box.h,
33 | col: box.col,
34 | };
35 | }
36 |
37 | function mergeEdges(boxes) {
38 | const edge_boxes = boxes.filter(is_edge);
39 | console.log('edges', edge_boxes);
40 |
41 | const grouped = group_neighbors(edge_boxes);
42 | console.log('grouped', grouped);
43 |
44 | const merged = grouped.map(mergeArr);
45 | console.log('merged', merged);
46 |
47 | const non_edge_boxes = boxes.filter((b) => !is_edge(b));
48 | return non_edge_boxes.concat(merged);
49 | }
50 |
51 | function is_edge(box) {
52 | return (box.x1 === 0) + (box.y1 === 0) + (box.z1 === 0) > 1; // At least to indices are 0.
53 | }
54 |
55 | function group_neighbors(boxes) {
56 | return boxes.reduce((acc, cur) => {
57 | var i = acc.findIndex((a) => neighbors(a[0], cur));
58 | if (i > -1) acc[i].push(cur);
59 | else acc.push([cur]);
60 | return acc;
61 | }, []);
62 | }
63 |
64 | function neighbors(b1, b2) {
65 | return b1.x1 === b2.x1 && b1.y1 === b2.y1 && b1.z1 === b2.z1;
66 | }
67 |
68 | function mergeArr(arr) {
69 | if (arr.length === 3) return mergeOrigo(...arr);
70 | return merge(...arr);
71 | }
72 |
73 | function merge(b1, b2) {
74 | var x1 = b1.w === b2.w ? b1.x1 : b1.x1 + Math.min(b1.w, b2.w);
75 | var y1 = b1.h === b2.h ? b1.y1 : b1.y1 + Math.min(b1.h, b2.h);
76 | var z1 = b1.d === b2.d ? b1.z1 : b1.z1 + Math.min(b1.d, b2.d);
77 |
78 | var w = b1.w === b2.w ? b1.w : Math.abs(b1.w) + Math.abs(b2.w);
79 | var h = b1.h === b2.h ? b1.h : Math.abs(b1.h) + Math.abs(b2.h);
80 | var d = b1.d === b2.d ? b1.d : Math.abs(b1.d) + Math.abs(b2.d);
81 |
82 | return { x1, y1, z1, w, h, d, col: b1.col };
83 | }
84 |
85 | function mergeOrigo(b1, b2, b3) {
86 | var xmin = Math.min(b1.w, b2.w, b3.w);
87 | var ymin = Math.min(b1.h, b2.h, b3.h);
88 | var zmin = Math.min(b1.d, b2.d, b3.d);
89 |
90 | var xmax = Math.max(b1.w, b2.w, b3.w);
91 | var ymax = Math.max(b1.h, b2.h, b3.h);
92 | var zmax = Math.max(b1.d, b2.d, b3.d);
93 |
94 | var w = -xmin + xmax;
95 | var h = -ymin + ymax;
96 | var d = -zmin + zmax;
97 |
98 | return { x1: xmin, y1: ymin, z1: zmin, w, d, h, col: b1.col };
99 | }
100 |
--------------------------------------------------------------------------------
/display.js:
--------------------------------------------------------------------------------
1 | export default function (
2 | p,
3 | box,
4 | xu,
5 | yu,
6 | zu,
7 | depth,
8 | shades,
9 | fillColors,
10 | paletteShift,
11 | strokeColor,
12 | outerStrokeWeight,
13 | innerStrokeWeight,
14 | hiddenTop,
15 | hiddenLeft,
16 | t1,
17 | t2,
18 | t3,
19 | fullOutline = false
20 | ) {
21 | const bx = box.x1 - box.x_off * depth; // X Position
22 | const by = box.y1 - box.y_off * depth; // Y Position
23 | const bw = box.w + box.x_off * depth; // Width
24 | const bh = box.h + box.y_off * depth; // Height
25 | const bd = box.z1 * depth; // Depth
26 |
27 | let cols = fillColors.slice(paletteShift).concat(fillColors.slice(0, paletteShift));
28 |
29 | p.fill(cols[box.col % cols.length]);
30 | p.noStroke();
31 |
32 | displayFront();
33 | displayLeft();
34 | displayTop();
35 |
36 | let shadeCol = p.color(strokeColor);
37 |
38 | shadeCol.setAlpha(shades[0] * 255);
39 | p.fill(shadeCol);
40 | displayFront();
41 |
42 | shadeCol.setAlpha(shades[1] * 255);
43 | p.fill(shadeCol);
44 | displayLeft();
45 |
46 | shadeCol.setAlpha(shades[2] * 255);
47 | p.fill(shadeCol);
48 | displayTop();
49 |
50 | p.noFill();
51 | p.stroke(strokeColor);
52 | p.strokeWeight(outerStrokeWeight);
53 |
54 | if (!(box.x1 === 0 && hiddenLeft) && !(box.y1 === 0 && hiddenTop)) displayInteriorFrontLine();
55 | if (!(box.x1 === 0 && hiddenLeft)) displayInteriorTopLine();
56 | if (!(box.y1 === 0 && hiddenTop)) displayInteriorLeftLine();
57 |
58 | p.strokeWeight(innerStrokeWeight);
59 | displayShape();
60 |
61 | function displayFront() {
62 | p.beginShape();
63 | p.vertex(...getPos(bx, by, bd, t1, t2, t3));
64 | p.vertex(...getPos(bx + bw, by, bd, t1, t2, t3));
65 | p.vertex(...getPos(bx + bw, by + bh, bd, t1, t2, t3));
66 | p.vertex(...getPos(bx, by + bh, bd, t1, t2, t3));
67 | p.endShape();
68 | }
69 |
70 | function displayLeft() {
71 | p.beginShape();
72 | p.vertex(...getPos(bx, by, bd, t1, t2, t3));
73 | p.vertex(...getPos(bx, by + bh, bd, t1, t2, t3));
74 | p.vertex(...getPos(bx, by + bh, 0, t1, t2, t3));
75 | p.vertex(...getPos(bx, by, 0, t1, t2, t3));
76 | p.endShape();
77 | }
78 |
79 | function displayTop() {
80 | p.beginShape();
81 | p.vertex(...getPos(bx + bw, by, bd, t1, t2, t3));
82 | p.vertex(...getPos(bx + bw, by, 0, t1, t2, t3));
83 | p.vertex(...getPos(bx, by, 0, t1, t2, t3));
84 | p.vertex(...getPos(bx, by, bd, t1, t2, t3));
85 | p.endShape();
86 | }
87 |
88 | function displayInteriorFrontLine() {
89 | p.line(...getPos(bx, by, bd, t1, t2, t3), ...getPos(bx, by, 0, t1, t2, t3));
90 | }
91 |
92 | function displayInteriorLeftLine() {
93 | p.line(...getPos(bx, by, bd, t1, t2, t3), ...getPos(bx + bw, by, bd, t1, t2, t3));
94 | }
95 |
96 | function displayInteriorTopLine() {
97 | p.line(...getPos(bx, by, bd, t1, t2, t3), ...getPos(bx, by + bh, bd, t1, t2, t3));
98 | }
99 |
100 | function displayShape() {
101 | p.beginShape();
102 | p.vertex(...getPos(bx + bw, by, 0, t1, t2, t3));
103 | p.vertex(...getPos(bx + bw, by, bd, t1, t2, t3));
104 | p.vertex(...getPos(bx + bw, by + bh, bd, t1, t2, t3));
105 | p.vertex(...getPos(bx, by + bh, bd, t1, t2, t3));
106 | p.vertex(...getPos(bx, by + bh, 0, t1, t2, t3));
107 | if (fullOutline) {
108 | p.vertex(...getPos(bx, by, 0, t1, t2, t3));
109 | p.vertex(...getPos(bx + bw, by, 0, t1, t2, t3));
110 | }
111 | p.endShape();
112 | }
113 |
114 | function getPos(x, y, z, tz, ty, tx) {
115 | const zc = tz(x * xu[0] + y * yu[0], x * xu[1] + y * yu[1]);
116 | const yc = ty(x * xu[0] + z * zu[0], x * xu[1] + z * zu[1]);
117 | const xc = tx(y * yu[0] + z * zu[0], y * yu[1] + z * zu[1]);
118 |
119 | return [zc[0] + yc[0] + xc[0], zc[1] + yc[1] + xc[1]];
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/dist/index_radial.js:
--------------------------------------------------------------------------------
1 | (function (factory) {
2 | typeof define === 'function' && define.amd ? define(factory) :
3 | factory();
4 | })((function () { 'use strict';
5 |
6 | var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
7 |
8 | function createCommonjsModule(fn, module) {
9 | return module = { exports: {} }, fn(module, module.exports), module.exports;
10 | }
11 |
12 | var seedRandom = createCommonjsModule(function (module) {
13 |
14 | var width = 256;// each RC4 output is 0 <= x < 256
15 | var chunks = 6;// at least six RC4 outputs for each double
16 | var digits = 52;// there are 52 significant digits in a double
17 | var pool = [];// pool: entropy pool starts empty
18 | var GLOBAL = typeof commonjsGlobal === 'undefined' ? window : commonjsGlobal;
19 |
20 | //
21 | // The following constants are related to IEEE 754 limits.
22 | //
23 | var startdenom = Math.pow(width, chunks),
24 | significance = Math.pow(2, digits),
25 | overflow = significance * 2,
26 | mask = width - 1;
27 |
28 |
29 | var oldRandom = Math.random;
30 |
31 | //
32 | // seedrandom()
33 | // This is the seedrandom function described above.
34 | //
35 | module.exports = function(seed, options) {
36 | if (options && options.global === true) {
37 | options.global = false;
38 | Math.random = module.exports(seed, options);
39 | options.global = true;
40 | return Math.random;
41 | }
42 | var use_entropy = (options && options.entropy) || false;
43 | var key = [];
44 |
45 | // Flatten the seed string or build one from local entropy if needed.
46 | mixkey(flatten(
47 | use_entropy ? [seed, tostring(pool)] :
48 | 0 in arguments ? seed : autoseed(), 3), key);
49 |
50 | // Use the seed to initialize an ARC4 generator.
51 | var arc4 = new ARC4(key);
52 |
53 | // Mix the randomness into accumulated entropy.
54 | mixkey(tostring(arc4.S), pool);
55 |
56 | // Override Math.random
57 |
58 | // This function returns a random double in [0, 1) that contains
59 | // randomness in every bit of the mantissa of the IEEE 754 value.
60 |
61 | return function() { // Closure to return a random double:
62 | var n = arc4.g(chunks), // Start with a numerator n < 2 ^ 48
63 | d = startdenom, // and denominator d = 2 ^ 48.
64 | x = 0; // and no 'extra last byte'.
65 | while (n < significance) { // Fill up all significant digits by
66 | n = (n + x) * width; // shifting numerator and
67 | d *= width; // denominator and generating a
68 | x = arc4.g(1); // new least-significant-byte.
69 | }
70 | while (n >= overflow) { // To avoid rounding up, before adding
71 | n /= 2; // last byte, shift everything
72 | d /= 2; // right using integer Math until
73 | x >>>= 1; // we have exactly the desired bits.
74 | }
75 | return (n + x) / d; // Form the number within [0, 1).
76 | };
77 | };
78 |
79 | module.exports.resetGlobal = function () {
80 | Math.random = oldRandom;
81 | };
82 |
83 | //
84 | // ARC4
85 | //
86 | // An ARC4 implementation. The constructor takes a key in the form of
87 | // an array of at most (width) integers that should be 0 <= x < (width).
88 | //
89 | // The g(count) method returns a pseudorandom integer that concatenates
90 | // the next (count) outputs from ARC4. Its return value is a number x
91 | // that is in the range 0 <= x < (width ^ count).
92 | //
93 | /** @constructor */
94 | function ARC4(key) {
95 | var t, keylen = key.length,
96 | me = this, i = 0, j = me.i = me.j = 0, s = me.S = [];
97 |
98 | // The empty key [] is treated as [0].
99 | if (!keylen) { key = [keylen++]; }
100 |
101 | // Set up S using the standard key scheduling algorithm.
102 | while (i < width) {
103 | s[i] = i++;
104 | }
105 | for (i = 0; i < width; i++) {
106 | s[i] = s[j = mask & (j + key[i % keylen] + (t = s[i]))];
107 | s[j] = t;
108 | }
109 |
110 | // The "g" method returns the next (count) outputs as one number.
111 | (me.g = function(count) {
112 | // Using instance members instead of closure state nearly doubles speed.
113 | var t, r = 0,
114 | i = me.i, j = me.j, s = me.S;
115 | while (count--) {
116 | t = s[i = mask & (i + 1)];
117 | r = r * width + s[mask & ((s[i] = s[j = mask & (j + t)]) + (s[j] = t))];
118 | }
119 | me.i = i; me.j = j;
120 | return r;
121 | // For robust unpredictability discard an initial batch of values.
122 | // See http://www.rsa.com/rsalabs/node.asp?id=2009
123 | })(width);
124 | }
125 |
126 | //
127 | // flatten()
128 | // Converts an object tree to nested arrays of strings.
129 | //
130 | function flatten(obj, depth) {
131 | var result = [], typ = (typeof obj)[0], prop;
132 | if (depth && typ == 'o') {
133 | for (prop in obj) {
134 | try { result.push(flatten(obj[prop], depth - 1)); } catch (e) {}
135 | }
136 | }
137 | return (result.length ? result : typ == 's' ? obj : obj + '\0');
138 | }
139 |
140 | //
141 | // mixkey()
142 | // Mixes a string seed into a key that is an array of integers, and
143 | // returns a shortened string seed that is equivalent to the result key.
144 | //
145 | function mixkey(seed, key) {
146 | var stringseed = seed + '', smear, j = 0;
147 | while (j < stringseed.length) {
148 | key[mask & j] =
149 | mask & ((smear ^= key[mask & j] * 19) + stringseed.charCodeAt(j++));
150 | }
151 | return tostring(key);
152 | }
153 |
154 | //
155 | // autoseed()
156 | // Returns an object for autoseeding, using window.crypto if available.
157 | //
158 | /** @param {Uint8Array=} seed */
159 | function autoseed(seed) {
160 | try {
161 | GLOBAL.crypto.getRandomValues(seed = new Uint8Array(width));
162 | return tostring(seed);
163 | } catch (e) {
164 | return [+new Date, GLOBAL, GLOBAL.navigator && GLOBAL.navigator.plugins,
165 | GLOBAL.screen, tostring(pool)];
166 | }
167 | }
168 |
169 | //
170 | // tostring()
171 | // Converts an array of charcodes to a string
172 | //
173 | function tostring(a) {
174 | return String.fromCharCode.apply(0, a);
175 | }
176 |
177 | //
178 | // When seedrandom.js is loaded, we immediately mix a few bits
179 | // from the built-in RNG into the entropy pool. Because we do
180 | // not want to intefere with determinstic PRNG state later,
181 | // seedrandom will not call Math.random on its own again after
182 | // initialization.
183 | //
184 | mixkey(Math.random(), pool);
185 | });
186 | seedRandom.resetGlobal;
187 |
188 | class index {
189 | constructor(
190 | width,
191 | height,
192 | {
193 | initiate_chance = 0.8,
194 | extension_chance = 0.8,
195 | vertical_chance = 0.8,
196 | horizontal_symmetry = true,
197 | vertical_symmetry = false,
198 | roundness = 0.1,
199 | solidness = 0.5,
200 | colors = [],
201 | color_mode = 'group',
202 | group_size = 0.8,
203 | simple = false,
204 | simplex = null,
205 | rate_of_change = 0.01,
206 | } = {}
207 | ) {
208 | this.xdim = Math.round(width * 2 + 11, 0);
209 | this.ydim = Math.round(height * 2 + 11, 0);
210 | this.radius_x = width;
211 | this.radius_y = height;
212 | this.chance_new = initiate_chance;
213 | this.chance_extend = extension_chance;
214 | this.chance_vertical = vertical_chance;
215 | this.colors = colors;
216 | this.color_mode = color_mode;
217 | this.group_size = group_size;
218 | this.h_symmetric = horizontal_symmetry;
219 | this.v_symmetric = vertical_symmetry;
220 | this.roundness = roundness;
221 | this.solidness = solidness;
222 | this.simple = simple;
223 | this.simplex = simplex;
224 | this.rate_of_change = rate_of_change;
225 | this.global_seed = Math.random();
226 | }
227 |
228 | generate(initial_top = null, initial_left = null, verbose = false, idx = 0, idy = 0) {
229 | this.idx = idx;
230 | this.idy = idy;
231 |
232 | this.main_color = this.get_random(this.colors, 1, 1);
233 | this.id_counter = 0;
234 |
235 | let grid = new Array(this.ydim + 1);
236 | for (var i = 0; i < grid.length; i++) {
237 | grid[i] = new Array(this.xdim + 1);
238 | for (var j = 0; j < grid[i].length; j++) {
239 | if (i == 0 || j == 0) grid[i][j] = { h: false, v: false, in: false, col: null, el: null };
240 | else if (i == 1 && initial_top != null) grid[i][j] = { ...initial_top[j], h: true };
241 | else if (j == 1 && initial_left != null) grid[i][j] = { ...initial_left[i], v: true };
242 | else if (this.h_symmetric && j > grid[i].length / 2) {
243 | grid[i][j] = deep_copy(grid[i][grid[i].length - j]);
244 | grid[i][j].v = grid[i][grid[i].length - j + 1].v;
245 | } else if (this.v_symmetric && i > grid.length / 2) {
246 | grid[i][j] = deep_copy(grid[grid.length - i][j]);
247 | grid[i][j].h = grid[grid.length - i + 1][j].h;
248 | } else {
249 | grid[i][j] = this.next_block(j, i, grid[i][j - 1], grid[i - 1][j]);
250 | }
251 | }
252 | }
253 | let rects = convert_linegrid_to_rectangles(grid);
254 | return verbose ? [rects, grid] : rects;
255 | }
256 |
257 | next_block(x, y, left, top) {
258 | const context = this;
259 |
260 | if (!left.in && !top.in) {
261 | return block_set_1(x, y);
262 | }
263 |
264 | if (left.in && !top.in) {
265 | if (left.h) return block_set_3(x, y);
266 | return block_set_2(x, y);
267 | }
268 |
269 | if (!left.in && top.in) {
270 | if (top.v) return block_set_5(x, y);
271 | return block_set_4(x, y);
272 | }
273 |
274 | if (left.in && top.in) {
275 | if (!left.h && !top.v) return block_set_6();
276 | if (left.h && !top.v) return block_set_7(x, y);
277 | if (!left.h && top.v) return block_set_8(x, y);
278 | return block_set_9(x, y);
279 | }
280 | // --- Block sets ----
281 |
282 | function block_set_1(x, y) {
283 | if (start_new_from_blank(x, y)) return new_block(x, y);
284 | return { v: false, h: false, in: false, col: null, el: null, id: null };
285 | }
286 |
287 | function block_set_2(x, y) {
288 | if (start_new_from_blank(x, y)) return new_block(x, y);
289 | return { v: true, h: false, in: false, col: null, el: null, id: null };
290 | }
291 |
292 | function block_set_3(x, y) {
293 | if (extend(x, y)) return { v: false, h: true, in: true, col: left.col, el: left.el, id: left.id };
294 | return block_set_2(x, y);
295 | }
296 |
297 | function block_set_4(x, y) {
298 | if (start_new_from_blank(x, y)) return new_block(x, y);
299 | return { v: false, h: true, in: false, col: null, el: null, id: null };
300 | }
301 |
302 | function block_set_5(x, y) {
303 | if (extend(x, y)) return { v: true, h: false, in: true, col: top.col, el: top.el, id: top.id };
304 | return block_set_4(x, y);
305 | }
306 |
307 | function block_set_6() {
308 | return { v: false, h: false, in: true, col: left.col, el: left.el, id: left.id };
309 | }
310 |
311 | function block_set_7(x, y) {
312 | if (extend(x, y)) return { v: false, h: true, in: true, col: left.col, el: left.el, id: left.id };
313 | if (start_new(x, y)) return new_block(x, y);
314 | return { v: true, h: true, in: false, col: null, el: null, id: null };
315 | }
316 |
317 | function block_set_8(x, y) {
318 | if (extend(x, y)) return { v: true, h: false, in: true, col: top.col, el: top.el, id: top.id };
319 | if (start_new(x, y)) return new_block(x, y);
320 | return { v: true, h: true, in: false, col: null, el: null, id: null };
321 | }
322 |
323 | function block_set_9(x, y) {
324 | if (vertical_dir(x, y)) return { v: true, h: false, in: true, col: top.col, el: top.el, id: top.id };
325 | return { v: false, h: true, in: true, col: left.col, el: left.el, id: left.id };
326 | }
327 |
328 | // ---- Blocks ----
329 |
330 | function new_block(nx, ny) {
331 | let col;
332 | if (context.color_mode === 'random') {
333 | col = context.get_random(context.colors, nx, ny);
334 | } else if (context.color_mode === 'main') {
335 | col = context.noise(x, y, '_main') > 0.75 ? context.get_random(context.colors, x, y) : context.main_color;
336 | } else if (context.color_mode === 'group') {
337 | let keep = context.noise(x, y, '_keep') > 0.5 ? left.col : top.col;
338 | context.main_color =
339 | context.noise(x, y, '_group') > context.group_size
340 | ? context.get_random(context.colors, x, y)
341 | : keep || context.main_color;
342 | col = context.main_color;
343 | } else {
344 | col = context.main_color;
345 | }
346 |
347 | return { v: true, h: true, in: true, col: col, el: context.noise(), id: context.id_counter++ };
348 | }
349 |
350 | // ---- Decisions ----
351 |
352 | function start_new_from_blank(x, y) {
353 | if (context.simple) return true;
354 | if (!active_position(x, y, -1 * (1 - context.roundness))) return false;
355 | return context.noise(x, y, '_blank') <= context.solidness;
356 | }
357 |
358 | function start_new(x, y) {
359 | if (context.simple) return true;
360 | if (!active_position(x, y, 0)) return false;
361 | return context.noise(x, y, '_new') <= context.chance_new;
362 | }
363 |
364 | function extend(x, y) {
365 | if (!active_position(x, y, 1 - context.roundness) && !context.simple) return false;
366 | return context.noise(x, y, '_extend') <= context.chance_extend;
367 | }
368 |
369 | function vertical_dir(x, y) {
370 | return context.noise(x, y, '_vert') <= context.chance_vertical;
371 | }
372 |
373 | function active_position(x, y, fuzzy) {
374 | let fuzziness = 1 + context.noise(x, y, '_active') * fuzzy;
375 | let xa = Math.pow(x - context.xdim / 2, 2) / Math.pow(context.radius_x * fuzziness, 2);
376 | let ya = Math.pow(y - context.ydim / 2, 2) / Math.pow(context.radius_y * fuzziness, 2);
377 | return xa + ya < 1;
378 | }
379 | }
380 |
381 | noise(nx, ny, nz = '') {
382 | if (!this.simplex) return Math.random();
383 | const rng = seedRandom('' + this.global_seed + nx + ny + nz);
384 | const n = this.simplex.noise3D(this.idx * this.rate_of_change, this.idy * this.rate_of_change, rng() * 23.4567);
385 | return (n + 1) / 2;
386 | }
387 |
388 | get_random(array, nx, ny) {
389 | return array[Math.floor(this.noise(nx, ny, '_array') * array.length)];
390 | }
391 | }
392 |
393 | function deep_copy(obj) {
394 | let nobj = [];
395 | for (var key in obj) {
396 | if (obj.hasOwnProperty(key)) {
397 | nobj[key] = obj[key];
398 | }
399 | }
400 | return nobj;
401 | }
402 |
403 | // --- Conversion ---
404 | function convert_linegrid_to_rectangles(grid) {
405 | let nw_corners = get_nw_corners(grid);
406 | extend_corners_to_rectangles(nw_corners, grid);
407 | return nw_corners;
408 | }
409 |
410 | function get_nw_corners(grid) {
411 | let nw_corners = [];
412 | for (let i = 0; i < grid.length; i++) {
413 | for (let j = 0; j < grid[i].length; j++) {
414 | let cell = grid[i][j];
415 | if (cell.h && cell.v && cell.in) nw_corners.push({ x1: j, y1: i, col: cell.col, el: cell.el, id: cell.id });
416 | }
417 | }
418 | return nw_corners;
419 | }
420 |
421 | function extend_corners_to_rectangles(corners, grid) {
422 | corners.map(c => {
423 | let accx = 1;
424 | while (c.x1 + accx < grid[c.y1].length && !grid[c.y1][c.x1 + accx].v) {
425 | accx++;
426 | }
427 | let accy = 1;
428 | while (c.y1 + accy < grid.length && !grid[c.y1 + accy][c.x1].h) {
429 | accy++;
430 | }
431 | c.w = accx;
432 | c.h = accy;
433 | return c;
434 | });
435 | }
436 |
437 | var misc = [
438 | {
439 | name: 'frozen-rose',
440 | colors: ['#29368f', '#e9697b', '#1b164d', '#f7d996'],
441 | background: '#f2e8e4',
442 | },
443 | {
444 | name: 'winter-night',
445 | colors: ['#122438', '#dd672e', '#87c7ca', '#ebebeb'],
446 | background: '#ebebeb',
447 | },
448 | {
449 | name: 'saami',
450 | colors: ['#eab700', '#e64818', '#2c6393', '#eecfca'],
451 | background: '#e7e6e4',
452 | },
453 | {
454 | name: 'knotberry1',
455 | colors: ['#20342a', '#f74713', '#686d2c', '#e9b4a6'],
456 | background: '#e5ded8',
457 | },
458 | {
459 | name: 'knotberry2',
460 | colors: ['#1d3b1a', '#eb4b11', '#e5bc00', '#f29881'],
461 | background: '#eae2d0',
462 | },
463 | {
464 | name: 'tricolor',
465 | colors: ['#ec643b', '#56b7ab', '#f8cb57', '#1f1e43'],
466 | background: '#f7f2df',
467 | },
468 | {
469 | name: 'foxshelter',
470 | colors: ['#ff3931', '#007861', '#311f27', '#bab9a4'],
471 | background: '#dddddd',
472 | },
473 | {
474 | name: 'hermes',
475 | colors: ['#253852', '#51222f', '#b53435', '#ecbb51'],
476 | background: '#eeccc2',
477 | },
478 | {
479 | name: 'olympia',
480 | colors: ['#ff3250', '#ffb33a', '#008c36', '#0085c6', '#4c4c4c'],
481 | stroke: '#0b0b0b',
482 | background: '#faf2e5',
483 | },
484 | {
485 | name: 'byrnes',
486 | colors: ['#c54514', '#dca215', '#23507f'],
487 | stroke: '#0b0b0b',
488 | background: '#e8e7d4',
489 | },
490 | {
491 | name: 'butterfly',
492 | colors: ['#f40104', '#f6c0b3', '#99673a', '#f0f1f4'],
493 | stroke: '#191e36',
494 | background: '#191e36',
495 | },
496 | {
497 | name: 'floratopia',
498 | colors: ['#bf4a2b', '#cd902a', '#4e4973', '#f5d4bc'],
499 | stroke: '#1e1a43',
500 | background: '#1e1a43',
501 | },
502 | {
503 | name: 'verena',
504 | colors: ['#f1594a', '#f5b50e', '#14a160', '#2969de', '#885fa4'],
505 | stroke: '#1a1a1a',
506 | background: '#e2e6e8',
507 | },
508 | {
509 | name: 'florida_citrus',
510 | colors: ['#ea7251', '#ebf7f0', '#02aca5'],
511 | stroke: '#050100',
512 | background: '#9ae2d3',
513 | },
514 | {
515 | name: 'lemon_citrus',
516 | colors: ['#e2d574', '#f1f4f7', '#69c5ab'],
517 | stroke: '#463231',
518 | background: '#f79eac',
519 | },
520 | {
521 | name: 'yuma_punk',
522 | colors: ['#f05e3b', '#ebdec4', '#ffdb00'],
523 | stroke: '#ebdec4',
524 | background: '#161616',
525 | },
526 | {
527 | name: 'yuma_punk2',
528 | colors: ['#f2d002', '#f7f5e1', '#ec643b'],
529 | stroke: '#19080e',
530 | background: '#f7f5e1',
531 | },
532 | {
533 | name: 'moir',
534 | colors: ['#a49f4f', '#d4501e', '#f7c558', '#ebbaa6'],
535 | stroke: '#161716',
536 | background: '#f7f4ef',
537 | },
538 | {
539 | name: 'sprague',
540 | colors: ['#ec2f28', '#f8cd28', '#1e95bb', '#fbaab3', '#fcefdf'],
541 | stroke: '#221e1f',
542 | background: '#fcefdf',
543 | },
544 | {
545 | name: 'bloomberg',
546 | colors: ['#ff5500', '#f4c145', '#144714', '#2f04fc', '#e276af'],
547 | stroke: '#000',
548 | background: '#fff3dd',
549 | },
550 | {
551 | name: 'revolucion',
552 | colors: ['#ed555d', '#fffcc9', '#41b797', '#eda126', '#7b5770'],
553 | stroke: '#fffcc9',
554 | background: '#2d1922',
555 | },
556 | {
557 | name: 'sneaker',
558 | colors: ['#e8165b', '#401e38', '#66c3b4', '#ee7724', '#584098'],
559 | stroke: '#401e38',
560 | background: '#ffffff',
561 | },
562 | {
563 | name: 'miradors',
564 | colors: ['#ff6936', '#fddc3f', '#0075ca', '#00bb70'],
565 | stroke: '#ffffff',
566 | background: '#020202',
567 | },
568 | {
569 | name: 'kaffeprat',
570 | colors: ['#BCAA8C', '#D8CDBE', '#484A42', '#746B58', '#9A8C73'],
571 | stroke: '#000',
572 | background: '#fff',
573 | },
574 | {
575 | name: 'jrmy',
576 | colors: ['#df456c', '#ea6a82', '#270b32', '#471e43'],
577 | stroke: '#270b32',
578 | background: '#ef9198',
579 | },
580 | {
581 | name: 'animo',
582 | colors: ['#f6c103', '#f6f6f6', '#d1cdc7', '#e7e6e5'],
583 | stroke: '#010001',
584 | background: '#f5f5f5',
585 | },
586 | {
587 | name: 'book',
588 | colors: ['#be1c24', '#d1a082', '#037b68', '#d8b1a5', '#1c2738', '#c95a3f'],
589 | stroke: '#0e0f27',
590 | background: '#f5b28a',
591 | },
592 | {
593 | name: 'juxtapoz',
594 | colors: ['#20357e', '#f44242', '#ffffff'],
595 | stroke: '#000000',
596 | background: '#cfc398',
597 | },
598 | {
599 | name: 'hurdles',
600 | colors: ['#e16503', '#dc9a0f', '#dfe2b4', '#66a7a6'],
601 | stroke: '#3c1c03',
602 | background: '#3c1c03',
603 | },
604 | {
605 | name: 'ludo',
606 | colors: ['#df302f', '#e5a320', '#0466b3', '#0f7963'],
607 | stroke: '#272621',
608 | background: '#dedccd',
609 | },
610 | {
611 | name: 'riff',
612 | colors: ['#e24724', '#c7c7c7', '#1f3e7c', '#d29294', '#010203'],
613 | stroke: '#010203',
614 | background: '#f2f2f2',
615 | },
616 | {
617 | name: 'san ramon',
618 | colors: ['#4f423a', '#f6a74b', '#589286', '#f8e9e2', '#2c2825'],
619 | stroke: '#2c2825',
620 | background: '#fff',
621 | },
622 | {
623 | name: 'one-dress',
624 | colors: ['#1767D2', '#FFFFFF', '#F9AB00', '#212121'],
625 | stroke: '#212121',
626 | background: '#fff',
627 | },
628 | ];
629 |
630 | var colourscafe = [
631 | {
632 | name: 'cc239',
633 | colors: ['#e3dd34', '#78496b', '#f0527f', '#a7e0e2'],
634 | background: '#e0eff0'
635 | },
636 | {
637 | name: 'cc234',
638 | colors: ['#ffce49', '#ede8dc', '#ff5736', '#ff99b4'],
639 | background: '#f7f4ed'
640 | },
641 | {
642 | name: 'cc232',
643 | colors: ['#5c5f46', '#ff7044', '#ffce39', '#66aeaa'],
644 | background: '#e9ecde'
645 | },
646 | {
647 | name: 'cc238',
648 | colors: ['#553c60', '#ffb0a0', '#ff6749', '#fbe090'],
649 | background: '#f5e9de'
650 | },
651 | {
652 | name: 'cc242',
653 | colors: ['#bbd444', '#fcd744', '#fa7b53', '#423c6f'],
654 | background: '#faf4e4'
655 | },
656 | {
657 | name: 'cc245',
658 | colors: ['#0d4a4e', '#ff947b', '#ead3a2', '#5284ab'],
659 | background: '#f6f4ed'
660 | },
661 | {
662 | name: 'cc273',
663 | colors: ['#363d4a', '#7b8a56', '#ff9369', '#f4c172'],
664 | background: '#f0efe2'
665 | }
666 | ];
667 |
668 | var ranganath = [
669 | {
670 | name: 'rag-mysore',
671 | colors: ['#ec6c26', '#613a53', '#e8ac52', '#639aa0'],
672 | background: '#d5cda1'
673 | },
674 | {
675 | name: 'rag-gol',
676 | colors: ['#d3693e', '#803528', '#f1b156', '#90a798'],
677 | background: '#f0e0a4'
678 | },
679 | {
680 | name: 'rag-belur',
681 | colors: ['#f46e26', '#68485f', '#3d273a', '#535d55'],
682 | background: '#dcd4a6'
683 | },
684 | {
685 | name: 'rag-bangalore',
686 | colors: ['#ea720e', '#ca5130', '#e9c25a', '#52534f'],
687 | background: '#f9ecd3'
688 | },
689 | {
690 | name: 'rag-taj',
691 | colors: ['#ce565e', '#8e1752', '#f8a100', '#3ac1a6'],
692 | background: '#efdea2'
693 | },
694 | {
695 | name: 'rag-virupaksha',
696 | colors: ['#f5736a', '#925951', '#feba4c', '#9d9b9d'],
697 | background: '#eedfa2'
698 | }
699 | ];
700 |
701 | var roygbivs = [
702 | {
703 | name: 'retro',
704 | colors: [
705 | '#69766f',
706 | '#9ed6cb',
707 | '#f7e5cc',
708 | '#9d8f7f',
709 | '#936454',
710 | '#bf5c32',
711 | '#efad57'
712 | ]
713 | },
714 | {
715 | name: 'retro-washedout',
716 | colors: [
717 | '#878a87',
718 | '#cbdbc8',
719 | '#e8e0d4',
720 | '#b29e91',
721 | '#9f736c',
722 | '#b76254',
723 | '#dfa372'
724 | ]
725 | },
726 | {
727 | name: 'roygbiv-warm',
728 | colors: [
729 | '#705f84',
730 | '#687d99',
731 | '#6c843e',
732 | '#fc9a1a',
733 | '#dc383a',
734 | '#aa3a33',
735 | '#9c4257'
736 | ]
737 | },
738 | {
739 | name: 'roygbiv-toned',
740 | colors: [
741 | '#817c77',
742 | '#396c68',
743 | '#89e3b7',
744 | '#f59647',
745 | '#d63644',
746 | '#893f49',
747 | '#4d3240'
748 | ]
749 | },
750 | {
751 | name: 'present-correct',
752 | colors: [
753 | '#fd3741',
754 | '#fe4f11',
755 | '#ff6800',
756 | '#ffa61a',
757 | '#ffc219',
758 | '#ffd114',
759 | '#fcd82e',
760 | '#f4d730',
761 | '#ced562',
762 | '#8ac38f',
763 | '#79b7a0',
764 | '#72b5b1',
765 | '#5b9bae',
766 | '#6ba1b7',
767 | '#49619d',
768 | '#604791',
769 | '#721e7f',
770 | '#9b2b77',
771 | '#ab2562',
772 | '#ca2847'
773 | ]
774 | }
775 | ];
776 |
777 | var tundra = [
778 | {
779 | name: 'tundra1',
780 | colors: ['#40708c', '#8e998c', '#5d3f37', '#ed6954', '#f2e9e2']
781 | },
782 | {
783 | name: 'tundra2',
784 | colors: ['#5f9e93', '#3d3638', '#733632', '#b66239', '#b0a1a4', '#e3dad2']
785 | },
786 | {
787 | name: 'tundra3',
788 | colors: [
789 | '#87c3ca',
790 | '#7b7377',
791 | '#b2475d',
792 | '#7d3e3e',
793 | '#eb7f64',
794 | '#d9c67a',
795 | '#f3f2f2'
796 | ]
797 | },
798 | {
799 | name: 'tundra4',
800 | colors: [
801 | '#d53939',
802 | '#b6754d',
803 | '#a88d5f',
804 | '#524643',
805 | '#3c5a53',
806 | '#7d8c7c',
807 | '#dad6cd'
808 | ]
809 | }
810 | ];
811 |
812 | var rohlfs = [
813 | {
814 | name: 'rohlfs_1R',
815 | colors: ['#004996', '#567bae', '#ff4c48', '#ffbcb3'],
816 | stroke: '#004996',
817 | background: '#fff8e7'
818 | },
819 | {
820 | name: 'rohlfs_1Y',
821 | colors: ['#004996', '#567bae', '#ffc000', '#ffdca4'],
822 | stroke: '#004996',
823 | background: '#fff8e7'
824 | },
825 | {
826 | name: 'rohlfs_1G',
827 | colors: ['#004996', '#567bae', '#60bf3c', '#d2deb1'],
828 | stroke: '#004996',
829 | background: '#fff8e7'
830 | },
831 | {
832 | name: 'rohlfs_2',
833 | colors: ['#4d3d9a', '#f76975', '#ffffff', '#eff0dd'],
834 | stroke: '#211029',
835 | background: '#58bdbc'
836 | },
837 | {
838 | name: 'rohlfs_3',
839 | colors: ['#abdfdf', '#fde500', '#58bdbc', '#eff0dd'],
840 | stroke: '#211029',
841 | background: '#f76975'
842 | },
843 | {
844 | name: 'rohlfs_4',
845 | colors: ['#fde500', '#2f2043', '#f76975', '#eff0dd'],
846 | stroke: '#211029',
847 | background: '#fbbeca'
848 | }
849 | ];
850 |
851 | var ducci = [
852 | {
853 | name: 'ducci_jb',
854 | colors: ['#395e54', '#e77b4d', '#050006', '#e55486'],
855 | stroke: '#050006',
856 | background: '#efe0bc'
857 | },
858 | {
859 | name: 'ducci_a',
860 | colors: ['#809498', '#d3990e', '#000000', '#ecddc5'],
861 | stroke: '#ecddc5',
862 | background: '#863f52'
863 | },
864 | {
865 | name: 'ducci_b',
866 | colors: ['#ecddc5', '#79b27b', '#000000', '#ac6548'],
867 | stroke: '#ac6548',
868 | background: '#d5c08e'
869 | },
870 | {
871 | name: 'ducci_d',
872 | colors: ['#f3cb4d', '#f2f5e3', '#20191b', '#67875c'],
873 | stroke: '#67875c',
874 | background: '#433d5f'
875 | },
876 | {
877 | name: 'ducci_e',
878 | colors: ['#c37c2b', '#f6ecce', '#000000', '#386a7a'],
879 | stroke: '#386a7a',
880 | background: '#e3cd98'
881 | },
882 | {
883 | name: 'ducci_f',
884 | colors: ['#596f7e', '#eae6c7', '#463c21', '#f4cb4c'],
885 | stroke: '#f4cb4c',
886 | background: '#e67300'
887 | },
888 | {
889 | name: 'ducci_g',
890 | colors: ['#c75669', '#000000', '#11706a'],
891 | stroke: '#11706a',
892 | background: '#ecddc5'
893 | },
894 | {
895 | name: 'ducci_h',
896 | colors: ['#6b5c6e', '#4a2839', '#d9574a'],
897 | stroke: '#d9574a',
898 | background: '#ffc34b'
899 | },
900 | {
901 | name: 'ducci_i',
902 | colors: ['#e9dcad', '#143331', '#ffc000'],
903 | stroke: '#ffc000',
904 | background: '#a74c02'
905 | },
906 | {
907 | name: 'ducci_j',
908 | colors: ['#c47c2b', '#5f5726', '#000000', '#7e8a84'],
909 | stroke: '#7e8a84',
910 | background: '#ecddc5'
911 | },
912 | {
913 | name: 'ducci_o',
914 | colors: ['#c15e1f', '#e4a13a', '#000000', '#4d545a'],
915 | stroke: '#4d545a',
916 | background: '#dfc79b'
917 | },
918 | {
919 | name: 'ducci_q',
920 | colors: ['#4bae8c', '#d0c1a0', '#2d3538'],
921 | stroke: '#2d3538',
922 | background: '#d06440'
923 | },
924 | {
925 | name: 'ducci_u',
926 | colors: ['#f6d700', '#f2d692', '#000000', '#5d3552'],
927 | stroke: '#5d3552',
928 | background: '#ff7426'
929 | },
930 | {
931 | name: 'ducci_v',
932 | colors: ['#c65f75', '#d3990e', '#000000', '#597e7a'],
933 | stroke: '#597e7a',
934 | background: '#f6eccb'
935 | },
936 | {
937 | name: 'ducci_x',
938 | colors: ['#dd614a', '#f5cedb', '#1a1e4f'],
939 | stroke: '#1a1e4f',
940 | background: '#fbb900'
941 | }
942 | ];
943 |
944 | var judson = [
945 | {
946 | name: 'jud_playground',
947 | colors: ['#f04924', '#fcce09', '#408ac9'],
948 | stroke: '#2e2925',
949 | background: '#ffffff'
950 | },
951 | {
952 | name: 'jud_horizon',
953 | colors: ['#f8c3df', '#f2e420', '#28b3d0', '#648731', '#ef6a7d'],
954 | stroke: '#030305',
955 | background: '#f2f0e1'
956 | },
957 | {
958 | name: 'jud_mural',
959 | colors: ['#ca3122', '#e5af16', '#4a93a2', '#0e7e39', '#e2b9bd'],
960 | stroke: '#1c1616',
961 | background: '#e3ded8'
962 | },
963 | {
964 | name: 'jud_cabinet',
965 | colors: ['#f0afb7', '#f6bc12', '#1477bb', '#41bb9b'],
966 | stroke: '#020508',
967 | background: '#e3ded8'
968 | }
969 | ];
970 |
971 | var iivonen = [
972 | {
973 | name: 'iiso_zeitung',
974 | colors: ['#ee8067', '#f3df76', '#00a9c0', '#f7ab76'],
975 | stroke: '#111a17',
976 | background: '#f5efcb'
977 | },
978 | {
979 | name: 'iiso_curcuit',
980 | colors: ['#f0865c', '#f2b07b', '#6bc4d2', '#1a3643'],
981 | stroke: '#0f1417',
982 | background: '#f0f0e8'
983 | },
984 | {
985 | name: 'iiso_airlines',
986 | colors: ['#fe765a', '#ffb468', '#4b588f', '#faf1e0'],
987 | stroke: '#1c1616',
988 | background: '#fae5c8'
989 | },
990 | {
991 | name: 'iiso_daily',
992 | colors: ['#e76c4a', '#f0d967', '#7f8cb6', '#1daeb1', '#ef9640'],
993 | stroke: '#000100',
994 | background: '#e2ded2'
995 | }
996 | ];
997 |
998 | var kovecses = [
999 | {
1000 | name: 'kov_01',
1001 | colors: ['#d24c23', '#7ba6bc', '#f0c667', '#ede2b3', '#672b35', '#142a36'],
1002 | stroke: '#132a37',
1003 | background: '#108266'
1004 | },
1005 | {
1006 | name: 'kov_02',
1007 | colors: ['#e8dccc', '#e94641', '#eeaeae'],
1008 | stroke: '#e8dccc',
1009 | background: '#6c96be'
1010 | },
1011 | {
1012 | name: 'kov_03',
1013 | colors: ['#e3937b', '#d93f1d', '#090d15', '#e6cca7'],
1014 | stroke: '#090d15',
1015 | background: '#558947'
1016 | },
1017 | {
1018 | name: 'kov_04',
1019 | colors: ['#d03718', '#292b36', '#33762f', '#ead7c9', '#ce7028', '#689d8d'],
1020 | stroke: '#292b36',
1021 | background: '#deb330'
1022 | },
1023 | {
1024 | name: 'kov_05',
1025 | colors: ['#de3f1a', '#de9232', '#007158', '#e6cdaf', '#869679'],
1026 | stroke: '#010006',
1027 | background: '#7aa5a6'
1028 | },
1029 | {
1030 | name: 'kov_06',
1031 | colors: [
1032 | '#a87c2a',
1033 | '#bdc9b1',
1034 | '#f14616',
1035 | '#ecbfaf',
1036 | '#017724',
1037 | '#0e2733',
1038 | '#2b9ae9'
1039 | ],
1040 | stroke: '#292319',
1041 | background: '#dfd4c1'
1042 | },
1043 | {
1044 | name: 'kov_06b',
1045 | colors: [
1046 | '#d57846',
1047 | '#dfe0cc',
1048 | '#de442f',
1049 | '#e7d3c5',
1050 | '#5ec227',
1051 | '#302f35',
1052 | '#63bdb3'
1053 | ],
1054 | stroke: '#292319',
1055 | background: '#dfd4c1'
1056 | },
1057 | {
1058 | name: 'kov_07',
1059 | colors: ['#c91619', '#fdecd2', '#f4a000', '#4c2653'],
1060 | stroke: '#111',
1061 | background: '#89c2cd'
1062 | }
1063 | ];
1064 |
1065 | var tsuchimochi = [
1066 | {
1067 | name: 'tsu_arcade',
1068 | colors: ['#4aad8b', '#e15147', '#f3b551', '#cec8b8', '#d1af84', '#544e47'],
1069 | stroke: '#251c12',
1070 | background: '#cfc7b9'
1071 | },
1072 | {
1073 | name: 'tsu_harutan',
1074 | colors: ['#75974a', '#c83e3c', '#f39140', '#e4ded2', '#f8c5a4', '#434f55'],
1075 | stroke: '#251c12',
1076 | background: '#cfc7b9'
1077 | },
1078 | {
1079 | name: 'tsu_akasaka',
1080 | colors: ['#687f72', '#cc7d6c', '#dec36f', '#dec7af', '#ad8470', '#424637'],
1081 | stroke: '#251c12',
1082 | background: '#cfc7b9'
1083 | }
1084 | ];
1085 |
1086 | var duotone = [
1087 | {
1088 | name: 'dt01',
1089 | colors: ['#172a89', '#f7f7f3'],
1090 | stroke: '#172a89',
1091 | background: '#f3abb0',
1092 | },
1093 | {
1094 | name: 'dt02',
1095 | colors: ['#302956', '#f3c507'],
1096 | stroke: '#302956',
1097 | background: '#eee3d3',
1098 | },
1099 | {
1100 | name: 'dt02b',
1101 | colors: ['#eee3d3'],
1102 | stroke: '#302956',
1103 | background: '#f3c507',
1104 | },
1105 | {
1106 | name: 'dt03',
1107 | colors: ['#000000', '#a7a7a7'],
1108 | stroke: '#000000',
1109 | background: '#0a5e78',
1110 | },
1111 | {
1112 | name: 'dt04',
1113 | colors: ['#50978e', '#f7f0df'],
1114 | stroke: '#000000',
1115 | background: '#f7f0df',
1116 | },
1117 | {
1118 | name: 'dt05',
1119 | colors: ['#ee5d65', '#f0e5cb'],
1120 | stroke: '#080708',
1121 | background: '#f0e5cb',
1122 | },
1123 | {
1124 | name: 'dt06',
1125 | colors: ['#271f47', '#e7ceb5'],
1126 | stroke: '#271f47',
1127 | background: '#cc2b1c',
1128 | },
1129 | {
1130 | name: 'dt07',
1131 | colors: ['#6a98a5', '#d24c18'],
1132 | stroke: '#efebda',
1133 | background: '#efebda',
1134 | },
1135 | {
1136 | name: 'dt08',
1137 | colors: ['#5d9d88', '#ebb43b'],
1138 | stroke: '#efebda',
1139 | background: '#efebda',
1140 | },
1141 | {
1142 | name: 'dt09',
1143 | colors: ['#052e57', '#de8d80'],
1144 | stroke: '#efebda',
1145 | background: '#efebda',
1146 | },
1147 | {
1148 | name: 'dt10',
1149 | colors: ['#e5dfcf', '#151513'],
1150 | stroke: '#151513',
1151 | background: '#e9b500',
1152 | },
1153 | {
1154 | name: 'dt11',
1155 | colors: ['#ece9e2'],
1156 | stroke: '#221e1f',
1157 | background: '#75c4bf',
1158 | },
1159 | {
1160 | name: 'dt12',
1161 | colors: ['#f5f2d3'],
1162 | stroke: '#073c5c',
1163 | background: '#c0d0c3',
1164 | },
1165 | {
1166 | name: 'dt13',
1167 | colors: ['#f5f2d3', '#f5f2d3', '#fbd6b8'],
1168 | stroke: '#ec5525',
1169 | background: '#ec5525',
1170 | },
1171 | {
1172 | name: 'dt14',
1173 | colors:['#b8bcc4','#ebe8dc','#ebe8dc','#ebe8dc'],
1174 | stroke:'#2d4059',
1175 | background:'#ca6b43'
1176 | },
1177 | {
1178 | name: 'dt14b',
1179 | colors:['#eaa763','#b8928c','#f5e5c0', '#f5e5c0', '#f5e5c0'],
1180 | stroke:'#581a47',
1181 | background:'#b5cdbd'
1182 | },
1183 | {
1184 | name: 'dt15',
1185 | colors:['#33308c'],
1186 | stroke:'#f3eca4',
1187 | background:'#33308c'
1188 | },
1189 | {
1190 | name: 'dt15b',
1191 | colors:['#f3eca4'],
1192 | stroke:'#33308c',
1193 | background:'#f3eca4'
1194 | },
1195 | {
1196 | name: 'dt16',
1197 | colors:['#e9e6dc'],
1198 | stroke:'#232827',
1199 | background:'#e9e6dc'
1200 | },
1201 | {
1202 | name: 'dt16b',
1203 | colors:['#232827'],
1204 | stroke:'#e9e6dc',
1205 | background:'#232827'
1206 | },
1207 | {
1208 | name: 'dt17',
1209 | colors: ['#e1ac28','#dfd2b9','#dfd2b9','#dfd2b9'],
1210 | stroke:'#1d2225',
1211 | background:'#1d2225'
1212 | }
1213 | ];
1214 |
1215 | var hilda = [
1216 | {
1217 | name: 'hilda01',
1218 | colors: ['#ec5526', '#f4ac12', '#9ebbc1', '#f7f4e2'],
1219 | stroke: '#1e1b1e',
1220 | background: '#e7e8d4'
1221 | },
1222 | {
1223 | name: 'hilda02',
1224 | colors: ['#eb5627', '#eebb20', '#4e9eb8', '#f7f5d0'],
1225 | stroke: '#201d13',
1226 | background: '#77c1c0'
1227 | },
1228 | {
1229 | name: 'hilda03',
1230 | colors: ['#e95145', '#f8b917', '#b8bdc1', '#ffb2a2'],
1231 | stroke: '#010101',
1232 | background: '#6b7752'
1233 | },
1234 | {
1235 | name: 'hilda04',
1236 | colors: ['#e95145', '#f6bf7a', '#589da1', '#f5d9bc'],
1237 | stroke: '#000001',
1238 | background: '#f5ede1'
1239 | },
1240 | {
1241 | name: 'hilda05',
1242 | colors: ['#ff6555', '#ffb58f', '#d8eecf', '#8c4b47', '#bf7f93'],
1243 | stroke: '#2b0404',
1244 | background: '#ffda82'
1245 | },
1246 | {
1247 | name: 'hilda06',
1248 | colors: ['#f75952', '#ffce84', '#74b7b2', '#f6f6f6', '#b17d71'],
1249 | stroke: '#0e0603',
1250 | background: '#f6ecd4'
1251 | }
1252 | ];
1253 |
1254 | var spatial = [
1255 | {
1256 | name: 'spatial01',
1257 | colors: ['#ff5937', '#f6f6f4', '#4169ff'],
1258 | stroke: '#ff5937',
1259 | background: '#f6f6f4'
1260 | },
1261 | {
1262 | name: 'spatial02',
1263 | colors: ['#ff5937', '#f6f6f4', '#f6f6f4'],
1264 | stroke: '#ff5937',
1265 | background: '#f6f6f4'
1266 | },
1267 | {
1268 | name: 'spatial02i',
1269 | colors: ['#f6f6f4', '#ff5937', '#ff5937'],
1270 | stroke: '#f6f6f4',
1271 | background: '#ff5937'
1272 | },
1273 |
1274 | {
1275 | name: 'spatial03',
1276 | colors: ['#4169ff', '#f6f6f4', '#f6f6f4'],
1277 | stroke: '#4169ff',
1278 | background: '#f6f6f4'
1279 | },
1280 | {
1281 | name: 'spatial03i',
1282 | colors: ['#f6f6f4', '#4169ff', '#4169ff'],
1283 | stroke: '#f6f6f4',
1284 | background: '#4169ff'
1285 | }
1286 | ];
1287 |
1288 | var jung = [
1289 | {
1290 | name: 'jung_bird',
1291 | colors: ['#fc3032', '#fed530', '#33c3fb', '#ff7bac', '#fda929'],
1292 | stroke: '#000000',
1293 | background: '#ffffff'
1294 | },
1295 | {
1296 | name: 'jung_horse',
1297 | colors: ['#e72e81', '#f0bf36', '#3056a2'],
1298 | stroke: '#000000',
1299 | background: '#ffffff'
1300 | },
1301 | {
1302 | name: 'jung_croc',
1303 | colors: ['#f13274', '#eed03e', '#405e7f', '#19a198'],
1304 | stroke: '#000000',
1305 | background: '#ffffff'
1306 | },
1307 | {
1308 | name: 'jung_hippo',
1309 | colors: ['#ff7bac', '#ff921e', '#3ea8f5', '#7ac943'],
1310 | stroke: '#000000',
1311 | background: '#ffffff'
1312 | },
1313 | {
1314 | name: 'jung_wolf',
1315 | colors: ['#e51c39', '#f1b844', '#36c4b7', '#666666'],
1316 | stroke: '#000000',
1317 | background: '#ffffff'
1318 | }
1319 | ];
1320 |
1321 | var system = [
1322 | {
1323 | name: 'system.#01',
1324 | colors: ['#ff4242', '#fec101', '#1841fe', '#fcbdcc', '#82e9b5'],
1325 | stroke: '#000',
1326 | background: '#fff'
1327 | },
1328 | {
1329 | name: 'system.#02',
1330 | colors: ['#ff4242', '#ffd480', '#1e365d', '#edb14c', '#418dcd'],
1331 | stroke: '#000',
1332 | background: '#fff'
1333 | },
1334 | {
1335 | name: 'system.#03',
1336 | colors: ['#f73f4a', '#d3e5eb', '#002c3e', '#1aa1b1', '#ec6675'],
1337 | stroke: '#110b09',
1338 | background: '#fff'
1339 | },
1340 | {
1341 | name: 'system.#04',
1342 | colors: ['#e31f4f', '#f0ac3f', '#18acab', '#26265a', '#ea7d81', '#dcd9d0'],
1343 | stroke: '#26265a',
1344 | backgrund: '#dcd9d0'
1345 | },
1346 | {
1347 | name: 'system.#05',
1348 | colors: ['#db4549', '#d1e1e1', '#3e6a90', '#2e3853', '#a3c9d3'],
1349 | stroke: '#000',
1350 | background: '#fff'
1351 | },
1352 | {
1353 | name: 'system.#06',
1354 | colors: ['#e5475c', '#95b394', '#28343b', '#f7c6a3', '#eb8078'],
1355 | stroke: '#000',
1356 | background: '#fff'
1357 | },
1358 | {
1359 | name: 'system.#07',
1360 | colors: ['#d75c49', '#f0efea', '#509da4'],
1361 | stroke: '#000',
1362 | background: '#fff'
1363 | },
1364 | {
1365 | name: 'system.#08',
1366 | colors: ['#f6625a', '#92b29f', '#272c3f'],
1367 | stroke: '#000',
1368 | background: '#fff'
1369 | }
1370 | ];
1371 |
1372 | var flourish = [
1373 | {
1374 | name: 'empusa',
1375 | colors: ['#c92a28', '#e69301', '#1f8793', '#13652b', '#e7d8b0', '#48233b', '#e3b3ac'],
1376 | stroke: '#1a1a1a',
1377 | background: '#f0f0f2',
1378 | },
1379 | {
1380 | name: 'delphi',
1381 | colors: ['#475b62', '#7a999c', '#2a1f1d', '#fbaf3c', '#df4a33', '#f0e0c6', '#af592c'],
1382 | stroke: '#2a1f1d',
1383 | background: '#f0e0c6',
1384 | },
1385 | {
1386 | name: 'mably',
1387 | colors: [
1388 | '#13477b',
1389 | '#2f1b10',
1390 | '#d18529',
1391 | '#d72a25',
1392 | '#e42184',
1393 | '#138898',
1394 | '#9d2787',
1395 | '#7f311b',
1396 | ],
1397 | stroke: '#2a1f1d',
1398 | background: '#dfc792',
1399 | },
1400 | {
1401 | name: 'nowak',
1402 | colors: ['#e85b30', '#ef9e28', '#c6ac71', '#e0c191', '#3f6279', '#ee854e', '#180305'],
1403 | stroke: '#180305',
1404 | background: '#ede4cb',
1405 | },
1406 | {
1407 | name: 'jupiter',
1408 | colors: ['#c03a53', '#edd09e', '#aab5af', '#023629', '#eba735', '#8e9380', '#6c4127'],
1409 | stroke: '#12110f',
1410 | background: '#e6e2d6',
1411 | },
1412 | {
1413 | name: 'hersche',
1414 | colors: [
1415 | '#df9f00',
1416 | '#1f6f50',
1417 | '#8e6d7f',
1418 | '#da0607',
1419 | '#a4a5a7',
1420 | '#d3d1c3',
1421 | '#42064f',
1422 | '#25393a',
1423 | ],
1424 | stroke: '#0a0a0a',
1425 | background: '#f0f5f6',
1426 | },
1427 | {
1428 | name: 'cherfi',
1429 | colors: ['#99cb9f', '#cfb610', '#d00701', '#dba78d', '#2e2c1d', '#bfbea2', '#d2cfaf'],
1430 | stroke: '#332e22',
1431 | background: '#e3e2c5',
1432 | },
1433 | {
1434 | name: 'harvest',
1435 | colors: [
1436 | '#313a42',
1437 | '#9aad2e',
1438 | '#f0ae3c',
1439 | '#df4822',
1440 | '#8eac9b',
1441 | '#cc3d3f',
1442 | '#ec8b1c',
1443 | '#1b9268',
1444 | ],
1445 | stroke: '#463930',
1446 | background: '#e5e2cf',
1447 | },
1448 | {
1449 | name: 'honey',
1450 | colors: ['#f14d42', '#f4fdec', '#4fbe5d', '#265487', '#f6e916', '#f9a087', '#2e99d6'],
1451 | stroke: '#141414',
1452 | background: '#f4fdec',
1453 | },
1454 | {
1455 | name: 'jungle',
1456 | colors: [
1457 | '#adb100',
1458 | '#e5f4e9',
1459 | '#f4650f',
1460 | '#4d6838',
1461 | '#cb9e00',
1462 | '#689c7d',
1463 | '#e2a1a8',
1464 | '#151c2e',
1465 | ],
1466 | stroke: '#0e0f27',
1467 | background: '#cecaa9',
1468 | },
1469 | {
1470 | name: 'skyspider',
1471 | colors: ['#f4b232', '#f2dbbd', '#f2dbbd', '#f2dbbd', '#f2dbbd', '#01799c', '#e93e48', '#006748', '#ed817d'],
1472 | stroke: '#050505',
1473 | background: '#f0dbbc',
1474 | },
1475 | {
1476 | name: 'atlas',
1477 | colors: ['#5399b1', '#f4e9d5', '#de4037', '#ed942f', '#4e9e48', '#7a6e62'],
1478 | stroke: '#3d352b',
1479 | background: '#f0c328',
1480 | },
1481 | {
1482 | name: 'giftcard',
1483 | colors: [
1484 | '#FBF5E9',
1485 | '#FF514E',
1486 | '#FDBC2E',
1487 | '#4561CC',
1488 | '#2A303E',
1489 | '#6CC283',
1490 | '#A71172',
1491 | '#238DA5',
1492 | '#9BD7CB',
1493 | '#231E58',
1494 | '#4E0942',
1495 | ],
1496 | stroke: '#000',
1497 | background: '#FBF5E9',
1498 | },
1499 | {
1500 | name: 'giftcard_sub',
1501 | colors: [
1502 | '#FBF5E9',
1503 | '#FF514E',
1504 | '#FDBC2E',
1505 | '#4561CC',
1506 | '#2A303E',
1507 | '#6CC283',
1508 | '#238DA5',
1509 | '#9BD7CB',
1510 | ],
1511 | stroke: '#000',
1512 | background: '#FBF5E9',
1513 | },
1514 | ];
1515 |
1516 | var dale = [
1517 | {
1518 | name: 'dale_paddle',
1519 | colors: [
1520 | '#ff7a5a',
1521 | '#765aa6',
1522 | '#fee7bc',
1523 | '#515e8c',
1524 | '#ffc64a',
1525 | '#b460a6',
1526 | '#ffffff',
1527 | '#4781c1',
1528 | ],
1529 | stroke: '#000000',
1530 | background: '#abe9e8',
1531 | },
1532 | {
1533 | name: 'dale_night',
1534 | colors: ['#ae5d9d', '#f1e8bc', '#ef8fa3', '#f7c047', '#58c9ed', '#f77150'],
1535 | stroke: '#000000',
1536 | background: '#00ae83',
1537 | },
1538 | {
1539 | name: 'dale_cat',
1540 | colors: ['#f77656', '#f7f7f7', '#efc545', '#dfe0e2', '#3c70bd', '#66bee4'],
1541 | stroke: '#000000',
1542 | background: '#f6e0b8',
1543 | },
1544 | ];
1545 |
1546 | var cako = [
1547 | {
1548 | name: 'cako1',
1549 | colors: ['#000000', '#d55a3a', '#2a5c8a', '#7e7d14', '#dbdac9'],
1550 | stroke: '#000000',
1551 | background: '#f4e9d5',
1552 | },
1553 | {
1554 | name: 'cako2',
1555 | colors: ['#dbdac9', '#d55a3a', '#2a5c8a', '#b47b8c', '#7e7d14'],
1556 | stroke: '#000000',
1557 | background: '#000000',
1558 | },
1559 | {
1560 | name: 'cako2_sub1',
1561 | colors: ['#dbdac9', '#d55a3a', '#2a5c8a'],
1562 | stroke: '#000000',
1563 | background: '#000000',
1564 | },
1565 | {
1566 | name: 'cako2_sub2',
1567 | colors: ['#dbdac9', '#d55a3a', '#7e7d14'],
1568 | stroke: '#000000',
1569 | background: '#000000',
1570 | },
1571 | ];
1572 |
1573 | var mayo = [
1574 | {
1575 | name: 'mayo1',
1576 | colors: ['#ea510e', '#ffd203', '#0255a3', '#039177', '#111111'],
1577 | stroke: '#111111',
1578 | background: '#fff',
1579 | },
1580 | {
1581 | name: 'mayo2',
1582 | colors: ['#ea663f', '#f9cc27', '#84afd7', '#7ca994', '#f1bbc9', '#242424'],
1583 | stroke: '#2a2a2a',
1584 | background: '#f5f6f1',
1585 | },
1586 | {
1587 | name: 'mayo3',
1588 | colors: ['#ea5b19', '#f8c9b9', '#137661', '#2a2a2a'],
1589 | stroke: '#2a2a2a',
1590 | background: '#f5f4f0',
1591 | },
1592 | ];
1593 |
1594 | var exposito = [
1595 | {
1596 | name: 'exposito',
1597 | colors: [
1598 | '#8bc9c3',
1599 | '#ffae43',
1600 | '#ea432c',
1601 | '#228345',
1602 | '#d1d7d3',
1603 | '#524e9c',
1604 | '#9dc35e',
1605 | '#f0a1a1',
1606 | ],
1607 | stroke: '#fff',
1608 | background: '#000000',
1609 | },
1610 | {
1611 | name: 'exposito_sub1',
1612 | colors: ['#8bc9c3', '#ffae43', '#ea432c', '#524e9c'],
1613 | stroke: '#fff',
1614 | background: '#000000',
1615 | },
1616 | {
1617 | name: 'exposito_sub2',
1618 | colors: ['#8bc9c3', '#ffae43', '#ea432c', '#524e9c', '#f0a1a1', '#228345'],
1619 | stroke: '#fff',
1620 | background: '#000000',
1621 | },
1622 | {
1623 | name: 'exposito_sub3',
1624 | colors: ['#ffae43', '#ea432c', '#524e9c', '#f0a1a1'],
1625 | stroke: '#fff',
1626 | background: '#000000',
1627 | },
1628 | ];
1629 |
1630 | const pals = misc.concat(
1631 | ranganath,
1632 | roygbivs,
1633 | tundra,
1634 | colourscafe,
1635 | rohlfs,
1636 | ducci,
1637 | judson,
1638 | iivonen,
1639 | kovecses,
1640 | tsuchimochi,
1641 | duotone,
1642 | hilda,
1643 | spatial,
1644 | jung,
1645 | system,
1646 | flourish,
1647 | dale,
1648 | cako,
1649 | mayo,
1650 | exposito
1651 | );
1652 |
1653 | var palettes = pals.map((p) => {
1654 | p.size = p.colors.length;
1655 | return p;
1656 | });
1657 |
1658 | function getRandom() {
1659 | return palettes[Math.floor(Math.random() * palettes.length)];
1660 | }
1661 |
1662 | function get(name) {
1663 | if (name === undefined) return getRandom();
1664 | return palettes.find(pal => pal.name == name);
1665 | }
1666 |
1667 | let sketch = function(p) {
1668 | let THE_SEED;
1669 |
1670 | const mag = 10;
1671 | const xu = [mag, 0]; //[1 * mag, -0.2 * mag]; // X Unit
1672 | const yu = [0, mag]; //[0.3 * mag, 0.8 * mag]; // Y Unit
1673 |
1674 | const palette = get_palette();
1675 | const generator = new index(15, 30, {
1676 | simple: true,
1677 | extension_chance: 0.95,
1678 | horizontal_symmetry: false,
1679 | vertical_chance: 0.5
1680 | });
1681 |
1682 | const innerApparatusOptions = {
1683 | simple: true,
1684 | extension_chance: 0.68,
1685 | horizontal_symmetry: false,
1686 | vertical_chance: 0.5,
1687 | color_mode: 'random',
1688 | colors: palette.colors
1689 | };
1690 |
1691 | let layout;
1692 |
1693 | p.setup = function() {
1694 | p.createCanvas(950, 950);
1695 | THE_SEED = p.floor(p.random(9999999));
1696 | p.randomSeed(THE_SEED);
1697 | p.noFill();
1698 | p.smooth();
1699 | //p.frameRate(3);
1700 | p.stroke(palette.stroke ? palette.stroke : '#111');
1701 | p.background(palette.background ? palette.background : '#eee');
1702 | p.noLoop();
1703 | };
1704 |
1705 | p.draw = function() {
1706 | reset();
1707 | displayLayout(4, true);
1708 | /*
1709 | if (tick % 9 == 0) reset();
1710 | displayLayout(tick % 9, tick % 9 > 2);
1711 | tick++;
1712 | */
1713 | };
1714 |
1715 | p.keyPressed = function() {
1716 | if (p.keyCode === 80) p.saveCanvas('sketch_' + THE_SEED, 'jpeg');
1717 | };
1718 |
1719 | function reset() {
1720 | p.background(palette.background ? palette.background : '#eee');
1721 | p.stroke(palette.background);
1722 | layout = generator
1723 | .generate()
1724 | .map(b => ({ ...b, level: 0, filled: false, content: createGrid(b) }));
1725 | }
1726 |
1727 | function displayLayout(depth, colorize) {
1728 | p.translate(p.width / 2, p.height / 2);
1729 | layout.forEach(box => {
1730 | displayBox(box, depth, colorize);
1731 | });
1732 | }
1733 |
1734 | function displayBox(box, maxLevel, colorize) {
1735 | if (box.content != null && box.content.length > 0 && maxLevel > box.level) {
1736 | box.content.forEach(c => displayBox(c, maxLevel, colorize));
1737 | }
1738 |
1739 | if (box.filled && colorize) p.fill(box.col);
1740 | else p.noFill();
1741 | var cir1 = (box.y1 / 71) * Math.PI * 2;
1742 | var cir2 = ((box.y1 + box.h) / 71) * Math.PI * 2;
1743 | var rad1 = box.x1;
1744 | var rad2 = box.x1 + box.w;
1745 |
1746 | var p1 = [Math.cos(cir1) * rad1, Math.sin(cir1) * rad1];
1747 | var p2 = [Math.cos(cir1) * rad2, Math.sin(cir1) * rad2];
1748 | var p3 = [Math.cos(cir2) * rad2, Math.sin(cir2) * rad2];
1749 | var p4 = [Math.cos(cir2) * rad1, Math.sin(cir2) * rad1];
1750 |
1751 | p.strokeWeight(5 / (box.level + 1));
1752 | //p.noFill();
1753 |
1754 | p.beginShape();
1755 | p.vertex(p1[0] * xu[0] + p1[1] * yu[0], p1[0] * xu[1] + p1[1] * yu[1]);
1756 | p.vertex(p2[0] * xu[0] + p2[1] * yu[0], p2[0] * xu[1] + p2[1] * yu[1]);
1757 | arc(p, rad2 * mag, cir1, cir2);
1758 | p.vertex(p3[0] * xu[0] + p3[1] * yu[0], p3[0] * xu[1] + p3[1] * yu[1]);
1759 | p.vertex(p4[0] * xu[0] + p4[1] * yu[0], p4[0] * xu[1] + p4[1] * yu[1]);
1760 | arc(p, rad1 * mag, cir2, cir1);
1761 | p.endShape(p.CLOSE);
1762 | }
1763 |
1764 | function createGrid(box) {
1765 | const { x1, y1, w, h } = box;
1766 | const cols = Math.ceil((Math.random() * w) / 2);
1767 | const rows = Math.ceil((Math.random() * h) / 2);
1768 | const cell_w = w / cols;
1769 | const cell_h = h / rows;
1770 |
1771 | const apparatus = createApparatus(cell_w, cell_h);
1772 |
1773 | const grid = [];
1774 | for (let i = 0; i < rows; i++) {
1775 | for (let j = 0; j < cols; j++) {
1776 | const cell = {
1777 | x1: x1 + j * cell_w,
1778 | y1: y1 + i * cell_h,
1779 | w: cell_w,
1780 | h: cell_h,
1781 | level: 1,
1782 | filled: false
1783 | };
1784 | const content = apparatus.map(app => ({
1785 | ...app,
1786 | x1: app.x1 + cell.x1,
1787 | y1: app.y1 + cell.y1,
1788 | level: 2,
1789 | filled: true,
1790 | crossed: app.w < 1.5 && app.h < 1.5 && Math.random() < 0.3,
1791 | legend_width: 2 + Math.random() * (app.w - 3)
1792 | }));
1793 |
1794 | grid.push({ ...cell, content: content });
1795 | }
1796 | }
1797 | return grid;
1798 | }
1799 |
1800 | function createApparatus(w, h) {
1801 | const cols = Math.round(w, 0);
1802 | const rows = Math.round(h, 0);
1803 |
1804 | const w_unit = w / cols;
1805 | const h_unit = h / rows;
1806 |
1807 | const generator = new index(
1808 | (cols - 11) / 2,
1809 | (rows - 11) / 2,
1810 | innerApparatusOptions
1811 | );
1812 |
1813 | return generator.generate().map(a => ({
1814 | x1: (a.x1 - 1) * w_unit,
1815 | y1: (a.y1 - 1) * h_unit,
1816 | w: a.w * w_unit,
1817 | h: a.h * h_unit,
1818 | col: a.col
1819 | }));
1820 | }
1821 | };
1822 |
1823 | new p5(sketch);
1824 |
1825 | function get_palette() {
1826 | const url = window.location.href.split('?');
1827 | if (url.length === 1) return get('spatial01');
1828 | return get(url[1]);
1829 | }
1830 |
1831 | function arc(p, rad, c1, c2) {
1832 | let c = c1;
1833 |
1834 | let pos = c1 < c2;
1835 |
1836 | if (pos) {
1837 | while (c < c2) {
1838 | p.vertex(Math.cos(c) * rad, Math.sin(c) * rad);
1839 | c += 0.02;
1840 | }
1841 | } else {
1842 | while (c > c2) {
1843 | p.vertex(Math.cos(c) * rad, Math.sin(c) * rad);
1844 | c -= 0.02;
1845 | }
1846 | }
1847 | }
1848 |
1849 | }));
1850 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Sketch
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | import Apparatus from 'apparatus-generator';
2 | import * as tome from 'chromotome';
3 |
4 | let sketch = function (p) {
5 | let THE_SEED;
6 |
7 | const mag = 18;
8 | const xu = [1 * mag, -0.2 * mag]; // X Unit
9 | const yu = [0.3 * mag, 0.8 * mag]; // Y Unit
10 |
11 | const palette = get_palette();
12 | const generator = new Apparatus(10, 18, {
13 | simple: true,
14 | extension_chance: 0.97,
15 | horizontal_symmetry: false,
16 | vertical_chance: 0.3,
17 | });
18 |
19 | const innerApparatusOptions = {
20 | simple: true,
21 | extension_chance: 0.8,
22 | horizontal_symmetry: false,
23 | vertical_chance: 0.3,
24 | color_mode: 'main',
25 | colors: palette.colors,
26 | };
27 |
28 | let layout;
29 | let tick;
30 |
31 | p.setup = function () {
32 | p.createCanvas(950, 950);
33 | THE_SEED = p.floor(p.random(9999999));
34 | p.randomSeed(THE_SEED);
35 | p.noFill();
36 | p.smooth();
37 | p.frameRate(3);
38 | p.stroke(palette.stroke ? palette.stroke : '#111');
39 | p.background(palette.background ? palette.background : '#eee');
40 |
41 | tick = 0;
42 | };
43 |
44 | p.draw = function () {
45 | if (tick % 9 == 0) reset();
46 | displayLayout(tick % 9, tick % 9 > 2);
47 | tick++;
48 | };
49 |
50 | p.keyPressed = function () {
51 | if (p.keyCode === 80) p.saveCanvas('sketch_' + THE_SEED, 'jpeg');
52 | };
53 |
54 | function reset() {
55 | p.background(palette.background ? palette.background : '#eee');
56 | layout = generator
57 | .generate()
58 | .map((b) => ({ ...b, level: 0, filled: false, content: createGrid(b) }));
59 | }
60 |
61 | function displayLayout(depth, colorize) {
62 | p.translate(45, 180);
63 | layout.forEach((box) => {
64 | displayBox(box, depth, colorize);
65 | });
66 | }
67 |
68 | function displayBox(box, maxLevel, colorize) {
69 | if (box.content != null && box.content.length > 0 && maxLevel > box.level) {
70 | box.content.forEach((c) => displayBox(c, maxLevel, colorize));
71 | }
72 |
73 | if (box.filled && colorize) p.fill(box.col);
74 | else p.noFill();
75 |
76 | p.strokeWeight(3 / (box.level + 1));
77 | p.beginShape();
78 | p.vertex(box.x1 * xu[0] + box.y1 * yu[0], box.x1 * xu[1] + box.y1 * yu[1]);
79 | p.vertex(
80 | (box.x1 + box.w) * xu[0] + box.y1 * yu[0],
81 | (box.x1 + box.w) * xu[1] + box.y1 * yu[1]
82 | );
83 | p.vertex(
84 | (box.x1 + box.w) * xu[0] + (box.y1 + box.h) * yu[0],
85 | (box.x1 + box.w) * xu[1] + (box.y1 + box.h) * yu[1]
86 | );
87 | p.vertex(
88 | box.x1 * xu[0] + (box.y1 + box.h) * yu[0],
89 | box.x1 * xu[1] + (box.y1 + box.h) * yu[1]
90 | );
91 | p.endShape(p.CLOSE);
92 |
93 | if (colorize && box.filled && box.h > 1.5 && box.w > 3) {
94 | displayLegend(box);
95 | }
96 |
97 | if (colorize && box.filled && box.crossed) {
98 | displayCross(box);
99 | }
100 | }
101 |
102 | function displayLegend(box) {
103 | p.line(
104 | (box.x1 + 0.5) * xu[0] + (box.y1 + 0.5) * yu[0],
105 | (box.x1 + 0.5) * xu[1] + (box.y1 + 0.5) * yu[1],
106 | (box.x1 + box.legend_width) * xu[0] + (box.y1 + 0.5) * yu[0],
107 | (box.x1 + box.legend_width) * xu[1] + (box.y1 + 0.5) * yu[1]
108 | );
109 | }
110 |
111 | function displayCross(box) {
112 | p.line(
113 | box.x1 * xu[0] + box.y1 * yu[0],
114 | box.x1 * xu[1] + box.y1 * yu[1],
115 | (box.x1 + box.w) * xu[0] + (box.y1 + box.h) * yu[0],
116 | (box.x1 + box.w) * xu[1] + (box.y1 + box.h) * yu[1]
117 | );
118 | p.line(
119 | (box.x1 + box.w) * xu[0] + box.y1 * yu[0],
120 | (box.x1 + box.w) * xu[1] + box.y1 * yu[1],
121 | box.x1 * xu[0] + (box.y1 + box.h) * yu[0],
122 | box.x1 * xu[1] + (box.y1 + box.h) * yu[1]
123 | );
124 | }
125 |
126 | function createGrid(box) {
127 | const { x1, y1, w, h } = box;
128 | const cols = Math.ceil((Math.random() * w) / 3);
129 | const rows = Math.ceil((Math.random() * h) / 2);
130 | const cell_w = w / cols;
131 | const cell_h = h / rows;
132 |
133 | const apparatus = createApparatus(cell_w, cell_h);
134 |
135 | const grid = [];
136 | for (let i = 0; i < rows; i++) {
137 | for (let j = 0; j < cols; j++) {
138 | const cell = {
139 | x1: x1 + j * cell_w,
140 | y1: y1 + i * cell_h,
141 | w: cell_w,
142 | h: cell_h,
143 | level: 1,
144 | filled: false,
145 | };
146 | const content = apparatus.map((app) => ({
147 | ...app,
148 | x1: app.x1 + cell.x1,
149 | y1: app.y1 + cell.y1,
150 | level: 2,
151 | filled: true,
152 | crossed: app.w < 1.5 && app.h < 1.5 && Math.random() < 0.3,
153 | legend_width: 2 + Math.random() * (app.w - 3),
154 | }));
155 |
156 | grid.push({ ...cell, content: content });
157 | }
158 | }
159 | return grid;
160 | }
161 |
162 | function createApparatus(w, h) {
163 | const cols = Math.round(w, 0);
164 | const rows = Math.round(h, 0);
165 |
166 | const w_unit = w / cols;
167 | const h_unit = h / rows;
168 |
169 | const generator = new Apparatus(
170 | (cols - 11) / 2,
171 | (rows - 11) / 2,
172 | innerApparatusOptions
173 | );
174 |
175 | return generator.generate().map((a) => ({
176 | x1: (a.x1 - 1) * w_unit,
177 | y1: (a.y1 - 1) * h_unit,
178 | w: a.w * w_unit,
179 | h: a.h * h_unit,
180 | col: a.col,
181 | }));
182 | }
183 | };
184 |
185 | new p5(sketch);
186 |
187 | function get_palette() {
188 | const url = window.location.href.split('#');
189 | if (url.length === 1) return tome.get('dt05');
190 | return tome.get(url[1]);
191 | }
192 |
--------------------------------------------------------------------------------
/index_building.js:
--------------------------------------------------------------------------------
1 | import Apparatus from 'apparatus-generator';
2 | import * as tome from 'chromotome';
3 | import toposort from 'toposort';
4 |
5 | let sketch = function(p) {
6 | let THE_SEED;
7 |
8 | const mag = 18;
9 | const xu = [1 * mag, 0.1 * mag]; // X Unit
10 | const yu = [0.15 * mag, 0.75 * mag]; // Y Unit
11 | const zu = [0.05 * mag, -0.05 * mag]; // Y Unit
12 |
13 | const decorEnabled = false;
14 | const shadingEnabled = true;
15 | const strokeEnabled = false;
16 |
17 | const palette = get_palette();
18 | const generator = new Apparatus(12, 25, {
19 | simple: false,
20 | extension_chance: 0.97,
21 | horizontal_symmetry: false,
22 | vertical_chance: 0.3
23 | });
24 |
25 | const innerApparatusOptions = {
26 | simple: true,
27 | extension_chance: 0.75,
28 | horizontal_symmetry: false,
29 | vertical_chance: 0.3,
30 | color_mode: 'random',
31 | colors: palette.colors
32 | };
33 |
34 | let layout;
35 |
36 | p.setup = function() {
37 | p.createCanvas(950, 950);
38 | THE_SEED = p.floor(p.random(9999999));
39 | p.randomSeed(THE_SEED);
40 | p.noFill();
41 | p.smooth();
42 | p.frameRate(1);
43 | p.background(palette.background ? palette.background : '#eee');
44 | p.strokeJoin(p.ROUND);
45 |
46 | if (strokeEnabled) p.stroke(palette.stroke ? palette.stroke : '#111');
47 | //if (strokeEnabled) p.stroke(0, 50);
48 | else p.noStroke();
49 | };
50 |
51 | p.draw = function() {
52 | reset();
53 | displayLayout(4, true);
54 | };
55 |
56 | p.keyPressed = function() {
57 | if (p.keyCode === 80) p.saveCanvas('sketch_' + THE_SEED, 'jpeg');
58 | };
59 |
60 | function reset() {
61 | p.background(palette.background ? palette.background : '#eee');
62 | layout = get_overlap_graph(generator.generate().flatMap(createGrid));
63 | }
64 |
65 | function displayLayout(depth, colorize) {
66 | p.translate(0, 0);
67 | layout.forEach(i => {
68 | displayBox(i, depth, colorize);
69 | });
70 | }
71 |
72 | function displayBox(box, maxLevel, colorize) {
73 | if (box.content != null && box.content.length > 0 && maxLevel > box.level) {
74 | box.content.forEach(c => displayBox(c, maxLevel, colorize));
75 | }
76 |
77 | if (box.filled && colorize) p.fill(box.col);
78 | else p.noFill();
79 |
80 | p.beginShape();
81 | p.vertex(
82 | box.x1 * xu[0] + box.y1 * yu[0] + box.z1 * zu[0],
83 | box.x1 * xu[1] + box.y1 * yu[1] + box.z1 * zu[1]
84 | );
85 | p.vertex(
86 | (box.x1 + box.w) * xu[0] + box.y1 * yu[0] + box.z1 * zu[0],
87 | (box.x1 + box.w) * xu[1] + box.y1 * yu[1] + box.z1 * zu[1]
88 | );
89 | p.vertex(
90 | (box.x1 + box.w) * xu[0] + (box.y1 + box.h) * yu[0] + box.z1 * zu[0],
91 | (box.x1 + box.w) * xu[1] + (box.y1 + box.h) * yu[1] + box.z1 * zu[1]
92 | );
93 | p.vertex(
94 | box.x1 * xu[0] + (box.y1 + box.h) * yu[0] + box.z1 * zu[0],
95 | box.x1 * xu[1] + (box.y1 + box.h) * yu[1] + box.z1 * zu[1]
96 | );
97 | p.endShape(p.CLOSE);
98 |
99 | p.beginShape();
100 | p.vertex(
101 | box.x1 * xu[0] + box.y1 * yu[0] + box.z1 * zu[0],
102 | box.x1 * xu[1] + box.y1 * yu[1] + box.z1 * zu[1]
103 | );
104 | p.vertex(
105 | box.x1 * xu[0] + (box.y1 + box.h) * yu[0] + box.z1 * zu[0],
106 | box.x1 * xu[1] + (box.y1 + box.h) * yu[1] + box.z1 * zu[1]
107 | );
108 | p.vertex(
109 | box.x1 * xu[0] + (box.y1 + box.h) * yu[0] - box.z1 * zu[0],
110 | box.x1 * xu[1] + (box.y1 + box.h) * yu[1] - box.z1 * zu[1]
111 | );
112 | p.vertex(
113 | box.x1 * xu[0] + box.y1 * yu[0] - box.z1 * zu[0],
114 | box.x1 * xu[1] + box.y1 * yu[1] - box.z1 * zu[1]
115 | );
116 | p.endShape(p.CLOSE);
117 |
118 | p.beginShape();
119 | p.vertex(
120 | (box.x1 + box.w) * xu[0] + (box.y1 + box.h) * yu[0] + box.z1 * zu[0],
121 | (box.x1 + box.w) * xu[1] + (box.y1 + box.h) * yu[1] + box.z1 * zu[1]
122 | );
123 | p.vertex(
124 | box.x1 * xu[0] + (box.y1 + box.h) * yu[0] + box.z1 * zu[0],
125 | box.x1 * xu[1] + (box.y1 + box.h) * yu[1] + box.z1 * zu[1]
126 | );
127 | p.vertex(
128 | box.x1 * xu[0] + (box.y1 + box.h) * yu[0] - box.z1 * zu[0],
129 | box.x1 * xu[1] + (box.y1 + box.h) * yu[1] - box.z1 * zu[1]
130 | );
131 | p.vertex(
132 | (box.x1 + box.w) * xu[0] + (box.y1 + box.h) * yu[0] - box.z1 * zu[0],
133 | (box.x1 + box.w) * xu[1] + (box.y1 + box.h) * yu[1] - box.z1 * zu[1]
134 | );
135 | p.endShape(p.CLOSE);
136 |
137 | if (shadingEnabled) {
138 | p.fill(0, 100);
139 | p.beginShape();
140 | p.vertex(
141 | box.x1 * xu[0] + box.y1 * yu[0] + box.z1 * zu[0],
142 | box.x1 * xu[1] + box.y1 * yu[1] + box.z1 * zu[1]
143 | );
144 | p.vertex(
145 | box.x1 * xu[0] + (box.y1 + box.h) * yu[0] + box.z1 * zu[0],
146 | box.x1 * xu[1] + (box.y1 + box.h) * yu[1] + box.z1 * zu[1]
147 | );
148 | p.vertex(
149 | box.x1 * xu[0] + (box.y1 + box.h) * yu[0] - box.z1 * zu[0],
150 | box.x1 * xu[1] + (box.y1 + box.h) * yu[1] - box.z1 * zu[1]
151 | );
152 | p.vertex(
153 | box.x1 * xu[0] + box.y1 * yu[0] - box.z1 * zu[0],
154 | box.x1 * xu[1] + box.y1 * yu[1] - box.z1 * zu[1]
155 | );
156 | p.endShape(p.CLOSE);
157 |
158 | p.fill(255, 100);
159 | p.beginShape();
160 | p.vertex(
161 | (box.x1 + box.w) * xu[0] + (box.y1 + box.h) * yu[0] + box.z1 * zu[0],
162 | (box.x1 + box.w) * xu[1] + (box.y1 + box.h) * yu[1] + box.z1 * zu[1]
163 | );
164 | p.vertex(
165 | box.x1 * xu[0] + (box.y1 + box.h) * yu[0] + box.z1 * zu[0],
166 | box.x1 * xu[1] + (box.y1 + box.h) * yu[1] + box.z1 * zu[1]
167 | );
168 | p.vertex(
169 | box.x1 * xu[0] + (box.y1 + box.h) * yu[0] - box.z1 * zu[0],
170 | box.x1 * xu[1] + (box.y1 + box.h) * yu[1] - box.z1 * zu[1]
171 | );
172 | p.vertex(
173 | (box.x1 + box.w) * xu[0] + (box.y1 + box.h) * yu[0] - box.z1 * zu[0],
174 | (box.x1 + box.w) * xu[1] + (box.y1 + box.h) * yu[1] - box.z1 * zu[1]
175 | );
176 | p.endShape(p.CLOSE);
177 | }
178 |
179 | if (decorEnabled && colorize && box.filled && box.h > 1.5 && box.w > 3) {
180 | displayLegend(box);
181 | }
182 |
183 | if (decorEnabled && colorize && box.filled && box.crossed) {
184 | displayCross(box);
185 | }
186 | }
187 |
188 | function displayLegend(box) {
189 | p.line(
190 | (box.x1 + 0.5) * xu[0] + (box.y1 + 0.5) * yu[0] + box.z1 * zu[0],
191 | (box.x1 + 0.5) * xu[1] + (box.y1 + 0.5) * yu[1] + box.z1 * zu[1],
192 | (box.x1 + box.legend_width) * xu[0] +
193 | (box.y1 + 0.5) * yu[0] +
194 | box.z1 * zu[0],
195 | (box.x1 + box.legend_width) * xu[1] +
196 | (box.y1 + 0.5) * yu[1] +
197 | box.z1 * zu[1]
198 | );
199 | }
200 |
201 | function displayCross(box) {
202 | p.line(
203 | box.x1 * xu[0] + box.y1 * yu[0] + box.z1 * zu[0],
204 | box.x1 * xu[1] + box.y1 * yu[1] + box.z1 * zu[1],
205 | (box.x1 + box.w) * xu[0] + (box.y1 + box.h) * yu[0] + box.z1 * zu[0],
206 | (box.x1 + box.w) * xu[1] + (box.y1 + box.h) * yu[1] + box.z1 * zu[1]
207 | );
208 | p.line(
209 | (box.x1 + box.w) * xu[0] + box.y1 * yu[0] + box.z1 * zu[0],
210 | (box.x1 + box.w) * xu[1] + box.y1 * yu[1] + box.z1 * zu[1],
211 | box.x1 * xu[0] + (box.y1 + box.h) * yu[0] + box.z1 * zu[0],
212 | box.x1 * xu[1] + (box.y1 + box.h) * yu[1] + box.z1 * zu[1]
213 | );
214 | }
215 |
216 | function createGrid(box) {
217 | const { x1, y1, w, h } = box;
218 | const cols = Math.ceil((Math.random() * w) / 3);
219 | const rows = Math.ceil((Math.random() * h) / 2);
220 | const cell_w = w / cols;
221 | const cell_h = h / rows;
222 |
223 | const apparatus = createApparatus(cell_w, cell_h).map(app => ({
224 | ...app,
225 | z1: 10 + Math.floor(Math.random() * 20)
226 | }));
227 |
228 | let grid = [];
229 | for (let i = 0; i < rows; i++) {
230 | for (let j = 0; j < cols; j++) {
231 | const cell = {
232 | x1: x1 + j * cell_w,
233 | y1: y1 + i * cell_h,
234 | w: cell_w,
235 | h: cell_h,
236 | level: 1,
237 | filled: false
238 | };
239 | const content = apparatus.map(app => ({
240 | ...app,
241 | x1: app.x1 + cell.x1,
242 | y1: app.y1 + cell.y1,
243 | level: 2,
244 | filled: true,
245 | crossed: app.w < 1.5 && app.h < 1.5 && Math.random() < 0.3,
246 | legend_width: 2 + Math.random() * (app.w - 3)
247 | }));
248 |
249 | grid = grid.concat(content);
250 | }
251 | }
252 | return grid;
253 | }
254 |
255 | function createApparatus(w, h) {
256 | const cols = Math.round(w, 0);
257 | const rows = Math.round(h, 0);
258 |
259 | const w_unit = w / cols;
260 | const h_unit = h / rows;
261 |
262 | const generator = new Apparatus(
263 | (cols - 11) / 2,
264 | (rows - 11) / 2,
265 | innerApparatusOptions
266 | );
267 |
268 | return generator.generate().map(a => ({
269 | x1: (a.x1 - 1) * w_unit,
270 | y1: (a.y1 - 1) * h_unit,
271 | w: a.w * w_unit,
272 | h: a.h * h_unit,
273 | col: a.col
274 | }));
275 | }
276 |
277 | function overlaps(a, b) {
278 | const lca = [a.x1, a.y1];
279 | const rca = [a.x1 + a.w, a.y1 + a.h];
280 | const ba = [a.x1, a.y1 + a.h];
281 |
282 | const lcb = [b.x1, b.y1];
283 | const rcb = [b.x1 + b.w, b.y1 + b.h];
284 | const bb = [b.x1, b.y1 + b.h];
285 |
286 | if (a.y1 + a.h <= b.y1 + 0.001 || a.x1 + 0.001 >= b.x1 + b.w) return false;
287 |
288 | if (ba[1] + ba[0] < bb[1] + bb[0]) {
289 | // A is left of B
290 | if (rca[1] + rca[0] <= lcb[1] + lcb[0]) return false;
291 | return rca[1] - rca[0] > lcb[1] - lcb[0]; // positive if A is in front of B
292 | }
293 |
294 | if (ba[1] + ba[0] > bb[1] + bb[0]) {
295 | // A is right of B
296 | if (lca[1] + lca[0] >= rcb[1] + rcb[0]) return false;
297 | return lca[1] - lca[0] > rcb[1] - rcb[0]; // positive if A is in front of B
298 | }
299 | return ba[1] - ba[0] > bb[1] - bb[0];
300 | }
301 |
302 | function get_overlap_graph(boxes) {
303 | const nodes = [];
304 | boxes.forEach((box, i) => nodes.push(i));
305 |
306 | const edges = [];
307 | boxes.forEach((b1, i) => {
308 | boxes.forEach((b2, j) => {
309 | if (overlaps(b1, b2)) edges.push([i, j, b1, b2]);
310 | });
311 | });
312 |
313 | const overlapping = toposort(edges);
314 | return overlapping.reverse().map(i => boxes[i]);
315 | }
316 | };
317 |
318 | new p5(sketch);
319 |
320 | function get_palette() {
321 | const url = window.location.href.split('#');
322 | if (url.length === 1) return tome.get();
323 | return tome.get(url[1]);
324 | }
325 |
--------------------------------------------------------------------------------
/index_cube.js:
--------------------------------------------------------------------------------
1 | import Apparatus from 'apparatus-generator';
2 | import * as tome from 'chromotome';
3 | import toposort from 'toposort';
4 |
5 | let sketch = function(p) {
6 | let THE_SEED;
7 |
8 | const cubedim = 35;
9 | const mag = 15;
10 |
11 | const xr = -Math.PI / 6;
12 | const yr = Math.PI / 2;
13 | const zr = Math.PI / 6;
14 |
15 | const xu = [Math.cos(xr) * mag, Math.sin(xr) * mag]; // X Unit
16 | const yu = [Math.cos(yr) * mag, Math.sin(yr) * mag]; // Y Unit
17 | const zu = [Math.cos(zr) * mag, Math.sin(zr) * mag]; // Z Unit
18 | const nxu = xu.map(v => -v);
19 | const nyu = yu.map(v => -v);
20 | const nzu = zu.map(v => -v);
21 |
22 | const shadingEnabled = true;
23 | const strokeEnabled = true;
24 |
25 | const maxDepth = 3;
26 | const depthSteps = 8;
27 |
28 | const palette = get_palette();
29 | const shade = [0, 80];
30 | const none = [0, 40];
31 | const light = [0, 0];
32 |
33 | const stroke = [0, 40];
34 |
35 | const generator = new Apparatus(cubedim, cubedim, {
36 | simple: true,
37 | extension_chance: 0.95,
38 | horizontal_symmetry: false,
39 | vertical_chance: 0.5
40 | });
41 |
42 | const innerApparatusOptions = {
43 | simple: true,
44 | extension_chance: 0.8,
45 | horizontal_symmetry: false,
46 | vertical_chance: 0.5,
47 | color_mode: 'group',
48 | group_size: 0.4,
49 | colors: palette.colors
50 | };
51 |
52 | let frontLayout, leftLayout, topLayout;
53 |
54 | p.setup = function() {
55 | p.createCanvas(1000, 1000);
56 | THE_SEED = p.floor(p.random(9999999));
57 | p.randomSeed(THE_SEED);
58 | p.noFill();
59 | p.smooth();
60 | p.frameRate(1);
61 | p.background(palette.background ? palette.background : '#eee');
62 | p.strokeJoin(p.ROUND);
63 |
64 | p.draw();
65 | };
66 |
67 | p.draw = function() {
68 | reset();
69 | displayLayout(4, true);
70 | };
71 |
72 | p.keyPressed = function() {
73 | if (p.keyCode === 80) p.saveCanvas('sketch_' + THE_SEED, 'jpeg');
74 | };
75 |
76 | function reset() {
77 | p.background(palette.background ? palette.background : '#eee');
78 | p.translate(p.width / 2, p.height / 2);
79 |
80 | const frontApp = generator.generate(null, null, true);
81 | const leftApp = generator.generate(
82 | frontApp[1].map(i => ({ ...i[1], v: i[1].h })),
83 | null,
84 | true
85 | );
86 | const topApp = generator.generate(
87 | leftApp[1].map(i => ({ ...i[1], v: i[1].h })),
88 | frontApp[1][1].map(i => ({ ...i, h: i.v })),
89 | true
90 | );
91 |
92 | const frontGrids = frontApp[0].map(a => createGrid(a, null, null));
93 | const leftGrids = leftApp[0].map(a => createGrid(a, frontGrids, null));
94 | const topGrids = topApp[0].map(a => createGrid(a, leftGrids, frontGrids));
95 |
96 | frontLayout = get_overlap_graph(frontGrids.flatMap(g => g.content));
97 | leftLayout = get_overlap_graph(leftGrids.flatMap(g => g.content));
98 | topLayout = get_overlap_graph(topGrids.flatMap(g => g.content));
99 | }
100 |
101 | function displayLayout(depth, colorize) {
102 | frontLayout.forEach(i =>
103 | displayBox(i, depth, colorize, xu, yu, zu, [none, shade, light])
104 | );
105 | leftLayout.forEach(i =>
106 | displayBox(i, depth, colorize, yu, nzu, nxu, [shade, light, none])
107 | );
108 | topLayout.forEach(i =>
109 | displayBox(i, depth, colorize, nzu, xu, nyu, [light, none, shade])
110 | );
111 | }
112 |
113 | function displayBox(box, maxLevel, colorize, xu, yu, zu, shades) {
114 | if (box.content != null && box.content.length > 0 && maxLevel > box.level) {
115 | box.content.forEach(c => displayBox(c, maxLevel, colorize));
116 | }
117 |
118 | if (box.filled && colorize) p.fill(box.col);
119 | else p.noFill();
120 |
121 | if (strokeEnabled) p.stroke(stroke[0], stroke[1]);
122 | else p.noStroke();
123 |
124 | p.beginShape();
125 | p.vertex(
126 | box.x1 * xu[0] + box.y1 * yu[0] + box.z1 * zu[0],
127 | box.x1 * xu[1] + box.y1 * yu[1] + box.z1 * zu[1]
128 | );
129 | p.vertex(
130 | (box.x1 + box.w) * xu[0] + box.y1 * yu[0] + box.z1 * zu[0],
131 | (box.x1 + box.w) * xu[1] + box.y1 * yu[1] + box.z1 * zu[1]
132 | );
133 | p.vertex(
134 | (box.x1 + box.w) * xu[0] + (box.y1 + box.h) * yu[0] + box.z1 * zu[0],
135 | (box.x1 + box.w) * xu[1] + (box.y1 + box.h) * yu[1] + box.z1 * zu[1]
136 | );
137 | p.vertex(
138 | box.x1 * xu[0] + (box.y1 + box.h) * yu[0] + box.z1 * zu[0],
139 | box.x1 * xu[1] + (box.y1 + box.h) * yu[1] + box.z1 * zu[1]
140 | );
141 | p.endShape(p.CLOSE);
142 |
143 | p.beginShape();
144 | p.vertex(
145 | box.x1 * xu[0] + box.y1 * yu[0] + box.z1 * zu[0],
146 | box.x1 * xu[1] + box.y1 * yu[1] + box.z1 * zu[1]
147 | );
148 | p.vertex(
149 | box.x1 * xu[0] + (box.y1 + box.h) * yu[0] + box.z1 * zu[0],
150 | box.x1 * xu[1] + (box.y1 + box.h) * yu[1] + box.z1 * zu[1]
151 | );
152 | p.vertex(
153 | box.x1 * xu[0] + (box.y1 + box.h) * yu[0],
154 | box.x1 * xu[1] + (box.y1 + box.h) * yu[1]
155 | );
156 | p.vertex(box.x1 * xu[0] + box.y1 * yu[0], box.x1 * xu[1] + box.y1 * yu[1]);
157 | p.endShape(p.CLOSE);
158 |
159 | p.beginShape();
160 | p.vertex(
161 | box.x1 * xu[0] + box.y1 * yu[0] + box.z1 * zu[0],
162 | box.x1 * xu[1] + box.y1 * yu[1] + box.z1 * zu[1]
163 | );
164 | p.vertex(
165 | (box.x1 + box.w) * xu[0] + box.y1 * yu[0] + box.z1 * zu[0],
166 | (box.x1 + box.w) * xu[1] + box.y1 * yu[1] + box.z1 * zu[1]
167 | );
168 | p.vertex(
169 | (box.x1 + box.w) * xu[0] + box.y1 * yu[0],
170 | (box.x1 + box.w) * xu[1] + box.y1 * yu[1]
171 | );
172 | p.vertex(box.x1 * xu[0] + box.y1 * yu[0], box.x1 * xu[1] + box.y1 * yu[1]);
173 | p.endShape(p.CLOSE);
174 |
175 | if (shadingEnabled) {
176 | p.noStroke();
177 |
178 | p.fill(shades[0][0], shades[0][1]);
179 | p.beginShape();
180 | p.vertex(
181 | box.x1 * xu[0] + box.y1 * yu[0] + box.z1 * zu[0],
182 | box.x1 * xu[1] + box.y1 * yu[1] + box.z1 * zu[1]
183 | );
184 | p.vertex(
185 | (box.x1 + box.w) * xu[0] + box.y1 * yu[0] + box.z1 * zu[0],
186 | (box.x1 + box.w) * xu[1] + box.y1 * yu[1] + box.z1 * zu[1]
187 | );
188 | p.vertex(
189 | (box.x1 + box.w) * xu[0] + (box.y1 + box.h) * yu[0] + box.z1 * zu[0],
190 | (box.x1 + box.w) * xu[1] + (box.y1 + box.h) * yu[1] + box.z1 * zu[1]
191 | );
192 | p.vertex(
193 | box.x1 * xu[0] + (box.y1 + box.h) * yu[0] + box.z1 * zu[0],
194 | box.x1 * xu[1] + (box.y1 + box.h) * yu[1] + box.z1 * zu[1]
195 | );
196 | p.endShape(p.CLOSE);
197 |
198 | p.fill(shades[1][0], shades[1][1]);
199 | p.beginShape();
200 | p.vertex(
201 | box.x1 * xu[0] + box.y1 * yu[0] + box.z1 * zu[0],
202 | box.x1 * xu[1] + box.y1 * yu[1] + box.z1 * zu[1]
203 | );
204 | p.vertex(
205 | box.x1 * xu[0] + (box.y1 + box.h) * yu[0] + box.z1 * zu[0],
206 | box.x1 * xu[1] + (box.y1 + box.h) * yu[1] + box.z1 * zu[1]
207 | );
208 | p.vertex(
209 | box.x1 * xu[0] + (box.y1 + box.h) * yu[0],
210 | box.x1 * xu[1] + (box.y1 + box.h) * yu[1]
211 | );
212 | p.vertex(
213 | box.x1 * xu[0] + box.y1 * yu[0],
214 | box.x1 * xu[1] + box.y1 * yu[1]
215 | );
216 | p.endShape(p.CLOSE);
217 |
218 | p.fill(shades[2][0], shades[2][1]);
219 | p.beginShape();
220 | p.vertex(
221 | box.x1 * xu[0] + box.y1 * yu[0] + box.z1 * zu[0],
222 | box.x1 * xu[1] + box.y1 * yu[1] + box.z1 * zu[1]
223 | );
224 | p.vertex(
225 | (box.x1 + box.w) * xu[0] + box.y1 * yu[0] + box.z1 * zu[0],
226 | (box.x1 + box.w) * xu[1] + box.y1 * yu[1] + box.z1 * zu[1]
227 | );
228 | p.vertex(
229 | (box.x1 + box.w) * xu[0] + box.y1 * yu[0],
230 | (box.x1 + box.w) * xu[1] + box.y1 * yu[1]
231 | );
232 | p.vertex(
233 | box.x1 * xu[0] + box.y1 * yu[0],
234 | box.x1 * xu[1] + box.y1 * yu[1]
235 | );
236 | p.endShape(p.CLOSE);
237 | }
238 | }
239 |
240 | function createGrid(box, topside, leftside) {
241 | const { x1, y1, w, h } = box;
242 |
243 | const topsideGrid =
244 | topside && y1 == 1
245 | ? topside.filter(c => c.x1 == 1 && c.y1 == x1)[0]
246 | : null;
247 |
248 | const leftsideGrid =
249 | leftside && x1 == 1
250 | ? leftside.filter(c => c.y1 == 1 && c.x1 == y1)[0]
251 | : null;
252 |
253 | const cols = topsideGrid
254 | ? topsideGrid.rows
255 | : Math.ceil((Math.random() * w) / 3);
256 | const rows = leftsideGrid
257 | ? leftsideGrid.cols
258 | : Math.ceil((Math.random() * h) / 2);
259 |
260 | const cell_w = w / cols;
261 | const cell_h = h / rows;
262 |
263 | const init_top = topsideGrid
264 | ? topsideGrid.apparatus.map(i => ({ ...i[1], v: i[1].h }))
265 | : null;
266 |
267 | const init_left = leftsideGrid
268 | ? leftsideGrid.apparatus[1].map(i => ({ ...i, h: i.v }))
269 | : null;
270 |
271 | const apparatus = createApparatus(cell_w, cell_h, init_top, init_left);
272 | let grid = [];
273 | for (let i = 0; i < rows; i++) {
274 | for (let j = 0; j < cols; j++) {
275 | const content = apparatus[0].map(app => {
276 | const xpos = x1 + app.x1 + j * cell_w - 1;
277 | const ypos = y1 + app.y1 + i * cell_h - 1;
278 | let y_offset =
279 | topsideGrid && i == 0 && ypos <= 0
280 | ? topsideGrid.content.filter(
281 | c => c.x1 <= 0 && p.max(c.y1, 0) == xpos
282 | )[0].z1
283 | : 0;
284 | let x_offset =
285 | leftsideGrid && j == 0 && xpos <= 0
286 | ? leftsideGrid.content.filter(
287 | c => c.y1 <= 0 && p.max(c.x1, 0) == ypos
288 | )[0].z1
289 | : 0;
290 | return {
291 | ...app,
292 | x1: xpos - x_offset,
293 | y1: ypos - y_offset,
294 | w: app.w + x_offset,
295 | h: app.h + y_offset,
296 | level: 2,
297 | filled: true,
298 | crossed: app.w < 1.5 && app.h < 1.5 && Math.random() < 0.3,
299 | legend_width: 2 + Math.random() * (app.w - 3)
300 | };
301 | });
302 |
303 | grid = grid.concat(content);
304 | }
305 | }
306 | return {
307 | x1: x1,
308 | y1: y1,
309 | cols: cols,
310 | rows: rows,
311 | apparatus: apparatus[1],
312 | content: grid
313 | };
314 | }
315 |
316 | function createApparatus(w, h, top, left) {
317 | const cols = Math.round(w, 0);
318 | const rows = Math.round(h, 0);
319 |
320 | const w_unit = w / cols;
321 | const h_unit = h / rows;
322 |
323 | const generator = new Apparatus(
324 | (cols - 11) / 2,
325 | (rows - 11) / 2,
326 | innerApparatusOptions
327 | );
328 |
329 | const apparatus = generator.generate(top, left, true);
330 | apparatus[0] = apparatus[0].map(a => ({
331 | x1: (a.x1 - 1) * w_unit,
332 | y1: (a.y1 - 1) * h_unit,
333 | z1: maxDepth * (Math.floor(Math.random() * depthSteps) / depthSteps),
334 | w: a.w * w_unit,
335 | h: a.h * h_unit,
336 | col: a.col
337 | }));
338 |
339 | return apparatus;
340 | }
341 |
342 | function overlaps(a, b) {
343 | const lca = [a.x1 + a.w, a.y1];
344 | const rca = [a.x1, a.y1 + a.h];
345 | const ba = [a.x1, a.y1];
346 |
347 | const lcb = [b.x1 + b.w, b.y1];
348 | const rcb = [b.x1, b.y1 + b.h];
349 | const bb = [b.x1, b.y1];
350 |
351 | if (a.y1 + 0.005 >= b.y1 + b.h || a.x1 + 0.005 >= b.x1 + b.w) return false;
352 |
353 | if (ba[1] - ba[0] < bb[1] - bb[0]) {
354 | // A is left of B
355 | if (rca[1] - rca[0] <= lcb[1] - lcb[0]) return false;
356 | return rca[1] + rca[0] < lcb[1] + lcb[0]; // positive if A is in front of B
357 | }
358 |
359 | if (ba[1] - ba[0] > bb[1] - bb[0]) {
360 | // A is right of B
361 | if (lca[1] - lca[0] >= rcb[1] - rcb[0]) return false;
362 | return lca[1] + lca[0] < rcb[1] + rcb[0]; // positive if A is in front of B
363 | }
364 | return ba[1] + ba[0] < bb[1] + bb[0];
365 | }
366 |
367 | function get_overlap_graph(boxes) {
368 | const nodes = [];
369 | boxes.forEach((box, i) => nodes.push(i));
370 |
371 | const edges = [];
372 | boxes.forEach((b1, i) => {
373 | boxes.forEach((b2, j) => {
374 | if (overlaps(b1, b2)) edges.push([i, j, b1, b2]);
375 | });
376 | });
377 |
378 | const overlapping = toposort(edges);
379 | return overlapping.reverse().map(i => boxes[i]);
380 | }
381 | };
382 |
383 | new p5(sketch);
384 |
385 | function get_palette() {
386 | const url = window.location.href.split('#');
387 | if (url.length === 1) return tome.get('kov_06b');
388 | return tome.get(url[1]);
389 | }
390 |
--------------------------------------------------------------------------------
/index_elevate.js:
--------------------------------------------------------------------------------
1 | import Apparatus from 'apparatus-generator';
2 | import * as tome from 'chromotome';
3 | import toposort from 'toposort';
4 |
5 | let sketch = function(p) {
6 | let THE_SEED;
7 |
8 | const mag = 18;
9 | const xu = [1 * mag, -0.2 * mag]; // X Unit
10 | const yu = [0.3 * mag, 0.8 * mag]; // Y Unit
11 | const zu = [0.02 * mag, -0.1 * mag]; // Y Unit
12 |
13 | const decorEnabled = false;
14 | const shadingEnabled = true;
15 | const strokeEnabled = true;
16 |
17 | const palette = get_palette();
18 | const generator = new Apparatus(10, 18, {
19 | simple: true,
20 | extension_chance: 0.97,
21 | horizontal_symmetry: false,
22 | vertical_chance: 0.3
23 | });
24 |
25 | const innerApparatusOptions = {
26 | simple: true,
27 | extension_chance: 0.8,
28 | horizontal_symmetry: false,
29 | vertical_chance: 0.3,
30 | color_mode: 'random',
31 | colors: palette.colors
32 | };
33 |
34 | let layout;
35 |
36 | p.setup = function() {
37 | p.createCanvas(950, 950);
38 | THE_SEED = p.floor(p.random(9999999));
39 | p.randomSeed(THE_SEED);
40 | p.noFill();
41 | p.smooth();
42 | p.frameRate(1);
43 | p.background(palette.background ? palette.background : '#eee');
44 | p.strokeJoin(p.ROUND);
45 |
46 | if (strokeEnabled) p.stroke(palette.stroke ? palette.stroke : '#111');
47 | else p.noStroke();
48 | };
49 |
50 | p.draw = function() {
51 | reset();
52 | displayLayout(4, true);
53 | };
54 |
55 | p.keyPressed = function() {
56 | if (p.keyCode === 80) p.saveCanvas('sketch_' + THE_SEED, 'jpeg');
57 | };
58 |
59 | function reset() {
60 | p.background(palette.background ? palette.background : '#eee');
61 | layout = get_overlap_graph(generator.generate().flatMap(createGrid));
62 | }
63 |
64 | function displayLayout(depth, colorize) {
65 | p.translate(45, 205);
66 | layout.forEach(i => {
67 | displayBox(i, depth, colorize);
68 | });
69 | }
70 |
71 | function displayBox(box, maxLevel, colorize) {
72 | if (box.content != null && box.content.length > 0 && maxLevel > box.level) {
73 | box.content.forEach(c => displayBox(c, maxLevel, colorize));
74 | }
75 |
76 | if (box.filled && colorize) p.fill(box.col);
77 | else p.noFill();
78 |
79 | p.beginShape();
80 | p.vertex(
81 | box.x1 * xu[0] + box.y1 * yu[0] + box.z1 * zu[0],
82 | box.x1 * xu[1] + box.y1 * yu[1] + box.z1 * zu[1]
83 | );
84 | p.vertex(
85 | (box.x1 + box.w) * xu[0] + box.y1 * yu[0] + box.z1 * zu[0],
86 | (box.x1 + box.w) * xu[1] + box.y1 * yu[1] + box.z1 * zu[1]
87 | );
88 | p.vertex(
89 | (box.x1 + box.w) * xu[0] + (box.y1 + box.h) * yu[0] + box.z1 * zu[0],
90 | (box.x1 + box.w) * xu[1] + (box.y1 + box.h) * yu[1] + box.z1 * zu[1]
91 | );
92 | p.vertex(
93 | box.x1 * xu[0] + (box.y1 + box.h) * yu[0] + box.z1 * zu[0],
94 | box.x1 * xu[1] + (box.y1 + box.h) * yu[1] + box.z1 * zu[1]
95 | );
96 | p.endShape(p.CLOSE);
97 |
98 | p.beginShape();
99 | p.vertex(
100 | box.x1 * xu[0] + box.y1 * yu[0] + box.z1 * zu[0],
101 | box.x1 * xu[1] + box.y1 * yu[1] + box.z1 * zu[1]
102 | );
103 | p.vertex(
104 | box.x1 * xu[0] + (box.y1 + box.h) * yu[0] + box.z1 * zu[0],
105 | box.x1 * xu[1] + (box.y1 + box.h) * yu[1] + box.z1 * zu[1]
106 | );
107 | p.vertex(
108 | box.x1 * xu[0] + (box.y1 + box.h) * yu[0],
109 | box.x1 * xu[1] + (box.y1 + box.h) * yu[1]
110 | );
111 | p.vertex(box.x1 * xu[0] + box.y1 * yu[0], box.x1 * xu[1] + box.y1 * yu[1]);
112 | p.endShape(p.CLOSE);
113 |
114 | p.beginShape();
115 | p.vertex(
116 | (box.x1 + box.w) * xu[0] + (box.y1 + box.h) * yu[0] + box.z1 * zu[0],
117 | (box.x1 + box.w) * xu[1] + (box.y1 + box.h) * yu[1] + box.z1 * zu[1]
118 | );
119 | p.vertex(
120 | box.x1 * xu[0] + (box.y1 + box.h) * yu[0] + box.z1 * zu[0],
121 | box.x1 * xu[1] + (box.y1 + box.h) * yu[1] + box.z1 * zu[1]
122 | );
123 | p.vertex(
124 | box.x1 * xu[0] + (box.y1 + box.h) * yu[0],
125 | box.x1 * xu[1] + (box.y1 + box.h) * yu[1]
126 | );
127 | p.vertex(
128 | (box.x1 + box.w) * xu[0] + (box.y1 + box.h) * yu[0],
129 | (box.x1 + box.w) * xu[1] + (box.y1 + box.h) * yu[1]
130 | );
131 | p.endShape(p.CLOSE);
132 |
133 | if (shadingEnabled) {
134 | p.fill(0, 80);
135 | p.beginShape();
136 | p.vertex(
137 | box.x1 * xu[0] + box.y1 * yu[0] + box.z1 * zu[0],
138 | box.x1 * xu[1] + box.y1 * yu[1] + box.z1 * zu[1]
139 | );
140 | p.vertex(
141 | box.x1 * xu[0] + (box.y1 + box.h) * yu[0] + box.z1 * zu[0],
142 | box.x1 * xu[1] + (box.y1 + box.h) * yu[1] + box.z1 * zu[1]
143 | );
144 | p.vertex(
145 | box.x1 * xu[0] + (box.y1 + box.h) * yu[0],
146 | box.x1 * xu[1] + (box.y1 + box.h) * yu[1]
147 | );
148 | p.vertex(
149 | box.x1 * xu[0] + box.y1 * yu[0],
150 | box.x1 * xu[1] + box.y1 * yu[1]
151 | );
152 | p.endShape(p.CLOSE);
153 |
154 | p.beginShape();
155 | p.vertex(
156 | (box.x1 + box.w) * xu[0] + (box.y1 + box.h) * yu[0] + box.z1 * zu[0],
157 | (box.x1 + box.w) * xu[1] + (box.y1 + box.h) * yu[1] + box.z1 * zu[1]
158 | );
159 | p.vertex(
160 | box.x1 * xu[0] + (box.y1 + box.h) * yu[0] + box.z1 * zu[0],
161 | box.x1 * xu[1] + (box.y1 + box.h) * yu[1] + box.z1 * zu[1]
162 | );
163 | p.vertex(
164 | box.x1 * xu[0] + (box.y1 + box.h) * yu[0],
165 | box.x1 * xu[1] + (box.y1 + box.h) * yu[1]
166 | );
167 | p.vertex(
168 | (box.x1 + box.w) * xu[0] + (box.y1 + box.h) * yu[0],
169 | (box.x1 + box.w) * xu[1] + (box.y1 + box.h) * yu[1]
170 | );
171 | p.endShape(p.CLOSE);
172 | }
173 |
174 | if (decorEnabled && colorize && box.filled && box.h > 1.5 && box.w > 3) {
175 | displayLegend(box);
176 | }
177 |
178 | if (decorEnabled && colorize && box.filled && box.crossed) {
179 | displayCross(box);
180 | }
181 | }
182 |
183 | function displayLegend(box) {
184 | p.line(
185 | (box.x1 + 0.5) * xu[0] + (box.y1 + 0.5) * yu[0] + box.z1 * zu[0],
186 | (box.x1 + 0.5) * xu[1] + (box.y1 + 0.5) * yu[1] + box.z1 * zu[1],
187 | (box.x1 + box.legend_width) * xu[0] +
188 | (box.y1 + 0.5) * yu[0] +
189 | box.z1 * zu[0],
190 | (box.x1 + box.legend_width) * xu[1] +
191 | (box.y1 + 0.5) * yu[1] +
192 | box.z1 * zu[1]
193 | );
194 | }
195 |
196 | function displayCross(box) {
197 | p.line(
198 | box.x1 * xu[0] + box.y1 * yu[0] + box.z1 * zu[0],
199 | box.x1 * xu[1] + box.y1 * yu[1] + box.z1 * zu[1],
200 | (box.x1 + box.w) * xu[0] + (box.y1 + box.h) * yu[0] + box.z1 * zu[0],
201 | (box.x1 + box.w) * xu[1] + (box.y1 + box.h) * yu[1] + box.z1 * zu[1]
202 | );
203 | p.line(
204 | (box.x1 + box.w) * xu[0] + box.y1 * yu[0] + box.z1 * zu[0],
205 | (box.x1 + box.w) * xu[1] + box.y1 * yu[1] + box.z1 * zu[1],
206 | box.x1 * xu[0] + (box.y1 + box.h) * yu[0] + box.z1 * zu[0],
207 | box.x1 * xu[1] + (box.y1 + box.h) * yu[1] + box.z1 * zu[1]
208 | );
209 | }
210 |
211 | function createGrid(box) {
212 | const { x1, y1, w, h } = box;
213 | const cols = Math.ceil((Math.random() * w) / 3);
214 | const rows = Math.ceil((Math.random() * h) / 2);
215 | const cell_w = w / cols;
216 | const cell_h = h / rows;
217 |
218 | const apparatus = createApparatus(cell_w, cell_h).map(app => ({
219 | ...app,
220 | z1: 25 + Math.floor(Math.random() * 10)
221 | }));
222 |
223 | let grid = [];
224 | for (let i = 0; i < rows; i++) {
225 | for (let j = 0; j < cols; j++) {
226 | const cell = {
227 | x1: x1 + j * cell_w,
228 | y1: y1 + i * cell_h,
229 | w: cell_w,
230 | h: cell_h,
231 | level: 1,
232 | filled: false
233 | };
234 | const content = apparatus.map(app => ({
235 | ...app,
236 | x1: app.x1 + cell.x1,
237 | y1: app.y1 + cell.y1,
238 | level: 2,
239 | filled: true,
240 | crossed: app.w < 1.5 && app.h < 1.5 && Math.random() < 0.3,
241 | legend_width: 2 + Math.random() * (app.w - 3)
242 | }));
243 |
244 | grid = grid.concat(content);
245 | }
246 | }
247 | return grid;
248 | }
249 |
250 | function createApparatus(w, h) {
251 | const cols = Math.round(w, 0);
252 | const rows = Math.round(h, 0);
253 |
254 | const w_unit = w / cols;
255 | const h_unit = h / rows;
256 |
257 | const generator = new Apparatus(
258 | (cols - 11) / 2,
259 | (rows - 11) / 2,
260 | innerApparatusOptions
261 | );
262 |
263 | return generator.generate().map(a => ({
264 | x1: (a.x1 - 1) * w_unit,
265 | y1: (a.y1 - 1) * h_unit,
266 | w: a.w * w_unit,
267 | h: a.h * h_unit,
268 | col: a.col
269 | }));
270 | }
271 |
272 | function overlaps(a, b) {
273 | const lca = [a.x1, a.y1];
274 | const rca = [a.x1 + a.w, a.y1 + a.h];
275 | const ba = [a.x1, a.y1 + a.h];
276 |
277 | const lcb = [b.x1, b.y1];
278 | const rcb = [b.x1 + b.w, b.y1 + b.h];
279 | const bb = [b.x1, b.y1 + b.h];
280 |
281 | if (a.y1 + a.h <= b.y1 + 0.001 || a.x1 + 0.001 >= b.x1 + b.w) return false;
282 |
283 | if (ba[1] + ba[0] < bb[1] + bb[0]) {
284 | // A is left of B
285 | if (rca[1] + rca[0] <= lcb[1] + lcb[0]) return false;
286 | return rca[1] - rca[0] > lcb[1] - lcb[0]; // positive if A is in front of B
287 | }
288 |
289 | if (ba[1] + ba[0] > bb[1] + bb[0]) {
290 | // A is right of B
291 | if (lca[1] + lca[0] >= rcb[1] + rcb[0]) return false;
292 | return lca[1] - lca[0] > rcb[1] - rcb[0]; // positive if A is in front of B
293 | }
294 | return ba[1] - ba[0] > bb[1] - bb[0];
295 | }
296 |
297 | function get_overlap_graph(boxes) {
298 | const nodes = [];
299 | boxes.forEach((box, i) => nodes.push(i));
300 |
301 | const edges = [];
302 | boxes.forEach((b1, i) => {
303 | boxes.forEach((b2, j) => {
304 | if (overlaps(b1, b2)) edges.push([i, j, b1, b2]);
305 | });
306 | });
307 |
308 | const overlapping = toposort(edges);
309 | return overlapping.reverse().map(i => boxes[i]);
310 | }
311 | };
312 |
313 | new p5(sketch);
314 |
315 | function get_palette() {
316 | const url = window.location.href.split('#');
317 | if (url.length === 1) return tome.get();
318 | return tome.get(url[1]);
319 | }
320 |
--------------------------------------------------------------------------------
/index_full.js:
--------------------------------------------------------------------------------
1 | import Apparatus from 'apparatus-generator';
2 | import * as tome from 'chromotome';
3 |
4 | let sketch = function (p) {
5 | let THE_SEED;
6 |
7 | const mag = 22;
8 | const xu = [1 * mag, -0.2 * mag]; // X Unit
9 | const yu = [0.3 * mag, 0.8 * mag]; // Y Unit
10 |
11 | const palette = get_palette();
12 | const generator = new Apparatus(40, 36, {
13 | simple: true,
14 | extension_chance: 0.97,
15 | horizontal_symmetry: false,
16 | vertical_chance: 0.3,
17 | });
18 |
19 | const innerApparatusOptions = {
20 | simple: true,
21 | extension_chance: 0.8,
22 | horizontal_symmetry: false,
23 | vertical_chance: 0.3,
24 | color_mode: 'main',
25 | colors: palette.colors,
26 | };
27 |
28 | let layout;
29 | let tick;
30 |
31 | p.setup = function () {
32 | p.createCanvas(950, 950);
33 | THE_SEED = p.floor(p.random(9999999));
34 | p.randomSeed(THE_SEED);
35 | p.noFill();
36 | p.smooth();
37 | p.frameRate(3);
38 | p.stroke(palette.stroke ? palette.stroke : '#111');
39 | p.background(palette.background ? palette.background : '#eee');
40 |
41 | tick = 0;
42 | };
43 |
44 | p.draw = function () {
45 | if (tick % 9 == 0) reset();
46 | displayLayout(tick % 9, tick % 9 > 2);
47 | tick++;
48 | };
49 |
50 | p.keyPressed = function () {
51 | if (p.keyCode === 80) p.saveCanvas('sketch_' + THE_SEED, 'jpeg');
52 | };
53 |
54 | function reset() {
55 | p.background(palette.background ? palette.background : '#eee');
56 | layout = generator
57 | .generate()
58 | .map((b) => ({ ...b, level: 0, filled: false, content: createGrid(b) }));
59 | }
60 |
61 | function displayLayout(depth, colorize) {
62 | p.translate(-500, -80);
63 | layout.forEach((box) => {
64 | displayBox(box, depth, colorize);
65 | });
66 | }
67 |
68 | function displayBox(box, maxLevel, colorize) {
69 | if (box.content != null && box.content.length > 0 && maxLevel > box.level) {
70 | box.content.forEach((c) => displayBox(c, maxLevel, colorize));
71 | }
72 |
73 | if (box.filled && colorize) p.fill(box.col);
74 | else p.noFill();
75 |
76 | p.strokeWeight(3 / (box.level + 1));
77 | p.beginShape();
78 | p.vertex(box.x1 * xu[0] + box.y1 * yu[0], box.x1 * xu[1] + box.y1 * yu[1]);
79 | p.vertex(
80 | (box.x1 + box.w) * xu[0] + box.y1 * yu[0],
81 | (box.x1 + box.w) * xu[1] + box.y1 * yu[1]
82 | );
83 | p.vertex(
84 | (box.x1 + box.w) * xu[0] + (box.y1 + box.h) * yu[0],
85 | (box.x1 + box.w) * xu[1] + (box.y1 + box.h) * yu[1]
86 | );
87 | p.vertex(
88 | box.x1 * xu[0] + (box.y1 + box.h) * yu[0],
89 | box.x1 * xu[1] + (box.y1 + box.h) * yu[1]
90 | );
91 | p.endShape(p.CLOSE);
92 |
93 | if (colorize && box.filled && box.h > 1.5 && box.w > 3) {
94 | displayLegend(box);
95 | }
96 |
97 | if (colorize && box.filled && box.crossed) {
98 | displayCross(box);
99 | }
100 | }
101 |
102 | function displayLegend(box) {
103 | p.line(
104 | (box.x1 + 0.5) * xu[0] + (box.y1 + 0.5) * yu[0],
105 | (box.x1 + 0.5) * xu[1] + (box.y1 + 0.5) * yu[1],
106 | (box.x1 + box.legend_width) * xu[0] + (box.y1 + 0.5) * yu[0],
107 | (box.x1 + box.legend_width) * xu[1] + (box.y1 + 0.5) * yu[1]
108 | );
109 | }
110 |
111 | function displayCross(box) {
112 | p.line(
113 | box.x1 * xu[0] + box.y1 * yu[0],
114 | box.x1 * xu[1] + box.y1 * yu[1],
115 | (box.x1 + box.w) * xu[0] + (box.y1 + box.h) * yu[0],
116 | (box.x1 + box.w) * xu[1] + (box.y1 + box.h) * yu[1]
117 | );
118 | p.line(
119 | (box.x1 + box.w) * xu[0] + box.y1 * yu[0],
120 | (box.x1 + box.w) * xu[1] + box.y1 * yu[1],
121 | box.x1 * xu[0] + (box.y1 + box.h) * yu[0],
122 | box.x1 * xu[1] + (box.y1 + box.h) * yu[1]
123 | );
124 | }
125 |
126 | function createGrid(box) {
127 | const { x1, y1, w, h } = box;
128 | const cols = Math.ceil((Math.random() * w) / 3);
129 | const rows = Math.ceil((Math.random() * h) / 2);
130 | const cell_w = w / cols;
131 | const cell_h = h / rows;
132 |
133 | const apparatus = createApparatus(cell_w, cell_h);
134 |
135 | const grid = [];
136 | for (let i = 0; i < rows; i++) {
137 | for (let j = 0; j < cols; j++) {
138 | const cell = {
139 | x1: x1 + j * cell_w,
140 | y1: y1 + i * cell_h,
141 | w: cell_w,
142 | h: cell_h,
143 | level: 1,
144 | filled: false,
145 | };
146 | const content = apparatus.map((app) => ({
147 | ...app,
148 | x1: app.x1 + cell.x1,
149 | y1: app.y1 + cell.y1,
150 | level: 2,
151 | filled: true,
152 | crossed: app.w < 1.5 && app.h < 1.5 && Math.random() < 0.3,
153 | legend_width: 2 + Math.random() * (app.w - 3),
154 | }));
155 |
156 | grid.push({ ...cell, content: content });
157 | }
158 | }
159 | return grid;
160 | }
161 |
162 | function createApparatus(w, h) {
163 | const cols = Math.round(w, 0);
164 | const rows = Math.round(h, 0);
165 |
166 | const w_unit = w / cols;
167 | const h_unit = h / rows;
168 |
169 | const generator = new Apparatus(
170 | (cols - 11) / 2,
171 | (rows - 11) / 2,
172 | innerApparatusOptions
173 | );
174 |
175 | return generator.generate().map((a) => ({
176 | x1: (a.x1 - 1) * w_unit,
177 | y1: (a.y1 - 1) * h_unit,
178 | w: a.w * w_unit,
179 | h: a.h * h_unit,
180 | col: a.col,
181 | }));
182 | }
183 | };
184 |
185 | new p5(sketch);
186 |
187 | function get_palette() {
188 | const url = window.location.href.split('#');
189 | if (url.length === 1) return tome.get('dt04');
190 | return tome.get(url[1]);
191 | }
192 |
--------------------------------------------------------------------------------
/index_interactive.js:
--------------------------------------------------------------------------------
1 | import Apparatus from 'apparatus-generator';
2 | import * as tome from 'chromotome';
3 | import toposort from 'toposort';
4 | import perspective from 'change-perspective';
5 |
6 | import ui from './ui';
7 | import display from './display';
8 | import { generateCSV } from './csv';
9 |
10 | import presets from './presets_interactive.js';
11 |
12 | let opts = {
13 | // gets values from presets.js
14 | cubedimX: 0,
15 | cubedimY: 0,
16 | cubedimZ: 0,
17 | depthDim: 0,
18 | mag: 0,
19 | tx: 0,
20 | ty: 0,
21 | shadeOpacityFront: 0,
22 | shadeOpacityLeft: 0,
23 | shadeOpacityTop: 0,
24 | outerStrokeWeight: 0,
25 | innerStrokeWeight: 0,
26 | outerSize: 0,
27 | minGridSize: 0,
28 | innerSize: 0,
29 | perspective: 0,
30 | colorMode: '',
31 | palette: '',
32 | paletteShift: 0,
33 | };
34 |
35 | let sketch = function (p) {
36 | let THE_SEED;
37 |
38 | let cubedimX;
39 | let cubedimY;
40 | let cubedimZ;
41 | let tx, ty;
42 |
43 | const xr = (-1 * Math.PI) / 6;
44 | const yr = (3 * Math.PI) / 6;
45 | const zr = (1 * Math.PI) / 6;
46 |
47 | let xu, yu, zu;
48 | let nxu, nyu, nzu;
49 |
50 | let maxDepth;
51 | const depthSteps = 6;
52 |
53 | let paletteShift;
54 | let palette;
55 | let strokeCol;
56 | let shadeOpacityFront, shadeOpacityLeft, shadeOpacityTop;
57 | let outerStrokeWeight, innerStrokeWeight;
58 |
59 | let sectionAppOpts, atomAppOpts;
60 | let minGridSize;
61 |
62 | let persp;
63 |
64 | let frontLayout, leftLayout, topLayout;
65 |
66 | p.setup = function () {
67 | p.createCanvas(1000, 1000);
68 | THE_SEED = p.floor(p.random(9999999));
69 | p.randomSeed(THE_SEED);
70 | p.pixelDensity(2);
71 | p.noFill();
72 | p.smooth();
73 | p.frameRate(1);
74 | p.strokeJoin(p.ROUND);
75 | p.noLoop();
76 |
77 | ui(opts, generateAndDraw, updateAndDraw, print, presets);
78 |
79 | generateAndDraw();
80 | };
81 |
82 | function generateAndDraw() {
83 | updateGlobals(opts);
84 | reset();
85 | displayLayout();
86 | }
87 |
88 | function updateAndDraw() {
89 | updateGlobals(opts);
90 | displayLayout();
91 | }
92 |
93 | function updateGlobals(opts) {
94 | cubedimX = opts.cubedimX;
95 | cubedimY = opts.cubedimY;
96 | cubedimZ = opts.cubedimZ;
97 |
98 | tx = opts.tx;
99 | ty = opts.ty;
100 |
101 | xu = [Math.cos(xr) * opts.mag, Math.sin(xr) * opts.mag];
102 | yu = [Math.cos(yr) * opts.mag, Math.sin(yr) * opts.mag];
103 | zu = [Math.cos(zr) * opts.mag, Math.sin(zr) * opts.mag];
104 |
105 | nxu = xu.map((v) => -v);
106 | nyu = yu.map((v) => -v);
107 | nzu = zu.map((v) => -v);
108 |
109 | shadeOpacityFront = opts.shadeOpacityFront;
110 | shadeOpacityLeft = opts.shadeOpacityLeft;
111 | shadeOpacityTop = opts.shadeOpacityTop;
112 | outerStrokeWeight = opts.outerStrokeWeight;
113 | innerStrokeWeight = opts.innerStrokeWeight;
114 |
115 | maxDepth = opts.depthDim;
116 | persp = opts.perspective;
117 |
118 | palette = tome.get(opts.palette);
119 | paletteShift = opts.paletteShift;
120 | strokeCol = palette.stroke ? palette.stroke : '#000';
121 |
122 | minGridSize = opts.minGridSize;
123 |
124 | sectionAppOpts = {
125 | simple: true,
126 | extension_chance: opts.outerSize,
127 | horizontal_symmetry: false,
128 | vertical_chance: 0.5,
129 | };
130 |
131 | atomAppOpts = {
132 | simple: true,
133 | extension_chance: opts.innerSize,
134 | horizontal_symmetry: false,
135 | vertical_chance: 0.5,
136 | color_mode: opts.colorMode,
137 | group_size: 0.4,
138 | colors: [...Array(1000).keys()],
139 | };
140 | }
141 |
142 | function reset() {
143 | const generatorFront = new Apparatus(cubedimX, cubedimY, sectionAppOpts);
144 | const generatorLeft = new Apparatus(cubedimY, cubedimZ, sectionAppOpts);
145 | const generatorTop = new Apparatus(cubedimZ, cubedimX, sectionAppOpts);
146 | const frontApp = generatorFront.generate(null, null, true);
147 | const leftApp = generatorLeft.generate(
148 | frontApp[1].map((i) => ({ ...i[1], v: i[1].h })),
149 | null,
150 | true
151 | );
152 | const topApp = generatorTop.generate(
153 | leftApp[1].map((i) => ({ ...i[1], v: i[1].h })),
154 | frontApp[1][1].map((i) => ({ ...i, h: i.v })),
155 | true
156 | );
157 |
158 | const frontGrids = frontApp[0].map((a) => createGrid(a, null, null));
159 | const leftGrids = leftApp[0].map((a) => createGrid(a, frontGrids, null));
160 | const topGrids = topApp[0].map((a) => createGrid(a, leftGrids, frontGrids));
161 |
162 | frontLayout = get_overlap_graph(frontGrids.flatMap((g) => g.content));
163 | leftLayout = get_overlap_graph(leftGrids.flatMap((g) => g.content));
164 | topLayout = get_overlap_graph(topGrids.flatMap((g) => g.content));
165 | }
166 |
167 | function displayLayout() {
168 | p.push();
169 | p.translate(tx + p.width / 2, ty + p.height / 2);
170 | p.background(palette.background ? palette.background : '#eee');
171 |
172 | const ft = perspective(...getSrcDst(xu, yu, persp, cubedimX));
173 | const lt = perspective(...getSrcDst(yu, nzu, persp, cubedimX));
174 | const tt = perspective(...getSrcDst(nzu, xu, persp, cubedimX));
175 |
176 | const sf = shadeOpacityFront;
177 | const sl = shadeOpacityLeft;
178 | const st = shadeOpacityTop;
179 |
180 | frontLayout.forEach((i) => displayBox(i, xu, yu, zu, [sf, sl, st], true, true, ft, tt, lt));
181 | leftLayout.forEach((i) => displayBox(i, yu, nzu, nxu, [sl, st, sf], false, true, lt, ft, tt));
182 | topLayout.forEach((i) => displayBox(i, nzu, xu, nyu, [st, sf, sl], false, false, tt, lt, ft));
183 | p.pop();
184 | }
185 |
186 | function displayBox(box, xu, yu, zu, shades, hiddenTop, hiddenLeft, t1, t2, t3) {
187 | display(
188 | p,
189 | box,
190 | xu,
191 | yu,
192 | zu,
193 | maxDepth,
194 | shades,
195 | palette.colors,
196 | paletteShift,
197 | strokeCol,
198 | innerStrokeWeight,
199 | outerStrokeWeight,
200 | hiddenTop,
201 | hiddenLeft,
202 | t1,
203 | t2,
204 | t3
205 | );
206 | }
207 |
208 | function createGrid(box, topside, leftside) {
209 | const { x1, y1, w, h } = box;
210 |
211 | const topsideGrid =
212 | topside && y1 == 1 ? topside.filter((c) => c.x1 == 1 && c.y1 == x1)[0] : null;
213 |
214 | const leftsideGrid =
215 | leftside && x1 == 1 ? leftside.filter((c) => c.y1 == 1 && c.x1 == y1)[0] : null;
216 |
217 | const cols = topsideGrid ? topsideGrid.rows : Math.ceil((Math.random() * w) / minGridSize);
218 | const rows = leftsideGrid ? leftsideGrid.cols : Math.ceil((Math.random() * h) / minGridSize);
219 |
220 | const cell_w = w / cols;
221 | const cell_h = h / rows;
222 |
223 | const init_top = topsideGrid
224 | ? topsideGrid.apparatus.map((i) => ({ ...i[1], v: i[1].h }))
225 | : null;
226 |
227 | const init_left = leftsideGrid
228 | ? leftsideGrid.apparatus[1].map((i) => ({ ...i, h: i.v }))
229 | : null;
230 |
231 | const apparatus = createApparatus(cell_w, cell_h, init_top, init_left);
232 | let grid = [];
233 | for (let i = 0; i < rows; i++) {
234 | for (let j = 0; j < cols; j++) {
235 | const content = apparatus[0].map((app) => {
236 | const xpos = x1 + app.x1 + j * cell_w - 1;
237 | const ypos = y1 + app.y1 + i * cell_h - 1;
238 | let y_offset =
239 | topsideGrid && i == 0 && ypos <= 0
240 | ? topsideGrid.content.filter((c) => c.x1 <= 0 && Math.max(c.y1, 0) == xpos)[0].z1
241 | : 0;
242 | let x_offset =
243 | leftsideGrid && j == 0 && xpos <= 0
244 | ? leftsideGrid.content.filter((c) => c.y1 <= 0 && Math.max(c.x1, 0) == ypos)[0].z1
245 | : 0;
246 | return {
247 | ...app,
248 | x1: xpos,
249 | y1: ypos,
250 | w: app.w,
251 | h: app.h,
252 | x_off: x_offset,
253 | y_off: y_offset,
254 | level: 2,
255 | filled: true,
256 | };
257 | });
258 |
259 | grid = grid.concat(content);
260 | }
261 | }
262 | return {
263 | x1: x1,
264 | y1: y1,
265 | cols: cols,
266 | rows: rows,
267 | apparatus: apparatus[1],
268 | content: grid,
269 | };
270 | }
271 |
272 | function createApparatus(w, h, top, left) {
273 | const cols = Math.round(w, 0);
274 | const rows = Math.round(h, 0);
275 |
276 | const w_unit = w / cols;
277 | const h_unit = h / rows;
278 |
279 | const generator = new Apparatus((cols - 11) / 2, (rows - 11) / 2, atomAppOpts);
280 |
281 | const apparatus = generator.generate(top, left, true);
282 | apparatus[0] = apparatus[0].map((a) => ({
283 | x1: (a.x1 - 1) * w_unit,
284 | y1: (a.y1 - 1) * h_unit,
285 | z1: 0.1 + Math.floor(Math.random() * depthSteps) / depthSteps,
286 | w: a.w * w_unit,
287 | h: a.h * h_unit,
288 | col: a.col,
289 | }));
290 |
291 | return apparatus;
292 | }
293 |
294 | function overlaps(a, b) {
295 | const lca = [a.x1 + a.w, a.y1];
296 | const rca = [a.x1, a.y1 + a.h];
297 | const ba = [a.x1, a.y1];
298 |
299 | const lcb = [b.x1 + b.w, b.y1];
300 | const rcb = [b.x1, b.y1 + b.h];
301 | const bb = [b.x1, b.y1];
302 |
303 | if (a.y1 + 0.005 >= b.y1 + b.h || a.x1 + 0.005 >= b.x1 + b.w) return false;
304 |
305 | if (ba[1] - ba[0] < bb[1] - bb[0]) {
306 | // A is left of B
307 | if (rca[1] - rca[0] <= lcb[1] - lcb[0]) return false;
308 | return rca[1] + rca[0] < lcb[1] + lcb[0]; // positive if A is in front of B
309 | }
310 |
311 | if (ba[1] - ba[0] > bb[1] - bb[0]) {
312 | // A is right of B
313 | if (lca[1] - lca[0] >= rcb[1] - rcb[0]) return false;
314 | return lca[1] + lca[0] < rcb[1] + rcb[0]; // positive if A is in front of B
315 | }
316 | return ba[1] + ba[0] < bb[1] + bb[0];
317 | }
318 |
319 | function get_overlap_graph(boxes) {
320 | const nodes = [];
321 | boxes.forEach((box, i) => nodes.push(i));
322 |
323 | const edges = [];
324 | boxes.forEach((b1, i) => {
325 | boxes.forEach((b2, j) => {
326 | if (overlaps(b1, b2)) edges.push([i, j, b1, b2]);
327 | });
328 | });
329 |
330 | const overlapping = toposort(edges);
331 | return overlapping.reverse().map((i) => boxes[i]);
332 | }
333 |
334 | function print() {
335 | p.saveCanvas('sketch_' + THE_SEED, 'png');
336 | }
337 |
338 | function downloadCSV() {
339 | const rows = generateCSV(frontLayout, leftLayout, topLayout, 0.1, palette.colors.length);
340 | const csvContent = 'data:text/csv;charset=utf-8,' + rows.map((e) => e.join(',')).join('\n');
341 |
342 | const encodedUri = encodeURI(csvContent);
343 | window.open(encodedUri);
344 | }
345 |
346 | p.keyPressed = function () {
347 | if (p.keyCode === 80) print();
348 | if (p.keyCode === 82) generateAndDraw();
349 | if (p.keyCode === 83) downloadCSV();
350 | };
351 | };
352 | new p5(sketch);
353 |
354 | function getSrcDst(xu, yu, persp, dim) {
355 | const m = dim * 2 + 11;
356 |
357 | const src = [
358 | 0,
359 | 0,
360 | m * xu[0],
361 | m * xu[1],
362 | m * (xu[0] + yu[0]),
363 | m * (xu[1] + yu[1]),
364 | m * yu[0],
365 | m * yu[1],
366 | ];
367 | const dst = [
368 | 0,
369 | 0,
370 | m * xu[0],
371 | m * xu[1],
372 | m * (xu[0] + yu[0]) * persp,
373 | m * (xu[1] + yu[1]) * persp,
374 | m * yu[0],
375 | m * yu[1],
376 | ];
377 |
378 | return [src, dst];
379 | }
380 |
--------------------------------------------------------------------------------
/index_interactive_print.js:
--------------------------------------------------------------------------------
1 | import Apparatus from 'apparatus-generator';
2 | import * as tome from 'chromotome';
3 | import toposort from 'toposort';
4 | import perspective from 'change-perspective';
5 |
6 | import ui from './ui';
7 | import display from './display';
8 |
9 | // Options suitable for print.
10 | let opts = {
11 | cubedimX: 15,
12 | cubedimY: 15,
13 | cubedimZ: 15,
14 | depthDim: 2,
15 | mag: 14,
16 | tx: 0,
17 | ty: 0,
18 | shadeOpacityFront: 0.2,
19 | shadeOpacityLeft: 0.1,
20 | shadeOpacityTop: 0,
21 | outerStrokeWeight: 5,
22 | innerStrokeWeight: 3,
23 | outerSize: 0.99,
24 | minGridSize: 4,
25 | innerSize: 0.78,
26 | perspective: 0.95,
27 | colorMode: 'group',
28 | palette: 'dt10',
29 | paletteShift: 0,
30 | };
31 |
32 | let sketch = function (p) {
33 | let THE_SEED;
34 |
35 | let cubedimX;
36 | let cubedimY;
37 | let cubedimZ;
38 | let tx, ty;
39 |
40 | const xr = (-1 * Math.PI) / 6;
41 | const yr = (3 * Math.PI) / 6;
42 | const zr = (1 * Math.PI) / 6;
43 |
44 | let xu, yu, zu;
45 | let nxu, nyu, nzu;
46 |
47 | let maxDepth;
48 | const depthSteps = 8;
49 |
50 | let paletteShift;
51 | let palette;
52 | let strokeCol;
53 | let shadeOpacityFront, shadeOpacityLeft, shadeOpacityTop;
54 | let outerStrokeWeight, innerStrokeWeight;
55 |
56 | let sectionAppOpts, atomAppOpts;
57 | let minGridSize;
58 |
59 | let persp;
60 |
61 | let frontLayout, leftLayout, topLayout;
62 |
63 | p.setup = function () {
64 | p.createCanvas(2500, 2500);
65 | THE_SEED = p.floor(p.random(9999999));
66 | p.randomSeed(THE_SEED);
67 | p.pixelDensity(2);
68 | p.noFill();
69 | p.smooth();
70 | p.frameRate(1);
71 | p.strokeJoin(p.ROUND);
72 | p.noLoop();
73 |
74 | ui(opts, generateAndDraw, updateAndDraw, print);
75 |
76 | generateAndDraw();
77 | };
78 |
79 | function generateAndDraw() {
80 | updateGlobals(opts);
81 | reset();
82 | displayLayout();
83 | }
84 |
85 | function updateAndDraw() {
86 | updateGlobals(opts);
87 | displayLayout();
88 | }
89 |
90 | function updateGlobals(opts) {
91 | cubedimX = opts.cubedimX;
92 | cubedimY = opts.cubedimY;
93 | cubedimZ = opts.cubedimZ;
94 |
95 | tx = opts.tx;
96 | ty = opts.ty;
97 |
98 | xu = [Math.cos(xr) * opts.mag, Math.sin(xr) * opts.mag];
99 | yu = [Math.cos(yr) * opts.mag, Math.sin(yr) * opts.mag];
100 | zu = [Math.cos(zr) * opts.mag, Math.sin(zr) * opts.mag];
101 |
102 | nxu = xu.map((v) => -v);
103 | nyu = yu.map((v) => -v);
104 | nzu = zu.map((v) => -v);
105 |
106 | shadeOpacityFront = opts.shadeOpacityFront;
107 | shadeOpacityLeft = opts.shadeOpacityLeft;
108 | shadeOpacityTop = opts.shadeOpacityTop;
109 | outerStrokeWeight = opts.outerStrokeWeight;
110 | innerStrokeWeight = opts.innerStrokeWeight;
111 |
112 | maxDepth = opts.depthDim;
113 | persp = opts.perspective;
114 |
115 | palette = tome.get(opts.palette);
116 | paletteShift = opts.paletteShift;
117 | strokeCol = palette.stroke ? palette.stroke : '#000';
118 |
119 | minGridSize = opts.minGridSize;
120 |
121 | sectionAppOpts = {
122 | simple: true,
123 | extension_chance: opts.outerSize,
124 | horizontal_symmetry: false,
125 | vertical_chance: 0.5,
126 | };
127 |
128 | atomAppOpts = {
129 | simple: true,
130 | extension_chance: opts.innerSize,
131 | horizontal_symmetry: false,
132 | vertical_chance: 0.5,
133 | color_mode: opts.colorMode,
134 | group_size: 0.4,
135 | colors: [...Array(1000).keys()],
136 | };
137 | }
138 |
139 | function reset() {
140 | const generatorFront = new Apparatus(cubedimX, cubedimY, sectionAppOpts);
141 | const generatorLeft = new Apparatus(cubedimY, cubedimZ, sectionAppOpts);
142 | const generatorTop = new Apparatus(cubedimZ, cubedimX, sectionAppOpts);
143 | const frontApp = generatorFront.generate(null, null, true);
144 | const leftApp = generatorLeft.generate(
145 | frontApp[1].map((i) => ({ ...i[1], v: i[1].h })),
146 | null,
147 | true
148 | );
149 | const topApp = generatorTop.generate(
150 | leftApp[1].map((i) => ({ ...i[1], v: i[1].h })),
151 | frontApp[1][1].map((i) => ({ ...i, h: i.v })),
152 | true
153 | );
154 |
155 | const frontGrids = frontApp[0].map((a) => createGrid(a, null, null));
156 | const leftGrids = leftApp[0].map((a) => createGrid(a, frontGrids, null));
157 | const topGrids = topApp[0].map((a) => createGrid(a, leftGrids, frontGrids));
158 |
159 | frontLayout = get_overlap_graph(frontGrids.flatMap((g) => g.content));
160 | leftLayout = get_overlap_graph(leftGrids.flatMap((g) => g.content));
161 | topLayout = get_overlap_graph(topGrids.flatMap((g) => g.content));
162 | }
163 |
164 | function displayLayout() {
165 | p.push();
166 | p.translate(tx + p.width / 2, ty + p.height / 2);
167 | //p.background(palette.background ? palette.background : '#eee');
168 | p.clear();
169 |
170 | const ft = perspective(...getSrcDst(xu, yu, persp, cubedimX));
171 | const lt = perspective(...getSrcDst(yu, nzu, persp, cubedimX));
172 | const tt = perspective(...getSrcDst(nzu, xu, persp, cubedimX));
173 |
174 | const sf = shadeOpacityFront;
175 | const sl = shadeOpacityLeft;
176 | const st = shadeOpacityTop;
177 |
178 | frontLayout.forEach((i) => displayBox(i, xu, yu, zu, [sf, sl, st], true, true, ft, tt, lt));
179 | leftLayout.forEach((i) => displayBox(i, yu, nzu, nxu, [sl, st, sf], false, true, lt, ft, tt));
180 | topLayout.forEach((i) => displayBox(i, nzu, xu, nyu, [st, sf, sl], false, false, tt, lt, ft));
181 | p.pop();
182 | }
183 |
184 | function displayBox(box, xu, yu, zu, shades, hiddenTop, hiddenLeft, t1, t2, t3) {
185 | display(
186 | p,
187 | box,
188 | xu,
189 | yu,
190 | zu,
191 | maxDepth,
192 | shades,
193 | palette.colors,
194 | paletteShift,
195 | strokeCol,
196 | innerStrokeWeight,
197 | outerStrokeWeight,
198 | hiddenTop,
199 | hiddenLeft,
200 | t1,
201 | t2,
202 | t3
203 | );
204 | }
205 |
206 | function createGrid(box, topside, leftside) {
207 | const { x1, y1, w, h } = box;
208 |
209 | const topsideGrid =
210 | topside && y1 == 1 ? topside.filter((c) => c.x1 == 1 && c.y1 == x1)[0] : null;
211 |
212 | const leftsideGrid =
213 | leftside && x1 == 1 ? leftside.filter((c) => c.y1 == 1 && c.x1 == y1)[0] : null;
214 |
215 | const cols = topsideGrid ? topsideGrid.rows : Math.ceil((Math.random() * w) / minGridSize);
216 | const rows = leftsideGrid ? leftsideGrid.cols : Math.ceil((Math.random() * h) / minGridSize);
217 |
218 | const cell_w = w / cols;
219 | const cell_h = h / rows;
220 |
221 | const init_top = topsideGrid
222 | ? topsideGrid.apparatus.map((i) => ({ ...i[1], v: i[1].h }))
223 | : null;
224 |
225 | const init_left = leftsideGrid
226 | ? leftsideGrid.apparatus[1].map((i) => ({ ...i, h: i.v }))
227 | : null;
228 |
229 | const apparatus = createApparatus(cell_w, cell_h, init_top, init_left);
230 | let grid = [];
231 | for (let i = 0; i < rows; i++) {
232 | for (let j = 0; j < cols; j++) {
233 | const content = apparatus[0].map((app) => {
234 | const xpos = x1 + app.x1 + j * cell_w - 1;
235 | const ypos = y1 + app.y1 + i * cell_h - 1;
236 | let y_offset =
237 | topsideGrid && i == 0 && ypos <= 0
238 | ? topsideGrid.content.filter((c) => c.x1 <= 0 && Math.max(c.y1, 0) == xpos)[0].z1
239 | : 0;
240 | let x_offset =
241 | leftsideGrid && j == 0 && xpos <= 0
242 | ? leftsideGrid.content.filter((c) => c.y1 <= 0 && Math.max(c.x1, 0) == ypos)[0].z1
243 | : 0;
244 | return {
245 | ...app,
246 | x1: xpos,
247 | y1: ypos,
248 | w: app.w,
249 | h: app.h,
250 | x_off: x_offset,
251 | y_off: y_offset,
252 | level: 2,
253 | filled: true,
254 | };
255 | });
256 |
257 | grid = grid.concat(content);
258 | }
259 | }
260 | return {
261 | x1: x1,
262 | y1: y1,
263 | cols: cols,
264 | rows: rows,
265 | apparatus: apparatus[1],
266 | content: grid,
267 | };
268 | }
269 |
270 | function createApparatus(w, h, top, left) {
271 | const cols = Math.round(w, 0);
272 | const rows = Math.round(h, 0);
273 |
274 | const w_unit = w / cols;
275 | const h_unit = h / rows;
276 |
277 | const generator = new Apparatus((cols - 11) / 2, (rows - 11) / 2, atomAppOpts);
278 |
279 | const apparatus = generator.generate(top, left, true);
280 | apparatus[0] = apparatus[0].map((a) => ({
281 | x1: (a.x1 - 1) * w_unit,
282 | y1: (a.y1 - 1) * h_unit,
283 | z1: Math.floor(Math.random() * depthSteps) / depthSteps,
284 | w: a.w * w_unit,
285 | h: a.h * h_unit,
286 | col: a.col,
287 | }));
288 |
289 | return apparatus;
290 | }
291 |
292 | function overlaps(a, b) {
293 | const lca = [a.x1 + a.w, a.y1];
294 | const rca = [a.x1, a.y1 + a.h];
295 | const ba = [a.x1, a.y1];
296 |
297 | const lcb = [b.x1 + b.w, b.y1];
298 | const rcb = [b.x1, b.y1 + b.h];
299 | const bb = [b.x1, b.y1];
300 |
301 | if (a.y1 + 0.005 >= b.y1 + b.h || a.x1 + 0.005 >= b.x1 + b.w) return false;
302 |
303 | if (ba[1] - ba[0] < bb[1] - bb[0]) {
304 | // A is left of B
305 | if (rca[1] - rca[0] <= lcb[1] - lcb[0]) return false;
306 | return rca[1] + rca[0] < lcb[1] + lcb[0]; // positive if A is in front of B
307 | }
308 |
309 | if (ba[1] - ba[0] > bb[1] - bb[0]) {
310 | // A is right of B
311 | if (lca[1] - lca[0] >= rcb[1] - rcb[0]) return false;
312 | return lca[1] + lca[0] < rcb[1] + rcb[0]; // positive if A is in front of B
313 | }
314 | return ba[1] + ba[0] < bb[1] + bb[0];
315 | }
316 |
317 | function get_overlap_graph(boxes) {
318 | const nodes = [];
319 | boxes.forEach((box, i) => nodes.push(i));
320 |
321 | const edges = [];
322 | boxes.forEach((b1, i) => {
323 | boxes.forEach((b2, j) => {
324 | if (overlaps(b1, b2)) edges.push([i, j, b1, b2]);
325 | });
326 | });
327 |
328 | const overlapping = toposort(edges);
329 | return overlapping.reverse().map((i) => boxes[i]);
330 | }
331 |
332 | function print() {
333 | p.saveCanvas('sketch_' + THE_SEED, 'png');
334 | }
335 |
336 | p.keyPressed = function () {
337 | if (p.keyCode === 80) print();
338 | if (p.keyCode === 82) generateAndDraw();
339 | };
340 | };
341 | new p5(sketch);
342 |
343 | function getSrcDst(xu, yu, persp, dim) {
344 | const m = dim * 2 + 11;
345 |
346 | const src = [
347 | 0,
348 | 0,
349 | m * xu[0],
350 | m * xu[1],
351 | m * (xu[0] + yu[0]),
352 | m * (xu[1] + yu[1]),
353 | m * yu[0],
354 | m * yu[1],
355 | ];
356 | const dst = [
357 | 0,
358 | 0,
359 | m * xu[0],
360 | m * xu[1],
361 | m * (xu[0] + yu[0]) * persp,
362 | m * (xu[1] + yu[1]) * persp,
363 | m * yu[0],
364 | m * yu[1],
365 | ];
366 |
367 | return [src, dst];
368 | }
369 |
--------------------------------------------------------------------------------
/index_interactive_print_b.js:
--------------------------------------------------------------------------------
1 | import Apparatus from 'apparatus-generator';
2 | import * as tome from 'chromotome';
3 | import toposort from 'toposort';
4 | import perspective from 'change-perspective';
5 |
6 | import ui from './ui';
7 | import display from './display';
8 |
9 | // Options suitable for print.
10 | let opts = {
11 | cubedimX: 5,
12 | cubedimY: 5,
13 | cubedimZ: 5,
14 | depthDim: 2,
15 | mag: 3,
16 | tx: 0,
17 | ty: -600,
18 | shadeOpacityFront: 0.2,
19 | shadeOpacityLeft: 0.1,
20 | shadeOpacityTop: 0,
21 | outerStrokeWeight: 1.5,
22 | innerStrokeWeight: 0.5,
23 | outerSize: 0.97,
24 | minGridSize: 6,
25 | innerSize: 0.82,
26 | perspective: 1,
27 | colorMode: 'group',
28 | palette: 'tsu_arcade',
29 | paletteShift: 0,
30 | };
31 |
32 | let sketch = function (p) {
33 | let THE_SEED;
34 |
35 | let cubedimX;
36 | let cubedimY;
37 | let cubedimZ;
38 | let tx, ty;
39 |
40 | const xr = (-1 * Math.PI) / 6;
41 | const yr = (3 * Math.PI) / 6;
42 | const zr = (1 * Math.PI) / 6;
43 |
44 | let xu, yu, zu;
45 | let nxu, nyu, nzu;
46 |
47 | let maxDepth;
48 | const depthSteps = 8;
49 |
50 | let paletteShift;
51 | let palette;
52 | let strokeCol;
53 | let shadeOpacityFront, shadeOpacityLeft, shadeOpacityTop;
54 | let outerStrokeWeight, innerStrokeWeight;
55 |
56 | let sectionAppOpts, atomAppOpts;
57 | let minGridSize;
58 |
59 | let persp;
60 |
61 | let frontLayout, leftLayout, topLayout;
62 |
63 | p.setup = function () {
64 | p.createCanvas(2000, 3000);
65 | THE_SEED = p.floor(p.random(9999999));
66 | p.randomSeed(THE_SEED);
67 | p.pixelDensity(2);
68 | p.noFill();
69 | p.smooth();
70 | p.frameRate(1);
71 | p.strokeJoin(p.ROUND);
72 | p.noLoop();
73 |
74 | ui(opts, generateAndDraw, updateAndDraw, print);
75 |
76 | generateAndDraw();
77 | };
78 |
79 | function generateAndDraw() {
80 | p.background('#dfe0cc');
81 |
82 | //var palette = tome.get();
83 | //updateGlobals(opts, j, i, palette);
84 |
85 | //reset();
86 |
87 | for (var i = -6; i <= 6; i++) {
88 | for (var j = -6; j <= 6; j++) {
89 | var palette = tome.get();
90 | updateGlobals(opts, j, i, palette);
91 | reset();
92 | displayLayout();
93 | }
94 | //reset();
95 | }
96 | }
97 |
98 | function updateAndDraw() {
99 | p.background('#dfe0cc');
100 |
101 | for (var i = -7; i <= 14; i++) {
102 | var palette = tome.get('empusa');
103 | for (var j = -4; j <= 4; j++) {
104 | updateGlobals(opts, j, i, palette);
105 | displayLayout();
106 | }
107 | }
108 | }
109 |
110 | function updateGlobals(opts, ax, ay, pal) {
111 | cubedimX = opts.cubedimX;
112 | cubedimY = opts.cubedimY;
113 | cubedimZ = opts.cubedimZ;
114 |
115 | xu = [Math.cos(xr) * opts.mag, Math.sin(xr) * opts.mag];
116 | yu = [Math.cos(yr) * opts.mag, Math.sin(yr) * opts.mag];
117 | zu = [Math.cos(zr) * opts.mag, Math.sin(zr) * opts.mag];
118 |
119 | nxu = xu.map((v) => -v);
120 | nyu = yu.map((v) => -v);
121 | nzu = zu.map((v) => -v);
122 |
123 | //tx = opts.tx + ax * (30 + (xu[0] * 1.2 + yu[0] * 0.9 + zu[0]) * 60);
124 | //ty = opts.ty + ay * (30 + (xu[1] * 1.2 + yu[1] * 0.9 + zu[1]) * 60);
125 |
126 | tx = opts.tx + (ax * (xu[0] + zu[0]) + ay * (zu[0] + yu[0])) * 55;
127 | ty = opts.tx + (ax * (xu[1] + zu[1]) + ay * (zu[1] + yu[1])) * 55;
128 |
129 | shadeOpacityFront = opts.shadeOpacityFront;
130 | shadeOpacityLeft = opts.shadeOpacityLeft;
131 | shadeOpacityTop = opts.shadeOpacityTop;
132 | outerStrokeWeight = opts.outerStrokeWeight;
133 | innerStrokeWeight = opts.innerStrokeWeight;
134 |
135 | maxDepth = opts.depthDim;
136 | persp = opts.perspective;
137 |
138 | palette = pal;
139 | paletteShift = opts.paletteShift;
140 | strokeCol = '#000'; //palette.stroke ? palette.stroke : '#000';
141 |
142 | minGridSize = opts.minGridSize;
143 |
144 | sectionAppOpts = {
145 | simple: true,
146 | extension_chance: opts.outerSize,
147 | horizontal_symmetry: false,
148 | vertical_chance: 0.5,
149 | };
150 |
151 | atomAppOpts = {
152 | simple: true,
153 | extension_chance: opts.innerSize,
154 | horizontal_symmetry: false,
155 | vertical_chance: 0.5,
156 | color_mode: opts.colorMode,
157 | group_size: 0.4,
158 | colors: [...Array(1000).keys()],
159 | };
160 | }
161 |
162 | function reset() {
163 | const generatorFront = new Apparatus(cubedimX, cubedimY, sectionAppOpts);
164 | const generatorLeft = new Apparatus(cubedimY, cubedimZ, sectionAppOpts);
165 | const generatorTop = new Apparatus(cubedimZ, cubedimX, sectionAppOpts);
166 | const frontApp = generatorFront.generate(null, null, true);
167 | const leftApp = generatorLeft.generate(
168 | frontApp[1].map((i) => ({ ...i[1], v: i[1].h })),
169 | null,
170 | true
171 | );
172 | const topApp = generatorTop.generate(
173 | leftApp[1].map((i) => ({ ...i[1], v: i[1].h })),
174 | frontApp[1][1].map((i) => ({ ...i, h: i.v })),
175 | true
176 | );
177 |
178 | const frontGrids = frontApp[0].map((a) => createGrid(a, null, null));
179 | const leftGrids = leftApp[0].map((a) => createGrid(a, frontGrids, null));
180 | const topGrids = topApp[0].map((a) => createGrid(a, leftGrids, frontGrids));
181 |
182 | frontLayout = get_overlap_graph(frontGrids.flatMap((g) => g.content));
183 | leftLayout = get_overlap_graph(leftGrids.flatMap((g) => g.content));
184 | topLayout = get_overlap_graph(topGrids.flatMap((g) => g.content));
185 | }
186 |
187 | function displayLayout() {
188 | p.push();
189 | p.translate(tx + p.width / 2, ty + p.height / 2);
190 | //p.background(palette.background ? palette.background : '#eee');
191 | //p.clear();
192 | const pal = p.shuffle(palette.colors).slice(0, 5);
193 |
194 | const ft = perspective(...getSrcDst(xu, yu, persp, cubedimX));
195 | const lt = perspective(...getSrcDst(yu, nzu, persp, cubedimX));
196 | const tt = perspective(...getSrcDst(nzu, xu, persp, cubedimX));
197 |
198 | const sf = shadeOpacityFront;
199 | const sl = shadeOpacityLeft;
200 | const st = shadeOpacityTop;
201 |
202 | frontLayout.forEach((i) =>
203 | displayBox(i, xu, yu, zu, [sf, sl, st], true, true, ft, tt, lt, pal)
204 | );
205 | leftLayout.forEach((i) =>
206 | displayBox(i, yu, nzu, nxu, [sl, st, sf], false, true, lt, ft, tt, pal)
207 | );
208 | topLayout.forEach((i) =>
209 | displayBox(i, nzu, xu, nyu, [st, sf, sl], false, false, tt, lt, ft, pal)
210 | );
211 |
212 | p.pop();
213 | }
214 |
215 | function displayBox(box, xu, yu, zu, shades, hiddenTop, hiddenLeft, t1, t2, t3, pal) {
216 | display(
217 | p,
218 | box,
219 | xu,
220 | yu,
221 | zu,
222 | maxDepth,
223 | shades,
224 | pal,
225 | paletteShift,
226 | strokeCol,
227 | innerStrokeWeight,
228 | outerStrokeWeight,
229 | hiddenTop,
230 | hiddenLeft,
231 | t1,
232 | t2,
233 | t3
234 | );
235 | }
236 |
237 | function createGrid(box, topside, leftside) {
238 | const { x1, y1, w, h } = box;
239 |
240 | const topsideGrid =
241 | topside && y1 == 1 ? topside.filter((c) => c.x1 == 1 && c.y1 == x1)[0] : null;
242 |
243 | const leftsideGrid =
244 | leftside && x1 == 1 ? leftside.filter((c) => c.y1 == 1 && c.x1 == y1)[0] : null;
245 |
246 | const cols = topsideGrid ? topsideGrid.rows : Math.ceil((Math.random() * w) / minGridSize);
247 | const rows = leftsideGrid ? leftsideGrid.cols : Math.ceil((Math.random() * h) / minGridSize);
248 |
249 | const cell_w = w / cols;
250 | const cell_h = h / rows;
251 |
252 | const init_top = topsideGrid
253 | ? topsideGrid.apparatus.map((i) => ({ ...i[1], v: i[1].h }))
254 | : null;
255 |
256 | const init_left = leftsideGrid
257 | ? leftsideGrid.apparatus[1].map((i) => ({ ...i, h: i.v }))
258 | : null;
259 |
260 | const apparatus = createApparatus(cell_w, cell_h, init_top, init_left);
261 | let grid = [];
262 | for (let i = 0; i < rows; i++) {
263 | for (let j = 0; j < cols; j++) {
264 | const content = apparatus[0].map((app) => {
265 | const xpos = x1 + app.x1 + j * cell_w - 1;
266 | const ypos = y1 + app.y1 + i * cell_h - 1;
267 | let y_offset =
268 | topsideGrid && i == 0 && ypos <= 0
269 | ? topsideGrid.content.filter((c) => c.x1 <= 0 && Math.max(c.y1, 0) == xpos)[0].z1
270 | : 0;
271 | let x_offset =
272 | leftsideGrid && j == 0 && xpos <= 0
273 | ? leftsideGrid.content.filter((c) => c.y1 <= 0 && Math.max(c.x1, 0) == ypos)[0].z1
274 | : 0;
275 | return {
276 | ...app,
277 | x1: xpos,
278 | y1: ypos,
279 | w: app.w,
280 | h: app.h,
281 | x_off: x_offset,
282 | y_off: y_offset,
283 | level: 2,
284 | filled: true,
285 | };
286 | });
287 |
288 | grid = grid.concat(content);
289 | }
290 | }
291 | return {
292 | x1: x1,
293 | y1: y1,
294 | cols: cols,
295 | rows: rows,
296 | apparatus: apparatus[1],
297 | content: grid,
298 | };
299 | }
300 |
301 | function createApparatus(w, h, top, left) {
302 | const cols = Math.round(w, 0);
303 | const rows = Math.round(h, 0);
304 |
305 | const w_unit = w / cols;
306 | const h_unit = h / rows;
307 |
308 | const generator = new Apparatus((cols - 11) / 2, (rows - 11) / 2, atomAppOpts);
309 |
310 | const apparatus = generator.generate(top, left, true);
311 | apparatus[0] = apparatus[0].map((a) => ({
312 | x1: (a.x1 - 1) * w_unit,
313 | y1: (a.y1 - 1) * h_unit,
314 | z1: Math.floor(Math.random() * depthSteps) / depthSteps,
315 | w: a.w * w_unit,
316 | h: a.h * h_unit,
317 | col: a.col,
318 | }));
319 |
320 | return apparatus;
321 | }
322 |
323 | function overlaps(a, b) {
324 | const lca = [a.x1 + a.w, a.y1];
325 | const rca = [a.x1, a.y1 + a.h];
326 | const ba = [a.x1, a.y1];
327 |
328 | const lcb = [b.x1 + b.w, b.y1];
329 | const rcb = [b.x1, b.y1 + b.h];
330 | const bb = [b.x1, b.y1];
331 |
332 | if (a.y1 + 0.005 >= b.y1 + b.h || a.x1 + 0.005 >= b.x1 + b.w) return false;
333 |
334 | if (ba[1] - ba[0] < bb[1] - bb[0]) {
335 | // A is left of B
336 | if (rca[1] - rca[0] <= lcb[1] - lcb[0]) return false;
337 | return rca[1] + rca[0] < lcb[1] + lcb[0]; // positive if A is in front of B
338 | }
339 |
340 | if (ba[1] - ba[0] > bb[1] - bb[0]) {
341 | // A is right of B
342 | if (lca[1] - lca[0] >= rcb[1] - rcb[0]) return false;
343 | return lca[1] + lca[0] < rcb[1] + rcb[0]; // positive if A is in front of B
344 | }
345 | return ba[1] + ba[0] < bb[1] + bb[0];
346 | }
347 |
348 | function get_overlap_graph(boxes) {
349 | const nodes = [];
350 | boxes.forEach((box, i) => nodes.push(i));
351 |
352 | const edges = [];
353 | boxes.forEach((b1, i) => {
354 | boxes.forEach((b2, j) => {
355 | if (overlaps(b1, b2)) edges.push([i, j, b1, b2]);
356 | });
357 | });
358 |
359 | const overlapping = toposort(edges);
360 | return overlapping.reverse().map((i) => boxes[i]);
361 | }
362 |
363 | function print() {
364 | p.saveCanvas('sketch_' + THE_SEED, 'png');
365 | }
366 |
367 | p.keyPressed = function () {
368 | if (p.keyCode === 80) print();
369 | if (p.keyCode === 82) generateAndDraw();
370 | };
371 | };
372 | new p5(sketch);
373 |
374 | function getSrcDst(xu, yu, persp, dim) {
375 | const m = dim * 2 + 11;
376 |
377 | const src = [
378 | 0,
379 | 0,
380 | m * xu[0],
381 | m * xu[1],
382 | m * (xu[0] + yu[0]),
383 | m * (xu[1] + yu[1]),
384 | m * yu[0],
385 | m * yu[1],
386 | ];
387 | const dst = [
388 | 0,
389 | 0,
390 | m * xu[0],
391 | m * xu[1],
392 | m * (xu[0] + yu[0]) * persp,
393 | m * (xu[1] + yu[1]) * persp,
394 | m * yu[0],
395 | m * yu[1],
396 | ];
397 |
398 | return [src, dst];
399 | }
400 |
--------------------------------------------------------------------------------
/index_interactive_print_c.js:
--------------------------------------------------------------------------------
1 | import Apparatus from 'apparatus-generator';
2 | import * as tome from 'chromotome';
3 | import toposort from 'toposort';
4 | import perspective from 'change-perspective';
5 |
6 | import ui from './ui';
7 | import display from './display';
8 |
9 | // Options suitable for print.
10 | let opts = {
11 | cubedimX: 55,
12 | cubedimY: 55,
13 | cubedimZ: 5,
14 | depthDim: 2,
15 | mag: 14,
16 | tx: 0,
17 | ty: 1600,
18 | shadeOpacityFront: 0,
19 | shadeOpacityLeft: 0,
20 | shadeOpacityTop: 1,
21 | outerStrokeWeight: 2,
22 | innerStrokeWeight: 1,
23 | outerSize: 1,
24 | minGridSize: 1.5,
25 | innerSize: 0.75,
26 | perspective: 0.8,
27 | colorMode: 'group',
28 | palette: 'nowak',
29 | paletteShift: 0,
30 | };
31 |
32 | let sketch = function (p) {
33 | let THE_SEED;
34 |
35 | let cubedimX;
36 | let cubedimY;
37 | let cubedimZ;
38 | let tx, ty;
39 |
40 | // const xr = (-1 * Math.PI) / 6;
41 | // const yr = (3 * Math.PI) / 6;
42 | // const zr = (1 * Math.PI) / 6;
43 |
44 | const xr = (-5 * Math.PI) / 6;
45 | const yr = (-1 * Math.PI) / 6;
46 | const zr = (-3 * Math.PI) / 6;
47 |
48 | let xu, yu, zu;
49 | let nxu, nyu, nzu;
50 |
51 | let maxDepth;
52 | const depthSteps = 8;
53 |
54 | let paletteShift;
55 | let palette;
56 | let strokeCol;
57 | let shadeOpacityFront, shadeOpacityLeft, shadeOpacityTop;
58 | let outerStrokeWeight, innerStrokeWeight;
59 |
60 | let sectionAppOpts, atomAppOpts;
61 | let minGridSize;
62 |
63 | let persp;
64 |
65 | let frontLayout, leftLayout, topLayout;
66 |
67 | p.setup = function () {
68 | p.createCanvas(2500, 1500);
69 | THE_SEED = p.floor(p.random(9999999));
70 | p.randomSeed(THE_SEED);
71 | p.pixelDensity(4);
72 | p.noFill();
73 | p.smooth();
74 | p.frameRate(1);
75 | p.strokeJoin(p.ROUND);
76 | p.noLoop();
77 |
78 | ui(opts, generateAndDraw, updateAndDraw, print);
79 |
80 | generateAndDraw();
81 | };
82 |
83 | function generateAndDraw() {
84 | updateGlobals(opts);
85 | reset();
86 | displayLayout();
87 | }
88 |
89 | function updateAndDraw() {
90 | updateGlobals(opts);
91 | displayLayout();
92 | }
93 |
94 | function updateGlobals(opts) {
95 | cubedimX = opts.cubedimX;
96 | cubedimY = opts.cubedimY;
97 | cubedimZ = opts.cubedimZ;
98 |
99 | tx = opts.tx;
100 | ty = opts.ty;
101 |
102 | xu = [Math.cos(xr) * opts.mag, Math.sin(xr) * opts.mag];
103 | yu = [Math.cos(yr) * opts.mag, Math.sin(yr) * opts.mag];
104 | zu = [Math.cos(zr) * opts.mag, Math.sin(zr) * opts.mag];
105 |
106 | nxu = xu.map((v) => -v);
107 | nyu = yu.map((v) => -v);
108 | nzu = zu.map((v) => -v);
109 |
110 | shadeOpacityFront = opts.shadeOpacityFront;
111 | shadeOpacityLeft = opts.shadeOpacityLeft;
112 | shadeOpacityTop = opts.shadeOpacityTop;
113 | outerStrokeWeight = opts.outerStrokeWeight;
114 | innerStrokeWeight = opts.innerStrokeWeight;
115 |
116 | maxDepth = opts.depthDim;
117 | persp = opts.perspective;
118 |
119 | palette = tome.get(opts.palette);
120 | paletteShift = opts.paletteShift;
121 | strokeCol = palette.stroke ? palette.stroke : '#000';
122 |
123 | minGridSize = opts.minGridSize;
124 |
125 | sectionAppOpts = {
126 | simple: true,
127 | extension_chance: opts.outerSize,
128 | horizontal_symmetry: false,
129 | vertical_chance: 1,
130 | };
131 |
132 | atomAppOpts = {
133 | simple: true,
134 | extension_chance: opts.innerSize,
135 | horizontal_symmetry: false,
136 | vertical_chance: 0.5,
137 | color_mode: opts.colorMode,
138 | group_size: 0.4,
139 | colors: [...Array(1000).keys()],
140 | };
141 | }
142 |
143 | function reset() {
144 | const generatorFront = new Apparatus(cubedimX, cubedimY, sectionAppOpts);
145 | const generatorLeft = new Apparatus(cubedimY, cubedimZ, sectionAppOpts);
146 | const generatorTop = new Apparatus(cubedimZ, cubedimX, sectionAppOpts);
147 | const frontApp = generatorFront.generate(null, null, true);
148 | /*
149 | const leftApp = generatorLeft.generate(
150 | frontApp[1].map((i) => ({ ...i[1], v: i[1].h })),
151 | null,
152 | true
153 | );
154 | const topApp = generatorTop.generate(
155 | leftApp[1].map((i) => ({ ...i[1], v: i[1].h })),
156 | frontApp[1][1].map((i) => ({ ...i, h: i.v })),
157 | true
158 | );
159 | */
160 |
161 | const frontGrids = frontApp[0].map((a) => createGrid(a, null, null));
162 | /*
163 | const leftGrids = leftApp[0].map((a) => createGrid(a, frontGrids, null));
164 | const topGrids = topApp[0].map((a) => createGrid(a, leftGrids, frontGrids));
165 | */
166 |
167 | frontLayout = get_overlap_graph(frontGrids.flatMap((g) => g.content));
168 |
169 | /*
170 | leftLayout = get_overlap_graph(leftGrids.flatMap((g) => g.content));
171 | topLayout = get_overlap_graph(topGrids.flatMap((g) => g.content));
172 | */
173 | }
174 |
175 | function displayLayout() {
176 | p.push();
177 | p.translate(tx + p.width / 2, ty + p.height / 2);
178 | //p.background(palette.background ? palette.background : '#eee');
179 | p.clear();
180 |
181 | const ft = perspective(...getSrcDst(xu, yu, persp, cubedimX));
182 | const lt = perspective(...getSrcDst(yu, nzu, persp, cubedimX));
183 | const tt = perspective(...getSrcDst(nzu, xu, persp, cubedimX));
184 |
185 | const sf = shadeOpacityFront;
186 | const sl = shadeOpacityLeft;
187 | const st = shadeOpacityTop;
188 |
189 | frontLayout.forEach((i) => displayBox(i, xu, yu, zu, [sf, sl, st], true, true, ft, tt, lt));
190 | //leftLayout.forEach((i) => displayBox(i, yu, nzu, nxu, [sl, st, sf], false, true, lt, ft, tt));
191 | //topLayout.forEach((i) => displayBox(i, nzu, xu, nyu, [st, sf, sl], false, false, tt, lt, ft));
192 | p.pop();
193 | }
194 |
195 | function displayBox(box, xu, yu, zu, shades, hiddenTop, hiddenLeft, t1, t2, t3) {
196 | display(
197 | p,
198 | box,
199 | xu,
200 | yu,
201 | zu,
202 | maxDepth,
203 | shades,
204 | palette.colors,
205 | paletteShift,
206 | strokeCol,
207 | innerStrokeWeight,
208 | outerStrokeWeight,
209 | hiddenTop,
210 | hiddenLeft,
211 | t1,
212 | t2,
213 | t3
214 | );
215 | }
216 |
217 | function createGrid(box, topside, leftside) {
218 | const { x1, y1, w, h } = box;
219 |
220 | const topsideGrid =
221 | topside && y1 == 1 ? topside.filter((c) => c.x1 == 1 && c.y1 == x1)[0] : null;
222 |
223 | const leftsideGrid =
224 | leftside && x1 == 1 ? leftside.filter((c) => c.y1 == 1 && c.x1 == y1)[0] : null;
225 |
226 | const cols = topsideGrid ? topsideGrid.rows : Math.ceil((Math.random() * w) / minGridSize);
227 | const rows = leftsideGrid ? leftsideGrid.cols : Math.ceil((Math.random() * h) / minGridSize);
228 |
229 | const cell_w = w / cols;
230 | const cell_h = h / rows;
231 |
232 | const init_top = topsideGrid
233 | ? topsideGrid.apparatus.map((i) => ({ ...i[1], v: i[1].h }))
234 | : null;
235 |
236 | const init_left = leftsideGrid
237 | ? leftsideGrid.apparatus[1].map((i) => ({ ...i, h: i.v }))
238 | : null;
239 |
240 | const apparatus = createApparatus(cell_w, cell_h, init_top, init_left);
241 | const apparatus2 = createApparatus(cell_w, cell_h, init_top, init_left);
242 | const apparatus3 = createApparatus(cell_w, cell_h, init_top, init_left);
243 | const apparatus4 = createApparatus(cell_w, cell_h, init_top, init_left);
244 | let grid = [];
245 | for (let i = 0; i < rows; i++) {
246 | for (let j = 0; j < cols; j++) {
247 | const roll = Math.random();
248 | const roll2 = Math.random();
249 | let curr_app =
250 | roll < (i / rows) * 1.5
251 | ? roll < (i / rows) * 1.5 - 0.5
252 | ? apparatus
253 | : apparatus2
254 | : apparatus3;
255 |
256 | if (roll < i / rows) curr_app = roll2 < j / cols ? apparatus : apparatus2;
257 | else curr_app = roll2 < j / cols ? apparatus3 : apparatus4;
258 |
259 | const content = curr_app[0].map((app) => {
260 | const xpos = x1 + app.x1 + j * cell_w - 1;
261 | const ypos = y1 + app.y1 + i * cell_h - 1;
262 | let y_offset =
263 | topsideGrid && i == 0 && ypos <= 0
264 | ? topsideGrid.content.filter((c) => c.x1 <= 0 && Math.max(c.y1, 0) == xpos)[0].z1
265 | : 0;
266 | let x_offset =
267 | leftsideGrid && j == 0 && xpos <= 0
268 | ? leftsideGrid.content.filter((c) => c.y1 <= 0 && Math.max(c.x1, 0) == ypos)[0].z1
269 | : 0;
270 | return {
271 | ...app,
272 | x1: xpos,
273 | y1: ypos,
274 | w: app.w,
275 | h: app.h,
276 | x_off: x_offset,
277 | y_off: y_offset,
278 | level: 2,
279 | filled: true,
280 | };
281 | });
282 |
283 | grid = grid.concat(content);
284 | }
285 | }
286 | return {
287 | x1: x1,
288 | y1: y1,
289 | cols: cols,
290 | rows: rows,
291 | apparatus: apparatus[1],
292 | content: grid,
293 | };
294 | }
295 |
296 | function createApparatus(w, h, top, left) {
297 | const cols = Math.round(w, 0);
298 | const rows = Math.round(h, 0);
299 |
300 | const w_unit = w / cols;
301 | const h_unit = h / rows;
302 |
303 | const generator = new Apparatus((cols - 11) / 2, (rows - 11) / 2, atomAppOpts);
304 |
305 | const apparatus = generator.generate(top, left, true);
306 | apparatus[0] = apparatus[0].map((a) => ({
307 | x1: (a.x1 - 1) * w_unit,
308 | y1: (a.y1 - 1) * h_unit,
309 | z1: Math.floor(Math.random() * depthSteps) / depthSteps,
310 | w: a.w * w_unit,
311 | h: a.h * h_unit,
312 | col: a.col,
313 | }));
314 |
315 | return apparatus;
316 | }
317 |
318 | function overlaps(a, b) {
319 | const lca = [a.x1 + a.w, a.y1];
320 | const rca = [a.x1, a.y1 + a.h];
321 | const ba = [a.x1, a.y1];
322 |
323 | const lcb = [b.x1 + b.w, b.y1];
324 | const rcb = [b.x1, b.y1 + b.h];
325 | const bb = [b.x1, b.y1];
326 |
327 | if (a.y1 + 0.005 >= b.y1 + b.h || a.x1 + 0.005 >= b.x1 + b.w) return false;
328 |
329 | if (ba[1] - ba[0] < bb[1] - bb[0]) {
330 | // A is left of B
331 | if (rca[1] - rca[0] <= lcb[1] - lcb[0]) return false;
332 | return rca[1] + rca[0] < lcb[1] + lcb[0]; // positive if A is in front of B
333 | }
334 |
335 | if (ba[1] - ba[0] > bb[1] - bb[0]) {
336 | // A is right of B
337 | if (lca[1] - lca[0] >= rcb[1] - rcb[0]) return false;
338 | return lca[1] + lca[0] < rcb[1] + rcb[0]; // positive if A is in front of B
339 | }
340 | return ba[1] + ba[0] < bb[1] + bb[0];
341 | }
342 |
343 | function get_overlap_graph(boxes) {
344 | const nodes = [];
345 | boxes.forEach((box, i) => nodes.push(i));
346 |
347 | const edges = [];
348 | boxes.forEach((b1, i) => {
349 | boxes.forEach((b2, j) => {
350 | if (overlaps(b1, b2)) edges.push([i, j, b1, b2]);
351 | });
352 | });
353 |
354 | const overlapping = toposort(edges);
355 | return overlapping.reverse().map((i) => boxes[i]);
356 | }
357 |
358 | function print() {
359 | p.saveCanvas('sketch_' + THE_SEED, 'png');
360 | }
361 |
362 | p.keyPressed = function () {
363 | if (p.keyCode === 80) print();
364 | if (p.keyCode === 82) generateAndDraw();
365 | };
366 | };
367 | new p5(sketch);
368 |
369 | function getSrcDst(xu, yu, persp, dim) {
370 | const m = dim * 2 + 11;
371 |
372 | const src = [
373 | 0,
374 | 0,
375 | m * xu[0],
376 | m * xu[1],
377 | m * (xu[0] + yu[0]),
378 | m * (xu[1] + yu[1]),
379 | m * yu[0],
380 | m * yu[1],
381 | ];
382 | const dst = [
383 | 0,
384 | 0,
385 | m * xu[0],
386 | m * xu[1],
387 | m * (xu[0] + yu[0]) * persp,
388 | m * (xu[1] + yu[1]) * persp,
389 | m * yu[0],
390 | m * yu[1],
391 | ];
392 |
393 | return [src, dst];
394 | }
395 |
--------------------------------------------------------------------------------
/index_radial.js:
--------------------------------------------------------------------------------
1 | import Apparatus from 'apparatus-generator';
2 | import * as tome from 'chromotome';
3 |
4 | let sketch = function(p) {
5 | let THE_SEED;
6 |
7 | const mag = 10;
8 | const xu = [mag, 0]; //[1 * mag, -0.2 * mag]; // X Unit
9 | const yu = [0, mag]; //[0.3 * mag, 0.8 * mag]; // Y Unit
10 |
11 | const palette = get_palette();
12 | const generator = new Apparatus(15, 30, {
13 | simple: true,
14 | extension_chance: 0.95,
15 | horizontal_symmetry: false,
16 | vertical_chance: 0.5
17 | });
18 |
19 | const innerApparatusOptions = {
20 | simple: true,
21 | extension_chance: 0.68,
22 | horizontal_symmetry: false,
23 | vertical_chance: 0.5,
24 | color_mode: 'random',
25 | colors: palette.colors
26 | };
27 |
28 | let layout;
29 | let tick;
30 |
31 | p.setup = function() {
32 | p.createCanvas(950, 950);
33 | THE_SEED = p.floor(p.random(9999999));
34 | p.randomSeed(THE_SEED);
35 | p.noFill();
36 | p.smooth();
37 | //p.frameRate(3);
38 | p.stroke(palette.stroke ? palette.stroke : '#111');
39 | p.background(palette.background ? palette.background : '#eee');
40 | p.noLoop();
41 |
42 | tick = 0;
43 | };
44 |
45 | p.draw = function() {
46 | reset();
47 | displayLayout(4, true);
48 | /*
49 | if (tick % 9 == 0) reset();
50 | displayLayout(tick % 9, tick % 9 > 2);
51 | tick++;
52 | */
53 | };
54 |
55 | p.keyPressed = function() {
56 | if (p.keyCode === 80) p.saveCanvas('sketch_' + THE_SEED, 'jpeg');
57 | };
58 |
59 | function reset() {
60 | p.background(palette.background ? palette.background : '#eee');
61 | p.stroke(palette.background);
62 | layout = generator
63 | .generate()
64 | .map(b => ({ ...b, level: 0, filled: false, content: createGrid(b) }));
65 | }
66 |
67 | function displayLayout(depth, colorize) {
68 | p.translate(p.width / 2, p.height / 2);
69 | layout.forEach(box => {
70 | displayBox(box, depth, colorize);
71 | });
72 | }
73 |
74 | function displayBox(box, maxLevel, colorize) {
75 | if (box.content != null && box.content.length > 0 && maxLevel > box.level) {
76 | box.content.forEach(c => displayBox(c, maxLevel, colorize));
77 | }
78 |
79 | if (box.filled && colorize) p.fill(box.col);
80 | else p.noFill();
81 | var cir1 = (box.y1 / 71) * Math.PI * 2;
82 | var cir2 = ((box.y1 + box.h) / 71) * Math.PI * 2;
83 | var rad1 = box.x1;
84 | var rad2 = box.x1 + box.w;
85 |
86 | var p1 = [Math.cos(cir1) * rad1, Math.sin(cir1) * rad1];
87 | var p2 = [Math.cos(cir1) * rad2, Math.sin(cir1) * rad2];
88 | var p3 = [Math.cos(cir2) * rad2, Math.sin(cir2) * rad2];
89 | var p4 = [Math.cos(cir2) * rad1, Math.sin(cir2) * rad1];
90 |
91 | p.strokeWeight(5 / (box.level + 1));
92 | //p.noFill();
93 |
94 | p.beginShape();
95 | p.vertex(p1[0] * xu[0] + p1[1] * yu[0], p1[0] * xu[1] + p1[1] * yu[1]);
96 | p.vertex(p2[0] * xu[0] + p2[1] * yu[0], p2[0] * xu[1] + p2[1] * yu[1]);
97 | arc(p, rad2 * mag, cir1, cir2);
98 | p.vertex(p3[0] * xu[0] + p3[1] * yu[0], p3[0] * xu[1] + p3[1] * yu[1]);
99 | p.vertex(p4[0] * xu[0] + p4[1] * yu[0], p4[0] * xu[1] + p4[1] * yu[1]);
100 | arc(p, rad1 * mag, cir2, cir1);
101 | p.endShape(p.CLOSE);
102 | }
103 |
104 | function createGrid(box) {
105 | const { x1, y1, w, h } = box;
106 | const cols = Math.ceil((Math.random() * w) / 2);
107 | const rows = Math.ceil((Math.random() * h) / 2);
108 | const cell_w = w / cols;
109 | const cell_h = h / rows;
110 |
111 | const apparatus = createApparatus(cell_w, cell_h);
112 |
113 | const grid = [];
114 | for (let i = 0; i < rows; i++) {
115 | for (let j = 0; j < cols; j++) {
116 | const cell = {
117 | x1: x1 + j * cell_w,
118 | y1: y1 + i * cell_h,
119 | w: cell_w,
120 | h: cell_h,
121 | level: 1,
122 | filled: false
123 | };
124 | const content = apparatus.map(app => ({
125 | ...app,
126 | x1: app.x1 + cell.x1,
127 | y1: app.y1 + cell.y1,
128 | level: 2,
129 | filled: true,
130 | crossed: app.w < 1.5 && app.h < 1.5 && Math.random() < 0.3,
131 | legend_width: 2 + Math.random() * (app.w - 3)
132 | }));
133 |
134 | grid.push({ ...cell, content: content });
135 | }
136 | }
137 | return grid;
138 | }
139 |
140 | function createApparatus(w, h) {
141 | const cols = Math.round(w, 0);
142 | const rows = Math.round(h, 0);
143 |
144 | const w_unit = w / cols;
145 | const h_unit = h / rows;
146 |
147 | const generator = new Apparatus(
148 | (cols - 11) / 2,
149 | (rows - 11) / 2,
150 | innerApparatusOptions
151 | );
152 |
153 | return generator.generate().map(a => ({
154 | x1: (a.x1 - 1) * w_unit,
155 | y1: (a.y1 - 1) * h_unit,
156 | w: a.w * w_unit,
157 | h: a.h * h_unit,
158 | col: a.col
159 | }));
160 | }
161 | };
162 |
163 | new p5(sketch);
164 |
165 | function get_palette() {
166 | const url = window.location.href.split('?');
167 | if (url.length === 1) return tome.get('spatial01');
168 | return tome.get(url[1]);
169 | }
170 |
171 | function arc(p, rad, c1, c2) {
172 | let c = c1;
173 |
174 | let pos = c1 < c2;
175 |
176 | if (pos) {
177 | while (c < c2) {
178 | p.vertex(Math.cos(c) * rad, Math.sin(c) * rad);
179 | c += 0.02;
180 | }
181 | } else {
182 | while (c > c2) {
183 | p.vertex(Math.cos(c) * rad, Math.sin(c) * rad);
184 | c -= 0.02;
185 | }
186 | }
187 | }
188 |
--------------------------------------------------------------------------------
/index_sheet.js:
--------------------------------------------------------------------------------
1 | import Apparatus from 'apparatus-generator';
2 | import * as tome from 'chromotome';
3 |
4 | let sketch = function(p) {
5 | let THE_SEED;
6 |
7 | const mag = 22;
8 | const xu = [1 * mag, 0 * mag]; // X Unit
9 | const yu = [0 * mag, 1 * mag]; // Y Unit
10 |
11 | const palette = get_palette();
12 |
13 | const top = [];
14 | for (let i = 0; i < 16 * 2 + 11; i++) {
15 | top.push({ h: false, v: true, in: true, col: '#f88' });
16 | }
17 |
18 | const left = [];
19 | for (let i = 0; i < 25 * 2 + 11; i++) {
20 | left.push({ h: true, v: false, in: true, col: '#f88' });
21 | }
22 |
23 | const generator = new Apparatus(16, 25, {
24 | simple: true,
25 | extension_chance: 0.97,
26 | horizontal_symmetry: false,
27 | vertical_chance: 0.2,
28 | initial_top: top,
29 | initial_left: left
30 | });
31 |
32 | const innerApparatusOptions = {
33 | simple: true,
34 | extension_chance: 0.8,
35 | horizontal_symmetry: false,
36 | vertical_chance: 0.2,
37 | color_mode: 'main',
38 | colors: palette.colors
39 | };
40 |
41 | let layout;
42 | let tick;
43 |
44 | p.setup = function() {
45 | p.createCanvas(1050, 1485);
46 | THE_SEED = p.floor(p.random(9999999));
47 | p.randomSeed(THE_SEED);
48 | p.noFill();
49 | p.smooth();
50 | p.frameRate(1);
51 | p.stroke(palette.stroke ? palette.stroke : '#111');
52 | p.background(palette.background ? palette.background : '#eee');
53 |
54 | tick = 0;
55 | };
56 |
57 | p.draw = function() {
58 | if (tick % 9 == 0) reset();
59 | displayLayout(tick % 9, true);
60 | tick++;
61 | };
62 |
63 | p.keyPressed = function() {
64 | if (p.keyCode === 80) p.saveCanvas('sketch_' + THE_SEED, 'jpeg');
65 | };
66 |
67 | function reset() {
68 | p.background(palette.background ? palette.background : '#eee');
69 | layout = generator
70 | .generate()
71 | .map(b => ({ ...b, level: 0, filled: false, content: createGrid(b) }));
72 | }
73 |
74 | function displayLayout(depth, colorize) {
75 | p.translate(30, 30);
76 | layout.forEach(box => {
77 | displayBox(box, depth, colorize);
78 | });
79 | }
80 |
81 | function displayBox(box, maxLevel, colorize) {
82 | if (box.content != null && box.content.length > 0 && maxLevel > box.level) {
83 | box.content.forEach(c => displayBox(c, maxLevel, colorize));
84 | }
85 |
86 | if (box.filled && colorize) p.fill(box.col);
87 | else p.noFill();
88 |
89 | p.strokeWeight(6 / (box.level + 1));
90 | p.beginShape();
91 | p.vertex(box.x1 * xu[0] + box.y1 * yu[0], box.x1 * xu[1] + box.y1 * yu[1]);
92 | p.vertex(
93 | (box.x1 + box.w) * xu[0] + box.y1 * yu[0],
94 | (box.x1 + box.w) * xu[1] + box.y1 * yu[1]
95 | );
96 | p.vertex(
97 | (box.x1 + box.w) * xu[0] + (box.y1 + box.h) * yu[0],
98 | (box.x1 + box.w) * xu[1] + (box.y1 + box.h) * yu[1]
99 | );
100 | p.vertex(
101 | box.x1 * xu[0] + (box.y1 + box.h) * yu[0],
102 | box.x1 * xu[1] + (box.y1 + box.h) * yu[1]
103 | );
104 | p.endShape(p.CLOSE);
105 |
106 | if (colorize && box.filled && box.h > 1.5 && box.w > 3) {
107 | displayLegend(box);
108 | }
109 |
110 | if (colorize && box.filled && box.crossed) {
111 | displayCross(box);
112 | }
113 | }
114 |
115 | function displayLegend(box) {
116 | p.line(
117 | (box.x1 + 0.5) * xu[0] + (box.y1 + 0.5) * yu[0],
118 | (box.x1 + 0.5) * xu[1] + (box.y1 + 0.5) * yu[1],
119 | (box.x1 + box.legend_width) * xu[0] + (box.y1 + 0.5) * yu[0],
120 | (box.x1 + box.legend_width) * xu[1] + (box.y1 + 0.5) * yu[1]
121 | );
122 | }
123 |
124 | function displayCross(box) {
125 | p.line(
126 | box.x1 * xu[0] + box.y1 * yu[0],
127 | box.x1 * xu[1] + box.y1 * yu[1],
128 | (box.x1 + box.w) * xu[0] + (box.y1 + box.h) * yu[0],
129 | (box.x1 + box.w) * xu[1] + (box.y1 + box.h) * yu[1]
130 | );
131 | p.line(
132 | (box.x1 + box.w) * xu[0] + box.y1 * yu[0],
133 | (box.x1 + box.w) * xu[1] + box.y1 * yu[1],
134 | box.x1 * xu[0] + (box.y1 + box.h) * yu[0],
135 | box.x1 * xu[1] + (box.y1 + box.h) * yu[1]
136 | );
137 | }
138 |
139 | function createGrid(box) {
140 | const { x1, y1, w, h } = box;
141 | const cols = Math.ceil((Math.random() * w) / 4);
142 | const rows = Math.ceil((Math.random() * h) / 2);
143 | const cell_w = w / cols;
144 | const cell_h = h / rows;
145 |
146 | const apparatus = createApparatus(cell_w, cell_h);
147 |
148 | const grid = [];
149 | for (let i = 0; i < rows; i++) {
150 | for (let j = 0; j < cols; j++) {
151 | const cell = {
152 | x1: x1 + j * cell_w,
153 | y1: y1 + i * cell_h,
154 | w: cell_w,
155 | h: cell_h,
156 | level: 1,
157 | filled: false
158 | };
159 | const content = apparatus.map(app => ({
160 | ...app,
161 | x1: app.x1 + cell.x1,
162 | y1: app.y1 + cell.y1,
163 | level: 2,
164 | filled: true,
165 | crossed: app.w < 1.5 && app.h < 1.5 && Math.random() < 0.3,
166 | legend_width: 2 + Math.random() * (app.w - 3)
167 | }));
168 |
169 | grid.push({ ...cell, content: content });
170 | }
171 | }
172 | return grid;
173 | }
174 |
175 | function createApparatus(w, h) {
176 | const cols = Math.round(w, 0);
177 | const rows = Math.round(h, 0);
178 |
179 | const w_unit = w / cols;
180 | const h_unit = h / rows;
181 |
182 | const generator = new Apparatus(
183 | (cols - 11) / 2,
184 | (rows - 11) / 2,
185 | innerApparatusOptions
186 | );
187 |
188 | return generator.generate().map(a => ({
189 | x1: (a.x1 - 1) * w_unit,
190 | y1: (a.y1 - 1) * h_unit,
191 | w: a.w * w_unit,
192 | h: a.h * h_unit,
193 | col: a.col
194 | }));
195 | }
196 | };
197 |
198 | new p5(sketch);
199 |
200 | function get_palette() {
201 | const url = window.location.href.split('#');
202 | if (url.length === 1) return tome.get();
203 | return tome.get(url[1]);
204 | }
205 |
--------------------------------------------------------------------------------
/index_symmetry.js:
--------------------------------------------------------------------------------
1 | import Apparatus from 'apparatus-generator';
2 | import * as tome from 'chromotome';
3 | import toposort from 'toposort';
4 | import perspective from 'change-perspective';
5 |
6 | import ui from './ui';
7 | import display from './display';
8 |
9 | // Options suitable for print.
10 | let opts = {
11 | cubedimX: 30,
12 | cubedimY: 10,
13 | cubedimZ: 5,
14 | depthDim: 2.5,
15 | mag: 6,
16 | tx: -400, // -935 for 20x20
17 | ty: 190, // 1220 for 20x20
18 | shadeOpacityFront: 0,
19 | shadeOpacityLeft: 1,
20 | shadeOpacityTop: 0,
21 | outerStrokeWeight: 3,
22 | innerStrokeWeight: 1,
23 | outerSize: 1,
24 | minGridSize: 22,
25 | innerSize: 0.9,
26 | perspective: 1,
27 | colorMode: 'group',
28 | palette: 'dt13',
29 | paletteShift: 0,
30 | };
31 |
32 | let sketch = function (p) {
33 | let THE_SEED;
34 |
35 | let cubedimX;
36 | let cubedimY;
37 | let cubedimZ;
38 | let tx, ty;
39 |
40 | // const xr = (-1 * Math.PI) / 6;
41 | // const yr = (3 * Math.PI) / 6;
42 | // const zr = (1 * Math.PI) / 6;
43 |
44 | const xr = (-5 * Math.PI) / 6;
45 | const yr = (-1 * Math.PI) / 6;
46 | const zr = (-3 * Math.PI) / 6;
47 |
48 | let xu, yu, zu;
49 | let nxu, nyu, nzu;
50 |
51 | let maxDepth;
52 | const depthSteps = 8;
53 |
54 | let paletteShift;
55 | let palette;
56 | let strokeCol;
57 | let shadeOpacityFront, shadeOpacityLeft, shadeOpacityTop;
58 | let outerStrokeWeight, innerStrokeWeight;
59 |
60 | let sectionAppOpts, atomAppOpts;
61 | let minGridSize;
62 |
63 | let persp;
64 |
65 | let frontLayout, leftLayout, topLayout;
66 |
67 | p.setup = function () {
68 | p.createCanvas(2970, 4200);
69 | THE_SEED = p.floor(p.random(9999999));
70 | p.randomSeed(THE_SEED);
71 | p.pixelDensity(4);
72 | p.noFill();
73 | p.smooth();
74 | p.frameRate(1);
75 | p.strokeJoin(p.ROUND);
76 | p.noLoop();
77 |
78 | ui(opts, generateAndDraw, updateAndDraw, print);
79 |
80 | generateAndDraw();
81 | };
82 |
83 | function generateAndDraw() {
84 | updateGlobals(opts);
85 |
86 | //p.background(palette.stroke ? palette.stroke : '#eee');
87 | p.background(palette.background ? palette.background : '#eee');
88 |
89 | p.push();
90 |
91 | var w = 12;
92 | var h = 8;
93 | p.translate(xu[0] * 160 * (h / 2), xu[1] * 160 * (h / 2));
94 | p.translate(yu[0] * 80 * (w / 2), yu[1] * 80 * (w / 2));
95 |
96 | var layout1 = reset();
97 | var layout2 = reset();
98 | var layout3 = reset();
99 | var layout4 = reset();
100 | var layout5 = reset();
101 | var layout6 = reset();
102 | var layout7 = reset();
103 | for (var i = 0; i < h; i++) {
104 | p.push();
105 | //var layout1 = reset();
106 | for (var j = 0; j < w; j++) {
107 | //displayLayout(layout1);
108 | var pick = Math.random();
109 | if (pick < 0.15) displayLayout(layout1);
110 | else if (pick < 0.3) displayLayout(layout2);
111 | else if (pick < 0.45) displayLayout(layout3);
112 | else if (pick < 0.6) displayLayout(layout4);
113 | else if (pick < 0.75) displayLayout(layout5);
114 | else if (pick < 0.9) displayLayout(layout6);
115 | else displayLayout(layout7);
116 | p.translate(-yu[0] * 80, -yu[1] * 80);
117 | }
118 | p.pop();
119 | p.translate(-xu[0] * 160, -xu[1] * 160);
120 | }
121 | p.pop();
122 | }
123 |
124 | function updateAndDraw() {
125 | generateAndDraw();
126 | }
127 |
128 | function updateGlobals(opts) {
129 | cubedimX = opts.cubedimX;
130 | cubedimY = opts.cubedimY;
131 | cubedimZ = opts.cubedimZ;
132 |
133 | tx = opts.tx;
134 | ty = opts.ty;
135 |
136 | xu = [Math.cos(xr) * opts.mag, Math.sin(xr) * opts.mag];
137 | yu = [Math.cos(yr) * opts.mag, Math.sin(yr) * opts.mag];
138 | zu = [Math.cos(zr) * opts.mag, Math.sin(zr) * opts.mag];
139 |
140 | nxu = xu.map((v) => -v);
141 | nyu = yu.map((v) => -v);
142 | nzu = zu.map((v) => -v);
143 |
144 | shadeOpacityFront = opts.shadeOpacityFront;
145 | shadeOpacityLeft = opts.shadeOpacityLeft;
146 | shadeOpacityTop = opts.shadeOpacityTop;
147 | outerStrokeWeight = opts.outerStrokeWeight;
148 | innerStrokeWeight = opts.innerStrokeWeight;
149 |
150 | maxDepth = opts.depthDim;
151 | persp = opts.perspective;
152 |
153 | palette = tome.get(opts.palette);
154 | paletteShift = opts.paletteShift;
155 | strokeCol = palette.stroke ? palette.stroke : '#000';
156 |
157 | minGridSize = opts.minGridSize;
158 |
159 | sectionAppOpts = {
160 | simple: true,
161 | extension_chance: opts.outerSize,
162 | horizontal_symmetry: false,
163 | vertical_chance: 1,
164 | };
165 |
166 | atomAppOpts = {
167 | simple: true,
168 | extension_chance: opts.innerSize,
169 | horizontal_symmetry: true,
170 | vertical_symmetry: true,
171 | vertical_chance: 0.5,
172 | color_mode: opts.colorMode,
173 | group_size: 0.4,
174 | colors: [...Array(1000).keys()],
175 | };
176 | }
177 |
178 | function reset() {
179 | const generatorFront = new Apparatus(cubedimX, cubedimY, sectionAppOpts);
180 | const frontApp = generatorFront.generate(null, null, true);
181 |
182 | const frontGrids = frontApp[0].map((a) => createGrid(a, null, null));
183 | return get_overlap_graph(frontGrids.flatMap((g) => g.content));
184 | }
185 |
186 | function displayLayout(layout) {
187 | p.push();
188 | p.translate(tx + p.width / 2, ty + p.height / 2);
189 | //p.clear();
190 |
191 | const ft = perspective(...getSrcDst(xu, yu, persp, cubedimX));
192 | const lt = perspective(...getSrcDst(yu, nzu, persp, cubedimX));
193 | const tt = perspective(...getSrcDst(nzu, xu, persp, cubedimX));
194 |
195 | const sf = shadeOpacityFront;
196 | const sl = shadeOpacityLeft;
197 | const st = shadeOpacityTop;
198 |
199 | layout.forEach((i) => displayBox(i, xu, yu, zu, [sf, sl, st], false, false, ft, tt, lt));
200 | p.pop();
201 | }
202 |
203 | function displayBox(box, xu, yu, zu, shades, hiddenTop, hiddenLeft, t1, t2, t3) {
204 | if (box.z1 < 0.15) return;
205 | display(
206 | p,
207 | box,
208 | xu,
209 | yu,
210 | zu,
211 | maxDepth,
212 | shades,
213 | palette.colors,
214 | paletteShift,
215 | strokeCol,
216 | innerStrokeWeight,
217 | outerStrokeWeight,
218 | hiddenTop,
219 | hiddenLeft,
220 | t1,
221 | t2,
222 | t3,
223 | true
224 | );
225 | }
226 |
227 | function createGrid(box, topside, leftside) {
228 | const { x1, y1, w, h } = box;
229 |
230 | const topsideGrid =
231 | topside && y1 == 1 ? topside.filter((c) => c.x1 == 1 && c.y1 == x1)[0] : null;
232 |
233 | const leftsideGrid =
234 | leftside && x1 == 1 ? leftside.filter((c) => c.y1 == 1 && c.x1 == y1)[0] : null;
235 |
236 | const cols = 1; // topsideGrid ? topsideGrid.rows : Math.ceil((Math.random() * w) / minGridSize);
237 | const rows = 1; // leftsideGrid ? leftsideGrid.cols : Math.ceil((Math.random() * h) / minGridSize);
238 |
239 | const cell_w = w / cols;
240 | const cell_h = h / rows;
241 |
242 | const init_top = topsideGrid
243 | ? topsideGrid.apparatus.map((i) => ({ ...i[1], v: i[1].h }))
244 | : null;
245 |
246 | const init_left = leftsideGrid
247 | ? leftsideGrid.apparatus[1].map((i) => ({ ...i, h: i.v }))
248 | : null;
249 |
250 | const apparatus = createApparatus(cell_w, cell_h, init_top, init_left);
251 | let grid = [];
252 | for (let i = 0; i < rows; i++) {
253 | for (let j = 0; j < cols; j++) {
254 | const content = apparatus[0].map((app) => {
255 | const xpos = x1 + app.x1 + j * cell_w - 1;
256 | const ypos = y1 + app.y1 + i * cell_h - 1;
257 | let y_offset =
258 | topsideGrid && i == 0 && ypos <= 0
259 | ? topsideGrid.content.filter((c) => c.x1 <= 0 && Math.max(c.y1, 0) == xpos)[0].z1
260 | : 0;
261 | let x_offset =
262 | leftsideGrid && j == 0 && xpos <= 0
263 | ? leftsideGrid.content.filter((c) => c.y1 <= 0 && Math.max(c.x1, 0) == ypos)[0].z1
264 | : 0;
265 | return {
266 | ...app,
267 | x1: xpos,
268 | y1: ypos,
269 | w: app.w,
270 | h: app.h,
271 | x_off: x_offset,
272 | y_off: y_offset,
273 | level: 2,
274 | filled: true,
275 | };
276 | });
277 |
278 | grid = grid.concat(content);
279 | }
280 | }
281 | return {
282 | x1: x1,
283 | y1: y1,
284 | cols: cols,
285 | rows: rows,
286 | apparatus: apparatus[1],
287 | content: grid,
288 | };
289 | }
290 |
291 | function createApparatus(w, h, top, left) {
292 | const cols = Math.round(w, 0);
293 | const rows = Math.round(h, 0);
294 |
295 | const w_unit = w / cols;
296 | const h_unit = h / rows;
297 |
298 | const generator = new Apparatus((cols - 11) / 2, (rows - 11) / 2, atomAppOpts);
299 |
300 | const apparatus = generator.generate(top, left, true);
301 | apparatus[0] = apparatus[0].map((a) => ({
302 | x1: (a.x1 - 1) * w_unit,
303 | y1: (a.y1 - 1) * h_unit,
304 | z1: Math.floor(a.el * depthSteps) / depthSteps,
305 | id: a.id,
306 | w: a.w * w_unit,
307 | h: a.h * h_unit,
308 | col: a.col,
309 | }));
310 |
311 | return apparatus;
312 | }
313 |
314 | function overlaps(a, b) {
315 | const lca = [a.x1 + a.w, a.y1];
316 | const rca = [a.x1, a.y1 + a.h];
317 | const ba = [a.x1, a.y1];
318 |
319 | const lcb = [b.x1 + b.w, b.y1];
320 | const rcb = [b.x1, b.y1 + b.h];
321 | const bb = [b.x1, b.y1];
322 |
323 | if (a.y1 + 0.005 >= b.y1 + b.h || a.x1 + 0.005 >= b.x1 + b.w) return false;
324 |
325 | if (ba[1] - ba[0] < bb[1] - bb[0]) {
326 | // A is left of B
327 | if (rca[1] - rca[0] <= lcb[1] - lcb[0]) return false;
328 | return rca[1] + rca[0] < lcb[1] + lcb[0]; // positive if A is in front of B
329 | }
330 |
331 | if (ba[1] - ba[0] > bb[1] - bb[0]) {
332 | // A is right of B
333 | if (lca[1] - lca[0] >= rcb[1] - rcb[0]) return false;
334 | return lca[1] + lca[0] < rcb[1] + rcb[0]; // positive if A is in front of B
335 | }
336 | return ba[1] + ba[0] < bb[1] + bb[0];
337 | }
338 |
339 | function get_overlap_graph(boxes) {
340 | const nodes = [];
341 | boxes.forEach((box, i) => nodes.push(i));
342 |
343 | const edges = [];
344 | boxes.forEach((b1, i) => {
345 | boxes.forEach((b2, j) => {
346 | if (overlaps(b1, b2)) edges.push([i, j, b1, b2]);
347 | });
348 | });
349 |
350 | const overlapping = toposort(edges);
351 | return overlapping.reverse().map((i) => boxes[i]);
352 | }
353 |
354 | function print() {
355 | p.saveCanvas('sketch_' + THE_SEED, 'png');
356 | }
357 |
358 | function draw_speckles(w, h, col) {
359 | p.stroke(col);
360 | p.strokeWeight(2);
361 | for (var i = 0; i < 2000; i++) p.point(Math.random() * w, Math.random() * h);
362 | }
363 |
364 | p.keyPressed = function () {
365 | if (p.keyCode === 80) print();
366 | if (p.keyCode === 82) generateAndDraw();
367 | };
368 | };
369 | new p5(sketch);
370 |
371 | function getSrcDst(xu, yu, persp, dim) {
372 | const m = dim * 2 + 11;
373 |
374 | const src = [
375 | 0,
376 | 0,
377 | m * xu[0],
378 | m * xu[1],
379 | m * (xu[0] + yu[0]),
380 | m * (xu[1] + yu[1]),
381 | m * yu[0],
382 | m * yu[1],
383 | ];
384 | const dst = [
385 | 0,
386 | 0,
387 | m * xu[0],
388 | m * xu[1],
389 | m * (xu[0] + yu[0]) * persp,
390 | m * (xu[1] + yu[1]) * persp,
391 | m * yu[0],
392 | m * yu[1],
393 | ];
394 |
395 | return [src, dst];
396 | }
397 |
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ballots",
3 | "version": "1.0.0",
4 | "lockfileVersion": 2,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "name": "ballots",
9 | "version": "1.0.0",
10 | "license": "ISC",
11 | "dependencies": {
12 | "apparatus-generator": "^1.6.5",
13 | "change-perspective": "^1.0.1",
14 | "chromotome": "^1.20.0",
15 | "dat.gui": "^0.7.7",
16 | "toposort": "^2.0.2"
17 | },
18 | "devDependencies": {
19 | "@rollup/plugin-commonjs": "^15.1.0",
20 | "@rollup/plugin-node-resolve": "^9.0.0",
21 | "rollup": "^2.63.0"
22 | }
23 | },
24 | "node_modules/@rollup/plugin-commonjs": {
25 | "version": "15.1.0",
26 | "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-15.1.0.tgz",
27 | "integrity": "sha512-xCQqz4z/o0h2syQ7d9LskIMvBSH4PX5PjYdpSSvgS+pQik3WahkQVNWg3D8XJeYjZoVWnIUQYDghuEMRGrmQYQ==",
28 | "dev": true,
29 | "dependencies": {
30 | "@rollup/pluginutils": "^3.1.0",
31 | "commondir": "^1.0.1",
32 | "estree-walker": "^2.0.1",
33 | "glob": "^7.1.6",
34 | "is-reference": "^1.2.1",
35 | "magic-string": "^0.25.7",
36 | "resolve": "^1.17.0"
37 | },
38 | "engines": {
39 | "node": ">= 8.0.0"
40 | },
41 | "peerDependencies": {
42 | "rollup": "^2.22.0"
43 | }
44 | },
45 | "node_modules/@rollup/plugin-commonjs/node_modules/estree-walker": {
46 | "version": "2.0.1",
47 | "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.1.tgz",
48 | "integrity": "sha512-tF0hv+Yi2Ot1cwj9eYHtxC0jB9bmjacjQs6ZBTj82H8JwUywFuc+7E83NWfNMwHXZc11mjfFcVXPe9gEP4B8dg==",
49 | "dev": true
50 | },
51 | "node_modules/@rollup/plugin-commonjs/node_modules/magic-string": {
52 | "version": "0.25.7",
53 | "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz",
54 | "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==",
55 | "dev": true,
56 | "dependencies": {
57 | "sourcemap-codec": "^1.4.4"
58 | }
59 | },
60 | "node_modules/@rollup/plugin-commonjs/node_modules/resolve": {
61 | "version": "1.17.0",
62 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz",
63 | "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==",
64 | "dev": true,
65 | "dependencies": {
66 | "path-parse": "^1.0.6"
67 | },
68 | "funding": {
69 | "url": "https://github.com/sponsors/ljharb"
70 | }
71 | },
72 | "node_modules/@rollup/plugin-node-resolve": {
73 | "version": "9.0.0",
74 | "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-9.0.0.tgz",
75 | "integrity": "sha512-gPz+utFHLRrd41WMP13Jq5mqqzHL3OXrfj3/MkSyB6UBIcuNt9j60GCbarzMzdf1VHFpOxfQh/ez7wyadLMqkg==",
76 | "dev": true,
77 | "dependencies": {
78 | "@rollup/pluginutils": "^3.1.0",
79 | "@types/resolve": "1.17.1",
80 | "builtin-modules": "^3.1.0",
81 | "deepmerge": "^4.2.2",
82 | "is-module": "^1.0.0",
83 | "resolve": "^1.17.0"
84 | },
85 | "engines": {
86 | "node": ">= 10.0.0"
87 | },
88 | "peerDependencies": {
89 | "rollup": "^1.20.0||^2.0.0"
90 | }
91 | },
92 | "node_modules/@rollup/plugin-node-resolve/node_modules/resolve": {
93 | "version": "1.17.0",
94 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz",
95 | "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==",
96 | "dev": true,
97 | "dependencies": {
98 | "path-parse": "^1.0.6"
99 | },
100 | "funding": {
101 | "url": "https://github.com/sponsors/ljharb"
102 | }
103 | },
104 | "node_modules/@rollup/pluginutils": {
105 | "version": "3.1.0",
106 | "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz",
107 | "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==",
108 | "dev": true,
109 | "dependencies": {
110 | "@types/estree": "0.0.39",
111 | "estree-walker": "^1.0.1",
112 | "picomatch": "^2.2.2"
113 | },
114 | "engines": {
115 | "node": ">= 8.0.0"
116 | },
117 | "peerDependencies": {
118 | "rollup": "^1.20.0||^2.0.0"
119 | }
120 | },
121 | "node_modules/@rollup/pluginutils/node_modules/estree-walker": {
122 | "version": "1.0.1",
123 | "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz",
124 | "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==",
125 | "dev": true
126 | },
127 | "node_modules/@types/estree": {
128 | "version": "0.0.39",
129 | "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz",
130 | "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==",
131 | "dev": true
132 | },
133 | "node_modules/@types/node": {
134 | "version": "12.6.9",
135 | "resolved": "https://registry.npmjs.org/@types/node/-/node-12.6.9.tgz",
136 | "integrity": "sha512-+YB9FtyxXGyD54p8rXwWaN1EWEyar5L58GlGWgtH2I9rGmLGBQcw63+0jw+ujqVavNuO47S1ByAjm9zdHMnskw==",
137 | "dev": true
138 | },
139 | "node_modules/@types/resolve": {
140 | "version": "1.17.1",
141 | "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz",
142 | "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==",
143 | "dev": true,
144 | "dependencies": {
145 | "@types/node": "*"
146 | }
147 | },
148 | "node_modules/apparatus-generator": {
149 | "version": "1.6.5",
150 | "resolved": "https://registry.npmjs.org/apparatus-generator/-/apparatus-generator-1.6.5.tgz",
151 | "integrity": "sha512-MM3LL+oADu+ZPMfE65GCbypmESFR+jJaesZHjpJNFm8C9pb6oH2huPCM/7OR4hPsATzEpABb17C9PI+Yo3TOUQ==",
152 | "dependencies": {
153 | "seed-random": "^2.2.0"
154 | }
155 | },
156 | "node_modules/balanced-match": {
157 | "version": "1.0.0",
158 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
159 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
160 | "dev": true
161 | },
162 | "node_modules/brace-expansion": {
163 | "version": "1.1.11",
164 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
165 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
166 | "dev": true,
167 | "dependencies": {
168 | "balanced-match": "^1.0.0",
169 | "concat-map": "0.0.1"
170 | }
171 | },
172 | "node_modules/builtin-modules": {
173 | "version": "3.1.0",
174 | "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.1.0.tgz",
175 | "integrity": "sha512-k0KL0aWZuBt2lrxrcASWDfwOLMnodeQjodT/1SxEQAXsHANgo6ZC/VEaSEHCXt7aSTZ4/4H5LKa+tBXmW7Vtvw==",
176 | "dev": true,
177 | "engines": {
178 | "node": ">=6"
179 | }
180 | },
181 | "node_modules/change-perspective": {
182 | "version": "1.0.1",
183 | "resolved": "https://registry.npmjs.org/change-perspective/-/change-perspective-1.0.1.tgz",
184 | "integrity": "sha512-x2w4Zv6lCAv85RXVSVFsL5MpOe1SveXyVmIcoT6RN+Kij22V9U8oNzzd4hxAmoZpvyK+/mUS0/2upGxu/FV09g=="
185 | },
186 | "node_modules/chromotome": {
187 | "version": "1.20.0",
188 | "resolved": "https://registry.npmjs.org/chromotome/-/chromotome-1.20.0.tgz",
189 | "integrity": "sha512-lxY3AwgdJZFbsq/8cAwQ6lzqPIUuj3uztWpQEfJ1fgEtCtjsvDD6Pp/sg1J5Mau+91nPxF0VH/UhV5CmYsBqDw=="
190 | },
191 | "node_modules/commondir": {
192 | "version": "1.0.1",
193 | "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
194 | "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=",
195 | "dev": true
196 | },
197 | "node_modules/concat-map": {
198 | "version": "0.0.1",
199 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
200 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
201 | "dev": true
202 | },
203 | "node_modules/dat.gui": {
204 | "version": "0.7.7",
205 | "resolved": "https://registry.npmjs.org/dat.gui/-/dat.gui-0.7.7.tgz",
206 | "integrity": "sha512-sRl/28gF/XRC5ywC9I4zriATTsQcpSsRG7seXCPnTkK8/EQMIbCu5NPMpICLGxX9ZEUvcXR3ArLYCtgreFoMDw=="
207 | },
208 | "node_modules/deepmerge": {
209 | "version": "4.2.2",
210 | "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz",
211 | "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==",
212 | "dev": true,
213 | "engines": {
214 | "node": ">=0.10.0"
215 | }
216 | },
217 | "node_modules/fs.realpath": {
218 | "version": "1.0.0",
219 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
220 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
221 | "dev": true
222 | },
223 | "node_modules/fsevents": {
224 | "version": "2.3.2",
225 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
226 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
227 | "dev": true,
228 | "hasInstallScript": true,
229 | "optional": true,
230 | "os": [
231 | "darwin"
232 | ],
233 | "engines": {
234 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
235 | }
236 | },
237 | "node_modules/glob": {
238 | "version": "7.1.6",
239 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
240 | "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
241 | "dev": true,
242 | "dependencies": {
243 | "fs.realpath": "^1.0.0",
244 | "inflight": "^1.0.4",
245 | "inherits": "2",
246 | "minimatch": "^3.0.4",
247 | "once": "^1.3.0",
248 | "path-is-absolute": "^1.0.0"
249 | },
250 | "engines": {
251 | "node": "*"
252 | },
253 | "funding": {
254 | "url": "https://github.com/sponsors/isaacs"
255 | }
256 | },
257 | "node_modules/inflight": {
258 | "version": "1.0.6",
259 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
260 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
261 | "dev": true,
262 | "dependencies": {
263 | "once": "^1.3.0",
264 | "wrappy": "1"
265 | }
266 | },
267 | "node_modules/inherits": {
268 | "version": "2.0.4",
269 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
270 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
271 | "dev": true
272 | },
273 | "node_modules/is-module": {
274 | "version": "1.0.0",
275 | "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz",
276 | "integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=",
277 | "dev": true
278 | },
279 | "node_modules/is-reference": {
280 | "version": "1.2.1",
281 | "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz",
282 | "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==",
283 | "dev": true,
284 | "dependencies": {
285 | "@types/estree": "*"
286 | }
287 | },
288 | "node_modules/minimatch": {
289 | "version": "3.0.4",
290 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
291 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
292 | "dev": true,
293 | "dependencies": {
294 | "brace-expansion": "^1.1.7"
295 | },
296 | "engines": {
297 | "node": "*"
298 | }
299 | },
300 | "node_modules/once": {
301 | "version": "1.4.0",
302 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
303 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
304 | "dev": true,
305 | "dependencies": {
306 | "wrappy": "1"
307 | }
308 | },
309 | "node_modules/path-is-absolute": {
310 | "version": "1.0.1",
311 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
312 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
313 | "dev": true,
314 | "engines": {
315 | "node": ">=0.10.0"
316 | }
317 | },
318 | "node_modules/path-parse": {
319 | "version": "1.0.6",
320 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
321 | "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
322 | "dev": true
323 | },
324 | "node_modules/picomatch": {
325 | "version": "2.2.2",
326 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz",
327 | "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==",
328 | "dev": true,
329 | "engines": {
330 | "node": ">=8.6"
331 | },
332 | "funding": {
333 | "url": "https://github.com/sponsors/jonschlinkert"
334 | }
335 | },
336 | "node_modules/rollup": {
337 | "version": "2.63.0",
338 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.63.0.tgz",
339 | "integrity": "sha512-nps0idjmD+NXl6OREfyYXMn/dar3WGcyKn+KBzPdaLecub3x/LrId0wUcthcr8oZUAcZAR8NKcfGGFlNgGL1kQ==",
340 | "dev": true,
341 | "bin": {
342 | "rollup": "dist/bin/rollup"
343 | },
344 | "engines": {
345 | "node": ">=10.0.0"
346 | },
347 | "optionalDependencies": {
348 | "fsevents": "~2.3.2"
349 | }
350 | },
351 | "node_modules/seed-random": {
352 | "version": "2.2.0",
353 | "resolved": "https://registry.npmjs.org/seed-random/-/seed-random-2.2.0.tgz",
354 | "integrity": "sha1-KpsZ4lCoFwmSMaW5mk2vgLf77VQ="
355 | },
356 | "node_modules/sourcemap-codec": {
357 | "version": "1.4.6",
358 | "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.6.tgz",
359 | "integrity": "sha512-1ZooVLYFxC448piVLBbtOxFcXwnymH9oUF8nRd3CuYDVvkRBxRl6pB4Mtas5a4drtL+E8LDgFkQNcgIw6tc8Hg==",
360 | "dev": true
361 | },
362 | "node_modules/toposort": {
363 | "version": "2.0.2",
364 | "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz",
365 | "integrity": "sha1-riF2gXXRVZ1IvvNUILL0li8JwzA="
366 | },
367 | "node_modules/wrappy": {
368 | "version": "1.0.2",
369 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
370 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
371 | "dev": true
372 | }
373 | },
374 | "dependencies": {
375 | "@rollup/plugin-commonjs": {
376 | "version": "15.1.0",
377 | "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-15.1.0.tgz",
378 | "integrity": "sha512-xCQqz4z/o0h2syQ7d9LskIMvBSH4PX5PjYdpSSvgS+pQik3WahkQVNWg3D8XJeYjZoVWnIUQYDghuEMRGrmQYQ==",
379 | "dev": true,
380 | "requires": {
381 | "@rollup/pluginutils": "^3.1.0",
382 | "commondir": "^1.0.1",
383 | "estree-walker": "^2.0.1",
384 | "glob": "^7.1.6",
385 | "is-reference": "^1.2.1",
386 | "magic-string": "^0.25.7",
387 | "resolve": "^1.17.0"
388 | },
389 | "dependencies": {
390 | "estree-walker": {
391 | "version": "2.0.1",
392 | "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.1.tgz",
393 | "integrity": "sha512-tF0hv+Yi2Ot1cwj9eYHtxC0jB9bmjacjQs6ZBTj82H8JwUywFuc+7E83NWfNMwHXZc11mjfFcVXPe9gEP4B8dg==",
394 | "dev": true
395 | },
396 | "magic-string": {
397 | "version": "0.25.7",
398 | "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz",
399 | "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==",
400 | "dev": true,
401 | "requires": {
402 | "sourcemap-codec": "^1.4.4"
403 | }
404 | },
405 | "resolve": {
406 | "version": "1.17.0",
407 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz",
408 | "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==",
409 | "dev": true,
410 | "requires": {
411 | "path-parse": "^1.0.6"
412 | }
413 | }
414 | }
415 | },
416 | "@rollup/plugin-node-resolve": {
417 | "version": "9.0.0",
418 | "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-9.0.0.tgz",
419 | "integrity": "sha512-gPz+utFHLRrd41WMP13Jq5mqqzHL3OXrfj3/MkSyB6UBIcuNt9j60GCbarzMzdf1VHFpOxfQh/ez7wyadLMqkg==",
420 | "dev": true,
421 | "requires": {
422 | "@rollup/pluginutils": "^3.1.0",
423 | "@types/resolve": "1.17.1",
424 | "builtin-modules": "^3.1.0",
425 | "deepmerge": "^4.2.2",
426 | "is-module": "^1.0.0",
427 | "resolve": "^1.17.0"
428 | },
429 | "dependencies": {
430 | "resolve": {
431 | "version": "1.17.0",
432 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz",
433 | "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==",
434 | "dev": true,
435 | "requires": {
436 | "path-parse": "^1.0.6"
437 | }
438 | }
439 | }
440 | },
441 | "@rollup/pluginutils": {
442 | "version": "3.1.0",
443 | "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz",
444 | "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==",
445 | "dev": true,
446 | "requires": {
447 | "@types/estree": "0.0.39",
448 | "estree-walker": "^1.0.1",
449 | "picomatch": "^2.2.2"
450 | },
451 | "dependencies": {
452 | "estree-walker": {
453 | "version": "1.0.1",
454 | "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz",
455 | "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==",
456 | "dev": true
457 | }
458 | }
459 | },
460 | "@types/estree": {
461 | "version": "0.0.39",
462 | "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz",
463 | "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==",
464 | "dev": true
465 | },
466 | "@types/node": {
467 | "version": "12.6.9",
468 | "resolved": "https://registry.npmjs.org/@types/node/-/node-12.6.9.tgz",
469 | "integrity": "sha512-+YB9FtyxXGyD54p8rXwWaN1EWEyar5L58GlGWgtH2I9rGmLGBQcw63+0jw+ujqVavNuO47S1ByAjm9zdHMnskw==",
470 | "dev": true
471 | },
472 | "@types/resolve": {
473 | "version": "1.17.1",
474 | "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz",
475 | "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==",
476 | "dev": true,
477 | "requires": {
478 | "@types/node": "*"
479 | }
480 | },
481 | "apparatus-generator": {
482 | "version": "1.6.5",
483 | "resolved": "https://registry.npmjs.org/apparatus-generator/-/apparatus-generator-1.6.5.tgz",
484 | "integrity": "sha512-MM3LL+oADu+ZPMfE65GCbypmESFR+jJaesZHjpJNFm8C9pb6oH2huPCM/7OR4hPsATzEpABb17C9PI+Yo3TOUQ==",
485 | "requires": {
486 | "seed-random": "^2.2.0"
487 | }
488 | },
489 | "balanced-match": {
490 | "version": "1.0.0",
491 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
492 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
493 | "dev": true
494 | },
495 | "brace-expansion": {
496 | "version": "1.1.11",
497 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
498 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
499 | "dev": true,
500 | "requires": {
501 | "balanced-match": "^1.0.0",
502 | "concat-map": "0.0.1"
503 | }
504 | },
505 | "builtin-modules": {
506 | "version": "3.1.0",
507 | "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.1.0.tgz",
508 | "integrity": "sha512-k0KL0aWZuBt2lrxrcASWDfwOLMnodeQjodT/1SxEQAXsHANgo6ZC/VEaSEHCXt7aSTZ4/4H5LKa+tBXmW7Vtvw==",
509 | "dev": true
510 | },
511 | "change-perspective": {
512 | "version": "1.0.1",
513 | "resolved": "https://registry.npmjs.org/change-perspective/-/change-perspective-1.0.1.tgz",
514 | "integrity": "sha512-x2w4Zv6lCAv85RXVSVFsL5MpOe1SveXyVmIcoT6RN+Kij22V9U8oNzzd4hxAmoZpvyK+/mUS0/2upGxu/FV09g=="
515 | },
516 | "chromotome": {
517 | "version": "1.20.0",
518 | "resolved": "https://registry.npmjs.org/chromotome/-/chromotome-1.20.0.tgz",
519 | "integrity": "sha512-lxY3AwgdJZFbsq/8cAwQ6lzqPIUuj3uztWpQEfJ1fgEtCtjsvDD6Pp/sg1J5Mau+91nPxF0VH/UhV5CmYsBqDw=="
520 | },
521 | "commondir": {
522 | "version": "1.0.1",
523 | "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
524 | "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=",
525 | "dev": true
526 | },
527 | "concat-map": {
528 | "version": "0.0.1",
529 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
530 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
531 | "dev": true
532 | },
533 | "dat.gui": {
534 | "version": "0.7.7",
535 | "resolved": "https://registry.npmjs.org/dat.gui/-/dat.gui-0.7.7.tgz",
536 | "integrity": "sha512-sRl/28gF/XRC5ywC9I4zriATTsQcpSsRG7seXCPnTkK8/EQMIbCu5NPMpICLGxX9ZEUvcXR3ArLYCtgreFoMDw=="
537 | },
538 | "deepmerge": {
539 | "version": "4.2.2",
540 | "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz",
541 | "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==",
542 | "dev": true
543 | },
544 | "fs.realpath": {
545 | "version": "1.0.0",
546 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
547 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
548 | "dev": true
549 | },
550 | "fsevents": {
551 | "version": "2.3.2",
552 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
553 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
554 | "dev": true,
555 | "optional": true
556 | },
557 | "glob": {
558 | "version": "7.1.6",
559 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
560 | "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
561 | "dev": true,
562 | "requires": {
563 | "fs.realpath": "^1.0.0",
564 | "inflight": "^1.0.4",
565 | "inherits": "2",
566 | "minimatch": "^3.0.4",
567 | "once": "^1.3.0",
568 | "path-is-absolute": "^1.0.0"
569 | }
570 | },
571 | "inflight": {
572 | "version": "1.0.6",
573 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
574 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
575 | "dev": true,
576 | "requires": {
577 | "once": "^1.3.0",
578 | "wrappy": "1"
579 | }
580 | },
581 | "inherits": {
582 | "version": "2.0.4",
583 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
584 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
585 | "dev": true
586 | },
587 | "is-module": {
588 | "version": "1.0.0",
589 | "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz",
590 | "integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=",
591 | "dev": true
592 | },
593 | "is-reference": {
594 | "version": "1.2.1",
595 | "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz",
596 | "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==",
597 | "dev": true,
598 | "requires": {
599 | "@types/estree": "*"
600 | }
601 | },
602 | "minimatch": {
603 | "version": "3.0.4",
604 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
605 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
606 | "dev": true,
607 | "requires": {
608 | "brace-expansion": "^1.1.7"
609 | }
610 | },
611 | "once": {
612 | "version": "1.4.0",
613 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
614 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
615 | "dev": true,
616 | "requires": {
617 | "wrappy": "1"
618 | }
619 | },
620 | "path-is-absolute": {
621 | "version": "1.0.1",
622 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
623 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
624 | "dev": true
625 | },
626 | "path-parse": {
627 | "version": "1.0.6",
628 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
629 | "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
630 | "dev": true
631 | },
632 | "picomatch": {
633 | "version": "2.2.2",
634 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz",
635 | "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==",
636 | "dev": true
637 | },
638 | "rollup": {
639 | "version": "2.63.0",
640 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.63.0.tgz",
641 | "integrity": "sha512-nps0idjmD+NXl6OREfyYXMn/dar3WGcyKn+KBzPdaLecub3x/LrId0wUcthcr8oZUAcZAR8NKcfGGFlNgGL1kQ==",
642 | "dev": true,
643 | "requires": {
644 | "fsevents": "~2.3.2"
645 | }
646 | },
647 | "seed-random": {
648 | "version": "2.2.0",
649 | "resolved": "https://registry.npmjs.org/seed-random/-/seed-random-2.2.0.tgz",
650 | "integrity": "sha1-KpsZ4lCoFwmSMaW5mk2vgLf77VQ="
651 | },
652 | "sourcemap-codec": {
653 | "version": "1.4.6",
654 | "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.6.tgz",
655 | "integrity": "sha512-1ZooVLYFxC448piVLBbtOxFcXwnymH9oUF8nRd3CuYDVvkRBxRl6pB4Mtas5a4drtL+E8LDgFkQNcgIw6tc8Hg==",
656 | "dev": true
657 | },
658 | "toposort": {
659 | "version": "2.0.2",
660 | "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz",
661 | "integrity": "sha1-riF2gXXRVZ1IvvNUILL0li8JwzA="
662 | },
663 | "wrappy": {
664 | "version": "1.0.2",
665 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
666 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
667 | "dev": true
668 | }
669 | }
670 | }
671 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ballots",
3 | "version": "1.0.0",
4 | "main": "index.js",
5 | "scripts": {
6 | "build": "rollup -c",
7 | "watch": "rollup -c -w"
8 | },
9 | "author": "Kjetil Golid",
10 | "license": "ISC",
11 | "keywords": [],
12 | "description": "Generates ballot-like visual structures using the Apparatus Generator repetetively.",
13 | "repository": {
14 | "type": "git",
15 | "url": "git://github.com/kgolid/ballots.git"
16 | },
17 | "browser": "dist/index.js",
18 | "browserSheet": "dist/index_sheet.js",
19 | "browserFull": "dist/index_full.js",
20 | "browserElevate": "dist/index_elevate.js",
21 | "browserCube": "dist/index_cube.js",
22 | "browserInteractive": "dist/index_interactive.js",
23 | "browserInteractivePrint": "dist/index_interactive_print.js",
24 | "browserInteractivePrintB": "dist/index_interactive_print_b.js",
25 | "browserInteractivePrintC": "dist/index_interactive_print_c.js",
26 | "browserSymmetry": "dist/index_symmetry.js",
27 | "browserRadial": "dist/index_radial.js",
28 | "dependencies": {
29 | "apparatus-generator": "^1.6.5",
30 | "change-perspective": "^1.0.1",
31 | "chromotome": "^1.20.0",
32 | "dat.gui": "^0.7.7",
33 | "toposort": "^2.0.2"
34 | },
35 | "devDependencies": {
36 | "@rollup/plugin-commonjs": "^15.1.0",
37 | "@rollup/plugin-node-resolve": "^9.0.0",
38 | "rollup": "^2.63.0"
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/presets_interactive.js:
--------------------------------------------------------------------------------
1 | export default {
2 | remembered: {
3 | Default: {
4 | 0: {
5 | cubedimX: 15,
6 | cubedimY: 15,
7 | cubedimZ: 15,
8 | depthDim: 2,
9 | mag: 5,
10 | tx: 0,
11 | ty: 0,
12 | shadeOpacityFront: 0.2,
13 | shadeOpacityLeft: 0.1,
14 | shadeOpacityTop: 0,
15 | outerStrokeWeight: 2,
16 | innerStrokeWeight: 1,
17 | outerSize: 0.97,
18 | minGridSize: 5,
19 | innerSize: 0.8,
20 | perspective: 0.85,
21 | colorMode: 'group',
22 | palette: 'tsu_arcade',
23 | paletteShift: 0,
24 | },
25 | },
26 | archetypish: {
27 | 0: {
28 | cubedimX: 35,
29 | cubedimY: 5,
30 | cubedimZ: 35,
31 | depthDim: 2,
32 | mag: 12,
33 | tx: 0,
34 | ty: 800,
35 | shadeOpacityFront: 1,
36 | shadeOpacityLeft: 1,
37 | shadeOpacityTop: 0,
38 | outerStrokeWeight: 3,
39 | innerStrokeWeight: 1,
40 | outerSize: 0.97,
41 | minGridSize: 5,
42 | innerSize: 0.8,
43 | perspective: 1,
44 | colorMode: 'group',
45 | palette: 'tsu_arcade',
46 | paletteShift: 0,
47 | },
48 | },
49 | },
50 | closed: false,
51 | };
52 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | import pkg from './package.json';
2 | import commonjs from '@rollup/plugin-commonjs';
3 | import resolve from '@rollup/plugin-node-resolve';
4 |
5 | export default [
6 | {
7 | input: 'index.js',
8 | output: {
9 | file: pkg.browser,
10 | format: 'umd',
11 | },
12 | plugins: [resolve(), commonjs()],
13 | },
14 | {
15 | input: 'index_full.js',
16 | output: {
17 | file: pkg.browserFull,
18 | format: 'umd',
19 | },
20 | plugins: [resolve(), commonjs()],
21 | },
22 | {
23 | input: 'index_elevate.js',
24 | output: {
25 | file: pkg.browserElevate,
26 | format: 'umd',
27 | },
28 | plugins: [resolve(), commonjs()],
29 | },
30 | {
31 | input: 'index_sheet.js',
32 | output: {
33 | file: pkg.browserSheet,
34 | format: 'umd',
35 | },
36 | plugins: [resolve(), commonjs()],
37 | },
38 | {
39 | input: 'index_cube.js',
40 | output: {
41 | file: pkg.browserCube,
42 | format: 'umd',
43 | },
44 | plugins: [resolve(), commonjs()],
45 | },
46 | {
47 | input: 'index_interactive.js',
48 | output: {
49 | file: pkg.browserInteractive,
50 | format: 'umd',
51 | },
52 | plugins: [resolve(), commonjs()],
53 | },
54 | {
55 | input: 'index_interactive_print.js',
56 | output: {
57 | file: pkg.browserInteractivePrint,
58 | format: 'umd',
59 | },
60 | plugins: [resolve(), commonjs()],
61 | },
62 | {
63 | input: 'index_interactive_print_b.js',
64 | output: {
65 | file: pkg.browserInteractivePrintB,
66 | format: 'umd',
67 | },
68 | plugins: [resolve(), commonjs()],
69 | },
70 | {
71 | input: 'index_interactive_print_c.js',
72 | output: {
73 | file: pkg.browserInteractivePrintC,
74 | format: 'umd',
75 | },
76 | plugins: [resolve(), commonjs()],
77 | },
78 | {
79 | input: 'index_symmetry.js',
80 | output: {
81 | file: pkg.browserSymmetry,
82 | format: 'umd',
83 | },
84 | plugins: [resolve(), commonjs()],
85 | },
86 | {
87 | input: 'index_radial.js',
88 | output: {
89 | file: pkg.browserRadial,
90 | format: 'umd',
91 | },
92 | plugins: [resolve(), commonjs()],
93 | },
94 | ];
95 |
--------------------------------------------------------------------------------
/simple.js:
--------------------------------------------------------------------------------
1 | let sketch = function (p) {
2 | let xdim = 1;
3 | let ydim = 1;
4 | let size = 30;
5 |
6 | let vseps;
7 | let hseps;
8 |
9 | p.setup = function () {
10 | p.createCanvas(1380, 900);
11 | p.frameRate(4);
12 | p.noLoop();
13 | p.background(230, 255, 172);
14 | p.noFill();
15 | };
16 |
17 | p.draw = function () {
18 | p.clear();
19 | p.translate(40, 40);
20 | var w = 4;
21 | var h = 2;
22 | for (var i = 0; i < 4; i++) {
23 | p.push();
24 | for (var j = 0; j < 4; j++) {
25 | generate_grid(xdim + w, ydim + h);
26 |
27 | p.strokeWeight(4);
28 | p.stroke(0);
29 | display(xdim + w, ydim + h);
30 |
31 | p.translate(100 + w * size, 0);
32 | }
33 | p.pop();
34 | p.translate(0, 100 + h * size);
35 | }
36 | };
37 |
38 | function generate_grid(xd, yd) {
39 | vseps = new Array(yd);
40 | hseps = new Array(yd - 1);
41 |
42 | for (var i = 0; i < yd; i++) {
43 | vseps[i] = new Array(xd - 1);
44 | vseps[i].fill(-1);
45 | }
46 |
47 | vseps[0] = randomly_fill_remaining_vseps(vseps[0]);
48 |
49 | for (var i = 0; i < yd - 1; i++) {
50 | hseps[i] = genereate_hseps(vseps[i], new Array(xd));
51 | vseps[i + 1] = fill_forced_vseps(hseps[i], vseps[i], vseps[i + 1]);
52 | vseps[i + 1] = randomly_fill_remaining_vseps(vseps[i + 1]);
53 | }
54 | }
55 |
56 | function display(xd, yd) {
57 | p.push();
58 | p.rect(0, 0, xd * size, yd * size);
59 |
60 | for (var j = 1; j < xd; j++) {
61 | if (vseps[0][j - 1] === 1) p.line(j * size, 0, j * size, size);
62 | }
63 |
64 | for (var i = 0; i < yd - 1; i++) {
65 | p.translate(0, size);
66 | if (hseps[i][0] === 1) p.line(0, 0, size, 0);
67 | for (var j = 1; j < xd; j++) {
68 | if (vseps[i + 1][j - 1] === 1) p.line(j * size, 0, j * size, size);
69 | if (hseps[i][j] === 1) p.line(j * size, 0, (j + 1) * size, 0);
70 | }
71 | }
72 | p.pop();
73 | }
74 |
75 | function randomly_fill_remaining_vseps(arr) {
76 | for (var i in arr) {
77 | if (arr[i] === -1) {
78 | arr[i] = flip_coin();
79 | }
80 | }
81 | return arr;
82 | }
83 |
84 | function genereate_hseps(vsep, arr) {
85 | arr[0] = flip_coin();
86 | for (var i = 1; i < arr.length; i++) {
87 | arr[i] = vsep[i - 1] === 0 ? arr[i - 1] : flip_coin();
88 | }
89 | return arr;
90 | }
91 |
92 | function fill_forced_vseps(hsep, last_vsep, arr) {
93 | for (var i = 0; i < arr.length; i++) {
94 | if (hsep[i] != hsep[i + 1]) {
95 | arr[i] = 1;
96 | } else if (hsep[i] === 0 && hsep[i + 1] === 0) {
97 | arr[i] = last_vsep[i];
98 | }
99 | }
100 | return arr;
101 | }
102 |
103 | function flip_coin() {
104 | return p.random() < 0.6 ? 0 : 1;
105 | }
106 |
107 | function display_ascii() {
108 | for (var r = 0; r < ydim - 1; r++) {
109 | var s = '|';
110 | if (hseps[r][0] === 1) s += '__';
111 | else s += ' ';
112 |
113 | for (var i = 0; i < xdim - 1; i++) {
114 | if (vseps[r][i] === 1) s += '|';
115 | else s += ' ';
116 | if (hseps[r][i + 1] === 1) s += '__';
117 | else s += ' ';
118 | }
119 | s += '|';
120 |
121 | console.log(s);
122 | }
123 |
124 | var s = '|__';
125 |
126 | for (var i = 0; i < xdim - 1; i++) {
127 | if (vseps[ydim - 1][i] === 1) s += '|';
128 | else s += ' ';
129 | s += '__';
130 | }
131 | s += '|';
132 |
133 | console.log(s);
134 | }
135 | };
136 |
137 | new p5(sketch);
138 |
--------------------------------------------------------------------------------
/ui.js:
--------------------------------------------------------------------------------
1 | import * as dat from 'dat.gui';
2 | import * as tome from 'chromotome';
3 |
4 | export default function (opts, full_reset, redraw, print, presets = null) {
5 | const onPaletteChange = function (controller) {
6 | controller.setValue(0);
7 | controller.max(tome.get(opts.palette).size - 1);
8 | };
9 |
10 | const ctrls = {
11 | print: print,
12 | reset: full_reset,
13 | };
14 |
15 | let gui;
16 | if (presets !== null) {
17 | gui = new dat.GUI({ load: presets });
18 | gui.remember(opts);
19 | } else {
20 | gui = new dat.GUI();
21 | }
22 | const f0 = gui.addFolder('Structural Changes');
23 | f0.open();
24 | f0.add(opts, 'cubedimX', 0, 70, 5).name('X Dimension').onChange(full_reset);
25 | f0.add(opts, 'cubedimY', 0, 70, 5).name('Y Dimension').onChange(full_reset);
26 | f0.add(opts, 'cubedimZ', 0, 70, 5).name('Z Dimension').onChange(full_reset);
27 | f0.add(opts, 'outerSize', 0.9, 1, 0.01).name('Section Sizes').onChange(full_reset);
28 | f0.add(opts, 'minGridSize', 1, 10, 1).name('Min Grid Size').onChange(full_reset);
29 | f0.add(opts, 'innerSize', 0.7, 1, 0.02).name('Atom Sizes').onChange(full_reset);
30 | f0.add(opts, 'colorMode', ['single', 'main', 'group', 'random'])
31 | .name('Color Distr.')
32 | .onChange(full_reset);
33 | const f1 = gui.addFolder('Stylistic Changes');
34 | f1.open();
35 | f1.add(opts, 'tx', -2400, 2400, 200).name('Translate X').onChange(redraw);
36 | f1.add(opts, 'ty', -2400, 2400, 200).name('Translate Y').onChange(redraw);
37 | f1.add(opts, 'mag', 2, 30, 1).name('Cell Size').onChange(redraw);
38 | f1.add(opts, 'depthDim', 0, 10, 0.5).name('Depth').onChange(redraw);
39 | f1.add(opts, 'perspective', 0.65, 1, 0.05).name('Perspective').onChange(redraw);
40 | const shiftController = f1
41 | .add(opts, 'paletteShift', 0, 10, 1)
42 | .listen()
43 | .name('Palette Shift')
44 | .onChange(redraw);
45 | f1.add(opts, 'palette', tome.getNames())
46 | .name('Palette')
47 | .onChange(redraw)
48 | .onFinishChange(() => onPaletteChange(shiftController));
49 | f1.add(opts, 'shadeOpacityFront', 0, 1, 0.1).name('Shade Opacity Front').onChange(redraw);
50 | f1.add(opts, 'shadeOpacityLeft', 0, 1, 0.1).name('Shade Opacity Left').onChange(redraw);
51 | f1.add(opts, 'shadeOpacityTop', 0, 1, 0.1).name('Shade Opacity Top').onChange(redraw);
52 | f1.add(opts, 'outerStrokeWeight', 0, 9, 1).name('Outer Stroke Weight').onChange(redraw);
53 | f1.add(opts, 'innerStrokeWeight', 0, 9, 1).name('Inner Stroke Weight').onChange(redraw);
54 | const f2 = gui.addFolder('Control');
55 | f2.open();
56 | f2.add(ctrls, 'reset').name('Generate new');
57 | f2.add(ctrls, 'print').name('Download image');
58 | }
59 |
--------------------------------------------------------------------------------