#ifndef _WTL_OSCILLOSCOPE_H_
#define _WTL_OSCILLOSCOPE_H_
#include <math.h>
#include <vector>
/////////////////////////////////////////////////////////////////////////////
#define OSC_MAX_CHANNELS 8
#define OSC_X_AXIS_HEIGHT 25
#define OSC_Y_AXIS_WIDTH 50
#define OSC_EPSILON 1e-15
#define OSC_ZERO(x) (fabs(x) < OSC_EPSILON)
#define OSC_LABEL_FORMAT "%g"
#define OSC_OBJ_OFFSET_X 4
#define OSC_OBJ_OFFSET_Y 4
#define OSC_BIG_TIC_LEN 10
#define OSC_SMALL_TIC_LEN 5
#define OSC_MIN_TIC1_DIST 15
#define OSC_MIN_TIC3_DIST 25
#define OSC_MIN_TIC4_DIST 35
#define OSC_DEF_LAST_POINTS 100
#define OSC_MIN_LAST_POINTS 10
#define OSC_MAX_LAST_POINTS 1000
/////////////////////////////////////////////////////////////////////////////
enum OscZoomMode { ZoomRect = 0, ZoomHorz, ZoomVert };
enum OscZoomDir { ZoomOut = 0, ZoomIn, ZoomOutFull };
typedef struct _OscZoomRect {
double xMin, xMax;
double yMin, yMax;
} OscZoomRect;
/////////////////////////////////////////////////////////////////////////////
enum OscPointStyle { None = 0, UTriangle, DTriangle, Diamond, Circle, Cross, XCross };
/////////////////////////////////////////////////////////////////////////////
class OscZoomStack
{
std::vector<OscZoomRect> m_zoomIn[OSC_MAX_CHANNELS];
std::vector<OscZoomRect> m_zoomOut[OSC_MAX_CHANNELS];
public:
OscZoomStack() {}
~OscZoomStack() {}
size_t ZoomInSize() { return m_zoomIn[0].size(); }
size_t ZoomOutSize() { return m_zoomOut[0].size(); }
void Clear()
{
for(size_t i=0; i<OSC_MAX_CHANNELS; i++) {
m_zoomIn[i].clear();
m_zoomOut[i].clear();
}
}
void AddZoom(OscZoomRect* zoom)
{
for(size_t i=0; i<OSC_MAX_CHANNELS; i++) {
m_zoomIn[i].clear();
m_zoomOut[i].push_back(zoom[i]);
}
}
bool ZoomIn(OscZoomRect* zoom)
{
if ( ZoomInSize() == 0 )
return false;
for(size_t i=0; i<OSC_MAX_CHANNELS; i++) {
m_zoomOut[i].push_back(zoom[i]);
zoom[i] = *(m_zoomIn[i].end() - 1);
m_zoomIn[i].pop_back();
}
return true;
}
bool ZoomOut(OscZoomRect* zoom)
{
if ( ZoomOutSize() == 0 )
return false;
for(size_t i=0; i<OSC_MAX_CHANNELS; i++) {
m_zoomIn[i].push_back(zoom[i]);
zoom[i] = *(m_zoomOut[i].end() - 1);
m_zoomOut[i].pop_back();
}
return true;
}
bool ZoomOutFull(OscZoomRect* zoom)
{
if ( ZoomOutSize() == 0 )
return false;
while( ZoomOut(zoom) );
return true;
}
};
/////////////////////////////////////////////////////////////////////////////
class OscHandler
{
public:
virtual bool UseGrid() = 0;
virtual bool UseNewMinMax() = 0;
virtual bool UsePadding() = 0;
virtual double PaddingCoef() = 0;
virtual bool EnableZoom() = 0;
virtual void Zoom(POINT* start, POINT* end) = 0;
virtual void Zoom(OscZoomDir dir) = 0;
virtual OscZoomMode ZoomMode() = 0;
virtual OscPointStyle PointStyle() = 0;
};
/////////////////////////////////////////////////////////////////////////////
template< class T, class TBase = CStatic, class TWinTraits = CControlWinTraits >
class ATL_NO_VTABLE OscilloscopeChannelImpl : public CWindowImpl< T, TBase, TWinTraits >
{
double m_xMin, m_xMax;
double m_yMin, m_yMax;
double m_yMinPad, m_yMaxPad;
double m_XK, m_YK;
size_t m_time;
double* m_values;
POINT* m_points;
size_t m_pointsCnt;
COLORREF m_bgColor; // background color
COLORREF m_lnColor; // curve color
COLORREF m_grColor; // grid color
COLORREF m_brColor; // brush color (for simbols)
bool m_zooming;
POINT m_startZoomPnt;
POINT m_endZoomPnt;
OscHandler* m_handler;
public:
typedef OscilloscopeChannelImpl< T, TBase, TWinTraits > thisClass;
DECLARE_WND_SUPERCLASS(NULL, TBase::GetWndClassName())
// Operations
BOOL SubclassWindow(HWND hWnd)
{
ATLASSERT(m_hWnd==NULL);
ATLASSERT(::IsWindow(hWnd));
#ifdef _DEBUG
// Check class
TCHAR szBuffer[16];
if( ::GetClassName(hWnd, szBuffer, (sizeof(szBuffer)/sizeof(TCHAR))-1) ) {
ATLASSERT(::lstrcmpi(szBuffer, TBase::GetWndClassName())==0);
}
#endif
BOOL bRet = CWindowImpl< T, TBase, TWinTraits >::SubclassWindow(hWnd);
if( bRet ) _Init();
return bRet;
}
// Interface
void SetBgColor(COLORREF bgColor) { m_bgColor = bgColor; Invalidate(FALSE); }
COLORREF GetBgColor() { return m_bgColor; }
void SetLnColor(COLORREF lnColor) { m_lnColor = lnColor; Invalidate(FALSE); }
COLORREF GetLnColor() { return m_lnColor; }
void SetGrColor(COLORREF grColor) { m_grColor = grColor; Invalidate(FALSE); }
COLORREF GetGrColor() { return m_grColor; }
void SetBrColor(COLORREF brColor) { m_brColor = brColor; Invalidate(FALSE); }
COLORREF GetBrColor() { return m_brColor; }
void UseNewMinMax(bool use) { m_useNewMinMax = use; }
bool UseNewMinMax(void) { return m_useNewMinMax }
void UsePadding(bool use) { m_usePadding = use; }
bool UsePadding(void) { return m_usePadding; }
void SetXRange(double min, double max)
{
m_xMin = min;
m_xMax = max;
}
void GetXRange(double& min, double& max)
{
min = m_xMin;
max = m_xMax;
}
void SetYRange(double min, double max)
{
m_yMin = m_yMinPad = min;
m_yMax = m_yMaxPad = max;
}
void GetYRange(double& min, double& max)
{
min = m_yMinPad;
max = m_yMaxPad;
}
void SetPoints(size_t time, double* values, size_t count)
{
m_time = time;
m_values = values;
m_pointsCnt = count;
if (m_points) delete [] m_points;
m_points = new POINT[m_pointsCnt];
_UpdateYRange();
_Recalc();
}
void SetHandler(OscHandler* handler) { m_handler = handler; }
// Message map and handlers
BEGIN_MSG_MAP(thisClass)
MESSAGE_HANDLER(WM_CREATE, OnCreate)
MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
MESSAGE_HANDLER(WM_PAINT, OnPaint)
MESSAGE_HANDLER(WM_SIZE, OnSize)
MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLMouseDown)
MESSAGE_HANDLER(WM_RBUTTONDOWN, OnRMouseDown)
MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove)
MESSAGE_HANDLER(WM_LBUTTONUP, OnLMouseUp)
DEFAULT_REFLECTION_HANDLER()
END_MSG_MAP()
LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
{
LRESULT lRes = DefWindowProc(uMsg, wParam, lParam);
_Init();
return lRes;
}
LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
{
LRESULT lRes = DefWindowProc(uMsg, wParam, lParam);
_Close();
return lRes;
}
LRESULT OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
RECT rc;
GetClientRect(&rc);
PAINTSTRUCT ps;
CDCHandle dc = BeginPaint(&ps);
CDCHandle dcMem;
dcMem.CreateCompatibleDC(dc);
CBitmap bmp;
bmp.CreateCompatibleBitmap(dc, rc.right, rc.bottom);
HBITMAP oldBmp = dcMem.SelectBitmap(bmp);
_DrawBackground(&dcMem);
_DrawGrid(&dcMem);
_DrawCurve(&dcMem);
_DrawPoints(&dcMem);
dc.BitBlt(0, 0, rc.right, rc.bottom, dcMem, 0, 0, SRCCOPY);
if (oldBmp) dcMem.SelectBitmap(oldBmp);
dcMem.DeleteDC();
EndPaint(&ps);
bHandled = TRUE;
return 1;
}
LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
{
_Recalc();
Invalidate(FALSE);
return 0;
}
LRESULT OnLMouseDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
{
if ( m_zooming || !m_handler->EnableZoom() ) return 0;
m_zooming = true;
::SetCapture(m_hWnd);
m_startZoomPnt.x = GET_X_LPARAM(lParam);
m_startZoomPnt.y = GET_Y_LPARAM(lParam);
m_endZoomPnt.x = GET_X_LPARAM(lParam);
m_endZoomPnt.y = GET_Y_LPARAM(lParam);
_DrawZoom();
return 0;
}
LRESULT OnRMouseDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
{
if ( m_zooming ) {
_DrawZoom();
m_zooming = false;
::ReleaseCapture();
} else