Recent

Author Topic: [Implemented] How to auto-scale an axis depending on the shown extent  (Read 20513 times)

wp

  • Hero Member
  • *****
  • Posts: 9017
Re: How to auto-scale an axis depending on the shown extent
« Reply #30 on: July 16, 2021, 05:27:09 pm »
Thanks. What a silly mistake to not handle the axes separately!

I moved both StoreAxisRange and RestoreAxisRange to public, knowing that poorly-thought calls may disrupt operation of the live view component.
Mainly Lazarus trunk / fpc 3.2.0 / all 32-bit on Win-10, but many more...

Muso

  • Full Member
  • ***
  • Posts: 239
Re: How to auto-scale an axis depending on the shown extent
« Reply #31 on: July 16, 2021, 06:49:21 pm »
Thanks. What a silly mistake to not handle the axes separately!

I moved both StoreAxisRange and RestoreAxisRange to public, knowing that poorly-thought calls may disrupt operation of the live view component.

Note that I am just a beginner, trying to fix expert issues  :). Therefore I would like to get feedback what areas is my version sensible and where not.

What is about the GetTransform.AxisToGraph change I made? While debugging, this turned out to the the cause that the left y-axis in my demo program was wrong then the right-axis was not shown. I am asking because I found now a way that works but did not yet fully understand why GetTransform.AxisToGraph performed at the left axis leads to another result when the right axis has an active series or not.

wp

  • Hero Member
  • *****
  • Posts: 9017
Re: How to auto-scale an axis depending on the shown extent
« Reply #32 on: July 16, 2021, 09:08:15 pm »
I think the reason is somewhere else. You are setting the axis.Range.Min/.Max only in the case where the corresponding .UseMin/.UseMax is NOT set, and then you apply the axis.GetTransform to the found ymin or ymax value - which are the correct values in this case.

I had set the axis.Range in all cases, and to take care of the user-frozen range I set ymin/ymax to the saved FStoredFranges.Min/.Max. In principle this should work, too, but my mistake was that I applied the axis transformation again to ymin/ymax - but I should have applied it to the FStoredRanges.Min/Max in this case (or, simpler, axis.Range.Min/.Max which is valid in both cases). When I revert my svn version to my commit of yesterday and change the transformation argument this way, the liveview is correct.

I do think, though, that setting lext.a.y := axis.GetTransform.AxisToGraph(yminAx) is dangerous because lext (the logical extent, the range in the invisible "graph" coordinate system behind the chart) depends on all axes. Therefore, lext.a.y should be the minimum of the current lext.a.y and the result of the transformation, likewise for lext.b.y:

Code: Pascal  [Select][+][-]
  1. lext.a.y := Min(lext.a.y, axis.GetTransform.AxisToGraph(yminAxis));
  2. ...
  3. lext.b.y := Max(lext.b.y, axis.GetTransform.AxisToGraph(ymaxAxis));

Since your solution is working I did not change this and want to keep it for the moment. Maybe we can find an example where your code (without Min() and Max()) leads to an incorrect display.

A simplification is probably to avoid the double usage of an axis and series ymax/ymin. The series method FindYRange() has ymin and ymax as var parameters and does not initialize them. So they are to be considered as the cumulative value of a list of series, like in the example. Therefore, it should be sufficient to initialize a ymin/ymax outside the series loop and have the total min and max at the end of the loop. For the moment I kept your code, but put the series max/min initialization inside the loop (and renamed these variables to yminser/ymaxser because they represent the range of the series, in contrast to yminax/ymaxax which are the total range of all series in this axis).

Maybe I'll change both items tomorrow. I am attaching a "preview" for you to test.

I added a if csDesigning in ComponentState then exit at the top because I found that an empty chart in design mode attached to an active liveview always activated an axis range of -1 to +1 - default, however, is an open range (Min/Max = 0 and UseMin/Max = false)
Mainly Lazarus trunk / fpc 3.2.0 / all 32-bit on Win-10, but many more...

Muso

  • Full Member
  • ***
  • Posts: 239
Re: How to auto-scale an axis depending on the shown extent
« Reply #33 on: July 17, 2021, 05:49:24 pm »
I had set the axis.Range in all cases, and to take care of the user-frozen range I set ymin/ymax to the saved FStoredFranges.Min/.Max. In principle this should work, too, but my mistake was that I applied the axis transformation again to ymin/ymax

Thanks, now I got it.

I tried now your latest version from today and I think we eventually have it!  :D


Since I need the LiveView in combinaition with the axisClickTool I needed to adapt the ChartEditor demo program (which I use for the AxisClickTool in my program) in CeAxisFrame.pas:

Code: Pascal  [Select][+][-]
  1. procedure TChartAxisFrame.seMaximumChange(Sender: TObject);
  2. begin
  3.   FAxis.Range.Max := seMaximum.Value;
  4.   cbAutoMax.Checked := false;
  5.   // wee need to set explicitly that UseMax is not used
  6.   // because TAChart's the LiveView feature need this info
  7.   FAxis.Range.UseMax := true;
  8. end;
  9.  
  10. procedure TChartAxisFrame.seMinimumChange(Sender: TObject);
  11. begin
  12.   FAxis.Range.Min := seMinimum.Value;
  13.   cbAutoMin.Checked := false;
  14.   // wee need to set explicitly that UseMin is not used
  15.   // because TAChart's the LiveView feature need this info
  16.   FAxis.Range.UseMin := true;
  17. end;

