# Tutorial 13 - Custom drawing on the Chart Panel

TeeChart offers extensive custom drawing facilities via the TCanvas3D component. With Canvas you may add shapes, lines and text anywhere on the Chart Panel and define their colours, pen and brush styles.

## TeeChart Canvas

### Drawing order

When using TeeChart's Canvas methods remember that drawing order is important. Drawing a Line on the Chart then adding Series data points will cause the Line to be overdrawn. You could put the Line in the Series BeforeDrawValues event for the Line to appear above the Chart grid and below the Series. You could place the Line code in the OnAfterDraw event for the Line to appear above the Series.

Event order, 4 principle Chart draw events

• OnBeforeDrawChart event
• OnBeforeDrawAxes event
• OnBeforeDrawSeries event
• OnAfterDraw event

### Drawing Lines

2D Chart

Example

```//Draw a Line diagonally from top left to bottom right
//in the Chart Area of a 2D Chart

With Chart1, ChartRect do
begin
//Move the pointer to the top left Chart point
Canvas.MoveTo(Left,Top);

//Draw the Line
Canvas.LineTo(Right,Bottom);
end;
```

3D Orthogonal Chart
On an Orthogonal 3D Chart the Axis positions are offset from the Chart area due to 3D orthogonal displacement. We can move the Line accordingly:

Example

```//Draw a Line diagonally from top left to bottom right
//in the Chart Area of a 3D Chart

With Chart1, ChartRect do
begin
//Move the pointer to the top left Chart point
Canvas.MoveTo(Left   Width3D,Top - Height3D);

//Draw the Line   adjustment for 3D displacement
Canvas.LineTo(Right   Width3D,Bottom - Height3D);
end;
```

3D Nativemode or OpenGL Chart
To draw the same line on a Native 3D Chart or OpenGL Chart (This will also work on a 2D and 3D Orthogonal Chart):

```//Draw a Line diagonally from top left to bottom right
//in the Chart Area of a 3D Nativemode or OpenGL Chart
With Chart1,Canvas do
begin
Pen.Color := clBlue;
Pen.Width := 1;
Pen.Style := psDot; //Pen must be 1 to use Pen.Style
Brush.Style := bsClear; //transparency
//Then draw the Line
MoveTo3D(ChartRect.Left,ChartRect.Top,0);
LineTo3D(ChartRect.Right,ChartRect.Bottom,Width3D);
end;
```

MoveTo3D and LineTo3D methods recognise the displacement made to the ChartRect as a result of Elevation and Rotation applied with 'Full' 3D.

### Canvas Pen and Brush

The Line above is drawn using the Pen and Brush defined for the last object drawn before the Line is drawn. That may or may not be the Pen you want. You can change the Pen accordingly:

Example

```//Define Pen and Brush before drawing the Line
With Chart1,Canvas,ChartRect do
begin
Pen.Color := clBlue;
Pen.Width := 1;
Pen.Style := psDot; //Pen must be 1 to use Pen.Style
Brush.Style := bsClear; //transparency

//Then draw the Line
MoveTo(Left   Width3D,Top - Height3D);
LineTo(Right   Width3D,Bottom - Height3D);
end;
```

Add 2D Canvas Shapes in a similar manner to Canvas Lines. The following example adds a Rectangle in the centre of the Chart Area:

2D Chart
3D orthogonal Charts support only 2D shapes.

Example

```With Chart1, Canvas do
begin
//prepare Pen and Brush
Pen.Color := clBlue;
Pen.Width := 1;
Pen.Style := psDot;
Brush.Color := clWhite;
Brush.Style := bsSolid;

//You can draw a Rectangle on any Chart (2D or 3D)
Rectangle(100,100,200,200);
end;
```

3D Charts
On a 3D Chart you can move the Rectangle in a Z plane too. See the RectangleWithZ method. This example for an orthogonal or Nativemode/OpenGL Chart places the Rectangle on the Left Wall but displaces it halfway towards the rear of the Chart (towards the Back Wall).

```With Chart1,Canvas,ChartRect do
begin
//prepare Pen and Brush
Pen.Color := clBlue;
Pen.Width := 1;
Pen.Style := psDot;
Brush.Color := clWhite;
Brush.Style := bsSolid;
//Draw Rectangle with Z displacement
RectangleWithZ(Rect(Left,
Top,
Left ((Right-Left) div 2),
Top ((Bottom-top) div 2)),
Width3D div 2);
end;
```

You may add 3D shapes to 3D Charts. This example draws a Cube in the top, left quadrant of the Chart rectangle. The depth covers the area from the front of the Walls to the Back Wall. See the Cube method.

```With Chart1,Canvas,ChartRect do
begin
//prepare Pen and Brush
Pen.Color := clBlue;
Pen.Width := 1;
Pen.Style := psDot;
Brush.Color := clWhite;
Brush.Style := bsSolid;

Cube(Left,
Left ((Right-Left) div 2),
Top,
Top ((Bottom-Top) div 2),
0,
Width3D,
True);
end;
```

2D Text location

Example

```procedure TForm1.Button1Click(Sender: TObject);
var rectLeft,rectTop,rectRight,rectBottom:Integer;
begin
With Chart1, Canvas, ChartRect do
begin
rectLeft:= Left;
rectTop:= Top;
rectRight:= Left   (Right - Left) div 2;
rectBottom:= Top   (Bottom - Top) div 2;

//prepare Pen and Brush
Pen.Color := clBlue;
Pen.Width := 1;
Pen.Style := psDot;
Brush.Color := clWhite;
Brush.Style := bsSolid;

//Draw the Rectangle
Rectangle(rectLeft,rectTop,rectRight,rectBottom);

//Modify Font
Font.Color := clRed;

//add the Text start at the midpoint of the Rectangle
TextOut(rectLeft   (rectRight - rectLeft) div 2,
rectTop   (rectBottom-rectTop) div 2,
'Hello');
end;
end;
```

