ASE Home Page Products Download Purchase Support About ASE
ChartDirector Support
Forum HomeForum Home   SearchSearch

Message ListMessage List     Post MessagePost Message

  Regarding Dual XY axis
Posted by Sai on Nov-22-2012 20:45
Attachments:
My application is VC++
1. I have one chart & inside that chart has 2 X axis & 2 Y axis. Initially I loads chart with sample data that time that time chart render data properly. But when I want to filter data that time X2 v/s Y2 doesn't get render on chart properly. In this case length of dataset may be different.
So how do I draw dual XY axis chart with different size of data.

2. When I show only secondary XY axis(Primary XY axis kept hidden.). Chart giving wrong output.

Please find the screenshot & code. And please help me to rectify the issue.

Thanks & Regards,
Sai
Dual Axis errot.jpg
ChartDirControl.cpp
// zoomscrolltrackDlg.cpp : implementation file
//

#include "stdafx.h"
#include "ChartDirSample.h"
#include "ChartDirControl.h"
#include "chartdir.h"
#include <math.h>
#include <vector>
#include <sstream>
#include <algorithm>
#include "Strmif.h"
using namespace std;

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

/////////////////////////////////////////////////////////////////////////////
// CChartDirControl dialog

//
// Constructor
//
CChartDirControl::CChartDirControl(CWnd* pParent /*=NULL*/)
    : CDialog(CChartDirControl::IDD, pParent)
{
    m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
	m_dYScale = m_dYScale2 = 0;
	m_eChartType = CHART_LINE;
	m_eChartType2 = CHART_LINE;

	//m_szTitleY = "Byte In & Out";
	//m_szTitleY2 = "Total Byte";
	m_szTitleY.Empty();
	m_szTitleY2.Empty();
	m_bIsIsAxisY2InUse = 0;
	m_colorY1 = RGB(0,0,255);
	m_colorY2 = RGB(255,0,0);
	m_eSeries1 = MERTRIC_SERIES_NONE;
	m_eSeries2 = MERTRIC_SERIES_NONE;
}

//
// Destructor
//
CChartDirControl::~CChartDirControl()
{
    delete m_ranSeries;
    delete m_ChartViewer.getChart();
}

void CChartDirControl::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CChartDirControl)
	//DDX_Control(pDX, IDC_PointerPB, m_PointerPB);
	DDX_Control(pDX, IDC_HScrollBar, m_HScrollBar);
	DDX_Control(pDX, IDC_ChartViewer, m_ChartViewer);
	//}}AFX_DATA_MAP
	DDX_Control(pDX, IDC_MFCLINK_AVG_RESPONSE, m_cAvgResponse);
	DDX_Control(pDX, IDC_MFCLINK_MIN, m_cMinLinkButton);
	DDX_Control(pDX, IDC_MFCLINK_LINE, m_cLineLinkButton);
	DDX_Control(pDX, IDC_MFCLINK_SERVER, m_cServerLinkButton);
	DDX_Control(pDX, IDC_MFCLINK_AVG_RESPONSE2, m_cAvgResponse2);
	DDX_Control(pDX, IDC_MFCLINK_MIN2, m_cMinLinkButton2);
	DDX_Control(pDX, IDC_MFCLINK_LINE2, m_cLineLinkButton2);
	DDX_Control(pDX, IDC_MFCLINK_SERVER2, m_cServerLinkButton2);
}

BEGIN_MESSAGE_MAP(CChartDirControl, CDialog)
    //{{AFX_MSG_MAP(CChartDirControl)
    ON_WM_PAINT()
    ON_WM_QUERYDRAGICON()
    ON_WM_DESTROY()
    ON_WM_HSCROLL()
    ON_WM_MOUSEWHEEL()
    ON_CONTROL(CVN_ViewPortChanged, IDC_ChartViewer, OnViewPortChanged)
    ON_CONTROL(CVN_MouseMovePlotArea, IDC_ChartViewer, OnMouseMovePlotArea)
    //}}AFX_MSG_MAP
	//ON_BN_CLICKED(IDC_BUTTON_SCALEUP, &CChartDirControl::OnBnClickedButtonScaleup)
	//ON_BN_CLICKED(IDC_BUTTON_SCALEDOWN, &CChartDirControl::OnBnClickedButtonScaledown)
	ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN_SCALEY1, &CChartDirControl::OnDeltaposSpinScaleY1)
	ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN_SCALEY2, &CChartDirControl::OnDeltaposSpinScaleY2)
	ON_WM_SIZE()
	ON_BN_CLICKED(IDC_MFCLINK_AVG_RESPONSE, &CChartDirControl::OnBnClickedMfclinkAvgResponse)
	ON_BN_CLICKED(IDC_MFCLINK_AVG_RESPONSE2, &CChartDirControl::OnBnClickedMfclinkAvgResponse2)
	ON_WM_CTLCOLOR()
	ON_WM_ERASEBKGND()
	ON_NOTIFY(BCN_DROPDOWN, IDC_MFCLINK_AVG_RESPONSE2, &CChartDirControl::OnBnDropDownMfclinkAvgResponse2)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CChartDirControl message handlers

//
// Initialization
//
BOOL CChartDirControl::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);

	ResizeControls();
    return TRUE;
}

void CChartDirControl::InitChart()
{
	//// Load the data
 //   loadData();

    // Initialize the CChartViewer
    initChartViewer(&m_ChartViewer);

    // Trigger the ViewPortChanged event to draw the chart
    m_ChartViewer.updateViewPort(true, true);
	SetTimeDuration();
	return;
}
// *** 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 CChartDirControl::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();
		CRect rcClient;
		GetClientRect(&rcClient);
        CPaintDC dc(this); // device context for painting
		dc.FillSolidRect(&rcClient,RGB(255,255,255));
		CRect rcChart;
		m_ChartViewer.GetWindowRect(&rcChart);
		ScreenToClient(&rcChart);
		rcChart.InflateRect(1,1);
		dc.Draw3dRect(&rcChart,RGB(122,122,122),RGB(122,122,122));
    }
}

// *** 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 CChartDirControl::OnQueryDragIcon()
{
    return (HCURSOR) m_hIcon;
}





//
// The ViewPortChanged event handler. This event occurs if the user scrolls or zooms in or 
// out the chart by dragging or clicking on the chart. It can also be triggered by calling
// CChartViewer.updateViewPort.
//
void CChartDirControl::OnViewPortChanged()
{
    // In addition to updating the chart, we may also need to update other controls that
    // changes based on the view port.
    updateControls(&m_ChartViewer);

    // Update the chart if necessary
    if (m_ChartViewer.needUpdateChart())
        drawChart(&m_ChartViewer);

    // We need to update the track line too. If the mouse is moving on the chart (eg. if 
    // the user drags the mouse on the chart to scroll it), the track line will be updated
    // in the MouseMovePlotArea event. Otherwise, we need to update the track line here.
	if (!m_ChartViewer.isInMouseMoveEvent()) 
	{
        trackLineLegend((XYChart *)m_ChartViewer.getChart(), m_ChartViewer.getPlotAreaMouseX());
        m_ChartViewer.updateDisplay();
    }
}

