Recent

Author Topic: [SOLVED] TAChart hours displayed backwards  (Read 9521 times)

wp

  • Hero Member
  • *****
  • Posts: 13412
Re: TAChart hours displayed backwards
« Reply #15 on: October 31, 2018, 01:44:44 pm »
Without the handler and your example code, [...] the bottom axis correctly shows the date/time [...]
But you were saying in the first post that without the handler the times are inverted. You lost me...

munair

  • Hero Member
  • *****
  • Posts: 887
  • compiler developer @SharpBASIC
    • SharpBASIC
Re: TAChart hours displayed backwards
« Reply #16 on: October 31, 2018, 01:53:50 pm »
Yes, as you provided some solutions, I picked the one that replaces the year. That works. But if I use your third option, which involves the OnMarkToText handler, then I get the SIGSEGV in the code as shown before.

Of note is that the handler itself is being processed correctly. It is AFTER the marks have been processed that the SIGSEGV occurs.
It's only logical.

munair

  • Hero Member
  • *****
  • Posts: 887
  • compiler developer @SharpBASIC
    • SharpBASIC
Re: TAChart hours displayed backwards
« Reply #17 on: October 31, 2018, 02:48:34 pm »
This is really interesting. When I add the OnMarkToText handler to the bottom axis without additional code, the project runs without errors. But as soon as I try to assign something to AText, I get a SIGSEGV.

This is ok:
Code: Pascal  [Select][+][-]
  1. procedure TfoSSGIGraphic.chartSSGIAxisList1MarkToText(var AText: String;
  2.   AMark: Double);
  3. begin
  4. end;

This raises a SIGSEGV:
Code: Pascal  [Select][+][-]
  1. procedure TfoSSGIGraphic.chartSSGIAxisList1MarkToText(var AText: String;
  2.   AMark: Double);
  3. begin
  4.   AText := FormatDateTime('d ', AMark)
  5. end;

I found no difference with wp's example project. I removed the ChartToolSet but it doesn't make a difference. If this was some initialization problem I would suspect a call to the handler would already raise an exception. But the exception comes afterwards in the Measure routine, as pointed out before:
Code: Pascal  [Select][+][-]
  1.     function TCustomChartAxisMarks.Measure(ADrawer: IChartDrawer;
  2.       AIsVertical: Boolean; ATickLength: Integer;
  3.       AValues: TChartValueTextArray): Integer;
  4.     var
  5.       t: TChartValueText;
  6.     begin
  7.       Result := 0;
  8.       if not Visible then exit;
  9.       for t in AValues do // SIGSEGV
  10.         // Workaround for issue #19780, fix after upgrade to FPC 2.6.
  11.         with MeasureLabel(ADrawer, t.FText) do
  12.           Result := Max(IfThen(AIsVertical, cy, cx), Result);
  13.       if Result = 0 then exit;
  14.       if DistanceToCenter then
  15.         Result := Result div 2;
  16.       Result += ADrawer.Scale(ATickLength) + ADrawer.Scale(Distance);
  17.     end;

It looks like the text array is somehow not created.
It's only logical.

munair

  • Hero Member
  • *****
  • Posts: 887
  • compiler developer @SharpBASIC
    • SharpBASIC
Re: TAChart hours displayed backwards
« Reply #18 on: October 31, 2018, 02:59:52 pm »
Further debugging shows that the string element of the text array cannot be accessed. See attached screenshot.
« Last Edit: October 31, 2018, 03:04:26 pm by Munair »
It's only logical.

wp

  • Hero Member
  • *****
  • Posts: 13412
Re: TAChart hours displayed backwards
« Reply #19 on: October 31, 2018, 03:49:53 pm »
I fear that you are doing something wrong with your data in the very beginning. But I don't have any clue how to start. I think the only way we can fix this is that you extract the charting code from your application, create a project and upload it here.

Begin with an empty chart. Add the series which you use in your application. Add some dummy data and set chart properties in the same way as you do in the app. Always test until the error appears. Upload the project which shows the error.
« Last Edit: November 01, 2018, 06:16:31 pm by wp »

munair

  • Hero Member
  • *****
  • Posts: 887
  • compiler developer @SharpBASIC
    • SharpBASIC
Re: TAChart hours displayed backwards
« Reply #20 on: October 31, 2018, 03:54:05 pm »
I fear that you are doing something wrong with your data in the very beginning. But I don't have clue how to start. I think the only way we can fix this is that you extract the charting code from your application, create a project and upload it here.

Begin with an empty chart. Add the series which you use in your application. Add some dummy data and set chart properties in the same way as you do in the app. Always test until the error appears. Upload the project which shows the error.
I will, but this may take some time. So I'll get back to it. Thank you for your input.
It's only logical.

munair

  • Hero Member
  • *****
  • Posts: 887
  • compiler developer @SharpBASIC
    • SharpBASIC
Re: TAChart hours displayed backwards
« Reply #21 on: October 31, 2018, 10:43:05 pm »
Here is a test project with the TAChart component taken from my large project. See attachment.

First run the project as-is. It should run. Then uncomment the code in the handler. A SIGSEGV exception will occur.

Note that extra tools are added to the chart which allow for panning and zooming (Ctrl+leftMouseButton, Ctrl+RightMouseButton, LeftMouseButton panning).

File: tachart-test.zip
It's only logical.

wp

  • Hero Member
  • *****
  • Posts: 13412
