I create the same library but using both double or Integers for faster execution and SIN/COS tables, is done in C# I was to lazy to do it on a uC:.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
struct _2d_point_i
{
public Int16 x;
public Int16 y;
public Int16 depth;
public Int16 scaleFactor;
};
struct _2d_point
{
public double x;
public double y;
public double depth;
public double scaleFactor;
};
struct _3d_points_i
{
public Int32[] x;
public Int32[] y;
public Int32[] z;
public Int32[] depth;
public Int32[] scaleFactor;
public Int32 focalLength;
public Int32 depthScale;
public UInt16 length;
};
struct _3d_points
{
public double[] x;
public double[] y;
public double[] z;
public double[] depth;
public double[] scaleFactor;
public double focalLength;
public double depthScale;
public UInt16 length;
};
struct axisRotations_i
{
public Int32 x;
public Int32 y;
public Int32 z;
};
struct axisRotations
{
public double x;
public double y;
public double z;
};
Int16[] sin = new Int16[91];
Int16[] cos = new Int16[91];
static Bitmap MyImage = new Bitmap(512, 512);
static Graphics graphic = Graphics.FromImage(MyImage);
_3d_points Points;
_3d_points_i Points_i;
public Form1()
{
InitializeComponent();
}
Int16 get_sin(UInt16 deg)
{
UInt16 degIn = (UInt16)(deg % 359);
if (degIn <= 90)
return sin[degIn];
else if (degIn <= 180)
return sin[180 - degIn];
else if (degIn <= 270)
return (Int16)(-sin[degIn - 180]);
else
return (Int16)(-sin[360 - degIn]);
}
Int16 get_cos(UInt16 deg)
{
UInt16 degIn = (UInt16)(deg % 359);
if (degIn <= 90)
return cos[degIn];
else if (degIn <= 180)
return (Int16)(-cos[180 - degIn]);
else if (degIn <= 270)
return (Int16)(-cos[degIn - 180]);
else
return cos[360 - degIn];
}
void make2DPointI(_2d_point_i[] _point, UInt32 Cell, Int16 x, Int16 y, Int16 depth, Int16 scaleFactor)
{
_point[Cell].x = x;
_point[Cell].y = y;
_point[Cell].depth = depth;
_point[Cell].scaleFactor = scaleFactor;
}
void make2DPoint(_2d_point[] _point, UInt32 Cell, double x, double y, double depth, double scaleFactor)
{
_point[Cell].x = x;
_point[Cell].y = y;
_point[Cell].depth = depth;
_point[Cell].scaleFactor = scaleFactor;
}
void Transform3Dto2D_i(_2d_point[] screenPoints, _3d_points_i Points, axisRotations_i AxisRotations)
{
Int16 sx = get_sin((UInt16)(AxisRotations.x));
Int16 cx = get_cos((UInt16)(AxisRotations.x));
Int16 sy = get_sin((UInt16)(AxisRotations.y));
Int16 cy = get_cos((UInt16)(AxisRotations.y));
Int16 sz = get_sin((UInt16)(AxisRotations.z));
Int16 cz = get_cos((UInt16)(AxisRotations.z));
Int32 x, y, z, xy, xz, yx, yz, zx, zy, scaleFactor;
UInt32 i = Points.length;
while (i-- != 0)
{
x = Points.x[i];
y = Points.y[i];
z = Points.z[i];
// rotation around x
xz = (Int32)((Int32)(sx * y) + (Int32)(cx * z)) / 65536;
xy = (Int32)((Int32)(cx * y) - (Int32)(sx * z)) / 65536;
// rotation around y
yx = (Int32)((Int32)(sy * xz) + (Int32)(cy * x)) / 65536;
yz = (Int32)((Int32)(cy * xz) - (Int32)(sy * x)) / 65536;
// rotation around z
zy = (Int32)((Int32)(sz * yx) + (Int32)(cz * xy)) / 65536;
zx = (Int32)((Int32)(cz * yx) - (Int32)(sz * xy)) / 65536;
scaleFactor = (Int32)((Points.focalLength * 65536) / ((Points.focalLength + yz)));
x = (Int32)((Int32)(zx * scaleFactor) / (Points.depthScale * 65536));
y = (Int32)((Int32)(zy * scaleFactor) / (Points.depthScale * 65536));
z = (Int32)((yz * 65536) / (Points.depthScale * 65536));
make2DPoint(screenPoints, i, x, y, -z, scaleFactor);
}
}
void Transform3Dto2D(_2d_point[] screenPoints, _3d_points Points, axisRotations AxisRotations)
{
double sx = Math.Sin(AxisRotations.x);
double cx = Math.Cos(AxisRotations.x);
double sy = Math.Sin(AxisRotations.y);
double cy = Math.Cos(AxisRotations.y);
double sz = Math.Sin(AxisRotations.z);
double cz = Math.Cos(AxisRotations.z);
double x, y, z, xy, xz, yx, yz, zx, zy, scaleFactor;
UInt32 i = Points.length;
while (i-- != 0)
{
x = Points.x[i];
y = Points.y[i];
z = Points.z[i];
// rotation around x
xy = cx * y - sx * z;
xz = sx * y + cx * z;
// rotation around y
yz = cy * xz - sy * x;
yx = sy * xz + cy * x;
// rotation around z
zx = cz * yx - sz * xy;
zy = sz * yx + cz * xy;
scaleFactor = Points.focalLength / (Points.focalLength + yz);
x = (zx * scaleFactor) / Points.depthScale;
y = (zy * scaleFactor) / Points.depthScale;
z = yz / Points.depthScale;
make2DPoint(screenPoints, i, x, y, -z, scaleFactor);
}
}
private void paint_3d_triangle_i(_3d_points_i Points, Int16 X_offset, Int16 Y_offset, Int16 X_Angle, Int16 Y_Angle, Int16 Z_Angle)
{
_2d_point[] screenPoints = new _2d_point[3];
axisRotations_i cubeAxisRotations;
cubeAxisRotations.y = Y_Angle;
cubeAxisRotations.x = X_Angle;
cubeAxisRotations.z = Z_Angle;
Transform3Dto2D_i(screenPoints, Points, cubeAxisRotations);
Int32 X_start = (Int32)screenPoints[0].x;
Int32 Y_start = (Int32)screenPoints[0].y;
Int32 X_end = (Int32)screenPoints[1].x;
Int32 Y_end = (Int32)screenPoints[1].y;
graphic.DrawLine(Pens.Red, X_offset + X_start, Y_offset + Y_start, X_offset + X_end, Y_offset + Y_end);
X_start = (Int32)screenPoints[1].x;
Y_start = (Int32)screenPoints[1].y;
X_end = (Int32)screenPoints[2].x;
Y_end = (Int32)screenPoints[2].y;
graphic.DrawLine(Pens.Red, X_offset + X_start, Y_offset + Y_start, X_offset + X_end, Y_offset + Y_end);
X_start = (Int32)screenPoints[2].x;
Y_start = (Int32)screenPoints[2].y;
X_end = (Int32)screenPoints[0].x;
Y_end = (Int32)screenPoints[0].y;
graphic.DrawLine(Pens.Red, X_offset + X_start, Y_offset + Y_start, X_offset + X_end, Y_offset + Y_end);
}
private void paint_3d_triangle(_3d_points Points, Int32 X_offset, Int32 Y_offset, double X_Angle, double Y_Angle, double Z_Angle)
{
_2d_point[] screenPoints = new _2d_point[3];
axisRotations cubeAxisRotations;
cubeAxisRotations.y = Y_Angle;
cubeAxisRotations.x = X_Angle;
cubeAxisRotations.z = Z_Angle;
Transform3Dto2D(screenPoints, Points, cubeAxisRotations);
Int32 X_start = (Int32)screenPoints[0].x;
Int32 Y_start = (Int32)screenPoints[0].y;
Int32 X_end = (Int32)screenPoints[1].x;
Int32 Y_end = (Int32)screenPoints[1].y;
graphic.DrawLine(Pens.Red, X_offset + X_start, Y_offset + Y_start, X_offset + X_end, Y_offset + Y_end);
X_start = (Int32)screenPoints[1].x;
Y_start = (Int32)screenPoints[1].y;
X_end = (Int32)screenPoints[2].x;
Y_end = (Int32)screenPoints[2].y;
graphic.DrawLine(Pens.Red, X_offset + X_start, Y_offset + Y_start, X_offset + X_end, Y_offset + Y_end);
X_start = (Int32)screenPoints[2].x;
Y_start = (Int32)screenPoints[2].y;
X_end = (Int32)screenPoints[0].x;
Y_end = (Int32)screenPoints[0].y;
graphic.DrawLine(Pens.Red, X_offset + X_start, Y_offset + Y_start, X_offset + X_end, Y_offset + Y_end);
}
void drawTriangle()
{
pictureBox1.Image = MyImage;
graphic.Clear(Color.White);
Points = new _3d_points();
Points.x = new double[3];
Points.y = new double[3];
Points.z = new double[3];
Points.depth = new double[3];
Points.scaleFactor = new double[3];
Points.x[0] = -100;
Points.y[0] = -100;
Points.z[0] = -100;
Points.x[1] = 100;
Points.y[1] = -100;
Points.z[1] = -100;
Points.x[2] = 0;
Points.y[2] = 100;
Points.z[2] = -100;
Points.focalLength = 1000;
Points.length = 3;
double rx = trackBar1.Value;
double ry = trackBar2.Value;
double rz = trackBar3.Value;
double d = trackBar4.Value;
Points.depthScale = d;
paint_3d_triangle(Points, 256, 256, rx / 1000, ry / 1000, rz / 1000);
}
void drawTriangle_i()
{
pictureBox1.Image = MyImage;
graphic.Clear(Color.White);
Points_i = new _3d_points_i();
Points_i.x = new Int32[3];
Points_i.y = new Int32[3];
Points_i.z = new Int32[3];
Points_i.depth = new Int32[3];
Points_i.scaleFactor = new Int32[3];
Points_i.x[0] = -100;
Points_i.y[0] = -100;
Points_i.z[0] = -100;
Points_i.x[1] = 100;
Points_i.y[1] = -100;
Points_i.z[1] = -100;
Points_i.x[2] = 0;
Points_i.y[2] = 100;
Points_i.z[2] = -100;
Points_i.focalLength = 1000;
Points_i.length = 3;
Int16 rx = (Int16)trackBar1.Value;
Int16 ry = (Int16)trackBar2.Value;
Int16 rz = (Int16)trackBar3.Value;
Int16 d = (Int16)trackBar4.Value;
Points_i.depthScale = d;
paint_3d_triangle_i(Points_i, (Int16)256, (Int16)256, (Int16)(rx), (Int16)(ry), (Int16)(rz));
}
private void Form1_Load(object sender, EventArgs e)
{
for (UInt16 cnt = 0; cnt < sin.Count(); cnt++)
{
sin[cnt] = (Int16)(Math.Sin(Math.PI * cnt / 180) * 32767);
}
for (UInt16 cnt = 0; cnt < cos.Count(); cnt++)
{
cos[cnt] = (Int16)(Math.Cos(Math.PI * cnt / 180) * 32767);
}
drawTriangle_i();
}
private void trackBar1_Scroll(object sender, EventArgs e)
{
drawTriangle_i();
}
private void trackBar2_Scroll(object sender, EventArgs e)
{
drawTriangle_i();
}
private void trackBar3_Scroll(object sender, EventArgs e)
{
drawTriangle_i();
}
private void trackBar4_Scroll(object sender, EventArgs e)
{
drawTriangle_i();
}
}
}
For double the X, Y, Z track bars need to have the range from - P*1000 to PI * 1000.
For integers the X, Y, Z track bars need to have the range from 0 to 360,
The sin and cos tables have the first 91 degrees sin and cos results in UInt16 representation.
The angle increment is 1 degree, but the affinity can be increased or decreased as needed.
This example is for a triangle, but can be implemented all combinations of lines, squares rectangles, hexagons, because the main function is working with points in space.