//
// User clicks on the the horizontal scroll bar 
//
void CChartDirControl::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) 
{
	double newViewPortLeft = moveScrollBar(nSBCode, nPos, pScrollBar);

	// Update the view port if the scroll bar has really moved
	if (newViewPortLeft != m_ChartViewer.getViewPortLeft()) 
	{
		m_ChartViewer.setViewPortLeft(moveScrollBar(nSBCode, nPos, pScrollBar));
		m_ChartViewer.updateViewPort(true, false);
	}
}

//
// The mouse wheel handler
//
BOOL CChartDirControl::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
{
	// Process the mouse wheel only if the mouse is over the plot area
	if (!m_ChartViewer.isMouseOnPlotArea())
		return FALSE;

    // We zoom in or out by 10% depending on the mouse wheel direction.
	double newVpWidth = m_ChartViewer.getViewPortWidth() * (zDelta > 0 ? 0.9 : 1 / 0.9);
	double newVpHeight = m_ChartViewer.getViewPortHeight() * (zDelta > 0 ? 0.9 : 1 / 0.9);

    // We do not zoom beyond the zoom width or height limits.
    newVpWidth = max(m_ChartViewer.getZoomInWidthLimit(), min(newVpWidth,
        m_ChartViewer.getZoomOutWidthLimit()));
    newVpHeight = max(m_ChartViewer.getZoomInHeightLimit(), min(newVpWidth,
        m_ChartViewer.getZoomOutHeightLimit()));
	
	if ((newVpWidth != m_ChartViewer.getViewPortWidth()) || 
		(newVpHeight != m_ChartViewer.getViewPortHeight()))
	{
		// Set the view port position and size so that the point under the mouse remains under
		// the mouse after zooming.

		double deltaX = (m_ChartViewer.getPlotAreaMouseX() - m_ChartViewer.getPlotAreaLeft()) * 
			(m_ChartViewer.getViewPortWidth() - newVpWidth) / m_ChartViewer.getPlotAreaWidth();
		m_ChartViewer.setViewPortLeft(m_ChartViewer.getViewPortLeft() + deltaX);
		m_ChartViewer.setViewPortWidth(newVpWidth);

		double deltaY = (m_ChartViewer.getPlotAreaMouseY() - m_ChartViewer.getPlotAreaTop()) *
			(m_ChartViewer.getViewPortHeight() - newVpHeight) / m_ChartViewer.getPlotAreaHeight();
		m_ChartViewer.setViewPortTop(m_ChartViewer.getViewPortTop() + deltaY);
		m_ChartViewer.setViewPortHeight(newVpHeight);

	    m_ChartViewer.updateViewPort(true, false);
	}

	return TRUE;
}

//
// Draw track cursor when mouse is moving over plotarea
//
void CChartDirControl::OnMouseMovePlotArea()
{
    // Get the focus to ensure being able to receive mouse wheel events
    m_ChartViewer.SetFocus();

    trackLineLegend((XYChart *)m_ChartViewer.getChart(), m_ChartViewer.getPlotAreaMouseX()); 
    m_ChartViewer.updateDisplay();
}

/////////////////////////////////////////////////////////////////////////////
// CChartDirControl methods

//
// Load the data
//
std::vector<std::string> buffer;
std::vector<const char *>labels;


//void CChartDirControl::loadData()
//{
//    // In this example, we just use random numbers as data.
//    m_ranSeries = new RanSeries(127);
//	
//	strStorage.resize(1827);
//	ptrStorage.resize(1827);
//	for (int i = 0; i < 1827; ++i)
//	{
//		ostringstream legendEntry;
//		legendEntry << "label " << i;
//		strStorage[i] = legendEntry.str();
//		ptrStorage[i] = strStorage[i].c_str();
//	}
//	m_timeStamps = StringArray(&(ptrStorage[0]), 1827);
//    m_dataSeriesA = m_ranSeries->getSeries(1827, 150, -10, 10);
//    m_dataSeriesB = m_ranSeries->getSeries(1827, 200, -10, 10);
//    m_dataSeriesC = m_ranSeries->getSeries(1827, 250, -8, 8);
//}

void CChartDirControl::loadData()
{
    // In this example, we just use random numbers as data.
    m_ranSeries = new RanSeries(127);

	map<CString,structByteInOut> mapData;
	ReadFile("F:\\\\Samples\\\\VC++\\\\AdvSoft Chart\\\\chartdir_cpp_win64\\\\ChartDirector\\\\ChartDirSample\\\\x64\\\\Debug\\\\DML4.txt",mapData);
	int nSize = mapData.size();
	
	map<CString,structByteInOut>::iterator it;
	int i = 0;
	CString szVerify;
	CString szTime;
	for ( it=mapData.begin() ; it != mapData.end(); it++,i++ )
	{
		structByteInOut stByteInOut;
		CString szDateTime = (*it).first;
		stByteInOut = (*it).second;
		

		m_dataSeriesA.push_back(stByteInOut.m_nByteIn);
		m_dataSeriesB.push_back(stByteInOut.m_nbyteOut);
		m_dataSeriesC.push_back((stByteInOut.m_nbyteOut + stByteInOut.m_nByteIn)/2);
		double dTime = atof(szDateTime);
		CTime t;
		t = dTime;
		CString sz;
		sz = t.FormatGmt("%A, %B %d, %Y %H:%M:%S \\n");
		OutputDebugString(sz);
		REFERENCE_TIME rt = static_cast<REFERENCE_TIME>(dTime);
		//dTime = Chart::chartTime(t.GetYear(),t.GetMonth(),t.GetDay(),t.GetHour(),t.GetMinute(),t.GetSecond());
		//dTime = Chart::chartTime2((int)dTime);
		m_timeStamps.push_back(dTime);
		m_timeStamps2.push_back(dTime);		
	}

	m_eSeries1 = MERTRIC_SERIES_Y1;
	m_eSeries2 = MERTRIC_SERIES_Y2;
}
BOOL CChartDirControl::ReadFile(CString szFilePath,map<CString,structByteInOut> &mapData)
{
	CFile cFile(szFilePath,CFile::modeRead);
	CArchive ar(&cFile,CArchive::load);
	CString szText;
	ar.ReadString(szText);
	
	CString szTime,szByteIn,szByteOut;
	while(!szText.IsEmpty())
	{
		ar.ReadString(szText);
		int Position = 0;
		if(!szText.IsEmpty())
		{
			szTime = szText.Tokenize("\\t",Position);
			szByteIn = szText.Tokenize("\\t",Position);
			szByteOut = szText.Tokenize("\\t",Position);
			
			structByteInOut stByteInOut;
			stByteInOut.m_nByteIn = atoi(szByteIn);
			stByteInOut.m_nbyteOut = atoi(szByteOut);
			
			mapData.insert ( make_pair(szTime,stByteInOut) );
		}
	}
	
	ar.Flush();
	ar.Close();
	return TRUE;
}

//
// Initialize the CChartViewer 
//
void CChartDirControl::initChartViewer(CChartViewer *viewer)
{
    // Set the full x range to be the duration of the data
	if(m_eSeries1 == MERTRIC_SERIES_Y1)
	{
		viewer->setFullRange("x", 0, m_timeStamps/*m_sztimeArray*/.size() - 1);
	}
	if(m_eSeries2 == MERTRIC_SERIES_Y2)
		viewer->setFullRange("x2", 0, m_timeStamps2/*m_sztimeArray*/.size() - 1);

    // Initialize the view port to show the latest 20% of the time range
    //viewer->setViewPortWidth(0.1);
    viewer->setViewPortWidth(1.0);
    viewer->setViewPortLeft(1 - viewer->getViewPortWidth());

    // Set the maximum zoom to 10 points
    viewer->setZoomInWidthLimit(10.0 / m_timeStamps/*m_sztimeArray*/.size());

    // Initially set the mouse to drag to scroll mode.
    //m_PointerPB.SetCheck(1);
    viewer->setMouseUsage(Chart::MouseUsageScroll);
}

