├── 3dlines.html ├── mawaru.html └── 3dlines.js /3dlines.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 3D lines 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |

3D lines

12 | 13 | 14 | 15 | 16 |

17 | FPS: 18 | 19 |

20 | 21 |

22 |

23 | 24 | 25 | -------------------------------------------------------------------------------- /mawaru.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | mawaru yatsu 4 | 5 | 6 | 58 | 59 | 60 | 61 | 62 |

mawaru yatsu

63 | 64 | 65 | 66 | 67 |

68 | 69 | 70 |


71 |
72 | 73 | Last modified: Sat Jun 27 01:49:00 JST 2009 74 | 75 | 76 | -------------------------------------------------------------------------------- /3dlines.js: -------------------------------------------------------------------------------- 1 | var NUM_HISTS = 50; 2 | var NUM_LINES = 50; 3 | var SW = 640; 4 | var SH = 480; 5 | 6 | var MAX_DIST = 400; 7 | 8 | var TIMEOUT_MSEC = 16; 9 | 10 | var Z_FACTOR = 200; 11 | var WIDTH_FACTOR = 50; 12 | 13 | var LINE_VEL = 20; 14 | var CAM_VEL_FACTOR = 30; 15 | 16 | var FILL_ALPHA = 10; 17 | var FILL_ALPHA_STR = ''; 18 | 19 | var V = function(x, y, z) { 20 | if (this == window) 21 | return new V(x, y, z); 22 | this.x = x; 23 | this.y = y; 24 | this.z = z; 25 | 26 | this.assign = function(p) { 27 | this.x = p.x; 28 | this.y = p.y; 29 | this.z = p.z; 30 | }; 31 | this.add = function(p) { 32 | this.x += p.x; 33 | this.y += p.y; 34 | this.z += p.z; 35 | }; 36 | this.neg = function() { 37 | this.x = -this.x; 38 | this.y = -this.y; 39 | this.z = -this.z; 40 | }; 41 | 42 | this.dist = function(p) { 43 | var dx = this.x - p.x; 44 | var dy = this.y - p.y; 45 | var dz = this.z - p.z; 46 | return Math.sqrt(dx * dx + dy * dy + dz * dz); 47 | }; 48 | }; 49 | 50 | var Line = function(p, v) { 51 | if (this == window) 52 | return new Line(p, v); 53 | 54 | this.hist = new Array(NUM_HISTS); 55 | for (var i = 0; i < this.hist.length; i++) { 56 | this.hist[i] = V(p.x, p.y, p.z); 57 | } 58 | this.index = 0; 59 | this.v = v; 60 | 61 | this.cur = function() { 62 | return this.hist[this.index]; 63 | }; 64 | this.next = function() { 65 | this.index = (this.index + 1) % this.hist.length; 66 | return this.cur(); 67 | }; 68 | this.nextIndex = function(index) { 69 | return (index + 1) % this.hist.length; 70 | }; 71 | }; 72 | 73 | var Camera = function() { 74 | this.angle = 0; 75 | 76 | this.x = function() { 77 | return Math.cos(this.angle) * 1000; 78 | }; 79 | this.y = function() { 80 | return Math.sin(this.angle) * 1000; 81 | }; 82 | this.z = function() { 83 | return 0; 84 | }; 85 | this.p = function() { 86 | return V(this.x(), this.y(), this.z()); 87 | }; 88 | 89 | this.move = function() { 90 | this.angle += CAM_VEL_FACTOR * 0.0001; 91 | } 92 | }; 93 | 94 | var Timer = function() { 95 | this.times = new Array(60); 96 | for (var i = 0; i < this.times.length; i++) { 97 | this.times[i] = new Date().getTime(); 98 | } 99 | this.index = 0; 100 | 101 | this.move = function() { 102 | this.index = (this.index + 1) % this.times.length; 103 | var prev = this.times[this.index]; 104 | var now = new Date().getTime(); 105 | this.times[this.index] = now; 106 | var diff = now - prev; 107 | if (diff == 0) { 108 | return "???"; 109 | } 110 | var fps = this.times.length * 1000 / diff; 111 | var fpsInt = Math.floor(fps); 112 | return fpsInt + "." + Math.round((fps - fpsInt) * 100); 113 | }; 114 | }; 115 | 116 | var camera = null; 117 | var timer = null; 118 | var lines = null; 119 | var out = V(0, 0, 0); 120 | var timeoutId = null; 121 | 122 | var MONOCHROME_COLORS = null; 123 | 124 | var make2d_funcs = { 125 | "xz" : function(p, c) { 126 | out.x = p.x - c.x + SW / 2; 127 | out.y = p.z - c.z + SH / 2; 128 | out.z = 255; 129 | }, 130 | "xy" : function(p, c) { 131 | out.x = p.x - c.x + SW / 2; 132 | out.y = p.y - c.y + SH / 2; 133 | out.z = 255; 134 | }, 135 | "xz_p3d" : function(p, c) { 136 | var dz = (p.y - c.y + MAX_DIST) * 0.5 / MAX_DIST; 137 | var r = 1 / (dz + 1); 138 | out.x = (p.x - c.x) * r + SW / 2; 139 | out.y = (p.z - c.z) * r + SH / 2; 140 | out.z = Math.floor(256 - dz * Z_FACTOR); 141 | }, 142 | "bug" : function(p, c) { 143 | var dz = (p.y - c.y); 144 | var r = 1 / (dz + 1); 145 | out.x = (p.x - c.x) * r + SW / 2; 146 | out.y = (p.z - c.z) * r + SH / 2; 147 | out.z = Math.floor(256 - dz * Z_FACTOR); 148 | } 149 | }; 150 | 151 | var zeffect_funcs = { 152 | "none": function(ctx, z) { 153 | }, 154 | "width": function(ctx, z) { 155 | ctx.lineWidth = z / WIDTH_FACTOR; 156 | }, 157 | "color": function(ctx, z) { 158 | ctx.strokeStyle = MONOCHROME_COLORS[z]; 159 | }, 160 | "w+c": function(ctx, z) { 161 | ctx.lineWidth = z / WIDTH_FACTOR; 162 | ctx.strokeStyle = MONOCHROME_COLORS[z]; 163 | } 164 | }; 165 | 166 | function hexstr(v) { 167 | v = new Number(v).toString(16); 168 | return v.length == 1 ? "0" + v : v; 169 | } 170 | 171 | function rgb(r, g, b) { 172 | r = hexstr(r); 173 | g = hexstr(g); 174 | b = hexstr(b); 175 | return "#" + r + g + b; 176 | } 177 | 178 | var gencolor_funcs = { 179 | "alpha": function(c) { 180 | return "rgba(255,255,255," + (c / 256) + ")"; 181 | }, 182 | "gray": function(c) { 183 | c = hexstr(c); 184 | return "#" + c + c + c; 185 | }, 186 | "red": function(c) { 187 | return "red"; 188 | }, 189 | "rainbow": function(c) { 190 | if (c < 64) { 191 | return rgb(255, c * 4, 0); 192 | } 193 | if (c < 128) { 194 | return rgb(255 - (c - 64) * 4, 255, 0); 195 | } 196 | if (c < 192) { 197 | return rgb(0, 255, (c - 128) * 4); 198 | } 199 | return rgb(0, 255 - (c - 192) * 4, 255); 200 | }, 201 | "chaos": function(c) { 202 | var g = function() { return Math.floor(Math.random() * 255); } 203 | return rgb(g(), g(), g()); 204 | } 205 | }; 206 | 207 | var CATEGORIES = [ 208 | { 209 | "id": "make2d", 210 | "name": "3D=>2D", 211 | "funcs": make2d_funcs, 212 | "default_func": "xz_p3d" 213 | }, 214 | { 215 | "id": "zeffect", 216 | "name": "Z effect", 217 | "funcs": zeffect_funcs, 218 | "default_func": "w+c" 219 | }, 220 | { 221 | "id": "gencolor", 222 | "name": "Colors", 223 | "funcs": gencolor_funcs, 224 | "default_func": "alpha", 225 | "onchange": initColors 226 | } 227 | ]; 228 | var category_map = {}; 229 | 230 | function log(msg) { 231 | document.getElementById("console").innerHTML += msg + "
"; 232 | } 233 | 234 | function initLine(cp) { 235 | var pg = function() { return Math.random() * 200 - 100; }; 236 | var t = Math.random() * Math.PI * 2; 237 | var p = Math.random() * Math.PI; 238 | var vr = LINE_VEL; 239 | var vx = vr * Math.sin(t) * Math.cos(p); 240 | var vy = vr * Math.sin(t) * Math.sin(p); 241 | var vz = vr * Math.cos(t); 242 | return Line(V(cp.x + pg(), cp.y + pg(), cp.z + pg()), V(vx, vy, vz)); 243 | } 244 | 245 | function selectCategory(self) { 246 | var id = self.name; 247 | var category = category_map[id]; 248 | window[id] = category.funcs[self.value]; 249 | if (category.onchange) { 250 | category.onchange(); 251 | } 252 | } 253 | 254 | function changeParameter(self) { 255 | var id = self.name; 256 | 257 | var val = parseInt(self.value, 10); 258 | if (window[id] == val) { 259 | return; 260 | } 261 | log(id + ": " + window[id] + " => " + val); 262 | 263 | window[id] = val; 264 | 265 | if (timeoutId) { 266 | clearTimeout(timeoutId); 267 | } 268 | initDemo(); 269 | } 270 | 271 | function initUI() { 272 | var html = ""; 273 | 274 | for (var i = 0; i < CATEGORIES.length; i++) { 275 | var category = CATEGORIES[i]; 276 | 277 | category_map[category.id] = category; 278 | 279 | html += ['
', category.name, ': '].join(''); 280 | var funcs = category.funcs; 281 | for (var key in funcs) { 282 | checked = ""; 283 | if (key == category.default_func) { 284 | checked = " checked"; 285 | } 286 | html += ['', key].join(''); 289 | } 290 | } 291 | 292 | PARAMETERS = [ 293 | 'NUM_HISTS', 294 | 'NUM_LINES', 295 | 'MAX_DIST', 296 | 'TIMEOUT_MSEC', 297 | 'WIDTH_FACTOR', 298 | 'Z_FACTOR', 299 | 'LINE_VEL', 300 | 'CAM_VEL_FACTOR', 301 | 'FILL_ALPHA' 302 | ]; 303 | for (var i = 0; i < PARAMETERS.length; i++) { 304 | var param = PARAMETERS[i]; 305 | html += ['
', param, ': ', 306 | ''].join(""); 309 | } 310 | 311 | document.getElementById("ui").innerHTML = html; 312 | } 313 | 314 | function initColors() { 315 | MONOCHROME_COLORS = []; 316 | for (var i = 0; i < 256; i++) { 317 | var c = gencolor(i); 318 | MONOCHROME_COLORS.push(c); 319 | } 320 | } 321 | 322 | function initDemo() { 323 | FILL_ALPHA_STR = 'rgba(0, 0, 0, ' + FILL_ALPHA * 0.01 + ')'; 324 | 325 | initColors(); 326 | 327 | timer = new Timer(); 328 | 329 | camera = new Camera(); 330 | var cp = camera.p(); 331 | 332 | lines = new Array(NUM_LINES); 333 | for (var i = 0; i < NUM_LINES; i++) { 334 | lines[i] = initLine(cp); 335 | } 336 | 337 | cont(); 338 | } 339 | 340 | function init() { 341 | initUI(); 342 | for (var i = 0; i < CATEGORIES.length; i++) { 343 | var category = CATEGORIES[i]; 344 | window[category.id] = category.funcs[category.default_func]; 345 | } 346 | initDemo(); 347 | log("init done"); 348 | } 349 | 350 | function cont() { 351 | timeoutId = setTimeout("run();", TIMEOUT_MSEC); 352 | } 353 | 354 | function reflect(d, n, v, c) { 355 | if (Math.abs(n[d] - c) > MAX_DIST) { 356 | v[d] = -v[d]; 357 | 358 | for (var j = 0; Math.abs(n[d] - c) > MAX_DIST; j++) { 359 | if (j > 10) { 360 | return false; 361 | } 362 | n[d] += v[d]; 363 | } 364 | } 365 | return true; 366 | } 367 | 368 | function move() { 369 | document.getElementById("fps").textContent = timer.move(); 370 | 371 | camera.move(); 372 | var cp = camera.p(); 373 | 374 | for (var i = 0; i < lines.length; i++) { 375 | var line = lines[i]; 376 | var p = line.cur(); 377 | var n = line.next(); 378 | n.assign(p); 379 | n.add(line.v); 380 | 381 | if (!reflect("x", n, line.v, cp.x) || 382 | !reflect("y", n, line.v, cp.y) || 383 | !reflect("z", n, line.v, cp.z)) { 384 | lines[i] = initLine(cp); 385 | } 386 | } 387 | } 388 | 389 | function draw() { 390 | var canvas = document.getElementById("canvas"); 391 | var ctx = canvas.getContext("2d"); 392 | ctx.fillStyle = FILL_ALPHA_STR; 393 | ctx.globalCompositeOperation = "source-over"; 394 | ctx.fillRect(0, 0, SW, SH); 395 | 396 | ctx.strokeStyle = 'rgb(255, 255, 255)'; 397 | ctx.fillStyle = 'rgb(255, 255, 255)'; 398 | ctx.lineWidth = 1; 399 | var cp = camera.p(); 400 | for (var i = 0; i < lines.length; i++) { 401 | var line = lines[i]; 402 | var k = 0; 403 | for (var j = line.nextIndex(line.index); j != line.index; j = k) { 404 | var k = line.nextIndex(j); 405 | var p = line.hist[j]; 406 | var n = line.hist[k]; 407 | 408 | ctx.beginPath(); 409 | make2d(p, cp); 410 | if (out.z <= 0) continue; 411 | if (out.z > 255) out.z = 255; 412 | zeffect(ctx, out.z); 413 | ctx.moveTo(out.x, out.y); 414 | make2d(n, cp); 415 | ctx.lineTo(out.x, out.y); 416 | ctx.closePath(); 417 | ctx.stroke(); 418 | } 419 | } 420 | } 421 | 422 | function run() { 423 | move(); 424 | draw(); 425 | cont(); 426 | } 427 | --------------------------------------------------------------------------------