Introduction
I wanted to draw a 3D math function on a static control like "MatLab" does. Finally, I found a solution.
Consider that you want to plot a 3D Pixel Q (a,b,c) on a plane with X and Y directions. In (figure 1), I illustrate this. At first, you must move in X direction from O to P1. It is important to know |P1 O| = b. Then you must move in Y direction from P1 to P2 and |P2 P1| = c. Now you must move in 't' direction from P2 to Q. And |Q P2 | = a. In 3D space, 't' direction is X-axis, X direction is Y-axis, and Y direction is Z-axis.
Figure 1 � Plot a 3D Point on a 2D page.
We want to obtain Q(x,y), so �
Eq. 1
Using the code
First of all, add 3DStatic.h and 3DStatic.cpp files to your project. Select Resource tab from Workspace window, and select your dialog that you want to add a display static. Select Static Control from Control toolbox and draw it on the dialog (Figure 2). Change its ID from IDC_STATIC
to IDC_MyStatic
.
Figure 2 - Add Static and Button Control to your dialog.
Now it's time to add a member variable to your dialog class. Call Class Wizard to do it for you. Figure 3 shows you how to do it. In this case, we add a member variable m_MyStatic
with type CStatic
.
Figure 3 - Add member variable to your dialog class.
OK, open your dialog class header file, add this line on top of your class definition:
#if !defined(AFX_2DFUNCTIONDLG_H__D5D048D5_079A_
40BD_86A0_32A26253D2E5__INCLUDED_)
#define AFX_2DFUNCTIONDLG_H__D5D048D5_079A_40BD_
86A0_32A26253D2E5__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif
#include "3DStatic.h"
class CMy3DPageDlg : public CDialog
{
public:
CMy3DPageDlg(CWnd* pParent = NULL);
enum { IDD = IDD_MY3DPage_DIALOG };
C3DStatic m_MyStatic;
protected:
virtual void DoDataExchange(CDataExchange* pDX);
protected:
HICON m_hIcon;
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
DECLARE_MESSAGE_MAP()
};
#endif
How does it work?
One of the important functions is Set3DPixel
.This function changes a point in 3D space to a 2D point. It is a polymorphism function and in its second form, it plots the obtained point on static. It uses equation 1.
CPoint C3DStatic::Set3DPixel(C3DStatic::C3DPoint pPixel)
{
CPoint newP;
float newX,newY;
CString aa;
newX=pPixel.y-pPixel.x*cos(iSteep);
newY=newX*tan(iSteep)+(pPixel.z-pPixel.y*tan(iSteep));
crColor=Set3DColor(pPixel.z);
if ((newX<=pPixel.y && pPixel.x>=0) || (newX>=pPixel.y && pPixel.x<0))
{
newP=GetNewPixel(newX,newY);
return newP;
}
else
{
newX=pPixel.y+pPixel.x*cos(iSteep);
newY=newX*tan(iSteep)+(pPixel.z-pPixel.y*tan(iSteep));
if ((newX<=pPixel.y && pPixel.x>=0) || (newX>=pPixel.y &&
pPixel.x<0))
{
newP=GetNewPixel(newX,newY);
return newP;
}
}
C3DStatic::Set3DPixel(CDC* pDC,C3DStatic::C3DPoint pPixel,COLORREF crColor)
{
CPoint newP,oldP;
float newX,newY;
CString aa;
CPen pen (PS_SOLID, 1, crColor);
pDC->SelectObject (&pen);
newX=pPixel.y-pPixel.x*cos(iSteep);
newY=newX*tan(iSteep)+(pPixel.z-pPixel.y*tan(iSteep));
if ((newX<=pPixel.y && pPixel.x>=0) || (newX>=pPixel.y && pPixel.x<0))
{
newP=GetNewPixel(newX,newY);
if ((newP.x>=0 && newP.y<=0) && (newP.x<=stcWidth && newP.y>=-stcHeight) )
pDC->SetPixel(newP,crColor);
}
else
{
newX=pPixel.y+pPixel.x*cos(iSteep);
newY=newX*tan(iSteep)+(pPixel.z-pPixel.y*tan(iSteep));
if ((newX<=pPixel.y && pPixel.x>=0) || (newX>=pPixel.y && pPixel.x<0))
{
newP=GetNewPixel(newX,newY);
if ((newP.x>=0 && newP.y<=0) &&
(newP.x<=stcWidth && newP.y>=-stcHeight))
pDC->SetPixel(newP,crColor);
}
}
}
C3DPoint
is a structure with three member variables x
, y
, z
to get a point in three dimensions.
struct C3DPoint
{
float x;
float y;
float z;
};
The next function is SetXSteep
: this function sets a steep for X axis in 3D space. iSteep
must be set before using the function Set3DPixel
. The next function is GetNewPixel
:
CPoint C3DStatic::GetNewPixel(float Oldx,float Oldy)
{
CPoint NewPoint;
NewPoint.x=int((stcWidth/(x_Max-x_Min))*Oldx-((stcWidth*x_Min)/(x_Max- x_Min)));
NewPoint.y=int(stcHeight/(y_Max-y_Min)*Oldy-(stcHeight*y_Max)/(y_Max- y_Min));
return NewPoint;
}
This function gets a CPoint
variable in the Static scale. The static maximum and minimum value can be obtained by using this code:
CRect rect;
GetClientRect (&rect);
stcWidth=rect.Width();
stcHeight=rect.Height();
In the constructor function, we can set a virtual scale for static width by defining x_Max
and x_Min
, and for static height by defining y_Max
and y_Min
. Note that variable Oldx
must be between x_Min
and y_Min
and similarly for Oldy
.
The most important part of the code is below:
for (j=y_Min;j<=y_Max;j+=(x_Max-x_Min)/500)
for (i=x_Min;i<=x_Max;i+=(x_Max-x_Min)/500)
{
_3DPixel.z=MathFunc(j,i);
if (_3DPixel.z>=MAXp) MAXp=_3DPixel.z;
if (_3DPixel.z<=MINp) MINp=_3DPixel.z;
}
SetHighLowColor(MAX,MIN);
for (j=y_Min+.5;j<=y_Max;j+=(y_Max-y_Min)/2000)
for (i=x_Min+.5;i<=x_Max;i+=(x_Max-x_Min)/2000)
{
_3DPixel.y=i;
_3DPixel.x=j;
_3DPixel.z=MathFunc(j,i);
crColor=Set3DColor(_3DPixel.z);
Set3DPixel(&dc,_3DPixel,crColor);
}
Function MathFunc
is a math function. In this example, we have:
float C3DStatic::MathFunc(float x,float y)
{
return 2*sin(x*y/3);
}
The two next functions are SetHighLowColor
and Set3DColor
. The first attributes maximum value of MyFunc (MAXp)
to pure red color and minimum value of MyFunc (MINp)
to pure blue color. The second one sets a color for a given value of Mathfunc
. Finally using Set3DPixel
, we plot a pixel on the static.
Have fun.