## 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:

// 3DFunctionDlg.h : header file
//
#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 // _MSC_VER > 1000
#include "3DStatic.h"
///////////////////////////////////////////////////////////
// C2DFunctionDlg dialog
class CMy3DPageDlg : public CDialog
{
// Construction
public:
CMy3DPageDlg(CWnd* pParent = NULL); // standard constructor
// Dialog Data
//{{AFX_DATA(CMy3DPageDlg)
enum { IDD = IDD_MY3DPage_DIALOG };
C3DStatic m_MyStatic; //We change it from CStatic m_MyStatic;
//to C3DStatic m_MyStatic;
//}}AFX_DATA
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CMy3DPageDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX);
// DDX/DDV support
//}}AFX_VIRTUAL
// Implementation
protected:
HICON m_hIcon;
// Generated message map functions
//{{AFX_MSG(C2DFunctionDlg)
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations
// immediately before the previous line.
#endif // !defined(AFX_My3DPageDLG_H__D5D048D5_079A_40BD_
//86A0_32A26253D2E5__INCLUDED_)

## 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)
{
// x=lenght ; in t Direction |P2 Q|
// y=b ; in X Direction |OP1|
// z=c ; in Y Direction |P1 P2|
// newX=b-+lcos(isteep)
// newY=newX*tan(isteep)+c-b*tan(isteep)
// so...
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)
{
// x=l
// y=A
// z=B
// newX=A-+lcos(isteep)
// newY=newX*tan(isteep)+B-A*tan(isteep)
// so...
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.