Re: TAChart hours displayed backwards
« Reply #22 on: October 31, 2018, 11:46:49 pm »
Oh, a very mean mistake... The {$mode} directive at the top of the unit says {$mode objfpc}, but there is no {$H+}. The {$H+} is very essential because otherwise Strings are Shortstrings instead of AnsiStrings (http://wiki.freepascal.org/Mode_ObjFPC).

Adding the {$H+} (or removing the {$mode} altogether because it is set up in the project settings already) fixes the issue. Alternatively you can also use {$mode delphi} (I prefer mode objfpc, though, because of its thigher syntax).

munair

  • Hero Member
  • *****
  • Posts: 887
  • compiler developer @SharpBASIC
    • SharpBASIC
Re: TAChart hours displayed backwards
« Reply #23 on: November 01, 2018, 06:33:08 am »
Ahhh! Well, that's the last thing I would've looked at.
« Last Edit: November 01, 2018, 07:08:17 am by Munair »
It's only logical.

munair

  • Hero Member
  • *****
  • Posts: 887
  • compiler developer @SharpBASIC
    • SharpBASIC
Re: TAChart hours displayed backwards {SOLVED}
« Reply #24 on: November 01, 2018, 07:15:29 am »
One thing I noticed is that with negative dates, the chart misses one day (as wp already pointed out). It starts correctly on the left, but when zoomed in, it actually shows the next day. In this example, January 22 is actually January 23 when zoomed in and the chart is plotted plus one day. Subtracting one day works, but then at the initial zoom level it shows January 21, which doesn't match the header at the top that says January 22.

So the best solution seems to be to replace the year with a positive one (1900>) and never show the year (which is/can be indicated at the top.

Thanks wp. This is quite a helpful thread.
It's only logical.

munair

  • Hero Member
  • *****
  • Posts: 887
  • compiler developer @SharpBASIC
    • SharpBASIC
Re: [SOLVED] TAChart hours displayed backwards
« Reply #25 on: November 01, 2018, 10:39:04 am »
In case anyone is interested in the solution I chose above, it might be preferred that the year substitution matches a similar leap year range, taking into account a possible gap of 7 years (in case a chart covers more than three years). The following code provides that solution. While I do not know if Pascal's built-in leap year function handles years < 1900 well, an alternative function is included.

Code: Pascal  [Select][+][-]
  1. function LeapYear(const y: integer): boolean;
  2. begin
  3.   result := false;
  4.   if (y mod 4) = 0 then
  5.     // divisible by 4
  6.     begin
  7.       if (y mod 100) = 0 then
  8.         // divisible by 100
  9.         begin
  10.           // must also be divisible by 400
  11.           if (y mod 400) = 0 then
  12.             result := true
  13.         end
  14.       else
  15.         result := true;
  16.     end;
  17. end;
  18.  
  19. procedure Above1899(var y: integer);
  20. { substitutes years < 1900 for 'modern' years and takes into account
  21.   a maximum of 7 years without a leap year. }
  22. var
  23.   n: byte;
  24. begin
  25.   if y >= 1900 then
  26.     exit;
  27.   n := 0;
  28.   while (LeapYear(y + n) = false) do
  29.     n := n + 1;
  30.   y := (2104 - n);
  31. end;
  32.  
  33.  
It's only logical.

wp

  • Hero Member
  • *****
  • Posts: 13412
Re: [SOLVED] TAChart hours displayed backwards
« Reply #26 on: November 01, 2018, 11:09:12 am »
In SysUtils:
Code: Pascal  [Select][+][-]
  1. function IsLeapYear(Year: Word): boolean;
  2. begin
  3.   Result := (Year mod 4 = 0) and ((Year mod 100 <> 0) or (Year mod 400 = 0));
  4. end;
  5.  

munair

  • Hero Member
  • *****
  • Posts: 887
  • compiler developer @SharpBASIC
    • SharpBASIC
Re: [SOLVED] TAChart hours displayed backwards
« Reply #27 on: November 01, 2018, 11:40:39 am »
In SysUtils:
Code: Pascal  [Select][+][-]
  1. function IsLeapYear(Year: Word): boolean;
  2. begin
  3.   Result := (Year mod 4 = 0) and ((Year mod 100 <> 0) or (Year mod 400 = 0));
  4. end;
  5.  
OK, so the built-in function can be used too. That's good to know. BTW, I used the same definition in the past, also for astronomical calculations. Just note that this function isn't optimized. Testing divisible by 4 first, already catches 75% of the cases without the need for further testing. Might be trivial, but it makes a difference with lengthy (astronomical) computations. Just a thingy for those who prefer/need speed.
It's only logical.

howardpc

  • Hero Member
  • *****
  • Posts: 4144
Re: [SOLVED] TAChart hours displayed backwards
« Reply #28 on: November 01, 2018, 12:58:50 pm »
Just note that this function isn't optimized. Testing divisible by 4 first, already catches 75% of the cases without the need for further testing.
How then would you optimize it further?
By default boolean expressions are short-circuit-evaluated. So once the "divisible by 4" test fails the function returns False.

munair

  • Hero Member
  • *****
  • Posts: 887
  • compiler developer @SharpBASIC
    • SharpBASIC
Re: [SOLVED] TAChart hours displayed backwards
« Reply #29 on: November 01, 2018, 01:48:54 pm »
Thank you for the clarification. I did several tests with the FreeBasic compiler last year and the results were quite surprising. Even FreeBasic's AndAlso short-circuit expression was slower than the code I provided in my example. I would have to do the same tests with the FreePascal compiler to see if there is any difference. Maybe there isn't. Thanks again.
It's only logical.

 

TinyPortal © 2005-2018