-- CGPrivateImpl.mesa
-- Last edited by Doug Wyatt, September 15, 1982 5:48 pm
DIRECTORY
CGArea USING [Empty, Ref],
CGClipper USING [Bounds, Empty, GenerateBox, GenerateLine, IsBox, Load,
Ref, SetArea, TestBox, TestPoint],
CGCubic USING [Bezier, Flat, Split],
CGContext USING [Ref, TouchClipper, TouchCP, TouchFill, TouchMatrix],
CGDevice USING [Ref],
CGDummyDevice USING [Get],
CGMatrix USING [Concat, Inv, InvRel, Map, MapRel, Ref, SetTrans],
CGPath USING [Bounds, Empty, MapAndFilter],
CGPrivate USING [],
CGReducer USING [Close, Generate, Ref, Vertex],
CGSource USING [Ref],
CGVector USING [Add],
Graphics USING [Context, Warning],
GraphicsBasic USING [Box, Color, PaintMode, Path, Vec, YMode],
SpecialReal USING [Round];
CGPrivateImpl: CEDAR PROGRAM
IMPORTS CGArea, CGClipper, CGContext, CGCubic, CGDummyDevice,
CGMatrix, CGPath, CGReducer, CGVector, Graphics, SpecialReal
EXPORTS CGContext, CGPrivate = { OPEN GraphicsBasic;
Context: TYPE = Graphics.Context;
Data: TYPE = CGContext.Ref;
Path: TYPE = GraphicsBasic.Path;
-- Procedures dependent on data representation --
GetCP: PUBLIC PROC[self: Context, rounded: BOOLEAN] RETURNS[x,y: REAL] = {
d: Data ← NARROW[self.data];
m: CGMatrix.Ref ← d.matrix;
cp: Vec ← d.cp;
p: Vec;
IF rounded THEN { cp.x ← SpecialReal.Round[cp.x]; cp.y ← SpecialReal.Round[cp.y] };
p ← CGMatrix.Inv[m,cp];
RETURN[p.x,p.y];
};
SetCP: PUBLIC PROC[self: Context, x,y: REAL, rel: BOOLEAN] = {
d: Data ← NARROW[self.data];
m: CGMatrix.Ref ← d.matrix;
p: Vec ← [x,y];
CGContext.TouchCP[d];
IF rel THEN d.cp ← CGVector.Add[d.cp,CGMatrix.MapRel[m,p]]
ELSE d.cp ← CGMatrix.Map[m,p];
};
DrawTo: PUBLIC PROC[self: Context, x,y: REAL, rel: BOOLEAN] = {
d: Data ← NARROW[self.data];
m: CGMatrix.Ref ← d.matrix;
clipper: CGClipper.Ref ← d.clipper;
area: CGArea.Ref ← d.area;
p,q: Vec;
p ← d.cp;
CGContext.TouchCP[d];
IF rel THEN q ← CGVector.Add[p,CGMatrix.MapRel[m,[x,y]]]
ELSE q ← CGMatrix.Map[m,[x,y]];
CGClipper.GenerateLine[clipper,p,q,area];
IF NOT CGArea.Empty[area] THEN {
device: CGDevice.Ref ← d.device;
src: CGSource.Ref ← d.src;
fat: BOOLEAN ← src.fat;
src.fat ← TRUE;
device.Show[device,area,src,m];
src.fat ← fat;
};
d.cp ← q;
};
DrawArea: PUBLIC PROC[self: Context, path: Path, parityFill: BOOLEAN] = {
d: Data ← NARROW[self.data];
device: CGDevice.Ref ← d.device;
area: CGArea.Ref ← GenPath[d: d, path: path, parityFill: parityFill, exclude: FALSE];
IF NOT CGArea.Empty[area] THEN device.Show[device, area, d.src, d.matrix];
};
DrawBox: PUBLIC PROC[self: Context, box: Box] = {
d: Data ← NARROW[self.data];
m: CGMatrix.Ref ← d.matrix;
device: CGDevice.Ref ← d.device;
area: CGArea.Ref ← GenBox[d,box,m];
IF NOT CGArea.Empty[area] THEN device.Show[device, area, d.src, m];
};
Translate: PUBLIC PROC[self: Context, tx,ty: REAL, round: BOOLEAN] = {
d: Data ← NARROW[self.data];
m: CGMatrix.Ref ← d.matrix;
t: Vec ← CGMatrix.Map[m,[tx,ty]];
CGContext.TouchMatrix[d];
IF round THEN {
t.x ← SpecialReal.Round[t.x];
t.y ← SpecialReal.Round[t.y];
};
CGMatrix.SetTrans[m,t];
};
Concat: PUBLIC PROC[self: Context, m11,m12,m21,m22: REAL] = {
d: Data ← NARROW[self.data];
CGContext.TouchMatrix[d];
CGMatrix.Concat[d.matrix,m11,m12,m21,m22];
};
WorldToUser: PUBLIC PROC[self: Context, wx,wy: REAL] RETURNS[x,y: REAL] = {
d: Data ← NARROW[self.data];
device: CGDevice.Ref ← d.device;
world: CGMatrix.Ref ← device.GetMatrix[device];
m: CGMatrix.Ref ← d.matrix;
u: Vec ← CGMatrix.Inv[m,CGMatrix.Map[world,[wx,wy]]];
RETURN[u.x,u.y];
};
UserToWorld: PUBLIC PROC[self: Context, x,y: REAL] RETURNS[wx,wy: REAL] = {
d: Data ← NARROW[self.data];
device: CGDevice.Ref ← d.device;
world: CGMatrix.Ref ← device.GetMatrix[device];
m: CGMatrix.Ref ← d.matrix;
w: Vec ← CGMatrix.Inv[world,CGMatrix.Map[m,[x,y]]];
RETURN[w.x,w.y];
};
SetColor: PUBLIC PROC[self: Context, color: Color] = {
d: Data ← NARROW[self.data];
CGContext.TouchFill[d];
d.src.type ← const; d.src.color ← color;
};
GetColor: PUBLIC PROC[self: Context] RETURNS[Color] = {
d: Data ← NARROW[self.data];
RETURN[d.src.color];
};
SetPaintMode: PUBLIC PROC[self: Context, mode: PaintMode] RETURNS[PaintMode] = {
d: Data ← NARROW[self.data];
old: PaintMode ← d.src.mode;
CGContext.TouchFill[d];
d.src.mode ← mode;
RETURN[old];
};
SetFat: PUBLIC PROC[self: Context, fat: BOOLEAN] RETURNS[BOOLEAN] = {
d: Data ← NARROW[self.data];
f: BOOLEAN ← d.src.fat;
CGContext.TouchFill[d];
d.src.fat ← fat; RETURN[f];
};
ClipArea: PUBLIC PROC[self: Context, path: Path,
parityFill: BOOLEAN, exclude: BOOLEAN] = {
d: Data ← NARROW[self.data];
clipper: CGClipper.Ref ← d.clipper;
area: CGArea.Ref;
CGContext.TouchClipper[d];
area ← GenPath[d, path, parityFill, exclude];
CGClipper.SetArea[self: clipper, area: area];
};
ClipBox: PUBLIC PROC[self: Context, box: Box, exclude: BOOLEAN] = {
d: Data ← NARROW[self.data];
area: CGArea.Ref ← GenBox[d,box,d.matrix,exclude];
CGContext.TouchClipper[d];
CGClipper.SetArea[d.clipper,area];
};
IsPointVisible: PUBLIC PROC[self: Context, x,y: REAL] RETURNS[BOOLEAN] = {
d: Data ← NARROW[self.data];
clipper: CGClipper.Ref ← d.clipper;
m: CGMatrix.Ref ← d.matrix;
p: Vec ← CGMatrix.Map[m,[x,y]];
RETURN[CGClipper.TestPoint[clipper,p]];
};
IsRectangular: PUBLIC PROC[self: Context] RETURNS[BOOLEAN] = {
d: Data ← NARROW[self.data];
clipper: CGClipper.Ref ← d.clipper;
RETURN[CGClipper.IsBox[clipper]];
};
GetBounds: PUBLIC PROC[self: Context] RETURNS[Box] = {
data: Data ← NARROW[self.data];
clipper: CGClipper.Ref ← data.clipper;
m: CGMatrix.Ref ← data.matrix;
box: Box ← CGClipper.Bounds[clipper];
result: Box;
a,b,c,d: Vec;
IF m.rectangular THEN {
a ← CGMatrix.Inv[m,[box.xmin,box.ymin]];
c ← CGMatrix.Inv[m,[box.xmax,box.ymax]];
result.xmin ← MIN[a.x,c.x];
result.xmax ← MAX[a.x,c.x];
result.ymin ← MIN[a.y,c.y];
result.ymax ← MAX[a.y,c.y];
}
ELSE {
a ← CGMatrix.Inv[m,[box.xmin,box.ymin]];
b ← CGMatrix.Inv[m,[box.xmax,box.ymin]];
c ← CGMatrix.Inv[m,[box.xmax,box.ymax]];
d ← CGMatrix.Inv[m,[box.xmin,box.ymax]];
result.xmin ← MIN[a.x,b.x,c.x,d.x];
result.xmax ← MAX[a.x,b.x,c.x,d.x];
result.ymin ← MIN[a.y,b.y,c.y,d.y];
result.ymax ← MAX[a.y,b.y,c.y,d.y];
};
RETURN[result];
};
Visible: PUBLIC PROC[self: Context] RETURNS[BOOLEAN] = {
d: Data ← NARROW[self.data];
RETURN[NOT CGClipper.Empty[d.clipper]];
};
UserToDevice: PUBLIC PROC[self: Context, x, y: REAL, rel: BOOLEAN] RETURNS[tx, ty: REAL] = {
d: Data ← NARROW[self.data];
m: CGMatrix.Ref ← d.matrix;
w: Vec;
IF rel THEN w ← CGMatrix.MapRel[m,[x,y]]
ELSE w ← CGMatrix.Map[m,[x,y]];
RETURN[w.x, w.y];
};
DeviceToUser: PUBLIC PROC[self: Context, tx, ty: REAL, rel: BOOLEAN] RETURNS[x, y: REAL] = {
d: Data ← NARROW[self.data];
m: CGMatrix.Ref ← d.matrix;
w: Vec;
IF rel THEN w ← CGMatrix.InvRel[m,[tx,ty]]
ELSE w ← CGMatrix.Inv[m,[tx,ty]];
RETURN[w.x, w.y];
};
GetYMode: PUBLIC PROC[self: Context] RETURNS[YMode] = {
d: Data ← NARROW[self.data];
RETURN[IF d.yUp THEN bottomUp ELSE topDown];
};
SetYMode: PUBLIC PROC[self: Context, mode: YMode] = {
d: Data ← NARROW[self.data];
yUp: BOOLEAN ← (mode=bottomUp);
IF d.yUp#yUp THEN {
CGContext.TouchMatrix[d];
CGMatrix.Concat[d.matrix,1,0,0,-1];
d.yUp ← yUp;
};
};
Disable: PUBLIC PROC[self: Context] = {
d: Data ← NARROW[self.data];
d.device ← CGDummyDevice.Get[];
d.dbase ← NIL; d.drast ← 0; d.haveRaster ← FALSE;
};
MoveDeviceRectangle: PUBLIC PROC[self: Context,
width, height, fromX, fromY, toX, toY: NAT] = {
d: Data ← NARROW[self.data];
device: CGDevice.Ref ← d.device;
IF device.MoveBlock#NIL THEN device.MoveBlock[self: device,
width: width, height: height, fromX: fromX, fromY: fromY, toX: toX, toY: toY]
ELSE SIGNAL Graphics.Warning[notImplemented];
};
-- Miscellaneous utility procedures --
GenPath: PROC[d: Data, path: Path, parityFill, exclude: BOOLEAN] RETURNS[CGArea.Ref] = {
area: CGArea.Ref ← d.area;
IF NOT CGPath.Empty[path] THEN {
matrix: CGMatrix.Ref ← d.matrix;
box: Box ← MapBox[matrix, CGPath.Bounds[path]];
clipper: CGClipper.Ref ← d.clipper;
reducer: CGReducer.Ref ← d.reducer;
IF CGClipper.TestBox[clipper, box, reducer] THEN {
EnterPath[path, matrix, reducer];
CGReducer.Generate[self: reducer, area: area, exclude: exclude, oddwrap: parityFill];
};
};
RETURN[area];
};
EnterPath: PROC[path: Path, m: CGMatrix.Ref, reducer: CGReducer.Ref] = {
lp: Vec;
Map: PROC[v: Vec] RETURNS[Vec] = { RETURN[CGMatrix.Map[m, v]] };
Vertex: PROC[v: Vec] = { CGReducer.Vertex[reducer, lp ← v] };
Curve: PROC[v1, v2, v3: Vec] = {
eps: REAL = 1.5;
maxdepth: NAT = 10;
Divide: PROC[b: CGCubic.Bezier, depth: NAT ← 0] = {
IF depth>=maxdepth OR CGCubic.Flat[b, eps] THEN CGReducer.Vertex[reducer, b.b3]
ELSE { b1, b2: CGCubic.Bezier; [b1, b2] ← CGCubic.Split[b];
Divide[b1, depth+1]; Divide[b2, depth+1] };
};
Divide[[lp, v1, v2, v3]]; lp ← v3;
};
Close: PROC = { CGReducer.Close[reducer] };
CGPath.MapAndFilter[path, Map, Vertex, Vertex, Curve, Close];
};
MapBox: PUBLIC PROC[m: CGMatrix.Ref, box: Box] RETURNS[Box] = {
mbox: Box;
IF m.rectangular THEN {
a: Vec ← CGMatrix.Map[m,[box.xmin,box.ymin]];
b: Vec ← CGMatrix.Map[m,[box.xmax,box.ymax]];
IF a.x<=b.x THEN { mbox.xmin ← a.x; mbox.xmax ← b.x }
ELSE { mbox.xmin ← b.x; mbox.xmax ← a.x };
IF a.y<=b.y THEN { mbox.ymin ← a.y; mbox.ymax ← b.y }
ELSE { mbox.ymin ← b.y; mbox.ymax ← a.y };
}
ELSE {
a: Vec ← CGMatrix.Map[m,[box.xmin,box.ymin]];
b: Vec ← CGMatrix.Map[m,[box.xmax,box.ymin]];
c: Vec ← CGMatrix.Map[m,[box.xmax,box.ymax]];
d: Vec ← CGMatrix.Map[m,[box.xmin,box.ymax]];
mbox ← [xmin: MIN[a.x, b.x, c.x, d.x], ymin: MIN[a.y, b.y, c.y, d.y],
xmax: MAX[a.x, b.x, c.x, d.x], ymax: MAX[a.y, b.y, c.y, d.y]];
};
RETURN[mbox];
};
InvBox: PUBLIC PROC[m: CGMatrix.Ref, box: Box] RETURNS[Box] = {
mbox: Box;
IF m.rectangular THEN {
a: Vec ← CGMatrix.Inv[m,[box.xmin,box.ymin]];
b: Vec ← CGMatrix.Inv[m,[box.xmax,box.ymax]];
IF a.x<=b.x THEN { mbox.xmin ← a.x; mbox.xmax ← b.x }
ELSE { mbox.xmin ← b.x; mbox.xmax ← a.x };
IF a.y<=b.y THEN { mbox.ymin ← a.y; mbox.ymax ← b.y }
ELSE { mbox.ymin ← b.y; mbox.ymax ← a.y };
}
ELSE {
a: Vec ← CGMatrix.Inv[m,[box.xmin,box.ymin]];
b: Vec ← CGMatrix.Inv[m,[box.xmax,box.ymin]];
c: Vec ← CGMatrix.Inv[m,[box.xmax,box.ymax]];
d: Vec ← CGMatrix.Inv[m,[box.xmin,box.ymax]];
mbox ← [xmin: MIN[a.x, b.x, c.x, d.x], ymin: MIN[a.y, b.y, c.y, d.y],
xmax: MAX[a.x, b.x, c.x, d.x], ymax: MAX[a.y, b.y, c.y, d.y]];
};
RETURN[mbox];
};
GenBox: PUBLIC PROC[d: Data, box: Box, m: CGMatrix.Ref, exclude: BOOLEAN ← FALSE]
RETURNS[CGArea.Ref] = {
clipper: CGClipper.Ref ← d.clipper;
reducer: CGReducer.Ref ← d.reducer;
area: CGArea.Ref ← d.area;
IF m.rectangular AND NOT exclude THEN {
mbox: Box;
a: Vec ← CGMatrix.Map[m,[box.xmin,box.ymin]];
b: Vec ← CGMatrix.Map[m,[box.xmax,box.ymax]];
IF a.x<=b.x THEN { mbox.xmin ← a.x; mbox.xmax ← b.x }
ELSE { mbox.xmin ← b.x; mbox.xmax ← a.x };
IF a.y<=b.y THEN { mbox.ymin ← a.y; mbox.ymax ← b.y }
ELSE { mbox.ymin ← b.y; mbox.ymax ← a.y };
CGClipper.GenerateBox[clipper,mbox,reducer,area];
}
ELSE {
CGClipper.Load[clipper,reducer];
CGReducer.Vertex[reducer,CGMatrix.Map[m,[box.xmin,box.ymin]]];
CGReducer.Vertex[reducer,CGMatrix.Map[m,[box.xmax,box.ymin]]];
CGReducer.Vertex[reducer,CGMatrix.Map[m,[box.xmax,box.ymax]]];
CGReducer.Vertex[reducer,CGMatrix.Map[m,[box.xmin,box.ymax]]];
CGReducer.Close[reducer];
CGReducer.Generate[self: reducer, area: area, exclude: exclude];
};
RETURN[area];
};
}.