Recent

Author Topic: Set pointerstyle at addxy time?  (Read 750 times)

AnthonyTekatch

  • Jr. Member
  • **
  • Posts: 52
Set pointerstyle at addxy time?
« on: July 28, 2019, 10:10:55 pm »
I would like to set the pointer style at the same time as the ADDXY time.

Currently I use OnGetPointerStyle to change the displayed pointer depending on some property of the data point like seriescolor like this:

Code: Pascal  [Select]
  1. if SomeCondition then
  2.   MyPointerColor:=clWhite
  3. else
  4.   MyPointerColor:=clRed;
  5.  
  6. MyLineSeries.AddXY(PlotRecordTime, MyValue,'', MyPointerColor);

Code: Pascal  [Select]
  1. procedure TPlotterForm.MyLineSeriesGetPointerStyle(ASender: TChartSeries;
  2.   AValueIndex: Integer; var AStyle: TSeriesPointerStyle);
  3. begin
  4.   if ASender.GetColor(AValueIndex)=clWhite then
  5.     AStyle:=psRectangle
  6.   else
  7.     AStyle:=psTriangle;
  8. end;

I have other conditions that require different pointer styles, and would like to store some hidden data or metadata at the time of ADDXY so they can be decoded in more detail by OnGetPointerStyle.

Are there any other options/suggestions?

wp

  • Hero Member
  • *****
  • Posts: 6231
Re: Set pointerstyle at addxy time?
« Reply #1 on: July 28, 2019, 11:31:25 pm »
Two solutions

(1)
Each chart source can house several x and y values per data point. While in case of a LineSeries the additional y values are reserved for additional data levels (such as in a stacked bar series) the additional x values are freely available. So you could stuff additional data point information into the extra x value. Unfortunately there is no direct series method to add an additional x value by a variant of the Add* methods, but you could call the method AddXListYList of the internal listsource to which the AddXY values are stored anyway.
Code: Pascal  [Select]
  1. procedure TForm1.PrepareDataForSolution1;
  2. var
  3.   i: Integer;
  4.   x, y: Double;
  5.   symbol: TSeriesPointerStyle;
  6.   clr: TColor;
  7. begin
  8.   Chart1LineSeries1.ListSource.XCount := 2;   // prepare for two x values!
  9.  
  10.   for i := 0 to DATA_COUNT do  begin
  11.     x := i;
  12.     y := Random;
  13.     symbol := TSeriesPointerStyle(Random(ord(High(TSeriesPointerStyle)) + 1));  // random pointer style
  14.     clr := RGBToColor(Random(256), Random(256), Random(256));
  15.     Chart1LineSeries1.ListSource.AddXListYList(
  16.       [x, ord(symbol)],    // two x values
  17.       [y],                 // one y value
  18.       '', clr);            // label and color
  19.   end;
  20. end;

(2)
A less "hacky" solution, in my eyes, would be to define your own data type which directly contains the additional data values:
Code: Pascal  [Select]
  1. type
  2.   TData = record
  3.     x, y: Double;   // x, y values
  4.     Symbol: TPointerStyle;   // the pointer style used for this data point
  5.     Color: TColor;    // the color of this data point
  6.   end;
  7.   TDataArray = array of TData;
  8.  
  9. var
  10.   data: TDataArray;
  11.  
  12. procedure TForm1.PrepareDataForSolution2;
  13. var
  14.   i: Integer;
  15. begin
  16.   Setlength(data, DATA_COUNT);
  17.   for i := 0 to DATA_COUNT-1 do begin
  18.     data[i].x := i;
  19.     data[i].y := Random;
  20.     data[i].Symbol := TSeriesPointerStyle(Random(ord(High(TSeriesPointerStyle)) + 1));  // random pointer style
  21.     data[i].Color := RGBToColor(Random(256), Random(256), Random(256));
  22.   end;
  23.  
  24.   UserDefinedChartSource1.PointsNumber := DATA_COUNT;
  25. end;
  26.  
  27. procedure TForm1.UserDefinedChartSource1GetChartDataItem(
  28.   ASource: TUserDefinedChartSource; AIndex: Integer; var AItem: TChartDataItem);
  29. begin
  30.   AItem.X := data[AIndex].x;
  31.   AItem.Y := data[AIndex].y;
  32.   AItem.Color := data[AIndex].Color;
  33. end;
Declare a variable "data" of type TDataArray and populate it with your data; add also the Symbol and Color elements needed.

In order to pass these data to the series add a TUserDefinedChartSource to the chart and link it to the "Source" property of the series. Once the data array is populated you must set the PointsNumber of the UserDefinedChartSource to the Length of the array. This tells the chartsource how many data points will be passed. Then write an event handler for TUserDefinedChartSeries.OnGetChartDataItem and define how the array data are assigned to the chart data items.

Find a demo showing both approaches in the attachment.
Lazarus trunk / fpc 3.0.4 / all 32-bit on Win-10

AnthonyTekatch

  • Jr. Member
  • **
  • Posts: 52
Re: Set pointerstyle at addxy time?
« Reply #2 on: July 28, 2019, 11:50:00 pm »
Thank you for your suggestions. I am trying that now.

The example you provided gives me the following error:
unit1.pas(100,34) Error: identifier idents no member "AddXListYList"

I am currently using Lazarus 2.0.2 FPC 3.0.4

wp

  • Hero Member
  • *****
  • Posts: 6231
Re: Set pointerstyle at addxy time?
« Reply #3 on: July 29, 2019, 12:24:51 am »
Oh, the AddXListYList was only introduced in Laz trunk...

With 2.0.2 or older you can replace it by
Code: Pascal  [Select]
  1. var
  2.   j: Integer;
  3. ...
  4.     // This is for Laz 2.0.2 or earlier
  5.     j := Chart1LineSeries1.AddXY(x, y, '', clr);
  6.     Chart1LineSeries1.XValues[j, 1] := ord(symbol);
  7. ...
  8.  
Lazarus trunk / fpc 3.0.4 / all 32-bit on Win-10

AnthonyTekatch

  • Jr. Member
  • **
  • Posts: 52
Re: Set pointerstyle at addxy time?
« Reply #4 on: July 29, 2019, 12:43:37 am »
Fantastic!! Thanks for your help.

AnthonyTekatch

  • Jr. Member
  • **
  • Posts: 52
Re: Set pointerstyle at addxy time?
« Reply #5 on: July 29, 2019, 02:56:26 am »
I noticed that the X axis range of the chart now includes the extra X value. For example, if I change your code to :
Code: Pascal  [Select]
  1. x := i+100;
The bottom scale still starts at 0 because the second element of X [j,1] is a very small value.

Is there a way for the chart to only use the range from the first element of the X array [j,0]?

wp

  • Hero Member
  • *****
  • Posts: 6231
Re: Set pointerstyle at addxy time?
« Reply #6 on: July 29, 2019, 09:25:58 am »
Extent calculation has seen considerable changes recently, the bug is fixed in Lazarus trunk. In v2.0.2 or older you can freeze the lower end of the x extent. In the Object Inspector set
- Extent.XMin = 100
- Extent.UseXMin = true
Lazarus trunk / fpc 3.0.4 / all 32-bit on Win-10

AnthonyTekatch

  • Jr. Member
  • **
  • Posts: 52
Re: Set pointerstyle at addxy time?
« Reply #7 on: July 29, 2019, 11:03:08 am »
OK, that should work fine, thanks.