Skip to content

Instantly share code, notes, and snippets.

@MidnightLightning
Created February 10, 2016 19:35
Show Gist options
  • Select an option

  • Save MidnightLightning/4da88f75b163e544f63c to your computer and use it in GitHub Desktop.

Select an option

Save MidnightLightning/4da88f75b163e544f63c to your computer and use it in GitHub Desktop.
Dimetric SVG creation
<?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