-- File: PadGraphicsImpl.mesa
-- Last edited by Bier on December 18, 1982 1:29 am
-- Author: Eric Bier on August 6, 1982 12:30 pm
-- Contents: Simple 2d graphics package for the scratchpad

DIRECTORY
 Graphics,
 PadGraphics,
 Matrix3d,
 RealFns,
 SVDraw,
 SVPolygon2d,
 SVVector2d;

PadGraphicsImpl: PROGRAM
IMPORTS Graphics, RealFns, SVDraw, SVVector2d
EXPORTS PadGraphics =

BEGIN

Path: TYPE = SVPolygon2d.Path;
Point2d: TYPE = Matrix3d.Point2d;
Polygon: TYPE = SVPolygon2d.Polygon;
Vector2d: TYPE = SVVector2d.Vector2d;

squareSide: REAL ← 6.0;

ScreenToPad: PUBLIC PROC [screenPoint: Point2d, origin: Point2d, scalar: REAL ← 1] RETURNS [padPoint: Point2d] = {
-- origin is the pad origin in screen coordintates
 padPoint[1] ← screenPoint[1] - origin[1];
 padPoint[2] ← screenPoint[2] - origin[2];
 padPoint ← SVVector2d.Scale[padPoint, 1/scalar];
 };

PadToScreen: PUBLIC PROC [padPoint: Point2d, origin: Point2d, scalar: REAL ← 1] RETURNS [screenPoint: Point2d] = {
 padPoint ← SVVector2d.Scale[padPoint, scalar];
 screenPoint[1] ← padPoint[1] + origin[1];
 screenPoint[2] ← padPoint[2] + origin[2];
 };

MoveTo: PUBLIC PROC [dc: Graphics.Context, padPoint: Point2d, origin: Point2d] = {
 screenPoint: Point2d;
 screenPoint ← PadToScreen[padPoint, origin];
 Graphics.SetCP[dc, screenPoint[1], screenPoint[2]];
 };
DrawTo: PUBLIC PROC [dc: Graphics.Context, padPoint: Point2d, origin: Point2d] = {
 screenPoint: Point2d;
 screenPoint ← PadToScreen[padPoint, origin];
 Graphics.DrawTo[dc, screenPoint[1], screenPoint[2]];
 };
MirrorMoveTo: PUBLIC PROC [dc: Graphics.Context, padPoint: Point2d, origin: Point2d] = {
 screenPoint: Point2d;
 padPoint[1] ← -padPoint[1];
 screenPoint ← PadToScreen[padPoint, origin];
 Graphics.SetCP[dc, screenPoint[1], screenPoint[2]];
 };
MirrorDrawTo: PUBLIC PROC [dc: Graphics.Context, padPoint: Point2d, origin: Point2d] = {
 screenPoint: Point2d;
 padPoint[1] ← -padPoint[1];
 screenPoint ← PadToScreen[padPoint, origin];
 Graphics.DrawTo[dc, screenPoint[1], screenPoint[2]];
 };

DrawPathNeighborHood: PUBLIC PROC [dc: Graphics.Context, path: Path, index: NAT, origin: Point2d] = {
-- draw three points and 2 edges corresponding to the neighborhood of path[index];
-- path is in pad coordinates.
-- if index is one of the two ends of the path then draw 2 points and 1 edge.
 DrawSquare[dc, squareSide, path[index], origin];
IF index>0 THEN {
  MoveTo[dc, path[index-1], origin];
  DrawTo[dc, path[index], origin];
  };
IF index<path.len-1 THEN {
  MoveTo[dc, path[index+1], origin];
  DrawTo[dc, path[index], origin];
  };
 };

MirrorDrawPathNeighborHood: PUBLIC PROC [dc: Graphics.Context, path: Path, index: NAT, origin: Point2d] = {
-- draw three points and 2 edges corresponding to the neighborhood of path[index];
-- path is in pad coordinates.
-- if index is one of the two ends of the path then draw 2 points and 1 edge.
 MirrorDrawSquare[dc, squareSide, path[index], origin];
IF index>0 THEN {
  MirrorMoveTo[dc, path[index-1], origin];
  MirrorDrawTo[dc, path[index], origin];
  };
IF index<path.len-1 THEN {
  MirrorMoveTo[dc, path[index+1], origin];
  MirrorDrawTo[dc, path[index], origin];
  };
 };

DrawPolyNeighborHood: PUBLIC PROC [dc: Graphics.Context, poly: Polygon, index: NAT, origin: Point2d] = {
 -- draw three points and 2 edges corresponding to the neighborhood of poly[index];
-- poly is in pad coordinates.
 -- poly.len = 2 then draw both points and the edge joining them.
 -- if
poly.len = 1 then just draw a point.
 indexPlusOne, indexMinusOne: NAT;
 indexPlusOne ← IF index = poly.len-1 THEN 0 ELSE index + 1;
 indexMinusOne ← IF index = 0 THEN poly.len-1 ELSE index - 1;
 DrawSquare[dc, squareSide, poly[index], origin];
 IF poly.len = 1 THEN RETURN;
 MoveTo[dc, poly[indexPlusOne], origin];
 DrawTo[dc, poly[index], origin];
 IF poly.len = 2 THEN RETURN;
 MoveTo[dc, poly[indexMinusOne], origin];
 DrawTo[dc, poly[index], origin];
 };

MirrorDrawPolyNeighborHood: PUBLIC PROC [dc: Graphics.Context, poly: Polygon, index: NAT, origin: Point2d] = {
 -- draw three points and 2 edges corresponding to the neighborhood of poly[index];
-- poly is in pad coordinates.
 -- poly.len = 2 then draw both points and the edge joining them.
 -- if
poly.len = 1 then just draw a point.
 indexPlusOne, indexMinusOne: NAT;
 indexPlusOne ← IF index = poly.len-1 THEN 0 ELSE index + 1;
 indexMinusOne ← IF index = 0 THEN poly.len-1 ELSE index - 1;
 MirrorDrawSquare[dc, squareSide, poly[index], origin];
 IF poly.len = 1 THEN RETURN;
 MirrorMoveTo[dc, poly[indexPlusOne], origin];
 MirrorDrawTo[dc, poly[index], origin];
 IF poly.len = 2 THEN RETURN;
 MirrorMoveTo[dc, poly[indexMinusOne], origin];
 MirrorDrawTo[dc, poly[index], origin];
 };

DrawPath: PUBLIC PROC [dc: Graphics.Context, path: Path, origin: Point2d, scalar: REAL ← 1] = {
-- poly is in Pad coordinates
-- transform each point to screen coordinates and draw using Graphics.
 outline: Graphics.Path ← Graphics.NewPath[path.len];
 screenPoint: Point2d;
IF path.len <= 1 THEN RETURN;
 screenPoint ← PadToScreen[path[0], origin, scalar];
 Graphics.MoveTo[outline, screenPoint[1], screenPoint[2]];
FOR i: NAT IN [1..path.len) DO
  screenPoint ← PadToScreen[path[i], origin, scalar];
  Graphics.LineTo[outline, screenPoint[1], screenPoint[2]];
ENDLOOP;
 Graphics.DrawStroke[dc, outline, 2, FALSE, round];
 };

DrawPolygon: PUBLIC PROC [dc: Graphics.Context, poly: Polygon, origin: Point2d, scalar: REAL ← 1] = {
-- poly is in Pad coordinates
-- transform each point to screen coordinates and draw using Graphics.
 screenPoint0, screenPoint1, screenPoint2: Point2d;
IF poly.len <= 1 THEN RETURN;
 screenPoint0 ← screenPoint1 ← PadToScreen[poly[0], origin, scalar];
FOR i: NAT IN [1..poly.len) DO
  screenPoint2 ← PadToScreen[poly[i], origin, scalar];
  SVDraw.LineSandwich[dc, screenPoint1[1], screenPoint1[2], screenPoint2[1], screenPoint2[2]];
  screenPoint1 ← screenPoint2;
ENDLOOP;
 SVDraw.LineSandwich[dc, screenPoint1[1], screenPoint1[2], screenPoint0[1], screenPoint0[2]];
 };

