YOUSEFUL / DELPHI / COMPONENTS

TAtlasCustomChart

Base class of the Atlas Plot Pro controls: painting, off-screen rendering, export, title, footnote, legend and batching.

Visual control Unit Atlas.ProChart · Inherits TCustomControl · Product Atlas Plot Pro

Description

The shared base of TAtlasBoxPlot, TAtlasHeatMap and TAtlasPolarPlot, and the starting point for new chart types. It is a TCustomControl that owns everything the charts share: painting through an Atlas Plot drawing surface, off-screen rendering to bitmaps, SVG and files, the title and footnote, a legend helper and update batching. A descendant overrides the protected DrawChart and ResetLayout and nothing else.

Every render, on screen or off, runs the same steps: ResetLayout; fill the bounds with Color; measure LineH from Font; draw the bold Title at the top and the Footnote at the bottom; then call DrawChart with the remaining area if it is at least four text lines wide and three high. Pixel queries of the descendants describe the most recent render. RenderTo, CreateBitmap, ToSVG and SaveToFile re-run the on-screen layout afterwards when the control has a window handle, so an export at another size does not break mouse hit testing; Render does not.

Besides the members below, the class publishes the standard Align, Anchors, Constraints, Enabled, ParentFont, PopupMenu, Visible, OnClick, OnDblClick, OnMouseDown, OnMouseUp and OnResize. The unit also declares the colours AtlasGridColor ($00E6E6E6) and AtlasAxisColor ($00808080) used for grid and axis lines. The members marked protected are for descendants only. Use the controls from the main thread. The class is not on the palette itself; there is no FMX version.

Members

MemberKindWhat it does
CreateconstructorSets up a double-buffered, opaque control of 480 x 320 pixels with a white background.
BeginUpdatemethodStarts a batch of changes: until the matching EndUpdate, property changes and added data do not repaint the control.
EndUpdatemethodEnds a batch started by BeginUpdate.
RendermethodDraws the chart into any TAtlasPlotSurface (for example a TAtlasSvgSurface or a TAtlasCanvasSurface over your own canvas), filling Bounds.
RenderTomethodDraws the chart onto a VCL canvas, for example a printer canvas or a bitmap you own, filling Bounds.
CreateBitmapmethodReturns a new 24-bit bitmap of the given size with the chart drawn on it; the caller frees it.
ToSVGmethodReturns the chart as an SVG document of the given size (1 to 16384 pixels).
SaveToFilemethodWrites the chart to .png, .bmp, .emf or .svg, chosen by the extension (case does not matter); any other extension raises EAtlasPlotError before anything is written.
PaletteColormethodOne of ten built-in series colours (blue, orange, green, red, purple, olive, dark blue, teal, salmon, grey).
TitlepropertyBold caption centred at the top.
FootnotepropertyCaption centred at the bottom.
ColorpropertyBackground colour, clWhite by default.
FontpropertyFont of every caption; the title uses it in bold.
OnMouseMoveeventThe standard VCL mouse event, published by the base class.
DrawChartmethodProtected; override it to draw the chart.
ResetLayoutmethodProtected; override it to forget hit regions and pixel mappings.
ChartChangedmethodProtected.
DrawLegendmethodProtected; call it from DrawChart.
UseFontColormethodProtected; selects the control's Font in another colour, optionally bold, on Surface for the following Text calls.
SurfacepropertyProtected.
LineHpropertyProtected.

Constructors

Create constructor

constructor Create(AOwner: TComponent); override;

Sets up a double-buffered, opaque control of 480 x 320 pixels with a white background. Descendants call inherited first and then create their own data.

Properties

Title property

property Title: string read FTitle write SetTitle;

Bold caption centred at the top. Empty (the default) leaves no room for it.

Footnote property

property Footnote: string read FFootnote write SetFootnote;

Caption centred at the bottom. Empty (the default) leaves no room for it.

Color property

property Color default clWhite;

Background colour, clWhite by default. Chart parts are blended towards it (box fills, radar fills), and heat map cells are outlined with it.

Font property

property Font;

Font of every caption; the title uses it in bold. The height of Ag in this font (at least 8 pixels) sets the chart's margins, label spacing and marker sizes. Changing it repaints the control.

Surface property

property Surface: TAtlasPlotSurface read FSurface;

Protected. The TAtlasPlotSurface being drawn on; valid only while ResetLayout and DrawChart run, nil otherwise.

LineH property

property LineH: Integer read FLineH;

