How about this?
Here's the code:
<svg width='500' height='500'
xmlns='http://www.w3.org/2000/svg'
xmlns:xlink='http://www.w3.org/1999/xlink'
onload='init(evt)' >
<!-- Starting point was
http://www.petercollingridge.co.uk/blog/rotating-3d-svg-cube
-->
<style>
.surface{
stroke-width: 0;
stroke: black;
stroke-width: 2;
}
.hidden-edge{
stroke: black;
stroke-width: .5;
stroke-dasharray:3,3;
}
.button{
fill: #2060dd;
stroke: #2580ff;
stroke-width: 1;
}
.button:hover{
stroke-width: 3;
}
</style>
<script type='text/ecmascript'>
<![CDATA[
// TODO: stop using globals and create object, for >1 diagram per page.
// Distance along z axis from viewer (and origin) to plane that image is
// projected onto.
focal_length = 2.0;
// Centre of rotation
//cofr = {x:0.0, y:0.0, z:3.5};
cofr = {x:0.2, y:0.2, z:3.3};
function init(evt)
{
if ( window.svgDocument == null )
{
svgDocument = evt.target.ownerDocument;
init_model();
init_edge_elements();
}
drawModel();
}
function init_edge_elements()
{
edge_elements = new Array(edges.length);
for(var i=0; i<edges.length; i++)
{
edge_elements[i] = svgDocument.getElementById('edge-'+i);
}
}
function init_model()
{
var left_x = -0.5;
var right_x = 0.5;
var top_y = -0.5;
var bottom_y = 0.5;
var front_z = focal_length + 1.0;
var back_z = focal_length + 2.0;
var tlf = {y:top_y, x:left_x, z:front_z};
var trf = {y:top_y, x:right_x, z:front_z};
var brf = {y:bottom_y, x:right_x, z:front_z};
var blf = {y:bottom_y, x:left_x, z:front_z};
var tlb = {y:top_y, x:left_x, z:back_z};
var trb = {y:top_y, x:right_x, z:back_z};
var brb = {y:bottom_y, x:right_x, z:back_z};
var blb = {y:bottom_y, x:left_x, z:back_z};
coords = [tlf, trf, brf, blf, tlb, trb, brb, blb];
// Surfaces defined clockwise when view from outside.
var front = {points:[tlf, trf, brf, blf], colour:'red'};
var back = {points:[tlb, blb, brb, trb], colour:'green'};
var top = {points:[tlb, trb, trf, tlf], colour:'pink'};
var bottom = {points:[blb, blf, brf, brb], colour:'yellow'};
var left = {points:[tlb, tlf, blf, blb], colour:'purple'};
var right = {points:[trb, brb, brf, trf], colour:'orange'};
surfaces = [front, back, top, bottom, left, right];
edges = [
{p1:tlf, p2:trf},
{p1:trf, p2:brf},
{p1:brf, p2:blf},
{p1:blf, p2:tlf},
{p1:tlb, p2:trb},
{p1:trb, p2:brb},
{p1:brb, p2:blb},
{p1:blb, p2:tlb},
{p1:tlf, p2:tlb},
{p1:trf, p2:trb},
{p1:brf, p2:brb},
{p1:blf, p2:blb},
];
}
function drawModel()
{
// Project all points onto the view port with perspective.
// http://homepages.inf.ed.ac.uk/rbf/CVonline/LOCAL_COPIES/MOHR_TRIGGS/node9.html
// http://www.cse.unr.edu/~bebis/CS791E/Notes/PerspectiveProjection.pdf
//
for(var i=0; i<coords.length; i++)
{
var p = coords[i];
p.vpx = focal_length * p.x / p.z * 250 + 200;
p.vpy = focal_length * p.y / p.z * 250 + 200;
}
// Rough sort of surfaces from back to front.
// Using z value midway between opposite corners.
var z_order = new Array(surfaces.length);
for(var i=0; i<surfaces.length; i++)
{
var s = surfaces[i];
var z1 = s.points[0].z;
var z2 = s.points[2].z;
s.mid_z = (z1 > z2) ? z2+(z1-z2)/2 : z1+(z2-z1)/2;
z_order[i] = s;
}
z_order.sort(function (a, b){return b.mid_z-a.mid_z;});
// Fill surfaces from back to front, with hard outline.
// This only really works for simple convex solids like a cube.
// And then only if your rotation point isn't too far from the centre
// of the cube.
for(var i=0; i<z_order.length; i++)
{
var s = z_order[i];
var attr = '' + s.points[0].vpx+','+s.points[0].vpy+' '+
s.points[1].vpx+','+s.points[1].vpy+' '+
s.points[2].vpx+','+s.points[2].vpy+' '+
s.points[3].vpx+','+s.points[3].vpy;
surfaceElement = svgDocument.getElementById('surface-'+i);
surfaceElement.setAttributeNS(null, 'points', attr);
surfaceElement.setAttributeNS(null, 'style', 'fill:'+s.colour);
}
// Fill in lines as if hidden (big trick).
for(var i=0; i<edges.length; i++)
{
var edge = edge_elements[i];
var p1 = edges[i].p1;
edge.setAttributeNS(null, 'x1', p1.vpx);
edge.setAttributeNS(null, 'y1', p1.vpy);
var p2 = edges[i].p2;
edge.setAttributeNS(null, 'x2', p2.vpx);
edge.setAttributeNS(null, 'y2', p2.vpy);
}
}
function rotateAboutX(radians)
{
for(var i=0; i<coords.length; i++)
{
y = coords[i].y - cofr.y;
z = coords[i].z - cofr.z;
d = Math.sqrt(y*y + z*z);
theta = Math.atan2(y, z) + radians;
coords[i].y = cofr.y + d * Math.sin(theta);
coords[i].z = cofr.z + d * Math.cos(theta);
}
drawModel();
}
function rotateAboutY(radians)
{
for(var i=0; i<coords.length; i++)
{
x = coords[i].x - cofr.x;
z = coords[i].z - cofr.z;
d = Math.sqrt(x*x + z*z);
theta = Math.atan2(x, z) + radians;
coords[i].x = cofr.x + d * Math.sin(theta);
coords[i].z = cofr.z + d * Math.cos(theta);
}
drawModel();
}
function rotateAboutZ(radians)
{
for(var i=0; i<coords.length; i++)
{
x = coords[i].x - cofr.x;
y = coords[i].y - cofr.y;
d = Math.sqrt(x*x + y*y);
theta = Math.atan2(x, y) + radians;
coords[i].x = cofr.x + d * Math.sin(theta);
coords[i].y = cofr.y + d * Math.cos(theta);
}
drawModel();
}
function beginRotateX(radians)
{
rotateAboutX(radians);
rotateX_timeout = setInterval("rotateAboutX(" + radians + ")", 20);
}
function endRotateX()
{
if (typeof(rotateX_timeout) != "undefined")
{
clearTimeout(rotateX_timeout);
}
}
function beginRotateY(radians)
{
rotateAboutY(radians);
rotateY_timeout = setInterval("rotateAboutY(" + radians + ")", 20);
}
function endRotateY()
{
if (typeof(rotateY_timeout) != "undefined")
{
clearTimeout(rotateY_timeout);
}
}
function beginRotateZ(radians)
{
rotateAboutZ(radians);
rotateZ_timeout = setInterval("rotateAboutZ(" + radians + ")", 20);
}
function endRotateZ()
{
if (typeof(rotateZ_timeout) != "undefined")
{
clearTimeout(rotateZ_timeout);
}
}
]]>
</script>
<polygon id='surface-0' points='0,0 1,0 1,1 0,1' class='surface' style='' />
<polygon id='surface-1' points='0,0 1,0 1,1 0,1' class='surface' style='' />
<polygon id='surface-2' points='0,0 1,0 1,1 0,1' class='surface' style='' />
<polygon id='surface-3' points='0,0 1,0 1,1 0,1' class='surface' style='' />
<polygon id='surface-4' points='0,0 1,0 1,1 0,1' class='surface' style='' />
<polygon id='surface-5' points='0,0 1,0 1,1 0,1' class='surface' style='' />
<line id='edge-0' class='hidden-edge' x1='100' y1='100' x2='100' y2='100'/>
<line id='edge-1' class='hidden-edge' x1='100' y1='100' x2='100' y2='100'/>
<line id='edge-2' class='hidden-edge' x1='100' y1='100' x2='100' y2='100'/>
<line id='edge-3' class='hidden-edge' x1='100' y1='100' x2='100' y2='100'/>
<line id='edge-4' class='hidden-edge' x1='100' y1='100' x2='100' y2='100'/>
<line id='edge-5' class='hidden-edge' x1='100' y1='100' x2='100' y2='100'/>
<line id='edge-6' class='hidden-edge' x1='100' y1='100' x2='100' y2='100'/>
<line id='edge-7' class='hidden-edge' x1='100' y1='100' x2='100' y2='100'/>
<line id='edge-8' class='hidden-edge' x1='100' y1='100' x2='100' y2='100'/>
<line id='edge-9' class='hidden-edge' x1='100' y1='100' x2='100' y2='100'/>
<line id='edge-10' class='hidden-edge' x1='100' y1='100' x2='100' y2='100'/>
<line id='edge-11' class='hidden-edge' x1='100' y1='100' x2='100' y2='100'/>
<path class="button"
d="m50.5 470.5 15 -15 0 8 45 0 0 14 -45 0 0 8 z"
onmousedown='beginRotateY(0.08)'
onmouseout='endRotateY()'
onmouseup='endRotateY()'/>
<path class="button"
d="m190.5 470.5 -15 -15 0 8 -45 0 0 14 45 0 0 8 z"
onmousedown='beginRotateY(-0.08)'
onmouseout='endRotateY()'
onmouseup='endRotateY()'/>
<path class="button"
d="m470.5 50.5 15 15 -8 0 0 45 -14 0 0 -45 -8 0 z"
onmousedown='beginRotateX(0.08)'
onmouseout='endRotateX()'
onmouseup='endRotateX()'/>
<path class="button"
d="m470.5 190.5 15 -15 -8 0 0 -45 -14 0 0 45 -8 0 z"
onmousedown='beginRotateX(-0.08)'
onmouseout='endRotateX()'
onmouseup='endRotateX()'/>
<path class="button"
d="m470.5 250.5 15 15 -8 0 0 45 -14 0 0 -45 -8 0 z"
onmousedown='beginRotateZ(0.08)'
onmouseout='endRotateZ()'
onmouseup='endRotateZ()'/>
<path class="button"
d="m470.5 390.5 15 -15 -8 0 0 -45 -14 0 0 45 -8 0 z"
onmousedown='beginRotateZ(-0.08)'
onmouseout='endRotateZ()'
onmouseup='endRotateZ()'/>
</svg>