Mercurial > defr > drupal > scald > dnd
comparison js/bt/other_libs/excanvas_0002/examples/example2.html @ 18:0d557e6e73f7
Added beautytips and some additional event handling code to the library.
| author | David Eads <eads@chicagotech.org> |
|---|---|
| date | Fri, 06 Mar 2009 14:11:46 -0600 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 17:1a77f87927dd | 18:0d557e6e73f7 |
|---|---|
| 1 <!-- | |
| 2 Copyright 2006 Google Inc. | |
| 3 | |
| 4 Licensed under the Apache License, Version 2.0 (the "License"); | |
| 5 you may not use this file except in compliance with the License. | |
| 6 You may obtain a copy of the License at | |
| 7 | |
| 8 http://www.apache.org/licenses/LICENSE-2.0 | |
| 9 | |
| 10 Unless required by applicable law or agreed to in writing, software | |
| 11 distributed under the License is distributed on an "AS IS" BASIS, | |
| 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 13 See the License for the specific language governing permissions and | |
| 14 limitations under the License. | |
| 15 --> | |
| 16 <html> | |
| 17 <head> | |
| 18 <title>ExplorerCanvas Example 1</title> | |
| 19 <!--[if IE]><script type="text/javascript" src="../excanvas.js"></script><![endif]--> | |
| 20 <script type="text/javascript"> | |
| 21 /* -------------------------------------------------------------------- */ | |
| 22 | |
| 23 var canvas, ctx; | |
| 24 var canvasWidth, halfCanvasWidth; | |
| 25 var canvasHeight, halfCanvasHeight; | |
| 26 | |
| 27 var space; // 3D Engine | |
| 28 var scene; // 3D Scene | |
| 29 | |
| 30 /* -------------------------------------------------------------------- */ | |
| 31 | |
| 32 /** | |
| 33 * Space is a simple 3D system. | |
| 34 * | |
| 35 * Y+ = up | |
| 36 * Z+ = into screen | |
| 37 * X+ = right | |
| 38 */ | |
| 39 function Space() { | |
| 40 this.m = this.createMatrixIdentity(); | |
| 41 this.mStack = []; | |
| 42 } | |
| 43 | |
| 44 Space.prototype.createMatrixIdentity = function() { | |
| 45 return [ | |
| 46 [1, 0, 0, 0], | |
| 47 [0, 1, 0, 0], | |
| 48 [0, 0, 1, 0], | |
| 49 [0, 0, 0, 1] | |
| 50 ]; | |
| 51 } | |
| 52 | |
| 53 /** | |
| 54 * Multiplies two 4x4 matricies together. | |
| 55 */ | |
| 56 Space.prototype.matrixMultiply = function(m1, m2) { | |
| 57 var result = this.createMatrixIdentity(); | |
| 58 | |
| 59 var width = m1[0].length; | |
| 60 var height = m1.length; | |
| 61 | |
| 62 if (width != m2.length) { | |
| 63 // error | |
| 64 } | |
| 65 | |
| 66 for (var x = 0; x < width; x++) { | |
| 67 for (var y = 0; y < height; y++) { | |
| 68 var sum = 0; | |
| 69 | |
| 70 for (var z = 0; z < width; z++) { | |
| 71 sum += m1[y][z] * m2[z][x]; | |
| 72 } | |
| 73 | |
| 74 result[y][x] = sum; | |
| 75 } | |
| 76 } | |
| 77 | |
| 78 return result; | |
| 79 } | |
| 80 | |
| 81 /** | |
| 82 * Transforms a coordinate using the current transformation | |
| 83 * matrix, then flattens it using the projection matrix. | |
| 84 */ | |
| 85 Space.prototype.flatten = function(point) { | |
| 86 var p = [[point.x, point.y, point.z, 1]]; | |
| 87 var pm = this.matrixMultiply(p, this.m); | |
| 88 | |
| 89 point.tx = pm[0][0]; | |
| 90 point.ty = pm[0][1]; | |
| 91 point.tz = pm[0][2]; | |
| 92 | |
| 93 // lazy projection | |
| 94 point.fx = halfCanvasWidth + (canvasWidth * point.tx / point.tz); | |
| 95 point.fy = halfCanvasHeight -(canvasWidth * point.ty / point.tz); | |
| 96 } | |
| 97 | |
| 98 /** | |
| 99 * Translate (move) the current transformation matrix | |
| 100 */ | |
| 101 Space.prototype.translate = function(x, y, z) { | |
| 102 var m = [ | |
| 103 [1, 0, 0, 0], | |
| 104 [0, 1, 0, 0], | |
| 105 [0, 0, 1, 0], | |
| 106 [x, y, z, 1] | |
| 107 ]; | |
| 108 | |
| 109 this.m = this.matrixMultiply(m, this.m); | |
| 110 } | |
| 111 | |
| 112 /** | |
| 113 * Rotate the current transformation matrix. Rotations are | |
| 114 * world-oriented, and occur in y,x,z order. | |
| 115 */ | |
| 116 Space.prototype.rotate = function(x, y, z) { | |
| 117 if (y) { | |
| 118 var cosY = Math.cos(y); | |
| 119 var sinY = Math.sin(y); | |
| 120 var rotY = [ | |
| 121 [cosY, 0, sinY, 0], | |
| 122 [0, 1, 0, 0], | |
| 123 [-sinY, 0, cosY, 0], | |
| 124 [0, 0, 0, 1] | |
| 125 ]; | |
| 126 | |
| 127 this.m = this.matrixMultiply(this.m, rotY); | |
| 128 } | |
| 129 | |
| 130 if (x) { | |
| 131 var cosX = Math.cos(x); | |
| 132 var sinX = Math.sin(x); | |
| 133 var rotX = [ | |
| 134 [1, 0, 0, 0], | |
| 135 [0, cosX, -sinX, 0], | |
| 136 [0, sinX, cosX,0], | |
| 137 [0, 0, 0, 1] | |
| 138 ]; | |
| 139 this.m = this.matrixMultiply(this.m, rotX); | |
| 140 } | |
| 141 | |
| 142 if (z) { | |
| 143 var cosZ = Math.cos(z); | |
| 144 var sinZ = Math.sin(z); | |
| 145 var rotZ = [ | |
| 146 [cosZ, -sinZ, 0, 0], | |
| 147 [sinZ, cosZ, 0, 0], | |
| 148 [0, 0, 1, 0], | |
| 149 [0, 0, 0, 1] | |
| 150 ]; | |
| 151 | |
| 152 this.m = this.matrixMultiply(this.m, rotZ); | |
| 153 } | |
| 154 } | |
| 155 | |
| 156 /** | |
| 157 * Pushes the current transformation onto the stack | |
| 158 */ | |
| 159 Space.prototype.push = function() { | |
| 160 this.mStack.push(this.m); | |
| 161 this.m = [ | |
| 162 [this.m[0][0], this.m[0][1], this.m[0][2], this.m[0][3]], | |
| 163 [this.m[1][0], this.m[1][1], this.m[1][2], this.m[1][3]], | |
| 164 [this.m[2][0], this.m[2][1], this.m[2][2], this.m[2][3]], | |
| 165 [this.m[3][0], this.m[3][1], this.m[3][2], this.m[3][3]] | |
| 166 ]; | |
| 167 } | |
| 168 | |
| 169 /** | |
| 170 * Pops the end off the transformation stack | |
| 171 */ | |
| 172 Space.prototype.pop = function() { | |
| 173 this.m = this.mStack.pop(); | |
| 174 } | |
| 175 | |
| 176 /* -------------------------------------------------------------------- */ | |
| 177 | |
| 178 /** | |
| 179 * A 3d coordinate | |
| 180 */ | |
| 181 function Point(x, y, z) { | |
| 182 this.x = x; | |
| 183 this.y = y; | |
| 184 this.z = z; | |
| 185 | |
| 186 // Relative to camera coordinates | |
| 187 this.tx; | |
| 188 this.ty; | |
| 189 this.tz; | |
| 190 | |
| 191 // Flattened coordinates | |
| 192 this.fx; | |
| 193 this.fy; | |
| 194 } | |
| 195 | |
| 196 /** | |
| 197 * A Shape is made up of polygons | |
| 198 */ | |
| 199 function Shape() { | |
| 200 this.points = []; | |
| 201 this.polygons = []; | |
| 202 } | |
| 203 | |
| 204 /** | |
| 205 * Draws the shape | |
| 206 */ | |
| 207 Shape.prototype.draw = function(drawlist) { | |
| 208 for (var i = 0; i< this.points.length; i++) { | |
| 209 space.flatten(this.points[i]); | |
| 210 } | |
| 211 | |
| 212 for (var i = 0; i< this.polygons.length; i++) { | |
| 213 var poly = this.polygons[i]; // convenience | |
| 214 | |
| 215 space.flatten(poly.origin); | |
| 216 | |
| 217 // lazy backface culling | |
| 218 if (poly.normal && this.backface) { | |
| 219 space.flatten(poly.normal); | |
| 220 | |
| 221 var originDist = Math.pow(poly.origin.tx, 2) | |
| 222 + Math.pow(poly.origin.ty, 2) | |
| 223 + Math.pow(poly.origin.tz, 2); | |
| 224 | |
| 225 var normalDist = Math.pow(poly.normal.tx, 2) | |
| 226 + Math.pow(poly.normal.ty, 2) | |
| 227 + Math.pow(poly.normal.tz, 2); | |
| 228 | |
| 229 if(originDist > normalDist) { | |
| 230 drawlist.push(poly); | |
| 231 } | |
| 232 } else { | |
| 233 drawlist.push(poly); | |
| 234 } | |
| 235 } | |
| 236 } | |
| 237 | |
| 238 /** | |
| 239 * A polygon is a connection of points in the shape object. You | |
| 240 * should probably try to make them coplanar. | |
| 241 */ | |
| 242 function Polygon(points, normal, backface, type, color) { | |
| 243 this.points = points; | |
| 244 | |
| 245 this.origin = new Point(0, 0, 0); | |
| 246 for(var i = 0; i < this.points.length; i++) { | |
| 247 this.origin.x += this.points[i].x; | |
| 248 this.origin.y += this.points[i].y; | |
| 249 this.origin.z += this.points[i].z; | |
| 250 } | |
| 251 | |
| 252 this.origin.x /= this.points.length; | |
| 253 this.origin.y /= this.points.length; | |
| 254 this.origin.z /= this.points.length; | |
| 255 | |
| 256 if (normal) { | |
| 257 this.normal = new Point(this.origin.x + normal.x, | |
| 258 this.origin.y + normal.y, | |
| 259 this.origin.z + normal.z); | |
| 260 } else { | |
| 261 this.normal = null; | |
| 262 } | |
| 263 | |
| 264 this.backface = backface; | |
| 265 this.type = type; | |
| 266 this.color = color; | |
| 267 } | |
| 268 | |
| 269 Polygon.SOLID = 0; | |
| 270 Polygon.WIRE = 1; | |
| 271 | |
| 272 /** | |
| 273 * Draws the polygon. Assumes that the points have already been | |
| 274 * flattened. | |
| 275 */ | |
| 276 Polygon.prototype.draw = function() { | |
| 277 ctx.beginPath(); | |
| 278 ctx.moveTo(this.points[0].fx, this.points[0].fy); | |
| 279 | |
| 280 for(var i = 0; i < this.points.length; i++) { | |
| 281 ctx.lineTo(this.points[i].fx, this.points[i].fy); | |
| 282 } | |
| 283 | |
| 284 ctx.closePath(); | |
| 285 | |
| 286 var color = this.color; | |
| 287 | |
| 288 /* | |
| 289 // Do lighting here | |
| 290 lightvector = Math.abs(this.normal.x + this.normal.y); | |
| 291 if(lightvector > 1) { | |
| 292 lightvector = 1; | |
| 293 } | |
| 294 | |
| 295 color[0] = (color[0] * lightvector).toString(); | |
| 296 color[1] = (color[1] * lightvector).toString(); | |
| 297 color[2] = (color[2] * lightvector).toString(); | |
| 298 */ | |
| 299 | |
| 300 if (color.length > 3) { | |
| 301 var style = ["rgba(", | |
| 302 color[0], ",", | |
| 303 color[1], ",", | |
| 304 color[2], ",", | |
| 305 color[3], ")"].join(""); | |
| 306 } else { | |
| 307 var style = ["rgb(", | |
| 308 color[0], ",", | |
| 309 color[1], ",", | |
| 310 color[2], ")"].join(""); | |
| 311 } | |
| 312 | |
| 313 if (this.type == Polygon.SOLID) { | |
| 314 ctx.fillStyle = style; | |
| 315 ctx.fill(); | |
| 316 } else if (this.type == Polygon.WIRE) { | |
| 317 ctx.strokeStyle = style; | |
| 318 ctx.stroke(); | |
| 319 } | |
| 320 } | |
| 321 | |
| 322 /* -------------------------------------------------------------------- */ | |
| 323 | |
| 324 /** | |
| 325 * Scene describes the 3D environment | |
| 326 */ | |
| 327 function Scene() { | |
| 328 this.shapes = {}; | |
| 329 this.camera = new Point(0, 0, 0); | |
| 330 this.cameraTarget = new Point(0, 0, 0); | |
| 331 this.cameraRotation = 0; | |
| 332 | |
| 333 this.drawlist = []; | |
| 334 } | |
| 335 | |
| 336 /** | |
| 337 * Draw the world | |
| 338 */ | |
| 339 Scene.prototype.draw = function() { | |
| 340 space.push(); | |
| 341 | |
| 342 // Camera transformation | |
| 343 space.translate( | |
| 344 -this.camera.x, | |
| 345 -this.camera.y, | |
| 346 -this.camera.z | |
| 347 ); | |
| 348 | |
| 349 // Camera rotation | |
| 350 var xdiff = this.cameraTarget.x - this.camera.x; | |
| 351 var ydiff = this.cameraTarget.y - this.camera.y; | |
| 352 var zdiff = this.cameraTarget.z - this.camera.z; | |
| 353 | |
| 354 var xzdist = Math.sqrt(Math.pow(xdiff, 2) + Math.pow(zdiff, 2)); | |
| 355 | |
| 356 var xrot = -Math.atan2(ydiff, xzdist); // up/down rotation | |
| 357 var yrot = Math.atan2(xdiff, zdiff); // left/right rotation | |
| 358 | |
| 359 space.rotate(xrot, yrot, this.cameraRotation); | |
| 360 | |
| 361 // Drawing | |
| 362 this.drawlist = []; | |
| 363 | |
| 364 for(var i in this.shapes) { | |
| 365 this.shapes[i].draw(this.drawlist); | |
| 366 } | |
| 367 | |
| 368 // Depth sorting (warning: this is only enough to drive this demo - feel | |
| 369 // free to contribute a better system). | |
| 370 this.drawlist.sort(function (poly1, poly2) { | |
| 371 return poly2.origin.tz - poly1.origin.tz; | |
| 372 }); | |
| 373 | |
| 374 for (var i = 0; i < this.drawlist.length; i++) { | |
| 375 this.drawlist[i].draw(); | |
| 376 } | |
| 377 | |
| 378 space.pop(); | |
| 379 } | |
| 380 | |
| 381 /* -------------------------------------------------------------------- */ | |
| 382 | |
| 383 var count = 0; | |
| 384 | |
| 385 function loop() { | |
| 386 ctx.clearRect(0, 0, canvasWidth, canvasHeight); | |
| 387 | |
| 388 scene.camera.x = 70*Math.sin(count); | |
| 389 scene.camera.y = 70; | |
| 390 scene.camera.z = 70*Math.cos(count); | |
| 391 scene.cameraRotation = count / 10; | |
| 392 | |
| 393 count += 0.01; | |
| 394 scene.draw(); | |
| 395 } | |
| 396 | |
| 397 function load() { | |
| 398 // Init drawing system | |
| 399 canvas = document.getElementById("cv"); | |
| 400 ctx = canvas.getContext("2d"); | |
| 401 | |
| 402 canvasWidth = canvas.width; | |
| 403 canvasHeight = canvas.height; | |
| 404 halfCanvasWidth = canvasWidth * 0.5; | |
| 405 halfCanvasHeight = canvasHeight * 0.5; | |
| 406 | |
| 407 // Init 3D components | |
| 408 space = new Space(); | |
| 409 scene = new Scene(); | |
| 410 | |
| 411 // Create a box shape and add it to the scene | |
| 412 scene.shapes['box'] = new Shape(); | |
| 413 var p = scene.shapes['box'].points; // for convenience | |
| 414 | |
| 415 p[0] = new Point(-10, -10, -10); // left bottom front | |
| 416 p[1] = new Point(10, -10, -10); // right bottom front | |
| 417 p[2] = new Point(10, 10, -10); // right top front | |
| 418 p[3] = new Point(-10, 10, -10); // left top front | |
| 419 | |
| 420 p[4] = new Point(-10, -10, 10); // left bottom back | |
| 421 p[5] = new Point(10, -10, 10); // right bottom back | |
| 422 p[6] = new Point(10, 10, 10); // right top back | |
| 423 p[7] = new Point(-10, 10, 10); // left top back | |
| 424 | |
| 425 // Back | |
| 426 scene.shapes['box'].polygons.push(new Polygon( | |
| 427 [ p[0], p[1], p[2], p[3] ], | |
| 428 new Point(0, 0, -1), | |
| 429 true /* double-sided */, | |
| 430 Polygon.SOLID, | |
| 431 [255, 0, 0] | |
| 432 )); | |
| 433 | |
| 434 // Front | |
| 435 scene.shapes['box'].polygons.push(new Polygon( | |
| 436 [ p[4], p[5], p[6], p[7] ], | |
| 437 new Point(0, 0, 1), | |
| 438 true /* double-sided */, | |
| 439 Polygon.SOLID, | |
| 440 [0, 0, 255] | |
| 441 )); | |
| 442 | |
| 443 // Top | |
| 444 scene.shapes['box'].polygons.push(new Polygon( | |
| 445 [ p[2], p[3], p[7], p[6] ], | |
| 446 new Point(0, 1, 0), | |
| 447 false /* single-sided */, | |
| 448 Polygon.WIRE, | |
| 449 [0, 255, 0] | |
| 450 )); | |
| 451 | |
| 452 // Transparent Top | |
| 453 scene.shapes['box'].polygons.push(new Polygon( | |
| 454 [ p[2], p[3], p[7], p[6] ], | |
| 455 new Point(0, 1, 0), | |
| 456 false /* single-sided */, | |
| 457 Polygon.SOLID, | |
| 458 [0, 255, 0, 0.4] | |
| 459 )); | |
| 460 | |
| 461 // Left | |
| 462 scene.shapes['box'].polygons.push(new Polygon( | |
| 463 [ p[0], p[4], p[7], p[3] ], | |
| 464 new Point(-1, 0, 0), | |
| 465 true /* double-sided */, | |
| 466 Polygon.SOLID, | |
| 467 [255, 255, 0] | |
| 468 )); | |
| 469 | |
| 470 // Right | |
| 471 scene.shapes['box'].polygons.push(new Polygon( | |
| 472 [ p[1], p[5], p[6], p[2] ], | |
| 473 new Point(1, 0, 0), | |
| 474 true /* double-sided */, | |
| 475 Polygon.SOLID, | |
| 476 [0, 255, 255] | |
| 477 )); | |
| 478 | |
| 479 // Create a floor shape and add it to the scene | |
| 480 scene.shapes['floor'] = new Shape(); | |
| 481 var p = scene.shapes['floor'].points; // for convenience | |
| 482 | |
| 483 p[0] = new Point(-40, -10, -40); | |
| 484 p[1] = new Point(-40, -10, 40); | |
| 485 p[2] = new Point( 40, -10, 40); | |
| 486 p[3] = new Point( 40, -10, -40); | |
| 487 | |
| 488 // Floor | |
| 489 scene.shapes['floor'].polygons.push(new Polygon( | |
| 490 [ p[0], p[1], p[2], p[3] ], | |
| 491 new Point(0, 1, 0), | |
| 492 false /* single-sided */, | |
| 493 Polygon.SOLID, | |
| 494 [45, 45, 45] | |
| 495 )); | |
| 496 | |
| 497 setInterval('loop()', 20); | |
| 498 } | |
| 499 | |
| 500 /* -------------------------------------------------------------------- */ | |
| 501 </script> | |
| 502 <style> | |
| 503 body { | |
| 504 background-color:black; | |
| 505 margin:50px; | |
| 506 text-align:center; | |
| 507 } | |
| 508 </style> | |
| 509 </head> | |
| 510 <body onload="load();"> | |
| 511 <canvas id="cv" width="400" height="300"></canvas> | |
| 512 </body> | |
| 513 </html> |