So when the user changes the axis range, we need to explicitely set the UseMin/UseMax to false so that LiveView knows that the user set its own range.

The second change is to call the StoreAxisRanges procedure of LiveView. In case for the demo it would be this in ceMain.pas:

Code: Pascal  [Select][+][-]
  1. procedure TMainForm.EditAxis(AAxis: TChartAxis; APage: TChartAxisEditorPage);
  2. var
  3.   F: TChartAxisEditor;
  4. begin
  5.   if cbUseAllInOneDialog.Checked then
  6.     EditChartAxis(AAxis, APage)
  7.   else
  8.   begin
  9.     F := TChartAxisEditor.Create(nil);
  10.     try
  11.       F.Prepare(AAxis, 'Edit chart axis "%s"');
  12.       F.Page := APage;
  13.       F.ShowModal;
  14.       // update the axis settings for the LiveView
  15.       ChartLiveView1.StoreAxisRanges;
  16.     finally
  17.       F.Free;
  18.     end;
  19.   end;

Muso

  • Full Member
  • ***
  • Posts: 239
Re: How to auto-scale an axis depending on the shown extent
« Reply #34 on: July 17, 2021, 05:55:12 pm »
While thinking about it,  typical application like mine that displays sensor data needs in most cases also the tools from the ChartEditor demo. The dialogs of the ChartEditor demo should therefore become part of TAChart itself in Lazarus 2.4.

My program is almost ready and I gave it to my engineer colleagues to test. And what was the first thing the did? - changing the axis range, changing the background color of the chart, changing the axis tick separation etc. So obviously users like to play with the layout a lot and a program that does not offer this, won't be accepted.

Speaking for myself, all my future programs will provide the ChartEditor demo functionality, no matter how simple the chart application might be. I felt the "pressure" from the colleagues in another project too that everything should be changeable. The main argument was "Excel charts can do this, so why does your program not offer at least basic Excel features?".  :(

wp

  • Hero Member
  • *****
  • Posts: 9017
Re: How to auto-scale an axis depending on the shown extent
« Reply #35 on: July 17, 2021, 06:39:07 pm »
This is a good example why the chart editor demo should NOT be part of TAChart. What if somebody does not need the LiveView? With your modification he will be forced to have it in his project.

Or what if somebody wants to arrange the controls in a different way, for example in a "side-bar" interface like in the combobox demo instead of the dialog interface of the charteditor demo?

Integrating the chart editor into TAChart would mean:
- add support for any series type supported and for any custom series written by a user
- add support for multiple axes, multiple transformations, rotated axes, date/time axes
- add support for i18n
- ...
This would be a project of its own, probably as complex as TAChart itself.

The current solution offers greatest flexibility for the extremely modular TAChart library. Just copy the demo files into your project, and then you can change whatever you want.
« Last Edit: July 17, 2021, 08:58:47 pm by wp »
Mainly Lazarus trunk / fpc 3.2.0 / all 32-bit on Win-10, but many more...

Muso

  • Full Member
  • ***
  • Posts: 239
Re: How to auto-scale an axis depending on the shown extent
« Reply #36 on: July 18, 2021, 12:52:40 am »
This is a good example why the chart editor demo should NOT be part of TAChart. What if somebody does not need the LiveView? With your modification he will be forced to have it in his project.

Then you misunderstood me. My aim that users don't have to reinvent the wheel. Like when I need a dialog to open a file, I can use a predefined component. Of course I can use my own dialog, but I have something to start with. The dialog can be changed or adapted by the programmer as he likes it. And having a dialog offered with i18n would be very helpful cool too.

So what I want is that when the user adds the click tools, they can (but of course not must) use a predefined dialog allowing the program users to change all chart properties live. See also below how simple this could in my opinion be done.

And of course I don't want the dialogs to work only with LiveView. I only posted the changes here for other users who will find this thread by googling because they might have the same task.

Quote
- add support for any series type supported and for any custom series written by a user

No, only for the official supported series types. And the main features like tilte/footer click, axes click etc. are independent of the series.

Quote
- add support for multiple axes, multiple transformations, rotated axes, date/time axes

This what is already there. in my program, I have 3 different exes, 13 series in total (line series and just lines), two different axis transformations and my x-axis is a time axis.

Quote
This would be a project of its own, probably as complex as TAChart itself.

I made the proposal because I think 90% is already there.

Quote
The current solution offers greatest flexibility for the extremely modular TAChart library. Just copy the demo files into your project, and then you can change whatever you want.

Exactly. So basically what I want is
- to find this feature so that one doesn't need to reinvent the wheel, googling around or asking in forums.
Without your help I would not knew that such a demo exists and that it is part of the Lazarus installation and where.

So the "integration" could in the first step just to add the ChartEditor demo dialog via the Lazarus form editor. A button there would do all the stuff I currently have to do manually
- knowing that is exists and where
- copying it t my project
- adapt my project to use the demo's dialog

 

TinyPortal © 2005-2018