Recent

Author Topic: [SOLVED] can I use TDataPointClickTool for TBarSeries?  (Read 7976 times)

Hartmut

  • Hero Member
  • *****
  • Posts: 742
[SOLVED] can I use TDataPointClickTool for TBarSeries?
« on: January 06, 2017, 08:12:54 pm »
I want to show the x and y values of a bar if you click on their upper edge with the mouse. I found a tutorial, which shows that for TLineSeries (which works) and tried to adapt that for TBarSeries, but I don't get it to work. Is TDataPointClickTool for TBarSeries not possible or what am I doing wrong?

Code: Pascal  [Select][+][-]
  1. procedure TForm1.ChartToolset1DataPointClickTool1PointClick(ATool: TChartTool;
  2.              APoint: TPoint);
  3.    var x,y: Double;
  4.    begin
  5.    with ATool as TDatapointClickTool do
  6.       if (Series is TBarSeries) then // replaced TLineSeries by TBarSeries
  7.          with TBarSeries(Series) do  // replaced TLineSeries by TBarSeries
  8.             begin
  9.             x := GetXValue(PointIndex);
  10.             y := GetYValue(PointIndex);
  11.             Statusbar1.SimpleText := Format('x = %f, y = %f', [x, y]);
  12.             end
  13.       else Statusbar1.SimpleText := 'hallo'; // this msg is never showed
  14.    end;    
   

I use Lazarus 1.6.2 with FPC 3.0.0 on Win7.
« Last Edit: January 09, 2017, 11:38:30 pm by Hartmut »

wp

  • Hero Member
  • *****
  • Posts: 11857
Re: can I use TDataPointClickTool for TBarSeries?
« Reply #1 on: January 06, 2017, 11:57:20 pm »
Huuuh, this is tricky, and I cannot give a general solution at the moment because the bar series comes in serveral flavors, stacked bars, or side-by-side bars. My solution is valid only for the simple case of a single bar series.

When a ChartTool detects a mouseclick it iterates through all series, and there, through all datapoints to find the one which is closest to the clicked point. For this purpose, the series provides a function GetNearestPoint which returns true if such a point is found and passes additional information in a TNearestPointResult parameter. This function is implemented in a very basic chart type and checks if the distance of a data point from the click point is smaller than a given tolerance. It works for most series types, but in case of the bar series it should check whether the clicked point simply is inside the drawn rectangle of the bar.

Fortunately, the function GetNearestPoint is virtual and can be overridden by a descendant class of the TBarSeries.  This is the code which works for me:
Code: Pascal  [Select][+][-]
  1. function TBarSeries.GetNearestPoint(const AParams: TNearestPointParams;
  2.   out AResults: TNearestPointResults): Boolean;
  3. var
  4.   i, lb, ub: Integer;
  5.   pt, pt1, pt2: TPoint;
  6.   sp: TDoublePoint;
  7.   barwidth: Integer;
  8. begin
  9.   AResults.FDist := Sqr(AParams.FRadius) + 1;
  10.   AResults.FIndex := -1;
  11.  
  12.   // Iterate through all points of the series
  13.   for i := 0 to Count - 1 do begin
  14.     sp := Source[i]^.Point;
  15.     if IsNan(sp) then continue;
  16.  
  17.     // Calculate bar width
  18.     barwidth := GetBarWidth(i) div 2;
  19.  
  20.     // Calculate image coordinates of the data point (this is the center of
  21.     // bar end)
  22.     pt := ParentChart.GraphToImage(AxisToGraph(sp));
  23.     pt1 := pt;
  24.     pt1.X := pt1.X - barwidth div 2;
  25.     // --> pt1 is the top-left corner point of the bar, in image coordinates
  26.  
  27.     // Calculate the bottom of the bar - we assume that the bar starts at the
  28.     // "ZeroLevel" - this is not valid for stacked bars!
  29.     sp.Y := 0;
  30.     pt2 := ParentChart.GraphToImage(AxisToGraph(sp));
  31.     pt2.X := pt2.X + barwidth div 2;
  32.     // --> pt2 is the bottom-right corner point of the bar
  33.  
  34.     // Check if the clicked point (AParams.FPoint) is inside the rectangle
  35.     // spanned by the corner point pt1 and pt2
  36.     if PtInRect(Rect(pt1.X, pt1.Y, pt2.X, pt2.Y), AParams.FPoint) then
  37.     begin
  38.       AResults.FDist := 0;
  39.       AResults.FIndex := i;
  40.       AResults.FImg := pt;
  41.       AResults.FValue := sp;
  42.       break;
  43.     end;
  44.   end;
  45.   Result := AResults.FIndex >= 0;
  46. end;  

It just does what I described above in words. You may notice that I call the derived series TBarSeries like its ancestor. This is a trick found sometimes in the Delphi community: because the new series only changes a virtual method the compiler can be cheated to replace the original TBarSeries by the newTBarSeries at runtime if the unit is in the uses list after TChartSeries (where the original TBarSeries is implemented). It you need the new BarSeries only once then simply implement it in the unit with the chart, like in the attached demo. This way you still can work with the bar series at design time in the object inspector (of course, if you create the series at runtime then it can be named arbitrarily).

In case of stacked bar series, the GetNearestPoint method should check the other series to calculate the y value where the previous series (onto which the current series is stacked) ends.

In case of side-by-side bar series the method must calculate the horizontal corner points of the bars being surrounded by the other adjacent bars.

The final code should also take care of the case of rotated bars.

Looking into the code of TBarSeries, this does not appear to be too difficult, but at the moment I don't have the time to figure it out.
« Last Edit: January 06, 2017, 11:59:12 pm by wp »

Hartmut

  • Hero Member
  • *****
  • Posts: 742
Re: can I use TDataPointClickTool for TBarSeries?
« Reply #2 on: January 07, 2017, 01:02:26 pm »
Thank you so much for your long and detailed answer. Now i know that TDataPointClickTool for TBarSeries is possible. In my case I have groups of side-by side bars, which are created dynamically because their number is flexible. I will walk through your code (which is diffucult for me as a LCL beginner) and try to adapt it for side-by side bars.

What I did not understand is your attached demo. It deals with TLineSeries which are added dynamically by a button. Nothing about TBarSeries. Did you mix up the attachment?

I'm not sure to adapt your delphi trick correctly to call the derived series TBarSeries like its ancestor. I get the following Warning:

unit1.pas(14,13) Warning: An inherited method is hidden by "GetNearestPoint(const TNearestPointParams;out TNearestPointResults):Boolean;"

Is this normal and correct?

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.  Classes, SysUtils, FileUtil, TAGraph, TASeries, Forms, Controls, Graphics,
  9.  Dialogs, TACustomSeries;
  10.  
  11. type
  12.  
  13.  TBarSeries = class(TASeries.TBarSeries)
  14.    function GetNearestPoint(const AParams: TNearestPointParams;
  15.                out AResults: TNearestPointResults): Boolean; VIRTUAL;
  16.  end;
  17.  
  18.  TForm1 = class(TForm)
  19.   Chart1: TChart;
  20.   Chart1BarSeries1: TBarSeries;
  21.  private
  22.   { private declarations }
  23.  public
  24.   { public declarations }
  25.  end;
  26.  
  27. var
  28.  Form1: TForm1;
  29.  
  30. implementation
  31.  
  32. {$R *.lfm}
  33.  
  34. { TBarSeries }
  35.  
  36. function TBarSeries.GetNearestPoint(const AParams: TNearestPointParams;
  37.             out AResults: TNearestPointResults): Boolean;
  38. begin
  39.    exit(true);
  40. end;
  41.  
  42. end.

wp

  • Hero Member
  • *****
  • Posts: 11857
Re: can I use TDataPointClickTool for TBarSeries?
« Reply #3 on: January 07, 2017, 01:23:38 pm »
I'm sorry, I picked the wrong demo... This should be the right one now. It also shows you how to declare the TBarSeries and the overridden method correctly.

Currently, I also have a working solution for side-by-side bars and stacked bars. Once I got it working also for rotated series I'll publish it here and adapt the TAChart sources.

wp

  • Hero Member
  • *****
  • Posts: 11857
Re: can I use TDataPointClickTool for TBarSeries?
« Reply #4 on: January 08, 2017, 12:50:56 am »
In Lazarus trunk, r53896/7, you can find a new version of TBarSeries for which ChartTools accept clicks inside the bar to become active. Just set the new property ToolTarget to bttBar to get this behavior; the other setting, bttDataPoint returns to the old behavior. The new code works for single bars, side-by-side bars, and stacked bars, both in vertical and horizontal orientation. 100%-stacking is handled correctly.

There is also a new demo, barseriestools, in which a DataPointClickTool, DataPointHintTool and DatapointDragTool are active. Here you can find code how to get the series, datapoint index, and x/y datapoint coordinates of the clicked bar.

Please note that this a new feature and, therefore, it will not be backported to Laz1.6.4. If you don't use trunk you may have to wait until version 1.8 (or 2.0) is released.

If you don't want to wait so long and can live with side-by-side bars you can try this code instead of the one from the previous post; it is some interim code during writing the "official" version for Lazarus trunk. Stacked bars are not handled here because this requires other modifications within the TAChart package.

Code: Pascal  [Select][+][-]
  1. function TBarSeries.GetNearestPoint(const AParams: TNearestPointParams;
  2.   out AResults: TNearestPointResults): Boolean;
  3. var
  4.   pointIndex: Integer;
  5.   graphClickPt: TDoublePoint;
  6.   sp, p: TDoublePoint;
  7.   ofs, w: Double;
  8.   heights: TDoubleDynArray;
  9.   y, z: Double;
  10.   stackindex: Integer;
  11. begin
  12.   Result := false;
  13.   AResults.FDist := Sqr(AParams.FRadius) + 1;
  14.   AResults.FIndex := -1;
  15.  
  16.   if IsRotated then
  17.     z := AxisToGraphX(ZeroLevel)
  18.   else
  19.     z := AxisToGraphY(ZeroLevel);
  20.   SetLength(heights, Source.YCount + 1);
  21.  
  22.   // clicked point in image coordinates
  23.   graphClickPt := ParentChart.ImageToGraph(AParams.FPoint);
  24.   if IsRotated then
  25.     Exchange(graphclickpt.X, graphclickpt.Y);
  26.  
  27.   // Iterate through all points of the series
  28.   for pointIndex := 0 to Count - 1 do begin
  29.     sp := Source[pointindex]^.Point;
  30.     if IsNan(sp) then
  31.       continue;
  32.     BarOffsetWidth(sp.X, pointindex, ofs, w);
  33.     sp.X := sp.X + ofs;
  34.     if not InRange(graphClickPt.X, sp.X - w, sp.X + w) then
  35.       continue;
  36.     heights[0] := z;
  37.     heights[1] := NumberOr(sp.Y, z);
  38.     for stackIndex := 1 to Source.YCount-1 do begin
  39.       y := NumberOr(Source[pointindex]^.YList[stackIndex - 1], 0);
  40.       heights[stackIndex + 1] := heights[stacKindex] + y;
  41.     end;
  42.     for stackindex := 0 to High(heights)-1 do
  43.       if InRange(graphClickPt.Y, heights[stackindex], heights[stackIndex+1]) then
  44.       begin
  45.         AResults.FDist := 0;
  46.         AResults.FIndex := pointindex;
  47.         AResults.FValue := DoublePoint(Source[pointindex]^.X, Source[pointindex]^.GetY(stackIndex));
  48.         if IsRotated then
  49.           Exchange(AResults.FValue.X, AResults.FValue.Y);
  50.         AResults.FImg := ParentChart.GraphToImage(AResults.FValue);
  51.         Result := true;
  52.         exit;
  53.       end;
  54.   end;
  55. end;

Look at the attached demo for details on usage.


Hartmut

  • Hero Member
  • *****
  • Posts: 742
Re: can I use TDataPointClickTool for TBarSeries?
« Reply #5 on: January 08, 2017, 11:59:48 am »
Great! Your new demo works perfectly and gave me some more good ideas for my program. I will study it and implement it in my program and will learn a lot. Thank you very much again for your effort and valuable help.

Hartmut

  • Hero Member
  • *****
  • Posts: 742
Re: [SOLVED] can I use TDataPointClickTool for TBarSeries?
« Reply #6 on: January 08, 2017, 06:07:54 pm »
When I implemented your demo in my program it did not work any longer. I reduced my program and found out, that the problem comes from the TChartAxisTransformations which I use for multiple Y-axis:

Code: Pascal  [Select][+][-]
  1. procedure TForm1.FormCreate(Sender: TObject);
  2.    const DATA: array[1..10,1..2] of integer = ( // some data to display:
  3.             (500,90), (600,30), (700,60), (800,90), (900,99),
  4.             (800,25), (700,50), (300,20), (400,80), (500,50));
  5.          al=0; ar=2; // axis index for left and right Y-axis
  6.    var ASTL,ASTR: TAutoScaleAxisTransform;
  7.        x: integer;
  8.    begin
  9.                               // create 2 Transformations:
  10.    Chart1.AxisList[al].Transformations:=TChartAxisTransformations.Create(Chart1);
  11.    Chart1.AxisList[ar].Transformations:=TChartAxisTransformations.Create(Chart1);
  12.                               // create 2 childs "auto scale":
  13.    ASTL:=TAutoScaleAxisTransform.Create(Chart1.AxisList[al].Transformations);
  14.    ASTR:=TAutoScaleAxisTransform.Create(Chart1.AxisList[ar].Transformations);
  15.                               // link 2 childs to their Y-axis:
  16.    ASTL.Transformations:=Chart1.AxisList[al].Transformations;
  17.    ASTR.Transformations:=Chart1.AxisList[ar].Transformations;
  18.  
  19.    for x:=1 to High(DATA) do  // show data:
  20.       begin
  21.       Chart1BarSeries1.AddXY(x,DATA[x,1]);
  22.       Chart1BarSeries2.AddXY(x,DATA[x,2]);
  23.       end;
  24.    end;

As soon as I skip them, it works. Do I something wrong with the Transformations or is it not possible to have DataPointClickTool for TBarSeries together with Transformations? I attached a small project for demonstation. Thanks a lot in advance.

wp

  • Hero Member
  • *****
  • Posts: 11857
Re: [Reopened] can I use TDataPointClickTool for TBarSeries?
« Reply #7 on: January 08, 2017, 10:28:51 pm »
Ah, I knew I forgot something. This version takes care of any transformations (and it also works for bars pointing from ZeroLevel downwards):

Code: Pascal  [Select][+][-]
  1. function TBarSeries.GetNearestPoint(const AParams: TNearestPointParams;
  2.   out AResults: TNearestPointResults): Boolean;
  3. var
  4.   pointIndex: Integer;
  5.   graphClickPt: TDoublePoint;
  6.   sp, p: TDoublePoint;
  7.   ofs, w: Double;
  8.   heights: TDoubleDynArray;
  9.   y, z: Double;
  10.   stackindex: Integer;
  11. begin
  12.   writeln('GetNearestPoint(', tag, ') called');
  13.   Result := false;
  14.   AResults.FDist := Sqr(AParams.FRadius) + 1;
  15.   AResults.FIndex := -1;
  16.  
  17.   if IsRotated then
  18.     z := AxisToGraphX(ZeroLevel)
  19.   else
  20.     z := AxisToGraphY(ZeroLevel);
  21.   SetLength(heights, Source.YCount + 1);
  22.  
  23.   // clicked point in image coordinates
  24.   graphClickPt := ParentChart.ImageToGraph(AParams.FPoint);
  25.   if IsRotated then
  26.     Exchange(graphclickpt.X, graphclickpt.Y);
  27.  
  28.   // Iterate through all points of the series
  29.   for pointIndex := 0 to Count - 1 do begin
  30.     sp := Source[pointindex]^.Point;
  31.     if IsNan(sp) then
  32.       continue;
  33.     BarOffsetWidth(sp.X, pointindex, ofs, w);
  34.     sp.X := sp.X + ofs;
  35.     if not InRange(graphClickPt.X, sp.X - w, sp.X + w) then
  36.       continue;
  37.     heights[0] := z;
  38.     heights[1] := NumberOr(sp.Y, z);
  39.     for stackIndex := 1 to Source.YCount-1 do begin
  40.       y := NumberOr(Source[pointindex]^.YList[stackIndex - 1], 0);
  41.       heights[stackIndex + 1] := heights[stacKindex] + y;
  42.     end;
  43.     for stackIndex := 1 to High(heights)-1 do                      // <-- added
  44.       heights[stackIndex] := AxisToGraphY(heights[stackIndex]);    // <-- added
  45.     for stackindex := 0 to High(heights)-1 do
  46.       if InRange(graphClickPt.Y, heights[stackindex], heights[stackIndex+1]) or
  47.          InRange(graphClickPt.Y, heights[stackIndex+1], heights[stackindex])    // <-- added
  48.       then begin
  49.         AResults.FDist := 0;
  50.         AResults.FIndex := pointindex;
  51.         AResults.FValue := DoublePoint(Source[pointindex]^.X, Source[pointindex]^.GetY(stackIndex));
  52.         if IsRotated then
  53.           Exchange(AResults.FValue.X, AResults.FValue.Y);
  54.         AResults.FImg := ParentChart.GraphToImage(AxisToGraph(AResults.FValue)); // <-- AxisToGraph added
  55.         Result := true;
  56.         exit;
  57.       end;
  58.   end;
  59. end;

Hartmut

  • Hero Member
  • *****
  • Posts: 742
Re: [Reopened] can I use TDataPointClickTool for TBarSeries?
« Reply #8 on: January 09, 2017, 06:54:57 pm »
Great, I implemented your modifications in my real program and it worked instantly.
There is a little difference in the behavior to the version before: now clicks anywhere above the upper edge of a bar are accepted too. I can live with that, just wanted you to be aware of this.
Thanks a lot again, I am deeply grateful for your help.

wp

  • Hero Member
  • *****
  • Posts: 11857
Re: [Solved] can I use TDataPointClickTool for TBarSeries?
« Reply #9 on: January 09, 2017, 09:51:27 pm »
Another oversight. It is correct in the official version, but I did not copy it correctly:

Instead of
Code: Pascal  [Select][+][-]
  1. if InRange(graphClickPt.Y, heights[stackindex], heights[stackIndex+1]) or
  2.    InRange(graphClickPt.Y, heights[stackIndex+1], heights[stackindex])    

use
Code: Pascal  [Select][+][-]
  1.  if ((heights[stackIndex] < heights[stackIndex+1]) and InRange(graphClickPt.Y, heights[stackindex], heights[stackIndex+1]))
  2.     or
  3.     ((heights[stackindex+1] < heights[stackindex]) and InRange(graphClickPt.Y, heights[stackIndex+1], heights[stackindex]))
  4.  

Hartmut

  • Hero Member
  • *****
  • Posts: 742
Re: [Solved] can I use TDataPointClickTool for TBarSeries?
« Reply #10 on: January 09, 2017, 10:37:06 pm »
hmm, I implemented your change both in my real program and in my little demo project and I can see no difference: any clicks above the upper edge of a bar are still accepted. I attached the demo project, if you want to look in.

wp

  • Hero Member
  • *****
  • Posts: 11857
