Skip to content
Gustav Schubert edited this page Jan 27, 2021 · 10 revisions

Documentation Drawings

It happend some day last year (2020), perhaps too late, that I decided how I wanted to do the documentation drawings for one of my Delphi projects.

I came up with the always new idea of hiding drawing details of a drawing element in a class, in an object oriented way which also caters for the caption of an element.

So far I could not come up with a better name for the project than 'documentation drawings'.

  • The drawing elements have an alphanumeric text attached to them,
  • the combination of the elements will be a documentation drawing,
  • the elements should NOT be called Alphaments.

So what should the name of the project be?

What it is Not

  • It is not a component library.
  • It is not RAD (Rapid Application Development).
  • It does not have any design time features.
  • You do not have to build packages.
  • It is not hosting your drawings in a database on the web.

What it is

  • A library of drawing elements.
  • An application with a viewer for the drawings.
  • A place for a small number of included example drawings.

How to use it

  • You will create your own drawings.
  • You will need to create the drawing elements that you also need.
  • You can start with the provided UI, and improve on that later.

The purpose

The intention was to make it easy for myself to finally create the missing documentation drawings for my project.

At home I still have two folders full of old drawings, together with paper and pencil computations. I intend to rescue the knowledge and convert some of them into live drawings. I want to have fewer projects and more drawings. This should be fun. The purpose of this project is to ensure that it will be fun. I intend to be proud of the result when it will be finished and the good news is that I do not have to work hard for it, find out why.

Drawings and Elements

To create a new drawing you start with declaring and creating drawing elements, in code.

Points are drawn as a circle with a small radius. The basic point element is a TRggCircle:

  TRggCircle = class(TRggElement)
  private
    FRadius: single;
    procedure SetRadius(const Value: single);
  protected
    property Radius: single read FRadius write SetRadius;
  public
    OriginalCenter: TRggPoint3D;
    Center: TRggPoint3D;
    class var
      Matrix: TMatrix3D;

    constructor Create; overload;
    constructor Create(ACaption: string); overload;

    procedure Save;
    procedure Reset;
    procedure Transform; override;
    procedure TransformI;
    procedure WriteCode(ML: TStrings);

    procedure Draw(g: TCanvas); override;

    procedure Param1(Delta: single); override;
    procedure Param2(Delta: single); override;
    procedure Param3(Delta: single); override;

    procedure Param1I(Delta: single);
    procedure Param2I(Delta: single);

    function IsEqual(B: TRggCircle): Boolean;
    function CompareZ(Q: TRggCircle): Integer;

    class function Compare(const Left, Right: TRggCircle): Integer;
  end;

It stores original and transformed coordinates of the point. The special type used to store coordinates (TRggPoint3D) is a union of a 3D point (TPoint3D) and a 2D point (TPointF):

  TRggPoint3D = record
    function Rotate(const AAngle: Single): TRggPoint3D;
    function Angle(const APoint: TRggPoint3D): single;
    function Length: single;
    function Normalize: TRggPoint3D;
    function Distance(const APoint: TRggPoint3D): single;
    class function Zero: TRggPoint3D; static;

    class operator Add(const APoint1, APoint2: TRggPoint3D): TRggPoint3D;
    class operator Subtract(const APoint1, APoint2: TRggPoint3D): TRggPoint3D;

    case Integer of
      0: (X: single;
          Y: single;
          Z: single;);
      1: (C: TPoint3D);
      2: (P: TPointF;
          T: single;);
      3: (V: TVectorArray;);
  end;

  TRggPoly = array of TRggPoint3D;

A line element will reference two point elements:

  TRggLine = class(TRggElement)
  private
    function GetLength: single;
  public
    Point1: TRggCircle;
    Point2: TRggCircle;

    constructor Create(ACaption: string = '');

    // ...

    procedure Draw(g: TCanvas); override;
    procedure Param1(Delta: single); override;
    procedure Param2(Delta: single); override;
    function V2: TPointF;
    function V3: TPoint3D;

    { compare two lines, see which is above the other }
    class function Compare(const Left, Right: TRggLine): Integer;

    property LineLength: single read GetLength;
  end;

