├── .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 | --------------------------------------------------------------------------------