/* VEC.PL */
:- module vec.
:- public vec_sub/3, vec_add/3, vec_smult/3, turn3/4, turn2/3.
/*
SPECIFICATION
-------------
This module exports some predicates for handling vectors. It is
an alternative to VEC.P, and equivalent thereto in function.
Vectors are represented as two-element lists, [X,Y].
PUBLIC vec_add( V1+, V2+, V- ):
PUBLIC vec_sub( V1+, V2+, V- ):
Return vectors corresponding to V1+V2 and V1-V2.
PUBLIC vec_smult( S+, V+, SV- ):
Returns the vector SV, where S is a scalar.
PUBLIC turn2( V1+, V2+, T- ):
Indicates how V1 must be rotated to get V2. It returns an atom in T
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 predicate 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+, T- ):
This is equivalent to
turn2( , , T )
This predicate is useful when calculating how a bug must turn, given
three successive points P0, P1, P2 on its path.
*/
/*
IMPLEMENTATION
--------------
This is straightforward. Using binary structures would be more efficient
than lists, but the latter interface better with Pop-11.
There is a HELP file for this module, HELP VEC. Keep the two in step.
*/
vec_sub( [X1,Y1], [X2,Y2], [X,Y] ) :-
X is X1-X2,
Y is Y1-Y2.
vec_add( [X1,Y1], [X2,Y2], [X,Y] ) :-
X is X2+X1,
Y is Y2+Y1.
vec_smult( S, [X1,Y1], [X,Y] ) :-
X is X1*S,
Y is Y1*S.
/* cross( V1+, V2+, V1xV2- ):
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.
*/
cross( [X1,Y1], [X2,Y2], C ) :-
C is X1*Y2 - X2*Y1.
turn3( P0, P1, P2, D ) :-
vec_sub( P1, P0, A ),
vec_sub( P2, P1, B ),
turn_1( A, B, D ).
turn2( A, B, D ) :-
cross( A, B, C ),
turn_1( C, A, B, D ).
/* turn_1( AxB+, A+, B+, T- ):
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.
*/
turn_1( 0, [Ax,Ay], [Bx,By], forward ) :-
sign(Ax) =:= sign(Bx),
sign(Ay) =:= sign(By),
!.
/* We needed a test on both components because one might be
zero.
*/
turn_1( 0, _, _, back ) :- !.
turn_1( C, A, B, right ) :-
C < 0,
!.
turn_1( _, _, _, left ).
:- endmodule.