//
// Initialize the CChartViewer 
//
void CChartDirControl::updateControls(CChartViewer *viewer)
{
    // In this demo, we need to update the scroll bar to reflect the view port position and
    // width of the view port.

    m_HScrollBar.EnableWindow(viewer->getViewPortWidth() < 1);
    if (viewer->getViewPortWidth() < 1)
    {
        SCROLLINFO info;
        info.cbSize = sizeof(SCROLLINFO);
        info.fMask = SIF_ALL;
        info.nMin = 0;
        info.nMax = 0x1fffffff;
	    info.nPage = (int)ceil(viewer->getViewPortWidth() * (info.nMax - info.nMin));
        info.nPos = (int)(0.5 + viewer->getViewPortLeft() * (info.nMax - info.nMin)) + info.nMin;
        m_HScrollBar.SetScrollInfo(&info);
    }
}

//
// Handle scroll bar events
//
double CChartDirControl::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);
}

//
// Draw the chart and display it in the given viewer
//
void CChartDirControl::drawChart(CChartViewer *viewer)
{
    // Get the array indexes that corresponds to the visible start and end dates
    int startIndex = (int)floor(viewer->getValueAtViewPort("x", viewer->getViewPortLeft()));
	int endIndex = (int)ceil(viewer->getValueAtViewPort("x", viewer->getViewPortLeft() +
        viewer->getViewPortWidth()));
    int noOfPoints = endIndex - startIndex + 1;
	int nScroll = m_HScrollBar.GetScrollPos();

    // Extract the part of the data array that are visible.
	DoubleArray viewPortTimeStamps,viewPortTimeStamps2;
    DoubleArray viewPortDataSeriesA,viewPortDataSeriesB;
	if(m_eSeries1 == MERTRIC_SERIES_Y1)
	{
		viewPortTimeStamps = DoubleArray(&m_timeStamps[0]/*m_sztimeArray*/ + startIndex, noOfPoints);
		viewPortDataSeriesA = DoubleArray(&m_dataSeriesA[0] + startIndex, noOfPoints);
	}
	if(m_eSeries2 == MERTRIC_SERIES_Y2)
	{
		viewPortTimeStamps2 = DoubleArray(&m_timeStamps2[0]/*m_sztimeArray*/ + startIndex, noOfPoints);
		viewPortDataSeriesB = DoubleArray(&m_dataSeriesB[0] + startIndex, noOfPoints);
	}
    
	///////////////////////////////////////////////////////////////////////////////////////
    // Configure overall chart appearance. 
    ///////////////////////////////////////////////////////////////////////////////////////

	CRect rcClient;
	m_ChartViewer.GetClientRect(&rcClient);

    // Create an XYChart object of size 650 x 350 pixels, with a white (ffffff) background and grey 
//	XYChart *c = new XYChart(rcClient.Width(), rcClient.Height(), 0xffffff, 0xaaaaaa);
	XYChart *c = new XYChart(rcClient.Width(), rcClient.Height(), RGB(255,255,255)/*Chart::brushedSilverColor()*/,
        Chart::Transparent,0);
	
    // Set the plotarea at (55, 50) with width 90 pixels less than chart width, and height 115 pixels
    // less than chart height. Use a vertical gradient from light blue (f0f6ff) to sky blue (a0c0ff)
    // as background. Set border to transparent and grid lines to white (ffffff).
  //  c->setPlotArea(55, 50, c->getWidth() - 90 - 20, c->getHeight() - 115 - 20, c->linearGradientColor(0, 55, 0, 
		//c->getHeight() - 35, 0xf0f6ff, 0xa0c0ff), -1, Chart::Transparent, 0xffffff, 0xffffff);
	 c->setPlotArea(55, 25, c->getWidth() - 90 - 20, c->getHeight() - 75, RGB(255,255,255), -1, Chart::Transparent, RGB(200,200,200), RGB(200,200,200));

    // As the data can lie outside the plotarea in a zoomed chart, we need enable clipping.
    c->setClipping();

    // Add a title to the chart using 18 pts Times New Roman Bold Italic font
    //c->addTitle("   Zooming & Scrolling Sample", "timesbi.ttf", 18);<<

    // Set legend icon style to use line style icon, sized for 8pt font
    c->getLegend()->setLineStyleKey();
    c->getLegend()->setFontSize(8);

	//if(m_eSeries1 == MERTRIC_SERIES_Y1)
	//	drawChart(viewer,c,c->xAxis(),c->yAxis(),MERTRIC_SERIES_Y1);
	//if(m_eSeries2 == MERTRIC_SERIES_Y2)
	//	drawChart(viewer,c,c->xAxis(),c->yAxis2(),MERTRIC_SERIES_Y2);

	////////////////////**************************
   
	if(m_eSeries1 == MERTRIC_SERIES_Y1)
	{
		 // Set the axis stem to transparent
		c->xAxis()->setColors(Chart::Transparent);
		c->yAxis()->setColors(Chart::Transparent); 
		// Add axis title using 10pts Arial Bold Italic font
		c->yAxis()->setTitle(m_szTitleY, "arialbi.ttf", 8);
	}
	if(m_eSeries2 == MERTRIC_SERIES_Y2)
	{
		 // Set the axis stem to transparent
		c->xAxis2()->setColors(Chart::Transparent);
		c->yAxis2()->setColors(Chart::Transparent);
		 // Add axis title using 10pts Arial Bold Italic font
		c->yAxis2()->setTitle(m_szTitleY2, "arialbi.ttf", 8);
	}

    ///////////////////////////////////////////////////////////////////////////////////////
    // Add data to chart
    ///////////////////////////////////////////////////////////////////////////////////////

    // 
    // In this example, we represent the data by lines. You may modify the code below to use other
	// representations (areas, scatter plot, etc).
    //
	Layer *pBaseLayer = NULL,*pBaseLayer2 = NULL;
	if(m_eSeries1 == MERTRIC_SERIES_Y1)
	{
		pBaseLayer = CreateChartLayer(c,m_eChartType);
		pBaseLayer->addDataSet(viewPortDataSeriesA, m_colorY1);//0xff3333
		pBaseLayer->setXData(viewPortTimeStamps);
	}
	if(m_eSeries2 == MERTRIC_SERIES_Y2)
	{
		pBaseLayer2 = CreateChartLayer(c,m_eChartType2);
		pBaseLayer2->addDataSet(viewPortDataSeriesB, m_colorY2);
		pBaseLayer2->setUseYAxis2();
		pBaseLayer2->setXData(viewPortTimeStamps2);
	}

    // In this demo, we do not have too many data points. In real code, the chart may contain a lot
    // of data points when fully zoomed out - much more than the number of horizontal pixels in this
    // plot area. So it is a good idea to use fast line mode.
    

    // Now we add the 3 data series to a line layer, using the color red (ff0000), green
    // (00cc00) and blue (0000ff)
    //pBaseLayer2->addDataSet(viewPortDataSeriesC, 0x3333CC);

    ///////////////////////////////////////////////////////////////////////////////////////
    // Configure axis scale and labelling
    ///////////////////////////////////////////////////////////////////////////////////////

	//<<
	if(m_eSeries1 == MERTRIC_SERIES_Y1)
	{
		c->xAxis()->setDateScale(viewPortTimeStamps[0], viewPortTimeStamps[noOfPoints-1]);
		c->xAxis()->setFormatCondition("align", 24*60*60);
		c->xAxis()->setLabelFormat("{value|mmm dd}");

		//tick is hourly aligned, so no need to show the nn:ss
		c->xAxis()->setFormatCondition("align", 60*60);
		c->xAxis()->setMultiFormat(Chart::StartOfDayFilter(),
		"<*font=bold*>{value|mmm dd}", Chart::AllPassFilter(), "{value|hh}");

		c->xAxis()->setFormatCondition("align", 20*60);
		c->xAxis()->setMultiFormat(Chart::StartOfHourFilter(), "<*font=bold*> {value|hh:nn}", Chart::AllPassFilter(), "{value|hh:nn:ss}");
		c->xAxis()->setFormatCondition("else");
		c->xAxis()->setMultiFormat(Chart::StartOfHourFilter(), "<*font=bold*> {value|hh:nn:ss}", Chart::AllPassFilter(), "{value|nn:ss}");
	}
	if(m_eSeries2 == MERTRIC_SERIES_Y2)
	{
		c->xAxis2()->setDateScale(viewPortTimeStamps2[0], viewPortTimeStamps2[noOfPoints-1]);
		c->xAxis2()->setFormatCondition("align", 24*60*60);
		c->xAxis2()->setLabelFormat("{value|mmm dd}");

		//tick is hourly aligned, so no need to show the nn:ss
		c->xAxis2()->setFormatCondition("align", 60*60);
		c->xAxis2()->setMultiFormat(Chart::StartOfDayFilter(),
		"<*font=bold*>{value|mmm dd}", Chart::AllPassFilter(), "{value|hh}");

		c->xAxis2()->setFormatCondition("align", 20*60);
		c->xAxis2()->setMultiFormat(Chart::StartOfHourFilter(), "<*font=bold*> {value|hh:nn}", Chart::AllPassFilter(), "{value|hh:nn:ss}");
		c->xAxis2()->setFormatCondition("else");
		c->xAxis2()->setMultiFormat(Chart::StartOfHourFilter(), "<*font=bold*> {value|hh:nn:ss}", Chart::AllPassFilter(), "{value|nn:ss}");
	}
	//<<

	if(m_eSeries1 == MERTRIC_SERIES_Y1)
	{
		c->yAxis()->setWidth(2);
		c->yAxis()->setColors(m_colorY1,m_colorY1);
		if(m_dYScale)
			c->yAxis()->setLinearScale(0,m_dYScale);
		CustomizeLabelOfYaxis(c,c->yAxis());
	}
	if(m_eSeries2 == MERTRIC_SERIES_Y2)
	{
		c->yAxis2()->setWidth(2);
		c->yAxis2()->setColors(m_colorY2,m_colorY2);	
		if(m_dYScale2)
			c->yAxis2()->setLinearScale(0,m_dYScale2);
		CustomizeLabelOfYaxis(c,c->yAxis2());
	}
    ///////////////////////////////////////////////////////////////////////////////////////
    // Output the chart
    ///////////////////////////////////////////////////////////////////////////////////////
	// Include tool tip for the chart
	////////////////////**************************
	
    delete viewer->getChart();
    viewer->setChart(c);

    m_ChartViewer.setImageMap(c->getHTMLImageMap("", "", "title='{x|hh:nn:ss}: {value|P4}'"));

}

