CDMEBESUtilitiesImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
McCreight, December 11, 1986 1:32:24 pm PST
DIRECTORY
Atom, Basics, CD, CDBasics, CDMEBES, CDVArrow, CDViewer, CStitching, D2Basic, IO, Rope, TerminalIO;
CDMEBESUtilitiesImpl: CEDAR PROGRAM
IMPORTS Atom, CDBasics, CDViewer, CDVArrow, CStitching, IO, Rope, TerminalIO
EXPORTS CDMEBES =
BEGIN OPEN CDMEBES;
SendCommand: PUBLIC PROC [ s: IO.STREAM, comm: REF ] =
BEGIN
sizeInWords: NAT;
WITH comm SELECT FROM
c: REF DrawRectangle => sizeInWords ← SIZE[DrawRectangle];
c: REF DrawTrapezoid => sizeInWords ← SIZE[DrawTrapezoid];
c: REF BasicCommand => sizeInWords ← SIZE[BasicCommand];
c: REF Mode12StartStripe => sizeInWords ← SIZE[Mode12StartStripe];
c: REF Mode12StartDrawing => sizeInWords ← SIZE[Mode12StartDrawing];
c: REF ExtAddrModeStartDrawing => sizeInWords ← SIZE[ExtAddrModeStartDrawing];
c: REF SegDirectoryEntry => sizeInWords ← SIZE[SegDirectoryEntry];
c: REF ExtAddrModeStartStripe => sizeInWords ← SIZE[ExtAddrModeStartStripe];
c: REF StartSegment => sizeInWords ← SIZE[StartSegment];
c: REF Date => sizeInWords ← SIZE[Date];
c: REF PatternFileName => sizeInWords ← SIZE[PatternFileName];
c: REF CARDINAL => sizeInWords ← SIZE[CARDINAL];
ENDCASE => ERROR;
TRUSTED {s.UnsafePutBlock[[base: LOOPHOLE[comm], startIndex: 0, count: Basics.bytesPerWord*sizeInWords]]};
END;
EBES IO Stream Types and Procedures
EBESStreamStateRef: TYPE = REF EBESStreamState;
EBESStreamState: TYPE = RECORD [
dest: IO.STREAM,
destIndexMod: [0..mebesBlockSize),
eor: BOOL,
buf: REF TEXT];
ebesStreamProcs: REF IO.StreamProcs = IO.CreateStreamProcs[
variety: output,
class: $EBESOutputStream,
putChar: PutEBESChar,
unsafePutBlock: PutEBESBlock,
flush: FlushEBES,
getIndex: GetIndexEBES,
setIndex: SetIndexEBES,
close: CloseEBES
];
EBESOpen: PUBLIC PROC [ dest: IO.STREAM, eor: BOOLTRUE ] RETURNS [ self: IO.STREAM ] =
BEGIN
state: EBESStreamStateRef = NEW[EBESStreamState ← [
dest: dest,
destIndexMod: dest.GetIndex[] MOD mebesBlockSize,
eor: eor,
buf: NEW[TEXT[mebesBlockSize]]
]];
state.buf.length ← 0;
self ← IO.CreateStream[
streamProcs: ebesStreamProcs,
streamData: state
];
END;
PutEBESChar: PROC [ self: IO.STREAM, char: CHAR ] =
TRUSTED BEGIN
state: EBESStreamStateRef = NARROW[self.streamData];
FlushEBESIfNecessary[state, 1];
state.buf[state.buf.length] ← char;
state.buf.length ← state.buf.length+1;
END;
PutEBESBlock: PROC [ self: IO.STREAM, block: IO.UnsafeBlock ] =
TRUSTED BEGIN
state: EBESStreamStateRef = NARROW[self.streamData];
FlushEBESIfNecessary[state, block.count];
FOR i: INT IN [0..block.count) DO
CharArrayPtr: TYPE = LONG POINTER TO PACKED ARRAY [0..0) OF CHAR;
state.buf[state.buf.length+i] ← LOOPHOLE[block.base, CharArrayPtr][block.startIndex+i];
ENDLOOP;
state.buf.length ← state.buf.length+block.count;
END;
FlushEBESIfNecessary: PROC [state: EBESStreamStateRef, newBytes: NAT] = INLINE
BEGIN
IF mebesBlockSize<state.destIndexMod+state.buf.length+newBytes+
(IF state.eor THEN 2*SIZE[BasicCommand] -- for end of record -- ELSE 0) THEN
DoFlushEBES[state];
END;
FlushEBES: PROC [ self: IO.STREAM ] = {DoFlushEBES[NARROW[self.streamData]]};
DoFlushEBES: PROC [ state: EBESStreamStateRef ] =
BEGIN
IF state.buf.length>0 OR state.destIndexMod>0 THEN
BEGIN
IF state.eor THEN
BEGIN
IF state.buf.length+2 <= mebesBlockSize-state.destIndexMod THEN
TRUSTED BEGIN -- append an end-of-record command to the buffer
endRec: PACKED ARRAY [0..2) OF CHAR = LOOPHOLE[endRecord];
state.buf[state.buf.length] ← endRec[0];
state.buf[state.buf.length+1] ← endRec[1];
state.buf.length ← state.buf.length+2;
END
ELSE ERROR; -- can't terminate buffer properly
END;
FOR i: INT IN [state.buf.length..mebesBlockSize-state.destIndexMod) DO
state.buf[i] ← 000C; -- pad out with 0's
ENDLOOP;
state.buf.length ← mebesBlockSize-state.destIndexMod;
state.dest.PutBlock[state.buf];
END;
state.buf.length ← 0;
state.destIndexMod ← 0;
END;
GetIndexEBES: PROC [ self: IO.STREAM ] RETURNS [ index: INT ] =
BEGIN
state: EBESStreamStateRef = NARROW[self.streamData];
index ← state.dest.GetIndex[]+state.buf.length;
END;
SetIndexEBES: PROC [ self: IO.STREAM, index: INT ] =
BEGIN
state: EBESStreamStateRef = NARROW[self.streamData];
IF state.buf.length>0 THEN state.dest.PutBlock[state.buf];
state.buf.length ← 0;
state.dest.SetIndex[index];
state.destIndexMod ← state.dest.GetIndex[] MOD mebesBlockSize;
END;
CloseEBES: PROC [ self: IO.STREAM, abort: BOOLFALSE ] =
BEGIN
state: EBESStreamStateRef = NARROW[self.streamData];
self.Flush[];
state.dest.Close[];
END;
Auxiliary Procedures
UserWantsToDebug: SIGNAL = CODE;
ComplainAt: PUBLIC PROC [ ms: MaskState, pos: TadPosition, explanation: ROPENIL, choice: LIST OF ROPENIL ] RETURNS [ chosen: NAT ] =
BEGIN
radius: Tad = 20*ms.nmPerLambda*ms.tadsPerNm;
cdRect: CD.Rect = ScaleTadToCD[ms, [x1: pos.x-radius, y1: pos.y-radius, x2: pos.x+radius, y2: pos.y+radius]];
CDVArrow.ShowArrow[ms.design, CDBasics.Center[cdRect]];
TerminalIO.PutRope[IO.PutFR["\nMEBES generation problem: %g\n", IO.rope[explanation]]];
chosen ← 0;
DO ENABLE UNWIND => CDVArrow.ShowArrow[ms.design, ms.viewerArrow];
selected: NAT;
viewerList: CDViewer.ViewerList = CDViewer.ViewersOf[ms.design];
CDVArrow.ShowArrow[ms.design, CDBasics.Center[cdRect]];
IF viewerList # NIL THEN CDViewer.ShowAndScale[viewerList.first, cdRect];
SELECT (selected ← TerminalIO.RequestSelection[header: "Action..", choice: CONS["Abort", CONS["Debug", choice]]]) FROM
0 => {chosen ← 0; EXIT};
1 => ERROR ABORTED;
2 => {SIGNAL UserWantsToDebug; LOOP};
ENDCASE => {chosen ← selected-2; EXIT};
ENDLOOP;
CDVArrow.ShowArrow[ms.design, ms.viewerArrow];
END;
NewTesselation: PUBLIC PROC [ initValue: REFNIL ] RETURNS [ tess: Tesselation ] =
BEGIN
tess ← CStitching.NewTesselation[];
tess.ChangeRect[CDBasics.universe, initValue];
END;
DisposeTesselation: PUBLIC PROC [ tess: Tesselation ] RETURNS [ Tesselation ] =
BEGIN
CStitching.ResetTesselation[plane: tess];
RETURN[NIL];
END;
RopeNeeded: PUBLIC SIGNAL [ ref: REF REF ] = CODE;
ToRope: PUBLIC PROC [ ref: REF ] RETURNS [ rope: ROPE ] =
BEGIN
IF ref = NIL THEN rope ← NIL
ELSE WITH ref SELECT FROM
r: ROPE => rope ← r;
rt: REF TEXT => rope ← Rope.FromRefText[rt];
a: ATOM => rope ← Atom.GetPName[a];
ri: REF INT => rope ← IO.PutFR[format: "%d", v1: IO.int[ri^]];
ENDCASE =>
BEGIN
refRef: REF REF = NEW[REF ← ref];
SIGNAL RopeNeeded[refRef];
rope ← ToRope[refRef^ ! RopeNeeded => GOTO NoHelp];
EXITS
NoHelp => ERROR;
END;
END;
ScaleCDToTad: PUBLIC PROC [ ms: MaskState, cdr: CD.Rect ] RETURNS [ tr: TadRect ] =
BEGIN
tr ← CDBasics.ReInterpreteRect[
[x1: RatIntMul[ms.scale, cdr.x1],
y1: RatIntMul[ms.scale, cdr.y1],
x2: RatIntMul[ms.scale, cdr.x2],
y2: RatIntMul[ms.scale, cdr.y2]]];
END;
ScaleTadToCD: PUBLIC PROC [ ms: MaskState, tr: TadRect ] RETURNS [ cdr: CD.Rect ] =
BEGIN
cdr ← CDBasics.ReInterpreteRect[
[x1: RatIntDiv[ms.scale, tr.x1],
y1: RatIntDiv[ms.scale, tr.y1],
x2: RatIntDiv[ms.scale, tr.x2],
y2: RatIntDiv[ms.scale, tr.y2]]];
END;
Bloat: PUBLIC PROC [ r: TadRect, deltaDiameter: Tad ] RETURNS [ br: TadRect ] =
BEGIN
b: Tad = deltaDiameter/2; -- split in "half" for radius
IF deltaDiameter<0 THEN ERROR;
IF (deltaDiameter MOD 2) # 0 THEN ERROR TadTooLarge;
Be careful not to exceed the limits of a D2Basic.Number, even temporarily
br ← [x1: MAX[FIRST[D2Basic.Number]+b, r.x1]-b,
y1: MAX[FIRST[D2Basic.Number]+b, r.y1]-b,
x2: MIN[LAST[D2Basic.Number]-b, r.x2]+b,
y2: MIN[LAST[D2Basic.Number]-b, r.y2]+b];
END;
TadTooLarge: PUBLIC ERROR = CODE;
ScaleRect: PUBLIC PROC [ r: D2Basic.Rect, factor: Rational ] RETURNS [ sr: D2Basic.Rect ] =
BEGIN
sr ← CDBasics.ReInterpreteRect[
[x1: RatIntMul[factor, r.x1],
y1: RatIntMul[factor, r.y1],
x2: RatIntMul[factor, r.x2],
y2: RatIntMul[factor, r.y2]]];
END;
ScalePoint: PUBLIC PROC [ p: D2Basic.Vector, factor: Rational ] RETURNS [ sp: D2Basic.Vector ] =
{sp ← [x: RatIntMul[factor, p.x], y: RatIntMul[factor, p.y]]};
Basic Rational Arithmetic
RatAdd: PUBLIC PROC [ r1, r2: Rational ] RETURNS [ Rational ] =
{RETURN[ReduceRational[[num: r2.denom*r1.num+r1.denom*r2.num, denom: r1.denom*r2.denom]]]};
RatNeg: PUBLIC PROC [ r: Rational ] RETURNS [ Rational ] =
{RETURN[[num: -r.num, denom: r.denom]]};
RatMul: PUBLIC PROC [ r1, r2: Rational ] RETURNS [ Rational ] =
{RETURN[ReduceRational[[num: r1.num*r2.num, denom: r1.denom*r2.denom]]]};
RatInv: PUBLIC PROC [ r: Rational ] RETURNS [ Rational ] =
{RETURN[ReduceRational[[num: r.denom, denom: r.num]]]};
RatIntMul: PROC [ mul: Rational, z: INT ] RETURNS [ INT ] =
INLINE {RETURN[(mul.num*z)/mul.denom]};
RatIntDiv: PUBLIC PROC [ div: Rational, z: INT ] RETURNS [ INT ] =
INLINE {RETURN[(div.denom*z)/div.num]};
ReduceRational: PUBLIC PROC [ r: Rational ] RETURNS [ Rational ] =
BEGIN
gcd: INT = IF r.num=0 THEN r.denom ELSE GCD[r.num, r.denom];
RETURN[[num: r.num/gcd, denom: r.denom/gcd]];
END;
GCD: PUBLIC PROC [ m, n: INT ] RETURNS [ INT ] =
BEGIN
r: INT;
SELECT m FROM
<0 => m ← -m;
ENDCASE => NULL;
SELECT n FROM
<0 => n ← -n;
>0 => NULL;
ENDCASE => RETURN[m];
r ← m MOD n;
WHILE r>0 DO m ← n; n ← r; r ← m MOD n; ENDLOOP;
RETURN[n];
END;
Ceiling: PUBLIC PROC [ r: Rational ] RETURNS [ c: INT ] =
BEGIN
c ← r.num/r.denom;
c = SGN[r.num]*SGN[r.denom]*FLOOR[ABS[r.num]/ABS[r.denom]] if r.denom#0
IF ((r.num>0) = (r.denom>0)) AND r.num MOD r.denom # 0 THEN c ← c+1;
END;
END. -- of CDMEBESUtilitiesImpl
McCreight December 10, 1986 3:45:16 pm PST
Denotching by a diameter that was an odd number of pixels moved the geometry by one pixel. Denotching consists of a positive Enlarge followed by a negative Enlarge. That called Bloat first on the geometry and then on its complement. The problem is that for odd pixel diameters, Bloat grew a smaller number of pixels on the bottom left than on the top right. Doing that first on the geometry and then on its complement had the effect of moving the geometry one pixel up and to the right. The fix is to have the units of measure in the Tesselations be Tad's, and to complain when Bloating by a non-even Tad diameter.
changes to: CDMEBESUtilitiesImpl, CDMEBESGeomFnsImpl, FlushEBES