Draw2dImpl.mesa
Copyright Ó 1985, 1989, 1992 by Xerox Corporation. All rights reserved.
Pier, November 22, 1989 12:43:30 pm PST
Bloomenthal, July 13, 1992 2:17 pm PDT
Doug Wyatt, April 9, 1992 2:36 pm PDT
DIRECTORY Draw2d, Imager, ImagerBackdoor, ImagerDevice, ImagerDeviceWorks, ImagerFont, ImagerInterpress, ImagerPath, ImagerRaster, ImagerState, ImagerTransformation, Prop, Real, RealFns, Rope, SF, Vector2, ViewerClasses;
Type Declarations
ROPE: TYPE ~ Rope.ROPE;
VEC: TYPE ~ Imager.VEC; -- RECORD [x, y: REAL]
Context: TYPE ~ Imager.Context;
Viewer: TYPE ~ ViewerClasses.Viewer;
DrawProc: TYPE ~ Draw2d.DrawProc;
DrawType: TYPE ~ Draw2d.DrawType;
IntegerBox: TYPE ~ Draw2d.IntegerBox;
MarkType: TYPE ~ Draw2d.MarkType;
PixelProc: TYPE ~ Draw2d.PixelProc;
Zip: TYPE ~ Draw2d.Zip;
ZipRep: TYPE ~ Draw2d.ZipRep;
General Utility Procedures
BoxFromContext:
PUBLIC
PROC [context: Imager.Context]
RETURNS [IntegerBox] ~ {
bounds: Imager.Rectangle ¬ ImagerBackdoor.GetBounds[context];
x: ImagerTransformation.Transformation ¬
ImagerBackdoor.GetTransformation[context, client, surface];
r: Imager.Rectangle ¬ ImagerTransformation.TransformRectangle[x, bounds];
ImagerTransformation.Destroy[x];
RETURN[[Real.Round[r.x], Real.Round[r.y], Real.Round[r.w], Real.Round[r.h]]];
};
ScreenScale:
PUBLIC
PROC [viewer: Viewer, aspectRatio:
REAL ¬ 4.0/3.0]
RETURNS [
REAL] ~ {
RETURN[MIN[viewer.cw, viewer.ch*aspectRatio]];
};
Clear:
PUBLIC
PROC [context: Context, color: Imager.Color ¬ Imager.white] ~ {
old: Imager.Color ¬ ImagerBackdoor.GetColor[context ! Imager.Error => GOTO Bad];
Imager.SetColor[context, color];
Imager.MaskRectangle[context, ImagerBackdoor.GetBounds[context !
Imager.Error => GOTO Bad]];
Imager.SetColor[context, old];
EXITS Bad => NULL;
};
Label:
PUBLIC
PROC [context: Context, position:
VEC, rope:
ROPE] ~ {
IF rope = NIL THEN RETURN;
AssureFont[context];
Imager.SetXY[context, position];
Imager.ShowRope[context, rope];
};
AssureFont:
PUBLIC PROC [context: Context] ~ {
font: Imager.Font ¬ NIL;
font ¬ ImagerBackdoor.GetFont[context ! Imager.Error => CONTINUE];
IF font = NIL THEN Imager.SetFont[context, ImagerFont.Find["xerox/tiogafonts/helvetica10"]];
};
DoWithBuffer:
PUBLIC
PROC [context: Context, action:
PROC, clear:
BOOL ¬
TRUE] ~ {
zip: Zip ¬ GetZip[context];
noRect: BOOL ¬ FALSE;
rect: Imager.Rectangle;
rect ¬ ImagerBackdoor.GetBounds[context ! Imager.Error => {noRect ¬ TRUE; CONTINUE}];
context.propList ¬ Prop.Put[context.propList, $Zip, zip];
IF noRect
THEN action[]
ELSE Imager.DoWithBuffer[context, action, 0, 0, Real.Round[rect.w], Real.Round[rect.h], IF clear THEN Imager.white ELSE NIL];
context.propList ¬ Prop.Rem[context.propList, $Zip];
ReleaseZip[zip];
};
IPOut:
PUBLIC
PROC [fileName:
ROPE, drawProc: DrawProc, clientData:
REF
ANY ¬
NIL] ~ {
IF fileName #
NIL
THEN {
ref: ImagerInterpress.Ref ¬ ImagerInterpress.Create[fileName];
ContextProc:
PROC [context: Context] ~ {
Imager.ScaleT[context, Imager.metersPerPoint];
Imager.SetStrokeWidth[context, 1.0];
Imager.SetStrokeEnd[context, round];
AssureFont[context];
Imager.TranslateT[context, [0.0, 0.5*11.0*Imager.pointsPerInch]];
drawProc[context, clientData, $IPOut, NIL];
};
ImagerInterpress.DoPage[ref, ContextProc];
ImagerInterpress.Close[ref];
};
};
Line Drawing Procedures
ContextOk:
PROC [context: Context]
RETURNS [
BOOL] ~
INLINE {
Dealing with context.state is awkward, below, because Imager defines it as an opaque type.
An alternative is to access context.state directly (see ImagerRasterImpl):
State: TYPE ~ ImagerState.State;
StateRep: PUBLIC TYPE ~ ImagerState.StateRep; -- export to Imager.StateRep
IF context.state.rasterData . . .
However, this would require Draw2dImpl to EXPORT ImagerState, which is undesirable.
state: REF ¬ context.state; -- sidestep the Compiler
WITH state
SELECT
FROM
st: REF ImagerState.StateRep => IF st.rasterData = NIL THEN RETURN[FALSE];
ENDCASE => RETURN[FALSE];
RETURN[ImagerBackdoor.GetReal[context, strokeWidth ! Imager.Error => GOTO Bad] = 0];
EXITS Bad => RETURN[FALSE];
};
GetZip:
PUBLIC
PROC [context: Context, zip: Zip ¬
NIL]
RETURNS [Zip] ~ {
box: SF.Box;
IF NOT ContextOk[context] THEN RETURN[NIL];
box ¬ ImagerRaster.GetClipper[context].clipBox;
IF SF.Empty[box] THEN RETURN[NIL];
IF zip = NIL THEN zip ¬ NEW[ZipRep];
zip.box ¬ [[box.min.s+1, box.min.f+1], [box.max.s-1, box.max.f-1]];
zip.transform ¬ ImagerBackdoor.GetTransformation[context, client, device];
zip.rasterDevice ¬ ImagerRaster.GetDevice[context] # NIL;
RETURN[zip];
};
ReleaseZip:
PUBLIC
PROC [zip: Zip] ~ {
IF zip # NIL AND zip.transform # NIL THEN ImagerTransformation.Destroy[zip.transform];
};
Line:
PUBLIC
PROC [
context: Context,
vec0, vec1: VEC,
drawType: DrawType ¬ solid,
zip: Zip ¬ NIL]
~ {
IF zip = NIL THEN zip ¬ NARROW[Prop.Get[context.propList, $Zip]];
SELECT drawType
FROM
solid => Solid[context, vec0, vec1, zip];
dashed => Dash[context, vec0, vec1, zip];
dotted => Dot[context, vec0, vec1];
ENDCASE => NULL;
};
DoWithLine:
PUBLIC PROC [vec0, vec1:
VEC, pixelProc: PixelProc] ~ {
x0: INTEGER ¬ Real.Round[vec0.x];
y0: INTEGER ¬ Real.Round[vec0.y];
x1: INTEGER ¬ Real.Round[vec1.x];
y1: INTEGER ¬ Real.Round[vec1.y];
dyPos: BOOL ~ y1 > y0;
dxPos: BOOL ~ x1 > x0;
dy: INTEGER ¬ IF dyPos THEN y1-y0 ELSE y0-y1;
dx: INTEGER ¬ IF dxPos THEN x1-x0 ELSE x0-x1;
eincy: INTEGER ~ dy+dy;
eincx: INTEGER ~ dx+dx;
x, y, incx, incy: INTEGER;
SELECT
TRUE
FROM
dy = 0 => {
IF NOT dxPos THEN {t: INTEGER ¬ x0; x0 ¬ x1; x1 ¬ t};
FOR x: INTEGER IN [x0..x1] DO IF NOT pixelProc[x, y0] THEN RETURN; ENDLOOP;
};
dx = 0 => {
IF NOT dyPos THEN {t: INTEGER ¬ y0; y0 ¬ y1; y1 ¬ t};
FOR y: INTEGER IN [y0..y1] DO IF NOT pixelProc[x0, y] THEN RETURN; ENDLOOP;
};
dy > dx => {
e:
INTEGER ¬ eincx-dy;
IF dyPos
THEN {y ¬ y0; x ¬ x0; incx ¬ IF dxPos THEN 1 ELSE -1}
ELSE {y ¬ y1; x ¬ x1; incx ¬ IF dxPos THEN -1 ELSE 1};
WHILE dy >= 0
DO
IF NOT pixelProc[x, y] THEN RETURN;
IF e > 0 THEN {x ¬ x+incx; e ¬ e-eincy};
e ¬ e+eincx;
y ¬ y+1;
dy ¬ dy-1;
ENDLOOP;
};
ENDCASE => {
e: INTEGER ¬ eincy-dx;
IF dxPos
THEN {x ¬ x0; y ¬ y0; incy ¬ IF dyPos THEN 1 ELSE -1}
ELSE {x ¬ x1; y ¬ y1; incy ¬ IF dyPos THEN -1 ELSE 1};
WHILE dx >= 0
DO
IF NOT pixelProc[x, y] THEN RETURN;
IF e > 0 THEN {y ¬ y+incy; e ¬ e-eincx};
e ¬ e+eincy;
x ¬ x+1;
dx ¬ dx-1;
ENDLOOP;
};
};
strokeWidth:
REAL ¬ 1.0;
-- hack because Imager is client unfriendly
Dot:
PROC [context: Context, vec0, vec1:
VEC] ~ {
stepInc: VEC;
delta: VEC ¬ [vec1.x-vec0.x, vec1.y-vec0.y];
nSteps: INTEGER ¬ Real.Round[0.2*RealFns.SqRt[delta.x*delta.x+delta.y*delta.y]];
strokeWidth ¬ ImagerBackdoor.GetReal[context, strokeWidth ! Imager.Error => CONTINUE];
IF nSteps < 1 THEN RETURN;
IF strokeWidth < 1.0 THEN strokeWidth ¬ 1.0; -- this new, needed in 1988; Imager change?
stepInc ¬ [delta.x/nSteps, delta.y/nSteps];
FOR n:
NAT
IN [0..nSteps)
DO
Imager.MaskRectangle[context, [vec0.x, vec0.y, strokeWidth, strokeWidth]];
vec0 ¬ [vec0.x+stepInc.x, vec0.y+stepInc.y];
ENDLOOP;
};
Dash:
PROC [context: Context, vec0, vec1:
VEC, zip: Zip] ~ {
PatternProc: PROC [i: NAT] RETURNS [REAL] ~ {RETURN[2]};
dash: Imager.Trajectory ← ImagerPath.MoveTo[vec0];
dash ← ImagerPath.LineTo[dash, vec1];
Imager.MaskDashedStrokeTrajectory[context, dash, 2, PatternProc, 0, 20];
delta: VEC ¬ [vec1.x-vec0.x, vec1.y-vec0.y];
nSteps: INTEGER ¬ Real.Round[0.10*RealFns.SqRt[delta.x*delta.x+delta.y*delta.y]];
IF nSteps > 0
THEN {
stepInc: VEC ¬ [delta.x/nSteps, delta.y/nSteps];
drawInc: VEC ¬ [0.5*stepInc.x, 0.5*stepInc.y];
FOR n:
NAT
IN[0..nSteps)
DO
Solid[context, vec0, [vec0.x+drawInc.x, vec0.y+drawInc.y], zip];
vec0 ¬ [vec0.x+stepInc.x, vec0.y+stepInc.y];
ENDLOOP;
};
};
Solid:
PROC [context: Context, vec0, vec1:
VEC, zip: Zip ¬
NIL] ~ {
Boxes: PROC [boxAction: SF.BoxAction] ~ {DoSolid[d0, d1, boxAction]};
Action:
PROC [device: ImagerDevice.Device, clipper: ImagerDevice.DeviceClipper] ~ {
ImagerDeviceWorks.MaskBoxes[device: device, bounds: box, boxes: Boxes];
};
d0, d1, min, max: VEC;
box: SF.Box;
transform: ImagerTransformation.Transformation;
IF zip =
NIL
THEN {
IF
NOT ContextOk[context]
OR ImagerRaster.
GetDevice[context] =
NIL
THEN GOTO CantDo;
box ¬ ImagerRaster.GetClipper[context].clipBox;
transform ¬ ImagerBackdoor.GetTransformation[context, client, device];
IF SF.Empty[box] THEN RETURN;
box.min.s ¬ box.min.s+1; -- simplify clipping
box.min.f ¬ box.min.f+1;
box.max.s ¬ box.max.s-1;
box.max.f ¬ box.max.f-1;
}
ELSE {
IF
NOT zip.rasterDevice
OR
ImagerBackdoor.GetReal[context, strokeWidth ! Imager.Error =>
GOTO CantDo] # 0
THEN GOTO CantDo;
box ¬ zip.box;
transform ¬ zip.transform;
};
d0 ¬ ImagerTransformation.Transform[transform, vec0];
d1 ¬ ImagerTransformation.Transform[transform, vec1];
d0.x ¬ d0.x-1;
d1.x ¬ d1.x-1;
Clip:
IF d0.x < box.min.s
THEN {
IF d1.x < box.min.s THEN RETURN;
d0.y ¬ d0.y+(box.min.s-d0.x)*(d1.y-d0.y)/(d1.x-d0.x);
d0.x ¬ box.min.s;
}
ELSE
IF d0.x > box.max.s
THEN {
IF d1.x > box.max.s THEN RETURN;
d0.y ¬ d0.y+(box.max.s-d0.x)*(d1.y-d0.y)/(d1.x-d0.x);
d0.x ¬ box.max.s;
};
IF d1.x < box.min.s
THEN {
IF d0.x < box.min.s THEN RETURN;
d1.y ¬ d1.y+(box.min.s-d1.x)*(d0.y-d1.y)/(d0.x-d1.x);
d1.x ¬ box.min.s;
}
ELSE
IF d1.x > box.max.s
THEN {
IF d0.x > box.max.s THEN RETURN;
d1.y ¬ d1.y+(box.max.s-d1.x)*(d0.y-d1.y)/(d0.x-d1.x);
d1.x ¬ box.max.s;
};
IF d0.y < box.min.f
THEN {
IF d1.y < box.min.f THEN RETURN;
d0.x ¬ d0.x+(box.min.f-d0.y)*(d1.x-d0.x)/(d1.y-d0.y);
d0.y ¬ box.min.f;
}
ELSE
IF d0.y > box.max.f
THEN {
IF d1.y > box.max.f THEN RETURN;
d0.x ¬ d0.x+(box.max.f-d0.y)*(d1.x-d0.x)/(d1.y-d0.y);
d0.y ¬ box.max.f;
};
IF d1.y < box.min.f
THEN {
IF d0.y < box.min.f THEN RETURN;
d1.x ¬ d1.x+(box.min.f-d1.y)*(d0.x-d1.x)/(d0.y-d1.y);
d1.y ¬ box.min.f;
}
ELSE
IF d1.y > box.max.f
THEN {
IF d0.y > box.max.f THEN RETURN;
d1.x ¬ d1.x+(box.max.f-d1.y)*(d0.x-d1.x)/(d0.y-d1.y);
d1.y ¬ box.max.f;
};
min ¬ [MIN[d0.x, d1.x], MIN[d0.y, d1.y]];
max ¬ [MAX[d0.x, d1.x], MAX[d0.y, d1.y]];
box.min ¬ [Real.Floor[min.x]-1, Real.Floor[min.y]-1];
box.max ¬ [Real.Ceiling[max.x]+1, Real.Ceiling[max.y]+1];
temp ← [[Real.Round[d0.x], Real.Round[d0.y]], [Real.Round[d1.x], Real.Round[d1.y]]];
box.min ← [MIN[temp.min.s, temp.max.s], MIN[temp.min.f, temp.max.f]];
box.max ← [MAX[temp.min.s, temp.max.s], MAX[temp.min.f, temp.max.f]];
ImagerRaster.MaskDeviceBoxes[context, box, Boxes];
ImagerRaster.DoWithDevice[context, box, Action];
IF zip = NIL THEN ImagerTransformation.Destroy[transform];
EXITS
CantDo => Imager.MaskVector[context, vec0, vec1];
};
DoSolid:
PROC [d0, d1:
VEC, box:
SF.BoxAction] ~ {
-- Bresenham's method
s0: INTEGER ¬ Real.Round[d0.x];
f0: INTEGER ¬ Real.Round[d0.y];
s1: INTEGER ~ Real.Round[d1.x];
f1: INTEGER ~ Real.Round[d1.y];
dsPos: BOOL ~ s1 > s0;
dfPos: BOOL ~ f1 > f0;
ds: INTEGER ¬ IF dsPos THEN s1-s0 ELSE s0-s1;
df: INTEGER ¬ IF dfPos THEN f1-f0 ELSE f0-f1;
eincs: INTEGER ~ ds+ds;
eincf: INTEGER ~ df+df;
len: INTEGER ¬ 0;
s, f, incf, incs: INTEGER;
SELECT
TRUE
FROM
ds = 0 => {
f ¬ IF dfPos THEN f0 ELSE f1;
box[[[s0, f], [s0+1, f+df]]];
};
df = 0 => {
s ¬ IF dsPos THEN s0 ELSE s1;
box[[[s, f0], [s+ds, f0+1]]];
};
ds > df => {
-- more vertical line
e: INTEGER ¬ eincf-ds;
IF dsPos
THEN {s ¬ s0; f ¬ f0; incf ¬ IF dfPos THEN 1 ELSE -1}
ELSE {s ¬ s1; f ¬ f1; incf ¬ IF dfPos THEN -1 ELSE 1};
WHILE ds >= 0
DO
len ¬ len+1;
IF e > 0
THEN {
box[[[s, f], [s+len, f+1]]];
f ¬ f+incf;
e ¬ e-eincs;
s ¬ s+len;
len ¬ 0;
};
e ¬ e+eincf;
ds ¬ ds-1;
ENDLOOP;
IF len > 0 THEN box[[[s, f], [s+len, f+1]]];
};
ENDCASE => {
-- more horizontal line
e: INTEGER ¬ eincs-df;
IF dfPos
THEN {f ¬ f0; s ¬ s0; incs ¬ IF dsPos THEN 1 ELSE -1}
ELSE {f ¬ f1; s ¬ s1; incs ¬ IF dsPos THEN -1 ELSE 1};
WHILE df >= 0
DO
len ¬ len+1;
IF e > 0
THEN {
box[[[s, f], [s+1, f+len]]];
s ¬ s+incs;
e ¬ e-eincf;
f ¬ f+len;
len ¬ 0;
};
e ¬ e+eincs;
df ¬ df-1;
ENDLOOP;
IF len > 0 THEN box[[[s, f], [s+1, f+len]]];
};
};
Miscellaneous Procedures
Mark:
PUBLIC
PROC [
context: Context,
position: VEC,
markType: MarkType ¬ cross,
zip: Zip ¬ NIL]
~ {
Action:
PROC ~ {
X:
PROC ~ {
Solid[context, [position.x-4.0, position.y-4.0], [position.x+4.0, position.y+4.0], zip];
Solid[context, [position.x+4.0, position.y-4.0], [position.x-4.0, position.y+4.0], zip];
};
Cross:
PROC ~ {
Solid[context, [position.x-5.0, position.y], [position.x+5.0, position.y], zip];
Solid[context, [position.x, position.y-5.0], [position.x, position.y+5.0], zip];
};
SELECT markType
FROM
dot => Imager.MaskRectangle[context, [position.x, position.y, 1, 1]];
x => X[];
cross => Cross[];
asterisk => {Cross[]; X[]};
ENDCASE => NULL;
};
Imager.DoSave[context, Action];
};
Square:
PUBLIC
PROC [context: Context, center:
VEC, size:
REAL] ~ {
size2: REAL ¬ size+size;
Imager.MaskRectangle[context, [center.x-size-1, center.y-size, size2, size2]];
};
Circle:
PUBLIC PROC [context: Context, center:
VEC, radius:
REAL, fill:
BOOL ¬
FALSE] ~ {
t: Imager.Trajectory ¬ ImagerPath.MoveTo[[center.x+radius, center.y]];
t ¬ ImagerPath.ArcTo[t, [center.x-radius, center.y], [center.x+radius, center.y]];
IF fill
THEN Imager.MaskFillTrajectory[context, t]
ELSE Imager.MaskStrokeTrajectory[context, t, TRUE];
};
Arrow:
PUBLIC PROC [
context: Context,
tail, head: VEC,
drawType: DrawType ¬ solid,
vary: BOOL ¬ TRUE,
zip: Zip ¬ NIL]
~ {
v: VEC ¬ Vector2.Sub[head, tail];
mul: REAL ¬ IF v.x*v.x+v.y*v.y > 75.0*75.0 THEN -0.175*75.0/Vector2.Length[v] ELSE -0.175;
vv: VEC ¬ Vector2.Mul[v, mul];
p0: VEC ¬ Vector2.Add[Vector2.Add[head, vv], [0.5*vv.y, -0.5*vv.x]];
p1: VEC ¬ Vector2.Add[p0, [-vv.y, vv.x]];
Line[context, head, tail, drawType, zip];
Line[context, head, p0, drawType, zip];
Line[context, head, p1, drawType, zip];
};
..
DoSolidSym:
PROC [a1, b1, a2, b2:
INTEGER, box:
SF.BoxAction] ~ {
-- Bresenham's ala Wyvill
reverse: BOOL;
incr1, incr2, d, x, y, xEnd, c, pixelsLeft, m1, m2, x1, y1, signX, signY, step, i: INTEGER;
dx: INTEGER ¬ a2-a1;
dy: INTEGER ¬ b2-b1;
IF dx < 0 THEN {signX ¬ -1; dx ¬ -dx} ELSE signX ¬ 1;
IF dy < 0 THEN {signY ¬ -1; dy ¬ -dy} ELSE signY ¬ 1;
step ¬ IF signX = signY THEN 1 ELSE -1; -- decide increment sign by slope sign:
IF dy > dx
THEN {
-- chooses axis of greatest movement (make dx)
temp: INTEGER;
temp ¬ a1; a1 ¬ b1; b1 ¬ temp;
temp ¬ a2; a2 ¬ b2; b2 ¬ temp;
temp ¬ dx; dx ¬ dy; dy ¬ temp;
reverse ¬ TRUE;
}
ELSE reverse ¬ FALSE;
Should check for dx=0 here.
IF a1 > a2
-- start from the smaller coordinate
THEN {x ¬ a2; y ¬ b2; x1 ¬ a1; y1 ¬ b1}
ELSE {x ¬ a1; y ¬ b1; x1 ¬ a2; y1 ¬ b2};
Note dx=n implies 0-n or (dx+1) pixels to be set (!?)
go round loop dx/4 times THEN plot last 0,1,2 or 3 pixels
in fact (dx-1)/4 as 2 pixels are already plottted
xEnd ¬ (dx-1)/4;
pixelsLeft ¬ (dx-1) MOD 4; -- number of pixels left over at the end
Plot[x, y, reverse];
Plot[x1, y1, reverse]; -- plot first two points
incr2 ¬ 4*dy-2*dx;
IF incr2 < 0
-- slope less than 1/2
THEN {
c ¬ dy+dy;
incr1 ¬ c+c;
d ¬ incr1-dx;
FOR i:
INTEGER ¬ 0, i+1
WHILE i < xEnd
DO
-- plotting loop
x ¬ x+1;
x1 ¬ x1-1;
IF d < 0
THEN {
Plot[x, y, reverse];
x ¬ x+1;
Plot[x, y, reverse]; -- pattern 1 forwards
Plot[x1, y1, reverse];
x1 ¬ x1-1;
Plot[x1, y1, reverse]; -- pattern 1 backwards
d ¬ d+incr1;
}
ELSE {
IF d < c
THEN {
Plot[x, y, reverse];
x ¬ x+1;
y ¬ y+step;
Plot[x, y, reverse]; -- pattern 2 forwards
Plot[x1, y1, reverse];
x1 ¬ x1-1;
y1 ¬ y1-step;
Plot[x1, y1, reverse]; -- pattern 2 backwards
}
ELSE {
y ¬ y+step;
Plot[x, y, reverse];
x ¬ x+1;
Plot[x, y, reverse]; -- pattern 3 forwards
y1 ¬ y1-step;
Plot[x1, y1, reverse];
x1 ¬ x1-1;
Plot[x1, y1, reverse]; -- pattern 3 backwards
};
d ¬ d+incr2;
};
ENDLOOP;
IF pixelsLeft
THEN {
IF d < 0
THEN {
Plot[++x,y,reverse]; -- pattern 1
IF pixelsLeft > 1 THEN {x ¬ x+1; Plot[x, y, reverse]};
IF pixelsLeft > 2 THEN {x1 ¬ x1-1; Plot[x1, y1, reverse]};
}
ELSE {
IF d < c
THEN {
x ¬ x+1;
Plot[x, y, reverse]; -- pattern 2
IF pixelsLeft > 1 THEN {x ¬ x+1; y ¬ y+step; Plot[x, y, reverse]};
IF pixelsLeft > 2 THEN {x1 ¬ x1-1; Plot[x1, y1, reverse]};
}
ELSE {
x ¬ x+1;
y ¬ y+step;
Plot[x, y, reverse]; -- pattern 3
IF pixelsLeft > 1 THEN {x ¬ x+1; Plot[x, y, reverse]};
IF pixelsLeft > 2 THEN {x1 ¬ x1-1; y1 ¬ y1-step Plot[x1, y1, reverse]};
}
};
}
} -- end slope < 1/2
ELSE {
-- slope greater than 1/2
c ¬ 2*(dy-dx);
incr1 ¬ c+c;
d ¬ incr1+dx;
FOR i:
INTEGER ¬ 0, i+1
WHILE i < xEnd
DO
x ¬ x+1; x1 ¬ x1-1;
IF d > 0
THEN {
y ¬ y+step;
Plot[x, y, reverse];
x ¬ x+1; y ¬ y+step;
Plot[x, y, reverse]; -- pattern 4 forwards
y1 ¬ y1-step;
Plot[x1, y1, reverse];
x1 ¬ x1-1; y1 ¬ y1-step;
Plot[x1, y1, reverse]; -- pattern 4 backwards
d ¬ d+incr1;
}
ELSE {
IF d < c
THEN {
Plot[x, y, reverse];
x ¬ x+1; y ¬ y+step;
Plot[x, y, reverse]; -- pattern 2 forwards
Plot[x1, y1, reverse];
x1 ¬ x1-1; y1 ¬ y1-step;
Plot[x1, y1, reverse]; -- pattern 2 backwards
}
ELSE {
y ¬ y+step;
Plot[x, y, reverse];
x ¬ x+1;
Plot[x, y, reverse]; -- pattern 3 forwards
Fix for case where line exactly divides pixel:
IF d = c
THEN {
-- plot step 2 backwards
Plot[x1, y1, reverse];
x1 ¬ x1-1; y1 ¬ y1-step;
Plot[x1, y1, reverse];
}
ELSE {
-- pattern 3 backwards
y1 ¬ y1-step;
Plot[x1, y1, reverse];
x1 ¬ x1-1;
Plot[x1, y1, reverse]; -- pattern 3 backwards
}
}
d ¬ d+incr2;
}
IF pixelsLeft # 0
THEN {
IF d > 0
THEN {
x ¬ x+1; y ¬ y+step;
Plot[x, y, reverse]; -- pattern 4
IF pixelsLeft > 1 THEN {x ¬ x+1; y ¬ y+step; Plot[x ,y, reverse]};
IF pixelsLeft > 2 THEN {x1 ¬ x1-1; y1 ¬ y1-step; Plot[x1, y1, reverse]}
}
ELSE {
x ¬ x+1;
IF d < c
THEN {
Plot[x, y, reverse]; -- pattern 2
IF (pixelsLeft>1) THEN {x ¬ x+1; y ¬ y+step; Plot[x, y, reverse];
IF (pixelsLeft>2) THEN {x1 ¬ x1-1; Plot[x1, y1, reverse]};
}
ELSE {
y ¬ y+step;
Plot[x, y, reverse]; -- pattern 3
IF pixelsLeft > 1 THEN {x ¬ x+1; Plot[x, y, reverse]};
IF pixelsLeft > 2
THEN {
x1 ¬ x1-1;
IF d > c
THEN {y1 ¬ y1-step; Plot[x1, y1, reverse]}; -- step 3
ELSE Plot[x1, y1, reverse]; -- step 2
};
};
};
};
}
}
};
IdentityTransform: PROC [matrix: ImagerTransformation.Transformation] RETURNS [BOOL] ~ {
RETURN[matrix.a = 1.0 AND matrix.e = 1.0
AND matrix.b = 0.0 AND matrix.c = 0.0 AND matrix.d = 0.0 AND matrix.f = 0.0];
};
Circle: PUBLIC PROC [context: Context, vec: VEC, radius: REAL, fill: BOOL ← FALSE] ~ {
data: Data ← ContextOK[context];
IF data # NIL AND NOT fill
THEN DoCircle[context, vec, radius, fill]
ELSE {
t: Imager.Trajectory ← ImagerPath.MoveTo[[vec.x+radius, vec.y]];
t ← ImagerPath.ArcTo[t, [vec.x, vec.y+radius], [vec.x-radius, vec.y]];
t ← ImagerPath.ArcTo[t, [vec.x, vec.y-radius], [vec.x+radius, vec.y]];
Imager.SetStrokeWidth[context, 1.15];
IF fill THEN Imager.MaskFillTrajectory[context, t]
ELSE Imager.MaskStrokeTrajectory[context, t, TRUE];
};
};
DoCircle: PROC [context: Context, vec: VEC, radius: REAL, fill: BOOL] ~ {
Imager.Trajectory is slightly faster for fill; this is about 1.7 faster for no fill.
r: INTEGER ← Real.Round[radius];
xc: INTEGER ← Real.Round[vec.x];
yc: INTEGER ← Real.Round[vec.y];
x: INTEGER ← 0;
y: INTEGER ← r;
d: INTEGER ← 3-r-r;
IF fill
THEN WHILE x <= y DO
xPlusX: INTEGER ← xc+x;
xMinusX: INTEGER ← xc-x;
xPlusY: INTEGER ← xc+y;
xMinusY: INTEGER ← xc-y;
yPlusX: INTEGER ← yc+x;
yMinusX: INTEGER ← yc-x;
yPlusY: INTEGER ← yc+y;
yMinusY: INTEGER ← yc-y;
Solid[context, [xMinusX, yPlusY], [xPlusX, yPlusY]];
Solid[context, [xMinusY, yPlusX], [xPlusY, yPlusX]];
Solid[context, [xMinusX, yMinusY], [xPlusX, yMinusY]];
Solid[context, [xMinusY, yMinusX], [xPlusY, yMinusX]];
The following produces somewhat diamond-shaped circles, why?
IF d < 0
THEN d ← d+4*(x+6)
ELSE {
d ← d+4*(x-y)+10;
y ← y-1;
};
x ← x+1;
ENDLOOP
ELSE WHILE x <= y DO
xPlusX: INTEGER ← xc+x;
xMinusX: INTEGER ← xc-x;
xPlusY: INTEGER ← xc+y;
xMinusY: INTEGER ← xc-y;
yPlusX: INTEGER ← yc+x;
yMinusX: INTEGER ← yc-x;
yPlusY: INTEGER ← yc+y;
yMinusY: INTEGER ← yc-y;
Imager.MaskRectangle[context, [xMinusX, yPlusY, 1, 1]];
Imager.MaskRectangle[context, [xPlusX, yPlusY, 1, 1]];
Imager.MaskRectangle[context, [xMinusY, yPlusX, 1, 1]];
Imager.MaskRectangle[context, [xPlusY, yPlusX, 1, 1]];
Imager.MaskRectangle[context, [xMinusX, yMinusY, 1, 1]];
Imager.MaskRectangle[context, [xPlusX, yMinusY, 1, 1]];
Imager.MaskRectangle[context, [xMinusY, yMinusX, 1, 1]];
Imager.MaskRectangle[context, [xPlusY, yMinusX, 1, 1]];
IF d < 0
THEN d ← d+4*(x+6)
ELSE {
d ← d+4*(x-y)+10;
y ← y-1;
};
x ← x+1;
ENDLOOP;
};