Jump to content
David Hoyle

Generic Drawing Engine

Recommended Posts

I'm trying to create a rending engine that can use different canvas's where the coordinate system could Integer or Single. This is so that the differences between the output systems can be abstracted at the canvas level.

I've tried to used generics (see code below) but it will not compile. I don't know whether I'm trying to be too ambitious or whether I'm missing something.

The below code is a test project that shows the issue. Any help would be appreciated.

Note: The below code is not intended to run as it's missing some implementations, I'm just looking for it to compile.

Program IndirectGenerics;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils;

Type
  IPen<T> = Interface
    Function  GetWidth : T;
    Procedure SetWidth(Const AValue : T);
    Property Width : T Read GetWidth Write SetWidth;
  End;

  ICanvas<T> = Interface
    Function GetPen : IPen<T>;
    Property Pen : IPen<T> Read GetPen;
  End;

  TConcreteCanvas<T> = Class(TInterfacedObject, ICanvas<T>)
  Strict Private
    FPen : IPen<T>;
  Strict Protected
    Function GetPen : IPen<T>;
  Public
    Constructor Create;
  End;

  TConcreteDrawingEngine<T> = Class
  Strict Private
    FCanvas : ICanvas<T>;
  Strict Protected
  Public
    Constructor Create(Const Canvas : ICanvas<T>);
    Procedure DoSomething;
  End;

{ TConcreteCanvas<T> }

Constructor TConcreteCanvas<T>.Create;

Begin
  // FPen := 
End;

Function TConcreteCanvas<T>.GetPen: IPen<T>;

Begin
  Result := FPen;
End;

{ TConcreteGantt<T> }

Constructor TConcreteDrawingEngine<T>.Create(Const Canvas: ICanvas<T>);

Begin
  FCanvas := Canvas;
End;

Procedure TConcreteDrawingEngine<T>.DoSomething;

Begin
  FCanvas.Pen.Width := 1; // <= Does not compile as it thinks T is not Integer
End;

Var
  IntegerCanvas : ICanvas<Integer>;
  IntegerDrawingEngine : TConcreteDrawingEngine<Integer>;
  SingleCanvas : ICanvas<Single>;
  SingleDrawingEngine : TConcreteDrawingEngine<Single>;

Begin
  Try
    IntegerCanvas := TConcreteCanvas<Integer>.Create;
    IntegerDrawingEngine := TConcreteDrawingEngine<Integer>.Create(IntegerCanvas);
    //...
    SingleCanvas := TConcreteCanvas<Single>.Create;
    SingleDrawingEngine := TConcreteDrawingEngine<Single>.Create(SingleCanvas);
    //...
  Except
    On E: Exception Do
      Writeln(E.ClassName, ': ', E.Message);
  End;

End.

 

Share this post


Link to post
type
  TNumbers = class
  public
    class function One<T>: T;
  end;

{ TNumbers }

const
  OneSingle: Single = 1;
  OneInteger: Integer = 1;
class function TNumbers.One<T>: T;
type
  PT = ^T;
begin
  if TypeInfo(T) = TypeInfo(Single) then Result := PT(@OneSingle)^
  else if TypeInfo(T) = TypeInfo(Integer) then Result := PT(@OneInteger)^
  else raise Exception.Create('Error Message');
end;
  
...
FPen.Width := TNumbers.One<T>;

 

Share this post


Link to post


 

class function TNumbers.FromInteger<T>(AValue: Integer): T;
type
  PSingle = ^Single;
begin
  if TypeInfo(T) = TypeInfo(Single) then PSingle(@Result)^ := AValue
  else if TypeInfo(T) = TypeInfo(Integer) then PInteger(@Result)^ := AValue
  else raise Exception.Create('Error Message');
end;

 

Share this post


Link to post

Thank you @David Heffernan and @Eugine Savin.

Clearly, I was too ambitious. I tried a few other things with generics and then abandoned them when they didn't work either.

I decided to change the underlying coordinate system to Single from Integer and I'm using TPointF and TRectF and the real-time rendering in my application, even with VCL styles, does not seem to have diminished.

Still need to test on an old machine with a crappy graphics card but I think this will allow me to do what I want going forwards.

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×