Issue with drawing of series mark

TeeChart FireMonkey (Windows,OSX,iOS & Android) for Embarcadero RAD Studio, Delphi and C++ Builder (XE5+)
Leo
Newbie
Newbie
Posts: 2
Joined: Fri Nov 06, 2015 12:00 am

Issue with drawing of series mark

Post by Leo » Wed Mar 09, 2016 3:55 pm

Hi,

I have a TFastLineSeries that, for every 50th value entered, should display a single character at the bottom of the chart (but above the x-axis). I've got everything working as I intended, except for a small drawing issue. I'm not sure if it is "as designed" or an issue I've overlooked.

The way I've implemented is as follows:
From a timer event I call this:

Code: Select all

procedure TForm1.RealTimeAdd(Series: TChartSeries);
var
  XValue,YValue : Double;
  aIndex: integer;
begin
  if Series.Count = 0 then begin  // First random point
    YValue := Random(50);
    XValue := now;
  end else begin
    // Next point
    YValue := Series.YValues.Last + Random(10) - 4.5;
    YValue := Max(Min(YValue, 100), 0);
    XValue := now;
  end;

  // Add new point
  aIndex := Series.AddXY(XValue, YValue);

  //For every 50th value add a mark
  if Series.Count mod 50 = 0 then begin
    Series.Marks.Item[aIndex].Text.Text := 'A'; //Just show an 'A' for now
  end;

end;
And then I've implemented the Chart's OnAfterDraw event like this:

Code: Select all

procedure TForm1.chMainAfterDraw(Sender: TObject);
var
  APosition: TSeriesMarkPosition;
  i: Integer;
begin
  inherited;
  APosition := TSeriesMarkPosition.Create;
  try
    begin
      for i := 0 to Series1.Count - 1 do begin
        APosition.Custom := true;
        APosition.LeftTop.x := Series1.CalcXPos(i);
        APosition.LeftTop.y := Series1.CalcYPosValue(8); //Static 
        Series1.Marks.Positions[i] := APosition;
      end;
    end;
  finally
    APosition.Free;
  end;
end;
Screenshot of output:
Please see the small dot right above the 'A'. Why is that there, and what to do to get rid of it? :)

[img]
SupportSteema001.PNG
SupportSteema001.PNG (20.65 KiB) Viewed 29293 times
[/img]

Thanks in advance.

Regards,
Leo

Yeray
Site Admin
Site Admin
Posts: 9614
Joined: Tue Dec 05, 2006 12:00 am
Location: Girona, Catalonia
Contact:

Re: Issue with drawing of series mark

Post by Yeray » Wed Mar 09, 2016 4:19 pm

Hello Leo,

I've added a TChart and a TTimer to a new form and the following code (basically your code plus series and timer initialization):

Code: Select all

uses Series, Math, TeeConst;

procedure TForm1.Chart1AfterDraw(Sender: TObject);
var
  APosition: TSeriesMarkPosition;
  i: Integer;
begin
  inherited;
  APosition := TSeriesMarkPosition.Create;
  try
  begin
    with Chart1[0] do
    begin
      for i := 0 to Chart1[0].Count - 1 do begin
        APosition.Custom := true;
        APosition.LeftTop.x := CalcXPos(i);
        APosition.LeftTop.y := CalcYPosValue(8); //Static
        Marks.Positions[i] := APosition;
      end;
    end;
  end;
  finally
    APosition.Free;
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Chart1.Title.Text.Text:=TeeMsg_Version;
  Chart1.View3D:=false;
  Chart1.Legend.Visible:=false;
  Chart1.Axes.Bottom.DateTimeFormat:='hh:mm:ss';

  with Chart1.AddSeries(TFastLineSeries)do
  begin
    XValues.DateTime:=true;
    Marks.Visible:=true;
    Marks.Style:=smsLabel;
  end;

  Timer1.Interval:=100;
  Timer1.Enabled:=true;
end;

procedure TForm1.RealTimeAdd(Series: TChartSeries);
var
  XValue,YValue : Double;
  aIndex: integer;
begin
  if Series.Count = 0 then begin  // First random point
    YValue := Random(50);
    XValue := now;
  end else begin
    // Next point
    YValue := Series.YValues.Last + Random(10) - 4.5;
    YValue := Max(Min(YValue, 100), 0);
    XValue := now;
  end;

  // Add new point
  aIndex := Series.AddXY(XValue, YValue);

  //For every 50th value add a mark
  if Series.Count mod 50 = 0 then begin
    Series.Marks.Item[aIndex].Text.Text := 'A'; //Just show an 'A' for now
  end;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  RealTimeAdd(Chart1[0]);
end;
And this is what I'm getting:
test.png
test.png (27.5 KiB) Viewed 29279 times
I can't see any dot above the "A"s.
Am I missing any setting? Are you using the latest version available?
Best Regards,
ImageYeray Alonso
Development & Support
Steema Software
Av. Montilivi 33, 17003 Girona, Catalonia (SP)
Image Image Image Image Image Image Please read our Bug Fixing Policy

Yeray
Site Admin
Site Admin
Posts: 9614
Joined: Tue Dec 05, 2006 12:00 am
Location: Girona, Catalonia
Contact:

Re: Issue with drawing of series mark

Post by Yeray » Wed Mar 09, 2016 4:23 pm

Hello again,

I now see it is reproducible in FMX, not in VCL.
We'll be back to you asap.
Best Regards,
ImageYeray Alonso
Development & Support
Steema Software
Av. Montilivi 33, 17003 Girona, Catalonia (SP)
Image Image Image Image Image Image Please read our Bug Fixing Policy

Leo
Newbie
Newbie
Posts: 2
Joined: Fri Nov 06, 2015 12:00 am

Re: Issue with drawing of series mark

Post by Leo » Thu Mar 10, 2016 3:25 pm

Hi,

Thanks for checking out the issue.
I just noticed that the issue disappears when app is run on the actual device. But the issue is still there for the FMX on Windows version.

Regards,
Leo

Yeray
Site Admin
Site Admin
Posts: 9614
Joined: Tue Dec 05, 2006 12:00 am
Location: Girona, Catalonia
Contact:

Re: Issue with drawing of series mark

Post by Yeray » Fri Mar 11, 2016 9:59 am

Hi Leo,

I found what happens.
Since you are assigning new Positions to the marks, they are being used. However, the positions (TSeriesMarkPosition) include Width and Height and you aren't initializing them so they are 0.
Then, depending on the framework a rectangle with width=0 and height=0 is drawn (as a dot) or not.
To fix that, you can calculate the Widht and Height:

Code: Select all

      APosition.Width:=Chart1.Canvas.TextWidth(Labels[i])+7;
      APosition.Height:=Chart1.Canvas.TextHeight(Labels[i])+5;
Or you can just hide the mark rectangles after creating the series:

Code: Select all

Chart1[0].Marks.Transparent:=true;
Best Regards,
ImageYeray Alonso
Development & Support
Steema Software
Av. Montilivi 33, 17003 Girona, Catalonia (SP)
Image Image Image Image Image Image Please read our Bug Fixing Policy

Leo_
Newbie
Newbie
Posts: 11
Joined: Mon Dec 12, 2016 12:00 am

Re: Issue with drawing of series mark

Post by Leo_ » Tue Dec 13, 2016 2:37 pm

Thank you for your help, I just have a small followup question to the case above:

- Is there a way to set the position of the Mark(s), without having to recalc position in ChartAfterDraw?
(Seems a bit odd to have to run through all Series.Count and to find the Marks in use?)

Regards,
Leo

Yeray
Site Admin
Site Admin
Posts: 9614
Joined: Tue Dec 05, 2006 12:00 am
Location: Girona, Catalonia
Contact:

Re: Issue with drawing of series mark

Post by Yeray » Tue Dec 13, 2016 3:24 pm

Hello Leo,

You are right, the Series' OnGetMarkText event could be a better place. Following the example above:

Code: Select all

Procedure TForm1.SeriesGetMarkText(Sender:TChartSeries; ValueIndex:Integer; var MarkText:String);
var
  APosition: TSeriesMarkPosition;
