Create a file to contain the main application named user_name.cpp to solve any numerical plotting problem as follows:
/*----------------------------------------------------------------------------*
** This is the main windows file for creating the numerical plotter used in
** the tuturial.
**
** To build this in Visual C++ 6.0 perform the following steps:
**
** 1. Select New from File menu.
**
** 2. In the New dialog box select "Win32 Application" and enter
** a project name. Then press the "Ok" button.
**
** 3. In the Win32 Application dialog box select "A simple Win32 Application"
** then press the "Finish" button.
**
** 4. Press "Ok" in the New Project Information dialog box.
**
** 5. Open the *.cpp file for entry of the program. (We do not edit StdAfx.h
** or StdAfx.cpp; VC++ requires these files.)
*/
#include "stdafx.h"
#include <math.h>
#include <string.h>
#include <stdio.h>
#include <windowsx.h>
#include "..\plotter\plotter.h"
#include "..\vector\vector.h"
plotter np( "Plotter", "Numerical Plotter" );
/*----------------------------------------------------------------------------*
** "WinMain" is the program entry point
**
** returns: message parameter
*/
int APIENTRY WinMain( HINSTANCE inst, // window instance
HINSTANCE prev, // previous window
LPSTR cmdl, // command line string
int show ) // initial display
{
np.initialize( inst );
if( !np.window_class() )
return 0;
np.create( show );
return np.message_loop();
}
/*----------------------------------------------------------------------------*
** "WindowFunc" processes messages to this application
**
** returns: 0 if message processed, else default value
*/
LRESULT CALLBACK WindowFunc( HWND hwnd, // handle to window of message
UINT msg, // message code
WPARAM wp, // short message amplifier
LPARAM lp ) // long message amplifier
{
switch( msg )
{
case WM_CREATE:
np.on_create( hwnd );
break;
case WM_DESTROY:
np.on_destroy();
break;
case WM_SIZE:
np.on_size( hwnd );
break;
case WM_HSCROLL:
np.on_hscroll( hwnd, wp );
break;
case WM_VSCROLL:
np.on_vscroll( hwnd, wp );
break;
case WM_PAINT:
np.on_paint( hwnd );
break;
case WM_COMMAND:
np.on_command( hwnd, wp );
break;
default:
return DefWindowProc( hwnd, msg, wp, lp );
}
return 0;
}
/*----------------------------------------------------------------------------*\
** Put your mathematical functions to plot here. **
\*----------------------------------------------------------------------------*/
.
.
.
This is the source file plotter.h which declares the plotter class
#define ID_FILE_SAVE 40001
#define ID_FILE_EXIT 40002
#define ID_PROGRAM_RUN 40011
#define ID_PROGRAM_RESET 40012
#define ID_HELP_ABOUT 40021
class plotter
{
public:
plotter( char *, char * );
void initialize( HINSTANCE );
void create( int );
bool window_class();
int message_loop();
void on_command( HWND, WPARAM );
void on_create( HWND );
void on_destroy();
void on_hscroll( HWND, WPARAM );
void on_paint( HWND );
void on_size( HWND );
void on_vscroll( HWND, WPARAM );
void draw_axes();
void plot_region( double, double, double, double );
void red_curve( POINT *, int );
void green_curve( POINT *, int );
void blue_curve( POINT *, int );
POINT plot_point( double, double );
private:
void about( HWND );
int file_save_dlg( HWND, PSTR, PSTR );
BOOL save_bitmap( HDC, HBITMAP, PSTR );
HMENU menu();
HINSTANCE hInstance; // instance of this program
char class_name[64];
char plotter_name[64];
RECT current; // current client dimensions
HDC hMemoryDC; // handle of memory DC
HBITMAP hbit; // handle of compatible bitmap
HPEN hOldpen; // handle of saved drawing pen
HPEN hRedpen, hGreenpen, hBluepen; // drawing pens
int X_max_pixels, Y_max_pixels; // full screen dimensions
int X_org, Y_org; // origin for painting bitmap
int X0_pixel, Y0_pixel; // coordinate origin in pixels
double dX, dY; // pixels per unit
bool plot_region_defined; // when true, plot region setup
};
LRESULT CALLBACK WindowFunc( HWND, UINT, WPARAM, LPARAM );
This is the source file plotter.cpp which defines methods for the plotter class
#include "stdafx.h"
#include "plotter.h"
#include <assert.h>
#include <commdlg.h>
#include <stdlib.h>
void run(); // user defined function
/*----------------------------------------------------------------------------*
** "plotter" constructor sets class and application names.
**
** returns: nothing
*/
plotter::plotter( char *cn, char *an )
{
strcpy( class_name, cn );
strcpy( plotter_name, an );
// initial origin for painting bitmap
X_org = 0;
Y_org = 0;
plot_region_defined = false;
}
/*----------------------------------------------------------------------------*
** "initialize" stores the instance of the application.
**
** returns: nothing
*/
void plotter::initialize( HINSTANCE inst )
{
hInstance = inst;
}
/*----------------------------------------------------------------------------*
** "window_class" defines and registers a window class for the application.
**
** returns: true on success
*/
bool plotter::window_class()
{
WNDCLASS wcl;
// define the window class
wcl.hInstance = hInstance;
wcl.lpszClassName = class_name;
wcl.lpfnWndProc = WindowFunc;
wcl.style = 0;
wcl.hIcon = LoadIcon( hInstance, IDI_WINLOGO );
wcl.hCursor = LoadCursor( NULL, IDC_ARROW );
wcl.lpszMenuName = 0;
wcl.cbClsExtra = 0;
wcl.cbWndExtra = 0;
wcl.hbrBackground = (HBRUSH)GetStockObject( WHITE_BRUSH );
if( !RegisterClass( &wcl ) )
return false;
return true;
}
/*----------------------------------------------------------------------------*
** "menu" defines the initial application menu
** displayed at the top of the window
**
** returns: handle to menu
*/
HMENU plotter::menu()
{
HMENU hMenu = CreateMenu();
HMENU hMenuPopup = CreateMenu();
AppendMenu( hMenuPopup, MF_STRING, ID_FILE_SAVE, "&Save" );
AppendMenu( hMenuPopup, MF_STRING, ID_FILE_EXIT, "E&xit" );
AppendMenu( hMenu, MF_POPUP, (UINT)hMenuPopup, "&File" );
hMenuPopup = CreateMenu();
AppendMenu( hMenuPopup, MF_STRING, ID_PROGRAM_RUN, "&Run" );
AppendMenu( hMenuPopup, MF_STRING, ID_PROGRAM_RESET, "Re&set" );
AppendMenu( hMenu, MF_POPUP, (UINT)hMenuPopup, "&Program" );
hMenuPopup = CreateMenu();
AppendMenu( hMenuPopup, MF_STRING, ID_HELP_ABOUT, "&About" );
AppendMenu( hMenu, MF_POPUP, (UINT)hMenuPopup, "&Help" );
return hMenu;
}
/*----------------------------------------------------------------------------*
** "create" creates and opens a window for the application.
**
** returns: nothing
*/
void plotter::create( int open )
{
HWND hwnd;
hwnd = CreateWindow( class_name, // class name
plotter_name, // name of this program
WS_OVERLAPPEDWINDOW | // normal window
WS_HSCROLL | // with horizontal and
WS_VSCROLL, // vertical scroll bars
CW_USEDEFAULT, // x - origin
CW_USEDEFAULT, // y - origin
CW_USEDEFAULT, // width
CW_USEDEFAULT, // height
HWND_DESKTOP, // no parent
menu(), // initial menu
hInstance, // this
NULL ); // no arguments
ShowWindow( hwnd, open );
UpdateWindow( hwnd );
}
/*----------------------------------------------------------------------------*
** "about" generates the about box for this application.
**
** returns: nothing
*/
void plotter::about( HWND hwnd )
{
MSGBOXPARAMS ab;
ab.cbSize = sizeof( MSGBOXPARAMS );
ab.hwndOwner = hwnd;
ab.hInstance = hInstance;
ab.lpszCaption = "About";
ab.lpszText = "Numerical Plotter, version 1.0 - January 2000\n\n"
"Stephen R. Schmitt\n";
ab.dwStyle = MB_OK ;
ab.lpszIcon = NULL;
ab.dwContextHelpId = 0;
ab.lpfnMsgBoxCallback = 0;
ab.dwLanguageId = 0;
MessageBoxIndirect( &ab );
}
/*----------------------------------------------------------------------------*
** "message_loop" processes messages received by the window
**
** returns: last message wParam
*/
int plotter::message_loop()
{
MSG msg;
// the message loop
while( GetMessage( &msg, NULL, 0, 0 ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
// all done
return msg.wParam;
}
/*----------------------------------------------------------------------------*
** "on_create" initializes the graphics parameters for the application.
**
** returns: nothing
*/
void plotter::on_create( HWND hwnd )
{
HDC hdc = GetDC( hwnd );
// get screen size
X_max_pixels = GetSystemMetrics( SM_CXSCREEN );
Y_max_pixels = GetSystemMetrics( SM_CYSCREEN );
// make a compatible bitmap
hMemoryDC = CreateCompatibleDC( hdc );
hbit = CreateCompatibleBitmap( hdc, X_max_pixels, Y_max_pixels );
SelectObject( hMemoryDC, hbit );
SelectObject( hMemoryDC, (HBRUSH)GetStockObject( WHITE_BRUSH ) );
PatBlt( hMemoryDC, 0, 0, X_max_pixels, Y_max_pixels, PATCOPY );
// create drawing pens
hRedpen = CreatePen( PS_SOLID, 1, RGB( 255, 0, 0 ) );
hGreenpen = CreatePen( PS_SOLID, 1, RGB( 0, 255, 0 ) );
hBluepen = CreatePen( PS_SOLID, 1, RGB( 0, 0, 255 ) );
// save default pen
hOldpen = (HPEN)SelectObject( hMemoryDC, hRedpen );
SelectObject( hMemoryDC, hOldpen );
ReleaseDC( hwnd, hdc );
}
/*----------------------------------------------------------------------------*
** "on_destroy" frees the graphics resource allocated in on_create() and
** ends program.
**
** returns: nothing
*/
void plotter::on_destroy()
{
DeleteObject( hRedpen );
DeleteObject( hGreenpen );
DeleteObject( hBluepen );
DeleteDC( hMemoryDC );
// end the program
PostQuitMessage( 0 );
}
/*----------------------------------------------------------------------------*
** "on_size" sets the scroll bar parameters.
**
** returns: nothing
*/
void plotter::on_size( HWND hwnd )
{
SCROLLINFO si;
// get current client dimensions and reset origin
GetClientRect( hwnd, ¤t );
while( X_org + current.right > X_max_pixels )
X_org--;
while( Y_org + current.bottom > Y_max_pixels )
Y_org--;
// reset scroll bars
si.cbSize = sizeof( SCROLLINFO );
si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
si.nMin = 0;
si.nMax = X_max_pixels;
si.nPos = X_org;
si.nPage = current.right;
SetScrollInfo( hwnd, SB_HORZ, &si, TRUE );
si.nMax = Y_max_pixels;
si.nPos = Y_org;
si.nPage = current.bottom;
SetScrollInfo( hwnd, SB_VERT, &si, TRUE );
InvalidateRect( hwnd, NULL, FALSE );
}
/*----------------------------------------------------------------------------*
** "on_hscroll" scrolls the client area of the application horizontally.
**
** returns: nothing
*/
void plotter::on_hscroll( HWND hwnd, WPARAM wParam )
{
SCROLLINFO si;
switch( LOWORD( wParam ) )
{
case SB_THUMBTRACK:
X_org = HIWORD( wParam );
break;
case SB_LINERIGHT:
if( X_org < X_max_pixels - current.right )
X_org++;
break;
case SB_PAGERIGHT:
if( X_org + 8 < X_max_pixels - current.right )
X_org += 8;
break;
case SB_LINELEFT:
if( X_org > 0 )
X_org--;
break;
case SB_PAGELEFT:
if( X_org - 8 > 0 )
X_org -= 8;
break;
}
si.cbSize = sizeof( SCROLLINFO );
si.fMask = SIF_POS;
si.nMin = 0;
si.nMax = X_max_pixels;
si.nPos = X_org;
si.nPage = current.right;
SetScrollInfo( hwnd, SB_HORZ, &si, TRUE );
InvalidateRect( hwnd, NULL, FALSE ); // force repaint
}
/*----------------------------------------------------------------------------*
** "on_vscroll" scrolls the client area of the application vertically.
**
** returns: nothing
*/
void plotter::on_vscroll( HWND hwnd, WPARAM wParam )
{
SCROLLINFO si; // scroll bar information
switch( LOWORD( wParam ) )
{
case SB_THUMBTRACK:
Y_org = HIWORD( wParam );
break;
case SB_LINEDOWN:
if( Y_org < Y_max_pixels - current.bottom )
Y_org++;
break;
case SB_PAGEDOWN:
if( Y_org + 8 < Y_max_pixels - current.bottom )
Y_org += 8;
break;
case SB_LINEUP:
if( Y_org > 0 )
Y_org--;
break;
case SB_PAGEUP:
if( Y_org - 8 > 0 )
Y_org -= 8;
break;
}
si.cbSize = sizeof( SCROLLINFO );
si.fMask = SIF_POS;
si.nMin = 0;
si.nMax = Y_max_pixels;
si.nPos = Y_org;
si.nPage = current.bottom;
SetScrollInfo( hwnd, SB_VERT, &si, TRUE );
InvalidateRect( hwnd, NULL, FALSE ); // force repaint
}
/*----------------------------------------------------------------------------*
** "on_paint" repaints the client area of the application by copying the
** memory device into the window device.
**
** returns: nothing
*/
void plotter::on_paint( HWND hwnd )
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint( hwnd, &ps );
BitBlt( hdc, // destination
ps.rcPaint.left,
ps.rcPaint.top,
ps.rcPaint.right - ps.rcPaint.left,
ps.rcPaint.bottom - ps.rcPaint.top,
hMemoryDC, // source
ps.rcPaint.left + X_org,
ps.rcPaint.top + Y_org,
SRCCOPY );
EndPaint( hwnd, &ps );
}
/*----------------------------------------------------------------------------*
** "on_command" executes user commands for the application.
**
** returns: nothing
*/
void plotter::on_command( HWND hwnd, WPARAM wParam )
{
char path[_MAX_PATH];
char name[_MAX_FNAME + _MAX_EXT];
switch( LOWORD( wParam ) )
{
case ID_FILE_SAVE:
path[0] = 0;
name[0] = 0;
if( file_save_dlg( hwnd, path, name ) )
{
if( !save_bitmap( hMemoryDC, hbit, path ) )
MessageBox( hwnd, "Could not save bitmap", NULL, MB_OK );
}
break;
case ID_FILE_EXIT:
SendMessage( hwnd, WM_CLOSE, 0, 0 );
break;
case ID_PROGRAM_RUN:
run();
InvalidateRect( hwnd, NULL, FALSE );
break;
case ID_PROGRAM_RESET:
PatBlt( hMemoryDC, 0, 0, X_max_pixels, Y_max_pixels, PATCOPY );
InvalidateRect( hwnd, NULL, FALSE );
break;
case ID_HELP_ABOUT:
about( hwnd );
break;
}
}
/*------------------------------------------------------------------------*
** "file_save_dlg" creates a file save dialog box.
**
** returns: non-zero on OK, else zero
*/
int plotter::file_save_dlg( HWND hwnd, PSTR file, PSTR title )
{
OPENFILENAME dlg;
static char filter[] = "Bitmap Files (*.bmp)\0*.bmp\0\0";
dlg.lStructSize = sizeof( OPENFILENAME );
dlg.hwndOwner = hwnd;
dlg.hInstance = NULL;
dlg.lpstrFilter = filter;
dlg.lpstrCustomFilter = NULL;
dlg.nMaxCustFilter = 0;
dlg.nFilterIndex = 0;
dlg.lpstrFile = file;
dlg.nMaxFile = _MAX_PATH;
dlg.lpstrFileTitle = title;
dlg.nMaxFileTitle = _MAX_FNAME + _MAX_EXT;
dlg.lpstrInitialDir = NULL;
dlg.lpstrTitle = NULL;
dlg.Flags = OFN_OVERWRITEPROMPT;
dlg.nFileOffset = 0;
dlg.nFileExtension = 0;
dlg.lpstrDefExt = "bmp";
dlg.lCustData = 0L;
dlg.lpfnHook = NULL;
dlg.lpTemplateName = NULL;
return GetSaveFileName( &dlg );
}
/*----------------------------------------------------------------------------*
** "save_bitmap" converts a device dependent bitmat into a device
** independant bitmap and saves in *.bmp format.
**
** returns: TRUE on success
*/
BOOL plotter::save_bitmap( HDC hdc, HBITMAP bitmap, PSTR file_name )
{
int file;
OFSTRUCT ofs;
HBITMAP temp_bmp, save_bmp;
BITMAPFILEHEADER bmp_header;
BITMAPINFO *pbmi, bmi;
void *image;
unsigned long bmp_info_size;
BOOL ok = TRUE;
if( !bitmap )
return FALSE;
// GetDIBits uses the size to determine if it's BITMAPCOREINFO or BITMAPINFO
bmi.bmiHeader.biSize = 40; // size of BITMAPINFO
bmi.bmiHeader.biBitCount = 0; // don't get the color table
if( ( GetDIBits( hdc, // handle to device context
bitmap, // handle to bitmap
0, // first scan line in destination bitmap
0, // number of scan lines to copy
(LPSTR)NULL, // address of array for bitmap bits
&bmi, // address of structure with bitmap data
DIB_RGB_COLORS ) ) == 0 ) // RGB or palette index
return FALSE;
// allocate enough memory to hold the actual bits of the image
image = GlobalAlloc( GMEM_FIXED | GMEM_ZEROINIT, bmi.bmiHeader.biSizeImage );
if( image == NULL)
return FALSE;
// save pointer to bitmap information header
pbmi = &bmi;
// 24 bits per pixel requires no color table, assume no color table
bmp_info_size = sizeof( BITMAPINFOHEADER );
switch( bmi.bmiHeader.biBitCount )
{
case 24: // has no color table
break;
case 16:
case 32:
bmp_info_size += sizeof( DWORD ) * 3;
break;
default:
bmp_info_size += sizeof( RGBQUAD ) * ( 1 << bmi.bmiHeader.biBitCount );
break;
}
// Allocate memory for color table if it is not 24 bits per pixel
if( bmp_info_size != sizeof( BITMAPINFOHEADER ) )
{
// get more memory for the color table
pbmi = (PBITMAPINFO)GlobalAlloc( GMEM_FIXED | GMEM_ZEROINIT, bmp_info_size );
if( pbmi == NULL )
{
ok = FALSE;
goto no_memory_for_color_table;
}
// copy the bitmap info header data into the allocated space
memmove( pbmi, &bmi, sizeof( BITMAPINFOHEADER ) );
}
// open the output file and get ready for writing
file = OpenFile( file_name, &ofs, OF_CREATE | OF_WRITE );
if( file == -1 )
{
ok = FALSE;
goto no_file_open;
}
// fill in the info for the bmp file header
bmp_header.bfType = 0x4D42; // 'BM'
bmp_header.bfSize = sizeof( BITMAPFILEHEADER );
bmp_header.bfSize += sizeof( BITMAPINFOHEADER );
bmp_header.bfSize += bmp_info_size + pbmi->bmiHeader.biSizeImage;
bmp_header.bfReserved1 = 0;
bmp_header.bfReserved2 = 0;
bmp_header.bfOffBits = sizeof( BITMAPFILEHEADER ) + bmp_info_size;
// write the file header to disk
if( _lwrite( file, (LPSTR)&bmp_header, sizeof( BITMAPFILEHEADER ) ) == -1 )
{
ok = FALSE;
goto no_bitmap;
}
// Since the bitmap can't be selected into a DC when calling GetDIBits,
// assume that the hdc is the DC where the bitmap would have been selected
temp_bmp = CreateCompatibleBitmap( hdc,
pbmi->bmiHeader.biWidth,
pbmi->bmiHeader.biHeight );
if( temp_bmp )
{
save_bmp = ( HBITMAP )SelectObject( hdc, temp_bmp );
if( !GetDIBits( hdc,
bitmap,
0,
pbmi->bmiHeader.biHeight,
image,
pbmi,
DIB_RGB_COLORS ) )
{
ok = FALSE;
goto no_file_write;
}
}
else
{
ok = FALSE;
goto no_bitmap;
}
// Now write out the BitmapInfoHeader and color table, if any
if( _lwrite( file, (char *)pbmi, bmp_info_size ) == -1 )
{
ok = FALSE;
goto no_file_write;
}
// write the bits also
if( _lwrite( file, (char *)image, pbmi->bmiHeader.biSizeImage ) == -1 )
{
ok = FALSE;
goto no_file_write;
}
no_file_write:
SelectObject( hdc, save_bmp );
DeleteObject( temp_bmp );
no_bitmap:
_lclose( file );
no_file_open:
GlobalFree( pbmi );
no_memory_for_color_table:
GlobalFree( image );
return ok;
}
/*----------------------------------------------------------------------------*
** "plot_region" calculates parameters for the plotting area of the screen.
**
** returns: nothing
*/
void plotter::plot_region( double left, // left of origin is negative
double right, // right of origin is positive
double top, // above origin is positive
double bottom ) // below origin is negative
{
// pixels per unit X and Y
dX = X_max_pixels / ( right - left );
dY = Y_max_pixels / ( top - bottom );
// location of pixel at coordinate origin
X0_pixel = int( -left * dX );
Y0_pixel = int( +top * dY );
plot_region_defined = true;
}
/*----------------------------------------------------------------------------*
** "draw_axes" puts the x, y coordinate axes on the screen.
**
** returns: nothing
*/
void plotter::draw_axes()
{
int x, y, z;
for( y = 0; y < Y_max_pixels; y++ )
{
SetPixel( hMemoryDC, X0_pixel, y, RGB( 0, 0, 0 ) );
if( ( y - Y0_pixel ) % int(dY) == 0 )
{
for( z = -3; z <= 3; z++ )
SetPixel( hMemoryDC, X0_pixel + z, y, RGB( 0, 0, 0 ) );
}
}
for( x = 0; x < X_max_pixels; x++ )
{
SetPixel( hMemoryDC, x, Y0_pixel, RGB( 0, 0, 0 ) );
if( ( x - X0_pixel ) % int(dX) == 0 )
{
for( z = -3; z <= 3; z++ )
SetPixel( hMemoryDC, x, Y0_pixel + z, RGB( 0, 0, 0 ) );
}
}
}
/*----------------------------------------------------------------------------*
** "red_curve" draws a red curve on the screen.
**
** returns: nothing
*/
void plotter::red_curve( POINT *curve, int points )
{
hOldpen = (HPEN)SelectObject( hMemoryDC, hRedpen );
Polyline( hMemoryDC, curve, points );
SelectObject( hMemoryDC, hOldpen );
}
/*----------------------------------------------------------------------------*
** "green_curve" draws a green curve on the screen.
**
** returns: nothing
*/
void plotter::green_curve( POINT *curve, int points )
{
hOldpen = (HPEN)SelectObject( hMemoryDC, hGreenpen );
Polyline( hMemoryDC, curve, points );
SelectObject( hMemoryDC, hOldpen );
}
/*----------------------------------------------------------------------------*
** "blue_curve" draws a blue curve on the screen.
**
** returns: nothing
*/
void plotter::blue_curve( POINT *curve, int points )
{
hOldpen = (HPEN)SelectObject( hMemoryDC, hBluepen );
Polyline( hMemoryDC, curve, points );
SelectObject( hMemoryDC, hOldpen );
}
/*----------------------------------------------------------------------------*
** "plot_point" converts a mathematical point to be plotted into the
** screen coordinate frame.
**
** returns: the point in screen coordinates
*/
POINT plotter::plot_point( double x, double y )
{
POINT p;
assert( plot_region_defined );
p.x = int( ( X0_pixel + x * dX ) );
p.y = int( ( Y0_pixel - y * dY ) );
return p;
}