main.js (8830B)
1 Math.TAU = 2 * Math.PI; 2 3 (function() { 4 let turn = 0; 5 let last_time = 0; 6 let math_ts; 7 let math_yss; 8 let math_polygon_radiuses; 9 let canvas_ts; 10 let canvas_yss; 11 let canvas_polygon_radiuses; 12 13 const NUMBER_OF_POLYGONS = 7; 14 const POLYGON_HEIGHT_MARGIN = 0.075; 15 const TURN_PER_MILLISECOND = 0.0005; 16 const CYCLE_LENGTH = 20; 17 const NUMBER_OF_SAMPLES = 500; 18 const START_T = 0; 19 const END_T = CYCLE_LENGTH; 20 const math_graph_y_extents = [ 21 [-1.2, 1.2], 22 [-1.2, 1.2], 23 [-1.2, 1.2], 24 [-1.2, 1.2], 25 [-1.2, 1.2], 26 [-1.2, 1.2], 27 [-1.2, 1.2], 28 ]; 29 30 31 32 const functions = [ 33 /* 34 turn => step_on(turn % CYCLE_LENGTH, 1, 1.5) - step_on(turn % CYCLE_LENGTH, 2, 2.5) + 35 step_on(turn % CYCLE_LENGTH, 3, 3.5) - step_on(turn % CYCLE_LENGTH, 4, 4.5) + 36 step_on(turn % CYCLE_LENGTH, 5, 5.5) - step_on(turn % CYCLE_LENGTH, 6, 6.5) + 37 38 step_on(turn % CYCLE_LENGTH, 7, 7.5) - step_on(turn % CYCLE_LENGTH, 8.5, 9) + 39 step_on(turn % CYCLE_LENGTH, 9.5, 10) - step_on(turn % CYCLE_LENGTH, 11, 11.5) + 40 step_on(turn % CYCLE_LENGTH, 12, 12.5) - step_on(turn % CYCLE_LENGTH, 13.5, 14) + 41 42 step_on(turn % CYCLE_LENGTH, 14.5, 15) - step_on(turn % CYCLE_LENGTH, 15.5, 16) + 43 step_on(turn % CYCLE_LENGTH, 16.5, 17) - step_on(turn % CYCLE_LENGTH, 17.5, 18) + 44 step_on(turn % CYCLE_LENGTH, 18.5, 19) - step_on(turn % CYCLE_LENGTH, 19.5, 20), 45 */ 46 turn => Math.sin(turn * Math.TAU / CYCLE_LENGTH * 7), 47 turn => Math.sin(turn * Math.TAU / CYCLE_LENGTH * 6), 48 turn => Math.sin(turn * Math.TAU / CYCLE_LENGTH * 5), 49 turn => Math.sin(turn * Math.TAU / CYCLE_LENGTH * 4), 50 turn => Math.sin(turn * Math.TAU / CYCLE_LENGTH * 3), 51 turn => Math.sin(turn * Math.TAU / CYCLE_LENGTH * 2), 52 turn => Math.sin(turn * Math.TAU / CYCLE_LENGTH), 53 ]; 54 55 function quadratic_stepper(t) { 56 if (t < 0) { 57 return 0; 58 } else if (t < 0.5) { 59 return 2 * t**2; 60 } else if (t < 1) { 61 return (-2*((t-1)**2) + 1); 62 } else { 63 return 1; 64 } 65 } 66 67 function lerp(t, a, b) { 68 return (b - a) * t + a; 69 } 70 71 function superlerp(t, a, b, c, d) { 72 // [a, b] ; -a 73 // [0, b-a] ; /(b-a) 74 // [0, 1] ; *(d-c) 75 // [0, d-c] ; +c 76 // [c, d] 77 return (t - a) / (b - a) * (d - c) + c; 78 } 79 80 function step_on(t, a, b) { 81 return quadratic_stepper(superlerp(t, a, b, 0, 1)); 82 } 83 84 function step_on_step_off(t, a, b, c, d) { 85 return step_on(t, a, b) - step_on(t, c, d); 86 } 87 88 function from_math_coordinates_to_canvas_coordinates(x, y, x_extents, y_extents) { 89 let canvas = document.getElementById('canvas'); 90 91 // a, b / - a 92 // 0, b-a / * w 93 // 0, (b-a)w / /(b-a) 94 // 0, w 95 96 // a, b / *(-1) 97 // -b, -a / +b 98 // 0, (b-a) / * h 99 // 0, (b-a)h / /(b-a) 100 // 0, h 101 102 return [ 103 (x - x_extents[0]) * canvas.width / (x_extents[1] - x_extents[0]), 104 ((-y) + y_extents[1]) * canvas.height / (y_extents[1] - y_extents[0]), 105 ]; 106 } 107 108 function make_regular_polygon_path2d(x, y, radius, number_of_sides, turn) { 109 // `x` and `y` are the center of the triangle. 110 // `radius` is the distance from the center to a vertex. 111 // `number_of_sides` of the polygon. 112 // `turn` is a value [0, 1] where 1 is a full turn. 113 114 let regular_polygon = new Path2D(); 115 116 regular_polygon.moveTo( 117 x + Math.cos(turn * Math.TAU) * radius, 118 y + Math.sin(turn * Math.TAU) * radius, 119 ); 120 121 for (let vertex_i = 1; vertex_i < number_of_sides; vertex_i++) { 122 regular_polygon.lineTo( 123 x + Math.cos((vertex_i / number_of_sides + turn) * Math.TAU) * radius, 124 y + Math.sin((vertex_i / number_of_sides + turn) * Math.TAU) * radius, 125 ); 126 } 127 128 regular_polygon.closePath(); 129 130 return regular_polygon; 131 } 132 133 function linspace(start, stop, number_of_points) { 134 let l = []; 135 for (let point_i = 0; point_i < number_of_points; point_i++) { 136 l.push(superlerp(point_i, 0, number_of_points - 1, start, stop)); 137 } 138 return l; 139 } 140 141 function draw_polygon(number_of_sides, turn, polygon_center_y, polygon_radius_addition) { 142 let canvas = document.getElementById('canvas'); 143 let ctx = canvas.getContext('2d'); 144 145 ctx.beginPath() 146 ctx.fillStyle = '#f0f'; // Fuchia from https://www.w3schools.com/colors/colors_names.asp 147 ctx.strokeStyle = '#f0f'; 148 ctx.fill( 149 make_regular_polygon_path2d( 150 canvas.width / 2, 151 polygon_center_y, 152 20 * canvas.height / 500 + polygon_radius_addition, 153 number_of_sides, 154 turn, 155 ), 156 ); 157 ctx.stroke( 158 make_regular_polygon_path2d( 159 canvas.width / 2, 160 polygon_center_y, 161 20 * canvas.height / 500 + polygon_radius_addition, 162 number_of_sides, 163 turn, 164 ), 165 ); 166 } 167 168 function draw_graph(polygon_i) { 169 let canvas = document.getElementById('canvas'); 170 let ctx = canvas.getContext('2d'); 171 172 ctx.beginPath() 173 ctx.strokeStyle = 'white'; 174 ctx.moveTo(canvas_ts[0], canvas_yss[polygon_i][0]); 175 for (let point_i = 1; point_i < NUMBER_OF_SAMPLES; point_i++) { 176 ctx.lineTo(canvas_ts[point_i], canvas_yss[polygon_i][point_i]); 177 } 178 ctx.stroke(); 179 } 180 181 function draw_position_circle(x, y) { 182 let canvas = document.getElementById('canvas'); 183 let ctx = canvas.getContext('2d'); 184 185 ctx.beginPath(); 186 ctx.fillStyle = "#00bfff"; // DeepSkyBlue from https://www.w3schools.com/colors/colors_names.asp 187 ctx.arc(x, y, canvas.height / 80, 0, Math.TAU, false); 188 ctx.fill(); 189 } 190 191 function draw() { 192 let canvas = document.getElementById('canvas'); 193 let ctx = canvas.getContext('2d'); 194 195 ctx.beginPath(); 196 ctx.fillStyle='black'; 197 ctx.fillRect(0, 0, canvas.width, canvas.height); 198 199 ctx.beginPath(); 200 ctx.strokeStyle = 'white'; 201 202 //ctx.moveTo(START_T, functions[polygon_i](-1) * canvas.width); 203 204 ctx.lineWidth = 2; 205 206 // [50, h - 50] 207 // 0, 1, 2 / /n-1 208 // 0, 0.5, 1 / *(h-h0.1) 209 // 0, 0.5(0.9h), 0.9h / +h0.05 210 // 50, 0.5(h-100)+0.05h, 0.9h+0.05h 211 for (let polygon_i = 0; polygon_i < NUMBER_OF_POLYGONS; polygon_i++) { 212 // top and bottom of the are dedicated to this graph. 213 let y_top = polygon_i * canvas.height / NUMBER_OF_POLYGONS; 214 let y_bottom = (polygon_i + 1) * canvas.height / NUMBER_OF_POLYGONS; 215 216 // the vertical value of the center of the current polygon. 217 let polygon_y = superlerp( 218 polygon_i, 219 0, 220 NUMBER_OF_POLYGONS - 1, 221 POLYGON_HEIGHT_MARGIN * canvas.height, 222 canvas.height * (1 - POLYGON_HEIGHT_MARGIN), 223 ); 224 225 // Get all cached values of the mathematical and graphical 226 let current_sample_i = Math.floor(turn / CYCLE_LENGTH * canvas_ts.length) % canvas_ts.length; 227 let current_math_t = math_ts[current_sample_i]; //superlerp(current_math_t, START_T, END_T, 0, canvas.width); 228 let current_canvas_t = canvas_ts[current_sample_i]; 229 let current_math_y = math_yss[polygon_i][current_sample_i]; 230 //let current_math_y = functions[1](current_math_t); 231 let current_canvas_y = canvas_yss[polygon_i][current_sample_i]; // superlerp(current_math_y, -1.2, 1.2, y_bottom, y_top); 232 233 draw_graph(polygon_i); 234 235 draw_polygon(polygon_i + 3, current_math_y, polygon_y, canvas_polygon_radiuses[polygon_i][current_sample_i]); 236 237 draw_position_circle(current_canvas_t, current_canvas_y); 238 } 239 } 240 241 function step(time_since_start) { 242 draw(); 243 244 turn = TURN_PER_MILLISECOND * time_since_start; 245 246 window.requestAnimationFrame(step); 247 } 248 249 function resize_window() { 250 // https://stackoverflow.com/a/32119392 251 252 let canvas = document.getElementById('canvas'); 253 254 canvas.width = window.innerWidth; 255 canvas.style.width = window.innerWidth; 256 canvas.height = window.innerHeight; 257 canvas.style.height = window.innerHeight; 258 259 cache_canvas_data(); 260 } 261 262 function cache_math_data() { 263 math_ts = Float32Array.from(linspace(START_T, END_T, NUMBER_OF_SAMPLES)); 264 math_yss = []; 265 math_polygon_radiuses = []; 266 for (let polygon_i = 0; polygon_i < NUMBER_OF_POLYGONS; polygon_i++) { 267 math_yss.push(Float32Array.from(math_ts)); 268 for (let t_i = 0; t_i < math_ts.length; t_i++) { 269 let y = functions[polygon_i](math_ts[t_i]); 270 math_yss[polygon_i][t_i] = y; 271 } 272 math_polygon_radiuses[polygon_i] = math_yss[polygon_i].map(y => (y > 0.7) ? (y - 0.7) : 0); 273 } 274 } 275 276 function cache_canvas_data() { 277 let canvas = document.getElementById('canvas'); 278 279 canvas_yss = []; 280 canvas_polygon_radiuses = []; 281 for (let polygon_i = 0; polygon_i < NUMBER_OF_POLYGONS; polygon_i++) { 282 let y_top = polygon_i * canvas.height / NUMBER_OF_POLYGONS; 283 let y_bottom = (polygon_i + 1) * canvas.height / NUMBER_OF_POLYGONS; 284 285 canvas_ts = math_ts.map(t => superlerp(t, START_T, END_T, 0, canvas.width)); 286 canvas_yss[polygon_i] = math_yss[polygon_i].map( 287 y => superlerp( 288 y, 289 math_graph_y_extents[polygon_i][0], 290 math_graph_y_extents[polygon_i][1], 291 y_bottom, y_top)); 292 canvas_polygon_radiuses[polygon_i] = math_polygon_radiuses[polygon_i].map(x => x * (canvas.height / 500) * 100) 293 } 294 } 295 296 function main() { 297 cache_math_data(); 298 resize_window(); 299 300 window.addEventListener('resize', resize_window) 301 window.requestAnimationFrame(step); 302 } 303 304 window.addEventListener('load', main); 305 })();