begin
  inherited;
  APosition := TSeriesMarkPosition.Create;
  try
  begin
    with Chart1[0] do
    begin
      APosition.Custom := true;
      APosition.LeftTop.x := CalcXPos(ValueIndex);
      APosition.LeftTop.y := CalcYPosValue(8); //Static
      Marks.Positions[ValueIndex] := APosition;
    end;
  end;
  finally
    APosition.Free;
  end;
end;
Best Regards,
ImageYeray Alonso
Development & Support
Steema Software
Av. Montilivi 33, 17003 Girona, Catalonia (SP)
Image Image Image Image Image Image Please read our Bug Fixing Policy

Leo_
Newbie
Newbie
Posts: 11
Joined: Mon Dec 12, 2016 12:00 am

Re: Issue with drawing of series mark

Post by Leo_ » Mon Feb 13, 2017 2:10 pm

Hi,

Unsure if I should create a new support entry, or continue posting to this thread. My request is related to the current topic so I'll post here, forgive if that's not according to the rules.

Anyway,
Case: Delphi 10.1 Upd 2 + TChart Pro (v2016.19.161025) FMX on Android. Simple TFastLineSeries simulating a running graph. A Timer is used to add a chart value every 45ms. For every 50th point I draw a mark (1-one single charachter) close to the x-axis.

Issue 1: Please have a look at the attached movie to see the odd (incorrect drawing and flickering) changing the device from landscape to portrait. I have 3 different devices available (2 phones and a Pad) and the issue is reproducible on both phones. For the Pad I have not be able to reproduce so far ??

Issue 2: In my simple code example (also attached) the Marks are drawn for every 50th sample coming in. Why is the horisontal space between my marks not equally separated? Am I overlooking something? Maybe using a TTimer is not best approach....

Issue 3: Do you have any comments on the performance (lack of) of the chart drawing, as it does not seems to be anywhere as smooth as I would have hoped? Once again; Am I overlooking something or is this as expected?

How to run the code ex:
1. Start App and let it run for 60 seconds ++. This to come to the point in the app where it deletes "Past values" to simulate a running graph.
2. Now move device back and forth from landscape to portrait to see drawing issue

Regards,
Leif Eirik
Attachments
SupportSteemaCode.zip
Code example
(14.59 KiB) Downloaded 1210 times
SupportSteemaVideo.zip
Video of drawing issue
(438.94 KiB) Downloaded 1204 times

Yeray
Site Admin
Site Admin
Posts: 9614
Joined: Tue Dec 05, 2006 12:00 am
Location: Girona, Catalonia
Contact:

Re: Issue with drawing of series mark

Post by Yeray » Tue Feb 14, 2017 9:37 am

Hello,

I don't see any timer in your application and the RealTimeAdd and DoScrollPoints methods seem not to be called anywhere.
Is that the correct version of the test application?
Best Regards,
ImageYeray Alonso
Development & Support
Steema Software
Av. Montilivi 33, 17003 Girona, Catalonia (SP)
Image Image Image Image Image Image Please read our Bug Fixing Policy

Leo_
Newbie
Newbie
Posts: 11
Joined: Mon Dec 12, 2016 12:00 am

Re: Issue with drawing of series mark

Post by Leo_ » Tue Feb 14, 2017 10:59 am

Ahh, sorry about the incorrect demo project. Had to strip it down due to size limit on attachments and I must have zipped the wrong project.

Anyway, new project attached.
Attachments
Code ex.zip
Demo project
(13.61 KiB) Downloaded 1261 times

Yeray
Site Admin
Site Admin
Posts: 9614
Joined: Tue Dec 05, 2006 12:00 am
Location: Girona, Catalonia
Contact:

Re: Issue with drawing of series mark

Post by Yeray » Wed Feb 15, 2017 8:15 am

Hello,

It seems to work fine for me here in a Nexus 4 running Android 7.1.1. See the video here.
The Android version may be relevant in this issue.
Best Regards,
ImageYeray Alonso
Development & Support
Steema Software
Av. Montilivi 33, 17003 Girona, Catalonia (SP)
Image Image Image Image Image Image Please read our Bug Fixing Policy