An element has 2 or 3 parameters, the value of which can be changed with the mouse wheel. This is very important. You can change the drawing, the model. The drawing is interactive because of the parameters of key elements.

  • Elements know how to draw themselves to a 2D canvas.
  • All elements have a name which is shown by default.
  • A drawing represents a computation to be documented.
  • A drawing is assembled from reusable elements.
  • There is a big circle element.
  • There is a polyline element.
  • You can create missing elements as needed.
  • Later you can add the new elements to the library.
  • Each element can be selected for manipulation via an item in a list view or list box.
  • The typical number of elements in a drawing is expected to be less than 30.
  • The drawing can be reset to its initial state via a button.

Not only can you change the model, the documentation drawing project supports view transformations.

  • Endless rotations around 3 axis are supported.
  • 3D Rotations can be enabled for a drawing if it makes sense.
  • Rotations are incremental.
  • Panning and zooming is also supported.

Good things

A touch screen device or a computer without mouse is supported.

Example drawings are included.

The project has only one form - the main form of the project - called FormDrawing. It is generic, you do not have to change it, but you can.

The project has only one library unit. I have compacted all the elements and the rest of the reusable code into one unit.

If you only want one drawing, then you can put this drawing as the only drawing into the available list. The list view of available drawings can be removed from the UI with a conditional define in FormDrawing.

I have created the documentation drawing code as part of the RiggVar project which is available in FMX, VCL, and LCL flavors.

I don't know how to do hdpi scaling properly in VCL or LCL. The good thing here is that at least I have a good solution for FMX.

The FMX TCanvas does antialiasing out of the box.

Assume a situation where you have optimized drawing code for your most important drawings already, and it is just that not many users know which element is which. The good thing here would be that you should be able to build a documentation drawing version of the drawing very quick.

Students see drawings - together with computations - all the time, in the text books of mathematics or physics classes. A computation without a drawing is almost unimaginable. What has not been readily available (in my time at school) was the related computer program. All good things are three - the drawing, the computation, and the test program.

I may rewrite the documentation drawings in other programming languages, but the good news is that I have it in Delphi already, and that I can create the original samples using Delphi. To convert the samples (drawings) should be very easy.

The samples do not contain platform dependent drawing code.

Potential problems

There are a few known problems, I will list them in paragraphs.

I have an opinion about how the transformations (rotations, panning and zooming) should be done. This may turn out to be a problem because it might be seen as unusual. In addition, my approach to the rotations (unconstrained around 3 axis) leads to some extra complexity. For example - when I change the fix point of the drawing (the center of future rotations) - I want to continue seamlessly with the incremental rotations, as if previous incremental rotations had happend around the new fix point.

An item in the list view of elements, from which you select the current element to manipulate, may not give enough information about the element it represents. It may not be clear enough which elements are the key elements the user is supposed to change. There are computed elements which can be selected while it does not make much sense to do so. The computation that runs when you change a parameter may override the change just made. I tried to indent unimportant elements, but I do not filter them out.

There is only one drawing active and displayed at a time. But all examples in the list of drawings are instantiated (loaded into memory) when a list is filled. This may be seen as a waste of memory. It should not be too difficult to improve on that. Is is only expected to become an issue if you put a lot of samples in the list. You could have multiple smaller lists and swap them in and out.

Work to do

You can start using the documentation drawings project to do one drawing only, the one which you want to test out interactively.

I assume that you have the computation ready and will do this:

  • Look at the examples.
  • Find and use the new drawing template which already exists as a file.
  • Define your elements in code and link them up.
  • When finished, save as a new drawing, register the new drawing, and revert the template.

I myself have also work to do: Convert the two big folders filled with handmade drawings and related computations, from the Eighties. The result of my home work will not be published here. But I will probably merge the improvements of the library code.

I would like to see the examples in typescript, C# and Swift. Please note that I am slow and you may be much faster.

And I think we need a TRggArrow element that looks like a vector.

Clone this wiki locally