-- Copyright (C) 1983  by Xerox Corporation. All rights reserved. 
-- IVectorImpl.mesa - last edited by
-- Poskanzer	23-May-83 20:23:59

DIRECTORY
  IVector USING [Angle, IPoint, IVec],
  Real USING [RoundI],
  RealFns USING [ArcTanDeg, SqRt];

IVectorImpl: PROGRAM IMPORTS Real, RealFns EXPORTS IVector =
  BEGIN

  IVec: TYPE = IVector.IVec;
  IPoint: TYPE = IVector.IPoint;
  Angle: TYPE = IVector.Angle;

  ArcTan: PUBLIC PROCEDURE [y, x: INTEGER] RETURNS [Angle] =
    BEGIN RETURN[RealFns.ArcTanDeg[y, x]]; END;
    
  Sqrt: PUBLIC PROCEDURE [i: LONG INTEGER] RETURNS [INTEGER] =
    BEGIN RETURN[Real.RoundI[RealFns.SqRt[i]]]; END;

  Sign: PUBLIC PROCEDURE [a: INTEGER] RETURNS [INTEGER] =
    BEGIN RETURN[IF a < 0 THEN -1 ELSE 1]; END;

  Go: PUBLIC PROCEDURE [from, to: IPoint] RETURNS [IVec] =
    BEGIN RETURN[[to.x - from.x, to.y - from.y]]; END;

  Add: PUBLIC PROCEDURE [v1, v2: IVec] RETURNS [IVec] =
    BEGIN RETURN[[v1.x + v2.x, v1.y + v2.y]]; END;

  Dot: PUBLIC PROCEDURE [v1, v2: IVec] RETURNS [LONG INTEGER] =
    BEGIN RETURN[LONG[v1.x]*LONG[v2.x] + LONG[v1.y]*LONG[v2.y]]; END;

  Sub: PUBLIC PROCEDURE [v1, v2: IVec] RETURNS [IVec] =
    BEGIN RETURN[[v1.x - v2.x, v1.y - v2.y]]; END;
  
  -- angle stuff
  
  AngleOf: PUBLIC PROCEDURE [v: IVec] RETURNS [REAL] =
    BEGIN RETURN[ArcTan[v.y, v.x]]; END;

  -- comarision stuff

  BetweenVector: PUBLIC PROCEDURE [cw, ccw, test: IVec] RETURNS [BOOLEAN] =
    BEGIN
    leftOfCw: BOOLEAN = LeftOf[ref: cw, test: test];
    rightOfCcw: BOOLEAN = RightOf[ref: ccw, test: test];
    RETURN[
      SELECT Det[cw: cw, ccw: ccw] FROM
        > 0 => leftOfCw AND rightOfCcw,
        < 0 => leftOfCw OR rightOfCcw,
        ENDCASE => (Dot[cw, ccw] < 0 OR Dot[cw, test] > 0) AND leftOfCw
          AND rightOfCcw];
    END;

  LeftOf: PUBLIC PROCEDURE [ref, test: IVec] RETURNS [BOOLEAN] =
    BEGIN RETURN[Det[cw: ref, ccw: test] >= 0]; END;

  RightOf: PUBLIC PROCEDURE [ref, test: IVec] RETURNS [BOOLEAN] =
    BEGIN RETURN[Det[ccw: ref, cw: test] >= 0]; END;

  SameDirection: PUBLIC PROCEDURE [v1, v2: IVec] RETURNS [BOOLEAN] =
    BEGIN RETURN[Dot[v1, v2] >= 0]; END;

  RLength: PROCEDURE [v: IVec] RETURNS [REAL] =
    BEGIN RETURN[RealFns.SqRt[LenSqrd[v]]]; END;
  Length: PUBLIC PROCEDURE [v: IVec] RETURNS [INTEGER] =
    BEGIN RETURN[Real.RoundI[RLength[v]]]; END;

  LenSqrd: PUBLIC PROCEDURE [v: IVec] RETURNS [LONG INTEGER] =
    BEGIN RETURN[LONG[v.x]*LONG[v.x] + LONG[v.y]*LONG[v.y]]; END;

  Scale: PUBLIC PROCEDURE [v: IVec, scale: REAL] RETURNS [IVec] =
    BEGIN RETURN[[Real.RoundI[v.x*scale], Real.RoundI[v.y*scale]]]; END;

  VectorOfLength: PUBLIC PROCEDURE [v: IVec, l: INTEGER] RETURNS [IVec] =
    BEGIN
    RETURN[Scale[v, REAL[l]/RLength[v]]];
    END;

  Minus: PUBLIC PROCEDURE [v: IVec] RETURNS [IVec] =
    BEGIN RETURN[[-v.x, -v.y]]; END;

  Rotate90CCW: PUBLIC PROCEDURE [v: IVec] RETURNS [IVec] =
    BEGIN RETURN[[-v.y, v.x]]; END;

  Rotate90CW: PUBLIC PROCEDURE [v: IVec] RETURNS [IVec] =
    BEGIN RETURN[[v.y, -v.x]]; END;

  AddPoint: PUBLIC PROCEDURE [p: IPoint, v: IVec] RETURNS [IPoint] =
    BEGIN RETURN[[p.x + v.x, p.y + v.y]]; END;

  SubPoint: PUBLIC PROCEDURE [p: IPoint, v: IVec] RETURNS [IPoint] =
    BEGIN RETURN[[p.x - v.x, p.y - v.y]]; END;

  Between: PUBLIC PROCEDURE [cw, ccw, test: IVec] RETURNS [BOOLEAN] =
    BEGIN
    RETURN[(Dot[test, Rotate90CCW[cw]] > 0) = (Dot[test, Rotate90CW[ccw]] > 0)];
    END;

  Combine: PUBLIC PROCEDURE [v1: IVec, s1: INTEGER, v2: IVec, s2: INTEGER]
    RETURNS [IVec] = BEGIN RETURN[[s1*v1.x + s2*v2.x, s1*v1.y + s2*v2.y]]; END;

  Det: PUBLIC PROCEDURE [cw, ccw: IVec] RETURNS [LONG INTEGER] =
    BEGIN RETURN[Dot[cw, Rotate90CW[ccw]]]; END;

  Colinear: PUBLIC PROCEDURE [p1, p2, p3: IPoint] RETURNS [BOOLEAN] =
    BEGIN
    IF p1 = p2 OR p2 = p3 OR p3 = p1 THEN RETURN[TRUE]
    ELSE RETURN[Parallel[Go[p1, p2], Go[p2, p3]]];
    END;

  Parallel: PUBLIC PROCEDURE [v1, v2: IVec] RETURNS [BOOLEAN] =
    BEGIN RETURN[v1.x*v2.y = v2.x*v1.y]; END;

  -- Coeffs: computes c1 and c2 such that v = c1*basis1 + c2*basis2.
  Coeffs: PUBLIC PROCEDURE [basis1, basis2, v: IVec] RETURNS [c1, c2: REAL] =
    BEGIN
    det: REAL = Det[basis1, basis2];
    c1 ← REAL[Det[v, basis2]]/det;
    c2 ← REAL[Det[basis1, v]]/det;
    END;

  -- Intersect: finds the intersection point of two rooted vectors.
  Intersect: PUBLIC PROCEDURE [v1: IVec, p1: IPoint, v2: IVec, p2: IPoint]
    RETURNS [p: IPoint] =
    BEGIN
    v: IVec;
    c1, c2: REAL;
    IF Parallel[v1, v2] THEN
      RETURN[p1]  -- p1 is as good as any other point
    ELSE
      BEGIN
      v ←  Go[p1, p2];
      [c1, c2] ← Coeffs[basis1: v1, basis2: v2, v: v];
      RETURN[AddPoint[p1, Scale[v1, c1]]];
      END;
    END;

  END.