Our screens are 2D dimensional planes and consists of pixels in X and Y directions. How we display a 3D object in this 2D plane ? 3D objects in our 2D screens are projection of 3D coordinates by using mathematical calculations. In 3D operations, i.e. in 3D graphics, 3D robotics, 3D mechanics; we use matrix forms which are multi dimensional arrays in C++. Generally they are 3 dimensional arrays formed with floats or doubles. In some cases to speed up graphical operations integers or integer arrays for float values are also used.
We can calculate the position of a point in 3D in X, Y, Z coordinate with its rotational angles (here we will use phi, omega and kappa for each angles) on the 2D x and y coordinates. The simplest way to transform 3D point with 3D rotation to 2D x and y points is using Rotational Matrix.
A Rotation Matrix is a Transformation Matrix defined in Linear Algebra, that is used to perform a rotation in Euclidean space. In this space, a basic rotation, also called elemental rotation, is a rotation about one of the axes of a coordinate system. The rotation matrix can be obtained from the multiplication of these three basic rotation matrices.
To operate with R Rotation Matrix, Let’s define a rotation matrix array as a global in 3×3 form as below,
1 2 3 |
float R[3][3]; |
You can define this matrix as double to get more precision. We need 3D points and phi, omg, kap rotation angles.
1 2 3 4 5 |
int numofpoints=100; TPoint3D p[100]; float phi,omg,kap; |
1 2 3 |
TBitmap *bmp; |
We can calculate each member of our rotation matrix in every angular change, lets define our R matrix as below,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
void set_R_matrix(void) { R[0][0]=cos(phi)*cos(kap); R[0][1]=cos(omg)*sin(kap)+sin(omg)*sin(phi)*cos(kap); R[0][2]=sin(omg)*sin(kap)-cos(omg)*sin(phi)*cos(kap); R[1][0]=-cos(phi)*sin(kap); R[1][1]=cos(omg)*cos(kap)-sin(omg)*sin(phi)*sin(kap); R[1][2]=sin(omg)*cos(kap)+cos(omg)*sin(phi)*sin(kap); R[2][0]=sin(phi); R[2][1]=-sin(omg)*cos(phi); R[2][2]=cos(omg)*cos(phi); } |
As you see Rotation Matrix is only consists 3D angles and trigonometric functions. That means when we move your object or when we do change in position of points we don’t need to calculate this R matrix. We only update it when any of phi, omg, kap angles is changed.
In C++ Builder, we can setup bitmap and these 3D points in a random in a Form creation procedure as below,
1 2 3 4 5 6 7 8 9 10 11 12 13 |
__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { TBitmap *bmp=new TBitmap(Image1->Width,Image1->Height); Image1->Bitmap->Assign(bmp); bmp->Free(); for(int i=0; i<numofpoints; i++) { p[i]=Point3D(random(500)-250, random(500)-250, random(500)-250); } } |
To draw all points by using a 3D Rotation Matrix, first we should have a bitmap to draw and origin points (orjx, orjy here) to center our drawing in this 2D view. We should set Rotation matrix, you can do this outside
How Do We Draw with Rotation Matrix ?
In this example, we will draw lines from center to our points into Canvas of our Bitmap. Before we draw to canvas we should use BeginScene() function and then we can clear the bitmap in a color. Then for all 3D points, we calculate x coordinate of a 3D point by starting with x origin and adding result of multiplication of appropriate points in direction with appropriate rotation matrix members; and as same we calculate y coordinate of the same 3D point by starting with y origin and adding result of multiplication of appropriate points in direction with appropriate rotation matrix members. For the each of these 2D points we draw lines from center to the x, y point coordination. After this loop, we ended Canvas drawing withy EndScene() function. See this example draw() function below,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
void draw(TBitmap *bmp, float orjx, float orjy) { int k; float x,y; bmp->Canvas->BeginScene(); bmp->Canvas->Clear(0xFF444488); bmp->Canvas->Stroke->Color=0xFFCCCCFF; for ( int i=0; i<numofpoints; i++) { x=orjx; for(k=0; k<=2; k++) x += R[0][k]*(p[i].V[k]); y=orjy; for(k=0; k<=2; k++) y -= R[1][k]*(p[i].V[k]); bmp->Canvas->DrawLine( TPointF(orjx,orjy), TPointF(x,y), 0.8); } bmp->Canvas->EndScene(); } |
Where We Update Rotation Matrix and Draw ?
To draw rotation in intervals we use Timer component and we can do these rotation, calculation and drawing event in OnTimer() event. In this OnTimer() event, first we can do some additions to some angular parameters (i.e. increasing omg and phi), and we set Rotation Matrix by using set_R_Matrix(); because of changes in these angles. Then we call draw() function to draw all points to our bitmap in 2D projection.
1 2 3 4 5 6 7 8 9 10 |
void __fastcall TForm1::Timer1Timer(TObject *Sender) { omg+=M_PI/360; phi+=M_PI/360; set_R_matrix(); draw(Image1->Bitmap, Image1->Width/2, Image1->Height/2); } |
As a result you will see your random points are pointed with lines and rotating in Timer intervals. You can set Timer Interval to speed up, set between 15-40 milliseconds, depends on your graphic card.
As you see in this example we didn’t use any OpenGL, DirectX, RADS Official Viewport3D or any other 3D Engine. We just use mathematics to calculate 2D position of 3D points, and we draw() all to a bitmap in a OnTimer() event.
Note that this Rotation Matrix can be used In 3D operations, i.e. in 3D graphics, 3D robotics, 3D mechanics to calculate Displacement, Velocity, Acceleration, Forces, Pressure of each points or joints.
Here is the full code of this simple C++ Builder Multi-Device Application, with an Image and Timer components.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
//--------------------------------------------------------------------------- #include <fmx.h> #pragma hdrstop #include "Unit1b.h" //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.fmx" TForm1 *Form1; float R[4][4]; int numofpoints=100; TPoint3D p[100]; float phi,omg,kap; //--------------------------------------------------------------------------- __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { TBitmap *bmp=new TBitmap(Image1->Width,Image1->Height); Image1->Bitmap->Assign(bmp); bmp->Free(); for(int i=0; i<numofpoints; i++) { p[i]=Point3D(random(500)-250, random(500)-250, random(500)-250); } } //--------------------------------------------------------------------------- void set_R_matrix(void) { R[0][0]=cos(phi)*cos(kap); R[0][1]=cos(omg)*sin(kap)+sin(omg)*sin(phi)*cos(kap); R[0][2]=sin(omg)*sin(kap)-cos(omg)*sin(phi)*cos(kap); R[1][0]=-cos(phi)*sin(kap); R[1][1]=cos(omg)*cos(kap)-sin(omg)*sin(phi)*sin(kap); R[1][2]=sin(omg)*cos(kap)+cos(omg)*sin(phi)*sin(kap); R[2][0]=sin(phi); R[2][1]=-sin(omg)*cos(phi); R[2][2]=cos(omg)*cos(phi); } //--------------------------------------------------------------------------- void draw(TBitmap *bmp, float orjx, float orjy) { int k; float x,y; bmp->Canvas->BeginScene(); bmp->Canvas->Clear(0xFF444488); bmp->Canvas->Stroke->Color=0xFFCCCCFF; for(int i=0; i<numofpoints; i++) { x=orjx; for(k=0; k<=2; k++) x+=R[0][k]*(p[i].V[k]); y=orjy; for(k=0; k<=2; k++) y-=R[1][k]*(p[i].V[k]); bmp->Canvas->DrawLine(TPointF(orjx,orjy),TPointF(x,y), 0.8); } bmp->Canvas->EndScene(); } //--------------------------------------------------------------------------- void __fastcall TForm1::Timer1Timer(TObject *Sender) { omg+=M_PI/360; phi+=M_PI/360; set_R_matrix(); draw(Image1->Bitmap, Image1->Width/2, Image1->Height/2); } //--------------------------------------------------------------------------- void __fastcall TForm1::FormResize(TObject *Sender) { Image1->Bitmap->SetSize(Image1->Width,Image1->Height); } //--------------------------------------------------------------------------- |