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');
```

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)

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 $3latex1 $

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);
```