--Griffin encoding types
--m.stone November 20, 1979 2:55 PM
-- Last Edited by: Stone, February 18, 1983 4:37 pm
-- Last Edited by: Pier, February 14, 1984 10:08:57 am PST

DIRECTORY
 GriffinMemoryDefs USING [CZone, Allocate, Free],
 PointDefs: FROM "PointDefs",
 PrincOpsUtils: FROM "PrincOpsUtils",
 ScreenDefs: FROM "ScreenDefs" USING [StartChain, NextChainPoint, EndChain,
  NextScanLine, StartArea],
 Graphics USING [Context],
 GriffinDefs: FROM "GriffinDefs" ,
 EncodingDefs: FROM "EncodingDefs";

Encoding: PROGRAM IMPORTS PrincOpsUtils,GriffinMemoryDefs,GriffinDefs,ScreenDefs
EXPORTS EncodingDefs =
BEGIN OPEN EncodingDefs,GriffinMemoryDefs;

firstChunk,thisChunk: ChainHandle;
ptCount,orIndex: INTEGER;
Dir: TYPE = [0..10];
YMode: TYPE = {up,down,init,horiz};
zeroDir, oneDir: Dir;
minpt,maxpt,oldpt: PointDefs.ScrPt;
oldYmode: YMode ← init;
octruns: PACKED ARRAY[0..63] OF BYTEALL[0];
newOctRuns: LONG DESCRIPTOR FOR PACKED ARRAY OF BYTEDESCRIPTOR[octruns];
first: BOOLEANTRUE;
normalUpdate: BOOLEANFALSE;
GetZeroDir: ARRAY [0..7] OF Dir = [6,2,1,0,4,8,9,10];
GetYMode: ARRAY [0..7] OF YMode = [horiz,down,down,horiz,horiz,up,up,horiz];
DirToYMode: ARRAY [0..10] OF YMode =
  [up,up,up,init,horiz,init,horiz,init,down,down,down];
OctToDx: ARRAY [0..7] OF INTEGER = [1,1,0,-1,-1,-1,0,1];
OctToDy: ARRAY [0..7] OF INTEGER = [0,-1,-1,-1,0,1,1,1];
centerDir: Dir = 5;
whatOctant: INTEGER = 9;
DirToOctant: ARRAY [0..10] OF CARDINAL = [3,2,1,whatOctant,4,whatOctant,0,whatOctant,5,6,7];
bitmask: ARRAY[0..15] OF CARDINAL = [1B,2B,4B,10B,20B,40B,100B,200B,400B,1000B,2000B,4000B,10000B,20000B,40000B,100000B];

AddChainPoint: PUBLIC PROCEDURE[pt: PointDefs.ScrPt]=
BEGIN OPEN PointDefs;
dx,dy: INTEGER;
newDir: Dir;
newYmode: YMode ← init;
IF first THEN BEGIN
 StartNewChunk[pt];
 first ← FALSE;
RETURN;
END;

--normal case
dx ← pt[X]-oldpt[X];
dy ← pt[Y]-oldpt[Y];
newDir ← (dx+1)+ (dy+1)*4;
IF newDir=centerDir THEN RETURN;

