📄 graph.cpp
字号:
while (pos) {
CGraphSeries* pSeries = (CGraphSeries*)m_olCGraphSeries.GetNext(pos);
ASSERT_VALID(pSeries);
nMax = max(nMax, pSeries->GetNonZeroElementCount());
}
return nMax;
}
// Get the largest data value in all series.
int CGraph::GetMaxDataValue()
{
VALIDATE;
int nMax(-32768);
POSITION pos(m_olCGraphSeries.GetHeadPosition());
while (pos) {
CGraphSeries* pSeries = (CGraphSeries*)m_olCGraphSeries.GetNext(pos);
ASSERT_VALID(pSeries);
nMax = max(nMax, pSeries->GetMaxDataValue());
}
return nMax;
}
// How many series are populated?
int CGraph::GetNonZeroSeriesCount()
{
VALIDATE;
int nCount(0);
POSITION pos(m_olCGraphSeries.GetHeadPosition());
while (pos) {
CGraphSeries* pSeries = (CGraphSeries*)m_olCGraphSeries.GetNext(pos);
ASSERT_VALID(pSeries);
if (0 < pSeries->GetNonZeroElementCount()) {
++nCount;
}
}
return nCount;
}
// Returns the group number for the sent label; -1 if not found.
int CGraph::LookupLabel(CString sLabel)
{
VALIDATE;
_ASSERTE(! sLabel.IsEmpty());
for (int nGroup = 0; nGroup < m_paLegend.GetSize(); ++nGroup) {
if (0 == sLabel.CompareNoCase(((PGRAPHICLEGEND)(m_paLegend.GetAt(nGroup)))->sLabel)) {
return nGroup;
}
}
return -1;
}
//
void CGraph::AddSeries(CGraphSeries* pCGraphSeries)
{
VALIDATE;
ASSERT_VALID(pCGraphSeries);
m_olCGraphSeries.AddTail(pCGraphSeries);
//TRACE("CGraph::AddSeries. Added Series.\n");
}
//
void CGraph::SetXAxisLabel(CString sLabel)
{
VALIDATE;
_ASSERTE(! sLabel.IsEmpty());
m_sXAxisLabel = sLabel;
}
//
void CGraph::SetYAxisLabel(CString sLabel)
{
VALIDATE;
_ASSERTE(! sLabel.IsEmpty());
m_sYAxisLabel = sLabel;
}
// Returns the group number added. Also, makes sure that all the series have
// this many elements.
int CGraph::AppendGroup(CString sLabel)
{
VALIDATE;
_ASSERTE(! sLabel.IsEmpty());
// Add the group.
int nGroup((int)m_paLegend.GetSize());
if (nGroup < MAX_GROUPS_ALLOWED)
{
AddLegend(nGroup, sLabel);
// Make sure that all series have this element.
POSITION pos(m_olCGraphSeries.GetHeadPosition());
while (pos) {
CGraphSeries* pSeries = (CGraphSeries*)m_olCGraphSeries.GetNext(pos);
ASSERT_VALID(pSeries);
if (nGroup >= pSeries->m_dwaValues.GetSize()) {
pSeries->m_dwaValues.SetAtGrow(nGroup, 0);
}
}
}
else
{
nGroup = -1;
}
return nGroup;
}
// Set this value to the legend.
void CGraph::AddLegend(int nGroup, CString sLabel)
{
VALIDATE;
_ASSERTE(nGroup <= m_paLegend.GetSize());
_ASSERTE(nGroup < MAX_GROUPS_ALLOWED);
_ASSERTE(! sLabel.IsEmpty());
PGRAPHICLEGEND pLegend = new GRAPHICLEGEND;
if(pLegend == NULL) return;
pLegend->sLabel = sLabel;
if (m_dwaColors.GetCount()) {
pLegend->dwColor = m_dwaColors.GetAt(0);
m_dwaColors.RemoveAt(0);
} else {
delete pLegend;
return;
}
m_paLegend.SetAtGrow(nGroup, (void*)pLegend);
}
void CGraph::RenameLegend(int nGroup, CString sLabel)
{
VALIDATE;
_ASSERTE(nGroup < m_paLegend.GetSize());
_ASSERTE(nGroup < MAX_GROUPS_ALLOWED);
_ASSERTE(! sLabel.IsEmpty());
PGRAPHICLEGEND pLegend = (PGRAPHICLEGEND)m_paLegend.GetAt(nGroup);
pLegend->sLabel = sLabel;
}
void CGraph::SetGraphTitle(CString sTitle)
{
VALIDATE;
_ASSERTE(! sTitle.IsEmpty());
m_sTitle = sTitle;
}
void CGraph::DrawGraph(CDC* pDc)
{
VALIDATE;
ASSERT_VALID(pDc);
pDc->SetBkMode(TRANSPARENT);
CWnd* graphWnd = pDc->GetWindow();
CRect rcWnd;
graphWnd->GetClientRect(&rcWnd);
// Reduce the graphable area by the frame window and status bar. We will
// leave GAP_PIXELS pixels blank on all sides of the graph. So top-left
// side of graph is at GAP_PIXELS,GAP_PIXELS and the bottom-right side
// of graph is at (m_rcGraph.Height() - GAP_PIXELS), (m_rcGraph.Width() -
// GAP_PIXELS). These settings are altered by axis labels and legends.
m_rcGraph.left = GAP_PIXELS;
m_rcGraph.top = GAP_PIXELS;
m_rcGraph.right = rcWnd.Width() - GAP_PIXELS;
m_rcGraph.bottom = rcWnd.Height() - GAP_PIXELS;
CBrush br;
VERIFY(br.CreateSolidBrush(::GetSysColor(COLOR_WINDOW)));
pDc->FillRect(rcWnd, &br);
// Draw graph title.
DrawTitle(pDc);
// Set the axes and origin values.
SetupAxes(pDc);
// Draw axes unless it's a pie.
if (m_eGraphType != CGraph::Pie) {
DrawAxes(pDc);
}
try {
// Draw legend if there is one.
if (m_paLegend.GetSize()) {
DrawLegend(pDc);
}
// Draw series data and labels.
switch (m_eGraphType) {
case CGraph::Bar: DrawSeriesBar(pDc); break;
case CGraph::Line: DrawSeriesLine(pDc); break;
case CGraph::Pie: DrawSeriesPie(pDc); break;
default: _ASSERTE(! "Bad default case"); break;
}
}
catch(...) {TRACE("Handled Exception\n");}
}
// Draw graph title; size is proportionate to width.
void CGraph::DrawTitle(CDC* pDc)
{
VALIDATE;
ASSERT_VALID(pDc);
// Create the title font.
CFont fontTitle;
VERIFY(fontTitle.CreatePointFont(m_rcGraph.Width() / TITLE_DIVISOR,
"Arial", pDc));
CFont* pFontOld = static_cast<CFont*> (pDc->SelectObject(&fontTitle));
ASSERT_VALID(pFontOld);
// Draw the title.
m_rcTitle.SetRect(GAP_PIXELS, GAP_PIXELS, m_rcGraph.Width() + GAP_PIXELS,
m_rcGraph.Height() + GAP_PIXELS);
pDc->DrawText(m_sTitle, m_rcTitle, DT_CENTER | DT_NOPREFIX | DT_SINGLELINE |
DT_TOP | DT_CALCRECT);
m_rcTitle.right = m_rcGraph.Width() + GAP_PIXELS;
pDc->DrawText(m_sTitle, m_rcTitle, DT_CENTER | DT_NOPREFIX | DT_SINGLELINE |
DT_TOP);
VERIFY(pDc->SelectObject(pFontOld));
}
// Set the axes and origin values.
void CGraph::SetupAxes(CDC* pDc)
{
VALIDATE;
ASSERT_VALID(pDc);
// Since pie has no axis lines, set to full size minus GAP_PIXELS on each
// side. These are needed for legend to plot itself.
if (CGraph::Pie == m_eGraphType) {
m_nXAxisWidth = m_rcGraph.Width() - (GAP_PIXELS * 2);
m_nYAxisHeight = m_rcGraph.Height() - m_rcTitle.bottom;
m_ptOrigin.x = GAP_PIXELS;
m_ptOrigin.y = m_rcGraph.Height() - GAP_PIXELS;
}
else {
// Bar and Line graphs.
CString sTickLabel;
sTickLabel.Format("-%d", GetMaxDataValue());
if (m_nMaxDataValueAllowed == INVALID_MAX_VALUE) {// no Max data value
sTickLabel.Format("-%d", GetMaxDataValue());
}
else {
sTickLabel.Format("-%d", m_nMaxDataValueAllowed);
}
CSize sizTickLabel(pDc->GetTextExtent(sTickLabel));
// Determine axis specifications. Assume tick label and axes label
// fonts are about the same size.
m_ptOrigin.x = GAP_PIXELS + sizTickLabel.cx + GAP_PIXELS +
sizTickLabel.cy + GAP_PIXELS + TICK_PIXELS;
m_ptOrigin.y = m_rcGraph.Height() - sizTickLabel.cy - GAP_PIXELS -
sizTickLabel.cy - GAP_PIXELS - TICK_PIXELS;
m_nYAxisHeight = m_ptOrigin.y - m_rcTitle.bottom - (2 * GAP_PIXELS);
m_nXAxisWidth = (m_rcGraph.Width() - GAP_PIXELS) - m_ptOrigin.x;
}
}
//
void CGraph::DrawLegend(CDC* pDc)
{
VALIDATE;
ASSERT_VALID(pDc);
// Create the legend font.
CFont fontLegend;
VERIFY(fontLegend.CreatePointFont(m_rcGraph.Height() / LEGEND_DIVISOR,
"Arial", pDc));
CFont* pFontOld = static_cast<CFont*> (pDc->SelectObject(&fontLegend));
ASSERT_VALID(pFontOld);
// Get the height of each label.
LOGFONT lf;
::ZeroMemory(&lf, sizeof(lf));
VERIFY(fontLegend.GetLogFont(&lf));
int nLabelHeight(abs(lf.lfHeight));
// Determine size of legend. A buffer of (GAP_PIXELS / 2) on each side,
// plus the height of each label based on the pint size of the font.
int nLegendHeight((GAP_PIXELS / 2) + ((int)m_paLegend.GetSize() * nLabelHeight) +
(GAP_PIXELS / 2));
// Draw the legend border. Allow LEGEND_COLOR_BAR_PIXELS pixels for
// display of label bars.
m_rcLegend.top = (m_rcGraph.Height() / 2) - (nLegendHeight / 2);
m_rcLegend.bottom = m_rcLegend.top + nLegendHeight;
m_rcLegend.right = m_rcGraph.Width() - GAP_PIXELS;
m_rcLegend.left = m_rcLegend.right - GetMaxLegendLabelLength(pDc) -
LEGEND_COLOR_BAR_WIDTH_PIXELS;
VERIFY(pDc->Rectangle(m_rcLegend));
// Draw each group's label and bar.
for (int nGroup = 0; nGroup < m_paLegend.GetSize(); ++nGroup)
{
int nLabelTop(m_rcLegend.top + (nGroup * nLabelHeight) +
(GAP_PIXELS / 2));
// Draw the label.
VERIFY(pDc->TextOut(m_rcLegend.left + GAP_PIXELS, nLabelTop,
((PGRAPHICLEGEND)(m_paLegend.GetAt(nGroup)))->sLabel));
// Determine the bar.
CRect rcBar;
rcBar.left = m_rcLegend.left + GAP_PIXELS + GetMaxLegendLabelLength(pDc) +
GAP_PIXELS;
rcBar.top = nLabelTop + LEGEND_COLOR_BAR_GAP_PIXELS;
rcBar.right = m_rcLegend.right - GAP_PIXELS;
rcBar.bottom = rcBar.top + nLabelHeight - LEGEND_COLOR_BAR_GAP_PIXELS;
VERIFY(pDc->Rectangle(rcBar));
// Draw bar for group.
//COLORREF crBar(m_dwaColors.GetAt(nGroup));
COLORREF crBar(((PGRAPHICLEGEND)(m_paLegend.GetAt(nGroup)))->dwColor);
CBrush br(crBar);
CBrush* pBrushOld = pDc->SelectObject(&br);
ASSERT_VALID(pBrushOld);
pDc->SelectObject(&pBrushOld);
rcBar.DeflateRect(LEGEND_COLOR_BAR_GAP_PIXELS, LEGEND_COLOR_BAR_GAP_PIXELS);
pDc->FillRect(rcBar, &br);
}
VERIFY(pDc->SelectObject(pFontOld));
}
//
void CGraph::DrawAxes(CDC* pDc)
{
VALIDATE;
ASSERT_VALID(pDc);
_ASSERTE(CGraph::Pie != m_eGraphType);
pDc->SetTextColor(::GetSysColor(COLOR_WINDOWTEXT));
// Draw y axis.
pDc->MoveTo(m_ptOrigin);
VERIFY(pDc->LineTo(m_ptOrigin.x, m_ptOrigin.y - m_nYAxisHeight));
// Draw x axis.
pDc->MoveTo(m_ptOrigin);
if (m_paLegend.GetSize()) {
VERIFY(pDc->LineTo(m_ptOrigin.x +
(m_nXAxisWidth - m_rcLegend.Width() - (GAP_PIXELS * 2)),
m_ptOrigin.y));
}
else {
VERIFY(pDc->LineTo(m_ptOrigin.x + m_nXAxisWidth, m_ptOrigin.y));
}
// Create the y-axis label font and draw it.
CFont fontYAxes;
VERIFY(fontYAxes.CreateFont(
/* nHeight */ 15,
/* nWidth */ 0, /* nEscapement */ 90 * 10, /* nOrientation */ 0,
/* nWeight */ FW_DONTCARE, /* bItalic */ false, /* bUnderline */ false,
/* cStrikeOut */ 0, ANSI_CHARSET, OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS, PROOF_QUALITY, VARIABLE_PITCH | FF_DONTCARE,
"Arial"));
CFont* pFontOld = static_cast<CFont*> (pDc->SelectObject(&fontYAxes));
ASSERT_VALID(pFontOld);
CSize sizYLabel(pDc->GetTextExtent(m_sYAxisLabel));
VERIFY(pDc->TextOut(GAP_PIXELS, (m_rcGraph.Height() - sizYLabel.cy)*3/4,
m_sYAxisLabel));
// Create the x-axis label font and draw it.
CFont fontXAxes;
VERIFY(fontXAxes.CreatePointFont(100,
"Arial", pDc));
VERIFY(pDc->SelectObject(&fontXAxes));
CSize sizXLabel(pDc->GetTextExtent(m_sXAxisLabel));
VERIFY(pDc->TextOut(m_ptOrigin.x + (m_nXAxisWidth - sizXLabel.cx) / 2,
m_rcGraph.Height() - (sizXLabel.cy/2),
m_sXAxisLabel));
// Print the date and time.
CTime logTime = CTime::GetCurrentTime();
CString timeLabel= logTime.Format( "%m/%d/%Y, %H:%M" );
CFont fontTimeLabel;
VERIFY(fontTimeLabel.CreatePointFont(100,
"Arial", pDc));
VERIFY(pDc->SelectObject(&fontTimeLabel));
CSize sizTimeLabel(pDc->GetTextExtent(timeLabel));
VERIFY(pDc->TextOut(m_ptOrigin.x ,
m_rcGraph.Height() - (sizTimeLabel.cy/2),
timeLabel));
// We hardwire TITLE_DIVISOR y-axis ticks here for simplicity.
int nMaxValue;
if (m_nMaxDataValueAllowed == INVALID_MAX_VALUE) {// no Max data value
nMaxValue = GetMaxDataValue();
}
else {
nMaxValue = m_nMaxDataValueAllowed;
}
int nTickCount(min(Y_AXIS_MAX_TICK_COUNT, ((nMaxValue-m_nYAxisShift) > 0) ? (nMaxValue-m_nYAxisShift) : 0));
float nTickSpace(0);
if (nTickCount)
nTickSpace = (float)m_nYAxisHeight / (float)nTickCount;
nMaxValue -= m_nYAxisShift;
for (int nTick = 0; nTick <= nTickCount; ++nTick) {
int nTickYLocation(m_ptOrigin.y - (int)(nTickSpace * nTick));
pDc->MoveTo(m_ptOrigin.x - TICK_PIXELS, nTickYLocation);
VERIFY(pDc->LineTo(m_ptOrigin.x + TICK_PIXELS, nTickYLocation));
// Draw tick label.
CString sTickLabel;
sTickLabel.Format("%d", ((nMaxValue * nTick) / nTickCount)+m_nYAxisShift);
CSize sizTickLabel(pDc->GetTextExtent(sTickLabel));
VERIFY(pDc->TextOut(m_ptOrigin.x - GAP_PIXELS - sizTickLabel.cx - TICK_PIXELS,
nTickYLocation - (sizTickLabel.cy/2), sTickLabel));
}
// Draw X axis tick marks.
// Get the spacing of the series.
float nSeriesSpace(0);
nSeriesSpace = (float)((float)(m_nXAxisWidth - m_rcLegend.Width() - (GAP_PIXELS * 2)) / (float)(m_nXAxisScale+1));
int jj = (m_nXAxisScale+20)/30;
int kk;
if (m_nXAxisScale == 10)
kk = 1;
else if (m_nXAxisScale > 10 && m_nXAxisScale <= 20)
kk = 2;
else
kk = jj * 3;
for (int ii=0; ii<=m_nXAxisScale; ii++) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -