--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 BYTE ← ALL[0];
newOctRuns: LONG DESCRIPTOR FOR PACKED ARRAY OF BYTE ← DESCRIPTOR[octruns];
first: BOOLEAN ← TRUE;
normalUpdate: BOOLEAN ← FALSE;
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: INTEGER ← Y;
L: INTEGER ← X;
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: INTEGER ← L;
EInc: INTEGER ← EIncL;
L ← S; EIncL ← EIncS;
S ← T; 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
E ← E + EIncL;
xy[L] ← xy[L]+IncL;
IF ABS[E + EIncS] < ABS[E] THEN BEGIN
xy[S] ← xy[S]+IncS;
E ← E+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: BOOLEAN ← FALSE;
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: BOOLEAN ← FALSE;
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: CARDINAL ← LENGTH[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: CARDINAL ← LENGTH[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.