-- File CIFDrcScan.mesa
-- Written by Dan Fitzpatrick and Martin Newell, August 1980
-- Last updated: March 25, 1981 4:31 PM

DIRECTORY

BitBltDefs: FROM "BitBltDefs",
--CGraphicsDefs: FROM "CGraphicsDefs" USING [Transform],
CIFDevicesDefs: FROM "CIFDevicesDefs" USING [MaxLENGTHLayerArray, LENGTHLayerArray],
CIFOutputDefs: FROM "CIFOutputDefs" USING [SetSorting],
CIFDrcScanDefs: FROM "CIFDrcScanDefs",
CIFDrcNMOSDefs: FROM "CIFDrcNMOSDefs" USING[InitDrcNMOS, FinishDrcNMOS, NewSwath, CheckTrap],
CIFDrcUtilsDefs: FROM "CIFDrcUtilsDefs" USING[Edge, EdgeRecord, MakeEdge, FreeEdge, WriteLongOctal, WriteFloat],
IntDefs: FROM "IntDefs" USING[IBoundBox],
IODefs: FROM "IODefs" USING [SP, CR, WriteString, WriteLine, WriteChar,
WriteDecimal];
--MoreTrapezoidDefs: FROM "MoreTrapezoidDefs" USING [SetOutputTrapezoid,
--
GetDefaultOutputTrapezoid],
--TrapezoidDefs: FROM "TrapezoidDefs" USING [TrapezoidBlock],
--XDisplayDefs: FROM "XDisplayDefs";

CIFDrcScan: PROGRAM
IMPORTS CIFDevicesDefs, CIFDrcNMOSDefs, CIFDrcUtilsDefs, CIFOutputDefs, IntDefs, IODefs
EXPORTS CIFDrcScanDefs =

BEGIN OPEN CIFDevicesDefs, CIFDrcNMOSDefs, CIFDrcUtilsDefs, CIFOutputDefs, IntDefs, IODefs;


-- Drc procedures

DrcScanStart: PUBLIC PROCEDURE [fileName: STRING] =
BEGIN
l,r,b,t: LONG INTEGER;
[l,r,b,t] ← IBoundBox[];
--Initialize EdgeList and ActiveList
EdgeList ← EdgeListEl ← ActiveList ← ALL[NIL];

SetSorting[incy];
YNext ← YCurr ← YPrev ← ALL[b]; --i.e.not set
VStripeBottom ← b;
VStripeTop ← b + 3*Lambda;
InitDrcNMOS[fileName, Lambda, l,b,r,t];
END;

DrcScanEnd: PUBLIC PROCEDURE [ymax: REAL] =
BEGIN --ymax is REAL, that gives ymax of picture
l,r,b,t: LONG INTEGER;
[l,r,b,t] ← IBoundBox[];
ymax ← t;
ymax ← t + 9 * Lambda;
UNTIL VStripeBottom>ymax+1 DO --1+ is kludge to make sure it all gets out
DrcNextStripe[];
ENDLOOP;
NewSwath[VStripeBottom,VStripeBottom];
FinishDrcNMOS[];
END;

DrcTrigger: PUBLIC PROCEDURE [bottom: REAL] =
--used to trigger scan conversion up to bottom of object about to be received
BEGIN
WHILE bottom>=VStripeTop DO DrcNextStripe[]; ENDLOOP;
--***IF VStripeBottom < bottom THEN NewSwath[VStripeBottom,bottom];** The usual case
--***DrcScanConvert[bottom, FALSE];
END;

--Private procedures--

DrcNextStripe: PROCEDURE =
BEGIN --finish off and output current stripe, and step to next
NewSwath[VStripeBottom,VStripeTop];
DrcScanConvert[VStripeTop, FALSE];
DrcScanConvert[VStripeTop, TRUE];
VStripeBottom ← VStripeTop;
VStripeTop ← VStripeTop + 3*Lambda;
END;

