Class TCubeSpin (unit Ccube) |
Inherits from
TPanel
Constructor Create(AOwner:TComponent);
- ReleaseDC(0, ScreenDC);
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.
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
event OnSpin : TNotifyEvent
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
Constructor Create(AOwner:TComponent);
ReleaseDC(0, ScreenDC);
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 ])); (
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
event OnSpin : TNotifyEvent
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