Created
February 10, 2016 19:35
-
-
Save MidnightLightning/4da88f75b163e544f63c to your computer and use it in GitHub Desktop.
Dimetric SVG creation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| // Create Dimetric SVG image | |
| // Diametric is a parallel projection similar to isometric (30 degrees x 30 degrees grid) which is unequal (15 degrees x 60 degrees). | |
| // To make a unit square, starting at (0,0), on the SVG canvas (Y axis flipped) as the closest point and going clockwise, | |
| // the other points are (-cos(15), -sin(15)), (-cos(15)+cos(60), -sin(15)-sin(60)), (cos(60), -sin(60)) | |
| // or, (-0.965925826289068, -0.258819045102521), (-0.465925826289068, -1.12484444888696), (0.50, -0.866025403784439) | |
| $unit_square = new Polygon( | |
| new Point(0,0), | |
| new Point(-1*cos(pi()/12), -1*sin(pi()/12)), | |
| new Point(-1*cos(pi()/12)+cos(pi()/3), -1*sin(pi()/12)-sin(pi()/3)), | |
| new Point(cos(pi()/3), -1*sin(pi()/3)) | |
| ); | |
| $left_face = new Polygon( | |
| new Point(0.01,-0.1), // Cheat right and up to hide any rounding error gap | |
| new Point(-1*cos(pi()/12), -1*sin(pi()/12)), | |
| new Point(-1*cos(pi()/12), -1*sin(pi()/12)+1), | |
| new Point(0.01,1) // Cheat right | |
| ); | |
| $right_face = new Polygon( | |
| new Point(0,-0.1), // Cheat up | |
| new Point(0,1), | |
| new Point(cos(pi()/3), -1*sin(pi()/3)+1), | |
| new Point(cos(pi()/3), -1*sin(pi()/3)) | |
| ); | |
| $global_scale = 21; // How many pixels is one square? | |
| $global_origin_x = 300; | |
| $global_origin_y = 300; | |
| $unit_square = $unit_square->scale($global_scale); | |
| $left_face = $left_face->scale($global_scale); | |
| $right_face = $right_face->scale($global_scale); | |
| // Create a scene | |
| $blocks = array( | |
| //array(1,1,0,1,2), | |
| array(2,2,2,2,2, 2,1,1,1,2, 2,1,1,1,2, 2,1,1,1,2, 2,2,2,2,2), | |
| array(0,0,0,0,3, 0,1,1,1,0, 0,0,0,1,0, 0,1,1,1,0), | |
| array(0,0,0,0,0, 0,1,1,1,0, 0,0,0,1,0, 0,1,1,1,0), | |
| array(0,0,0,0,0, 0,1,1,1,0, 0,1,0,1,0, 0,1,1,1,0), | |
| array(0,0,0,0,0, 0,1,1,1,0, 0,1,0,1,0, 0,1,1,1,0), | |
| array(0,0,0,0,0, 0,1,1,1,0, 0,1,0,1,0, 0,1,1,1,0), | |
| array(1,1,1,1,1, 1,1,1,1,1, 1,1,0,1,1, 1,1,1,1,1, 1,1,1,1,1), | |
| array(1,0,1,0,1, 0,0,0,0,0, 1,0,0,0,1, 0,0,0,0,0, 1,0,1,0,1), | |
| ); | |
| $global_row_size = 5; // How many blocks in a row? | |
| /* | |
| list($sample_top, $sample_left, $sample_right) = multiTranslate($global_origin_x, $global_origin_y, array($unit_square, $left_face, $right_face)); | |
| $extra = $sample_right->translate(0, -$global_scale+1); | |
| */ | |
| // Output SVG | |
| header("Content-Type: image/svg+xml"); | |
| echo '<?xml version="1.0" standalone="no"?>'."\n"; | |
| echo '<svg version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events">'."\n"; | |
| echo '<defs>'."\n"; | |
| echo '<linearGradient id="stone_right" x1="52%" y1="0%" x2="100%" y2="100%"><stop offset="50%" stop-color="#666" /><stop offset="100%" stop-color="#333" /></linearGradient>'."\n"; | |
| echo '<linearGradient id="stone_left" x1="20%" y1="0%" x2="0%" y2="100%"><stop offset="50%" stop-color="#999" /><stop offset="100%" stop-color="#666" /></linearGradient>'."\n"; | |
| echo '<linearGradient id="stone_top" x1="0%" y1="0%" x2="0%" y2="100%"><stop offset="50%" stop-color="#CCC" /><stop offset="100%" stop-color="#AAA" /></linearGradient>'."\n"; | |
| echo '<linearGradient id="grass_right" x1="52%" y1="0%" x2="100%" y2="100%"><stop offset="30%" stop-color="#227012" /><stop offset="35%" stop-color="#5e301e" /><stop offset="50%" stop-color="#5e301e" /><stop offset="100%" stop-color="#432317" /></linearGradient>'."\n"; | |
| echo '<linearGradient id="grass_left" x1="20%" y1="0%" x2="0%" y2="100%"><stop offset="20%" stop-color="#308320" /><stop offset="25%" stop-color="#654336" /><stop offset="50%" stop-color="#654336" /><stop offset="100%" stop-color="#4e342a" /></linearGradient>'."\n"; | |
| echo '<linearGradient id="grass_top" x1="0%" y1="0%" x2="0%" y2="100%"><stop offset="50%" stop-color="#3bc021" /><stop offset="100%" stop-color="#319c1c" /></linearGradient>'."\n"; | |
| echo '<linearGradient id="dirt_right" x1="52%" y1="0%" x2="100%" y2="100%"><stop offset="50%" stop-color="#5e301e" /><stop offset="100%" stop-color="#432317" /></linearGradient>'."\n"; | |
| echo '<linearGradient id="dirt_left" x1="20%" y1="0%" x2="0%" y2="100%"><stop offset="50%" stop-color="#654336" /><stop offset="100%" stop-color="#4e342a" /></linearGradient>'."\n"; | |
| echo '<linearGradient id="dirt_top" x1="0%" y1="0%" x2="0%" y2="100%"><stop offset="50%" stop-color="#9f5f2c" /><stop offset="100%" stop-color="#845127" /></linearGradient>'."\n"; | |
| echo '<linearGradient id="wood_right" x1="52%" y1="0%" x2="100%" y2="100%"><stop offset="50%" stop-color="#554729" /><stop offset="100%" stop-color="#443922" /></linearGradient>'."\n"; | |
| echo '<linearGradient id="wood_left" x1="20%" y1="0%" x2="0%" y2="100%"><stop offset="50%" stop-color="#827251" /><stop offset="100%" stop-color="#6a5d40" /></linearGradient>'."\n"; | |
| echo '<linearGradient id="wood_top" x1="0%" y1="0%" x2="0%" y2="100%"><stop offset="50%" stop-color="#b07f16" /><stop offset="100%" stop-color="#9a721c" /></linearGradient>'."\n"; | |
| echo '<linearGradient id="water_right" x1="52%" y1="0%" x2="100%" y2="100%"><stop offset="50%" stop-color="#24389d" /><stop offset="100%" stop-color="#203188" /></linearGradient>'."\n"; | |
| echo '<linearGradient id="water_left" x1="20%" y1="0%" x2="0%" y2="100%"><stop offset="50%" stop-color="#475bc0" /><stop offset="100%" stop-color="#4455ac" /></linearGradient>'."\n"; | |
| echo '<linearGradient id="water_top" x1="0%" y1="0%" x2="0%" y2="100%"><stop offset="50%" stop-color="#4f69ed" /><stop offset="100%" stop-color="#485fd1" /></linearGradient>'."\n"; | |
| echo '</defs>'."\n"; | |
| // Translate blocks to scene | |
| for ($i=0; $i<count($blocks); $i++) { | |
| $layer = $blocks[$i]; // Current layer; | |
| for ($j=count($layer)-1; $j>=0; $j--) { | |
| $y_off = floor($j/$global_row_size); // Which vertical row are we on? | |
| $x_off = fmod($j, $global_row_size); // What horizontal offset are we at? | |
| $draw_top = (count($blocks) > $i+1 && $blocks[$i+1][$j] > 0)? false : true; // No top if block above is solid | |
| $draw_left = ($j > 0 && $layer[$j-1] > 0 && fmod($j, $global_row_size) != 0)? false : true; // No left side if prior block is solid | |
| $draw_right = ($j >= $global_row_size && $layer[$j-$global_row_size] > 0)? false : true; // No right side if same block in prior row was solid | |
| if ($layer[$j] > 0 && ($draw_top || $draw_left || $draw_right)) { | |
| // Some part of the cube is visible; draw it | |
| list($x, $y) = dimetricToTranslate($x_off, $y_off); | |
| $x = $x*($global_scale-1) + $global_origin_x; | |
| $y = $y*($global_scale-1) + $global_origin_y - $i*($global_scale); | |
| switch ($layer[$j]) { | |
| case 1: | |
| case 4: | |
| $class = 'stone'; | |
| break; | |
| case 2: | |
| $class = 'grass'; | |
| break; | |
| case 3: | |
| case 60: | |
| $class = 'dirt'; | |
| break; | |
| case 5: | |
| case 47: | |
| $class = 'wood'; | |
| break; | |
| case 8: | |
| case 9: | |
| $class = 'water'; | |
| break; | |
| case 10: | |
| case 11: | |
| $class = 'lava'; | |
| break; | |
| case 12: | |
| $class = 'sand'; | |
| break; | |
| case 13: | |
| $class = 'gravel'; | |
| break; | |
| case 14: | |
| case 15: | |
| case 16: | |
| case 56: | |
| case 73: | |
| $class = 'ore'; | |
| break; | |
| case 17: | |
| $class = 'log'; | |
| break; | |
| case 18: | |
| $class = 'leaves'; | |
| break; | |
| case 20: | |
| $class = 'glass'; | |
| break; | |
| case 49: | |
| $class = 'obsidian'; | |
| break; | |
| default: | |
| $class = 'stone'; | |
| } | |
| if ($draw_right) { | |
| $right = $right_face->translate($x,$y); | |
| echo '<polygon points="'.$right->getPoints().'" fill="url(#'.$class.'_right)" />'."\n"; | |
| } | |
| if ($draw_left) { | |
| $left = $left_face->translate($x,$y); | |
| echo '<polygon points="'.$left->getPoints().'" fill="url(#'.$class.'_left)" />'."\n"; | |
| } | |
| if ($draw_top) { | |
| $top = $unit_square->translate($x,$y); | |
| echo '<polygon points="'.$top->getPoints().'" fill="url(#'.$class.'_top)" />'."\n"; | |
| } | |
| } | |
| } | |
| } | |
| /* | |
| echo '<polygon points="'.$sample_top->getPoints().'" fill="url(#topFace)" />'."\n"; | |
| echo '<polygon points="'.$sample_left->getPoints().'" fill="url(#leftFace)" />'."\n"; | |
| echo '<polygon points="'.$sample_right->getPoints().'" fill="url(#rightFace)" />'."\n"; | |
| echo '<polygon points="'.$extra->getPoints().'" fill="url(#rightFace)" />'."\n"; | |
| */ | |
| echo '</svg>'; | |
| ///// Class Definitions ///// | |
| /** | |
| * Hold a graphical point | |
| */ | |
| class Point { | |
| var $x; | |
| var $y; | |
| function __construct($x, $y) { | |
| $this->x = floatval($x); | |
| $this->y = floatval($y); | |
| } | |
| function __toString() { | |
| return "(".$this->x.", ".$this->y.")"; | |
| } | |
| } | |
| class Polygon { | |
| var $points; | |
| function __construct() { | |
| $args = func_get_args(); | |
| $this->points = array(); // Empty array to start with | |
| foreach($args as $arg) { | |
| if ($arg instanceof Point) $this->points[] = $arg; | |
| } | |
| } | |
| function addPoint($point) { | |
| if ($point instanceof Point) { | |
| $this->points[] = $point; | |
| return true; | |
| } else { | |
| return false; | |
| } | |
| } | |
| function getPoints() { | |
| $str = ""; | |
| foreach($this->points as $point) { | |
| $str .= $point->x.",".$point->y." "; | |
| } | |
| return substr($str,0, -1); // trim final space | |
| } | |
| function scale($factor) { | |
| $scaled = new Polygon(); | |
| foreach($this->points as $point) { | |
| $scaled->addPoint(new Point($point->x*$factor, $point->y*$factor)); | |
| } | |
| return $scaled; | |
| } | |
| function translate($x, $y) { | |
| $translated = new Polygon(); | |
| foreach($this->points as $point) { | |
| $translated->addPoint(new Point($point->x+$x, $point->y+$y)); | |
| } | |
| return $translated; | |
| } | |
| } | |
| ///// Functions ///// | |
| function multiTranslate($x, $y, $polygons) { | |
| $final = array(); | |
| foreach($polygons as $polygon) { | |
| if ($polygon instanceof Polygon) $final[] = $polygon->translate($x, $y); | |
| } | |
| return $final; | |
| } | |
| // Take a point on the dimetric grid, and translate it into a screen-space translation offset. | |
| function dimetricToTranslate($x, $y) { | |
| $nx = 0; | |
| $ny = 0; | |
| // Shift by X | |
| $nx += cos(pi()/3)*$x; | |
| $ny -= sin(pi()/3)*$x; | |
| // Shift by Y | |
| $nx -= cos(pi()/12)*$y; | |
| $ny -= sin(pi()/12)*$y; | |
| return array($nx, $ny); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment