Recent

Author Topic: [SOLVED] How to use NiceSteps in a Left Axis  (Read 6948 times)

donnie

  • Jr. Member
  • **
  • Posts: 72
[SOLVED] How to use NiceSteps in a Left Axis
« on: September 29, 2016, 03:15:07 pm »
Hi there,
 
In a TaChart I want the user to set the Nice Steps in the left axis. The Ymax and Ymin are also set by the user. Now let's say that we have Ymax=10 and Ymin=-10. I have the Intervals.MaxLength=100 and the Intervals.MinLength=20, the Intervals.Count = 5 (aipUseVount=False) and I set the NiceStep to 0.1. When I do that the Left Axis works fine and is separated to 10,9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-10.
   NiceStepsValues          Left Axis Values
          0.15 --------->            9, 7.5, 6, 4.5, 3, 1.5, 0 -1,5, -3, -4.5, -6,-7.5, -9          (OK)
          0.20 --------->           10, 8, 6, 4, 2, 0, -2, -4, -6, -8, -10                              (OK)
          0.3 --------->             9, 6, 3, 0, -3, -6, -9                                                   (OK)
          0.35 --------->           7, 3.5, 0, -3.5, -7                                                      (OK)
          0.36 --------->          10.14, 6.08, 2.03, -2.03, -6.08, -10.14                        (NOT OK)
          0.4 --------->            10.14, 6.08, 2.03, -2.03, -6.08, -10.14                        (NOT OK)
          0.5 --------->            10.14, 6.08, 2.03, -2.03, -6.08, -10.14                        (NOT OK)
           ...                                   ....                                                                          (NOT OK the same until 0.7)
          0.7 --------->            10.14, 6.08, 2.03, -2.03, -6.08, -10.14                             
          0.8  --------->            9.6, 8.8, 8, 7.2, 6.4, 5.6, 4.8, 4, 3.2, 2.4, 1.6, 0.8, 0, -0.8, .... -8.8, -9.6 (OK but it is x10 compared to above OK values)
          0.9   --------->          9.9, 9, 8.1, 7.2, 6.3, 5.4, 4.5, 3.6, 2.7, 1.8, 0.9, 0 ....-9, -9.9

Really confused with the above results.
With some values it works fine and with others it is confused.
I have also read the following topic http://forum.lazarus.freepascal.org/index.php/topic,13333.15.html  and http://wiki.lazarus.freepascal.org/TAChart_documentation#Date-time_interval_source at the Axis Intervals
and haven't got the idea very well. Can someone help me with the Intervals.MaxLength , Intervals.MinLength , Intervals.Count, Tolerance and the nice step value.
Why the nice step value has 3 values eg. 0.1|0.5|1.0 as it takes only the left and ignores all the others values?

Thank you in advance!!!
« Last Edit: October 04, 2016, 03:03:17 pm by donnie »

wp

  • Hero Member
  • *****
  • Posts: 11923
Re: How to use NiceSteps in a Left Axis
« Reply #1 on: September 29, 2016, 11:18:47 pm »
I agree that axis labeling of TAChart may be difficult to understand.

The problem is that there are several conflicting requirements: nice intervals and density of labels. "Nice" intervals, or "nice steps", means: I want labels 0, 2, 4, 6, 8 10, but not 0, 1.232, 2.464, 5.918 etc. "Densitiy of labels" means:the labels should not be too close (and overlap), but also there should be "enough" labels, not only - say - two.

