-- File CIFRamtekScan.mesa
-- Written by Martin Newell, June 1980
-- Last updated: September 29, 1981 10:28 AM by Pasco

DIRECTORY

CIFDevicesDefs: FROM "CIFDevicesDefs" USING [MaxLENGTHLayerArray,
LENGTHLayerArray],
CIFRamtekScanDefs: FROM "CIFRamtekScanDefs",
CIFRamtekTypeDefs: FROM "CIFRamtekTypeDefs" USING [Edge, EdgeRecord,
EdgeAngle, EdgeType],
CIFRamtekUtilsDefs: FROM "CIFRamtekUtilsDefs" USING[ VersatecMakeEdge, FreeEdge, EdgeLessThan, XatY, HorColorLine, DrawTrap],
IODefs: FROM "IODefs" USING [SP, CR, WriteString, WriteLine, WriteOctal,
WriteChar, WriteDecimal],
Real: FROM "Real" USING [WriteReal];

CIFRamtekScan: PROGRAM
IMPORTS CIFDevicesDefs, CIFRamtekUtilsDefs, IODefs, Real
EXPORTS CIFRamtekScanDefs =

BEGIN OPEN CIFDevicesDefs, CIFRamtekTypeDefs, CIFRamtekUtilsDefs, IODefs, Real;

InitRamtekScan: PUBLIC PROCEDURE =
BEGIN
--Initialize EdgeList and ActiveList
EdgeList ← EdgeListEl ← ActiveList ← ALL[NIL];
YNext ← YCurr ← YPrev ← ALL[0]; --i.e.not set
END;

RamtekScanConvert: PUBLIC 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: 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
HorColorLine[XatY[segleft,YCurr[layer]], XatY[e,YCurr[layer]], YCurr[layer], 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
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;
AddToEdgeList[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;

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 ← VersatecMakeEdge[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 ← VersatecMakeEdge[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;

--
The next 3 procedures (AddToEdgeList, PeekAtEdgeList, GetFromEdgeList) implement
--
the EdgeList and are subject to replacement

-- In this version the EdgeList is kept doubly linked and search for
--
insertion place is started at last-inserted Edge
--
Back pointers use the .mate field

--
AddToEdgeList: PROCEDURE[layer: CARDINAL, edge: Edge] =
--
BEGIN
--
eptr: POINTER TO Edge;
--
y: REAL ← edge.ystart;
--
FOR eptr ← @EdgeList[layer], @eptr.next UNTIL eptr↑=NIL DO
--
IF y<eptr.ystart OR (y=eptr.ystart AND EdgeLessThan[edge, eptr↑, y]) THEN EXIT;
--
ENDLOOP;
--
edge.next ← eptr↑;
--
eptr↑ ← edge;
--
END;

AddToEdgeList: 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: PUBLIC PROCEDURE[layer: CARDINAL] RETURNS[edge: Edge] =
--Returns next edge from EdgeList without removing it from EdgeList
BEGIN
edge ← EdgeList[layer];
END;

GetFromEdgeList: PUBLIC 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; --***IF new
EdgeListEl[layer] ← edge.next;
edge.mate ← NIL; --clean up
END;
END;

GetEdge: PUBLIC 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;

QueueForEdgeList: PROCEDURE[e: Edge] =
BEGIN
e.next ← EdgeListQueue;
EdgeListQueue ← e;
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;

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;
WriteOctal[list]; WriteChar[CR];
FOR e ← list, e.next UNTIL e=NIL DO
WriteString["next:"]; WriteOctal[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:"]; WriteOctal[e.mate]; WriteChar[SP];
WriteString["XatYCurr: "]; WriteFloat[XatY[e,YCurr[layer]]]; WriteChar[CR];
WriteChar[CR];
ENDLOOP;
END;

WriteFloat: PROCEDURE[r: REAL] =
BEGIN
WriteReal[WriteChar,r];
END;

YNext,YCurr,YPrev: ARRAY [0..MaxLENGTHLayerArray) OF REAL;
EdgeList,EdgeListEl,ActiveList: ARRAY [0..MaxLENGTHLayerArray) OF Edge;
EdgeListQueue: Edge; --isolates edges being thrown back onto edge list

END.