├── .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 | [](https://abagames.github.io/folmura/index.html)
8 |
9 | ## Interesting random seeds
10 |
11 | [](https://abagames.github.io/folmura/index.html?s=62065297)
12 | [](https://abagames.github.io/folmura/index.html?s=27197560)
13 | [](https://abagames.github.io/folmura/index.html?s=827394148)
14 | [](https://abagames.github.io/folmura/index.html?s=59931162)
15 | [](https://abagames.github.io/folmura/index.html?s=82205833)
16 | [](https://abagames.github.io/folmura/index.html?s=161440481)
17 | [](https://abagames.github.io/folmura/index.html?s=92028492)
18 | [](https://abagames.github.io/folmura/index.html?s=388848054)
19 | [](https://abagames.github.io/folmura/index.html?s=521822124)
20 | [](https://abagames.github.io/folmura/index.html?s=718006707)
21 | [](https://abagames.github.io/folmura/index.html?s=509235666)
22 | [](https://abagames.github.io/folmura/index.html?s=950816576)
23 | [](https://abagames.github.io/folmura/index.html?s=71464548)
24 | [](https://abagames.github.io/folmura/index.html?s=303599227)
25 | [](https://abagames.github.io/folmura/index.html?s=774417313)
26 | [](https://abagames.github.io/folmura/index.html?s=635382188)
27 | [](https://abagames.github.io/folmura/index.html?s=506507566)
28 | [](https://abagames.github.io/folmura/index.html?s=375615874)
29 | [](https://abagames.github.io/folmura/index.html?s=359947371)
30 | [](https://abagames.github.io/folmura/index.html?s=596174466)
31 | [](https://abagames.github.io/folmura/index.html?s=929464066)
32 | [](https://abagames.github.io/folmura/index.html?s=38676985)
33 | [](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 |
--------------------------------------------------------------------------------