ColorToolViewerImpl.mesa
Written by Darlene Plebon on June 24, 1983 11:14 am
Last Edited by: Stone, June 9, 1986 11:18:16 am PDT
Last Edited by: Beach, April 1, 1984 6:00:29 pm PST
Rick Beach, October 25, 1985 8:40:58 pm PDT
Avi Naiman, September 8, 1985 7:30:32 pm PDT
DIRECTORY
Buttons USING [Button, ButtonProc, Create],
ChoiceButtons USING [BuildEnumTypeSelection, ButtonList, EnumTypeRef, GetSelectedButton, SelectionNotifierProc, UpdateChoiceButtons],
CIEViewer,
ColorPatch,
CNSColor USING [CNSFromCSL, CSLFromCNS, CSLChroma, CSLLightness, CSLSaturation, CSLFromHSL, HSLFromCSL],
ColorToolViewer,
ColorToolViewerExtras,
Containers USING [Container, Create],
Convert USING [RealFromRope],
ImagerColor USING [ConstantColor, RGB, HSV, HSL, YIQ, HSVFromRGB, RGBFromHSV, HSLFromRGB, RGBFromHSL, YIQFromRGB, RGBFromYIQ, ColorFromAtom],
ImagerDitheredDevice USING [ColorFromSpecialRGB],
Terminal USING [Current, ChannelValue, ColorValue],
ImagerColorMap USING [LoadEntries, MapEntry],
Real USING [RoundC],
IO USING [PutFR, real],
Menus USING [Menu],
MessageWindow USING [Append],
CGColorWithCIE,
Rope USING [Equal, ROPE],
Sliders USING [Create, SetContents, SliderProc],
Commander USING [CommandProc, Register],
TiogaOps USING [InsertRope],
VFonts USING [CharWidth],
ViewerClasses USING [Column, Viewer, ViewerClass],
ViewerOps USING [PaintViewer, SetOpenHeight, OpenIcon ],
ViewerTools USING [MakeNewTextViewer, GetContents, SetContents],
WindowManager USING [colorDisplayOn];
ColorToolViewerImpl: CEDAR PROGRAM
IMPORTS Buttons, ChoiceButtons, CIEViewer, CNSColor, ColorPatch, Commander, Containers, Convert, ImagerColor, IO, MessageWindow, CGColorWithCIE, Rope, Sliders, TiogaOps, VFonts, ViewerOps, ViewerTools, WindowManager, ImagerColorMap, ImagerDitheredDevice, Terminal, Real
EXPORTS ColorToolViewer, ColorToolViewerExtras
= BEGIN
entryHeight: CARDINAL = 15;  -- how tall to make each line of text items
entryVSpace: CARDINAL = 8;   -- vertical leading space between lines
entryHSpace: CARDINAL = 10;   -- horizontal space between items in a line
columnSpacing: INTEGER = 20; -- space between columns
sliderWidth: CARDINAL = 20;  -- width of all sliders in color tool
sliderHeight: CARDINAL = 102;  -- height of all sliders in color tool
initialRed: REAL = 0.75;    -- initial color values for color patch
initialGreen: REAL = 0.75;
initialBlue: REAL = 0.2;
SixViewers: TYPE = RECORD[text1, text2, text3, slider1, slider2, slider3: ViewerClasses.Viewer];
hack to avoid an unsafe return
lastHandle: Handle ← NIL;  --so the viewer can be manipulated by a program
ColorPatchViewer: TYPE = ColorPatch.ColorPatchViewer;
ColorPatchData: TYPE = ColorPatch.ColorPatchData;
Handle: TYPE = REF ColorToolRec;
ColorToolRec: TYPE = RECORD [
outer: Containers.Container ← NIL, -- handle for the enclosing container
height: CARDINAL ← 0,     -- height measured from the top of the container
rgb: RGBViewer,      -- the RGB viewer's state
hsv: HSVViewer,      -- the HSV viewer's state
hsl: HSLViewer,       -- the HSL viewer's state
yiq: YIQViewer,       -- the YIQ viewer's state
argyb: ARgYbViewer,     -- the ARgYb viewer's state
cns: CNSViewer,       -- the CNS viewer's state
menus: MenusViewer,     -- the menu viewer's state
colorPatch: ColorPatchViewer,   -- the color patch viewer's state
cie: CIEViewer.Viewer     -- the CIE Viewer
];
GetRGBValue: PUBLIC PROC RETURNS [red,green,blue: REAL] = {
data: ColorPatchData ← NARROW[lastHandle.colorPatch.data];
RETURN[red: data.rgb.R, green: data.rgb.G, blue: data.rgb.B];
};
GetCSLValue: PUBLIC PROC RETURNS[cslChroma: CNSColor.CSLChroma, cslSaturation: CNSColor.CSLSaturation, cslLightness: CNSColor.CSLLightness] = {
data: ColorPatchData ← NARROW[lastHandle.colorPatch.data];
RETURN[cslChroma: data.cslC, cslSaturation: data.cslS, cslLightness: data.cslL];
};
SetRGBValue: PUBLIC PROC [red,green,blue: REAL] = {
UpdateColorViewers[handle: lastHandle, color: [rgb[[red,green,blue]]]];
};
SetCSLValue: PUBLIC PROC [cslChroma: CNSColor.CSLChroma, cslSaturation: CNSColor.CSLSaturation, cslLightness: CNSColor.CSLLightness] = {
UpdateColorViewers[handle: lastHandle, color: [csl[cslChroma, cslSaturation, cslLightness]]];
};
GetSpecialColor: PUBLIC PROC RETURNS[ImagerColor.ConstantColor] = {
ToByte: PROC[v: REAL] RETURNS[Terminal.ColorValue] = INLINE { RETURN[Real.RoundC[v*255]] };
patchData: ColorPatchData ← NARROW[lastHandle.colorPatch.data];
patchData.patchMapEntry.red ← ToByte[patchData.rgb.R];
patchData.patchMapEntry.green ← ToByte[patchData.rgb.G];
patchData.patchMapEntry.blue ← ToByte[patchData.rgb.B];
ImagerColorMap.LoadEntries[vt: Terminal.Current[],
mapEntries: LIST[patchData.patchMapEntry], shared: FALSE];
RETURN[ImagerDitheredDevice.ColorFromSpecialRGB[
specialPixel: [patchData.patchMapEntry.mapIndex,null], rgb: patchData.rgb]];
};
MakeColorTool: Commander.CommandProc = {
my: Handle ← NEW[ColorToolRec];
column: ViewerClasses.Column ← color;
IF WindowManager.colorDisplayOn = FALSE THEN column ← left;
my.outer ← Containers.Create[info: [
name: "Color Tool",
iconic: TRUE,
column: column,
scrollable: TRUE ],
paint: FALSE ];
MakeRGB[my];
MakeHSV[my];
MakeHSL[my];
MakeYIQ[my];
MakeARgYb[my];
MakeCNS[my];
MakeMenus[my];
MakeColorPatch[my];
MakeCIEViewer[my];
UpdateColorViewers[handle: my, color: [rgb[[initialRed, initialGreen, initialBlue]]]];
ViewerOps.SetOpenHeight[my.outer, my.height];
ViewerOps.OpenIcon[my.outer];
lastHandle ← my;
};
ComputeFromCIE: CIEViewer.CIEProc = {
handle: Handle ← NARROW[clientData];
UpdateColorViewers[handle: handle, color: [cie[x,y,Y]]];
};
MakeCIEViewer: PROCEDURE [handle: Handle] = {
handle.cie ← CIEViewer.Create[
info: [name: "ColorToolCIE"],
sliderWidth: sliderWidth,
sliderHeight: 2*sliderHeight,
clientData: handle,
proc: ComputeFromCIE,
maxY: 1,
initR: initialRed,
initG: initialGreen,
initB: initialBlue ];
};
RGBViewer: TYPE = RECORD [
redText: ViewerClasses.Viewer ← NIL,
greenText: ViewerClasses.Viewer ← NIL,
blueText: ViewerClasses.Viewer ← NIL,
redSlider: ViewerClasses.Viewer ← NIL,
greenSlider: ViewerClasses.Viewer ← NIL,
blueSlider: ViewerClasses.Viewer ← NIL
];
MakeRGB: PROCEDURE [handle: Handle] = {
[handle.rgb.redText, handle.rgb.greenText, handle.rgb.blueText, handle.rgb.redSlider, handle.rgb.greenSlider, handle.rgb.blueSlider] ← MakeColorSchemeViewer[
handle: handle,
name: "RGB",
buttonProc: ComputeFromRGB,
sliderProc1: RedSliderUpdate,
sliderProc2: GreenSliderUpdate,
sliderProc3: BlueSliderUpdate,
color1: ImagerColor.ColorFromAtom[$Red],
color2: ImagerColor.ColorFromAtom[$Green],
color3: ImagerColor.ColorFromAtom[$Blue],
xOrigin: 200,
yOrigin: 0
]^;
};
HSVViewer: TYPE = RECORD [
hueText: ViewerClasses.Viewer ← NIL,
saturationText: ViewerClasses.Viewer ← NIL,
valueText: ViewerClasses.Viewer ← NIL,
hueSlider: ViewerClasses.Viewer ← NIL,
saturationSlider: ViewerClasses.Viewer ← NIL,
valueSlider: ViewerClasses.Viewer ← NIL
];
MakeHSV: PROCEDURE [handle: Handle] = {
[handle.hsv.hueText, handle.hsv.saturationText, handle.hsv.valueText, handle.hsv.hueSlider, handle.hsv.saturationSlider, handle.hsv.valueSlider] ← MakeColorSchemeViewer[
handle: handle,
name: "HSV",
buttonProc: ComputeFromHSV,
sliderProc1: HueSliderUpdate,
sliderProc2: SaturationSliderUpdate,
sliderProc3: ValueSliderUpdate,
xOrigin: 0,
yOrigin: 0
]^;
};
HSLViewer: TYPE = RECORD [
hueText: ViewerClasses.Viewer ← NIL,
saturationText: ViewerClasses.Viewer ← NIL,
lightnessText: ViewerClasses.Viewer ← NIL,
hueSlider: ViewerClasses.Viewer ← NIL,
saturationSlider: ViewerClasses.Viewer ← NIL,
lightnessSlider: ViewerClasses.Viewer ← NIL
];
MakeHSL: PROCEDURE [handle: Handle] = {
[handle.hsl.hueText, handle.hsl.saturationText, handle.hsl.lightnessText, handle.hsl.hueSlider, handle.hsl.saturationSlider, handle.hsl.lightnessSlider] ← MakeColorSchemeViewer[
handle: handle,
name: "HSL",
buttonProc: ComputeFromHSL,
sliderProc1: HSLHueSliderUpdate,
sliderProc2: HSLSaturationSliderUpdate,
sliderProc3: HSLLightnessSliderUpdate,
xOrigin: 0,
yOrigin: 150
]^;
};
YIQViewer: TYPE = RECORD [
intensityText: ViewerClasses.Viewer ← NIL,
inPhaseText: ViewerClasses.Viewer ← NIL,
quadratureText: ViewerClasses.Viewer ← NIL,
intensitySlider: ViewerClasses.Viewer ← NIL,
inPhaseSlider: ViewerClasses.Viewer ← NIL,
quadratureSlider: ViewerClasses.Viewer ← NIL
];
MakeYIQ: PROCEDURE [handle: Handle] = {
[handle.yiq.intensityText, handle.yiq.inPhaseText, handle.yiq.quadratureText, handle.yiq.intensitySlider, handle.yiq.inPhaseSlider, handle.yiq.quadratureSlider] ← MakeColorSchemeViewer[
handle: handle,
name: "YIQ",
buttonProc: ComputeFromYIQ,
sliderProc1: YIQIntensitySliderUpdate,
sliderProc2: YIQInPhaseSliderUpdate,
sliderProc3: YIQQuadratureSliderUpdate,
xOrigin: 400,
yOrigin: 0
]^;
};
ARgYbViewer: TYPE = RECORD [
achromaticText: ViewerClasses.Viewer ← NIL,
redGreenText: ViewerClasses.Viewer ← NIL,
yellowBlueText: ViewerClasses.Viewer ← NIL,
achromaticSlider: ViewerClasses.Viewer ← NIL,
redGreenSlider: ViewerClasses.Viewer ← NIL,
yellowBlueSlider: ViewerClasses.Viewer ← NIL
];
MakeARgYb: PROCEDURE [handle: Handle] = {
[handle.argyb.achromaticText, handle.argyb.redGreenText, handle.argyb.yellowBlueText, handle.argyb.achromaticSlider, handle.argyb.redGreenSlider, handle.argyb.yellowBlueSlider] ← MakeColorSchemeViewer[
handle: handle,
name: "ARgYb",
buttonProc: ComputeFromARgYb,
sliderProc1: ARgYbAchromaticSliderUpdate,
sliderProc2: ARgYbRedGreenSliderUpdate,
sliderProc3: ARgYbYellowBlueSliderUpdate,
xOrigin: 400,
yOrigin: 150
]^;
};
MakeColorSchemeViewer: PROCEDURE [handle: Handle, name: Rope.ROPE, buttonProc: Buttons.ButtonProc, sliderProc1, sliderProc2, sliderProc3: Sliders.SliderProc, color1, color2, color3: ImagerColor.ConstantColor ← NIL, xOrigin, yOrigin: CARDINAL] RETURNS [ret: REF SixViewers] = {
valueEntryWidth: CARDINAL = 8 * VFonts.CharWidth['0]; -- eight digits worth of width
xPosition: CARDINAL ← xOrigin;
yPosition: CARDINAL ← yOrigin;
button: Buttons.Button;
ret ← NEW[SixViewers];
yPosition ← yPosition + entryVSpace;
button ← Buttons.Create[
info: [
name: name,
wx: xPosition + entryHSpace,
wy: yPosition,
wh: entryHeight,
parent: handle.outer,
border: TRUE ],
clientData: handle,
proc: buttonProc,
paint: FALSE ];
yPosition ← yPosition + entryHeight;
ret.text1 ← ViewerTools.MakeNewTextViewer[info: [
parent: handle.outer,
wx: xPosition + entryHSpace,
wy: yPosition+2,
ww: valueEntryWidth,
wh: entryHeight,
data: " ",
scrollable: FALSE,
border: FALSE ],
paint: FALSE ];
ret.text2 ← ViewerTools.MakeNewTextViewer[info: [
parent: handle.outer,
wx: ret.text1.wx + ret.text1.ww + entryHSpace,
wy: yPosition+2,
ww: valueEntryWidth,
wh: entryHeight,
data: " ",
scrollable: FALSE,
border: FALSE ],
paint: FALSE ];
ret.text3 ← ViewerTools.MakeNewTextViewer[info: [
parent: handle.outer,
wx: ret.text2.wx + ret.text2.ww + entryHSpace,
wy: yPosition+2,
ww: valueEntryWidth,
wh: entryHeight,
data: " ",
scrollable: FALSE,
border: FALSE ],
paint: FALSE ];
yPosition ← yPosition + entryHeight + entryVSpace;
ret.slider1 ← Sliders.Create[
info: [
parent: handle.outer,
wx: xPosition + entryHSpace,
wy: yPosition,
ww: sliderWidth,
wh: sliderHeight
],
foreground: color1,
clientData: handle,
sliderProc: sliderProc1,
paint: FALSE
];
ret.slider2 ← Sliders.Create[
info: [
parent: handle.outer,
wx: ret.text1.wx + ret.text1.ww + entryHSpace,
wy: yPosition,
ww: sliderWidth,
wh: sliderHeight
],
foreground: color2,
clientData: handle,
sliderProc: sliderProc2,
paint: FALSE
];
ret.slider3 ← Sliders.Create[
info: [
parent: handle.outer,
wx: ret.text2.wx + ret.text2.ww + entryHSpace,
wy: yPosition,
ww: sliderWidth,
wh: sliderHeight
],
foreground: color3,
clientData: handle,
sliderProc: sliderProc3,
paint: FALSE
];
yPosition ← yPosition + sliderHeight;
handle.height ← MAX[handle.height, yPosition];
RETURN[ret];
};
The color naming scheme.
CNSHues: ChoiceButtons.ButtonList ← LIST[
"Red", "OrangishRed", "RedOrange", "ReddishOrange",
"Orange", "YellowishOrange", "OrangeYellow", "OrangishYellow",
"Yellow", "GreenishYellow", "YellowGreen", "YellowishGreen",
"Green", "BluishGreen", "GreenBlue", "GreenishBlue",
"Blue", "PurplishBlue", "BluePurple", "BluishPurple",
"Purple", "ReddishPurple", "PurpleRed", "PurplishRed",
"BrownishRed", "RedBrown", "ReddishBrown",
"Brown", "YellowishBrown", "BrownYellow", "BrownishYellow",
"Black", "Gray", "White"
];
CNSLightnesses: ChoiceButtons.ButtonList ← LIST[
"VeryDark", "Dark", "Medium", "Light", "VeryLight"
];
CNSSaturations: ChoiceButtons.ButtonList ← LIST[
"Grayish", "Moderate", "Strong", "Vivid"
];
CNSViewer: TYPE = RECORD [
saturationChoices: ChoiceButtons.EnumTypeRef,
lightnessChoices: ChoiceButtons.EnumTypeRef,
hueChoices: ChoiceButtons.EnumTypeRef
];
MakeCNS: PROCEDURE [handle: Handle] = {
maxWidth: CARDINAL = 610; -- not really width, it's the max x allowed on the screen!
xPosition: CARDINAL = 0;
yPosition: CARDINAL ← 300;
button: Buttons.Button;
yPosition ← yPosition + entryVSpace;
button ← Buttons.Create[
info: [
name: "CNS",
wx: xPosition + entryHSpace,
wy: yPosition,
wh: entryHeight,
parent: handle.outer,
border: TRUE ],
clientData: handle,
proc: ComputeFromCNS,
paint: FALSE ];
yPosition ← yPosition + entryHeight;
handle.cns.saturationChoices ← ChoiceButtons.BuildEnumTypeSelection[
viewer: handle.outer,
x: button.wx,
y: yPosition,
title: "Saturation:",
buttonNames: CNSSaturations,
default: "Vivid",
borderOnButtons: FALSE,
notifyClientProc: CNSSaturationUpdate,
clientdata: handle,
style: menuSelection,
allInOneRow: TRUE ];
yPosition ← yPosition + entryHeight;
handle.cns.lightnessChoices ← ChoiceButtons.BuildEnumTypeSelection[
viewer: handle.outer,
x: button.wx,
y: yPosition,
title: "Lightness:",
buttonNames: CNSLightnesses,
default: "Medium",
borderOnButtons: FALSE,
notifyClientProc: CNSLightnessUpdate,
clientdata: handle,
style: menuSelection,
allInOneRow: TRUE ];
yPosition ← yPosition + entryHeight;
handle.cns.hueChoices ← ChoiceButtons.BuildEnumTypeSelection[
viewer: handle.outer,
x: button.wx,
y: yPosition,
title: "Hue: ",
buttonNames: CNSHues,
borderOnButtons: FALSE,
notifyClientProc: CNSHueUpdate,
clientdata: handle,
style: menuSelection,
allInOneRow: FALSE,
maxWidth: maxWidth ];
yPosition ← yPosition + 8*entryHeight + entryVSpace;
handle.height ← MAX[handle.height, yPosition];
};
DitherChoices: ChoiceButtons.ButtonList ← LIST[
"True", "False"
];
ContrastChoices: ChoiceButtons.ButtonList ← LIST[
"None", "All", "RGB", "HSV", "HSL", "YIQ", "ARgYb"
];
ConsistencyCheckChoices: ChoiceButtons.ButtonList ← LIST[
"On", "Off"
];
InterpolationChoices: ChoiceButtons.ButtonList ← LIST[
"Left", "Right", "Stop", "Off"
];
MenusViewer: TYPE = RECORD [
ditherChoices: ChoiceButtons.EnumTypeRef,
contrastChoices: ChoiceButtons.EnumTypeRef,
interpolationChoices: ChoiceButtons.EnumTypeRef,
consistencyCheckChoices: ChoiceButtons.EnumTypeRef
];
MakeMenus: PROCEDURE [handle: Handle] = {
xPosition: CARDINAL ← 350;
yPosition: CARDINAL ← 300;
yPosition ← yPosition + entryVSpace;
handle.menus.contrastChoices ← ChoiceButtons.BuildEnumTypeSelection[
viewer: handle.outer,
x: xPosition,
y: yPosition,
title: "Contrast:",
buttonNames: ContrastChoices,
default: "None",
borderOnButtons: FALSE,
notifyClientProc: ContrastUpdate,
clientdata: handle,
style: menuSelection,
allInOneRow: TRUE ];
yPosition ← yPosition + entryHeight;
handle.menus.ditherChoices ← ChoiceButtons.BuildEnumTypeSelection[
viewer: handle.outer,
x: xPosition,
y: yPosition,
title: "Dither:",
buttonNames: DitherChoices,
default: "False",
borderOnButtons: FALSE,
notifyClientProc: DitherUpdate,
clientdata: handle,
style: menuSelection,
allInOneRow: TRUE ];
xPosition ← 480; -- terrible isn't it?
handle.menus.consistencyCheckChoices ← ChoiceButtons.BuildEnumTypeSelection[
viewer: handle.outer,
x: xPosition,
y: yPosition,
title: "Consist. Check:",
buttonNames: ConsistencyCheckChoices,
default: "Off",
borderOnButtons: FALSE,
notifyClientProc: ConsistencyCheckUpdate,
clientdata: handle,
style: menuSelection,
allInOneRow: TRUE ];
xPosition ← 350; -- just as bad
yPosition ← yPosition + entryHeight;
handle.menus.interpolationChoices ← ChoiceButtons.BuildEnumTypeSelection[
viewer: handle.outer,
x: xPosition,
y: yPosition,
title: "Interpolation:",
buttonNames: InterpolationChoices,
default: "Off",
borderOnButtons: FALSE,
notifyClientProc: InterpolationUpdate,
clientdata: handle,
style: menuSelection,
allInOneRow: TRUE ];
yPosition ← yPosition + 8*entryHeight + entryVSpace;
handle.height ← MAX[handle.height, yPosition];
};
The color patch reflecting the currently selected color.
MakeColorPatch: PROCEDURE [handle: Handle] = {
xPosition: CARDINAL = 167;
yPosition: CARDINAL ← 150;
height: INTEGER = 150;
width: INTEGER ← 200;
yPosition ← yPosition + entryVSpace;
handle.colorPatch ← ColorPatch.MakeColorPatch[parent: handle.outer,
wx: xPosition + entryHSpace,
wy: yPosition,
width: width,
height: height];
yPosition ← yPosition + height + entryVSpace;
handle.height ← MAX[handle.height, yPosition];
};
ComputeFromRGB: Buttons.ButtonProc = {
handle: Handle ← NARROW[clientData];
redValue: REAL ← GetColorValue[handle.rgb.redText];
greenValue: REAL ← GetColorValue[handle.rgb.greenText];
blueValue: REAL ← GetColorValue[handle.rgb.blueText];
UpdateColorViewers[handle: handle, color: [rgb[[redValue, greenValue, blueValue]]]];
IF shift THEN {
StuffSelection[handle.rgb.redText, handle.rgb.greenText, handle.rgb.blueText];
};
};
RedSliderUpdate: Sliders.SliderProc = {
handle: Handle ← NARROW[clientData];
patchData: ColorPatchData ← NARROW[handle.colorPatch.data];
UpdateColorViewers[handle: handle, color: [rgb[[value, patchData.rgb.G, patchData.rgb.B]]]];
};
GreenSliderUpdate: Sliders.SliderProc = {
handle: Handle ← NARROW[clientData];
patchData: ColorPatchData ← NARROW[handle.colorPatch.data];
UpdateColorViewers[handle: handle, color: [rgb[[patchData.rgb.R, value, patchData.rgb.B]]]];
};
BlueSliderUpdate: Sliders.SliderProc = {
handle: Handle ← NARROW[clientData];
patchData: ColorPatchData ← NARROW[handle.colorPatch.data];
UpdateColorViewers[handle: handle, color: [rgb[[patchData.rgb.R, patchData.rgb.G, value]]]];
};
ComputeFromHSV: Buttons.ButtonProc = {
handle: Handle ← NARROW[clientData];
hueValue: REAL ← GetColorValue[handle.hsv.hueText];
saturationValue: REAL ← GetColorValue[handle.hsv.saturationText];
valueValue: REAL ← GetColorValue[handle.hsv.valueText];
UpdateColorViewers[handle: handle, color: [hsv[[hueValue, saturationValue, valueValue]]]];
IF shift THEN {
StuffSelection[handle.hsv.hueText, handle.hsv.saturationText, handle.hsv.valueText];
};
};
HueSliderUpdate: Sliders.SliderProc = {
handle: Handle ← NARROW[clientData];
patchData: ColorPatchData ← NARROW[handle.colorPatch.data];
UpdateColorViewers[handle: handle, color: [hsv[[value, patchData.hsv.S, patchData.hsv.V]]]];
};
SaturationSliderUpdate: Sliders.SliderProc = {
handle: Handle ← NARROW[clientData];
patchData: ColorPatchData ← NARROW[handle.colorPatch.data];
UpdateColorViewers[handle: handle, color: [hsv[[patchData.hsv.H, value, patchData.hsv.V]]]];
};
ValueSliderUpdate: Sliders.SliderProc = {
handle: Handle ← NARROW[clientData];
patchData: ColorPatchData ← NARROW[handle.colorPatch.data];
UpdateColorViewers[handle: handle, color: [hsv[[patchData.hsv.H, patchData.hsv.S, value]]]];
};
ComputeFromHSL: Buttons.ButtonProc = {
handle: Handle ← NARROW[clientData];
hueValue: REAL ← GetColorValue[handle.hsl.hueText];
saturationValue: REAL ← GetColorValue[handle.hsl.saturationText];
lightnessValue: REAL ← GetColorValue[handle.hsl.lightnessText];
UpdateColorViewers[handle: handle, color: [hsl[[hueValue, saturationValue, lightnessValue]]]];
IF shift THEN {
StuffSelection[handle.hsl.hueText, handle.hsl.saturationText, handle.hsl.lightnessText];
};
};
HSLHueSliderUpdate: Sliders.SliderProc = {
handle: Handle ← NARROW[clientData];
patchData: ColorPatchData ← NARROW[handle.colorPatch.data];
UpdateColorViewers[handle: handle, color: [hsl[[value, patchData.hsl.S, patchData.hsl.L]]]];
};
HSLSaturationSliderUpdate: Sliders.SliderProc = {
handle: Handle ← NARROW[clientData];
patchData: ColorPatchData ← NARROW[handle.colorPatch.data];
UpdateColorViewers[handle: handle, color: [hsl[[patchData.hsl.H, value, patchData.hsl.L]]]];
};
HSLLightnessSliderUpdate: Sliders.SliderProc = {
handle: Handle ← NARROW[clientData];
patchData: ColorPatchData ← NARROW[handle.colorPatch.data];
UpdateColorViewers[handle: handle, color: [hsl[[patchData.hsl.H, patchData.hsl.S, value]]]];
};
GetColorValue: PROCEDURE [v: ViewerClasses.Viewer] RETURNS [value: REAL] = {
rope: Rope.ROPE ~ ViewerTools.GetContents[v];
value ← Convert.RealFromRope[rope];
IF value > 1.0 OR value < 0.0 THEN {
MessageWindow.Append[message: "Invalid value ", clearFirst: TRUE];
MessageWindow.Append[message: rope, clearFirst: FALSE];
value ← 1.0;
};
RETURN[value];
};
StuffSelection: PROC [v1, v2, v3: ViewerClasses.Viewer] ~ {
depend on the viewer contents having form: "d.dd " from the "%-5f" format in UpdateColorViewers
TiogaOps.InsertRope[ViewerTools.GetContents[v1]];
TiogaOps.InsertRope[ViewerTools.GetContents[v2]];
TiogaOps.InsertRope[ViewerTools.GetContents[v3]];
};
ComputeFromYIQ: Buttons.ButtonProc = {
handle: Handle ← NARROW[clientData];
intensityValue: REAL ← GetColorValue[handle.yiq.intensityText];
inPhaseValue: REAL ← GetColorValue[handle.yiq.inPhaseText];
quadratureValue: REAL ← GetColorValue[handle.yiq.quadratureText];
UpdateColorViewers[handle: handle, color: [yiq[[intensityValue, inPhaseValue, quadratureValue]]]];
IF shift THEN {
StuffSelection[handle.yiq.intensityText, handle.yiq.inPhaseText, handle.yiq.quadratureText];
};
};
YIQIntensitySliderUpdate: Sliders.SliderProc = {
handle: Handle ← NARROW[clientData];
patchData: ColorPatchData ← NARROW[handle.colorPatch.data];
UpdateColorViewers[handle: handle, color: [yiq[[value, patchData.yiq.I, patchData.yiq.Q]]]];
};
YIQInPhaseSliderUpdate: Sliders.SliderProc = {
handle: Handle ← NARROW[clientData];
patchData: ColorPatchData ← NARROW[handle.colorPatch.data];
UpdateColorViewers[handle: handle, color: [yiq[[patchData.yiq.Y, value, patchData.yiq.Q]]]];
};
YIQQuadratureSliderUpdate: Sliders.SliderProc = {
handle: Handle ← NARROW[clientData];
patchData: ColorPatchData ← NARROW[handle.colorPatch.data];
UpdateColorViewers[handle: handle, color: [yiq[[patchData.yiq.Y, patchData.yiq.I, value]]]];
};
ComputeFromARgYb: Buttons.ButtonProc = {
handle: Handle ← NARROW[clientData];
achromaticValue: REAL ← GetColorValue[handle.argyb.achromaticText];
redGreenValue: REAL ← GetColorValue[handle.argyb.redGreenText];
yellowBlueValue: REAL ← GetColorValue[handle.argyb.yellowBlueText];
UpdateColorViewers[handle: handle, color: [argyb[achromaticValue, redGreenValue, yellowBlueValue]]];
IF shift THEN {
StuffSelection[handle.argyb.achromaticText, handle.argyb.redGreenText, handle.argyb.yellowBlueText];
};
};
ARgYbAchromaticSliderUpdate: Sliders.SliderProc = {
handle: Handle ← NARROW[clientData];
patchData: ColorPatchData ← NARROW[handle.colorPatch.data];
UpdateColorViewers[handle: handle, color: [argyb[value, patchData.argybRg, patchData.argybYb]]];
};
ARgYbRedGreenSliderUpdate: Sliders.SliderProc = {
handle: Handle ← NARROW[clientData];
patchData: ColorPatchData ← NARROW[handle.colorPatch.data];
UpdateColorViewers[handle: handle, color: [argyb[patchData.argybA, value, patchData.argybYb]]];
};
ARgYbYellowBlueSliderUpdate: Sliders.SliderProc = {
handle: Handle ← NARROW[clientData];
patchData: ColorPatchData ← NARROW[handle.colorPatch.data];
UpdateColorViewers[handle: handle, color: [argyb[patchData.argybA, patchData.argybRg, value]]];
};
ComputeFromCNS: Buttons.ButtonProc = {
handle: Handle ← NARROW[clientData];
c: CNSColor.CSLChroma;
s: CNSColor.CSLSaturation;
l: CNSColor.CSLLightness;
hue: Rope.ROPE ← ChoiceButtons.GetSelectedButton[handle.cns.hueChoices];
saturation: Rope.ROPE ← ChoiceButtons.GetSelectedButton[handle.cns.saturationChoices];
lightness: Rope.ROPE ← ChoiceButtons.GetSelectedButton[handle.cns.lightnessChoices];
[c, s, l] ← CNSColor.CSLFromCNS[hue, saturation, lightness];
UpdateColorViewers[handle: handle, color: [csl[c, s, l]]];
};
CNSHueUpdate: ChoiceButtons.SelectionNotifierProc = {
handle: Handle ← NARROW[clientdata];
patchData: ColorPatchData ← NARROW[handle.colorPatch.data];
hue, saturation, lightness: Rope.ROPE;
c: CNSColor.CSLChroma;
s: CNSColor.CSLSaturation;
l: CNSColor.CSLLightness;
[hue, saturation, lightness] ← CNSColor.CNSFromCSL[patchData.cslC, patchData.cslS, patchData.cslL];
[c, s, l] ← CNSColor.CSLFromCNS[ name, saturation, lightness];
UpdateColorViewers[handle: handle, color: [csl[c, s, l]]];
};
CNSSaturationUpdate: ChoiceButtons.SelectionNotifierProc = {
handle: Handle ← NARROW[clientdata];
patchData: ColorPatchData ← NARROW[handle.colorPatch.data];
hue, saturation, lightness: Rope.ROPE;
c: CNSColor.CSLChroma;
s: CNSColor.CSLSaturation;
l: CNSColor.CSLLightness;
[hue, saturation, lightness] ← CNSColor.CNSFromCSL[patchData.cslC, patchData.cslS, patchData.cslL];
[c, s, l] ← CNSColor.CSLFromCNS[ hue, name, lightness];
UpdateColorViewers[handle: handle, color: [csl[c, s, l]]];
};
CNSLightnessUpdate: ChoiceButtons.SelectionNotifierProc = {
handle: Handle ← NARROW[clientdata];
patchData: ColorPatchData ← NARROW[handle.colorPatch.data];
hue, saturation, lightness: Rope.ROPE;
c: CNSColor.CSLChroma;
s: CNSColor.CSLSaturation;
l: CNSColor.CSLLightness;
[hue, saturation, lightness] ← CNSColor.CNSFromCSL[patchData.cslC, patchData.cslS, patchData.cslL];
[c, s, l] ← CNSColor.CSLFromCNS[ hue, saturation, name];
UpdateColorViewers[handle: handle, color: [csl[c, s, l]]];
};
DitherUpdate: ChoiceButtons.SelectionNotifierProc = {
handle: Handle ← NARROW[clientdata];
patchData: ColorPatchData ← NARROW[handle.colorPatch.data];
ditherRope: Rope.ROPE ← ChoiceButtons.GetSelectedButton[handle.menus.ditherChoices];
SELECT ditherRope FROM
"True" =>
patchData.dither ← TRUE;
"False" =>
patchData.dither ← FALSE;
ENDCASE;
ViewerOps.PaintViewer[viewer: handle.colorPatch, hint: client, clearClient: FALSE];
};
ConsistencyCheckUpdate: ChoiceButtons.SelectionNotifierProc = {
handle: Handle ← NARROW[clientdata];
patchData: ColorPatchData ← NARROW[handle.colorPatch.data];
consistencyCheckRope: Rope.ROPE ← ChoiceButtons.GetSelectedButton[handle.menus.consistencyCheckChoices];
redValue: REAL ← GetColorValue[handle.rgb.redText];
greenValue: REAL ← GetColorValue[handle.rgb.greenText];
blueValue: REAL ← GetColorValue[handle.rgb.blueText];
SELECT consistencyCheckRope FROM
"On" => {
patchData.consistencyCheck ← TRUE;
UpdateColorViewers[handle: handle, color: [rgb[[redValue, greenValue, blueValue]]]];
};
"Off" =>
patchData.consistencyCheck ← FALSE;
ENDCASE;
};
InterpolationUpdate: ChoiceButtons.SelectionNotifierProc = {
handle: Handle ← NARROW[clientdata];
patchData: ColorPatchData ← NARROW[handle.colorPatch.data];
interpolationRope: Rope.ROPE ← ChoiceButtons.GetSelectedButton[handle.menus.interpolationChoices];
repaint: BOOLEANFALSE;
wasOff: BOOLEANIF patchData.interpolationRequest = off THEN TRUE ELSE FALSE;
SELECT interpolationRope FROM
"Left" => {
patchData.interpolationRequest ← left;
IF wasOff THEN
repaint ← TRUE;
};
"Right" => {
patchData.interpolationRequest ← right;
IF wasOff THEN
repaint ← TRUE;
};
"Stop" => {
patchData.interpolationRequest ← stop;
IF wasOff THEN
repaint ← TRUE;
};
"Off" => {
patchData.interpolationRequest ← off;
repaint ← TRUE;
};
ENDCASE;
IF repaint THEN
ViewerOps.PaintViewer[viewer: handle.colorPatch, hint: client, clearClient: FALSE];
};
ContrastUpdate: ChoiceButtons.SelectionNotifierProc = {
handle: Handle ← NARROW[clientdata];
patchData: ColorPatchData ← NARROW[handle.colorPatch.data];
contrastRope: Rope.ROPE ← ChoiceButtons.GetSelectedButton[handle.menus.contrastChoices];
SELECT contrastRope FROM
"All" => {
patchData.rgbContrast ← TRUE;
patchData.hsvContrast ← TRUE;
patchData.hslContrast ← TRUE;
patchData.yiqContrast ← TRUE;
patchData.argybContrast ← TRUE;
};
"None" => {
patchData.rgbContrast ← FALSE;
patchData.hsvContrast ← FALSE;
patchData.hslContrast ← FALSE;
patchData.yiqContrast ← FALSE;
patchData.argybContrast ← FALSE;
};
"RGB" => {
patchData.rgbContrast ← TRUE;
patchData.hsvContrast ← FALSE;
patchData.hslContrast ← FALSE;
patchData.yiqContrast ← FALSE;
patchData.argybContrast ← FALSE;
};
"HSV" => {
patchData.rgbContrast ← FALSE;
patchData.hsvContrast ← TRUE;
patchData.hslContrast ← FALSE;
patchData.yiqContrast ← FALSE;
patchData.argybContrast ← FALSE;
};
"HSL" => {
patchData.rgbContrast ← FALSE;
patchData.hsvContrast ← FALSE;
patchData.hslContrast ← TRUE;
patchData.yiqContrast ← FALSE;
patchData.argybContrast ← FALSE;
};
"YIQ" => {
patchData.rgbContrast ← FALSE;
patchData.hsvContrast ← FALSE;
patchData.hslContrast ← FALSE;
patchData.yiqContrast ← TRUE;
patchData.argybContrast ← FALSE;
};
"ARgYb" => {
patchData.rgbContrast ← FALSE;
patchData.hsvContrast ← FALSE;
patchData.hslContrast ← FALSE;
patchData.yiqContrast ← FALSE;
patchData.argybContrast ← TRUE;
};
ENDCASE;
ViewerOps.PaintViewer[viewer: handle.colorPatch, hint: client, clearClient: FALSE];
};
ColorSpecification: TYPE = RECORD [
color: SELECT type: ColorType FROM
hsv => [val: ImagerColor.HSV],
hsl => [val: ImagerColor.HSL],
yiq => [val: ImagerColor.YIQ],
argyb => [a, rg, yb: REAL],
rgb => [val: ImagerColor.RGB],
cie => [x, y, Y: REAL],
csl => [c: CNSColor.CSLChroma, s: CNSColor.CSLSaturation, l: CNSColor.CSLLightness],
ENDCASE
];
ColorType: TYPE = {hsv, rgb, csl, hsl, yiq, argyb, cie};
UpdateColorViewers: PROCEDURE [handle: Handle, color: ColorSpecification] RETURNS [] = {
UpdateColorValue: PROCEDURE [oldValue, newValue: REAL, textViewer, sliderViewer: ViewerClasses.Viewer] RETURNS [new: REAL] = {
IF newValue=CGColorWithCIE.undefined THEN RETURN[oldValue];
IF newValue # oldValue THEN {
ViewerTools.SetContents[viewer: textViewer, contents: IO.PutFR["%-5f", IO.real[newValue]]];
Sliders.SetContents[slider: sliderViewer, contents: newValue];
};
RETURN[newValue];
};
patchData: ColorPatchData ← NARROW[handle.colorPatch.data];
hsv: ImagerColor.HSV;
rgb: ImagerColor.RGB;
hsl: ImagerColor.HSL;
yiq: ImagerColor.YIQ;
a, rg, yb: REAL;
chroma: CNSColor.CSLChroma;
saturation: CNSColor.CSLSaturation;
lightness: CNSColor.CSLLightness;
cnsHue, cnsSaturation, cnsLightness: Rope.ROPE;
WITH col: color SELECT FROM
hsv => {
hsv ← col.val;
rgb ← ImagerColor.RGBFromHSV[hsv];
hsl ← ImagerColor.HSLFromRGB[rgb];
yiq ← ImagerColor.YIQFromRGB[rgb];
yiq.I ← (yiq.I + 0.6) / 1.2; yiq.Q ← (yiq.Q + 0.52) / 1.04;
[a, rg, yb] ← CGColorWithCIE.RGBToARgYb[rgb.R, rgb.G, rgb.B];
[chroma, saturation, lightness] ← CNSColor.CSLFromHSL[hsl.H, hsl.S, hsl.L];
};
hsl => {
hsl ← col.val;
rgb ← ImagerColor.RGBFromHSL[hsl];
hsv ← ImagerColor.HSVFromRGB[rgb];
yiq ← ImagerColor.YIQFromRGB[rgb];
yiq.I ← (yiq.I + 0.6) / 1.2; yiq.Q ← (yiq.Q + 0.52) / 1.04;
[a, rg, yb] ← CGColorWithCIE.RGBToARgYb[rgb.R, rgb.G, rgb.B];
[chroma, saturation, lightness] ← CNSColor.CSLFromHSL[hsl.H, hsl.S, hsl.L];
};
yiq => {
yiq ← col.val;
yiq.I ← yiq.I * 1.2 - 0.6; yiq.Q ← yiq.Q * 1.04 - 0.52;
rgb ← ImagerColor.RGBFromYIQ[yiq];
we have to protect ourselves from receiving back RGB values outside of the [0,1] range
rgb.R ← MIN[rgb.R,1]; rgb.G ← MIN[rgb.G,1]; rgb.B ← MIN[rgb.B,1];
rgb.R ← MAX[rgb.R,0]; rgb.G ← MAX[rgb.G,0]; rgb.B ← MAX[rgb.B,0];
IF patchData.consistencyCheck THEN
yiq ← ImagerColor.YIQFromRGB[rgb];
yiq.I ← (yiq.I + 0.6) / 1.2; yiq.Q ← (yiq.Q + 0.52) / 1.04;
hsv ← ImagerColor.HSVFromRGB[rgb];
hsl ← ImagerColor.HSLFromRGB[rgb];
[a, rg, yb] ← CGColorWithCIE.RGBToARgYb[rgb.R, rgb.G, rgb.B];
[chroma, saturation, lightness] ← CNSColor.CSLFromHSL[hsl.H, hsl.S, hsl.L];
};
argyb => {
a ← col.a; rg ← col.rg; yb ← col.yb;
[rgb.R, rgb.G, rgb.B] ← CGColorWithCIE.ARgYbToRGB[a, rg, yb];
we have to protect ourselves from receiving back RGB values outside of the [0,1] range
rgb.R ← MIN[rgb.R,1]; rgb.G ← MIN[rgb.G,1]; rgb.B ← MIN[rgb.B,1];
rgb.R ← MAX[rgb.R,0]; rgb.G ← MAX[rgb.G,0]; rgb.B ← MAX[rgb.B,0];
IF patchData.consistencyCheck THEN
[a, rg, yb] ← CGColorWithCIE.RGBToARgYb[rgb.R, rgb.G, rgb.B];
hsv ← ImagerColor.HSVFromRGB[rgb];
hsl ← ImagerColor.HSLFromRGB[rgb];
yiq ← ImagerColor.YIQFromRGB[rgb];
yiq.I ← (yiq.I + 0.6) / 1.2; yiq.Q ← (yiq.Q + 0.52) / 1.04;
[chroma, saturation, lightness] ← CNSColor.CSLFromHSL[hsl.H, hsl.S, hsl.L];
};
rgb => {
rgb ← col.val;
hsv ← ImagerColor.HSVFromRGB[rgb];
hsl ← ImagerColor.HSLFromRGB[rgb];
yiq ← ImagerColor.YIQFromRGB[rgb];
yiq.I ← (yiq.I + 0.6) / 1.2; yiq.Q ← (yiq.Q + 0.52) / 1.04;
[a, rg, yb] ← CGColorWithCIE.RGBToARgYb[rgb.R, rgb.G, rgb.B];
[chroma, saturation, lightness] ← CNSColor.CSLFromHSL[hsl.H, hsl.S, hsl.L];
};
cie => {
[rgb.R, rgb.G, rgb.B] ← CGColorWithCIE.CIEToRGB[col.x,col.y,col.Y];
control precision problems
rgb.R ← MIN[rgb.R,1]; rgb.G ← MIN[rgb.G,1]; rgb.B ← MIN[rgb.B,1];
rgb.R ← MAX[rgb.R,0]; rgb.G ← MAX[rgb.G,0]; rgb.B ← MAX[rgb.B,0];
hsv ← ImagerColor.HSVFromRGB[rgb];
hsl ← ImagerColor.HSLFromRGB[rgb];
yiq ← ImagerColor.YIQFromRGB[rgb];
yiq.I ← (yiq.I + 0.6) / 1.2; yiq.Q ← (yiq.Q + 0.52) / 1.04;
[a, rg, yb] ← CGColorWithCIE.RGBToARgYb[rgb.R, rgb.G, rgb.B];
[chroma, saturation, lightness] ← CNSColor.CSLFromHSL[hsl.H, hsl.S, hsl.L];
};
csl => {
chroma ← col.c; saturation ← col.s; lightness ← col.l;
[hsl.H, hsl.S, hsl.L] ← CNSColor.HSLFromCSL[ chroma, saturation, lightness];
rgb ← ImagerColor.RGBFromHSL[hsl];
hsv ← ImagerColor.HSVFromRGB[rgb];
yiq ← ImagerColor.YIQFromRGB[rgb];
yiq.I ← (yiq.I + 0.6) / 1.2; yiq.Q ← (yiq.Q + 0.52) / 1.04;
[a, rg, yb] ← CGColorWithCIE.RGBToARgYb[rgb.R, rgb.G, rgb.B];
}
ENDCASE;
patchData.rgb.R ← UpdateColorValue[oldValue: patchData.rgb.R, newValue: rgb.R,
textViewer: handle.rgb.redText, sliderViewer: handle.rgb.redSlider];
patchData.rgb.G ← UpdateColorValue[oldValue: patchData.rgb.G, newValue: rgb.G,
textViewer: handle.rgb.greenText, sliderViewer: handle.rgb.greenSlider];
patchData.rgb.B ← UpdateColorValue[oldValue: patchData.rgb.B, newValue: rgb.B,
textViewer: handle.rgb.blueText, sliderViewer: handle.rgb.blueSlider];
patchData.hsv.H ← UpdateColorValue[oldValue: patchData.hsv.H, newValue: hsv.H,
textViewer: handle.hsv.hueText, sliderViewer: handle.hsv.hueSlider];
patchData.hsv.S ← UpdateColorValue[oldValue: patchData.hsv.S, newValue: hsv.S,
textViewer: handle.hsv.saturationText, sliderViewer: handle.hsv.saturationSlider];
patchData.hsv.V ← UpdateColorValue[oldValue: patchData.hsv.V, newValue: hsv.V,
textViewer: handle.hsv.valueText, sliderViewer: handle.hsv.valueSlider];
patchData.hsl.H ← UpdateColorValue[oldValue: patchData.hsl.H, newValue: hsl.H,
textViewer: handle.hsl.hueText, sliderViewer: handle.hsl.hueSlider];
patchData.hsl.S ← UpdateColorValue[oldValue: patchData.hsl.S, newValue: hsl.S,
textViewer: handle.hsl.saturationText, sliderViewer: handle.hsl.saturationSlider];
patchData.hsl.L ← UpdateColorValue[oldValue: patchData.hsl.L, newValue: hsl.L,
textViewer: handle.hsl.lightnessText, sliderViewer: handle.hsl.lightnessSlider];
patchData.yiq.Y ← UpdateColorValue[oldValue: patchData.yiq.Y, newValue: yiq.Y,
textViewer: handle.yiq.intensityText, sliderViewer: handle.yiq.intensitySlider];
patchData.yiq.I ← UpdateColorValue[oldValue: patchData.yiq.I, newValue: yiq.I,
textViewer: handle.yiq.inPhaseText, sliderViewer: handle.yiq.inPhaseSlider];
patchData.yiq.Q ← UpdateColorValue[oldValue: patchData.yiq.Q, newValue: yiq.Q,
textViewer: handle.yiq.quadratureText, sliderViewer: handle.yiq.quadratureSlider];
patchData.argybA ← UpdateColorValue[oldValue: patchData.argybA, newValue: a,
textViewer: handle.argyb.achromaticText, sliderViewer: handle.argyb.achromaticSlider];
patchData.argybRg ← UpdateColorValue[oldValue: patchData.argybRg, newValue: rg,
textViewer: handle.argyb.redGreenText, sliderViewer: handle.argyb.redGreenSlider];
patchData.argybYb ← UpdateColorValue[oldValue: patchData.argybYb, newValue: yb,
textViewer: handle.argyb.yellowBlueText, sliderViewer: handle.argyb.yellowBlueSlider];
[cnsHue, cnsSaturation, cnsLightness] ← CNSColor.CNSFromCSL[chroma, saturation, lightness];
IF patchData.cslC # chroma AND ~Rope.Equal[cnsHue, ""] THEN {
patchData.cslC ← chroma;
ChoiceButtons.UpdateChoiceButtons[viewer: handle.outer, enumTypeInfo: handle.cns.hueChoices, newName: cnsHue];
};
IF patchData.cslS # saturation AND ~Rope.Equal[cnsSaturation, ""] THEN {
patchData.cslS ← saturation;
ChoiceButtons.UpdateChoiceButtons[viewer: handle.outer, enumTypeInfo: handle.cns.saturationChoices, newName: cnsSaturation];
};
IF patchData.cslL # lightness AND ~Rope.Equal[cnsLightness, ""] THEN {
patchData.cslL ← lightness;
ChoiceButtons.UpdateChoiceButtons[viewer: handle.outer, enumTypeInfo: handle.cns.lightnessChoices, newName: cnsLightness];
};
IF patchData.consistencyCheck OR color.type#cie THEN
{
x,y,Y: REAL;
[x,y,Y] ← CGColorWithCIE.RGBToCIE[rgb.R,rgb.G,rgb.B];
CIEViewer.SetContents[handle.cie, x,y,Y];
};
ViewerOps.PaintViewer[viewer: handle.colorPatch, hint: client, clearClient: FALSE];
};
Register a command that will create an instance of this tool.
Commander.Register[key: "ColorTool", proc: MakeColorTool, doc: "Create a color tool"];
END.