├── js ├── examples │ ├── dev │ │ └── reflection.js │ ├── p5setup.js │ ├── random │ │ ├── random_noise2.js │ │ ├── random.js │ │ ├── random_squared.js │ │ ├── random_noise.js │ │ ├── random_average.js │ │ ├── random_norm.js │ │ ├── random_power.js │ │ ├── random_average2.js │ │ └── random_walk.js │ ├── physics │ │ ├── position.js │ │ ├── velocity.js │ │ ├── acceleration.js │ │ ├── gravity.js │ │ ├── orbit.js │ │ └── spring.js │ ├── math │ │ ├── distance.js │ │ ├── rotate2d.js │ │ ├── manhattan_distance.js │ │ ├── dot_product.js │ │ ├── raycasting.js │ │ ├── sine_cosine.js │ │ ├── cross_product.js │ │ └── perspective.js │ ├── wave │ │ ├── wave_equation.js │ │ ├── sine_wave.js │ │ ├── square_wave.js │ │ ├── triangle_wave.js │ │ ├── saw_wave.js │ │ └── simple_harmonic.js │ ├── machines │ │ ├── crank.js │ │ ├── piston.js │ │ └── crank2.js │ ├── drawings │ │ ├── line.js │ │ ├── circle.js │ │ ├── line_segments.js │ │ ├── centroid.js │ │ ├── spinner.js │ │ ├── incenter.js │ │ ├── orthocenter.js │ │ ├── reuleaux.js │ │ ├── bezier.js │ │ ├── circumcenter.js │ │ ├── excenter.js │ │ └── bspline.js │ ├── easing │ │ ├── colliding.js │ │ ├── composite01.js │ │ ├── composite02.js │ │ ├── tweens.js │ │ ├── bouncing.js │ │ ├── tweens2.js │ │ └── sequence.js │ ├── collision │ │ ├── collision_line_circle.js │ │ ├── collision_circles.js │ │ ├── collision_rectangles.js │ │ └── collision_triangles.js │ ├── verlet │ │ ├── verlet_points.js │ │ ├── verlet_sticks.js │ │ ├── pendulum.js │ │ ├── ragdoll.js │ │ └── ragdoll3d.js │ ├── distance │ │ ├── circle.js │ │ ├── rectangle.js │ │ └── rotated_rectangle.js │ ├── light_path │ │ ├── reflection.js │ │ ├── parabola.js │ │ ├── refraction.js │ │ └── mirrors.js │ └── sound │ │ ├── equal_temperament.js │ │ └── just_intonation.js ├── .DS_Store └── main.js ├── .DS_Store ├── extra ├── .DS_Store └── wave_function │ ├── .DS_Store │ ├── screenshot.png │ ├── shaders │ ├── flat.vert │ ├── render.frag │ └── update.frag │ ├── index.html │ ├── package.json │ └── index.js ├── css ├── example.css └── style.css ├── README.md ├── vector2d.html ├── dev.html ├── .gitignore ├── machines.html ├── index.html ├── easing.html ├── physics.html ├── light_path.html ├── distance.html ├── verlet.html ├── pitch.html ├── random.html ├── collision.html ├── wave.html ├── handy_math.html └── drawings.html /js/examples/dev/reflection.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kynd/p5sketches/HEAD/.DS_Store -------------------------------------------------------------------------------- /js/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kynd/p5sketches/HEAD/js/.DS_Store -------------------------------------------------------------------------------- /extra/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kynd/p5sketches/HEAD/extra/.DS_Store -------------------------------------------------------------------------------- /css/example.css: -------------------------------------------------------------------------------- 1 | body { margin:0; padding: 0; background: #e4e4e4; overflow: none;} 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # p5sketches 2 | 3 | sketches in p5.js 4 | 5 | https://kynd.github.io/p5sketches/index.html 6 | -------------------------------------------------------------------------------- /extra/wave_function/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kynd/p5sketches/HEAD/extra/wave_function/.DS_Store -------------------------------------------------------------------------------- /extra/wave_function/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kynd/p5sketches/HEAD/extra/wave_function/screenshot.png -------------------------------------------------------------------------------- /extra/wave_function/shaders/flat.vert: -------------------------------------------------------------------------------- 1 | attribute vec2 position; 2 | varying vec2 uv; 3 | 4 | void main() { 5 | gl_Position = vec4(position,0.0,1.0); 6 | uv = 0.5 * (position+1.0); 7 | } 8 | -------------------------------------------------------------------------------- /vector2d.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 2D Vector 8 | 9 | 10 | 11 |

Redirect

12 | 13 | 14 | -------------------------------------------------------------------------------- /extra/wave_function/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Wave 7 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /extra/wave_function/shaders/render.frag: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | uniform sampler2D buffer; 3 | uniform int channelIndex; 4 | uniform vec2 dims; 5 | varying vec2 uv; 6 | 7 | void main() { 8 | vec4 samp0 = texture2D(buffer, uv + vec2(0.0, -1.0) / dims); 9 | vec4 samp1 = texture2D(buffer, uv + vec2(0.0, 1.0) / dims); 10 | 11 | vec4 color = vec4(0.88, 0.94, 0.95, 1.0); 12 | if (abs(uv.x - 0.5) < 0.3 && abs(uv.y - 0.5) < 0.02) { 13 | color = vec4(0.0, 0.0, 0.0, 1.0); 14 | } 15 | color.rgb -= smoothstep(0.026, 0.025, length(uv - vec2(0.5, 0.7))); 16 | 17 | float v = smoothstep(0.0, 0.1, samp0.r - samp1.r); 18 | color = vec4(color.rgb * (1.0 - v), 1.0); 19 | gl_FragColor = color; 20 | } 21 | -------------------------------------------------------------------------------- /extra/wave_function/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wave_function", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "browserify": { 7 | "transform": [ 8 | "glslify" 9 | ] 10 | }, 11 | "scripts": { 12 | "start": "budo -H 127.0.0.1 -p 9000 --title wave index.js:bundle.js --open --live", 13 | "bundle": "browserify index.js -o bundle.js" 14 | }, 15 | "devDependencies": { 16 | "browserify": "*", 17 | "budo": "*" 18 | }, 19 | "author": "", 20 | "license": "ISC", 21 | "dependencies": { 22 | "a-big-triangle": "^1.0.3", 23 | "gl-clear": "^2.0.0", 24 | "gl-fbo": "^2.0.5", 25 | "gl-now": "^1.4.0", 26 | "gl-shader": "^4.2.1", 27 | "glslify": "^6.1.0" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /js/examples/p5setup.js: -------------------------------------------------------------------------------- 1 | let __ready = false; 2 | let __clicked = false; 3 | 4 | 5 | function windowResized() { 6 | resizeCanvas(windowWidth, windowHeight); 7 | } 8 | 9 | function start() { 10 | if (__ready) { 11 | loop(); 12 | } 13 | } 14 | 15 | function stop() { 16 | if (__ready && !__clicked) { 17 | noLoop(); 18 | if (stopAudio) { 19 | stopAudio(); 20 | } 21 | } 22 | __clicked = false; 23 | } 24 | 25 | let __setup = setup; 26 | 27 | setup = function() { 28 | __setup(); 29 | 30 | if (!__autorun) { noLoop(); } 31 | canvas.mouseClicked(function() {__clicked = true; __parent.focusExample(window)}); 32 | canvas.touchStarted(function() {__clicked = true; __parent.focusExample(window)}); 33 | __ready = true; 34 | } 35 | -------------------------------------------------------------------------------- /js/examples/random/random_noise2.js: -------------------------------------------------------------------------------- 1 | let record = [], x = 0; 2 | let resolution = 150; 3 | 4 | function setup() { 5 | for (let i = 0; i < resolution * 10; i ++) { 6 | nextRandom(); 7 | } 8 | canvas = createCanvas(windowWidth, windowHeight); 9 | } 10 | 11 | function draw() { 12 | clear(); 13 | background(254, 253, 183); 14 | 15 | nextRandom(); 16 | 17 | fill(0);stroke(0); 18 | for (let i = 0; i < record.length; i ++) { 19 | let w = width / resolution; 20 | let h = height * record[i]; 21 | let x = w * i; 22 | let y = height - h; 23 | rect(x, y, w, h); 24 | } 25 | } 26 | 27 | function nextRandom() { 28 | let v = noise(x); x += 0.01; 29 | record.push(v); 30 | if (record.length > resolution) { 31 | record.shift(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /js/examples/physics/position.js: -------------------------------------------------------------------------------- 1 | function setup() { 2 | canvas = createCanvas(windowWidth, windowHeight); 3 | position = createVector(100, -100); 4 | } 5 | 6 | function draw() { 7 | clear(); 8 | const hw = width / 2, hh = height / 2; 9 | 10 | background(117, 147, 208); 11 | push(); 12 | translate(hw, hh); 13 | 14 | fill(0); stroke(0); 15 | strokeWeight(2); 16 | drawArrow(0, 0, position.x, position.y); 17 | drawLabel(position.x, position.y, `position (${position.x.toPrecision(4)}, ${position.y.toPrecision(4)})`, LEFT); 18 | 19 | strokeWeight(1) 20 | line(-hw, 0, hw, 0); 21 | line(0, -hh, 0, hh); 22 | drawLabel(0, -4, "Origin", LEFT); 23 | 24 | pop(); 25 | 26 | position = createVector(mouseX - hw, mouseY - hh); 27 | } 28 | 29 | /** drawArrow **/ 30 | 31 | /** drawLabel **/ 32 | -------------------------------------------------------------------------------- /dev.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Dev 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /extra/wave_function/shaders/update.frag: -------------------------------------------------------------------------------- 1 | 2 | precision mediump float; 3 | uniform float frameCount; 4 | uniform sampler2D buffer; 5 | uniform vec2 dims; 6 | varying vec2 uv; 7 | 8 | void main() { 9 | 10 | vec4 color; 11 | if (length(uv - vec2(0.5, 0.7)) < 0.025) { 12 | // Source 13 | color = vec4(0.5 + sin(frameCount * 0.5) * 0.2 * (sin(frameCount * 0.05) + 1.0), 0.5, 0.5, 1.0); 14 | } else if (abs(uv.x - 0.5) < 0.3 && abs(uv.y - 0.5) < 0.02) { 15 | // Obstacle 16 | color = vec4(0.5, 0.5, 0.5, 1.0); 17 | } else { 18 | vec4 samp = texture2D(buffer, uv); 19 | float va = samp.r; 20 | 21 | vec2 d[4]; 22 | d[0] = vec2(-1.0, -0.0); 23 | d[1] = vec2(0.0, -1.0); 24 | d[2] = vec2(1.0, 0.0); 25 | d[3] = vec2(-0.0, 1.0); 26 | 27 | float vb = 0.0; 28 | for (int i = 0; i < 4; i ++) { 29 | vb += texture2D(buffer, uv + d[i] / dims * 2.0).r; 30 | } 31 | 32 | float vc = samp.g; 33 | samp.b = va * 2.0 + 0.2 * (vb - va * 4.0) - vc; 34 | color = clamp(vec4(0.0), vec4(1.0), samp.brga); 35 | } 36 | 37 | gl_FragColor = color; 38 | } 39 | -------------------------------------------------------------------------------- /js/examples/math/distance.js: -------------------------------------------------------------------------------- 1 | function setup() { 2 | canvas = createCanvas(windowWidth, windowHeight); 3 | 4 | counter = 0; 5 | p = createVector(0, 0); 6 | q = createVector(0, 0); 7 | } 8 | 9 | function draw() { 10 | clear(); 11 | const cx = width / 2, cy = height / 2; 12 | const t = frameCount - 1; 13 | const a0 = radians(t), a1 = radians(t * 2), a2 = radians(t * 3), a3 = radians(t * 4); 14 | p.x = cos(a0) * 150; 15 | p.y = sin(a3) * 100; 16 | q.x = -cos(a1) * 150; 17 | q.y = -sin(a2) * 100; 18 | 19 | // see also p5.Vector.dist() 20 | // https://p5js.org/reference/#/p5.Vector/dist 21 | const distance = sqrt((p.x - q.x) * (p.x - q.x) + (p.y - q.y) * (p.y - q.y)); 22 | 23 | background(245, 177, 217); 24 | 25 | push(); 26 | translate(cx, cy); 27 | fill(0); stroke(0); 28 | drawLineWithLabel(p.x, p.y, q.x, q.y, "distance:" + distance.toPrecision(4), CENTER); 29 | drawSquareMarker(p, 10); 30 | drawTriangleMarker(q, 12); 31 | pop(); 32 | 33 | counter ++; 34 | } 35 | 36 | /** drawSquareMarker **/ 37 | 38 | /** drawTriangleMarker **/ 39 | 40 | /** drawLineWithLabel **/ 41 | 42 | /** drawLabel **/ -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | -------------------------------------------------------------------------------- /js/examples/random/random.js: -------------------------------------------------------------------------------- 1 | let record, max; 2 | let resolution = 150; 3 | 4 | function setup() { 5 | initRecord(); 6 | for (let i = 0; i < resolution * 10; i ++) { 7 | nextRandom(); 8 | } 9 | canvas = createCanvas(windowWidth, windowHeight); 10 | } 11 | 12 | function draw() { 13 | clear(); 14 | background(254, 253, 183); 15 | 16 | let v; 17 | for (let i = 0; i < resolution; i ++) { 18 | v = nextRandom(); 19 | line(v * width, 0, v * width, height); 20 | } 21 | 22 | fill(0);stroke(0); 23 | for (let i = 0; i < resolution; i ++) { 24 | let w = width / resolution; 25 | let h = height / max * record[i]; 26 | let x = w * i; 27 | let y = height - h; 28 | rect(x, y, w, h); 29 | } 30 | 31 | if (frameCount % 600 == 0) { 32 | initRecord(); 33 | } 34 | } 35 | 36 | function initRecord() { 37 | max = 1; 38 | if (!record) { 39 | record = []; 40 | } else { 41 | record.length = 0; 42 | } 43 | 44 | for (let i = 0; i < resolution; i ++) { 45 | record.push(0); 46 | } 47 | } 48 | 49 | function nextRandom() { 50 | let v = random(1); 51 | if (++record[floor(v * resolution)] > max) {max ++}; 52 | return v; 53 | } 54 | -------------------------------------------------------------------------------- /js/examples/random/random_squared.js: -------------------------------------------------------------------------------- 1 | let record, max; 2 | let resolution = 150; 3 | 4 | function setup() { 5 | initRecord(); 6 | for (let i = 0; i < resolution * 10; i ++) { 7 | nextRandom(); 8 | } 9 | canvas = createCanvas(windowWidth, windowHeight); 10 | } 11 | 12 | function draw() { 13 | clear(); 14 | background(254, 253, 183); 15 | 16 | let v; 17 | for (let i = 0; i < resolution; i ++) { 18 | v = nextRandom(); 19 | line(v * width, 0, v * width, height); 20 | } 21 | 22 | fill(0);stroke(0); 23 | for (let i = 0; i < resolution; i ++) { 24 | let w = width / resolution; 25 | let h = height / max * record[i]; 26 | let x = w * i; 27 | let y = height - h; 28 | rect(x, y, w, h); 29 | } 30 | 31 | if (frameCount % 600 == 0) { 32 | initRecord(); 33 | } 34 | } 35 | 36 | function initRecord() { 37 | max = 1; 38 | if (!record) { 39 | record = []; 40 | } else { 41 | record.length = 0; 42 | } 43 | 44 | for (let i = 0; i < resolution; i ++) { 45 | record.push(0); 46 | } 47 | } 48 | 49 | function nextRandom() { 50 | let v = pow(random(1), 2); 51 | if (++record[floor(v * resolution)] > max) {max ++}; 52 | return v; 53 | } 54 | -------------------------------------------------------------------------------- /js/examples/random/random_noise.js: -------------------------------------------------------------------------------- 1 | let record, max, x = 0; 2 | let resolution = 150; 3 | 4 | function setup() { 5 | initRecord(); 6 | for (let i = 0; i < resolution * 10; i ++) { 7 | nextRandom(); 8 | } 9 | canvas = createCanvas(windowWidth, windowHeight); 10 | } 11 | 12 | function draw() { 13 | clear(); 14 | background(254, 253, 183); 15 | 16 | let v; 17 | for (let i = 0; i < resolution; i ++) { 18 | v = nextRandom(); 19 | line(v * width, 0, v * width, height); 20 | } 21 | 22 | fill(0);stroke(0); 23 | for (let i = 0; i < resolution; i ++) { 24 | let w = width / resolution; 25 | let h = height / max * record[i]; 26 | let x = w * i; 27 | let y = height - h; 28 | rect(x, y, w, h); 29 | } 30 | 31 | if (frameCount % 600 == 0) { 32 | initRecord(); 33 | } 34 | } 35 | 36 | function initRecord() { 37 | max = 1; 38 | if (!record) { 39 | record = []; 40 | } else { 41 | record.length = 0; 42 | } 43 | 44 | for (let i = 0; i < resolution; i ++) { 45 | record.push(0); 46 | } 47 | } 48 | 49 | function nextRandom() { 50 | let v = noise(x); x += 0.001; 51 | if (++record[floor(v * resolution)] > max) {max ++}; 52 | return v; 53 | } 54 | -------------------------------------------------------------------------------- /js/examples/random/random_average.js: -------------------------------------------------------------------------------- 1 | let record, max; 2 | let resolution = 150; 3 | 4 | function setup() { 5 | for (let i = 0; i < resolution * 10; i ++) { 6 | initRecord(); 7 | nextRandom(); 8 | } 9 | canvas = createCanvas(windowWidth, windowHeight); 10 | } 11 | 12 | function draw() { 13 | clear(); 14 | background(254, 253, 183); 15 | 16 | let v; 17 | for (let i = 0; i < resolution; i ++) { 18 | v = nextRandom(); 19 | line(v * width, 0, v * width, height); 20 | } 21 | 22 | fill(0);stroke(0); 23 | for (let i = 0; i < resolution; i ++) { 24 | let w = width / resolution; 25 | let h = height / max * record[i]; 26 | let x = w * i; 27 | let y = height - h; 28 | rect(x, y, w, h); 29 | } 30 | 31 | if (frameCount % 600 == 0) { 32 | initRecord(); 33 | } 34 | } 35 | 36 | function initRecord() { 37 | max = 1; 38 | if (!record) { 39 | record = []; 40 | } else { 41 | record.length = 0; 42 | } 43 | 44 | for (let i = 0; i < resolution; i ++) { 45 | record.push(0); 46 | } 47 | } 48 | 49 | function nextRandom() { 50 | let v = (random(1) + random(1)) / 2; 51 | if (++record[floor(v * resolution)] > max) {max ++}; 52 | return v; 53 | } 54 | -------------------------------------------------------------------------------- /js/examples/wave/wave_equation.js: -------------------------------------------------------------------------------- 1 | let u = []; 2 | 3 | function setup() { 4 | canvas = createCanvas(windowWidth, windowHeight); 5 | initValues(); 6 | } 7 | 8 | function draw() { 9 | clear(); 10 | background(224, 239, 243); 11 | 12 | let f = frameCount; 13 | let iN = (f + 1) % 3; // Next (t + 1) 14 | let iC = f % 3; // Current (t) 15 | let iP = (f + 2) % 3; // Previous (t - 1) 16 | let span = width / (u[0].length - 1); 17 | 18 | u[iC][0] += signedNoise(frameCount * 0.1) * 0.5; 19 | 20 | updateSimulation(iN, iC, iP) 21 | 22 | noFill(); stroke(1); strokeWeight(3); 23 | beginShape(); 24 | for (let i = 0; i < u[0].length; i ++) { 25 | vertex(span * i, height / 2 + u[iN][i]); 26 | } 27 | endShape(); 28 | } 29 | 30 | function updateSimulation(iN, iC, iP) { 31 | for (let i = 0; i < u[0].length; i ++) { 32 | let dhdx = 0; 33 | if (i != 0 && i != u[0].length - 1) { 34 | dhdx = u[iC][i - 1] + u[iC][i + 1] - 2 * u[iC][i] 35 | } 36 | let c2 = 0.1; 37 | let dampen = 0.995; 38 | u[iN][i] = 2 * u[iC][i] + c2 * dhdx - u[iP][i]; 39 | u[iN][i] *= dampen; 40 | } 41 | } 42 | 43 | function initValues() { 44 | u = []; 45 | for (let i = 0; i < 3; i ++) { 46 | u.push(new Array(floor(width / 8) + 1).fill(0)); 47 | } 48 | } 49 | 50 | /** signedNoise **/ 51 | -------------------------------------------------------------------------------- /js/examples/wave/sine_wave.js: -------------------------------------------------------------------------------- 1 | let prevInitFrame = 1; 2 | let radius = 100; 3 | let plotData = []; 4 | 5 | function setup() { 6 | canvas = createCanvas(windowWidth, windowHeight); 7 | } 8 | 9 | function draw() { 10 | clear(); 11 | background(224, 239, 243); 12 | 13 | let rad = radians(frameCount - 1) * 2; 14 | let sine = sin(rad); 15 | let cosine = cos(rad); 16 | let x = cosine * radius; 17 | let y = -sine * radius; 18 | 19 | push(); 20 | translate(radius + 20, height / 2); 21 | fill(255); strokeWeight(1); 22 | ellipse(0, 0, radius * 2, radius * 2); 23 | line(-radius, 0, radius, 0); 24 | line(radius + 20, y, -radius - 20, y); 25 | strokeWeight(3); 26 | line(0,0,x, y); 27 | line(x, 0, x, y); 28 | line(0,0,x, 0); 29 | strokeWeight(1); 30 | drawCircleMarker(createVector(x, y), 4); 31 | pop(); 32 | 33 | let graphW = width - (radius + 20) * 2; 34 | let graphX = (radius + 20) * 2; 35 | 36 | if (frameCount > prevInitFrame + graphW) { 37 | plotData = []; 38 | prevInitFrame += graphW; 39 | } 40 | plotData.push({x:frameCount - prevInitFrame, y: -y}); 41 | 42 | plotGraph(plotData, graphX, height / 2, graphW, height - 64, 0, graphW, -height / 2 + 32, height / 2 - 32, "time", "y"); 43 | } 44 | 45 | /** plotGraph **/ 46 | 47 | /** drawLabel **/ 48 | 49 | /** drawCircleMarker **/ 50 | -------------------------------------------------------------------------------- /js/examples/math/rotate2d.js: -------------------------------------------------------------------------------- 1 | function setup() { 2 | canvas = createCanvas(windowWidth, windowHeight); 3 | 4 | degA = degB = degC = 0; 5 | pA = createVector(0, -80); 6 | pB = createVector(100, 0); 7 | pC = createVector(-100, 100); 8 | } 9 | 10 | function draw() { 11 | clear(); 12 | let cx = width / 2, cy = height / 2; 13 | 14 | let radA = radians(degA); // PI * deg / 180; 15 | let radB = radians(degB); // PI * deg / 180; 16 | let radC = radians(degC); // PI * deg / 180; 17 | let npA = rotate2d(pA, radA); 18 | let npB = rotate2d(pB, radB); 19 | let npC = rotate2d(pC, radC); 20 | 21 | background(15, 157, 88); 22 | 23 | push(); 24 | translate(cx, cy); 25 | fill(0); stroke(0); 26 | line(0, 0, pA.x, pA.y); 27 | line(0, 0, pB.x, pB.y); 28 | line(0, 0, pC.x, pC.y); 29 | drawTriangleMarker(pA, 12); 30 | drawCircleMarker(pB, 12); 31 | drawSquareMarker(pC, 10); 32 | 33 | fill(255); stroke(255); 34 | ellipse(0, 0, 8, 8); 35 | line(0, 0, npA.x, npA.y) 36 | line(0, 0, npB.x, npB.y) 37 | line(0, 0, npC.x, npC.y) 38 | drawTriangleMarker(npA, 12); 39 | drawCircleMarker(npB, 12); 40 | drawSquareMarker(npC, 10); 41 | 42 | pop(); 43 | 44 | degA += 1; 45 | degB += 2; 46 | degC += 4; 47 | } 48 | 49 | /** rotate2d **/ 50 | 51 | /** drawCircleMarker **/ 52 | 53 | /** drawSquareMarker **/ 54 | 55 | /** drawTriangleMarker **/ 56 | -------------------------------------------------------------------------------- /js/examples/random/random_norm.js: -------------------------------------------------------------------------------- 1 | let record, max; 2 | let resolution = 150; 3 | 4 | function setup() { 5 | initRecord(); 6 | for (let i = 0; i < resolution * 10; i ++) { 7 | nextRandom(); 8 | } 9 | canvas = createCanvas(windowWidth, windowHeight); 10 | } 11 | 12 | function draw() { 13 | clear(); 14 | background(254, 253, 183); 15 | 16 | let v; 17 | for (let i = 0; i < resolution; i ++) { 18 | v = nextRandom(); 19 | line(v * width, 0, v * width, height); 20 | } 21 | 22 | fill(0);stroke(0); 23 | for (let i = 0; i < resolution; i ++) { 24 | let w = width / resolution; 25 | let h = height / max * record[i]; 26 | let x = w * i; 27 | let y = height - h; 28 | rect(x, y, w, h); 29 | } 30 | 31 | if (frameCount % 600 == 0) { 32 | initRecord(); 33 | } 34 | } 35 | 36 | function initRecord() { 37 | max = 1; 38 | if (!record) { 39 | record = []; 40 | } else { 41 | record.length = 0; 42 | } 43 | 44 | for (let i = 0; i < resolution; i ++) { 45 | record.push(0); 46 | } 47 | } 48 | 49 | function nextRandom() { 50 | // Box-Muller Transform 51 | // avoiding log(0) by using (1 - Math.random()) 52 | let mean = 0.5, variance = 0.1; 53 | let v = sqrt(-2 * log(1 - random(1))) * cos(2 * PI * random(1)) * variance + mean; 54 | if (++record[floor(v * resolution)] > max) {max ++}; 55 | return v; 56 | } 57 | -------------------------------------------------------------------------------- /js/examples/machines/crank.js: -------------------------------------------------------------------------------- 1 | let log = []; 2 | let center, pivot, radius, armLength; 3 | 4 | function setup() { 5 | canvas = createCanvas(windowWidth, windowHeight); 6 | center = createVector(width * 0.25, height * 0.5); 7 | pivot = createVector(width * 0.5, height * 0.5); 8 | armLength = width * 0.5; 9 | radius = min(height * 0.2, center.dist(pivot) * 0.5); 10 | } 11 | 12 | function draw() { 13 | clear(); 14 | background(69, 157, 193); 15 | 16 | let a = radians((frameCount - 1) * 2) + PI * 0.5; 17 | let p = createVector(cos(a) * radius, sin(a) * radius).add(center); 18 | let arm = new LineSegment().fromTwoPointsAndLength(p, pivot, armLength); 19 | if (frameCount % 2 == 0) { log.push(arm.p1) }; 20 | if (log.length > 90) { log.shift(); } 21 | 22 | fill(253, 219, 192); stroke(0); 23 | ellipse(center.x, center.y, radius * 2, radius * 2); 24 | 25 | noFill();stroke(0); 26 | line(center.x, center.y, p.x, p.y); 27 | 28 | strokeWeight(3); 29 | beginShape(); 30 | for (let i = 0; i < log.length; i ++) { 31 | vertex(log[i].x, log[i].y); 32 | } 33 | endShape(); 34 | arm.draw(); 35 | 36 | fill(255); strokeWeight(1); 37 | drawCircleMarker(center, 4); 38 | drawCircleMarker(p, 4); 39 | drawCircleMarker(arm.p1, 4); 40 | drawCircleMarker(pivot, 4); 41 | } 42 | 43 | /** LineSegment **/ 44 | 45 | /** drawLabel **/ 46 | 47 | /** drawCircleMarker **/ 48 | -------------------------------------------------------------------------------- /js/examples/physics/velocity.js: -------------------------------------------------------------------------------- 1 | function setup() { 2 | canvas = createCanvas(windowWidth, windowHeight); 3 | 4 | counter = 0; 5 | position = createVector(100, -100); 6 | velocity = createVector(48, -48); 7 | fps = 60; // naively assuming 60 fps 8 | } 9 | 10 | function draw() { 11 | clear(); 12 | const hw = width / 2, hh = height / 2; 13 | const vx = position.x + velocity.x; 14 | const vy = position.y + velocity.y; 15 | 16 | background(117, 147, 208); 17 | push(); 18 | translate(hw, hh); 19 | 20 | fill(0); stroke(0); 21 | strokeWeight(2); 22 | drawArrow(position.x, position.y, vx, vy); 23 | drawCircleMarker(position, 4); 24 | 25 | drawLabel(position.x, position.y, `position (${position.x.toPrecision(4)}, ${position.y.toPrecision(4)})`, LEFT); 26 | drawLabel(vx, vy, `velocity (${velocity.x.toPrecision(4)}, ${velocity.y.toPrecision(4)})`, LEFT); 27 | 28 | strokeWeight(1) 29 | line(-hw, 0, hw, 0); 30 | line(0, -hh, 0, hh); 31 | drawLabel(0, -4, "Origin", LEFT); 32 | 33 | pop(); 34 | 35 | const pDelta = velocity.copy().div(fps); 36 | position.add(pDelta); 37 | if (position.x > hw || position.x < -hw) {velocity.x = -velocity.x;} 38 | if (position.y > hh || position.y < -hh) {velocity.y = -velocity.y;} 39 | } 40 | 41 | 42 | /** drawCircleMarker **/ 43 | 44 | /** drawArrow **/ 45 | 46 | /** drawLabel **/ 47 | -------------------------------------------------------------------------------- /js/examples/drawings/line.js: -------------------------------------------------------------------------------- 1 | let lines = []; 2 | 3 | function setup() { 4 | canvas = createCanvas(windowWidth, windowHeight); 5 | 6 | lines.push(new Line(0, 1, 0)); // X-axis: a = 0, b != 0, c = 0 7 | lines.push(new Line(1, 0, 0)); // Y-axis: a != 0, b = 0, c = 0 8 | lines.push(new Line()); 9 | lines.push(new Line()); 10 | lines.push(new Line()); 11 | } 12 | 13 | function draw() { 14 | clear(); 15 | background(255, 235, 59); 16 | let boundary = {x: -width / 2, y: -height / 2, w: width, h: height}; 17 | 18 | for (let i = 2; i < lines.length; i ++) { 19 | let f = frameCount; 20 | let t0 = i + f * 0.00123, t1 = i + f * 0.00234, t2 = i + f * 0.00345, t3 = i + f * 0.00456; 21 | lines[i].fromTwoPoints({ x:signedNoise(t0) * 640, y: signedNoise(t1) * 640 }, { x:signedNoise(t2) * 640, y: signedNoise(t3) * 640 }); 22 | } 23 | 24 | translate(width / 2, height / 2); 25 | 26 | fill(255); stroke(0); 27 | for (let i = 0; i < lines.length; i ++) { 28 | lines[i].draw(boundary); 29 | } 30 | 31 | for (let i = 0; i < lines.length; i ++) { 32 | for (let j = i + 1; j < lines.length; j ++) { 33 | let p = lines[i].getIntersectionPoint(lines[j]); 34 | if (p) { 35 | drawCircleMarker(p, 3); 36 | } 37 | } 38 | } 39 | } 40 | 41 | /** signedNoise **/ 42 | 43 | /** Line **/ 44 | 45 | /** LineSegment **/ 46 | 47 | /** drawCircleMarker **/ 48 | -------------------------------------------------------------------------------- /js/examples/math/manhattan_distance.js: -------------------------------------------------------------------------------- 1 | function setup() { 2 | canvas = createCanvas(windowWidth, windowHeight); 3 | 4 | counter = 0; 5 | p = createVector(0, 0); 6 | q = createVector(0, 0); 7 | r = createVector(0, 0); 8 | } 9 | 10 | function draw() { 11 | clear(); 12 | const cx = width / 2, cy = height / 2; 13 | const t = frameCount + 30; 14 | const a0 = radians(t), a1 = radians(t * 2), a2 = radians(t * 3), a3 = radians(t * 4); 15 | p.x = cos(a0) * 150; 16 | p.y = sin(a3) * 100; 17 | q.x = -cos(a1) * 150; 18 | q.y = -sin(a2) * 100; 19 | r.x = p.x; 20 | r.y = q.y; 21 | 22 | const dx = Math.abs(p.x - q.x); 23 | const dy = Math.abs(p.y - q.y); 24 | const distance = dx + dy; 25 | 26 | background(245, 177, 217); 27 | 28 | push(); 29 | translate(cx, cy); 30 | fill(0); stroke(0); 31 | drawLineWithLabel(p.x, p.y, r.x, r.y, "dx: " + dx.toPrecision(4), LEFT); 32 | drawLineWithLabel(q.x, q.y, r.x, r.y, "dy: " + dy.toPrecision(4), CENTER, 0, -4); 33 | drawLabel(0, cy - 16, "distance = dx + dy = " + distance.toPrecision(4), CENTER); 34 | drawSquareMarker(p, 10); 35 | drawTriangleMarker(q, 12); 36 | drawCircleMarker(r, 4); 37 | pop(); 38 | 39 | counter ++; 40 | } 41 | 42 | /** drawCircleMarker **/ 43 | 44 | /** drawSquareMarker **/ 45 | 46 | /** drawTriangleMarker **/ 47 | 48 | /** drawLineWithLabel **/ 49 | 50 | /** drawLabel **/ -------------------------------------------------------------------------------- /js/examples/random/random_power.js: -------------------------------------------------------------------------------- 1 | let n = 0.2; 2 | let record, max; 3 | let resolution = 150; 4 | 5 | function setup() { 6 | initRecord(); 7 | for (let i = 0; i < resolution * 10; i ++) { 8 | nextRandom(); 9 | } 10 | canvas = createCanvas(windowWidth, windowHeight); 11 | } 12 | 13 | function draw() { 14 | clear(); 15 | background(254, 253, 183); 16 | 17 | let v; 18 | for (let i = 0; i < resolution; i ++) { 19 | v = nextRandom(); 20 | line(v * width, 0, v * width, height); 21 | } 22 | 23 | fill(0);stroke(0); 24 | for (let i = 0; i < resolution; i ++) { 25 | let w = width / resolution; 26 | let h = height / max * record[i]; 27 | let x = w * i; 28 | let y = height - h; 29 | rect(x, y, w, h); 30 | } 31 | 32 | fill(254, 253, 183); 33 | drawLabel((n < 1) ? width -16 : 16, height - 16, "n = " + n.toPrecision(2), (n < 1) ? RIGHT : LEFT); 34 | 35 | n = (floor(frameCount / 60) % 10 + 1) * 0.2; 36 | if (frameCount % 60 == 0) { 37 | initRecord(); 38 | } 39 | } 40 | 41 | function initRecord() { 42 | max = 1; 43 | if (!record) { 44 | record = []; 45 | } else { 46 | record.length = 0; 47 | } 48 | 49 | for (let i = 0; i < resolution; i ++) { 50 | record.push(0); 51 | } 52 | } 53 | 54 | function nextRandom() { 55 | let v = pow(random(1), n); 56 | if (++record[floor(v * resolution)] > max) {max ++}; 57 | return v; 58 | } 59 | 60 | /** drawLabel **/ 61 | -------------------------------------------------------------------------------- /js/examples/random/random_average2.js: -------------------------------------------------------------------------------- 1 | let n = 10; 2 | let record, max; 3 | let resolution = 150; 4 | 5 | function setup() { 6 | initRecord(); 7 | for (let i = 0; i < resolution * 10; i ++) { 8 | nextRandom(); 9 | } 10 | canvas = createCanvas(windowWidth, windowHeight); 11 | } 12 | 13 | function draw() { 14 | clear(); 15 | background(254, 253, 183); 16 | 17 | let v; 18 | for (let i = 0; i < resolution; i ++) { 19 | v = nextRandom(); 20 | line(v * width, 0, v * width, height); 21 | } 22 | 23 | fill(0);stroke(0); 24 | for (let i = 0; i < resolution; i ++) { 25 | let w = width / resolution; 26 | let h = height / max * record[i]; 27 | let x = w * i; 28 | let y = height - h; 29 | rect(x, y, w, h); 30 | } 31 | 32 | fill(254, 253, 183); 33 | drawLabel(width / 2, height - 16, "n = " + n, CENTER); 34 | 35 | n = floor(frameCount / 60) % 9 + 2; 36 | if (frameCount % 60 == 0) { 37 | initRecord(); 38 | } 39 | } 40 | 41 | function initRecord() { 42 | max = 1; 43 | if (!record) { 44 | record = []; 45 | } else { 46 | record.length = 0; 47 | } 48 | 49 | for (let i = 0; i < resolution; i ++) { 50 | record.push(0); 51 | } 52 | } 53 | 54 | function nextRandom() { 55 | let v = 0; 56 | for (let i = 0; i < n; i ++) { 57 | v += random(1); 58 | } 59 | v /= n; 60 | if (++record[floor(v * resolution)] > max) {max ++}; 61 | return v; 62 | } 63 | 64 | /** drawLabel **/ 65 | -------------------------------------------------------------------------------- /js/examples/easing/colliding.js: -------------------------------------------------------------------------------- 1 | let msec = 0, prevNow = 0; 2 | let tween, tweens; 3 | 4 | function setup() { 5 | canvas = createCanvas(windowWidth, windowHeight); 6 | tween = new Tween(); 7 | tweens = [ 8 | (t)=>{return tween.powerInOut(t, 2)}, 9 | (t)=>{return tween.powerOut(t, 2)}, 10 | (t)=>{return tween.powerOut(t, 3)} 11 | ] 12 | } 13 | 14 | function draw() { 15 | clear(); 16 | background(73, 5, 255); 17 | 18 | let duration = 500, baseSize = 72; 19 | let t = (msec % (duration)) / duration; 20 | let i = Math.floor(msec / duration); 21 | let phase = i % 4; 22 | let reverse = i % 2; 23 | 24 | 25 | fill(255); stroke(0); 26 | push(); 27 | translate(width / 2, height / 2); 28 | for (let i = 0; i < 3; i ++) { 29 | let xr0 = 0, xr1 = 0, y = (i - 1) * baseSize * 1.3; 30 | let d = width * 0.4; 31 | switch(phase) { 32 | case 0: 33 | xr0 = tweens[i](t); 34 | break; 35 | case 1: 36 | xr0 = tweens[i](1 - t); 37 | break; 38 | case 2: 39 | xr1 = tweens[i](t); 40 | break; 41 | case 3: 42 | xr1 = tweens[i](1 - t); 43 | break; 44 | } 45 | 46 | ellipse(d * -xr0 - baseSize / 2, y, baseSize); 47 | ellipse(d * xr1 + baseSize / 2, y, baseSize); 48 | } 49 | pop(); 50 | 51 | ellapseTime(); 52 | } 53 | 54 | function ellapseTime() { 55 | msec += min(100, window.performance.now() - prevNow); 56 | prevNow = window.performance.now(); 57 | } 58 | 59 | /** Tween **/ 60 | -------------------------------------------------------------------------------- /js/examples/drawings/circle.js: -------------------------------------------------------------------------------- 1 | 2 | let lines = []; 3 | 4 | function setup() { 5 | canvas = createCanvas(windowWidth, windowHeight); 6 | lines.push(new Line(0, 1, 0)); // X-axis: a = 0, b != 0, c = 0 7 | lines.push(new Line(1, 0, 0)); // Y-axis: a != 0, b = 0, c = 0 8 | } 9 | 10 | function draw() { 11 | clear(); 12 | background(255, 235, 59); 13 | 14 | let boundary = {x: -width / 2, y: -height / 2, w: width, h: height} 15 | 16 | let points = []; 17 | for (let i = 0; i < 3; i ++) { 18 | let f = frameCount + 100; 19 | let t0 = i + f * 0.00123, t1 = i + f * 0.00234; 20 | points.push(createVector(signedNoise(t0) * 320,signedNoise(t1) * 320)); 21 | } 22 | let circle = new Circle().fromThreePoints(points[0], points[1], points[2]); 23 | 24 | translate(width / 2, height / 2); 25 | 26 | noFill(); stroke(0); 27 | for (let i = 0; i < lines.length; i ++) { 28 | lines[i].draw(boundary); 29 | } 30 | 31 | circle.draw(); 32 | 33 | fill(255); 34 | for (let i = 0; i < points.length; i ++) { 35 | drawCircleMarker(points[i], 3); 36 | } 37 | drawCircleMarker(circle.center, 3); 38 | 39 | 40 | fill(0); noStroke(); 41 | for (let i = 0; i < lines.length; i ++) { 42 | let intersections = circle.getIntersectionPoints(lines[i]); 43 | for (let j = 0; j < intersections.length; j++) { 44 | drawCircleMarker(intersections[j], 3); 45 | } 46 | } 47 | } 48 | 49 | /** signedNoise **/ 50 | 51 | /** Line **/ 52 | 53 | /** Circle **/ 54 | 55 | /** drawCircleMarker **/ 56 | -------------------------------------------------------------------------------- /js/examples/collision/collision_line_circle.js: -------------------------------------------------------------------------------- 1 | let A, B, P; 2 | let radius = 60; 3 | 4 | function setup() { 5 | canvas = createCanvas(windowWidth, windowHeight); 6 | A = createVector(); 7 | B = createVector(); 8 | P = createVector(); 9 | } 10 | 11 | function draw() { 12 | clear(); 13 | background(249, 246, 236); 14 | let t = radians(frameCount); 15 | P.x = cos(t) * 120; 16 | P.y = sin(t * 1.5) * 120; 17 | A.x = 60; 18 | A.y = -height / 2; 19 | B.x = -60; 20 | B.y = height / 2; 21 | 22 | // test 23 | let ap = P.copy().sub(A); 24 | let ab = B.copy().sub(A); 25 | let d = (ap.x * ab.y - ab.x * ap.y) / ab.mag(); 26 | 27 | let result = abs(d) <= radius; 28 | 29 | // rendering 30 | 31 | push(); 32 | translate(width / 2, height / 2); 33 | 34 | noStroke(); 35 | if (result) { 36 | fill(225, 81, 106); 37 | } else { 38 | fill(255); 39 | } 40 | ellipse(P.x, P.y, radius * 2); 41 | 42 | noFill(); stroke(0); strokeWeight(1); 43 | ellipse(P.x, P.y, radius * 2); 44 | line(A.x, A.y, B.x, B.y); 45 | 46 | let l = new Line().fromTwoPoints(A, B); 47 | let q = l.getNearestPoint(P); 48 | if (result) { 49 | strokeWeight(3); 50 | stroke(0, 0, 0, 255); 51 | } 52 | line(q.x, q.y, P.x, P.y); 53 | 54 | stroke(0, 0, 0, 32); strokeWeight(1); 55 | 56 | line(A.x, A.y, P.x, P.y); 57 | 58 | fill(0); 59 | drawLabel(P.x + 8, P.y, "P", "RIGHT"); 60 | drawLabel(A.x + 8, A.y + 16, "A", "RIGHT"); 61 | drawLabel(B.x + 8, B.y - 8, "B", "RIGHT"); 62 | 63 | pop(); 64 | } 65 | 66 | /** Line **/ 67 | 68 | /** drawLabel **/ 69 | -------------------------------------------------------------------------------- /js/examples/verlet/verlet_points.js: -------------------------------------------------------------------------------- 1 | 2 | let points = []; 3 | let accelIndex, accel; 4 | 5 | function setup() { 6 | canvas = createCanvas(windowWidth, windowHeight); 7 | for (let i = 0; i < 10; i ++) { 8 | points.push(new VerletPoint( createVector(random(width), random(height))) ); 9 | points[i].position.add(random2D().mult(random(2) + 1)); 10 | } 11 | } 12 | 13 | function draw() { 14 | clear(); 15 | background(100, 150, 180); 16 | points.forEach((p)=>{ 17 | p.update(); 18 | if (p.position.x < 0) { p.position.x += 2; } 19 | if (p.position.x > width) { p.position.x -= 2; } 20 | if (p.position.y < 0) { p.position.y += 2; } 21 | if (p.position.y > height) { p.position.y -= 2; } 22 | if (p.getVelocity().mag() > 3) { 23 | p.setVelocity(p.getVelocity().normalize().mult(3)); // limit the speed 24 | } 25 | }); 26 | 27 | let f = (frameCount - 1) % 60; 28 | if (f == 0) { 29 | accelIndex = floor(random(points.length)); 30 | accel = random2D().mult((random(0.5) + 0.5)); 31 | } 32 | 33 | 34 | fill(255); stroke(0); strokeWeight(3); 35 | if (f < 30) { 36 | points[accelIndex].position.add(accel); 37 | let p0 = points[accelIndex].position; 38 | let p1 = points[accelIndex].position.copy().add(accel.copy().mult(100)); 39 | drawArrow(p0.x, p0.y, p1.x, p1.y); 40 | } 41 | strokeWeight(1); 42 | points.forEach((p)=>{p.draw();}); 43 | } 44 | 45 | function random2D() { 46 | return createVector(random(-1, 1), random(-1, 1)).normalize(); 47 | } 48 | 49 | /** random2D **/ 50 | 51 | /** drawArrow **/ 52 | 53 | /** VerletPoint **/ 54 | -------------------------------------------------------------------------------- /js/examples/drawings/line_segments.js: -------------------------------------------------------------------------------- 1 | 2 | let lines = []; 3 | 4 | function setup() { 5 | canvas = createCanvas(windowWidth, windowHeight); 6 | 7 | lines.push(new Line(0, 1, 0)); // X-axis: a = 0, b != 0, c = 0 8 | lines.push(new Line(1, 0, 0)); // Y-axis: a != 0, b = 0, c = 0 9 | lines.push(new LineSegment()); 10 | lines.push(new LineSegment()); 11 | lines.push(new LineSegment()); 12 | } 13 | 14 | function draw() { 15 | clear(); 16 | background(255, 235, 59); 17 | 18 | let boundary = {x: -width / 2, y: -height / 2, w: width, h: height} 19 | 20 | for (let i = 2; i < lines.length; i ++) { 21 | let f = frameCount + 100; 22 | let t0 = i + f * 0.00123, t1 = i + f * 0.00234, t2 = i + f * 0.00345, t3 = i + f * 0.00456; 23 | lines[i].fromTwoPoints(createVector(signedNoise(t0) * 320,signedNoise(t1) * 320), createVector(signedNoise(t2) * 320, signedNoise(t3) * 320)); 24 | } 25 | 26 | translate(width / 2, height / 2); 27 | 28 | fill(255); stroke(0); 29 | for (let i = 0; i < lines.length; i ++) { 30 | lines[i].draw(boundary); 31 | } 32 | 33 | for (let i = 0; i < lines.length; i ++) { 34 | if (lines[i] instanceof LineSegment) { 35 | drawCircleMarker(lines[i].p0, 3); 36 | drawCircleMarker(lines[i].p1, 3); 37 | } 38 | for (let j = i + 1; j < lines.length; j ++) { 39 | let p = lines[i].getIntersectionPoint(lines[j]); 40 | if (p) { 41 | drawCircleMarker(p, 3); 42 | } 43 | } 44 | } 45 | } 46 | 47 | /** signedNoise **/ 48 | 49 | /** Line **/ 50 | 51 | /** LineSegment **/ 52 | 53 | /** drawCircleMarker **/ 54 | -------------------------------------------------------------------------------- /js/examples/math/dot_product.js: -------------------------------------------------------------------------------- 1 | function setup() { 2 | canvas = createCanvas(windowWidth, windowHeight); 3 | 4 | deg = 240; 5 | } 6 | 7 | function draw() { 8 | clear(); 9 | let m = 60; // magnifier 10 | let v1 = createVector(1, -1).normalize(); 11 | let rad = radians(deg); 12 | let v2 = createVector(cos(rad), sin(rad)).mult(3); 13 | let dotProduct = v2.dot(v1.copy().normalize()); 14 | let p = v1.copy().normalize().mult(dotProduct); 15 | 16 | background(15, 157, 88); 17 | 18 | push(); 19 | translate(width / 2, height / 2); 20 | 21 | fill(255); stroke(0); strokeWeight(1); 22 | let hh = height / 2; 23 | //line(-hh, -hh, hh, hh); 24 | line(-hh, hh, hh, -hh); 25 | line(v2.x * m, v2.y * m, p.x * m, p.y * m); 26 | 27 | fill(0); stroke(0); strokeWeight(3); 28 | drawArrow(0, 0, v1.x * m, v1.y * m); 29 | drawArrow(0, 0, v2.x * m, v2.y * m); 30 | 31 | strokeWeight(1); 32 | line(0, 0, 72, 72); 33 | line(p.x * m, p.y * m, p.x * m + 72, p.y * m + 72); 34 | drawLineWithLabel(64, 64, p.x * m + 64, p.y * m + 64, " dot product = " + dotProduct.toPrecision(3), LEFT); 35 | 36 | fill(255); stroke(0); strokeWeight(1); 37 | drawCircleMarker(p.copy().mult(m), 4); 38 | drawCircleMarker(v2.copy().mult(m), 4); 39 | drawCircleMarker(createVector(0, 0), 4); 40 | 41 | fill(0); noStroke() 42 | drawLabel(v1.x * m + 4, v1.y * m, "v1(magnitude = 1)", LEFT); 43 | drawLabel(v2.x * m + 4, v2.y * m, "v2(magnitude = 3)", LEFT); 44 | deg ++; 45 | } 46 | 47 | /** drawLineWithLabel **/ 48 | 49 | /** drawLabel **/ 50 | 51 | /** drawArrow **/ 52 | 53 | /** drawCircleMarker **/ 54 | -------------------------------------------------------------------------------- /js/examples/math/raycasting.js: -------------------------------------------------------------------------------- 1 | function setup() { 2 | canvas = createCanvas(windowWidth, windowHeight); 3 | deg = 45; 4 | } 5 | 6 | function draw() { 7 | clear(); 8 | 9 | let hypotenuse = 100; 10 | let rad = radians(deg); // PI * deg / 180; 11 | 12 | let sine = sin(rad); 13 | let cosine = cos(rad); 14 | let opposite = sine * hypotenuse; 15 | let adjacent = cosine * hypotenuse; 16 | let tangent = sine / cosine; 17 | 18 | background(135, 108, 209); 19 | 20 | push(); 21 | translate(width / 2, height / 2); 22 | fill(255); 23 | stroke(0); strokeWeight(1); 24 | ellipse(0, 0, hypotenuse * 2, hypotenuse * 2); 25 | line(-hypotenuse, 0, hypotenuse, 0); 26 | 27 | fill(0); stroke(0); strokeWeight(3); 28 | line(0, 0, adjacent, 0); 29 | drawLineWithLabel(0, 0, adjacent, -opposite, "hypotenuse:" + hypotenuse.toPrecision(3), (adjacent > 0) ? RIGHT : LEFT); 30 | drawLineWithLabel(adjacent, 0, adjacent, -opposite, "opposite:" + opposite.toPrecision(3), (adjacent > 0) ? LEFT : RIGHT); 31 | drawLabel(adjacent / 2, 16, "adjacent:" + adjacent.toPrecision(3), LEFT); 32 | pop(); 33 | 34 | drawLabel(16, windowHeight - 120, "angle(degrees):" + deg.toPrecision(3), LEFT); 35 | drawLabel(16, windowHeight - 96, "angle(radians):" + rad.toPrecision(3), LEFT); 36 | drawLabel(16, windowHeight - 72, "sine:" + sine.toPrecision(3), LEFT); 37 | drawLabel(16, windowHeight - 48, "cosine:" + cosine.toPrecision(3), LEFT); 38 | drawLabel(16, windowHeight - 24, "tangent:" + tangent.toPrecision(3), LEFT); 39 | 40 | deg ++; 41 | } 42 | 43 | /** drawLineWithLabel **/ 44 | 45 | /** drawLabel **/ 46 | -------------------------------------------------------------------------------- /js/examples/math/sine_cosine.js: -------------------------------------------------------------------------------- 1 | function setup() { 2 | canvas = createCanvas(windowWidth, windowHeight); 3 | deg = 45; 4 | } 5 | 6 | function draw() { 7 | clear(); 8 | 9 | let hypotenuse = 100; 10 | let rad = radians(deg); // PI * deg / 180; 11 | 12 | let sine = sin(rad); 13 | let cosine = cos(rad); 14 | let opposite = sine * hypotenuse; 15 | let adjacent = cosine * hypotenuse; 16 | let tangent = sine / cosine; 17 | 18 | background(135, 108, 209); 19 | 20 | push(); 21 | translate(width / 2, height / 2); 22 | fill(255); 23 | stroke(0); strokeWeight(1); 24 | ellipse(0, 0, hypotenuse * 2, hypotenuse * 2); 25 | line(-hypotenuse, 0, hypotenuse, 0); 26 | 27 | fill(0); stroke(0); strokeWeight(3); 28 | line(0, 0, adjacent, 0); 29 | drawLineWithLabel(0, 0, adjacent, -opposite, "hypotenuse:" + hypotenuse.toPrecision(3), (adjacent > 0) ? RIGHT : LEFT); 30 | drawLineWithLabel(adjacent, 0, adjacent, -opposite, "opposite:" + opposite.toPrecision(3), (adjacent > 0) ? LEFT : RIGHT); 31 | drawLabel(adjacent / 2, 16, "adjacent:" + adjacent.toPrecision(3), LEFT); 32 | pop(); 33 | 34 | drawLabel(16, windowHeight - 120, "angle(degrees):" + deg.toPrecision(3), LEFT); 35 | drawLabel(16, windowHeight - 96, "angle(radians):" + rad.toPrecision(3), LEFT); 36 | drawLabel(16, windowHeight - 72, "sine:" + sine.toPrecision(3), LEFT); 37 | drawLabel(16, windowHeight - 48, "cosine:" + cosine.toPrecision(3), LEFT); 38 | drawLabel(16, windowHeight - 24, "tangent:" + tangent.toPrecision(3), LEFT); 39 | 40 | deg ++; 41 | } 42 | 43 | /** drawLineWithLabel **/ 44 | 45 | /** drawLabel **/ 46 | -------------------------------------------------------------------------------- /js/examples/machines/piston.js: -------------------------------------------------------------------------------- 1 | let center, centerLine, armLength, radius; 2 | 3 | function setup() { 4 | canvas = createCanvas(windowWidth, windowHeight); 5 | center = createVector(width * 0.5, height * 0.75); 6 | centerLine = new Line().fromTwoPoints(createVector(width * 0.5, 0), createVector(width * 0.5, height)); 7 | armLength = height * 0.3; 8 | radius = height * 0.14; 9 | } 10 | 11 | function draw() { 12 | clear(); 13 | background(0, 171, 107); 14 | 15 | let a = radians((frameCount - 1) * 4); 16 | let p = createVector(cos(a) * radius, sin(a) * radius).add(center); 17 | let circle = new Circle().fromCenterAndRadius(p, armLength); 18 | let intersections = circle.getIntersectionPoints(centerLine); 19 | let pistonBottom = intersections[0]; 20 | 21 | if (intersections.length > 1 && intersections[1].y < intersections[0].y) { 22 | pistonBottom = intersections[1]; 23 | } 24 | let arm = new LineSegment().fromTwoPoints(p, pistonBottom); 25 | 26 | fill(171, 232, 199); stroke(0); 27 | ellipse(center.x, center.y, radius * 2, radius * 2); 28 | fill(12, 228, 113); 29 | rect(center.x - 60, -1, 120, pistonBottom.y + 1); 30 | 31 | noFill();stroke(0); 32 | line(center.x, center.y, p.x, p.y); 33 | centerLine.draw(); 34 | 35 | strokeWeight(3); 36 | arm.draw(); 37 | 38 | fill(255); strokeWeight(1); 39 | drawCircleMarker(center, 4); 40 | drawCircleMarker(p, 4); 41 | drawCircleMarker(arm.p1, 4); 42 | drawCircleMarker(pistonBottom, 4); 43 | } 44 | 45 | /** Line **/ 46 | 47 | /** LineSegment **/ 48 | 49 | /** Circle **/ 50 | 51 | /** drawLabel **/ 52 | 53 | /** drawCircleMarker **/ 54 | -------------------------------------------------------------------------------- /js/examples/machines/crank2.js: -------------------------------------------------------------------------------- 1 | let log = []; 2 | let centerA, centerB, radiusA, radiusB, armLength; 3 | 4 | function setup() { 5 | canvas = createCanvas(windowWidth, windowHeight); 6 | centerA = createVector(width * 0.25, height * 0.5); 7 | centerB = createVector(width * 0.5, height * 0.5); 8 | armLength = width * 0.5; 9 | radiusA = min(height * 0.2, centerA.dist(centerB) * 0.5); 10 | radiusB = radiusA * 0.5; 11 | } 12 | 13 | function draw() { 14 | clear(); 15 | background(69, 157, 193); 16 | 17 | let aA = radians((frameCount - 1) * 2) + PI * 0.5; 18 | let aB = radians((frameCount - 1) * 5) - PI * 0.5; 19 | let pA = createVector(cos(aA) * radiusA, sin(aA) * radiusA).add(centerA); 20 | let pB = createVector(cos(aB) * radiusB, sin(aB) * radiusB).add(centerB); 21 | let arm = new LineSegment().fromTwoPointsAndLength(pA, pB, armLength); 22 | noFill();stroke(0); 23 | 24 | log.push(arm.p1); 25 | if (log.length > 120) { log.shift(); } 26 | 27 | fill(253, 219, 192); stroke(0); 28 | ellipse(centerA.x, centerA.y, radiusA * 2, radiusA * 2); 29 | ellipse(centerB.x, centerB.y, radiusB * 2, radiusB * 2); 30 | noFill(); 31 | line(centerA.x, centerA.y, pA.x, pA.y); 32 | line(centerB.x, centerB.y, pB.x, pB.y); 33 | 34 | 35 | strokeWeight(3); 36 | beginShape(); 37 | for (let i = 0; i < log.length; i ++) { 38 | vertex(log[i].x, log[i].y); 39 | } 40 | endShape(); 41 | 42 | arm.draw(); 43 | 44 | fill(255); strokeWeight(1); 45 | drawCircleMarker(centerA, 4); 46 | drawCircleMarker(centerB, 4); 47 | drawCircleMarker(pA, 4); 48 | drawCircleMarker(pB, 4); 49 | drawCircleMarker(arm.p1, 4); 50 | } 51 | 52 | /** LineSegment **/ 53 | 54 | /** drawLabel **/ 55 | 56 | /** drawCircleMarker **/ 57 | -------------------------------------------------------------------------------- /js/examples/distance/circle.js: -------------------------------------------------------------------------------- 1 | function setup() { 2 | canvas = createCanvas(windowWidth, windowHeight); 3 | stepLength = 16; 4 | x = 0; 5 | y = 0; 6 | dx = 0; 7 | dy = -1; 8 | steps = 0; 9 | next = 1; 10 | phase = 0; 11 | 12 | count = 0; 13 | background(20, 80, 130); 14 | } 15 | 16 | function distance(p, center, radius) { 17 | return Math.sqrt(Math.pow(p.x - center.x, 2) + Math.pow(p.y - center.y, 2)) - radius; // or use p5.Vector.dist() 18 | } 19 | 20 | function draw() { 21 | const hw = width / 2; 22 | const hh = height / 2; 23 | 24 | const p = createVector(x * stepLength, y * stepLength); 25 | const dist = distance(p, createVector(0, 0), 128); 26 | const size = Math.max(0.25, 1 - (128 - Math.abs(dist)) / 128) * 4; 27 | push(); 28 | translate(hw, hh); 29 | if (dist >= 0) { 30 | fill(0); noStroke(); 31 | } else { 32 | fill(255); noStroke(); 33 | } 34 | 35 | drawCircleMarker(p, size); 36 | pop(); 37 | 38 | x += dx; 39 | y += dy; 40 | steps ++; 41 | 42 | if (steps >= next) { 43 | steps = 0; 44 | if (dx == 1) { 45 | dx = 0; dy = 1; 46 | } else if (dx == -1) { 47 | dx = 0; dy = -1; 48 | } else if (dy == 1) { 49 | dx = -1; dy = 0; 50 | } else if (dy == -1) { 51 | dx = 1; dy = 0; 52 | } 53 | if (phase == 1) { 54 | next ++; 55 | } 56 | phase = (phase + 1) % 2; 57 | 58 | if (next * stepLength >= height) { 59 | background(20, 80, 130); 60 | x = 0; y = 0; dx = 0; dy = -1; steps = 0; next = 1; phase = 0; 61 | } 62 | } 63 | } 64 | 65 | /** drawCircleMarker **/ 66 | -------------------------------------------------------------------------------- /js/examples/wave/square_wave.js: -------------------------------------------------------------------------------- 1 | let prevInitFrame = 1; 2 | let n = 1; 3 | let baseRadius = 100; 4 | let plotData = []; 5 | 6 | function setup() { 7 | canvas = createCanvas(windowWidth, windowHeight); 8 | } 9 | 10 | function draw() { 11 | clear(); 12 | background(224, 239, 243); 13 | 14 | let x = 0, y = 0; 15 | 16 | push(); 17 | translate(baseRadius + 20, height / 2); 18 | fill(255); stroke(0); strokeWeight(1); 19 | ellipse(x, y, baseRadius * 2, baseRadius * 2); 20 | 21 | let m = 4 / PI; 22 | for (let i = 0; i < n; i ++) { 23 | let d = 1 + i * 2; 24 | let rad = radians(frameCount - 1) * 2 * d; 25 | let radius = baseRadius / d * m; 26 | let sine = sin(rad); 27 | let cosine = cos(rad); 28 | let nx = x + cosine * radius; 29 | let ny = y - sine * radius; 30 | 31 | line(x, y, nx, ny); 32 | drawCircleMarker(createVector(nx, ny), 2); 33 | x = nx; 34 | y = ny; 35 | } 36 | 37 | drawCircleMarker(createVector(0, 0), 2); 38 | line(baseRadius + 20, y, -baseRadius - 20, y); 39 | drawCircleMarker(createVector(x, y), 4); 40 | pop(); 41 | 42 | let graphW = width - (baseRadius + 20) * 2; 43 | let graphX = (baseRadius + 20) * 2; 44 | 45 | if (frameCount > prevInitFrame + graphW) { 46 | plotData = []; 47 | prevInitFrame += graphW; 48 | } 49 | plotData.push({x:frameCount - prevInitFrame, y: -y}); 50 | 51 | if (frameCount % 90 == 0) { 52 | n ++; 53 | if (n > 40) { 54 | n = 1; 55 | } 56 | } 57 | 58 | plotGraph(plotData, graphX, height / 2, graphW, height - 64, 0, graphW, -height / 2 + 32, height / 2 - 32, "time", "y"); 59 | 60 | drawLabel(8, height - 32, "n = " + n, LEFT); 61 | } 62 | 63 | /** plotGraph **/ 64 | 65 | /** drawLabel **/ 66 | 67 | /** drawCircleMarker **/ 68 | -------------------------------------------------------------------------------- /js/examples/distance/rectangle.js: -------------------------------------------------------------------------------- 1 | function setup() { 2 | canvas = createCanvas(windowWidth, windowHeight); 3 | stepLength = 16; 4 | x = 0; 5 | y = 0; 6 | dx = 0; 7 | dy = -1; 8 | steps = 0; 9 | next = 1; 10 | phase = 0; 11 | 12 | count = 0; 13 | background(20, 80, 130); 14 | } 15 | 16 | function distance(p, size, center, radius) { 17 | const diff = p.copy().sub(center); 18 | return Math.max(Math.abs(diff.x) - size.x, Math.abs(diff.y) - size.y); 19 | } 20 | 21 | function draw() { 22 | const hw = width / 2; 23 | const hh = height / 2; 24 | 25 | const p = createVector(x * stepLength, y * stepLength); 26 | const dist = distance(p, createVector(128, 64), createVector(0, 0), 128); 27 | const size = Math.max(0.25, 1 - (128 - Math.abs(dist)) / 128) * 4; 28 | push(); 29 | translate(hw, hh); 30 | if (dist >= 0) { 31 | fill(0); noStroke(); 32 | } else { 33 | fill(255); noStroke(); 34 | } 35 | 36 | drawCircleMarker(p, size); 37 | pop(); 38 | 39 | x += dx; 40 | y += dy; 41 | steps ++; 42 | 43 | if (steps >= next) { 44 | steps = 0; 45 | if (dx == 1) { 46 | dx = 0; dy = 1; 47 | } else if (dx == -1) { 48 | dx = 0; dy = -1; 49 | } else if (dy == 1) { 50 | dx = -1; dy = 0; 51 | } else if (dy == -1) { 52 | dx = 1; dy = 0; 53 | } 54 | if (phase == 1) { 55 | next ++; 56 | } 57 | phase = (phase + 1) % 2; 58 | 59 | if (next * stepLength >= height) { 60 | background(20, 80, 130); 61 | x = 0; y = 0; dx = 0; dy = -1; steps = 0; next = 1; phase = 0; 62 | } 63 | } 64 | } 65 | 66 | /** drawCircleMarker **/ 67 | -------------------------------------------------------------------------------- /js/examples/wave/triangle_wave.js: -------------------------------------------------------------------------------- 1 | let prevInitFrame = 1; 2 | let n = 1; 3 | let baseRadius = 100; 4 | let plotData = []; 5 | 6 | function setup() { 7 | canvas = createCanvas(windowWidth, windowHeight); 8 | } 9 | 10 | function draw() { 11 | clear(); 12 | background(224, 239, 243); 13 | 14 | let x = 0, y = 0; 15 | 16 | push(); 17 | translate(baseRadius + 20, height / 2); 18 | fill(255); stroke(0); strokeWeight(1); 19 | ellipse(x, y, baseRadius * 2, baseRadius * 2); 20 | 21 | let m = 8 / PI / PI; 22 | for (let i = 0; i < n; i ++) { 23 | let d = 1 + i * 2; 24 | let rad = radians(frameCount - 1) * 2 * d; 25 | let radius = baseRadius / pow(d, 2) * (i % 2 == 0 ? 1 : -1) * m; 26 | let sine = sin(rad); 27 | let cosine = cos(rad); 28 | let nx = x + cosine * radius; 29 | let ny = y - sine * radius; 30 | 31 | line(x, y, nx, ny); 32 | drawCircleMarker(createVector(nx, ny), 2); 33 | x = nx; 34 | y = ny; 35 | } 36 | 37 | drawCircleMarker(createVector(0, 0), 2); 38 | line(baseRadius + 20, y, -baseRadius - 20, y); 39 | drawCircleMarker(createVector(x, y), 4); 40 | pop(); 41 | 42 | 43 | let graphW = width - (baseRadius + 20) * 2; 44 | let graphX = (baseRadius + 20) * 2; 45 | 46 | if (frameCount > prevInitFrame + graphW) { 47 | plotData = []; 48 | prevInitFrame += graphW; 49 | } 50 | plotData.push({x:frameCount - prevInitFrame, y: -y}); 51 | 52 | if (frameCount % 90 == 0) { 53 | n ++; 54 | if (n > 40) { 55 | n = 1; 56 | } 57 | } 58 | 59 | plotGraph(plotData, graphX, height / 2, graphW, height - 64, 0, graphW, -height / 2 + 32, height / 2 - 32, "time", "y"); 60 | drawLabel(8, height - 32, "n = " + n, LEFT); 61 | } 62 | 63 | /** plotGraph **/ 64 | 65 | /** drawLabel **/ 66 | 67 | /** drawCircleMarker **/ 68 | -------------------------------------------------------------------------------- /js/examples/collision/collision_circles.js: -------------------------------------------------------------------------------- 1 | let A, B; 2 | 3 | function setup() { 4 | canvas = createCanvas(windowWidth, windowHeight); 5 | 6 | A = {center: createVector(), radius: 80}; 7 | B = {center: createVector(), radius: 60}; 8 | } 9 | 10 | function draw() { 11 | clear(); 12 | background(249, 246, 236); 13 | let t = radians(frameCount) / 2; 14 | A.center.x = cos(t) * 120; 15 | A.center.y = sin((t + 1) * 2) * 120; 16 | B.center.x = -cos((t + 2) * 3) * 100; 17 | B.center.y = -sin((t + 3) * 4) * 100; 18 | 19 | // test 20 | 21 | let result = A.center.dist(B.center) - A.radius - B.radius <= 0; 22 | 23 | // rendering 24 | 25 | push(); 26 | translate(width / 2, height / 2); 27 | 28 | noStroke(); 29 | if (result) { 30 | fill(225, 81, 106); 31 | } else { 32 | fill(255); 33 | } 34 | ellipse(A.center.x, A.center.y, A.radius * 2); 35 | ellipse(B.center.x, B.center.y, B.radius * 2); 36 | 37 | noFill(); stroke(0); strokeWeight(1); 38 | ellipse(A.center.x, A.center.y, A.radius * 2); 39 | ellipse(B.center.x, B.center.y, B.radius * 2); 40 | 41 | let pA = A.center.copy().add(B.center.copy().sub(A.center).normalize().mult(A.radius)); 42 | let pB = B.center.copy().add(A.center.copy().sub(B.center).normalize().mult(B.radius)); 43 | 44 | strokeWeight(1); 45 | let boundary = {x: -width / 2, y: -height / 2, w: width, h: height}; 46 | let l = new Line().fromTwoPoints(A.center, B.center); 47 | let perpA = l.getPerpendicular(pA); 48 | let perpB = l.getPerpendicular(pB); 49 | l.draw(boundary); 50 | 51 | stroke(0, 0, 0, 128); 52 | perpA.draw(boundary); 53 | perpB.draw(boundary); 54 | 55 | if (result) { 56 | strokeWeight(3); 57 | stroke(0, 0, 0, 255); 58 | line(pA.x, pA.y, pB.x, pB.y); 59 | } 60 | 61 | pop(); 62 | } 63 | 64 | /** Line **/ 65 | -------------------------------------------------------------------------------- /js/examples/wave/saw_wave.js: -------------------------------------------------------------------------------- 1 | let prevInitFrame = 1; 2 | let n = 1; 3 | let baseRadius = 100; 4 | let plotData = []; 5 | 6 | function setup() { 7 | canvas = createCanvas(windowWidth, windowHeight); 8 | } 9 | 10 | function draw() { 11 | clear(); 12 | background(224, 239, 243); 13 | 14 | let x = 0, y = 0; 15 | 16 | push(); 17 | translate(baseRadius + 20, height / 2); 18 | fill(255); stroke(0); strokeWeight(1); 19 | ellipse(x, y, baseRadius * 2, baseRadius * 2); 20 | 21 | let m = 2 / PI; 22 | for (let i = 1; i <= n; i ++) { 23 | let rad = radians(frameCount - 1) * 2 * i; 24 | let radius = baseRadius / i * (i % 2 == 0 ? -1 : 1) * m; 25 | let sine = sin(rad); 26 | let cosine = cos(rad); 27 | let nx = x + cosine * radius; 28 | let ny = y - sine * radius; 29 | if (i == 0) { 30 | ellipse(x, y, radius * 2, radius * 2); 31 | } 32 | 33 | line(x, y, nx, ny); 34 | drawCircleMarker(createVector(nx, ny), 2); 35 | x = nx; 36 | y = ny; 37 | } 38 | 39 | drawCircleMarker(createVector(0, 0), 2); 40 | line(baseRadius + 20, y, -baseRadius - 20, y); 41 | drawCircleMarker(createVector(x, y), 4); 42 | pop(); 43 | 44 | 45 | let graphW = width - (baseRadius + 20) * 2; 46 | let graphX = (baseRadius + 20) * 2; 47 | 48 | if (frameCount > prevInitFrame + graphW) { 49 | plotData = []; 50 | prevInitFrame += graphW; 51 | } 52 | plotData.push({x:frameCount - prevInitFrame, y: -y}); 53 | 54 | if (frameCount % 90 == 0) { 55 | n ++; 56 | if (n > 40) { 57 | n = 1; 58 | } 59 | } 60 | 61 | plotGraph(plotData, graphX, height / 2, graphW, height - 64, 0, graphW, -height / 2 + 32, height / 2 - 32, "time", "y"); 62 | drawLabel(8, height - 32, "n = " + n, LEFT); 63 | 64 | } 65 | 66 | /** plotGraph **/ 67 | 68 | /** drawLabel **/ 69 | 70 | /** drawCircleMarker **/ 71 | -------------------------------------------------------------------------------- /js/examples/distance/rotated_rectangle.js: -------------------------------------------------------------------------------- 1 | function setup() { 2 | canvas = createCanvas(windowWidth, windowHeight); 3 | stepLength = 16; 4 | x = 0; 5 | y = 0; 6 | dx = 0; 7 | dy = -1; 8 | steps = 0; 9 | next = 1; 10 | phase = 0; 11 | 12 | count = 0; 13 | background(20, 80, 130); 14 | } 15 | 16 | function distance(p, size, center, radius) { 17 | let diff = p.copy().sub(center); 18 | diff = rotate2d(diff, Math.PI * 0.25); 19 | return Math.max(Math.abs(diff.x) - size.x, Math.abs(diff.y) - size.y); 20 | } 21 | 22 | function draw() { 23 | const hw = width / 2; 24 | const hh = height / 2; 25 | 26 | const p = createVector(x * stepLength, y * stepLength); 27 | const dist = distance(p, createVector(128, 64), createVector(0, 0), 128); 28 | const size = Math.max(0.25, 1 - (128 - Math.abs(dist)) / 128) * 4; 29 | push(); 30 | translate(hw, hh); 31 | if (dist >= 0) { 32 | fill(0); noStroke(); 33 | } else { 34 | fill(255); noStroke(); 35 | } 36 | 37 | drawCircleMarker(p, size); 38 | pop(); 39 | 40 | x += dx; 41 | y += dy; 42 | steps ++; 43 | 44 | if (steps >= next) { 45 | steps = 0; 46 | if (dx == 1) { 47 | dx = 0; dy = 1; 48 | } else if (dx == -1) { 49 | dx = 0; dy = -1; 50 | } else if (dy == 1) { 51 | dx = -1; dy = 0; 52 | } else if (dy == -1) { 53 | dx = 1; dy = 0; 54 | } 55 | if (phase == 1) { 56 | next ++; 57 | } 58 | phase = (phase + 1) % 2; 59 | 60 | if (next * stepLength >= height) { 61 | background(20, 80, 130); 62 | x = 0; y = 0; dx = 0; dy = -1; steps = 0; next = 1; phase = 0; 63 | } 64 | } 65 | } 66 | 67 | /** rotate2d **/ 68 | 69 | /** drawCircleMarker **/ 70 | -------------------------------------------------------------------------------- /js/examples/drawings/centroid.js: -------------------------------------------------------------------------------- 1 | function setup() { 2 | canvas = createCanvas(windowWidth, windowHeight); 3 | } 4 | 5 | function draw() { 6 | clear(); 7 | background(62, 252, 100); 8 | let boundary = {x: -width / 2, y: -height / 2, w: width, h: height}; 9 | 10 | let t = (frameCount % 720) / 720; 11 | let a0 = PI / 3 * cos(t * TWO_PI) + 0.01 + TWO_PI / 3 ; 12 | let a1 = PI / 3 * cos(t * TWO_PI * 3) + 0.02 + TWO_PI / 3 * 2; 13 | let a2 = PI / 3 * cos(t * TWO_PI * 5) + 0.03; 14 | 15 | let p0 = createVector(cos(a0), sin(a0)).mult(120); 16 | let p1 = createVector(cos(a1), sin(a1)).mult(140); 17 | let p2 = createVector(cos(a2), sin(a2)).mult(160); 18 | 19 | let s0 = new LineSegment().fromTwoPoints(p0, p1); 20 | let s1 = new LineSegment().fromTwoPoints(p1, p2); 21 | let s2 = new LineSegment().fromTwoPoints(p2, p0); 22 | 23 | let mp0 = s0.getMidPoint(); 24 | let mp1 = s1.getMidPoint(); 25 | let mp2 = s2.getMidPoint(); 26 | 27 | let l0 = new Line().fromTwoPoints(p0, mp1); 28 | let l1 = new Line().fromTwoPoints(p1, mp2); 29 | let l2 = new Line().fromTwoPoints(p2, mp0); 30 | let centroid = l0.getIntersectionPoint(l1); 31 | 32 | push(); 33 | translate(width / 2, height / 2); 34 | noFill(); stroke(0); strokeWeight(1); 35 | fill(0); strokeWeight(3); 36 | s0.draw(); 37 | s1.draw(); 38 | s2.draw(); 39 | strokeWeight(1); 40 | 41 | l0.draw(boundary); 42 | l1.draw(boundary); 43 | l2.draw(boundary); 44 | drawCircleMarker(mp0, 3); 45 | drawCircleMarker(mp1, 3); 46 | drawCircleMarker(mp2, 3); 47 | 48 | fill(255); 49 | drawCircleMarker(p0, 3); 50 | drawCircleMarker(p1, 3); 51 | drawCircleMarker(p2, 3); 52 | if (centroid) { 53 | drawCircleMarker(centroid, 4); 54 | } 55 | pop(); 56 | } 57 | 58 | /** Line **/ 59 | 60 | /** LineSegment **/ 61 | 62 | /** Circle **/ 63 | 64 | /** drawCircleMarker **/ 65 | -------------------------------------------------------------------------------- /js/examples/easing/composite01.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | let msec = 0, prevNow = 0; 4 | const duration = 1000; 5 | 6 | function setup() { 7 | canvas = createCanvas(windowWidth, windowHeight); 8 | plotData = []; 9 | tween = new Tween(); 10 | tweens = [ 11 | (t)=>{return tween.powerInOut(t, 2)}, 12 | (t)=>{return tween.powerInOut(t, 4)}, 13 | (t)=>{return tween.linear(t)} 14 | ] 15 | } 16 | 17 | function draw() { 18 | clear(); 19 | background(240, 240, 30); 20 | 21 | const t = msec / duration; 22 | 23 | const hw = width / 2; 24 | const hh = height / 2; 25 | const p0 = createVector(-160, -160); 26 | const p1 = createVector(80, -80); 27 | const p2 = createVector(160, 160); 28 | let p0_1 = p0.copy().lerp(p1, tweens[0](t)); 29 | let p1_2 = p1.copy().lerp(p2, tweens[1](t)); 30 | let p = p0_1.copy().lerp(p1_2, tweens[2](t)); 31 | plotData.push(p); 32 | 33 | push(); 34 | translate(hw, hh); 35 | noFill(); stroke(0); 36 | line(p0.x, p0.y, p1.x, p1.y); 37 | line(p1.x, p1.y, p2.x, p2.y); 38 | line(p0_1.x, p0_1.y, p1_2.x, p1_2.y); 39 | 40 | for (let i = 1; i < plotData.length; i ++) { 41 | line(plotData[i - 1].x, plotData[i - 1].y, plotData[i].x, plotData[i].y); 42 | } 43 | 44 | fill(0); noStroke(); 45 | drawCircleMarker(p0, 4); 46 | drawCircleMarker(p1, 4); 47 | drawCircleMarker(p2, 4); 48 | fill(255); stroke(0); 49 | drawCircleMarker(p0_1, 4); 50 | drawCircleMarker(p1_2, 4); 51 | drawCircleMarker(p, 4); 52 | 53 | 54 | pop(); 55 | ellapseTime(); 56 | } 57 | 58 | function ellapseTime() { 59 | if (msec > duration) { 60 | msec = 0; 61 | plotData = []; 62 | } else { 63 | msec += min(100, window.performance.now() - prevNow); 64 | } 65 | prevNow = window.performance.now(); 66 | } 67 | 68 | /** drawCircleMarker **/ 69 | 70 | /** Tween **/ 71 | -------------------------------------------------------------------------------- /js/examples/collision/collision_rectangles.js: -------------------------------------------------------------------------------- 1 | let A, B; 2 | 3 | function setup() { 4 | canvas = createCanvas(windowWidth, windowHeight); 5 | 6 | A = {x: 0, y: 0, w: 160, h: 90}; 7 | B = {x: 0, y: 0, w: 90, h: 160}; 8 | } 9 | 10 | function draw() { 11 | clear(); 12 | background(249, 246, 236); 13 | let t = radians(frameCount) / 2; 14 | A.x = cos(t) * 120 - A.w / 2; 15 | A.y = sin(t * 2) * 120 - A.h / 2; 16 | B.x = -cos(t * 3) * 120 - B.w / 2; 17 | B.y = -sin(t * 4) * 120 - B.h / 2; 18 | 19 | // test 20 | 21 | let xResult = A.x <= B.x + B.w && A.x + A.w >= B.x; 22 | let yResult = A.y <= B.y + B.h && A.h + A.y >= B.y; 23 | let result = xResult && yResult; 24 | 25 | // rendering 26 | 27 | push(); 28 | translate(width / 2, height / 2); 29 | 30 | noStroke(); 31 | if (result) { 32 | fill(225, 81, 106); 33 | } else { 34 | fill(255); 35 | } 36 | rect(A.x, A.y, A.w, A.h); 37 | rect(B.x, B.y, B.w, B.h); 38 | 39 | noFill(); stroke(0); strokeWeight(1); 40 | rect(A.x, A.y, A.w, A.h); 41 | rect(B.x, B.y, B.w, B.h); 42 | 43 | fill(0); strokeWeight(1); 44 | let l0 = -width / 2, l1 = l0 + 16; 45 | let b0 = height / 2, b1 = b0 - 16; 46 | 47 | line(-width / 2, b1, width / 2, b1); 48 | line(l1, -height / 2, l1, height / 2); 49 | 50 | stroke(0, 0, 0, 32); 51 | line(A.x, A.y, A.x, b0); 52 | line(A.x + A.w, A.y, A.x + A.w, b0); 53 | line(A.x, A.y, l0, A.y); 54 | line(A.x, A.y + A.h, l0, A.y + A.h); 55 | 56 | line(B.x, B.y, l0, B.y); 57 | line(B.x, B.y + B.h, l0, B.y + B.h); 58 | line(B.x, B.y, B.x, b0); 59 | line(B.x + B.w, B.y, B.x + B.w, b0); 60 | 61 | stroke(0); 62 | strokeWeight(3); 63 | if (xResult) { 64 | let sorted = [A.x, B.x + B.w, A.x + A.w, B.x].sort((a,b)=>{return a > b ? 1: -1}); 65 | line(sorted[1], b1, sorted[2], b1); 66 | } 67 | 68 | if (yResult) { 69 | let sorted = [A.y, B.y + B.h, A.y + A.h, B.y].sort((a,b)=>{return a > b ? 1: -1}); 70 | line(l1, sorted[1], l1, sorted[2]); 71 | } 72 | 73 | pop(); 74 | } 75 | -------------------------------------------------------------------------------- /machines.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Mechanical Movement 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 |

Geometric Machines

16 |

Sketching with Math and Quasi Physics

17 |

There is a newer and more elaborate version of this site.
Visit Sketching with Math and Quasi Physics on Notion.

18 |

Crank Slider(1)

19 |
20 | 21 |

Crank Slider(2)

22 |
23 | 24 |

Piston

25 |
26 | 27 |

index

28 | 29 |
30 | 31 | 32 | 33 | 34 | 35 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /js/examples/verlet/verlet_sticks.js: -------------------------------------------------------------------------------- 1 | 2 | let points = [], sticks = []; 3 | let accelIndex, accel; 4 | 5 | function setup() { 6 | canvas = createCanvas(windowWidth, windowHeight); 7 | for (let i = 0; i < 10; i ++) { 8 | points.push(new VerletPoint( createVector(random(width), random(height))) ); 9 | points[i].position.add(random2D().mult(random(2) + 1)); 10 | } 11 | } 12 | 13 | function draw() { 14 | clear(); 15 | background(100, 150, 180); 16 | points.forEach((p)=>{ 17 | p.update(); 18 | if (p.position.x < 0) { p.position.x += 2; } 19 | if (p.position.x > width) { p.position.x -= 2; } 20 | if (p.position.y < 0) { p.position.y += 2; } 21 | if (p.position.y > height) { p.position.y -= 2; } 22 | if (p.getVelocity().mag() > 3) { 23 | p.setVelocity(p.getVelocity().normalize().mult(3)); // limit the speed 24 | } 25 | }); 26 | 27 | sticks.forEach((s)=>{ 28 | s.update(); 29 | }); 30 | 31 | let f = (frameCount - 1) % 60; 32 | if (f == 0) { 33 | accelIndex = floor(random(points.length)); 34 | accel = random2D().mult((random(0.5) + 0.5)); 35 | let idx0, idx1; 36 | do { 37 | idx0 = floor(random(points.length)); 38 | idx1 = floor(random(points.length)); 39 | } while (idx0 == idx1); 40 | 41 | sticks.push(new VerletStick(points[idx0], points[idx1])); 42 | if (sticks.length > 5) { 43 | sticks.shift(); 44 | } 45 | } 46 | 47 | fill(255); stroke(0); strokeWeight(3); 48 | if (f < 30) { 49 | points[accelIndex].position.add(accel); 50 | let p0 = points[accelIndex].position; 51 | let p1 = points[accelIndex].position.copy().add(accel.copy().mult(100)); 52 | drawArrow(p0.x, p0.y, p1.x, p1.y); 53 | } 54 | 55 | strokeWeight(1); 56 | sticks.forEach((s)=>{s.draw();}); 57 | points.forEach((p)=>{p.draw();}); 58 | } 59 | 60 | function random2D() { 61 | return createVector(random(-1, 1), random(-1, 1)).normalize(); 62 | } 63 | 64 | /** random2D **/ 65 | 66 | /** drawArrow **/ 67 | 68 | /** VerletPoint **/ 69 | 70 | /** VerletStick **/ 71 | -------------------------------------------------------------------------------- /js/examples/drawings/spinner.js: -------------------------------------------------------------------------------- 1 | let lines = [[],[],[],[],[]]; 2 | let sqrt2 = Math.sqrt(2); 3 | 4 | function setup() { 5 | canvas = createCanvas(windowWidth, windowHeight); 6 | } 7 | 8 | function draw() { 9 | clear(); 10 | background(72, 140, 255); 11 | 12 | let f = frameCount - 1; 13 | 14 | if (f % 180 >= 90) { 15 | for (let i = 0; i < lines.length; i ++) { 16 | lines[i].shift(); lines[i].shift(); 17 | } 18 | } 19 | 20 | let theta = (f / 90 - 0.25) * TWO_PI; 21 | let u = min(width, height) / 8; 22 | let c0 = createVector(0, -u * 2); 23 | let c1 = createVector(0, u * 2); 24 | let m = createVector(cos(theta) * u * 3, sin(theta) * u * 3); 25 | let cp0 = (m.copy().sub(c0)).normalize().mult(u).add(c0); 26 | let cp1 = (m.copy().sub(c1)).normalize().mult(u).add(c1); 27 | let mp = createVector(0, constrain(m.y, c0.y, c1.y)); 28 | 29 | let sp; 30 | if (m.y < c0.y) { 31 | sp = cp0.copy(); 32 | } else if (m.y > c1.y) { 33 | sp = cp1.copy(); 34 | } else { 35 | if (m.x < 0) { 36 | sp = createVector(-u, mp.y); 37 | } else { 38 | sp = createVector(u, mp.y); 39 | } 40 | } 41 | 42 | lines[0].push(m); 43 | lines[1].push(cp0); 44 | lines[2].push(cp1); 45 | lines[3].push(mp); 46 | lines[4].push(sp); 47 | 48 | push(); 49 | translate(width / 2, height / 2); 50 | rotate((f / 180 - 0.125) * TWO_PI); 51 | noFill(); stroke(0); 52 | for (let i = 0; i < lines.length; i++) { 53 | drawLine(lines[i]); 54 | } 55 | line(m.x, m.y, c0.x, c0.y); 56 | line(m.x, m.y, c1.x, c1.y); 57 | line(m.x, m.y, mp.x, mp.y); 58 | fill(255); 59 | drawCircleMarker(m, 4); 60 | drawCircleMarker(cp0, 4); 61 | drawCircleMarker(cp1, 4); 62 | drawCircleMarker(c0, 4); 63 | drawCircleMarker(c1, 4); 64 | drawCircleMarker(mp, 4); 65 | drawCircleMarker(sp, 4); 66 | pop(); 67 | } 68 | 69 | function drawLine(line) { 70 | beginShape(); 71 | for (let i = 0; i < line.length; i ++) { 72 | vertex(line[i].x, line[i].y); 73 | } 74 | endShape(); 75 | } 76 | 77 | /** drawCircleMarker **/ 78 | -------------------------------------------------------------------------------- /js/examples/math/cross_product.js: -------------------------------------------------------------------------------- 1 | let project = getProjectionFunc(2, 3); 2 | 3 | function setup() { 4 | canvas = createCanvas(windowWidth, windowHeight); 5 | } 6 | 7 | function draw() { 8 | clear(); 9 | let t = frameCount * 0.005; 10 | 11 | let m = 320; // Magnifier 12 | let x1 = signedNoise(t + 123); 13 | let y1 = signedNoise(t + 234); 14 | let z1 = signedNoise(t + 456); 15 | let x2 = signedNoise(t + 789); 16 | let y2 = signedNoise(t + 890); 17 | let z2 = signedNoise(t + 901); 18 | 19 | let v1 = createVector(x1, y1, z1).normalize(); 20 | let v2 = createVector(x2, y2, z2).normalize(); 21 | let v3 = v1.cross(v2); 22 | let v4 = v1.cross(v3); 23 | 24 | let p1 = project(v1).mult(m); 25 | let p2 = project(v2).mult(m); 26 | let p3 = project(v3).mult(m); 27 | 28 | let sp0 = project(v1.copy().sub(v4)).mult(m * 1.2); 29 | let sp1 = project(v1.copy().add(v4)).mult(m * 1.2); 30 | let sp2 = project(v1.copy().mult(-1).sub(v4)).mult(m * 1.2); 31 | let sp3 = project(v1.copy().mult(-1).add(v4)).mult(m * 1.2); 32 | 33 | background(245, 177, 217); 34 | 35 | push(); 36 | translate(width / 2, height / 2); 37 | 38 | noFill(); stroke(0); strokeWeight(1); 39 | line(sp0.x, sp0.y, sp1.x, sp1.y); 40 | line(sp1.x, sp1.y, sp3.x, sp3.y); 41 | line(sp0.x, sp0.y, sp2.x, sp2.y); 42 | line(sp2.x, sp2.y, sp3.x, sp3.y); 43 | strokeWeight(2); 44 | drawArrow(0, 0, p1.x, p1.y); 45 | drawArrow(0, 0, p2.x, p2.y); 46 | strokeWeight(3); 47 | drawArrow(0, 0, p3.x, p3.y); 48 | 49 | 50 | fill(0); noStroke() 51 | drawLabel(p1.x, p1.y, "v1 [" + v1.x.toPrecision(2) + ", " + v1.y.toPrecision(2) + ", " + v1.z.toPrecision(2) + "]", LEFT); 52 | drawLabel(p2.x, p2.y, "v2 [" + v2.x.toPrecision(2) + ", " + v2.y.toPrecision(2) + ", " + v2.z.toPrecision(2) + "]", LEFT); 53 | drawLabel(p3.x, p3.y, "Cross Product [" + v3.x.toPrecision(2) + ", " + v3.y.toPrecision(2) + ", " + v3.z.toPrecision(2) + "]", LEFT); 54 | pop(); 55 | } 56 | 57 | /** signedNoise **/ 58 | 59 | /** getProjectionFunc **/ 60 | 61 | /** drawArrow **/ 62 | 63 | /** drawLabel **/ 64 | -------------------------------------------------------------------------------- /js/examples/physics/acceleration.js: -------------------------------------------------------------------------------- 1 | function setup() { 2 | canvas = createCanvas(windowWidth, windowHeight); 3 | 4 | counter = 0; 5 | position = createVector(100, -100); 6 | velocity = createVector(48, -48); 7 | acceleration = createVector(30, 30); 8 | fps = 60; // naively assuming 60 fps 9 | maxSpeed = 256; 10 | } 11 | 12 | function draw() { 13 | clear(); 14 | const hw = width / 2, hh = height / 2; 15 | const vx = position.x + velocity.x; 16 | const vy = position.y + velocity.y; 17 | const ax = position.x + acceleration.x; 18 | const ay = position.y + acceleration.y; 19 | 20 | background(117, 147, 208); 21 | push(); 22 | translate(hw, hh); 23 | 24 | fill(0); stroke(0); 25 | strokeWeight(1); 26 | drawArrow(position.x, position.y, vx, vy); 27 | drawCircleMarker(position, 4); 28 | 29 | strokeWeight(2); 30 | drawArrow(position.x, position.y, ax, ay); 31 | 32 | drawLabel(position.x, position.y, `position (${position.x.toPrecision(4)}, ${position.y.toPrecision(4)})`, LEFT); 33 | drawLabel(vx, vy, `velocity (${velocity.x.toPrecision(4)}, ${velocity.y.toPrecision(4)})`, LEFT); 34 | drawLabel(ax, ay, `acceleration (${acceleration.x.toPrecision(4)}, ${acceleration.y.toPrecision(4)})`, LEFT); 35 | 36 | strokeWeight(1) 37 | line(-hw, 0, hw, 0); 38 | line(0, -hh, 0, hh); 39 | drawLabel(0, -4, "Origin", LEFT); 40 | 41 | pop(); 42 | 43 | acceleration = createVector(mouseX - position.x - hw, mouseY - position.y - hh).div(2); 44 | const vDelta = acceleration.copy().div(fps); 45 | velocity.add(vDelta); 46 | if (velocity.mag() > maxSpeed) { 47 | velocity.setMag(maxSpeed) 48 | } 49 | const pDelta = velocity.copy().div(fps); 50 | position.add(pDelta); 51 | if (position.x > hw || position.x < -hw) {velocity.x = -velocity.x;} 52 | if (position.y > hh || position.y < -hh) {velocity.y = -velocity.y;} 53 | } 54 | 55 | 56 | /** drawCircleMarker **/ 57 | 58 | /** drawArrow **/ 59 | 60 | /** drawLabel **/ 61 | -------------------------------------------------------------------------------- /js/examples/drawings/incenter.js: -------------------------------------------------------------------------------- 1 | function setup() { 2 | canvas = createCanvas(windowWidth, windowHeight); 3 | } 4 | 5 | function draw() { 6 | clear(); 7 | background(62, 252, 100); 8 | let boundary = {x: -width / 2, y: -height / 2, w: width, h: height}; 9 | 10 | let t = (frameCount % 720) / 720; 11 | let a0 = PI / 3 * cos(t * TWO_PI) + 0.01 + TWO_PI / 3 ; 12 | let a1 = PI / 3 * cos(t * TWO_PI * 3) + 0.02 + TWO_PI / 3 * 2; 13 | let a2 = PI / 3 * cos(t * TWO_PI * 5) + 0.03; 14 | 15 | let p0 = createVector(cos(a0), sin(a0)).mult(120); 16 | let p1 = createVector(cos(a1), sin(a1)).mult(140); 17 | let p2 = createVector(cos(a2), sin(a2)).mult(160); 18 | 19 | let s0 = new LineSegment().fromTwoPoints(p0, p1); 20 | let s1 = new LineSegment().fromTwoPoints(p1, p2); 21 | let s2 = new LineSegment().fromTwoPoints(p2, p0); 22 | let bs0 = getAngleBisection(p0, p1, p2); 23 | let bs1 = getAngleBisection(p1, p2, p0); 24 | let bs2 = getAngleBisection(p2, p0, p1); 25 | 26 | let incenter = bs0.getIntersectionPoint(bs1); 27 | let radius = incenter.dist(s0.getIntersectionPoint(s0.getPerpendicular(incenter))); 28 | let incircle = new Circle().fromCenterAndRadius(incenter, radius); 29 | 30 | push(); 31 | translate(width / 2, height / 2); 32 | noFill(); stroke(0); strokeWeight(1); 33 | fill(0); strokeWeight(3); 34 | s0.draw(); 35 | s1.draw(); 36 | s2.draw(); 37 | strokeWeight(1); 38 | 39 | bs0.draw(boundary); 40 | bs1.draw(boundary); 41 | bs2.draw(boundary); 42 | 43 | fill(255); 44 | drawCircleMarker(p0, 3); 45 | drawCircleMarker(p1, 3); 46 | drawCircleMarker(p2, 3); 47 | 48 | if (incenter) { 49 | noFill(); stroke(0); strokeWeight(1); 50 | incircle.draw(); 51 | fill(255); 52 | drawCircleMarker(incenter, 4); 53 | } 54 | 55 | pop(); 56 | } 57 | 58 | function getAngleBisection(o, p0, p1) { 59 | let a0 = atan2((p0.y - o.y), (p0.x - o.x)); 60 | let a1 = atan2((p1.y - o.y), (p1.x - o.x)); 61 | return new Line().fromPointAndAngle(o, (a0 + a1) / 2); 62 | } 63 | 64 | /** Line **/ 65 | 66 | /** LineSegment **/ 67 | 68 | /** Circle **/ 69 | 70 | /** drawCircleMarker **/ 71 | -------------------------------------------------------------------------------- /js/examples/drawings/orthocenter.js: -------------------------------------------------------------------------------- 1 | function setup() { 2 | canvas = createCanvas(windowWidth, windowHeight); 3 | } 4 | 5 | function draw() { 6 | clear(); 7 | background(62, 252, 100); 8 | let boundary = {x: -width / 2, y: -height / 2, w: width, h: height}; 9 | 10 | let t = (frameCount % 720) / 720; 11 | let a0 = PI / 3 * cos(t * TWO_PI) + 0.01 + TWO_PI / 3 ; 12 | let a1 = PI / 3 * cos(t * TWO_PI * 3) + 0.02 + TWO_PI / 3 * 2; 13 | let a2 = PI / 3 * cos(t * TWO_PI * 5) + 0.03; 14 | 15 | let p0 = createVector(cos(a0), sin(a0)).mult(120); 16 | let p1 = createVector(cos(a1), sin(a1)).mult(140); 17 | let p2 = createVector(cos(a2), sin(a2)).mult(160); 18 | 19 | let s0 = new LineSegment().fromTwoPoints(p0, p1); 20 | let s1 = new LineSegment().fromTwoPoints(p1, p2); 21 | let s2 = new LineSegment().fromTwoPoints(p2, p0); 22 | 23 | let pl0 = s0.getPerpendicular(p2); 24 | let pl1 = s1.getPerpendicular(p0); 25 | let pl2 = s2.getPerpendicular(p1); 26 | let l0 = s0.toLine(); 27 | let l1 = s1.toLine(); 28 | let l2 = s2.toLine(); 29 | 30 | let orthocenter = pl0.getIntersectionPoint(pl1); 31 | 32 | push(); 33 | translate(width / 2, height / 2); 34 | fill(0); stroke(0); strokeWeight(3); 35 | s0.draw(); 36 | s1.draw(); 37 | s2.draw(); 38 | 39 | strokeWeight(1); 40 | pl0.draw(boundary); 41 | pl1.draw(boundary); 42 | pl2.draw(boundary); 43 | 44 | stroke(0, 0, 0, 30); 45 | l0.draw(boundary); 46 | l1.draw(boundary); 47 | l2.draw(boundary); 48 | 49 | stroke(0); noFill(); 50 | drawSquareMark(l0, pl0); 51 | drawSquareMark(l1, pl1); 52 | drawSquareMark(l2, pl2); 53 | 54 | fill(255); 55 | drawCircleMarker(p0, 3); 56 | drawCircleMarker(p1, 3); 57 | drawCircleMarker(p2, 3); 58 | if (orthocenter) { 59 | drawCircleMarker(orthocenter, 4); 60 | } 61 | 62 | pop(); 63 | } 64 | 65 | function drawSquareMark(l0, l1) { 66 | let p = l0.getIntersectionPoint(l1); 67 | push(); 68 | translate(p.x, p.y); 69 | rotate(l0.getAngle()); 70 | rect(0,0,8,8); 71 | pop(); 72 | } 73 | 74 | /** Line **/ 75 | 76 | /** LineSegment **/ 77 | 78 | /** Circle **/ 79 | 80 | /** drawCircleMarker **/ 81 | -------------------------------------------------------------------------------- /js/examples/verlet/pendulum.js: -------------------------------------------------------------------------------- 1 | 2 | let points = [], sticks = []; 3 | let accelIndex, accel; 4 | 5 | function setup() { 6 | canvas = createCanvas(windowWidth, windowHeight); 7 | for (let i = 0; i < 10; i ++) { 8 | points.push(new VerletPoint( createVector(i * 16, i * 16)) ); 9 | if (i != 0) { 10 | sticks.push(new VerletStick(points[i - 1], points[i])); 11 | } 12 | } 13 | let p0 = points[points.length - 1]; 14 | let p1 = new VerletPoint( p0.position.copy().add(createVector(32, 0)) ); 15 | let p2 = new VerletPoint( p0.position.copy().add(createVector(32, 32)) ); 16 | let p3 = new VerletPoint( p0.position.copy().add(createVector(0, 32)) ); 17 | sticks.push(new VerletStick(p0, p1)); 18 | sticks.push(new VerletStick(p1, p2)); 19 | sticks.push(new VerletStick(p2, p3)); 20 | sticks.push(new VerletStick(p3, p0)); 21 | sticks.push(new VerletStick(p0, p2)); 22 | sticks.push(new VerletStick(p1, p3)); 23 | points.push(p1); 24 | points.push(p2); 25 | points.push(p3); 26 | } 27 | 28 | function draw() { 29 | clear(); 30 | background(254, 131, 49); 31 | points.forEach((p, i)=>{ 32 | p.update(); 33 | if (i != 0) { 34 | p.position.y += 1; 35 | } 36 | }); 37 | 38 | for (let i = 0; i < 3; i ++) { 39 | sticks.forEach((s)=>{ 40 | s.update(); 41 | }); 42 | points[0].position.x = points[0].position.y = 0; 43 | } 44 | 45 | let f = (frameCount - 1) % 120; 46 | if (f == 0) { 47 | accelIndex = points.length - 2; 48 | accel = createVector((random() < 0.5) ? -random(1,3) : random(1,3), -random(0,3)); 49 | } 50 | 51 | push(); 52 | translate(width / 2, 0); 53 | fill(255); stroke(0); strokeWeight(3); 54 | if (f > 90) { 55 | points[accelIndex].position.add(accel); 56 | let p0 = points[accelIndex].position; 57 | let p1 = points[accelIndex].position.copy().add(accel.copy().mult(40)); 58 | drawArrow(p0.x, p0.y, p1.x, p1.y); 59 | } 60 | strokeWeight(1); 61 | sticks.forEach((s)=>{s.draw();}); 62 | //points.forEach((p)=>{p.draw();}); 63 | pop(); 64 | } 65 | 66 | /** random2D **/ 67 | 68 | /** drawArrow **/ 69 | 70 | /** VerletPoint **/ 71 | 72 | /** VerletStick **/ 73 | -------------------------------------------------------------------------------- /js/examples/drawings/reuleaux.js: -------------------------------------------------------------------------------- 1 | let cLog = [], vLog = [[],[],[]]; 2 | function setup() { 3 | canvas = createCanvas(windowWidth, windowHeight); 4 | } 5 | 6 | function draw() { 7 | clear(); 8 | background(72, 140, 255); 9 | let t = ((frameCount - 1) % 180) / 180; 10 | if (t == 0) { 11 | cLog = []; 12 | vLog[0] = []; 13 | vLog[1] = []; 14 | vLog[2] = []; 15 | } 16 | 17 | let s = height * 0.8; 18 | let lb = -s / 2; 19 | let rb = s / 2; 20 | let tb = -s / 2; 21 | let bb = s / 2; 22 | 23 | let cos120 = cos(PI / 3 * 2); 24 | let r = sqrt(s * s / (2 * (1 - cos120))); 25 | 26 | let rot = t * TWO_PI / 3 * 4; 27 | let p = []; 28 | let adjX = 0, adjY = 0; 29 | for (let i = 0; i < 3; i ++) { 30 | let a = rot + TWO_PI / 3 * i; 31 | p[i] = createVector(cos(a) * r, sin(a) * r); 32 | if (p[i].x + adjX < lb) { 33 | adjX = lb - p[i].x; 34 | } 35 | if (p[i].x + adjX > rb) { 36 | adjX = rb - p[i].x; 37 | } 38 | if (p[i].y + adjY < tb) { 39 | adjY = tb - p[i].y; 40 | } 41 | if (p[i].y + adjY > bb) { 42 | adjY = bb - p[i].y; 43 | } 44 | } 45 | for (let i = 0; i < 3; i ++) { 46 | p[i].x += adjX; 47 | p[i].y += adjY; 48 | vLog[i].push(p[i]); 49 | } 50 | 51 | push(); 52 | translate(width / 2, height / 2); 53 | 54 | noFill(); stroke(0); strokeWeight(1); 55 | rect(lb, tb, s, s); 56 | 57 | fill(255); 58 | let t0 = constrain(t * 8 - 1, 0, 1); 59 | for (let i = 0; i < 3; i ++) { 60 | let i1 = (i + 2) % 3; 61 | let pt = p[i].copy().lerp(p[i1], t0); 62 | line(p[i].x, p[i].y, pt.x, pt.y); 63 | drawCircleMarker(p[i], 4); 64 | } 65 | 66 | noFill(); 67 | strokeWeight(3); 68 | let ph = constrain(t * 8 - 2 , 0, 1); 69 | if (ph > 0) { 70 | let a = ph * PI / 3; 71 | drawArc(p[0], p[2], a); 72 | drawArc(p[1], p[0], a); 73 | drawArc(p[2], p[1], a); 74 | } 75 | pop(); 76 | } 77 | 78 | function drawArc(p0, p1, a) { 79 | let diameter = p0.dist(p1) * 2; 80 | let angle = atan2(p1.y - p0.y, p1.x - p0.x); 81 | arc(p0.x, p0.y, diameter, diameter, angle - a, angle); 82 | } 83 | 84 | /** drawCircleMarker **/ 85 | -------------------------------------------------------------------------------- /js/examples/physics/gravity.js: -------------------------------------------------------------------------------- 1 | let msec = 0, prevInitMsec = 0, prevNow = 0; 2 | let plotMode = -1, plotLabels = ["position(y)", "velocity(y)", "acceleration(y)"], plotStartMsec = 0, plotData = []; 3 | let gravity = 9.8 * 2; // 2px ~= 1m 4 | let apple; 5 | 6 | function setup() { 7 | canvas = createCanvas(windowWidth, windowHeight); 8 | apple = new Body(1); 9 | } 10 | 11 | function draw() { 12 | clear(); 13 | background(247, 227, 218); 14 | 15 | ellapseTime(); 16 | 17 | let minWH = min(width, height); 18 | push(); 19 | translate(min((width - minWH) / 2 + 32, width / 4) , 32); 20 | fill(250, 170, 165); stroke(0); 21 | ellipse(0, apple.position.y, 24, 24); 22 | line(0, apple.position.y - 8, 0, apple.position.y - 16); 23 | pop(); 24 | 25 | let graphW = minWH - 64; 26 | plotGraph(plotData, (width - graphW) / 2 + 32, 32, graphW, height - 64, 0, 8000, -(height - 64), 0, "time", plotLabels[plotMode]); 27 | } 28 | 29 | function ellapseTime() { 30 | let ellapsedMsec = min(100, window.performance.now() - prevNow); 31 | msec += ellapsedMsec; 32 | let secondsRemaining = ellapsedMsec / 1000; 33 | let maxStepSize = 1 / 60; 34 | while (secondsRemaining > 0) { 35 | let stepSize = secondsRemaining > maxStepSize ? maxStepSize : secondsRemaining; 36 | updateSimulation(stepSize); 37 | secondsRemaining -= maxStepSize; 38 | } 39 | 40 | if (msec > prevInitMsec + 8000) { 41 | apple = new Body(1); 42 | plotData = []; 43 | prevInitMsec += 8000; 44 | } 45 | 46 | if (plotData.length == 0) { 47 | plotStartMsec = msec; 48 | plotMode = (plotMode + 1) % 3; 49 | } 50 | 51 | let protMsec = msec - plotStartMsec; 52 | switch(plotMode) { 53 | case 0: plotData.push({x:protMsec, y: -apple.position.y}); break; 54 | case 1: plotData.push({x:protMsec, y: -apple.velocity.y}); break; 55 | case 2: plotData.push({x:protMsec, y: -getForce(apple).y}); break; 56 | } 57 | } 58 | 59 | function updateSimulation(t) { 60 | apple.applyForce(getForce(apple), t); 61 | apple.update(t); 62 | } 63 | 64 | function getForce(o) { 65 | return createVector(0, gravity).mult(o.mass) 66 | } 67 | 68 | /** plotGraph **/ 69 | 70 | /** Body **/ 71 | 72 | /** drawCircleMarker **/ 73 | 74 | /** drawLabel **/ 75 | -------------------------------------------------------------------------------- /extra/wave_function/index.js: -------------------------------------------------------------------------------- 1 | // ----------------------------------------------------------- 2 | let glShader = require('gl-shader'); 3 | let glFBO = require("gl-fbo"); 4 | let clear = require('gl-clear')({color: [0.5, 0.5, 0.5, 1.0]}); 5 | let glslify = require('glslify'); 6 | let fillScreen = require("a-big-triangle"); 7 | let now = require("gl-now"); 8 | // ----------------------------------------------------------- 9 | 10 | class glRenderer { 11 | constructor() { 12 | this.shell = now(); 13 | this.shell.on("gl-init", this.init.bind(this)); 14 | } 15 | 16 | init() { 17 | let gl = this.shell.gl; 18 | //this.shell.on("tick", this.update.bind(this)); 19 | this.shell.on("gl-render", this.draw.bind(this)); 20 | 21 | this.updateShader = glShader( gl, glslify( './shaders/flat.vert' ), glslify( './shaders/update.frag' ) ); 22 | this.renderShader = glShader( gl, glslify( './shaders/flat.vert' ), glslify( './shaders/render.frag' ) ); 23 | 24 | let w = 640, h = 640; 25 | this.fbos = [ glFBO(gl, [w,h]), glFBO(gl, [w,h]) ]; 26 | this.pingpongIndex = 0; 27 | this.frameCount = 0; 28 | 29 | this.fbos[0].bind(); 30 | clear(gl); 31 | this.fbos[1].bind(); 32 | clear(gl); 33 | } 34 | 35 | update() { 36 | // NULL 37 | } 38 | 39 | draw() { 40 | // OFF SCREEEN 41 | if (!this.updateShader) return; 42 | let gl = this.shell.gl; 43 | 44 | let prevFbo = this.fbos[this.pingpongIndex]; 45 | let currentFbo = this.fbos[this.pingpongIndex ^= 1]; 46 | 47 | currentFbo.bind(); 48 | this.updateShader.bind(); 49 | this.updateShader.uniforms.buffer = prevFbo.color[0].bind(0); 50 | this.updateShader.uniforms.frameCount = this.frameCount; 51 | this.updateShader.uniforms.dims = prevFbo.shape; 52 | fillScreen(gl); 53 | this.frameCount ++; 54 | 55 | // ON SCREEN 56 | 57 | gl.bindFramebuffer(gl.FRAMEBUFFER, null); 58 | if (!this.renderShader) return; 59 | 60 | this.renderShader.bind() 61 | this.renderShader.uniforms.buffer =this.fbos[this.pingpongIndex].color[0].bind(0); 62 | this.renderShader.uniforms.dims = [gl.drawingBufferWidth, gl.drawingBufferHeight]; 63 | fillScreen(gl); 64 | } 65 | } 66 | 67 | let renderer = new glRenderer(); 68 | -------------------------------------------------------------------------------- /js/examples/drawings/bezier.js: -------------------------------------------------------------------------------- 1 | let log = []; 2 | let pDef = [[-1, 1], [-0.5, -1], [0.5, 1], [1, -1]] ; 3 | let p = []; 4 | 5 | function setup() { 6 | canvas = createCanvas(windowWidth, windowHeight); 7 | 8 | for (let i = 0; i < pDef.length; i ++) { 9 | p.push(createVector(pDef[i][0], pDef[i][1])); 10 | } 11 | } 12 | 13 | function draw() { 14 | clear(); 15 | background(15, 243, 208); 16 | 17 | let interval = 30; 18 | let duration = 120; 19 | let len = interval + duration; 20 | let f = frameCount - 1; 21 | if (f % len == 0) { log = []; } 22 | let t = constrain((f % len - interval / 2) / duration, 0, 1); 23 | 24 | fill(0); stroke(0); 25 | let s = createVector(width * 0.3, height * 0.2); 26 | 27 | push(); 28 | translate(width / 2, height / 2); 29 | 30 | for (let i = 0; i < p.length; i ++) { 31 | drawCircleMarker(createVector(p[i].x * s.x, p[i].y * s.y), 3); 32 | drawLabel(p[i].x * s.x, p[i].y * s.y + 24, "P" + i); 33 | if (i > 0) { 34 | line(p[i - 1].x * s.x, p[i - 1].y * s.y, p[i].x * s.x, p[i].y * s.y); 35 | } 36 | } 37 | 38 | tp = []; 39 | tp.push(p[0].copy().lerp(p[1], t)); 40 | tp.push(p[1].copy().lerp(p[2], t)); 41 | tp.push(p[2].copy().lerp(p[3], t)); 42 | tp.push(tp[0].copy().lerp(tp[1], t)); 43 | tp.push(tp[1].copy().lerp(tp[2], t)); 44 | tp.push(tp[3].copy().lerp(tp[4], t)); 45 | log.push(tp[5]); 46 | 47 | for (let i = 0; i < tp.length; i ++) { 48 | drawCircleMarker(createVector(tp[i].x * s.x, tp[i].y * s.y), 3); 49 | } 50 | 51 | line(tp[0].x * s.x, tp[0].y * s.y, tp[1].x * s.x, tp[1].y * s.y); 52 | line(tp[1].x * s.x, tp[1].y * s.y, tp[2].x * s.x, tp[2].y * s.y); 53 | line(tp[3].x * s.x, tp[3].y * s.y, tp[4].x * s.x, tp[4].y * s.y); 54 | 55 | 56 | let pt = p[0].copy().mult((1 - t) * (1 - t) * (1 - t)) 57 | .add(p[1].copy().mult(3 * (1 - t) * (1 - t) * t)) 58 | .add(p[2].copy().mult(3 * (1 - t) * t * t)) 59 | .add(p[3].copy().mult(t * t * t)); 60 | 61 | noFill(); 62 | drawCircleMarker(createVector(pt.x * s.x, pt.y * s.y), 8); 63 | 64 | strokeWeight(2); 65 | beginShape(); 66 | for (let i = 0; i < log.length; i ++) { 67 | vertex(log[i].x * s.x, log[i].y * s.y); 68 | } 69 | endShape(); 70 | 71 | pop(); 72 | } 73 | 74 | /** drawLabel **/ 75 | 76 | /** drawCircleMarker **/ 77 | -------------------------------------------------------------------------------- /js/examples/wave/simple_harmonic.js: -------------------------------------------------------------------------------- 1 | let msec = 0, prevInitMsec = 0, prevNow = 0; 2 | let plotData = []; 3 | let tention = 1, friction = 0.0; 4 | let ball; 5 | 6 | function setup() { 7 | canvas = createCanvas(windowWidth, windowHeight); 8 | ball = new Body(1); 9 | ball.position.y = 100; 10 | } 11 | 12 | function draw() { 13 | clear(); 14 | background(224, 239, 243); 15 | 16 | ellapseTime(); 17 | 18 | push(); 19 | translate(32, height / 2); 20 | fill(250, 170, 165); stroke(0); 21 | ellipse(0, ball.position.y, 24, 24); 22 | 23 | noFill(); stroke(0); 24 | 25 | let springEndY = -height / 2; 26 | let len = height / 2 + ball.position.y - 12; 27 | beginShape(); 28 | for (let i = 0; i <= 72; i ++) { 29 | let angle = i / 2 * PI; 30 | let x = sin(angle) * 6; 31 | let y = springEndY + len / 72 * i; 32 | vertex(x, y); 33 | } 34 | endShape(); 35 | pop(); 36 | 37 | let graphW = width - 64; 38 | let graphX = 64; 39 | plotGraph(plotData, graphX, height / 2, graphW, height - 64, 0, 20 * graphW, -height / 2 + 32, height / 2 - 32, "time", "y"); 40 | } 41 | 42 | function ellapseTime() { 43 | let ellapsedMsec = min(100, window.performance.now() - prevNow); 44 | msec += ellapsedMsec; 45 | let secondsRemaining = ellapsedMsec / 1000; 46 | let maxStepSize = 1 / 60; 47 | while (secondsRemaining > 0) { 48 | let stepSize = secondsRemaining > maxStepSize ? maxStepSize : secondsRemaining; 49 | updateSimulation(stepSize); 50 | secondsRemaining -= maxStepSize; 51 | } 52 | 53 | if (msec > prevInitMsec + 20 * (width - 64)) { 54 | ball = new Body(1); 55 | ball.position.y = 100; 56 | plotData = []; 57 | prevInitMsec += 20 * (width - 64); 58 | } 59 | 60 | if (plotData.length == 0) { 61 | plotStartMsec = msec; 62 | } 63 | 64 | let plotMsec = (msec - plotStartMsec); 65 | plotData.push({x:plotMsec, y: -ball.position.y}); 66 | } 67 | 68 | function updateSimulation(t) { 69 | ball.applyForce(getForce(ball), t); 70 | ball.update(t); 71 | } 72 | 73 | function getForce(o) { 74 | let springForce = o.position.copy().mult(-tention); 75 | let frictionForce = o.velocity.copy().mult(-friction); 76 | return springForce.add(frictionForce); 77 | } 78 | 79 | /** plotGraph **/ 80 | 81 | /** Body **/ 82 | 83 | /** drawCircleMarker **/ 84 | 85 | /** drawLabel **/ 86 | -------------------------------------------------------------------------------- /js/examples/easing/composite02.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | let msec = 0, prevNow = 0; 4 | const duration0 = 900, duration1 = 1200, duration2 = 3000; 5 | 6 | function setup() { 7 | canvas = createCanvas(windowWidth, windowHeight); 8 | plotData = []; 9 | tween = new Tween(); 10 | console.log(tween.sinInOut) 11 | tweens = [ 12 | (t)=>{return recprocate(t, tween.sineInOut)}, 13 | (t)=>{return recprocate(t, tween.sineInOut)}, 14 | (t)=>{return recprocate(t, tween.sineInOut)} 15 | ] 16 | } 17 | 18 | function draw() { 19 | clear(); 20 | background(240, 240, 30); 21 | 22 | const t0 = (msec % duration0) / duration0; 23 | const t1 = (msec % duration1) / duration1; 24 | const t2 = (msec % duration2) / duration2; 25 | 26 | const hw = width / 2; 27 | const hh = height / 2; 28 | const p0 = createVector(-160, -160); 29 | const p1 = createVector(160, -160); 30 | const p2 = createVector(-160, 160); 31 | const p3 = createVector(160, 160); 32 | let p0_1 = p0.copy().lerp(p1, tweens[0](t0)); 33 | let p2_3 = p2.copy().lerp(p3, tweens[1](t1)); 34 | let p = p0_1.copy().lerp(p2_3, tweens[2](t2)); 35 | plotData.push(p); 36 | if (plotData.length > 40) { 37 | plotData.shift(); 38 | } 39 | 40 | push(); 41 | translate(hw, hh); 42 | noFill(); stroke(0); 43 | line(p0.x, p0.y, p1.x, p1.y); 44 | line(p2.x, p2.y, p3.x, p3.y); 45 | line(p0_1.x, p0_1.y, p2_3.x, p2_3.y); 46 | 47 | for (let i = 1; i < plotData.length; i ++) { 48 | line(plotData[i - 1].x, plotData[i - 1].y, plotData[i].x, plotData[i].y); 49 | } 50 | 51 | fill(0); noStroke(); 52 | drawCircleMarker(p0, 4); 53 | drawCircleMarker(p1, 4); 54 | drawCircleMarker(p2, 4); 55 | drawCircleMarker(p3, 4); 56 | fill(255); stroke(0); 57 | drawCircleMarker(p0_1, 4); 58 | drawCircleMarker(p2_3, 4); 59 | drawCircleMarker(p, 4); 60 | 61 | pop(); 62 | ellapseTime(); 63 | } 64 | 65 | 66 | function recprocate(t, f) { 67 | if (t < 0.5) { 68 | return f(t * 2); 69 | } else { 70 | return 1 - f((t - 0.5) * 2); 71 | } 72 | } 73 | 74 | function ellapseTime() { 75 | msec += min(100, window.performance.now() - prevNow); 76 | prevNow = window.performance.now(); 77 | } 78 | 79 | /** drawCircleMarker **/ 80 | 81 | /** Tween **/ 82 | -------------------------------------------------------------------------------- /js/examples/drawings/circumcenter.js: -------------------------------------------------------------------------------- 1 | function setup() { 2 | canvas = createCanvas(windowWidth, windowHeight); 3 | } 4 | 5 | function draw() { 6 | clear(); 7 | background(62, 252, 100); 8 | let boundary = {x: -width / 2, y: -height / 2, w: width, h: height}; 9 | 10 | let t = (frameCount % 720) / 720; 11 | let a0 = PI / 3 * cos(t * TWO_PI) + 0.01 + TWO_PI / 3 ; 12 | let a1 = PI / 3 * cos(t * TWO_PI * 3) + 0.02 + TWO_PI / 3 * 2; 13 | let a2 = PI / 3 * cos(t * TWO_PI * 5) + 0.03; 14 | 15 | let p0 = createVector(cos(a0), sin(a0)).mult(120); 16 | let p1 = createVector(cos(a1), sin(a1)).mult(140); 17 | let p2 = createVector(cos(a2), sin(a2)).mult(160); 18 | 19 | let s0 = new LineSegment().fromTwoPoints(p0, p1); 20 | let s1 = new LineSegment().fromTwoPoints(p1, p2); 21 | let s2 = new LineSegment().fromTwoPoints(p2, p0); 22 | 23 | let mp0 = s0.getMidPoint(); 24 | let mp1 = s1.getMidPoint(); 25 | let mp2 = s2.getMidPoint(); 26 | 27 | let bs0 = s0.getBisection(); 28 | let bs1 = s1.getBisection(); 29 | let bs2 = s2.getBisection(); 30 | 31 | let circumcenter = bs0.getIntersectionPoint(bs1); 32 | let circumcircle = new Circle().fromCenterAndPoint(circumcenter, p0); 33 | 34 | push(); 35 | translate(width / 2, height / 2); 36 | fill(0); stroke(0); strokeWeight(3); 37 | s0.draw(); 38 | s1.draw(); 39 | s2.draw(); 40 | strokeWeight(1); 41 | 42 | bs0.draw(boundary); 43 | bs1.draw(boundary); 44 | bs2.draw(boundary); 45 | 46 | noFill(); 47 | drawSquareMark(s0.toLine(), bs0); 48 | drawSquareMark(s1.toLine(), bs1); 49 | drawSquareMark(s2.toLine(), bs2); 50 | 51 | fill(255); 52 | drawCircleMarker(mp0, 3); 53 | drawCircleMarker(mp1, 3); 54 | drawCircleMarker(mp2, 3); 55 | 56 | if (circumcenter) { 57 | noFill(); stroke(0); strokeWeight(1); 58 | circumcircle.draw(); 59 | fill(255); 60 | drawCircleMarker(circumcenter, 4); 61 | } 62 | 63 | fill(255); 64 | drawCircleMarker(p0, 3); 65 | drawCircleMarker(p1, 3); 66 | drawCircleMarker(p2, 3); 67 | 68 | pop(); 69 | } 70 | 71 | function drawSquareMark(l0, l1) { 72 | let p = l0.getIntersectionPoint(l1); 73 | push(); 74 | translate(p.x, p.y); 75 | rotate(l0.getAngle()); 76 | rect(0,0,8,8); 77 | pop(); 78 | } 79 | 80 | /** Line **/ 81 | 82 | /** LineSegment **/ 83 | 84 | /** Circle **/ 85 | 86 | /** drawCircleMarker **/ 87 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Sketching with Math and Quasi Physics 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 |

Sketching with Math and Quasi Physics

16 | 17 |

There is a newer and more elaborate version of this site.
Visit Sketching with Math and Quasi Physics on Notion.

18 |

Index

19 |
    20 |
  1. Handy Math
  2. 21 |
  3. Taming Randomness
  4. 22 |
  5. Hello Isaac
  6. 23 |
  7. Distance and Shape
  8. 24 |
  9. Geometric Drawings
  10. 25 |
  11. Mechanical Movement
  12. 26 |
  13. Wave Anatomy
  14. 27 |
  15. Pitch and Frequency
  16. 28 |
  17. Easing
  18. 29 |
  19. Detecting Collision
  20. 30 |
  21. Path of Light
  22. 31 |
  23. Building Ragdolls
  24. 32 |
33 | 34 |
35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /js/examples/light_path/reflection.js: -------------------------------------------------------------------------------- 1 | let mirror; 2 | 3 | function setup() { 4 | canvas = createCanvas(windowWidth, windowHeight); 5 | mirror = new Line(); 6 | } 7 | 8 | function reflect(i, n) { 9 | return i.copy().add(n.copy().mult(i.copy().mult(2).dot(n) / pow(n.mag(), 2) * -1)); 10 | } 11 | 12 | function draw() { 13 | clear(); 14 | background(255, 235, 200); 15 | const boundary = {x: -width / 2, y: -height / 2, w: width, h: height}; 16 | 17 | const mx = cos(frameCount / 120 * PI + PI * 0.251); 18 | const my = 1; 19 | mirror.fromTwoPoints({x:0, y: 0}, {x: mx, y: my}); 20 | 21 | const ty = sin(frameCount / 90 * PI) * height / 4; 22 | const ray = new Line().fromTwoPoints({x: -width / 2, y:0}, {x: 0, y: ty}); 23 | const rayVec = createVector(width / 2, ty).normalize(); 24 | 25 | const intersection = mirror.getIntersectionPoint(ray); 26 | const normal = createVector(-my, mx); 27 | if (normal.dot(rayVec) > 0) { normal.mult(-1);} 28 | const reflectionVec = reflect(rayVec, normal); 29 | 30 | const rayHead = intersection.copy().add(rayVec.copy().mult(-32)); 31 | const rayTail = intersection.copy().add(rayVec.copy().mult(-56)); 32 | const normalHead = intersection.copy().add(normal.copy().mult(24)); 33 | const reflectionHead = intersection.copy().add(reflectionVec.copy().mult(56)); 34 | const reflectionTail = intersection.copy().add(reflectionVec.copy().mult(32)); 35 | const reflectionLineEnd = intersection.copy().add(reflectionVec.copy().mult(width)); 36 | 37 | push(); 38 | 39 | fill(255); stroke(0); strokeWeight(1); 40 | translate(width / 2, height / 2); 41 | line(-width / 2, 0, intersection.x, intersection.y); 42 | line(intersection.x, intersection.y, reflectionLineEnd.x, reflectionLineEnd.y); 43 | 44 | strokeWeight(2); 45 | mirror.draw(boundary); 46 | drawArrow(intersection.x, intersection.y, normalHead.x, normalHead.y); 47 | strokeWeight(4); 48 | drawArrow(rayTail.x, rayTail.y, rayHead.x, rayHead.y); 49 | drawArrow(reflectionTail.x, reflectionTail.y, reflectionHead.x, reflectionHead.y); 50 | 51 | fill(0); noStroke(0); 52 | drawLabel(rayHead.x + rayVec.x * 16, rayHead.y + rayVec. y * 16, "i"); 53 | drawLabel(reflectionHead.x + reflectionVec.x * 16, reflectionHead.y + reflectionVec.y * 16, "r"); 54 | drawLabel(normalHead.x + normal.x * 16, normalHead.y + normal.y * 16, "n"); 55 | 56 | pop(); 57 | } 58 | 59 | /** Line **/ 60 | 61 | /** drawArrow **/ 62 | 63 | /** drawLabel **/ 64 | 65 | -------------------------------------------------------------------------------- /js/examples/sound/equal_temperament.js: -------------------------------------------------------------------------------- 1 | let audioCtx; 2 | let prevClickFrame = -999; 3 | let buffer_size = 16384; 4 | let baseFreq = 440.0; 5 | let pitch = 0; 6 | let gain = 0.1; 7 | let phase = 0; 8 | let labels = ["A", "A#", "B", "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A(Octave)"]; 9 | 10 | function setup() { 11 | canvas = createCanvas(windowWidth, windowHeight); 12 | canvas.mousePressed(function() { toggleAudio(); }); 13 | canvas.touchStarted(function() { toggleAudio(); }); 14 | } 15 | 16 | function draw() { 17 | clear(); 18 | background(0, 222, 201); 19 | let freq = Math.pow(2.0, pitch / 12.0) * baseFreq; 20 | 21 | if (frameCount % 100 == 0) { 22 | pitch = (pitch + 1) % 13; 23 | } 24 | 25 | push(); 26 | translate(0, height / 2); 27 | noFill(); stroke(0); 28 | beginShape(); 29 | for (let i = 0; i <= width; i ++) { 30 | let ph = i / width * freq / 100; 31 | vertex(i, f(ph) * height * 0.3); 32 | } 33 | endShape(); 34 | pop(); 35 | 36 | stroke(0, 0, 0, 64); 37 | for (let i = 0; i < 10; i ++) { 38 | let x = i * width / 10; 39 | if (i != 0) { line(x, 0, x, height); } 40 | drawLabel(x, height - 16, i + "ms", LEFT); 41 | } 42 | 43 | drawLabel(8, 32, labels[pitch] + " = " + freq.toPrecision(4) + "Hz", LEFT); 44 | drawLabel(width - 8, 32, "Click to turn on/off the sound", RIGHT); 45 | } 46 | 47 | function f(phase) { 48 | return Math.sin(phase * 3.141592 * 2); 49 | } 50 | 51 | function audiohandler(event) { 52 | let outL = event.outputBuffer.getChannelData(0); 53 | let outR = event.outputBuffer.getChannelData(1); 54 | let acc = Math.pow(2.0, pitch / 12.0) * baseFreq / audioCtx.sampleRate; 55 | for(let i = 0 ; i < outL.length; i++) { 56 | phase += acc; 57 | let out = f(phase); 58 | outL[i] = outR[i] = out * gain; 59 | } 60 | } 61 | 62 | function toggleAudio() { 63 | if (prevClickFrame + 10 >= frameCount) { 64 | return; 65 | } 66 | prevClickFrame = frameCount; 67 | 68 | if (audioCtx) { 69 | stopAudio(); 70 | } else { 71 | initAudio(); 72 | } 73 | } 74 | 75 | function initAudio() { 76 | if (!audioCtx) { 77 | audioCtx = new (window.webkitAudioContext || window.AudioContext); 78 | let scriptproc = audioCtx.createScriptProcessor(buffer_size); 79 | scriptproc.connect(audioCtx.destination); 80 | scriptproc.onaudioprocess = audiohandler; 81 | } 82 | } 83 | 84 | function stopAudio() { 85 | if (audioCtx) { 86 | audioCtx.close(); 87 | audioCtx = null; 88 | } 89 | } 90 | 91 | /** drawLabel **/ 92 | -------------------------------------------------------------------------------- /js/examples/easing/tweens.js: -------------------------------------------------------------------------------- 1 | let msec = 0, prevNow = 0; 2 | let tween, tweens; 3 | function setup() { 4 | canvas = createCanvas(windowWidth, windowHeight); 5 | tween = new Tween(); 6 | tweens = [ 7 | {func:tween.linear , label: "linear"}, 8 | {func:(t)=>{return tween.powerIn(t,2);} , label: "powerIn(2)"}, 9 | {func:(t)=>{return tween.powerOut(t,2);} , label: "powerOut(2)"}, 10 | {func:(t)=>{return tween.powerInOut(t,2);} , label: "powerInOut(2)"}, 11 | {func:(t)=>{return tween.powerIn(t,3);} , label: "powerIn(3)"}, 12 | {func:(t)=>{return tween.powerOut(t,3);} , label: "powerOut(3)"}, 13 | {func:(t)=>{return tween.powerInOut(t,3);} , label: "powerInOut(3)"}, 14 | {func:tween.sineIn , label: "sineIn"}, 15 | {func:tween.sineOut , label: "sineOut"}, 16 | {func:tween.circularIn , label: "circularIn"}, 17 | {func:tween.circularOut , label: "circularOut"}, 18 | {func:tween.circularInOut , label: "circularInOut"}, 19 | {func:tween.createCubicBezier({x:0.4, y: 0.0}, {x:0.2, y: 1}), label:"bezier([0.2,0.8],[0.6,0.4])"} 20 | ]; 21 | 22 | } 23 | 24 | function draw() { 25 | clear(); 26 | background(29, 223, 182); 27 | ellapseTime(); 28 | 29 | let duration = 1000, interval = 500; 30 | let t = (msec % (duration + interval) - interval / 2) / duration; 31 | let i = Math.floor(msec / (duration + interval)) % tweens.length; 32 | t = Math.min(1, Math.max(0, t)); 33 | 34 | push(); 35 | translate(width / 2, height / 2); 36 | plot(0, 0, tweens[i].func, tweens[i].label, t); 37 | pop(); 38 | } 39 | 40 | function ellapseTime() { 41 | msec += min(100, window.performance.now() - prevNow); 42 | prevNow = window.performance.now(); 43 | } 44 | 45 | function plot(cx, cy, func, label, t) { 46 | let w = height * 0.8, h = height * 0.8; 47 | let ox = -w / 2, oy = h / 2; 48 | let resolution = w / 2; 49 | noFill(); stroke(0); 50 | 51 | beginShape(); 52 | for (let i = 0; i <= resolution; i ++) { 53 | let x = i / resolution; 54 | vertex(ox + w * x, oy - h * func(x)); 55 | } 56 | endShape(); 57 | 58 | let x = ox + w * t, y = oy - h * func(t); 59 | rect(ox, oy - h, w, h); 60 | line(ox, y, ox + w, y); 61 | line(x, oy, x, oy - h); 62 | fill(255); stroke(0); 63 | 64 | drawCircleMarker(createVector(x, y), 4); 65 | fill(0); noStroke(0); 66 | drawLabel(ox, oy + 20, label, LEFT); 67 | drawLabel(ox + w, oy + 20, "t", RIGHT); 68 | drawLabel(ox , oy - h + 16, "f(t)", RIGHT); 69 | } 70 | 71 | /** Tween **/ 72 | 73 | /** drawCircleMarker **/ 74 | 75 | /** drawLabel **/ 76 | -------------------------------------------------------------------------------- /js/examples/sound/just_intonation.js: -------------------------------------------------------------------------------- 1 | let audioCtx; 2 | let prevClickFrame = -999; 3 | let buffer_size = 16384; 4 | let baseFreq = 440.0; 5 | let note = 0; 6 | let gain = 0.1; 7 | let phase = 0; 8 | let ratios = [1, 9/8, 5/4, 4/3, 3/2, 5/3, 15/8, 2]; 9 | let labels = ["A", "B(M2 9/8)", "C#(M3 5/4)", "D(P4 4/3)", "E(P5 3/2)", "F#(M6 5/3)", "G#(M7 15/8)", "A(Octave 2/1)"]; 10 | 11 | function setup() { 12 | canvas = createCanvas(windowWidth, windowHeight); 13 | canvas.mousePressed(function() { toggleAudio(); }); 14 | canvas.touchStarted(function() { toggleAudio(); }); 15 | } 16 | 17 | function draw() { 18 | clear(); 19 | background(0, 222, 201); 20 | let freq = ratios[note] * baseFreq; 21 | 22 | if (frameCount % 100 == 0) { 23 | note = (note + 1) % 8; 24 | } 25 | 26 | push(); 27 | translate(0, height / 2); 28 | noFill(); stroke(0); 29 | beginShape(); 30 | for (let i = 0; i <= width; i ++) { 31 | let ph = i / width * freq / 100; 32 | vertex(i, f(ph) * height * 0.3); 33 | } 34 | endShape(); 35 | pop(); 36 | 37 | stroke(0, 0, 0, 64); 38 | for (let i = 0; i < 10; i ++) { 39 | let x = i * width / 10; 40 | if (i != 0) { line(x, 0, x, height); } 41 | drawLabel(x, height - 16, i + "ms", LEFT); 42 | } 43 | 44 | drawLabel(8, 32, labels[note] + " = " + freq.toPrecision(4) + "Hz", LEFT); 45 | drawLabel(width - 8, 32, "Click to turn on/off the sound", RIGHT); 46 | } 47 | 48 | function f(phase) { 49 | return Math.sin(phase * 3.141592 * 2); 50 | } 51 | 52 | function audiohandler(event) { 53 | let outL = event.outputBuffer.getChannelData(0); 54 | let outR = event.outputBuffer.getChannelData(1); 55 | let acc = ratios[note] * baseFreq / audioCtx.sampleRate; 56 | for(let i = 0 ; i < outL.length; i++) { 57 | phase += acc; 58 | let out = f(phase); 59 | outL[i] = outR[i] = out * gain; 60 | } 61 | } 62 | 63 | function toggleAudio() { 64 | if (prevClickFrame + 10 >= frameCount) { 65 | return; 66 | } 67 | prevClickFrame = frameCount; 68 | 69 | if (audioCtx) { 70 | stopAudio(); 71 | } else { 72 | initAudio(); 73 | } 74 | } 75 | 76 | function initAudio() { 77 | if (!audioCtx) { 78 | audioCtx = new (window.webkitAudioContext || window.AudioContext); 79 | let scriptproc = audioCtx.createScriptProcessor(buffer_size); 80 | scriptproc.connect(audioCtx.destination); 81 | scriptproc.onaudioprocess = audiohandler; 82 | } 83 | } 84 | 85 | function stopAudio() { 86 | if (audioCtx) { 87 | audioCtx.close(); 88 | audioCtx = null; 89 | } 90 | } 91 | 92 | /** drawLabel **/ 93 | -------------------------------------------------------------------------------- /js/examples/random/random_walk.js: -------------------------------------------------------------------------------- 1 | let walkers = []; 2 | 3 | class RandomWalker { 4 | constructor(x, y, randomFunc, renderFunc, label) { 5 | this.x = x; 6 | this.y = y; 7 | this.label = label; 8 | this.random = randomFunc; 9 | this.render = renderFunc; 10 | this.log = []; 11 | } 12 | 13 | update() { 14 | this.x = constrain(this.x + this.random() * 2, 0, width); 15 | this.y = constrain(this.y + this.random() * 2, 0, height); 16 | this.log.push(createVector(this.x, this.y)); 17 | if (this.log.length > 100) { 18 | this.log.shift(); 19 | } 20 | } 21 | 22 | draw() { 23 | push(); 24 | fill(0); noStroke(); 25 | this.render(createVector(this.x, this.y), 8); 26 | drawLabel(this.x + ((this.x < width / 2) ? 8 : -8), this.y + 16, 27 | this.label, (this.x < width / 2) ? LEFT : RIGHT); 28 | noFill(); stroke(0); 29 | beginShape(); 30 | for (let i = 0; i < this.log.length; i ++) { 31 | vertex(this.log[i].x, this.log[i].y); 32 | } 33 | endShape(); 34 | stroke(0); 35 | line(this.x, 0, this.x, height); 36 | line(0, this.y, width, this.y); 37 | pop(); 38 | } 39 | } 40 | 41 | function setup() { 42 | canvas = createCanvas(windowWidth, windowHeight); 43 | walkers.push(new RandomWalker(random(0, width), random(0, height), uniform(), drawCircleMarker, "uniform")); 44 | walkers.push(new RandomWalker(random(0, width), random(0, height), perlin(), drawTriangleMarker, "perlin")); 45 | walkers.push(new RandomWalker(random(0, width), random(0, height), gaussian(), drawSquareMarker, "gaussian")); 46 | walkers.push(new RandomWalker(random(0, width), random(0, height), gaussian_perlin(), drawStarMarker, "gaussian + perlin")); 47 | } 48 | 49 | function draw() { 50 | clear(); 51 | background(254, 253, 183); 52 | 53 | let v; 54 | for (let i = 0; i < walkers.length; i ++) { 55 | walkers[i].update(); 56 | } 57 | 58 | for (let i = 0; i < walkers.length; i ++) { 59 | walkers[i].draw(); 60 | } 61 | } 62 | 63 | function uniform() { 64 | return ()=>{return random(-1, 1);} 65 | } 66 | 67 | function perlin() { 68 | let i = 0, n = random(100); 69 | return ()=>{ 70 | n += 0.01; 71 | i = (i + 1) % 2; 72 | return (noise(i + n) - 0.5) * 2; 73 | } 74 | } 75 | 76 | function gaussian() { 77 | return ()=>{return randomGaussian(0,1);} 78 | } 79 | 80 | function gaussian_perlin() { 81 | let g = gaussian(); 82 | let p = perlin(); 83 | return ()=>{return (g() * 0.2 + p() * 0.8);} 84 | } 85 | 86 | /** drawLabel **/ 87 | 88 | /** drawCircleMarker **/ 89 | 90 | /** drawSquareMarker **/ 91 | 92 | /** drawTriangleMarker **/ 93 | 94 | /** drawStarMarker **/ 95 | -------------------------------------------------------------------------------- /easing.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Easing 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 |

Easing

16 |

Sketching with Math and Quasi Physics

17 |

There is a newer and more elaborate version of this site.
Visit Sketching with Math and Quasi Physics on Notion.

18 |

Tween functions(1)

19 |
20 | 21 |

Tween functions(2)

22 |
23 | 24 |

Learn more

25 |

Robert Penner's Easing Functions

26 | 27 |

Colliding

28 |
29 | 30 |

Bouncing

31 |
32 | 33 |

Composite motion(1)

34 |
35 |

See also

36 |

Bézier Curve

37 | 38 |

Composite motion(2)

39 |
40 | 41 |

Sequenced Actors

42 |
43 | 44 |

index

45 | 46 |
47 | 48 | 49 | 50 | 51 | 52 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /physics.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Hello Isaac 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 |

Hello Isaac

16 |

Sketching with Math and Quasi Physics

17 |

There is a newer and more elaborate version of this site.
Visit Sketching with Math and Quasi Physics on Notion.

18 | 19 |

Position

20 |
21 | 22 |

Velocity

23 |
24 | 25 |

Acceleration

26 |
27 | 28 | 29 |

Gravity (Force, Acceleration, Velocity, Position)

30 |
31 | 32 |

Orbit

33 |
34 | 35 |

Spring (Tension, Friction)

36 |
37 | 38 | 39 |

Learn more

40 |

41 | The Nature of code - Chapter 1. Vectors

42 |

43 | The Nature of code - Chapter 2. Forces

44 | 45 | 46 |
47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /js/examples/light_path/parabola.js: -------------------------------------------------------------------------------- 1 | let points = [], focusPoint; 2 | 3 | function setup() { 4 | canvas = createCanvas(windowWidth, windowHeight); 5 | for (let y = -height / 1.8; y < height / 1.8; y += height / 40) { 6 | points.push(createVector(f(y), y)); 7 | } 8 | } 9 | 10 | function f(y) { 11 | return -pow(y * 0.1, 2); 12 | } 13 | 14 | function n(y) { 15 | return -2 * y * 0.01; 16 | } 17 | 18 | function reflect(i, n) { 19 | return i.copy().add(n.copy().mult(i.copy().mult(2).dot(n) / pow(n.mag(), 2) * -1)); 20 | } 21 | 22 | function draw() { 23 | clear(); 24 | background(255, 235, 200); 25 | 26 | const left = - width / 4 * 3; 27 | const boundary = {x: left, y: -height / 2, w: width, h: height}; 28 | 29 | const y = ((frameCount % 120) / 120 - 0.5) * height; 30 | const x = f(y); 31 | 32 | const intersection = createVector(x, y); 33 | 34 | const normal = createVector(-1, n(y)).normalize(); 35 | const rayVec = createVector(1, 0); 36 | const reflectionVec = reflect(rayVec, normal); 37 | 38 | 39 | 40 | 41 | const rayHead = intersection.copy().add(rayVec.copy().mult(-32)); 42 | const rayTail = intersection.copy().add(rayVec.copy().mult(-56)); 43 | const normalHead = intersection.copy().add(normal.copy().mult(24)); 44 | const reflectionHead = intersection.copy().add(reflectionVec.copy().mult(56)); 45 | const reflectionTail = intersection.copy().add(reflectionVec.copy().mult(32)); 46 | const reflectionLineEnd = intersection.copy().add(reflectionVec.copy().mult(width)); 47 | 48 | const centerLine = new Line().fromTwoPoints({x:0, y:0}, {x:1, y:0}); 49 | const reflectionLine = new Line().fromTwoPoints(intersection, intersection.copy().add(reflectionVec)); 50 | const focus = centerLine.getIntersectionPoint(reflectionLine); 51 | if (focus) { 52 | focusPoint = focus; 53 | } 54 | 55 | fill(255); stroke(0); strokeWeight(1); 56 | push(); 57 | translate(-left, height / 2); 58 | for (let i = 1; i < points.length; i ++) { 59 | line(points[i].x, points[i].y, points[i - 1].x, points[i - 1].y); 60 | } 61 | line(left, y, x, y); 62 | centerLine.draw(boundary); 63 | centerLine.draw(reflectionLine); 64 | line(intersection.x, intersection.y, reflectionLineEnd.x, reflectionLineEnd.y); 65 | 66 | strokeWeight(2); 67 | drawArrow(intersection.x, intersection.y, normalHead.x, normalHead.y); 68 | strokeWeight(4); 69 | drawArrow(rayTail.x, rayTail.y, rayHead.x, rayHead.y); 70 | drawArrow(reflectionTail.x, reflectionTail.y, reflectionHead.x, reflectionHead.y); 71 | 72 | fill(0); noStroke(0); 73 | drawCircleMarker(focusPoint, 4); 74 | pop(); 75 | } 76 | 77 | /** Line **/ 78 | 79 | /** drawCircleMarker **/ 80 | 81 | /** drawArrow **/ 82 | 83 | /** drawLabel **/ -------------------------------------------------------------------------------- /js/examples/light_path/refraction.js: -------------------------------------------------------------------------------- 1 | let IOR = 1; 2 | 3 | function setup() { 4 | canvas = createCanvas(windowWidth, windowHeight); 5 | } 6 | 7 | function refract(i, n, ior) { 8 | const cosi = -i.copy().dot(n); 9 | const eta = 1 / ior; 10 | const k = 1 - eta * eta * (1 - cosi * cosi); 11 | return t = i.copy().mult(eta).add( 12 | n.copy().mult(eta * cosi - sqrt(k)) 13 | ); 14 | } 15 | 16 | 17 | function draw() { 18 | clear(); 19 | background(255, 235, 200); 20 | 21 | if (frameCount % 60 == 0) { 22 | IOR = 1 + (frameCount % 600) / 600; 23 | } 24 | 25 | const boundary = {x: -width / 2, y: -height / 2, w: width, h: height}; 26 | 27 | const mx = cos(frameCount / 120 * PI + PI * 0.251); 28 | const my = 1; 29 | const border = new Line().fromTwoPoints({x:0, y: 0}, {x: mx, y: my}); 30 | 31 | const ty = sin(frameCount / 90 * PI) * height / 4; 32 | const ray = new Line().fromTwoPoints({x: -width / 2, y:0}, {x: 0, y: ty}); 33 | const rayVec = createVector(width / 2, ty).normalize(); 34 | 35 | const intersection = border.getIntersectionPoint(ray); 36 | const normal = createVector(-my, mx).normalize(); 37 | if (normal.dot(rayVec) > 0) { normal.mult(-1);} 38 | const refractionVec = refract(rayVec, normal, IOR); 39 | 40 | const rayHead = intersection.copy().add(rayVec.copy().mult(-32)); 41 | const rayTail = intersection.copy().add(rayVec.copy().mult(-56)); 42 | const normalHead = intersection.copy().add(normal.copy().mult(24)); 43 | const refractionHead = intersection.copy().add(refractionVec.copy().mult(56)); 44 | const refractionTail = intersection.copy().add(refractionVec.copy().mult(32)); 45 | const refractionLineEnd = intersection.copy().add(refractionVec.copy().mult(width)); 46 | 47 | push(); 48 | 49 | fill(255); stroke(0); strokeWeight(1); 50 | translate(width / 2, height / 2); 51 | line(-width / 2, 0, intersection.x, intersection.y); 52 | line(intersection.x, intersection.y, refractionLineEnd.x, refractionLineEnd.y); 53 | 54 | strokeWeight(2); 55 | border.draw(boundary); 56 | drawArrow(intersection.x, intersection.y, normalHead.x, normalHead.y); 57 | strokeWeight(4); 58 | drawArrow(rayTail.x, rayTail.y, rayHead.x, rayHead.y); 59 | drawArrow(refractionTail.x, refractionTail.y, refractionHead.x, refractionHead.y); 60 | 61 | fill(0); noStroke(0); 62 | drawLabel(rayHead.x + rayVec.x * 16, rayHead.y + rayVec. y * 16, "i"); 63 | drawLabel(refractionHead.x + refractionVec.x * 16, refractionHead.y + refractionVec.y * 16, "r"); 64 | drawLabel(normalHead.x + normal.x * 16, normalHead.y + normal.y * 16, "n"); 65 | 66 | pop(); 67 | drawLabel(8, 32, "IOR = " + IOR, LEFT); 68 | } 69 | 70 | /** Line **/ 71 | 72 | /** drawArrow **/ 73 | 74 | /** drawLabel **/ 75 | 76 | -------------------------------------------------------------------------------- /js/examples/drawings/excenter.js: -------------------------------------------------------------------------------- 1 | function setup() { 2 | canvas = createCanvas(windowWidth, windowHeight); 3 | } 4 | 5 | function draw() { 6 | clear(); 7 | background(62, 252, 100); 8 | let boundary = {x: -width / 2, y: -height / 2, w: width, h: height}; 9 | 10 | let t = (frameCount % 720) / 720; 11 | let a0 = PI / 3 * cos(t * TWO_PI) + 0.01 + TWO_PI / 3 ; 12 | let a1 = PI / 3 * cos(t * TWO_PI * 3) + 0.02 + TWO_PI / 3 * 2; 13 | let a2 = PI / 3 * cos(t * TWO_PI * 5) + 0.03; 14 | 15 | let p0 = createVector(cos(a0), sin(a0)).mult(60); 16 | let p1 = createVector(cos(a1), sin(a1)).mult(70); 17 | let p2 = createVector(cos(a2), sin(a2)).mult(80); 18 | 19 | let s0 = new LineSegment().fromTwoPoints(p0, p1); 20 | let s1 = new LineSegment().fromTwoPoints(p1, p2); 21 | let s2 = new LineSegment().fromTwoPoints(p2, p0); 22 | 23 | let l0 = s0.toLine(); 24 | let l1 = s1.toLine(); 25 | let l2 = s2.toLine(); 26 | 27 | let bs0 = getExternalAngleBisctor(p0, p1, p2); 28 | let bs1 = getExternalAngleBisctor(p1, p2, p0); 29 | let bs2 = getExternalAngleBisctor(p2, p0, p1); 30 | 31 | let excenters = []; 32 | excenters.push(bs0.getIntersectionPoint(bs1)); 33 | excenters.push(bs1.getIntersectionPoint(bs2)); 34 | excenters.push(bs2.getIntersectionPoint(bs0)); 35 | 36 | let excircles = []; 37 | if (excenters[0]) { excircles.push(getExcircle(excenters[0], l2)); } 38 | if (excenters[1]) { excircles.push(getExcircle(excenters[1], l0)); } 39 | if (excenters[2]) { excircles.push(getExcircle(excenters[2], l1)); } 40 | 41 | push(); 42 | translate(width / 2, height / 2); 43 | noFill(); stroke(0); strokeWeight(1); 44 | fill(0); strokeWeight(3); 45 | s0.draw(); 46 | s1.draw(); 47 | s2.draw(); 48 | 49 | strokeWeight(1); 50 | l0.draw(boundary); 51 | l1.draw(boundary); 52 | l2.draw(boundary); 53 | 54 | stroke(0, 0, 0, 30); 55 | bs0.draw(boundary); 56 | bs1.draw(boundary); 57 | bs2.draw(boundary); 58 | 59 | fill(255); stroke(0); 60 | drawCircleMarker(p0, 3); 61 | drawCircleMarker(p1, 3); 62 | drawCircleMarker(p2, 3); 63 | 64 | for (let i = 0; i < 3; i ++) { 65 | if (excenters[i]) { 66 | noFill(); stroke(0); strokeWeight(1); 67 | excircles[i].draw(); 68 | fill(255); 69 | drawCircleMarker(excenters[i], 4); 70 | } 71 | } 72 | 73 | pop(); 74 | } 75 | 76 | function getExternalAngleBisctor(o, p0, p1) { 77 | let a0 = atan2((o.y - p0.y), (o.x - p0.x)); 78 | let a1 = atan2((p1.y - o.y), (p1.x - o.x)); 79 | return new Line().fromPointAndAngle(o, (a0 + a1) / 2); 80 | } 81 | 82 | function getExcircle(p, l) { 83 | let radius = p.dist(l.getIntersectionPoint(l.getPerpendicular(p))); 84 | return new Circle().fromCenterAndRadius(p, radius); 85 | } 86 | 87 | /** Line **/ 88 | 89 | /** LineSegment **/ 90 | 91 | /** Circle **/ 92 | 93 | /** drawCircleMarker **/ 94 | -------------------------------------------------------------------------------- /js/examples/physics/orbit.js: -------------------------------------------------------------------------------- 1 | let msec = 0, prevInitMsec = 0, prevNow = 0; 2 | let bodies = []; 3 | 4 | function setup() { 5 | canvas = createCanvas(windowWidth, windowHeight); 6 | initBodies(); 7 | } 8 | 9 | function draw() { 10 | clear(); 11 | background(247, 227, 218); 12 | 13 | ellapseTime(); 14 | 15 | push(); 16 | translate(width / 2, height / 2); 17 | stroke(0); 18 | bodies.forEach((b, i)=>{ 19 | noFill(); 20 | let step = 4; 21 | for (let i = step; i < b.tail.length; i += step) { 22 | line(b.tail[i].x, b.tail[i].y, b.tail[i - 1].x, b.tail[i - 1].y); 23 | } 24 | }); 25 | bodies.forEach((b, i)=>{ 26 | if (i == 0) { 27 | fill(250, 170, 165); 28 | } else { 29 | fill(117, 147, 208); 30 | } 31 | let s = pow(b.mass, 1 / 3) * 4; 32 | ellipse(b.position.x, b.position.y, s, s); 33 | }); 34 | pop(); 35 | } 36 | 37 | function ellapseTime() { 38 | let ellapsedMsec = min(100, window.performance.now() - prevNow); 39 | msec += ellapsedMsec; 40 | let secondsRemaining = ellapsedMsec / 1000; 41 | let maxStepSize = 1 / 60; 42 | while (secondsRemaining > 0) { 43 | let stepSize = secondsRemaining > maxStepSize ? maxStepSize : secondsRemaining; 44 | updateSimulation(stepSize); 45 | secondsRemaining -= maxStepSize; 46 | } 47 | 48 | if (msec > prevInitMsec + 20000) { 49 | initBodies(); 50 | prevInitMsec += 20000; 51 | } 52 | 53 | } 54 | 55 | function updateSimulation(t) { 56 | for (let i = 0; i < bodies.length - 1; i ++) { 57 | for (let j = i + 1; j < bodies.length; j ++) { 58 | gravitate(bodies[i], bodies[j], t); 59 | } 60 | } 61 | for (let i = 0; i < bodies.length; i ++) { 62 | bodies[i].update(t); 63 | if (bodies[i].prevPosition.dist(bodies[i].position) > 4) { 64 | bodies[i].tail.push(bodies[i].position.copy()); 65 | bodies[i].prevPosition = bodies[i].position.copy(); 66 | } 67 | } 68 | } 69 | 70 | function gravitate(o0, o1, t) { 71 | let distance = max(1, o0.position.dist(o1.position) / 10); 72 | let force = o1.position.copy().sub(o0.position).normalize().mult(o0.mass * o1.mass / distance / distance); 73 | o0.applyForce(force, t); 74 | o1.applyForce(force.mult(-1), t); 75 | } 76 | 77 | function initBodies() { 78 | bodies = []; 79 | for (let i = 0; i < 8; i ++) { 80 | let body; 81 | if (i == 0) { 82 | body = new Body(500); 83 | } else { 84 | let angle = random(PI * 2); 85 | let distance = random(100, 200); 86 | let speed = random(10, 30); 87 | body = new Body(random(2,30)); 88 | body.position = createVector(cos(angle), sin(angle)).mult(distance); 89 | body.velocity = createVector(-sin(angle), cos(angle)).mult(speed); 90 | } 91 | body.tail = []; 92 | body.prevPosition = body.position.copy(); 93 | bodies.push(body); 94 | } 95 | } 96 | 97 | /** Body **/ 98 | -------------------------------------------------------------------------------- /js/examples/easing/bouncing.js: -------------------------------------------------------------------------------- 1 | let msec = 0, prevNow = 0; 2 | let tween, bezier, quad; 3 | 4 | function setup() { 5 | canvas = createCanvas(windowWidth, windowHeight); 6 | tween = new Tween(); 7 | bezier = tween.createCubicBezier({x:0.4, y: 0.0}, {x:0.2, y: 1}); 8 | quad = (t)=>{return tween.powerOut(t, 2)}; 9 | } 10 | 11 | function draw() { 12 | clear(); 13 | background(73, 5, 255); 14 | let groundY = height - 24; 15 | let baseSize = 72; 16 | let mt = msec % 1900; 17 | 18 | for (let i = 0; i < 2; i++) { 19 | let x = width / 2 + baseSize * (i * 2 - 1), y = 0, w = baseSize, h = baseSize; 20 | if (mt < 500) { 21 | let t = map(mt, 0, 500, 0, 1); 22 | if (i == 1) { 23 | let r = 1 - tween.powerInOut(t, 2) * 0.2; 24 | w = baseSize / r; 25 | h = baseSize * r; 26 | x += randomGaussian(0, t * 2); 27 | } 28 | y = groundY - h / 2; 29 | } else if (mt < 1000) { 30 | let t = map(mt, 500, 1000, 0, 1); 31 | if (i == 1) { 32 | let r = 1.2 - tween.powerOut(t, 2) * 0.2; 33 | w = baseSize / r; 34 | h = baseSize * r; 35 | } 36 | y = groundY - h / 2 - tween.powerOut(t, 2) * height * 2; 37 | } else if (mt < 1500) { 38 | let t = map(mt, 1000, 1500, 0, 1); 39 | y = groundY - h / 2 - tween.powerOut(1 - t, 2) * height * 2; 40 | } else { 41 | if (i == 0) { 42 | y = groundY - h / 2; 43 | } else if (mt < 1600) { 44 | let t = map(mt, 1500, 1600, 0, 1); 45 | let r = 1 - tween.powerOut(t, 2) * 0.3; 46 | w = baseSize / r; 47 | h = baseSize * r; 48 | y = groundY - h / 2; 49 | } else if (mt < 1750) { 50 | let t = map(mt, 1600, 1750, 0, 1); 51 | let t2 = map(mt, 1600, 1650, 0, 1); 52 | let r = 0.7 + tween.powerOut(t2, 2) * 0.3; 53 | w = baseSize / r; 54 | h = baseSize * r; 55 | y = groundY - h / 2 - tween.powerOut(t, 2) * baseSize; 56 | } else if (mt < 1900) { 57 | let t = map(mt, 1750, 1900, 0, 1); 58 | y = groundY - h / 2 - tween.powerOut(1 - t, 2) * baseSize; 59 | } 60 | } 61 | 62 | fill(255); stroke(0); 63 | ellipse(x, y, w, h); 64 | } 65 | 66 | 67 | noFill(); stroke(0); 68 | drawGround(groundY); 69 | ellapseTime(); 70 | } 71 | 72 | function drawGround(groundY) { 73 | let n = Math.ceil(width / 32); 74 | beginShape(); 75 | curveVertex(-8, groundY); 76 | for (let i = 0; i <= n; i ++) { 77 | let x = width / n * i; 78 | curveVertex(x, groundY + randomGaussian(0, 1)); 79 | 80 | } 81 | curveVertex(width + 8, groundY); 82 | endShape(); 83 | } 84 | 85 | function ellapseTime() { 86 | msec += min(100, window.performance.now() - prevNow); 87 | prevNow = window.performance.now(); 88 | } 89 | 90 | function drawBox(x, y, size) { 91 | rect(x - size / 2, y - size / 2, size, size); 92 | } 93 | 94 | /** Tween **/ 95 | -------------------------------------------------------------------------------- /light_path.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Light Path 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 |

Path of Light

16 |

Sketching with Math and Quasi Physics

17 |

There is a newer and more elaborate version of this site.
Visit Sketching with Math and Quasi Physics on Notion.

18 |

Reflection

19 |

$${r=i-\frac{2i\cdot n}{\left\|n\right\|^2}n}$$

20 |

$${i}$$: vector of the ray to the surface, $${n}$$: normal of the surface, $${r}$$: vector of the reflected ray

21 |
22 | 23 |

Parabola

24 |

Parallel rays coming into a parabolic mirror converge at a single focal point

25 |
26 | 27 |

Refraction

28 |
29 |

Learn more

30 |

Scratchapixel 2.0

31 | 32 |

Random Mirrors

33 |
34 |

Learn more

35 |

Scratchapixel 2.0

36 | 37 | 38 | 39 |
40 | 41 | 42 | 43 | 44 | 45 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /js/examples/verlet/ragdoll.js: -------------------------------------------------------------------------------- 1 | let points = [], sticks = []; 2 | let headTarget; 3 | 4 | function setup() { 5 | canvas = createCanvas(windowWidth, windowHeight); 6 | let u = height / 16; 7 | points.push(new VerletPoint( createVector(0, u * 2)) ); // 0 8 | points.push(new VerletPoint( createVector(-u * 2, u * 3)) ); // 1 9 | points.push(new VerletPoint( createVector(u * 2, u * 3)) ); // 2 10 | points.push(new VerletPoint( createVector(-u * 1.5, u * 8)) ); // 3 11 | points.push(new VerletPoint( createVector(u * 1.5, u * 8)) ); // 4 12 | points.push(new VerletPoint( createVector(-u * 5, u * 3)) ); // 5 13 | points.push(new VerletPoint( createVector(-u * 8, u * 3)) ); // 6 14 | points.push(new VerletPoint( createVector(u * 5, u * 3)) ); // 7 15 | points.push(new VerletPoint( createVector(u * 8, u * 3)) ); // 8 16 | points.push(new VerletPoint( createVector(-u * 1.5, u * 12)) ); // 9 17 | points.push(new VerletPoint( createVector(-u * 1.5, u * 16)) ); // 10 18 | points.push(new VerletPoint( createVector(u * 1.5, u * 12)) ); // 11 19 | points.push(new VerletPoint( createVector(u * 1.5, u * 16)) ); // 12 20 | 21 | sticks.push(new VerletStick(points[0], points[1])); 22 | sticks.push(new VerletStick(points[0], points[2])); 23 | sticks.push(new VerletStick(points[1], points[2])); 24 | sticks.push(new VerletStick(points[1], points[3])); 25 | sticks.push(new VerletStick(points[2], points[4])); 26 | sticks.push(new VerletStick(points[3], points[4])); 27 | sticks.push(new VerletStick(points[1], points[4])); 28 | 29 | sticks.push(new VerletStick(points[1], points[5])); 30 | sticks.push(new VerletStick(points[5], points[6])); 31 | sticks.push(new VerletStick(points[2], points[7])); 32 | sticks.push(new VerletStick(points[7], points[8])); 33 | 34 | sticks.push(new VerletStick(points[3], points[9])); 35 | sticks.push(new VerletStick(points[9], points[10])); 36 | sticks.push(new VerletStick(points[4], points[11])); 37 | sticks.push(new VerletStick(points[11], points[12])); 38 | 39 | } 40 | 41 | function draw() { 42 | clear(); 43 | background(254, 131, 49); 44 | points.forEach((p, i)=>{ 45 | p.update(); 46 | p.position.y += 0.1; 47 | p.position.y = min(p.position.y, height); 48 | }); 49 | 50 | for (let i = 0; i < 5; i ++) { 51 | sticks.forEach((s)=>{ 52 | s.update(); 53 | }); 54 | } 55 | 56 | let f = (frameCount - 1) % 60; 57 | if (f == 0) { 58 | let u = height / 16; 59 | headTarget = createVector(random(-u * 4, u * 4), random(u * 2) + u); 60 | } 61 | 62 | if (f < 30) { 63 | points[0].position.add(headTarget.sub(points[0].position).normalize().mult(3)); 64 | } 65 | 66 | push(); 67 | translate(width / 2, 0); 68 | fill(255); stroke(0); strokeWeight(3); 69 | 70 | strokeWeight(1); 71 | sticks.forEach((s)=>{s.draw();}); 72 | //points.forEach((p)=>{p.draw();}); 73 | pop(); 74 | 75 | var fps = frameRate(); 76 | fill(255); 77 | stroke(0); 78 | } 79 | 80 | 81 | /** VerletPoint **/ 82 | 83 | /** VerletStick **/ -------------------------------------------------------------------------------- /distance.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Distance and Shape 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 |

Distance and Shape

16 |

Sketching with Math and Quasi Physics

17 |

There is a newer and more elaborate version of this site.
Visit Sketching with Math and Quasi Physics on Notion.

18 |

Distance function

19 |

A shape can be defined by a function that takes the coordinate of a point and returns the distance to the closest boudary of the shape. This function is called distance function and used as a modeling technique in computer graphics.

20 |

Learn more

21 |

Ray Marching and Signed Distance Functions by Jamie Wong

22 |

The book of shaders - Shaping function by Patricio Gonzalez Vivo & Jen Lowe

23 |

Distance functions by Inigo Quilez

24 | 25 |

Circle

26 |

The size of dots indicate the distance from the boundary of the shape (the smaller the closer). White dots are inside the shape. Black dots are outside.

27 |
28 | 29 |

Rectangle

30 |
31 | 32 |

Rotated rectangle

33 |
34 |

See also

35 |

Rotating 2D points

36 | 37 | 38 | 39 |
40 | 41 | 42 | 43 | 44 | 45 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /js/examples/easing/tweens2.js: -------------------------------------------------------------------------------- 1 | let graphData = [{y:0, msec: -5000}]; 2 | let resolution = 80; 3 | let duration = 1000, interval = 500; 4 | let msec = 0, prevNow = 0, prevUpdateMsec = 0; 5 | let tween, tweens; 6 | 7 | function setup() { 8 | canvas = createCanvas(windowWidth, windowHeight); 9 | tween = new Tween(); 10 | tweens = [ 11 | {func:tween.linear , label: "linear"}, 12 | {func:(t)=>{return tween.powerIn(t,2);} , label: "powerIn(2)"}, 13 | {func:(t)=>{return tween.powerOut(t,2);} , label: "powerOut(2)"}, 14 | {func:(t)=>{return tween.powerInOut(t,2);} , label: "powerInOut(2)"}, 15 | {func:(t)=>{return tween.powerIn(t,3);} , label: "powerIn(3)"}, 16 | {func:(t)=>{return tween.powerOut(t,3);} , label: "powerOut(3)"}, 17 | {func:(t)=>{return tween.powerInOut(t,3);} , label: "powerInOut(3)"}, 18 | {func:tween.sineIn , label: "sineIn"}, 19 | {func:tween.sineOut , label: "sineOut"}, 20 | {func:tween.circularIn , label: "circularIn"}, 21 | {func:tween.circularOut , label: "circularOut"}, 22 | {func:tween.circularInOut , label: "circularInOut"}, 23 | {func:tween.createCubicBezier({x:0.4, y: 0.0}, {x:0.2, y: 1}), label:"bezier([0.2,0.8],[0.6,0.4])"} 24 | ]; 25 | } 26 | 27 | function draw() { 28 | clear(); 29 | background(29, 223, 182); 30 | 31 | while (msec + duration + interval >= prevUpdateMsec) { 32 | updateGraphData(); 33 | } 34 | ellapseTime(); 35 | 36 | let s = height * 0.8; 37 | let x = (width + s) / 2, oy = (height + s) / 2; 38 | let y = oy - s * msecToValue(msec); 39 | 40 | noFill(); stroke(0); 41 | 42 | beginShape(); 43 | for (let i = 0; i < graphData.length; i ++) { 44 | let vx = x - s * (msec - graphData[i].msec) / duration; 45 | vertex(vx, oy - graphData[i].y * s); 46 | } 47 | endShape(); 48 | 49 | fill(255); stroke(0); 50 | drawCircleMarker(createVector(x, y), 4); 51 | 52 | fill(0); noStroke(); 53 | drawLabel(width / 2, height / 2, msecToLabel(msec)); 54 | } 55 | 56 | function ellapseTime() { 57 | msec += min(100, window.performance.now() - prevNow); 58 | prevNow = window.performance.now(); 59 | } 60 | 61 | function msecToValue(msec) { 62 | let t = (msec % (duration + interval)) / duration; 63 | t = Math.min(1, Math.max(0, t)); 64 | let i = Math.floor(msec / (duration + interval)); 65 | let reverse = i % 2 == 0; 66 | let idx = Math.floor(i / 2) % tweens.length; 67 | return reverse ? tweens[idx].func(t) : (1 - tweens[idx].func(t)); 68 | } 69 | 70 | function msecToLabel(msec) { 71 | let idx = Math.floor(Math.floor(msec / (duration + interval)) / 2) % tweens.length; 72 | return tweens[idx].label; 73 | } 74 | 75 | function updateGraphData() { 76 | for (let i = 0; i <= resolution; i ++) { 77 | let dataMsec = prevUpdateMsec + i / resolution * duration; 78 | let y = msecToValue(dataMsec); 79 | graphData.push({msec: dataMsec, y: y}); 80 | } 81 | if (graphData.length > resolution * 4) { 82 | graphData = graphData.slice(resolution); 83 | } 84 | 85 | prevUpdateMsec += (duration + interval); 86 | } 87 | 88 | /** Tween **/ 89 | 90 | /** drawCircleMarker **/ 91 | 92 | /** drawLabel **/ 93 | -------------------------------------------------------------------------------- /js/examples/light_path/mirrors.js: -------------------------------------------------------------------------------- 1 | const maxReflection = 32; 2 | let mirrors; 3 | 4 | function setup() { 5 | canvas = createCanvas(windowWidth, windowHeight); 6 | updateMirrors(); 7 | } 8 | 9 | function updateMirrors() { 10 | mirrors = []; 11 | const w = min(width, height); 12 | for (let i = 0; i < 4; i ++) { 13 | p0 = createVector(-w * 0.5 + random() * w, -w * 0.5 + random() * w); 14 | p1 = createVector(-w * 0.5 + random() * w, -w * 0.5 + random() * w); 15 | mirrors.push(new LineSegment().fromTwoPoints(p0, p1)); 16 | } 17 | } 18 | 19 | function hitTest(o, d, lineSegments, ignore) { 20 | let intersection, hitObject, minDistance = Infinity; 21 | const ray = new Line().fromPointAndVector(o, d); 22 | lineSegments.forEach((ls)=>{ 23 | if (ls != ignore && ls.intersects(ray)) { 24 | const temp = ls.getIntersectionPoint(ray); 25 | const dot = d.dot(temp.copy().sub(o)); 26 | const dist = o.dist(temp); 27 | if (dot >= 0 && dist < minDistance) { 28 | intersection = temp; 29 | minDistance = dist; 30 | hitObject = ls; 31 | } 32 | } 33 | }); 34 | 35 | return intersection ? {point: intersection, object: hitObject} : null; 36 | } 37 | 38 | function reflect(i, n) { 39 | return i.copy().add(n.copy().mult(i.copy().mult(2).dot(n) / pow(n.mag(), 2) * -1)); 40 | } 41 | 42 | function draw() { 43 | clear(); 44 | background(255, 235, 200); 45 | 46 | const l = max(width, height); 47 | const angle = frameCount / 60 * PI; 48 | if (frameCount % 120 == 0) { updateMirrors();} 49 | 50 | let origin = createVector(cos(angle), sin(angle)).mult(l); 51 | let dir = origin.copy().mult(-1).normalize(); 52 | 53 | const points = [origin]; 54 | let currentMirror = null; 55 | for (let i = 0; i < maxReflection; i ++) { 56 | const hitResult = hitTest(origin, dir, mirrors, currentMirror); 57 | 58 | if (hitResult) { 59 | points.push(hitResult.point); 60 | currentMirror = hitResult.object; 61 | 62 | const p0 = hitResult.object.p0; 63 | const p1 = hitResult.object.p1; 64 | normal = createVector( 65 | -(p0.y - p1.y), 66 | p0.x - p1.x 67 | ).normalize(); 68 | 69 | normal = (normal.dot(dir) > 0) ? normal.mult(-1) : normal; 70 | origin = hitResult.point; 71 | dir = reflect(dir, normal); 72 | } else { 73 | points.push(origin.copy().add(dir.copy().mult(l * 2))); 74 | break; 75 | } 76 | } 77 | 78 | push(); 79 | 80 | translate(width / 2, height / 2); 81 | fill(255); stroke(0); strokeWeight(1); 82 | for (let i = 1; i < points.length; i++) { 83 | line(points[i - 1].x, points[i - 1].y, points[i].x, points[i].y); 84 | } 85 | strokeWeight(3); 86 | mirrors.forEach((m)=>{m.draw();}); 87 | fill(0); noStroke(0); 88 | points.forEach((p)=>{drawCircleMarker(p, 4);}) 89 | pop(); 90 | } 91 | 92 | /** Line **/ 93 | 94 | /** LineSegment **/ 95 | 96 | /** drawCircleMarker **/ 97 | -------------------------------------------------------------------------------- /js/examples/math/perspective.js: -------------------------------------------------------------------------------- 1 | function setup() { 2 | canvas = createCanvas(windowWidth, windowHeight); 3 | } 4 | 5 | function draw() { 6 | clear(); 7 | let cx = width / 4 * 3, cy = height / 2; 8 | let cameraZ = -width / 8 * 5; 9 | let planeZ = cameraZ / 2; 10 | let radius = width / 8; 11 | let r0 = radians(frameCount + 45); 12 | let r1 = radians(frameCount + 135); 13 | let r2 = radians(frameCount + 225); 14 | let r3 = radians(frameCount + 315); 15 | let p0 = createVector(0, cos(r0), sin(r0)).mult(radius); 16 | let p1 = createVector(0, cos(r1), sin(r1)).mult(radius); 17 | let p2 = createVector(0, cos(r2), sin(r2)).mult(radius); 18 | let p3 = createVector(0, cos(r3), sin(r3)).mult(radius); 19 | 20 | let pp0 = project(p0, planeZ, cameraZ) 21 | let pp1 = project(p1, planeZ, cameraZ) 22 | let pp2 = project(p2, planeZ, cameraZ) 23 | let pp3 = project(p3, planeZ, cameraZ) 24 | 25 | background(135, 108, 209); 26 | 27 | push(); 28 | 29 | fill(0); stroke(0); strokeWeight(1); 30 | translate(cx, cy); 31 | line(p0.z, p0.y, cameraZ, 0); 32 | line(p1.z, p1.y, cameraZ, 0); 33 | line(p2.z, p2.y, cameraZ, 0); 34 | line(p3.z, p3.y, cameraZ, 0); 35 | line(-width / 4 * 3, 0, width / 4, 0); 36 | line(0, -height / 2, 0, height / 2); 37 | line(cameraZ, 0, cameraZ, -height / 3); 38 | line(cameraZ, -height / 4 + 16, planeZ, -height / 4 + 16); 39 | line(p1.z, p1.y, p1.z, -height / 3); 40 | line(cameraZ, -height / 3 + 16, p1.z, -height / 3 + 16); 41 | 42 | strokeWeight(2); 43 | line(p0.z, p0.y, p1.z, p1.y); 44 | line(p1.z, p1.y, p2.z, p2.y); 45 | line(p2.z, p2.y, p3.z, p3.y); 46 | line(p3.z, p3.y, p0.z, p0.y); 47 | line(planeZ, -height / 4, planeZ, height / 4); 48 | 49 | fill(255); stroke(0); strokeWeight(1); 50 | drawCircleMarker(createVector(p1.z, p1.y), 4); 51 | drawCircleMarker(createVector(planeZ, pp1.y), 4); 52 | drawSquareMarker(createVector(cameraZ, 0), 8); 53 | 54 | fill(0); stroke(0); strokeWeight(1); 55 | drawCircleMarker(createVector(p0.z, p0.y), 2); 56 | drawCircleMarker(createVector(p2.z, p2.y), 2); 57 | drawCircleMarker(createVector(p3.z, p3.y), 2); 58 | 59 | drawCircleMarker(createVector(planeZ, pp0.y), 2); 60 | drawCircleMarker(createVector(planeZ, pp2.y), 2); 61 | drawCircleMarker(createVector(planeZ, pp3.y), 2); 62 | 63 | 64 | noStroke(); fill(0); 65 | drawLabel(cameraZ, 24, "camera"); 66 | drawLabel(planeZ, height / 4, "image plane", LEFT); 67 | drawLabel(-width / 4 * 3, -12, "z(+)", LEFT); 68 | drawLabel(width / 4, -12, "z(-)", RIGHT); 69 | drawLabel(4, -height / 2 + 16, "y(-)", LEFT); 70 | drawLabel(4, height / 2 - 8, "y(+)", LEFT); 71 | drawLabel(4, 16, "[0, 0]", LEFT); 72 | drawLabel((cameraZ + planeZ) / 2, -height / 4 + 8, "d1"); 73 | drawLabel((cameraZ + p1.z) / 2, -height / 3 + 8, "d2"); 74 | pop(); 75 | } 76 | 77 | function project(v, nearClip, cameraZ) { 78 | let r = abs(cameraZ - nearClip) / abs(cameraZ - v.z); 79 | return createVector(v.x * r, v.y * r); 80 | // Look up for Projection Matrix for more proper 3D projection 81 | // e.g. http://www.songho.ca/opengl/gl_projectionmatrix.html 82 | } 83 | 84 | /** drawCircleMarker **/ 85 | 86 | /** drawSquareMarker **/ 87 | 88 | /** drawLabel **/ 89 | -------------------------------------------------------------------------------- /js/examples/physics/spring.js: -------------------------------------------------------------------------------- 1 | let msec = 0, prevInitMsec = 0, prevNow = 0; 2 | let plotMode = -1, plotLabels = ["position(y)", "velocity(y)", "acceralation(y)"], plotStartMsec = 0, plotData = []; 3 | let paramMode = -1, tention = 1, friction = 0.0; 4 | let ball; 5 | 6 | function setup() { 7 | canvas = createCanvas(windowWidth, windowHeight); 8 | ball = new Body(1); 9 | ball.position.y = 100; 10 | } 11 | 12 | function draw() { 13 | clear(); 14 | background(180, 235, 190); 15 | 16 | ellapseTime(); 17 | 18 | let minWH = min(width, height); 19 | push(); 20 | translate(min((width - minWH) / 2 + 32, width / 4) , height / 2); 21 | fill(250, 170, 165); stroke(0); 22 | ellipse(0, ball.position.y, 24, 24); 23 | 24 | noFill(); stroke(0); 25 | 26 | let springEndY = -height / 2; 27 | let len = height / 2 + ball.position.y - 12; 28 | beginShape(); 29 | for (let i = 0; i <= 72; i ++) { 30 | let angle = i / 2 * PI; 31 | let x = sin(angle) * 6; 32 | let y = springEndY + len / 72 * i; 33 | vertex(x, y); 34 | } 35 | endShape(); 36 | 37 | pop(); 38 | 39 | let graphW = minWH - 64; 40 | let graphX = (width - graphW) / 2 + 32; 41 | 42 | plotGraph(plotData, graphX, height / 2, graphW, height - 64, 0, 10000, -height / 2 + 32, height / 2 - 32, "time", plotLabels[plotMode]); 43 | 44 | drawLabel(graphX + graphW, 40, "tention: " + tention, RIGHT); 45 | drawLabel(graphX + graphW, 56, "friction: " + friction, RIGHT); 46 | } 47 | 48 | function ellapseTime() { 49 | let ellapsedMsec = min(100, window.performance.now() - prevNow); 50 | msec += ellapsedMsec; 51 | let secondsRemaining = ellapsedMsec / 1000; 52 | let maxStepSize = 1 / 60; 53 | while (secondsRemaining > 0) { 54 | let stepSize = secondsRemaining > maxStepSize ? maxStepSize : secondsRemaining; 55 | updateSimulation(stepSize); 56 | secondsRemaining -= maxStepSize; 57 | } 58 | 59 | if (msec > prevInitMsec + 10000) { 60 | ball = new Body(1); 61 | ball.position.y = 100; 62 | plotData = []; 63 | prevInitMsec += 10000; 64 | } 65 | 66 | if (plotData.length == 0) { 67 | plotStartMsec = msec; 68 | plotMode = (plotMode + 1) % 3; 69 | 70 | if (plotMode == 0) { 71 | paramMode = (paramMode + 1) % 3; 72 | switch(paramMode) { 73 | case 0: tention = 1; friction = 0; break; 74 | case 1: tention = 4; friction = 0.5; break; 75 | case 2: tention = 2; friction = 1.5; break; 76 | } 77 | } 78 | } 79 | 80 | let plotMsec = (msec - plotStartMsec); 81 | switch(plotMode) { 82 | case 0: plotData.push({x:plotMsec, y: -ball.position.y}); break; 83 | case 1: plotData.push({x:plotMsec, y: -ball.velocity.y}); break; 84 | case 2: plotData.push({x:plotMsec, y: -getForce(ball).y}); break; 85 | } 86 | } 87 | 88 | function updateSimulation(t) { 89 | ball.applyForce(getForce(ball), t); 90 | ball.update(t); 91 | } 92 | 93 | function getForce(o) { 94 | let springForce = o.position.copy().mult(-tention); 95 | let frictionForce = o.velocity.copy().mult(-friction); 96 | return springForce.add(frictionForce); 97 | } 98 | 99 | /** plotGraph **/ 100 | 101 | /** Body **/ 102 | 103 | /** drawCircleMarker **/ 104 | 105 | /** drawLabel **/ 106 | -------------------------------------------------------------------------------- /verlet.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Building Ragdolls 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 |

Building Ragdolls

16 |

Sketching with Math and Quasi Physics

17 |

There is a newer and more elaborate version of this site.
Visit Sketching with Math and Quasi Physics on Notion.

18 |

Verlet Points

19 |

Verlet integration is based on the assumption that, without force applied, an object will continue the uniform motion that it previously had. Instead of explcitly maintaining velocity as a variable belongs to the object, the change from the previous position to the current position is used as substitute for the velocity.

20 | 21 |

let v = (random(1) + random(1)) / 2;
22 | update() {
23 |   let temp = this.position.copy();
24 |   this.position.add(this.getVelocity());
25 |   this.prevPosition = temp;
26 | }
27 | 
28 | setVelocity(v) {
29 |   this.prevPosition = this.position.copy().sub(v);
30 | }
31 | 
32 | getVelocity() {
33 |   return this.position.copy().sub(this.prevPosition);
34 | }
35 | 

36 |
37 | 38 |

Learn more

39 |

Verlet integration

40 | 41 |

Verlet Sticks

42 |

The points can be connected by segments(sticks) that constrain the distances between them

43 |

update() {
44 |   let dist = this.pa.position.dist(this.pb.position);
45 |   let diff = this.length - dist;
46 |   let offset = this.pa.position.copy().sub(this.pb.position).mult(diff / dist / 2);
47 |   this.pa.position.add(offset);
48 |   this.pb.position.sub(offset);
49 | }
50 | 

51 |
52 | 53 |

Pendulum

54 |
55 | 56 |

Ragdoll

57 |
58 | 59 |

Ragdoll 3D

60 |
61 | 62 |

index

63 | 64 |
65 | 66 | 67 | 68 | 69 | 70 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /js/examples/drawings/bspline.js: -------------------------------------------------------------------------------- 1 | let log = []; 2 | let b = []; 3 | let resolution = 50; 4 | let n = 2; 5 | let knots = [0, 0, 0, 1, 2, 3, 3, 3]; 6 | let pDef = [[-1, 1], [-0.5, -1], [0, 1], [0.5, -1], [1, 1]]; 7 | let p = []; 8 | let tmin = 0; 9 | let tmax = 3; 10 | 11 | function setup() { 12 | canvas = createCanvas(windowWidth, windowHeight); 13 | 14 | for (let i = 0; i < pDef.length; i ++) { 15 | p.push(createVector(pDef[i][0], pDef[i][1])); 16 | } 17 | 18 | for (let i = 0; i < p.length; i ++) { 19 | b.push([]); 20 | for (let j = tmin; j <= tmax * resolution; j += 1) { 21 | let x = j / resolution; 22 | if (x >= tmax) { x = tmax - 0.00001; } 23 | b[i].push(bN(i, n, x)); 24 | } 25 | } 26 | } 27 | 28 | function draw() { 29 | clear(); 30 | background(15, 243, 208); 31 | 32 | let interval = 30; 33 | let len = b[0].length + interval; 34 | let f = frameCount - 1; 35 | if (f % len == 0) { log = []; } 36 | let idx = constrain(f % len - interval / 2, 0, b[0].length - 1); 37 | 38 | drawGraph(idx); 39 | drawCurve(idx); 40 | } 41 | 42 | function drawGraph(idx) { 43 | noFill(); stroke(0); 44 | let w = width * 0.6, h = height * 0.2; 45 | let x = (width - w) / 2, y = height - 32; 46 | 47 | push(); 48 | translate(x, y); 49 | for (let i = 0; i < b.length; i ++) { 50 | beginShape(); 51 | for (let j = 0; j < b[i].length; j ++) { 52 | let v = b[i][j]; 53 | vertex(j / resolution * w / tmax, -v * h); 54 | } 55 | endShape(); 56 | } 57 | line(idx / resolution * w / tmax, 0, idx / resolution * w / tmax, -h); 58 | 59 | fill(0); 60 | for (let i = 0; i < b.length; i ++) { 61 | let v = b[i][idx]; 62 | drawCircleMarker(createVector(idx / resolution * w / tmax, -v * h), 2); 63 | } 64 | 65 | for (let i = 0; i <= tmax; i ++) { 66 | drawCircleMarker(createVector(i * w / tmax, 0), 2); 67 | 68 | drawLabel(i * w / tmax, 16, "x = " + i); 69 | } 70 | 71 | drawLabel(0, -h - 8, "B i,n(x)"); 72 | pop(); 73 | } 74 | 75 | 76 | function drawCurve(idx) { 77 | fill(0); stroke(0); 78 | let s = createVector(width * 0.4, height * 0.2); // scale 79 | 80 | push(); 81 | translate(width / 2, height / 3 * 1); 82 | 83 | for (let i = 0; i < p.length; i ++) { 84 | drawCircleMarker(createVector(p[i].x * s.x, p[i].y * s.y), 3); 85 | drawLabel(p[i].x * s.x, p[i].y * s.y + 24, "P" + i); 86 | if (i > 0) { 87 | line(p[i - 1].x * s.x, p[i - 1].y * s.y, p[i].x * s.x, p[i].y * s.y); 88 | } 89 | } 90 | 91 | let pt = createVector(); 92 | let v = []; 93 | for (let i = 0; i < p.length; i ++) { 94 | v.push(b[i][idx]); 95 | pt.add(p[i].copy().mult(v[i])) 96 | } 97 | 98 | drawCircleMarker(createVector(pt.x * s.x, pt.y * s.y), 4); 99 | log.push(pt); 100 | for (let i = 0; i < p.length; i ++) { 101 | stroke(0, 0, 0, v[i] * 255); 102 | line(pt.x * s.x, pt.y * s.y, p[i].x * s.x, p[i].y * s.y); 103 | } 104 | 105 | noFill(); stroke(1); strokeWeight(2); 106 | beginShape(); 107 | for (let i = 0; i < log.length; i ++) { 108 | vertex(log[i].x * s.x, log[i].y * s.y); 109 | } 110 | endShape(); 111 | 112 | pop(); 113 | } 114 | 115 | 116 | function bN(i, k, x){ 117 | let w1 = 0, w2 = 0; 118 | if(k == 0){ 119 | if (x >= knots[i] && x < knots[i + 1]) { 120 | return 1; 121 | } else { 122 | return 0; 123 | } 124 | } else { 125 | if ((knots[i + k] - knots[i]) != 0 ){ 126 | w1 = ((x - knots[i]) / (knots[i + k] - knots[i])) * bN(i, k - 1, x); 127 | } 128 | 129 | if ((knots[i + k + 1] - knots[i + 1]) != 0) { 130 | w2 = ((knots[i + k + 1] - x) / (knots[i + k + 1] - knots[i + 1])) * bN(i + 1, k - 1, x); 131 | } 132 | return w1 + w2; 133 | } 134 | } 135 | 136 | /** drawLabel **/ 137 | 138 | /** drawCircleMarker **/ 139 | -------------------------------------------------------------------------------- /pitch.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Pitch and Frequency 8 | 9 | 10 | 11 | 12 | 13 |
14 |

Pitch and Frequency

15 |

Sketching with Math and Quasi Physics

16 |

There is a newer and more elaborate version of this site.
Visit Sketching with Math and Quasi Physics on Notion.

17 |

Sound is vibration transmitted through a medium, i.e. a solid, liquid or gas. A high pitch sound corresponds to a high frequency sound wave and a low pitch sound corresponds to a low frequency sound wave. The frequency is most often measured in hertz(Hz). One hertz means that an event repeats once per second, in case of wave, one cycle per second.

18 | 19 |

Twelve Tone Equal Temperament

20 | 21 |

Twelve-tone equal temperament divides the octave into 12 equal parts(semitones). The frequency ratio of two adjacent notes is the twelfth root of two: 22 | \(12\sqrt2=2^{1⁄12}\approx1.059463\). 23 | The frequency \(f\) of the nth key on a piano is

24 |
25 |

$$f(n)=2^\frac{n-49}{12}\times(A=440Hz)$$

26 |
27 |

* While A=440Hz is widely used and accepted as standard, slightly different frequencies are used in some cases. See Concert pitch(Wikipedia) for reference.

28 |
29 | 30 | 31 |

Just Intonation

32 | 33 |

In just intonation, the frequencies of notes are defined by ratios of small whole numbers, e.g.

34 |

Major 2nd: \(\frac98\) (= \(\frac32\times\frac32\times\frac12\) = an octave below the perfect 5th of the perfect 5th)

35 |

Major 3rd: \(\frac54\)

36 |

Perfect 4th: \(\frac43\) (= \(\frac21/\frac32\) = perfect 5th below the octave)

37 |

Perfect 5th: \(\frac32\)

38 |

Major 6th: \(\frac53\) (= \(\frac54\times\frac43\) = the perfect 4th of the major 3rd)

39 |

Major 7th: \(\frac{15}8\) (= \(\frac54\times\frac32\) = the perfect 5th of the major 3rd)

40 |

Octave: \(\frac21\)

41 | 42 |
43 | 44 |

index

45 | 46 |
47 | 48 | 49 | 50 | 51 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /css/style.css: -------------------------------------------------------------------------------- 1 | .note-latest { 2 | background-color: #FFFF9F; 3 | padding: 16px 24px 16px 24px; 4 | display: inline-block; 5 | border-radius: 8px; 6 | font-weight: 900; 7 | } 8 | 9 | body { margin:0; padding: 0; font-family: 'Open Sans', sans-serif; } 10 | #header {width: 100%; } 11 | #main { max-width:960px; margin: 0 auto 0 auto; } 12 | .example { width:100%; height: 540px;; margin: 0; padding:0; } 13 | .example iframe {width: 100%; height: 540px; margin: 0; padding:0; border: none; } 14 | #main .example { width:100%; height: 400px; } 15 | #main .example iframe {width: 100%; height: 400px; } 16 | 17 | h1 { font-family: 'Fredericka the Great', cursive; font-weight: normal; font-size: 4em; 18 | line-height: 1.5em; margin: 32px 0px 0px 0px; padding: 0; } 19 | 20 | h2 { font-family: 'Amatic SC', cursive; font-weight: bold; font-size: 2em; line-height: 1.5em; 21 | margin: 64px 0px 12px 0px; padding: 0; } 22 | 23 | h3 {font-family: 'Open Sans', sans-serif; font-weight: bold; font-size: 1em; 24 | line-height: 1.5em; margin: 24px 0px 12px 0px; padding: 0; } 25 | 26 | p {font-size: 1em; line-height: 1.7em; font-family: 'Open Sans', sans-serif; 27 | font-weight: 300; color: #222222; margin: 24px 0px 24px 0px; } 28 | 29 | dt, dd {font-size: 0.9em; line-height: 1.7em; font-family: 'Open Sans', sans-serif; 30 | font-weight: 300; color: #222222; } 31 | 32 | a { color: #024997; text-decoration: none; border-bottom: dotted 1px #024997; padding: 2px; } 33 | 34 | a:hover { color: #2962ff; border-bottom: dashed 1px #2962ff; } 35 | 36 | li { margin: 4px 0 8px 0; } 37 | .example-footer { display: flex; flex-direction: row; flex-wrap: wrap; 38 | margin: 8px 0px 12px 0px; 39 | } 40 | 41 | .example-footer .note { display: block; flex-grow: 1; flex-shrink: 1; 42 | vertical-align: middle; font-size: 0.7em; line-height: 1.2em; color: #888888; 43 | margin: 4px 0px 4px 0px; } 44 | 45 | .button-container { flex-grow: 1; flex-shrink: 1; text-align: right; } 46 | 47 | a.button { display: inline-block; line-height: 1.5em; font-size: 1.3em; 48 | font-weight: bold; text-align:right; font-family: 'Amatic SC', cursive; 49 | white-space: nowrap; min-width: 80px; text-align: center; 50 | margin: 4px 0px 0px 8px; } 51 | 52 | a.button { border: dashed 1px #024997; 53 | border-radius: 4px; padding: 2px 4px 2px 4px; } 54 | 55 | a.button:hover { border: dashed 1px #2962ff; } 56 | 57 | a.button:first-child { margin-left: 0; } 58 | 59 | 60 | 61 | .hidden { display: none; } 62 | 63 | #index { margin-left: 32px; list-style-type: upper-roman; } 64 | 65 | #index a { border: none; font-family: 'Open Sans', sans-serif; 66 | font-weight: 300; font-size: 1.2em; color: #222222; } 67 | 68 | #index li { padding: 8px 24px 8px 32px; } 69 | 70 | #index li.disabled, #index li.disabled a { color: #AAAAAA; } 71 | 72 | .code-block { border-top: dashed 1px #eeeeee; border-bottom: dashed 1px #eeeeee; } 73 | 74 | code { font-family: 'Roboto Mono', cursive; line-height: 1.7em; font-size: 0.8em; 75 | margin: 24px 0px 24px 0px; } 76 | 77 | div.math-wrapper { overflow:auto; } 78 | 79 | div.math-wrapper p { display: inline-block; margin: 0px 16px 24px 16px; } 80 | 81 | span.math-wrapper { display: inline-block; margin: 0 4px 0 4px; } 82 | 83 | footer { font-size: 0.8em; line-height: 1.5em; text-align:right; 84 | font-family: 'Open Sans', sans-serif; font-weight: 300; 85 | margin:64px 0px 24px 0px; padding-top: 12px; border-top: solid 1px #DDDDDD; } 86 | 87 | .row { 88 | display: flex; 89 | flex-direction: row; 90 | } 91 | 92 | .row div:first-child { 93 | flex: 1 1; 94 | } 95 | 96 | .row div { 97 | flex: 3 3; 98 | } 99 | 100 | .index-link { 101 | margin-top: 64px; 102 | } 103 | 104 | @media (max-width: 540px) { 105 | .example {height: 400px; } 106 | .example iframe { height: 400px; } 107 | } 108 | 109 | @media (max-width: 960px) { 110 | h1, h2, h3, p, pre, footer, dl, div.example-footer { 111 | padding-left: 16px; padding-right: 16px; } 112 | .math p { font-size: 0.8em; } 113 | } 114 | -------------------------------------------------------------------------------- /random.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Taming Randomness 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 |

Taming Randomness

16 |

Sketching with Math and Quasi Physics

17 |

There is a newer and more elaborate version of this site.
Visit Sketching with Math and Quasi Physics on Notion.

18 |

Uniform Random

19 |

let v = random(1)

20 | 21 |
22 | 23 |

Random2

24 |

let v = pow(random(1), 2)

25 |
26 | 27 |

Randomn

28 |

let v = pow(random(1), n)

29 |
30 | 31 |

(Random + Random) / 2

32 |

let v = (random(1) + random(1)) / 2;

33 |
34 | 35 |

(Random1 + Random2 + ... + Randomn) / n

36 |

let v = 0;
37 | for (let i = 0; i < n; i ++) {
38 |   v += random(1);
39 | }
40 | v /= n;
41 | 

42 |
43 | 44 |

Normal Distribution(Gaussian)

45 |

// Box-Muller Transform (Unif(0,1) => N(0,1))
46 | // avoiding log(0) by using (1 - random(1))
47 | let mean = 0.5, variance = 0.1;
48 | let v = sqrt(-2 * log(1 - random(1))) * cos(2 * Math.PI * Math.random(1)) * variance + mean;
49 | 

50 |

note: randomGaussian() is available in p5.js

51 |
52 | 53 |

Perin Noise

54 |

Distribution

55 |

let v = noise(x); x += 0.001;
56 | 

57 |

see implementation of noise() function in p5.js

58 |
59 | 60 |

Variation over time

61 |

let v = noise(x); x += 0.01;
62 | 

63 |
64 | 65 |

Random Walkers

66 |
67 | 68 |

index

69 | 70 |
71 | 72 | 73 | 74 | 75 | 76 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /js/examples/verlet/ragdoll3d.js: -------------------------------------------------------------------------------- 1 | let project; 2 | let points = [], sticks = []; 3 | let headTarget; 4 | 5 | function setup() { 6 | canvas = createCanvas(windowWidth, windowHeight); 7 | 8 | project = getProjectionFunc(height / 3 * 2, height, 2); 9 | let u = height / 16; 10 | points.push(new VerletPoint( createVector(0, u * 2 - u * 8)) ); // 0 11 | points.push(new VerletPoint( createVector(-u * 2, u * 3 - u * 8)) ); // 1 12 | points.push(new VerletPoint( createVector(u * 2, u * 3 - u * 8)) ); // 2 13 | points.push(new VerletPoint( createVector(-u * 1.5, u * 8 - u * 8)) ); // 3 14 | points.push(new VerletPoint( createVector(u * 1.5, u * 8 - u * 8)) ); // 4 15 | points.push(new VerletPoint( createVector(-u * 5, u * 3 - u * 8)) ); // 5 16 | points.push(new VerletPoint( createVector(-u * 8, u * 3 - u * 8)) ); // 6 17 | points.push(new VerletPoint( createVector(u * 5, u * 3 - u * 8)) ); // 7 18 | points.push(new VerletPoint( createVector(u * 8, u * 3 - u * 8)) ); // 8 19 | points.push(new VerletPoint( createVector(-u * 1.5, u * 12 - u * 8)) ); // 9 20 | points.push(new VerletPoint( createVector(-u * 1.5, u * 16 - u * 8)) ); // 10 21 | points.push(new VerletPoint( createVector(u * 1.5, u * 12 - u * 8)) ); // 11 22 | points.push(new VerletPoint( createVector(u * 1.5, u * 16 - u * 8)) ); // 12 23 | 24 | sticks.push(new VerletStick(points[0], points[1])); 25 | sticks.push(new VerletStick(points[0], points[2])); 26 | sticks.push(new VerletStick(points[1], points[2])); 27 | sticks.push(new VerletStick(points[1], points[3])); 28 | sticks.push(new VerletStick(points[2], points[4])); 29 | sticks.push(new VerletStick(points[3], points[4])); 30 | sticks.push(new VerletStick(points[1], points[4])); 31 | sticks.push(new VerletStick(points[2], points[3])); 32 | 33 | sticks.push(new VerletStick(points[1], points[5])); 34 | sticks.push(new VerletStick(points[5], points[6])); 35 | sticks.push(new VerletStick(points[2], points[7])); 36 | sticks.push(new VerletStick(points[7], points[8])); 37 | 38 | sticks.push(new VerletStick(points[3], points[9])); 39 | sticks.push(new VerletStick(points[9], points[10])); 40 | sticks.push(new VerletStick(points[4], points[11])); 41 | sticks.push(new VerletStick(points[11], points[12])); 42 | 43 | } 44 | 45 | function draw() { 46 | clear(); 47 | background(254, 131, 49); 48 | points.forEach((p, i)=>{ 49 | p.update(); 50 | p.position.y += 0.1; 51 | p.position.y = min(p.position.y, height / 2); 52 | }); 53 | 54 | for (let i = 0; i < 2; i ++) { 55 | sticks.forEach((s)=>{ 56 | s.update(); 57 | }); 58 | } 59 | 60 | let f = (frameCount - 1) % 60; 61 | if (f == 0) { 62 | let u = height / 16; 63 | headTarget = createVector(random(-u * 4, u * 4), random(u * 2) - u * 8, random(-u , u)); 64 | } 65 | 66 | if (f < 30) { 67 | points[0].position.add(headTarget.sub(points[0].position).normalize().mult(4)); 68 | } 69 | 70 | push(); 71 | translate(width / 2, height / 2); 72 | fill(255); stroke(0); strokeWeight(3); 73 | 74 | strokeWeight(1); 75 | points.forEach((p)=>{ 76 | p.screenPos = project(p.position); 77 | }); 78 | 79 | sticks.forEach((s)=>{ 80 | let p0 = s.pa.screenPos; 81 | let p1 = s.pb.screenPos; 82 | line(p0.x, p0.y, p1.x, p1. y); 83 | }); 84 | 85 | 86 | //points.forEach((p)=>{drawCircleMarker(p.screenPos, 4);}); 87 | 88 | pop(); 89 | } 90 | 91 | function drawCircleMarker(p, size) { 92 | ellipse(p.x, p.y, size * 2, size * 2); 93 | } 94 | 95 | function getProjectionFunc(n, z, m = 1) { 96 | let nearClip = n; 97 | let cameraZ = z; 98 | let multiplier = m; 99 | return (v)=>{ 100 | let r = abs(cameraZ - nearClip) / abs(cameraZ - v.z) * multiplier; 101 | return createVector(v.x * r, v.y * r); 102 | } 103 | // Look up for Projection Matrix for more proper 3D projection 104 | // e.g. http://www.songho.ca/opengl/gl_projectionmatrix.html 105 | } 106 | 107 | 108 | /** VerletPoint **/ 109 | 110 | /** VerletStick **/ -------------------------------------------------------------------------------- /js/examples/easing/sequence.js: -------------------------------------------------------------------------------- 1 | let msec = 0, prevNow = 0; 2 | let tween, bezier; 3 | let actors = []; 4 | 5 | function setup() { 6 | canvas = createCanvas(windowWidth, windowHeight); 7 | tween = new Tween(); 8 | bezier = tween.createCubicBezier({x:0.4, y: 0.0}, {x:0.2, y: 1}); 9 | for (let i = 0; i < 10; i++) { 10 | let o = createVector(width / 2, height / 2); 11 | let d = createVector(randomBool() ? -1: 1, randomBool() ? -1: 1) 12 | let actor = randomBool() ? new ArcActor(o, d) : new LineActor(o, d); 13 | actors.push(actor); 14 | } 15 | } 16 | 17 | function draw() { 18 | clear(); 19 | background(255, 60, 0); 20 | ellapseTime(); 21 | 22 | push(); 23 | noFill(); stroke(0); 24 | for (let i = actors.length - 1; i >= 0; i --) { 25 | actors[i].update(); 26 | actors[i].draw(); 27 | 28 | if (actors[i].reproductive) { 29 | actors.push(actors[i].reproduce()); 30 | actors[i].reproductive = false; 31 | } 32 | if (actors[i].isDone) { 33 | actors.splice(i, 1); 34 | } 35 | } 36 | pop(); 37 | } 38 | 39 | function ellapseTime() { 40 | msec += min(100, window.performance.now() - prevNow); 41 | prevNow = window.performance.now(); 42 | } 43 | 44 | class Actor { 45 | constructor(o, d) { 46 | if (o.x < 0 || o.x >= width) { o.x = width / 2; } 47 | if (o.y < 0 || o.y >= width) { o.y = height / 2; } 48 | this.origin = o; 49 | this.dir = d.normalize(); 50 | this.startMs = msec; 51 | this.duration = randomBool() ? 500 : 1000; 52 | this.reproductive = false; 53 | this.isDone = false; 54 | this.reproduceFlag = false; 55 | } 56 | 57 | update() { 58 | let t = map(msec - this.startMs, 0, this.duration, 0, 1); 59 | this.t0 = t * 1.5; 60 | this.t1 = t * 1.5 - 0.5; 61 | if (this.t0 >= 0.99 && !this.reproduceFlag) { 62 | this.reproductive = this.reproduceFlag = true; 63 | } 64 | if (t >= 0.99) {this.isDone = true; } 65 | } 66 | } 67 | 68 | class ArcActor extends Actor{ 69 | constructor(o, d) { 70 | super(o, d); 71 | let rotDir = randomBool() ? -1 : 1; 72 | let size = randomBool() ? 30 : 60; 73 | this.center = o.copy().add(d.copy().rotate(Math.PI / 2 * rotDir).mult(size)); 74 | this.angle = Math.floor(Math.random() * 3 + 1) * Math.PI / 2 * rotDir; 75 | this.radius = this.origin.copy().sub(this.center); 76 | } 77 | 78 | reproduce() { 79 | let o = this.getHeadPosition(this.angle); 80 | let d = this.dir.copy().rotate(this.angle); 81 | return randomBool() ? new ArcActor(o, d) : new LineActor(o, d); 82 | } 83 | 84 | getHeadPosition(angle) { 85 | return this.center.copy().add(this.radius.copy().rotate(angle)); 86 | } 87 | 88 | draw() { 89 | let a0 = bezier(this.t0) * this.angle; 90 | let a1 = bezier(this.t1) * this.angle; 91 | let aDiff = a1 - a0; 92 | let n = Math.floor(Math.abs(aDiff / 0.1)); 93 | beginShape(); 94 | for (let i = 0; i <= n; i ++) { 95 | let p = this.getHeadPosition(a0 + aDiff / n * i); 96 | vertex(p.x, p.y); 97 | } 98 | endShape(); 99 | } 100 | } 101 | 102 | 103 | class LineActor extends Actor{ 104 | constructor(o, d) { 105 | super(o, d); 106 | this.length = randomBool() ? 180 : 120; 107 | } 108 | 109 | reproduce() { 110 | let o = this.getHeadPosition(tween.powerIn(this.t0, 3)); 111 | if (randomBool()) { 112 | let d = this.dir.copy().rotate(randomBool() ? -PI / 2 : PI / 2); 113 | return new LineActor(o, d); 114 | } else { 115 | return new ArcActor(o, this.dir.copy()); 116 | } 117 | } 118 | 119 | getHeadPosition(t) { 120 | return this.origin.copy().add(this.dir.copy().mult(t * this.length)); 121 | } 122 | 123 | draw() { 124 | let p0 = this.getHeadPosition(tween.powerOut(this.t0, 3)); 125 | let p1 = this.getHeadPosition(tween.powerIn(this.t1, 3)); 126 | line(p0.x, p0.y, p1.x, p1.y); 127 | endShape(); 128 | } 129 | } 130 | 131 | function randomBool() { 132 | return Math.random() < 0.5; 133 | } 134 | 135 | /** Tween **/ 136 | 137 | /** drawCircleMarker **/ 138 | -------------------------------------------------------------------------------- /js/examples/collision/collision_triangles.js: -------------------------------------------------------------------------------- 1 | let A, B; 2 | 3 | function setup() { 4 | canvas = createCanvas(windowWidth, windowHeight); 5 | 6 | A = new Triangle(0, 0, createVector(0, -80), createVector(-80, 40), createVector(80, 40)); 7 | B = new Triangle(0, 0, createVector(0, -80), createVector(-40, 40), createVector(40, 40)); 8 | } 9 | 10 | function draw() { 11 | clear(); 12 | background(249, 246, 236); 13 | let t = radians(frameCount) / 2; 14 | A.position.x = cos(t) * 120; 15 | A.position.y = sin(t * 2) * 120; 16 | B.position.x = -cos(t * 3) * 120; 17 | B.position.y = -sin(t * 4) * 120; 18 | A.rotation = t; 19 | B.rotation = -t; 20 | A.update(); 21 | B.update(); 22 | 23 | // test 24 | 25 | let renderingData = []; 26 | let result = true; 27 | for (let i = 0; i < 3; i++) { 28 | let i0 = i; 29 | let i1 = (i + 1) % 3; 30 | let i2 = (i + 2) % 3; 31 | let vec = A.p[i1].copy().sub(A.p[i0]).normalize(); 32 | let axis = createVector(vec.y, - vec.x); 33 | let o = axis.dot(A.p[i0]); 34 | let dA0 = axis.dot(A.p[i1]); 35 | let dA1 = axis.dot(A.p[i2]); 36 | let Amin = min(dA0, dA1); 37 | let Amax = max(dA0, dA1); 38 | 39 | let dB0 = axis.dot(B.p[0]); 40 | let dB1 = axis.dot(B.p[1]); 41 | let dB2 = axis.dot(B.p[2]); 42 | let Bmin = min(dB0, dB1, dB2); 43 | let Bmax = max(dB0, dB1, dB2); 44 | 45 | renderingData.push({vec: vec, axis: axis, o: o, Amin: Amin, Amax: Amax, Bmin, Bmax, result: false}); 46 | if ( 47 | ( Bmin <= Amin && Amin <= Bmax ) || 48 | ( Bmin <= Amax && Amax <= Bmax ) || 49 | ( Amin <= Bmin && Bmin <= Amax ) || 50 | ( Amin <= Bmax && Bmax <= Amax ) 51 | ) { 52 | renderingData[i].result = true; 53 | continue; 54 | } 55 | result = false; 56 | } 57 | 58 | // rendering 59 | 60 | push(); 61 | translate(width / 2, height / 2); 62 | noStroke(); 63 | if (result) { 64 | fill(225, 81, 106); 65 | } else { 66 | fill(255); 67 | } 68 | A.draw(); 69 | B.draw(); 70 | 71 | noFill(); stroke(0); strokeWeight(1); 72 | A.draw(); 73 | B.draw(); 74 | 75 | fill(0); 76 | 77 | let n = floor(frameCount / 180) % 4; 78 | let boundary = {x: -width / 2, y: -height / 2, w: width, h: height}; 79 | for (let i = 0; i < n; i ++) { 80 | let d = renderingData[i]; 81 | let origin = A.p[i].copy(); 82 | let axis = new Line().fromPointAndVector(origin, d.axis); 83 | 84 | stroke(0); strokeWeight(1); 85 | axis.draw(boundary); 86 | 87 | let pAmin = origin.copy().add(d.axis.copy().mult(d.Amin - d.o)); 88 | let pAmax = origin.copy().add(d.axis.copy().mult(d.Amax - d.o)); 89 | let pBmin = origin.copy().add(d.axis.copy().mult(d.Bmin - d.o)); 90 | let pBmax = origin.copy().add(d.axis.copy().mult(d.Bmax - d.o)); 91 | 92 | stroke(0, 0, 0, 32); 93 | axis.getPerpendicular(pAmin).draw(boundary); 94 | axis.getPerpendicular(pAmax).draw(boundary); 95 | axis.getPerpendicular(pBmin).draw(boundary); 96 | axis.getPerpendicular(pBmax).draw(boundary); 97 | 98 | if (d.result) { 99 | let sorted = [d.Amin, d.Amax, d.Bmin, d.Bmax].sort((a,b)=>{return a > b ? 1: -1}); 100 | let p0 = origin.copy().add(d.axis.copy().mult(sorted[1] - d.o)); 101 | let p1 = origin.copy().add(d.axis.copy().mult(sorted[2] - d.o)); 102 | stroke(0); strokeWeight(3); 103 | line(p0.x, p0.y, p1.x, p1.y); 104 | } 105 | } 106 | pop(); 107 | } 108 | 109 | class Triangle { 110 | constructor(x, y, p0, p1, p2) { 111 | this.position = createVector(x, y); 112 | this.op = []; 113 | this.p = []; 114 | this.op.push(p0); 115 | this.op.push(p1); 116 | this.op.push(p2); 117 | this.p.push(createVector()); 118 | this.p.push(createVector()); 119 | this.p.push(createVector()); 120 | this.rotation = 0; 121 | this.update(); 122 | } 123 | 124 | update() { 125 | for (let i = 0; i < 3; i ++) { 126 | this.p[i] = rotate2d(this.op[i], this.rotation).add(this.position); 127 | } 128 | } 129 | 130 | draw() { 131 | beginShape(); 132 | for (let i = 0; i < 3; i ++) { 133 | vertex(this.p[i].x, this.p[i].y); 134 | } 135 | endShape(CLOSE); 136 | } 137 | } 138 | 139 | /** rotate2d **/ 140 | 141 | /** Line **/ 142 | -------------------------------------------------------------------------------- /collision.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Detecting Collision 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |

Detecting Collision

16 |

Sketching with Math and Quasi Physics

17 |

There is a newer and more elaborate version of this site.
Visit Sketching with Math and Quasi Physics on Notion.

18 |

Circles

19 |

Two circles, 20 | \(A\) and 21 | \(B\) collide if the distance between the center points is equal or less than the two radii added together.

22 |
23 |

$$distance(A_{center},B_{center}) \leq A_{radius}+B_{radius}\Rightarrow$$

24 |

$$distance(A_{center},B_{center})-A_{radius}-B_{radius}\leq 0$$

25 |
26 | 27 |
28 | 29 |

Line and Circle

30 |

The distance from a point 31 | \(P\) to a line that passes through points 32 | \(A\) and 33 | \(B\) is

34 |
35 |

$$distance=\left\|\overrightarrow{AP}\right\|\left|\sin(\theta)\right|=\left|(\overrightarrow{AP}_x\cdot \overrightarrow{AB}_y-\overrightarrow{AB}_x\cdot \overrightarrow{AP}_y)\right|/\left\|\overrightarrow{AB}\right\|$$ where 36 | \(\theta\) is the angle between 37 | \(\overrightarrow{AP}\) and 38 | \(\overrightarrow{AB}\)

39 |
40 |
41 | 42 |

Rectangles

43 |

Two rectangles, 44 | \(A\) and 45 | \(B\) that are axis aligned collide if

46 |
47 |

$$(A_{left}\leq B_{right})\;\wedge\;(A_{right}\geq B_{left})\;\wedge\;(A_{top}\leq B_{bottom})\wedge(A_{bottom}\geq B_{top})$$

48 |
49 |

This is to project the shapes to x and y-axis and checking overlap on each axis. Note that screen coordinate system in which y increase from top to bottom is assumed.

50 | 51 |
52 | 53 | 54 |

Triangles

55 |

Similarly, collision between two convex polygons can be detected by projecting the shapes to the vectors perpendicular to each side of one of the polygons.

56 | 57 |
58 | 59 |

Learn More

60 |

Collision Detection Using the Separating Axis Theorem

61 | 62 |
63 |
64 | 65 |

index

66 | 67 |
68 | 69 | 70 | 71 | 72 | 73 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /js/main.js: -------------------------------------------------------------------------------- 1 | let elms = document.getElementsByClassName("example"); 2 | let flms = []; 3 | let codeLoadCnt = 0; 4 | 5 | Array.prototype.forEach.call(elms, (elm, i)=> { 6 | let h = elm.clientWidth, w = elm.clientHeight; 7 | 8 | let iframe = createElement(document, "iframe", {scrolling:"no"}, ""); 9 | elm.appendChild(iframe); 10 | var interval = setInterval(function() { 11 | var frame = iframe.contentWindow; 12 | var doc = iframe.contentDocument || iframe.contentWindow.document; 13 | 14 | 15 | if(doc.readyState == "complete") { 16 | clearInterval(interval); 17 | buildIframeContents(elm, frame, doc, i); 18 | } 19 | flms.push(frame); 20 | }, 100); 21 | }); 22 | 23 | function buildIframeContents(elm, frame, doc, i) { 24 | let scriptPath = "js/examples/" + elm.getAttribute("data-embed") + ".js"; 25 | frame.__parent = window; 26 | frame.__autorun = (i == 0); 27 | 28 | let css = createElement(doc, "link", {href: "css/example.css", rel: "stylesheet"}, ""); 29 | doc.head.appendChild(css); 30 | appendScript(doc, "js/examples/lib.js") 31 | .then(()=>{appendScript(doc, scriptPath) 32 | .then(()=>{appendScript(doc, "js/examples/p5setup.js") 33 | .then(()=>{appendScript(doc, "https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.3/p5.min.js") 34 | }) }); 35 | 36 | if (i != 0) { 37 | createExampleFooter(elm, frame, scriptPath); 38 | } 39 | }); 40 | } 41 | 42 | function createExampleFooter(elm, frame, scriptPath) { 43 | let exampleFooter = createElement(document, "p", {class: "example-footer"}, ""); 44 | let buttonContainer = createElement(document, "div", {class: "button-container"}, ""); 45 | let note = createElement(document, "span", {class: "note"}, "Click the example to run"); 46 | 47 | let open = createElement(document, "a", {href: "", class: "button"}, "Show sample code"); 48 | let edit = createElement(document, "a", {href: elm.getAttribute("data-edit"), class: "button", target:"_blank"}, "Edit in Codepen"); 49 | exampleFooter.appendChild(note); 50 | buttonContainer.appendChild(open); 51 | buttonContainer.appendChild(edit); 52 | exampleFooter.appendChild(buttonContainer); 53 | elm.parentNode.insertBefore(exampleFooter, elm.nextElementSibling); 54 | 55 | let xhr = new XMLHttpRequest(); 56 | xhr.open('GET', scriptPath); 57 | xhr.onload = ()=> { 58 | let script = xhr.responseText; 59 | let re = /\/\*\* (.+) \*\*\//g; 60 | let match = script.match(re); 61 | if (match) { 62 | Array.prototype.forEach.call(match, (s)=>{ 63 | let func = frame[s.split(" ")[1]]; 64 | if (func) { 65 | script = script.replace(s, func.toString()); 66 | } else { 67 | console.log("failed replacing comment with code: " + s); 68 | } 69 | }); 70 | } 71 | 72 | let text = document.createTextNode(script); 73 | let code = createElement(document, "code", {}, text); 74 | let pre = createElement(document, "pre", {class: "javascript"}, code); 75 | let codeBlock = createElement(document, "div", {class: "code-block hidden"}, pre); 76 | 77 | elm.parentNode.insertBefore(codeBlock, exampleFooter.nextElementSibling); 78 | 79 | open.addEventListener("click", (evt)=>{ 80 | evt.preventDefault(); 81 | if (codeBlock.classList.contains("hidden")) { 82 | open.innerHTML = "Hide sample code"; 83 | codeBlock.classList.remove("hidden"); 84 | } else { 85 | open.innerHTML = "Show sample code"; 86 | codeBlock.classList.add("hidden"); 87 | } 88 | }); 89 | if (--codeLoadCnt == 0) { 90 | hljs.initHighlightingOnLoad(); 91 | } 92 | } 93 | codeLoadCnt ++; xhr.send(); 94 | } 95 | 96 | function appendScript(doc, path) { 97 | return new Promise((resolve, reject)=>{ 98 | let script = createElement(doc, "script", {src: path}, ""); 99 | script.onload = ()=>{resolve();}; 100 | doc.head.appendChild(script); 101 | }); 102 | }; 103 | 104 | function focusExample(flm) { 105 | for (let i = 0; i < flms.length; i ++) { 106 | flms[i].stop(); 107 | } 108 | flm.start(); 109 | } 110 | 111 | function createElement(doc, tag, attr, content) { 112 | let elm = doc.createElement(tag); 113 | Object.keys(attr).forEach(function(key) { 114 | elm.setAttribute(key, attr[key]); 115 | var value = this[key]; 116 | }); 117 | if (typeof content == 'string') { 118 | elm.innerHTML = content; 119 | } else { 120 | elm.appendChild(content); 121 | } 122 | return elm; 123 | } 124 | -------------------------------------------------------------------------------- /wave.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Wave Anatomy 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 |

Wave Anatomy

16 |

Sketching with Math and Quasi Physics

17 |

There is a newer and more elaborate version of this site.
Visit Sketching with Math and Quasi Physics on Notion.

18 |

Sine Wave

19 |
20 | 21 |

Square Wave (Additive Synthesis)

22 |

23 | $${\displaystyle {\begin{aligned}x_{\text{square}}(t)&={\frac {4}{\pi }}\sum _{k=1}^{\infty }{\frac {\sin \left(2\pi (2k-1)ft\right)}{2k-1}}\\&={\frac {4}{\pi }}\left(\sin(2\pi ft)+{\frac {1}{3}}\sin(6\pi ft)+{\frac {1}{5}}\sin(10\pi ft)+\dots \right)\end{aligned}}}$$

24 |
25 | 26 |

Triangle Wave (Additive Synthesis)

27 |

28 | $${\displaystyle {\begin{aligned}x_{{\mathrm {triangle}}}(t)&{}={\frac {8}{\pi ^{2}}}\sum _{{k=0}}^{\infty }(-1)^{k}\,{\frac {\sin \left(2\pi (2k+1)ft\right)}{(2k+1)^{2}}}\\&{}={\frac {8}{\pi ^{2}}}\left(\sin(2\pi ft)-{1 \over 9}\sin(6\pi ft)+{1 \over 25}\sin(10\pi ft)-\cdots \right)\end{aligned}}}$$

29 |
30 | 31 |

Sawtooth Wave (Additive Synthesis)

32 |

33 | $${\displaystyle x_{\mathrm {reversesawtooth} }(t)={\frac {2}{\pi }}\sum _{k=1}^{\infty }{(-1)}^{k}{\frac {\sin(2\pi kft)}{k}}}$$

34 |

$${\displaystyle x_{\mathrm {sawtooth} }(t)={\frac {1}{2}}-{\frac {1}{\pi }}\sum _{k=1}^{\infty }{(-1)}^{k}{\frac {\sin(2\pi kft)}{k}}}$$

35 |
36 | 37 |

Simple Harmonic Motion

38 |

Simple harmonic motion is a type of periodic motion where the restoring force obeys Hooke's Law ( 39 | \(\mathbf F=-k\mathbf x\)). The motion is sinusoidal in time and demonstrates a single resonant frequency.

40 |
41 | 42 |

Wave Equation

43 | 44 |
45 |

One dimension

46 |

$${\displaystyle {\partial ^{2}u \over \partial t^{2}}=c^{2}{\partial ^{2}u \over \partial x^{2}}}$$

47 |

Approximation in Code

48 |

$$\frac{\partial^2u}{\partial t^2}\Rightarrow\;(u_{t+1}-u_t)\;-\;(u_{t-1}-u_t)\;=\;u_{t+1}\;+\:u_{t-1\;}-\;2u_t$$

49 |

$$c^2\frac{\partial^2u}{\partial x^2}\Rightarrow\;c^2\left[(u_{x-1}-u_x)\;-\;(u_{x+1}-u_x)\right]\;=\;c^2(u_{x-1}\;+u_{x+1\;}-\;2u_x)$$

50 |

$$u_{t+1}\;+\:u_{t-1\;}-\;2u_t=\;c^2(u_{x-1}\;+u_{x+1\;}-\;2u_x)$$

51 |

$$u_{t+1}\;=\;2u_t\;\;+\;c^2(u_{x-1}\;+u_{x+1\;}-\;2u_x)\;-\:u_{t-1\;}$$

52 |
53 |
54 | 55 |

Two dimensions

56 | 57 |
58 |
59 |
60 |
61 |

$$\frac{\partial^2u}{\partial t^2}=c^2\left(\frac{\partial^2u}{\partial x^2}\;+\;\frac{\partial^2u}{\partial y^2}\right)$$

62 |

$$\Rightarrow u_{t+1}\;=\;2u_t\;\;+\;c^2(u_{x-1}\;+u_{x+1\;}+u_{y-1}\;+u_{y+1\;}-\;4u_x)\;-\:u_{t-1\;}$$

63 |

Open WebGL Demo Source

64 |
65 |
66 | 67 |

index

68 | 69 |
70 | 71 | 72 | 73 | 74 | 75 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /handy_math.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Handy Math 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 |

Handy Math

16 |

Sketching with Math and Quasi Physics

17 |

There is a newer and more elaborate version of this site.
Visit Sketching with Math and Quasi Physics on Notion.

18 |

Sine, Cosine and Tangent

19 | 20 |
21 |

$$\mathrm{sine}=\frac{\mathrm{opposite}}{\mathrm{hypotenuse}}$$

22 |

$$\mathrm{cosine}=\frac{\mathrm{adjacent}}{\mathrm{hypotenuse}}$$

23 |

$$\mathrm{tangent}=\frac{\mathrm{sine}}{\mathrm{cosine}}$$

24 |
25 |

* Note that p5.js is based on the screen coordinate system in which y increase from top to bottom. The opposite is flipped by multiplying -1 in the rendering below.

26 |
27 | 28 | 29 |

Rotating 2D points

30 | 31 |
32 |

$$\begin{bmatrix}x'\\y'\end{bmatrix}=\begin{bmatrix}\cos\theta&-\sin\theta\\\sin\theta&\cos\theta\end{bmatrix}\begin{bmatrix}x\\y\end{bmatrix}$$

33 |
34 |
35 |

Learn more

36 |

A Gentle Primer on 2D Rotations

37 | 38 |

Euclidean distance between two points

39 | 40 |

Euclidean distance is the most intuitive, "ordinary" straight-line distance between two points in Euclidean space.

41 |
42 |

Two dimensions

43 |

$$p=(p_x,p_y),\;q=(q_x,q_y)$$

44 |

$$\mathrm{distance}(p,q)=\sqrt{(p_x-q_x)^2+(p_y-q_y)^2}$$

45 | 46 |

n dimensions

47 |

$$p=(p_1,p_2\cdots,p_n),\;q=(q_1,q_2\cdots,q_n)$$

48 |

$$\mathrm{distance}(p,q)=\sqrt{(p_1-q_1)^2+(p_2-q_2)^2+\cdots+(p_n-q_n)^2}=\sqrt{\sum_{i=1}^n(p_i+q_i)^2}$$

49 |
50 |
51 | 52 | 53 |

Manhattan distance between two points

54 |

Euclidean distance is not the only way to measure distance between two points. In mathmatics, distance is defined by a function called distance function or metric, which can be any function that satisfy a set of conditions.

55 |

Manhattan distance is an example of a non-Euclidean distance, which is the sum of the absolute differences of their Cartesian coordinates, or the distance a car would drive in a city laid out in square blocks.

56 |
57 |

Two dimensions

58 |

$$p=(p_x,p_y),\;q=(q_x,q_y)$$

59 |

$$distance\left(p,\;q\right)\;=\;\left|p_x\;-\;q_x\right|\;-\;\left|p_y\;-\;qy_{}\right|\;$$

60 |
61 |
62 | 63 | 64 |

Perspective Projection (Fixed Camera)

65 | 66 |

\(\lbrack x,y,z\rbrack\) projects to \(\left[x\frac{\displaystyle d_1}{\displaystyle d_2},y\frac{\displaystyle d_1}{\displaystyle d_2}\right]\)

67 |

68 |

69 |
\(d_1=\) Focal length
70 |
the axial distance from the camera center to the image plane
71 |
\(d_2=\) Subject distance
72 |
the axial distance from the camera center to the subject
73 |
74 |

75 | 76 |
77 | 78 | 79 |

Dot Product

80 |

Two dimensions

81 |
82 |

$${\boldsymbol v}_\mathbf1=\lbrack x_1,y_1\rbrack,\hspace{8px}{\boldsymbol v}_\mathbf2=\lbrack x_2,y_2\rbrack$$

83 |

$${\boldsymbol v}_\mathbf1\cdot{\boldsymbol v}_\mathbf2=x_1x_2+y_1y_2$$

84 |
85 |

n dimensions

86 |
87 |

$$\boldsymbol a=\lbrack a_1,a_2,a_3,\dots,a_n\rbrack,\hspace{8px}\boldsymbol b=\lbrack b_1,b_2,b_3,\dots,b_n\rbrack$$

88 |

$$\boldsymbol a\cdot\boldsymbol b=a_1b_1+a_2b_2+a_3b_3\dots a_nb_n$$

89 |
90 |

91 |

96 |

97 |
98 | 99 |

Cross Product

100 |

3 dimensions

101 |
102 |

$${\boldsymbol v}_\mathbf1=\lbrack x_1,y_1,z_1\rbrack,\;{\boldsymbol v}_\mathbf2=\lbrack x_2,y_2,z_2\rbrack$$

103 |

$${\boldsymbol v}_\mathbf1\times{\boldsymbol v}_\mathbf2=\lbrack y_1z_2-z_1y_2,z_1x_2-x_1z_2,x_1y_2-y_1x_2\rbrack$$

104 |
105 |

106 |

110 |

111 |
112 | 113 |

index

114 | 115 |
116 | 117 | 118 | 119 | 120 | 121 | 124 | 125 | 126 | 127 | 128 | -------------------------------------------------------------------------------- /drawings.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Geometric Drawings 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 |

Geometric Drawings

16 |

Sketching with Math and Quasi Physics

17 |

There is a newer and more elaborate version of this site.
Visit Sketching with Math and Quasi Physics on Notion.

18 |

Lines

19 |

Two lines, 20 | \(a_1x+by_1+\;c_1=0\) 21 | and 22 | \(a_2x+by_2+\;c_2=0\) 23 | intersect if 24 | \(a_1b_2\neq a_2b_1\) 25 |

26 |

The point of intersection is

27 |
28 |

$$x=(b_1c_2-b_2c_1)/(a_1b_2-a_2b_1)$$

29 |

$$y=(a_1c_2-a_2c_1)/(a_1b_2-a_2b_1)$$

30 |
31 | 32 |
33 | 34 |

Line Segments

35 |
36 | 37 |

Circle

38 |
39 | 40 |

Bézier Curve

41 | 42 |

Linear Bézier curves

43 |

A linear Bézier curve is simply a straight line between two points, which can be defined as linear interpolation between two points

44 |

$$\mathbf {B}_{linear\mathbf {P} _{0}, \mathbf {P} _{1}} (t)=(1-t)\mathbf {P} _{0}+t\mathbf {P} _{1}{\mbox{ , }}0\leq t\leq 1$$

45 | 46 |

Quadratic Bézier curves

47 |

A quadratic Bézier curves can be defined as linear interpolation between corresponding points on two linear Bézier curves.

48 |

$${\displaystyle \mathbf {B}_{quadratic \mathbf {P} _{0}, \mathbf {P} _{1}, \mathbf {P} _{2}} (t)=(1-t)\mathbf {B}_{linear \mathbf {P} _{0}, \mathbf {P} _{1}}(t)+t\mathbf {B}_{linear \mathbf {P} _{0}, \mathbf {P} _{2}}(t){\mbox{ , }}0\leq t\leq 1}$$

49 |

Qubic Bézier curves

50 |

And a cubic Bézier curves is linear interpolation between corresponding points on two quadratic Bézier curves.

51 |

$$\mathbf {B}_{cubic{\mathbf {P} _{0}, \mathbf {P} _{1}, \mathbf {P} _{2},\mathbf {P} _{3}}} (t)=(1-t)\mathbf {B} _{quadratic \mathbf {P} _{0},\mathbf {P} _{1},\mathbf {P} _{2}}(t)+t\mathbf {B} _{quadratic \mathbf {P} _{1},\mathbf {P} _{2},\mathbf {P} _{3}}(t)$$

52 |

$$=(1-t)^{3}\mathbf {P} _{0}+3(1-t)^{2}t\mathbf {P} _{1}+3(1-t)t^{2}\mathbf {P} _{2}+t^{3}\mathbf {P} _{3}{\mbox{ , }}0\leq t\leq 1$$

53 |
54 | 55 |
56 | 57 |

B-Spline Curve

58 |

\(P\) is a set of control points and \(t\) is a vector of non-decreasing numbers called "knot vector" which has \(number\;of\;control\;points\;+\;order\;of\;the\;curve(n)\;+\;1\) elements, e.g. \((0, 0, 0, 1, 2, 3, 3, 3)\).

B-spline basis function, \(B\) is defined on the knot vector and used to weight the control points.

59 |
60 | 61 |

$${\displaystyle Spline_{n,t}(x)=\sum _{i}P _{i}B_{i,n}(x)}$$

62 |

$$B_{i,0}(x):=$$

63 |

$$\begin{array}{lc}1&\mathrm{if}\; {t}_{i}\leq{x}<{t}_{i + 1}\end{array},$$

64 |

$$\begin{array}{lc}0&otherwise\end{array}$$

65 |

$${\displaystyle B_{i,k}(x):={\frac {x-t_{i}}{t_{i+k}-t_{i}}}B_{i,k-1}(x)+{\frac {t_{i+k+1}-x}{t_{i+k+1}-t_{i+1}}}B_{i+1,k-1}(x)}$$

66 | 67 |
68 | 69 |

Learn more

70 |

B-spline Basis Functions: Definition

71 |

B-spline curve

72 | 73 |

Circumcircle and Circumcenter

74 |

75 |

76 |
Circumcircle
77 |
The circle that passes through three vertices of a triangle.
78 |
Circumcenter
79 |
The center of the circumcircle. The intersection of the perpendicular bisectors of a triangle.
80 |
81 |

82 |
83 | 84 |

Incircle and Incenter

85 |

86 |

87 |
Incircle
88 |
The circle tangent to the three sides of a triangle.
89 |
Incenter
90 |
The center of the incircle. The intersection of angle bisectors of a triangle.
91 |
92 |

93 |
94 | 95 |

Excircles and Excenters

96 |

97 |

98 |
Excircles
99 |
The circles tangent to one of a triangle's sides and to the extensions of the other two.
100 |
Excenters
101 |
The centers of an excircles. Points where the external angle bisectors of a triangle intersect.
102 |
103 |

104 | 105 |
106 | 107 |

Orthocenter

108 |

109 |

110 |
Orthocenter
111 |
The intersection of the three altitudes of a triangle.
112 |
113 |

114 |
115 | 116 |

Centroid

117 |

118 |

119 |
Centroid
120 |
The arithmetic mean position of all the points in a shape. The centroid of a triangle is the intersection of its three medians.
121 |
122 |

123 |
124 | 125 |

Reuleaux Triangle

126 |

127 | A reuleaux triangle can rotate within a square while touching all four sides of a square. 128 |

129 |
130 | 131 |

Spinner

132 |
133 | 134 |

index

135 | 136 |
137 | 138 | 139 | 140 | 141 | 142 | 145 | 146 | 147 | 148 | --------------------------------------------------------------------------------