DIRECTORY SelectRegions, Basics USING [RawWords], RefTab USING [Create, Fetch, Ref, Store], SampleMapOps USING [Buffer, Clear, Create, Fill, Function, Get, GetPointer, ObtainBuffer, Put, PutSample, SampleMap, Transfer, UnsafeCreate]; SelectRegionsImpl: CEDAR PROGRAM IMPORTS RefTab, SampleMapOps EXPORTS SelectRegions = BEGIN OPEN SelectRegions; Buffer: TYPE ~ SampleMapOps.Buffer; SampleArrayHack: TYPE ~ REF SampleArrayHackRep; SampleArrayHackRep: TYPE ~ RECORD [ SEQUENCE n: NAT OF SampleVectorHack ]; SampleVectorHack: TYPE ~ LONG POINTER TO PACKED ARRAY [0..0) OF [0..256); ArrayHack: TYPE ~ REF ArrayHackRep; ArrayHackRep: TYPE ~ RECORD [ SEQUENCE n: NAT OF VectorHack ]; VectorHack: TYPE ~ LONG POINTER TO PACKED ARRAY [0..0) OF BOOLEAN; sampleMapToSampleArrayHack: RefTab.Ref ~ RefTab.Create[]; maskFcns: ARRAY Operation OF SampleMapOps.Function _ [ on: [or, null], off: [and, complement], flip: [xor, null] ]; PaintBrush: PUBLIC PROC [sm: SampleMap, loc: CVEC, op: Operation, brush: SampleMap] ~ { SampleMapOps.Transfer[ dest: sm, destStart: loc, source: [sampleMap: brush], function: maskFcns[op] ]; }; PaintPixel: PUBLIC PROC [sm: SampleMap, loc: CVEC, op: Operation] ~ { SampleMapOps.PutSample[sampleMap: sm, index: loc, value: 1, function: maskFcns[op]]; }; FillRectangle: PUBLIC PROC [sm: SampleMap, rect: Rectangle, op: Operation] ~ { SampleMapOps.Fill[dest: [sm, [rect.sMin, rect.fMin], [rect.sMax-rect.sMin, rect.fMax-rect.fMin]], value: 1, function: maskFcns[op]]; }; TestDifferences: PROC [a, b: Rectangle] RETURNS [list: LIST OF Rectangle _ NIL] ~ { Action: PROC [r: Rectangle] ~ { list _ CONS[r, list]; }; Differences[a, b, Action]; }; Intersect: PUBLIC PROC [a, b: Rectangle] RETURNS [Rectangle] ~ { RETURN [[sMin: MAX[a.sMin, b.sMin], fMin: MAX[a.fMin, b.fMin], sMax: MIN[a.sMax, b.sMax], fMax: MIN[a.fMax, b.fMax]]]; }; Differences: PUBLIC PROC [a, b: Rectangle, action: PROC [r: Rectangle]] ~ { i: Rectangle ~ Intersect[a, b]; DoRectangle: PROC [q: Rectangle] ~ { IF q.fMax>q.fMin THEN { IF q.sMini.sMax THEN action[[sMin: i.sMax, sMax: q.sMax, fMin: q.fMin, fMax: q.fMax]]; }; IF i.sMax>i.sMin THEN { IF q.fMini.fMax THEN action[[sMin: i.sMin, sMax: i.sMax, fMin: i.fMax, fMax: q.fMax]]; }; }; IF i.sMaxa.sMin AND a.fMax>a.fMin THEN action[a]; IF b.sMax>b.sMin AND b.fMax>b.fMin THEN action[b]; } ELSE { DoRectangle[a]; DoRectangle[b]; }; }; trcSize: CARDINAL ~ 256; ApplyTRC: PUBLIC PROC [sm: SampleMap, sa: SampleArray, trcs: TRCSequence, bounds: Rectangle _ nullRectangle] ~ TRUSTED { UnsafeLookup: UNSAFE PROC [tbl, src, dst: LONG POINTER TO Basics.RawWords, count: NAT] ~ UNCHECKED { THROUGH [0..count/8) DO dst[0] _ tbl[src[0]]; dst[1] _ tbl[src[1]]; dst[2] _ tbl[src[2]]; dst[3] _ tbl[src[3]]; dst[4] _ tbl[src[4]]; dst[5] _ tbl[src[5]]; dst[6] _ tbl[src[6]]; dst[7] _ tbl[src[7]]; src _ src+8; dst _ dst+8; ENDLOOP; FOR k: NAT IN[0..count MOD 8) DO dst[k] _ tbl[src[k]] ENDLOOP; }; fSize: CARDINAL; scratchBuffer, bitBuffer: Buffer; scratchPtr: LONG POINTER TO Basics.RawWords; scratchMapByteKludge: SampleMap; table: Buffer; tablePtr: LONG POINTER TO Basics.RawWords; IF sa.n#trcs.n THEN ERROR; bounds _ Intersect[bounds, [sMin: 0, fMin: 0, sMax: sa.sSize-1, fMax: sa.fSize-1]]; fSize _ bounds.fMax+1-bounds.fMin; bitBuffer _ SampleMapOps.ObtainBuffer[length: fSize]; scratchBuffer _ SampleMapOps.ObtainBuffer[length: fSize]; scratchPtr _ SampleMapOps.GetPointer[buffer: scratchBuffer, start: 0, count: fSize]; scratchMapByteKludge _ SampleMapOps.UnsafeCreate[sSize: 1, fSize: fSize*2, bitsPerSample: 8, bitsPerLine: fSize*16, base: [word: scratchPtr, bit: 0], nWords: fSize, ref: NIL]; table _ SampleMapOps.ObtainBuffer[length: trcSize*2]; tablePtr _ SampleMapOps.GetPointer[buffer: table, start: 0, count: trcSize*2]; FOR sample: NAT IN [0..trcSize) DO table[sample] _ sample; ENDLOOP; FOR i: NAT IN [0..sa.n) DO sep: SampleMap ~ sa.layer[i].sm; LOOPHOLE[tablePtr+trcSize, LONG POINTER TO TRC]^ _ trcs[i]^; FOR scan: CARDINAL IN [bounds.sMin .. bounds.sMax] DO SampleMapOps.Get[buffer: bitBuffer, count: fSize, sampleMap: sm, s: scan, f: bounds.fMin]; SampleMapOps.Get[buffer: scratchBuffer, count: fSize, sampleMap: sep, s: scan, f: bounds.fMin]; SampleMapOps.Put[buffer: bitBuffer, count: fSize, sampleMap: scratchMapByteKludge, df: 2]; UnsafeLookup[tbl: tablePtr, src: scratchPtr, dst: scratchPtr, count: fSize]; SampleMapOps.Put[buffer: scratchBuffer, count: fSize, sampleMap: sep, s: scan, f: bounds.fMin]; ENDLOOP; ENDLOOP; }; OutOfRectangle: PUBLIC ERROR ~ CODE; Direction: TYPE ~ { r, ur, u, ul, l, dl, d, dr}; next: ARRAY Direction OF Direction ~ [ur, u, ul, l, dl, d, dr, r]; prv: ARRAY Direction OF Direction ~ [dr, r, ur, u, ul, l, dl, d]; start: ARRAY Direction OF Direction ~ [dr, dr, r, r, ul, ul, dl, dl]; ds: ARRAY Direction OF INTEGER ~ [ 0, -1, -1, -1, 0, 1, 1, 1]; df: ARRAY Direction OF INTEGER ~ [ 1, 1, 0, -1, -1, -1, 0, 1]; Fill: PUBLIC PROC [sm: SampleMap, loc: CVEC, bounds: Rectangle _ nullRectangle] RETURNS [extent: Rectangle] ~ TRUSTED { sMin, sMax, fMin, fMax: CARDINAL; filledMap: SampleMap ~ SampleMapOps.Create[sSize: sm.sSize, fSize: sm.fSize, bitsPerSample: 1]; edgesMap: SampleMap ~ SampleMapOps.Create[sSize: sm.sSize, fSize: sm.fSize, bitsPerSample: 1]; filled: ArrayHack ~ SampleMapToArrayHack[filledMap]; edges: ArrayHack ~ SampleMapToArrayHack[edgesMap]; mask: ArrayHack ~ SampleMapToArrayHack[sm]; leftEdges: LIST OF CVEC _ NIL; IfRectangle: PROC [v: CVEC] RETURNS [BOOLEAN] ~ TRUSTED INLINE { RETURN [~(v.s IN (sMin .. sMax) AND v.f IN (fMin .. fMax))] }; IfEdge: PROC [v: CVEC] RETURNS [BOOLEAN] ~ TRUSTED INLINE { RETURN [(mask[v.s][v.f] AND ~filled[v.s][v.f]) OR IfRectangle[v]] }; RememberLeftEdgePos: PROC [p: CVEC] ~ TRUSTED INLINE { leftEdges _ CONS[p, leftEdges]; extent.sMin _ MIN[extent.sMin, p.s]; extent.sMax _ MAX[extent.sMax, p.s]; extent.fMin _ MIN[extent.fMin, p.f]; }; GetNextLeftEdgePos: PROC RETURNS [p: CVEC] ~ TRUSTED INLINE { p _ leftEdges.first; leftEdges _ leftEdges.rest; }; Navigate: PROC [initial: CVEC] ~ TRUSTED { IsSingleton: PROC RETURNS [BOOLEAN] ~ TRUSTED INLINE { count: CARDINAL _ 0; IF IfRectangle[initial] THEN RETURN [FALSE]; --We know it's not a singleton on the bounds edge, and it may be dangerous to check as below... FOR f: INTEGER IN [-1..1] DO FOR s: INTEGER IN [-1..1] DO IF IfEdge[[s: initial.s+s, f: initial.f+f]] THEN count _ count+1; ENDLOOP; ENDLOOP; RETURN [count <= 1] }; direction, lastDirection: Direction _ dr; --(Choosing dr rather than r, so that the algorithm checks dl and d). pos: CVEC _ initial; IF edges[initial.s][initial.f] THEN RETURN; --We've already seen this edge IF IsSingleton[] THEN { RememberLeftEdgePos[initial]; RETURN; }; lastDirection _ r; --(Actually, know can't get this...) UNTIL IfEdge[[s: pos.s-ds[lastDirection], f: pos.f-df[lastDirection]]] DO --Reversed sign is because we're moving into rather than out of pos. lastDirection _ prv[lastDirection]; ENDLOOP; DO edges[pos.s][pos.f] _ TRUE; direction _ start[direction]; DO IF IfEdge[[s: pos.s+ds[direction], f: pos.f+df[direction]]] THEN EXIT ELSE IF direction=r THEN RememberLeftEdgePos[pos]; --I.e., if we see a blank spot to our right, then this is a left edge direction _ next[direction]; ENDLOOP; pos _ [s: pos.s+ds[direction], f: pos.f+df[direction]]; IF pos=initial AND direction=lastDirection THEN EXIT; ENDLOOP; IF lastDirection IN [ur..ul] THEN RememberLeftEdgePos[initial]; }; SampleMapOps.Clear[filledMap]; SampleMapOps.Clear[edgesMap]; bounds _ Intersect[bounds, [sMin: 0, fMin: 0, sMax: sm.sSize-1, fMax: sm.fSize-1]]; sMin _ bounds.sMin; sMax _ bounds.sMax; fMin _ bounds.fMin; fMax _ bounds.fMax; IF ~loc.s IN (sMin .. sMax) OR ~loc.f IN (fMin .. fMax) THEN ERROR OutOfRectangle[]; IF mask[loc.s][loc.f] THEN RETURN; --If it's already on, there's nothing we can do... extent _ [sMin: loc.s, sMax: loc.s, fMin: loc.f, fMax: loc.f]; UNTIL mask[loc.s][loc.f] DO --Find a right edge loc.f _ loc.f + 1; ENDLOOP; Navigate[loc]; --Navigate the first edge found UNTIL leftEdges=NIL DO --While left edge positions remain... f, s: CARDINAL; vector: LONG POINTER TO PACKED ARRAY [0..0) OF BOOLEAN; [[s: s, f: f]] _ GetNextLeftEdgePos[]; vector _ mask[s]; f _ f+1; --Start on blank square UNTIL vector[f] OR f=fMax DO vector[f] _ filled[s][f] _ TRUE; f _ f+1; ENDLOOP; extent.fMax _ MAX[extent.fMax, f]; Navigate[[s: s, f: f]]; ENDLOOP; }; defaultColorDistance: PUBLIC CARDINAL _ 10; MatchColorFill: PUBLIC PROC [sm: SampleMap, sa: SampleArray, loc: CVEC, bounds: Rectangle _ nullRectangle, colorDistance: CARDINAL _ defaultColorDistance] RETURNS [extent: Rectangle] ~ TRUSTED { sMin, sMax, fMin, fMax: CARDINAL; filledMap: SampleMap ~ SampleMapOps.Create[sSize: sm.sSize, fSize: sm.fSize, bitsPerSample: 1]; edgesMap: SampleMap ~ SampleMapOps.Create[sSize: sm.sSize, fSize: sm.fSize, bitsPerSample: 1]; filled: ArrayHack ~ SampleMapToArrayHack[filledMap]; edges: ArrayHack ~ SampleMapToArrayHack[edgesMap]; mask: ArrayHack ~ SampleMapToArrayHack[sm]; redHack, greenHack, blueHack: SampleArrayHack; leftEdges: LIST OF CVEC _ NIL; red, green, blue, g2r, g2b: INTEGER; ColorDistance: PROC [r, g, b: CARDINAL] RETURNS [d: CARDINAL] ~ TRUSTED INLINE { RETURN [ABS[INTEGER[g]-INTEGER[r] - g2r] + ABS[INTEGER[g]-INTEGER[b] - g2b]]; }; FetchRed: PROC [v: CVEC] RETURNS [value: CARDINAL] ~ TRUSTED INLINE { RETURN [redHack[v.s][v.f]]; }; FetchGreen: PROC [v: CVEC] RETURNS [value: CARDINAL] ~ TRUSTED INLINE { RETURN [greenHack[v.s][v.f]]; }; FetchBlue: PROC [v: CVEC] RETURNS [value: CARDINAL] ~ TRUSTED INLINE { RETURN [blueHack[v.s][v.f]]; }; IfBounds: PROC [v: CVEC] RETURNS [BOOLEAN] ~ TRUSTED INLINE { RETURN [~(v.s IN (sMin .. sMax) AND v.f IN (fMin .. fMax))] }; IfEdge: PROC [v: CVEC] RETURNS [BOOLEAN] ~ TRUSTED INLINE { RETURN [IfBounds[v] OR (ColorDistance[r: FetchRed[v], g: FetchGreen[v], b: FetchBlue[v]] > colorDistance)]; }; RememberLeftEdgePos: PROC [p: CVEC] ~ TRUSTED INLINE { leftEdges _ CONS[p, leftEdges]; extent.sMin _ MIN[extent.sMin, p.s]; extent.sMax _ MAX[extent.sMax, p.s]; extent.fMin _ MIN[extent.fMin, p.f]; }; GetNextLeftEdgePos: PROC RETURNS [p: CVEC] ~ TRUSTED INLINE { p _ leftEdges.first; leftEdges _ leftEdges.rest; }; Navigate: PROC [initial: CVEC] ~ TRUSTED { IsSingleton: PROC RETURNS [BOOLEAN] ~ TRUSTED INLINE { count: CARDINAL _ 0; IF IfBounds[initial] THEN RETURN [FALSE]; --We know it's not a singleton on the bounds edge, and it may be dangerous to check as below... FOR f: INTEGER IN [-1..1] DO FOR s: INTEGER IN [-1..1] DO IF IfEdge[[s: initial.s+s, f: initial.f+f]] THEN count _ count+1; ENDLOOP; ENDLOOP; RETURN [count <= 1] }; direction, lastDirection: Direction _ dr; --(Choosing dr rather than r, so that the algorithm checks dl and d). pos: CVEC _ initial; IF edges[initial.s][initial.f] THEN RETURN; --We've already seen this edge IF IsSingleton[] THEN { RememberLeftEdgePos[initial]; RETURN; }; lastDirection _ r; --(Actually, know can't get this...) UNTIL IfEdge[[s: pos.s-ds[lastDirection], f: pos.f-df[lastDirection]]] DO --Reversed sign is because we're moving into rather than out of pos. lastDirection _ prv[lastDirection]; ENDLOOP; DO edges[pos.s][pos.f] _ TRUE; direction _ start[direction]; DO IF IfEdge[[s: pos.s+ds[direction], f: pos.f+df[direction]]] THEN EXIT ELSE IF direction=r THEN RememberLeftEdgePos[pos]; --I.e., if we see a blank spot to our right, then this is a left edge direction _ next[direction]; ENDLOOP; pos _ [s: pos.s+ds[direction], f: pos.f+df[direction]]; IF pos=initial AND direction=lastDirection THEN EXIT; ENDLOOP; IF lastDirection IN [ur..ul] THEN RememberLeftEdgePos[initial]; }; IF sa.n#3 THEN ERROR; redHack _ SampleMapToSampleArrayHack[sa[0].sm]; greenHack _ SampleMapToSampleArrayHack[sa[1].sm]; blueHack _ SampleMapToSampleArrayHack[sa[2].sm]; SampleMapOps.Clear[filledMap]; SampleMapOps.Clear[edgesMap]; bounds _ Intersect[bounds, [sMin: 0, fMin: 0, sMax: sm.sSize-1, fMax: sm.fSize-1]]; sMin _ bounds.sMin; sMax _ bounds.sMax; fMin _ bounds.fMin; fMax _ bounds.fMax; IF ~loc.s IN (sMin .. sMax) OR ~loc.f IN (fMin .. fMax) THEN ERROR OutOfRectangle[]; extent _ [sMin: loc.s, sMax: loc.s, fMin: loc.f, fMax: loc.f]; red _ FetchRed[loc]; green _ FetchGreen[loc]; blue _ FetchBlue[loc]; g2r _ green - red; g2b _ green - blue; UNTIL IfEdge[loc] DO --Find a right edge loc.f _ loc.f + 1; ENDLOOP; Navigate[loc]; --Navigate the first edge found UNTIL leftEdges=NIL DO --While left edge positions remain... f, s: CARDINAL; vector: LONG POINTER TO PACKED ARRAY [0..0) OF BOOLEAN; [[s: s, f: f]] _ GetNextLeftEdgePos[]; vector _ mask[s]; f _ f+1; --Start on blank square UNTIL IfEdge[[s: s, f: f]] OR f=fMax DO vector[f] _ filled[s][f] _ TRUE; f _ f+1; ENDLOOP; extent.fMax _ MAX[extent.fMax, f]; Navigate[[s: s, f: f]]; ENDLOOP; }; MatchColor: PUBLIC PROC [sm: SampleMap, sa: SampleArray, rgb: RGB, bounds: Rectangle _ nullRectangle, colorDistance: CARDINAL _ defaultColorDistance] ~ TRUSTED { mask: ArrayHack ~ SampleMapToArrayHack[sm]; redHack, greenHack, blueHack: SampleArrayHack; red, green, blue: INTEGER; ColorDistance: PROC [r, g, b: CARDINAL] RETURNS [d: CARDINAL] ~ TRUSTED INLINE { RETURN [ABS[INTEGER[r] - red] + ABS[INTEGER[g]-green] + ABS[INTEGER[b] - blue]]; }; FetchRed: PROC [s, f: CARDINAL] RETURNS [value: CARDINAL] ~ TRUSTED INLINE { RETURN [redHack[s][f]]; }; FetchGreen: PROC [s, f: CARDINAL] RETURNS [value: CARDINAL] ~ TRUSTED INLINE { RETURN [greenHack[s][f]]; }; FetchBlue: PROC [s, f: CARDINAL] RETURNS [value: CARDINAL] ~ TRUSTED INLINE { RETURN [blueHack[s][f]]; }; IF sa.n#3 THEN ERROR; redHack _ SampleMapToSampleArrayHack[sa[0].sm]; greenHack _ SampleMapToSampleArrayHack[sa[1].sm]; blueHack _ SampleMapToSampleArrayHack[sa[2].sm]; red _ rgb.r; green _ rgb.g; blue _ rgb.b; bounds _ Intersect[bounds, [sMin: 0, fMin: 0, sMax: sm.sSize-1, fMax: sm.fSize-1]]; FOR s: CARDINAL IN [bounds.sMin .. bounds.sMax] DO vector: LONG POINTER TO PACKED ARRAY [0..0) OF BOOLEAN ~ mask[s]; FOR f: CARDINAL IN [bounds.fMin .. bounds.fMax] DO IF ColorDistance[r: FetchRed[s: s, f: f], g: FetchGreen[s: s, f: f], b: FetchBlue[s: s, f: f]] < colorDistance THEN vector[f] _ TRUE; ENDLOOP; ENDLOOP; }; DiscriminateColor: PUBLIC PROC [sm: SampleMap, sa: SampleArray, near, far: RGB, bounds: Rectangle _ nullRectangle] ~ TRUSTED { nearR: INTEGER ~ near.r; nearG: INTEGER ~ near.g; nearB: INTEGER ~ near.b; farR: INTEGER ~ far.r; farG: INTEGER ~ far.g; farB: INTEGER ~ far.b; mask: ArrayHack ~ SampleMapToArrayHack[sm]; redHack, greenHack, blueHack: SampleArrayHack; Near: PROC [r, g, b: CARDINAL] RETURNS [BOOLEAN] ~ TRUSTED INLINE { RETURN [ ABS[INTEGER[r] - nearR] + ABS[INTEGER[g]-nearG] + ABS[INTEGER[b] - nearB] < ABS[INTEGER[r] - farR] + ABS[INTEGER[g]-farG] + ABS[INTEGER[b] - farB] ]; }; IF sa.n#3 THEN ERROR; redHack _ SampleMapToSampleArrayHack[sa[0].sm]; greenHack _ SampleMapToSampleArrayHack[sa[1].sm]; blueHack _ SampleMapToSampleArrayHack[sa[2].sm]; bounds _ Intersect[bounds, [sMin: 0, fMin: 0, sMax: sm.sSize-1, fMax: sm.fSize-1]]; FOR s: CARDINAL IN [bounds.sMin .. bounds.sMax] DO vector: LONG POINTER TO PACKED ARRAY [0..0) OF BOOLEAN ~ mask[s]; FOR f: CARDINAL IN [bounds.fMin .. bounds.fMax] DO IF Near[r: redHack[s][f], g: greenHack[s][f], b: blueHack[s][f]] THEN vector[f] _ TRUE; ENDLOOP; ENDLOOP; }; SampleMapToSampleArrayHack: PROC [sm: SampleMap] RETURNS [sah: SampleArrayHack] ~ TRUSTED { ref: REF ~ RefTab.Fetch[x: sampleMapToSampleArrayHack, key: sm].val; IF ref=NIL THEN { ptr: LONG POINTER _ sm.base.word; offset: NAT _ sm.bitsPerLine/16; IF sm.base.bit#0 OR sm.bitsPerSample#8 THEN ERROR; sah _ NEW[SampleArrayHackRep[sm.sSize]]; FOR i: NAT IN [0..sah.n) DO sah[i] _ ptr; ptr _ ptr+offset; ENDLOOP; [] _ RefTab.Store[x: sampleMapToSampleArrayHack, key: sm, val: sah]; } ELSE sah _ NARROW[ref]; }; SampleMapToArrayHack: PROC [sm: SampleMap] RETURNS [array: ArrayHack] ~ TRUSTED { ptr: LONG POINTER _ sm.base.word; offset: NAT _ sm.bitsPerLine/16; IF sm.base.bit#0 OR sm.bitsPerSample#1 THEN ERROR; array _ NEW[ArrayHackRep[sm.sSize]]; FOR i: NAT IN [0..array.n) DO array[i] _ ptr; ptr _ ptr+offset; ENDLOOP; }; END.  SelectRegionsImpl.mesa Copyright c 1984, 1985 by Xerox Corporation. All rights reserved. Provides facilities for selecting rectangular regions on the screen. Created Thursday, February 14, 1985 1:13:25 am PST Last edited by Eric Nickell, January 7, 1986 8:13:34 pm PST Twiddling procedures Quarantee that i is contained by q. IF a.sMin>b.sMax OR b.sMin>a.sMax OR a.fMin>b.fMax OR b.fMin>a.fMax THEN RETURN; Apply a TRC The Fill and MatchColor procedures When one has just come from direction x, one needs to check next in direction start[x]. The spots for u and ul (both containing r) are anomolous: there is no possibility of finding a pixel there, but this way those directions will record themselves as left edges. Basic Approach (taken from M. Bird and other Lispers): 1. Find a left edge. 2. For each edge not previously found: a. Circumnavigate the edge, marking and remembering it, and stashing location of new left edges b. Start moving right from the edge until you come to another edge. extent.fMax _ MAX[extent.fMax, p.f]; --Max is check elsewhere Assumes you got here from the left. Find the last direction to turn that places us on the initial square, so that main loop can test for it Here, direction contain the next direction to move. Make the move. See if this is the end of the line... Main section. Here, [x, y] is on a right edge Basic Approach (taken from M. Bird and other Lispers): 1. Find a left edge. 2. For each edge not previously found: a. Circumnavigate the edge, marking and remembering it, and stashing location of new left edges b. Start moving right from the edge until you come to another edge. extent.fMax _ MAX[extent.fMax, p.f]; --Max is check elsewhere Assumes you got here from the left. Find the last direction to turn that places us on the initial square, so that main loop can test for it Here, direction contain the next direction to move. Make the move. See if this is the end of the line... Main section. Initial values for colors Here, [x, y] is on a right edge Initial values for colors ΚΛ˜titlešœ™Icodešœ Οmœ7™BLšœD™DLšœ2™2L™;L™—šΟk ˜ L˜Lšœžœ ˜Lšœžœ˜)Lšœ žœ{˜L˜—šœžœž˜ Lšžœ˜Lšžœ˜Lšœž˜Lšžœ˜L˜Lšœžœ˜#Lšœžœžœ˜/šœžœžœ˜#Lšžœžœžœ˜#Lšœ˜—Lšœžœžœžœžœžœžœžœ ˜ILšœ žœžœ˜#šœžœžœ˜Lšžœžœžœ ˜Lšœ˜—Lšœ žœžœžœžœžœžœžœžœ˜BL˜L•StartOfExpansion#[mod: RefTab.SeqIndex _ 21B (17)]˜9—™šœ žœ žœ˜6L˜L˜L˜Lšœ˜—code2šΟn œž œžœ&˜W–š[dest: SampleMapOps.SampleMap, destStart: CVEC, source: SampleMapOps.SubMap, function: SampleMapOps.Function _ [dstFunc: null, srcFunc: null]]˜L˜ L˜L˜Lšœ˜L˜—L˜L˜—šŸ œžœžœžœ˜EL–[sampleMap: SampleMapOps.SampleMap, index: CVEC, value: CARDINAL, function: SampleMapOps.Function _ [dstFunc: null, srcFunc: null]]˜TL˜—šŸ œž œ4˜NL–x[dest: SampleMapOps.SubMap, value: CARDINAL, function: SampleMapOps.Function _ [dstFunc: null, srcFunc: null]]˜„L˜—š Ÿœžœžœžœžœ žœ˜SšŸœžœ˜Lšœžœ ˜L˜—L˜L˜—šŸ œžœžœžœ˜@Lš žœ žœžœžœžœ˜vL˜L˜—šŸ œž œžœ˜KLšœ˜šŸ œžœ˜$L™#šžœžœ˜šžœž˜LšœB˜B—šžœž˜LšœB˜B—Lšœ˜—šžœžœ˜šžœž˜LšœB˜B—šžœž˜LšœB˜B—Lšœ˜—L˜—šžœžœžœ˜(Lšžœžœžœ ˜2Lšžœžœžœ ˜2Lšœ˜—šžœ˜L˜L˜Lšœ˜—Lš žœžœžœžœžœžœ™PL˜—L˜—™ Mšœ žœ˜šŸœžœžœZžœ˜xšŸ œžœžœžœžœžœžœž œ˜dšžœž˜Lšœ˜Lšœ˜Lšœ˜Lšœ˜Lšœ˜Lšœ˜Lšœ˜Lšœ˜Lšœ ˜ L˜ Lšžœ˜—Lš žœžœžœ žœžœžœ˜>L˜L™—Lšœžœ˜L˜!Lšœ žœžœžœ˜,L˜ L–[length: NAT]˜L–;[buffer: SampleMapOps.Buffer, start: NAT, count: NAT]šœ žœžœžœ˜*L˜Lšžœ žœžœ˜L˜SL˜"L˜L–[length: NAT]˜5L–[length: NAT]˜9L–;[buffer: SampleMapOps.Buffer, start: NAT, count: NAT]šœT˜TL–Ν[sSize: CARDINAL, fSize: CARDINAL, bitsPerSample: [0..16], bitsPerLine: NAT, base: PrincOps.BitAddress, nWords: LONG CARDINAL, ref: REF ANY, scratchDescriptor: SampleMapOps.SampleMap _ NIL]šœͺžœ˜―L–[length: NAT]šœ5˜5L–;[buffer: SampleMapOps.Buffer, start: NAT, count: NAT]šœN˜NL˜šžœ žœžœž˜"L˜Lšžœ˜L˜—šžœžœžœ ž˜L˜ Lš žœžœžœžœžœ˜<šžœžœžœž˜5L–¦[buffer: SampleMapOps.Buffer, start: NAT _ 0, count: NAT _ 32767, sampleMap: SampleMapOps.SampleMap, s: NAT _ 0, f: NAT _ 0, ds: NAT _ 0, df: NAT _ 1]˜ZL˜_L–ξ[buffer: SampleMapOps.Buffer, start: NAT _ 0, count: NAT _ 32767, sampleMap: SampleMapOps.SampleMap, s: NAT _ 0, f: NAT _ 0, ds: NAT _ 0, df: NAT _ 1, function: SampleMapOps.Function _ [dstFunc: null, srcFunc: null]]˜ZLšœL˜LL–ξ[buffer: SampleMapOps.Buffer, start: NAT _ 0, count: NAT _ 32767, sampleMap: SampleMapOps.SampleMap, s: NAT _ 0, f: NAT _ 0, ds: NAT _ 0, df: NAT _ 1, function: SampleMapOps.Function _ [dstFunc: null, srcFunc: null]]˜_Lšžœ˜—Lšžœ˜—L˜——™"Lšœžœžœžœ˜$L˜Lšœ žœΟf!˜4Lšœžœ žœ  !˜FLšœžœ žœ  !˜Ešœžœ žœ  !˜GLšœ‰™‰—Lšœžœ žœžœ !˜BLšœžœ žœžœ !˜Bš Ÿœžœžœžœ%žœžœ˜w™6L™™'L™`L™D——L–[]šœžœ˜!L–@[sSize: CARDINAL, fSize: CARDINAL, bitsPerSample: [0..16]]˜_L–@[sSize: CARDINAL, fSize: CARDINAL, bitsPerSample: [0..16]]˜^L˜4L˜2L˜+Lšœ žœžœžœ˜šŸ œžœžœžœžœžœžœ˜@Lšžœžœžœžœ˜;L˜—šŸœžœžœžœžœžœžœ˜;Lšžœžœžœ˜AL˜—š Ÿœžœžœžœžœ˜6Lšœ žœ˜Lšœžœ˜$Lšœžœ˜$Lšœžœ˜$LšœžœΟc™=L˜—š Ÿœžœžœžœžœžœ˜=L˜L˜L˜—šŸœžœ žœžœ˜*š Ÿ œžœžœžœžœžœ˜6Lšœžœ˜Lš žœžœžœžœ‘_˜Œšžœžœžœ ž˜šžœžœžœ ž˜Lšžœ*žœ˜ALšžœ˜—Lšžœ˜—Lšžœ ˜L˜—L™#Lšœ*‘E˜oLšœžœ ˜L˜Lšžœžœžœ‘˜Jšžœžœ˜Lšœ˜Lšžœ˜Lšœ˜—L™gLšœ‘$˜9š žœBžœ‘(Πbc‘ ’‘˜ŽL˜#Lšžœ˜—L˜šž˜Lšœžœ˜L˜šž˜Lšžœ:žœž˜ELšžœžœ žœ‘E˜xL˜Lšžœ˜—L™L™CLšœ7˜7L˜L™%Lšžœ žœžœžœ˜5Lšžœ˜—Lšžœžœ žœ˜?L˜—L˜L™ L˜L˜L˜L˜SLšœ˜L–[]šœ˜L–[]šœ˜L–[]šœ˜Lš žœžœžœžœžœžœ˜TL˜Lšžœžœžœ‘2˜UL˜L˜>L˜šžœžœ‘˜/Lšœ˜Lšž˜—Lšœ‘˜/šžœ žœžœ‘%˜=Lšœžœ˜Lšœžœžœžœžœžœžœžœ˜7L˜Lšœ&˜&Lšœ˜L˜Lšœ ‘˜#šžœ žœž˜Lšœžœ˜ Lšœ˜Lšž˜—L™Lšœžœ˜"Lšœ˜Lšž˜—L˜L˜—Lšœžœžœ˜+šŸœžœžœ'žœ4žœžœžœ˜Β™6L™™'L™`L™D——L–[]šœžœ˜!L–@[sSize: CARDINAL, fSize: CARDINAL, bitsPerSample: [0..16]]˜_L–@[sSize: CARDINAL, fSize: CARDINAL, bitsPerSample: [0..16]]˜^L˜4L˜2L˜+L˜.Lš œ žœžœžœžœ˜Lšœžœ˜$šŸ œžœ žœžœžœžœžœ˜PLšžœžœžœžœ žœžœžœ ˜ML˜—šŸœžœžœžœ žœžœžœ˜ELšžœ˜L˜—šŸ œžœžœžœ žœžœžœ˜GLšžœ˜L˜—šŸ œžœžœžœ žœžœžœ˜FLšžœ˜L˜—šŸœžœžœžœžœžœžœ˜=Lšžœžœžœžœ˜;L˜—šŸœžœžœžœžœžœžœ˜;LšžœžœU˜kL˜—š Ÿœžœžœžœžœ˜6Lšœ žœ˜Lšœžœ˜$Lšœžœ˜$Lšœžœ˜$Lšœžœ‘™=L˜—š Ÿœžœžœžœžœžœ˜=L˜L˜L˜—šŸœžœ žœžœ˜*š Ÿ œžœžœžœžœžœ˜6Lšœžœ˜Lš žœžœžœžœ‘_˜‰šžœžœžœ ž˜šžœžœžœ ž˜Lšžœ*žœ˜ALšžœ˜—Lšžœ˜—Lšžœ ˜L˜—L™#Lšœ*‘E˜oLšœžœ ˜L˜Lšžœžœžœ‘˜Jšžœžœ˜Lšœ˜Lšžœ˜Lšœ˜—L™gLšœ‘$˜9š žœBžœ‘(’‘ ’‘˜ŽL˜#Lšžœ˜—L˜šž˜Lšœžœ˜L˜šž˜Lšžœ:žœž˜ELšžœžœ žœ‘E˜xL˜Lšžœ˜—L™L™CLšœ7˜7L˜L™%Lšžœ žœžœžœ˜5Lšžœ˜—Lšžœžœ žœ˜?L˜—L˜L™ L˜Lšžœžœžœ˜L˜/L˜1L˜0L˜L˜L˜L˜SLšœ˜L–[]šœ˜L–[]šœ˜L–[]šœ˜Lš žœžœžœžœžœžœ˜TL˜L˜>L˜šœ™Lšœ˜Lšœ˜Lšœ˜L˜L˜L˜—šžœ žœ‘˜(Lšœ˜Lšžœ˜—Lšœ‘˜/šžœ žœžœ‘%˜=Lšœžœ˜Lšœžœžœžœžœžœžœžœ˜7L˜Lšœ&˜&Lšœ˜L˜Lšœ ‘˜#šžœžœž˜'Lšœžœ˜ Lšœ˜Lšžœ˜—L™Lšœžœ˜"Lšœ˜Lšžœ˜—L˜—š Ÿ œžœžœ'žœ4žœžœ˜‘L˜+L˜.Lšœžœ˜šŸ œžœ žœžœžœžœžœ˜PLšžœžœžœ žœžœ žœžœ ˜PL˜—šŸœžœžœžœ žœžœžœ˜LLšžœ˜L˜—šŸ œžœžœžœ žœžœžœ˜NLšžœ˜L˜—šŸ œžœžœžœ žœžœžœ˜MLšžœ˜L˜—L˜Lšžœžœžœ˜L˜/L˜1L˜0L˜šœ™Lšœ ˜ Lšœ˜Lšœ ˜ L˜—L˜Sšžœžœžœž˜2Lšœžœžœžœžœžœžœžœ ˜Ašžœžœžœž˜2Lšžœmžœ žœ˜…Lšžœ˜—Lšžœ˜—L˜—š Ÿœžœžœ-žœ'žœ˜~Lšœžœ ˜Lšœžœ ˜Lšœžœ ˜Lšœžœ ˜Lšœžœ ˜Lšœžœ ˜L˜+L˜.šŸœžœ žœžœžœžœžœ˜Cšžœ˜Lš žœžœžœžœ žœžœ ˜ILš žœžœžœžœ žœžœ ˜HLšœ˜—L˜—L˜Lšžœžœžœ˜L˜/L˜1L˜0L˜L˜Sšžœžœžœž˜2Lšœžœžœžœžœžœžœžœ ˜Ašžœžœžœž˜2Lšžœ?žœ žœ˜WLšžœ˜—Lšžœ˜—L˜—šŸœžœžœžœ˜[L–$[x: RefTab.Ref, key: RefTab.Key]šœžœ<˜Dšžœžœžœ˜Lšœžœžœ˜!Lšœžœ˜ Lšžœžœžœžœ˜2Lšœžœ˜(šžœžœžœ ž˜Lšœ ˜ L˜Lšžœ˜—L–7[x: RefTab.Ref, key: RefTab.Key, val: RefTab.Val]šœD˜DLšœ˜—Lšžœžœ˜L˜—šŸœžœžœžœ˜QLšœžœžœ˜!Lšœžœ˜ Lšžœžœžœžœ˜2Lšœžœ˜$šžœžœžœž˜L˜L˜Lšžœ˜—L˜—Lšžœ˜——…—?κcΏ