<?php
/**
* draw_cube
*
* Draw a 3d cube.
*
* @version 0.8
* @author Contributors at eXorithm
* @link /algorithm/view/draw_cube Listing at eXorithm
* @link /algorithm/history/draw_cube History at eXorithm
* @license /home/show/license
*
* @param number $image_size Size of the resulting image.
* @param number $degree_x Degrees to rotate around the x axis.
* @param number $degree_y Degrees to rotate around the y axis.
* @param number $degree_z Degrees to rotate around the z axis.
* @param number $vdist Distance to vanishing point.
* @param number $dist Distance from origin. This number should not be less than 0 (or else you will be inside the cube!)
* @param string $vertex_color (hex color code) Color of the vertices of the cube.
* @param string $face_color (hex color code) Color of the face of the cube.
* @param bool $wireframe Display as a wireframe (face color will be ignored).
* @param bool $dashes Display "hidden" lines as dashes (only if wireframe is not selected).
* @param bool $rainbow Instead of using the face color, draw each side a different color.
* @return resource GD image
*/
function draw_cube($image_size=400,$degree_x=35,$degree_y=35,$degree_z=0,$vdist=800,$dist=100,$vertex_color='0000ff',$face_color='ff0000',$wireframe=false,$dashes=false,$rainbow=false)
{
$degree_x = $degree_x % 360;
$degree_y = $degree_y % 360;
$degree_z = $degree_z % 360;
// construct the cube polygons
$size = 400; // the size is arbitrary
$x1=$size/2;
$x0=-($size/2);
$y1=$size/2;
$y0=-($size/2);
$z1=$size/2;
$z0=-($size/2);
$sides = array();
$sides[] = array($x0,$y0,$z0, $x0,$y0,$z1, $x0,$y1,$z1, $x0,$y1,$z0);
$sides[] = array($x1,$y0,$z0, $x1,$y0,$z1, $x1,$y1,$z1, $x1,$y1,$z0);
$sides[] = array($x0,$y0,$z0, $x0,$y0,$z1, $x1,$y0,$z1, $x1,$y0,$z0);
$sides[] = array($x0,$y1,$z0, $x0,$y1,$z1, $x1,$y1,$z1, $x1,$y1,$z0);
$sides[] = array($x0,$y0,$z0, $x0,$y1,$z0, $x1,$y1,$z0, $x1,$y0,$z0);
$sides[] = array($x0,$y0,$z1, $x0,$y1,$z1, $x1,$y1,$z1, $x1,$y0,$z1);
// project each of the 6 polygons that makes up the cube
for ($i=0; $i<count($sides); $i++) {
$points[] = project_polygon($sides[$i], $degree_x, $degree_y, $degree_z, 0, 0, 0, $vdist+($size/2), $dist+($size/2), true);
}
// scale the image somewhat
$scale = $image_size/($size*1.8);
if ($rainbow) {
$face_color = array('ff0000', '00d000', 'ffff00', 'a000a0', '0000ff', 'FF8040');
}
return render_polygons($points, $vertex_color, $face_color, $wireframe, $dashes, $image_size, $scale);
}
/**
* project_polygon
*
* Project a the points of a 3d polygon onto a flat 2d surface (the screen).
*
* @version 0.5
* @author Contributors at eXorithm
* @link /algorithm/view/project_polygon Listing at eXorithm
* @link /algorithm/history/project_polygon History at eXorithm
* @license /home/show/license
*
* @param array $points Points of the polygon, in the form x1, y1, z1, x2, y2, z2, etc.
* @param number $degree_x Degrees to rotate around the x axis.
* @param number $degree_y Degrees to rotate around the y axis.
* @param number $degree_z Degrees to rotate around the z axis.
* @param number $center_x Location to center the view on.
* @param number $center_y Location to center the view on.
* @param number $center_z Location to center the view on.
* @param number $dist1 Distance from viewer to screen (distance to vanishing point).
* @param number $dist2 Distance from screen to object.
* @param bool $include_z Return the z (distance) parameter for each point as well.
* @return mixed
*/
function project_polygon($points=array(0=>'-1',1=>'-1',2=>'0',3=>'-1',4=>'1',5=>'0',6=>'1',7=>'1',8=>'0',9=>'1',10=>'-1',11=>'0'),$degree_x=45,$degree_y=45,$degree_z=45,$center_x=0,$center_y=0,$center_z=0,$dist1=5,$dist2=2,$include_z=false)
{
// check points
if ((count($points)%3)!=0) {
throw new Exception('The points must be a list like x1, y1, z1, x2, y2, z2, etc. The number of points therefore must be divisible by three.');
}
$degree_x = deg2rad($degree_x);
$degree_y = deg2rad($degree_y);
$degree_z = deg2rad($degree_z);
$cosx = cos($degree_x);
$sinx = sin($degree_x);
$cosy = cos($degree_y);
$siny = sin($degree_y);
$cosz = cos($degree_z);
$sinz = sin($degree_z);
$array = array();
for ($i=0;$i<count($points);$i=$i+3) {
$x0 = $points[$i]-$center_x;
$y0 = $points[$i+1]-$center_y;
$z0 = $points[$i+2]-$center_z;
$x1 = $cosy*($sinz*$y0 + $cosz*$x0) - $siny*$z0;
$y1 = $sinx*($cosy*$z0 + $siny*($sinz*$y0 + $cosz*$x0)) + $cosx*($cosz*$y0 - $sinz*$x0);
$z1 = $cosx*($cosy*$z0 + $siny*($sinz*$y0 + $cosz*$x0)) - $sinx*($cosz*$y0 - $sinz*$x0);
$x2 = $x1*$dist1/($z1+$dist1+$dist2);
$y2 = $y1*$dist1/($z1+$dist1+$dist2);
$z2 = $z1*$dist1/($z1+$dist1+$dist2);
$array[] = $x2;
$array[] = $y2;
if ($include_z) $array[] = $z2;
}
return $array;
}
/**
* render_polygons
*
* Helper function to render polygons to the screen with a number of options.
*
* @version 0.5
* @author Contributors at eXorithm
* @link /algorithm/view/render_polygons Listing at eXorithm
* @link /algorithm/history/render_polygons History at eXorithm
* @license /home/show/license
*
* @param array $polygons Multi-dimensional array. Each element will be an array of points (x1, y1, z1, x2, y2, z2, etc).
* @param string $vertex_color (hex color code) Color of the vertices of the polygons.
* @param string $face_color (hex color code) Color of the faces of the polygons.
* @param bool $wireframe Display as a wireframe (face color will be ignored).
* @param bool $dashes Display "hidden" vertices as dashes (only if wireframe is not selected).
* @param number $image_size Size of the resulting image.
* @param number $scale Scale factor. Set to 0 to auto-scale (fill the image).
* @return resource GD image
*/
function render_polygons($polygons=array(0=>array(0=>-100,1=>-100,2=>1,3=>0,4=>100,5=>1,6=>100,7=>-100,8=>1)),$vertex_color='000000',$face_color='ffd700',$wireframe=false,$dashes=false,$image_size=300,$scale=1)
{
foreach ($polygons as $polygon) {
if (!is_array($polygon)) {
throw new Exception('Each polygon must be a list.');
} else if ((count($polygon)%3)!=0) {
throw new Exception('Each polygon must be a list like x1, y1, z1, x2, y2, z2, etc. The number of points therefore must be divisible by three.');
}
}
if (is_array($vertex_color)) {
if (count($vertex_color) != count($polygons)) {
throw new Exception('If vertex colors is an array, it must contain the same number of colors as the number of polygons.');
}
}
if (is_array($face_color)) {
if (count($face_color) != count($polygons)) {
throw new Exception('If face colors is an array, it must contain the same number of colors as the number of polygons.');
}
}
// if scale=0 then we auto-scale
if ($scale==0) {
$max = 0;
for ($i=0; $i<count($polygons); $i++) {
for ($j=0; $j<count($polygons[$i]); $j=$j+3) {
if (abs($polygons[$i][$j])>$max)
$max = abs($polygons[$i][$j]);
if (abs($polygons[$i][$j+1])>$max)
$max = abs($polygons[$i][$j+1]);
}
}
if ($max>0)
$scale = ($image_size-2)/($max*2);
}
// the polygon arrays (x,y,z) must be converted into shapes (x,y)
$shapes = array();
$z_max = array();
for ($i=0; $i<count($polygons); $i++) {
$max = $polygons[$i][2];
for ($j=0; $j<count($polygons[$i]); $j=$j+3) {
$x = $polygons[$i][$j];
$y = $polygons[$i][$j+1];
// map each x,y coord to a screen position
$x = round($image_size/2 + $x*$scale);
$y = round($image_size/2 - $y*$scale);
$shapes[$i][$j] = $x;
$shapes[$i][$j+1] = $y;
// keep track of the maximum z-value for each shape
if ($polygons[$i][$j+2]>$max)
$max = $polygons[$i][$j+2];
}
$shapes[$i] = array_values($shapes[$i]);
$z_max[$i] = $max;
}
// create a blank image
$image = image_create_alpha($image_size, $image_size);
// create the colors
if (!is_array($vertex_color))
$vertex_color = array_fill(0, count($polygons), $vertex_color);
if (!is_array($face_color))
$face_color = array_fill(0, count($polygons), $face_color);
// painter's algorithm - draw farther polygons first
array_multisort($z_max, SORT_DESC, $shapes, $face_color, $vertex_color);
// draw the polygons
for ($i=0; $i<count($shapes); $i++) {
$v_color = allocate_color($image, $vertex_color[$i]);
$f_color = allocate_color($image, $face_color[$i]);
if (!$wireframe) {
imagefilledpolygon($image, $shapes[$i], count($shapes[$i])/2, $f_color);
}
imagepolygon($image, $shapes[$i], count($shapes[$i])/2, $v_color);
}
// draw dashes - BUGGY
if ($dashes) {
for ($i=0; $i<count($shapes); $i++) {
$v_color = allocate_color($image, $vertex_color[$i]);
$style = array($v_color, IMG_COLOR_TRANSPARENT, IMG_COLOR_TRANSPARENT, IMG_COLOR_TRANSPARENT, IMG_COLOR_TRANSPARENT);
imagesetstyle($image, $style);
imagepolygon($image, $shapes[$i], count($shapes[$i])/2, IMG_COLOR_STYLED);
}
}
return $image;
}
/**
* image_create_alpha
*
* Helper function to create a new blank image with transparency.
*
* @version 0.1
* @author Contributors at eXorithm
* @link /algorithm/view/image_create_alpha Listing at eXorithm
* @link /algorithm/history/image_create_alpha History at eXorithm
* @license /home/show/license
*
* @param mixed $width
* @param mixed $height
* @return resource GD image
*/
function image_create_alpha($width='',$height='')
{
// Create a normal image and apply required settings
$img = imagecreatetruecolor($width, $height);
imagealphablending($img, false);
imagesavealpha($img, true);
// Apply the transparent background
$trans = imagecolorallocatealpha($img, 0, 0, 0, 127);
for ($x = 0; $x < $width; $x++)
{
for ($y = 0; $y < $height; $y++)
{
imagesetpixel($img, $x, $y, $trans);
}
}
return $img;
}
/**
* allocate_color
*
* Helper function to allocate a color to an image. Color should be a 6-character hex string.
*
* @version 0.2
* @author Contributors at eXorithm
* @link /algorithm/view/allocate_color Listing at eXorithm
* @link /algorithm/history/allocate_color History at eXorithm
* @license /home/show/license
*
* @param resource $image (GD image) The image that will have the color allocated to it.
* @param string $color (hex color code) The color to allocate to the image.
* @param mixed $transparency The level of transparency from 0 to 127.
* @return mixed
*/
function allocate_color($image=null,$color='268597',$transparency='0')
{
if (preg_match('/[0-9ABCDEF]{6}/i', $color)==0) {
throw new Exception("Invalid color code.");
}
if ($transparency<0 || $transparency>127) {
throw new Exception("Invalid transparency.");
}
$r = hexdec(substr($color, 0, 2));
$g = hexdec(substr($color, 2, 2));
$b = hexdec(substr($color, 4, 2));
if ($transparency>127) $transparency = 127;
if ($transparency<=0)
return imagecolorallocate($image, $r, $g, $b);
else
return imagecolorallocatealpha($image, $r, $g, $b, $transparency);
}
?>