Page 1 of 1

Marks inside of stacked bars?

Posted: Wed Nov 19, 2003 1:27 pm
by 8577662
The marks of a stacked TBarSeries appear above the top of each bar. I'd like to have the marks appear inside of each stack (with the same distance for each stack from the bottom of the stack) and with a mark at
the top of the topmost stack showing the total of all the Series in the
stack.

I've already searched in the demo application but wasn't able to find a way to set the position of the marks

Is there any way to do this?

Thanks in advance
Pascal

Posted: Wed Nov 19, 2003 2:49 pm
by Marjan
Hi, Pascal.

One easy workarund for this is to set marks ArrowLength propety to negative (reasonable) value. For example, the following code:

Code: Select all

Series1.Marks.ArrowLength := -25;
will display marks inside bars. Of course, the problem might arise if bars heights are less than 25 pixels, but in other cases it will work fine. There are additional refinements you can introduce - calculate best negative value to position series marks in the middle of bars, add code for the eventuality bar height is too small for mark, ...

Posted: Wed Nov 19, 2003 3:02 pm
by 8577662
Marjan wrote: One easy workarund for this is to set marks ArrowLength propety to negative (reasonable) value.
This is what i currently do, but for the reasons you mentioned it's not an acceptable solution for me.
Marjan wrote: calculate best negative value to position series marks in the middle of bars, add code for the eventuality bar height is too small for mark, ...
Do you have any code examples for this?

Is there also a solution for displaying an additional mark with the total of the entire bar (sum of all stacks) at the top of each bar?

Posted: Fri Nov 21, 2003 6:29 am
by Marjan
8577662 wrote:Do you have any code examples for this?
Yes, sure. Try using the following code:

Code: Select all

procedure CenterMarks(barSeries: TBarSeries);
var i : Integer;
  yPos,yPos1,yPos2: Integer;
  xPos: Integer;
  MarkPos: TSeriesMarkPosition;
begin
  MarkPos := TSeriesMarkPosition.Create;
  for i := 0 to barSeries.Count - 1 do
  begin
    // just an example, you can do further customization here
    MarkPos.Width := barSeries.BarWidth - 10;
    MarkPos.Height := 16;

    yPos1 := barSeries.CalcYPos(i); // get y screen pixel coordinate
    if barSeries.UseYOrigin then yPos2 := barSeries.CalcYPosValue(0.0)
    else yPos2 := barSeries.GetHorizAxis.PosAxis;
    yPos := (YPos1+YPos2-MarkPos.Height) div 2;
    xPos := barSeries.CalcXPos(i);

    MarkPos.Custom := True;
    MarkPos.LeftTop.X := xPos;
    MarkPos.LeftTop.Y := yPos;

    barSeries.Marks.Positions[i] := MarkPos;
  end;
  MarkPos.Free;
  barSeries.Repaint;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Series1.Marks.Arrow.Visible := False;
  Series1.Marks.Visible := True;
  Series1.Add(5);
  Series1.Add(11);
  Series1.Add(-3);
  Series1.Add(2.5);
end;

procedure TForm1.Series1BeforeDrawValues(Sender: TObject);
begin
  CenterMarks(Sender as TBarSeries);
end;


Of course, you can do further customization and refinements, but the basic code will remain the same.

8577662 wrote:Is there also a solution for displaying an additional mark with the total of the entire bar (sum of all stacks) at the top of each bar?
Yes, it can be done. Basically, hide all other marks and display only last (top) series marks. Then in it's OnGetMarkText event, calculate the sum for each stack. Something like this:

Code: Select all

procedure TForm1.LastSeriesGetMarkText(Sender: TChartSeries;
      ValueIndex: Integer; var MarkText: String);
var i:integer;
    tmp:double;
begin
     //sweeps through all values and calculates total
     tmp:=0;
     for i:=0 to Chart1.SeriesList.Count-1 do
     if Chart1.Series[i].Active then
      tmp:=tmp+Chart1.Series[i].YValue[ValueIndex]; //sum YValues
      MarkText:=FloatToStr(tmp); //write a new MarkText

end;


procedure TForm1.FormCreate(Sender: TObject);
var i:Integer;
begin
     for i:=0 to Chart1.SeriesList.Count-1 do
     with Chart1.Series[i] do
     begin
       FillSampleValues(10);
       Marks.Visible:=false; //we don't want marks for series
     end;
     with Chart1.Series[i-1] do
     begin
      OnGetMarkText:=LastSeriesGetMarkText; //assign OnGetMarkText event to last series
      Marks.Visible:=true; //on the other hand, we want a mark for last series
     end;

