// zoomscrolldemoDlg.cpp : implementation file
//
#include "stdafx.h"
#include "zoomscrolldemo.h"
#include "zoomscrolldemoDlg.h"
#include "HotSpotDlg.h"
#include "chartdir.h"
#include <math.h>
#include <time.h>
#include <algorithm>
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CZoomscrolldemoDlg dialog
//
// Constructor
//
CZoomscrolldemoDlg::CZoomscrolldemoDlg(CWnd* pParent /*=NULL*/)
: CDialog(CZoomscrolldemoDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CZoomscrolldemoDlg)
//}}AFX_DATA_INIT
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
//
// Destructor
//
CZoomscrolldemoDlg::~CZoomscrolldemoDlg()
{
delete m_dataTable;
delete m_ChartViewer.getChart();
}
void CZoomscrolldemoDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CZoomscrolldemoDlg)
DDX_Control(pDX, IDC_StartDate, m_StartDate);
DDX_Control(pDX, IDC_Duration, m_Duration);
DDX_Control(pDX, IDC_VScrollBar, m_VScrollBar);
DDX_Control(pDX, IDC_HScrollBar, m_HScrollBar);
DDX_Control(pDX, IDC_XZoomPB, m_XZoomPB);
DDX_Control(pDX, IDC_PointerPB, m_PointerPB);
DDX_Control(pDX, IDC_ChartViewer, m_ChartViewer);
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CZoomscrolldemoDlg, CDialog)
//{{AFX_MSG_MAP(CZoomscrolldemoDlg)
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_WM_DESTROY()
ON_BN_CLICKED(IDC_PointerPB, OnPointerPB)
ON_BN_CLICKED(IDC_ZoomInPB, OnZoomInPB)
ON_BN_CLICKED(IDC_ZoomOutPB, OnZoomOutPB)
ON_BN_CLICKED(IDC_XZoomPB, OnXZoomPB)
ON_BN_CLICKED(IDC_XYZoomPB, OnXYZoomPB)
ON_BN_CLICKED(IDC_ChartViewer, OnChartViewer)
ON_CONTROL(CVN_ViewPortChanged, IDC_ChartViewer, OnViewPortChanged)
ON_WM_HSCROLL()
ON_WM_VSCROLL()
ON_NOTIFY(DTN_DATETIMECHANGE, IDC_StartDate, OnDatetimechangeStartDate)
ON_CBN_SELCHANGE(IDC_Duration, OnSelchangeDuration)
ON_CBN_KILLFOCUS(IDC_Duration, OnKillfocusDuration)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CZoomscrolldemoDlg message handlers
//
// Initialization
//
BOOL CZoomscrolldemoDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// *** code automatically generated by VC++ MFC AppWizard ***
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// Load icons to mouse usage buttons
loadButtonIcon(IDC_PointerPB, IDI_PointerPB, 100, 20);
loadButtonIcon(IDC_ZoomInPB, IDI_ZoomInPB, 100, 20);
loadButtonIcon(IDC_ZoomOutPB, IDI_ZoomOutPB, 100, 20);
// Load icons to zoom/scroll direction control buttons
loadButtonIcon(IDC_XZoomPB, IDI_XZoomPB, 105, 20);
loadButtonIcon(IDC_XYZoomPB, IDI_XYZoomPB, 105, 20);
//
// Initialize member variables
//
m_extBgColor = getDefaultBgColor(); // Default background color
m_minValue = m_maxValue = 0; // y axes ranges
// Load the data
loadData();
//
// In this demo, we deduce the horizontal scroll range from the data.
//
// Earliest date is the first timestamp
m_minDate = m_timeStamps[0];
// Duration is the seconds elapsed of the last timestamp from the earliest date
m_dateRange = m_timeStamps[m_timeStamps.len - 1] - m_minDate;
// Convert the start time (in chartTime format) as an MFC CTime value
int startYMD = Chart::getChartYMD(m_timeStamps[0]);
int startHMS = (int)fmod(m_timeStamps[0], 86400);
CTime startDate = CTime(startYMD / 10000, (startYMD % 10000) / 100, startYMD % 100,
startHMS / 3600, (startHMS % 3600) / 60, startHMS % 60);
// Convert the end time (in chartTime format) as an MFC CTime value
int endYMD = Chart::getChartYMD(m_timeStamps[m_timeStamps.len - 1]);
int endHMS = (int)fmod(m_timeStamps[m_timeStamps.len - 1], 86400);
CTime endDate = CTime(endYMD / 10000, (endYMD % 10000) / 100, endYMD % 100,
endHMS / 3600, (endHMS % 3600) / 60, endHMS % 60);
// Set the startDate and endDate to the CDateTimeCtrl control
m_StartDate.SetRange(&startDate, &endDate);
// In this demo, the maximum zoom-in is set to 10 days (1 day = 86400 seconds)
m_minDuration = 10 * 86400;
// Set up the ChartViewer to reflect the visible and minimum duration
m_ChartViewer.setZoomInWidthLimit(m_minDuration / m_dateRange);
m_ChartViewer.setViewPortWidth(m_currentDuration / m_dateRange);
m_ChartViewer.setViewPortLeft(1 - m_ChartViewer.getViewPortWidth());
// Initially set the mouse to drag to scroll mode in horizontal direction.
m_PointerPB.SetCheck(1);
m_XZoomPB.SetCheck(1);
m_ChartViewer.setMouseUsage(Chart::MouseUsageScroll);
// Can update chart now
m_ChartViewer.updateViewPort(true, true);
return TRUE;
}
// *** code automatically generated by VC++ MFC AppWizard ***
// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.
void CZoomscrolldemoDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
}
// *** code automatically generated by VC++ MFC AppWizard ***
// The system calls this to obtain the cursor to display while the user drags
// the minimized window.
HCURSOR CZoomscrolldemoDlg::OnQueryDragIcon()
{
return (HCURSOR) m_hIcon;
}
//
// User clicks on the Pointer pushbutton
//
void CZoomscrolldemoDlg::OnPointerPB()
{
m_ChartViewer.setMouseUsage(Chart::MouseUsageScroll);
}
//
// User clicks on the Zoom In pushbutton
//
void CZoomscrolldemoDlg::OnZoomInPB()
{
m_ChartViewer.setMouseUsage(Chart::MouseUsageZoomIn);
}
//
// User clicks on the Zoom Out pushbutton
//
void CZoomscrolldemoDlg::OnZoomOutPB()
{
m_ChartViewer.setMouseUsage(Chart::MouseUsageZoomOut);
}
//
// User clicks on the X-Zoom pushbutton
//
void CZoomscrolldemoDlg::OnXZoomPB()
{
m_ChartViewer.setZoomDirection(Chart::DirectionHorizontal);
m_ChartViewer.setScrollDirection(Chart::DirectionHorizontal);
// Viewport is always unzoomed as y-axis is auto-scaled
m_ChartViewer.setViewPortTop(0);
m_ChartViewer.setViewPortHeight(1);
// Update chart to auto-scale axis
m_ChartViewer.updateViewPort(true, true);
}
//
// User clicks on the XY-Zoom pushbutton
//
void CZoomscrolldemoDlg::OnXYZoomPB()
{
m_ChartViewer.setZoomDirection(Chart::DirectionHorizontalVertical);
m_ChartViewer.setScrollDirection(Chart::DirectionHorizontalVertical);
}
//
// User selects a start date from the CDateTimeCtrl control
//
void CZoomscrolldemoDlg::OnDatetimechangeStartDate(NMHDR* pNMHDR, LRESULT* pResult)
{
// Get the selected date
CTime startDate;
m_StartDate.GetTime(startDate);
// Compute the new view port position based on the selected date
m_ChartViewer.setViewPortLeft(
(Chart::chartTime2((int)startDate.GetTime()) - m_minDate) / m_dateRange);
m_ChartViewer.updateViewPort(true, true);
*pResult = 0;
}
//
// User selects a duration from the Duration combo box
//
void CZoomscrolldemoDlg::OnSelchangeDuration()
{
// Get the selected duration
CString text;
m_Duration.GetLBText(m_Duration.GetCurSel(), text);
// Validate and update the chart
validateDuration(text);
}
//
// The Duration combo box lost focus (User may have entered a new duration.)
//
void CZoomscrolldemoDlg::OnKillfocusDuration()
{
// Get the duration text
CString text;
m_Duration.GetWindowText(text);
// Validate and update the chart
validateDuration(text);
}
//
// User presses "Enter" key. (User may have entered a new duration.)
//
void CZoomscrolldemoDlg::OnOK()
{
// Same processing as OnKillfocusDuration
OnKillfocusDuration();
}
//
// User clicks on the the horizontal scroll bar
//
void CZoomscrolldemoDlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
if (nSBCode != SB_ENDSCROLL)
{
// User is still scrolling
// Set the view port based on the scroll bar
m_ChartViewer.setViewPortLeft(moveScrollBar(nSBCode, nPos, pScrollBar));
// Update the chart image only, but no need to update the image map.
m_ChartViewer.updateViewPort(true, false);
}
else
// Scroll bar has stoped moving. Can update image map now.
m_ChartViewer.updateViewPort(false, true);
}
//
// User clicks on the the vertical scroll bar
//
void CZoomscrolldemoDlg::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
if (nSBCode != SB_ENDSCROLL)
{
// User is still scrolling
// Set the view port based on the scroll bar
m_ChartViewer.setViewPortTop(moveScrollBar(nSBCode, nPos, pScrollBar));
// Update the chart image only, but no need to update the image map.
m_ChartViewer.updateViewPort(true, false);
}
else
// Scroll bar has stoped moving. Can update image map now.
m_ChartViewer.updateViewPort(false, true);
}
//
// CChartViewer ViewPortChanged event
//
void CZoomscrolldemoDlg::OnViewPortChanged()
{
//
// Set up the scroll bars to reflect the current position and size of the view port
//
SCROLLINFO info;
info.cbSize = sizeof(SCROLLINFO);
info.fMask = SIF_ALL;
info.nMin = 0;
info.nMax = 0x1fffffff;
m_HScrollBar.EnableWindow(m_ChartViewer.getViewPortWidth() < 1);
if (m_ChartViewer.getViewPortWidth() < 1)
{
info.nPage = (int)ceil(m_ChartViewer.getViewPortWidth() * (info.nMax - info.nMin));
info.nPos = (int)(0.5 + m_ChartViewer.getViewPortLeft() * (info.nMax - info.nMin))
+ info.nMin;
m_HScrollBar.SetScrollInfo(&info);
}
m_VScrollBar.EnableWindow(m_ChartViewer.getViewPortHeight() < 1);
if (m_ChartViewer.getViewPortHeight() < 1)
{
info.nPage = (int)ceil(m_ChartViewer.getViewPortHeight() * (info.nMax - info.nMin));
info.nPos = (int)(0.5 + m_ChartViewer.getViewPortTop() * (info.nMax - info.nMin))
+ info.nMin;
m_VScrollBar.SetScrollInfo(&info);
}
//
// Set the start date CDateTimeCtrl control and duration combo box to reflect the current
// position and size of the view port.
//
// Compute the start date (in chartTime) and duration (in seconds) of the view port
double currentStartDate = m_minDate + (int)(0.5 + m_ChartViewer.getViewPortLeft()
* m_dateRange);
m_currentDuration = (int)(0.5 + m_ChartViewer.getViewPortWidth() * m_dateRange);
// Set the CDateTimeCtrl control to reflect the start date
int startYMD = Chart::getChartYMD(currentStartDate);
int startHMS = (int)fmod(currentStartDate, 86400);
CTime startDate = CTime(startYMD / 10000, (startYMD % 10000) / 100, startYMD % 100,
startHMS / 3600, (startHMS % 3600) / 60, startHMS % 60);
m_StartDate.SetTime(&startDate);
// Set the duration combo box to reflect the duration (in days)
CString buffer;
buffer.Format(_T("%d"), (int)(0.5 + m_currentDuration / 86400));
m_Duration.SetWindowText(buffer);
//
// Update chart and image map if necessary
//
if (m_ChartViewer.needUpdateChart())
drawChart(&m_ChartViewer);
if (m_ChartViewer.needUpdateImageMap())
updateImageMap(&m_ChartViewer);
}
//
// User clicks on the CChartViewer
//
void CZoomscrolldemoDlg::OnChartViewer()
{
ImageMapHandler *handler = m_ChartViewer.getImageMapHandler();
if (0 != handler)
{
//
// Query the ImageMapHandler to see if the mouse is on a clickable hot spot. We
// consider the hot spot as clickable if its href ("path") parameter is not empty.
//
const char *path = handler->getValue("path");
if ((0 != path) && (0 != *path))
{
// In this sample code, we just show all hot spot parameters.
CHotSpotDlg hs;
hs.SetData(handler);
hs.DoModal();
}
}
}
/////////////////////////////////////////////////////////////////////////////
// CZoomscrolldemoDlg methods
//
// Load the data
//
void CZoomscrolldemoDlg::loadData()
{
// In this demo, we allow scrolling for the last 5 years.
time_t t = time(0);
struct tm *ymd = localtime(&t);
double lastDate = Chart::chartTime(ymd->tm_year + 1900, ymd->tm_mon, ymd->tm_mday);
double firstDate = Chart::chartTime(ymd->tm_year + 1900 - 5, ymd->tm_mon, ymd->tm_mday);
// The initial view port is to show 1 year of data.
m_currentDuration = lastDate - Chart::chartTime(ymd->tm_year + 1900 - 1, ymd->tm_mon,
ymd->tm_mday);
//
// Get the data and stores them in a memory buffer for fast scrolling / zooming. In
// this demo, we just use a random number generator. In practice, you may get the data
// from a database or XML or by other means.
//
// Set up random number generator
m_dataTable = new RanTable(127, 4, (int)((lastDate - firstDate) / 86400) + 1);
m_dataTable->setDateCol(0, firstDate, 86400);
m_dataTable->setCol(1, 150, -10, 10);
m_dataTable->setCol(2, 200, -10, 10);
m_dataTable->setCol(3, 250, -10, 10);
// Read random data into the data arrays
m_timeStamps = m_dataTable->getCol(0);
m_dataSeriesA = m_dataTable->getCol(1);
m_dataSeriesB = m_dataTable->getCol(2);
m_dataSeriesC = m_dataTable->getCol(3);
}
//
// Handle scroll bar events
//
double CZoomscrolldemoDlg::moveScrollBar(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
//
// Get current scroll bar position
//
SCROLLINFO info;
info.cbSize = sizeof(SCROLLINFO);
info.fMask = SIF_ALL;
pScrollBar->GetScrollInfo(&info);
//
// Compute new position based on the type of scroll bar events
//
int newPos = info.nPos;
switch (nSBCode)
{
case SB_LEFT:
newPos = info.nMin;
break;
case SB_RIGHT:
newPos = info.nMax;
break;
case SB_LINELEFT:
newPos -= (info.nPage > 10) ? info.nPage / 10 : 1;
break;
case SB_LINERIGHT:
newPos += (info.nPage > 10) ? info.nPage / 10 : 1;
break;
case SB_PAGELEFT:
newPos -= info.nPage;
break;
case SB_PAGERIGHT:
newPos += info.nPage;
break;
case SB_THUMBTRACK:
newPos = info.nTrackPos;
break;
}
if (newPos < info.nMin) newPos = info.nMin;
if (newPos > info.nMax) newPos = info.nMax;
// Update the scroll bar with the new position
pScrollBar->SetScrollPos(newPos);
// Returns the position of the scroll bar as a ratio of its total length
return ((double)(newPos - info.nMin)) / (info.nMax - info.nMin);
}
//
// Validate the contents of the duration combo box and update ViewPortWidth accordingly
//
void CZoomscrolldemoDlg::validateDuration(const CString &text)
{
// Parse the duration text
double newDuration = _tcstod(text, 0) * 86400;
// Duration too short or not numeric?
if (newDuration < m_minDuration)
newDuration = m_minDuration;
if (newDuration != m_currentDuration)
{
// Set the ViewPortWidth according to the new duration
m_currentDuration = newDuration;
m_ChartViewer.setViewPortWidth(newDuration / m_dateRange);
// Update the chart
m_ChartViewer.updateViewPort(true, true);
}
}
//
// Draw the chart and display it in the given viewer
//
void CZoomscrolldemoDlg::drawChart(CChartViewer *viewer)
{
//
// In this demo, we copy the visible part of the data to a separate buffer for chart
// plotting.
//
// Note that if you only have a small amount of data (a few hundred data points), it
// may be easier to just plot all data in any case (so the following copying code is
// not needed), and let ChartDirector "clip" the chart to the plot area.
//
// Using ViewPortLeft and ViewPortWidth, get the start and end dates of the view port.
double viewPortStartDate = m_minDate + (__int64)(viewer->getViewPortLeft() *
m_dateRange + 0.5);
double viewPortEndDate = viewPortStartDate + (__int64)(viewer->getViewPortWidth() *
m_dateRange + 0.5);
// Get the starting index of the array using the start date
int startIndex = (int)(std::lower_bound(m_timeStamps.data, m_timeStamps.data + m_timeStamps.len,
viewPortStartDate) - m_timeStamps.data);
if ((startIndex > 0) && (m_timeStamps[startIndex] != viewPortStartDate))
--startIndex;
// Get the ending index of the array using the end date
int endIndex = (int)(std::upper_bound(m_timeStamps.data, m_timeStamps.data + m_timeStamps.len,
viewPortEndDate) - m_timeStamps.data);
if (endIndex >= m_timeStamps.len - 1)
endIndex = m_timeStamps.len - 1;
// Get the length
int noOfPoints = endIndex - startIndex + 1;
// We copy the visible data from the main arrays to separate data arrays
double* viewPortTimeStamps = new double[noOfPoints];
double* viewPortDataSeriesA = new double[noOfPoints];
double* viewPortDataSeriesB = new double[noOfPoints];
double* viewPortDataSeriesC = new double[noOfPoints];
int arraySizeInBytes = noOfPoints * sizeof(double);
memcpy(viewPortTimeStamps, m_timeStamps.data + startIndex, arraySizeInBytes);
memcpy(viewPortDataSeriesA, m_dataSeriesA.data + startIndex, arraySizeInBytes);
memcpy(viewPortDataSeriesB, m_dataSeriesB.data + startIndex, arraySizeInBytes);
memcpy(viewPortDataSeriesC, m_dataSeriesC.data + startIndex, arraySizeInBytes);
if (noOfPoints >= 520)
{
//
// Zoomable chart with high zooming ratios often need to plot many thousands of
// points when fully zoomed out. However, it is usually not needed to plot more
// data points than the resolution of the chart. Plotting too many points may cause
// the points and the lines to overlap. So rather than increasing resolution, this
// reduces the clarity of the chart. So it is better to aggregate the data first if
// there are too many points.
//
// In our current example, the chart only has 520 pixels in width and is using a 2
// pixel line width. So if there are more than 520 data points, we aggregate the
// data using the ChartDirector aggregation utility method.
//
// If in your real application, you do not have too many data points, you may
// remove the following code altogether.
//
// Set up an aggregator to aggregate the data based on regular sized slots
ArrayMath m(DoubleArray(viewPortTimeStamps, noOfPoints));
m.selectRegularSpacing(noOfPoints / 260);
// For the timestamps, take the first timestamp on each slot
int aggregatedNoOfPoints = m.aggregate(DoubleArray(viewPortTimeStamps, noOfPoints),
Chart::AggregateFirst).len;
// For the data values, aggregate by taking the averages
m.aggregate(DoubleArray(viewPortDataSeriesA, noOfPoints), Chart::AggregateAvg);
m.aggregate(DoubleArray(viewPortDataSeriesB, noOfPoints), Chart::AggregateAvg);
m.aggregate(DoubleArray(viewPortDataSeriesC, noOfPoints), Chart::AggregateAvg);
noOfPoints = aggregatedNoOfPoints;
}
//
// Now we have obtained the data, we can plot the chart.
//
///////////////////////////////////////////////////////////////////////////////////////
// Step 1 - Configure overall chart appearance.
///////////////////////////////////////////////////////////////////////////////////////
// Create an XYChart object 600 x 300 pixels in size, with pale blue (0xf0f0ff)
// background, black (000000) border, 1 pixel raised effect, and with a rounded frame.
XYChart *c = new XYChart(600, 300, 0xf0f0ff, 0, 1);
c->setRoundedFrame(m_extBgColor);
// Set the plotarea at (52, 60) and of size 520 x 192 pixels. Use white (ffffff)
// background. Enable both horizontal and vertical grids by setting their colors to
// grey (cccccc). Set clipping mode to clip the data lines to the plot area.
c->setPlotArea(52, 60, 520, 192, 0xffffff, -1, -1, 0xcccccc, 0xcccccc);
c->setClipping();
// Add a top title to the chart using 15 pts Times New Roman Bold Italic font, with a
// light blue (ccccff) background, black (000000) border, and a glass like raised effect.
c->addTitle("Zooming and Scrolling Demonstration", "timesbi.ttf", 15
)->setBackground(0xccccff, 0x0, Chart::glassEffect());
// Add a bottom title to the chart to show the date range of the axis, with a light blue
// (ccccff) background.
char formattedStartDate[32];
char formattedEndDate[32];
strcpy(formattedStartDate, c->formatValue(viewPortStartDate, "{value|mmm dd, yyyy}"));
strcpy(formattedEndDate, c->formatValue(viewPortEndDate, "{value|mmm dd, yyyy}"));
char buffer[2048];
sprintf(buffer, "From <*font=arialbi.ttf*>%s<*/font*> to <*font=arialbi.ttf*>%s<*/font*>"
" (Duration <*font=arialbi.ttf*>%d<*/font*> days)", formattedStartDate,
formattedEndDate, (int)((viewPortEndDate - viewPortStartDate) / 86400.0 + 0.5));
c->addTitle(Chart::Bottom, buffer, "ariali.ttf", 10)->setBackground(0xccccff);
// Add a legend box at the top of the plot area with 9pts Arial Bold font with flow layout.
c->addLegend(50, 33, false, "arialbd.ttf", 9)->setBackground(Chart::Transparent,
Chart::Transparent);
// Set axes width to 2 pixels
c->yAxis()->setWidth(2);
c->xAxis()->setWidth(2);
// Add a title to the y-axis
c->yAxis()->setTitle("Price (USD)", "arialbd.ttf", 9);
///////////////////////////////////////////////////////////////////////////////////////
// Step 2 - Add data to chart
///////////////////////////////////////////////////////////////////////////////////////
//
// In this example, we represent the data by lines. You may modify the code below if
// you want to use other representations (areas, scatter plot, etc).
//
// Add a line layer for the lines, using a line width of 2 pixels
Layer *layer = c->addLineLayer();
layer->setLineWidth(2);
// Now we add the 3 data series to a line layer, using the color red (ff0000), green
// (00cc00) and blue (0000ff)
layer->setXData(DoubleArray(viewPortTimeStamps, noOfPoints));
layer->addDataSet(DoubleArray(viewPortDataSeriesA, noOfPoints), 0xff0000, "Product Alpha");
layer->addDataSet(DoubleArray(viewPortDataSeriesB, noOfPoints), 0x00cc00, "Product Beta");
layer->addDataSet(DoubleArray(viewPortDataSeriesC, noOfPoints), 0x0000ff, "Product Gamma");
///////////////////////////////////////////////////////////////////////////////////////
// Step 3 - Set up x-axis scale
///////////////////////////////////////////////////////////////////////////////////////
// Set x-axis date scale to the view port date range.
c->xAxis()->setDateScale(viewPortStartDate, viewPortEndDate);
//
// In the current demo, the x-axis range can be from a few years to a few days. We can
// let ChartDirector auto-determine the date/time format. However, for more beautiful
// formatting, we set up several label formats to be applied at different conditions.
//
// If all ticks are yearly aligned, then we use "yyyy" as the label format.
c->xAxis()->setFormatCondition("align", 360 * 86400);
c->xAxis()->setLabelFormat("{value|yyyy}");
// If all ticks are monthly aligned, then we use "mmm yyyy" in bold font as the first
// label of a year, and "mmm" for other labels.
c->xAxis()->setFormatCondition("align", 30 * 86400);
c->xAxis()->setMultiFormat(Chart::StartOfYearFilter(), "<*font=bold*>{value|mmm yyyy}",
Chart::AllPassFilter(), "{value|mmm}");
// If all ticks are daily algined, then we use "mmm dd<*br*>yyyy" in bold font as the
// first label of a year, and "mmm dd" in bold font as the first label of a month, and
// "dd" for other labels.
c->xAxis()->setFormatCondition("align", 86400);
c->xAxis()->setMultiFormat(Chart::StartOfYearFilter(),
"<*block,halign=left*><*font=bold*>{value|mmm dd<*br*>yyyy}",
Chart::StartOfMonthFilter(), "<*font=bold*>{value|mmm dd}");
c->xAxis()->setMultiFormat(Chart::AllPassFilter(), "{value|dd}");
// For all other cases (sub-daily ticks), use "hh:nn<*br*>mmm dd" for the first label of
// a day, and "hh:nn" for other labels.
c->xAxis()->setFormatCondition("else");
c->xAxis()->setMultiFormat(Chart::StartOfDayFilter(),
"<*font=bold*>{value|hh:nn<*br*>mmm dd}", Chart::AllPassFilter(), "{value|hh:nn}");
///////////////////////////////////////////////////////////////////////////////////////
// Step 4 - Set up y-axis scale
///////////////////////////////////////////////////////////////////////////////////////
if ((viewer->getZoomDirection() == Chart::DirectionHorizontal) ||
((m_minValue == 0) && (m_maxValue == 0)))
{
// y-axis is auto-scaled - save the chosen y-axis scaled to support xy-zoom mode
c->layout();
m_minValue = c->yAxis()->getMinValue();
m_maxValue = c->yAxis()->getMaxValue();
}
else
{
// xy-zoom mode - compute the actual axis scale in the view port
double axisLowerLimit = m_maxValue - (m_maxValue - m_minValue) *
(viewer->getViewPortTop() + viewer->getViewPortHeight());
double axisUpperLimit = m_maxValue - (m_maxValue - m_minValue) *
viewer->getViewPortTop();
// *** use the following formula if you are using a log scale axis ***
// double axisLowerLimit = m_maxValue * pow(m_minValue / m_maxValue,
// viewer->getViewPortTop() + viewer->getViewPortHeight());
// double axisUpperLimit = m_maxValue * pow(m_minValue / m_maxValue,
// viewer->getViewPortTop());
// use the zoomed-in scale
c->yAxis()->setLinearScale(axisLowerLimit, axisUpperLimit);
c->yAxis()->setRounding(false, false);
}
///////////////////////////////////////////////////////////////////////////////////////
// Step 5 - Display the chart
///////////////////////////////////////////////////////////////////////////////////////
// Set the chart image to the WinChartViewer
delete m_ChartViewer.getChart();
m_ChartViewer.setChart(c);
// Free resources
delete[] viewPortTimeStamps;
delete[] viewPortDataSeriesA;
delete[] viewPortDataSeriesB;
delete[] viewPortDataSeriesC;
}
//
// Update the image map
//
void CZoomscrolldemoDlg::updateImageMap(CChartViewer *viewer)
{
if (0 == viewer->getImageMapHandler())
{
// no existing image map - creates a new one
viewer->setImageMap(viewer->getChart()->getHTMLImageMap("clickable", "",
"title='[{dataSetName}] {x|mmm dd, yyyy}: USD {value|2}'"));
}
}
/////////////////////////////////////////////////////////////////////////////
// General utilities
//
// Get the default background color
//
int CZoomscrolldemoDlg::getDefaultBgColor()
{
LOGBRUSH LogBrush;
HBRUSH hBrush = (HBRUSH)SendMessage(WM_CTLCOLORDLG, (WPARAM)CClientDC(this).m_hDC,
(LPARAM)m_hWnd);
::GetObject(hBrush, sizeof(LOGBRUSH), &LogBrush);
int ret = LogBrush.lbColor;
return ((ret & 0xff) << 16) | (ret & 0xff00) | ((ret & 0xff0000) >> 16);
}
//
// Load an icon resource into a button
//
void CZoomscrolldemoDlg::loadButtonIcon(int buttonId, int iconId, int width, int height)
{
GetDlgItem(buttonId)->SendMessage(BM_SETIMAGE, IMAGE_ICON, (LPARAM)::LoadImage(
AfxGetResourceHandle(), MAKEINTRESOURCE(iconId), IMAGE_ICON, width, height,
LR_DEFAULTCOLOR));
} |