Recent

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

Muso

  • Sr. Member
  • ****
  • Posts: 356
Re: How to auto-scale an axis depending on the shown extent
« Reply #15 on: July 09, 2021, 09:35:17 pm »
The problem I see is: what happens if the user intentionally modifies the axis.Range himself - but when the ChartLiveview becomes active this setting will be lost.

Thanks for having a look.

I think there should be a new option to the LiveView feature.

So something like this:
- when the user enables the new LiveView option (called e.g. "useViewPortExtent")
- take the LiveView 's ViewPort setting
- check all currently active series connected to the left axis
- take the Min/Max of these series within the ViewPort's x-range
- a new axis property (e.g. called "useLiveViewRange") is checked.
- If true take the determined Min/Max value as UseMin/UseMax for the axis
- do the same for the right axis

When the user now changes at runtime the axis range, he sees the currently set Min/Max from the LiveView. He can overwrite these values with custom ones by setting useLiveViewRange to false since then he blocks the LiveView range setting.

wp

  • Hero Member
  • *****
  • Posts: 12353
Re: How to auto-scale an axis depending on the shown extent
« Reply #16 on: July 13, 2021, 10:29:39 am »
Please try the new revision. In ExtentY=lveAuto mode, now multiple axes are supported. And there is a new mode, lveMultiAxisRange which respects the range preset for an axis. So, if your second y axis, for example, displays data ranging between 99.1 and 100.95, and you want stable axis labels you can set Range.Min := 99 and Range.Max := 101 for this axis, and the liveview will zoom into this interval (but note the limitation of the range that it will be expanded when data values are outside). When ExtentY=lveAuto in this case, this axis will adjust according to the current data max and min by setting the axis.Range accordingly (any previously set Axis.range is stored when the LiveView becomes active and restored when it becomes inactive).

Muso

  • Sr. Member
  • ****
  • Posts: 356
Re: How to auto-scale an axis depending on the shown extent
« Reply #17 on: July 14, 2021, 02:21:23 am »
Please try the new revision.

Excellent!

lveMultiAxisRange which respects the range preset for an axis. So, if your second y axis, for example, displays data ranging between 99.1 and 100.95, and you want stable axis labels you can set Range.Min := 99 and Range.Max := 101 for this axis, and the liveview will zoom into this interval

I tried it and in the attached demo program I set the right axis to range 19 - 30. However, also when I set the extent to the new "lveMultiAxisRange", I don't get this range but the range 22 - 30.
Do I have to use the "UseMin" option or must I not use this?

---------------

I hope you don't beat me for this: I wanted to understand how you implemented the new extent and realized that the source code has no single comment line:
https://github.com/graemeg/lazarus/blob/b980b69a582a062c401b89ba48f18ef68e4c689b/components/tachart/tachartliveview.pas
I can only speak for myself, after e.g. a year i forgot why I implemented something the way i did it and to help myself and of course others, there should be some comment line explaining they Why in the code.

wp

  • Hero Member
  • *****
  • Posts: 12353
Re: How to auto-scale an axis depending on the shown extent
« Reply #18 on: July 14, 2021, 04:38:11 pm »
I tried it and in the attached demo program I set the right axis to range 19 - 30. However, also when I set the extent to the new "lveMultiAxisRange", I don't get this range but the range 22 - 30.
Do I have to use the "UseMin" option or must I not use this?
Where do you set the range to start at 19? I only see the value 23 in the object inspector. BTW, there are two AutoScaleTransformations in ChartAxisTransformR - you only need one.

The reason why in the MultiAxisRange mode the axis has the first label at 22 rather than 23 is that your series2 is constantly 24.3. The liveview component selects in this case of seriesmax = seriesmin the series maxiumum to be 10% higher, and the series miniumum to be 10% smaller than the common value. 90% of 24.3 is 21,87. Since in TAChart the series range overrides the axis range the right axis is set up to start at 21.87; due to the chart's margin there are some pixels between the axis and the first label, therefore, the first axis label at the right is 22. If you add some noise to the y data of series2 ( Chart1Lineseries2.AddXY(x, 24.3 + 0.1*(random-0.5)) ), or if you set the right axis Range.Min to 19 as you wrote in the post, you will see that the live view works as expected.

