|
I've found some issues with the time axis.
This is how I fixed it:
(file: XGraphAxis.cpp)
void CXGraphAxis::FitTimeScale(double *fStepWidth, int nBestCount, double fStart, double fEnd)
{
COleDateTime cStart, cEnd;
COleDateTimeSpan cSpan;
double fStep;
double fSpan;
cStart = fStart;
cEnd = fEnd;
cSpan = cEnd - cStart;
fSpan = cSpan.GetTotalSeconds ();
if(nBestCount > 0) // <------------ avoid divide by zero!
{
fStep = fSpan / (double)nBestCount;
}
else
{
fStep = 0;
}
for (int i = 1; TimeStepTable[i] != -1; i++)
{
if (fStep > TimeStepTable[i-1] && fStep <= TimeStepTable[i])
{
fStep = TimeStepTable[i];
break;
}
}
*fStepWidth = fStep;
*fStepWidth /= 24.0 * 3600;
}
//--------------------------------------------------------------------------------
void CXGraphAxis::AdaptTimeAxis(double * pfStart, double * pfEnd, double fStep)
{
double fDummy;
unsigned long iDummy;
int iModulo = 0;
COleDateTimeSpan cSpan;
COleDateTime cOffset,
cStart,
cEnd;
double lSeconds;
cOffset.SetDateTime (1970, 1, 1, 0, 0, 0);
fStep *= 24 * 3600;
cStart = *pfStart;
cSpan = cStart - cOffset;
lSeconds = cSpan.GetTotalSeconds ();
if(fStep > 0.0)
{
iModulo = ((long) lSeconds - (long) fStep) % (long)fStep;
}
if (iModulo == 0)
{
;//*pfStart = *pfStart - fStep / 24.0 / 3600.0;
}
else
{
iDummy = (unsigned long) lSeconds / (unsigned long) fStep;
fDummy = (double) ((iDummy) * (unsigned long)fStep);
*pfStart = (DATE)(COleDateTime(fDummy / 24.0 / 3600.0)) + cOffset ;
}
cEnd = *pfEnd;
cSpan = cEnd - cOffset;
lSeconds = cSpan.GetTotalSeconds ();
if(fStep > 0.0) // <------------ avoid divide by zero!
{
iModulo = ((long) lSeconds - (long) fStep) % (long)fStep;
}
else
{
iModulo = 0;
}
if (iModulo == 0)
{
;//*pfEnd = *pfEnd + fStep / 24.0 / 3600.0;
}
else
{
iDummy = (unsigned long) lSeconds / (unsigned long) fStep;
fDummy = (double) ((iDummy) * (unsigned long)fStep);
*pfEnd = (DATE)(COleDateTime(fDummy / 24.0 / 3600.0)) + cOffset ;
}
}
//-------------------------------------------------------------------------------------
void CXGraphAxis::Draw(CDCEx* pDC)
{
m_fRange = m_fCurMax - m_fCurMin;
if (m_bAutoScale)
AutoScale(pDC);
if (m_AxisKind == yAxis)
{
m_fSpareRange = (2*m_nArcSize);
m_fMarkerSpareSize = DrawCurveMarkers(pDC);
if (pDC->m_bMono)
pDC->SetBkColor(RGB(255,255,255));
else
pDC->SetBkColor(m_pGraph->m_crGraphColor);
pDC->SetBkMode(OPAQUE);
int nChartTop = m_clChart.top + m_fMarkerSpareSize;
int nTop = m_pGraph->m_clInnerRect.top + m_fSpareRange + m_fMarkerSpareSize;
int nBottom = m_pGraph->m_clInnerRect.bottom;
//m_nRange = nBottom - nTop;
if (m_cLabel != "")
{
CSize titleSize;
{
CFontSelector fs(&m_TitleFont, pDC, false);
titleSize = GetTitleSize(pDC);
m_fSpareRange += titleSize.cy;
}
CFontSelector fs(&m_TitleFont, pDC);
if (m_Placement == apLeft)
{
CRect clTitle(m_nLeft - titleSize.cx - m_nTickSize, nChartTop, m_nLeft - m_nTickSize, nChartTop + titleSize.cy);
pDC->DrawText(m_cLabel, clTitle, DT_RIGHT);
}
else
{
CRect clTitle(m_nLeft + m_nTickSize, nChartTop, m_nLeft + titleSize.cx + m_nTickSize, nChartTop + titleSize.cy);
pDC->DrawText(m_cLabel, clTitle, DT_LEFT);
}
}
m_nRange = m_nTop - nChartTop - m_fSpareRange; // <------------ calc m_nRange BEFORE drawing the scale divisions!
double fSteps = (m_fCurMax - m_fCurMin) / m_fStep;
double fStep = ((double)m_nRange / fSteps);
double fnY = m_nTop;
CString cTxt;
CSize csText;
for (double fY = m_fCurMin; fY <= m_fCurMax; fY += m_fStep, fnY -= fStep)
{
{
CFontSelector fs(&m_Font, pDC, false);
if (m_bDateTime)
cTxt = COleDateTime(fY).Format(m_cDisplayFmt);
else
cTxt.Format(m_cDisplayFmt, fY);
csText = GetTextExtentEx (pDC, cTxt);
}
CFontSelector fs(&m_Font, pDC);
if (m_Placement == apLeft)
{
CRect crText(m_nLeft - csText.cx - m_nTickSize, fnY - csText.cy / 2, m_nLeft - m_nTickSize, fnY + csText.cy / 2);
pDC->DrawText(cTxt, crText, DT_RIGHT);
CPenSelector ps(m_crGridColor, 1, pDC, m_nGridStyle);
pDC->MoveTo(m_nLeft - m_nTickSize, fnY);
// Spare last gridline to prevent axis to be overdrawn
if (!m_bShowGrid || (int)fnY == m_nTop)
pDC->LineTo(m_nLeft, fnY);
else
pDC->LineTo(m_pGraph->m_clInnerRect.right, fnY);
}
if (m_Placement == apRight)
{
CRect crText(m_nLeft + m_nTickSize, fnY - csText.cy / 2, m_nLeft + csText.cx + m_nTickSize, fnY + csText.cy / 2);
pDC->DrawText(cTxt, crText, DT_LEFT);
CPenSelector ps(m_crGridColor, 1, pDC, m_nGridStyle);
pDC->MoveTo(m_nLeft + m_nTickSize, fnY);
// Spare last gridline to prevent axis to be overdrawn
if (!m_bShowGrid || (int)fnY == m_nTop)
pDC->LineTo(m_nLeft, fnY);
else
pDC->LineTo(m_pGraph->m_clInnerRect.left, fnY);
}
}
CPenSelector ps(m_crColor, 1, pDC);
pDC->MoveTo (m_nLeft, m_nTop);
pDC->LineTo (m_nLeft, nChartTop);
DrawArc(pDC, CPoint(m_nLeft, nChartTop), up, m_nArcSize);
m_clRect.SetRect(m_nLeft - 5, nChartTop, m_nLeft + 5, m_nTop);
}
else
{
m_fSpareRange = (4*m_nArcSize);
m_fMarkerSpareSize = DrawCurveMarkers(pDC);
pDC->SetBkColor(m_pGraph->m_crGraphColor);
pDC->SetBkMode(OPAQUE);
int nChartRight = m_pGraph->m_clInnerRect.right - m_fMarkerSpareSize; //m_clChart.right - m_fMarkerSpareSize;
int nLeft = m_pGraph->m_clInnerRect.left;
int nRight = m_pGraph->m_clInnerRect.right - m_fSpareRange - m_fMarkerSpareSize;
if (m_cLabel != "")
{
CSize titleSize;
{
CFontSelector fs(&m_TitleFont, pDC, false);
titleSize = GetTitleSize(pDC);
m_fSpareRange += titleSize.cx;
}
CFontSelector fs(&m_TitleFont, pDC);
CRect clTitle(nChartRight - titleSize.cx, m_nTop + m_nTickSize, nChartRight, m_nTop + m_nTickSize + titleSize.cy);
pDC->DrawText(m_cLabel, clTitle, DT_LEFT);
}
m_nRange = nChartRight - m_nLeft - m_fSpareRange; // <------------ calc m_nRange BEFORE drawing the scale divisions!
double fSteps = ((m_fCurMax - m_fCurMin) / m_fStep);
double fStep = ((double)m_nRange / fSteps);
double fnX = m_nLeft;
CString cTxt;
CSize csText;
for (double fX = m_fCurMin; fX <= m_fCurMax; fX += m_fStep, fnX += fStep)
{
if (m_bDateTime)
cTxt = COleDateTime(fX).Format(m_cDisplayFmt);
else
cTxt.Format(m_cDisplayFmt, fX);
{
CFontSelector fs(&m_Font, pDC, false);
csText = GetTextExtentEx (pDC, cTxt);
}
CFontSelector fs(&m_Font, pDC);
CRect crText(fnX - csText.cx / 2, m_nTop + m_nTickSize, fnX + csText.cx / 2, m_nTop + csText.cy + m_nTickSize);
pDC->DrawText(cTxt, crText, DT_CENTER);
CPenSelector ps(m_crGridColor, 1, pDC, m_nGridStyle);
pDC->MoveTo(fnX, m_nTop + m_nTickSize);
// Spare last gridline to prevent axis to be overdrawn
if (!m_bShowGrid || (int)fnX == m_nLeft)
pDC->LineTo(fnX, m_nTop);
else
pDC->LineTo(fnX, m_clChart.top);
}
CPenSelector ps(m_crColor, 1, pDC);
pDC->MoveTo (m_nLeft, m_nTop);
pDC->LineTo (nChartRight, m_nTop);
DrawArc(pDC, CPoint(nChartRight, m_nTop), right, m_nArcSize);
m_clRect.SetRect(m_nLeft, m_nTop - 5, nChartRight, m_nTop + 5);
}
if (m_bSelected)
pDC->DrawFocusRect (m_clRect);
DrawColorRanges(pDC);
}
Otherwise - thxs for this control - great work!
|
|
|
|
|
See:
PRB: Clipping Doesn't Work Correctly in Print Preview (Q128334)
http://support.microsoft.com/default.aspx?scid=kb;EN-US;q128334
MS says it affects only VC 4.0 and prior versions.
However, this "behaviour" exists also in 6.0.
Few lines must be inserted to have correct clipping.
Adios
|
|
|
|
|
I already tried to use this solution but it sill doesn't work either.
|
|
|
|
|
first I like to say that I love it.
I'm using it for research on high energy astrophysics, and the multi graph/multi axis is great for me.
I've seen that if I try to use a range of say, 1E50 to +1E53 it does'nt work
I didn't have time to fix it. also the auto scale for numbers smaller than 0.1 doesn't work.
for now I've bypassed it, but when I'll fix it I'll post it here.
|
|
|
|
|
In my opinion, some values have truncated.
That is, fDummy = (double)((int)fEnd).
Of course, fDummy and fEnd are double variables.
This must be fDummy = (double((INT64)fEnd) to correct it.
Here is some fix;
---------------------------------------------------------
typedef INT64 myint64;
void CXGraphAxis::AdaptAxis(double* fStart, double* fEnd, double fStep)
{
myint64 iDummy;
myint64 iModulo;
double fDivisor;
double fDummy;
fDivisor = 1.0;
while ((myint64)fStep == 0)
{
fStep *= 10.0;
fDivisor *= 10.0;
}
*fStart *= fDivisor;
*fEnd *= fDivisor;
iModulo = ((myint64) *fStart - (myint64) fStep) % (myint64)fStep;
if (iModulo == 0)
{
if (((double) ((myint64) *fStart) == *fStart) &&
(((myint64) *fStart % (myint64)fStep) == 0))
fDummy = (double)((myint64)*fStart);
else
fDummy = (double)((myint64) *fStart - (myint64) fStep);
}
else
{
iDummy = (myint64) *fStart / (myint64) fStep;
if (*fStart < 0)
fDummy = (double) ((iDummy-1) * (myint64)fStep);
else
fDummy = (double) ((iDummy) * (myint64)fStep);
}
*fStart = fDummy / fDivisor;
iModulo = ((myint64) *fEnd + (myint64) fStep) % (myint64)fStep;
if (iModulo == 0)
{
if (((double) ((myint64) *fEnd) == *fEnd) &&
(((int) *fEnd % (myint64)fStep) == 0))
fDummy = (double)((myint64)*fEnd);
else
fDummy = (double)((myint64) *fEnd + (myint64) fStep);
}
else
{
iDummy = (myint64) *fEnd / (myint64) fStep;
if (*fEnd < 0)
fDummy = (double) ((iDummy-1) * (myint64)fStep);
else
fDummy = (double) ((iDummy+1) * (myint64)fStep);
}
*fEnd = fDummy / fDivisor;
}
|
|
|
|
|
Using INT64 in AdaptAxis won't work either since casting large doubles to INT64 still produces 0 values (INT64 -> 9.223E +/- 18 whereas double -> 1.7E +/- 308).
For now i provide the following fix to overcome this problem, i'll introduce it in the next version.
// bypass fmod problem (results unexpected due to binary representation)
double saveFmod(double x, double y)
{
double fRes = 0.0;
while ((fRes+y) <= fabs(x))
fRes += y;
if (x < 0.0)
return x + fRes;
else
return x - fRes;
}
void CXGraphAxis::AdaptAxis(double* fStart, double* fEnd, double fStep)
{
double fRemainder;
fRemainder = saveFmod(*fStart-fStep, fStep);
if (fRemainder != 0.0)
*fStart -= fRemainder + fStep;
fRemainder = saveFmod(*fEnd+fStep, fStep);
if (fRemainder != 0.0)
*fEnd -= fRemainder - fStep;
}
|
|
|
|
|
Nice work.
While playing with it I found a bug in the CXGraph::DrawCursorLegend method. It crashes in cursor mode, if the x-axis is in a datetime mode. A bad format string is passed to CString::Format.
Here is the fix:
void CXGraph::DrawCursorLegend (CDCEx* pDC)
{
int i;
double fVal;
CString cLabel = "", cFmt, cItem;
m_CursorLabel.m_bVisible = true;
for (i = 0; i < m_XAxis.size(); i++)
{
//CHANGED: bugfix, correct handling of X axis in time mode
fVal = m_XAxis[i].GetValueForPos (m_CurrentPoint.x);
if(m_XAxis[i].m_bDateTime)
{
cFmt = "X[%02d] : " + COleDateTime(fVal).Format(m_XAxis[i].m_cDisplayFmt) + " %s";
cItem.Format(cFmt, i+1, m_XAxis[i].m_cLabel);
cLabel += (" " + cItem + "\r\n");
}
else
{
cFmt = "X[%02d] : " + m_XAxis[i].m_cDisplayFmt + " %s";
cItem.Format(cFmt, i+1, fVal, m_XAxis[i].m_cLabel);
cLabel += (" " + cItem + "\r\n");
}
//ENDCHANGES
}
for (i = 0; i < m_YAxis.size(); i++)
{
fVal = m_YAxis[i].GetValueForPos (m_CurrentPoint.y);
cFmt = "Y[%02d] : " + m_YAxis[i].m_cDisplayFmt + " %s";
cItem.Format(cFmt, i+1, fVal, m_YAxis[i].m_cLabel);
cLabel += (" " + cItem + "\r\n");
}
if (m_nSnappedCurve != -1)
{
//CHANGED: bugfix, correct handling of X axis in time mode
CString cXFmt = m_XAxis[m_Data[m_nSnappedCurve].m_nXAxis].m_cDisplayFmt;
CString cYFmt = m_YAxis[m_Data[m_nSnappedCurve].m_nYAxis].m_cDisplayFmt;
if(m_XAxis[m_Data[m_nSnappedCurve].m_nXAxis].m_bDateTime)
{
cFmt = "%s[%02d] : " + COleDateTime(m_fSnappedXVal).Format(cXFmt) + ", " + cYFmt;
cItem.Format(cFmt, m_Data[m_nSnappedCurve].m_cLabel, m_nSnappedCurve + 1, m_fSnappedYVal);
cLabel += (" " + cItem + "\r\n");
}
else
{
cFmt = "%s[%02d] : " + cXFmt + ", " + cYFmt;
cItem.Format(cFmt, m_Data[m_nSnappedCurve].m_cLabel, m_nSnappedCurve + 1, m_fSnappedXVal, m_fSnappedYVal);
cLabel += (" " + cItem + "\r\n");
}
//ENDCHANGES
}
m_CursorLabel.m_cText = cLabel;
m_CursorLabel.Draw (pDC);
}
Sonork 100.15206;PavelK
|
|
|
|
|
Thanks for that. I've posted a new version.
|
|
|
|
|
Did you try root ? (root.cern.ch)
Jonathan de Halleux, Belgium.
|
|
|
|
|
Well done,
it needs a bit of work still,
Regardz
Colin J Davies
Sonork ID 100.9197:Colin
Testing Current Sig
<marquee><marquee behavior="alternate" scrollamount="12">< >
|
|
|
|
|
Indeed, feel free to advance this one
Regards
Gunnar
|
|
|
|
|