//New DrawChart
void CChartDirControl::drawChart(CChartViewer *viewer,XYChart *pXYChart,Axis* pAxisX,Axis* pAxisY,MERTRIC_SERIES eSeries)
{
    // Get the array indexes that corresponds to the visible start and end dates
    int startIndex = (int)floor(viewer->getValueAtViewPort(eSeries == MERTRIC_SERIES_Y1 ? "x" :"x2", viewer->getViewPortLeft()));
	int endIndex = (int)ceil(viewer->getValueAtViewPort(eSeries == MERTRIC_SERIES_Y1 ? "x" :"x2", viewer->getViewPortLeft() +
        viewer->getViewPortWidth()));
    int noOfPoints = endIndex - startIndex + 1;	

    // Extract the part of the data array that are visible.
    DoubleArray viewPortTimeStamps;
	DoubleArray viewPortDataSeriesA;
	if(eSeries == MERTRIC_SERIES_Y1)
	{
		viewPortTimeStamps = DoubleArray(&m_timeStamps[0] + startIndex, noOfPoints);
		viewPortDataSeriesA = DoubleArray(&m_dataSeriesA[0] + startIndex, noOfPoints);
	}
	if(eSeries == MERTRIC_SERIES_Y2)
	{
		viewPortTimeStamps = DoubleArray(&m_timeStamps2[0] + startIndex, noOfPoints);
		viewPortDataSeriesA = DoubleArray(&m_dataSeriesB[0] + startIndex, noOfPoints);
	}
    

    //
    // At this stage, we have extracted the visible data. We can use those data to plot the chart.
    //

	///////////////////////////////////////////////////////////////////////////////////////
    // Configure overall chart appearance. 
    ///////////////////////////////////////////////////////////////////////////////////////

	

    // Set the axis stem to transparent
    pAxisX->setColors(Chart::Transparent);
    pAxisY->setColors(Chart::Transparent);

    // Add axis title using 10pts Arial Bold Italic font
	pAxisY->setTitle(m_szTitleY, "arialbi.ttf", 8);

    ///////////////////////////////////////////////////////////////////////////////////////
    // Add data to chart
    ///////////////////////////////////////////////////////////////////////////////////////

    // 
    // In this example, we represent the data by lines. You may modify the code below to use other
	// representations (areas, scatter plot, etc).
    //
	Layer *pBaseLayer = NULL;
	pBaseLayer = CreateChartLayer(pXYChart,eSeries == MERTRIC_SERIES_Y1 ? m_eChartType : m_eChartType2);
	if(eSeries == MERTRIC_SERIES_Y2)
	{
		pBaseLayer->setUseYAxis2();
	}
    pBaseLayer->addDataSet(viewPortDataSeriesA, eSeries == MERTRIC_SERIES_Y2 ? m_colorY2 : m_colorY1);//0xff3333
	pBaseLayer->setXData(viewPortTimeStamps);
	
 


    // In this demo, we do not have too many data points. In real code, the chart may contain a lot
    // of data points when fully zoomed out - much more than the number of horizontal pixels in this
    // plot area. So it is a good idea to use fast line mode.
    

    // Now we add the 3 data series to a line layer, using the color red (ff0000), green
    // (00cc00) and blue (0000ff)
    //pBaseLayer2->addDataSet(viewPortDataSeriesC, 0x3333CC);

    ///////////////////////////////////////////////////////////////////////////////////////
    // Configure axis scale and labelling
    ///////////////////////////////////////////////////////////////////////////////////////

	//<<
	pAxisX->setDateScale(viewPortTimeStamps[0], viewPortTimeStamps[noOfPoints-1]);
	pAxisX->setFormatCondition("align", 24*60*60);
	pAxisX->setLabelFormat("{value|mmm dd}");

	//tick is hourly aligned, so no need to show the nn:ss
	pAxisX->setFormatCondition("align", 60*60);
	pAxisX->setMultiFormat(Chart::StartOfDayFilter(),
	"<*font=bold*>{value|mmm dd}", Chart::AllPassFilter(), "{value|hh}");

	pAxisX->setFormatCondition("align", 20*60);
	pAxisX->setMultiFormat(Chart::StartOfHourFilter(), "<*font=bold*> {value|hh:nn}", Chart::AllPassFilter(), "{value|hh:nn:ss}");
	pAxisX->setFormatCondition("else");
	pAxisX->setMultiFormat(Chart::StartOfHourFilter(), "<*font=bold*> {value|hh:nn:ss}", Chart::AllPassFilter(), "{value|nn:ss}");
	//<<



	pAxisY->setWidth(2);
	if(eSeries == MERTRIC_SERIES_Y1)
	{
		pAxisY->setColors(m_colorY1,m_colorY1);
		if(m_dYScale)
			pAxisY->setLinearScale(0,m_dYScale);
	}
	else
	{
		pAxisY->setColors(m_colorY2,m_colorY2);
		if(m_dYScale)
			pAxisY->setLinearScale(0,m_dYScale2);
	}
	
	CustomizeLabelOfYaxis(pXYChart,pAxisY);
}

//
// Customize label formatting for Y/Y2 Axis
//
void CChartDirControl::CustomizeLabelOfYaxis(BaseChart* pBaseChart,Axis* pAxisY)
{
	pAxisY->setFormatCondition(">", 1000*1000);
	pAxisY->setLabelFormat("{={value}/1000000}M");
    pAxisY->setFormatCondition(">", 1000);
	pAxisY->setLabelFormat("{={value}/1000}K");
	pAxisY->setFormatCondition("else");
	pAxisY->setLabelFormat("{={value}}");
	pBaseChart->layout();
	DoubleArray ticks = pAxisY->getTicks();
	for (int i = 0; i < ticks.len; i++)
	{
		double AbsTick = ticks[i];
		CString szLable;

		if (AbsTick < 1000)
		{
			szLable.Format("%d",(UINT)(AbsTick));
			pAxisY->addLabel(AbsTick, szLable);
		}
		else if (AbsTick < (1000*1000))
		{
			if(!((UINT)AbsTick%1000))
				szLable.Format("%0.0fK",AbsTick/1000);
			else
				szLable.Format("%0.1fK",AbsTick/1000.0);
			pAxisY->addLabel(AbsTick, szLable);
		}
		else if(AbsTick < (1000*1000*1000))
		{
			if(!((UINT)AbsTick%1000000))
				szLable.Format("%0.0fM",AbsTick/1000000);
			else
				szLable.Format("%0.1fM",AbsTick/1000000.0);
			pAxisY->addLabel(AbsTick, szLable);
		}
	}

}
//
// Add layer to chart
//
Layer * CChartDirControl::CreateChartLayer(XYChart *pXYChart,CHART_TYPE eChartType)
{
	Layer *pBaseLayer = NULL;
	// Add a line layer for the lines, using a line width of 2 pixels
	if(eChartType == CHART_LINE)
	{
		LineLayer *layer = pXYChart->addLineLayer();
		pBaseLayer = layer;
		layer->setLineWidth(1);
		layer->setFastLineMode();
	}
	else if(eChartType == CHART_BAR)
	{
		BarLayer *layer = pXYChart->addBarLayer();
		pBaseLayer = layer;
		layer->setLineWidth(1);
		layer->setBarShape(Chart::CircleShape);
	}
	else if(eChartType == CHART_SPINE)
	{
		SplineLayer *layer = pXYChart->addSplineLayer();
		pBaseLayer = layer;
		layer->setLineWidth(1);
		layer->setFastLineMode();
	}
	return pBaseLayer;
}
//
// Draw the track line with legend
//
void CChartDirControl::trackLineLegend(XYChart *c, int mouseX)
{
    // Clear the current dynamic layer and get the DrawArea object to draw on it.
    DrawArea *d = c->initDynamicLayer();

    // The plot area object
    PlotArea *plotArea = c->getPlotArea();

    // Get the data x-value that is nearest to the mouse, and find its pixel coordinate.
    double xValue = c->getNearestXValue(mouseX);
    int xCoor = c->getXCoor(xValue);

    // Draw a vertical track line at the x-position
    d->vline(plotArea->getTopY(), plotArea->getBottomY(), xCoor, d->dashLineColor(0x000000, 0x0101));

    // Container to hold the legend entries
	vector<string> legendEntries;

    // Iterate through all layers to build the legend array
    for (int i = 0; i < c->getLayerCount(); ++i) {
        Layer *layer = c->getLayerByZ(i);

        // The data array index of the x-value
        int xIndex = layer->getXIndexOf(xValue);

        // Iterate through all the data sets in the layer
        for (int j = 0; j < layer->getDataSetCount(); ++j) {
            DataSet *dataSet = layer->getDataSetByZ(j);

            // We are only interested in visible data sets with names
            const char *dataName = dataSet->getDataName();
            int color = dataSet->getDataColor();
			if (dataName && *dataName && (color != Chart::Transparent)) {
                // Build the legend entry, consist of the legend icon, name and data value.
                double dataValue = dataSet->getValue(xIndex);
				ostringstream legendEntry;
				legendEntry << "<*block*>" << dataSet->getLegendIcon() << " " << dataName << ": " <<
					((dataValue == Chart::NoValue) ? "N/A" : c->formatValue(dataValue, "{value|P4}"))
					<< "<*/*>";
				legendEntries.push_back(legendEntry.str());

                // Draw a track dot for data points within the plot area
                int yCoor = c->getYCoor(dataSet->getPosition(xIndex), dataSet->getUseYAxis());
                if ((yCoor >= plotArea->getTopY()) && (yCoor <= plotArea->getBottomY())) {
                    d->circle(xCoor, yCoor, 4, 4, color, color);
                }
            }
        }
    }

    // Create the legend by joining the legend entries
	ostringstream legendText;
	legendText << "<*block,maxWidth=" << plotArea->getWidth() << "*><*block*><*font=arialbd.ttf*>["
		<< c->xAxis()->getFormattedLabel(xValue, "mmm dd") << "]<*/*>";
	for (int i = ((int)legendEntries.size()) - 1; i >= 0; --i)
		legendText << "        " << legendEntries[i];
	
    // Display the legend on the top of the plot area
    TTFText *t = d->text(legendText.str().c_str(), "arial.ttf", 8);
	t->draw(plotArea->getLeftX() + 5, plotArea->getTopY() - 13, 0x000000, Chart::BottomLeft);
	t->destroy();
}

/////////////////////////////////////////////////////////////////////////////
// General utilities

//
// Load an icon resource into a button
//
void CChartDirControl::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));  
}

void CChartDirControl::OnBnClickedButtonScaleup()
{
	// TODO: Add your control notification handler code here
	

}



void CChartDirControl::OnBnClickedButtonScaledown()
{
	// TODO: Add your control notification handler code here
	XYChart *pXYChart = dynamic_cast<XYChart*>( m_ChartViewer.getChart());
	//pXYChart->layoutAxes();
	m_dYScale = pXYChart->yAxis()->getMaxValue();
	double dYScale = m_dYScale;
	m_dYScale -= (m_dYScale * .20);
	if(m_dYScale < 1)
		m_dYScale = dYScale;
	m_ChartViewer.updateViewPort(true, true);
}


