Skip to content
Browse files

- Added DynamicCanvas renderer and all of the needed javascript files

- Added DynamicCanvas example called dynamic_cubes


git-svn-id: https://svn.php.net/repository/pear/packages/Image_3D/trunk@233186 c90b9560-bf6c-de11-be94-00142212c4b1
  • Loading branch information...
1 parent d6fc584 commit ad6a2dd4cd3c9ddcb0bcf0cc6a38f9f40bff3722 Jakob Westhoff committed Apr 3, 2007
View
334 Image/3D/Driver/DynamicCanvas.php
@@ -0,0 +1,334 @@
+<?php
+/**
+ * 3d Library
+ *
+ * PHP versions 5
+ *
+ * LICENSE:
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * Creates a HTML document, with embedded javascript code to draw, move, rotate
+ * and export the 3D-object at runtime
+ *
+ * @category Image
+ * @package Image_3D
+ * @author Jakob Westhoff <jakob@westhoffswelt.de>
+ */
+class Image_3D_Driver_DynamicCanvas extends Image_3D_Driver {
+
+ /**
+ * Width of the image
+ *
+ * @var integer
+ */
+ protected $_x;
+ /**
+ * Height of the image
+ *
+ * @var integer
+ */
+ protected $_y;
+
+ /**
+ * Polygones created during the rendering process
+ *
+ * @var array
+ */
+ protected $_polygones;
+
+ /**
+ * Background Color of the rendered image
+ *
+ * @var string
+ */
+ protected $_background;
+
+
+ /**
+ * Name of the Render created from the filename
+ * Needed for the correct creation of the Image3D java class
+ *
+ * @var mixed
+ */
+ protected $_name;
+
+ /**
+ * Class constructor
+ */
+ public function __construct()
+ {
+ $this->_image = '';
+ $this->_polygones = array();
+ $this->_background = array();
+ }
+
+ /**
+ * Create the inital image
+ *
+ * @param float $x Width of the image
+ * @param float $y Height of the image
+ * @return void
+ */
+ public function createImage( $x, $y )
+ {
+ $this->_x = ( int ) $x;
+ $this->_y = ( int ) $y;
+ }
+
+ /**
+ * Set the background color of the image
+ *
+ * @param Image_3D_Color $color Desired background color of the image
+ * @return void
+ */
+ public function setBackground( Image_3D_Color $color )
+ {
+ $colorarray = $this->_getRgba( $color );
+ $this->_background = sprintf( "{ r: %d, g: %d, b: %d, a:%.2f }",$colorarray['r'], $colorarray['g'], $colorarray['b'], $colorarray['a'] );
+ }
+
+ /**
+ * Create an appropriate array representation from a Image_3D_Color object
+ *
+ * @param Image_3D_Color $color Color to transform to rgba syntax
+ * @param float $alpha optional Override the alpha value set in the Image_3D_Color object
+ * @return array Array of color values reflecting the different color
+ * components of the input object
+ */
+ protected function _getRgba( Image_3D_Color $color, $alpha = null )
+ {
+ $values = $color->getValues();
+
+ $values[0] = ( int ) round( $values[0] * 255 );
+ $values[1] = ( int ) round( $values[1] * 255 );
+ $values[2] = ( int ) round( $values[2] * 255 );
+
+ if ( $alpha !== null )
+ {
+ $values[3] = 1.0 - $alpha;
+ }
+ else
+ {
+ $values[3] = 1.0 - $values[3];
+ }
+
+ return array( 'r' => $values[0], 'g' => $values[1], 'b' => $values[2], 'a' => $values[3] );
+ }
+
+ /**
+ * Add a polygon to the polygones array
+ *
+ * @param array $points Array of points which represent the polygon to add
+ * @param array $colors Array of maximal three colors. The second and the
+ * third color are allowed to be null
+ * @return void
+ */
+ protected function _addPolygon( array $points, array $colors )
+ {
+ $this->_polygones[] = array( "points" => $points, "colors" => $colors );
+ }
+
+ /**
+ * Draw a specified polygon
+ *
+ * @param Image_3D_Polygon $polygon Polygon to draw
+ * @return void
+ */
+ public function drawPolygon( Image_3D_Polygon $polygon )
+ {
+ $pointarray = array();
+ $points = $polygon->getPoints();
+ foreach ( $points as $key => $point )
+ {
+ $pointarray[$key] = array( 'x' => $point->getX(), 'y' => $point->getY(), 'z' => $point->getZ() );
+ }
+
+ $this->_addPolygon(
+ $pointarray,
+ array(
+ $this->_getRgba( $polygon->getColor() ),
+ null,
+ null
+ )
+ );
+ }
+
+ /**
+ * Draw a specified polygon utilizing gradients between his points for
+ * color representation (Gauroud-Shading)
+ *
+ * @param Image_3D_Polygon $polygon Polygon to draw
+ * @return void
+ */
+ public function drawGradientPolygon( Image_3D_Polygon $polygon )
+ {
+ $pointarray = array();
+ $colorarray = array();
+ $points = $polygon->getPoints();
+ foreach ( $points as $key => $point )
+ {
+ $pointarray[$key] = array( 'x' => $point->getX(), 'y' => $point->getY(), 'z' => $point->getZ() );
+ $colorarray[$key] = $this->_getRgba( $point->getColor() );
+ }
+
+ $this->_addPolygon(
+ $pointarray,
+ $colorarray
+ );
+ }
+
+ /**
+ * Convert php array to a javascript parsable data structure
+ *
+ * @param array $data Array to convert
+ * @return string Javascript readable representation of the given php array
+ */
+ private function _arrayToJs( array $data )
+ {
+ $output = array();
+ $assoiative = false;
+ // Is our array associative?
+ // Does anyone know a better/faster way to check this?
+ foreach( array_keys( $data ) as $key )
+ {
+ if ( is_int( $key ) === false )
+ {
+ $assoiative = true;
+ break;
+ }
+ }
+ $output[] = $assoiative === true ? "{" : "[";
+ foreach( $data as $key => $value )
+ {
+ $line = '';
+ if ( $assoiative === true )
+ {
+ $line .= "\"$key\": ";
+ }
+ switch ( gettype( $value ) )
+ {
+ case "array":
+ $line .= $this->_arrayToJs( $value );
+ break;
+ case "integer":
+ case "boolean":
+ $line .= $value;
+ break;
+ case "double":
+ $line .= sprintf( "%.2f", $value );
+ break;
+ case "string":
+ $line .= "\"$value\"";
+ break;
+ case "NULL":
+ case "resource":
+ case "object":
+ $line .= "undefined";
+ break;
+ }
+ if( $key !== end( array_keys( $data ) ) )
+ {
+ $line .= ",";
+ }
+ $output[] = $line;
+ }
+
+ $output[] = $assoiative === true ? "}" : "]";
+
+ // If the output array has more than 5 entries seperate them by a new line.
+ return implode( count( $data ) > 5 ? "\n" : " ", $output );
+ }
+
+ /**
+ * Get the Javascript needed for dynamic rendering, moving, rotating
+ * and exporting of the 3D Object
+ *
+ * @return string needed javascript code (with <script> tags)
+ */
+ private function _getJs()
+ {
+ $identifiers = array(
+ "%polygones%",
+ "%background%",
+ "%width%",
+ "%height%",
+ "%uid%",
+ );
+ $replacements = array(
+ $this->_arrayToJs( $this->_polygones ) . ";\n",
+ $this->_background,
+ $this->_x,
+ $this->_y,
+ sha1( mt_rand() . mt_rand() . mt_rand() . mt_rand() . mt_rand() . mt_rand() . mt_rand() ),
+ );
+
+ $jsfiles = array(
+ 'Init.js',
+ 'Renderer.js',
+ 'CanvasDriver.js',
+ 'PngDriver.js',
+ 'SvgDriver.js',
+ 'MouseEventGenerator.js',
+ 'RotateAnimationEventGenerator.js',
+ 'Toolbar.js',
+ 'Base64.js',
+ 'Image3D.js',
+ 'Startup.js',
+ );
+
+ return str_replace(
+ $identifiers,
+ $replacements,
+ implode(
+ "\n\n",
+ array_map(
+ create_function(
+ '$jsfile',
+ ( is_dir( dirname( __FILE__ ) . '/../../../data/DynamicCanvas' ) )
+ ? ( 'return file_get_contents( dirname( __FILE__ ) . "/../../../data/DynamicCanvas/" . $jsfile );' )
+ : ( 'return file_get_contents( "@data_dir@/Image_3D/data/DynamicCanvas/" . $jsfile );' )
+ ),
+ $jsfiles
+ )
+ )
+ );
+ }
+
+ /**
+ * Save all the gathered information to a html file
+ *
+ * @param string $file File to write output to
+ * @return void
+ */
+ public function save( $file )
+ {
+ file_put_contents( $file, $this->_getJs() );
+ }
+
+ /**
+ * Return the shading methods this output driver is capable of
+ *
+ * @return array Shading methods supported by this driver
+ */
+ public function getSupportedShading()
+ {
+ return array( Image_3D_Renderer::SHADE_NO, Image_3D_Renderer::SHADE_FLAT );
+// return array( Image_3D_Renderer::SHADE_NO, Image_3D_Renderer::SHADE_FLAT, Image_3D_Renderer::SHADE_GAUROUD );
+ }
+}
+
+?>
View
40 data/DynamicCanvas/Base64.js
@@ -0,0 +1,40 @@
+Base64_%uid% = {
+ charset: [ "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "+", "/" ],
+
+ encode: function( data )
+ {
+ // Tranform data string to an array for easier handling
+ var input = Array();
+ for ( var i = 0; i<data.length; i++ )
+ {
+ input[i] = data.charCodeAt( i );
+ }
+
+ var encoded = Array();
+
+ // Create padding to let us operate on 24 bit ( 3 byte ) chunks till the end
+ var padding = 0;
+ while ( input.length % 3 != 0 )
+ {
+ input.push( 0 );
+ padding++;
+ }
+
+ for( var i=0; i<input.length; i+=3 )
+ {
+ encoded.push( Base64_%uid%.charset[ input[i] >> 2 ] );
+ encoded.push( Base64_%uid%.charset[ ( ( input[i] & 3) << 4 ) | ( input[i+1] >> 4 ) ] );
+ encoded.push( Base64_%uid%.charset[ ( ( input[i+1] & 15) << 2 ) | ( input[i+2] >> 6 ) ] );
+ encoded.push( Base64_%uid%.charset[ ( input[i+2] & 63 ) ] );
+ }
+
+
+ // Replace our added zeros with the correct padding characters
+ for( var i=0; i<padding; i++ )
+ {
+ encoded[encoded.length-1-i]= "=";
+ }
+
+ return encoded.join( "" );
+ }
+}
View
94 data/DynamicCanvas/CanvasDriver.js
@@ -0,0 +1,94 @@
+/**
+ * Output Driver to render into a canvas object
+ */
+function CanvasDriver_%uid%( canvasElement ) {
+ if ( !canvasElement.getContext )
+ {
+ window.alert( 'Unfortunatly your browser does not support the "Canvas" control.\\nDownload Firefox <http://mozilla.org/firefox> to make the 3D control display in your browser.' );
+ throw "Canvas Control not available.";
+ }
+
+ this._canvas = canvasElement.getContext( '2d' );
+}
+
+CanvasDriver_%uid%.prototype = {
+ /**
+ * Canvas rendering context
+ */
+ _canvas: false,
+
+ /**
+ * Create a "rgba" string from a specified color array
+ */
+ _getRgba: function( color )
+ {
+ if ( arguments[1] != undefined )
+ {
+ color["a"] = arguments[1];
+ }
+ return "rgba( " + color["r"] + ", " + color["g"] + ", " + color["b"] + ", " + color["a"] + " )";
+ },
+
+ /**
+ * Starts output by recieving width and height of the image
+ * to be rendered
+ */
+ begin: function( x, y ) {
+ // Nothing to do here
+ },
+
+ /**
+ * Draw a given polygon into the approriate context
+ */
+ drawPolygon: function( polygon ) {
+ this._canvas.beginPath();
+ this._canvas.moveTo( polygon["points"][0][0], polygon["points"][0][1] );
+ for ( var j = 1; j<polygon["points"].length; j++ )
+ {
+ this._canvas.lineTo( polygon["points"][j][0], polygon["points"][j][1] );
+ }
+ this._canvas.lineTo( polygon["points"][0][0], polygon["points"][0][1] );
+ if ( polygon["colors"][1] == undefined )
+ {
+ // Only one color, means flat shading
+ this._canvas.fillStyle = this._getRgba( polygon["colors"][0] );
+ this._canvas.fill();
+ }
+ else
+ {
+ // More than one color. Gauroud shading is used
+
+ // Create the main gradient between the first and the second point
+ var mainGradient = this._canvas.createLinearGradient( polygon["points"][0][0], polygon["points"][0][1], polygon["points"][1][0], polygon["points"][1][1] );
+ mainGradient.addColorStop( 0.0, this._getRgba( polygon["colors"][0] ) );
+ mainGradient.addColorStop( 1.0, this._getRgba( polygon["colors"][1] ) );
+
+ // Create the overlay gradient between the third point and the inbetween of the two other points
+ var overlayGradient = this._canvas.createLinearGradient(
+ polygon["points"][2][0],
+ polygon["points"][2][1],
+ ( polygon["points"][0][0] + polygon["points"][1][0] ) / 2.0,
+ ( polygon["points"][0][1] + polygon["points"][1][1] ) / 2.0
+ );
+ overlayGradient.addColorStop( 0.0, this._getRgba( polygon["colors"][2] ) );
+ overlayGradient.addColorStop( 1.0, this._getRgba( polygon["colors"][2], 0.0 ) );
+
+ // Draw the gradients
+ this._canvas.fillStyle = mainGradient;
+ this._canvas.fill();
+ this._canvas.fillStyle = overlayGradient;
+ this._canvas.fill();
+
+ // Delete the gradients to free memory
+ delete mainGradient;
+ delete overlayGradient;
+ }
+ },
+
+ /**
+ * Finish the output
+ */
+ finish: function() {
+ // Nothing to do here
+ }
+}
View
107 data/DynamicCanvas/Image3D.js
@@ -0,0 +1,107 @@
+Image3D_%uid% = {
+ container: false,
+ canvas: false,
+ controlOverlay: false,
+ toolbar: false,
+ bodyElement: false,
+ mouseEventGenerator: false,
+
+ init: function()
+ {
+ Image3D_%uid%.container = document.createElement( 'div' );
+ Image3D_%uid%.canvas = document.createElement( 'canvas' );
+ Image3D_%uid%.controlOverlay = document.createElement( 'div' );
+ Image3D_%uid%.container.style.position = "relative";
+ Image3D_%uid%.container.style.width = %width% + "px";
+ Image3D_%uid%.container.style.height = %height% + "px";
+ Image3D_%uid%.canvas.style.position = "absolute";
+ Image3D_%uid%.canvas.style.top = "0px";
+ Image3D_%uid%.canvas.style.left = "0px";
+ Image3D_%uid%.canvas.style.width = %width% + "px";
+ Image3D_%uid%.canvas.style.height = %height% + "px";
+ Image3D_%uid%.canvas.width = %width%;
+ Image3D_%uid%.canvas.height = %height%;
+ Image3D_%uid%.controlOverlay.style.position = "absolute";
+ Image3D_%uid%.controlOverlay.style.top = "0px";
+ Image3D_%uid%.controlOverlay.style.left = "0px";
+ Image3D_%uid%.controlOverlay.style.width = %width% + "px";
+ Image3D_%uid%.controlOverlay.style.height = %height% + "px";
+ Image3D_%uid%.toolbar = new Toolbar_%uid%( Image3D_%uid%.controlOverlay );
+
+ // Create a new MouseEventGenerator
+ var bodyElement = document.getElementsByTagName( "body" );
+ Image3D_%uid%.bodyElement = bodyElement[0];
+ Image3D_%uid%.mouseEventGenerator = new MouseEventGenerator_%uid%( Image3D_%uid%.controlOverlay, Image3D_%uid%.bodyElement, Image3D_%uid%.bodyElement );
+
+ // Create the needed toolbar buttons
+ Image3D_%uid%.toolbar.activate(
+ Image3D_%uid%.toolbar.addButton(
+ 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAUwwAAFMMBFXBNQgAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAALHSURBVEiJrZU9aBRBFMd/s0lIiJdCMaiFYKyE4G5zexsEURFFkYAfIGJjo4UoWFxMTvJhzMflLjEEhWipIKLYGDURGysRHQbRaS2MdmIgGhEhYm4sMhvXc+88NQ+OvTc78/vPe/PmrTDGUKl5fjAEfNZKDlW6xvlLeAbIen7QtawCEXho/Z4fnF8WgSJ4Drhq//d6ftD3XwJF8EGt5DngVESk2/ODwX8S8PygCthk3X6tZBeAVtIUibR6fpAoqWCM+e3nJlPr7LPGTaYOl5gj3GTqnJtMNVo/ETdPFJepTctxYJdW8lW58CNr1gCPgQc2jfEpiuR8NXCwEri1Y0AzkLGMJVuKoOhAR+caD9wUgmvCmPEqmPiO8wgKk0aIJgEb+Dq/mxV12zAmL4wZaJiZaAHSdn0ujMSJgY9oJdvePsy8NDBjhOhbEE4GYTZXFcQNDNeBrdTXnjSmMAAmUZ/4dl8r2QaMWMZSJMJNpqLwYa1kRxjexr1Z1zjOS7uRy9NTmTMATfvyd8HsAepA7J+e6rgXSXMeaA8jcYC5SMoaPD8QoeMUzEfgm3XfhuMFYdJALfCkCC6Ahgjvk6OVzAGdduAkMB6KLFQ7OUAAz4Hu9XvGVgG8m8y8MbAgEK+L4OOWAdCulcw7AFrJLNAVFWlqzW4BcRS4UhCcBlZWVc9fIMZi4Ge1kiMQKVOt5CDQE4qsmH12CIcd1Wa+991k5gWYFlMwt5fSh9hJQYSHeiICT2slL4bz4i5aD3AE2K6V/BC345gIaoBbwFOt5NgvL0u0gYR9rnWTqfYyrSLnJlNu6FfUKiK7SgCKxYY3aus8fBfN+SzQrJV8H8cp2U21kl+AO9ZNe34wHAMHuFQKDjFnEBNJH9Bt3WEW6zyE92gl+8ut/6OAFRng510JrctWXlmr6JtsPzbZyFBnJXCoMILQbAObs7e/IvsBkv5/BeJknOEAAAAASUVORK5CYII=',
+ function( event )
+ {
+ Image3D_%uid%.mouseEventGenerator.setControlState( event, MouseEventGenerator_%uid%.CONTROL_TRANSLATE_XY );
+ Image3D_%uid%.toolbar.activate( this );
+ }
+ )
+ );
+ Image3D_%uid%.toolbar.addButton(
+ 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAUwwAAFMMBFXBNQgAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAKDSURBVEiJrZU9aBRBFIC/2btoc2k0onYqKYTgbnN722ohiqiFiNjZaBGi1ZmfMz9Kfi5nLhpEQnobg9gpsbK0GKZxaiFYpAgWxogoMbmMxc2Gybp3rpoHw+6bee97+97OvBHGGLJKEEbTwFet5HRWH+8v4UNANQijkT0N4MBjmQjC6P6eBEjAa8CCfX8QhNH4fwVIwKe0khWgzwkyGoTR1D8FCMIoB5y06oRWcgRAK2kSQS4FYVRoGcEY89vwi6Wj9tnhF0vXWtgIv1iq+MXSIasX0uxEcpvastwEzmol37dL3/E5DLwFXtkyppfIqXkXcCUL3MoNoAcYsowd2ckg8UMfaSXvxkbd52vdjRwvXUdjROXj0uAb5+NmgbJVa3EmXgq87sIBhGfWQSw2B13AKYFYc22sT92qO5kIv1hy4TNaycFWdThxoXbbCJ4KYfqXX1dm02yCMHoIDMSZeMC6s94ZhJFIczx2sXpmWzAHPGsDF0CnM/VFGGMIwugeEB+YBaDP7vcm/PLkcbGVVwhWRH77nPE2N/IbB358WLqzkYDPA712akArWfcAtJJVIG5gvcC8m4nXyF9FcBAIzJa3ys/9a5t8L7eB92sl6wD52EgrORWEkQeMW8MVoArgbTUWG/tyyi1HTrDsqLcceFkr+TheSDtoY8B14LRW8hMZJAijDuA58E4rObdrsUUbKNjnEb9YGmjTKmp+seTHeqZW4XxVAVA0G96ug5eo+WegRyu5msZp2U21kt+AF1YtB2E0kwIHeNIKDin/ICWTcWDUqjM093kMH9NKTrTz/2MAG2QSGE5Mj2gl2142kPFOtpdN1ZkazgKHjBnEYhvYulayltXnF0ETY0N5irsxAAAAAElFTkSuQmCC',
+ function( event )
+ {
+ Image3D_%uid%.mouseEventGenerator.setControlState( event, MouseEventGenerator_%uid%.CONTROL_TRANSLATE_Z );
+ Image3D_%uid%.toolbar.activate( this );
+ }
+ );
+ Image3D_%uid%.toolbar.addButton(
+ 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAUwwAAFMMBFXBNQgAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAPmSURBVEiJtZVdbFNlGMd/zznbWrqJCdkFuJisMqPJas/CTj9YMKEhIgwQg+K1KGq8MTFeWLxw4MWYEcFA/P64Nhr87qYsZFEjtqeDrdskJoNsiVEvFsc2t46t7Xm8GG0Krhsk+r98n+f5/Z//m5zziqryf6pqpaKISCAcbpWC7hAxdmSn/to2Ojq6YIUjZ9XljGD0Dp1PDugKW0qlmhWKPgp6ClhfPJup860Z6+u7aoUiZUP6B5jPZtI/f3VTBpZl1WqN96TAE0sd/CiududN6RlJpTIA97VGg4ahu4HdwOYlH3mf3PzzmUxmrqKBZVm11HhTQDMwgeiTGcf5ulJ8gGAoulfQD4B64BcWr0bKTYzrumu8x4BmhGRBNLgaHGAonfyyWgtBhCTQjMf7xrIJgqHQTsHoBial2gwOnjv3+2rwctm2vSEn5hBQLwb7BlOpz69LIGq8BSAGB28VDtDf3/+noTwOoC5vi4hRStASDjerygjohUzaaQWQxz41GycuVo9vZVE7Oty72095PHWTujC7Tjx1kzrySceiCNK49YinEfJ9fR15gJZQ5CeFNkM0POA46aUErjwIoGIkihttnLnsx+eZaXQ8bzbt6GrKy1w2O1dzPC/Zj+fmPFP+Xa+t9+88ehCfZ37M59lbnFNIALgq20tXpMJmYFpcKRlc+jZ+SVXfAQ4UTI6pkDXy1a+4oi8szeUPuyIvA9+PJ+Kni3MFgwQwjRAtXVEl3fPQ6/ULbm5MlDrg8FgifgTAv6urEzgEuKrY493xgUoMAyAQCNT4YzHvjcWc5htE8QEIcmfxvDa70AnkUL5ZDu6PxbyBQKCmZGD6ak+vnc3OByIRq7xRXU6gXBHkQ0UP3LXnVQtgpK9jFsgjTN0It+zNkbWz2XlzTV2iZCAuZwCqCrqztEX70YdBYyraWW1WxYFZdfV4pasoSdxrDF1iArgUegUTNaQd6AIY6z70BSBlo7eXc8YScd9yfIV2AQzR3lKCof7+X4HLKPdb4fCeVbesICsUfUQgBPw2mE5nSgZL0fQZQFH5yLbtDbcK37Rpyx2g7wEqLk8V34iSQcZxzqJ6EqjPGeZnLW1tDTcLb2lrayhU5U4D61A9OXg+9V2xdt3fdOa22jgwgBLVfGE4aEf2rwYP2pH9mi8Mo0RBnOz0lRfL6//60PyxmHft33NdiDwHiCgZ15AecHuGHOcHgBY7ukVx2wVpV8ECVOHE/NTkS6OjowsrGhRlhcPbUHkX2FhKuNyTqYy7Ik8Pp5O9y3EqPvoZxzkLNAVt+14D8wE12F47MeFeK3cjcsY06L2QTF6sxFgxwX+lfwBl/7JL8gMxeQAAAABJRU5ErkJggg==',
+ function( event )
+ {
+ Image3D_%uid%.mouseEventGenerator.setControlState( event, MouseEventGenerator_%uid%.CONTROL_ROTATE_XY );
+ Image3D_%uid%.toolbar.activate( this );
+ }
+ );
+ Image3D_%uid%.toolbar.addButton(
+ 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAUwwAAFMMBFXBNQgAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAANkSURBVEiJtZXNT5xVFMZ/574DTKGyaEi0kkZIXWiYzjsp73ww6cJJY20JVaPWtbFa48bEFdWNX2msiYkGkxq18R9Q2vgxgExIE01wZqCFAetmVJoYdYHWUoEpwtzjgs4I+DJQo8/ynOc8zz25OeeIqvJ/IlArKSISisU6payHRczhxWu/HSwWi0tuLD6ilmHBZKYuZie0xitls5wbTTwG+g5wRyV2fWfjjpkLF2640fiaIv0ZnGcLY19/ui0D13WbtD7YJ/DkKoOvxOrAiiOD3+RyBYB9nYmwMdoD9ABdqz7yAcul5wuFwsKmBq7rNlEfzAEdwCyixwv5/GebtQ8QjiYeEvQs0AJc5s8b8bUmZh27Pvgm0IGQLYuGtxIHmBrLflKn5TBCFuigIfi2bwfhaPSIYAaAq1LnhCdHR3/aSnwtPM/bvSzOFNAihkcmc7nz6zoQNWcAxPDUrYoDjI+P/2KUJwDU8q6ImKpBJBbrQGgDvVRx/jeYGM+lBUaB2yPRaCdU5sDKAwiomLRf4Z6jp1oD1unfEH55Jn1yaCNXIQ0krcohYMwAqNAFzIkVXwOjziIwBAwptAIxY2Xej1s2pIE5hATUGDQ/tPW8flxUziLy0sznva9up8YAhEKh+vZUKlhb/I2kqJwBOXcl3ftaLW57KhUMhUL1VQOnsam/eX6xFIrHXb+CPUdPtYpqP/D9coM90XbfKw3y+EeOH9f1uuLN84slZ8fOdNVALMMAgbIe8StybOBhVnfSvXVL8iuNDaX2hR9e8H2+2JsauqoJYClnBAc10g2c3lhjA3LesXp5fXBlxk9foVsAI5qBNZ/sRuPfAXsRfXA7K8IPbjTxKOjHwI9T4/m7VFX/3kWizwCKyoee5+2+VfH9+w/cCfo+oGJ5unIjqgaFfH4E1T6gZdk45yLJZOt2xSPJZGs5sNwP7EK1b/Ji7otKbt02vX5b00lgAiWhK+XpsBc/tpV42Isf05XyNEoCJL8493vv2vw/Bq09lQo2/7FwGpHnABGlYI0Mgh2cyue/BIh4iQOK7RakWwUXUIW3SteuvlgsFpdqGlTgxmIHUXkP2Fvt0O9kKlesyInpsWzGT2fTo1/I50eAu8Oed4/BuV8Nh5pmZ+3N9AAiw44hcymb/XYzjZod/Ff4C3BLaHc8XLxLAAAAAElFTkSuQmCC',
+ function( event )
+ {
+ Image3D_%uid%.mouseEventGenerator.setControlState( event, MouseEventGenerator_%uid%.CONTROL_ROTATE_Z );
+ Image3D_%uid%.toolbar.activate( this );
+ }
+ );
+ Image3D_%uid%.toolbar.addButton(
+ 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAUwwAAFMMBFXBNQgAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAKYSURBVEiJtZRPSFRRFIe/8+Zppi2KoiiCojSEyKmcZhISE4Ig3UQFQS2iTaVWq1CjsEXQH2whZdKuCIRatAg1aBOtauZJqIsWRSkZFET0hwqdZubXoiZnpueUpgfe4p7z3vedc3n3miRmM1yA9ZHITiVVk1s0U/eA58XyATaEw5tSKfbl5oXFhvqj3S6AkqrB7Hg2na5Bz/P+1uGA5/VXVEYOYGrIbo4bQLfj+5XRNeTFGvUP+ydJg/2PGw11+tX/FEwBnhkDXqzJ4Ep+wTThE5LoUcFlf8F/wtMx5EWPIXX8TkgiGAqFAZPETD3BysgWST+hsxlu5qJ0e/vipJu4+2v5Fbg53NtyfVXd+R6Mjy97Wvavqr+wX1KTq5KasXkJJ/At3mFSBPhk4uzLvpb7mcysCcp2XFyesNQoRh+iCNgaSLhLk25iAFiK2AtagdmFRMmcYvfLeBfGXuAaqA7MRkpWr9HtPck00/8cSKOg54CjOcmiiXa4iFlxxpv1YPdcjZ82nIMSu1e+e1qQifIXYIeAPZK1v7jb/OpX8h6wCDgMUPghXogxH/T2u1NULfTQjCeBkoLSvwpknBrubV040td8YiLJqGHngCUA8QWFceAZUOt+Twwha/BjTTKBf+jbWDvY8EQjdgIoS7qBV5iugmJjhfYmj2DuOxxqA47dzAI72uWYLg0/aBtLmbbhUPt67efxkZ7mXnNTy5RSHWjzSDhe9fpO6/vsriTWVVaWz+Qhk8TajVWlkn5OYDiHg5siZ6ayXfkiGAofcZ3EydwtapsJSTAUPoJZp8ycXMF/S9JwwNI5v79oWhI/+GSCKUsmg0P6sjN7AnYrp1YeDFVFBvsfRfPBK0KhcrNANdjtrEIqFQVm/7r+AbMwjQnKOMtVAAAAAElFTkSuQmCC',
+ function( event )
+ {
+ var oldDriver = Image3D_%uid%.renderer.getDriver();
+ Image3D_%uid%.renderer.setDriver( new PngDriver_%uid%() );
+ Image3D_%uid%.renderer.render();
+ Image3D_%uid%.renderer.setDriver( oldDriver );
+ return false;
+ }
+ );
+ Image3D_%uid%.toolbar.addButton(
+ 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAUwwAAFMMBFXBNQgAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAALDSURBVEiJtZVfiFR1FMc/33vHGQ0CEbaoxCxWWsictrnOsGDsQxrpRlGvvki9ZKZBuKx/EFsCdX0TXYTeRNpog32IzT8EQi/W7B1ldx8iiNgtMEZMqIdwWefebw825czOrrmuB+7LOfd+Pueee35c2eZhRgbgxVLpLSfubi5KHhqP47GFAJ3F4sY0ZXtz3mhsslIeygA4cTfSh410Tk/EcXyvDsfjuLKhUNqB/H5jc5wBhoKWT4nTk/HYLv+P+dn2ROX7XcKDrepzBfcBvzvG47EPBKcWFiwS/p+kvNtwsrXgAeH1mIzLe7BP/JuwTT6KioBss1RXvlDaZPsO9GFGpjnR/vqR9QnBgMzjmAtT5/cdWrvt2AVbk9Pn+nqf3Xr8OQfpGQcMhDO6lGR9EuiU+d2BDk6P9l2+mzdnixIHg5g2iy9T0fd0z0AXcFNK31F/f+DArwGlIE2uplmfFbwt+AaxTvY9tggA/QwUsF/B7Jge7bssewS0ak2c6zTeLLiy6nrbb8CroOFUfIK93ebdKPp02YIjIjvzEbeXT9vsltjyTM/R7K1k2fCKTO1WaG017k6tY38+dnMlKAeuhqjH4qyA60/cWA1ca/kGEmI29zlOV4SzagcSEayvXtz7F+ii8R7gUWXSkZ/O7b8BVI231EJdAg7MnUaTwMbGFVBvmvWvwEwaJp8BiHQEaAN+mPpq/4//3L9XKApr6TXgCPDt7dncHw1Nt1rTdduOttWkAoTjU1/3VgGeeqP/kaxzReRqXQCw5s3jTwaJO42rv4zuuzIHZpsXCoWOpTxktnn+pa5223dGJIL38htLH7ea4WIiHxV3ZoLaAWj8BoeXQpKPijuRBi0FzYIHltThgOq5Vj+cRUlawecT3LdkPjjUT7J0FfRFU60jH3WVJirflReCb4iiDil8GTTcUEjTMsxzDpYy/gZYSq+GrT8bOwAAAABJRU5ErkJggg==',
+ function( event )
+ {
+ var oldDriver = Image3D_%uid%.renderer.getDriver();
+ Image3D_%uid%.renderer.setDriver( new SvgDriver_%uid%() );
+ Image3D_%uid%.renderer.render();
+ Image3D_%uid%.renderer.setDriver( oldDriver );
+ return false;
+ }
+ );
+
+ // Put everything together and add it to the document
+ Image3D_%uid%.container.appendChild( Image3D_%uid%.canvas );
+ Image3D_%uid%.container.appendChild( Image3D_%uid%.controlOverlay );
+ scriptTag_%uid%.parentNode.insertBefore( Image3D_%uid%.container, scriptTag_%uid%.nextSibling );
+
+ // Init a new renderer
+ Image3D_%uid%.renderer = new Renderer_%uid%();
+ Image3D_%uid%.renderer.setDriver( new CanvasDriver_%uid%( Image3D_%uid%.canvas ) );
+ Image3D_%uid%.renderer.setEventGenerator( Image3D_%uid%.mouseEventGenerator );
+
+ // Render the scene for the first time
+ Image3D_%uid%.renderer.render();
+ }
+}
View
9 data/DynamicCanvas/Init.js
@@ -0,0 +1,9 @@
+// We need to know where the script tag of this file is in the dom
+var allScriptTags = document.documentElement.getElementsByTagName( 'script' );
+var scriptTag_%uid% = false;
+scriptTag_%uid% = allScriptTags[allScriptTags.length - 1];
+// scriptTag is now the last added script, so it should be ours.
+
+// Data section *start*
+var polygones_%uid% = %polygones%
+// Data section *end*
View
204 data/DynamicCanvas/MouseEventGenerator.js
@@ -0,0 +1,204 @@
+/**
+ * EventGenerator which enables controlling the render by mouse movements
+ */
+function MouseEventGenerator_%uid%( activateObject, deactivateObject, movementObject ) {
+ this._activateObject = activateObject;
+ this._deactivateObject = deactivateObject;
+ this._movementObject = movementObject;
+ this.setControlState( null, MouseEventGenerator_%uid%.CONTROL_TRANSLATE_XY );
+}
+
+// Class constants
+MouseEventGenerator_%uid%.CONTROL_TRANSLATE_XY = 1;
+MouseEventGenerator_%uid%.CONTROL_TRANSLATE_Z = 2;
+MouseEventGenerator_%uid%.CONTROL_ROTATE_XY = 3;
+MouseEventGenerator_%uid%.CONTROL_ROTATE_Z = 4;
+
+MouseEventGenerator_%uid%.prototype = {
+ /**
+ * The renderer which should be notified
+ */
+ _renderer: false,
+
+ /**
+ * Dom object to register mousedown event on
+ */
+ _activateObject: false,
+ /**
+ * Dom object to register mouseup event on
+ */
+ _deactivateObject: false,
+ /**
+ * Dom object to register mousemove event on
+ */
+ _movementObject: false,
+
+ /**
+ * Anonymous function to be called on activation
+ * Needed to compensate javascripts strange "this" behaviour
+ */
+ _activateFunction: false,
+ /**
+ * Anonymous function to be called on deactivation
+ * Needed to compensate javascripts strange "this" behaviour
+ */
+ _deactivateFunction: false,
+ /**
+ * Anonymous function to be called on movement
+ * Needed to compensate javascripts strange "this" behaviour
+ */
+ _movementFunction: false,
+
+ /**
+ * Last captured x mouse position
+ */
+ _lastMouseX: 0,
+ /**
+ * Last captured y mouse position
+ */
+ _lastMouseY: 0,
+ /**
+ * Calculated x offset from the last mouse position
+ */
+ _mouseXOffset: 0,
+ /**
+ * Calculated y offset from the last mouse position
+ */
+ _mouseYOffset: 0,
+
+ /**
+ * Manipulation state
+ */
+ _inProgress: false,
+
+ /**
+ * Current control state
+ */
+ _currentControlState: 0,
+
+ /**
+ * Attach to a renderer
+ */
+ attach: function( renderer ) {
+ // Register events on the appropriate DOM objects
+ var self = this;
+ this._activateFunction = function( event ) { self._onMouseDown( event ); };
+ this._deactivateFunction = function( event ) { self._onMouseUp( event ); };
+ this._movementFunction = function( event ) { self._onMouseMove( event ); };
+ this._activateObject.addEventListener( 'mousedown', this._activateFunction , true );
+ this._deactivateObject.addEventListener( 'mouseup', this._deactivateFunction, true );
+ this._movementObject.addEventListener( 'mousemove', this._movementFunction, true );
+ // Set the renderer to notify of events
+ this._renderer = renderer;
+ },
+
+ /**
+ * Detach from a renderer
+ */
+ detach: function() {
+ // Stop notifying the renderer of events
+ this._renderer = false;
+
+ // Remove the registered event listeners
+ this._activateObject.removeEventListener( 'mousedown', this._activateFunction, false );
+ this._deactivateObject.removeEventListener( 'mouseup', this._deactivateFunction, false );
+ this._movementObject.removeEventListener( 'mousemove', this._movementFunction, false );
+ },
+
+ /**
+ * Notify the attached renderer of an occured event
+ */
+ _notifyRenderer: function( event, data ) {
+ if ( this._renderer != false )
+ {
+ this._renderer.notify( event, data );
+ }
+ },
+
+ /**
+ * Capture any mousemove event
+ */
+ _onMouseMove: function( event )
+ {
+ var progressOffset = 4;
+ var calcOffsetX = 0;
+ var calcOffsetY = 0;
+
+ if( !this._inProgress )
+ {
+ return;
+ }
+
+ if ( this._mouseXOffset < progressOffset && this._mouseXOffset > -progressOffset )
+ {
+ this._mouseXOffset += this._lastMouseX - event.clientX;
+ calcOffsetX = 0;
+ }
+ else
+ {
+ calcOffsetX = Math.round( ( this._lastMouseX - event.clientX ) / progressOffset )
+ this._lastMouseX = event.clientX;
+ this._mouseXOffset = 0;
+ }
+
+ if ( this._mouseYOffset < progressOffset && this._mouseYOffset > -progressOffset )
+ {
+ this._mouseYOffset += this._lastMouseY - event.clientY;
+ calcOffsetY = 0;
+ }
+ else
+ {
+ calcOffsetY = Math.round( ( this._lastMouseY - event.clientY ) / progressOffset )
+ this._lastMouseY = event.clientY;
+ this._mouseYOffset = 0;
+ }
+
+
+ if ( calcOffsetX != 0 || calcOffsetY != 0 )
+ {
+ switch ( this._currentControlState )
+ {
+ case MouseEventGenerator_%uid%.CONTROL_TRANSLATE_XY:
+ this._notifyRenderer( Renderer_%uid%.EVENT_TRANSLATE, [ -calcOffsetX * 2, -calcOffsetY * 2, 0 ] );
+ break
+ case MouseEventGenerator_%uid%.CONTROL_TRANSLATE_Z:
+ this._notifyRenderer( Renderer_%uid%.EVENT_TRANSLATE, [ 0, 0, -calcOffsetY * 2 ] );
+ break;
+ case MouseEventGenerator_%uid%.CONTROL_ROTATE_XY:
+ this._notifyRenderer( Renderer_%uid%.EVENT_ROTATE, [ calcOffsetY < 0 ? -calcOffsetY * 2 : 360 - calcOffsetY * 2, calcOffsetX < 0 ? 360 - -calcOffsetX * 2 : calcOffsetX * 2, 0 ] );
+ break;
+ case MouseEventGenerator_%uid%.CONTROL_ROTATE_Z:
+ this._notifyRenderer( Renderer_%uid%.EVENT_ROTATE, [ 0, 0, calcOffsetX < 0 ? 360 - -calcOffsetX * 2 : calcOffsetX * 2 ] );
+ break;
+ }
+ }
+ },
+
+ /**
+ * Capture any mousedown event
+ */
+ _onMouseDown: function( event )
+ {
+ this._lastMouseX = event.clientX;
+ this._lastMouseY = event.clientY;
+ this._inProgress = true;
+ },
+
+ /**
+ * Capture any mouseup event
+ */
+ _onMouseUp: function( event )
+ {
+ this._inProgress = false;
+ },
+
+ /**
+ * Set the specified control state and change the control
+ * representation appropriatly
+ */
+ setControlState: function( event, state )
+ {
+ this._currentControlState = state;
+ }
+
+}
View
45 data/DynamicCanvas/PngDriver.js
@@ -0,0 +1,45 @@
+/**
+ * Driver which outputs a png image of the rendering context
+ */
+function PngDriver_%uid%() {
+ // Nothing to do here
+}
+
+PngDriver_%uid%.prototype = {
+ /**
+ * The in-memory canvas element, where the image will be
+ * rendered to before it is saved to a png
+ */
+ _canvasElement: false,
+
+ /**
+ * Begin output by creating in-memory canvas to draw to
+ */
+ begin: function( x, y ) {
+ this._canvasElement = document.createElement( 'canvas' );
+ this._canvasElement.width = x;
+ this._canvasElement.height = y;
+
+ if ( !this._canvasElement.toDataURL )
+ {
+ window.alert('Sorry your browser does not support export to an image file.');
+ throw "Canvas does not support toDataURL";
+ }
+ this._canvasDriver = new CanvasDriver_%uid%( this._canvasElement );
+ },
+
+ /**
+ * Draw a given polygon
+ */
+ drawPolygon: function( polygon )
+ {
+ this._canvasDriver.drawPolygon( polygon );
+ },
+
+ /**
+ * Finish the output process
+ */
+ finish: function() {
+ window.location = this._canvasElement.toDataURL();
+ }
+}
View
263 data/DynamicCanvas/Renderer.js
@@ -0,0 +1,263 @@
+/**
+ * Class to render the given set of polygones
+ * Rendering will be done using a specified output driver
+ * All further manipulations are based on an event generator
+ * which is associated with the renderer
+ */
+function Renderer_%uid%()
+{
+ // Generate sinus and cosinus lookup tables
+ this._generateSinCosTables();
+}
+
+// Class constants
+Renderer_%uid%.EVENT_TRANSLATE = 1;
+Renderer_%uid%.EVENT_ROTATE = 2;
+
+Renderer_%uid%.prototype = {
+ /**
+ * Width of the image
+ */
+ _imageSizeX: %width%.0,
+ /**
+ * Height of the image
+ */
+ _imageSizeY: %height%.0,
+
+ /**
+ * Cos values (0-360 degrees, step 1 )
+ */
+ _cos: Array(),
+ /**
+ * Sin values ( 0-360 degrees, step 1 )
+ */
+ _sin: Array(),
+
+ /**
+ * Viewpoint used for rendering
+ */
+ _viewpoint: 500.0,
+ /**
+ * Distance used for rendering
+ */
+ _distance: 500.0,
+
+ /**
+ * Output driver which renders the actual data
+ */
+ _driver: false,
+
+ /**
+ * Event generator to notify the renderer
+ */
+ _eventGenerator: false,
+
+ /**
+ * Generate sinus and cosinus lookup tables for faster access
+ */
+ _generateSinCosTables: function() {
+ var factor = Math.PI*2 / 360;
+ this._sin = new Array();
+ this._cos = new Array();
+ for(var i=0; i<=360; i++) {
+ this._sin[i] = Math.sin(factor*i);
+ this._cos[i] = Math.cos(factor*i);
+ }
+ },
+
+ /**
+ * Set a driver to use for output rendering
+ */
+ setDriver: function( driver ) {
+ // Delete the old driver class from memory
+ delete this._driver;
+
+ // Set the new one
+ this._driver = driver;
+ },
+
+ /**
+ * Return the current used driver
+ */
+ getDriver: function() {
+ return this._driver;
+ },
+
+ /**
+ * Set an event generator to listen to
+ */
+ setEventGenerator: function( eventGenerator ) {
+ // Delete the old event generator to free memory and stop it from notifying the renderer
+ if ( this._eventGenerator != false )
+ {
+ this._eventGenerator.detach();
+ }
+ delete this._eventGenerator;
+
+ // Set new event generator and attach it to this renderer
+ this._eventGenerator = eventGenerator;
+ this._eventGenerator.attach( this );
+ },
+
+ /**
+ * Set the viewpoint for rendering
+ */
+ setViewpoint: function( viewpoint ) {
+ this._viewpoint = viewpoint;
+ },
+
+ /**
+ * Set the distance for rendering
+ */
+ setDistance: function( distance ) {
+ this._distance = distance;
+ },
+
+ /**
+ * Compare two polygones by their medium z distance
+ * Used for sorting the polygones array
+ */
+ _sortByMidZ: function( polygon1, polygon2 )
+ {
+ var midZ_polygon1 = 0.0;
+ var midZ_polygon2 = 0.0;
+
+ for( var i = 0; i<polygon1["points"].length; i++ )
+ {
+ midZ_polygon1 += polygon1["points"][i]["z"];
+ }
+ midZ_polygon1 = midZ_polygon1 / parseFloat( polygon1["points"].length );
+
+ for( var i = 0; i<polygon2["points"].length; i++ )
+ {
+ midZ_polygon2 += polygon2["points"][i]["z"];
+ }
+ midZ_polygon2 = midZ_polygon2 / parseFloat( polygon2["points"].length );
+
+ return midZ_polygon2 - midZ_polygon1;
+ },
+
+ /**
+ * Sort the polygones by their medium z distance
+ */
+ _sortPolygones: function()
+ {
+ polygones_%uid%.sort( this._sortByMidZ );
+ },
+
+ /**
+ * Apply a specified rotation on all of the polygones
+ */
+ _rotate: function( rx, ry, rz )
+ {
+ for( var i = 0; i<polygones_%uid%.length; i++ )
+ {
+ for ( var j = 0; j<polygones_%uid%[i]["points"].length; j++ )
+ {
+ var px = polygones_%uid%[i]["points"][j]["x"];
+ var py = polygones_%uid%[i]["points"][j]["y"];
+ var pz = polygones_%uid%[i]["points"][j]["z"];
+
+ // Rotate around the z axis
+ if ( rz != 0 )
+ {
+ var x = this._cos[rz] * px + this._sin[rz] * py;
+ var y = -this._sin[rz] * px + this._cos[rz] * py;
+ var z = pz;
+
+ px = x; py = y; pz = z;
+ }
+
+ // Rotate around the x axis
+ if ( rx != 0 )
+ {
+ var x = px;
+ var y = this._cos[rx] * py + ( -this._sin[rx] * pz );
+ var z = this._sin[rx] * py + this._cos[rx] * pz;
+
+ px = x; py = y; pz = z;
+ }
+
+ // Rotate around the y axis
+ if ( ry != 0 )
+ {
+ var x = this._cos[ry] * px + this._sin[ry] * pz;
+ var y = py;
+ var z = -this._sin[ry] * px + this._cos[ry] * pz;
+
+ px = x; py = y; pz = z;
+ }
+
+ polygones_%uid%[i]["points"][j]["x"] = px;
+ polygones_%uid%[i]["points"][j]["y"] = py;
+ polygones_%uid%[i]["points"][j]["z"] = pz;
+ }
+ }
+ },
+
+ /**
+ * Apply a specified translation on all of the polygones
+ */
+ _translate: function( tx, ty, tz )
+ {
+ for( var i = 0; i<polygones_%uid%.length; i++ )
+ {
+ for ( var j = 0; j<polygones_%uid%[i]["points"].length; j++ )
+ {
+ polygones_%uid%[i]["points"][j]["x"] = polygones_%uid%[i]["points"][j]["x"] + tx;
+ polygones_%uid%[i]["points"][j]["y"] = polygones_%uid%[i]["points"][j]["y"] + ty;
+ polygones_%uid%[i]["points"][j]["z"] = polygones_%uid%[i]["points"][j]["z"] + tz;
+ }
+ }
+ },
+
+
+ /**
+ * A new event has been occured
+ */
+ notify: function( event, data ) {
+ switch ( event )
+ {
+ case Renderer_%uid%.EVENT_TRANSLATE:
+ this._translate( data[0], data[1], data[2] );
+ break;
+ case Renderer_%uid%.EVENT_ROTATE:
+ this._rotate( data[0], data[1], data[2] );
+ break;
+ }
+ this.render();
+ },
+
+ /**
+ * Calculate screen coordinates for every polygon and render it
+ */
+ render: function() {
+ // Begin output
+ this._driver.begin( this._imageSizeX, this._imageSizeY );
+
+ // Sort all polygones by their Z-Order
+ this._sortPolygones();
+
+ // Draw the background
+ this._driver.drawPolygon( { points: [ [ 0, 0 ], [ this._imageSizeX, 0 ], [ this._imageSizeX, this._imageSizeY ], [ 0, this._imageSizeY ] ], colors: [ %background%, undefined, undefined ] } );
+
+ // Calculate screen coordinate for every polygon point and send it to the driver for drawing
+ for( var i = 0; i<polygones_%uid%.length; i++ )
+ {
+ var screenCoords = new Array();
+ for ( var j = 0; j<polygones_%uid%[i]["points"].length; j++ )
+ {
+ screenCoords.push(
+ [
+ this._viewpoint * polygones_%uid%[i]["points"][j]["x"] / (polygones_%uid%[i]["points"][j]["z"] + this._distance) + this._imageSizeX/2,
+ this._viewpoint * polygones_%uid%[i]["points"][j]["y"] / (polygones_%uid%[i]["points"][j]["z"] + this._distance) + this._imageSizeY/2
+ ]
+ );
+ }
+ this._driver.drawPolygon( { points: screenCoords, colors: polygones_%uid%[i]["colors"] } );
+ }
+
+ // Tell the driver to finish his work
+ this._driver.finish();
+ }
+}
View
47 data/DynamicCanvas/RotateAnimationEventGenerator.js
@@ -0,0 +1,47 @@
+/**
+ * Class to generate timer based events to roate the object
+ * ( For testing purpose only )
+ */
+function RotateAnimationEventGenerator_%uid%() {
+ this._timeout( this );
+}
+
+RotateAnimationEventGenerator_%uid%.prototype = {
+ /**
+ * Renderer to send events to
+ */
+ _renderer: false,
+
+ /**
+ * Attach to a renderer
+ */
+ attach: function( renderer ) {
+ // Set the renderer to notify of events
+ this._renderer = renderer;
+ },
+
+ /**
+ * Detach from a renderer
+ */
+ detach: function() {
+ this._renderer = false;
+ },
+
+ /**
+ * Notify the renderer of an occured event
+ */
+ _notifyRenderer: function( event, data ) {
+ if ( this._renderer != false )
+ {
+ this._renderer.notify( event, data );
+ }
+ },
+
+ /**
+ * Called every 10 ms to send rotation event
+ */
+ _timeout: function( self ) {
+ self._notifyRenderer( Renderer_%uid%.EVENT_ROTATE, [ 2, 4, 2 ] );
+ window.setTimeout( function(){ self._timeout( self ); }, 10 );
+ }
+}
View
2 data/DynamicCanvas/Startup.js
@@ -0,0 +1,2 @@
+// Register our new onload function
+document.addEventListener( "DOMContentLoaded", Image3D_%uid%.init, false );
View
62 data/DynamicCanvas/SvgDriver.js
@@ -0,0 +1,62 @@
+function SvgDriver_%uid%() {
+ this._polygones = new Array();
+ this._gradients = new Array();
+}
+
+SvgDriver_%uid%.prototype = {
+ _svg: "",
+ _index: 0,
+ _polygones: new Array(),
+ _gradients: new Array(),
+
+ _decimal2hex: function( decimal ) {
+ var charset = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"];
+ var hex = Array();
+
+ for( var d = decimal; d != 0; d = parseInt( d / 16 ) )
+ {
+ hex.unshift( charset[ d%16 ] )
+
+ }
+ if ( hex.length == 0 )
+ {
+ hex.unshift( "0" );
+ }
+
+ return hex.join( "" );
+ },
+
+ _getStyle: function( color ) {
+ var rx = ( this._decimal2hex( color["r"] ).length < 2 ) ? "0" + this._decimal2hex( color["r"] ) : this._decimal2hex( color["r"] );
+ var gx = ( this._decimal2hex( color["g"] ).length < 2 ) ? "0" + this._decimal2hex( color["g"] ) : this._decimal2hex( color["g"] );
+ var bx = ( this._decimal2hex( color["b"] ).length < 2 ) ? "0" + this._decimal2hex( color["b"] ) : this._decimal2hex( color["b"] );
+
+ return "fill: #" + rx + gx + bx + "; fill-opacity: " + color["a"] + "; stroke: none;";
+ },
+
+ _getGradientStop: function( color, offset, alpha ) {
+ },
+
+ begin: function( x, y ) {
+ this._svg += '<?xml version="1.0" ?>\n';
+ this._svg += '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"\n';
+ this._svg += '"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\n\n';
+ this._svg += '<svg xmlns="http://www.w3.org/2000/svg" x="0" y="0" width="' + x + '" height="' + y + '">\n';
+ },
+
+ drawPolygon: function( polygon ) {
+ var pointlist = ""
+ for( var i=0; i<polygon["points"].length; i++ )
+ {
+ pointlist += ( Math.round( polygon["points"][i][0] * 100 ) / 100 ) + "," + ( Math.round( polygon["points"][i][1] * 100 ) / 100 ) + " ";
+ }
+ this._polygones.push( "<polygon points=\"" + pointlist.substr( 0, pointlist.length -1 ) + "\" style=\"" + this._getStyle( polygon["colors"][0] ) +"\" />\n" );
+ },
+
+ finish: function() {
+ this._svg += "<defs></defs>\n";
+ this._svg += this._polygones.join( "" );
+ this._svg += "</svg>";
+ window.location = "data:image/svg+xml;base64," + Base64_%uid%.encode( this._svg );
+ }
+}
View
64 data/DynamicCanvas/Toolbar.js
@@ -0,0 +1,64 @@
+/**
+ * Class managing the toolbar with all of the control buttons
+ */
+function Toolbar_%uid%( container )
+{
+ // Create the needed ul to hold the toolbar buttons
+ this.container = document.createElement( 'ul' );
+ this.container.setAttribute( 'style', '-moz-user-select: none;' );
+ this.container.style.margin = '0px';
+ this.container.style.padding = '0px';
+ this.container.style.marginLeft = '8px';
+ this.container.style.marginTop = '4px';
+ this.container.style.width = ( %width% - 8 ) + 'px';
+
+ // Add the ul to our outer container
+ container.appendChild( this.container );
+}
+
+Toolbar_%uid%.prototype = {
+ container: false,
+ buttons: new Array(),
+ activeButton: false,
+
+ addButton: function( image, onClick ) {
+ var button = document.createElement( 'li' );
+ var img = document.createElement( 'img' );
+ img.src = image;
+ button.innerHtml = image;
+ button.appendChild( img );
+ button.style.backgroundColor = '#d3d7cf';
+ button.style.borderBottom = '1px solid #555753';
+ button.style.borderRight = '1px solid #555753';
+ button.style.borderTop = '1px solid #eeeeec';
+ button.style.borderLeft = '1px solid #eeeec';
+ button.style.display = 'block';
+ button.style.cssFloat = 'left';
+ button.style.height = "24px";
+ button.style.width = "24px";
+ button.style.padding = "0px";
+ button.style.margin = "0px";
+ button.style.marginRight = "6px";
+
+ button.addEventListener( 'click', onClick, false );
+
+ this.container.appendChild( button );
+ this.buttons.push( button );
+ return button;
+ },
+
+ activate: function( o ) {
+ for( var key in this.buttons )
+ {
+ this.buttons[key].style.borderBottom = '1px solid #555753';
+ this.buttons[key].style.borderRight = '1px solid #555753';
+ this.buttons[key].style.borderTop = '1px solid #eeeeec';
+ this.buttons[key].style.borderLeft = '1px solid #eeeeec';
+ }
+ o.style.borderBottom = '1px solid #eeeeec';
+ o.style.borderRight = '1px solid #eeeeec';
+ o.style.borderTop = '1px solid #555753';
+ o.style.borderLeft = '1px solid #555753';
+ this.activeButton = o;
+ }
+}
View
43 docs/examples/dynamic_cubes.php
@@ -0,0 +1,43 @@
+<?php
+
+set_time_limit(0);
+require_once('Image/3D.php');
+
+$world = new Image_3D();
+$world->setColor(new Image_3D_Color(0, 0, 0));
+
+$light1 = $world->createLight('Light', array(-300, 0, -300));
+$light1->setColor(new Image_3D_Color(252, 175, 62));
+
+$light2 = $world->createLight('Light', array(300, -300, -300));
+$light2->setColor(new Image_3D_Color(164, 0, 0));
+
+$count = 3;
+
+$size = 20;
+$offset = 10;
+
+for ($x = -($count - 1) / 2; $x <= ($count - 1) / 2; ++$x) {
+ for ($y = -($count - 1) / 2; $y <= ($count - 1) / 2; ++$y) {
+ for ($z = -($count - 1) / 2; $z <= ($count - 1) / 2; ++$z) {
+// if (max(abs($x), abs($y), abs($z)) < ($count - 1) / 2) continue;
+ if (max($x, $y, $z) <= 0) continue;
+
+ $cube = $world->createObject('quadcube', array($size, $size, $size));
+ $cube->setColor(new Image_3D_Color(255, 255, 255, 75));
+ $cube->transform($world->createMatrix('Move', array($x * ($size + $offset), $y * ($size + $offset), $z * ($size + $offset))));
+ }
+ }
+}
+
+$world->transform($world->createMatrix('Rotation', array(220, 50, 0)));
+$world->transform($world->createMatrix('Scale', array(2, 2, 2)));
+
+$world->setOption(Image_3D::IMAGE_3D_OPTION_BF_CULLING, true);
+$world->setOption(Image_3D::IMAGE_3D_OPTION_FILLED, true);
+
+$world->createRenderer('perspectively');
+$world->createDriver('DynamicCanvas');
+$world->render(250, 250, 'Image_3D_Dynamic_Cubes.js');
+
+echo $world->stats();

0 comments on commit ad6a2dd

Please sign in to comment.
Something went wrong with that request. Please try again.