Marks inside of stacked bars?

TeeChart VCL for Borland/CodeGear/Embarcadero RAD Studio, Delphi and C++ Builder.
Post Reply
Timeware AG
Newbie
Newbie
Posts: 10
Joined: Thu Nov 06, 2003 5:00 am
Location: Switzerland
Contact:

Marks inside of stacked bars?

Post by Timeware AG » Wed Nov 19, 2003 1:27 pm

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

Marjan
Site Admin
Site Admin
Posts: 745
Joined: Fri Nov 07, 2003 5:00 am
Location: Slovenia
Contact:

Post by Marjan » Wed Nov 19, 2003 2:49 pm

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, ...

Timeware AG
Newbie
Newbie
Posts: 10
Joined: Thu Nov 06, 2003 5:00 am
Location: Switzerland
Contact:

Post by Timeware AG » Wed Nov 19, 2003 3:02 pm

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?

Marjan
Site Admin
Site Admin
Posts: 745
Joined: Fri Nov 07, 2003 5:00 am
Location: Slovenia
Contact:

Post by Marjan » Fri Nov 21, 2003 6:29 am

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;

Timeware AG
Newbie
Newbie
Posts: 10
Joined: Thu Nov 06, 2003 5:00 am
Location: Switzerland
Contact:

Post by Timeware AG » Fri Nov 21, 2003 6:51 pm

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

Marjan
Site Admin
Site Admin
Posts: 745
Joined: Fri Nov 07, 2003 5:00 am
Location: Slovenia
Contact:

Post by Marjan » Sun Nov 23, 2003 5:11 pm

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

Timeware AG
Newbie
Newbie
Posts: 10
Joined: Thu Nov 06, 2003 5:00 am
Location: Switzerland
Contact:

Post by Timeware AG » Mon Nov 24, 2003 1:16 pm

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?

Marjan
Site Admin
Site Admin
Posts: 745
Joined: Fri Nov 07, 2003 5:00 am
Location: Slovenia
Contact:

Post by Marjan » Mon Nov 24, 2003 3:06 pm

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.

Timeware AG
Newbie
Newbie
Posts: 10
Joined: Thu Nov 06, 2003 5:00 am
Location: Switzerland
Contact:

Post by Timeware AG » Mon Nov 24, 2003 3:21 pm

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

Pep
Site Admin
Site Admin
Posts: 3306
Joined: Fri Nov 14, 2003 5:00 am
Contact:

Post by Pep » Wed Nov 26, 2003 11:01 am

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

Timeware AG
Newbie
Newbie
Posts: 10
Joined: Thu Nov 06, 2003 5:00 am
Location: Switzerland
Contact:

Post by Timeware AG » Wed Nov 26, 2003 3:00 pm

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

Pep
Site Admin
Site Admin
Posts: 3306
Joined: Fri Nov 14, 2003 5:00 am
Contact:

Post by Pep » Wed Nov 26, 2003 10:04 pm

Ok, I'll add on our wish list to be considered for the next releases.

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

Post Reply