CrossHairs: PUBLIC PROC [dc: Graphics.Context, origin: Point2d] = {
 leftPoint, rightPoint, topPoint, bottomPoint, screenPoint: Point2d;
 leftPoint ← [-1000, 0];
 rightPoint ← [1000, 0];
 topPoint ← [0, 1000];
 bottomPoint ← [0, -1000];
 screenPoint ← PadToScreen[leftPoint, origin];
 Graphics.SetCP[dc, screenPoint[1], screenPoint[2]];
 screenPoint ← PadToScreen[rightPoint, origin];
 Graphics.DrawTo[dc, screenPoint[1], screenPoint[2]];
 screenPoint ← PadToScreen[topPoint, origin];
 Graphics.SetCP[dc, screenPoint[1], screenPoint[2]];
 screenPoint ← PadToScreen[bottomPoint, origin];
 Graphics.DrawTo[dc, screenPoint[1], screenPoint[2]];
 };

DrawSquare: PUBLIC PROC [dc: Graphics.Context, side: REAL, center: Point2d, origin: Point2d] = {
 delta: REAL ← side/2.0;
 screenPoint: Point2d;
 square: Graphics.Path ← Graphics.NewPath[4];
 screenPoint ← PadToScreen[[center[1]-delta, center[2]-delta], origin];
 Graphics.MoveTo[square, screenPoint[1], screenPoint[2]];
 screenPoint ← PadToScreen[[center[1]-delta, center[2]+delta], origin];
 Graphics.LineTo[square, screenPoint[1], screenPoint[2]];
 screenPoint ← PadToScreen[[center[1]+delta, center[2]+delta], origin];
 Graphics.LineTo[square, screenPoint[1], screenPoint[2]];
 screenPoint ← PadToScreen[[center[1]+delta, center[2]-delta], origin];
 Graphics.LineTo[square, screenPoint[1], screenPoint[2]];
 Graphics.DrawStroke[dc, square, 1, TRUE];
 };

MirrorDrawSquare: PUBLIC PROC [dc: Graphics.Context, side: REAL, center: Point2d, origin: Point2d] = {
 delta: REAL ← side/2.0;
 screenPoint: Point2d;
 square: Graphics.Path ← Graphics.NewPath[4];
 center[1] ← -center[1];
 screenPoint ← PadToScreen[[center[1]-delta, center[2]-delta], origin];
 Graphics.MoveTo[square, screenPoint[1], screenPoint[2]];
 screenPoint ← PadToScreen[[center[1]-delta, center[2]+delta], origin];
 Graphics.LineTo[square, screenPoint[1], screenPoint[2]];
 screenPoint ← PadToScreen[[center[1]+delta, center[2]+delta], origin];
 Graphics.LineTo[square, screenPoint[1], screenPoint[2]];
 screenPoint ← PadToScreen[[center[1]+delta, center[2]-delta], origin];
 Graphics.LineTo[square, screenPoint[1], screenPoint[2]];
 Graphics.DrawStroke[dc, square, 1, TRUE];
 };

Draw2dVector: PUBLIC PROC [dc: Graphics.Context, vec: Vector2d, at: Point2d, origin: Point2d] = {
 unitVec, vec50: Vector2d;
 mag: REAL;
 mag ← RealFns.SqRt[vec[1]*vec[1] + vec[2]*vec[2]];
 unitVec[1] ← vec[1]/mag;
 unitVec[2] ← vec[2]/mag;
 vec50[1] ← unitVec[1]*50;
 vec50[2] ← unitVec[2]*50;
 MoveTo[dc, at, origin];
 DrawTo[dc, [at[1]+vec50[1], at[2]+vec50[2]], origin];
 }; -- end of Draw2dVector

END.