Tutorial - Coordinate Origin

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 Output of basic_origin_1.grs basic_origin_1.png

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 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 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 Output of basic_origin_2.grs basic_origin_2.png

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 Output of basic_origin_3.grs basic_origin_3.png

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.

Output of basic_origin_ex.grs basic_origin_ex.png