HTML5 includes some features that allows developers to draw 3D shapes by drawing bi dimensional shapes and applying 3d transform, such as rotateX, rotateY and rotateZ. For convenience, you can shift the origin of axes using the style property If you don’t want the 3D image too flat (for example, all the faces of a cube having the same size) use perspective and perspective-origin style properties.
You can use the matrix3d style properties instead of the named transforms if, for example, you don’t want to compute angles.
The Style Properties
A style property can be defined by adding the attribute style
to an HTML element, defining a CSS class or accessing a DOM node.
In this section I will explain the properties using a little Javascript program that draws a regular tetrahedron.
Drawing a tetrahedron is done by drawing 4 isosceles triangles and rotating each of them once or twice.
“perspective” and “perspective-origin”
The distance and angle from which the shape is viewed.
“perspective” holds the distance
“perspective-origin” – a position value.
For example:
var main_div = d3.select('body')
.append('div')
.style('position','absolute')
.style('top','50px')
.style('left','50px')
.style('perspective','50px')
.style('perspective-origin','bottom left');
Transform Values: “rotateX”, “rotateY”, “rotate” and “transform-origin”
Rotate an axis. Keep the value of the rotated axis coordinate unchanged, and change the rest. The axis is rotated around the position defined by “transform-origin”
The following code adds the data for creating 4 triangles, and rotates 1 triangle 120 degrees to the right and 1 triangle 120 degrees to the left. Rotation is done around the bottom face’s centroid.
main_div.selectAll('div').
data([{color: 'red', transform: null,upperVertexInd: true},
{color: 'black', transform: 'rotateX(90deg)', 'origin':'
100px 100px 0'},
{color: 'blue', transform: 'rotateY(120deg)',origin: cent
roidString,upperVertexInd: true},
{color: 'green', transform: 'rotateY(-120deg)',origin: ce
ntroidString,upperVertexInd: true}])
.enter()
.append('div')
.style('position','absolute')
.style('top',0)
.style('left',0)
.style('transform',d=>d.transform)
.style('transform-origin',d=>d.origin)
.style('transform-style','preserve-3d')
(To be more precise, it rotates the DIV elements)
Tarnsform Values: “matrix3d”
This matrix is used if you want to use a transformation out of the comfort zone. For example, a rotation transform with cosines and sines of the angle. The argument list contains 16 values, which are the cells of a square matrix of order 4 (4 rows and 4 columns).
This matrix will be applied on (x,y,z,w) vector to get the target vector. When rotating a 2d vector )point), our original z-coordinate will be 0, and w will be 1.
To specify the matrix:
use
In my example, I will rotate 3 triangles, so their top vertex will go to a line perpendicular to the tetrahedron base, and passing through the base’s median.
The median of a triangle is the point where median cross its other, dividing each median at the ratio 1:2.
So, if each side of a triangle is of length 1. The height is
Since, the height is the length of the median, the distance from a side to the centroid is the height divided by 3, and the requested sine is latex13latex 1 \over 3
The cosine is
so, we will compute the matrix as follows:
var rotateXCos = Math.sqrt(8) / 3;
var rotateXSin = 1 / 3;
var rotateXMat3d = [1,0,0,0,
0,rotateXCos,rotateXSin,0,
0,-rotateXSin,rotateXCos,0,
0,0,0,1];
var matrixTransformString = 'matrix3d(' + rotateXMat3d + ')';
Now, the code to draw the tetrahedron with *d3.js( is:
var side=100;
var len=100;
var height=side * Math.sqrt(3)/2;
var centroidZValue = -height / 3; // The point where medians meet.
var rotateXCos = Math.sqrt(8) / 3;
var rotateXSin = 1 / 3;
var rotateXMat3d = [1,0,0,0,
0,rotateXCos,rotateXSin,0,
0,-rotateXSin,rotateXCos,0,
0,0,0,1];
var matrixTransformString = 'matrix3d(' + rotateXMat3d + ')';
var centroidString = '150px 0 ' + centroidZValue + 'px';
var main_div = d3.select('body')
.append('div')
.style('position','absolute')
.style('top','50px')
.style('left','50px')
.style('perspective','50px')
.style('perspective-origin','bottom left');
main_div.selectAll('div').
data([{color: 'red', transform: null,upperVertexInd: true},
{color: 'black', transform: 'rotateX(90deg)', 'origin':'
100px 100px 0'},
{color: 'blue', transform: 'rotateY(120deg)',origin: cent
roidString,upperVertexInd: true},
{color: 'green', transform: 'rotateY(-120deg)',origin: ce
ntroidString,upperVertexInd: true}])
.enter()
.append('div')
.style('position','absolute')
.style('top',0)
.style('left',0)
.style('transform',d=>d.transform)
.style('transform-origin',d=>d.origin)
.style('transform-style','preserve-3d')
.append('div')
.style('transform-style','preserve-3d')
.style('position','absolute')
.style('top',0)
.style('left',0)
.style('transform',function(d){
return d.upperVertexInd?matrixTransformString:false;
})
.style('transform-origin',function(d){
return d.upperVertexInd?'0 100px 0':false;
})
.append('svg')
.append('polygon')
.attr('points',[100,100,150,100-height,200,100])
.style('fill','none')
.style('stroke',d=>d.color);