Protected. The height of Ag in Font on the current surface, at least 8 pixels; use it for margins and spacing so the chart scales with the font. Valid during a render.

Events

OnMouseMove event

property OnMouseMove;

The standard VCL mouse event, published by the base class. Pass X and Y to HitTest to find what is under the pointer.

Methods

BeginUpdate method

procedure BeginUpdate;

Starts a batch of changes: until the matching EndUpdate, property changes and added data do not repaint the control. Calls nest.

pascal
Chart.BeginUpdate;
try
  Chart.Title := 'Updated';
  Chart.Footnote := 'Source: monitoring';
finally
  Chart.EndUpdate;
end;

EndUpdate method

procedure EndUpdate;

Ends a batch started by BeginUpdate. When the outermost batch ends the control is invalidated once. A call without a matching BeginUpdate is ignored.

Render method

procedure Render(ASurface: TAtlasPlotSurface; const Bounds: TRect);

Draws the chart into any TAtlasPlotSurface (for example a TAtlasSvgSurface or a TAtlasCanvasSurface over your own canvas), filling Bounds. Raises EAtlasPlotError when ASurface is nil. Pixel queries such as HitTest then describe this render; unlike the other output methods, Render does not restore the on-screen layout afterwards.

pascal
Svg := TAtlasSvgSurface.Create(600, 400);
try
  Chart.Render(Svg, Rect(0, 0, 600, 400));
  Text := Svg.Document;
finally
  Svg.Free;
end;

RenderTo method

procedure RenderTo(Target: TCanvas; const Bounds: TRect);

Draws the chart onto a VCL canvas, for example a printer canvas or a bitmap you own, filling Bounds. When the control has a window handle it then re-runs its on-screen layout, so hit tests keep matching the screen.

pascal
Chart.RenderTo(Bitmap.Canvas, Rect(0, 0, Bitmap.Width, Bitmap.Height));

CreateBitmap method

function CreateBitmap(AWidth, AHeight: Integer): Vcl.Graphics.TBitmap;

Returns a new 24-bit bitmap of the given size with the chart drawn on it; the caller frees it. Both sizes must be 1 to 16384 pixels, otherwise EAtlasPlotError is raised. Works without a window, for example in a console program or a service.

pascal
Bitmap := Chart.CreateBitmap(800, 500);
try
  Bitmap.SaveToFile('chart.bmp');
finally
  Bitmap.Free;
end;

ToSVG method

function ToSVG(AWidth, AHeight: Integer): string;

Returns the chart as an SVG document of the given size (1 to 16384 pixels). Text is XML-escaped. Works without a window.

pascal
Text := Chart.ToSVG(640, 420);

SaveToFile method

procedure SaveToFile(const FileName: string; AWidth: Integer = 0; AHeight: Integer = 0);

Writes the chart to .png, .bmp, .emf or .svg, chosen by the extension (case does not matter); any other extension raises EAtlasPlotError before anything is written. A size of 0 or less uses the control's Width and Height; sizes above 16384 raise. The image is written to FileName.part and then renamed over the target, so a failed export keeps an existing file. Works without a window.

pascal
Chart.SaveToFile('chart.png', 800, 500);
Chart.SaveToFile('chart.svg');   // the control's own size

PaletteColor method

class function PaletteColor(Index: Integer): TColor; static;

One of ten built-in series colours (blue, orange, green, red, purple, olive, dark blue, teal, salmon, grey). The index wraps: PaletteColor(10) equals PaletteColor(0), and negative indexes use their absolute value. Categories and series with colour clDefault take PaletteColor of their index.

pascal
Chart.Color := TAtlasCustomChart.PaletteColor(3);

DrawChart method

procedure DrawChart(const Area: TRect); virtual; abstract;

Protected; override it to draw the chart. Area is what is left of the render bounds after a half-line margin, the title and the footnote. Draw with Surface only, so the same code produces the screen image, bitmaps, EMF and SVG. Record whatever your pixel queries need here. It is not called when Area is less than four text lines wide or three high.

ResetLayout method

procedure ResetLayout; virtual; abstract;

Protected; override it to forget hit regions and pixel mappings. It is called at the start of every render, before anything is drawn, including renders where DrawChart is then skipped.

ChartChanged method

procedure ChartChanged;

Protected. Invalidates the control unless a BeginUpdate batch is open. Call it from every setter and data method of a descendant. (It is not called Changed because TControl already has one.)

DrawLegend method

function DrawLegend(const Area: TRect; const Entries: TArray<TAtlasLegendEntry>): TRect;

Protected; call it from DrawChart. Draws a vertical legend, one colour swatch and caption per TAtlasLegendEntry, vertically centred at the right of Area, and returns Area without the legend and a one-line gap. The column is at most two fifths of the width, long captions end in ..., and entries that do not fit the height are left out. With no entries, or no room, it draws nothing and returns Area unchanged.

UseFontColor method

procedure UseFontColor(AColor: TColor; Bold: Boolean = False);

Protected; selects the control's Font in another colour, optionally bold, on Surface for the following Text calls. Call Surface.UseFont(Font) to return to the normal font.

Examples

Write a new chart type

A minimal progress chart: one bar per goal against its target, with a legend. The CustomChartType sample develops it with hit testing and checks.

pascal
uses
  System.Classes, System.Types, System.Math, Atlas.PlotAxis, Atlas.PlotSurface, Atlas.ProChart;

type
  TGoalChart = class(TAtlasCustomChart)
  private
    FNames: TStringList;
    FShares: TArray<Double>;   // done / target
  protected
    procedure DrawChart(const Area: TRect); override;
    procedure ResetLayout; override;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure AddGoal(const Name: string; const Done, Target: Double);
  end;

constructor TGoalChart.Create(AOwner: TComponent);
begin
  inherited;
  FNames := TStringList.Create;
end;

destructor TGoalChart.Destroy;
begin
  FNames.Free;
  inherited;
end;

procedure TGoalChart.AddGoal(const Name: string; const Done, Target: Double);
begin
  CheckPlotValue(Done);
  if Target <= 0 then raise EAtlasPlotError.Create('A target must be positive');
  FNames.Add(Name);
  FShares := FShares + [Min(Max(Done / Target, 0), 1.25)];
  ChartChanged;   // repaint unless inside BeginUpdate/EndUpdate
end;

procedure TGoalChart.ResetLayout;
begin
  // Nothing recorded between renders in this minimal version.
end;

procedure TGoalChart.DrawChart(const Area: TRect);
var Legend: TArray<TAtlasLegendEntry>; Plot: TRect; I, Row, Left: Integer;
begin
  SetLength(Legend, 1);
  Legend[0].Caption := 'Done';
  Legend[0].Color := PaletteColor(0);
  Plot := DrawLegend(Area, Legend);
  if FNames.Count = 0 then Exit;
  Left := Plot.Left + Plot.Width div 4;
  Row := Plot.Height div FNames.Count;
  for I := 0 to FNames.Count - 1 do
  begin
    Surface.UseFont(Font);
    Surface.Text(Left - LineH div 2, Plot.Top + I * Row + (Row - LineH) div 2,
      AtlasFitText(Surface, FNames[I], Left - Plot.Left - LineH), taRightJustify);
    Surface.UsePen(PaletteColor(0));
    Surface.UseBrush(PaletteColor(0));
    Surface.Box(Rect(Left, Plot.Top + I * Row + Row div 4,
      Left + Round(FShares[I] * (Plot.Right - Left) * 0.8), Plot.Top + (I + 1) * Row - Row div 4));
  end;
  Surface.UsePen(AtlasAxisColor, 2);
  Surface.Line(Left + Round((Plot.Right - Left) * 0.8), Plot.Top, Left + Round((Plot.Right - Left) * 0.8), Plot.Bottom);
end;

procedure SaveGoals(const FileName: string);
var Chart: TGoalChart;
begin
  Chart := TGoalChart.Create(nil);
  try
    Chart.Title := 'Quarterly goals';
    Chart.AddGoal('Signups', 1200, 1500);
    Chart.AddGoal('Releases', 4, 3);
    Chart.SaveToFile(FileName, 600, 300);
  finally
    Chart.Free;
  end;
end;

Render any chart into an SVG string

Works for every Atlas Plot Pro control, with or without a window.

pascal
uses
  System.SysUtils, Atlas.ProChart;

function ChartAsSvg(Chart: TAtlasCustomChart): string;
begin
  Chart.BeginUpdate;
  try
    Chart.Title := 'Snapshot';
    Chart.Footnote := DateToStr(Date);
  finally
    Chart.EndUpdate;
  end;
  Result := Chart.ToSVG(800, 500);
end;

TAtlasLegendEntry · TAtlasBoxPlot · TAtlasHeatMap · TAtlasPolarPlot · TAtlasPlotSurface · TAtlasSvgSurface

Also in Atlas Plot Pro

TAtlasBoxPlot · TAtlasHeatMap · TAtlasPolarPlot