I hope you don't beat me for this: I wanted to understand how you implemented the new extent and realized that the source code has no single comment line:
https://github.com/graemeg/lazarus/blob/b980b69a582a062c401b89ba48f18ef68e4c689b/components/tachart/tachartliveview.pas
I can only speak for myself, after e.g. a year i forgot why I implemented something the way i did it and to help myself and of course others, there should be some comment line explaining they Why in the code.
I will not participate in a discussion about commenting of source code here. The TAChart coding style guidelines (https://wiki.lazarus.freepascal.org/TAChart_documentation#Comments, not written by myself) say: "Comments should only include information not evident from the source. In particular, choose meaningful procedure and argument names in preference to adding comments describing their usage." The condition "not evident from the source", of course, is relative...

Muso

  • Sr. Member
  • ****
  • Posts: 356
Re: How to auto-scale an axis depending on the shown extent
« Reply #19 on: July 14, 2021, 05:58:22 pm »
Where do you set the range to start at 19? I only see the value 23 in the object inspector. BTW, there are two AutoScaleTransformations in ChartAxisTransformR - you only need one.

Oops, that was the problem.
But also with the fixed version I attached, it is not working as expected.
Attached is the screencast of what I see - sometimes the number 19, sometimes the number 30 in the right axis suddenly disappears (when the y-axis reaches 83 s, the 30 disappears, when it reaches 86s, the 19 disappears) for a second. I expected that the right axis stays stable when I set a range.

Can you please add in the docs:
https://wiki.freepascal.org/TAChart_documentation#Live_View
the info that one can set the range via lvMultiAxis and that one must use the booleans UseMin/UseMax in order to get the range setting for lvMultiAxis ?

The reason why in the MultiAxisRange mode the axis has the first label at 22 rather than 23 is...
I will not participate in a discussion about commenting of source code here....

But that is my point: You made a new implementation of a feature. You also have good reasons why you implemented the things as they are and there are forum posts about these design decisions and discussions. But looking in the code as a stranger, I am lost. I miss the info what the idea was, what were the design decisions, where in the forums were the debates?, etc. When  look e.g. a year later in the code , it is not clear to me why there is a lvMultiAxis, what are the differences to lvAuto and so on. therefore one has to spend a lot of time to google around and hopefully find this forum thread. So why not adding a comment with a link to this forum thread?
« Last Edit: July 14, 2021, 06:02:15 pm by Muso »

wp

  • Hero Member
  • *****
  • Posts: 12353
Re: How to auto-scale an axis depending on the shown extent
« Reply #20 on: July 14, 2021, 06:48:17 pm »
The main problem is that the axis range is not "absolute". When the data in the series attached to that axis have a greater range, then the axis range is extended to display all series data - you already noticed that in the ChartEditorDemo. The only way to truncate series data is to set the Chart.Extent, but this is in graph units, i.e. in the common invisible coordinate system in which the AutoScaleAxistransforms operate, and thus it is not possible to restrict the Extent to individual series (which was the reason why the initial approach did not work).

I tried to modify the behaviour of the axis range for the ChartEditor demo but ran into numerous side-effects, so I will not touch this any more.

I will update the chart documentation when things have settled down here. Worse than "no documentation" is "false documentation".

OK, it makes sense to add a link to the forum discussion. But I will not explain every step in the sources; a stranger who does not know anything about TAChart will not understand these comments as little as he understands the code itself.

Muso

  • Sr. Member
  • ****
  • Posts: 356
Re: How to auto-scale an axis depending on the shown extent
« Reply #21 on: July 14, 2021, 07:24:24 pm »
The main problem is that the axis range is not "absolute". When the data in the series attached to that axis have a greater range, then the axis range is extended to display all series data - you already noticed that in the ChartEditorDemo. The only way to truncate series data is to set the Chart.Extent

I understand. I played around with this as well and also got a lot of problems.

However, as it is. lvMultiAxis is almost what I need. the small jumps in the right axis are negligible for most users I hope.

Quote
I will update the chart documentation when things have settled down here. Worse than "no documentation" is "false documentation".

OK, it makes sense to add a link to the forum discussion. But I will not explain every step in the sources; a stranger who does not know anything about TAChart will not understand these comments as little as he understands the code itself.

Many thanks.

Muso

  • Sr. Member
  • ****
  • Posts: 356
Re: How to auto-scale an axis depending on the shown extent
« Reply #22 on: July 14, 2021, 07:57:15 pm »
One further question: Since I use the cool new Live View feature for a real-life program, I would like to see it in Lazarus 2.2.0

I had a look and in
https://wiki.lazarus.freepascal.org/Lazarus_2.2.0_release_notes#TAChart
I don't see LiveView.
Also in the recent merges to the 2.2 branch I don't see your recent commits:
https://wiki.freepascal.org/Lazarus_2.2_fixes_branch

What state of the LiveView feature would be in Lazarus 2.2.0?

Muso

  • Sr. Member
  • ****
  • Posts: 356
Re: How to auto-scale an axis depending on the shown extent
« Reply #23 on: July 14, 2021, 08:18:28 pm »
However, as it is. lvMultiAxis is almost what I need.

Seems I was too rash.   ::)
Attached is my demo program in which I set the right axis to range 19-33 and left axis to 500 - 1400. When I run it and set lvMultiAxis then the left axis gets a range of about 720 to 1350. Do I have a wrong setting somewhere?