void CChartDirControl::OnDeltaposSpinScaleY1(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMUPDOWN pNMUpDown = reinterpret_cast<LPNMUPDOWN>(pNMHDR);
	// TODO: Add your control notification handler code here
	XYChart *pXYChart = dynamic_cast<XYChart*>( m_ChartViewer.getChart());
	if(!m_dYScale)
	{
		pXYChart->layoutAxes();
		m_dYScale = pXYChart->yAxis()->getMaxValue();
	}
	if(pNMUpDown->iDelta < 0)
	{
		
		//m_dYScale = pXYChart->yAxis()->getMaxValue();
		m_dYScale += (m_dYScale * .10);	
	}
	else
	{
		//m_dYScale = pXYChart->yAxis()->getMaxValue();
		m_dYScale -= (m_dYScale * .20);	
	}
	m_ChartViewer.updateViewPort(true, true);
	*pResult = 0;
}


void CChartDirControl::OnDeltaposSpinScaleY2(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMUPDOWN pNMUpDown = reinterpret_cast<LPNMUPDOWN>(pNMHDR);
	// TODO: Add your control notification handler code here
	XYChart *pXYChart = dynamic_cast<XYChart*>( m_ChartViewer.getChart());
	if(!m_dYScale2)
	{
		pXYChart->layoutAxes();
		m_dYScale2 = pXYChart->yAxis2()->getMaxValue();
	}
	if(pNMUpDown->iDelta < 0)
	{
		
		//m_dYScale2 = pXYChart->yAxis2()->getMaxValue();
		m_dYScale2 += (m_dYScale2 * .10);	
	}
	else
	{
		//m_dYScale2 = pXYChart->yAxis2()->getMaxValue();
		m_dYScale2 -= (m_dYScale2 * .20);	
	}
	m_ChartViewer.updateViewPort(true, true);
	*pResult = 0;
}


void CChartDirControl::ResizeControls()
{
	CWnd *pWnd = GetDlgItem(IDC_ChartViewer);
	CRect rcWndRect,rcParent;
	int nSpace = 3;
	if(pWnd->GetSafeHwnd())
	{
		int nX = rcParent.left + nSpace;
		int nY = rcParent.top + nSpace;
		GetClientRect(&rcParent);
		//<<
		pWnd = GetDlgItem(IDC_SPIN_SCALEY1);
		pWnd->GetWindowRect(&rcWndRect);
		pWnd->MoveWindow(nX,nY,rcWndRect.Width(),rcWndRect.Height());
		nX += rcWndRect.Width();

		pWnd = GetDlgItem(IDC_MFCLINK_AVG_RESPONSE);
		pWnd->GetWindowRect(&rcWndRect);
		pWnd->MoveWindow(nX,nY,rcWndRect.Width(),rcWndRect.Height());
		nX += rcWndRect.Width();
		
		pWnd = GetDlgItem(IDC_STATIC1);
		pWnd->GetWindowRect(&rcWndRect);
		pWnd->MoveWindow(nX,nY,rcWndRect.Width(),rcWndRect.Height());
		nX += rcWndRect.Width();
	
		pWnd = GetDlgItem(IDC_MFCLINK_MIN);
		pWnd->GetWindowRect(&rcWndRect);
		pWnd->MoveWindow(nX,nY,rcWndRect.Width(),rcWndRect.Height());
		nX += rcWndRect.Width();

		pWnd = GetDlgItem(IDC_STATIC2);
		pWnd->GetWindowRect(&rcWndRect);
		pWnd->MoveWindow(nX,nY,rcWndRect.Width(),rcWndRect.Height());
		nX += rcWndRect.Width();

		pWnd = GetDlgItem(IDC_MFCLINK_LINE);
		pWnd->GetWindowRect(&rcWndRect);
		pWnd->MoveWindow(nX,nY,rcWndRect.Width(),rcWndRect.Height());
		nX += rcWndRect.Width() + 0;

		
		pWnd = GetDlgItem(IDC_MFCLINK_TIMEPERIOD);
		pWnd->GetWindowRect(&rcWndRect);
		nX = rcParent.Width() / 2 - rcWndRect.Width() / 2;
		pWnd->MoveWindow(nX,nY,rcWndRect.Width(),rcWndRect.Height());
		nX += rcWndRect.Width() + 50;
		//*** 
		pWnd = GetDlgItem(IDC_SPIN_SCALEY2);
		pWnd->GetWindowRect(&rcWndRect);
		nX = rcParent.right - nSpace - rcWndRect.Width();
		pWnd->MoveWindow(nX,nY,rcWndRect.Width(),rcWndRect.Height());
		
		pWnd = GetDlgItem(IDC_MFCLINK_LINE2);
		pWnd->GetWindowRect(&rcWndRect);
		nX -= rcWndRect.Width();
		pWnd->MoveWindow(nX,nY,rcWndRect.Width(),rcWndRect.Height());

		pWnd = GetDlgItem(IDC_STATIC4);
		pWnd->GetWindowRect(&rcWndRect);
		nX -= rcWndRect.Width();
		pWnd->MoveWindow(nX,nY,rcWndRect.Width(),rcWndRect.Height());
		
		pWnd = GetDlgItem(IDC_MFCLINK_MIN2);
		pWnd->GetWindowRect(&rcWndRect);
		nX -= rcWndRect.Width();
		pWnd->MoveWindow(nX,nY,rcWndRect.Width(),rcWndRect.Height());

		pWnd = GetDlgItem(IDC_STATIC3);
		pWnd->GetWindowRect(&rcWndRect);
		nX -= rcWndRect.Width();
		pWnd->MoveWindow(nX,nY,rcWndRect.Width(),rcWndRect.Height());

		pWnd = GetDlgItem(IDC_MFCLINK_AVG_RESPONSE2);
		pWnd->GetWindowRect(&rcWndRect);
		nX -= rcWndRect.Width();
		pWnd->MoveWindow(nX,nY,rcWndRect.Width(),rcWndRect.Height());
		pWnd->GetWindowRect(&rcWndRect);
		ScreenToClient(&rcWndRect);
		nX = rcWndRect.left;
		nY = rcWndRect.bottom;
		
		pWnd = GetDlgItem(IDC_MFCLINK_SERVER2);
		pWnd->GetWindowRect(&rcWndRect);
		pWnd->MoveWindow(nX,nY,rcWndRect.Width(),rcWndRect.Height());
		
		pWnd = GetDlgItem(IDC_MFCLINK_AVG_RESPONSE);
		pWnd->GetWindowRect(&rcWndRect);
		ScreenToClient(&rcWndRect);
		nX = rcWndRect.left;
		nY = rcWndRect.bottom;
		pWnd = GetDlgItem(IDC_MFCLINK_SERVER);
		pWnd->GetWindowRect(&rcWndRect);
		pWnd->MoveWindow(nX,nY,rcWndRect.Width(),rcWndRect.Height());
		//>>
		rcParent.DeflateRect(5,10+rcWndRect.Height()*2,5,25);
		pWnd = GetDlgItem(IDC_ChartViewer);
		pWnd->MoveWindow(rcParent);

		CRect rcChartView;
		m_ChartViewer.GetClientRect(&rcChartView);
		m_ChartViewer.MoveWindow(rcParent);

		pWnd = GetDlgItem(IDC_HScrollBar);
		pWnd->GetClientRect(&rcWndRect);
		pWnd->MoveWindow(rcParent.left,rcParent.bottom + nSpace,rcParent.Width(),rcWndRect.Height());

		if(m_ChartViewer.getChart())
			m_ChartViewer.updateViewPort(true, true);

	}
	return;
}

