-- Copyright (C) 1983 by Xerox Corporation. All rights reserved. -- DisplayImplF.mesa - last edited by -- S. Schiller 8-Mar-83 18:06:34 -- Rick 22-Nov-83 17:37:12 -- Bruce 24-Feb-83 16:08:51 -- Poskanzer 20-May-83 9:04:41 DIRECTORY BitBlt USING [BitBltFlags], Display USING [], DisplayOps USING [ AbsPlace, Color, LogError, FillList, FillObject, Intersect, Shade], DisplayInternal USING [], IVector USING [IPoint, IVec], SpecialDisplay USING [defaultContext, LineStyle, solid, Special], Window USING [BoxHandle, Place], WindowOps USING [ AbsoluteBoxHandle, Bounds, DisplayList, GetContext, lock, Object, RecList, ScanLine, ScreenBox, SpecialTimesWpl]; DisplayImplF: MONITOR LOCKS WindowOps.lock IMPORTS DisplayOps, SpecialDisplay, WindowOps EXPORTS DisplayInternal, SpecialDisplay, Window = BEGIN -- exported types Handle: TYPE = LONG POINTER TO Object; Object: PUBLIC TYPE = WindowOps.Object; FillHandle: TYPE = LONG POINTER TO FillObject; FillObject: PUBLIC TYPE = DisplayOps.FillObject; -- copied types because John doesn't like them LineStyle: TYPE = SpecialDisplay.LineStyle; Special: TYPE = SpecialDisplay.Special; yMin, yLen: INTEGER; -- used for filing fillLeft, fillRight: FillHandle; gA, gB, gC: LONG INTEGER; -- global coefficients for conic gD, gE: LONG INTEGER; gF: REAL; gStop: IVector.IPoint; -- global stop place gOrg: Window.Place; -- origin for coeffs clip: WindowOps.RecList; globeContext: Special; globeShade: DisplayOps.Color; StoreGlobalCoeffs: PROC [ a, b, c, d, e, errorTerm: LONG INTEGER, start: IVector.IPoint] = { gA ← a; gB ← b; gC ← c; gD ← d; gE ← e; gF ← errorTerm - ConicEval[start]}; ConicEval: PROC [p: IVector.IPoint] RETURNS [REAL] = { x: REAL = p.x - gOrg.x; y: REAL = p.y - gOrg.y; RETURN[gA*x*x + y*y*gB + x*y*gC + gD*x + gE*y]}; DxDyToSlopeDir: PUBLIC PROC [dx, dy: LONG INTEGER] RETURNS [xDir, yDir: INTEGER] = { SELECT dx FROM -- Note, position goes up as y decreases < 0 => IF dy >= 0 THEN {xDir ← -1; yDir ← 1} ELSE {xDir ← -1; yDir ← -1}; > 0 => IF dy > 0 THEN {xDir ← 1; yDir ← 1} ELSE {xDir ← 1; yDir ← -1}; ENDCASE => IF dy > 0 THEN {xDir ← 1; yDir ← 1} ELSE {xDir ← -1; yDir ← -1}}; DxConic: PROC [place: IVector.IPoint] RETURNS [LONG INTEGER] = { RETURN[2*gA*(place.x - gOrg.x) + gC*(place.y - gOrg.y) + gD]}; DyConic: PROC [place: IVector.IPoint] RETURNS [LONG INTEGER] = { RETURN[2*gB*(place.y - gOrg.y) + gC*(place.x - gOrg.x) + gE]}; Conic: PUBLIC ENTRY PROC [ window: Handle, a, b, c, d, e, errorTerm: LONG INTEGER, start, stop, origin: Window.Place, sharpCornered: BOOLEAN, bounds: Window.BoxHandle ← NIL] = { ENABLE UNWIND => NULL; absBounds: WindowOps.ScreenBox = IF bounds = NIL THEN WindowOps.Bounds[window] ELSE WindowOps.AbsoluteBoxHandle[window, bounds]; absStart: Window.Place = DisplayOps.AbsPlace[window, start]; absStop: Window.Place = DisplayOps.AbsPlace[window, stop]; gOrg ← DisplayOps.AbsPlace[window, origin]; globeShade ← black; IF ~window.inTree THEN RETURN; StoreGlobalCoeffs[a, b, c, d, e, errorTerm, absStart]; FOR r: WindowOps.RecList ← WindowOps.DisplayList[window], r.link UNTIL r = NIL DO clip ← r; IF DisplayOps.Intersect[r, absBounds] THEN { globeContext ← SpecialDisplay.defaultContext; DDAStartUp[ start: absStart, stop: absStop, startError: errorTerm, selected: FALSE, filled: FALSE, sharp: sharpCornered, dashes: SpecialDisplay.solid]}; ENDLOOP}; SpecialConic: PUBLIC ENTRY PROC [ window: Handle, a, b, c, d, e, errorTerm: LONG INTEGER, start, stop, origin: Window.Place, sharpCornered: BOOLEAN, bounds: Window.BoxHandle, dashes: SpecialDisplay.LineStyle, flags: BitBlt.BitBltFlags, context: Special ← SpecialDisplay.defaultContext] = { ENABLE UNWIND => NULL; absBounds: WindowOps.ScreenBox = IF bounds = NIL THEN WindowOps.Bounds[window] ELSE WindowOps.AbsoluteBoxHandle[window, bounds]; absStart: Window.Place = DisplayOps.AbsPlace[window, start]; absStop: Window.Place = DisplayOps.AbsPlace[window, stop]; filled: BOOLEAN = context.alloc # NIL; gOrg ← DisplayOps.AbsPlace[window, origin]; globeShade ← DisplayOps.Shade[flags]; IF ~window.inTree THEN RETURN; FOR r: WindowOps.RecList ← DisplayOps.FillList[window, filled], r.link UNTIL r = NIL DO globeContext ← WindowOps.GetContext[r, context]; clip ← r; IF DisplayOps.Intersect[r, absBounds] THEN { StoreGlobalCoeffs[a, b, c, d, e, errorTerm, absStart]; IF filled THEN { yMin ← MAX[r.box.top, absBounds.top] - 1; -- add saftey bit below yLen ← MIN[r.box.bottom, absBounds.bottom] - yMin + 1; -- and above fillLeft ← globeContext.alloc[window, yMin, yLen]; fillRight ← globeContext.alloc[window, yMin, yLen]}; DDAStartUp[ absStart, absStop, errorTerm, TRUE, filled, sharpCornered, dashes]; IF filled THEN { -- clip fill arrays to window FOR i: INTEGER IN [0..yLen) DO fillLeft.xs[i] ← MIN[ MAX[fillLeft.xs[i], r.box.left], r.box.right]; fillRight.xs[i] ← MIN[ MAX[fillRight.xs[i], r.box.left], r.box.right]; ENDLOOP}}; ENDLOOP}; -- variables below are initalized by DDAStartUp and used by DDA routines. -- DDALoopCount, dot, xDot, yDot, xyDot and inBox are also updated -- by DDA routines. stopInRightUpDDA, stopInLeftUpDDA, stopInLeftDownDDA, stopInRightDownDDA: BOOLEAN; -- informs apprpriate DDA to check if it is passing stop point DDALoopMax, DDALoopCount: CARDINAL; -- used make sure we dont stop too soon dx, dy, dxdy, dxdx, dydy: LONG INTEGER; dot, xDot, yDot, xyDot, newXyDot: INTEGER; --used to corerce sharp corned curves dotVec: IVector.IVec; dotFudge: INTEGER = -1; -- more sharp cornered stuff inBox: BOOLEAN; -- true if point currently maintained in DDA in visible oldX, oldY: INTEGER; gSelected: BOOLEAN ← FALSE; -- global selected gFilled: BOOLEAN; -- global filled gSharp: BOOLEAN; -- global sharp flag -- dashed lines stuff below dOrtho: CARDINAL = 5; dDiag: CARDINAL = 7; start1: CARDINAL = 0; stop1, start2, stop2, start3, stop3: CARDINAL; dashSum, dashCnt: CARDINAL; thickness: CARDINAL; -- line thickness dotVisible: BOOLEAN; DotVisible: PROC [x, y: INTEGER] RETURNS [BOOLEAN] = INLINE { RETURN[ x IN [clip.box.left..clip.box.right) AND y IN [clip.box.top..clip.box.bottom)]}; DDAStartUp: PROC [ start, stop: IVector.IPoint, startError: LONG INTEGER, selected: BOOLEAN, filled: BOOLEAN, sharp: BOOLEAN, dashes: LineStyle] = { dxStart: LONG INTEGER ← DyConic[start]; dyStart: LONG INTEGER ← -DxConic[start]; dxStop: LONG INTEGER ← DyConic[stop]; dyStop: LONG INTEGER ← -DxConic[stop]; startXDir, startYDir, stopXDir, stopYDir: INTEGER; Slope: TYPE = RECORD [xDir, yDir: INTEGER]; x: INTEGER = start.x; y: INTEGER = start.y; -- Returns true if we have to loop around the curve to get from start -- to stop, note start = stop returns TRUE. StartAfterStop: PROC [xDir, yDir: INTEGER] RETURNS [BOOLEAN] = INLINE { RETURN[ xDir*(start.x - stop.x) > 0 OR yDir*(start.y - stop.y) > 0 OR (start.x = stop.x AND start.y = stop.y -- for ellipses -- )]}; -- set up global variables gStop ← stop; gFilled ← filled; gSharp ← sharp; gSelected ← selected; -- set up line style parameters thickness ← dashes.thickness; stop1 ← dashes.widths[0]*dOrtho; start2 ← stop1 + dashes.widths[1]*dOrtho; stop2 ← start2 + dashes.widths[2]*dOrtho; start3 ← stop2 + dashes.widths[3]*dOrtho; stop3 ← start3 + dashes.widths[4]*dOrtho; dashSum ← stop3 + dashes.widths[5]*dOrtho; dashCnt ← 0; -- set up loop monitoring and stop detection DDALoopCount ← 0; DDALoopMax ← 0; stopInRightUpDDA ← stopInLeftUpDDA ← stopInLeftDownDDA ← stopInRightDownDDA ← FALSE; -- set up DDA parameters dxdy ← gC; dxdx ← gA*2; dydy ← gB*2; -- inBox used to for fill projections stuff inBox ← DotVisible[start.x, start.y]; IF sharp THEN { -- set up parameters for keeping conic on right side of line xDif: INTEGER = stop.x - start.x; yDif: INTEGER = stop.y - start.y; tan45: INTEGER = 1; -- tangent of 45 degrees, rotation angle -- below does a psuedo rotation by arctan[tan45]; dxStart ← xDif/tan45 - yDif; dyStart ← xDif + yDif/tan45; dxStop ← xDif/tan45 + yDif; dyStop ← -xDif + yDif/tan45; dotVec ← [-yDif, xDif]; dot ← 0}; [xDir: stopXDir, yDir: stopYDir] ← DxDyToSlopeDir[dxStop, dyStop]; SELECT Slope[xDir: stopXDir, yDir: stopYDir] FROM [1, -1] => stopInRightUpDDA ← TRUE; [-1, -1] => stopInLeftUpDDA ← TRUE; [-1, 1] => stopInLeftDownDDA ← TRUE; [1, 1] => stopInRightDownDDA ← TRUE; ENDCASE; [xDir: startXDir, yDir: startYDir] ← DxDyToSlopeDir[dxStart, dyStart]; SELECT Slope[xDir: startXDir, yDir: startYDir] FROM [1, -1] => { IF stopInRightUpDDA AND StartAfterStop[xDir: startXDir, yDir: startYDir] THEN DDALoopMax ← 2; RightUpORLeftDown[start: start, error: startError, isRightUp: TRUE]}; [-1, -1] => { IF stopInLeftUpDDA AND StartAfterStop[xDir: startXDir, yDir: startYDir] THEN DDALoopMax ← 2; LeftUpORRightDown[start: start, error: startError, isLeftUp: TRUE]}; [-1, 1] => { IF stopInLeftDownDDA AND StartAfterStop[xDir: startXDir, yDir: startYDir] THEN DDALoopMax ← 2; RightUpORLeftDown[start: start, error: startError, isRightUp: FALSE]}; [1, 1] => { IF stopInRightDownDDA AND StartAfterStop[xDir: startXDir, yDir: startYDir] THEN DDALoopMax ← 2; LeftUpORRightDown[start: start, error: startError, isLeftUp: FALSE]}; ENDCASE}; -- Below are two DDA routines. -- They are pretty much the same, the difference being that one -- is optimized to handle the right-up and left-down directions -- while the other handles the left-up and right-down directions. -- IMPORTANT NOTE FOR MODIFICATIONS: -- If you fix a bug in one of the DDA routines, most likely you need to -- fix it in the other. In this case note the following symetry -- in the code of the routines. Each DDA routine consists of two parts, -- a steep DDA and a shalow DDA. In one the shallow DDA is first and -- in the other the steep DDA is first. The difference between a steep DDA -- and a shalow DDA is that x and y are interchanged. The difference -- between first and second DDAs is that different variables are -- maintained and used to determine the direction of the next move. -- (in particluar, newXyerror is used in place of xyerror in the second DDA.) -- The only other differences in the routines are 1) they have different -- names, 2) the declarations of the constants checkYstop and checkXstop and the -- INLINE procedures xDir and yDir and the StartFillSeg EndFillSeg INLINES, 3) -- The DDA's they call when done with their section of the curve are -- different. There is one other important asymetry: RightUpORLeftDown -- increments the variable DDALoopCount, while the other one doesn't. RightUpORLeftDown: PROC [ start: IVector.IPoint, error: LONG INTEGER, isRightUp: BOOLEAN] = { xDir: PROC [i: INTEGER] RETURNS [INTEGER] = INLINE { RETURN[IF isRightUp THEN i ELSE -i]}; yDir: PROC [i: INTEGER] RETURNS [INTEGER] = INLINE { RETURN[IF isRightUp THEN -i ELSE i]}; xDirL: PROC [i: LONG INTEGER] RETURNS [LONG INTEGER] = INLINE { RETURN[IF isRightUp THEN i ELSE -i]}; yDirL: PROC [i: LONG INTEGER] RETURNS [LONG INTEGER] = INLINE { RETURN[IF isRightUp THEN -i ELSE i]}; newDy, newDx: LONG INTEGER; xerror, yerror, xyerror, newXyerror: LONG INTEGER; x: INTEGER ← start.x; y: INTEGER ← start.y; ySL: WindowOps.ScanLine ← globeContext.bmAddress + WindowOps.SpecialTimesWpl[INTEGER[start.y], globeContext]; checkYstop: BOOLEAN = IF isRightUp THEN (stopInRightUpDDA OR stopInLeftUpDDA) AND DDALoopMax <= DDALoopCount ELSE (stopInLeftDownDDA OR stopInRightDownDDA) AND DDALoopMax <= DDALoopCount; checkXstop: BOOLEAN = IF isRightUp THEN stopInRightUpDDA AND DDALoopMax <= DDALoopCount ELSE stopInLeftDownDDA AND DDALoopMax <= DDALoopCount; StartFillSegment: PROC [x, y: INTEGER] = INLINE { IF gFilled AND y IN [clip.box.top..clip.box.bottom) THEN { y ← y - yMin; IF y < 0 OR y >= yLen THEN DisplayOps.LogError[]; IF isRightUp THEN fillRight.xs[y] ← x ELSE fillLeft.xs[y] ← x}}; EndFillSegment: PROC [x, y: INTEGER] = INLINE { IF gFilled AND y IN [clip.box.top..clip.box.bottom) THEN { y ← y - yMin; IF y < 0 OR y >= yLen THEN DisplayOps.LogError[]; IF isRightUp THEN fillRight.xs[y] ← x ELSE fillLeft.xs[y] ← x}}; IncDDALoopCount: PROC = { DDALoopCount ← DDALoopCount + 1; IF DDALoopCount > 2 THEN DisplayOps.LogError[infiniteConicDDALoop]}; -- StartFillSegment[x, y]; dx ← DxConic[start] + xDirL[gA]; dy ← DyConic[start] + yDirL[gB]; oldX ← x; oldY ← y; -- shallow part UNTIL checkXstop AND xDir[x - gStop.x] >= 0 OR checkYstop AND yDir[y - gStop.y] > 0 DO -- x varies more quickly xerror ← error + xDirL[dx]; newDy ← dy + xDirL[dxdy]; xyerror ← xerror + yDirL[newDy]; yerror ← error + yDirL[dy]; IF gSharp THEN { xDot ← dot + xDir[dotVec.x]; xyDot ← xDot + yDir[dotVec.y]; yDot ← dot + yDir[dotVec.y]}; IF xerror <= 0 OR (xyerror <= 0 AND xerror <= -xyerror) OR (gSharp AND xyDot <= dotFudge) THEN { -- inc x only error ← xerror; x ← x + xDir[1]; dx ← dx + xDirL[dxdx]; dy ← newDy; dot ← xDot; dashCnt ← dashCnt + dOrtho} ELSE IF xyerror <= 0 OR (yerror <= 0 AND xyerror <= -yerror) OR (gSharp AND yDot <= dotFudge) THEN { -- both x and y change error ← xyerror; ySL ← ySL + yDir[globeContext.wpl]; y ← y + yDir[1]; x ← x + xDir[1]; dx ← dx + xDirL[dxdx] + yDirL[dxdy]; dy ← newDy + yDirL[dydy]; dot ← xyDot; dashCnt ← dashCnt + dDiag; EndFillSegment[oldX, oldY]; StartFillSegment[x, y]} ELSE EXIT; -- "paint dot" dotVisible ← DotVisible[x, y]; UNTIL dashCnt < dashSum DO dashCnt ← dashCnt - dashSum ENDLOOP; IF NOT gFilled AND (dashCnt IN [start1..stop1) OR dashCnt IN [start2..stop2) OR dashCnt IN [start3..stop3)) THEN { IF dotVisible THEN ySL[x] ← (SELECT globeShade FROM white => FALSE, black => TRUE, ENDCASE => ~ySL[x]); IF thickness > 1 THEN { -- shalow part, interior above quotiet: INTEGER = thickness/2; remainder: INTEGER = thickness MOD 2; FOR i: INTEGER IN [1..quotiet] DO thickY: INTEGER ← y + yDir[i]; thickYSL: WindowOps.ScanLine ← ySL + yDir[i*globeContext.wpl]; IF DotVisible[x, thickY] THEN thickYSL[x] ← (SELECT globeShade FROM white => FALSE, black => TRUE, ENDCASE => ~thickYSL[x]); IF i < quotiet OR remainder = 1 THEN { thickY ← y - yDir[i]; thickYSL ← ySL - yDir[i*globeContext.wpl]; IF DotVisible[x, thickY] THEN thickYSL[x] ← (SELECT globeShade FROM white => FALSE, black => TRUE, ENDCASE => ~thickYSL[x])}; ENDLOOP}}; oldX ← x; oldY ← y; REPEAT FINISHED => {EndFillSegment[x, y]; RETURN} ENDLOOP; EndFillSegment[x, y]; -- steep part UNTIL checkXstop AND xDir[x - gStop.x] > 0 OR checkYstop AND yDir[y - gStop.y] >= 0 DO yerror ← error + yDirL[dy]; newDx ← dx + yDirL[dxdy]; xyerror ← yerror + xDirL[newDx]; newXyerror ← yerror - xDirL[newDx] + dxdx; IF gSharp THEN { yDot ← dot + yDir[dotVec.y]; xyDot ← yDot + xDir[dotVec.x]; newXyDot ← yDot - xDir[dotVec.x]}; IF xyerror <= 0 OR (yerror <= 0 AND xyerror <= -yerror) OR (gSharp AND yDot <= dotFudge) THEN { -- both change error ← xyerror; ySL ← ySL + yDir[globeContext.wpl]; y ← y + yDir[1]; x ← x + xDir[1]; dx ← newDx + xDirL[dxdx]; dy ← dy + yDirL[dydy] + xDirL[dxdy]; dot ← xyDot; dashCnt ← dashCnt + dDiag} ELSE IF yerror <= 0 OR (newXyerror <= 0 AND yerror <= -newXyerror) OR (gSharp AND newXyDot <= dotFudge) THEN { -- only y changes error ← yerror; ySL ← ySL + yDir[globeContext.wpl]; y ← y + yDir[1]; dx ← newDx; dy ← dy + yDirL[dydy]; dot ← yDot; dashCnt ← dashCnt + dOrtho} ELSE EXIT; dotVisible ← DotVisible[x, y]; UNTIL dashCnt < dashSum DO dashCnt ← dashCnt - dashSum ENDLOOP; IF NOT gFilled AND (dashCnt IN [start1..stop1) OR dashCnt IN [start2..stop2) OR dashCnt IN [start3..stop3)) THEN { IF dotVisible THEN ySL[x] ← (SELECT globeShade FROM white => FALSE, black => TRUE, ENDCASE => ~ySL[x]); IF thickness > 1 THEN { -- steep part, interior to the left quotiet: INTEGER = thickness/2; remainder: INTEGER = thickness MOD 2; FOR i: INTEGER IN [1..quotiet] DO thickX: INTEGER ← x - xDir[i]; IF DotVisible[thickX, y] THEN ySL[thickX] ← (SELECT globeShade FROM white => FALSE, black => TRUE, ENDCASE => ~ySL[thickX]); IF i < quotiet OR remainder = 1 THEN { thickX ← x + xDir[i]; IF DotVisible[thickX, y] THEN ySL[thickX] ← (SELECT globeShade FROM white => FALSE, black => TRUE, ENDCASE => ~ySL[thickX])}; ENDLOOP}}; StartFillSegment[x, y]; EndFillSegment[x, y]; oldX ← x; oldY ← y; REPEAT FINISHED => {RETURN} ENDLOOP; IncDDALoopCount[]; LeftUpORRightDown[start: [x, y], error: error, isLeftUp: isRightUp]}; LeftUpORRightDown: PROC [ start: IVector.IPoint, error: LONG INTEGER, isLeftUp: BOOLEAN] = { xDir: PROC [i: INTEGER] RETURNS [INTEGER] = INLINE { RETURN[IF isLeftUp THEN -i ELSE i]}; yDir: PROC [i: INTEGER] RETURNS [INTEGER] = INLINE { RETURN[IF isLeftUp THEN -i ELSE i]}; xDirL: PROC [i: LONG INTEGER] RETURNS [LONG INTEGER] = INLINE { RETURN[IF isLeftUp THEN -i ELSE i]}; yDirL: PROC [i: LONG INTEGER] RETURNS [LONG INTEGER] = INLINE { RETURN[IF isLeftUp THEN -i ELSE i]}; newDy, newDx: LONG INTEGER; xerror, yerror, xyerror, newXyerror: LONG INTEGER; x: INTEGER ← start.x; y: INTEGER ← start.y; ySL: WindowOps.ScanLine ← globeContext.bmAddress + WindowOps.SpecialTimesWpl[INTEGER[start.y], globeContext]; checkXstop: BOOLEAN = IF isLeftUp THEN (stopInLeftUpDDA OR stopInLeftDownDDA) AND DDALoopMax <= DDALoopCount ELSE (stopInRightDownDDA OR stopInRightUpDDA) AND DDALoopMax <= DDALoopCount; checkYstop: BOOLEAN = IF isLeftUp THEN stopInLeftUpDDA AND DDALoopMax <= DDALoopCount ELSE stopInRightDownDDA AND DDALoopMax <= DDALoopCount; StartFillSegment: PROC [x, y: INTEGER] = INLINE { IF gFilled AND y IN [clip.box.top..clip.box.bottom) THEN { y ← y - yMin; IF y < 0 OR y >= yLen THEN DisplayOps.LogError[]; IF isLeftUp THEN fillRight.xs[y] ← x ELSE fillLeft.xs[y] ← x}}; EndFillSegment: PROC [x, y: INTEGER] = INLINE { IF gFilled AND y IN [clip.box.top..clip.box.bottom) THEN { y ← y - yMin; IF y < 0 OR y >= yLen THEN DisplayOps.LogError[]; IF isLeftUp THEN fillRight.xs[y] ← x ELSE fillLeft.xs[y] ← x}}; -- StartFillSegment[x, y]; EndFillSegment[x, y]; dx ← DxConic[start] + xDirL[gA]; dy ← DyConic[start] + yDirL[gB]; oldX ← x; oldY ← y; -- steep part UNTIL checkXstop AND xDir[x - gStop.x] > 0 OR checkYstop AND yDir[y - gStop.y] >= 0 DO -- y varies more quickly yerror ← error + yDirL[dy]; newDx ← dx + yDirL[dxdy]; xyerror ← yerror + xDirL[newDx]; xerror ← error + xDirL[dx]; IF gSharp THEN { xDot ← dot + xDir[dotVec.x]; xyDot ← xDot + yDir[dotVec.y]; yDot ← dot + yDir[dotVec.y]}; IF yerror <= 0 OR (xyerror <= 0 AND yerror <= -xyerror) OR (gSharp AND xyDot <= dotFudge) THEN { -- only y changes error ← yerror; ySL ← ySL + yDir[globeContext.wpl]; y ← y + yDir[1]; dx ← newDx; dy ← dy + yDirL[dydy]; dot ← yDot; dashCnt ← dashCnt + dOrtho} ELSE IF xyerror <= 0 OR (xerror <= 0 AND xyerror <= -xerror) OR (gSharp AND xDot <= dotFudge) THEN { -- both change error ← xyerror; ySL ← ySL + yDir[globeContext.wpl]; y ← y + yDir[1]; x ← x + xDir[1]; dx ← newDx + xDirL[dxdx]; dy ← dy + yDirL[dydy] + xDirL[dxdy]; dot ← xyDot; dashCnt ← dashCnt + dDiag} ELSE EXIT; dotVisible ← DotVisible[x, y]; UNTIL dashCnt < dashSum DO dashCnt ← dashCnt - dashSum ENDLOOP; IF NOT gFilled AND (dashCnt IN [start1..stop1) OR dashCnt IN [start2..stop2) OR dashCnt IN [start3..stop3)) THEN { IF dotVisible THEN ySL[x] ← (SELECT globeShade FROM white => FALSE, black => TRUE, ENDCASE => ~ySL[x]); IF thickness > 1 THEN { -- steep part, interior to the left quotiet: INTEGER = thickness/2; remainder: INTEGER = thickness MOD 2; FOR i: INTEGER IN [1..quotiet] DO thickX: INTEGER ← x + xDir[i]; IF DotVisible[thickX, y] THEN ySL[thickX] ← (SELECT globeShade FROM white => FALSE, black => TRUE, ENDCASE => ~ySL[thickX]); IF i < quotiet OR remainder = 1 THEN { thickX ← x - xDir[i]; IF DotVisible[thickX, y] THEN ySL[thickX] ← (SELECT globeShade FROM white => FALSE, black => TRUE, ENDCASE => ~ySL[thickX])}; ENDLOOP}}; StartFillSegment[x, y]; EndFillSegment[x, y]; oldX ← x; oldY ← y; REPEAT FINISHED => {RETURN} ENDLOOP; -- shallow part oldX ← x; oldY ← y; StartFillSegment[x, y]; UNTIL checkXstop AND xDir[x - gStop.x] >= 0 OR checkYstop AND yDir[y - gStop.y] > 0 DO xerror ← error + xDirL[dx]; newDy ← dy + xDirL[dxdy]; xyerror ← xerror + yDirL[newDy]; newXyerror ← xerror - yDirL[newDy] + dydy; IF gSharp THEN { xDot ← dot + xDir[dotVec.x]; xyDot ← xDot + yDir[dotVec.y]; newXyDot ← xDot - yDir[dotVec.y]}; IF xyerror <= 0 OR (xerror <= 0 AND xyerror <= -xerror) OR (gSharp AND xDot <= dotFudge) THEN { -- both change error ← xyerror; ySL ← ySL + yDir[globeContext.wpl]; y ← y + yDir[1]; x ← x + xDir[1]; dx ← dx + xDirL[dxdx] + yDirL[dxdy]; dy ← newDy + yDirL[dydy]; dot ← xyDot; dashCnt ← dashCnt + dDiag; EndFillSegment[oldX, oldY]; StartFillSegment[x, y]} ELSE IF xerror <= 0 OR (newXyerror <= 0 AND xerror <= -newXyerror) OR (gSharp AND newXyDot <= dotFudge) THEN { -- only x changes error ← xerror; x ← x + xDir[1]; dx ← dx + xDirL[dxdx]; dy ← newDy; dashCnt ← dashCnt + dOrtho; dot ← xDot} ELSE EXIT; dotVisible ← DotVisible[x, y]; UNTIL dashCnt < dashSum DO dashCnt ← dashCnt - dashSum ENDLOOP; IF NOT gFilled AND (dashCnt IN [start1..stop1) OR dashCnt IN [start2..stop2) OR dashCnt IN [start3..stop3)) THEN { IF dotVisible THEN ySL[x] ← (SELECT globeShade FROM white => FALSE, black => TRUE, ENDCASE => ~ySL[x]); IF thickness > 1 THEN { -- shalow part, interior below quotiet: INTEGER = thickness/2; remainder: INTEGER = thickness MOD 2; FOR i: INTEGER IN [1..quotiet] DO thickY: INTEGER ← y - yDir[i]; thickYSL: WindowOps.ScanLine ← ySL - yDir[i*globeContext.wpl]; IF DotVisible[x, thickY] THEN thickYSL[x] ← (SELECT globeShade FROM white => FALSE, black => TRUE, ENDCASE => ~thickYSL[x]); IF i < quotiet OR remainder = 1 THEN { thickY ← y + yDir[i]; thickYSL ← ySL + yDir[i*globeContext.wpl]; IF DotVisible[x, thickY] THEN thickYSL[x] ← (SELECT globeShade FROM white => FALSE, black => TRUE, ENDCASE => ~thickYSL[x])}; ENDLOOP}}; oldX ← x; oldY ← y; REPEAT FINISHED => {EndFillSegment[x, y]; RETURN} ENDLOOP; EndFillSegment[x, y]; RightUpORLeftDown[start: [x, y], error: error, isRightUp: NOT isLeftUp]}; END.