Recent

Author Topic: TAChart: minor ticks, minor grid, logarithmic scaling  (Read 24849 times)

wp

  • Hero Member
  • *****
  • Posts: 13334
TAChart: minor ticks, minor grid, logarithmic scaling
« on: June 02, 2011, 12:14:20 am »
Hi

I modified the source files TAChartAxis.pas and TAIntervalsources.pas to include optional drawing of minor ticks for both linear and logarithmic axes.

The new property of TChartAxis, MinorTickCount, determines the number of minor ticks drawn between major tick marks:
  • 0 --> no minor ticks (default)
  • -1 -->  automatic determination of minor ticks
  • other --> number of minor ticks between major ticks
Optionally, a minor grid can be drawn at the positions of the minor ticks.

I also fixed the behavior of the ticks for logarithmic axes. In the original version the marks were drawn at the same numbers as for a linear scale. Now the marks are located at the powers of the logarithmic base. In case of base 10, these are the powers of 10, i.e. .01, .1, 1, 10, 100, 1000 etc. Minor ticks are drawn at even numbers in between (except for non-integer bases).

Unfortunately, the log base of the TLogarithmAxisTransform is e (2.7...) by default. I would have preferred to have the value of 10 here - I have rarely seen scientific or technical log diagrams with a log base other than 10.

I should mention that minor tick marking does not work correctly if the axis property MarkAtDataOnly is set. What does this parameter mean?

This posting is accompanied by the source codes of the modified units along with a simple demo project. The TAChart units are based on revision 30686, my changes are marked by "wp". I would appreciate if this code could be incorportated into the main development line of TAChart.

Werner

Ask

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 687
Re: TAChart: minor ticks, minor grid, logarithmic scaling
« Reply #1 on: June 02, 2011, 02:07:19 am »
Thanks for your contribution.

I am at the conference right now, so can not work on TAChart.
After my return, I intend to implement both minor ticks and key events for tools
you requested in the other topic.

I have already thought about possible implementations,
including the hard-coded method you chose. I still hope I can find a way
to avoid breaking encapsulation. If I fail, I will certainly use your code.   

Quote
Unfortunately, the log base of the TLogarithmAxisTransform is e
The whole axis transformation code is somewhat experimental.
As far as I know, you are the first one to use non-linear transformation ;-)
So I am open to suggestions.
In particular, maybe the default base should change.

Quote
axis property MarkAtDataOnly is set. What does this parameter mean?
It means that marks should only be drawn near the data points.
Some if the axis demo pages should have an example.

Quote
I would appreciate if this code could be incorporated into the main development line of TAChart.
While I am away, you could do the following. It is certainly not required,
but if done, would simplify my work and increase the chance your code will be accepted.
1) Create a feature request on Mantis in TAChart category.
It will be auto-assigned to me. You can add a link to this thread in the description.
2) Post your code there as patches, not whole files.
This will allow you to remove the comments near your changes.
3) You changes include two topics: adding logarithmic interval source and
minor ticks. It would be best to separate them in a different patches
and possibly different feature requests.
The last point may be hardest to do, but also the most important.

Anyway, as I said, if you do not have time or inclination, it is ok --
it will just take longer, but I will still fulfill your request.

Ask

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 687
Re: TAChart: minor ticks, minor grid, logarithmic scaling
« Reply #2 on: June 02, 2011, 02:38:47 am »
Looking at your code, I have a few notes
(I am unable to actually run it, so might have missed something):
1) It would be good if you used the same coding style as the other TAChart code.
I know that Lazarus is pretty inconsistent in this area, but I have taken
a serious effort to at least keep a single style in TAChart.
2) axis drawing code seems to be overwriting the properties of it's data source.
It can be quite a surprise to the user.
3) It seems you ignore scaling factor with minor ticks drawing.
4) It seems that you abandoned step-selection code for the logarithmic interval source. This might pose a problem for large zoom factors.
5) Did you test stripe drawing? I am not quite sure it will work.
Also, you decided not to apply stripes to minor grid -- I am not saying it is
incorrect decision, but I think it merits at least a discussion.
6) While are you special-casing the LogBase of 2?

Thanks again for you contribution. Please do not be discouraged by the
notes above -- even the fact that you did attempt to help TAChart development
is very pleasant to me ;-)

wp

  • Hero Member
  • *****
  • Posts: 13334
Re: TAChart: minor ticks, minor grid, logarithmic scaling
« Reply #3 on: June 02, 2011, 12:24:23 pm »
Quote
The whole axis transformation code is somewhat experimental.
As far as I know, you are the first one to use non-linear transformation ;-)
Then you, maybe, have some freedom to apply some changes to that without too much breaking existing code. Besides the logbase selected for the LogarithmAxistransform I feel uncomfortable with the way data are added to the series: when I want to draw a log plot I have to calculate the logarithm before adding the data to the series. I would prefer to add the data directly, without doing the transformation, and let the transformation do the calculation. In this case, the user would just activate or deactivate the transformation without a need to redo the calculation. This is the way gnuplot and TChart work.

By the way, the way you handle transformations seems to be very flexible. I think it will be possible to implement a transformation for the horizontal axis of Arrhenius diagrams. In these diagram the reciprocal temperature (1/T) is plotted on the x axis, but the axis is labelled with the temperature directly. gnuplot can't do it that way, it just labels the 1/T values...

Quote
It would be good if you used the same coding style as the other TAChart code.
Sorry, I was just writing code the way I usually do. Feel free to modify according to your style. You can also remove my "wp" comments - code gets easily unreadable with these things.

Quote
axis drawing code seems to be overwriting the properties of it's data source.
I temporarily hooked a ChartSource to the Marks property and it works as expected (at least by me - maybe I am seeing something wrong). However, there is a bug in my insertion in TAChartAxis.GetMarkValues: remove the "else" branch.

Quote
It seems you ignore scaling factor with minor ticks drawing.
Oh, I overlooked that feature. I'll check it out if when I have updated Lazarus on my home PC to a more recent version.

Quote
It seems that you abandoned step-selection code for the logarithmic interval source. This might pose a problem for large zoom factors.
Yes, I am aware of that. I wanted to get your response before going into such details.

Quote
Did you test stripe drawing?
Yes, it works, it is included in the demo. But if stripes and minor grids are activated for both axes only the minor grid of one axis is drawn. But I think, unless using alpha transparency, that's the way it should be.

Quote
While are you special-casing the LogBase of 2?
I just wanted to have a minor tick between 2 and 4, 4 and 8 etc. But this is not covered in the case of the other LogBases. Cetainly there is a way to combine it all.

Quote
even the fact that you did attempt to help TAChart development
is very pleasant to me ;-
Some kind of acknowledgement of your great work that you put into TAChart.



wp

  • Hero Member
  • *****
  • Posts: 13334
Re: TAChart: minor ticks, minor grid, logarithmic scaling
« Reply #4 on: June 29, 2011, 08:56:30 am »
Alexander, I saw that you recently added minor tick and grid support. Great work. Thank you.

There is a small issue, though: Minor ticks seen to be drawn only between the first and last visible marks. So, when, for example, the data start at -0,3 and the first mark is at 0 with minor tick distance 0.1, then there are no minors between -0,3 and 0. I think you should extent the minor tick drawing range on both sides by one mark distance and skip the minors that are off axis.


Ask

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 687
Re: TAChart: minor ticks, minor grid, logarithmic scaling
« Reply #5 on: June 30, 2011, 01:59:06 pm »
I know, this is why I did not post about the feature yet.
I am working on it ;-)

Edit: Just to explain why it takes so long -- consider that marks may be
not equidistant, so to draw minor ticks on the edges correctly I have to
get one extra mark to the right and left of the requested range.
This, in turn, requires modifying all kinds of ChartSource's which provide mark data.

I also prefer to refactor code to fit new features -- for example, look at
r31423-r31428 as preparation for minor ticks drawing, and r31249 as final implementation.
« Last Edit: June 30, 2011, 05:51:21 pm by Ask »

Ask

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 687
Re: TAChart: minor ticks, minor grid, logarithmic scaling
« Reply #6 on: July 10, 2011, 08:53:59 am »
Fixed the problem with minor ticks at the edges in r31643

wp

  • Hero Member
  • *****
  • Posts: 13334
Re: TAChart: minor ticks, minor grid, logarithmic scaling
« Reply #7 on: July 21, 2011, 10:22:25 am »
You added labelling of minor ticks in the recent svn. That is a fine feature, but may make standard charts quite crowded. I'd prefer to have minor marks labelling off by default, i.e. Minors[...].Marks.Style = smsNone.

BTW, Minors[..].Visible and Minors[...].Marks.Visible seem to have the same effect. What would be the intended difference between these options?

Another remark on the number of minor ticks (IntervalsCount): presently, this is a fixed quantity, its default value of 5 is fine for most cases. However, when the major tick interval is 2, 20, 200, etc, I'd prefer to have only 4 minor intervals. Then, for the major interval between 0 and 20, the minor ticks would be at 5, 10 and 15 which is more graspable for the user than the present case with minor ticks at 4, 8, 12 and 16. Would it be possible to have a default "automatic" IntervalsCount setting (maybe 0, or -1) where the internal minor interval count is determined from the mantissa of the major tick interval.

Ask

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 687
Re: TAChart: minor ticks, minor grid, logarithmic scaling
« Reply #8 on: July 21, 2011, 06:45:05 pm »
Quote
I'd prefer to have minor marks labelling off by default,
I know, I just did not have the time to write it yesterday.
Unfortunately, adding correct defaults for components is cumbersome --
in this instance, it required about 60 lines of code!

Anyway, default should be correct since r31776

Quote
Minors[..].Visible and Minors[...].Marks.Visible
Not much. Basically, MinorAxis.Visible = Marks.Visible + Grid.Visible

Quote
Would it be possible to have a default "automatic" IntervalsCount setting ... where the internal minor interval count is determined from the mantissa of the major tick interval.
The setting itself is easy, but the question is, what should be the algorithm
for auto-determining the intervals.
For example, for interval of 0..20, current IntervalChartSource
will get 5, 10, 15, but for interval 0..40 it will get 5, 10, 15, 20, 25, 30, 35
which may be too many.

I have some plans for improvement of IntervalChartSource in the general case,
but have not fully thought them out yet.
In short, axis intervals should be determined from:
  • value range
  • desired number of intervals
  • desired interval minimum interval length (both in axis units and pixels)
  • desired interval maximum interval length (both in axis units and pixels)
  • desired divisor(s) -- e.g. currently they are 0.2, 0.5, 1.0, but user may want, e.g. 0.25
  • axis transformation -- e.g. logarithmic, exponential and other non-linear transformations may require non-equidistant marks
  • mark labels format -- sometimes it is preferable to change axis intervals
      so as to avoid overlapping labels

Obviously, the above criteria may conflict,
so user should additionally provide some method of prioritization.
Finally, the solution space may have non-trivial structure, so
some advanced searching algorithm (e.g. genetic algorithm) may be required.

wp

  • Hero Member
  • *****
  • Posts: 13334
Re: TAChart: minor ticks, minor grid, logarithmic scaling
« Reply #9 on: July 21, 2011, 08:05:58 pm »
Quote
I know, I just did not have the time to write it yesterday.
I did not want to urge you. TAChart is in a highly usable state now, and I am preferring Lazarus & TAChart over Delphi & TeeChart.

Quote
The setting itself is easy, but the question is, what should be the algorithm for auto-determining the intervals.
I see the complexity, but your recent additions to TAChart demonstrated that you will find a good solution.  Even if you would decide to leave it as it is, TAChart's capabilities would surely outpass most of the dumb office charting packages.

wp

  • Hero Member
  • *****
  • Posts: 13334
Re: TAChart: minor ticks, minor grid, logarithmic scaling
« Reply #10 on: July 22, 2011, 12:21:44 pm »
Quote
the above criteria may conflict, so user should additionally provide some method of prioritization.

Maybe it is not that complicated if we consider that automatic labelling must be handled correctly only for the default settings.

A linear chart uses automatic tic labels with 1, 2 or 5 increments, that's hardcoded in TAChart. The minor labels then should be drawn with 5, 4 or 5 divisions, respectively. Logarithmic axes should be labelled at integer powers of the logbase (0.1, 1, 10, 100, ...), minor ticks should be at 2,3,4,..,8, 20,30, etc. If the logarithmic range is less than 2 decades then tics should be drawn as if it were a linear axis. I don't know about logarithmic axes with logbases different from 10, though, but this is rarely used. Also, other transformations will not be used very often.

Maybe another useful requirement to be put into the tick-finding procedure would be the size of the chart. A large chart will accept more divisions than a small chart.

All other cases, like the 0...40 interval that you mentioned above, can happen only by user changes of the default values. In this case, the user should be made responsible for correct labelling according to his needs by using the axis' chartsource.


Ask

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 687
Re: TAChart: minor ticks, minor grid, logarithmic scaling
« Reply #11 on: August 03, 2011, 07:46:05 pm »
I have implemented a Axis.Intervals property in r31836-31861.
See "Intervals" page in the axis demo for an example (try resizing the window),
and http://wiki.lazarus.freepascal.org/TAChart_documentation#Axis_intervals
for some documentation.
Some other pages in axis and axistransf demos now also use Intervals property.
There is no logarithmic support yet, but other than that, it is pretty flexible.

Note that minor axises now also use Intervals property,
and IntervalsCount property is now removed. Sorry for the inconvenience.

Please test -- I would like to hear you opinion on various aspects of the design,
and in particular the default values for interval parameters.

Ask

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 687
Re: TAChart: minor ticks, minor grid, logarithmic scaling
« Reply #12 on: August 05, 2011, 08:02:16 am »
Quote
There is no logarithmic support yet

Added aipGraphCoords option -- see "Logarithm" page of axistransf demo
since r31867

wp

  • Hero Member
  • *****
  • Posts: 13334
Re: TAChart: minor ticks, minor grid, logarithmic scaling
« Reply #13 on: August 07, 2011, 09:38:26 am »
I'm having problems plugging it together such that it works as I would expect.

I created a series of the function y = exp(x) with 100 x values spread equidistantly between -6 and 6 and used a LogTransform for the y axis with LogBase = 10 to create the semilogarithmic plot.

Activating aipGraphCoords in the left axis' intervals options created a diagram almost as I would expect. It had labels at full decades, i.e. at 0.01, 0.1, 1, 10, 100 etc. When I increased the size of the diagram, however, suddenly half-decade labels at 0.31622, 3.1622 etc. jumped in. How can I get rid of these labels? - I only want labels at the 1's.

Alternatively to the default labelling I tried to use a ListChartSource with all full decade values ranging from 1e-10|1e-10|? to 1e10|1e10|?. This gave immediately the correct behavior. However,the label at 0.1 was skipped (it is contained in the ChartSource, though). Can there be rounding errors when working with chart sources? When removing the aipGraphCoords option, however, the missing label re-appeared.

In addition, the minor labels (10 divisions) were not shown above the last major label. This does not change when aipGraphCoords is cleared. I know you fixed that some time ago, but could you re-check the TListChartsource?

See the enclosed screenshot for the ListChartSource issues.


Ask

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 687
Re: TAChart: minor ticks, minor grid, logarithmic scaling
« Reply #14 on: August 08, 2011, 11:39:20 am »
Quote
When I increased the size of the diagram, however, suddenly half-decade labels at 0.31622, 3.1622 etc. jumped in. How can I get rid of these labels? - I only want labels at the 1's.
The algorithm works like this:
1) It tries various "nice" steps to see if it can find a set of marks satisfying all the requirements
2) It the above fails, it fall back to simple equidistant division, which usually results in "ugly" numbers you see.
So, to get "nice" labels, you can:
1) Do not allow user to zoom/pan the chart, and pre-select good axis ranges --
this method is used by most non-interactive charting libraries.
2) Extend the set of "nice" labels -- for example, you can try setting NiceSteps to
"0.30102999566398|0.698970004336|1.0" which is Log10(2), Log10(5) and Log10(10)
3) Reduce the requirements -- for example, remove UseMinLength and/or UseMaxLength options.
Unfortunately, it seems that the finding a good set of marks for logarithmic scale
is surprisingly hard in general case -- for example, try to find a good set of 5 logarithmic steps between values 200 and 300.

Quote
This gave immediately the correct behavior. However,the label at 0.1 was skipped (it is contained in the ChartSource, though). Can there be rounding errors when working with chart sources? When removing the aipGraphCoords option, however, the missing label re-appeared.

This is probably because of aipUseMinLength option.
When this option is set, if the marks are too near to each other, one of them is hidden.
Setting aipGraphCoords confused calculation of image coordinates
(this option is not currently useful for list sources), so the chart decided that 0.1 mark is too close to its neighbor.

Quote
In addition, the minor labels (10 divisions) were not shown above the last major label. This does not change when aipGraphCoords is cleared. I know you fixed that some time ago, but could you re-check the TListChartsource?

It was due to a different bug. Should be fixed in r31911

 

TinyPortal © 2005-2018