/* 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;