-- File: SVDrawImpl.mesa
-- Last edited by Bier on December 18, 1982 1:32 am
-- Author: Eric Bier on October 19, 1982 3:10 pm
-- Contents: Some convenience functions to draw shapes not directly available in Cedar Graphics
DIRECTORY
Graphics,
GraphicsColor,
Real,
RealFns,
SV2d,
SVDraw,
SVLines2d,
SVVector2d;
SVDrawImpl: PROGRAM
IMPORTS Graphics, Real, RealFns, SVLines2d, SVVector2d
EXPORTS SVDraw =
BEGIN
Point2d: TYPE = SV2d.Point2d;
globalSandwich: Graphics.Path ← Graphics.NewPath[2];
Circle: PUBLIC PROC [dc: Graphics.Context, originX, originY, radius: REAL] = {
-- draw 1/8 of a circle carefully and get the rest by mirroring around vertical, horizontal, and 45 degree axes.
-- as an exercise in flatness tests, I declared that a straight line adequately approximates an arc of the circle if the maximum deviation of the line from the circle is less than z pixel heights. Given a circle of radius r (r in pixel heights), an arc of angle theta, starts and ends on a line of distance r*cos(theta/2) from the origin, reaches a maximum distance r-r*cos(theta/2) from this line and returns. Hence, we must chose theta so that r*(1-cos(theta/2) < z. Theta is clearly less than 90 degrees so 0 < cos(theta/2) < 1, hence 0 < (1 - cos(theta/2)) < 1. Also, r >0, so
-- (1-cos(theta/2) < z/r. -cos(theta/2) < z/r - 1. cos(theta/2) > (1 - z/r). Theta/2 < ArcCos[1 - z/r]. Theta < 2*ArcCos[1 - z/r]. Let z = 0.5.
cosine, sine, rcosine, rsine, lastRsine, lastRcosine: REAL;
z: REAL = 0.5;
deltaTheta, theta, lastTheta: REAL;
numberOfTimesGoesIn: NAT;
cosine ← 1-z/radius;
sine ← RealFns.SqRt[1-cosine*cosine];
deltaTheta ← RealFns.ArcTanDeg[sine, cosine];
-- Now find a deltaTheta smaller than this which goes evenly into 45 degrees.
numberOfTimesGoesIn ← Real.FixC[45.0/deltaTheta];
deltaTheta ← 45.0/(numberOfTimesGoesIn + 1);
lastTheta ← 0;
lastRsine ← 0;
lastRcosine ← radius;
FOR i: NAT IN [1..numberOfTimesGoesIn+1] DO
theta ← lastTheta + deltaTheta;
rsine ← radius*RealFns.SinDeg[theta];
rcosine ← radius*RealFns.CosDeg[theta];
DrawLine[dc, originX + lastRcosine, originY + lastRsine,
originX + rcosine, originY + rsine];
DrawLine[dc, originX + lastRsine, originY + lastRcosine,
originX + rsine, originY + rcosine];
DrawLine[dc, originX + lastRcosine, originY - lastRsine,
originX + rcosine, originY - rsine];
DrawLine[dc, originX + lastRsine, originY - lastRcosine,
originX + rsine, originY - rcosine];
DrawLine[dc, originX - lastRcosine, originY + lastRsine,
originX - rcosine, originY + rsine];
DrawLine[dc, originX - lastRsine, originY + lastRcosine,
originX - rsine, originY + rcosine];
DrawLine[dc, originX - lastRcosine, originY - lastRsine,
originX - rcosine, originY - rsine];
DrawLine[dc, originX - lastRsine, originY - lastRcosine,
originX - rsine, originY - rcosine];
lastTheta ← theta;
lastRsine ← rsine;
lastRcosine ← rcosine;
ENDLOOP;
}; -- end of Circle
DrawLine: PROC [dc: Graphics.Context, fromX, fromY, toX, toY: REAL] = {
Graphics.SetCP[dc, fromX, fromY];
Graphics.DrawTo[dc, toX, toY];
};
Cross: PUBLIC PROC [dc: Graphics.Context, originX, originY, length: REAL] = {
halfLength: REAL ← length/2.0;
DrawLine[dc, originX-halfLength, originY, originX+halfLength, originY];
DrawLine[dc, originX, originY-halfLength, originX, originY+halfLength];
};
centerLine: SV2d.TrigLine ← SVLines2d.CreateEmptyTrigLine[];
LineSandwich: PUBLIC PROC [dc: Graphics.Context, fromX, fromY, toX, toY: REAL] = {
leftFirst, leftSecond, rightFirst, rightSecond: Point2d;
-- Draw three parallel lines, 2 black with 1 white in between so that the total width is three screen dots. This requires that we calculate the points with are 1 unit from the given central line segment and normal to that line segment.
-- find the left and right segments given the center segment
SVLines2d.FillTrigLineFromPoints[[fromX, fromY], [toX, toY], centerLine];
leftFirst ← SVLines2d.PointLeftOfTrigLine[1, [fromX, fromY], centerLine];
leftSecond ← SVLines2d.PointLeftOfTrigLine[1, [toX, toY], centerLine];
rightFirst ← SVVector2d.Sum[SVVector2d.Difference[[fromX, fromY], leftFirst], [fromX, fromY]];
rightSecond ← SVVector2d.Sum[SVVector2d.Difference[[toX, toY], leftSecond], [toX, toY]];
-- now draw the left line
Graphics.SetColor[dc, GraphicsColor.black]; -- black
Graphics.MoveTo[globalSandwich, leftFirst[1], leftFirst[2]];
Graphics.LineTo[globalSandwich, leftSecond[1], leftSecond[2]];
Graphics.DrawStroke[dc, globalSandwich, 1, FALSE, butt];
-- now draw the center line
Graphics.SetColor[dc, GraphicsColor.white]; -- white
Graphics.MoveTo[globalSandwich, fromX, fromY];
Graphics.LineTo[globalSandwich, toX, toY];
Graphics.DrawStroke[dc, globalSandwich, 1, FALSE, butt];
-- finally, draw the right line
Graphics.SetColor[dc, GraphicsColor.black]; -- dark gray
Graphics.MoveTo[globalSandwich, rightFirst[1], rightFirst[2]];
Graphics.LineTo[globalSandwich, rightSecond[1], rightSecond[2]];
Graphics.DrawStroke[dc, globalSandwich, 1, FALSE, butt];
};
END.