The GLE99 Coordinate System.

This is central to the operation of GLE99 and it is vital for 
any programmer to have a clear idea of its organisation. Referring 
back to the original GLE package, a central feature is the 
ability to change the local co-ordinate system by scaling, 
rotation, translation and change of origin. In this package the 
screen handling operations were relatively simple, but in a 
system employing sizeable windows this also needs attention.

In GLE99 coordinates are handled at four levels.

(1) Screen Level.
The current window* has its own pixel coordinate system which is
defined by the Windows Operating SYstem. In this the top left 
hand corner is the point 0,0 and the bottom right hand corner 
the point px,py where px and py are the dimensions of the window 
in pixels. (* In the Delphi implimentation this primary window 
is the image area seen on the screen. This has two parameters, 
Image.Height and Image.Width that define its dimensions in pixels.) 
In Delphi screen coordinates are norally held in records of type 
Tpoint. A Tpoint record has two integer components defining the 
x and y coordinates in pixels.

(2) Drawing Level
This window provides a view of a part of the actual drawing space, 
which is normally assumed to be dimensioned in cms. To handle 
drawing space coordinates we define records of type RealPoint, 
which are similar to Tpoint records, except that the elements are 
real rather than integer. 
The conversions to and from drawing space coordinates are handled 
by two functions ScreenPoint and ToRealPoint. 
The listings of these are:

function ScreenPoint(Input:RealPoint):TPoint;
{Derives screen coordinates for a plot point}
begin
    ScreenPoint.x := Round((Input.x-XOffset)*PixelsPerCm);
    ScreenPoint.y := Round(DisplayHeight 
                            -(Input.y-YOffset)*PixelsPerCm);
end; {Screenpoint}

function ToRealPoint(Input:Tpoint):RealPoint;
{Derives co-ordiates in cm corresponding to a screen point
 Note, these are absolute coordinates}
begin
    ToRealPoint.x := Input.x/PixelsPerCm+XOffset;
    ToRealPoint.y := (DisplayHeight - Input.y)/PixelsPerCm+YOffset;
end; {ToRealPoint}

These conversions take account of the current absolute scaling 
given by the parameter PixelsPerCm. They also allow for the 
offset of the bottom left hand corner of the Window - at starting  
time - relative to the origin of the paper. This complication 
arises from the manner in which the Windows system is organised and 
functions. As previously noted the pixel y-coordinates start at 0 
at the top of the window and increase downwards. Normal drawing 
practice is the reverse of this, namely zero y is at the bottom and 
the positive direction is upwards. Thus to covert from pixel to
cms we must include a term (DisplayHeight - ypixels). 
Unfortunately, when the window is resized its display height 
changes but the top left corner always stays as the point 0,0. 
Thus the conversion should either always use the inital setting 
for the display height or else it will be necessary to completely 
redraw the screen after each resizing, which introduces an 
annoying flicker. It is not practical to retain a constant origin 
at the bottom left hand corner of the window when resizing it.

However, despite all this we can set up a window to examine a
small portion of the drawing by resetting the Xoffset, Yoffset 
and PixelsPerCm parameters.

(3) Local Coordinate Level.
The actual GLE drawing commands refer to the local coordinate 
system in operation at the time, which may be modified by any
previous rescalings, rotations, or changes in origin. In GLE 
these are organised as blocks by using statements such as:
  BEGIN ROTATION 30
    .....
  END ROTATION
and these blocks may be nested.

This leads to complications at two levels. First, we must organise 
translations to and from the local coordinates through a reversable 
process. I have chosen to use a stack process, since return to a 
previous level does not then involve any recalculations, with 
conequent build-up of round-off errors.

The second complication arises from the fact that most of the 
drawing procedures supplied as part of the Delphi system cannot 
be scaled or rotated. I have therefore ony used the basic line,
polyline and polygon procedures. Boxes, arcs, circles, etc. are all 
constructed from these.

(4) Graph Coordinates
It is also essential in some circumstances to carry out drawing 
operations in terms of the coordinate system of an assiciated graph.
As in the previous versions of GLe this is handled by two functions, 
xg() anf yg(). In GLE99 these functions take both the x and y graph
coordinates as arguments. The reason for this added complexity is to 
allow for future use of axes not at right-angles. This can arise, for 
example, when dealing with 3 dimensional plots. In such cases the
position on the drawing plane will depend on at least two of the 
three coordinates.
  
09:36 18/04/98
Co-ordinate Transformations.
To include these all drawing operations must be based on elements 
that allow x and y to be modified. This excludes ellipses, arcs, 
etc. Hence substitute procedures based on a procedure BasicArc 
are used. These generate arrays of type TRPoint (similar to 
Tpoint but real.) These are then transformed to absolute screen
pixel co-ordinates xa,ya by:

            xa = a + c*x + e*y
            ya = b + d*x + f*y

The commands Origin and Translate only change a & b
Effectively they produce a new current origin ac, bc.
Similarly there is a current rotation Rc which is the sum of all
rotations in force and  current scaling factors Sxc, Syc which are
the products of all scaling factors in force. Then

            xa = Ac + Sxc.cos(Rc)*x - Syc.sin(Rc)*y
            ya = Bc + Sxc.sin(Rc)*x + Syc.cos(Rc)*y

The individual transforms are then

 begin Origin (x0, y0):   a   ->  xa (a + c*x0 + e*y0)
                          b   ->  ya (b + d*x0 + f*y0) 
 begin Translate xt, yt:  a   ->  a + c*xt + e*yt
                          b   ->  b + d*xt + f*yt
 begin Rotate Rt:         Rc  ->  Rc + Rt
 begin Scale xs, ys:      Sxc ->  Sxc*xs
                          Syc ->  Syc*ys   

When drawing on the screen with the mouse  we will also need
the inverse transformation to convert from absolute coordinates 
xa,ya to those  within the current transform block. We then use:

            x = a' + c'*xa + e'*ya
            y = b' + d'*xa + f'*ya
where
            a' = (af-be)/(de-cf)
            b' = (bc-ad)/(de-cf)
            c' =  -f/(de-cf)
            d' =   d/(de-cf)    
            e' =   e/(de-cf)
            f' =  -c/(de-cf)

Since de-fc = SxcSyc these implify to

            a' = (Ac.Cos(Rc) + Bc.sin(rc))/Sxc
            b' = (Bc.Cos(Rc0 - Ac.sin(Rc))/Syc
            c' = -cos(Rc)/Sxc
            d' = sin(Rc)/Syc
            e' = -sin(Rc)/Sxc
            f' = -cos(Rc)/Syc

The settings of origin, angle and scale factors
are held in an array of records covering all transforms currently
in force. Whenever a new transform block is entered or the 
current one exited the parameters a to f' are recalculated, so 
these are always valid for the current situation.
Special treatment will be needed for scaling text. - see later.

If we have an initial origin at screen pixel position xp, yp
and inital scaling of p pixels per cm. the initial conversions 
from cm to screen are:

    xs  = xp + p*x
    ys  = yp - p*y  (Remember y goes down!)

  