In this section we are going to look at the local coordinate origin within a Shape.
The Origin
In our earlier example of the Earth symbol we created a shape that drew a circle and some lines with respect to a local coordinate origin. The selection of this origin is the choice of the programmer. In the case of a circle it makes sense to have the origin at the centre. In the case of a rectangle the origin could be chosen to be the centroid, but it could just as well be a corner ( as with the system's Rectangle shape ).
In this section we are going to look into the choice of origin. We are going to continue with our theme of planetary symbols with a routine for Venus.
The symbol for Venus is a circle with a cross underneath. With the Earth symbol we took the centre of the circle as the origin, so we will do the same for Venus.
Shape VenusSymbol(Number radius) Number crossarms = radius / 2; Number shift = radius + crossarms; Begin Circle(radius); Line({-crossarms, shift}, {crossarms, shift}); Line({0, -crossarms + shift}, {0, crossarms + shift}); End;basic_origin_1.grs
In the Venus code we have created a variable to hold the length of the cross arms from the centre to the tips. We have used this length, together with the circle radius, to work out how far down to shift the cross. We add this to the y coordinate for all the points of the cross.
We end up adding the shift in four places. If we had a more complex shape we would have to add it even more places. This seems a lot of effort and is error prone. There must be a better way - and there is.
The 'With' Construct
Look at the following :-
With {0, shift} Do Line({-crossarms, 0}, {crossarms, 0}); Line({0, -crossarms }, {0, crossarms }); EndWith;
The 'With' is used to override the origin ( and, as we will see later, the other graphics context elements ). The shift in origin is always relative to the current origin. These constructs can be nested and the inner shift will be relative to the outer With shift rather than the original origin. Also if you include an explicit coordinate using the '=>' operator: the coordinates will be relative to the shifted origin. When the EndWith is encountered the origin reverts to its previous location.
The same construct can also be used when drawing two shapes at the same coordinates :-
[= the following =] EarthSymbol(20) => {100, 100}; VenusSymbol(30) => {100, 100}; [= can be replaced by =] With {100, 100} Do EarthSymbol(20); VenusSymbol(30); EndWith;
If this is not clear, try the following :-
Program( ) Begin Circle(10); Circle(12) => {0,50}; With {100, 100} Do Circle(10); Circle(12) => {0, 50}; EndWith; End;
The LineTo Routine
Given the centrality of the origin it will be quite common to have the starting point of a line at the origin. In this case the first parameter of the Line routine will be {0,0}. An alternative routine is the LineTo which automatically user the origin as the start point and only needs to be given the end point - so the following two statements will be equivalent ( assuming x and y exist ) :-
Line({0, 0}, {x, y}); LineTo({x, y});
Reusing Code
In producing the Earth and Venus symbols we ended up writing code to draw a cross in both routines. This is wasteful and is considered bad practice. So we are now going to write a reusable cross routine. Look at the following :-
Program( ) Begin EarthSymbol(20) => {100, 100}; VenusSymbol(20) => {150, 100}; End; Shape Cross(Number armlen) Begin Line({-armlen, 0}, {armlen, 0}); Line({0, -armlen}, {0, armlen}); End; Shape EarthSymbol(Number size) Begin Circle(size); Cross(size); End; Shape VenusSymbol(Number size) Number armlen = size / 2; Begin Circle(size); Cross(armlen) => {0, size + armlen}; End;basic_origin_2.grs
That it a lot better. There is just one more point I would like to make. In the target coordinates for the Cross in the VenusSymbol routine there are two terms "size" and "armlen". The "size" term comes from the location at which the cross is placed within the symbol and is conceptually part of that routine. The "armlen" is there to allow for the location of the origin within the Cross itself and is not really part of the definition if the symbol. Conceptually there is a cross hanging directly from the circle rather that a centred cross floating at distance 'armlen' below the circle.
It would make sense to define a HangingCross routine that takes the attachment point as its origin and work out for itself where its centre should be.
Shape HangingCross(Number armlen) Begin [= adjust the origin =] Cross(armlen) => {0, armlen}; End; Shape VenusSymbol(Number size) Number armlen = size / 2; Begin Circle(size); HangingCross(armlen) => {0, size}; End;basic_origin_3.grs
As you can see, the output is the same.
Exercise
As an exercise try to produce a symbol for Mars. Define an arrow routine that takes the end attached to the circle as its origin. It should take an angle and length as parameters.
There is a Sin(angle) and a Cos(angle) routine to help ( look in the function reference for details ). When drawing the head of the arrow it may be easier to use With to shift the working origin to the tip of the arrow and to use LineTo.
The expected output is shown below. My solution is in "Samples/basic_origin_ex.grs" - I have not added a link as that would make it too tempting to cheat.