Class TCubeSpin (unit Ccube)

Inherits from

TPanel

Constructors


Constructor Create(AOwner:TComponent);

ReleaseDC(0, ScreenDC);


Functions

Destructor Destroy;

SetSize;

Procedure SetAngles( AX,AY,AZ :Integer);

Downscale Z-Axis values by this } { Create a bitmap to handle smooth redraws

procedure SetContinuous( newValue:Boolean);

Faces requires hidden line removal

procedure SetOptions( newValue :TCubeDispOpts);


procedure SetXSpin( newValue :Integer);

Request a Redraw

procedure SetYSpin( newValue :Integer);


procedure SetZSpin( newValue :Integer);


procedure Paint;

Protected declarations

Windows calls here to ask us to repaint


procedure BMPDraw;


procedure Connect(dc:HDC; V1,V2:Integer);

Draw a line from the last point to this point.

procedure DrawCube;

Draw the Sides

procedure DrawEdges(dc:HDC);

Come here to draw the cube by it's edges

procedure DrawFaces(c:TCanvas);

Come here to draw the cube by it's faces

procedure LinetoPt(dc:HDC; P:TPoint);

Does a GDI lineto using a point instead of X,Y coords

function MapPt(Vertex:Integer):TPoint;

Take a 3D Point (vertex #) and map it to the 2D screen

procedure MovetoPt(dc:HDC; P:TPoint);

Does a GDI moveto using a point instead of X,Y coords

function Rotate(P:TPoint; Rotation:Integer):TPoint;

Rotate a point by transforming it from rectangular to polar, adjusting the polar angle, and transforming it back.

function Rotate2D(P:TPoint; Rotation:Integer):TPoint;

2D Rotation via transform matrix: [ cos A, sin A, 0 ] general [ a b 0 ] [ -sin A, cos A, 0 ] [ c d 0 ] [ 0, 0, 0 ] [ tx ty 1 ] or X' := aX + bY + tx := (cos A)X + (sin A)Y; Y' := cX + dY + ty := (-sin A)X + (cos A)Y;

function Rotate3D(Const P:TPoint3D):TPoint3D;

Do a 3D rotate by performing 2D rotates around each axis.

function Rotate3D2(Const P:TPoint3D):TPoint3D;

Do a 3D rotate by using a 3D transformation Matrix: [ cos Az * cos Ay, sin Az, -sin Ay, 0 ] general [ a b c 0 ] [ -sin Az, cos Az * cos Ax, sin Ax, 0 ] [ d e f 0 ] [ sin Ay, -sin Ax, cos Ax * cos Ay, 0 ] [ g h i 0 ] [ 0, 0, 0, 1 ] [ tx ty tz 1 ] or X' := aX + bY + cZ + tx := (cos Az)(cos Ay)X + (sin Az)Y - (sin Ay)Z; Y' := dX + eY + fZ + ty := (-sin Az)X + (cos Az)(cos Ax)Y + (sin Ax)Z; Z' := fX + gY + hZ + ty := (sin Ay)X -(sin Ax)Y + (cos Ax)(cos Ay)Z; [ cosY*cosZ cosY*-sinZ -sinY 0 ] [ -sinXsinY*cosZ+cosX*sinZ -sinXsinY*-sinZ+cosX*cosZ -sinXcosY 0 ] [ cosXsinY*cosZ+sinX*sinZ cosXsinY*-sinZ+sinX*cosZ cosXcosY 0 ] [ 0 0 0 1 ] or X' = cosAY*cosAZ*X - cosAY*sinAZ*Y - sinAY*Z Y' = (-sinAX*sinAY*cosAZ + cosAX*sinAZ)*X + (sinAX*sinAY*sinAZ+cosAX*cosAZ)Y - sinAX*cosAY*Z Z' = (cosAX*sinAY*cosAZ+sinAX*sinAZ)*X + (-cosAX*sinAY*sinAZ+sinAX*cosAZ)Y + (cosAX*cosAY)Z

function Rotate3D3(Const P:TPoint3D):TPoint3D;

Precompute Transformation Matrix

Now use it!


procedure RotateCube;

Rotate the Cube to the correct angles, then Check for hidden line removal and process

procedure SetSize;

Builds a cube of the proper size, and allocates a bitmap of the correct size for double buffering

procedure TimerElapse(Sender: TObject);

Not our window that was resized

procedure WMEnterIdle(var Message: TWMEnterIdle);

Else Inherited;

procedure WMEraseBkgnd(var Message: TWMEraseBkgnd);

Suppress Windows normal background erasure.

procedure WMSize(var Message: TWMSize);

Trap the Windows message requesting our size change, let it, then recreate the bitmap drawing buffer, resize the cube, and request a redraw

Procedure XFormBld;

NewPt := Rotate3d(P); If (Abs(NewPt.

Properties

property Continuous : Boolean


property Options : TCubeDispOpts


property SpinInc : Integer


property XSpin : Integer

Published declarations

property XSpinOn : Boolean


property YSpin : Integer


property YSpinOn : Boolean


property ZSpin : Integer


property ZSpinOn : Boolean


Events

event OnSpin : TNotifyEvent


Variables

Probs : TStrings;

Public declarations

AngleX : Integer;


AngleY : Integer;

Y/Z Rotation about X

AngleZ : Integer;

X/Z Rotation about Y

BMPCanvas : TCanvas;

Cache the bitmap for redraw speed

CubeBMP : HBITMAP;

Last Drawn Vector

fContinuous : Boolean;


fForceErase : Boolean;


fOnSpin : TNotifyEvent;


fOptions : TCubeDispOpts;


fSpinInc : Integer;


fTimer : TTimer;

Updating Controls

fUpdating : Boolean;


fXSpinOn : Boolean;

X/Y Rotation about Z

fYSpinOn : Boolean;


fZSpinOn : Boolean;


HideV : Integer;


LastV : Integer;

Hidden vertex

Perspective : Integer;


ScaleMe : Integer;


Vertices : Array [0..7] of TPoint3d;

Private declarations

VRotated : Array [0..7] of TPoint3d;


XForm3d : Array [0..3,0..3] of Single;


XP : Integer;


YP : Integer;


ZScale : Integer;

Scaling up from 1


Constructors


Constructor Create(AOwner:TComponent);

ReleaseDC(0, ScreenDC);


Functions


Destructor Destroy;

SetSize;


Procedure SetAngles( AX,AY,AZ :Integer);

Downscale Z-Axis values by this } { Create a bitmap to handle smooth redraws


procedure SetContinuous( newValue:Boolean);

Faces requires hidden line removal


procedure SetOptions( newValue :TCubeDispOpts);


procedure SetXSpin( newValue :Integer);

Request a Redraw


procedure SetYSpin( newValue :Integer);


procedure SetZSpin( newValue :Integer);


procedure Paint;

Protected declarations

Windows calls here to ask us to repaint


procedure BMPDraw;


procedure Connect(dc:HDC; V1,V2:Integer);

Draw a line from the last point to this point. Also does 3D to 2D mapping and limited hidden line removal.


procedure DrawCube;

Draw the Sides


procedure DrawEdges(dc:HDC);

Come here to draw the cube by it's edges


procedure DrawFaces(c:TCanvas);

Come here to draw the cube by it's faces


procedure LinetoPt(dc:HDC; P:TPoint);

Does a GDI lineto using a point instead of X,Y coords


function MapPt(Vertex:Integer):TPoint;

Take a 3D Point (vertex #) and map it to the 2D screen


procedure MovetoPt(dc:HDC; P:TPoint);

Does a GDI moveto using a point instead of X,Y coords


function Rotate(P:TPoint; Rotation:Integer):TPoint;

Rotate a point by transforming it from rectangular to polar, adjusting the polar angle, and transforming it back. This was WAY too slow and was replaced with the 2D transformation matrix approach below


function Rotate2D(P:TPoint; Rotation:Integer):TPoint;

2D Rotation via transform matrix: [ cos A, sin A, 0 ] general [ a b 0 ] [ -sin A, cos A, 0 ] [ c d 0 ] [ 0, 0, 0 ] [ tx ty 1 ] or X' := aX + bY + tx := (cos A)X + (sin A)Y; Y' := cX + dY + ty := (-sin A)X + (cos A)Y;


function Rotate3D(Const P:TPoint3D):TPoint3D;

Do a 3D rotate by performing 2D rotates around each axis. This is probably much slower than using a 3D transformation Matrix, but I've been too lazy to figure out what a three-D transformation Matrix would look like.


function Rotate3D2(Const P:TPoint3D):TPoint3D;

Do a 3D rotate by using a 3D transformation Matrix: [ cos Az * cos Ay, sin Az, -sin Ay, 0 ] general [ a b c 0 ] [ -sin Az, cos Az * cos Ax, sin Ax, 0 ] [ d e f 0 ] [ sin Ay, -sin Ax, cos Ax * cos Ay, 0 ] [ g h i 0 ] [ 0, 0, 0, 1 ] [ tx ty tz 1 ] or X' := aX + bY + cZ + tx := (cos Az)(cos Ay)X + (sin Az)Y - (sin Ay)Z; Y' := dX + eY + fZ + ty := (-sin Az)X + (cos Az)(cos Ax)Y + (sin Ax)Z; Z' := fX + gY + hZ + ty := (sin Ay)X -(sin Ax)Y + (cos Ax)(cos Ay)Z; [ cosY*cosZ cosY*-sinZ -sinY 0 ] [ -sinXsinY*cosZ+cosX*sinZ -sinXsinY*-sinZ+cosX*cosZ -sinXcosY 0 ] [ cosXsinY*cosZ+sinX*sinZ cosXsinY*-sinZ+sinX*cosZ cosXcosY 0 ] [ 0 0 0 1 ] or X' = cosAY*cosAZ*X - cosAY*sinAZ*Y - sinAY*Z Y' = (-sinAX*sinAY*cosAZ + cosAX*sinAZ)*X + (sinAX*sinAY*sinAZ+cosAX*cosAZ)Y - sinAX*cosAY*Z Z' = (cosAX*sinAY*cosAZ+sinAX*sinAZ)*X + (-cosAX*sinAY*sinAZ+sinAX*cosAZ)Y + (cosAX*cosAY)Z


function Rotate3D3(Const P:TPoint3D):TPoint3D;

Precompute Transformation Matrix

Now use it!


procedure RotateCube;

Rotate the Cube to the correct angles, then Check for hidden line removal and process


procedure SetSize;

Builds a cube of the proper size, and allocates a bitmap of the correct size for double buffering


procedure TimerElapse(Sender: TObject);

Not our window that was resized


procedure WMEnterIdle(var Message: TWMEnterIdle);

Else Inherited;


procedure WMEraseBkgnd(var Message: TWMEraseBkgnd);

Suppress Windows normal background erasure. If we're double buffering, then we always paint the whole area, so erasing the background would cause unnecessary flicker. If we're not double buffering, then we let the normal erase occur (which DOES cause flicker), unless the user has turned off erasing.


procedure WMSize(var Message: TWMSize);

Trap the Windows message requesting our size change, let it, then recreate the bitmap drawing buffer, resize the cube, and request a redraw


Procedure XFormBld;

NewPt := Rotate3d(P); If (Abs(NewPt.X-Result.X)>2) or (Abs(NewPt.Y-Result.Y)>2) or (Abs(NewPt.Z-Result.Z)>2) Then { Raise Exception.Create( } Probs.Add( Format( 'No match on X %3d:%4d ->%4d %4d, Y %3d:%4d ->%4d %4d, Z %3d:%4d ->%4d %4d', [AngleX,P.X,Result.X,NewPt.X, AngleY,P.Y,Result.Y,NewPt.Y, AngleZ,P.Z,Result.Z,NewPt.Z ])); (


Properties


property Continuous : Boolean


property Options : TCubeDispOpts


property SpinInc : Integer


property XSpin : Integer

Published declarations


property XSpinOn : Boolean


property YSpin : Integer


property YSpinOn : Boolean


property ZSpin : Integer


property ZSpinOn : Boolean


Events


event OnSpin : TNotifyEvent


Variables


Probs : TStrings;

Public declarations


AngleX : Integer;


AngleY : Integer;

Y/Z Rotation about X


AngleZ : Integer;

X/Z Rotation about Y


BMPCanvas : TCanvas;

Cache the bitmap for redraw speed


CubeBMP : HBITMAP;

Last Drawn Vector


fContinuous : Boolean;


fForceErase : Boolean;


fOnSpin : TNotifyEvent;


fOptions : TCubeDispOpts;


fSpinInc : Integer;


fTimer : TTimer;

Updating Controls


fUpdating : Boolean;


fXSpinOn : Boolean;

X/Y Rotation about Z


fYSpinOn : Boolean;


fZSpinOn : Boolean;


HideV : Integer;


LastV : Integer;

Hidden vertex


Perspective : Integer;


ScaleMe : Integer;


Vertices : Array [0..7] of TPoint3d;

Private declarations


VRotated : Array [0..7] of TPoint3d;


XForm3d : Array [0..3,0..3] of Single;


XP : Integer;


YP : Integer;


ZScale : Integer;

Scaling up from 1