Leo_
Newbie
Newbie
Posts: 11
Joined: Mon Dec 12, 2016 12:00 am

Re: Issue with drawing of series mark

Post by Leo_ » Wed Feb 15, 2017 11:37 am

Hi,

Well, that does unfortunately not help me much. Android 7.1.1 is not even available for our Samsung devices yet.
I've tested with Android 4.4.4, 5.0.1 and 6.0.1 For both version #4 and #6 I can see the issue.

Maybe I can ask if you have any suggestions as to think differently to solve the issue...maybe there is a different way to display a running graph with these characters (ID codes) displayed where they are? Maybe use a separate chart for the ID codes, maybe a separate series, use the feature split axis ?
May I be so bold as to ask for your recommendation? (performance is of importance)

Best regards,
Leif Eirik

Yeray
Site Admin
Site Admin
Posts: 9614
Joined: Tue Dec 05, 2006 12:00 am
Location: Girona, Catalonia
Contact:

Re: Issue with drawing of series mark

Post by Yeray » Wed Feb 15, 2017 4:04 pm

Hello,
Leo_ wrote:I've tested with Android 4.4.4, 5.0.1 and 6.0.1 For both version #4 and #6 I can see the issue.
I've just tested your application with an Android 5.1.1 and I could reproduce the problem with the labels drawn at different distances (Issue 2, I'll investigate what's wrong there) but not the flickering problem (Issue 1).
Leo_ wrote:Maybe I can ask if you have any suggestions as to think differently to solve the issue...maybe there is a different way to display a running graph with these characters (ID codes) displayed where they are? Maybe use a separate chart for the ID codes, maybe a separate series, use the feature split axis ?
An alternative could be to use OnAfterDraw event, loop your series points in it and draw the texts manually instead of looping your series points inside OnGetMarkText event.
Ie:

Code: Select all

procedure TfrmMain.chMainAfterDraw(Sender: TObject);
var i: Integer;
    tmpX, tmpY: Integer;
begin
  for i := 0 to aSeries.Count - 1 do begin
    if chMain[0].Marks.Item[i].Text.Text <> '' then
    begin
      tmpX:=chMain[0].CalcXPos(i);
      tmpY:=chMain[0].CalcYPosValue(8);
      chMain.Canvas.TextOut(tmpX, tmpY,chMain[0].Marks.Item[i].Text.Text,False);
    end;
  end;
end;
Leo_ wrote:May I be so bold as to ask for your recommendation? (performance is of importance)
Find here some tips to improve the performance when drawing many points.
However, I don't think you can win much speed than the one you are getting. Why do you think that's slow?
Best Regards,
ImageYeray Alonso
Development & Support
Steema Software
Av. Montilivi 33, 17003 Girona, Catalonia (SP)
Image Image Image Image Image Image Please read our Bug Fixing Policy

Yeray
Site Admin
Site Admin
Posts: 9614
Joined: Tue Dec 05, 2006 12:00 am
Location: Girona, Catalonia
Contact:

Re: Issue with drawing of series mark

Post by Yeray » Thu Feb 16, 2017 10:34 am

Hello,

Regarding the "Issue 1", the problem is you are adding a label each 50 points, and that's regular. But, you are adding values with an XValue that depends on "Now", called from a Timer. And this isn't so regular.
Here the test I did:

Code: Select all

procedure TfrmMain.chMainAfterDraw(Sender: TObject);
var i: Integer;
    tmpX, tmpY: Integer;
    tmpDiff: TDateTime;