Re: can I use TDataPointClickTool for TBarSeries?
« Reply #11 on: January 09, 2017, 11:03:41 pm »
This is the exact copy from the trunk version (the uncompilable parts due to other changes done within TAChart are commented), and it is working finally - sorry:
Code: Pascal  [Select][+][-]
  1. function TBarSeries.GetNearestPoint(const AParams: TNearestPointParams;
  2.   out AResults: TNearestPointResults): Boolean;
  3. var
  4.   pointIndex: Integer;
  5.   graphClickPt: TDoublePoint;
  6.   sp, p: TDoublePoint;
  7.   ofs, w: Double;
  8.   heights: TDoubleDynArray;
  9.   y: Double;
  10.   stackindex: Integer;
  11. begin
  12. {
  13.   if FToolTarget = bttDatapoint then
  14.   begin
  15.     Result := inherited;
  16.     exit;
  17.   end;
  18.  }
  19.   Result := false;
  20.   AResults.FDist := Sqr(AParams.FRadius) + 1;
  21.   //AResults.FIndex := -1;
  22.  
  23.   SetLength(heights, Source.YCount + 1);
  24.  
  25.   // clicked point in image units
  26.   graphClickPt := ParentChart.ImageToGraph(AParams.FPoint);
  27.   if IsRotated then
  28.     Exchange(graphclickpt.X, graphclickpt.Y);
  29.  
  30.   // Iterate through all points of the series
  31.   for pointIndex := 0 to Count - 1 do begin
  32.     sp := Source[pointindex]^.Point;
  33.     if IsNan(sp) then
  34.       continue;
  35.     sp.X := AxisToGraphX(sp.X);
  36.     BarOffsetWidth(sp.X, pointindex, ofs, w); // works with graph units
  37.     sp.X := sp.X + ofs;
  38.     if not InRange(graphClickPt.X, sp.X - w, sp.X + w) then
  39.       continue;
  40.     // Calculate stacked bar levels (in axis units)
  41.     heights[0] := ZeroLevel;
  42.     heights[1] := NumberOr(sp.Y, ZeroLevel);
  43.     for stackIndex := 1 to Source.YCount-1 do begin
  44.       y := NumberOr(Source[pointindex]^.YList[stackIndex - 1], 0);
  45.       heights[stackIndex + 1] := heights[stackindex] + y;
  46.     end;
  47.     // Convert heights to graph units
  48.     for stackIndex := 0 to High(heights) do
  49.       heights[stackIndex] := AxisToGraphY(heights[stackIndex]);
  50.     // Check if clicked pt is inside stacked bar
  51.     for stackindex := 0 to High(heights)-1 do
  52.       if ((heights[stackindex] < heights[stackindex + 1]) and
  53.          InRange(graphClickPt.Y, heights[stackindex], heights[stackIndex + 1]))
  54.       or
  55.          ((heights[stackindex + 1] < heights[stackindex]) and
  56.          InRange(graphClickPt.Y, heights[stackindex + 1], heights[stackIndex]))
  57.       then  begin
  58.         AResults.FDist := 0;
  59.         AResults.FIndex := pointindex;
  60. //        AResults.FYIndex := stackIndex;
  61.         AResults.FValue := DoublePoint(Source[pointindex]^.X, Source[pointindex]^.GetY(stackIndex));
  62.         AResults.FValue := AxisToGraph(AResults.FValue);
  63.         AResults.FImg := ParentChart.GraphToImage(AResults.FValue);
  64.         Result := true;
  65.         exit;
  66.       end;
  67.   end;
  68. end;

Hartmut

  • Hero Member
  • *****
  • Posts: 742
Re: can I use TDataPointClickTool for TBarSeries?
« Reply #12 on: January 09, 2017, 11:38:07 pm »
now clicks above the upper edge of a bar are not longer accepted. perfect. Thanks again for your effort.

wp

  • Hero Member
  • *****
  • Posts: 11857
Re: can I use TDataPointClickTool for TBarSeries?
« Reply #13 on: January 12, 2017, 12:54:52 am »
In Lazarus trunk, r53896/7, you can find a new version of TBarSeries for which ChartTools accept clicks inside the bar to become active. Just set the new property ToolTarget to bttBar to get this behavior; the other setting, bttDataPoint returns to the old behavior. The new code works for single bars, side-by-side bars, and stacked bars, both in vertical and horizontal orientation. 100%-stacking is handled correctly.
I decided to redo this feature and removed the ToolTarget from the BarSeries because it better fits to the ChartTools than to the series and thus allows greater flexibility. Now there is a set of TNearestpointTarget elements describing which "part" of a series is considered by the tool when finding the closest distance to the series:
  • nptPoint refers to the "main" datapoint at (x, y)
  • nptXList and nptYList, in case of multiple x and y values, refer to XList and YList of x and y values
  • nptCustom refers to special handling by the series.

These elements are accessible via the new property Target of each DataPointTool. By default all these options are set. This means that the distance of the clicked point to the (x,y) data point is evaluated (nptPoint), as well as to all points defined by the XList (nptXList) and Ylist (nptYList) of the source; and, in case of the bar series, it is also checked if the clicked point is in the inside of a bar (nptCustom). Other multi-valued series like Box-Whisker or Open-Low-High-Close will be extended to support nptCustom as well.

Turning off nptCustom, for example, has the effect that a barseries must be clicked at the end of the bars, inside-clicks are no longer accepted. This might be interesting for usage of a DatapointDragTool to modify the length of bars (note that at the moment the click must occur at the x value of the data point, not across the width of the bar, if nptCustom is off).

Another feature was added in this context: TLineSeries and TAreaSeries now correctly support multiple y values - this feature already has been there, but the series extent was not calculated correctly. Setting "Stacked" to true stacks each y value on top of the preceding one. Having Stacked=false plots the series in an unshifted way. The BarSeries always has been stackable. At the moment, multiple y values cannot be made unstackable.

See:

 

TinyPortal © 2005-2018