📄 graph.cpp
字号:
// Graph.cpp : implementation file
#include "stdafx.h"
#include "CDrawLine.h"//应改为对应应用程序的头文件
#include "GraphSeries.h"
#include "GraphLegend.h"
#include "math.h"
#include "Graph.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#define GREEN RGB(0,255,0)
#define BLUE RGB(0,0,255)
#define RED RGB(255,0,0)
#define YELLOW RGB(255,255,0)
#define ORANGE RGB(255,153,51)
#define HOT_PINK RGB(255,51,153)
#define PURPLE RGB(153,0,204)
#define CYAN RGB(0,255,255)
#define BLACK RGB(0,0,0)
#define WHITE RGB(255,255,255)
/////////////////////////////////////////////////////////////////////////////
// CGraph
CGraph::CGraph()
{
graphSeries = new CObList();
tickRange = 100;
seriesSize = 0;
graphHasLegend = FALSE;
legendWidth = 0;
graphType = 0;
//以下为设置图例的背景色
dataColor[0] = GREEN; //green
dataColor[1] = BLUE; //blue
dataColor[2] = RED; //red
dataColor[3] = YELLOW; //yellow
dataColor[4] = ORANGE; //orange
dataColor[5] = HOT_PINK; //hot pink
dataColor[6] = PURPLE; //purple
dataColor[7] = CYAN; //cyan
dataColor[8] = BLACK; //black
dataColor[9] = WHITE; //white
//以上为图例的背景色
xAxisAlign = 0; //horizontal
xAxisLabelLength = 0;
xTickFontSize = 12;//x轴的单元格字体大小
yTickFontSize = 12;//y轴的单元格字体
legendFontSize = 12;//图例中的字体大小
m_DrawMode=DEFAULTMODE;
}
CGraph::CGraph(int type,int drawmode)
{
graphSeries = new CObList();
tickRange = 100;
seriesSize = 0;
graphHasLegend = FALSE;
legendWidth = 0;
m_DrawMode=drawmode;
graphType = type;
//以下为设置图例的背景色
dataColor[0] = GREEN; //green
dataColor[1] = BLUE; //blue
dataColor[2] = RED; //red
dataColor[3] = YELLOW; //yellow
dataColor[4] = ORANGE; //orange
dataColor[5] = HOT_PINK; //hot pink
dataColor[6] = PURPLE; //purple
dataColor[7] = CYAN; //cyan
dataColor[8] = BLACK; //black
dataColor[9] = WHITE; //white
//以上为图例的背景色
xAxisAlign = 0; //horizontal
xAxisLabelLength = 0;
xTickFontSize = 12;//x轴的单元格字体大小
yTickFontSize = 12;//y轴的单元格字体大小
legendFontSize =16;//图例中的字体大小
}
CGraph::~CGraph()
{
POSITION pos;
CGraphSeries* pSeries;
for( pos = graphSeries->GetHeadPosition(); pos != NULL; )
{
pSeries = (CGraphSeries*) graphSeries->GetNext( pos );
delete pSeries;
}
graphSeries->RemoveAll();
delete graphSeries;
}
BEGIN_MESSAGE_MAP(CGraph, CStatic)
//{{AFX_MSG_MAP(CGraph)
// NOTE - the ClassWizard will add and remove mapping macros here.
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CGraph message handlers
void CGraph::SetTickSpace(int yDist)
{
tickSpace = yDist;
}
void CGraph::SetTickRange(int maxTick)
{
tickRange = maxTick;
}
void CGraph::SetGraphType(int gType)
{
graphType = gType;
}
void CGraph::SetXAxisAlignment(int alignValue)
{
xAxisAlign = alignValue;
}
int CGraph::GetXAxisAlignment()
{
return xAxisAlign;
}
void CGraph::DrawGraph(CDC* pDC)
{
CString tickLabel;
CWnd* graphWnd = pDC->GetWindow();//此调用得到pDC对应的窗口,即此绘图的区域
CRect graphRect;
graphWnd->GetClientRect(&graphRect);
//reset graph to be clear background
COLORREF backColor;
backColor = RGB(255,255,255); //设置你所希望的背景色
m_background=backColor;
CBrush backBrush (backColor);
CBrush* pOldBackBrush;
pOldBackBrush = pDC->SelectObject(&backBrush);
pDC->Rectangle(0, 0, graphRect.Width(), graphRect.Height());
pDC->SelectObject(pOldBackBrush);
pDC->SetBkMode(TRANSPARENT);
maxHeight = graphRect.Height() - 20; //for frame window and status bar
maxWidth = graphRect.Width() - 5; //for frame window
//We will leave 5 pixels blank on all sides of the graph. So
//top-left side of graph is at 5,5 and the bottom-right side of
//graph is at ((maxHeight - 5), (maxWidth - 5))
//these settings are altered by axis labels and legends.
//draw graph title
CFont titleFont;//设置标题的自体
titleFont.CreateFont(28, 0, 0, 0, 700, FALSE, FALSE, 0,
ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY, DEFAULT_PITCH | FF_ROMAN,"Arial");
CFont* pOldFont = (CFont*) pDC->SelectObject(&titleFont);
pDC->TextOut((maxWidth / 2) - ((graphTitle.GetLength() * 12) / 2),
10, graphTitle);//绘制标题的位置
pDC->SelectObject(pOldFont);
if(graphType == 2) //pie
{
//since pie does not have axis lines, set to full size minus 5 on each side
//these are needed for legend to plot itself
xAxisWidth = maxWidth - 10;
yAxisHeight = maxHeight - 20; //10 buffer and 20 for title
xApexPoint = 5;
yApexPoint = maxHeight - 5;
}
else
{
//X-axis will be raised by 20 pixels for tick-labels
//Y-axis will be shifted by (max(tick-label size) * 10) + label * 10
tickLabel.Format("%d", tickRange);
//determine axis specifications
//tick label offset is 4 (for the tick) + 6 (total 10) from axis line
xApexPoint = 5 + (tickLabel.GetLength() * (yTickFontSize/ 2 )) + 10 + 30/*(axisYLabel.GetLength() * 8)*/ + 5; //allowing 8 pixels per char in tick label
//y apex based on 5 + 15 (x label) + 4 (for the tick) + 4 (text below tick) + 12 (tick label) + 10
if(!xAxisAlign) //horizontal
yApexPoint = (maxHeight - 5) - 45; //apex points are the cross section of axis lines
else
yApexPoint = (maxHeight - 5) - (xAxisLabelLength * (xTickFontSize / 2)) - 10;
yAxisHeight = yApexPoint - 40;//从客户区往下40个像素
xAxisWidth = (maxWidth - 5) - xApexPoint;//实际长度为maxwidth-5
}
//draw legend
if(graphHasLegend)
DrawLegend(pDC);
if(graphType != 2) //pie
{
//draw axis lines
DrawAxis(pDC);
}
//draw series data and labels
DrawSeries(pDC);
}
void CGraph::DrawAxis(CDC* pDC)
{
pDC->SetTextColor(RGB(0,0,0));
BOOL drawarrow=FALSE;
//draw y axis
double tickScale = 0.00;//tickScale按比例缩小y轴单元
if(tickRange > yAxisHeight)
tickScale = ((yAxisHeight * 1.00) / (tickRange * 1.00)) * tickSpace;
else tickScale = tickSpace;
int ymutiple;
ymutiple = tickRange / tickSpace;
m_nUnits=yAxisHeight/ymutiple;//设置每个逻辑单元的像素数目
int ylength = yApexPoint - (ymutiple* tickScale);
if(m_DrawMode==DEFAULTMODE)
{
pDC->MoveTo(xApexPoint, yApexPoint);
pDC->LineTo(xApexPoint, ylength);//(yApexPoint - yAxisHeight)被ylength替换
}
else
{
pDC->MoveTo(xApexPoint, yApexPoint);
pDC->LineTo(xApexPoint, yApexPoint - yAxisHeight);
}
//draw x axis
POINT xaxisRight;
pDC->MoveTo(xApexPoint, yApexPoint);
if(graphHasLegend)
{
pDC->LineTo(xApexPoint + (xAxisWidth - legendWidth - 10), yApexPoint);
xaxisRight.x=xApexPoint+ (xAxisWidth - legendWidth - 10);
}
else
{
pDC->LineTo(xApexPoint + xAxisWidth, yApexPoint);
xaxisRight.x=xApexPoint + xAxisWidth;
}
xaxisRight.y =yApexPoint;
//绘制x轴的箭头
POINT pointx4[4];
pointx4[0].x=xaxisRight.x;
pointx4[0].y=xaxisRight.y;
pointx4[1].x=xaxisRight.x-10;
pointx4[1].y=xaxisRight.y+4;
pointx4[2].x=xaxisRight.x-8;
pointx4[2].y=xaxisRight.y;
pointx4[3].x=xaxisRight.x-10;
pointx4[3].y=xaxisRight.y-4;
HRGN arrowrgnx;
arrowrgnx=::CreatePolygonRgn(pointx4,4,WINDING);
::Polygon(pDC->m_hDC,pointx4,4);
::InvertRgn(pDC->m_hDC,arrowrgnx);
//以上绘制x轴的箭头
//draw labels
CFont sideFont;
sideFont.CreateFont(16, 0, 900, 0, 700, FALSE, FALSE, 0,
ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY, DEFAULT_PITCH | FF_ROMAN,"Arial");//创建y轴标签字体
CFont* pOldFont = (CFont*) pDC->SelectObject(&sideFont);
pDC->TextOut(10, (maxHeight / 2) + ((axisYLabel.GetLength() / 2) * 4), axisYLabel);
pDC->SelectObject(pOldFont);
pDC->TextOut(xApexPoint + (xAxisWidth / 2) - ((axisXLabel.GetLength() / 2) * 19), maxHeight - 5 - 6, axisXLabel);
//draw y axis ticks
for(int y = 1; y <= tickRange / tickSpace; y++) //no tick at 0
{
int tickYLocation = yApexPoint - (y * tickScale);
if(y < tickRange / tickSpace&&m_DrawMode==DEFAULTMODE)
{
pDC->MoveTo(xApexPoint - 4, tickYLocation);
pDC->LineTo(xApexPoint + 4, tickYLocation);
}
else if(m_DrawMode==DEFAULTMODE)//绘制y轴顶端的箭头(其中点为坐标值)
{
POINT point4[4];
point4[0].x=xApexPoint;
point4[0].y=tickYLocation-6;
point4[1].x=xApexPoint-4;
point4[1].y=tickYLocation+6;
point4[2].x=xApexPoint;
point4[2].y=tickYLocation+2;
point4[3].x=xApexPoint+4;
point4[3].y=tickYLocation+6;
HRGN arrowrgn;
arrowrgn=::CreatePolygonRgn(point4,4,WINDING);
::Polygon(pDC->m_hDC,point4,4);
::InvertRgn(pDC->m_hDC,arrowrgn);
}
else
{
tickYLocation = yApexPoint - (y *m_nUnits);
if(drawarrow==FALSE)
{
POINT point4[4];
point4[0].x=xApexPoint;
point4[0].y=yApexPoint -yAxisHeight-6;
point4[1].x=xApexPoint-4;
point4[1].y=yApexPoint -yAxisHeight+6;
point4[2].x=xApexPoint;
point4[2].y=yApexPoint -yAxisHeight+2;
point4[3].x=xApexPoint+4;
point4[3].y=yApexPoint -yAxisHeight+6;
HRGN arrowrgn;
arrowrgn=::CreatePolygonRgn(point4,4,WINDING);
::Polygon(pDC->m_hDC,point4,4);
::InvertRgn(pDC->m_hDC,arrowrgn);
drawarrow=TRUE;
}
pDC->MoveTo(xApexPoint - 4, tickYLocation);
pDC->LineTo(xApexPoint + 4, tickYLocation);
}
//draw tick label
CString tickLabel;
tickLabel.Format("%d", y * tickSpace);
CFont yFont;
yFont.CreateFont(yTickFontSize, 0, 0, 0, 700, FALSE, FALSE, 0,
ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY, DEFAULT_PITCH | FF_ROMAN,"Arial");
pOldFont = (CFont*) pDC->SelectObject(&yFont);
pDC->TextOut(xApexPoint - 10 - (tickLabel.GetLength() * (yTickFontSize / 2)), tickYLocation - 6, tickLabel);
pDC->SelectObject(pOldFont);
}
//draw X axis tick marks
POSITION pos;
pos = graphSeries->GetHeadPosition();
CGraphSeries* tmpSeries;
for(int x = 1; x <= graphSeries->GetCount(); x++)
{
tmpSeries = (CGraphSeries*)graphSeries->GetNext(pos);
int seriesSpace;
int tickXLocation;
if(graphHasLegend)
seriesSpace= (xAxisWidth - legendWidth - 10) / graphSeries->GetCount();
else
seriesSpace= xAxisWidth / graphSeries->GetCount();
tickXLocation = xApexPoint + ((x * seriesSpace) - (seriesSpace / 2));
pDC->MoveTo(tickXLocation,yApexPoint - 4);
pDC->LineTo(tickXLocation,yApexPoint + 4);
//draw tick label
CString tickLabel;
tickLabel = tmpSeries->GetLabel();
if(!xAxisAlign) //horizontal
pDC->TextOut(tickXLocation - ((tickLabel.GetLength() * 8) / 2), yApexPoint + 8, tickLabel);
else
{
CFont sideFont2;
sideFont2.CreateFont(xTickFontSize, 0, (xAxisAlign * 10), 0, 700, FALSE, FALSE, 0,
ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY, DEFAULT_PITCH | FF_ROMAN,"Arial");
pOldFont = (CFont*) pDC->SelectObject(&sideFont2);
if(xAxisAlign < 180)
pDC->TextOut(tickXLocation - 8, yApexPoint + 8 + (xAxisLabelLength * (xTickFontSize / 2)), tickLabel);
else
pDC->TextOut(tickXLocation + 2, yApexPoint + 8, tickLabel);
pDC->SelectObject(pOldFont);
}
}
}
void CGraph::AddSeries(CGraphSeries* dataSet)
{
int numData = 0;
for(int i = 0; i < 10; i++)
{
if(dataSet->GetData(i) > 0)
numData++;
if(dataSet->GetLabel().GetLength() > xAxisLabelLength)
xAxisLabelLength = dataSet->GetLabel().GetLength();
}
if(numData > seriesSize)
seriesSize = numData;
graphSeries->AddTail(dataSet);
}
void CGraph::SetXAxisLabel(CString label)
{
axisXLabel = label;
}
void CGraph::SetYAxisLabel(CString label)
{
axisYLabel = label;
}
void CGraph::SetColor(int group, COLORREF groupColor)
{
dataColor[group] = groupColor;
}
COLORREF CGraph::GetColor(int group)
{
return dataColor[group];
}
void CGraph::DrawSeries(CDC* pDC)
{
int barWidth;
int dataPlotSize; //used to plot rects of data
int barL, barT, barR, barB;
int tickXLocation;
int seriesSpace;
double barHeight; //for scalability
POSITION pos;
CGraphSeries* tmpSeries;
if(graphType == 0) //bar
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -