Page 1 of 1

How to best draw (custom) legend with used Pointers

Posted: Thu Jun 27, 2019 3:28 pm
by 16585377
Hi,

We have a 3D plot where for the points different Pointer styles are used:
3DPlotCapture.PNG
3DPlotCapture.PNG (220.18 KiB) Viewed 12698 times
What is the best way to draw/add a (custom) legend showing the different used Pointers with some custom text?
The requirement is to display (legend like) what the meaning of the different pointers is.

best regards

Re: How to best draw (custom) legend with used Pointers

Posted: Fri Jun 28, 2019 2:26 pm
by yeray
Hello,

The easier would probably be to hide all your series from the legend (ShowInLegend:=False) and then create as many new series as pointers+text you want to show in the legend. These new series won't have any value but you can set them your pointers and titles to show them in the legend.

Re: How to best draw (custom) legend with used Pointers

Posted: Mon Jul 01, 2019 2:49 pm
by 16585377
Hi Yeray,

indeed that works nicely:
WorkingPointersCapture.PNG
WorkingPointersCapture.PNG (54.71 KiB) Viewed 12678 times
It's just that the pointers in the legend a painted 2D and in the graphics these are
painted 3D. Is there a way to get 3D pointers in legend too ?

Thanks and best regards,

Re: How to best draw (custom) legend with used Pointers

Posted: Tue Jul 02, 2019 9:38 am
by yeray
Hello,

Drawing legend items in 3D as the series using OpenGL is not so easy because all the objects are drawn following the view (rotation and elevation) except for the default legend, where we disable the 3D to draw it as a plane.

This means you can't use the Legend.Symbols.OnDraw event to draw 3D figures.
Instead, you could hide the default legend and manually draw it at OnAfterDraw event.

Code: Select all

uses TeePoin3, Math;

procedure TForm1.Chart1AfterDraw(Sender: TObject);
var i: Integer;
    R1, R2: TRect;
    maxTitleWidth: Integer;
    maxTitleHeight: Integer;
    tmpZ: Integer;
begin
  tmpZ:=Round(Chart1.DepthAxis.IAxisSize/2);
  maxTitleWidth:=0;
  maxTitleHeight:=0;
  for i:=0 to Chart1.SeriesCount-1 do
  begin
    maxTitleWidth:=Max(maxTitleWidth, Chart1.Canvas.TextWidth(Chart1.SeriesTitleLegend(i))+TPoint3DSeries(Chart1[i]).Pointer.Size+15);
    maxTitleHeight:=Max(Chart1.Canvas.TextHeight(Chart1.SeriesTitleLegend(i)),TPoint3DSeries(Chart1[i]).Pointer.Size);
  end;

  R1:=Rect(Chart1.ChartRect.Right+10,Chart1.ChartRect.Top,
          Chart1.ChartRect.Right+10+maxTitleWidth,
          Chart1.ChartRect.Top+5+Chart1.SeriesCount*(maxTitleHeight+5));

  Chart1.Canvas.Rectangle(R1, tmpZ);

  R1.Offset(3,5);

  for i:=0 to Chart1.SeriesCount-1 do
  begin
    if Chart1[i] is TPoint3DSeries then
      with Chart1.Canvas,TPoint3DSeries(Chart1[i]) do
      begin
        Pointer.PrepareCanvas(Chart1.Canvas,Color);

        R2:=R1;
        R2.Width:=Pointer.Size;
        R2.Height:=Pointer.Size;
        R2.Offset(0,3);
        case Pointer.Style of
          psRectangle: Cube(R2.Left,R2.Right,R2.Top,R2.Bottom,tmpZ-Pointer.Depth,tmpZ,True,0);
          psCircle: Sphere(R2.Left+Pointer.Size div 2,R2.Top+Pointer.Size div 2,tmpZ-Pointer.Depth div 2,Pointer.Size div 2);
          psTriangle: Pyramid(True,R2.Left,R2.Top,R2.Right,R2.Bottom,tmpZ-Pointer.Depth,tmpZ,True);
          psDownTriangle: Pyramid(True,R2.Left,R2.Bottom,R2.Right,R2.Top,tmpZ-Pointer.Depth,tmpZ,True);
          psCross: ;
          psDiagCross: ;
          psStar: ;
          psDiamond: ;
          psSmallDot: ;
          psNothing: ;
          psLeftTriangle: ;
          psRightTriangle: ;
          psHexagon: ;
          psVisual: ;
          psDonut: ;
          psArrow: ;
        end;

        TextOut3D(R1.Left+Pointer.Size+5,R1.Top,tmpZ,Chart1.SeriesTitleLegend(i));
      end;

    R1.Offset(0,maxTitleHeight+5);
  end;
end;

Re: How to best draw (custom) legend with used Pointers

Posted: Wed Jul 03, 2019 8:03 am
by 16585377
Hi Yeray,