void CChartDirControl::OnSize(UINT nType, int cx, int cy)
{
	CDialog::OnSize(nType, cx, cy);

	// TODO: Add your message handler code here
	ResizeControls();
}

void CChartDirControl::SetChartType(CHART_TYPE eChartType)
{
	m_eChartType = eChartType;
	m_ChartViewer.updateViewPort(1,1);
}

void CChartDirControl::SetTitleAxixY(CString szTitle)
{
	m_szTitleY = szTitle;
}

void CChartDirControl::SetTitleAxixY2(CString szTitle)
{
	m_szTitleY2 = szTitle;
}

void CChartDirControl::SetChartTitle(CString szTitle)
{
	m_szChartTitle = szTitle;
}


void CChartDirControl::OnBnClickedMfclinkAvgResponse()
{
	// TODO: Add your control notification handler code here m_szChartTitle
	AfxMessageBox("What To do in OnBnClickedMfclinkAvgResponse()");
}


void CChartDirControl::OnBnClickedMfclinkAvgResponse2()
{
	// TODO: Add your control notification handler code here
	AfxMessageBox("What To do in OnBnClickedMfclinkAvgResponse2()");
}


HBRUSH CChartDirControl::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
	HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);

	// TODO:  Change any attributes of the DC here
	if(pWnd->GetDlgCtrlID() == IDC_STATIC1 || pWnd->GetDlgCtrlID() == IDC_STATIC2 || pWnd->GetDlgCtrlID() == IDC_STATIC3
		 || pWnd->GetDlgCtrlID() == IDC_STATIC4)
    {
        pDC->SetBkMode(TRANSPARENT);
        return reinterpret_cast<HBRUSH>(::GetStockObject(NULL_BRUSH));
    }

	else if(pWnd->GetDlgCtrlID() == IDC_MFCLINK_AVG_RESPONSE || pWnd->GetDlgCtrlID() == IDC_MFCLINK_MIN 
		|| pWnd->GetDlgCtrlID() == IDC_MFCLINK_LINE
		 || pWnd->GetDlgCtrlID() == IDC_MFCLINK_AVG_RESPONSE2 || pWnd->GetDlgCtrlID() == IDC_MFCLINK_MIN2 
		|| pWnd->GetDlgCtrlID() == IDC_MFCLINK_LINE2)
    {
		
        pDC->SetBkMode(TRANSPARENT);
        return reinterpret_cast<HBRUSH>(::GetStockObject(NULL_BRUSH));
    }
   
        return CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
	// TODO:  Return a different brush if the default is not desired
	return hbr;
}


BOOL CChartDirControl::OnEraseBkgnd(CDC* pDC)
{
	//// TODO: Add your message handler code here and/or call default
	
	return CDialog::OnEraseBkgnd(pDC);
}


void CChartDirControl::OnBnDropDownMfclinkAvgResponse2(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMBCDROPDOWN pDropDown = reinterpret_cast<LPNMBCDROPDOWN>(pNMHDR);
	// TODO: Add your control notification handler code here
	*pResult = 0;
}


void CChartDirControl::AddRealTimeData(double dTime,double dDataY,double dDataY2)
{
	dTime = *m_timeStamps.rbegin() + 500;
	dDataY = 999999 %rand();
	dDataY2 = 999999 %rand();
	m_timeStamps.push_back(dTime);
	m_dataSeriesA.push_back(dDataY);
	m_dataSeriesB.push_back(dDataY2);
	//m_dataSeriesC.push_back(dDataY2);

	
    m_ChartViewer.setFullRange("x", 0, m_timeStamps/*m_sztimeArray*/.size() - 1);    
    m_ChartViewer.setZoomInWidthLimit(10.0 / m_timeStamps/*m_sztimeArray*/.size());    
    m_ChartViewer.setMouseUsage(Chart::MouseUsageScroll);	
    m_ChartViewer.updateViewPort(true, true);
	SetTimeDuration();
	return;
}

void CChartDirControl::AddData(double dTime,double dDataY,MERTRIC_SERIES eSeries)
{
	//dTime = Chart::chartTime2((int)time((time_t*)&dTime));
	if(eSeries == MERTRIC_SERIES_Y1)
	{
		m_timeStamps.push_back(dTime);
		m_dataSeriesA.push_back(dDataY);
	}
	if(eSeries == MERTRIC_SERIES_Y2)
	{
		m_timeStamps2.push_back(dTime);
		m_dataSeriesB.push_back(dDataY);
	}
	//m_dataSeriesC.push_back(dDataY2);
	return;
}

void CChartDirControl::SetTimeDuration()
{
	CWnd *pWnd = GetDlgItem(IDC_MFCLINK_TIMEPERIOD);

	if(m_eSeries1 == MERTRIC_SERIES_Y1)
	{
		double dTime = m_timeStamps[0];
		CTime tm;
		tm = dTime;
		CString sz;
		sz = tm.FormatGmt("%d-%m-%Y %H:%M:%S");

		dTime = *m_timeStamps.rbegin();
		tm = dTime;
		sz = sz + " - " + tm.FormatGmt("%H:%M:%S");
		pWnd->SetWindowText(sz);
	}
	if(m_eSeries1 == MERTRIC_SERIES_Y2)
	{
		double dTime = m_timeStamps2[0];
		CTime tm;
		tm = dTime;
		CString sz;
		sz = tm.FormatGmt("%d-%m-%Y %H:%M:%S");

		dTime = *m_timeStamps2.rbegin();
		tm = dTime;
		sz = sz + " - " + tm.FormatGmt("%H:%M:%S");
		pWnd->SetWindowText(sz);
	}
	return;
}

void CChartDirControl::ClearChartData()
{
	m_timeStamps.clear();
	
	m_dataSeriesA.clear();
	m_dataSeriesB.clear();
	m_dataSeriesC.clear();
	m_eSeries1 = MERTRIC_SERIES_NONE;
	m_eSeries2 = MERTRIC_SERIES_NONE;
	return;
}
ChartDirControl.h
// zoomscrolltrackDlg.h : header file
//

#pragma once

#include "ChartViewer.h"
#include <afxmt.h>
#include <vector>
#include <string>
#include "IWLinkCtrlButton.h"
#include "afxlinkctrl.h"

// CChartDirControl dialog
class CChartDirControl : public CDialog
{
// Construction
public:
	CChartDirControl(CWnd* pParent = NULL);	// standard constructor
	~CChartDirControl();

// Dialog Data
	enum { IDD = IDD_EXACT_ZOOMSCROLL };
	CButton m_PointerPB;
	CScrollBar m_HScrollBar;
	CChartViewer m_ChartViewer;

	protected:
	virtual void DoDataExchange(CDataExchange* pDX);	// DDX/DDV support


// Implementation
protected:
	HICON m_hIcon;

