ColorizeViewPointMappingImpl.mesa
Copyright Ó 1989, 1990 by Xerox Corporation. All rights reserved.
Bob Coleman, September 13, 1990 12:02 pm PDT
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;
Applying Mappings
ApplyMappings: PUBLIC PROC [toMap: REF, palette: Profiles.Profile, mapData: MapData, mapOnly: ROPENIL, subpaletteList: LIST OF ROPENIL] RETURNS [mappedRope: ROPENIL, mappedList: LIST OF ROPENIL] ~ {
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;
while we're at it, now's the time to set the appropriate palette entries TRUE
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
[key: ROPE] RETURNS [quit: BOOL ← FALSE]
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
[key: ROPE] RETURNS [quit: BOOL ← FALSE]
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: BOOLFALSE;
data: LIST OF ROPENIL;
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 REALALL[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 ~ {
[toMap: Color, tokenAfter: ROPE] RETURNS [LIST OF ROPE]
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 ROPENIL] ~ {
ExpandEach: Profiles.EnumProc ~ { --like ForceToSpring, ForceToGrey, .... Changes "ForceToSpring: c1, c2" to "c1: ForceToSpring\n c2: ForceToSpring\n"
[key: ROPE] RETURNS [quit: BOOL ← FALSE]
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: ROPENIL] ~ { --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 ROPELIST[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];
};
MappingProcs
SnapList: TYPE ~ LIST OF RGBColor;
SnapMapper: MappingProc ~ { --snaps to nearest color in a list of colors
PROC [valueIn: LIST OF ROPE, name: ROPE, palette: Profiles.Profile, data: REFNIL] RETURNS [mapped: LIST OF ROPE]
ApplySnapMap: ApplyMappingProc ~ {
[toMap: Color, tokenAfter: ROPE] RETURNS [mapped: LIST OF ROPE, quit: BOOLFALSE]
dummyHead: LIST OF ROPELIST[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
PROC [valueIn: LIST OF ROPE, name: ROPE, palette: Profiles.Profile, data: REFNIL] RETURNS [mapped: LIST OF ROPE]
ApplyFormula: ApplyMappingProc ~ {
[toMap: Color, tokenAfter: ROPE] RETURNS [mapped: LIST OF ROPE, quit: BOOLFALSE]
TokenVal: GetValueProc ~ { --used by ParseFormula
PROC [r: ROPE, s: Stack] RETURNS [v: REAL ← Real.LargestNumber, pop: NAT ← 0]
RETURN [v: (IF Rope.Match[pattern: r.Concat["*"], object: tokenAfter, case: FALSE] THEN 1.0 ELSE 0.0)]};
dummyHead: LIST OF ROPELIST[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: ROPENARROW[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
[valueIn: LIST OF ROPE, name: ROPE, palette: Profiles.Profile, data: REFNIL] RETURNS [mapped: LIST OF ROPE]
RETURN [NARROW[data]];
};
NoMapper: MappingProc ~ {--returns input with no mapping
[valueIn: LIST OF ROPE, name: ROPE, palette: Profiles.Profile, data: REFNIL] RETURNS [mapped: LIST OF ROPE]
RETURN [valueIn];
};
NilMapper: MappingProc ~ {--returns NIL, causing b&w pattern to stay uncolorized
[valueIn: LIST OF ROPE, name: ROPE, palette: Profiles.Profile, data: REFNIL] RETURNS [mapped: LIST OF ROPE]
RETURN [NIL];
};
BWMapper: MappingProc ~ {   --map from RGB or CMYK to BW
PROC [valueIn: LIST OF ROPE, name: ROPE, palette: Profiles.Profile, data: REFNIL] RETURNS [mapped: LIST OF ROPE]
ApplyBWMap: ApplyMappingProc ~ {
[toMap: Color, tokenAfter: ROPE] RETURNS [mapped: LIST OF ROPE, quit: BOOLFALSE]
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
PROC [valueIn: LIST OF ROPE, name: ROPE, palette: Profiles.Profile, data: REFNIL] RETURNS [mapped: LIST OF ROPE]
ApplyGreyMap: ApplyMappingProc ~ {
[toMap: Color, tokenAfter: ROPE] RETURNS [mapped: LIST OF ROPE, quit: BOOLFALSE]
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
PROC [valueIn: LIST OF ROPE, name: ROPE, palette: Profiles.Profile, data: REFNIL] RETURNS [mapped: LIST OF ROPE]
ApplyBrightMap: ApplyMappingProc ~ {
[toMap: Color, tokenAfter: ROPE] RETURNS [mapped: LIST OF ROPE, quit: BOOLFALSE]
dummyHead: LIST OF ROPELIST[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];
};
Mapping Utilities
ApplyMappingProc: TYPE ~ PROC [toMap: Color, tokenAfter: ROPE] RETURNS [mapped: LIST OF ROPE, quit: BOOLFALSE]; --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 ROPELIST[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] ~ {
if no formula or bad formula in palette, use this
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] ~ {
if no formula or bad formula in palette, use this
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 REALALL[0.0],
top: INT ← 0,
curEntry: ROPENIL, --used in error reporting
levelsExceeded: BOOLFALSE --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: BOOLFALSE] ~ { --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: BOOLTRUE] ~ {
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: BOOLTRUE] ~ {
SELECT c FROM --numbers
IN ['0..'9], '. => Push[Convert.RealFromRope[tok]];
ENDCASE => found ← FALSE;
};
IsShortTok: PROC [] RETURNS [found: BOOLTRUE] ~ {
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]<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};
'# => {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: BOOLTRUE] ~ {
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: BOOLTRUE] ~ {
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: BOOLTRUE] ~ { --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 = {
[char: CHAR] RETURNS [IO.CharClass]
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: BOOLTRUE, 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: BOOLFALSE;
in: IO.STREAMIO.RIS[formula];
tok, nextTok: ROPENIL;
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: ROPENIL] ~ {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]]]};
Standard Mappings
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.