--for monotonic pieces
newYmode ← DirToYMode[newDir];
--update to clear init or if make a change.
--horizontal ymode should be ignored except when would start new chunk anyway
IF (oldYmode=init AND newYmode#horiz) OR (oldYmode=init AND ptCount=63)
THEN oldYmode ← newYmode
ELSE IF newYmode#oldYmode AND newYmode#horiz THEN BEGIN
 savept: PointDefs.ScrPt ← pt; --shouldn't be necessary
 EndChunk[];
 StartNewChunk[oldpt];
 oldYmode ← newYmode;
 pt ← savept;
END;
oldpt ←pt;

IF ptCount =64 THEN BEGIN
 EndChunk[];
 StartNewChunk[pt];
RETURN;
END;

IF pt[X] > maxpt[X] THEN maxpt[X] ← pt[X];
IF pt[Y] > maxpt[Y] THEN maxpt[Y] ← pt[Y];
IF pt[X] < minpt[X] THEN minpt[X] ← pt[X];
IF pt[Y] < minpt[Y] THEN minpt[Y] ← pt[Y];

IF orIndex<0 THEN ChangeOctant[newDir]; --entry after startnewchunk
IF (newDir#oneDir AND newDir#zeroDir) THEN ChangeOctant[newDir];

--assume same octant, gen true for lines
IF ReadRun[newOctRuns[orIndex]] =31THEN BEGIN
 oct: CARDINAL ← ReadOctant[newOctRuns[orIndex]];
 orIndex ← orIndex+1;
 newOctRuns[orIndex] ← SetOctant[oct];
 normalUpdate ← FALSE;
END;

IF newDir =oneDir THEN BEGIN
 index: CARDINAL ← ptCount/16;
 thisChunk.bits[index] ← SetBit[ptCount MOD 16,thisChunk.bits[index]];
END;
ptCount ← ptCount+1;
IF normalUpdate THEN newOctRuns[orIndex] ← newOctRuns[orIndex]+1
ELSE normalUpdate ← TRUE;
END;

--top 3 bits are octant, bottom 5 are run
SetOctant: PROCEDURE[octant: [0..7]] RETURNS[BYTE]= INLINE
BEGIN OPEN PrincOpsUtils;
RETURN[BITSHIFT[octant,5]];
END;

ReadOctant: PROCEDURE[octant: [0..7]] RETURNS[CARDINAL]= INLINE
BEGIN RETURN[PrincOpsUtils.BITSHIFT[octant,-5]]; END;

ReadRun: PROCEDURE[byte: BYTE] RETURNS[CARDINAL]= INLINE
BEGIN RETURN[PrincOpsUtils.BITAND[byte,37B]]; END;

SetBit: PROCEDURE[bitnum,word: CARDINAL] RETURNS[CARDINAL]= INLINE
BEGIN RETURN[PrincOpsUtils.BITOR[bitmask[bitnum],word]]; END;

ChangeOctant: PROCEDURE[new: Dir]=
BEGIN
newOct: CARDINAL ← DirToOctant[new];
IF newOct = whatOctant THEN GriffinDefs.UserMessage["bad direction"];
orIndex ← orIndex+1;
newOctRuns[orIndex] ← SetOctant[newOct];
zeroDir ← new;
oneDir ← GetZeroDir[(newOct+1) MOD 8];
normalUpdate ← FALSE;
END;

StartNewChunk: PROCEDURE[pt: PointDefs.ScrPt]=
BEGIN
IF first THEN BEGIN
 firstChunk ← thisChunk 𡤌Zone.NEW[ChainEncoding];
 oldYmode ← init;
END
ELSE BEGIN
 thisChunk.link ← CZone.NEW[ChainEncoding];
 thisChunk ← thisChunk.link;
END;
thisChunk^ ← [NIL,0,[0,0],[0,0],pt,DESCRIPTOR[NIL,0],[0,0,0,0]];
Zero[BASE[newOctRuns],LENGTH[newOctRuns]/2]; --cause it's packed
ptCount ← 0;
orIndex ← -1;
maxpt ← minpt ← oldpt ← pt;
END;

EndChunk: PROCEDURE=
BEGIN
thisChunk.tl ← minpt;
thisChunk.br ← maxpt;
thisChunk.octants ← DESCRIPTOR[Allocate[(orIndex+2)/2],orIndex+1];
PrincOpsUtils.LongCOPY[BASE[newOctRuns],(orIndex+2)/2,BASE[thisChunk.octants]];
END;

MakeChainEncoding: PUBLIC PROCEDURE RETURNS[ptr: ChainHandle] =
BEGIN OPEN PointDefs;
IF first THEN RETURN[NIL]; --case called without any points
IF BASE[thisChunk.octants]=NIL THEN EndChunk[];
first ← TRUE;
RETURN[firstChunk];
END;

TestChainChunk: PUBLIC PROCEDURE[encoding: ChainHandle,
NextPoint: PROCEDURE[PointDefs.ScrPt] RETURNS[stop: BOOLEAN] ]=
BEGIN OPEN PointDefs;
pt: ScrPt ← encoding.p0;
dx0,dy0,dx1,dy1: INTEGER;
oct,orNum,cnt,i: CARDINAL;
ptNum: CARDINAL ← 0;
[] ← NextPoint[pt] ;
FOR orNum IN [0..LENGTH[encoding.octants]) DO
 oct ← ReadOctant[encoding.octants[orNum]];
 dx0 ← OctToDx[oct]; dy0 ← OctToDy[oct];
 oct ← (oct+1) MOD 8;
 dx1 ← OctToDx[oct]; dy1 ← OctToDy[oct];
 cnt ← ReadRun[encoding.octants[orNum]];
FOR i IN [0..cnt] DO
  IF BitOn[ptNum MOD 16, encoding.bits[ptNum/16]]
   THEN BEGIN pt[X] ← pt[X]+dx1; pt[Y] ← pt[Y]+dy1 END
  ELSE BEGIN pt[X] ← pt[X]+dx0; pt[Y] ← pt[Y]+dy0 END;
  IF NextPoint[pt] THEN RETURN;
  ptNum ←ptNum+1;
  ENDLOOP;
ENDLOOP;
END;


--philosopy: ShowObjects.PlotChainEncoding does culling
PlotChainChunk
: PUBLIC PROCEDURE[encoding: ChainHandle, dc: Graphics.Context]=
BEGIN OPEN PointDefs;
dx0,dy0,dx1,dy1: INTEGER;
oct,orNum,cnt,i: CARDINAL;
ptNum: CARDINAL ← 0;
ScreenDefs.StartChain[encoding.p0, dc];
FOR orNum IN [0..LENGTH[encoding.octants]) DO
 oct ← ReadOctant[encoding.octants[orNum]];
 dx0 ← OctToDx[oct]; dy0 ← OctToDy[oct];
 oct ← (oct+1) MOD 8;
 dx1 ← OctToDx[oct]; dy1 ← OctToDy[oct];
 cnt ← ReadRun[encoding.octants[orNum]];
FOR i IN [0..cnt] DO
  IF BitOn[ptNum MOD 16, encoding.bits[ptNum/16]]
   THEN ScreenDefs.NextChainPoint[dx1,dy1, dc]
   ELSE ScreenDefs.NextChainPoint[dx0,dy0, dc];
  ptNum ←ptNum+1;
  ENDLOOP;
ENDLOOP;
ScreenDefs.EndChain[dc];
END
;

BitOn: PROCEDURE[bitnum,word: CARDINAL] RETURNS[BOOLEAN]= INLINE
BEGIN
RETURN[IF PrincOpsUtils.BITAND[bitmask[bitnum],word] = 0 THEN FALSE ELSE TRUE];
END;

AddChainLine: PUBLIC PROCEDURE[pt: PointDefs.ScrPt] =
BEGIN OPEN PointDefs;
E: INTEGER 𡤀
S: INTEGERY;
L: INTEGERX;
IncS: INTEGER ← 1; --point increments
IncL: INTEGER ← 1;
xy: ScrPt ← oldpt;
EIncS: INTEGER ← pt[X]-xy[X]; --error increments. S,L are direction, not size
EIncL: INTEGER ← pt[Y]-xy[Y];
--one dot wide
IF (ABS[pt[X]-xy[X]]<=1 AND ABS[pt[Y]-xy[Y]] <=1) THEN BEGIN
 AddChainPoint[pt];
RETURN;
END;
--IncL goes with EIncS, and IncS goes with EIncL in this section
IF ABS[EIncS] < ABS[EIncL] THEN BEGIN--45-90 octant
T: INTEGERL;
 EInc: INTEGER ← EIncL;
LS; EIncL ← EIncS;
ST; EIncS ← EInc;
END;
--following assures that S error increment is positive, the other is negative, and the point increments
--are the correct signs
IF EIncL < 0 THEN IncS ← -1 ELSE EIncL ← -EIncL;
IF EIncS < 0 THEN BEGIN IncL ← -1; EIncS ← - EIncS; END;
--now IncL goes with EIncL and IncS with EIncS
AddChainPoint[xy]; --get first point in. Need this doubling in scan convert
UNTIL xy[L] = pt[L] DO
EE + EIncL;
 xy[L] ← xy[L]+IncL;
IF ABS[E + EIncS] < ABS[E] THEN BEGIN
  xy[S] ← xy[S]+IncS;
  EE+EIncS;
  END;
 AddChainPoint[xy];
ENDLOOP; 
AddChainPoint[pt]; --get last point in
oldpt ← pt;
END;

DeleteChainEncoding: PUBLIC PROCEDURE[ptr: ChainHandle] =
BEGIN
encoding: ChainHandle;
UNTIL ptr=NIL DO
 Free[BASE[ptr.octants]];
 encoding ← ptr;
 ptr ← ptr.link;
-- Free[encoding];
ENDLOOP;
END;
CopyChainEncoding: PUBLIC PROCEDURE[chain: ChainHandle] RETURNS[newchain: ChainHandle]=
--should really carefully clean up and deallocate, I suppose
BEGIN ENABLE UNWIND => {newchain ← NIL};
length: CARDINAL;
firstChunk: REF ChainEncoding ← NIL;
thisChunk,ptr: REF ChainEncoding;
FOR ptr ← chain, ptr.link UNTIL ptr=NIL DO
IF firstChunk=NIL THEN firstChunk ← thisChunk 𡤌Zone.NEW[ChainEncoding]
ELSE BEGIN
  thisChunk.link ← CZone.NEW[ChainEncoding];
  thisChunk ← thisChunk.link;
  END;
 thisChunk^ ← ptr^;
 length ← LENGTH[ptr.octants];
 thisChunk.octants ← DESCRIPTOR[Allocate[length],length];
 PrincOpsUtils.LongCOPY[from: BASE[ptr.octants], to: BASE[thisChunk.octants], nwords: length];
ENDLOOP;
newchain ← firstChunk;
END;


--START OF AREA ENCODING
--reads the chain encoding, which is assumed to be in monotonic sections.
--once it makes and sorts the edges, is a classic scan conversion algorithm.
--each encoded scan line may have 1 or more runs on it. The flag in the encoding is
--a startx of -1 (377B for packed) which implies that dx is the number of runs per line.
--Care is taken that chunks don't break across scan lines. Chunks are roughly 64
--scan lines each, or until there is a size change

Edge: TYPE = RECORD
 [nextEdge: LONG POINTER TO Edge,
 nextCurrent: LONG POINTER TO Edge,
 currentMinX,currentMaxX,ymin,ymax: INTEGER,
 chain: ChainHandle,
 end: ChainHandle
 ];

firstArea: AreaHandle ← NIL;
thisArea: AreaHandle ← NIL;
runs: ARRAY [0..64) OF LongRun ← ALL[[0,0]]; --runs while we are building them
newRuns: LONG DESCRIPTOR FOR ARRAY OF LongRun ← DESCRIPTOR[runs];
runIndex: CARDINAL ← 0;
yFirst: INTEGER ← 0; --first y in a chunk
flag: INTEGER ← -77777B; --large negative number

MakeAreaEncoding: PUBLIC PROCEDURE[chain: ChainHandle] RETURNS[AreaHandle] =
BEGIN OPEN PointDefs;
nInters: CARDINAL ← 2;
allEdges,allCurrent,ptr,tptr: LONG POINTER TO Edge;
startX,dX,yScan: INTEGER;
scanIndex: CARDINAL;
patch: ChainHandle;

--Initialize
Zero[BASE[newRuns],LENGTH[newRuns]*SIZE[LongRun]]; 
runIndex ← 0;
firstArea ← NIL;
allCurrent ← NIL;

--make the edges, linked together thru allEdges in ymin order
[allEdges,patch] ← MakeEdges[chain];
IF allEdges=NIL THEN RETURN[NIL];
yScan ← yFirst ← allEdges.ymin;
--YLOOP until no more current edges
DO
-- links together edges thru allCurrent
UNTIL allEdges=NIL DO
  IF allEdges.ymin <=yScan THEN BEGIN
--add to allCurrent list
   allEdges.nextCurrent ← allCurrent;
   allCurrent ← allEdges;
--delete from allEdges list
   allEdges ← allEdges.nextEdge;
   END
--can exit since list is ymin sorted
  ELSE EXIT;
  ENDLOOP;
--delete out anything we've passed up
 ptr ← allCurrent;
UNTIL ptr=NIL DO
  IF ptr.ymax < yScan THEN BEGIN
   IF ptr = allCurrent THEN BEGIN
    allCurrent ← ptr.nextCurrent;
    Free[ptr];
    ptr ← allCurrent;
    END
   ELSE BEGIN
    tptr.nextCurrent ← ptr.nextCurrent;
    Free[ptr];
    ptr ← tptr.nextCurrent;
    END
   END
  ELSE BEGIN
   tptr ← ptr;
   ptr ← ptr.nextCurrent;
   END;
  ENDLOOP;

IF allCurrent=NIL THEN EXIT; --no more edges

--set currentX for all X, count edges and sort list
 scanIndex ← 0;
FOR ptr ← allCurrent, ptr.nextCurrent UNTIL ptr=NIL DO
  SetCurrentX[ptr,yScan];
  scanIndex ← scanIndex+1;
  ENDLOOP;
 allCurrent ← XSort[allCurrent];
--test for change in number of intersections
IF (scanIndex MOD 2#0) THEN BEGIN
  NULL; EXIT; END;
IF scanIndex#nInters THEN BEGIN
  nInters ← scanIndex;
  AddRun[flag,nInters/2,yScan];
  END;
--XLOOP
 ptr ← allCurrent;
UNTIL ptr=NIL DO
  startX ← ptr.currentMinX;
  IF startX<=flag THEN BEGIN
   NULL; EXIT; END;
  ptr ← ptr.nextCurrent;
  dX ← ptr.currentMaxX;
  AddRun[startX,dX-startX+1,yScan];
  ptr ← ptr.nextCurrent;
  ENDLOOP;
--inc yScan and YLOOP
 yScan ← yScan+1;
ENDLOOP;
IF runIndex#0 THEN EndArea[yScan];
IF patch#NIL THEN patch.link ← NIL; --break the circle in the chain encoding if necessary
RETURN[firstArea];
END;

AddRun: PROCEDURE[startx,dx,y: INTEGER]=
BEGIN
IF runIndex>=64 THEN EndArea[y];
--IF startx#flag THEN ScreenDefs.HFill[y,startx,dx, NIL];
newRuns[runIndex] ← [startx,dx];
runIndex ← runIndex+1;
END;

EndArea: PROCEDURE[yscan: INTEGER]=
BEGIN
type: RunType ← short;
xmin,xmax,rx,lx: INTEGER;
i,j: CARDINAL;
rpl: CARDINAL ← 1;
nextra: CARDINAL ← 0;
yLast: INTEGER ← yscan-1; --current scan line isn't in this chunk
i ← 0;
xmin ← 1000; xmax ← 0;
UNTIL i =runIndex DO
IF newRuns[i].lx=flag THEN BEGIN
  rpl ← newRuns[i].dx;
  i ← i+1;
  LOOP;
  END;
--the nextra stuff is so that we don't split scanlines across chunks
IF i+rpl >runIndex THEN BEGIN
  nextra← runIndex-i;
  runIndex ← i;
  EXIT;
  END;
 lx ← newRuns[i].lx;
FOR j IN [1..rpl] DO
  rx ← newRuns[i].lx+newRuns[i].dx-1;
  i ← i+1;
  yLast ← yLast-nextra;
  ENDLOOP;
IF lx < xmin THEN xmin ← lx;
IF rx > xmax THEN xmax ← rx;
ENDLOOP;
--make the lx relative to xmin, and determine the type of encoding
FOR i IN [0..runIndex) DO
IF newRuns[i].dx>377B THEN type←long;
IF newRuns[i].lx =flag THEN LOOP;
 newRuns[i].lx ← newRuns[i].lx-xmin;
IF newRuns[i].lx>376B THEN type←long;
ENDLOOP;
IF firstArea=NIL THEN firstArea ← thisArea ← AllocateArea[type]
ELSE BEGIN
 thisArea.link ← AllocateArea[type];
 thisArea ← thisArea.link;
END;
--set common fields
thisArea.tl ← [xmin,yFirst];
thisArea.br ← [xmax,yLast];
--set varient fields
WITH area: thisArea SELECT FROM
 short => BEGIN
  sr: ShortRun;
  area.runs ← DESCRIPTOR[Allocate[runIndex*SIZE[ShortRun]],runIndex];
  FOR i IN [0..runIndex) DO
   IF newRuns[i].lx = flag THEN sr.lx ← 377B ELSE sr.lx ← newRuns[i].lx;
   sr.dx ← newRuns[i].dx;
   area.runs[i] ← sr;
   ENDLOOP;
  END;
 long => BEGIN
  area.runs ← DESCRIPTOR[Allocate[runIndex*SIZE[LongRun]],runIndex];
  PrincOpsUtils.LongCOPY[BASE[newRuns],runIndex*SIZE[LongRun],BASE[area.runs]];
  END;
ENDCASE;
--copy in any extra
IF nextra#0 THEN BEGIN
 newRuns[0] ← [flag,rpl]; --set the switch
FOR i IN [1..nextra] DO
  newRuns[i] ← newRuns[runIndex];
  newRuns[i].lx ← newRuns[i].lx; 
  runIndex ← runIndex+1;
  ENDLOOP;
--zero rest (compulsive tidyness)
FOR i IN [nextra+1..LENGTH[newRuns]) DO
  newRuns[i] ← [0,0];
  ENDLOOP;
 runIndex ← nextra+1; --to account for the switch
END
ELSE BEGIN
 Zero[BASE[newRuns],LENGTH[newRuns]*SIZE[LongRun]]; 
 runIndex ← 0;
IF rpl#1 THEN AddRun[flag,rpl,yLast+1];
END;
yFirst ← yLast+1;
END;

MakeEdges: PROCEDURE[chain: ChainHandle] RETURNS[LONG POINTER TO Edge,ChainHandle]=
BEGIN OPEN PointDefs;
thisChain: ChainHandle ← chain;
firstChain: ChainHandle ← NIL;
lastInFirstChain: ChainHandle ← chain;
thisChunk: ChainHandle ← chain;
newEdge: LONG POINTER TO Edge ← NIL;
firstEdge: LONG POINTER TO Edge ← NIL;
thisYmode,nextYmode: YMode;
endpt: ScrPt;
--add in ymin order
AddEdge: PROCEDURE[edge: LONG POINTER TO Edge] = BEGIN
 ptr: LONG POINTER TO Edge ← NIL;
IF firstEdge=NIL THEN BEGIN
  firstEdge ← edge ;
  RETURN;
  END;
--find right spot
FOR ptr ← firstEdge, ptr ← ptr.nextEdge UNTIL ptr.nextEdge=NIL DO
  IF ptr.nextEdge.ymin>edge.ymin THEN EXIT;
  ENDLOOP;
--insert after ptr with another special case at list head
IF ptr=firstEdge AND edge.ymin<firstEdge.ymin THEN BEGIN
  edge.nextEdge ← firstEdge; --insert before firstEdge
  firstEdge ← edge;
  END
ELSE BEGIN
  edge.nextEdge ← ptr.nextEdge;
  ptr.nextEdge ← edge;
  END;
END;

thisYmode ← SetYMode[thisChunk];
FOR thisChunk ← chain, thisChunk.link UNTIL thisChunk=NIL DO
--usual exit. RETURN at end of routine is for fuckups
IF thisChunk.link=NIL THEN BEGIN
  ymode: YMode ← init;
  chunk: ChainHandle 𡤏irstChain;
  firstendpt: ScrPt; --last point in edge
--case of 2 edges total
  IF firstChain=NIL THEN BEGIN
   IF thisYmode#horiz THEN BEGIN
    endpt ← ReadEndPt[thisChunk]; --last point in edge
    newEdge ← MakeEdge[thisChain,thisChunk.link,endpt];  --last edge
    AddEdge[newEdge];
    END;
   RETURN[firstEdge,NIL];
   END;
--can compute parameters for first chain
  firstendpt ← ReadEndPt[lastInFirstChain]; --last point in edge
  UNTIL chunk=lastInFirstChain.link DO
   ymode ← SetYMode[chunk];
   IF ymode#horiz THEN EXIT; --direction of firstchain
   chunk ← chunk.link;
   ENDLOOP;
--case of separate edge for first chain
  IF thisYmode#ymode THEN BEGIN
--make a separate first edge if it isn't horizontal
   IF ymode#horiz THEN BEGIN
    newEdge ← MakeEdge[firstChain,lastInFirstChain.link,firstendpt];
    AddEdge[newEdge];
    END;
--make the last edge if it isn't horizontal
   IF thisYmode#horiz THEN BEGIN
    endpt ← ReadEndPt[thisChunk]; --last point in edge
    newEdge ← MakeEdge[thisChain,thisChunk.link,endpt];  --last edge
    AddEdge[newEdge];
    END;
   RETURN[firstEdge,NIL];
   END;
--otherwise, combine the chains and make one edge
--this will make a ring out of the encoding. Can't just rearrange it because of external
--pointers to it. This cludge lets MakeAreaEncoding know how to fix the ring.
--can be removed if this is rewritten to copy the data structure
  thisChunk.link ← firstChain;
  newEdge ← MakeEdge[thisChain,lastInFirstChain.link,firstendpt];
  AddEdge[newEdge];
  RETURN[firstEdge,thisChunk];
  END;
--direction change in Y makes new edge
 nextYmode ← SetYMode[thisChunk.link];
IF nextYmode= thisYmode OR nextYmode=horiz THEN LOOP; --just ignore horizontal chunks
--Got a new edge
--just save first piece of chain
IF firstChain=NIL THEN BEGIN
  firstChain ← thisChain;
  lastInFirstChain ← thisChunk;
  END
--set min,max and currentX
ELSE BEGIN
  endpt ← ReadEndPt[thisChunk]; --last point in edge
  newEdge ← MakeEdge[thisChain,thisChunk.link,endpt];
  AddEdge[newEdge];
  END;
--init the next chain
 thisChain ← thisChunk.link;
 thisYmode ← nextYmode;
ENDLOOP;
RETURN[firstEdge,NIL];
END;

MakeEdge: PROCEDURE[thisChain,end: ChainHandle,endpt: PointDefs.ScrPt] RETURNS[LONG POINTER TO Edge]=
BEGIN OPEN PointDefs;
newEdge: LONG POINTER TO Edge ← Allocate[SIZE[Edge]];
newEdge^ ← [NIL,NIL,0,0,0,0,thisChain,end];
--set min,max and currentX
IF endpt[Y] < thisChain.p0[Y] THEN BEGIN--thisChain.p0 is first point
 newEdge.ymin ← endpt[Y];
 newEdge.ymax ← thisChain.p0[Y];
END
ELSE BEGIN
 newEdge.ymin ← thisChain.p0[Y];
 newEdge.ymax ← endpt[Y];
END;
RETURN[newEdge];
END;

SetYMode: PROCEDURE[chunk: ChainHandle] RETURNS[YMode]=
BEGIN OPEN PointDefs;
ymode: YMode;
oldY,dy: INTEGER;
i: CARDINAL;
done: BOOLEANFALSE;
CheckDy: PROCEDURE[newpt: ScrPt] RETURNS[stop: BOOLEAN]= BEGIN
 dy ← newpt[Y] - oldY;
 ymode ← SELECT dy FROM
  -1 => down,
  0 => horiz,
  1 => up,
  ENDCASE => horiz;
IF ymode#horiz THEN {done ← TRUE; RETURN[TRUE]} ELSE RETURN[FALSE];
END;
--will only return horiz if entire chunk is horizontal
IF chunk.tl[Y]=chunk.br[Y] THEN RETURN[horiz];
FOR i IN [0..LENGTH[chunk.octants]) DO
 ymode ← GetYMode[ReadOctant[chunk.octants[i]]];
IF ymode#horiz THEN RETURN[ymode];
ENDLOOP;
--all octants read as horizontal. Are going to have to check directions
oldY ← chunk.p0[Y];
TestChainChunk[chunk,CheckDy];
RETURN[ymode];
END;

ReadEndPt: PROCEDURE[chunk: ChainHandle] RETURNS[PointDefs.ScrPt]=
BEGIN
point: PointDefs.ScrPt ← chunk.p0;
Update: PROCEDURE[newPt: PointDefs.ScrPt] RETURNS[stop: BOOLEAN]=
BEGIN point ← newPt; RETURN[FALSE]; END;
TestChainChunk[chunk,Update];
RETURN[point];
END;

--X Sort (switching sort, since x list is nearly sorted anyway)
XSort: PROCEDURE[edges: LONG POINTER TO Edge] RETURNS[LONG POINTER TO Edge]=
BEGIN
head,trail,ptr,tptr: LONG POINTER TO Edge;
switches: CARDINAL ← 1;
head ← edges;
IF edges = NIL THEN RETURN[edges];
UNTIL switches=0 DO
 switches ← 0;
 ptr ← head;
UNTIL ptr.nextCurrent = NIL DO
IF ptr.currentMinX > ptr.nextCurrent.currentMinX OR
   (ptr.currentMinX=ptr.nextCurrent.currentMinX
   AND ptr.currentMaxX>ptr.nextCurrent.currentMaxX) THEN BEGIN
   switches ← switches+1;
   tptr ← ptr.nextCurrent;
   IF ptr=head THEN head ← tptr
   ELSE trail.nextCurrent ← tptr;
   ptr.nextCurrent ← tptr.nextCurrent;
   tptr.nextCurrent ← ptr;
   trail ← tptr;
   END
  ELSE BEGIN
   trail ← ptr;
   ptr ← ptr.nextCurrent;
   END;
  ENDLOOP;
ENDLOOP;
RETURN[head];
END;

SetCurrentX: PROCEDURE[edge: LONG POINTER TO Edge,y: INTEGER]=
BEGIN OPEN PointDefs;
encoding: ChainHandle;
dx0,dy0,dx1,dy1: INTEGER;
oct,orNum,cnt,i: CARDINAL;
ptNum: CARDINAL ← 0;
found: BOOLEANFALSE;
pt: ScrPt ← edge.chain.p0;
scanning: {up,down} ← (IF pt[Y]=edge.ymin THEN up ELSE down);
edge.currentMinX ← 77777B;
edge.currentMaxX ← -77777B;
FOR encoding ← edge.chain, encoding.link UNTIL encoding=edge.end DO
IF y IN [encoding.tl[Y]..encoding.br[Y]] THEN BEGIN
 pt ← encoding.p0;
 ptNum ← 0;
--check encoding.p0
IF pt[Y]=y THEN BEGIN
  found ← TRUE; --found something
  IF pt[X]<edge.currentMinX THEN edge.currentMinX ← pt[X];
  IF pt[X]>edge.currentMaxX THEN edge.currentMaxX ← pt[X];
  END;
--generate and test the rest of the points
FOR orNum IN [0..LENGTH[encoding.octants]) DO
  oct ← ReadOctant[encoding.octants[orNum]];
  dx0 ← OctToDx[oct]; dy0 ← OctToDy[oct];
  oct ← (oct+1) MOD 8;
  dx1 ← OctToDx[oct]; dy1 ← OctToDy[oct];
  cnt ← ReadRun[encoding.octants[orNum]];
  FOR i IN [0..cnt] DO
   IF BitOn[ptNum MOD 16, encoding.bits[ptNum/16]]
    THEN BEGIN pt[X] ← pt[X]+dx1; pt[Y] ← pt[Y]+dy1 END
   ELSE BEGIN pt[X] ← pt[X]+dx0; pt[Y] ← pt[Y]+dy0 END;
--this is the test for definitely done. may not hit it if pts are at min or max of edge. In that case,
--will drop out the bottom with found set
   IF found AND ((scanning=up AND pt[Y]>y) OR (scanning=down AND pt[Y]<y))
    THEN RETURN;
   IF pt[Y]=y THEN BEGIN
    found ← TRUE; --found something
    IF pt[X]<edge.currentMinX THEN edge.currentMinX ← pt[X];
    IF pt[X]>edge.currentMaxX THEN edge.currentMaxX ← pt[X];
    END;
   ptNum ←ptNum+1;
   ENDLOOP;
  ENDLOOP;
END;
ENDLOOP;
IF NOT found THEN GriffinDefs.UserMessage["no point on this edge"];
END;

DeleteAreaEncoding: PUBLIC PROCEDURE[ptr: AreaHandle] =
BEGIN
encoding,tptr: AreaHandle;
encoding ← ptr;
UNTIL encoding=NIL DO
 tptr ← encoding.link;
WITH area: encoding SELECT FROM
  long => Free[BASE[area.runs]];
  short => Free[BASE[area.runs]];
  ENDCASE;
-- Free[encoding];
 encoding ← tptr;
ENDLOOP;
END;

AllocateArea: PROC[type: RunType] RETURNS [AreaHandle] = INLINE {
IF type=short THEN RETURN[CZone.NEW[AreaEncoding[short]]]
ELSE RETURN[CZone.NEW[AreaEncoding[long]]];
 };

CopyAreaEncoding: PUBLIC PROCEDURE[oldarea: AreaHandle] RETURNS[newarea: AreaHandle]=
BEGIN ENABLE UNWIND => {newarea ← NIL};
firstArea,lastArea: REF AreaEncoding ← NIL;
thisArea,areaptr: REF AreaEncoding;
FOR areaptr ← oldarea, areaptr.link UNTIL areaptr=NIL DO
WITH areaptr SELECT FROM
  area: REF AreaEncoding[short] => BEGIN
   short: REF AreaEncoding[short] ← CZone.NEW[AreaEncoding[short]];
   length: CARDINALLENGTH[area.runs];
   short.tl ← area.tl; short.br ← area.br;
   short.runs ← DESCRIPTOR[Allocate[length*SIZE[ShortRun]],length];
   PrincOpsUtils.LongCOPY[from: BASE[area.runs],
    to: BASE[short.runs],
    nwords: length*SIZE[ShortRun]];
   thisArea ← short;
   END;
  area: REF AreaEncoding[long] => BEGIN
   long: REF AreaEncoding[long] ← CZone.NEW[AreaEncoding[long]];
   length: CARDINALLENGTH[area.runs];
   long.tl ← area.tl; long.br ← area.br;
   long.runs ← DESCRIPTOR[Allocate[length*SIZE[LongRun]],length];
   PrincOpsUtils.LongCOPY[from: BASE[area.runs],
    to: BASE[long.runs],
    nwords: length*SIZE[LongRun]];
   thisArea ← long;
   END;
  ENDCASE;
IF firstArea=NIL THEN firstArea ← lastArea ← thisArea
ELSE {lastArea.link ←thisArea; lastArea ← lastArea.link};
ENDLOOP;
newarea ← firstArea;
END;

TestAreaChunk: PUBLIC PROCEDURE[encoding: AreaHandle,
TestLine: PROCEDURE[y,lx,dx: INTEGER] RETURNS[stop: BOOLEAN] ]=
BEGIN OPEN PointDefs;
xorig,yScan,lx,dx: INTEGER;
i,rpl: CARDINAL;
xorig ← encoding.tl[X];
yScan ← encoding.tl[Y];
rpl ← 1;
i← 0;
--not overlayed varient since eventually will have rules, etc
WITH area: encoding SELECT FROM
 long => UNTIL yScan>area.br[Y] DO
  IF area.runs[i].lx=flag THEN BEGIN
   rpl ← area.runs[i].dx;
   i←i+1;
   LOOP;
   END;
  THROUGH [1..rpl] DO
   lx ← xorig+area.runs[i].lx;
   dx ← area.runs[i].dx;
   IF TestLine[yScan,lx,dx] THEN RETURN;
   i ← i+1;
   ENDLOOP;
  yScan ← yScan+1;
  ENDLOOP;
 short => UNTIL yScan>area.br[Y] DO
  IF area.runs[i].lx=377B THEN BEGIN
   rpl ← area.runs[i].dx;
   i←i+1;
   LOOP;
   END;
  THROUGH [1..rpl] DO
   lx ← xorig+area.runs[i].lx;
   dx ← area.runs[i].dx;
   IF TestLine[yScan,lx,dx] THEN RETURN;
   i ← i+1;
   ENDLOOP;
  yScan ← yScan+1;
  ENDLOOP;
ENDCASE;
END;

--ShowObject.PlotChainEncoding does culling
PlotAreaChunk: PUBLIC PROCEDURE[encoding: AreaHandle, dc: Graphics.Context]=
BEGIN OPEN PointDefs;
xorig,yScan,lx,rx: INTEGER;
i,rpl: CARDINAL;
[yScan,xorig] ← ScreenDefs.StartArea[encoding.tl[Y], dc];
xorig ← xorig+encoding.tl[X];
rpl ← 1;
i← 0;
--not overlayed varient since eventually will have rules, etc
WITH area: encoding SELECT FROM
 long => UNTIL i>=LENGTH[area.runs] DO
  IF area.runs[i].lx=flag THEN BEGIN
   rpl ← area.runs[i].dx;
   i←i+1;
   LOOP;
   END;
  THROUGH [1..rpl] DO
   lx ← xorig+area.runs[i].lx;
   rx ← lx+area.runs[i].dx-1;
   ScreenDefs.NextScanLine[yScan,lx,rx,dc];
   i ← i+1;
   ENDLOOP;
  yScan ← yScan+1;
  ENDLOOP;
 short => UNTIL i>=LENGTH[area.runs] DO
  IF area.runs[i].lx=377B THEN BEGIN
   rpl ← area.runs[i].dx;
   i←i+1;
   LOOP;
   END;
  THROUGH [1..rpl] DO
   lx ← xorig+area.runs[i].lx;
   rx ← lx+area.runs[i].dx-1;
   ScreenDefs.NextScanLine[yScan,lx,rx,dc];
   i ← i+1;
   ENDLOOP;
  yScan ← yScan+1;
  ENDLOOP;
ENDCASE;
END;

Zero: PROC [block: LONG POINTER, length: CARDINAL] = {
 block^ ← 0;
 PrincOpsUtils.LongCOPY[from: block, to: block+1, nwords: length-1];
 };

END.