├── .gitattributes ├── .gitignore ├── LICENSE.txt ├── README.md ├── docs ├── index.html ├── main.e14af47b.js └── screenshots │ ├── 161440481.gif │ ├── 27197560.gif │ ├── 303599227.gif │ ├── 359947371.gif │ ├── 375615874.gif │ ├── 38676985.gif │ ├── 388848054.gif │ ├── 506507566.gif │ ├── 509235666.gif │ ├── 521822124.gif │ ├── 567782962.gif │ ├── 596174466.gif │ ├── 59931162.gif │ ├── 62065297.gif │ ├── 635382188.gif │ ├── 71464548.gif │ ├── 718006707.gif │ ├── 774417313.gif │ ├── 82205833.gif │ ├── 827394148.gif │ ├── 92028492.gif │ ├── 929464066.gif │ ├── 950816576.gif │ └── folmura.gif ├── package.json ├── src ├── formula.ts ├── index.html ├── main.ts ├── math.ts ├── random.ts └── vector.ts └── tsconfig.json /.gitattributes: -------------------------------------------------------------------------------- 1 | docs/* linguist-documentation 2 | build/* linguist-generated 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .vscode/ 3 | .cache/ 4 | tmp/ 5 | package-lock.json 6 | npm-debug.log -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 ABA Games 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # folmura ([DEMO](https://abagames.github.io/folmura/index.html)) 2 | 3 | Randomly generated visual with randomly generated formula. 4 | 5 | Click/Tap to generate another one. Press the 'F' key to see the formulas. 6 | 7 | [![screenshot](docs/screenshots/folmura.gif)](https://abagames.github.io/folmura/index.html) 8 | 9 | ## Interesting random seeds 10 | 11 | [![62065297](docs/screenshots/62065297.gif)](https://abagames.github.io/folmura/index.html?s=62065297) 12 | [![27197560](docs/screenshots/27197560.gif)](https://abagames.github.io/folmura/index.html?s=27197560) 13 | [![827394148](docs/screenshots/827394148.gif)](https://abagames.github.io/folmura/index.html?s=827394148) 14 | [![59931162](docs/screenshots/59931162.gif)](https://abagames.github.io/folmura/index.html?s=59931162) 15 | [![82205833](docs/screenshots/82205833.gif)](https://abagames.github.io/folmura/index.html?s=82205833) 16 | [![161440481](docs/screenshots/161440481.gif)](https://abagames.github.io/folmura/index.html?s=161440481) 17 | [![92028492](docs/screenshots/92028492.gif)](https://abagames.github.io/folmura/index.html?s=92028492) 18 | [![388848054](docs/screenshots/388848054.gif)](https://abagames.github.io/folmura/index.html?s=388848054) 19 | [![521822124](docs/screenshots/521822124.gif)](https://abagames.github.io/folmura/index.html?s=521822124) 20 | [![718006707](docs/screenshots/718006707.gif)](https://abagames.github.io/folmura/index.html?s=718006707) 21 | [![509235666](docs/screenshots/509235666.gif)](https://abagames.github.io/folmura/index.html?s=509235666) 22 | [![950816576](docs/screenshots/950816576.gif)](https://abagames.github.io/folmura/index.html?s=950816576) 23 | [![71464548](docs/screenshots/71464548.gif)](https://abagames.github.io/folmura/index.html?s=71464548) 24 | [![303599227](docs/screenshots/303599227.gif)](https://abagames.github.io/folmura/index.html?s=303599227) 25 | [![774417313](docs/screenshots/774417313.gif)](https://abagames.github.io/folmura/index.html?s=774417313) 26 | [![635382188](docs/screenshots/635382188.gif)](https://abagames.github.io/folmura/index.html?s=635382188) 27 | [![506507566](docs/screenshots/506507566.gif)](https://abagames.github.io/folmura/index.html?s=506507566) 28 | [![375615874](docs/screenshots/375615874.gif)](https://abagames.github.io/folmura/index.html?s=375615874) 29 | [![359947371](docs/screenshots/359947371.gif)](https://abagames.github.io/folmura/index.html?s=359947371) 30 | [![596174466](docs/screenshots/596174466.gif)](https://abagames.github.io/folmura/index.html?s=596174466) 31 | [![929464066](docs/screenshots/929464066.gif)](https://abagames.github.io/folmura/index.html?s=929464066) 32 | [![38676985](docs/screenshots/38676985.gif)](https://abagames.github.io/folmura/index.html?s=38676985) 33 | [![567782962](docs/screenshots/567782962.gif)](https://abagames.github.io/folmura/index.html?s=567782962) 34 | 35 | ## How to generate a visual 36 | 37 | The canvas consists of points connected with lines. The position, size and color of each point are defined by variables: 38 | 39 | ``` 40 | x/y = horizontal/vertical position 41 | w/h = width/height ((width + height) / 2 is used as a stroke weight of each line) 42 | H/S/B = color (Hue/Saturation/Brightness) 43 | ``` 44 | 45 | Each variable is calculated by a randomly generated formula. The formula is generated with combination of functions: 46 | 47 | ``` 48 | v1 + v2, v1 - v2, v1 * v2, v1 / v2, 49 | sin(v1), cos(v1), exp(v1), pow(v1, v2), noise(v1), 50 | v1 > v2 ? v3 : v4 51 | ``` 52 | 53 | The functions described above or variables described below are placed recursively to v1-v4. 54 | 55 | ``` 56 | t = time (in seconds) 57 | i = index of the point (each point has an index starting from 0) 58 | a/b = random integer value (2 to 10) 59 | ``` 60 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | folmura -------------------------------------------------------------------------------- /docs/screenshots/161440481.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abagames/folmura/3c9ad4c44e3c320934fe1e68a04ea6ad6bc63ff6/docs/screenshots/161440481.gif -------------------------------------------------------------------------------- /docs/screenshots/27197560.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abagames/folmura/3c9ad4c44e3c320934fe1e68a04ea6ad6bc63ff6/docs/screenshots/27197560.gif -------------------------------------------------------------------------------- /docs/screenshots/303599227.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abagames/folmura/3c9ad4c44e3c320934fe1e68a04ea6ad6bc63ff6/docs/screenshots/303599227.gif -------------------------------------------------------------------------------- /docs/screenshots/359947371.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abagames/folmura/3c9ad4c44e3c320934fe1e68a04ea6ad6bc63ff6/docs/screenshots/359947371.gif -------------------------------------------------------------------------------- /docs/screenshots/375615874.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abagames/folmura/3c9ad4c44e3c320934fe1e68a04ea6ad6bc63ff6/docs/screenshots/375615874.gif -------------------------------------------------------------------------------- /docs/screenshots/38676985.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abagames/folmura/3c9ad4c44e3c320934fe1e68a04ea6ad6bc63ff6/docs/screenshots/38676985.gif -------------------------------------------------------------------------------- /docs/screenshots/388848054.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abagames/folmura/3c9ad4c44e3c320934fe1e68a04ea6ad6bc63ff6/docs/screenshots/388848054.gif -------------------------------------------------------------------------------- /docs/screenshots/506507566.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abagames/folmura/3c9ad4c44e3c320934fe1e68a04ea6ad6bc63ff6/docs/screenshots/506507566.gif -------------------------------------------------------------------------------- /docs/screenshots/509235666.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abagames/folmura/3c9ad4c44e3c320934fe1e68a04ea6ad6bc63ff6/docs/screenshots/509235666.gif -------------------------------------------------------------------------------- /docs/screenshots/521822124.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abagames/folmura/3c9ad4c44e3c320934fe1e68a04ea6ad6bc63ff6/docs/screenshots/521822124.gif -------------------------------------------------------------------------------- /docs/screenshots/567782962.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abagames/folmura/3c9ad4c44e3c320934fe1e68a04ea6ad6bc63ff6/docs/screenshots/567782962.gif -------------------------------------------------------------------------------- /docs/screenshots/596174466.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abagames/folmura/3c9ad4c44e3c320934fe1e68a04ea6ad6bc63ff6/docs/screenshots/596174466.gif -------------------------------------------------------------------------------- /docs/screenshots/59931162.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abagames/folmura/3c9ad4c44e3c320934fe1e68a04ea6ad6bc63ff6/docs/screenshots/59931162.gif -------------------------------------------------------------------------------- /docs/screenshots/62065297.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abagames/folmura/3c9ad4c44e3c320934fe1e68a04ea6ad6bc63ff6/docs/screenshots/62065297.gif -------------------------------------------------------------------------------- /docs/screenshots/635382188.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abagames/folmura/3c9ad4c44e3c320934fe1e68a04ea6ad6bc63ff6/docs/screenshots/635382188.gif -------------------------------------------------------------------------------- /docs/screenshots/71464548.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abagames/folmura/3c9ad4c44e3c320934fe1e68a04ea6ad6bc63ff6/docs/screenshots/71464548.gif -------------------------------------------------------------------------------- /docs/screenshots/718006707.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abagames/folmura/3c9ad4c44e3c320934fe1e68a04ea6ad6bc63ff6/docs/screenshots/718006707.gif -------------------------------------------------------------------------------- /docs/screenshots/774417313.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abagames/folmura/3c9ad4c44e3c320934fe1e68a04ea6ad6bc63ff6/docs/screenshots/774417313.gif -------------------------------------------------------------------------------- /docs/screenshots/82205833.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abagames/folmura/3c9ad4c44e3c320934fe1e68a04ea6ad6bc63ff6/docs/screenshots/82205833.gif -------------------------------------------------------------------------------- /docs/screenshots/827394148.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abagames/folmura/3c9ad4c44e3c320934fe1e68a04ea6ad6bc63ff6/docs/screenshots/827394148.gif -------------------------------------------------------------------------------- /docs/screenshots/92028492.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abagames/folmura/3c9ad4c44e3c320934fe1e68a04ea6ad6bc63ff6/docs/screenshots/92028492.gif -------------------------------------------------------------------------------- /docs/screenshots/929464066.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abagames/folmura/3c9ad4c44e3c320934fe1e68a04ea6ad6bc63ff6/docs/screenshots/929464066.gif -------------------------------------------------------------------------------- /docs/screenshots/950816576.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abagames/folmura/3c9ad4c44e3c320934fe1e68a04ea6ad6bc63ff6/docs/screenshots/950816576.gif -------------------------------------------------------------------------------- /docs/screenshots/folmura.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abagames/folmura/3c9ad4c44e3c320934fe1e68a04ea6ad6bc63ff6/docs/screenshots/folmura.gif -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "folmura", 3 | "version": "1.0.0", 4 | "description": "Randomly generated visual with randomly generated formula", 5 | "scripts": { 6 | "watch": "parcel src/index.html -d tmp", 7 | "build": "parcel build src/index.html -d docs --no-source-maps --public-url ./" 8 | }, 9 | "author": "abagames", 10 | "license": "MIT", 11 | "browserslist": [ 12 | "last 1 Chrome version" 13 | ], 14 | "devDependencies": { 15 | "@types/p5": "^0.7.3", 16 | "parcel-bundler": "^1.12.3", 17 | "parcel-plugin-clean-dist": "0.0.6", 18 | "typescript": "^3.5.3" 19 | }, 20 | "dependencies": { 21 | "gif-capture-canvas": "^1.0.1", 22 | "p5": "^0.9.0" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/formula.ts: -------------------------------------------------------------------------------- 1 | import { p } from "./main"; 2 | import { Random } from "./random"; 3 | import { range } from "./math"; 4 | 5 | export type Formula = { 6 | func: Function; 7 | args?: Formula[]; 8 | value?: string; 9 | }; 10 | 11 | const funcList = [ 12 | [plus, 2], 13 | [minus, 2], 14 | [times, 2], 15 | [divide, 2], 16 | [sin, 1], 17 | [sin, 1], 18 | [cos, 1], 19 | [cos, 1], 20 | [exp, 1], 21 | [pow, 2], 22 | [noise, 1], 23 | [condition, 4] 24 | ]; 25 | 26 | const funcListNames = [ 27 | "plus", 28 | "minus", 29 | "times", 30 | "divide", 31 | "sin", 32 | "sin", 33 | "cos", 34 | "cos", 35 | "exp", 36 | "pow", 37 | "noise", 38 | "condition" 39 | ]; 40 | 41 | export function generate(random: Random, depth = 0): Formula { 42 | const r = random.get(); 43 | const dr = depth / 3; 44 | if (r < 0.5 * dr) { 45 | return { 46 | func: variable, 47 | value: random.select(["t", "i", "a", "b"]) 48 | }; 49 | } else { 50 | const fa = random.select(funcList); 51 | const func = fa[0]; 52 | const args = range(fa[1]).map(() => generate(random, depth + 1)); 53 | return { func, args }; 54 | } 55 | } 56 | 57 | export function calc(formula: Formula, variables) { 58 | return formula.func(formula, variables); 59 | } 60 | 61 | export function swapSinCos(f: Formula) { 62 | return { 63 | func: f.func === sin ? cos : f.func === cos ? sin : f.func, 64 | args: f.args == null ? undefined : f.args.map(f => swapSinCos(f)), 65 | value: f.value 66 | }; 67 | } 68 | 69 | export function toString(formula: Formula) { 70 | const func = formula.func; 71 | let argStrs = []; 72 | if (formula.args != null) { 73 | argStrs = formula.args.map(a => toString(a)); 74 | } 75 | if (func === plus) { 76 | return `(${argStrs[0]}+${argStrs[1]})`; 77 | } else if (func === minus) { 78 | return `(${argStrs[0]}-${argStrs[1]})`; 79 | } else if (func === times) { 80 | return `${argStrs[0]}*${argStrs[1]}`; 81 | } else if (func === divide) { 82 | return `${argStrs[0]}/${argStrs[1]}`; 83 | } else if (func === variable) { 84 | return formula.value; 85 | } else if (func == condition) { 86 | return `(${argStrs[0]}>${argStrs[1]}?${argStrs[2]}:${argStrs[3]})`; 87 | } 88 | const fi = funcList.findIndex(f => f[0] === func); 89 | const name = funcListNames[fi]; 90 | return `${name}(${argStrs.join(",")})`; 91 | } 92 | 93 | function variable(formula: Formula, variables) { 94 | return variables[formula.value]; 95 | } 96 | 97 | function plus(formula: Formula, variables) { 98 | return calc(formula.args[0], variables) + calc(formula.args[1], variables); 99 | } 100 | 101 | function minus(formula: Formula, variables) { 102 | return calc(formula.args[0], variables) - calc(formula.args[1], variables); 103 | } 104 | 105 | function times(formula: Formula, variables) { 106 | return calc(formula.args[0], variables) * calc(formula.args[1], variables); 107 | } 108 | 109 | function divide(formula: Formula, variables) { 110 | let v = calc(formula.args[1], variables); 111 | if (p.abs(v) < 0.01) { 112 | v = 0.01; 113 | } 114 | return calc(formula.args[0], variables) / v; 115 | } 116 | 117 | function sin(formula: Formula, variables) { 118 | return p.sin(calc(formula.args[0], variables)); 119 | } 120 | 121 | function cos(formula: Formula, variables) { 122 | return p.cos(calc(formula.args[0], variables)); 123 | } 124 | 125 | function exp(formula: Formula, variables) { 126 | return p.exp(calc(formula.args[0], variables)); 127 | } 128 | 129 | function pow(formula: Formula, variables) { 130 | return p.pow( 131 | calc(formula.args[0], variables), 132 | calc(formula.args[1], variables) 133 | ); 134 | } 135 | 136 | function noise(formula: Formula, variables) { 137 | return p.noise(calc(formula.args[0], variables)); 138 | } 139 | 140 | function condition(formula: Formula, variables) { 141 | return calc(formula.args[0], variables) > calc(formula.args[1], variables) 142 | ? calc(formula.args[2], variables) 143 | : calc(formula.args[3], variables); 144 | } 145 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | folmura 6 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import p5 from "p5"; 2 | import * as gcc from "gif-capture-canvas"; 3 | import * as formula from "./formula"; 4 | import { Random } from "./random"; 5 | import { range, wrap } from "./math"; 6 | import { Vector } from "./vector"; 7 | 8 | export let p: p5; 9 | 10 | const isCapturing = false; 11 | const seedRandom = new Random(); 12 | const random = new Random(); 13 | const variables: any = {}; 14 | let renderer; 15 | let formulaDiv: HTMLDivElement; 16 | let isShowingFormula = false; 17 | 18 | function setup() { 19 | renderer = p.createCanvas(500, 250); 20 | renderer.canvas.style.cssText = ` 21 | position: absolute; 22 | left: 50%; 23 | top: 50%; 24 | transform: translate(-50%, -50%); 25 | `; 26 | p.colorMode(p.HSB); 27 | p.background(0); 28 | formulaDiv = document.createElement("div"); 29 | formulaDiv.style.cssText = ` 30 | color: white; 31 | font-family: monospace; 32 | font-size: 12px; 33 | width: 500px; 34 | position: absolute; 35 | left: 50%; 36 | transform: translate(-50%, 0%); 37 | `; 38 | document.body.appendChild(formulaDiv); 39 | const seed = loadFromUrl(); 40 | if (seed == null) { 41 | nextFormulas(); 42 | } else { 43 | random.setSeed(seed); 44 | generateFormulas(); 45 | } 46 | p.touchStarted = nextFormulas; 47 | p.keyPressed = () => { 48 | if (p.key !== "f") { 49 | return; 50 | } 51 | isShowingFormula = !isShowingFormula; 52 | isShowingFormula ? showFormula() : hideFormula(); 53 | }; 54 | if (isCapturing) { 55 | gcc.setOptions({ capturingFps: 60, durationSec: 2 }); 56 | } 57 | } 58 | 59 | type FormulaRange = { 60 | min: number; 61 | max: number; 62 | targetMin: number; 63 | targetMax: number; 64 | frameMin: number; 65 | frameMax: number; 66 | }; 67 | 68 | type Shape = { 69 | pos: Vector; 70 | size: Vector; 71 | color: number[]; 72 | }; 73 | 74 | let formulas: formula.Formula[]; 75 | let formulaRanges: FormulaRange[]; 76 | let shapes: Shape[]; 77 | let t = 0; 78 | 79 | function nextFormulas() { 80 | const seed = seedRandom.getInt(0, 999999999); 81 | saveAsUrl(seed); 82 | random.setSeed(seed); 83 | generateFormulas(); 84 | return false; 85 | } 86 | 87 | function generateFormulas() { 88 | formulas = range(7).map(() => formula.generate(random)); 89 | if (random.get() < 0.5) { 90 | formulas[1] = formula.swapSinCos(formulas[0]); 91 | } 92 | if (random.get() < 0.5) { 93 | formulas[3] = formula.swapSinCos(formulas[2]); 94 | } 95 | formulaRanges = formulas.map(() => { 96 | return { 97 | min: -1, 98 | max: 1, 99 | targetMin: -1, 100 | targetMax: 1, 101 | frameMin: 1, 102 | frameMax: -1 103 | }; 104 | }); 105 | formulaRanges[0].targetMin = -p.width / 2; 106 | formulaRanges[0].targetMax = p.width / 2; 107 | formulaRanges[1].targetMin = -p.height / 2; 108 | formulaRanges[1].targetMax = p.height / 2; 109 | formulaRanges[2].targetMin = 1; 110 | formulaRanges[2].targetMax = p.width / 8; 111 | formulaRanges[3].targetMin = 1; 112 | formulaRanges[3].targetMax = p.height / 8; 113 | formulaRanges[4].targetMin = 0; 114 | formulaRanges[4].targetMax = 360; 115 | formulaRanges[5].targetMin = 50; 116 | formulaRanges[5].targetMax = 100; 117 | formulaRanges[6].targetMin = 50; 118 | formulaRanges[6].targetMax = 100; 119 | shapes = range(random.getInt(10, 100)).map(() => { 120 | return { pos: new Vector(), size: new Vector(), color: [0, 0, 0] }; 121 | }); 122 | variables["a"] = random.getInt(2, 10); 123 | variables["b"] = random.getInt(2, 10); 124 | t = 0; 125 | if (isShowingFormula) { 126 | showFormula(); 127 | } 128 | } 129 | 130 | function draw() { 131 | p.background(0); 132 | variables["t"] = t; 133 | shapes.forEach((s, i) => { 134 | variables["i"] = i; 135 | s.pos.set( 136 | adjustFormulaValue( 137 | formulaRanges[0], 138 | formula.calc(formulas[0], variables) 139 | ), 140 | adjustFormulaValue(formulaRanges[1], formula.calc(formulas[1], variables)) 141 | ); 142 | s.size.set( 143 | adjustFormulaValue( 144 | formulaRanges[2], 145 | formula.calc(formulas[2], variables) 146 | ), 147 | adjustFormulaValue(formulaRanges[3], formula.calc(formulas[3], variables)) 148 | ); 149 | s.color[0] += 150 | wrap( 151 | adjustFormulaValue( 152 | formulaRanges[4], 153 | formula.calc(formulas[4], variables) 154 | ) - s.color[0], 155 | -180, 156 | 180 157 | ) * 0.1; 158 | s.color[1] += 159 | (adjustFormulaValue( 160 | formulaRanges[5], 161 | formula.calc(formulas[5], variables) 162 | ) - 163 | s.color[1]) * 164 | 0.1; 165 | s.color[2] += 166 | (adjustFormulaValue( 167 | formulaRanges[6], 168 | formula.calc(formulas[6], variables) 169 | ) - 170 | s.color[2]) * 171 | 0.1; 172 | p.stroke(wrap(s.color[0], 0, 360), s.color[1], s.color[2], 0.5); 173 | p.strokeWeight(p.max((s.size.x + s.size.y) / 2, 1)); 174 | if (i > 0) { 175 | const ps = shapes[i - 1]; 176 | p.line( 177 | ps.pos.x + p.width / 2, 178 | ps.pos.y + p.height / 2, 179 | s.pos.x + p.width / 2, 180 | s.pos.y + p.height / 2 181 | ); 182 | } 183 | }); 184 | formulaRanges.forEach(fr => { 185 | adjustFormulaRange(fr); 186 | }); 187 | t += 1 / 60; 188 | if (isCapturing) { 189 | gcc.capture(renderer.canvas); 190 | } 191 | } 192 | 193 | function adjustFormulaValue(fr: FormulaRange, v: number) { 194 | if (isNaN(v)) { 195 | return 0; 196 | } 197 | if (v < fr.frameMin) { 198 | fr.frameMin = v; 199 | } 200 | if (v > fr.frameMax) { 201 | fr.frameMax = v; 202 | } 203 | return ( 204 | ((v - fr.min) / (fr.max - fr.min)) * (fr.targetMax - fr.targetMin) + 205 | fr.targetMin 206 | ); 207 | } 208 | 209 | function adjustFormulaRange(fr: FormulaRange) { 210 | fr.min += (fr.frameMin - fr.min) * (fr.frameMin < fr.min ? 0.1 : 0.01); 211 | fr.max += (fr.frameMax - fr.max) * (fr.frameMax > fr.max ? 0.1 : 0.01); 212 | if (fr.max <= fr.min) { 213 | fr.max = fr.min + 0.01; 214 | } 215 | fr.frameMin = fr.max; 216 | fr.frameMax = fr.min; 217 | } 218 | 219 | function showFormula() { 220 | formulaDiv.innerText = ` 221 | x = ${formula.toString(formulas[0])} 222 | y = ${formula.toString(formulas[1])} 223 | w = ${formula.toString(formulas[2])} 224 | h = ${formula.toString(formulas[3])} 225 | H = ${formula.toString(formulas[4])} 226 | S = ${formula.toString(formulas[5])} 227 | B = ${formula.toString(formulas[6])} 228 | a = ${variables["a"]} 229 | b = ${variables["b"]} 230 | `; 231 | } 232 | 233 | function hideFormula() { 234 | formulaDiv.innerText = ""; 235 | } 236 | 237 | function saveAsUrl(seed: number) { 238 | const baseUrl = window.location.href.split("?")[0]; 239 | let url = `${baseUrl}?s=${seed}`; 240 | try { 241 | window.history.replaceState({}, "", url); 242 | } catch (e) { 243 | console.log(e); 244 | } 245 | } 246 | 247 | function loadFromUrl() { 248 | const query = window.location.search.substring(1); 249 | if (query == null) { 250 | return undefined; 251 | } 252 | let params = query.split("&"); 253 | let seedStr: string; 254 | for (let i = 0; i < params.length; i++) { 255 | const param = params[i]; 256 | const pair = param.split("="); 257 | if (pair[0] === "s") { 258 | seedStr = pair[1]; 259 | } 260 | } 261 | if (seedStr == null) { 262 | return undefined; 263 | } 264 | return Math.floor(Number(seedStr)); 265 | } 266 | 267 | new p5((_p: p5) => { 268 | p = _p; 269 | p.setup = setup; 270 | p.draw = draw; 271 | }); 272 | -------------------------------------------------------------------------------- /src/math.ts: -------------------------------------------------------------------------------- 1 | export function clamp(v: number, low = 0, high = 1) { 2 | return Math.max(low, Math.min(v, high)); 3 | } 4 | 5 | export function wrap(v: number, low: number, high: number) { 6 | const w = high - low; 7 | const o = v - low; 8 | if (o >= 0) { 9 | return (o % w) + low; 10 | } else { 11 | let wv = w + (o % w) + low; 12 | if (wv >= high) { 13 | wv -= w; 14 | } 15 | return wv; 16 | } 17 | } 18 | 19 | export function isInRange(v: number, low: number, high: number) { 20 | return low <= v && v <= high; 21 | } 22 | 23 | export function range(v: number) { 24 | return [...Array(v).keys()]; 25 | } 26 | 27 | export function stableSort(values: any[], compareFunc?: Function) { 28 | if (compareFunc == null) { 29 | compareFunc = (a, b) => a - b; 30 | } 31 | const indexedValues = values.map((v, i) => [v, i]); 32 | indexedValues.sort((a, b) => { 33 | const cmp = compareFunc(a[0], b[0]); 34 | return cmp !== 0 ? cmp : a[1] - b[1]; 35 | }); 36 | return indexedValues.map(v => v[0]); 37 | } 38 | -------------------------------------------------------------------------------- /src/random.ts: -------------------------------------------------------------------------------- 1 | export class Random { 2 | x!: number; 3 | y!: number; 4 | z!: number; 5 | w!: number; 6 | 7 | constructor(seed: number = null) { 8 | this.setSeed(seed); 9 | } 10 | 11 | get(lowOrHigh: number = 1, high?: number) { 12 | if (high == null) { 13 | high = lowOrHigh; 14 | lowOrHigh = 0; 15 | } 16 | return (this.next() / 0xffffffff) * (high - lowOrHigh) + lowOrHigh; 17 | } 18 | 19 | getInt(lowOrHigh: number, high?: number) { 20 | if (high == null) { 21 | high = lowOrHigh; 22 | lowOrHigh = 0; 23 | } 24 | return (this.next() % (high - lowOrHigh)) + lowOrHigh; 25 | } 26 | 27 | getPlusOrMinus() { 28 | return this.getInt(2) * 2 - 1; 29 | } 30 | 31 | select(values: any[]) { 32 | return values[this.getInt(values.length)]; 33 | } 34 | 35 | setSeed( 36 | w?: number, 37 | x = 123456789, 38 | y = 362436069, 39 | z = 521288629, 40 | loopCount = 32 41 | ) { 42 | this.w = w != null ? w >>> 0 : Math.floor(Math.random() * 0xffffffff) >>> 0; 43 | this.x = x >>> 0; 44 | this.y = y >>> 0; 45 | this.z = z >>> 0; 46 | for (let i = 0; i < loopCount; i++) { 47 | this.next(); 48 | } 49 | return this; 50 | } 51 | 52 | next() { 53 | const t = this.x ^ (this.x << 11); 54 | this.x = this.y; 55 | this.y = this.z; 56 | this.z = this.w; 57 | this.w = (this.w ^ (this.w >>> 19) ^ (t ^ (t >>> 8))) >>> 0; 58 | return this.w; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/vector.ts: -------------------------------------------------------------------------------- 1 | import { clamp, isInRange, wrap } from "./math"; 2 | 3 | export interface VectorLike { 4 | x: number; 5 | y: number; 6 | } 7 | 8 | function isVectorLike(v: any): v is VectorLike { 9 | return v.x != null && v.y != null; 10 | } 11 | 12 | export class Vector { 13 | x = 0; 14 | y = 0; 15 | 16 | constructor(x: number | VectorLike = 0, y?: number) { 17 | this.set(x, y); 18 | } 19 | 20 | set(x: number | VectorLike, y?: number) { 21 | if (isVectorLike(x)) { 22 | this.x = x.x; 23 | this.y = x.y; 24 | return this; 25 | } 26 | this.x = x; 27 | this.y = y == null ? x : y; 28 | return this; 29 | } 30 | 31 | add(v: VectorLike) { 32 | this.x += v.x; 33 | this.y += v.y; 34 | return this; 35 | } 36 | 37 | sub(v: VectorLike) { 38 | this.x -= v.x; 39 | this.y -= v.y; 40 | return this; 41 | } 42 | 43 | mul(v: number) { 44 | this.x *= v; 45 | this.y *= v; 46 | return this; 47 | } 48 | 49 | div(v: number) { 50 | this.x /= v; 51 | this.y /= v; 52 | return this; 53 | } 54 | 55 | clamp(xLow: number, xHigh: number, yLow: number, yHigh: number) { 56 | this.x = clamp(this.x, xLow, xHigh); 57 | this.y = clamp(this.y, yLow, yHigh); 58 | return this; 59 | } 60 | 61 | wrap(xLow: number, xHigh: number, yLow: number, yHigh: number) { 62 | this.x = wrap(this.x, xLow, xHigh); 63 | this.y = wrap(this.y, yLow, yHigh); 64 | return this; 65 | } 66 | 67 | addAngle(angle: number, value: number) { 68 | this.x += Math.cos(angle) * value; 69 | this.y += Math.sin(angle) * value; 70 | return this; 71 | } 72 | 73 | swapXy() { 74 | const t = this.x; 75 | this.x = this.y; 76 | this.y = t; 77 | return this; 78 | } 79 | 80 | normalize() { 81 | this.div(this.length); 82 | return this; 83 | } 84 | 85 | rotate(angle: number) { 86 | if (angle === 0) { 87 | return this; 88 | } 89 | const tx = this.x; 90 | this.x = tx * Math.cos(angle) - this.y * Math.sin(angle); 91 | this.y = tx * Math.sin(angle) + this.y * Math.cos(angle); 92 | return this; 93 | } 94 | 95 | getAngle(to?: VectorLike) { 96 | return to == null 97 | ? Math.atan2(this.y, this.x) 98 | : Math.atan2(to.y - this.y, to.x - this.x); 99 | } 100 | 101 | distanceTo(to: VectorLike) { 102 | const ox = this.x - to.x; 103 | const oy = this.y - to.y; 104 | return Math.sqrt(ox * ox + oy * oy); 105 | } 106 | 107 | isInRect(x: number, y: number, width: number, height: number) { 108 | return isInRange(this.x, x, x + width) && isInRange(this.y, y, y + height); 109 | } 110 | 111 | equals(other: VectorLike) { 112 | return this.x === other.x && this.y === other.y; 113 | } 114 | 115 | floor() { 116 | this.x = Math.floor(this.x); 117 | this.y = Math.floor(this.y); 118 | return this; 119 | } 120 | 121 | round() { 122 | this.x = Math.round(this.x); 123 | this.y = Math.round(this.y); 124 | return this; 125 | } 126 | 127 | ceil() { 128 | this.x = Math.ceil(this.x); 129 | this.y = Math.ceil(this.y); 130 | return this; 131 | } 132 | 133 | get length() { 134 | return Math.sqrt(this.x * this.x + this.y * this.y); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2015", 4 | "module": "commonjs", 5 | "esModuleInterop": true 6 | }, 7 | "include": ["src/**/*"] 8 | } 9 | --------------------------------------------------------------------------------