Draw2dImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Bloomenthal, October 21, 1986 1:21:46 pm PDT
DIRECTORY Draw2d, Imager, ImagerBackdoor, ImagerDevice, ImagerFont, ImagerPath, ImagerOps, ImagerRasterPrivate, ImagerState, ImagerTransformation, IO, Real, Rope, Vector2;
Draw2dImpl:
CEDAR
PROGRAM
IMPORTS Imager, ImagerBackdoor, ImagerFont, ImagerOps, ImagerPath, ImagerRasterPrivate, ImagerTransformation, Real, Vector2
EXPORTS Draw2d, Imager
Type Declarations
ROPE: TYPE ~ Rope.ROPE;
Context: TYPE ~ Imager.Context;
VEC: TYPE ~ Imager.VEC; -- RECORD [x, y: REAL]
DrawType: TYPE ~ Draw2d.DrawType;
MarkType: TYPE ~ Draw2d.MarkType;
PixelProc: TYPE ~ Draw2d.PixelProc;
General Procedures
Clear:
PUBLIC
PROC [context: Context] ~ {
Imager.SetColor[context, Imager.white];
Imager.MaskRectangle[context, ImagerBackdoor.GetBounds[context]];
Imager.SetColor[context, Imager.black];
};
Label:
PUBLIC
PROC [context: Context, vec:
VEC, rope:
ROPE] ~ {
font: Imager.Font ← NIL;
font ← ImagerBackdoor.GetFont[context ! Imager.Error => CONTINUE];
IF font = NIL THEN Imager.SetFont[context, ImagerFont.Find["xerox/tiogafonts/helvetica10"]];
Imager.SetXY[context, vec];
Imager.ShowRope[context, rope];
};
DoWithBuffer:
PUBLIC
PROC [context: Context, action:
PROC, clear:
BOOL ←
TRUE] ~ {
rect: Imager.Rectangle ← ImagerBackdoor.GetBounds[context];
color: Imager.Color ← IF clear THEN Imager.white ELSE NIL;
Imager.SetStrokeWidth[context, 0.0];
ImagerOps.DoWithBuffer[context, action, 0, 0, Real.RoundI[rect.w], Real.RoundI[rect.h], color];
};
Line Drawing Procedures
Line:
PUBLIC
PROC [context: Context, vec0, vec1:
VEC, drawType: DrawType ← solid] ~ {
SELECT drawType
FROM
solid => Solid[context, vec0, vec1];
dotted => Dot[context, vec0, vec1];
dashed => Dash[context, vec0, vec1];
ENDCASE => NULL;
};
DoWithLine:
PUBLIC PROC [vec0, vec1:
VEC, pixelProc: PixelProc] ~ {
x0: INTEGER ← Real.RoundI[vec0.x];
y0: INTEGER ← Real.RoundI[vec0.y];
x1: INTEGER ← Real.RoundI[vec1.x];
y1: INTEGER ← Real.RoundI[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;
};
};
Dot:
PROC [context: Context, vec0, vec1:
VEC] ~ {
stepInc: VEC;
delta: VEC ← [vec1.x-vec0.x, vec1.y-vec0.y];
nSteps: INTEGER ← Real.RoundI[0.2*Real.SqRt[delta.x*delta.x+delta.y*delta.y]];
IF nSteps < 1 THEN RETURN;
stepInc ← [delta.x/nSteps, delta.y/nSteps];
FOR n:
NAT
IN [0..nSteps)
DO
Imager.MaskRectangle[context, [vec0.x, vec0.y, 1, 1]];
vec0 ← [vec0.x+stepInc.x, vec0.y+stepInc.y];
ENDLOOP;
};
Dash:
PROC [context: Context, vec0, vec1:
VEC] ~ {
delta: VEC ← [vec1.x-vec0.x, vec1.y-vec0.y];
nSteps: INTEGER ← Real.RoundI[0.10*Real.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]];
vec0 ← [vec0.x+stepInc.x, vec0.y+stepInc.y];
ENDLOOP;
};
};
Data: TYPE ~ ImagerRasterPrivate.Data;
State:
TYPE ~ ImagerState.State;
StateRep:
PUBLIC
TYPE ~ ImagerState.StateRep;
-- export to Imager.StateRep
ContextOK:
PROC [context: Context]
RETURNS [Data] ~ {
state: State ~ context.state;
needs: ImagerRasterPrivate.Flags ~
[clientToDevice: TRUE, clientClipper: TRUE, deviceColor: TRUE, devicePriority: TRUE];
WITH context.data
SELECT
FROM
data: Data => {
device: ImagerDevice.Device ~ data.device;
IF state.changed # ImagerState.notChanged THEN ImagerRasterPrivate.NoteStateChanges[data, state];
IF ImagerRasterPrivate.AndFlags[data.valid, needs] # needs THEN ImagerRasterPrivate.ValidateIfNeeded[data, state, needs];
IF state.np.strokeWidth = 0 AND data.clientClipBoxOnly AND device.class.MaskBoxes#NIL THEN RETURN[data];
};
ENDCASE => NULL;
RETURN[NIL];
};
Solid:
PROC [context: Context, vec0, vec1:
VEC] ~ {
-- Bresenham's method
data: Data ← ContextOK[context];
IF data #
NIL
THEN {
Boxes:
PROC [box: ImagerDevice.BoxProc] ~ {
DoSolid[d0, d1, data.clientClipBox, box];
};
device: ImagerDevice.Device ~ data.device;
bounds: ImagerDevice.DeviceBox ← data.clientClipBox;
d0: VEC ← ImagerTransformation.Transform[data.clientToDevice, vec0];
d1: VEC ← ImagerTransformation.Transform[data.clientToDevice, vec1];
IF bounds.smin = bounds.smax OR bounds.fmin = bounds.fmax THEN RETURN;
bounds.smin ← bounds.smin+1; -- simplify clipping
bounds.fmin ← bounds.fmin+1;
bounds.smax ← bounds.smax-1;
bounds.fmax ← bounds.fmax-1;
IF d0.x < bounds.smin
THEN {
IF d1.x < bounds.smin THEN RETURN;
d0.y ← d0.y+(bounds.smin-d0.x)*(d1.y-d0.y)/(d1.x-d0.x);
d0.x ← bounds.smin;
}
ELSE
IF d0.x > bounds.smax
THEN {
IF d1.x > bounds.smax THEN RETURN;
d0.y ← d0.y+(bounds.smax-d0.x)*(d1.y-d0.y)/(d1.x-d0.x);
d0.x ← bounds.smax;
};
IF d1.x < bounds.smin
THEN {
IF d0.x < bounds.smin THEN RETURN;
d1.y ← d1.y+(bounds.smin-d1.x)*(d0.y-d1.y)/(d0.x-d1.x);
d1.x ← bounds.smin;
}
ELSE
IF d1.x > bounds.smax
THEN {
IF d0.x > bounds.smax THEN RETURN;
d1.y ← d1.y+(bounds.smax-d1.x)*(d0.y-d1.y)/(d0.x-d1.x);
d1.x ← bounds.smax;
};
IF d0.y < bounds.fmin
THEN {
IF d1.y < bounds.fmin THEN RETURN;
d0.x ← d0.x+(bounds.fmin-d0.y)*(d1.x-d0.x)/(d1.y-d0.y);
d0.y ← bounds.fmin;
}
ELSE
IF d0.y > bounds.fmax
THEN {
IF d1.y > bounds.fmax THEN RETURN;
d0.x ← d0.x+(bounds.fmax-d0.y)*(d1.x-d0.x)/(d1.y-d0.y);
d0.y ← bounds.fmax;
};
IF d1.y < bounds.fmin
THEN {
IF d0.y < bounds.fmin THEN RETURN;
d1.x ← d1.x+(bounds.fmin-d1.y)*(d0.x-d1.x)/(d0.y-d1.y);
d1.y ← bounds.fmin;
}
ELSE
IF d1.y > bounds.fmax
THEN {
IF d0.y > bounds.fmax THEN RETURN;
d1.x ← d1.x+(bounds.fmax-d1.y)*(d0.x-d1.x)/(d0.y-d1.y);
d1.y ← bounds.fmax;
};
device.class.MaskBoxes[device: device, bounds: bounds, boxes: Boxes];
}
ELSE Imager.MaskVector[context, vec0, vec1];
};
DoSolid:
PROC [d0, d1:
VEC, clip: ImagerDevice.DeviceBox, box: ImagerDevice.BoxProc] ~ {
s0: INTEGER ← Real.RoundI[d0.x];
f0: INTEGER ← Real.RoundI[d0.y];
s1: INTEGER ~ Real.RoundI[d1.x];
f1: INTEGER ~ Real.RoundI[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[[smin: s0, fmin: f, smax: s0+1, fmax: f+df]];
};
df = 0 => {
s ← IF dsPos THEN s0 ELSE s1;
box[[smin: s, fmin: f0, smax: s+ds, fmax: f0+1]];
};
ds > df => {
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[[smin: s, fmin: f, smax: s+len, fmax: 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[[smin: s, fmin: f, smax: s+len, fmax: f+1]];
};
ENDCASE => {
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[[smin: s, fmin: f, smax: s+1, fmax: 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[[smin: s, fmin: f, smax: s+1, fmax: f+len]];
};
};
Miscellaneous Procedures
Mark:
PUBLIC
PROC [context: Context, vec:
VEC, markType: MarkType ← cross] ~ {
Action:
PROC ~ {
Dot: PROC ~ {Imager.MaskRectangle[context, [vec.x-1, vec.y-1, 3, 3]]};
X:
PROC ~ {
Solid[context, [vec.x-4.0, vec.y-4.0], [vec.x+4.0, vec.y+4.0]];
Solid[context, [vec.x+4.0, vec.y-4.0], [vec.x-4.0, vec.y+4.0]];
};
Cross:
PROC ~ {
Solid[context, [vec.x-5.0, vec.y], [vec.x+5.0, vec.y]];
Solid[context, [vec.x, vec.y-5.0], [vec.x, vec.y+5.0]];
};
SELECT markType
FROM
dot => Dot[];
x => X[];
cross => Cross[];
asterisk => {Cross[]; X[];};
ENDCASE => NULL;
};
Imager.DoSave[context, Action];
};
Square:
PUBLIC
PROC [context: Context, vec:
VEC, size:
REAL] ~ {
size2: REAL ← size+size;
Imager.MaskRectangle[context, [vec.x-size-1, vec.y-size, size2, size2]];
};
Circle:
PUBLIC PROC [context: Context, vec:
VEC, radius:
REAL, fill:
BOOL ←
FALSE] ~ {
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];
};
Arrow:
PUBLIC PROC [
context: Context,
tail, head: VEC,
drawType: DrawType ← solid,
vary: BOOL ← TRUE]
~ {
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];
Line[context, head, p0, drawType];
Line[context, head, p1, drawType];
};
..