/* VEC.P */
section $-vec => consvec vec_x vec_y
vec_sub --
vec_add ++
vec_smult ***
turn2 turn3;
/*
SPECIFICATION
-------------
This module exports some routines for handling vectors.
PUBLIC consvec( x, y ):
Builds the vector (x,y).
PUBLIC vec_x( v ):
PUBLIC vec_y( v ):
Return v's x and y components.
PUBLIC vec_add( v1, v2 ):
PUBLIC vec_sub( v1, v2 ):
PUBLIC 5 v1 ++ v2:
PUBLIC 5 v1 -- v2:
Return vectors corresponding to v1+v2 and v1-v2. You can also use the
infix operators ++ and -- as synonyms.
PUBLIC vec_smult( s, v ):
PUBLIC 4 s *** v:
Returns the vector sv, where s is a scalar.
PUBLIC turn2( v1, v2 ):
Indicates how v1 must be rotated to get v2. It returns a word
which is
"forward" if v1 and v2 are colinear and point in the same
direction;
"back" if colinear and in opposite directions;
"right" if v2 is the result of rotating v1 to the right;
"left" if v2 is the result of rotating v1 to the left.
This routine is useful when calculating how a bug must turn, given
an existing bearing (forwardvector) v1 and a new one v2.
PUBLIC turn3( p0, p1, p2 ):
This is equivalent to
turn2( p1--p0, p2--p1 )
This routine is useful when calculating how a bug must turn, given
three successive points p0, p1, p2 on its path.
*/
/*
IMPLEMENTATION
--------------
I have implemented vectors as lists so that they can be passed to
Prolog. Many bug-writers will use that, and I didn't want to proliferate
representations. However, having explicit constructors and selectors
makes it clearer that these lists are in fact vectors.
This library comes with the file HELP VEC. Please keep the two in step.
HELP PATH_TO_MOVES also refers to vectors.
What's the overhead of returning intermediate results, e.g. in
v1++v2--v3? Is there an efficient way to avoid this while keeping to the
same syntax? Also, can it be arranged for + and - and other familiar
operators to work on vectors as well. Also also, is there an efficient,
public-domain, library of vector and matrix operations? Answer if you
can.
*/
global vars consvec = (cons <> cons)(% [] %);
/* Nifty functional, but obscure, way of building a constructor
for [%x,y%].
*/
global vars vec_x = hd;
global vars vec_y = (tl<>hd);
define global 5 V1 -- V2;
consvec( V1.vec_x-V2.vec_x, V1.vec_y-V2.vec_y );
enddefine;
global vars procedure vec_sub = (nonop --);
define global 5 V1 ++ V2;
consvec( V1.vec_x+V2.vec_x, V1.vec_y+V2.vec_y );
enddefine;
global vars procedure vec_add = (nonop ++);
define global 4 S *** V;
consvec( S*V.vec_x, S*V.vec_y );
enddefine;
global vars procedure vec_smult = (nonop ***);
/* cross( V1, V2 ):
Gives the length of the "cross-product" of 2-D vectors.
Useful properties of cross are:
A x A = 0;
A x nA = 0;
A x nB = nA x B;
A x B > 0 if B is the result of rotating A left,
with an angle >= 0 and <= 180;
A x B < 0 if B is the result of rotating A right,
with an angle >= 0 and <= 180.
The last two are why we use it for calculating bug paths.
*/
define cross( V1, V2 );
V1.vec_x*V2.vec_y - V2.vec_x*V1.vec_y;
enddefine;
vars turn2, turn_1; /*forward*/
define global turn3( P0, P1, P2 );
turn2( vec_sub( P1, P0 ), vec_sub( P2, P1 ) );
enddefine;
define global turn2( A, B );
turn_1( cross(A,B), A, B );
enddefine;
/* turn_1( AxB, A, B ):
AxB is cross(A,B).
'turn_1' returns:
"forward" if A and B are colinear and point in the same
direction;
"back" if colinear and in opposite directions;
"right" if B is the result of rotating A to the right;
"left" if B is the result of rotating A to the left.
*/
define turn_1( AxB, A, B );
lvars AxB, A, B;
if AxB = 0 and
sign(A.vec_x) = sign(B.vec_x) and
sign(A.vec_y) = sign(B.vec_y) then
"forward"
/* We needed a test on both components because one might be
zero.
*/
elseif AxB = 0 then
"back"
elseif AxB < 0 then
"right"
else
"left"
endif;
enddefine;
endsection;