thank you for the code, in principle that works.
But looking at the plot this still needs serious tweaking:
3DLegendCapture.PNG
3DLegendCapture.PNG (56.89 KiB) Viewed 12654 times
Distance and sizes are not Ok and also some pointers are missing.
I may for the next release rather stay with the 2D legend.

best regards

Re: How to best draw (custom) legend with used Pointers

Posted: Wed Jul 03, 2019 10:28 am
by yeray
Hello,
axrDegen wrote:
Wed Jul 03, 2019 8:03 am
Distance and sizes are not Ok
This is probably due to some difference between your application and my test app.
Please arrange a simple example project we can run as-is to reproduce the problem here.
axrDegen wrote:
Wed Jul 03, 2019 8:03 am
some pointers are missing
I only implemented a few pointer styles (psRectangle, psCircle, psTriangle and psDownTriangle). I see you also use psCross and psStar.
Here I'm adding psCross, psDiagCross and psStar:

Code: Select all

procedure TForm1.Chart1AfterDraw(Sender: TObject);
var R2: TRect;
    tmpZ: Integer;

  procedure DrawCross;
  begin
    with Chart1.Canvas do
    begin
      VertLine3D(R2.Left+(R2.Right-R2.Left) div 2,R2.Top,R2.Bottom,tmpZ);
      HorizLine3D(R2.Left,R2.Right,R2.Top+(R2.Bottom-R2.Top) div 2,tmpZ);
    end;
  end;

  procedure DrawDiagonalCross;
  begin
    with Chart1.Canvas do
    begin
      LineWithZ(R2.Left,R2.Top,R2.Right,R2.Bottom,tmpZ);
      LineWithZ(R2.Right,R2.Top,R2.Left,R2.Bottom,tmpZ);
    end;
  end;

var i: Integer;
    R1: TRect;
    maxTitleWidth: Integer;
    maxTitleHeight: Integer;
begin
  tmpZ:=Round(Chart1.DepthAxis.IAxisSize/2);
  maxTitleWidth:=0;
  maxTitleHeight:=0;
  for i:=0 to Chart1.SeriesCount-1 do
  begin
    maxTitleWidth:=Max(maxTitleWidth, Chart1.Canvas.TextWidth(Chart1.SeriesTitleLegend(i))+TPoint3DSeries(Chart1[i]).Pointer.Size+15);
    maxTitleHeight:=Max(Chart1.Canvas.TextHeight(Chart1.SeriesTitleLegend(i)),TPoint3DSeries(Chart1[i]).Pointer.Size);
  end;

  R1:=Rect(Chart1.ChartRect.Right+10,Chart1.ChartRect.Top,
          Chart1.ChartRect.Right+10+maxTitleWidth,
          Chart1.ChartRect.Top+5+Chart1.SeriesCount*(maxTitleHeight+5));

  Chart1.Canvas.Rectangle(R1, tmpZ);

  R1.Offset(3,5);

  for i:=0 to Chart1.SeriesCount-1 do
  begin
    if Chart1[i] is TPoint3DSeries then
      with Chart1.Canvas,TPoint3DSeries(Chart1[i]) do
      begin
        Pointer.PrepareCanvas(Chart1.Canvas,Color);

        R2:=R1;
        R2.Width:=Pointer.Size;
        R2.Height:=Pointer.Size;
        R2.Offset(0,3);
        case Pointer.Style of
          psRectangle: Cube(R2.Left,R2.Right,R2.Top,R2.Bottom,tmpZ-Pointer.Depth,tmpZ,True,0);
          psCircle: Sphere(R2.Left+Pointer.Size div 2,R2.Top+Pointer.Size div 2,tmpZ-Pointer.Depth div 2,Pointer.Size div 2);
          psTriangle: Pyramid(True,R2.Left,R2.Top,R2.Right,R2.Bottom,tmpZ-Pointer.Depth,tmpZ,True);
          psDownTriangle: Pyramid(True,R2.Left,R2.Bottom,R2.Right,R2.Top,tmpZ-Pointer.Depth,tmpZ,True);
          psCross: DrawCross;
          psDiagCross: DrawDiagonalCross;
          psStar: begin
                    DrawCross;
                    DrawDiagonalCross;
                  end;
          psDiamond: ;
          psSmallDot: ;
          psNothing: ;
          psLeftTriangle: ;
          psRightTriangle: ;
          psHexagon: ;
          psVisual: ;
          psDonut: ;
          psArrow: ;
        end;

        TextOut3D(R1.Left+Pointer.Size+5,R1.Top,tmpZ,Chart1.SeriesTitleLegend(i));
      end;

    R1.Offset(0,maxTitleHeight+5);
  end;
end;
Project1_2019-07-03_12-27-37.png
Project1_2019-07-03_12-27-37.png (7.34 KiB) Viewed 12650 times