	// Generated message map functions
	virtual BOOL OnInitDialog();
	afx_msg void OnPaint();
	afx_msg HCURSOR OnQueryDragIcon();
	afx_msg void OnPointerPB();
	afx_msg void OnZoomInPB();
	afx_msg void OnZoomOutPB();
	afx_msg void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar);
	afx_msg BOOL OnMouseWheel(UINT nFlags, short zDelta, CPoint pt);
	afx_msg void OnViewPortChanged();	
	afx_msg void OnMouseMovePlotArea();
	DECLARE_MESSAGE_MAP()


private:
	//
	// Data arrays for the scrollable / zoomable chart.
	// - In this demo, we just use a RanSeries object to generate random data for the chart.
	//
	RanSeries *m_ranSeries;
	vector<double> m_timeStamps;
	vector<double> m_timeStamps2;
	std::vector<std::string> strStorage;
	std::vector<const char *> ptrStorage;
	vector<double> m_dataSeriesA;
	vector<double> m_dataSeriesB;
	vector<double> m_dataSeriesC;
	CString m_szTitleY,m_szTitleY2;
	CString m_szChartTitle;
	COLORREF m_colorY1;
	COLORREF m_colorY2;
	MERTRIC_SERIES m_eSeries1; 
	MERTRIC_SERIES m_eSeries2; 
 	
	
    // Initialize the CChartViewer
    void initChartViewer(CChartViewer *viewer);

	// Draw chart
	void drawChart(CChartViewer *viewer);
	void drawChart(CChartViewer *viewer,XYChart *pXYChart,Axis* pAxisX,Axis* pAxisY,MERTRIC_SERIES eSeries);
    void trackLineLegend(XYChart *c, int mouseX);

	// Moves the scroll bar when the user clicks on it
	double moveScrollBar(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar);

	// Update controls when the view port changed
    void updateControls(CChartViewer *viewer);

	// utility to load icon resource to a button
	void loadButtonIcon(int buttonId, int iconId, int width, int height);
	BOOL ReadFile(CString szFilePath,map<CString,structByteInOut> &mapData);
	StringArray m_sztimeArray;
	BOOL m_bIsIsAxisY2InUse;
	Layer * CreateChartLayer(XYChart *pXYChart,CHART_TYPE eChartType);
	void CustomizeLabelOfYaxis(BaseChart* pBaseChart,Axis* pAxisY);
	CHART_TYPE m_eChartType;
	CHART_TYPE m_eChartType2;
public:
	// Load data into data arrays
	void loadData();
	afx_msg void OnBnClickedButtonScaleup();
	afx_msg void OnBnClickedButtonScaledown();
	double m_dYScale,m_dYScale2;
	afx_msg void OnDeltaposSpinScaleY1(NMHDR *pNMHDR, LRESULT *pResult);
	afx_msg void OnDeltaposSpinScaleY2(NMHDR *pNMHDR, LRESULT *pResult);
	afx_msg void OnNMReleasedcaptureSpinScaleY2(NMHDR *pNMHDR, LRESULT *pResult);
	afx_msg void OnNMThemeChangedSpinScaleY2(NMHDR *pNMHDR, LRESULT *pResult);
	afx_msg void OnSize(UINT nType, int cx, int cy);
	void ResizeControls();
	void InitChart();
	void SetChartType(CHART_TYPE eChartType);
	void SetChartType2(CHART_TYPE eChartType)
	{
		m_eChartType2 = eChartType;
	};	

	void SetTitleAxixY(CString szTitle);
	void SetTitleAxixY2(CString szTitle);
	void SetChartTitle(CString szTitle);
	afx_msg void OnBnClickedMfclinkAvgResponse();
	afx_msg void OnBnClickedMfclinkAvgResponse2();
	afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
	afx_msg BOOL OnEraseBkgnd(CDC* pDC);
	afx_msg void OnBnDropDownMfclinkAvgResponse2(NMHDR *pNMHDR, LRESULT *pResult);
	IWLinkCtrlButton m_cAvgResponse,m_cAvgResponse2;
	IWLinkCtrlButton m_cMinLinkButton,m_cMinLinkButton2;
	IWLinkCtrlButton m_cLineLinkButton,m_cLineLinkButton2;
	IWLinkCtrlButton m_cServerLinkButton,m_cServerLinkButton2;

	void AddRealTimeData(double dTime,double dDataY,double dDataY2);
	void AddData(double dTime,double dDataY,MERTRIC_SERIES eSeries);
	void SetTimeDuration();
	void SetAxisY2InUse(BOOL bIsAxisY2InUse)
	{
		m_bIsIsAxisY2InUse = bIsAxisY2InUse;
	};
	BOOL GetAxisY2InUse()
	{
		return m_bIsIsAxisY2InUse;
	};

	void ClearChartData();
	void SetColorY1(COLORREF clr)
	{
		m_colorY1 = clr;
	};
	void SetColorY2(COLORREF clr)
	{
		m_colorY2 = clr;
	};

	void SetSeriesActive1(MERTRIC_SERIES eSeries)
	{
		m_eSeries1 = eSeries;
	};
	void SetSeriesActive2(MERTRIC_SERIES eSeries)
	{
		m_eSeries2 = eSeries;
	};
};

  Re: Regarding Dual XY axis
Posted by Peter Kwan on Nov-23-2012 04:40
Hi Sai,

In your original enquiry, you mentioned the x-axis are text strings. Because text strings are just names for human reading, and are meaningless to the computer, so I suggest using the x array index to control the zooming and scrolling, which is in the code you are using.

However, in your actual code, the x-coordinates are dates, not text strings. Also, there are two set of x-coordinates with different number of points, which you want to zoom and scroll synchronously. Something like the followings is therefore not possible:

c->xAxis()->setDateScale(viewPortTimeStamps[0], viewPortTimeStamps[noOfPoints-1]);
c->xAxis2()->setDateScale(viewPortTimeStamps2[0], viewPortTimeStamps2[noOfPoints-1]);

It is because the two series are having different number of points (not both being "noOfPoints"). Also, they may not align with each others (eg., viewPortTimeStamps[0] may not be aligned with any data point in viewPortTimeStamps2).

For your case, I suggest the followings:

(a) Please use the original sample code included in ChartDirector, which uses date/time as the x-axis. This matches exact what you are using.

(b) Whereas different data series can use different y-coordinates, all data series must use the same x-coordinates. So in your case, you would need to convert the m_timeStamps2 to m_timeStamps before passing the data to ChartDirector. I am not sure how is m_timeStamps2 related to m_timeStamps. In your code, they seem to have no relationship at all. (In other words, it is entire possible that 20 seconds in m_timeStamps2 can become 3.6532 hours in m_timeStamp.) In this case, you can use the following code to convert m_timeStamps2 to m_timeStamps:

double scaleFactor = (m_timeStamps[m_timeStamps.size() - 1] - m_timeStamps[0]) / (m_timeStamps2[m_timeStamps2.size() - 1] - m_timeStamps2[0]);
double offsetFactor = m_timeStamps[0] - scaleFactor * m_timeStamps2[0];

for (int i = 0; i < m_timeStamps2.size(); ++i)
    m_timeStamps2[i] = m_timeStamps2[i] * scaleFactor + offsetFactor;

(c) You can still have a second x-axis for human reading.

double viewPortStartDate2 = (viewPortStartDate1 - offsetFactor) / scaleFactor;
double viewPortEndDate2  = (viewPortEndDate1 - offsetFactor) / scaleFactor;
c->xAxis2()->setDateScale(viewPortStartDate2, viewPortEndDate2);

Hope this can help.

Regards
Peter Kwan