   [web] 3D Engine for VRML written in PHP
  This is my first post here on GameDev. I ran into a problem when I was trying to code a simple 3D Engine in PHP. The problem occurs with hidden planes or what I believe is referred to here as backface culling. I believe everything is in order but for some reason certain hidden faces are being drawn when they're not supposed to be. As of right now I have the code working with four VRML models you can get HERE.The problem is only noticeable on the sphere.wrl model. The cylinder.wrl model does not render. However, the cube.wrl and pyramid.wrl render fine.Here's the code:<?php$filename = 'sphere.wrl';$vrml_str = file_get_contents($filename);// Convert the file into a string$vrml_tok = str_split($vrml_str);// Split the string into an array of each character// Traverse the array and eliminate anything that isn't of numeric relevance// Also retain brackets to allow dissection of quantitiesfor($i = 0; $i < sizeof($vrml_tok); $i++){switch($vrml_tok[$i]){case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9':case '-':case '.':case ' ':case '{':case '}':case '[':case ']':break;default:$vrml_tok[$i] = "";break;}}$vrml_tok = implode($vrml_tok);// Join the array into a string$vrml_tok = explode(' 01 ', $vrml_tok);// Split the string into arraysarray_shift($vrml_tok);// Destroy the first entry in the array// Extract the relevant pieces of info from the strings in each array// $vrml_1: Object's center location// $vrml_2: Object's rotational orientation// $vrml_3: Object's vertices// $vrml_4: Object's polygonsfor($i = 0; $i < sizeof($vrml_tok); $i++){$vrml_tok[$i] = substr($vrml_tok[$i], strpos($vrml_tok[$i], "{") + 2);$vrml_1[$i] = substr($vrml_tok[$i], 0, strpos($vrml_tok[$i], "[") - 1);$vrml_tok[$i] = substr($vrml_tok[$i], strpos($vrml_tok[$i], "[") + 1);$vrml_tok[$i] = substr($vrml_tok[$i], strpos($vrml_tok[$i], "[") + 1);$vrml_3[$i] = substr($vrml_tok[$i], 0, strpos($vrml_tok[$i], "]"));$vrml_tok[$i] = substr($vrml_tok[$i], strpos($vrml_tok[$i], "[") + 1);$vrml_4[$i] = substr($vrml_tok[$i], 0, strpos($vrml_tok[$i], "]"));$vrml_tok[$i] = substr($vrml_tok[$i], strpos($vrml_tok[$i], "]") + 1);$vrml_1[$i] = explode(" ", $vrml_1[$i]);$vrml_2[$i] = array_slice($vrml_1[$i], 3);$vrml_1[$i] = array_slice($vrml_1[$i], 0, 3);$vrml_3[$i] = explode(" ", $vrml_3[$i]);$vrml_4[$i] = explode(" ", $vrml_4[$i]);$temp = array_slice($vrml_4[$i], 0, 3);for($j = 4; $j < sizeof($vrml_4[$i]); $j += 4){$temp = array_merge($temp, array_slice($vrml_4[$i], $j, 3));}$vrml_4[$i] = $temp;}// Swap the values of x z y to become x y zfor($i = 0; $i < sizeof($vrml_1); $i++){$temp = $vrml_1[$i][1];$vrml_1[$i][1] = $vrml_1[$i][2];$vrml_1[$i][2] = $temp;}for($i = 0; $i < sizeof($vrml_2); $i++){$temp = $vrml_2[$i][1];$vrml_2[$i][1] = $vrml_2[$i][2];$vrml_2[$i][2] = $temp;}for($i = 0; $i < sizeof($vrml_3); $i++){for($j = 0; $j < sizeof($vrml_3[0]); $j += 3){$temp = $vrml_3[$i][$j + 1];$vrml_3[$i][$j + 1] = $vrml_3[$i][$j + 2];$vrml_3[$i][$j + 2] = $temp;}}// Compute the orientation of the object so as to find a rotational transform matrixfor($i = 0; $i < sizeof($vrml_2); $i++){$theta_1 = asin(abs($vrml_2[$i][2]));if($vrml_2[$i][1] >= 0 && $vrml_2[$i][2] >= 0){$theta_1 = -(pi() / 2 - $theta_1);}else{if($vrml_2[$i][1] < 0 && $vrml_2[$i][2] >= 0){$theta_1 = pi() / 2 - $theta_1;}else{if($vrml_2[$i][1] < 0 && $vrml_2[$i][2] < 0){$theta_1 = pi() / 2 + $theta_1;}else{if($vrml_2[$i][1] >= 0 && $vrml_2[$i][2] < 0){$theta_1 = -pi() / 2 - $theta_1;}}}}$theta_2 = acos(abs($vrml_2[$i][0]) / sqrt(pow($vrml_2[$i][0], 2) + pow($vrml_2[$i][1], 2)));if($vrml_2[$i][0] >= 0 && $vrml_2[$i][1] >= 0){$theta_2 = -(pi() / 2 - $theta_2);}else{if($vrml_2[$i][0] < 0 && $vrml_2[$i][1] >= 0){$theta_2 = pi() / 2 - $theta_2;}else{if($vrml_2[$i][0] < 0 && $vrml_2[$i][1] < 0){$theta_2 = -(pi() / 2 - $theta_2);}else{if($vrml_2[$i][0] >= 0 && $vrml_2[$i][1] < 0){$theta_2 = pi() / 2 - $theta_2;}}}}$ini_orient[$i] = identity(3);// Rotate the object about the world's X axis according to the computed $theta_1$rotX =array(array(1, 0, 0),array(0, cos($theta_1), -sin($theta_1)),array(0, sin($theta_1), cos($theta_1)));$ini_orient[$i] = multiply($rotX, $ini_orient[$i]);// Rotate the object about the world's Z axis according to the computed $theta_2$rotZ = array(array(cos($theta_2), -sin($theta_2), 0),array(sin($theta_2), cos($theta_2), 0),array(0, 0, 1));$ini_orient[$i] = multiply($rotZ, $ini_orient[$i]);// Rotate the object about its own Z axis using the value given$rotZ = array(array(cos($vrml_2[$i][3]), -sin($vrml_2[$i][3]), 0),array(sin($vrml_2[$i][3]), cos($vrml_2[$i][3]), 0),array(0, 0, 1));$ini_orient[$i] = multiply($ini_orient[$i], $rotZ);}// Create all the objects find in the world and store them in an array// Initialize these as wellfor($i = 0; $i < sizeof($vrml_1); $i++){$worldContainer[$i] = new VirtualObject();$temp = array(array());$temp[0][0] = $vrml_1[$i][0];$temp[1][0] = $vrml_1[$i][1];$temp[2][0] = $vrml_1[$i][2];$worldContainer[$i]->setCenter($temp);$worldContainer[$i]->setOrientation($ini_orient[$i]);for($j = 0; $j < sizeof($vrml_3[0]); $j += 3){$temp[0][0] = $vrml_3[$i][$j];$temp[1][0] = $vrml_3[$i][$j + 1];$temp[2][0] = $vrml_3[$i][$j + 2];$worldContainer[$i]->setVertex($temp);}$worldContainer[$i]->setPolygons($vrml_4[$i]);}// The class that stores virtual objectsclass VirtualObject{// Member declarationsprivate $center;// Object centerprivate $orientation;// Object orientationprivate $vertices;// Object verticesprivate $polyOrder;// Object polygons// Constructorfunction __construct(){}// Method declarationspublic function setCenter($n){$this->center = $n;}public function getCenter(){return $this->center;}public function setOrientation($n){$this->orientation = $n;}public function getOrientation(){return $this->orientation;}public function setVertex($n){$this->vertices[] = $n;}public function getVertex($n){return $this->vertices[$n];}public function getVertexCount(){return sizeof($this->vertices);}public function setPolygons($n){$this->polyOrder = $n;}public function getPolygon($n){$i = 3 * $n;returnarray($this->getVertex($this->polyOrder[$i]),$this->getVertex($this->polyOrder[$i + 1]),$this->getVertex($this->polyOrder[$i + 2]));}public function getPolyCount(){return sizeof($this->polyOrder) / 3;}}// Viewable screen dimensions$width = 640;$height = 480;// Sets up the camera's orientation and its focal distance$x = sqrt(2) / 2;$focal = 300;$a =array(array(-$x, 0.5, -0.5),array($x, 0.5, -0.5),array(0, -$x, -$x));// The vector normal to the viewing plane and parallel to the viewing direction$n2[0][0] = $a[0][2];$n2[1][0] = $a[1][2];$n2[2][0] = $a[2][2];// Invert camera's plane to allow for change of basis$a = inverse($a);// Create two images$image1 = imagecreate($width, $height);$image2 = imagecreate($width, $height);// Set the line thickness of each imageimagesetthickness($image1, 1);imagesetthickness($image2, 1);// Set the background colors for each image$blue1 = imagecolorallocate($image1, 0, 0, 127);$blue2 = imagecolorallocate($image2, 0, 0, 127);// Define some colors for each image$white = imagecolorallocate($image1, 255, 255, 255);$gray = imagecolorallocate($image2, 127, 127, 127);// Draw each polygonfor($i = 0; $i < $worldContainer[0]->getPolyCount(); $i++){// Retrieve the polygons from the cube object$points = $worldContainer[0]->getPolygon($i);// Rotate each vertex vector according to the indicated object's orientation$points[0] = multiply($worldContainer[0]->getOrientation(), $points[0]);$points[1] = multiply($worldContainer[0]->getOrientation(), $points[1]);$points[2] = multiply($worldContainer[0]->getOrientation(), $points[2]);// Determine the normal vector for each polygon$n1 = crossProduct(addition($points[0], scale(-1, $points[1])), addition($points[2], scale(-1, $points[1])));// Modify each point of the polygon so that its now distanced to the camera's center pointfor($j = 0; $j < 3; $j++){$points[$j][0][0] -= 150;$points[$j][1][0] -= 150;$points[$j][2][0] -= 300 * $x;}// Write each vector in terms of the camera's orientation (change of basis)$points[0] = multiply($a, $points[0]);$points[1] = multiply($a, $points[1]);$points[2] = multiply($a, $points[2]);// Determine the distortion due to perspective$focus[0] = $focal / magnitude($points[0]);$focus[1] = $focal / magnitude($points[1]);$focus[2] = $focal / magnitude($points[2]);// Transform the 3d points into 2d coordinates for drawing$x0 = $width / 2 + ($points[0][0][0] * $focus[0]);$x1 = $width / 2 + ($points[1][0][0] * $focus[1]);$x2 = $width / 2 + ($points[2][0][0] * $focus[2]);$y0 = $height / 2 + ($points[0][1][0] * $focus[0]);$y1 = $height / 2 + ($points[1][1][0] * $focus[1]);$y2 = $height / 2 + ($points[2][1][0] * $focus[2]);// Determine the cosine of the angle between the viewing plane and the polygonal plane$cosTheta = dotProduct($n1, $n2) / (magnitude($n1) * magnitude($n2));if($cosTheta <= 0){// White lines because these are the planes at the front of the objectimageline($image1, $x0, $y0, $x1, $y1, $white);imageline($image1, $x1, $y1, $x2, $y2, $white);imageline($image1, $x2, $y2, $x0, $y0, $white);}else{// Gray lines because these are the planes at the reare of the objectimageline($image2, $x0, $y0, $x1, $y1, $gray);imageline($image2, $x1, $y1, $x2, $y2, $gray);imageline($image2, $x2, $y2, $x0, $y0, $gray);}}// Make transparent the background of the viewable polygon drawingimagecolortransparent($image1, $blue1);// Layer it over the non-viewable polygon drawingimagecopymerge($image2, $image1, 0, 0, 0, 0, 640, 480, 100);// Flip the image horizontally// Output the finished imageheader("Content-type: image/png");imagepng($image2);// Destroy the imagesimagedestroy($image1);imagedestroy($image2);// Computes the cross product of two vectors// $a: a row or column vector// $b: a row or column vectorfunction crossProduct($a, $b){if(sizeof($a) == 1){// Cross product of row vectorsreturnarray(// Return the cross product of row vectors as a row vectorarray($a[0][1] * $b[0][2] - $a[0][2] * $b[0][1],-1 * ($a[0][0] * $b[0][2] - $a[0][2] * $b[0][0]),$a[0][0] * $b[0][1] - $a[0][1] * $b[0][0]));}else{// Cross product of column vectorsreturnarray(// Return the cross product of column vectors as a column vectorarray($a[1][0] * $b[2][0] - $a[2][0] * $b[1][0]),array(-1 * ($a[0][0] * $b[2][0] - $a[2][0] * $b[0][0])),array($a[0][0] * $b[1][0] - $a[1][0] * $b[0][0]));}}// Computes the addition of two matrices// $A: an m x n matrix// $B: an m x n matrixfunction addition($A, $B){$M;// The added matrix// Add the two matrices togetherfor($i = 0; $i < sizeof($A); $i++){for($j = 0; $j < sizeof($A[0]); $j++){$M[$i][$j] = $A[$i][$j] + $B[$i][$j];}}return $M;// Return the added matrix}// Computes a random matrix// $m: the row dimension// $n: the column dimension// $lower: the lower bound of random numbers// $upper: the upper bound of random numbersfunction randMatrix($m, $n, $lower, $upper){$M;// A random matrix// Generate the random matrixfor($i = 0; $i < $m; $i++){for($j = 0; $j < $n; $j++){$M[$i][$j] = rand($lower, $upper);}}return $M;// Return the random matrix}// Computes the magnitude of a vector// $a: a row or column vectorfunction magnitude($a){$result = 0;// The magnitude of the vector// Use the Pythagorean theorem to determine the vector's magnitudefor($i = 0; $i < sizeof($a); $i++){for($j = 0; $j < sizeof($a[0]); $j++){$result += pow($a[$i][$j], 2);}}return sqrt($result);// Return the magnitude of the vector}// Computes the adjoint of a matrix// $A: a square matrixfunction adjoint($A){return scale(determinant($A), inverse($A));// Returns the adjoint of a matrix}// Computes a scaled matrix// $k: a scalar number// $A: an m x n matrixfunction scale($k, $A){// Scale the matrix by multiplying each entry by kfor($i = 0; $i < sizeof($A); $i++){for($j = 0; $j < sizeof($A[0]); $j++){$A[$i][$j] *= $k;}}return $A;// Returns the scaled matrix}// Computes the determinant of a matrix// $A: a square matrixfunction determinant($A){$determinant = 1;// The determinant of the matrix$n = sizeof($A);// Dimension of the square matrix$E = identity($n);// An elementary matrix// Find the determinant of a matrix by performing elementary matrix operations// on an augmented matrix through the use of elementary matrices in order// to convert the matrix into upper triangular form// Multiply the diagonals of the upper triangular matrix to arrive at the// determinant, only elementary operations I and III are allowed on the matrix.// If operation I is performed at any time, multiply the determinant by a -1.for($j = 0; $j < $n; $j++){for($i = $j; $i < $n; $i++){if($i == $j){if($A[$i][$j] == 0){for($k = $j + 1; $k < $n; $k++){if($A[$k][$j] != 0){$swap = $A[$i];$A[$i] = $A[$k];$A[$k] = $swap;$determinant *= -1;$k = $n;}}}$determinant *= $A[$i][$j];}else{$E[$i][$j] = -$A[$i][$j] / $A[$j][$j];}$A = multiply($E, $A);$E = identity($n);}}return $determinant;// Return the determinant of the matrix}// Computes the inverse of a matrix// $A: a square matrixfunction inverse($A){$n = sizeof($A);// Dimension of the square matrix$V = identity($n);// The inverse of the matrix$E = identity($n);// An elementary matrix// Find the inverse matrix by performing elementary matrix operations// on an augmented matrix through the use of elementary matricesfor($j = 0; $j < $n; $j++){for($i = $j; $i < $n; $i++){if($i == $j){if($A[$i][$j] == 0){for($k = $j + 1; $k < $n; $k++){if($A[$k][$j] != 0){$swap = $A[$i];$A[$i] = $A[$k];$A[$k] = $swap;$swap = $V[$i];$V[$i] = $V[$k];$V[$k] = $swap;$k = $n;}}}$E[$i][$j] = 1 / $A[$i][$j];}else{$E[$i][$j] = -$A[$i][$j];}$A = multiply($E, $A);$V = multiply($E, $V);$E = identity($n);}}for($j = $n - 1; $j > 0; $j--){for($i = $j - 1; $i > -1; $i--){$E[$i][$j] = -$A[$i][$j];$A = multiply($E, $A);$V = multiply($E, $V);$E = identity($n);}}return $V;// Return the inverse matrix}// Computes the product of a matrix multiplication// $A: An m x n matrix// $B: An n x o matrixfunction multiply($A, $B){$M;// The product of the multiplication$B = transpose($B);// Transpose the second matrix// Do the multiplication by making use of the dot productfor($i = 0; $i < sizeof($A); $i++){for($j = 0; $j < sizeof($B); $j++){$M[$i][$j] = dotProduct(array($A[$i]), array($B[$j]));}}return $M;// Return the product}// Computes the dot product of two vectors// $a: a row or column vector// $b; a row or column vectorfunction dotProduct($a, $b){$result = 0;// The result of the dot product// The dot productfor($i = 0; $i < sizeof($a); $i++){for($j = 0; $j < sizeof($a[0]); $j++){$result += $a[$i][$j] * $b[$i][$j];}}return $result;// Return the dot product}// Computes the transpose of a matrix// $A: the matrix to be transposedfunction transpose($A){$T;// Transpose matrix// Switch the i,jth entry to the j,ith entryfor($i = 0; $i < sizeof($A); $i++){for($j = 0; $j < sizeof($A[0]); $j++){$T[$j][$i] = $A[$i][$j];}}return $T;// Return the transpose matrix}// Computes an identity matrix of size n x n// $n: size of the matrix wantedfunction identity($n){$I;// Identity matrix// Fill the matrices diagonal with 1's and everything else with 0'sfor($i = 0; $i < $n; $i++){for($j = 0; $j < $n; $j++){if($i == $j){$I[$i][$j] = 1;}else{$I[$i][$j] = 0;}}}return $I;// Return the identity matrix}?>If someone can tell me why the code isn't working I would really appreciate it, because honestly I'm at a loss. If you need explanations about the actual code please just ask and I'll gladly answer. Thx for any help.   
  Figured I'd put up a picture of the output and that also highlights the problem.;  Change this on line 104  $divisor = sqrt(pow($vrml_2[$i][0], 2) + pow($vrml_2[$i][1], 2));  if ($divisor <   ;  I don't understand exactly what it is I'm editing Anonymous. Should the code on line 104 look like this?$theta_2 = sqrt(pow($vrml_2[$i][0], 2) + pow($vrml_2[$i][1], 2));if($vrml_2[$i][0] >= 0 && $vrml_2[$i][1] >= 0){That $divisor variable you threw in there in the if statement and the code I'm not sure what I'm supposed to do with it.   ;  I think the < sign ate part of the AP's reply. @AP: Try entering greater-than and lesser-than as &lt; and &gt; instead of < or > so the forum post parser doesn't eat your post. Or put a [code][/code] block around it.   ;  SUCCESS! First things first, I have to give credit where credit is due. Much thanks to MarkR from here at Gamedev or as he's known on IRC MarkR42 on irc.freenode.net/#php . He brilliantly pointed out that the problem with my code was that I was using the vector normal to the camera's plane when in fact I should've been using a vector which extended from the camera to a point on the plane. Then by using this new vector and the plane's normal the correct angle between the camera's viewpoint and the object can be computed and produces the proper output. Thanks again Mark.Here's the code if anyone is interested:<?php$filename = 'sphere.wrl';$vrml_str = file_get_contents($filename);// Convert the file into a string$vrml_tok = str_split($vrml_str);// Split the string into an array of each character// Traverse the array and eliminate anything that isn't of numeric relevance// Also retain brackets to allow dissection of quantitiesfor($i = 0; $i < sizeof($vrml_tok); $i++){switch($vrml_tok[$i]){case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9':case '-':case '.':case ' ':case '{':case '}':case '[':case ']':break;default:$vrml_tok[$i] = "";break;}}$vrml_tok = implode($vrml_tok);// Join the array into a string$vrml_tok = explode(' 01 ', $vrml_tok);// Split the string into arraysarray_shift($vrml_tok);// Destroy the first entry in the array// Extract the relevant pieces of info from the strings in each array// $vrml_1: Object's center location// $vrml_2: Object's rotational orientation// $vrml_3: Object's vertices// $vrml_4: Object's polygonsfor($i = 0; $i < sizeof($vrml_tok); $i++){$vrml_tok[$i] = substr($vrml_tok[$i], strpos($vrml_tok[$i], "{") + 2);$vrml_1[$i] = substr($vrml_tok[$i], 0, strpos($vrml_tok[$i], "[") - 1);$vrml_tok[$i] = substr($vrml_tok[$i], strpos($vrml_tok[$i], "[") + 1);$vrml_tok[$i] = substr($vrml_tok[$i], strpos($vrml_tok[$i], "[") + 1);$vrml_3[$i] = substr($vrml_tok[$i], 0, strpos($vrml_tok[$i], "]"));$vrml_tok[$i] = substr($vrml_tok[$i], strpos($vrml_tok[$i], "[") + 1);$vrml_4[$i] = substr($vrml_tok[$i], 0, strpos($vrml_tok[$i], "]"));$vrml_tok[$i] = substr($vrml_tok[$i], strpos($vrml_tok[$i], "]") + 1);$vrml_1[$i] = explode(" ", $vrml_1[$i]);$vrml_2[$i] = array_slice($vrml_1[$i], 3);$vrml_1[$i] = array_slice($vrml_1[$i], 0, 3);$vrml_3[$i] = explode(" ", $vrml_3[$i]);$vrml_4[$i] = explode(" ", $vrml_4[$i]);$temp = array_slice($vrml_4[$i], 0, 3);for($j = 4; $j < sizeof($vrml_4[$i]); $j += 4){$temp = array_merge($temp, array_slice($vrml_4[$i], $j, 3));}$vrml_4[$i] = $temp;}// Swap the values of x z y to become x y zfor($i = 0; $i < sizeof($vrml_1); $i++){$temp = $vrml_1[$i][1];$vrml_1[$i][1] = $vrml_1[$i][2];$vrml_1[$i][2] = $temp;}for($i = 0; $i < sizeof($vrml_2); $i++){$temp = $vrml_2[$i][1];$vrml_2[$i][1] = $vrml_2[$i][2];$vrml_2[$i][2] = $temp;}for($i = 0; $i < sizeof($vrml_3); $i++){for($j = 0; $j < sizeof($vrml_3[0]); $j += 3){$temp = $vrml_3[$i][$j + 1];$vrml_3[$i][$j + 1] = $vrml_3[$i][$j + 2];$vrml_3[$i][$j + 2] = $temp;}}// Compute the orientation of the object so as to find a rotational transform matrixfor($i = 0; $i < sizeof($vrml_2); $i++){$theta_1 = asin(abs($vrml_2[$i][2]));if($vrml_2[$i][1] >= 0 && $vrml_2[$i][2] >= 0){$theta_1 = -(pi() / 2 - $theta_1);}else{if($vrml_2[$i][1] < 0 && $vrml_2[$i][2] >= 0){$theta_1 = pi() / 2 - $theta_1;}else{if($vrml_2[$i][1] < 0 && $vrml_2[$i][2] < 0){$theta_1 = pi() / 2 + $theta_1;}else{if($vrml_2[$i][1] >= 0 && $vrml_2[$i][2] < 0){$theta_1 = -pi() / 2 - $theta_1;}}}}$theta_2 = acos(abs($vrml_2[$i][0]) / sqrt(pow($vrml_2[$i][0], 2) + pow($vrml_2[$i][1], 2)));if($vrml_2[$i][0] >= 0 && $vrml_2[$i][1] >= 0){$theta_2 = -(pi() / 2 - $theta_2);}else{if($vrml_2[$i][0] < 0 && $vrml_2[$i][1] >= 0){$theta_2 = pi() / 2 - $theta_2;}else{if($vrml_2[$i][0] < 0 && $vrml_2[$i][1] < 0){$theta_2 = -(pi() / 2 - $theta_2);}else{if($vrml_2[$i][0] >= 0 && $vrml_2[$i][1] < 0){$theta_2 = pi() / 2 - $theta_2;}}}}$ini_orient[$i] = identity(3);// Rotate the object about the world's X axis according to the computed $theta_1$rotX =array(array(1, 0, 0),array(0, cos($theta_1), -sin($theta_1)),array(0, sin($theta_1), cos($theta_1)));$ini_orient[$i] = multiply($rotX, $ini_orient[$i]);// Rotate the object about the world's Z axis according to the computed $theta_2$rotZ = array(array(cos($theta_2), -sin($theta_2), 0),array(sin($theta_2), cos($theta_2), 0),array(0, 0, 1));$ini_orient[$i] = multiply($rotZ, $ini_orient[$i]);// Rotate the object about its own Z axis using the value given$rotZ = array(array(cos($vrml_2[$i][3]), -sin($vrml_2[$i][3]), 0),array(sin($vrml_2[$i][3]), cos($vrml_2[$i][3]), 0),array(0, 0, 1));$ini_orient[$i] = multiply($ini_orient[$i], $rotZ);}// Create all the objects find in the world and store them in an array// Initialize these as wellfor($i = 0; $i < sizeof($vrml_1); $i++){$worldContainer[$i] = new VirtualObject();$temp = array(array());$temp[0][0] = $vrml_1[$i][0];$temp[1][0] = $vrml_1[$i][1];$temp[2][0] = $vrml_1[$i][2];$worldContainer[$i]->setCenter($temp);$worldContainer[$i]->setOrientation($ini_orient[$i]);for($j = 0; $j < sizeof($vrml_3[0]); $j += 3){$temp[0][0] = $vrml_3[$i][$j];$temp[1][0] = $vrml_3[$i][$j + 1];$temp[2][0] = $vrml_3[$i][$j + 2];$worldContainer[$i]->setVertex($temp);}$worldContainer[$i]->setPolygons($vrml_4[$i]);}// The class that stores virtual objectsclass VirtualObject{// Member declarationsprivate $center;// Object centerprivate $orientation;// Object orientationprivate $vertices;// Object verticesprivate $polyOrder;// Object polygons// Constructorfunction __construct(){}// Method declarationspublic function setCenter($n){$this->center = $n;}public function getCenter(){return $this->center;}public function setOrientation($n){$this->orientation = $n;}public function getOrientation(){return $this->orientation;}public function setVertex($n){$this->vertices[] = $n;}public function getVertex($n){return $this->vertices[$n];}public function getVertexCount(){return sizeof($this->vertices);}public function setPolygons($n){$this->polyOrder = $n;}public function getPolygon($n){$i = 3 * $n;returnarray($this->getVertex($this->polyOrder[$i]),$this->getVertex($this->polyOrder[$i + 1]),$this->getVertex($this->polyOrder[$i + 2]));}public function getPolyCount(){return sizeof($this->polyOrder) / 3;}}// Viewable screen dimensions$width = 640;$height = 480;// Sets up the camera's orientation and its focal distance$x = sqrt(2) / 2;$focal = 300;$a =array(array(-$x, 0.5, -0.5),array($x, 0.5, -0.5),array(0, -$x, -$x));// Invert camera's plane to allow for change of basis$a = inverse($a);// Create two images$image1 = imagecreate($width, $height);$image2 = imagecreate($width, $height);// Set the line thickness of each imageimagesetthickness($image1, 1);imagesetthickness($image2, 1);// Set the background colors for each image$blue1 = imagecolorallocate($image1, 0, 0, 127);$blue2 = imagecolorallocate($image2, 0, 0, 127);// Define some colors for each image$white = imagecolorallocate($image1, 255, 255, 255);$gray = imagecolorallocate($image2, 127, 127, 127);// Draw each polygonfor($i = 0; $i < $worldContainer[0]->getPolyCount(); $i++){// Retrieve the polygons from the cube object$points = $worldContainer[0]->getPolygon($i);// Rotate each vertex vector according to the indicated object's orientation$points[0] = multiply($worldContainer[0]->getOrientation(), $points[0]);$points[1] = multiply($worldContainer[0]->getOrientation(), $points[1]);$points[2] = multiply($worldContainer[0]->getOrientation(), $points[2]);// Modify each point of the polygon so that its now distanced to the camera's center pointfor($j = 0; $j < 3; $j++){$points[$j][0][0] -= 150;$points[$j][1][0] -= 150;$points[$j][2][0] -= 300 * $x;}// Write each vector in terms of the camera's orientation (change of basis)$points[0] = multiply($a, $points[0]);$points[1] = multiply($a, $points[1]);$points[2] = multiply($a, $points[2]);// Determine the normal vector and viewing vector for each polygon, respectively$n1 = crossProduct(addition($points[0], scale(-1, $points[1])), addition($points[2], scale(-1, $points[1])));$n2 = $points[1];// Determine the distortion due to perspective$focus[0] = $focal / magnitude($points[0]);$focus[1] = $focal / magnitude($points[1]);$focus[2] = $focal / magnitude($points[2]);// Transform the 3d points into 2d coordinates for drawing$x0 = $width / 2 + ($points[0][0][0] * $focus[0]);$x1 = $width / 2 + ($points[1][0][0] * $focus[1]);$x2 = $width / 2 + ($points[2][0][0] * $focus[2]);$y0 = $height / 2 + ($points[0][1][0] * $focus[0]);$y1 = $height / 2 + ($points[1][1][0] * $focus[1]);$y2 = $height / 2 + ($points[2][1][0] * $focus[2]);// Determine the cosine of the angle between the viewing plane and the polygonal plane$cosTheta = dotProduct($n1, $n2) / (magnitude($n1) * magnitude($n2));if($cosTheta <= 0){// White lines because these are the planes at the front of the objectimageline($image1, $x0, $y0, $x1, $y1, $white);imageline($image1, $x1, $y1, $x2, $y2, $white);imageline($image1, $x2, $y2, $x0, $y0, $white);}else{// Gray lines because these are the planes at the reare of the objectimageline($image2, $x0, $y0, $x1, $y1, $gray);imageline($image2, $x1, $y1, $x2, $y2, $gray);imageline($image2, $x2, $y2, $x0, $y0, $gray);}}// Make transparent the background of the viewable polygon drawingimagecolortransparent($image1, $blue1);// Layer it over the non-viewable polygon drawingimagecopymerge($image2, $image1, 0, 0, 0, 0, 640, 480, 100);// Flip the image horizontally// Output the finished imageheader("Content-type: image/png");imagepng($image2);// Destroy the imagesimagedestroy($image1);imagedestroy($image2);// Computes the cross product of two vectors// $a: a row or column vector// $b: a row or column vectorfunction crossProduct($a, $b){if(sizeof($a) == 1){// Cross product of row vectorsreturnarray(// Return the cross product of row vectors as a row vectorarray($a[0][1] * $b[0][2] - $a[0][2] * $b[0][1],-1 * ($a[0][0] * $b[0][2] - $a[0][2] * $b[0][0]),$a[0][0] * $b[0][1] - $a[0][1] * $b[0][0]));}else{// Cross product of column vectorsreturnarray(// Return the cross product of column vectors as a column vectorarray($a[1][0] * $b[2][0] - $a[2][0] * $b[1][0]),array(-1 * ($a[0][0] * $b[2][0] - $a[2][0] * $b[0][0])),array($a[0][0] * $b[1][0] - $a[1][0] * $b[0][0]));}}// Computes the addition of two matrices// $A: an m x n matrix// $B: an m x n matrixfunction addition($A, $B){$M;// The added matrix// Add the two matrices togetherfor($i = 0; $i < sizeof($A); $i++){for($j = 0; $j < sizeof($A[0]); $j++){$M[$i][$j] = $A[$i][$j] + $B[$i][$j];}}return $M;// Return the added matrix}// Computes a random matrix// $m: the row dimension// $n: the column dimension// $lower: the lower bound of random numbers// $upper: the upper bound of random numbersfunction randMatrix($m, $n, $lower, $upper){$M;// A random matrix// Generate the random matrixfor($i = 0; $i < $m; $i++){for($j = 0; $j < $n; $j++){$M[$i][$j] = rand($lower, $upper);}}return $M;// Return the random matrix}// Computes the magnitude of a vector// $a: a row or column vectorfunction magnitude($a){$result = 0;// The magnitude of the vector// Use the Pythagorean theorem to determine the vector's magnitudefor($i = 0; $i < sizeof($a); $i++){for($j = 0; $j < sizeof($a[0]); $j++){$result += pow($a[$i][$j], 2);}}return sqrt($result);// Return the magnitude of the vector}// Computes the adjoint of a matrix// $A: a square matrixfunction adjoint($A){return scale(determinant($A), inverse($A));// Returns the adjoint of a matrix}// Computes a scaled matrix// $k: a scalar number// $A: an m x n matrixfunction scale($k, $A){// Scale the matrix by multiplying each entry by kfor($i = 0; $i < sizeof($A); $i++){for($j = 0; $j < sizeof($A[0]); $j++){$A[$i][$j] *= $k;}}return $A;// Returns the scaled matrix}// Computes the determinant of a matrix// $A: a square matrixfunction determinant($A){$determinant = 1;// The determinant of the matrix$n = sizeof($A);// Dimension of the square matrix$E = identity($n);// An elementary matrix// Find the determinant of a matrix by performing elementary matrix operations// on an augmented matrix through the use of elementary matrices in order// to convert the matrix into upper triangular form// Multiply the diagonals of the upper triangular matrix to arrive at the// determinant, only elementary operations I and III are allowed on the matrix.// If operation I is performed at any time, multiply the determinant by a -1.for($j = 0; $j < $n; $j++){for($i = $j; $i < $n; $i++){if($i == $j){if($A[$i][$j] == 0){for($k = $j + 1; $k < $n; $k++){if($A[$k][$j] != 0){$swap = $A[$i];$A[$i] = $A[$k];$A[$k] = $swap;$determinant *= -1;$k = $n;}}}$determinant *= $A[$i][$j];}else{$E[$i][$j] = -$A[$i][$j] / $A[$j][$j];}$A = multiply($E, $A);$E = identity($n);}}return $determinant;// Return the determinant of the matrix}// Computes the inverse of a matrix// $A: a square matrixfunction inverse($A){$n = sizeof($A);// Dimension of the square matrix$V = identity($n);// The inverse of the matrix$E = identity($n);// An elementary matrix// Find the inverse matrix by performing elementary matrix operations// on an augmented matrix through the use of elementary matricesfor($j = 0; $j < $n; $j++){for($i = $j; $i < $n; $i++){if($i == $j){if($A[$i][$j] == 0){for($k = $j + 1; $k < $n; $k++){if($A[$k][$j] != 0){$swap = $A[$i];$A[$i] = $A[$k];$A[$k] = $swap;$swap = $V[$i];$V[$i] = $V[$k];$V[$k] = $swap;$k = $n;}}}$E[$i][$j] = 1 / $A[$i][$j];}else{$E[$i][$j] = -$A[$i][$j];}$A = multiply($E, $A);$V = multiply($E, $V);$E = identity($n);}}for($j = $n - 1; $j > 0; $j--){for($i = $j - 1; $i > -1; $i--){$E[$i][$j] = -$A[$i][$j];$A = multiply($E, $A);$V = multiply($E, $V);$E = identity($n);}}return $V;// Return the inverse matrix}// Computes the product of a matrix multiplication// $A: An m x n matrix// $B: An n x o matrixfunction multiply($A, $B){$M;// The product of the multiplication$B = transpose($B);// Transpose the second matrix// Do the multiplication by making use of the dot productfor($i = 0; $i < sizeof($A); $i++){for($j = 0; $j < sizeof($B); $j++){$M[$i][$j] = dotProduct(array($A[$i]), array($B[$j]));}}return $M;// Return the product}// Computes the dot product of two vectors// $a: a row or column vector// $b; a row or column vectorfunction dotProduct($a, $b){$result = 0;// The result of the dot product// The dot productfor($i = 0; $i < sizeof($a); $i++){for($j = 0; $j < sizeof($a[0]); $j++){$result += $a[$i][$j] * $b[$i][$j];}}return $result;// Return the dot product}// Computes the transpose of a matrix// $A: the matrix to be transposedfunction transpose($A){$T;// Transpose matrix// Switch the i,jth entry to the j,ith entryfor($i = 0; $i < sizeof($A); $i++){for($j = 0; $j < sizeof($A[0]); $j++){$T[$j][$i] = $A[$i][$j];}}return $T;// Return the transpose matrix}// Computes an identity matrix of size n x n// $n: size of the matrix wantedfunction identity($n){$I;// Identity matrix// Fill the matrices diagonal with 1's and everything else with 0'sfor($i = 0; $i < $n; $i++){for($j = 0; $j < $n; $j++){if($i == $j){$I[$i][$j] = 1;}else{$I[$i][$j] = 0;}}}return $I;// Return the identity matrix}?>;  Well thanks to Mark's challenge I was motivated to figure out how to program a cel-shading technique. That is to render the outline of an object in a thickness greater than the wireframe. Or to draw thick lines where back faces and front faces meet. I managed to do it, but it's grotesquely inefficient I'm sure. Anyhow, here's the code and a sample of the output.<?php$filename = 'sphere.wrl';$vrml_str = file_get_contents($filename);// Convert the file into a string$vrml_tok = str_split($vrml_str);// Split the string into an array of each character// Traverse the array and eliminate anything that isn't of numeric relevance// Also retain brackets to allow dissection of quantitiesfor($i = 0; $i < sizeof($vrml_tok); $i++){switch($vrml_tok[$i]){case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9':case '-':case '.':case ' ':case '{':case '}':case '[':case ']':break;default:$vrml_tok[$i] = "";break;}}$vrml_tok = implode($vrml_tok);// Join the array into a string$vrml_tok = explode(' 01 ', $vrml_tok);// Split the string into arraysarray_shift($vrml_tok);// Destroy the first entry in the array// Extract the relevant pieces of info from the strings in each array// $vrml_1: Object's center location// $vrml_2: Object's rotational orientation// $vrml_3: Object's vertices// $vrml_4: Object's polygonsfor($i = 0; $i < sizeof($vrml_tok); $i++){$vrml_tok[$i] = substr($vrml_tok[$i], strpos($vrml_tok[$i], "{") + 2);$vrml_1[$i] = substr($vrml_tok[$i], 0, strpos($vrml_tok[$i], "[") - 1);$vrml_tok[$i] = substr($vrml_tok[$i], strpos($vrml_tok[$i], "[") + 1);$vrml_tok[$i] = substr($vrml_tok[$i], strpos($vrml_tok[$i], "[") + 1);$vrml_3[$i] = substr($vrml_tok[$i], 0, strpos($vrml_tok[$i], "]"));$vrml_tok[$i] = substr($vrml_tok[$i], strpos($vrml_tok[$i], "[") + 1);$vrml_4[$i] = substr($vrml_tok[$i], 0, strpos($vrml_tok[$i], "]"));$vrml_tok[$i] = substr($vrml_tok[$i], strpos($vrml_tok[$i], "]") + 1);$vrml_1[$i] = explode(" ", $vrml_1[$i]);$vrml_2[$i] = array_slice($vrml_1[$i], 3);$vrml_1[$i] = array_slice($vrml_1[$i], 0, 3);$vrml_3[$i] = explode(" ", $vrml_3[$i]);$vrml_4[$i] = explode(" ", $vrml_4[$i]);$temp = array_slice($vrml_4[$i], 0, 3);for($j = 4; $j < sizeof($vrml_4[$i]); $j += 4){$temp = array_merge($temp, array_slice($vrml_4[$i], $j, 3));}$vrml_4[$i] = $temp;}// Swap the values of x z y to become x y zfor($i = 0; $i < sizeof($vrml_1); $i++){$temp = $vrml_1[$i][1];$vrml_1[$i][1] = $vrml_1[$i][2];$vrml_1[$i][2] = $temp;}for($i = 0; $i < sizeof($vrml_2); $i++){$temp = $vrml_2[$i][1];$vrml_2[$i][1] = $vrml_2[$i][2];$vrml_2[$i][2] = $temp;}for($i = 0; $i < sizeof($vrml_3); $i++){for($j = 0; $j < sizeof($vrml_3[0]); $j += 3){$temp = $vrml_3[$i][$j + 1];$vrml_3[$i][$j + 1] = $vrml_3[$i][$j + 2];$vrml_3[$i][$j + 2] = $temp;}}// Compute the orientation of the object so as to find a rotational transform matrixfor($i = 0; $i < sizeof($vrml_2); $i++){$theta_1 = asin(abs($vrml_2[$i][2]));if($vrml_2[$i][1] >= 0 && $vrml_2[$i][2] >= 0){$theta_1 = -(pi() / 2 - $theta_1);}else{if($vrml_2[$i][1] < 0 && $vrml_2[$i][2] >= 0){$theta_1 = pi() / 2 - $theta_1;}else{if($vrml_2[$i][1] < 0 && $vrml_2[$i][2] < 0){$theta_1 = pi() / 2 + $theta_1;}else{if($vrml_2[$i][1] >= 0 && $vrml_2[$i][2] < 0){$theta_1 = -pi() / 2 - $theta_1;}}}}$theta_2 = acos(abs($vrml_2[$i][0]) / sqrt(pow($vrml_2[$i][0], 2) + pow($vrml_2[$i][1], 2)));if($vrml_2[$i][0] >= 0 && $vrml_2[$i][1] >= 0){$theta_2 = -(pi() / 2 - $theta_2);}else{if($vrml_2[$i][0] < 0 && $vrml_2[$i][1] >= 0){$theta_2 = pi() / 2 - $theta_2;}else{if($vrml_2[$i][0] < 0 && $vrml_2[$i][1] < 0){$theta_2 = -(pi() / 2 - $theta_2);}else{if($vrml_2[$i][0] >= 0 && $vrml_2[$i][1] < 0){$theta_2 = pi() / 2 - $theta_2;}}}}$ini_orient[$i] = identity(3);// Rotate the object about the world's X axis according to the computed $theta_1$rotX =array(array(1, 0, 0),array(0, cos($theta_1), -sin($theta_1)),array(0, sin($theta_1), cos($theta_1)));$ini_orient[$i] = multiply($rotX, $ini_orient[$i]);// Rotate the object about the world's Z axis according to the computed $theta_2$rotZ = array(array(cos($theta_2), -sin($theta_2), 0),array(sin($theta_2), cos($theta_2), 0),array(0, 0, 1));$ini_orient[$i] = multiply($rotZ, $ini_orient[$i]);// Rotate the object about its own Z axis using the value given$rotZ = array(array(cos($vrml_2[$i][3]), -sin($vrml_2[$i][3]), 0),array(sin($vrml_2[$i][3]), cos($vrml_2[$i][3]), 0),array(0, 0, 1));$ini_orient[$i] = multiply($ini_orient[$i], $rotZ);}// Create all the objects find in the world and store them in an array// Initialize these as wellfor($i = 0; $i < sizeof($vrml_1); $i++){$worldContainer[$i] = new VirtualObject();$temp = array(array());$temp[0][0] = $vrml_1[$i][0];$temp[1][0] = $vrml_1[$i][1];$temp[2][0] = $vrml_1[$i][2];$worldContainer[$i]->setCenter($temp);$worldContainer[$i]->setOrientation($ini_orient[$i]);for($j = 0; $j < sizeof($vrml_3[0]); $j += 3){$temp[0][0] = $vrml_3[$i][$j];$temp[1][0] = $vrml_3[$i][$j + 1];$temp[2][0] = $vrml_3[$i][$j + 2];$worldContainer[$i]->setVertex($temp);}$worldContainer[$i]->setPolygons($vrml_4[$i]);}// The class that stores virtual objectsclass VirtualObject{// Member declarationsprivate $center;// Object centerprivate $orientation;// Object orientationprivate $vertices;// Object verticesprivate $polyOrder;// Object polygons// Constructorfunction __construct(){}// Method declarationspublic function setCenter($n){$this->center = $n;}public function getCenter(){return $this->center;}public function setOrientation($n){$this->orientation = $n;}public function getOrientation(){return $this->orientation;}public function setVertex($n){$this->vertices[] = $n;}public function getVertex($n){return $this->vertices[$n];}public function getVertexCount(){return sizeof($this->vertices);}public function setPolygons($n){$this->polyOrder = $n;}public function getPolygon($n){$i = 3 * $n;returnarray($this->getVertex($this->polyOrder[$i]),$this->getVertex($this->polyOrder[$i + 1]),$this->getVertex($this->polyOrder[$i + 2]));}public function getPolyCount(){return sizeof($this->polyOrder) / 3;}}// Viewable screen dimensions$width = 640;$height = 480;// Sets up the camera's orientation and its focal distance$x = sqrt(2) / 2;$focal = 300;$a =array(array(-$x, 0.5, -0.5),array($x, 0.5, -0.5),array(0, -$x, -$x));// Invert camera's plane to allow for change of basis$a = inverse($a);// Create two images$image1 = imagecreate($width, $height);$image2 = imagecreate($width, $height);// Set the line thickness of each imageimagesetthickness($image1, 1);imagesetthickness($image2, 1);// Set the background colors for each image$blue1 = imagecolorallocate($image1, 0, 0, 127);$blue2 = imagecolorallocate($image2, 0, 0, 127);// Define some colors for each image$white = imagecolorallocate($image1, 255, 255, 255);$black = imagecolorallocate($image1, 0, 0, 0);$gray = imagecolorallocate($image2, 127, 127, 127);// Draw each polygonfor($i = 0; $i < $worldContainer[0]->getPolyCount(); $i++){// Retrieve the polygons from the cube object$points = $worldContainer[0]->getPolygon($i);// Rotate each vertex vector according to the indicated object's orientation$points[0] = multiply($worldContainer[0]->getOrientation(), $points[0]);$points[1] = multiply($worldContainer[0]->getOrientation(), $points[1]);$points[2] = multiply($worldContainer[0]->getOrientation(), $points[2]);// Modify each point of the polygon so that its now distanced to the camera's center pointfor($j = 0; $j < 3; $j++){$points[$j][0][0] -= 150;$points[$j][1][0] -= 150;$points[$j][2][0] -= 300 * $x;}// Write each vector in terms of the camera's orientation (change of basis)$points[0] = multiply($a, $points[0]);$points[1] = multiply($a, $points[1]);$points[2] = multiply($a, $points[2]);// Determine the normal vector and viewing vector for each polygon, respectively$n1 = crossProduct(addition($points[0], scale(-1, $points[1])), addition($points[2], scale(-1, $points[1])));$n2 = $points[1];// Determine the distortion due to perspective$focus[0] = $focal / magnitude($points[0]);$focus[1] = $focal / magnitude($points[1]);$focus[2] = $focal / magnitude($points[2]);// Transform the 3d points into 2d coordinates for drawing$x0 = $width / 2 + ($points[0][0][0] * $focus[0]);$x1 = $width / 2 + ($points[1][0][0] * $focus[1]);$x2 = $width / 2 + ($points[2][0][0] * $focus[2]);$y0 = $height / 2 + ($points[0][1][0] * $focus[0]);$y1 = $height / 2 + ($points[1][1][0] * $focus[1]);$y2 = $height / 2 + ($points[2][1][0] * $focus[2]);// Determine the cosine of the angle between the viewing plane and the polygonal plane$cosTheta = dotProduct($n1, $n2) / (magnitude($n1) * magnitude($n2));if($cosTheta <= 0){// White lines because these are the planes at the front of the objectimageline($image1, $x0, $y0, $x1, $y1, $white);imageline($image1, $x1, $y1, $x2, $y2, $white);imageline($image1, $x2, $y2, $x0, $y0, $white);// Copy all lines on the front faceif($x0 < $x1){$border[] = $x0;$border[] = $y0;$border[] = $x1;$border[] = $y1;}else{$border[] = $x1;$border[] = $y1;$border[] = $x0;$border[] = $y0;}if($x1 < $x2){$border[] = $x1;$border[] = $y1;$border[] = $x2;$border[] = $y2;}else{$border[] = $x2;$border[] = $y2;$border[] = $x1;$border[] = $y1;}if($x2 < $x0){$border[] = $x2;$border[] = $y2;$border[] = $x0;$border[] = $y0;}else{$border[] = $x0;$border[] = $y0;$border[] = $x2;$border[] = $y2;}}else{// Gray lines because these are the planes at the rear of the objectimageline($image2, $x0, $y0, $x1, $y1, $gray);imageline($image2, $x1, $y1, $x2, $y2, $gray);imageline($image2, $x2, $y2, $x0, $y0, $gray);}}// Determine the outline of the figure by selecting only lines that are drawn once on the front face$silhouette = array();$running = true;do{// Extract individual linesfor($i = 0; $i < 4; $i++){$temp[$i] = $border[0];array_shift($border);}// Convert both the line and group of lines to a string$temp = implode(" ", $temp);$border = implode(" ", $border);// Check to see if the line in question is only drawn onceif(substr_count($border, $temp) == 0){// Copy the line into the silhouette$temp = explode(" ", $temp);for($i = 0; $i < 4; $i++){$silhouette[] = $temp[$i];}}else{// Otherwise remove all occurances of this line since it is not part of the silhouettedo{$location = strpos($border, $temp);$border = substr_replace($border, "", $location, strlen($temp) + 1);}while(substr_count($border, $temp) > 0);$temp = explode(" ", $temp);}// Do this until all lines have been examinedif(strlen($border) > 0){$border = explode(" ", $border);}else{$running = false;}}while($running);// Set the thickness to a greater valueimagesetthickness($image1, 4);// Draw the silhouette of the objectfor($i = 0; $i < sizeof($silhouette); $i += 4){imageline($image1, $silhouette[$i], $silhouette[$i + 1], $silhouette[$i + 2], $silhouette[$i + 3], $black);}// Make transparent the background of the viewable polygon drawingimagecolortransparent($image1, $blue1);// Layer it over the non-viewable polygon drawingimagecopymerge($image2, $image1, 0, 0, 0, 0, $width, $height, 100);// Output the finished imageheader("Content-type: image/png");imagepng($image2);// Destroy the imagesimagedestroy($image1);imagedestroy($image2);// Computes the cross product of two vectors// $a: a row or column vector// $b: a row or column vectorfunction crossProduct($a, $b){if(sizeof($a) == 1){// Cross product of row vectorsreturnarray(// Return the cross product of row vectors as a row vectorarray($a[0][1] * $b[0][2] - $a[0][2] * $b[0][1],-1 * ($a[0][0] * $b[0][2] - $a[0][2] * $b[0][0]),$a[0][0] * $b[0][1] - $a[0][1] * $b[0][0]));}else{// Cross product of column vectorsreturnarray(// Return the cross product of column vectors as a column vectorarray($a[1][0] * $b[2][0] - $a[2][0] * $b[1][0]),array(-1 * ($a[0][0] * $b[2][0] - $a[2][0] * $b[0][0])),array($a[0][0] * $b[1][0] - $a[1][0] * $b[0][0]));}}// Computes the addition of two matrices// $A: an m x n matrix// $B: an m x n matrixfunction addition($A, $B){$M;// The added matrix// Add the two matrices togetherfor($i = 0; $i < sizeof($A); $i++){for($j = 0; $j < sizeof($A[0]); $j++){$M[$i][$j] = $A[$i][$j] + $B[$i][$j];}}return $M;// Return the added matrix}// Computes a random matrix// $m: the row dimension// $n: the column dimension// $lower: the lower bound of random numbers// $upper: the upper bound of random numbersfunction randMatrix($m, $n, $lower, $upper){$M;// A random matrix// Generate the random matrixfor($i = 0; $i < $m; $i++){for($j = 0; $j < $n; $j++){$M[$i][$j] = rand($lower, $upper);}}return $M;// Return the random matrix}// Computes the magnitude of a vector// $a: a row or column vectorfunction magnitude($a){$result = 0;// The magnitude of the vector// Use the Pythagorean theorem to determine the vector's magnitudefor($i = 0; $i < sizeof($a); $i++){for($j = 0; $j < sizeof($a[0]); $j++){$result += pow($a[$i][$j], 2);}}return sqrt($result);// Return the magnitude of the vector}// Computes the adjoint of a matrix// $A: a square matrixfunction adjoint($A){return scale(determinant($A), inverse($A));// Returns the adjoint of a matrix}// Computes a scaled matrix// $k: a scalar number// $A: an m x n matrixfunction scale($k, $A){// Scale the matrix by multiplying each entry by kfor($i = 0; $i < sizeof($A); $i++){for($j = 0; $j < sizeof($A[0]); $j++){$A[$i][$j] *= $k;}}return $A;// Returns the scaled matrix}// Computes the determinant of a matrix// $A: a square matrixfunction determinant($A){$determinant = 1;// The determinant of the matrix$n = sizeof($A);// Dimension of the square matrix$E = identity($n);// An elementary matrix// Find the determinant of a matrix by performing elementary matrix operations// on an augmented matrix through the use of elementary matrices in order// to convert the matrix into upper triangular form// Multiply the diagonals of the upper triangular matrix to arrive at the// determinant, only elementary operations I and III are allowed on the matrix.// If operation I is performed at any time, multiply the determinant by a -1.for($j = 0; $j < $n; $j++){for($i = $j; $i < $n; $i++){if($i == $j){if($A[$i][$j] == 0){for($k = $j + 1; $k < $n; $k++){if($A[$k][$j] != 0){$swap = $A[$i];$A[$i] = $A[$k];$A[$k] = $swap;$determinant *= -1;$k = $n;}}}$determinant *= $A[$i][$j];}else{$E[$i][$j] = -$A[$i][$j] / $A[$j][$j];}$A = multiply($E, $A);$E = identity($n);}}return $determinant;// Return the determinant of the matrix}// Computes the inverse of a matrix// $A: a square matrixfunction inverse($A){$n = sizeof($A);// Dimension of the square matrix$V = identity($n);// The inverse of the matrix$E = identity($n);// An elementary matrix// Find the inverse matrix by performing elementary matrix operations// on an augmented matrix through the use of elementary matricesfor($j = 0; $j < $n; $j++){for($i = $j; $i < $n; $i++){if($i == $j){if($A[$i][$j] == 0){for($k = $j + 1; $k < $n; $k++){if($A[$k][$j] != 0){$swap = $A[$i];$A[$i] = $A[$k];$A[$k] = $swap;$swap = $V[$i];$V[$i] = $V[$k];$V[$k] = $swap;$k = $n;}}}$E[$i][$j] = 1 / $A[$i][$j];}else{$E[$i][$j] = -$A[$i][$j];}$A = multiply($E, $A);$V = multiply($E, $V);$E = identity($n);}}for($j = $n - 1; $j > 0; $j--){for($i = $j - 1; $i > -1; $i--){$E[$i][$j] = -$A[$i][$j];$A = multiply($E, $A);$V = multiply($E, $V);$E = identity($n);}}return $V;// Return the inverse matrix}// Computes the product of a matrix multiplication// $A: An m x n matrix// $B: An n x o matrixfunction multiply($A, $B){$M;// The product of the multiplication$B = transpose($B);// Transpose the second matrix// Do the multiplication by making use of the dot productfor($i = 0; $i < sizeof($A); $i++){for($j = 0; $j < sizeof($B); $j++){$M[$i][$j] = dotProduct(array($A[$i]), array($B[$j]));}}return $M;// Return the product}// Computes the dot product of two vectors// $a: a row or column vector// $b; a row or column vectorfunction dotProduct($a, $b){$result = 0;// The result of the dot product// The dot productfor($i = 0; $i < sizeof($a); $i++){for($j = 0; $j < sizeof($a[0]); $j++){$result += $a[$i][$j] * $b[$i][$j];}}return $result;// Return the dot product}// Computes the transpose of a matrix// $A: the matrix to be transposedfunction transpose($A){$T;// Transpose matrix// Switch the i,jth entry to the j,ith entryfor($i = 0; $i < sizeof($A); $i++){for($j = 0; $j < sizeof($A[0]); $j++){$T[$j][$i] = $A[$i][$j];}}return $T;// Return the transpose matrix}// Computes an identity matrix of size n x n// $n: size of the matrix wantedfunction identity($n){$I;// Identity matrix// Fill the matrices diagonal with 1's and everything else with 0'sfor($i = 0; $i < $n; $i++){for($j = 0; $j < $n; $j++){if($i == $j){$I[$i][$j] = 1;}else{$I[$i][$j] = 0;}}}return $I;// Return the identity matrix}?>