end;

Posted: Fri Nov 21, 2003 6:51 pm
by 8577662
Marjan wrote: Yes, it can be done. Basically, hide all other marks and display only last (top) series marks. Then in it's OnGetMarkText event, calculate the sum for each stack
Is there also a possibility to have the summary *AND* the marks (the summary in an additional mark above all stacks?

Thanks
Pascal

Posted: Sun Nov 23, 2003 5:11 pm
by Marjan
Hi. Pascal.

Not automatically. But you can do several things:

1) Add extra line (summary) to existing mark text. This can be done in TChartSeries.OnGetMarkText event.

2) Manually draw required texts above series marks in TChart OnAfterDraw event. Basically, use TChart.Canvas.TextOut method to paint specific text at given screen coordinate.

3) Use series of TAnnotationTool tools to add extra text above series marks,

I hope any of these methods will help you :D

Posted: Mon Nov 24, 2003 1:16 pm
by 8577662
Marjan wrote: Yes, sure. Try using the following code:
The problem with this code is, that it will center the Marks for the size of all stacked bars. What I need is to center the mark between the top of a stack and the bottom of a stack (and not the bottom of the bar / position of the x axes).
Is there a way to get the bottom position of a stack? or the top position of the underlying stack?

Posted: Mon Nov 24, 2003 3:06 pm
by Marjan
You'll have to calculate it manually. First calculate the sum of all values, except the last series value. Then the middle of last bar will be equal to ("sum of all values" + "sum of all except the last value")*0.5.

Posted: Mon Nov 24, 2003 3:21 pm
by 8577662
Marjan wrote:You'll have to calculate it manually. First calculate the sum of all values, except the last series value. Then the middle of last bar will be equal to ("sum of all values" + "sum of all except the last value")*0.5.
I think you misunderstood my question. My problem is, that the code you've posted for centering the marks in the stacks (CenterMarks procedure), does only work for the first stack.
For every other stack the mark is centered in the *full* bar height and not in the *stack* height

Thanks
Pascal

Posted: Wed Nov 26, 2003 11:01 am
by Pep
How about using the following code ? :

Code: Select all

procedure TForm1.PositionChartMarks(BarSeries: TBarSeries);
var
  i: integer;
  YPos: integer;
  BarTop: integer;
  BarBottom: integer;
  XPos: integer;
  MarkPos: TSeriesMarkPosition;
begin
  MarkPos := TSeriesMarkPosition.Create;
  try
    for i := 0 to BarSeries.Count - 1 do begin
      // just an example, you can do further customization here
      MarkPos.Width := BarSeries.BarWidth;
      MarkPos.Height := 16;

      BarTop := BarSeries.CalcYPos(i); // get y screen pixel coordinate
      BarBottom := BarSeries.CalcYPosValue(BarSeries.PointOrigin(i,false));

      YPos := (BarTop + BarBottom - MarkPos.Height) div 2;
      // Make sure that the value isn't painted below the X Axis
      if YPos > (BarSeries.GetHorizAxis.PosAxis - (MarkPos.Height)) then begin
        YPos := BarSeries.GetHorizAxis.PosAxis - (MarkPos.Height);
      end;
      XPos := BarSeries.CalcXPos(i);

      MarkPos.Custom := True;
      MarkPos.LeftTop.X := XPos;
      MarkPos.LeftTop.Y := YPos;

      BarSeries.Marks.Positions[i] := MarkPos;
    end;
  finally
    MarkPos.Free;
  end;
  BarSeries.Repaint;
end;


procedure TForm1.Series1BeforeDrawValues(Sender: TObject);
begin
  PositionChartMarks(Sender as TBarSeries);
end;

Josep Lluis Jorge
http://support.steema.com

Posted: Wed Nov 26, 2003 3:00 pm
by 8577662
Pep wrote:How about using the following code ? :
Many thanks, works fine now.

How about adding this features to a future version (it's something I normaly expect built-in in a charting component)?

Thanks again
Pascal

Posted: Wed Nov 26, 2003 10:04 pm
by Pep
Ok, I'll add on our wish list to be considered for the next releases.

Josep Lluis Jorge
http://support.steema.com