3D Text location
You can place Text in a differing 3D plane by using the TextOut3D method.

Example

``` With Chart1, Canvas, ChartRect do
begin
Brush.Style := bsClear;

TextOut3D(Left, Top, Width3D div 2, 'Hello');
end;
```

### Applied example

This example takes the 3rd and 10th values of a Series, plots a Line between them and tells us the value of the first and Last point of the new Line and the difference between them:

Example

```'First add some data to the empty Chart
procedure TForm1.BitBtn1Click(Sender: TObject);
begin
Series1.FillSampleValues(20);
end;
```
```//You could put this code in the OnAfterDraw event
procedure TForm1.Chart1AfterDraw(Sender: TObject);
begin
With Chart1 do
Begin
If SeriesCount > 0 Then
begin
If Series1.Count > 10 Then
begin
Canvas.Pen.Color := clBlue;
Canvas.Pen.Width := 1;
Canvas.Pen.Style := psDot;
Canvas.Brush.Style := bsClear;
Canvas.MoveTo (Axes.Bottom.CalcXPosValue(Series1.XValues[3]),
Axes.Left.CalcYPosValue(Series1.YValues[3]));
Canvas.LineTo (Axes.Bottom.CalcXPosValue(Series1.XValues[10]),
Axes.Left.CalcYPosValue(Series1.YValues[10]));
Canvas.Brush.Style := bsSolid;
Canvas.TextOut(Axes.Bottom.CalcXPosValue(Series1.XValues[3]),
Axes.Left.CalcYPosValue(Series1.YValues[3]),
'Point value: '   FloatToStr(Series1.YValues[3]));
Canvas.TextOut(Axes.Bottom.CalcXPosValue(Series1.XValues[10]),
Axes.Left.CalcYPosValue(Series1.YValues[10]),
'Point value: '   FloatToStr(Series1.YValues[10]));
Canvas.TextOut(Axes.Bottom.CalcXPosValue(Series1.XValues[10]),
Axes.Left.CalcYPosValue(Series1.YValues[10])
Canvas.TextHeight('Any letter'), 'Change is: '
FloatToStr(Series1.YValues[10] - Series1.YValues[3]));
end;
end;
end;
end;
```

### Component overview

Almost all TeeChart units use now the TeCanvas unit. This unit is a low-level unit that provides all drawing functions encapsulated in the new TCanvas3D component.
If you do special customized drawing using the TeeChart Canvas property, you should add this unit to your Forms "Uses" clause. It is safe to use it as it has no dependencies other than the normal Delphi units.

TCanvas3D class
This Canvas-derived class incorporates support for 3D rotation, zoom, scroll and 3D primitives. It has all methods virtual and abstract, so it means you can not use it directly. You should use a derived class which implements all methods and properties.

Using this Canvas, the Chart components can now support "plug-in" Canvases. That is, you can change at design-time or run-time the "Chart1.Canvas" property, and all drawing will be redirected to the new Canvas:

```Chart1.Canvas := TGLCanvas.Create ;   //<-- switches display to OpenGL
```

TeeChart includes these new virtual Canvases:

• TTeeCanvas3D
• TGLCanvas ( for OpenGL rendering )

Implementing a new virtual Canvas, though not trivial, should be quite easy to do. You can even create, for example, a TVRMLCanvas, which will generate an ascii file containing VRML (Virtual Reality) graphic instructions. ( Or another to save to DXF, 3DS, etc ). These Canvases apply to the base class TCustomChart, so they work for TChart, TDBChart, TQRChart or any other derived Chart class. Note: OpenGL DLL's are only for 32bit Windows, hence too, the GLCanvas. TTeeCanvas3D class:

This is the implementation of the virtual TCanvas3D class used internally. It is a wrapper around a standard Delphi TCanvas class. It has many direct calls to Windows GDI to speed up drawing. It adds the TCanvas3D features like rotation, zoom, etc. It works with all Delphi and C Builder versions. TGLCanvas class:

This class resides in a separate unit / package to make applications independant of OpenGL's DLLs. It works only for 32bit Delphi (and C Builder ). When using the TGLCanvas for a Chart include 'Uses TeeGLEditor' and the Chart Editor will add another page at runtime to change GL characteristics such as Light location and colour. For OpenGL to be available at designtime, add the TTeeOpenGL component to the Form, associate it with the Chart and set Active:=True. There is a new non-visual component ( TTeeOpenGL ), that allows you to "connect" an existing Chart to it at design or run-time, and immediately see the Chart rendered using OpenGL 3D libraries. This component allows you to also define OpenGL specific properties, like Lighting attributes. Comparison between TTeeCanvas3D and TGLCanvas:

• The OpenGL canvas supports lighting ( lights , light colors, positions, etc ) while the TeeCanvas draws 3D sides with darker colors to simulate lighting.
• The OpenGL canvas does not support metafile creation, so for printing a big and fat TBitmap should be created and sent to the printer Canvas, which means it works only with printers with good memory and sane drivers. Metafiles cannot be created, copied to clipboard, saved or printed.
• The OpenGL canvas needs the OpenGL DLLs ( available in Windows NT and later and alternative libraries from www.sgi.com. Depending on your CPU and video hardware, the OpenGL Dlls from Silicon Graphics may be faster than the ones from Microsoft.
• The OpenGL canvas supports different Font Sizes and Colors, but it does not support more than one Font style. All characters of a Font are converted to drawing instructions. This takes up quite a lot of memory and CPU speed.