MakeLongPointer: PROCEDURE[low,high: UNSPECIFIED]
RETURNS[LONG POINTER] = MACHINE CODE
BEGIN --nothing to do-- END;

BreakLongPointer: PROCEDURE[lptr: LONG POINTER]
RETURNS[low,high: UNSPECIFIED] = MACHINE CODE
BEGIN --nothing to do-- END;

DrcScanConvert: PROCEDURE [upto: REAL, forceOutput: BOOLEAN] =
-- Run the scan converter up to y=upto
-- forceOutput forces output up to and including upto
BEGIN
edge,prevedge: Edge;
edgetype: EdgeType;
newActiveList,pendingFreeEdges,nextedge: Edge;
ptrnewActiveList: LONG POINTER TO Edge;
i,layer: CARDINAL;
segleft,trapleft: Edge;
adepth,edepth: INTEGER;
StartSegment: PROCEDURE[e: Edge] = BEGIN segleft ← e; END;
EndSegment: PROCEDURE[e: Edge] =
BEGIN
HorLine[XatY[segleft,YCurr[layer]], XatY[e,YCurr[layer]], YCurr[layer]];
END;
StartTrap: PROCEDURE[e: Edge] =
BEGIN
trapleft ← e;
END;
EndTrap: PROCEDURE[right: Edge] =
BEGIN
left: Edge ← trapleft;
IF left.mate#right THEN
BEGIN --pairing changed or NIL
OutTrap[left,YPrev[layer]];
OutTrap[right,YPrev[layer]];
left.mate ← right;
right.mate ← left;
END;
IF left.flagout OR right.flagout OR forceOutput THEN
BEGIN
OutTrap[left,YCurr[layer]];
left.flagout ← right.flagout ← FALSE; --mustn’t do this in OutTrap because of other calls to it
END;
END;
OutTrap: PROCEDURE[e1: Edge, ytop: REAL] =
BEGIN
e2: Edge ← e1.mate;
IF e2#NIL THEN
BEGIN
CheckTrap[e1,e2,e1.lastouty,ytop,layer];
DrawTrap[e1,e1.lastouty,e2,ytop,layer];
e1.lastouty ← e2.lastouty ← ytop;
e1.mate ← e2.mate ← NIL;
END;
END;
UpdateYNextNewALAndprevedge: PROCEDURE[edge: Edge] =
--Update YNext, newActiveList, and prevedge
BEGIN
--YNext:
YNext[layer] ←
CheckIntersection[prevedge,edge,YCurr[layer],MIN[YNext[layer],edge.yend]];
--newActiveList:
edge.next ← NIL;
ptrnewActiveList↑ ← edge;
ptrnewActiveList ← @edge.next;
--and prevedge:
prevedge ← edge;
END;
QueueForFreeEdge: PROCEDURE[edge: Edge] =
BEGIN
edge.next ← pendingFreeEdges;
pendingFreeEdges ← edge;
END;
FOR layer IN [0..LENGTHLayerArray) DO
UNTIL (forceOutput AND YPrev[layer]>=upto) OR
(~forceOutput AND YNext[layer]>=upto) OR
(ActiveList[layer]=NIL AND EdgeList[layer]=NIL)
DO
--in one pass through each ActiveList:
--
output trapezoids in previous swath
--
remove terminating edges from previous swath,
--
introduce newly entering ones,
--
drawing capping lines as necessary,
--
and find lowest upper y in YNext
adepth ← edepth ← 0;
[edge,edgetype] ← GetEdge[layer];
prevedge ← NIL;
YNext[layer] ← upto;
pendingFreeEdges ← NIL;
newActiveList ← EdgeListQueue ← NIL;
ptrnewActiveList ← @newActiveList;
IF YCurr[layer]=YPrev[layer] THEN
BEGIN --scan only to find YNext[layer] (and therefore to do intersections)
UNTIL edge=NIL DO
SELECT edgetype FROM
aterminatingUp,aterminatingDown => QueueForFreeEdge[edge];
acontinuingUp,acontinuingDown,estartingUp,estartingDown =>
UpdateYNextNewALAndprevedge[edge];
ENDCASE;
[edge,edgetype] ← GetEdge[layer];
ENDLOOP;
END
ELSE
BEGIN --normal case
UNTIL edge=NIL DO
SELECT edgetype FROM
aterminatingUp =>
BEGIN
IF adepth=0 THEN
BEGIN
edge.flagout ← TRUE;
StartTrap[edge];
IF edepth#0
THEN EndSegment[edge]
ELSE StartSegment[edge];
END;
adepth ← adepth+1;
QueueForFreeEdge[edge];
END;
aterminatingDown =>
BEGIN
adepth ← adepth-1;
IF adepth=0 THEN
BEGIN
edge.flagout ← TRUE;
EndTrap[edge];
IF edepth=0
THEN EndSegment[edge]
ELSE StartSegment[edge];
END;
QueueForFreeEdge[edge];
END;
acontinuingUp =>
BEGIN
UpdateYNextNewALAndprevedge[edge]; --must do this first incase intersection set .flagout
IF adepth=0 THEN
BEGIN
IF edepth#0 THEN
BEGIN
edge.flagout ← TRUE;
EndSegment[edge];
END;
StartTrap[edge];
END
ELSE
BEGIN
IF edepth=0 THEN
BEGIN
edge.lastouty ← YCurr[layer];
EndSegment[edge];
END;
END;
adepth ← adepth+1;
edepth ← edepth+1;
END;
acontinuingDown =>
BEGIN
UpdateYNextNewALAndprevedge[edge];
adepth ← adepth-1;
edepth ← edepth-1;
IF adepth=0 THEN
BEGIN
IF edepth#0 THEN
BEGIN
edge.flagout ← TRUE;
StartSegment[edge];
END;
EndTrap[edge];
END
ELSE
BEGIN
IF edepth=0 THEN
BEGIN
edge.lastouty ← YCurr[layer];
StartSegment[edge];
END;
END;
END;
estartingUp =>
BEGIN
UpdateYNextNewALAndprevedge[edge];
IF edepth=0 THEN
IF adepth#0
THEN EndSegment[edge]
ELSE StartSegment[edge];
edepth ← edepth+1;
END;
estartingDown =>
BEGIN
UpdateYNextNewALAndprevedge[edge];
edepth ← edepth-1;
IF edepth=0 THEN
IF adepth=0
THEN EndSegment[edge]
ELSE StartSegment[edge];
END;
ENDCASE;
[edge,edgetype] ← GetEdge[layer];
ENDLOOP;
END;
ActiveList[layer] ← newActiveList;
FOR edge ← pendingFreeEdges,nextedge UNTIL edge=NIL DO
nextedge ← edge.next;
FreeEdge[edge];
ENDLOOP;
FOR edge ← EdgeListQueue,nextedge UNTIL edge=NIL DO
nextedge ← edge.next;
EnterEdge[layer,edge];
ENDLOOP;
IF EdgeList[layer]#NIL THEN YNext[layer] ← MIN[YNext[layer],EdgeList[layer].ystart];
YPrev[layer] ← YCurr[layer];
YCurr[layer] ← YNext[layer];
ENDLOOP; --UNTIL (...
ENDLOOP; --FOR layer ...
FOR i IN [0..LENGTHLayerArray) DO --because next entry requires re-finding YNext[layer]
YCurr[layer] ← YPrev[layer];
ENDLOOP;
END;

GetEdge: PROCEDURE[layer: CARDINAL] RETURNS[edge: Edge, edgeType: EdgeType] =
--Get next edge for new active list - filter out zero height edges
BEGIN
DO
[edge,edgeType] ← GetEdge1[layer];
IF edge=NIL OR edge.yend>edge.ystart THEN EXIT;
FreeEdge[edge]; --exists and zero height
ENDLOOP;
END;

GetEdge1: PROCEDURE[layer: CARDINAL] RETURNS[edge: Edge, edgeType: EdgeType] =
--Get next edge for new active list
BEGIN
peekAtEdgeList: Edge ← PeekAtEdgeList[layer];
TakeFromActiveList: PROCEDURE =
BEGIN
edge ← ActiveList[layer];
ActiveList[layer] ← ActiveList[layer].next;
edgeType ←IF edge.yend<=YCurr[layer]
THEN IF edge.up THEN aterminatingUp ELSE aterminatingDown
ELSE IF edge.up THEN acontinuingUp ELSE acontinuingDown;
END;
TakeFromEdgeList: PROCEDURE =
BEGIN
edge ← GetFromEdgeList[layer];
edgeType ← IF edge.up THEN estartingUp ELSE estartingDown;
--tidy it up
edge.flagout ← FALSE;
edge.lastouty ← edge.ystart;
edge.mate ← NIL;
END;

SELECT TRUE FROM
ActiveList[layer]=NIL=>
BEGIN --take it from EdgeList provided starting at YCurr
IF peekAtEdgeList#NIL AND peekAtEdgeList.ystart <= YCurr[layer] THEN
TakeFromEdgeList[]
ELSE
BEGIN
edge ← NIL;
edgeType ← null;
END;
END;
peekAtEdgeList=NIL => TakeFromActiveList[]; -- must be non-NIL if get here
ENDCASE => --both lists are candidates
IF peekAtEdgeList.ystart <= YCurr[layer] AND
EdgeLessThan[peekAtEdgeList,ActiveList[layer], YCurr[layer]]
THEN TakeFromEdgeList[]
ELSE TakeFromActiveList[];
END;

CheckIntersection: PROCEDURE[e1,e2: Edge, ylow,yhigh: REAL] RETURNS[y: REAL] =
--check for and deal with intersection of e1 and e2 between ylow and yhigh
--Returns either yhigh or y of intersection
--assumes edges are in order at ylow
BEGIN
IF e1#NIL AND XatY[e1,yhigh]>XatY[e2,yhigh] THEN
BEGIN
xint,yint: REAL;
s1: REAL ← Slope[e1];
s2: REAL ← Slope[e2];
yint ← IF s1#s2 THEN
MAX[ylow, MIN[yhigh,
((e2.ystart*s2 - e1.ystart*s1)-(e2.xstart - e1.xstart))/(s2 - s1)]]
ELSE yhigh;
xint ← XatY[IF e1.vert THEN e1 ELSE e2,yint];
--put upper fragments back onto edgeList
IF yint<e1.yend THEN
BEGIN
e1upper: Edge ← MakeEdge[xint,yint,Xend[e1],e1.yend,e1.up];
--mustn’t put it back on edgeList yet in case yint=ylow
QueueForEdgeList[e1upper];
END;
IF yint<e2.yend THEN
BEGIN
e2upper: Edge ← MakeEdge[xint,yint,Xend[e2],e2.yend,e2.up];
QueueForEdgeList[e2upper];
END;
--cut back e1 and e2
WITH e:e1 SELECT IF e1.vert THEN vertical ELSE oblique FROM
oblique => e.xend ← xint;
ENDCASE;
e1.yend ← yint;
WITH e:e2 SELECT IF e2.vert THEN vertical ELSE oblique FROM
oblique => e.xend ← xint;
ENDCASE;
e2.yend ← yint;
IF yint=ylow THEN e1.flagout ← e2.flagout ← TRUE;
RETURN[yint];
END
ELSE RETURN[yhigh];
END;

QueueForEdgeList: PROCEDURE[e: Edge] =
BEGIN
e.next ← EdgeListQueue;
EdgeListQueue ← e;
END;

XatY: PROCEDURE[edge: Edge, y: REAL] RETURNS[x: REAL] =
BEGIN
RETURN[
WITH e:edge SELECT IF edge.vert THEN vertical ELSE oblique FROM
oblique => --i.e. oblique
SELECT TRUE FROM
y=e.ystart => e.xstart,
y=e.yend => e.xend,
ENDCASE => e.xstart + (y-e.ystart)*e.slope, --must compute from consistent end!
ENDCASE => edge.xstart --i.e. vertical
];
END;

Xend: PROCEDURE[edge: Edge] RETURNS[xend: REAL] = INLINE
BEGIN
RETURN[WITH e:edge SELECT IF edge.vert THEN vertical ELSE oblique FROM
oblique => e.xend,
ENDCASE => edge.xstart --i.e.vertical
];
END;

Slope: PROCEDURE[edge: Edge] RETURNS[slope: REAL] = INLINE
BEGIN
RETURN[WITH e:edge SELECT IF edge.vert THEN vertical ELSE oblique FROM
oblique => e.slope,
ENDCASE => 0 --i.e.vertical
];
END;

HorLine: PROCEDURE[xleft,xright,y: REAL] =
BEGIN
IF xleft=xright THEN RETURN;
END;

DrawTrap: PROCEDURE[left: Edge, ystart: REAL, right: Edge, yend: REAL, layer: CARDINAL] =
BEGIN --draw left and right edges on screen for now
--left edge
END;

--AllocateVerticalEdge: PROCEDURE RETURNS[Edge] =
--BEGIN
--RETURN[AllocateHeapNode[SIZE[vertical EdgeRecord]]];
--END;

--AllocateObliqueEdge: PROCEDURE RETURNS[Edge] =
--BEGIN
--RETURN[AllocateHeapNode[SIZE[oblique EdgeRecord]]];
--END;

--FreeEdge: PROCEDURE[edge: Edge] =
--BEGIN
--FreeHeapNode[edge];
--END;

EnterEdge: PUBLIC PROCEDURE[layer: CARDINAL, edge: Edge] =
BEGIN
e,save,start: Edge;
y: REAL ← edge.ystart;
start ← EdgeListEl[layer];
EdgeListEl[layer] ← edge;
IF EdgeList[layer]=NIL THEN
BEGIN
edge.next ← NIL;
edge.mate ← NIL;
EdgeList[layer] ← edge;
RETURN;
END;
IF start=NIL THEN start ← EdgeList[layer];
--search forward
save ← NIL;
FOR e ← start, e.next UNTIL e=NIL DO
IF y<e.ystart OR (y=e.ystart AND EdgeLessThan[edge, e, y])
THEN GOTO searchbackwards;
save ← e;
REPEAT
searchbackwards =>
BEGIN --edge "<" e
FOR e ← e.mate, e.mate UNTIL e=NIL DO
IF e.ystart<y OR (e.ystart=y AND EdgeLessThan[e, edge, y])
THEN GOTO append;
REPEAT
append =>
BEGIN --append edge to e
edge.next ← e.next;
edge.mate ← e;
e.next.mate ← edge;
e.next ← edge;
RETURN;
END;
ENDLOOP;
--reached begin of list - insert edge before 1st element
edge.next ← EdgeList[layer];
edge.mate ← NIL;
EdgeList[layer].mate ← edge;
EdgeList[layer] ← edge;
RETURN;
END;
ENDLOOP;
--reached end of list - append edge to save
edge.next ← NIL;
edge.mate ← save;
save.next ← edge;
END;

PeekAtEdgeList: PROCEDURE[layer: CARDINAL] RETURNS[edge: Edge] =
--Returns next edge from EdgeList without removing it from EdgeList
BEGIN
edge ← EdgeList[layer];
END;

GetFromEdgeList: PROCEDURE[layer: CARDINAL] RETURNS[edge: Edge] =
--Returns next edge from EdgeList having removed it from EdgeList
BEGIN
edge ← EdgeList[layer];
IF edge#NIL THEN
BEGIN
EdgeList[layer] ← edge.next;
IF edge.next # NIL THEN edge.next.mate ← NIL;
EdgeListEl[layer] ← edge.next;
edge.mate ← NIL; --clean up
END;
END;

--

EdgeLessThan: PROCEDURE[e1,e2: Edge, y: REAL] RETURNS[BOOLEAN] =
BEGIN
--orders up/down within slope within x
x1: REAL ← XatY[e1,y];
x2: REAL ← XatY[e2,y];
RETURN[x1<x2 OR (x1=x2 AND (Slope[e1]<Slope[e2] OR (Slope[e1]=Slope[e2] AND e1.up)))];
END;

--for debugging:

PrintList: PUBLIC PROCEDURE[list: Edge, layer: CARDINAL] =
BEGIN
e: Edge;
WriteString["YPrev,YCurr,YNext["]; WriteDecimal[layer]; WriteString["]: "];
WriteFloat[YPrev[layer]]; WriteChar[’,];
WriteFloat[YCurr[layer]]; WriteChar[’,];
WriteFloat[YNext[layer]]; WriteChar[CR];
SELECT list FROM
ActiveList[layer] => WriteLine["ActiveList"];
EdgeList[layer] => WriteLine["EdgeList"];
ENDCASE;
WriteLongOctal[list]; WriteChar[CR];
FOR e ← list, e.next UNTIL e=NIL DO
WriteString["next:"]; WriteLongOctal[e.next]; WriteChar[SP];
WriteString["xstart:"]; WriteFloat[e.xstart]; WriteChar[SP];
WriteString["ystart:"]; WriteFloat[e.ystart]; WriteChar[SP];
WriteString["xend:"]; WriteFloat[Xend[e]]; WriteChar[SP];
WriteString["yend:"]; WriteFloat[e.yend]; WriteChar[SP];
WriteString["slope:"]; WriteFloat[Slope[e]]; WriteChar[SP];
WriteString["slant:"];
WriteString[IF e.vert THEN "vertical" ELSE "oblique"]; WriteChar[SP];
WriteString["up:"]; WriteChar[IF e.up THEN ’T ELSE ’F]; WriteChar[SP];
WriteString["flagout:"]; WriteChar[IF e.flagout THEN ’T ELSE ’F]; WriteChar[SP];
WriteString["lastouty:"]; WriteFloat[e.lastouty]; WriteChar[SP];
WriteString["mate:"]; WriteLongOctal[e.mate]; WriteChar[SP];
WriteString["XatYCurr: "]; WriteFloat[XatY[e,YCurr[layer]]]; WriteChar[CR];
WriteChar[CR];
ENDLOOP;
END;

SetLambda: PUBLIC PROCEDURE[l:REAL] =
BEGIN
Lambda ← l*Shrinkage;
END;

EdgeList,EdgeListEl,ActiveList: ARRAY [0..MaxLENGTHLayerArray) OF Edge;
EdgeListQueue: Edge; --isolates edges being thrown back onto edge list
EdgeType: TYPE = {acontinuingUp, acontinuingDown, aterminatingUp, aterminatingDown,
estartingUp, estartingDown, null};

YNext,YCurr,YPrev: ARRAY [0..MaxLENGTHLayerArray) OF REAL;
VStripeBottom,VStripeTop: REAL; --limits to be mapped onto current stripe
VStripeWidth: CARDINAL ← 3472; --***should be wired to required buffer width

Num: REAL ← 999;
Den: REAL ← 1000;
Shrinkage: REAL ← Num/Den;
-- Shrink lambda by 0.1% to avoid roundoff errors

Lambda: REAL ← 250*Shrinkage;

END.