Re: PolyLine and autoscrolling canvas
HI Joe
switch (iGraphType)
****
Why is iGraphType not a parameter to this function? Depending on globa=
l state is bad
style
Because it's an attribute of the class object where later could draw
polylines or rectangles (vertical bars)...
int saveobject = dc.SaveDC();
****
Why? What's wrong with SaveDC/RestoreDC here?
****
Nothing here... .just to "release" objects like CPen
memDC.CreateCompatibleDC(&dc);
bBitmap.CreateCompatibleBitmap(&dc, r.Width(), r=
..Height());
****
Did you look at the size? Do you know how big this actually is? It =
must be the client
rectangle size, and the clip box is unrelated to this except by accident.
****
I forgot to change GetClipBox with GetClientRect....
anyway the r values are of the rectangle size of the custom control..
bOldBitmap = memDC.SelectObject(&bBitmap);
memDC.FillSolidRect(r, dc.GetBkColor());
memDC.SetMapMode(dc.GetMapMode());
memDC.SetWindowExt(dc.GetWindowExt());
memDC.SetViewportExt(dc.GetViewportExt());
****
Why did you not call PrepareDC on the memory DC? What good is it to ha=
ve an abstraction
if you ignore it?
****
memDC.SetWindowOrg(dc.GetWindowOrg());
memDC.SetViewportOrg(dc.GetViewportOrg());
if (bActivateGrid)
{
CPen qLinePen(PS_SOLID, 0, RGB(0=
,139,0));
memDC.SelectObject(qLinePen);
// Grid - Horizontal lines
for(int y = 0; y < r.bottom; y=
+= (int)12) {
/* scan y */
memDC.MoveTo(r.l=
eft, y);
ight, y);
} /* scan y */
// Grid - Vertical lines
for(int x = 0 - (int)gridOffse=
t; x <= r.right - (int)gridOffset; x
+= (int)12 ) {
/* scan x */
memDC.MoveTo(x, =
r.top);
r.bottom);
} /* scan x */
}
DoDrawing(memDC, r);
// Copy the offscreen bitmap onto the screen.
dc.BitBlt(r.left, r.top, r.Width(), r.Height(),
&memDC, r.left, r.top, SRCCOPY);
****
Since r is the clipbox, and usually has nothing to do with the client rec=
tangle except by
accident, using it as a reference point is erroneous. Note also that s=
ince you are moving
relative to logical coordinates of the dc, and you've remapped those, the=
BitBlt moves the
image to the lower right corner so it hangs off the right edge, e.g.,
+---+
| A|
+---+---+
| B |
+---+
(depending on your fonts, this image may or may not look right, but you j=
ust did a
transfer into position B, because 0,0 is at the bottom right)
Key here: you do NOT map the raw DC; you do NOT use the ClipBox as a size=
reference, and
you ONLY call PrepareDC on the memory DC.
So as Stephen said, I should avoid to map DC and directly map memDC
then re-map the memDC to MM_TEXT right before the BitBlt to set the
0,0 point origin at the original point at top-left so the bitblt would
be done in that position right?...
I tried that and I figured it doesn't appear at all, I mean it doesn't
even draw the black background rectangle on the form...
Here I paste the example class files I'm implementing:
**************************************************************************
************************ GRAPHCLASS.H ****************************
**************************************************************************
#pragma once
#include <vector>
using namespace std;
enum GRAPH_TYPE { GRAPH_BARS=0, GRAPH_LINES };
#define LOCAL_CLASSNAME _T("CGraphClassCtrl") // Window class name
// CGraphClass
class CGraphClass : public CWnd
{
DECLARE_DYNAMIC(CGraphClass)
// matrix of POINT for each element to represent
typedef vector<POINT> vectfPoints; // array representing points
values for each single source
typedef vector<vectfPoints> vectPoints; // array representing list
of sources having their POINT values
typedef vector<POINT> vect_to_draw;
typedef vector<vect_to_draw> vectToDraw;
public:
CGraphClass();
virtual ~CGraphClass();
// methods
public:
BOOL InitializeGraph(int maxsizearray, int maxinterval, GRAPH_TYPE
typegraph=GRAPH_LINES);
void AddLinesElement(UINT iIndexSource, LPCTSTR description, double
value);
// Attributes
public:
void set_GraphType(GRAPH_TYPE graphtype);
GRAPH_TYPE get_GraphType();
protected:
BOOL RegisterWindowClass();
protected:
int iGraphType;
int iYMaxInterval;
BOOL bActivateGrid;
UINT iTimerVal;
UINT nElapse;
double gridOffset;
BOOL bSetDraw;
int iSourceMaxSizeArray;
vectPoints vtPoints; // matrix of sources representing POINT
values
vectToDraw vtToDraw; // matrix of sources in POINT as logical
unit to draw
BOOL EnableTimer();
protected:
afx_msg void OnPaint();
afx_msg void OnTimer(UINT TimerVal);
afx_msg BOOL OnEraseBkgnd(CDC* pDC);
DECLARE_MESSAGE_MAP()
};
**************************************************************************
********************** GRAPHCLASS.CPP **************************
**************************************************************************
// GraphClass.cpp : implementation file
//
#include "stdafx.h"
#include "test_compatibledc.h"
#include "GraphClass.h"
#include <algorithm>
// CGraphClass
#define IDT_TIMER_0 ( WM_USER + 1000 )
IMPLEMENT_DYNAMIC(CGraphClass, CWnd)
CGraphClass::CGraphClass()
{
RegisterWindowClass();
}
CGraphClass::~CGraphClass()
{
}
// Register the window class if it has not already been registered.
BOOL CGraphClass::RegisterWindowClass()
{
WNDCLASS wndcls;
if (!(::GetClassInfo(AfxGetInstanceHandle(), LOCAL_CLASSNAME,
&wndcls)))
{
// otherwise we need to register a new class
wndcls.style = CS_DBLCLKS | CS_HREDRAW |
CS_VREDRAW;
wndcls.lpfnWndProc = ::DefWindowProc;
wndcls.cbClsExtra = wndcls.cbWndExtra = 0;
wndcls.hInstance = AfxGetInstanceHandle();
wndcls.hIcon = NULL;
wndcls.hCursor = AfxGetApp()-
LoadStandardCursor(IDC_ARROW);
wndcls.hbrBackground = (HBRUSH) (COLOR_3DFACE + 1);
wndcls.lpszMenuName = NULL;
wndcls.lpszClassName = LOCAL_CLASSNAME;
if (!AfxRegisterClass(&wndcls))
{
AfxThrowResourceException();
return FALSE;
}
}
return TRUE;
}
BEGIN_MESSAGE_MAP(CGraphClass, CWnd)
ON_WM_PAINT()
ON_WM_TIMER()
ON_WM_ERASEBKGND()
END_MESSAGE_MAP()
// CGraphClass message handlers
void CGraphClass::OnTimer(UINT TimerVal)
{
// ****** processing event ******
if (TimerVal == IDT_TIMER_0)
{
gridOffset++;
if (gridOffset > 12.5)
gridOffset = 0.0;
Invalidate(); // to call OnPaint()
UpdateWindow();
}
// call base class handler
CWnd::OnTimer(TimerVal);
}
void CGraphClass::OnPaint()
{
CPaintDC dc(this); // device context for painting
CRect r;
GetClientRect(&r);
dc.SetBkColor(RGB(0,0,0));
int saveobject = dc.SaveDC();
if(bSetDraw)
{
// ********** Background ***********
// Grid
CDC memDC;
CBitmap bBitmap; // Offscreen bitmap
CBitmap* bOldBitmap; // bitmap originally found in CMemDC
memDC.CreateCompatibleDC(&dc);
bBitmap.CreateCompatibleBitmap(&dc, r.Width(), r.Height());
bOldBitmap = memDC.SelectObject(&bBitmap);
memDC.SetMapMode(MM_ANISOTROPIC);
memDC.SetWindowExt(-r.Width(), iYMaxInterval);
memDC.SetViewportExt(r.Width(), -r.Height());
memDC.SetWindowOrg(r.Width(), 0);
memDC.SetViewportOrg(0, r.Height());
memDC.FillRect(r,
CBrush::FromHandle((HBRUSH)GetStockObject(BLACK_BRUSH)));
if (bActivateGrid)
{
CPen qLinePen(PS_SOLID, 1, RGB(0,139,0));
memDC.SelectObject(qLinePen);
// Grid - Horizontal lines
for(int y = 0; y < r.bottom; y += (int)12) {
/* scan y */
memDC.MoveTo(r.left, y);
memDC.LineTo(r.right, y);
} /* scan y */
// Grid - Vertical lines
for(int x = 0 - (int)gridOffset; x <= r.right - (int)gridOffset; x
+= (int)12 ) {
/* scan x */
memDC.MoveTo(x, r.top);
memDC.LineTo(x, r.bottom);
} /* scan x */
}
if (vtPoints.capacity() == 1)
{
for (int i=vtPoints[0].size()-1;i>=0;i--) {
vtToDraw[0].at(i).x = vtPoints[0].at(i).x;
vtToDraw[0].at(i).y = ( (vtPoints[0].at(i).y > 0) ?
vtPoints[0].at(i).y : 86 );
}
//int savedraw = memDC.SaveDC();
CPen qPolylinePen(PS_SOLID, 1, RGB(0, 255, 0));
memDC.SelectObject(qPolylinePen);
vectfPoints* pointsline = &vtToDraw[0];
memDC.Polyline(&(*pointsline)[0], (int)pointsline->size());
//dc.PolyPolyline()
//memDC.RestoreDC(savedraw);
}
// Copy the offscreen bitmap onto the screen.
//memDC.SetMapMode(MM_TEXT);
dc.BitBlt(r.left, r.top, r.Width(), r.Height(),
&memDC, r.left, r.top, SRCCOPY);
//Swap back the original bitmap.
memDC.SelectObject(bOldBitmap);
}
dc.RestoreDC(saveobject);
}
BOOL CGraphClass::OnEraseBkgnd(CDC* pDC)
{
//return CWnd::OnEraseBkgnd(pDC);
return FALSE;
}
// ****************************** ATTRIBUTES
******************************
void CGraphClass::set_GraphType(GRAPH_TYPE graphtype)
{
iGraphType = graphtype;
}
GRAPH_TYPE CGraphClass::get_GraphType()
{
return (GRAPH_TYPE)iGraphType;
}
// ******************************* METHODS
*******************************
BOOL CGraphClass::InitializeGraph(int maxsizearray, int maxinterval,
GRAPH_TYPE typegraph/*=GRAPH_LINES*/)
{
// Initializing graph's DC...
iGraphType = typegraph;
gridOffset = 0;
bActivateGrid = TRUE;
bSetDraw = TRUE;
nElapse = 1000;
iTimerVal = 0;
iYMaxInterval = maxinterval;
iSourceMaxSizeArray = maxsizearray; // getting max size array of
each source which has to be fixed
vtPoints.resize(1);
vtPoints[0].resize(iSourceMaxSizeArray);
vtToDraw.resize(1);
vtToDraw[0].resize(iSourceMaxSizeArray);
CRect rc;
GetClientRect(&rc);
int iPointOfOrigin = 0;
for (int i=vtPoints[0].size()-1;i>=0;i--) // initializing this
first source to initial values
{ // which are consecutive integer values for X-axis
vtPoints[0].at(i).x = iPointOfOrigin++; // and bottom screen value
for Y-axis
vtPoints[0].at(i).y = 0;
vtToDraw[0].at(i).x = iPointOfOrigin;
vtToDraw[0].at(i).y = 0;
}
iTimerVal = SetTimer(IDT_TIMER_0, nElapse, 0);
return TRUE;
}
void CGraphClass::AddLinesElement(UINT iIndexSource, LPCTSTR
description, double value)
{
// add in value format because CPoint depends from resize...
int i;
CRect rc;
GetClientRect(&rc);
vtPoints.resize(iIndexSource+1); // setting the array to max
size defined
vtPoints[iIndexSource].resize(iSourceMaxSizeArray);
int iPointOfOrigin = 0;
for (i=vtPoints[iIndexSource].size()-1;i>=0;i--) // initializing
this first source to initial values
{ // which are consecutive integer values for X-axis
vtPoints[iIndexSource].at(i).x = iPointOfOrigin++; // and bottom
screen value for Y-axis
vtPoints[iIndexSource].at(i).y = 0;
}
// Now I add element value to the source
rotate(vtPoints[iIndexSource].begin(), vtPoints[iIndexSource].begin()
+1, vtPoints[iIndexSource].end()); // SHIFT-LEFT the array of POINTs
to add new value
vtPoints[iIndexSource].at(vtPoints[iIndexSource].size() - 1).y =
(LONG)value;
iPointOfOrigin = 0;
for (i=vtPoints[iIndexSource].size()-1;i>=0;i--)
vtPoints[iIndexSource].at(i).x = iPointOfOrigin++;
Invalidate();
UpdateWindow();
}
BOOL CGraphClass::EnableTimer()
{
//
***************************************************************************
// if enabled it's a realtime task manager, otherwise it'd be a
graphical
// representation of values passed from external source
//
***************************************************************************
iTimerVal = SetTimer(IDT_TIMER_0, nElapse, 0);
return TRUE;
}
Ciao,
Luigi