PHP & More

A technical blog about programming in PHP and about technology in general: programming, workarounds and troubleshooting

Drawing Simple 3D Shapes in HTML5 — May 5, 2019

Drawing Simple 3D Shapes in HTML5

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.

enter image description here

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)

enter image description here

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:

\left( \begin{matrix} a_0 \ a_4 \ a_8 \ a_{12} \\ a_1 \ a_5 \ a_9 \ a_{13} \\ a_2 \ a_6 \ a_{10} a_{14} \\ a_3 \ a_7 \ a_{11} \ a_{15}\end{matrix} \right)

use

matrix3d(a_0,a_1,a_2,...,a_{15})

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 \sqrt(3)\over2

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 \sqrt {1 - {1 \over 3}^2} = {\sqrt 8 \over 3}

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

enter image description here

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

Advertisements
Rotate Your Movie — September 17, 2015

Rotate Your Movie

I have received by e-mail a rotated video in the ‘flv’ format. The video was supposed to be a vertical one, but it turned out to be horizontal, that is ROTATED. So, I wrote a little program to rotate it back using libming.
There are two things to take care of when processing the input FLV:

  • The video stream.
  • The sound stream.

Both can be taken from the FLV file.
The code is written in C++, but can be translated easily into PHP. Following is the code:

#include <iostream>
#include <mingpp.h>

using namespace std;

int main(){
  const char *flvFile ="/path/to/inputVideoFile.flv";

  // Get the video stream from the file. The input file can be in the FLV format.
  SWFVideoStream *stream = new SWFVideoStream(flvFile);

  SWFMovie mov (9);  // Create the movie object.

  // The method 'add' returns a display item.
  // Display items can be rotated, transformed, etc.
  SWFDisplayItem *di=mov.add(stream);  

  // Sound streams are taken from a file object. 
  FILE *soundFD = fopen(flvFile, "rb+");
  SWFSoundStream sound(soundFD);

  // The original dimensions of the video are 426 X 240.
  di->rotate(-90);  // Rotate the item 90 degrees clockwise
  di->move(240, 0);  // The rotation moves point (0,240) to (-240,0).


  mov.setSoundStream(&sound,0);  // Add the sound stream at the beginning
                                 // of the movie.

  // Show the frames one by one.
  int noFrames = stream->getNumFrames();
  for (int i=0; i<noFrames; i++)
      mov.nextFrame();

  mov.setDimension(240, 426); // The new dimensions.
  mov.save("/path/to/outputVideoFile.swf", 9);
  cerr<<"Fin\n";
  return 0;
}

This will create a real vertical movie. Don’t share it on YouTube or anywhere you cannot control your movie dimensions.