DIRECTORY ColorizeViewPoint, ColorizeViewPointBackdoor, Convert, IO, Profiles, Random, Real, RealFns, Rope, RuntimeError; ColorizeViewPointMappingImpl: CEDAR PROGRAM IMPORTS ColorizeViewPoint, ColorizeViewPointBackdoor, Convert, IO, Profiles, Random, Real, RealFns, Rope, RuntimeError EXPORTS ColorizeViewPointBackdoor ~ BEGIN OPEN ColorizeViewPointBackdoor; ROPE: TYPE ~ Rope.ROPE; ApplyMappings: PUBLIC PROC [toMap: REF, palette: Profiles.Profile, mapData: MapData, mapOnly: ROPE _ NIL, subpaletteList: LIST OF ROPE _ NIL] RETURNS [mappedRope: ROPE _ NIL, mappedList: LIST OF ROPE _ NIL] ~ { IF toMap#NIL THEN WITH toMap SELECT FROM toMap: ROPE => { --single keyName to be mapped. IF mapOnly=NIL THEN {--the normal case. Search the exception palettes, don't calculate SELECT TRUE FROM mapData=NIL OR (mapData.exceptionsPalette=NIL AND mapData.requestedMappings=NIL) => RETURN; mapData.exceptionsPalette#NIL => IF (mappedRope _ Profiles.Line[profile: mapData.exceptionsPalette, key: toMap])#NIL THEN RETURN; --first look in the exceptions palette ENDCASE => NULL; FOR each: LIST OF ColorMapping _ mapData.requestedMappings, each.rest UNTIL each=NIL DO IF (mappedRope _ Profiles.Line[profile: palette, key: Rope.Concat[each.first.name, toMap]])#NIL THEN RETURN; --then try regular palette with exceptions prefixes ENDLOOP; } ELSE { --apply a single mapping (mapOnly) and protect it with braces. Used in exceptions like c13: ForceToSpring. Braces prevent later mappings. tokenList: LIST OF ROPE; --first, look up toMap in palette levelsExceeded: BOOL; colorMapping: ColorMapping; IF mapData#NIL THEN [colorMapping, mapData.installedMappings] _ IsAvailable[mapOnly, mapData.installedMappings, palette] ELSE RETURN; [tokenList, levelsExceeded] _ GetRecursiveValue[key: toMap, palette: palette, subpaletteList: CONS[mapOnly, subpaletteList], mapData: NIL, noMappings: TRUE]; --get the actual color numbers for toMap SELECT TRUE FROM --then check various errors tokenList=NIL => RETURN; levelsExceeded => { SIGNAL ColorizeViewPoint.Warning[class: $MalformedPaletteEntry, explanation: IO.PutFR[format: "%g is part of a recursive color definition beyond allowable levels; ignoring it.", v1: [rope[toMap]]]]; RETURN; }; colorMapping=NIL => {--tokenList was found but mapping wasn't; so "mapping" is not a true mapping but IS a sub-palette. Return the tokenList mappedList _ tokenList; }; colorMapping.bad => RETURN; --error already sent when bad was set ENDCASE => --finally, apply mapping mappedList _ colorMapping.mappingProc[valueIn: tokenList, name: mapOnly, palette: palette, data: colorMapping.data ! BadFormula => {SIGNAL ColorizeViewPoint.Warning[$MalformedPaletteEntry, IO.PutFR[format: "Ignoring mapping \"%g\" because: \n %g", v1: [rope[colorMapping.name]], v2: [rope[msg]]]]; colorMapping.bad _ TRUE; GOTO Bad}]; IF mappedList#NIL --possible with NilMapper-- THEN RETURN [mappedList: CONS["{", JoinLists[mappedList, LIST["}"]]]]; --add braces to protect further mapping }; }; toMap: LIST OF ROPE => { --use calculations to produce a mapped LIST OF ROPE IF mapData=NIL OR (mapOnly=NIL AND mapData.requestedMappings=NIL) THEN RETURN [mappedList: StripBraces[toMap]]; mappedList _ toMap; IF mapOnly=NIL THEN FOR each: LIST OF ColorMapping _ mapData.requestedMappings, each.rest UNTIL each=NIL DO IF ~each.first.bad THEN mappedList _ each.first.mappingProc[mappedList, each.first.name, palette, each.first.data ! BadFormula => {SIGNAL ColorizeViewPoint.Warning[$MalformedPaletteEntry, IO.PutFR[format: "Ignoring mapping \"%g\" because: \n %g", v1: [rope[each.first.name]], v2: [rope[msg]]]]; each.first.bad _ TRUE; LOOP}]; ENDLOOP ELSE { --just do a single mapping colorMapping: ColorMapping; [colorMapping, mapData.installedMappings] _ IsAvailable[mapOnly, mapData.installedMappings, palette]; IF colorMapping#NIL AND ~colorMapping.bad THEN mappedList _ colorMapping.mappingProc[mappedList, mapOnly, palette, colorMapping.data ! BadFormula => {SIGNAL ColorizeViewPoint.Warning[$MalformedPaletteEntry, IO.PutFR[format: "Ignoring mapping \"%g\" because: \n %g", v1: [rope[colorMapping.name]], v2: [rope[msg]]]]; colorMapping.bad _ TRUE; GOTO Bad}] ELSE RETURN; }; }; ENDCASE => ERROR --System error! EXITS Bad => RETURN; }; GetMappings: PUBLIC PROC [palette: Profiles.Profile] RETURNS [mapData: MapData] ~ { ENABLE { BadFormula => { SIGNAL ColorizeViewPoint.Warning[$MalformedPaletteEntry, IO.PutFR[format: "Can't do any mapping because: \n %g", v1: [rope[msg]]]]; GOTO Bad} }; installedMappings, requestedMappings: LIST OF ColorMapping; [installedMappings, requestedMappings] _ GetRequested[palette, GetStandardMappings[palette]]; mapData _ NEW[ MapDataRep _ [ installedMappings: installedMappings, requestedMappings: requestedMappings, exceptionsPalette: GetExceptionsPalette[palette]]]; EXITS Bad => RETURN [NIL]; }; GetRequested: PROC [palette: Profiles.Profile, installed: LIST OF ColorMapping] RETURNS [installedMappings, requestedMappings: LIST OF ColorMapping _ NIL] ~ { dummyHead: LIST OF ColorMapping _ LIST[NIL]; --to add elements to end of a list tail: LIST OF ColorMapping _ dummyHead; namesRequested: LIST OF ROPE _ Profiles.ListOfTokens[profile: palette, key: "Palette"]; --this will include subpalettes and mappings, so sort them out. installedMappings _ installed; FOR each: LIST OF ROPE _ namesRequested, each.rest UNTIL each=NIL DO mapping: ColorMapping; [mapping, installedMappings] _ IsAvailable[each.first, installedMappings, palette]; IF mapping#NIL THEN tail _ (tail.rest _ LIST[mapping]); ENDLOOP; requestedMappings _ dummyHead.rest; FOR each: LIST OF ColorMapping _ requestedMappings, each.rest UNTIL each=NIL DO FOR every: LIST OF ROPE _ each.first.setTrue, every.rest UNTIL every=NIL DO SetProfileBoolean[profile: palette, key: every.first, val: TRUE]; --for each requested Mapping, set every "setTrue" listing TRUE ENDLOOP; ENDLOOP; }; IsAvailable: PROC [name: ROPE, installed: LIST OF ColorMapping, palette: Profiles.Profile] RETURNS [mapping: ColorMapping _ NIL, installedMappings: LIST OF ColorMapping] ~ { ENABLE { BadFormula => { SIGNAL ColorizeViewPoint.Warning[$MalformedPaletteEntry, IO.PutFR[format: "Ignoring mapping \"%g\" because: \n %g", v1: [rope[name]], v2: [rope[msg]]]]; GOTO Bad} }; DefinedFormulas: Profiles.EnumProc = { --check defined "Formula" mappings prefix: ROPE _ key.Substr[0, key.Find[s2: "Formula", case: FALSE]]; IF prefix.Equal[s2: name, case: FALSE] THEN { mapping _ NEW [ColorMappingRep _ [ name: prefix, mappingProc: FormulaMapper, setTrue: CONS["AmbushAllYESColors", Profiles.ListOfTokens[profile: palette, key: prefix.Concat["SetTrue"]]], --eg, "GreySetTrue: IP2" data: Profiles.Line[profile: palette, key: key] ]]; installedMappings _ CONS[mapping, installedMappings]; found _ TRUE; RETURN [quit: TRUE]; }; }; DefinedSnapMaps: Profiles.EnumProc = { --check defined "SnapList" mappings prefix: ROPE _ key.Substr[0, key.Find[s2: "SnapList", case: FALSE]]; IF prefix.Equal[s2: name, case: FALSE] THEN { mapping _ NEW [ColorMappingRep _ [ name: prefix, mappingProc: SnapMapper, setTrue: CONS["AmbushAllYESColors", Profiles.ListOfTokens[profile: palette, key: prefix.Concat["SetTrue"]]], --eg, "SpringSetTrue: foo" data: GetSnapList[key, palette] ]]; installedMappings _ CONS[mapping, installedMappings]; found _ TRUE; RETURN [quit: TRUE]; }; }; found: BOOL _ FALSE; data: LIST OF ROPE _ NIL; FOR each: LIST OF ColorMapping _ installed, each.rest UNTIL each=NIL DO --check if currently installed IF name.Equal[s2: each.first.name, case: FALSE] THEN RETURN [each.first, installed]; ENDLOOP; installedMappings _ installed; Profiles.EnumerateKeys[profile: palette, pattern: "*Formula", proc: DefinedFormulas]; --check the defined "Formula" mappings, like "GreyFormula" IF ~found THEN Profiles.EnumerateKeys[profile: palette, pattern: "*SnapList", proc: DefinedSnapMaps]; --check the defined "SnapList" mappings, like "SpringSnapList" IF ~found AND (data _ GetRecursiveValue[key: name, palette: palette, subpaletteList: SubpaletteSearchList[prefixesIn: NIL, profile: palette], mapData: NIL, noMappings: TRUE].value)#NIL --name is in profile-- THEN { mapping _ NEW [ColorMappingRep _ [ --This allows users to say "ForceTolightblue" or "ForceToC15" and list a whole bunch of colors. This mapping simply substitutes "lightblue" or "C15" for those colors name: name, mappingProc: Substituter, setTrue: NIL, data: data ]]; installedMappings _ CONS[mapping, installedMappings]; }; EXITS Bad => RETURN [mapping: NIL, installedMappings: installed]; }; Color: TYPE ~ RECORD [vals: ARRAY [1..4] OF REAL _ ALL[0.0], size: [0..4] _ 0]; RGBColor: TYPE ~ Color; --size will always be 3 nullColor: Color; nullRGB: RGBColor; GetSnapList: PROC [name: ROPE, palette: Profiles.Profile] RETURNS [snapList: SnapList _ NIL] ~ { ConvertColors: ApplyMappingProc ~ { IF Rope.Match[pattern: "Sweep*", object: tokenAfter, case: FALSE] THEN SIGNAL ColorizeViewPoint.Warning[$MalformedPaletteEntry, IO.PutFR[format: "Can't snap to a sweep color--will take each color in Sweep def as a constant color"]]; IF toMap.size#0 THEN tail _ (tail.rest _ LIST[ConvertToRGB[toConvert: toMap, palette: palette]]); RETURN [NIL]; --don't care what we return }; dummyHead: LIST OF RGBColor _ LIST[nullRGB]; --to add elements to end of a list tail: LIST OF RGBColor _ dummyHead; subpaletteList: LIST OF ROPE _ SubpaletteSearchList[prefixesIn: NIL, profile: palette]; snapColors: LIST OF ROPE _ GetRecursiveValue[key: name, palette: palette, subpaletteList: subpaletteList, mapData: NIL, noMappings: TRUE].value; IF snapColors=NIL THEN RETURN; [] _ MapValues[valueIn: snapColors, mapper: ConvertColors]; snapList _ dummyHead.rest; }; GetExceptionsPalette: PROC [palette: Profiles.Profile] RETURNS [exceptionsPalette: Profiles.Profile _ NIL]~ { --finds all the exceptions to the mappings specified by the user, such as "ForceToBlack: c1, c2", which says map c1 & c2 to black no matter what the normal mapping would be. exceptionList: LIST OF ROPE _ ExpandExceptionCommands[palette]; IF exceptionList#NIL THEN exceptionsPalette _ Profiles.CreateFromRope[slices: exceptionList]; }; ExpandExceptionCommands: PROC [palette: Profiles.Profile] RETURNS [outList: LIST OF ROPE _ NIL] ~ { ExpandEach: Profiles.EnumProc ~ { --like ForceToSpring, ForceToGrey, .... Changes "ForceToSpring: c1, c2" to "c1: ForceToSpring\n c2: ForceToSpring\n" temp: ROPE; SELECT TRUE FROM --black and white are treated specially; need to have a 1 color black/white to get thru an IP2 printer key.Equal[s2: "ForceToBlack", case: FALSE] => IF (temp _ ExpandCommand[cmd: "ForceToBlack", value: IF palette.Boolean[key: "IP2"] THEN "1.0" --1 SetGray-- ELSE "ForceToBlack", palette: palette])#NIL THEN outList _ CONS[temp, outList]; --eg, "c5: 1.0" for IP2 printers else "c5: ForceToBlack" key.Equal[s2: "ForceToWhite", case: FALSE] => IF (temp _ ExpandCommand[cmd: "ForceToWhite", value: IF palette.Boolean[key: "IP2"] THEN "0.0" --0 SetGray-- ELSE "ForceToWhite", palette: palette])#NIL THEN outList _ CONS[temp, outList]; ENDCASE => { IF (temp _ ExpandCommand[cmd: key, value: key, palette: palette])#NIL THEN outList _ CONS[temp, outList]; --eg, "c1: ForceToSpring" }; }; Profiles.EnumerateKeys[profile: palette, pattern: "ForceTo*", proc: ExpandEach]; }; ExpandCommand: PROC [cmd, value: ROPE, palette: Profiles.Profile] RETURNS [out: ROPE _ NIL] ~ { --looks in palette for cmd; expands that cmd line. Command lines should be of the form "cmd: color1, color2, ...\n", and are expanded to "color1: value\n color2: value\n ..." Eg, "ForceToRed: c1, c2" => "c1: ForceToRed\n, c2: ForceToRed\n..", which causes c1 & c2 to be forced to red. A separator other than space must be used btwn colors since named colors may have spaces. valueLine: ROPE _ Rope.Cat[": ", value, " \n"]; tokens: LIST OF ROPE _ GetRecursiveValue[key: cmd, palette: palette, subpaletteList: LIST[""], levelsAllowed: 0--don't recurse!--, mapData: NIL, noMappings: TRUE].value; --this automatically changes bad tokens like c04 to c4, etc. IF tokens=NIL THEN RETURN; FOR each: LIST OF ROPE _ tokens, each.rest UNTIL each=NIL DO SELECT each.first.Fetch FROM ',, ';, '/ --separators-- => LOOP; ENDCASE => out _ out.Cat[each.first, valueLine]; --eg "c1: 1.0 \n" ENDLOOP; }; StripBraces: PROC [tokens: LIST OF ROPE] RETURNS [LIST OF ROPE] ~ { --w/o "{" or "}" dummyHead: LIST OF ROPE _ LIST[NIL]; tail: LIST OF ROPE _ dummyHead; FOR each: LIST OF ROPE _ tokens, each.rest UNTIL each=NIL DO SELECT TRUE FROM each.first.Equal["{"], each.first.Equal["}"] => NULL; ENDCASE => tail _ (tail.rest _ LIST[each.first]); ENDLOOP; RETURN [dummyHead.rest]; }; SnapList: TYPE ~ LIST OF RGBColor; SnapMapper: MappingProc ~ { --snaps to nearest color in a list of colors ApplySnapMap: ApplyMappingProc ~ { dummyHead: LIST OF ROPE _ LIST[NIL]; --mechanism to add elements to end of a list tail: LIST OF ROPE _ dummyHead; toMapRGB, mappedColor: RGBColor; IF toMap.size=0 THEN RETURN [mapped: LIST[tokenAfter]]; toMapRGB _ ConvertToRGB[toMap, palette]; --deal uniformly w RBG mappedColor _ FindNearest[toMapRGB, snapList, palette]; FOR i: INT IN [1..3] DO tail _ (tail.rest _ LIST[Convert.FtoRope[r: mappedColor.vals[i], afterDot: 2]]); ENDLOOP; tail _ (tail.rest _ LIST[tokenAfter]); --add the tokenAfter to end RETURN [mapped: dummyHead.rest]; }; snapList: SnapList _ NARROW[data]; IF snapList=NIL THEN { SIGNAL ColorizeViewPoint.Warning[$MalformedPaletteEntry, IO.PutFR[format: "No colors found to define mapping \"%g\"", v1: [rope[name]]]]; RETURN [valueIn]; }; mapped _ MapValues[valueIn: valueIn, mapper: ApplySnapMap]; }; FormulaMapper: MappingProc ~ { --applies a formula from palette to color ApplyFormula: ApplyMappingProc ~ { TokenVal: GetValueProc ~ { --used by ParseFormula RETURN [v: (IF Rope.Match[pattern: r.Concat["*"], object: tokenAfter, case: FALSE] THEN 1.0 ELSE 0.0)]}; dummyHead: LIST OF ROPE _ LIST[NIL]; --mechanism to add elements to end of a list tail: LIST OF ROPE _ dummyHead; mappedColor: Color; [mappedColor, quit] _ ParseFormula[toMap, formula, palette, TokenVal]; FOR i: INT IN [1..mappedColor.size] DO tail _ (tail.rest _ LIST[Convert.FtoRope[r: mappedColor.vals[i], afterDot: 2]]); ENDLOOP; IF ~quit THEN tail _ (tail.rest _ LIST[tokenAfter]); --add the tokenAfter to end RETURN [mapped: dummyHead.rest, quit: quit]; }; formula: ROPE _ NARROW[data]; IF formula=NIL THEN { SIGNAL ColorizeViewPoint.Warning[$MalformedPaletteEntry, IO.PutFR[format: "No formula found to define mapping \"%g\"", v1: [rope[name]]]]; RETURN [valueIn]; }; mapped _ MapValues[valueIn: valueIn, mapper: ApplyFormula]; }; Substituter: MappingProc ~ {--substitutes data for valueIn RETURN [NARROW[data]]; }; NoMapper: MappingProc ~ {--returns input with no mapping RETURN [valueIn]; }; NilMapper: MappingProc ~ {--returns NIL, causing b&w pattern to stay uncolorized RETURN [NIL]; }; BWMapper: MappingProc ~ { --map from RGB or CMYK to BW ApplyBWMap: ApplyMappingProc ~ { RGBEq: PROC [r: REAL] RETURNS [BOOL] ~ INLINE {FOR i: INT IN [1..3] DO IF toMap.vals[i]#r THEN RETURN [FALSE]; ENDLOOP; RETURN [TRUE]}; IF Rope.Match[pattern: "Sweep*", object: tokenAfter, case: FALSE] THEN RETURN [mapped: LIST["0.0"], quit: TRUE]; --all sweeps go to white in BW SELECT toMap.size FROM 0 --nothing in toMap-- => RETURN [mapped: LIST[tokenAfter]]; 1 --SetGray-- => RETURN [mapped: IF toMap.vals[1]>0.8 THEN LIST["1.0", tokenAfter] ELSE LIST["0.0", tokenAfter]]; --dark grays to black, rest to white 3 --rgb-- => RETURN [mapped: LIST[IF RGBEq[0.0--black--] THEN "1.0" ELSE "0.0", tokenAfter]]; ENDCASE --4=cmyk-- => RETURN [mapped: LIST[IF (RGBEq[1.0--black--] OR toMap.vals[4]=1.0) THEN "1.0" ELSE "0.0", tokenAfter]]; }; mapped _ MapValues[valueIn: valueIn, mapper: ApplyBWMap]; }; GreyMapper: MappingProc ~ { --map from RGB or CMYK to Grey ApplyGreyMap: ApplyMappingProc ~ { SELECT toMap.size FROM 0 --nothing in toMap-- => RETURN [mapped: LIST[tokenAfter]]; 1 --SetGray-- => RETURN [mapped: LIST[Convert.FtoRope[r: MIN[1.0, toMap.vals[1]], afterDot: 2], tokenAfter]]; 3 --rgb-- => RETURN [mapped: LIST[Convert.FtoRope[r:--1.0-(.253r+.684g+.063b)-- MAX[0.0, 1.0 - (.253*toMap.vals[1]+.684*toMap.vals[2]+.063*toMap.vals[3])], afterDot: 2], tokenAfter]]; ENDCASE --4=cmyk-- => RETURN [mapped: LIST[Convert.FtoRope[r:--.253c+.684m+.063y+k-- MIN[1.0, (.253*toMap.vals[1]+.684*toMap.vals[2]+.063*toMap.vals[3]+toMap.vals[4])], afterDot: 2], tokenAfter]]; }; mapped _ MapValues[valueIn: valueIn, mapper: ApplyGreyMap]; }; BrightMapper: MappingProc ~ { --map from RGB or CMYK to nearest saturated color ApplyBrightMap: ApplyMappingProc ~ { dummyHead: LIST OF ROPE _ LIST[NIL]; --mechanism to add elements to end of a list tail: LIST OF ROPE _ dummyHead; IF toMap.size=0 THEN RETURN [mapped: LIST[tokenAfter]]; FOR i: INT IN [1..toMap.size] DO tail _ (tail.rest _ LIST[Convert.RopeFromInt[ColorizeViewPointBackdoor.AltRound[toMap.vals[i]]]]); --each value will map to 0 or 1 (eg, light green .4 .9 .3 => 0 1 0). ENDLOOP; tail _ (tail.rest _ LIST[tokenAfter]); --add the tokenAfter to end RETURN [mapped: dummyHead.rest]; }; mapped _ MapValues[valueIn: valueIn, mapper: ApplyBrightMap]; }; ApplyMappingProc: TYPE ~ PROC [toMap: Color, tokenAfter: ROPE] RETURNS [mapped: LIST OF ROPE, quit: BOOL _ FALSE]; --each MappingProc supplies one of these to MapValues. toMap contains "size" numbers found, tokenAfter is the first non-number token found after numbers MapValues: PROC [valueIn: LIST OF ROPE, mapper: ApplyMappingProc] RETURNS [mapped: LIST OF ROPE] ~ { --by convention, tokens in braces are not mapped; braces removed. Tokens in parens are not mapped; parens retained. (This allows dropShadow info to be contained in parens.) AddToEnd: PROC [list: LIST OF ROPE] ~ { --adds a list of rope to end, one element at a time FOR each: LIST OF ROPE _ list, each.rest UNTIL each=NIL DO IF each.first#NIL THEN tail _ (tail.rest _ LIST[each.first]); ENDLOOP; }; color: Color _ nullColor; dummyHead: LIST OF ROPE _ LIST[NIL]; --mechanism to add elements to end of a list tail: LIST OF ROPE _ dummyHead; braceCount: INT _ 0; temp: LIST OF ROPE; quit: BOOL; FOR each: LIST OF ROPE _ valueIn, each.rest UNTIL each=NIL DO token: ROPE _ each.first; SELECT token.Fetch FROM IN ['0..'9] , '. => { IF braceCount=0 THEN { IF color.size=4 THEN { SIGNAL ColorizeViewPoint.Warning[$MalformedPaletteEntry, IO.PutFR[format: "Too many numbers in color definition %g. Ignoring all values beyond fourth number.", v1: [rope[RopeFromList[valueIn]]] ]]; LOOP; } ELSE color.size _ color.size+1; color.vals[color.size] _ Convert.RealFromRope[r: token ! Convert.Error => { SIGNAL ColorizeViewPoint.Warning[$MalformedPaletteEntry, IO.PutFR[format: "Difficulty converting token [%g]. Grey value set to 0.0.", v1: [rope[token]]]]; color.vals[color.size] _ 0.0; CONTINUE } ]; } ELSE AddToEnd[LIST[token]]; --without mapping }; '{ => braceCount _ braceCount+1; -- discard '{ '( => {braceCount _ braceCount+1; AddToEnd[LIST[token]]}; -- don't discard '( '} => braceCount _ MAX[0, braceCount-1]; -- discard '} ') => {braceCount _ MAX[0, braceCount-1]; AddToEnd[LIST[token]]}; -- don't discard ') ENDCASE => IF braceCount=0 THEN { IF color.size=2 THEN GOTO Barf; [temp, quit] _ mapper[color, token]; AddToEnd[temp]; IF quit THEN RETURN [dummyHead.rest]; color.size _ 0; } ELSE AddToEnd[LIST[token]]; --without mapping ENDLOOP; IF color.size#2 THEN AddToEnd[mapper[color, NIL].mapped] ELSE GOTO Barf; mapped _ dummyHead.rest; EXITS Barf => { SIGNAL ColorizeViewPoint.Warning[$MalformedPaletteEntry, IO.PutFR[format: "Only 2 numbers in color definition %g. Being mapped to white.", v1: [rope[RopeFromList[valueIn]]]]]; RETURN [LIST["0.0"]]; }; }; ConvertToRGB: PROC [toConvert: Color, palette: Profiles.Profile] RETURNS [rgbVal: RGBColor _ nullRGB] ~ { BuiltInConvert: PROC [] RETURNS [rgbVal: RGBColor _ nullRGB] ~ { SELECT toConvert.size FROM 0 --not a color-- => RETURN; 1 --transform gray to RGB-- => --.9 SetGray => .1 .1 .1 RGB rgbVal.vals[3] _ rgbVal.vals[2]_ (rgbVal.vals[1] _ 1.0 - toConvert.vals[1]); 3 --no transform-- => rgbVal _ toConvert; 4 --transform CMYK to RGB-- => { addBlack: REAL _ toConvert.vals[4]; --distr. the black component among CMY FOR i: INT IN [1..3] DO rgbVal.vals[i] _ 1.0 - MIN[1.0, toConvert.vals[i]+addBlack]; -- Red=1.0 - [Cyan + Black] ENDLOOP; }; ENDCASE => NULL; rgbVal.size _ 3; }; formula: ROPE _ Profiles.Line[profile: palette, key: "ConvertToRGBFormula"]; IF formula#NIL THEN rgbVal _ ParseFormula[toMap: toConvert, formula: formula, palette: palette ! BadFormula => { SIGNAL ColorizeViewPoint.Warning[$MalformedPaletteEntry, IO.PutFR[format: "Can't use ConvertToRGBFormula because: \n\t %g \n Using built-in convert formula instead.", v1: [rope[msg]]]]; rgbVal _ BuiltInConvert[]; }; ].mappedColor ELSE RETURN [BuiltInConvert[]]; }; FindNearest: PROC [target: RGBColor, snapList: SnapList, palette: Profiles.Profile] RETURNS [nearest: RGBColor] ~ { BuiltInNearest: PROC [] RETURNS [nearest: RGBColor] ~ { FOR each: LIST OF RGBColor _ snapList, each.rest UNTIL each=NIL DO a: REAL _ each.first.vals[1]-target.vals[1]; b: REAL _ each.first.vals[2]-target.vals[2]; c: REAL _ each.first.vals[3]-target.vals[3]; distance: REAL _ a*a + b*b + c*c; --SqRt not nec since only used for comparison IF distance < minDistance THEN {minDistance _ distance; nearest _ each.first}; ENDLOOP; }; minDistance: REAL _ Real.LargestNumber; formula: ROPE _ Profiles.Line[profile: palette, key: "NearestFormula"]; IF formula#NIL THEN FOR each: LIST OF RGBColor _ snapList, each.rest UNTIL each=NIL DO EachVal: GetValueProc ~ {RETURN [v: (SELECT TRUE FROM r.Equal[s2: "e1", case: FALSE] => each.first.vals[1], r.Equal[s2: "e2", case: FALSE] => each.first.vals[2], r.Equal[s2: "e3", case: FALSE] => each.first.vals[3], ENDCASE => Real.LargestNumber)]}; --formula must use tokens "e*" to access each.first distance: REAL _ ParseFormula[toMap: target, formula: formula, palette: palette, getValue: EachVal ! BadFormula => { SIGNAL ColorizeViewPoint.Warning[$MalformedPaletteEntry, IO.PutFR[format: "Can't use ConvertToRGBFormula because: \n\t %g \n Using built-in convert formula instead.", v1: [rope[msg]]]]; GOTO Bad }; ].mappedColor.vals[1]; --formula must store dist in [1] IF distance < minDistance THEN {minDistance _ distance; nearest _ each.first}; REPEAT Bad => RETURN [BuiltInNearest[]]; ENDLOOP ELSE RETURN [BuiltInNearest[]] }; stackSize: NAT ~ 30; Stack: TYPE ~ REF StackRep; StackRep: TYPE ~ RECORD [ stack: ARRAY [1..stackSize] OF REAL _ ALL[0.0], top: INT _ 0, curEntry: ROPE _ NIL, --used in error reporting levelsExceeded: BOOL _ FALSE --used in error reporting ]; GetValueProc: TYPE ~ PROC [r: ROPE, s: Stack] RETURNS [v: REAL _ Real.LargestNumber, pop: NAT _ 0]; --client of ParseFormula can choose to map any ROPE into a REAL maxLevels: NAT _ 4; --levels of recursion allowed in subroutines BadFormula: ERROR [msg: ROPE] ~ CODE; --never seen outside ParseFormula: PROC [toMap: Color, formula: ROPE, palette: Profiles.Profile, getValue: GetValueProc _ NIL, level: NAT _ 0, s: Stack _ NEW[StackRep]] RETURNS [mappedColor: Color, quit: BOOL _ FALSE] ~ { --formula expected in Rev Polish; spaces separating each element. quit reports that the word "quit" was found in formula; client handles it ENABLE { RuntimeError.BoundsFault => ERROR BadFormula[msg: IO.PutFR[format: "Stack or array overflow or underflow at \"%g\" in formula \"%g\"", v1: [rope[s.curEntry]], v2: [rope[formula]]]]; Convert.Error => ERROR BadFormula[msg: IO.PutFR[format: "Problem converting number \"%g\" in formula \"%g\"", v1: [rope[s.curEntry]], v2: [rope[formula]]]]; }; ProcessToken: PROC [] RETURNS [done: BOOL _ FALSE] ~ { --this stuff embedded in proc to catch BoundsFault in DCedar Push: PROC [r: REAL] ~ INLINE {s.top _ s.top+1; s.stack[s.top] _ r}; Pop: PROC [i: NAT] ~ INLINE {s.top _ s.top-i}; Store: PROC [at: [1..4]] ~ INLINE {mappedColor.vals[at] _ s.stack[s.top]; mappedColor.size _ MAX[mappedColor.size, at]; Pop[1]}; StoreToMap: PROC [at: [1..4]] ~ INLINE {toMap.vals[at] _ s.stack[s.top]; toMap.size _ MAX[toMap.size, at]; Pop[1]}; CheckForSkip: PROC [] RETURNS [BOOL] ~ INLINE {match: BOOL _ s.stack[s.top]=op.toMatch; Pop[1]; RETURN [match=op.matchMeansSkip]}; GetValue: PROC [] ~ {temp: REAL; pop: NAT; IF getValue=NIL OR ([temp, pop] _ getValue[tok, s]).v=Real.LargestNumber THEN ReportProblem[unrecognized] ELSE {Pop[pop]; Push[temp]}; --pop whatever amt client says, then push the value returned by client--}; IsSwitchToken: PROC [] RETURNS [found: BOOL _ TRUE] ~ { SELECT TRUE FROM --tokens used for switch stmts skipToken => {skipToken _ FALSE; RETURN}; tok.Equal[s2: "Switch", case: FALSE] => --Switch statement, ended with ; IF skipToEndStmt OR skipToEndBranch THEN --just count-- op.stmtCount _ op.stmtCount+1 ELSE { --begin processing new: Op _ NEW [OpRep _ [toMatch: s.stack[s.top], matchMeansSkip: FALSE]]; Pop[1]; --pop toMatch opList _ CONS[new, opList] }; tok.Equal[s2: "If", case: FALSE] => --If statement, ended with ; IF skipToEndStmt OR skipToEndBranch THEN --just count-- op.stmtCount _ op.stmtCount+1 ELSE { --begin processing new: Op _ NEW [OpRep _ [toMatch: s.stack[s.top]]]; Pop[1]; --pop toMatch Push[0.0]; --thus, anything#0.0 is true (noMatch=do it) opList _ CONS[new, opList]}; tok.Equal[s2: "Loop", case: FALSE] => {--Loop statement, ended with "EndLoop ;" IF skipToEndStmt OR skipToEndBranch THEN --just count-- {op.stmtCount _ op.stmtCount+1; RETURN}; IF op#NIL AND op.loopCount=100 THEN ReportProblem[tooManyLoops]; IF op=NIL OR op.loopCount=Real.LargestNumber THEN {--set up loop first time thru. Loop is reset by EndLoop; loops until loopcount=toMatch, then sets skipToEndStmt where it finds the ; after EndLoop new: Op _ NEW [OpRep _ [toMatch: s.stack[s.top]+1--+1 needed for correct number of loops before skipping--, parensMatter: FALSE, stmtStart: curIndex, loopCount: 1.0]]; Pop[1]; --pop toMatch skipToEndStmt _ new.toMatch<=1.0; --first time, don't do loop if loop value <=0 opList _ CONS[new, opList]; }; }; c='; => IF op#NIL AND (op.stmtCount _ op.stmtCount-1)=0 THEN --we've reached the end of a statement; pop the statement and reset. {opList _ opList.rest --pop op--; skipToEndStmt _ skipToEndBranch _ FALSE}; skipToEndStmt => RETURN; c='( , c='[ => {--count up. Then, if currently skipping, skip on. Otherwise, compare stack top with op.toMatch. If they match, set skipToEndBranch according to op.matchMeansSkip. Pop stack top. IF op=NIL OR ~op.parensMatter THEN RETURN; --parens allowed but no meaning op.parenCount _ op.parenCount+1; IF ~skipToEndBranch --not skipping-- THEN skipToEndBranch _ CheckForSkip[]; }; c=') , c='] => {--when parenCount is 0, done skipping or processing that branch. If you were skipping, stop skipping. If you were processing, skip all other branches to the end bracket IF op=NIL OR ~op.parensMatter THEN RETURN; --parens allowed but no meaning IF (op.parenCount _ op.parenCount-1)=0 THEN { IF skipToEndBranch THEN skipToEndBranch _ FALSE ELSE skipToEndStmt _ TRUE};--because the match was found and branch has been processed }; skipToEndBranch => RETURN; tok.Equal[s2: "Else", case: FALSE] => IF op#NIL THEN {Push[op.toMatch]; op.matchMeansSkip _ FALSE}; --the Else branch will always match, which is ok since it's skipped over if prev match found already ENDCASE => found _ FALSE; }; IsNumber: PROC [] RETURNS [found: BOOL _ TRUE] ~ { SELECT c FROM --numbers IN ['0..'9], '. => Push[Convert.RealFromRope[tok]]; ENDCASE => found _ FALSE; }; IsShortTok: PROC [] RETURNS [found: BOOL _ TRUE] ~ { IF length=1 THEN SELECT c FROM --most common toks 'R, 'r, 'C, 'c => Push[toMap.vals[1]]; 'G, 'g, 'M, 'm => Push[toMap.vals[2]]; 'B, 'b, 'Y, 'y => Push[toMap.vals[3]]; 'K, 'k => Push[toMap.vals[4]]; 'I, 'i => IF op#NIL THEN Push[op.loopCount]; '+ => {Pop[1]; s.stack[s.top] _ s.stack[s.top]+s.stack[s.top+1]}; '- => {Pop[1]; s.stack[s.top] _ s.stack[s.top]-s.stack[s.top+1]}; '* => {Pop[1]; s.stack[s.top] _ s.stack[s.top]*s.stack[s.top+1]}; '/ => {Pop[1]; s.stack[s.top] _ s.stack[s.top]/s.stack[s.top+1]}; '< => {Pop[1]; s.stack[s.top] _ IF s.stack[s.top] => {Pop[1]; s.stack[s.top] _ IF s.stack[s.top]>s.stack[s.top+1] THEN 1.0 ELSE 0.0}; '= => {Pop[1]; s.stack[s.top] _ IF s.stack[s.top]=s.stack[s.top+1] THEN 1.0 ELSE 0.0}; '# => {Pop[1]; s.stack[s.top] _ IF s.stack[s.top]#s.stack[s.top+1] THEN 1.0 ELSE 0.0}; ENDCASE => ReportProblem[unrecognized] ELSE found _ FALSE; }; IsLongTok1: PROC [] RETURNS [found: BOOL _ TRUE] ~ { SELECT TRUE FROM --less common tokens tok.Equal[s2: "R2", case: FALSE], tok.Equal[s2: "C2", case: FALSE] => Push[mappedColor.vals[1]]; tok.Equal[s2: "G2", case: FALSE], tok.Equal[s2: "M2", case: FALSE] => Push[mappedColor.vals[2]]; tok.Equal[s2: "B2", case: FALSE], tok.Equal[s2: "Y2", case: FALSE] => Push[mappedColor.vals[3]]; tok.Equal[s2: "K2", case: FALSE] => Push[mappedColor.vals[4]]; tok.Equal[s2: "V1", case: FALSE] => {Pop[1]; Push[toMap.vals[ColorizeViewPointBackdoor.AltRound[s.stack[s.top+1]]]]}; tok.Equal[s2: "V2", case: FALSE] => {Pop[1]; Push[mappedColor.vals[ColorizeViewPointBackdoor.AltRound[s.stack[s.top+1]]]]}; tok.Equal[s2: "Store", case: FALSE] => Store[mappedColor.size+1]; Rope.Match[pattern: "*Formula", object: tok, case: FALSE] --SubRout-- => { newFormula: ROPE _ Profiles.Line[profile: palette, key: tok]; IF newFormula#NIL THEN [mappedColor, quit] _ ParseFormula[toMap, newFormula, palette, getValue, level+1, s] ELSE ReportProblem[unrecognized]; IF s.levelsExceeded THEN ReportProblem[tooDeep]; }; tok.Equal[s2: "ConvertToRGB", case: FALSE] --oft used formula-- => mappedColor _ ConvertToRGB[toConvert: toMap, palette: palette]; tok.Equal[s2: "End", case: FALSE] => done _ TRUE; --normal end tok.Equal[s2: "EndLoop", case: FALSE] => IF op#NIL AND op.loopCount#Real.LargestNumber THEN { Push[(op.loopCount _ op.loopCount+1)]; skipToEndStmt _ CheckForSkip[]; IF ~skipToEndStmt THEN {IO.SetIndex[self: in, index: op.stmtStart]; resetLoop _ TRUE};--resets to beginning of loop }; ENDCASE => found _ FALSE; }; IsLongTok2: PROC [] RETURNS [found: BOOL _ TRUE] ~ { SELECT TRUE FROM --less common tokens tok.Equal[s2: "Quit", case: FALSE] => {quit _ TRUE; done _ TRUE}; --eg, BWMapper quits all future parsing once a "Sweep" tag is found tok.Equal[s2: "Nil", case: FALSE] => RETURN; tok.Equal[s2: "Error", case: FALSE] => ReportProblem[errorBranch]; tok.Equal[s2: "Round", case: FALSE] => s.stack[s.top] _ ColorizeViewPointBackdoor.AltRound[s.stack[s.top]]; tok.Equal[s2: "Size", case: FALSE] => Push[toMap.size]; tok.Equal[s2: "Size2", case: FALSE] => Push[mappedColor.size]; tok.Equal[s2: "Random", case: FALSE] => Push[Random.ChooseInt[min: 0, max:100]/100.0]; --returns random real btwn 0.00 & 1.00 tok.Equal[s2: "<=", case: FALSE] => {Pop[1]; s.stack[s.top] _ IF s.stack[s.top]<=s.stack[s.top+1] THEN 1.0 ELSE 0.0}; tok.Equal[s2: ">=", case: FALSE] => {Pop[1]; s.stack[s.top] _ IF s.stack[s.top]>=s.stack[s.top+1] THEN 1.0 ELSE 0.0}; tok.Equal[s2: "Min", case: FALSE] => {Pop[1]; s.stack[s.top] _ MIN[s.stack[s.top], s.stack[s.top+1]]}; tok.Equal[s2: "Max", case: FALSE] => {Pop[1]; s.stack[s.top] _ MAX[s.stack[s.top], s.stack[s.top+1]]}; ENDCASE => found _ FALSE; }; IsLongTok3: PROC [] RETURNS [found: BOOL _ TRUE] ~ { --most uncommon toks SELECT TRUE FROM --less common tokens tok.Equal[s2: "SqRt", case: FALSE] => s.stack[s.top] _ RealFns.SqRt[s.stack[s.top]]; tok.Equal[s2: "Ln", case: FALSE] => s.stack[s.top] _ RealFns.Ln[s.stack[s.top]]; tok.Equal[s2: "Sin", case: FALSE] => s.stack[s.top] _ RealFns.Sin[s.stack[s.top]]; tok.Equal[s2: "Cos", case: FALSE] => s.stack[s.top] _ RealFns.Cos[s.stack[s.top]]; tok.Equal[s2: "Tan", case: FALSE] => s.stack[s.top] _ RealFns.Tan[s.stack[s.top]]; tok.Equal[s2: "Not", case: FALSE] => {s.stack[s.top] _ IF s.stack[s.top]=0.0 THEN 1.0 ELSE 0.0}; tok.Equal[s2: "Dup", case: FALSE] => Push[s.stack[s.top]]; tok.Equal[s2: "Exch", case: FALSE] => {tmp: REAL _ s.stack[s.top]; s.stack[s.top] _ s.stack[s.top-1]; s.stack[s.top-1] _ tmp}; tok.Equal[s2: "C1ToC2", case: FALSE] => {FOR i: INT IN [1..toMap.size] DO mappedColor.vals[i] _ toMap.vals[i] ENDLOOP; mappedColor.size _ toMap.size; toMap _ nullColor}; tok.Equal[s2: "C1ToTemp", case: FALSE] => {FOR i: INT IN [1..toMap.size] DO tempColor.vals[i] _ toMap.vals[i] ENDLOOP; tempColor.size _ toMap.size; toMap _ nullColor}; tok.Equal[s2: "C2ToC1", case: FALSE] => {FOR i: INT IN [1..mappedColor.size] DO toMap.vals[i] _ mappedColor.vals[i] ENDLOOP; toMap.size _ mappedColor.size; mappedColor _ nullColor}; tok.Equal[s2: "C2ToTemp", case: FALSE] => {FOR i: INT IN [1..mappedColor.size] DO tempColor.vals[i] _ mappedColor.vals[i] ENDLOOP; tempColor.size _ mappedColor.size; mappedColor _ nullColor}; tok.Equal[s2: "C1ExchC2", case: FALSE] => {temp: Color; FOR i: INT IN [1..mappedColor.size] DO temp.vals[i] _ mappedColor.vals[i] ENDLOOP; temp.size _ mappedColor.size; FOR i: INT IN [1..toMap.size] DO mappedColor.vals[i] _ toMap.vals[i] ENDLOOP; mappedColor.size _ toMap.size; FOR i: INT IN [1..temp.size] DO toMap.vals[i] _ temp.vals[i] ENDLOOP; toMap.size _ temp.size; }; tok.Equal[s2: "ZeroC1", case: FALSE] => toMap _ nullColor; tok.Equal[s2: "ZeroC2", case: FALSE] => mappedColor _ nullColor; ENDCASE => found _ FALSE; }; ErrType: TYPE ~ {unrecognized, tooDeep, tooManyLoops, errorBranch}; ReportProblem: PROC [errType: ErrType] ~ { SELECT errType FROM unrecognized => ERROR BadFormula[msg: IO.PutFR[format: "Unrecognized object \"%g\" in formula \"%g\"", v1: [rope[tok]], v2: [rope[formula]]]]; tooDeep => ERROR BadFormula[msg: IO.PutFR[format: "Recursion beyond %g levels at \"%g\" in formula \"%g\"", v1: [cardinal[maxLevels]], v2: [rope[tok]], v3: [rope[formula]]]]; tooManyLoops => ERROR BadFormula[msg: IO.PutFR[format: "Looped more than 100 times; probably no EndLoop in formula \"%g\"", v1: [rope[formula]]]]; errorBranch => ERROR BadFormula[msg: IO.PutFR[format: "Error branch reached in formula \"%g\"", v1: [rope[formula]]]]; ENDCASE => ERROR; --system error! }; length: INT _ tok.Length; c: CHAR _ Rope.Fetch[tok]; op: Op _ IF opList#NIL THEN opList.first ELSE NIL; SELECT TRUE FROM IsSwitchToken[], IsNumber[], IsShortTok[], IsLongTok1[], IsLongTok2[], IsLongTok3[] => NULL; ENDCASE => GetValue[]; }; GetTokens: PROC [] ~ { TokenBreak: IO.BreakProc = { RETURN [SELECT char FROM IN [IO.NUL..IO.SP], '\t => sepr, '*, '+, '/, '-, '{, '}, '(, '), '[, '], '#, '; => break, ENDCASE => other]}; IF tok=NIL--first time-- OR resetLoop OR skipToken THEN { curIndex _ IO.GetIndex[in]; tok _ IO.GetTokenRope[stream: in, breakProc: TokenBreak ! IO.EndOfStream => {tok _ NIL; GOTO End}].token; nextIndex _ IO.GetIndex[in]; nextTok _ IO.GetTokenRope[stream: in, breakProc: TokenBreak ! IO.EndOfStream => {nextTok _ NIL; GOTO End}].token; resetLoop _ skipToken _ FALSE; } ELSE { tok _ nextTok; curIndex _ nextIndex; nextIndex _ IO.GetIndex[in]; nextTok _ IO.GetTokenRope[stream: in, breakProc: TokenBreak ! IO.EndOfStream => {nextTok _ NIL; GOTO End}].token; }; EXITS End => RETURN; }; Op: TYPE ~ REF OpRep; OpRep: TYPE ~ RECORD [parenCount: INT _ 0, stmtCount: INT _ 1, toMatch, loopCount: REAL _ Real.LargestNumber, matchMeansSkip, parensMatter: BOOL _ TRUE, stmtStart: INT _ 0--set by each stmt--]; opList: LIST OF Op _ NIL; --keeps "stack" of current switching operator status tempColor: Color; skipToken, resetLoop, skipToEndStmt, skipToEndBranch: BOOL _ FALSE; in: IO.STREAM _ IO.RIS[formula]; tok, nextTok: ROPE _ NIL; curIndex, nextIndex: INT _ 0; IF level>maxLevels THEN {s.levelsExceeded _ TRUE; RETURN}; DO GetTokens[]; IF (s.curEntry _ tok)=NIL OR ProcessToken[]--returns done if forced done-- THEN EXIT; ENDLOOP; }; RopeFromList: PROC [list: LIST OF ROPE] RETURNS [rope: ROPE _ NIL] ~ {FOR each: LIST OF ROPE _ list, each.rest UNTIL each=NIL DO rope _ rope.Cat[" ", each.first]; ENDLOOP}; JoinLists: PROC [start: LIST OF ROPE, addAtEnd: LIST OF ROPE] RETURNS [joined: LIST OF ROPE] ~ {RETURN [IF start=NIL THEN addAtEnd ELSE CONS[start.first, JoinLists[start.rest, addAtEnd]]]}; GetStandardMappings: PROC [palette: Profiles.Profile] RETURNS [standardMappings: LIST OF ColorMapping] ~ { formula: ROPE; paletteMapping: ColorMapping ~ NEW [ColorMappingRep _ [ --by doing nothing, allows "{}" to be put around value by ApplyMappings, defeating other mappings. Net result: color is "mapped" to it's original value of whatever palette is named, despite other mappings happening name: "Palette", mappingProc: NoMapper, setTrue: NIL ]]; nilMapping: ColorMapping ~ NEW [ColorMappingRep _ [ --by returning NIL, causes b&w pattern to remain uncolorized name: "None", mappingProc: NilMapper, setTrue: NIL ]]; bwMapping: ColorMapping ~ NEW [ColorMappingRep _ [ name: "BW", mappingProc: IF (formula _ Profiles.Line[palette, "BWFormula"])#NIL THEN FormulaMapper ELSE BWMapper, setTrue: LIST["AmbushAllYESColors", "IP2"], data: formula ]]; grayMapping: ColorMapping ~ NEW [ColorMappingRep _ [ name: "Gray", mappingProc: IF (formula _ Profiles.Line[palette, "GrayFormula"])#NIL THEN FormulaMapper ELSE GreyMapper, setTrue: LIST["AmbushAllYESColors", "IP2"], data: formula ]]; greyMapping: ColorMapping ~ NEW [ColorMappingRep _ [ name: "Grey", mappingProc: IF (formula _ Profiles.Line[palette, "GreyFormula"])#NIL THEN FormulaMapper ELSE GreyMapper, setTrue: LIST["AmbushAllYESColors", "IP2"], data: formula ]]; presentationMapping: ColorMapping ~ NEW [ColorMappingRep _ [ name: "Bright", mappingProc: IF (formula _ Profiles.Line[palette, "BrightFormula"])#NIL THEN FormulaMapper ELSE BrightMapper, setTrue: LIST["AmbushAllYESColors"], data: formula ]]; RETURN [LIST[paletteMapping, nilMapping, presentationMapping, grayMapping, greyMapping, bwMapping]]; }; END. ๊ ColorizeViewPointMappingImpl.mesa Copyright ำ 1989, 1990 by Xerox Corporation. All rights reserved. Bob Coleman, September 13, 1990 12:02 pm PDT Applying Mappings while we're at it, now's the time to set the appropriate palette entries TRUE [key: ROPE] RETURNS [quit: BOOL _ FALSE] [key: ROPE] RETURNS [quit: BOOL _ FALSE] [toMap: Color, tokenAfter: ROPE] RETURNS [LIST OF ROPE] [key: ROPE] RETURNS [quit: BOOL _ FALSE] MappingProcs PROC [valueIn: LIST OF ROPE, name: ROPE, palette: Profiles.Profile, data: REF _ NIL] RETURNS [mapped: LIST OF ROPE] [toMap: Color, tokenAfter: ROPE] RETURNS [mapped: LIST OF ROPE, quit: BOOL _ FALSE] PROC [valueIn: LIST OF ROPE, name: ROPE, palette: Profiles.Profile, data: REF _ NIL] RETURNS [mapped: LIST OF ROPE] [toMap: Color, tokenAfter: ROPE] RETURNS [mapped: LIST OF ROPE, quit: BOOL _ FALSE] PROC [r: ROPE, s: Stack] RETURNS [v: REAL _ Real.LargestNumber, pop: NAT _ 0] [valueIn: LIST OF ROPE, name: ROPE, palette: Profiles.Profile, data: REF _ NIL] RETURNS [mapped: LIST OF ROPE] [valueIn: LIST OF ROPE, name: ROPE, palette: Profiles.Profile, data: REF _ NIL] RETURNS [mapped: LIST OF ROPE] [valueIn: LIST OF ROPE, name: ROPE, palette: Profiles.Profile, data: REF _ NIL] RETURNS [mapped: LIST OF ROPE] PROC [valueIn: LIST OF ROPE, name: ROPE, palette: Profiles.Profile, data: REF _ NIL] RETURNS [mapped: LIST OF ROPE] [toMap: Color, tokenAfter: ROPE] RETURNS [mapped: LIST OF ROPE, quit: BOOL _ FALSE] PROC [valueIn: LIST OF ROPE, name: ROPE, palette: Profiles.Profile, data: REF _ NIL] RETURNS [mapped: LIST OF ROPE] [toMap: Color, tokenAfter: ROPE] RETURNS [mapped: LIST OF ROPE, quit: BOOL _ FALSE] PROC [valueIn: LIST OF ROPE, name: ROPE, palette: Profiles.Profile, data: REF _ NIL] RETURNS [mapped: LIST OF ROPE] [toMap: Color, tokenAfter: ROPE] RETURNS [mapped: LIST OF ROPE, quit: BOOL _ FALSE] Mapping Utilities if no formula or bad formula in palette, use this if no formula or bad formula in palette, use this [char: CHAR] RETURNS [IO.CharClass] Standard Mappings ส+๛•NewlineDelimiter ™™!IcodešœB™BK™,K™—šฯk ˜ Kšœ7œ6˜oK˜—Kšะlnœ ˜+Kšœ8œ5˜vKšœ˜!Kšœ˜šœ˜K˜Kšœœœ˜—subtitle™š"ฯn œœœ œ8œœœœœœœœœœœœœ˜าš œœœœœ˜(šœœฯc"˜2šœ œœ B˜Wšœœœ˜Kš œœœœœœœ˜[Kš œœœNœœœ &˜จKšœœ˜—š œœœ5œœ˜WKš œZœœœ 4˜ Kš˜—K˜—šœ ‹˜’Kšœ œœœ !˜:Kšœœ˜Kšœ˜Kš œ œœgœœ˜†Kšœ^œ$œœ (˜ฦšœœœ ˜.Kšœ œ ˜šœฯt˜KšœGœw˜ฦKš˜Kšœ˜—šœ œกะctx˜Kšœ˜Kšœ˜—Kšœœ %˜Ašœ ˜$šœ‚˜‚Kš œœ3œ~œœ˜ฬ———Kšœ œ œœœœœ  '˜œK˜—K˜—šœœœœ 3˜LKšœ œœ œœœœœ"˜oKšœ˜šœ œœ˜š œœœ5œœ˜WšœœZ˜qKš œœ3œzœœ˜ำ—Kš˜——šœ ˜!Kšœ˜Kšœe˜ešœœœœV˜„Kš œœ3œ~œœ˜ฺ—Kšœœ˜ K˜—Kšœ˜—Kšœœ ˜ —Kšœœ˜K˜—K˜šŸ œ œœ˜Sšœ˜šœ˜Kšœ3œH˜ƒKšœ˜ —Kšœ˜—Kšœ&œœ˜;Kšœ]˜]šœ œ˜Kšœ%˜%Kšœ%˜%Kšœ3˜3—Kšœœœ˜Kšœ˜K˜—šŸ œœ(œœœ(œœœ˜žKš œ œœœœ "˜OKšœœœ˜'Kšœœœœ< ?˜—Kšœ˜š œœœœœœ˜DKšœ˜KšœS˜SKšœ œœ ˜7Kšœ˜—Kšœ#˜#KšœM™Mš œœœ-œœ˜Oš œœœœ"œœ˜KKšœ;œ :ะck˜€Kšœ˜—Kšœ˜—K˜K˜—šŸ œœœ œœ*œœœœ˜ญšœ˜šœ˜Kšœ3œ]˜˜Kšœ˜ —Kšœ˜—šŸœ "˜IKšฃ(™(Kšœœ/œ˜Cšœœœ˜-šœ œ˜"Kšœ ˜ Kšœ˜Kšœ œa ˜†K•StartOfExpansionI[profile: Profiles.Profile, key: ROPE, default: LIST OF ROPE _ NIL]šœกœ-˜3—Kšœœ˜5Kšœœ˜ Kšœœ˜K˜—K˜—–, -- [key: ROPE] RETURNS [quit: BOOL _ FALSE]šŸœ #˜JKšฃ(™(Kšœœ0œ˜Dšœœœ˜-šœ œ˜"Kšœ ˜ Kšœ˜Kšœ œ` ˜‡Kšœก œกœ ˜#—Kšœœ˜5Kšœœ˜ Kšœœ˜K˜—K˜—Kšœœœ˜Kš œœœœœ˜š œœœ%œœœ ˜fKš œ'œœกœ ก ˜TKšœ˜—Kšœ˜KšœV :˜K–*[profile: Profiles.Profile, key: ROPE]šœœX >˜คšœœiœœœ œ œœ˜ึšœ œ ฆ˜ษKšœ ˜ Kšœ˜Kšœ ˜ Kšœ˜—Jšœœ˜5Kšœ˜—Kšœœ œ ˜AK˜K˜—Kš œœœœœœœ˜OKšœ œ  ˜0K˜K˜š Ÿ œœœœœ˜`šŸ œ˜#Kš(œœœœ™7šœ9œœ˜HKšœ3œf˜ก—Kšœœœ4˜aKšœœ ˜)K˜—Kšœ œœ œ  "˜OKšœœœ˜#Kš œœœœ$œ˜WKš œ œœœ[œœ ˜’Kšœ œœœ˜Kšœ;˜;K˜K˜K˜—š Ÿœœœ(œ ญ˜›Kšœ œ$˜?K–[slices: LIST OF ROPE]šœœœD˜]K˜K˜—šŸœœœ œœœœ˜c–, -- [key: ROPE] RETURNS [quit: BOOL _ FALSE]šŸ œ t˜–Kš(™(Kšœœ˜ šœœœ f˜wK–C[profile: Profiles.Profile, key: ROPE, default: BOOL _ FALSE]šœ$œœ3œœกœ  œœ$œœ œ 8˜ฃKšœ$œœ3œœกœ  œœ$œœ œ˜๊šœ˜ Kš œ@œœ œ ˜ƒKšœ˜——K˜—K–I[profile: Profiles.Profile, pattern: ROPE, proc: Profiles.EnumProc]˜PK˜K˜—š Ÿ œœœœœœ ๙˜ูKšœ œ ˜/K–I[profile: Profiles.Profile, key: ROPE, default: LIST OF ROPE _ NIL]šœœœœAœ œ œœ  <˜ๆKšœœœœ˜š œœœœœœ˜<šœ˜Kšœ  œœ˜"Kšœ* ˜C—Kšœ˜—K˜K˜—šŸ œœ œœœœœœœ ˜TKš œ œœœœœ˜$Kšœœœœ ˜š œœœœœœ˜<š˜Kšœ0œ˜5Kšœœ˜1—Kšœ˜—Kšœ˜K˜K˜——™ Kšœ œœœ ˜"šŸ œ .˜IKš'œ#œœ™sšŸ œ˜"Kš (œ œœœœœ™SKš œ œœœœœ ,˜QKšœœœœ ˜Kšœ ˜ Kšœœœ œ˜7Kšœ) ˜?Kšœ7˜7šœœœ˜Kšœœ)ก œ˜QKšœ˜—Kšœœ ˜CKšœ˜ K˜—Kšœœ˜"šœ œœ˜Kšœ3œN˜‰Kšœ ˜K˜—Kšœ;˜;K˜K˜—šŸ œ +˜IKš'œ#œœ™sšŸ œ˜"Kš (œ œœœœœ™SšŸœ ˜1KšM™MK–6[pattern: ROPE, object: ROPE, case: BOOL _ TRUE]š œœ>œœœ˜h—Kš œ œœœœœ ,˜QKšœœœœ ˜Kšœ˜KšœF˜Fšœœœ˜&Kšœœ)ก œ˜QKšœ˜—Kšœœœ ˜QKšœ&˜,K˜—Kšœ œœ˜šœ œœ˜Kšœ3œO˜ŠKšœ ˜K˜—Kšœ;˜;K˜K˜—šŸ œ ˜:Kšฃ=œœฃ ™nKšœœ˜K˜—K–a -- [valueIn: LIST OF ROPE, name: ROPE, palette: Profiles.Profile] RETURNS [mapped: LIST OF ROPE]˜šŸœ ˜8Kšฃ=œœฃ ™nKšœ ˜K˜K˜—šŸ œ 6˜PKšฃ=œœฃ ™nKšœœ˜ K˜—K˜šŸœ  ˜9Kš'œ#œœ™sšŸ œ˜ Kš (œ œœœœœ™SKš"Ÿœœœœœœœœœœœœœœœœœ˜‡Kš œ9œœœ œœ ˜šœ ˜Kšœ œœ œ˜KšœL˜L—Kšœ œ˜*šœ œ˜ Kšœ œ &˜Kšœœœ˜Kšœœ# ˜XKšœ˜—K˜—Kšœœ˜—K˜K˜—Kšœ œ?˜L–๑[toMap: ARRAY [1..4] OF REAL, size: [0..4], formula: LIST OF ROPE, s: ColorizeViewPointMappingImpl.Stack, palette: Profiles.Profile, level: NAT, getValue: ColorizeViewPointMappingImpl.GetValueProc _ ColorDisplayHeadDorado.SetB]šœ œ˜šœJ˜Jšœ˜Kšœ3œ~˜นKšœ˜K˜—Kšœ ˜ ——Kšœœ˜K˜K˜—šŸ œœกœœ˜s–I[profile: Profiles.Profile, key: ROPE, default: LIST OF ROPE _ NIL]šŸœœœ˜7Kšœ1™1š œœœ œœ˜BKšœœ)œ)œ&˜‡Kšœ œ -˜P—Kšœœ0˜NKšœ˜K˜—Kšœ œ˜'K–I[profile: Profiles.Profile, key: ROPE, default: LIST OF ROPE _ NIL]šœ œ:˜Gšœ œœœœœ œœ˜VKšŸœœœœœœ1œ1œœ 3˜ญšœ œT˜bšœ˜Kšœ3œ~˜นKšœ˜K˜—Kšœ  ˜7—Kšœœ0˜Nš˜Kšœœ˜!—Kš˜—Kšœœ˜K˜K˜—Kšœ œ˜Kšœœœ ˜šœ œœ˜Kš œœœœœ˜/Kšœœ˜ Kšœ œœ ˜/Kšœœœ ˜6K˜—Kšœœœœ œœœ ?˜ฃKšœ œ ,˜@Kš Ÿ œœœœ ˜:šŸ œœœ6œ œœ œœœ Œ˜ีšœ˜Kšœœœ˜ตKšœœœs˜œK˜—š Ÿ œœœ œ <˜sKšŸœœœœ'˜DKšŸœœœœ˜.KšŸœœœ<œ ˜€KšŸ œœœ0œ˜sKšŸ œœœœœ œ&œ˜‚šŸœœ œœ˜+Kš œ œœ7œœ Hœ˜ั—š Ÿ œœœ œœ˜7šœœœ ˜/Kšœœœ˜)šœœ  ˜IKšœœœ œ˜Ušœ ˜Kšœ œ4œ˜IKšœ  ˜Kšœ œ ˜Kšœ˜——šœœ ˜AKšœœœ œ˜Ušœ ˜Kšœ œ%˜2Kšœ  ˜Kšœ  ,˜7Kšœ œ˜——šœœ (˜PKš œœœ œ!œ˜`Kšœœœœ˜@š œœœ!œ “˜ฦKšœ œ$ 9œœ(˜งKšœ  ˜Kšœ# -˜PKšœ œ˜Kšœ˜—K˜—š œœœœ#œ D˜Kšœ  œ$œ˜K—Kšœœ˜šœ ด˜ฤKš œœœœœ ˜KKšœ ˜ Kšœ œœ"˜KK˜—šœ ซ˜ปKš œœœœœ ˜Kšœ%œ˜-Kš œœœœœ ;˜†—K˜—Kšœœ˜Kš œœœœœ(œ f˜ษKšœ œ˜—K˜—š Ÿœœœ œœ˜2šœœ  ˜Kšœ1˜3Kšœ œ˜—K˜—š Ÿ œœœ œœ˜4š œ œœœ ˜1Kšœ&˜&Kšœ&˜&Kšœ&˜&Kšœ˜Kšœ œœœ˜,KšœA˜AKšœA˜AKšœA˜AKšœA˜AKšœ œ!œœ˜VKšœ œ!œœ˜VKšœ œ!œœ˜VKšœ œ!œœ˜VKšœ˜&—Kšœ œ˜K˜—š Ÿ œœœ œœ˜4šœœœ ˜+Kšœœœ˜`Kšœœœ˜`Kšœœœ˜`Kšœœ˜>KšœœW˜vKšœœ\˜{K–6[pattern: ROPE, object: ROPE, case: BOOL _ TRUE]šœœ˜Ašœ3œ  œก˜JKšœ œ-˜=Kšœ œœ7ก˜kKšœก˜!Kšœœ˜0K˜—Kšœ$œ œD˜ƒKšœœ œ  ˜>š œœœœœ!œ˜]Kšœ&˜&K–[self: STREAM, index: INT]šœ˜Kš œœœ6œ ˜sK˜—Kšœ œ˜K˜——š Ÿ œœœ œœ˜4šœœœ ˜+Kšœœ œ œ C˜…Kšœœœ˜,Kšœœ ˜BKšœœJ˜lKšœœ˜7Kšœœ˜>Kšœœ4 &˜}Kš œœœ"œœ˜uKš œœœ"œœ˜uKšœœœ$˜fKšœœœ$˜fKšœ œ˜—K˜—š Ÿ œœœ œœ ˜Išœœœ ˜+Kšœœ3˜TKšœœ1˜PKšœœ2˜RKšœœ2˜RKšœœ2˜RKš œœœœœ˜`Kšœœ˜:Kšœœ œN˜~Kš œœœœœœ%œ4˜ฉKš œ œœœœœ#œ2˜งKš œœœœœœ$œ:˜ตKš œ œœœœœ(œ>˜ฟšœ œ˜7Kš œœœœ$œ˜pKš œœœœ%œ ˜lKš œœœœœ˜]K˜—Kšœœ˜:Kšœœ˜@Kšœ œ˜K˜——Kšœ œ6˜CšŸ œœ˜*šœ ˜Kšœœœf˜ŽKšœ œœ‹˜ฎKšœœœj˜’KšœœœO˜vKšœœ ˜"—K˜—Kšœœ˜Kšœœ˜Kš œ œœœœœ˜2šœœ˜KšœWœ˜\Kšœ˜—K˜—šŸ œœ˜–' -- [char: CHAR] RETURNS [IO.CharClass]šŸ œœ˜Kšฃ#™#KšœœœœœœœœIœ ˜†—š œ œœ œ œ˜9Kšœ œ˜Kš œœ2œœœ ˜iKšœ œ˜Kš œ œ2œœœ ˜qKšœœ˜K˜—šœ˜Kšœ$˜$Kšœ œ˜Kš œ œ2œœœ ˜qK˜—Kšœœ˜K˜—Kšœœœ˜Kšœœœœœœ5œœ œ œ˜มKšœœœœ 4˜NK˜Kšœ6œœ˜CKš œœœœœ ˜ Kšœœœ˜Kšœœ˜Kšœœœœ˜:š˜K–A[stream: STREAM, breakProc: IO.BreakProc, buffer: REF TEXT]šœ ˜ Kš œœœ œœœ˜UKšœ˜—K˜K˜—Kš"Ÿ œœœœœœœœœœœœœœœœœ˜ฌK˜š$Ÿ œœ œœœ œœœœ œœœœœœœ œœ1˜ฝK˜——™š Ÿœœœœœ˜jKšœ œ˜šœœ ื˜Kšœœœ˜Kšœ˜Kšœ ˜ K˜—šœœ <˜pKšœ ˜ K–I[profile: Profiles.Profile, key: ROPE, default: LIST OF ROPE _ NIL]šœ˜Kšœ ˜ K˜—šœœ˜2Kšœ ˜ K–I[profile: Profiles.Profile, key: ROPE, default: LIST OF ROPE _ NIL]š œ œ1œœœ ˜eKšœ œ˜+Kšœ ˜ K˜—šœœ˜4Kšœœœ˜ Kš œ œ3œœœ ˜iKšœ œ˜+Kšœ ˜ K˜—šœœ˜4Kšœœœ˜ Kš œ œ3œœœ ˜iKšœ œ˜+Kšœ ˜ K˜—šœ$œ˜