📄 graph.cpp
字号:
int nTickXLocation((int)(m_ptOrigin.x + ii * nSeriesSpace));
ASSERT(m_nXAxisScale >= 10);
if(ii%jj == 0) {
pDc->MoveTo(nTickXLocation, m_ptOrigin.y - TICK_PIXELS);
VERIFY(pDc->LineTo(nTickXLocation, m_ptOrigin.y + TICK_PIXELS));
if(ii%kk == 0) {
// Draw x-axis tick label.
CString sTickLabel;
CFileTime logFT = m_nXAxisStartFT.UTCToLocal();
BYTE Minute = (BYTE)(((logFT.GetTime() + ii * CFileTime::Second) % CFileTime::Hour)/CFileTime::Minute);
BYTE Second = (BYTE)(((logFT.GetTime() + ii * CFileTime::Second) % CFileTime::Minute)/CFileTime::Second);
sTickLabel.Format("%.2d:%.2d", Minute, Second);
CSize sizTickLabel(pDc->GetTextExtent(sTickLabel));
VERIFY(pDc->TextOut(nTickXLocation - (sizTickLabel.cx / 2),
m_ptOrigin.y + sizTickLabel.cy, sTickLabel));
}
}
}
VERIFY(pDc->SelectObject(pFontOld));
}
//
void CGraph::DrawSeriesBar(CDC* pDc)
{
VALIDATE;
ASSERT_VALID(pDc);
// How much space does each series get (includes interseries space)?
// We ignore series whose members are all zero.
int nSeriesSpace(0);
if (m_paLegend.GetSize()) {
nSeriesSpace = (m_nXAxisWidth - m_rcLegend.Width() - (GAP_PIXELS * 2)) /
GetNonZeroSeriesCount();
}
else {
nSeriesSpace = m_nXAxisWidth / GetNonZeroSeriesCount();
}
// Determine width of bars. Data points with a value of zero are assumed
// to be empty. This is a bad assumption.
int nBarWidth(nSeriesSpace / GetMaxNonZeroSeriesSize());
if (1 < GetNonZeroSeriesCount()) {
nBarWidth = (int) ((double) nBarWidth * INTERSERIES_PERCENT_USED);
}
// This is the width of the largest series (no interseries space).
int nMaxSeriesPlotSize(GetMaxNonZeroSeriesSize() * nBarWidth);
// Iterate the series.
POSITION pos(m_olCGraphSeries.GetHeadPosition());
int nSeries(0);
while (pos) {
CGraphSeries* pSeries = (CGraphSeries*)m_olCGraphSeries.GetNext(pos);
ASSERT_VALID(pSeries);
// Ignore unpopulated series.
if (0 < pSeries->GetNonZeroElementCount()) {
// Draw each bar; empty bars are not drawn.
int nRunningLeft(m_ptOrigin.x + ((nSeries + 1) * nSeriesSpace) -
nMaxSeriesPlotSize);
for (int nGroup = 0; nGroup < GetMaxSeriesSize(); ++nGroup) {
if (pSeries->GetData(nGroup) > 0) {
CRect rcBar;
rcBar.left = nRunningLeft;
rcBar.top = m_ptOrigin.y - (m_nYAxisHeight *
pSeries->GetData(nGroup)) / (GetMaxDataValue()-m_nYAxisShift);
rcBar.right = rcBar.left + nBarWidth;
rcBar.bottom = m_ptOrigin.y;
//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);
VERIFY(pDc->Rectangle(rcBar));
pDc->SelectObject(&pBrushOld);
nRunningLeft += nBarWidth;
}
}
++nSeries;
}
}
}
//
void CGraph::DrawSeriesLine(CDC* pDc)
{
VALIDATE;
ASSERT_VALID(pDc);
// Iterate the groups.
for (int nGroup = 0; nGroup < GetMaxSeriesSize(); nGroup++) {
// How much space does each series get (includes interseries space)?
float nSeriesSpace(0);
nSeriesSpace = (float)((float)(m_nXAxisWidth - m_rcLegend.Width() - (GAP_PIXELS * 2)) / (float)(m_nXAxisScale+1));
int nBarWidth((int)nSeriesSpace / GetMaxSeriesSize());
if (1 < m_olCGraphSeries.GetCount()) {
nBarWidth = (int) ((double) nBarWidth * INTERSERIES_PERCENT_USED);
}
// This is the width of the largest series (no interseries space).
int nMaxSeriesPlotSize(GetMaxSeriesSize() * nBarWidth);
// Iterate the series.
POSITION pos(m_olCGraphSeries.GetHeadPosition());
bool firstSeries = true;
CPoint ptLastLoc(0,0);
for (int nSeries = 0; nSeries < m_olCGraphSeries.GetCount(); ++nSeries) {
CGraphSeries* pSeries = (CGraphSeries*)m_olCGraphSeries.GetNext(pos);
ASSERT_VALID(pSeries);
// Get x and y location of center of ellipse.
CPoint ptLoc(0,0);
CFileTime logFT = m_nXAxisStartFT.UTCToLocal();
ptLoc.x = m_ptOrigin.x +
(LONG)((_atoi64(pSeries->m_sLabel.Right(20)) - logFT.GetTime())*nSeriesSpace/CFileTime::Second);
if (((int)pSeries->m_dwaValues.GetUpperBound() >= nGroup) &&
(pSeries->m_dwaValues[nGroup]) &&
(pSeries->GetData(nGroup) >= m_nYAxisShift))
{
double dLineHeight = 0;
int nMaxValue;
if (m_nMaxDataValueAllowed == INVALID_MAX_VALUE) {// no Max data value
nMaxValue = (GetMaxDataValue()-m_nYAxisShift);
}
else {
nMaxValue = (m_nMaxDataValueAllowed-m_nYAxisShift);
}
if (nMaxValue != 0)
dLineHeight = ((pSeries->GetData(nGroup)-m_nYAxisShift) * (float)m_nYAxisHeight)/(float)nMaxValue;
ptLoc.y = (int) ((double) m_ptOrigin.y - dLineHeight);
// Build objects.
_ASSERTE(nGroup <= m_paLegend.GetUpperBound());
COLORREF crLine(((PGRAPHICLEGEND)(m_paLegend.GetAt(nGroup)))->dwColor);
CBrush br(crLine);
CBrush* pBrushOld = pDc->SelectObject(&br);
ASSERT_VALID(pBrushOld);
// Draw line back to last data member.
if (!firstSeries) {
CPen penLine(PS_SOLID, 1, crLine);
CPen* pPenOld = pDc->SelectObject(&penLine);
ASSERT_VALID(pPenOld);
pDc->MoveTo(ptLastLoc.x + 2, ptLastLoc.y - 1);
VERIFY(pDc->LineTo(ptLoc.x - 3, ptLoc.y - 1));
VERIFY(pDc->SelectObject(pPenOld));
}
else firstSeries = false;
// Now draw ellipse.
CRect rcEllipse(ptLoc.x - 3, ptLoc.y - 3, ptLoc.x + 3, ptLoc.y + 3);
VERIFY(pDc->Ellipse(rcEllipse));
pDc->SelectObject(&pBrushOld);
ptLastLoc = ptLoc;
}
}
}
}
//
void CGraph::DrawSeriesPie(CDC* pDc)
{
VALIDATE;
ASSERT_VALID(pDc);
_ASSERTE(0 < GetNonZeroSeriesCount() && "Div by zero");
// Determine width of pie display area (pie and space).
int nSeriesSpace(0);
if (m_paLegend.GetSize()) {
int nPieAndSpaceWidth((m_nXAxisWidth - m_rcLegend.Width() -
(GAP_PIXELS * 2)) / GetNonZeroSeriesCount());
// Height is limiting factor.
if (nPieAndSpaceWidth > m_nYAxisHeight - (GAP_PIXELS * 2)) {
nSeriesSpace = (m_nYAxisHeight - (GAP_PIXELS * 2)) /
GetNonZeroSeriesCount();
}
else {
// Width is limiting factor.
nSeriesSpace = nPieAndSpaceWidth;
}
}
else {
// No legend box.
// Height is limiting factor.
if (m_nXAxisWidth > m_nYAxisHeight) {
nSeriesSpace = m_nYAxisHeight / GetNonZeroSeriesCount();
}
else {
// Width is limiting factor.
nSeriesSpace = m_nXAxisWidth / GetNonZeroSeriesCount();
}
}
// Draw each pie.
int nPie(0);
int nRadius((int) (nSeriesSpace * INTERSERIES_PERCENT_USED / 2.0));
POSITION pos(m_olCGraphSeries.GetHeadPosition());
while (pos) {
CGraphSeries* pSeries = (CGraphSeries*)m_olCGraphSeries.GetNext(pos);
ASSERT_VALID(pSeries);
// Don't leave a space for empty pies.
if (0 < pSeries->GetNonZeroElementCount()) {
// Locate this pie.
CRect rcPie;
rcPie.left = m_ptOrigin.x + GAP_PIXELS + (nSeriesSpace * nPie);
rcPie.right = rcPie.left + (2 * nRadius);
rcPie.top = (m_nYAxisHeight / 2) - nRadius;
rcPie.bottom = (m_nYAxisHeight / 2) + nRadius;
CPoint ptCenter((rcPie.left + rcPie.right) / 2,
(rcPie.top + rcPie.bottom) / 2);
// Draw series label.
CSize sizPieLabel(pDc->GetTextExtent(pSeries->GetLabel()));
VERIFY(pDc->TextOut((rcPie.left + nRadius) - (sizPieLabel.cx / 2),
ptCenter.y + nRadius + GAP_PIXELS, pSeries->GetLabel()));
// How much do the wedges total to?
double dPieTotal(pSeries->GetDataTotal());
// Draw each wedge in this pie.
CPoint ptStart(rcPie.left, ptCenter.y);
double dRunningWedgeTotal(0.0);
for (int nGroup = 0; nGroup < m_paLegend.GetSize(); ++nGroup) {
// Ignore empty wedges.
if (0 < pSeries->GetData(nGroup)) {
// Get the degrees of this wedge.
dRunningWedgeTotal += pSeries->GetData(nGroup);
double dPercent(dRunningWedgeTotal * 100.0 / dPieTotal);
int nDegrees((int) (360.0 * dPercent / 100.0));
// Find the location of the wedge's endpoint.
CPoint ptEnd(WedgeEndFromDegrees(nDegrees, &ptCenter, nRadius));
// Special case: a wedge that takes up the whole pie would
// otherwise be confused with an empty wedge.
if (1 == pSeries->GetNonZeroElementCount()) {
_ASSERTE(360 == nDegrees && ptStart == ptEnd && "This is the problem we're correcting");
--ptEnd.y;
}
// If the wedge is of zero size, don't paint it!
if (ptStart != ptEnd) {
// Draw wedge.
//COLORREF crWedge(m_dwaColors.GetAt(nGroup));
COLORREF crWedge(((PGRAPHICLEGEND)(m_paLegend.GetAt(nGroup)))->dwColor);
CBrush br(crWedge);
CBrush* pBrushOld = pDc->SelectObject(&br);
ASSERT_VALID(pBrushOld);
VERIFY(pDc->Pie(rcPie, ptStart, ptEnd));
// Create a region from the path we create.
VERIFY(pDc->BeginPath());
VERIFY(pDc->Pie(rcPie, ptStart, ptEnd));
VERIFY(pDc->EndPath());
CRgn* prgnWedge = new CRgn;
VERIFY(prgnWedge->CreateFromPath(pDc));
// Cleanup.
pDc->SelectObject(pBrushOld);
ptStart = ptEnd;
}
}
}
++nPie;
}
}
}
// Convert degrees to x and y coords.
CPoint CGraph::WedgeEndFromDegrees(int nDegrees, CPoint* ptCenter,
int nRadius)
{
VALIDATE;
CPoint pt;
pt.x = (int) ((double) nRadius * cos((double) nDegrees / 360.0 * PI * 2.0));
pt.x = ptCenter->x - pt.x;
pt.y = (int) ((double) nRadius * sin((double) nDegrees / 360.0 * PI * 2.0));
pt.y = ptCenter->y + pt.y;
return pt;
}
// Spin The Message Loop: C++ version. See "Advanced Windows Programming",
// M. Heller, p. 153, and the MS TechNet CD, PSS ID Number: Q99999.
/* static */ UINT CGraph::SpinTheMessageLoop(bool bNoDrawing /* = false */ ,
bool bOnlyDrawing /* = false */ ,
UINT uiMsgAllowed /* = WM_NULL */ )
{
MSG msg;
::ZeroMemory(&msg, sizeof(msg));
while (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
// Do painting only.
if (bOnlyDrawing && WM_PAINT == msg.message) {
::TranslateMessage(&msg);
::DispatchMessage(&msg);
// Update user interface.
AfxGetApp()->OnIdle(0);
}
// Do everything *but* painting.
else if (bNoDrawing && WM_PAINT == msg.message) {
break;
}
// Special handling for this message.
else if (WM_QUIT == msg.message) {
::PostQuitMessage((int)msg.wParam);
break;
}
// Allow one message (like WM_LBUTTONDOWN).
else if (uiMsgAllowed == msg.message
&& ! AfxGetApp()->PreTranslateMessage(&msg)) {
::TranslateMessage(&msg);
::DispatchMessage(&msg);
break;
}
// This is the general case.
else if (! bOnlyDrawing && ! AfxGetApp()->PreTranslateMessage(&msg)) {
::TranslateMessage(&msg);
::DispatchMessage(&msg);
// Update user interface, then free temporary objects.
AfxGetApp()->OnIdle(0);
AfxGetApp()->OnIdle(1);
}
}
return msg.message;
}
/////////////////////////////////////////////////////////////////////////////
// Conversion routines: RGB to HLS (Red-Green-Blue to Hue-Luminosity-Saturation).
// See Microsoft KnowledgeBase article Q29240.
#define HLSMAX 240 // H,L, and S vary over 0-HLSMAX
#define RGBMAX 255 // R,G, and B vary over 0-RGBMAX
// HLSMAX BEST IF DIVISIBLE BY 6
// RGBMAX, HLSMAX must each fit in a byte (255).
#define UNDEFINED (HLSMAX * 2 / 3) // Hue is undefined if Saturation is 0
// (grey-scale). This value determines
// where the Hue scrollbar is initially
// set for achromatic colors.
// Convert HLS to RGB.
/* static */ COLORREF CGraph::HLStoRGB(WORD wH, WORD wL, WORD wS)
{
_ASSERTE(0 <= wH && 240 >= wH && "Illegal hue value");
_ASSERTE(0 <= wL && 240 >= wL && "Illegal lum value");
_ASSERTE(0 <= wS && 240 >= wS && "Illegal sat value");
WORD wR(0);
WORD wG(0);
WORD wB(0);
// Achromatic case.
if (0 == wS) {
wR = wG = wB = (wL * RGBMAX) / HLSMAX;
if (UNDEFINED != wH) {
_ASSERTE(! "ERROR");
}
}
else {
// Chromatic case.
WORD Magic1(0);
WORD Magic2(0);
// Set up magic numbers.
if (wL <= HLSMAX / 2) {
Magic2 = (wL * (HLSMAX + wS) + (HLSMAX / 2)) / HLSMAX;
}
else {
Magic2 = wL + wS - ((wL * wS) + (HLSMAX / 2)) / HLSMAX;
}
Magic1 = 2 * wL - Magic2;
// Get RGB, change units from HLSMAX to RGBMAX.
wR = (HueToRGB(Magic1, Magic2, wH + (HLSMAX / 3)) * RGBMAX + (HLSMAX / 2)) / HLSMAX;
wG = (HueToRGB(Magic1, Magic2, wH) * RGBMAX + (HLSMAX / 2)) / HLSMAX;
wB = (HueToRGB(Magic1, Magic2, wH - (HLSMAX / 3)) * RGBMAX + (HLSMAX / 2)) / HLSMAX;
}
return RGB(wR,wG,wB);
}
// Utility routine for HLStoRGB.
/* static */ WORD CGraph::HueToRGB(WORD w1, WORD w2, WORD wH)
{
// Range check: note values passed add/subtract thirds of range.
if (wH < 0) {
wH += HLSMAX;
}
if (wH > HLSMAX) {
wH -= HLSMAX;
}
// Return r, g, or b value from this tridrant.
if (wH < HLSMAX / 6) {
return w1 + (((w2 - w1) * wH + (HLSMAX / 12)) / (HLSMAX / 6));
}
if (wH < HLSMAX / 2) {
return w2;
}
if (wH < (HLSMAX * 2) / 3) {
return w1 + (((w2 - w1) * (((HLSMAX * 2) / 3) - wH) + (HLSMAX / 12)) / (HLSMAX / 6));
}
else {
return w1;
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -