/*  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( <P1-P0>, <P2-P1>, 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.
