12,829,103 members (39,918 online)
alternative version

#### Stats

69.8K views
45 bookmarked
Posted 6 Jun 2005

# 3DStatic

, 6 Jun 2005
 Rate this:
An article to draw a 3D math function on a static control.

## 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`.

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`.

```// 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.

A list of licenses authors might use can be found here

## Share

 Web Developer Iran (Islamic Republic of)
I live in Iran . I started hardware programming when I was young. I designed and built some ISA cards for attaching to PC.like ocsiloscope and signal generator. Now I am working for a engineering company and Manager of some project.

## You may also be interested in...

 First Prev Next
 My 5 Amir Mahfoozi19-Dec-11 2:13 Amir Mahfoozi 19-Dec-11 2:13
 Again ... M. Bonvari9-Jun-05 4:48 M. Bonvari 9-Jun-05 4:48
 Re: Again ... asef10-Jun-05 18:41 asef 10-Jun-05 18:41
 The working is exemplary, ... WREY8-Jun-05 7:30 WREY 8-Jun-05 7:30
 Re: The working is exemplary, ... Anonymous8-Jun-05 20:53 Anonymous 8-Jun-05 20:53
 Super Georgi Petrov7-Jun-05 8:38 Georgi Petrov 7-Jun-05 8:38
 Re: Super asef7-Jun-05 18:48 asef 7-Jun-05 18:48
 Re: Super Anonymous7-Jun-05 21:28 Anonymous 7-Jun-05 21:28
 Re: Super asef8-Jun-05 0:38 asef 8-Jun-05 0:38
 Re: Super Rick York25-Jun-05 11:04 Rick York 25-Jun-05 11:04
 It's not terribly hard really. Draw the points from the front to back and keep track of what the highest point (smallest y coordinate) for every given x coordinate is. For each point if its y coordinate is below the highest value for that x coordinate then it is not visible so it is not drawn. I have used this approach in the past and it worked well.You have to determine how to traverse the grid according its rotation in order to draw front to back.One more thing - you can optimize many of your calculations by keeping values like tan(steep) and cos(steep) in temporary variables instead of calculating them for every pixel. I see many repeatedly computed values that do not change for every pixel. You could see a potentially large improvement in speed by doing this.BTW - I use the little class shown here : http://www.codeproject.com/script/comments/forums.asp?forumid=1647&fr=4476&df=4&select=1115048#xx1115048xx[^] to time things.
 Re: Super Georgi Petrov19-Jul-05 11:33 Georgi Petrov 19-Jul-05 11:33
 Re: Super asef22-Jul-05 20:37 asef 22-Jul-05 20:37
 Re: Super Georgi Petrov29-Jul-05 1:33 Georgi Petrov 29-Jul-05 1:33
 Excellent! A. Riazi6-Jun-05 22:03 A. Riazi 6-Jun-05 22:03
 Re: Excellent! asef6-Jun-05 22:07 asef 6-Jun-05 22:07
 Re: Excellent! Majid Shahabfar7-Jun-05 4:04 Majid Shahabfar 7-Jun-05 4:04
 Re: Excellent! asef7-Jun-05 18:42 asef 7-Jun-05 18:42
 Last Visit: 31-Dec-99 19:00     Last Update: 29-Mar-17 16:13 Refresh 1