Let me give you an example (I don't know if I can describe the algorithm exactly, but you should catch its principle).

Suppose the axis ranges between -1 and 11. Suppose at first that aipUseCount is the only Option checked. Then all other parameters of the Intervals are ignored except for the Count which is 5 by default. This means that there will be 5 intervals (i.e. 6 labels) between -1 and 11: the first label is -1, the last label is at 11, and the others will be at equally-spaced increments of (11-(-1) / 5 = 2.4 --> there will be labels at -1, 1.4, 3.8, 6.2, 8.6 and 11.

Well - these are not "nice" numbers (although they could still be much worse!)

Therefore, the aipUseCount is normally not very useful (although there can be opposite cases). Normally you should stick to the default Options: aipUseNiceSteps, aipUseMinLength, aipUseMaxLength. This operation conditions are primarily determined by the values specified in the "NiceSteps" property: "0.2|0.5|1.0" by default. Let me modify it to "2|5|10" - it has the same effect, but is easier to explain:

The algorithm begins with the first number, 2, and checks which multiples of 2 would fit into the data range: 0, 2, 4, 6, 8, 10 - Very nice numbers. The algorithm could stop here, but this is just due to the selection of the numbers we started with.

The next step is to calculate the distance, in pixels, between adjacent label centers, in other words, between the grid lines belonging to these labels. This distance is compared with the values entered for MinLength and MaxLength.

There are three cases

(1) The distance is between MinLength and MaxLength --> the current label interval (2) is fine, and labels will be selected to be at 0, 2, 4, 6, 8, 10

(2) If the distance is larger than MaxLength (i.e. the labels are too much apart) then the algorithm picks the next value in the NiceSteps, 5. The labels now would be drawn at 0, 5, 10. If the distance between these labels is too large again, the next number would be picked once more: 10. And we would be left with labels at 0 and 10 only. etc. And in case of very small charts and / or unfavorable MaxLength, the algorithm could even go one step further and end up with one label left at 0.

(3) The distance is smaller than MinLength (i.e. the labels are too close). Then the algorithm picks the next smaller number from the NiceSteps list. Well, in this case, the 2 was the first number in the list, therefore, the program picks now the last number and divides it by 10, i.e. we select an interval length of 10/10 = 1. Now our labels would be at -1, 0, 1, 2, 3, 4, 5, 6 7, 8, 9, 10, 11. If the distance of these labels still would be smaller than MinLength the next test interval would be 5/10 = 0.5 --> -1, -0.5, 0, 0.5, 1, 1.5, ... etc.

I hope you understand now that the values specified between the bar characters in the NiceSteps define the basic steps used for label increments. These steps are multipied by 10, 100, or 0.1, 0.01 etc as needed for the data range. It is not important wether NiceSteps is specified as "0.2|0.5|1.0" or "2|5|10" or "20|50|100" etc. You may even change the order (i.e. use "1|2|5").

Why use 1, 2 and 5? Because these are the only integer numbers with which you can span a full decade (e.g., 0, 2, 4, 6, 8, 10). If you would add a "3" to the NiceSteps ("1|2|3|5") then our example might lead to labels like 0, 3, 6, 9, which may look ok here but could get very terrible in more general cases.

The NiceSteps of the first post above, '0.1|0.5|1.0', contains a redundant 1, you can skip either the 0.1 or the 1.0 because it will be tested anyway, depending on the MinLength/MaxLength values. But having the redundant value in the NiceSteps does not affect the label selection process.

Using the DateTimeChartSource it may be a good idea to modify the NiceSteps because the base order of magnitude on time scales is not a factor 10 but the factor 60 (1 hour = 60 min, 1 min = 60 sec), or 24 (1 day = 24 hours). Therefore, I recommended in the other thread to replace the NiceSteps by "1|2|3", i.e labels at 0, 10, 20, 30, 40, 50 seconds, or at 0, 20, 40 sec, or at 0, 30 sec. This is for minute and second units (Steps). For hours, I'd use NiceSteps = '1|2|4|6|12', similarly for months. Years should use the normal settings. With days there's the problem that the natural period length varies: a month may be 28, 29, 30, or 31 days long, and whatever parameters you select, the 1st label of a month will always drift. This is still an issue.

Or if you plot an angle the basic period is - maybe - 90 degrees. Here you may also want increments of 15 (0, 15, 30, 45, 60,...) or 30 (0, 30, 60, 90), or 45 (45, 90), i.e. NiceSteps = '10|15|30|45|90', or similar. Here it is very important that MinLength and MaxLength are selected correctly because if the algorithm picks the 90 and divides it by 10 you will see labels at 0, 9, 18, 27, etc.

A difficult case is given by logarithmic charts. Here the logarithms of values are plotted on an invisible linear plot, like on old-fashioned graph paper. The units on the graph paper are called "graph units" by TAChart, those of the original numbers are "axis units". The labelling algorithm normally uses the axis units to calculate the intervals. But on a log axis the intervals are not constant, and an interval found at one end of the axis is too close (or too wide) on the other end. Therefore, there's the option aipGraphCoords which switches the labeling algorithm to use the graph units, i.e. the log values. Suppose that data extends from 1 to 1000, the decadic logarithms are 0 and 3. The labeller finds the intervals 0, 1, 2, 3 and after inverting the log operation we end up with labels at 10^0 = 1, 10^1 = 10, 10^2 = 100 and 10^3 = 1000.

Unfortunately, it is very difficult to find the correct parameter settings for "nice" log axes. It is advisable to enter a small number (like 3 or 5) to the Tolerance property in order to the reduce the effect of round-off errors. And the NiceSteps should be set at runtime to "Format('%f|%f|%f', [log10(1), log10(2), log10(5)])" - this puts more emphasis on selecting "nice" decadic intermediate values.

I hope this lengthy description explains some of the mystery behind the TAChart axis labels. Maybe I'll add it to the TAChart tutorial series (http://wiki.lazarus.freepascal.org/TAChart#Documentation).
« Last Edit: September 29, 2016, 11:37:20 pm by wp »

donnie

  • Jr. Member
  • **
  • Posts: 72
Re: How to use NiceSteps in a Left Axis
« Reply #2 on: September 30, 2016, 11:52:57 am »
Excellent as always wp!!!  :)
Yes indeed, the aipUseCount to false is better. It doesn't try to fit it no matter what. But still can't understand how will I know how many pixels will be the distance between two adjacent label centers. I ask this in order to set the Min and Max. I have the left axis from -10 to 10 and usually I have a step 1 or 2 or 3. When I have 4 it goes to 1.
According to your documentation this happens because the distance is larger or smaller. I believe that in my case it is larger.
But again how can I estimate the pixels between two grid lines? I ask this because I must set the Min and Max Length.
In my left axis I always have mm which I show as integer numbers. One time it might be from -10 to 10 and the other time it might be -100 to 100. The first time the user might want steps per 1 or 2 or 3 or 4 mm and at the other per 10 or 20 or 30 or 40 mm. At both examples the distance to pixels from two grid lines will be the same. Let's say that I want nice step per 5 which means -10,-5,0,5,10. What should be the Min and Max Length?

wp

  • Hero Member
  • *****
  • Posts: 11923
Re: How to use NiceSteps in a Left Axis
« Reply #3 on: September 30, 2016, 12:17:42 pm »
Could you post a small demo (pas, lfm,lpi and lpr files only, packed into a single zip)? And list the labels that you want to see.

I am asking because the labels also depend on the size of the chart. Therefore it may happen (i.e. will certainly happen) that you tune everything to the label you want, but when you resize the chart, or zoom in or out, you will get something else. In my eyes this is correct, because if you have a large chart you'll need more labels than for a small chart. If you don't want this you could hook into the OnResize event of the chart and scale the Min/MaxLengths with the size change ratio. (Never tries this one, though...)

MinLength should be selected such that labels do not overlap and have a decent spacing between them. Suppose you expect 2 or 3 digit labels and a standard 10-point font - the pixel height of this font is in the order of 12, and the average characterwidth is something like 50 % of the height. Therefore, 3 digits occupy about 15-20 pixels - you may want to set MinLenth to 20.

You see that TAChart uses label distance together with "nice labels" for the labeling process. If you insist on a specific label distance I made good experience if I drop all other items from the NiceLabels and significantly increase the MaxLength. Example: NiceLabels = '5' and MaxLength = 300.
« Last Edit: September 30, 2016, 12:59:36 pm by wp »

donnie

  • Jr. Member
  • **
  • Posts: 72
Re: How to use NiceSteps in a Left Axis
« Reply #4 on: September 30, 2016, 02:12:22 pm »
It is very difficult to make a demo of my example as my charts take data from many .pas files.
Imagine that I have a left axis that the user set the min and max. Let's say that it is -10 to 10.
now if the user select a nicestep let's say 0.1 the left axis must have -10,-9,-8,-7,...0,...,7,8,9,10.
if the user selects nicestep 0.2 the left axis must be -10,-8,-6,-4,-2,0,2,4,6,8,10.
if the user selects nicestep 0.3 the left axis must be -9,-3,0,3,9
if the user selects nicestep 0.5 the left axis must be -10,-5,0,5,10
if the user selects nicestep 0.6 the left axis must be -6,0,6
if the user selects nicestep 0.9 the left axis must be -9,0,9
if the user selects nicestep 1.0 the left axis must be -10,0,10

I set MinLength =1 and MaxLength=270 and all the above steps works fine except the last one with step 1.0 which gives the result of the 0.1 step.
if I want good results for nicestep 1.0 (10,0,10) I must set MaxLength = 300 but then I loose the results for 0.1.
Anyway I think I must sacrifise something in order to divide the grids as I want.
The problem is that these results might differ from screen resolution to screen resolution (As MinLength and MaxLength refers to pixels).
Am I right?
« Last Edit: September 30, 2016, 02:15:26 pm by donnie »

wp

  • Hero Member
  • *****
  • Posts: 11923
Re: How to use NiceSteps in a Left Axis
« Reply #5 on: September 30, 2016, 08:36:06 pm »
I just was thinking of a simple demo without any data. You can use the Axis.Range to define axis limits even without data. But anyway, I understand now what you want to achieve.

If the user can select the label steps then there must be some event which the user is firing, like a ButtonClick, or MenuClick. In this event you can see which increment the user wants and you can adjust the axis parameters accordingly, optimized for this particular increment.

Please see the attached demo in which the user can select an interval from the combobox (I added also a "general" selection which keeps the standard automatic 1/2/5 steps). Change the size of the window and see if the labels are always maintained. On my 1920x1080 monitor this works always, except for rather small charts.

donnie

  • Jr. Member
  • **
  • Posts: 72
Re: How to use NiceSteps in a Left Axis
« Reply #6 on: October 03, 2016, 10:21:00 am »
Exactly that wp!!!
Simple as that! Change the min max according the step. You can't have one min max for all the desired nicesteps. I made the same for more steps.
Thanks once again for your solution.

kupferstecher

  • Hero Member
  • *****
  • Posts: 583
Re: [SOLVED] How to use NiceSteps in a Left Axis
« Reply #7 on: October 06, 2016, 01:45:57 pm »
Hello donnie,

although it's already solved, perhaps this could be helpful for you:
Lately I had a similar issue when implementing a chart where the bounds and spacing (quantity of intervalls) can be defined by the user. Using the spacing instead of the niceSteps gives an advantage when changing the range to different "dimensions", e.g. from 10 to 1000 as than the quantity of labels would increase suddenly, which - in my opinion - is not so nice.

When only using the Count property without niceSteps then zooming will get ugly. I created a new ChartSource where in the unzoomed case the user defined Count and Extent properties are used no matter how queer the labels will result (100% user control). In case of zooming or autoranging the intervals are interpolated which results in a very nice and smooth zooming and panning. In the built-in algorithm in contrast I experienced "jumping" label positions. The Code, a demo and descriptions you can find in the end of the below thread (Thread name is missleading, just scroll down).

http://forum.lazarus.freepascal.org/index.php/topic,33570.15.html

donnie

  • Jr. Member
  • **
  • Posts: 72
Re: [SOLVED] How to use NiceSteps in a Left Axis
« Reply #8 on: October 10, 2016, 01:36:27 pm »
I saw your demo kupferstecher and your approach is very intersting.
I believe that it is for a little more ''advanced'' users.
Everything is set to user and this is nice but dangerous sometimes.
Very nice approach by the way setting the intervals according to the zoom factor. The uFixedIntervalChartSource.pas is  quite customized.
Really don't know what approach to use, yours or wp's. Nice headache... :D

 

TinyPortal © 2005-2018