ColorNamesImpl.mesa
Written by Maureen Stone on June 23, 1983 11:48 am
Last Edited by: Beach, June 22, 1983 2:52 pm
Last Edited by: Stone, October 19, 1983 5:20 pm
Last Edited by: Pier, January 18, 1984 1:09 pm
DIRECTORY
ColorNames,
ColorModels USING [undefined],
RuntimeError USING [BoundsFault],
IO USING [STREAM, RIS, GetTokenRope, PutFR, rope],
Rope USING [ROPE, Length, Match,Cat ];
ColorNamesImpl: CEDAR PROGRAM
IMPORTS IO, Rope, RuntimeError EXPORTS ColorNames =
BEGIN OPEN ColorNames;
ROPE: TYPE=Rope.ROPE;
UndefinedName: PUBLIC SIGNAL = CODE;
undefined: REAL = ColorModels.undefined; --for undefined color values
hueRecord: TYPE = RECORD [
name: ROPE,
value: REAL
];
hueMapping: ARRAY HueType OF hueRecord ← [
undefined:  ["Undefined",  undefined], 
black:    ["Black",    undefined], --achromatic
white:    ["White",   undefined], --achromatic
gray:    ["Gray",    undefined], --achromatic
grey:    ["Grey",    undefined], --achromatic
red:    ["Red",      0.0],
orangishRed:  ["OrangishRed",   0.01],
redOrange:  ["RedOrange",    0.02],
reddishOrange: ["ReddishOrange",   0.03],
brownishRed: ["BrownishRed",   0.01],
redBrown:  ["RedBrown",    0.02],
reddishBrown: ["ReddishBrown",   0.03],
orange:   ["Orange",     0.04],
yellowishOrange: ["YellowishOrange",  0.07],
orangeYellow:  ["OrangeYellow",   0.10],
orangishYellow:  ["OrangishYellow",   0.13],
brown:   ["Brown",     0.04],
yellowishBrown: ["YellowishBrown",  0.07],
brownYellow: ["BrownYellow",   0.10],
brownishYellow: ["BrownishYellow",  0.13],
yellow:   ["Yellow",     0.1673],
greenishYellow: ["GreenishYellow",   0.2073],
yellowGreen:  ["YellowGreen",   0.2473],
yellowishGreen: ["YellowishGreen",   0.2873],
green:    ["Green",     0.3333],
bluishGreen:  ["BluishGreen",    0.4133],
greenBlue:  ["GreenBlue",    0.4933],
greenishBlue: ["GreenishBlue",   0.5733],
blue:    ["Blue",      0.6666],
purplishBlue: ["PurplishBlue",   0.6816],
bluePurple:  ["BluePurple",    0.6966],
bluishPurple: ["BluishPurple",   0.7116],
purple:   ["Purple",     0.73],
reddishPurple: ["ReddishPurple",   0.80],
purpleRed:  ["PurpleRed",    0.87],
purplishRed:  ["PurplishRed",    0.94]
];
saturationRecord: TYPE = RECORD [
name: ROPE,
value: REAL
];
saturationMapping: ARRAY SaturationType OF saturationRecord ← [
default:  ["Default",   1.0], 
achromatic: ["Achromatic",  0.0],
weak:   ["Weak",    0.25],
moderate:  ["Moderate",  0.50],
strong:  ["Strong",   0.75],
vivid:   ["Vivid",    1.0]
];
lightnessRecord: TYPE = RECORD [
name: ROPE,
value: REAL
];
lightnessMapping: ARRAY LightnessType OF lightnessRecord ← [
default:  ["Default",   0.5],
veryDark: ["VeryDark",  0.1666],
dark:   ["Dark",    0.3333],
medium:  ["Medium",   0.5],
light:   ["Light",    0.6666],
veryLight: ["VeryLight",  0.8333]
];
ishTable: ARRAY [0..7) OF ROPE ← [
"Reddish", "Orangish", "Brownish", "Yellowish", "Greenish", "Bluish", "Purplish"
];
ParseColorName: PUBLIC PROCEDURE[rope: ROPE] RETURNS[names: Names] = {
hue: HueType ← undefined;
saturation: SaturationType ← default;
lightness: LightnessType ← default;
hueList: ARRAY [0..2) OF ROPE;
index: CARDINAL ← 0; --for hueList
very, found: BOOLEANFALSE;
token: ROPE;
Done: SIGNAL=CODE;
stream: IO.STREAMIO.RIS[rope];
FindLightness: PROC[r: ROPE] RETURNS [lt: LightnessType, fnd: BOOLEAN] = {
FOR lt IN LightnessType DO
IF Rope.Match[r,lightnessMapping[lt].name,FALSE] THEN RETURN[lt, TRUE];
ENDLOOP;
RETURN[default,FALSE];
};
FindSaturation: PROC[r: ROPE] RETURNS [sat: SaturationType, fnd: BOOLEAN] = {
FOR sat IN SaturationType DO
IF Rope.Match[r,saturationMapping[sat].name,FALSE] THEN RETURN[sat, TRUE];
ENDLOOP;
RETURN[default,FALSE];
};
FindHue: PROC[r: ROPE] RETURNS [h: HueType, fnd: BOOLEAN] = {
FOR h IN HueType DO
IF Rope.Match[r,hueMapping[h].name,FALSE] THEN RETURN[h, TRUE];
ENDLOOP;
RETURN[undefined,FALSE];
};
FindHuish: PROC[r: ROPE] RETURNS [name: ROPE, fnd: BOOLEAN] = {
FOR i: CARDINAL IN [0..LENGTH[ishTable]) DO
IF Rope.Match[r,ishTable[i],FALSE] THEN RETURN[ishTable[i], TRUE];
ENDLOOP;
RETURN[NIL,FALSE];
};
NextToken: PROC = {
DO
[token: token, charsSkipped: ] ← IO.GetTokenRope[stream];
IF token=NIL THEN SIGNAL Done;
IF Rope.Length[token] > 2 THEN EXIT;
ENDLOOP;
};
DO
NextToken[! Done => EXIT];
IF lightness=default THEN {
IF Rope.Match[token, "Very", FALSE] THEN {very ← TRUE; LOOP};
[lightness, found] ← FindLightness[token];
IF found THEN {
IF very AND (lightness=dark OR lightness=light) THEN
[lightness, found] ← FindLightness[Rope.Cat["Very",token]];
LOOP;
}
ELSE very ← FALSE; --very has to immediately precede a lightness term
};
IF saturation=default THEN {
[saturation, found] ← FindSaturation[token];
IF found THEN LOOP;
};
hue is more complicated because there may be compound hues
[hue, found] ← FindHue[token];
IF found THEN { ENABLE RuntimeError.BoundsFault => SIGNAL UndefinedName;
hueList[index] ← token;
index ← index+1;
}
ELSE IF index=0 THEN { --*ish only occures for the first half of a compound color
huish: ROPE;
[huish, found] ← FindHuish[token];
IF found THEN {hueList[index] ← huish; index ← index+1}
};
ENDLOOP;
SELECT index FROM
1 => { --check for valid single hue
[hue, found] ← FindHue[hueList[0]];
IF ~found THEN SIGNAL UndefinedName;
};
2 => { --compound hue
[hue, found] ← FindHue[Rope.Cat[hueList[0], hueList[1]]];
IF ~found THEN SIGNAL UndefinedName;
};
ENDCASE => UndefinedName; --we must have a hue
RETURN[[hue: hue, saturation: saturation, lightness: lightness]];
};
NamesToHSL: PUBLIC PROCEDURE [names: Names] RETURNS [h,s,l: REAL] = {
SELECT names.hue FROM
black => {h ← undefined; s𡤀 l𡤀};
white => {h ← undefined; s𡤀 l𡤁};
ENDCASE => {
h ← hueMapping[names.hue].value;
IF h = undefined THEN s ← 0 ELSE s ← saturationMapping[names.saturation].value;
l ← lightnessMapping[names.lightness].value;
};
RETURN[h,s,l];
};
NamesToRope: PUBLIC PROCEDURE [names: Names] RETURNS [rope: ROPE] = {
rope ← IO.PutFR["%g %g %g", IO.rope[lightnessMapping[names.lightness].name], IO.rope[saturationMapping[names.saturation].name], IO.rope[hueMapping[names.hue].name]];
RETURN[rope];
};
HSLToNames: PUBLIC PROCEDURE [h,s,l: REAL] RETURNS [Names] = {
hue: HueType;
saturation: SaturationType;
lightness: LightnessType;
minD, d: REAL ← 10; --infinity
IF l=0.0 THEN RETURN[[hue: black, saturation: achromatic, lightness: default]];
IF l=1.0 THEN RETURN[[hue: white, saturation: achromatic, lightness: default]];
FOR index: LightnessType IN [veryDark..veryLight] DO
d ← ABS[l-lightnessMapping[index].value];
IF d < minD THEN {minD ← d; lightness ← index};
ENDLOOP;
minD ← 10; --infinity
FOR index: SaturationType IN [achromatic..vivid] DO
d ← ABS[s-saturationMapping[index].value];
IF d < minD THEN {minD ← d; saturation ← index};
ENDLOOP;
IF saturation=achromatic THEN RETURN[[hue: gray, saturation: achromatic, lightness: lightness]];
IF h=undefined THEN SIGNAL UndefinedName;
minD ← 10; --infinity
FOR index: HueType IN [red..purplishRed] DO
d ← ABS[h-hueMapping[index].value];
IF d < minD THEN {minD ← d; hue ← index};
ENDLOOP;
RETURN[[hue: hue, saturation: saturation, lightness: lightness]];
};
END.