-- File Merge.mesa
-- Written by Maureen Stone, October 1980
-- based on Polygon, Written by Martin Newell, May 1980
-- Last updated: November 5, 1980 3:21 PM

-- Merges rectangular areas for the refresh process
-- Last Edited by: Stone, February 3, 1983 4:09 pm
-- Last Edited by: Pier, February 14, 1984 10:37:27 am PST

DIRECTORY

PrincOpsUtils: FROM "PrincOpsUtils" USING [BITOR],
MergeDefs: FROM "MergeDefs",
PointDefs: FROM "PointDefs" USING[ScrPt, X, Y],
GriffinMemoryDefs USING[UCZone, CZone];

Merge: PROGRAM
IMPORTS PrincOpsUtils, GriffinMemoryDefs
EXPORTS MergeDefs =

BEGIN OPEN PrincOpsUtils, MergeDefs, GriffinMemoryDefs, PointDefs;
Edge: TYPE = LONG POINTER TO EdgeRecord;
EdgeRecord: TYPE = RECORD[
 next: Edge,
 x: INTEGER,
 ystart: INTEGER,
 yend: INTEGER,
 z: INTEGER, --an indication of overlap order. z constant for whole box
 lastouty: INTEGER,  --y value where edge of deferred box was last output
 mate: Edge,   --other edge making up a deferred box
 up: BOOLEAN,   --TRUE if edge defined in increasing y
 flagout: BOOLEAN--TRUE if edge will need to be output at ycurr
];

AreaBlock: PUBLIC TYPE = RECORD[
  edgeList: Edge,   --unprocessed edges
  edgeListEl: Edge,  --last edge added to EdgeList
  activeList: Edge  --edges being processed
  ];

AreaDescriptor: TYPE = REF AreaBlock;

EdgeType: TYPE = {acontinuingUp, acontinuingDown, aterminatingUp, aterminatingDown,
 estartingUp, estartingDown, null};

AreaCreate: PUBLIC PROCEDURE RETURNS[area: AreaDescriptor] =
BEGIN
 area ← CZone.NEW[AreaBlock];
 area^ ← [
   edgeList: NIL,
   edgeListEl: NIL,
   activeList: NIL
   ];
END;

AreaNewBox: PUBLIC PROCEDURE[area: AreaDescriptor, tl,br: ScrPt,z: INTEGER] =
-- edge list is built in sort in increasing lower.y
BEGIN
--make an edge and bubble it into edgeList
 lEdge: Edge ← MakeEdge[tl[X],tl[Y],br[Y],z,TRUE];
 rEdge: Edge ← MakeEdge[br[X],tl[Y],br[Y],z,FALSE];
 AddToEdgeList[area, lEdge];
 AddToEdgeList[area, rEdge];
END;

AreaGenerate: PUBLIC PROCEDURE[area: AreaDescriptor,
     outputBox: PROCEDURE[tl,br: ScrPt, z: INTEGER]] =
-- Generate the area defined up to last AreaNewBox
BEGIN OPEN area;
 ynext,ycurr,yprev: INTEGER;
 edgeListQueue: Edge; --isolates edges being thrown back onto edge list
 edge,prevedge: Edge;
 edgetype: EdgeType;
 newActiveList,pendingFreeEdges,nextedge: Edge;
 ptrnewActiveList: LONG POINTER TO Edge;
 boxleft: Edge;
 adepth,edepth: INTEGER;
 inc: INTEGER;
StartBox: PROCEDURE[e: Edge] =
  BEGIN
  boxleft ← e;
  END;
EndBox: PROCEDURE[right: Edge] =
  BEGIN
  left: Edge ← boxleft;
  IF left.mate#right THEN
   BEGIN --pairing changed or NIL
   OutBox[left,yprev];
   OutBox[right,yprev];
   left.mate ← right;
   right.mate ← left;
   END;
  IF left.flagout OR right.flagout THEN
   BEGIN
   OutBox[left,ycurr];
   left.flagout ← right.flagout ← FALSE; --mustn't do this in OutBox because of other calls to it
   END;
  END;
OutBox: PROCEDURE[e1: Edge, ytop: INTEGER] =
  BEGIN
  e2: Edge ← e1.mate;
  IF e2#NIL THEN
   BEGIN
   rx: INTEGER ← XatY[e2,ytop];
   lx: INTEGER ← XatY[e1,e1.lastouty];
   outputBox[[lx,e1.lastouty],[rx,ytop],MIN[e1.z, e2.z]];
--debug
--   PrintBox[[lx,e1.lastouty],[rx,ytop],MIN[e1.z, e2.z]];
   e1.lastouty ← e2.lastouty ← ytop;
   e1.mate ← e2.mate ← NIL;
   END;
  END;
UpdateynextNewALAndprevedge: PROCEDURE[edge: Edge] =
--Update ynext, newActiveList, and prevedge
  BEGIN
--ynext:
  ynext ← CheckIntersection[prevedge,edge,ycurr,MIN[ynext,edge.yend]];
--newActiveList:
  edge.next ← NIL;
  ptrnewActiveList^ ← edge;
  ptrnewActiveList ← @edge.next;
--and prevedge:
  prevedge ← edge;
  END;
CheckIntersection: PROCEDURE[e1,e2: Edge, ylow,yhigh: INTEGER] RETURNS[y: INTEGER] =
--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
  RETURN[yhigh];
  END;

QueueForFreeEdge: PROCEDURE[edge: Edge] =
  BEGIN
  edge.next ← pendingFreeEdges;
  pendingFreeEdges ← edge;
  END;
QueueForEdgeList: PROCEDURE[edge: Edge] =
  BEGIN
  edge.next ← edgeListQueue;
  edgeListQueue ← edge;
  END;

--AreaGenerate starts here
--WriteLine["new area"];


UNTIL activeList=NIL AND edgeList=NIL DO
--in one pass through each ActiveList:
-- output box 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
  IF activeList=NIL THEN
   BEGIN
   ycurr ← edgeList.ystart;
   ynext ← edgeList.yend;
   END
  ELSE ynext ← activeList.yend;
  adepth ← edepth ← 0;
  [edge,edgetype] ← GetEdge[area,ycurr];
  prevedge ← NIL;
  pendingFreeEdges ← NIL;
  newActiveList ← edgeListQueue ← NIL;
  ptrnewActiveList ← @newActiveList;
  IF ycurr=yprev THEN
   BEGIN --scan only to find ynext (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[area,ycurr];
   ENDLOOP;
   END
  ELSE
   BEGIN --normal case
   UNTIL edge=NIL DO
    inc ← IF edge.up THEN 1 ELSE -1;
    SELECT edgetype FROM
    aterminatingUp,aterminatingDown =>
     BEGIN
     IF adepth=0 THEN
      BEGIN
      edge.flagout ← TRUE;
      StartBox[edge];
      END;
     adepth ← adepth+inc;
     IF adepth=0 THEN
      BEGIN
      edge.flagout ← TRUE;
      EndBox[edge];
      END;
     QueueForFreeEdge[edge];
     END;
    acontinuingUp,acontinuingDown =>
     BEGIN
     case: CARDINAL ← 0;
     UpdateynextNewALAndprevedge[edge]; --must do this first incase intersection set .flagout
     IF edepth#0 THEN case ← 10B;
     IF adepth#0 THEN case ← BITOR[case,4];
     adepth ← adepth+inc;
     edepth ← edepth+inc;
     IF edepth#0 THEN case ← BITOR[case,2];
     IF adepth#0 THEN case ← BITOR[case,1];
     SELECT case FROM
     3   => StartBox[edge];
     6,16B  => BEGIN edge.flagout ← TRUE; EndBox[edge]; END;
     7,15B  => edge.lastouty ← ycurr;
     11B,13B => BEGIN edge.flagout ← TRUE; StartBox[edge]; END;
     14B  => EndBox[edge];
     ENDCASE;
     END;
    estartingUp,estartingDown =>
     BEGIN
     UpdateynextNewALAndprevedge[edge];
     edepth ← edepth+inc;
     END;
    ENDCASE;
    [edge,edgetype] ← GetEdge[area,ycurr];
   ENDLOOP;
   END;
  activeList ← 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[area,edge];
  ENDLOOP;
  IF edgeList#NIL THEN ynext ← MIN[ynext,edgeList.ystart];
  yprev ← ycurr;
  ycurr ← ynext;
ENDLOOP; --UNTIL (...
END;

AreaDestroy: PUBLIC PROCEDURE[area: AreaDescriptor] =
-- Destroy area
BEGIN OPEN area;
 e,next: Edge;
FOR e ← edgeList, next UNTIL e=NIL DO
  next ← e.next;
  UCZone.FREE[@e];
ENDLOOP;
FOR e ← activeList, next UNTIL e=NIL DO
  next ← e.next;
  UCZone.FREE[@e];
ENDLOOP;
END;

-- Private procedures

MakeEdge: PROCEDURE [xstart,ystart,yend: INTEGER, z: INTEGER, up: BOOLEAN]
  RETURNS[edge: Edge] =
BEGIN
 dy: INTEGER ← yend-ystart;
IF dy<=0 THEN RETURN[NIL];
--make vertical edge
 edge ← AllocateEdge[];
 edge^ ← [
  next: ,
  x: xstart,
  ystart: ystart,
  yend: yend,
  z: z,
  lastouty: ystart,
  mate: NIL,
  up: up,
  flagout: FALSE
  ];
RETURN[edge];
END;

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

GetEdge1: PROCEDURE[area: AreaDescriptor, ycurr: INTEGER]
  RETURNS[edge: Edge, edgeType: EdgeType] =
--Get next edge for new active list
BEGIN OPEN area;
 peekAtEdgeList: Edge ← PeekAtEdgeList[area];
TakeFromActiveList: PROCEDURE =
  BEGIN
  edge ← activeList;
  activeList ← activeList.next;
  edgeType ← IF edge.yend<=ycurr
      THEN IF edge.up THEN aterminatingUp ELSE aterminatingDown
      ELSE IF edge.up THEN acontinuingUp ELSE acontinuingDown;
  END;
TakeFromEdgeList: PROCEDURE =
  BEGIN
  edge ← GetFromEdgeList[area];
  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=NIL =>
  BEGIN --take it from EdgeList provided starting at ycurr
  IF peekAtEdgeList#NIL AND peekAtEdgeList.ystart <= ycurr 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 AND
   EdgeLessThan[peekAtEdgeList,activeList, ycurr]
  THEN TakeFromEdgeList[]
  ELSE TakeFromActiveList[];
END;

XatY: PROCEDURE[edge: Edge, y: INTEGER] RETURNS[x: INTEGER] = INLINE
BEGIN
RETURN[edge.x];
END;

Xend: PROCEDURE[edge: Edge] RETURNS[xend: INTEGER] = INLINE
BEGIN
RETURN[edge.x];
END;


AllocateEdge: PROCEDURE RETURNS[Edge] =
BEGIN
RETURN[UCZone.NEW[EdgeRecord]];
END;

FreeEdge: PROCEDURE[edge: Edge] =
BEGIN
 UCZone.FREE[@edge];
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[area: AreaDescriptor, edge: Edge] =
BEGIN OPEN area;
 e,save,start: Edge;
 y: INTEGER ← edge.ystart;
 start ← edgeListEl;
 edgeListEl ← edge;
IF edgeList=NIL THEN
  BEGIN
  edge.next ← NIL;
  edge.mate ← NIL;
  edgeList ← edge;
  RETURN;
  END;
IF start=NIL THEN start ← edgeList;
--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;
   edge.mate ← NIL;
   edgeList.mate ← edge;
   edgeList ← edge;
   RETURN;
   END;
ENDLOOP;
--reached end of list - append edge to save
 edge.next ← NIL;
 edge.mate ← save;
 save.next ← edge;
END;

PeekAtEdgeList: PROCEDURE[area: AreaDescriptor] RETURNS[edge: Edge] =
--Returns next edge from EdgeList without removing it from EdgeList
BEGIN OPEN area;
 edge ← edgeList;
END;

GetFromEdgeList: PROCEDURE[area: AreaDescriptor] RETURNS[edge: Edge] =
--Returns next edge from EdgeList having removed it from EdgeList
BEGIN OPEN area;
 edge ← edgeList;
IF edge#NIL THEN
  BEGIN
  edgeList ← edge.next;
  IF edge.next#NIL THEN edge.next.mate ← NIL;
  edgeListEl ← edge.next;
  edge.mate ← NIL; --clean up
  END;
END;

--

EdgeLessThan: PROCEDURE[e1,e2: Edge, y: INTEGER] RETURNS[BOOLEAN] =
BEGIN
--orders up/down within slope within x
 x1: INTEGER ← XatY[e1,y];
 x2: INTEGER ← XatY[e2,y];
RETURN[x1<x2];
END;

--for debugging:

PrintBox: PUBLIC PROCEDURE[tl,br: ScrPt, z: INTEGER] = {
-- WriteString["xl:"]; WriteDecimal[tl[X]]; WriteChar[SP];
-- WriteString["ty:"]; WriteDecimal[tl[Y]]; WriteChar[SP];
-- WriteString["rx:"]; WriteDecimal[br[X]]; WriteChar[SP];
-- WriteString["by:"]; WriteDecimal[br[Y]]; WriteChar[SP];
-- WriteString["z:"]; WriteDecimal[z]; WriteChar[CR];
 };

PrintList: PUBLIC PROCEDURE[area: AreaDescriptor, list: Edge] =
BEGIN OPEN area;
-- e: Edge;
-- SELECT list FROM
-- activeList => WriteLine["ActiveList"];
-- edgeList => WriteLine["EdgeList"];
-- ENDCASE;
-- WriteOctal[list]; WriteChar[CR];
-- FOR e ← list, e.next UNTIL e=NIL DO
--  WriteString["next:"]; WriteOctal[e.next]; WriteChar[SP];
--  WriteString["x:"]; WriteDecimal[e.x]; WriteChar[SP];
--  WriteString["ystart:"]; WriteDecimal[e.ystart]; WriteChar[SP];
--  WriteString["xend:"]; WriteDecimal[Xend[e]]; WriteChar[SP];
--  WriteString["yend:"]; WriteDecimal[e.yend]; 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:"]; WriteDecimal[e.lastouty]; WriteChar[SP];
--  WriteString["mate:"]; WriteOctal[e.mate]; WriteChar[CR];
--  WriteChar[CR];
-- ENDLOOP;
END;


END.
(635)\214b9B292b5B257b10B182b10B341b12B451b8B54b6B404b6B372b27B302b17B262b16B101b16B2755b11B294b8B360b7B325b8B196b18B243b16B727b4B96b4B88b12B87b8B357b13B310b13B1109b14B167b15B328b12B197b8B334b9B