/* * * ChartLineSerie.cpp * * Written by Cédric Moonen (cedric_moonen@hotmail.com) * * * * This code may be used for any non-commercial and commercial purposes in a compiled form. * The code may be redistributed as long as it remains unmodified and providing that the * author name and this disclaimer remain intact. The sources can be modified WITH the author * consent only. * * This code is provided without any garanties. I cannot be held responsible for the damage or * the loss of time it causes. Use it at your own risks * * An e-mail to notify me that you are using this code is appreciated also. * * History: * - 25/03/2008: Line series with a width > 1 can now have a style other than solid * (thanks to Bruno Lavier). * - 12/08/2008: Performance fix: pen use the PS_GEOMETRIC style only when necessary * (thanks to Nick Holgate). * * */ #include "stdafx.h" #include "ChartLineSerie.h" #include "ChartCtrl.h" #include "Math.h" #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// CChartLineSerie::CChartLineSerie(CChartCtrl* pParent) : CChartXYSerie(pParent) { m_iLineWidth = 1; m_iPenStyle = PS_SOLID; m_bSmooth = false; m_bShadow = false; } CChartLineSerie::~CChartLineSerie() { } void CChartLineSerie::SetPenStyle(int NewStyle) { m_iPenStyle = NewStyle; m_pParentCtrl->RefreshCtrl(); } void CChartLineSerie::SetWidth(int PenWidth) { m_iLineWidth = PenWidth; m_pParentCtrl->RefreshCtrl(); } void CChartLineSerie::SetSmooth(bool bSmooth) { m_bSmooth = bSmooth; m_pParentCtrl->RefreshCtrl(); } void CChartLineSerie::DrawAll(CDC *pDC) { if (!m_bIsVisible) return; if (!pDC->GetSafeHdc()) return; unsigned uFirst=0, uLast=0; if (!GetVisiblePoints(uFirst,uLast)) return; if (uFirst>0) uFirst--; if (uLastSetBkMode(TRANSPARENT); //To have lines limited in the drawing rectangle : pDC->IntersectClipRect(m_PlottingRect); pOldPen = pDC->SelectObject(&NewPen); if (m_bSmooth) { // For a Bezier curve, all points must be drawn. uFirst = 0; uLast = GetPointsCount() - 1; SChartXYPoint* pKnots = NULL; SChartXYPoint* pFirstControlPts = NULL; SChartXYPoint* pSecondControlPts = NULL; GetBezierControlPoints(uFirst,uLast,pKnots,pFirstControlPts,pSecondControlPts); unsigned Count = uLast - uFirst; CPoint* pBezierPts = new CPoint[3*(Count-1)+1]; CPoint* pShadowPts = NULL; if (m_bShadow) pShadowPts = new CPoint[3*(Count-1)+1]; unsigned index = 0; for (unsigned n=0; nSelectObject(&ShadowPen); pDC->PolyBezier(pShadowPts,3*(Count-1)+1); pDC->SelectObject(&NewPen); delete[] pShadowPts; } pDC->PolyBezier(pBezierPts,3*(Count-1)+1); delete[] pKnots; delete[] pFirstControlPts; delete[] pSecondControlPts; delete[] pBezierPts; } else // Non-smoothed curve { if (uLast-uFirst >= 1) { CPoint* pPoints = new CPoint[uLast-uFirst+1]; CPoint* pShadow = NULL; if (m_bShadow) pShadow = new CPoint[uLast-uFirst+1]; unsigned long pointsCount = 0; CPoint LastScreenPoint; for (m_uLastDrawnPoint=uFirst;m_uLastDrawnPoint<=uLast;m_uLastDrawnPoint++) { //We don't draw a line between the origin and the first point -> we must have // a least 2 points before begining drawing SChartXYPoint Point = GetPoint(m_uLastDrawnPoint); CPoint ScreenPoint; ValueToScreen(Point.X, Point.Y, ScreenPoint); if(LastScreenPoint != ScreenPoint) { //Only collate the unique points pPoints[pointsCount] = ScreenPoint; LastScreenPoint = ScreenPoint; if (m_bShadow) { ScreenPoint.Offset(m_iShadowDepth,m_iShadowDepth); pShadow[pointsCount] = ScreenPoint; } pointsCount++; } } // We have to do that in order for the Draw function to work properly. m_uLastDrawnPoint--; if (m_bShadow) { pDC->SelectObject(&ShadowPen); pDC->Polyline(pShadow, pointsCount); } pDC->SelectObject(&NewPen); pDC->Polyline(pPoints, pointsCount); delete[] pPoints; delete[] pShadow; } } pDC->SelectClipRgn(NULL); pDC->SelectObject(pOldPen); NewPen.DeleteObject(); ShadowPen.DeleteObject(); } void CChartLineSerie::Draw(CDC* pDC) { if (!m_bIsVisible) return; // If shadow or smooth is enabled, then the complete series // must be redrawn. if (m_bShadow || m_bSmooth) { DrawAll(pDC); return; } if (pDC->GetSafeHdc()) { CPen NewPen; if (m_iPenStyle != PS_SOLID) { LOGBRUSH lb; lb.lbStyle = BS_SOLID; lb.lbColor = m_SerieColor; NewPen.CreatePen(PS_GEOMETRIC | m_iPenStyle, m_iLineWidth, &lb); } else { NewPen.CreatePen(m_iPenStyle, m_iLineWidth, m_SerieColor); } CPen* pOldPen; pDC->SetBkMode(TRANSPARENT); //To have lines limited in the drawing rectangle : pDC->IntersectClipRect(m_pParentCtrl->GetPlottingRect()); pOldPen = pDC->SelectObject(&NewPen); //Draw all points that haven't been drawn yet for (m_uLastDrawnPoint;m_uLastDrawnPointMoveTo(ScreenPoint.x,ScreenPoint.y); Point = GetPoint(m_uLastDrawnPoint+1); ValueToScreen(Point.X, Point.Y, ScreenPoint); pDC->LineTo(ScreenPoint.x,ScreenPoint.y); } pDC->SelectClipRgn(NULL); pDC->SelectObject(pOldPen); DeleteObject(NewPen); } } void CChartLineSerie::DrawLegend(CDC *pDC, const CRect& rectBitmap) const { if (m_strSerieName== _T("")) return; //Draw line: LOGBRUSH lb; lb.lbStyle = BS_SOLID; lb.lbColor = m_SerieColor; CPen NewPen(PS_GEOMETRIC | m_iPenStyle,m_iLineWidth,&lb); CPen* pOldPen = pDC->SelectObject(&NewPen); pDC->MoveTo(rectBitmap.left,rectBitmap.CenterPoint().y); pDC->LineTo(rectBitmap.right,rectBitmap.CenterPoint().y); pDC->SelectObject(pOldPen); DeleteObject(NewPen); } bool CChartLineSerie::IsPointOnSerie(const CPoint& screenPoint, unsigned& uIndex) const { uIndex = INVALID_POINT; if (!m_bIsVisible) return false; unsigned uFirst=0, uLast=0; if (!GetVisiblePoints(uFirst, uLast)) return false; if (uFirst>0) uFirst--; if (uLast