TAChart: some series still use uninitialized scaling
Original Reporter info from Mantis: Marcin Wiazowski
-
Reporter name:
Original Reporter info from Mantis: Marcin Wiazowski
- Reporter name:
Description:
Test 1: Load the attached Reproduce application in IDE - when displaying application's form, IDE will hang for about 10 seconds.
This is because the form contains two charts having their X axis ranges from 0 to 100000000. Because chart scaling is used although not yet initialized properly, this leads to about 2*100000000 calls to some calculation function - which takes a lot of time.
Test 2: Go to tacustomfuncseries.pas and, at the beginning of TDrawFuncHelper.Create(), insert:
if not ASeries.ParentChart.ScalingValid then
raise EChartError.Create('Test failed');
Then start the Reproduce application under the debugger. In FormCreate(), there are 11 calls to functions, that internally create an TDrawFuncHelper object (which requires valid chart scaling) - 10 of them will raise a 'Test failed' exception. This means, that uninitialized chart scaling has been used.
Currently, there exist 11 different ways of using an internal TDrawFuncHelper object - this is as in FormCreate():
- TExpressionSeries.Draw()
- TExpressionSeries.GetGraphBounds()
- TExpressionSeries.GetNearestPoint()
- TFuncSeries.Draw()
- TFuncSeries.GetGraphBounds()
- TFuncSeries.GetNearestPoint()
- TCubicSplineSeries.Draw()
- TCubicSplineSeries.GetNearestPoint()
- TFitSeries.Draw()
- TFitSeries.Extent()
- TFitSeries.GetNearestPoint()
All of them are public functions, so they are allowed to fail, but not to hang. So all of them should validate the chart scaling before use.
The solution is as follows:
A) GetGraphBounds() and Extent() methods are mostly called in a scaling calculation loop, where - at the first iteration - scaling is not available. So:
- multi-pass scaling must be requested,
- it must be verified that scaling is valid before further actions.
B) Draw() and GetNearestPoint() methods are, under normal circumstances, called when scaling is already initialized. So the only needed action is:
- it must be verified that scaling is valid before further actions.
But, in B) case, it is also allowed to request multi-pass scaling - this doesn't hurt us in any way (this will be just ignored if performed beyond the scale calculation loop). Thanks to that, the code for case A) can also be used in case B).
Since it's not good to copy same code to 11 places, I created a function for checking / multi-pass scaling requesting. This function must work on series' FChart field, which is implemented in TBasicChartSeries - so I implemented an "RequestValidChartScaling" function there.
The TChart's "MultiPassScalingNeeded" method, that has been introduced few days ago, I renamed to "RequestMultiPassScaling" - just to be more similar to "RequestValidChartScaling".
To avoid further problems with uninitialized scaling, I also added exceptions where the scaling is used at the lowest level - i.e. to:
- TChart.XGraphToImage()
- TChart.XImageToGraph()
- TChart.YGraphToImage()
- TChart.YImageToGraph()
As I checked, this adds only microseconds to chart painting, so should not be a problem.
Thanks to these new exceptions, I also found references to uninitialized scaling in TBasicPointSeries.GetNearestPoint() - so I fixed the problem.
And, of course, the attached patch solves also problems in TExpressionSeries, TFuncSeries, TCubicSplineSeries and TFitSeries.
Mantis conversion info:
- Mantis ID: 35207
- Build: 60623
- Version: 2.1 (SVN)
- Fixed in revision: 60636 (#fe85948f)
- Target version: 2.2