begin
  for i := 0 to aSeries.Count - 1 do begin
    if chMain[0].Marks.Item[i].Text.Text <> '' then
    begin
      tmpX:=chMain[0].CalcXPos(i);
      tmpY:=chMain[0].CalcYPosValue(8);
      chMain.Canvas.TextOut(tmpX, tmpY, 'A', False);
      chMain.Canvas.VertLine3D(tmpX, chMain.ChartRect.Top, chMain.ChartRect.Bottom, 0);

      Dec(tmpY, 30);
      chMain.Canvas.TextOut(tmpX, tmpY, FormatDateTime(chMain.Axes.Bottom.DateTimeFormat, chMain[0].XValue[i]), False);

      if i>=50 then
      begin
        tmpDiff:=chMain[0].XValue[i] - chMain[0].XValue[i-50];
        Inc(tmpY, 15);
        chMain.Canvas.TextOut(tmpX, tmpY, '+'+FormatDateTime('s', tmpDiff)+'s', False);
      end;
    end;
  end;
end;
When you let the app run, it add points at a constant time difference. However, if the CPU demand varies, the differences also vary. You can check it by rotating the device or switching to another app for a moment.

I'm not sure if it will satisfy your needs, but another possibility would be to add the labels when the 9 seconds have passed instead of each 50 points. Ie:

Code: Select all

var lastLabelIndex: Integer;

procedure TfrmMain.FormCreate(Sender: TObject);
begin
  FNumOfEntries := 0;
  lastLabelIndex:=0;
//..

procedure TfrmMain.RealTimeAdd(Series: TChartSeries);
var
  XValue,YValue : Double;
  CurrentIndex: integer;
begin
  if Series.Count = 0 then begin  // First random point
    YValue := Random(50);
    XValue := now;
  end else begin
    // Next point
    YValue := Series.YValues.Last + Random(10) - 4.5;
    YValue := Max(Min(YValue, 100), 0);
    XValue := now;
  end;

  // Add new point
  CurrentIndex := Series.AddXY(XValue,YValue);
  if Series.XValue[CurrentIndex]-Series.XValue[lastLabelIndex] >= DateTimeStep[dtOneSecond]*9 then
  begin
    Series.Marks.Item[CurrentIndex].Text.Text:='A';
    lastLabelIndex:=CurrentIndex;
  end;
end;

procedure TfrmMain.DoScrollPoints(Series: TChartSeries);
begin
  // Delete 'past' points.
  if Series.XValues.Count > 0 then begin
    while Series.XValues.First < IncSecond(now, -NUM_OF_SEC_T0_VEIW) do begin
      Series.Delete(0);
      Dec(lastLabelIndex);
    end;
  end;

  // Adjust 'view'
  AdjustView(Series);
end;
However, if you want to draw a mark each 9 regular seconds, it would be easier if you stored only a reference point and draw all the labels from there.
Best Regards,
ImageYeray Alonso
Development & Support
Steema Software
Av. Montilivi 33, 17003 Girona, Catalonia (SP)
Image Image Image Image Image Image Please read our Bug Fixing Policy

Leo_
Newbie
Newbie
Posts: 11
Joined: Mon Dec 12, 2016 12:00 am

Re: Issue with drawing of series mark

Post by Leo_ » Fri Feb 17, 2017 9:52 am

Thank you very much for your feedback, much appreciated.

1. Regarding the drawing issue of the characters (ID Codes): I just did a test where I create a second separate series for the ID Codes. It's a TPointSeries with Style set to "Nothing" to not show the point itself. Now I can add (Assign) a Mark to the point and use the default positioning and thus do not have to implement OnAfterDraw or Series.OnGetMarkText to reposition the Marks when the main graph is scrolling. It seems to work great, so I'll go for this solution.

2. Inconsistent spacing of marks: I agree, the combination of a TTimer and function the Now from the main Thread does not seem to be a good thing in this case. My incoming datastream for the real app (not the demo attached here) does not have a TimeStamp for the incoming datasamples as they are considered live data. Thus I have to use the "Now" function I think, but I'll wrap this part of my app in a separate thread. I would not be surprised if this solves the issue :)

3. Speed: I've done some more testing and on my three available devices it is actually the 10" pad that seems to suffer the most. This device is now running on Android 5.0.1. What I'll do:
- Improve my own code by using threads
- Follow the hints you provided in the link
- Upgrade the device to maybe Android 6

Once again thank you for the support.

regards,
Leo

Post Reply