wp

  • Hero Member
  • *****
  • Posts: 12353
Re: How to auto-scale an axis depending on the shown extent
« Reply #24 on: July 15, 2021, 06:21:27 pm »
Oh man, this is a brain-twister...

Played with it once more and found that we do not need lveMultiAxisRange at all. After a small modification in the recent code this case becomes a subset of lveAuto: When you define a range for an axis (by setting axis.Range.Min/axis.Range.UseMin and/or axis.Range.Max/axis.Range.UseMax) then this range is respected as axis range (as long as this range is larger than the y data range in the viewport - this is the old ChartEditor demo story...).

Your program is working with the ranges set after the transition at 10 sec. Before the transition the minimum is extended down to 0. I do not yet fully understand why the other axis is affected also before the transition.

But I think I will keep it as such. Maybe sometime I see the perfect solution, until then you'll have to live with the 90%-perfect solution.

Muso

  • Sr. Member
  • ****
  • Posts: 356
Re: How to auto-scale an axis depending on the shown extent
« Reply #25 on: July 15, 2021, 08:44:55 pm »
After a small modification in the recent code this case becomes a subset of lveAuto: When you define a range for an axis (by setting axis.Range.Min/axis.Range.UseMin and/or axis.Range.Max/axis.Range.UseMax) then this range is respected as axis range (as long as this range is larger than the y data range in the viewport - this is the old ChartEditor demo story...).

Hmm, I mean the user is the boss, when he wants to see only the y-range for e.g. 500-1400, it is his decision. What was possible wit the lvMultiAxis method is no longer possible, see my attached example program:
No matter if within the viewport are only data in the range 500-1400, the y-range setting is ignored.

To reproduce
- start my program and switch to Live mode
- wait 16 seconds
- check the option to hide the right axis

result: the y-range is now approx. -1100 to + 1500

It is not clear to me why the visibility of the right axis affects the y-range of the left axis.

wp

  • Hero Member
  • *****
  • Posts: 12353
Re: How to auto-scale an axis depending on the shown extent
« Reply #26 on: July 15, 2021, 10:30:31 pm »
As I told you the live view is 90% perfect. There are other activities for me than to aim for 100% perfection of this component and will stop this now, but I'll be glad to accept your patch for the remaining 10%.

Muso

  • Sr. Member
  • ****
  • Posts: 356
Re: How to auto-scale an axis depending on the shown extent
« Reply #27 on: July 16, 2021, 02:24:13 am »
As I told you the live view is 90% perfect. There are other activities for me than to aim for 100% perfection of this component and will stop this now, but I'll be glad to accept your patch for the remaining 10%.

You know that I am amazed on how much you helped me and your work on TAChart is outstanding.

When the feature stays as it is, OK. I see that your today's commit r65456 was not merged to the 2.2 stable branch yet. Your other commits are in.
Whatever the final state in 2.2.0 would be, can you please add a note about the known issues?

Concerning a patch, I already looked into the code and it was not cleat to me why the active state of the LineSeries connected to the right axis, makes the difference.
Now I think the issue is that the series are not distinguished between being connected to the axis which should be currently scaled. I will have a deeper look the next days.

Muso

  • Sr. Member
  • ****
  • Posts: 356
Re: How to auto-scale an axis depending on the shown extent
« Reply #28 on: July 16, 2021, 05:35:07 am »
I'll be glad to accept your patch

Attached is the tachartliveview.pas that fixes the issue for me.
It turned out that one must separate the different y-axes and that GetTransform.AxisToGraph must not be performed when the user set its own range for the axis.

I remodeled the file a bit just to understand it better, this is no offense nor a request to take it as it is. Most changes and comments are only in to help myself understanding.

Muso

  • Sr. Member
  • ****
  • Posts: 356
Re: How to auto-scale an axis depending on the shown extent
« Reply #29 on: July 16, 2021, 01:40:42 pm »
Attached is the tachartliveview.pas that fixes the issue for me.

I forgot to mention that I there purposely made the procedure "StoreAxisRanges" public becase for the TAChart AxisClickTool, one need to run this procedure because the user might this way changed the axis range. (The LiveView must take the current axis state not the one it once stored when it was first activated.)

 

TinyPortal © 2005-2018