<> <> <> <> DIRECTORY Buttons USING [Button, ButtonProc, Create], ChoiceButtons USING [BuildEnumTypeSelection, ButtonList, EnumTypeRef, GetSelectedButton, SelectionNotifierProc, UpdateChoiceButtons], CIEViewer, CNSColor USING [CSLToCNS, CNSToCSL, CSLChroma, CSLLightness, CSLSaturation, CSLToHSL, HSLToCSL, medium, red, vivid], ColorMap USING [GetIndex, SetRGBColor], ColorToolViewer, Containers USING [Container, Create], Convert USING [RealFromRope], Graphics USING [Box, Context, DrawBox, GetBounds, SetColor], GraphicsBasic USING [Color], GraphicsColor USING [RGBToColor, red, green, blue], IO USING [PutFR, real], Menus USING [Menu], MessageWindow USING [Append], CGColorWithCIE, Rope USING [Equal, ROPE], Sliders USING [Create, SetContents, SliderProc, sliderGray], Commander USING [CommandProc, Register], UserProfile, VFonts USING [CharWidth], ViewerClasses USING [Column, PaintProc, Viewer, ViewerClass, ViewerClassRec, DestroyProc], ViewerOps USING [CreateViewer, PaintViewer, RegisterViewerClass, SetOpenHeight ], ViewerTools USING [MakeNewTextViewer, GetContents, SetContents], WindowManager USING [colorDisplayOn, ScreenPos, StartColorViewers, StopColorViewers]; ColorToolViewerImpl: CEDAR PROGRAM IMPORTS Buttons, ChoiceButtons, CIEViewer, CNSColor, ColorMap, Commander, Containers, Convert, Graphics, GraphicsColor, IO, MessageWindow, CGColorWithCIE, Rope, Sliders, UserProfile, VFonts, ViewerOps, ViewerTools, WindowManager EXPORTS ColorToolViewer = 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 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 height2: CARDINAL _ 0, -- height of second column from 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 cns: CNSViewer, -- the CNS viewer's state colorPatch: ColorPatchViewer, -- the color patch viewer's state cie: CIEViewer.Viewer -- the CIE Viewer (a REF not a RECORD) ]; GetRGBValue: PUBLIC PROC RETURNS [red,green,blue: REAL] = { data: ColorPatchData _ NARROW[lastHandle.colorPatch.data]; RETURN[red: data.rgbR, green: data.rgbG, blue: data.rgbB]; }; 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]]]; }; MakeColorTool: Commander.CommandProc = { my: Handle _ NEW[ColorToolRec]; column: ViewerClasses.Column _ color; IF WindowManager.colorDisplayOn = FALSE THEN column _ left; my.outer _ Containers.Create[ [ name: "Color Tool", iconic: FALSE, column: column, scrollable: FALSE ] ]; MakeRGB[my]; MakeHSV[my]; MakeHSL[my]; MakeOnOffButtons[my]; MakeCNS[my]; MakeColorPatch[my]; MakeCIEViewer[my]; ViewerOps.SetOpenHeight[my.outer, my.height]; UpdateColorViewers[handle: my, color: [rgb[initialRed, initialGreen, initialBlue]]]; 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: GraphicsColor.red, color2: GraphicsColor.green, color3: GraphicsColor.blue ]^; }; 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 ]^; }; 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 ]^; }; MakeColorSchemeViewer: PROCEDURE [handle: Handle, name: Rope.ROPE, buttonProc: Buttons.ButtonProc, sliderProc1, sliderProc2, sliderProc3: Sliders.SliderProc, color1, color2, color3: GraphicsBasic.Color _ Sliders.sliderGray] RETURNS [ret: REF SixViewers] = { valueEntryWidth: CARDINAL = 8 * VFonts.CharWidth['0]; -- eight digits worth of width button: Buttons.Button; ret _ NEW[SixViewers]; handle.height _ handle.height + entryVSpace; button _ Buttons.Create[ info: [ name: name, wy: handle.height, wh: entryHeight, parent: handle.outer, border: TRUE ], clientData: handle, proc: buttonProc ]; ret.text1 _ ViewerTools.MakeNewTextViewer[ [ parent: handle.outer, wx: button.wx + button.ww + entryHSpace, wy: handle.height+2, ww: valueEntryWidth, wh: entryHeight, data: " ", scrollable: FALSE, border: FALSE ] ]; ret.text2 _ ViewerTools.MakeNewTextViewer[ [ parent: handle.outer, wx: ret.text1.wx + ret.text1.ww + entryHSpace, wy: handle.height+2, ww: valueEntryWidth, wh: entryHeight, data: " ", scrollable: FALSE, border: FALSE ] ]; ret.text3 _ ViewerTools.MakeNewTextViewer[ [ parent: handle.outer, wx: ret.text2.wx + ret.text2.ww + entryHSpace, wy: handle.height+2, ww: valueEntryWidth, wh: entryHeight, data: " ", scrollable: FALSE, border: FALSE ] ]; handle.height _ handle.height + entryHeight + entryVSpace; ret.slider1 _ Sliders.Create[ info: [ parent: handle.outer, wx: button.wx + button.ww + entryHSpace, wy: handle.height, ww: sliderWidth, wh: sliderHeight ], foreground: color1, clientData: handle, sliderProc: sliderProc1 ]; ret.slider2 _ Sliders.Create[ info: [ parent: handle.outer, wx: ret.text1.wx + ret.text1.ww + entryHSpace, wy: handle.height, ww: sliderWidth, wh: sliderHeight ], foreground: color2, clientData: handle, sliderProc: sliderProc2 ]; ret.slider3 _ Sliders.Create[ info: [ parent: handle.outer, wx: ret.text2.wx + ret.text2.ww + entryHSpace, wy: handle.height, ww: sliderWidth, wh: sliderHeight ], foreground: color3, clientData: handle, sliderProc: sliderProc3 ]; handle.height _ handle.height + sliderHeight + entryVSpace; RETURN[ret]; }; <> 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 = 625; button: Buttons.Button; handle.height2 _ handle.height2 + entryVSpace; button _ Buttons.Create[ info: [ name: "CNS", wx: handle.rgb.blueText.wx + handle.rgb.blueText.ww + columnSpacing, wy: handle.height2, wh: entryHeight, parent: handle.outer, border: TRUE ], clientData: handle, proc: ComputeFromCNS ]; handle.cns.saturationChoices _ ChoiceButtons.BuildEnumTypeSelection[ viewer: handle.outer, x: button.wx + button.ww + entryHSpace, y: handle.height2, title: "Saturation:", buttonNames: CNSSaturations, default: "Vivid", borderOnButtons: FALSE, notifyClientProc: CNSSaturationUpdate, clientdata: handle, style: menuSelection, allInOneRow: TRUE ]; handle.height2 _ handle.height2 + entryHeight; handle.cns.lightnessChoices _ ChoiceButtons.BuildEnumTypeSelection[ viewer: handle.outer, x: button.wx + button.ww + entryHSpace, y: handle.height2, title: "Lightness:", buttonNames: CNSLightnesses, default: "Medium", borderOnButtons: FALSE, notifyClientProc: CNSLightnessUpdate, clientdata: handle, style: menuSelection, allInOneRow: TRUE ]; handle.height2 _ handle.height2 + entryHeight; handle.cns.hueChoices _ ChoiceButtons.BuildEnumTypeSelection[ viewer: handle.outer, x: button.wx + button.ww + entryHSpace, y: handle.height2, title: "Hue:", buttonNames: CNSHues, borderOnButtons: FALSE, notifyClientProc: CNSHueUpdate, clientdata: handle, style: menuSelection, allInOneRow: FALSE, maxWidth: maxWidth ]; handle.height2 _ handle.height2 + 8*entryHeight + entryVSpace; }; <> ColorPatchViewer: TYPE = RECORD [viewer: ViewerClasses.Viewer _ NIL]; ColorPatchData: TYPE = REF ColorPatchDataRec; ColorPatchDataRec: TYPE = RECORD [ index: CARDINAL _ 51, -- color map index hasWrongIndex: BOOLEAN _ TRUE, rgbR: REAL _ 1.0, rgbG: REAL _ 1.0, rgbB: REAL _ 0.0, hsvH: REAL _ 0.0, hsvS: REAL _ 0.0, hsvV: REAL _ 0.0, hslH: REAL _ 0.0, hslS: REAL _ 0.0, hslL: REAL _ 0.0, xCIE: REAL _ 0.0, yCIE: REAL _ 0.0, cslC: CNSColor.CSLChroma _ CNSColor.red, cslS: CNSColor.CSLSaturation _ CNSColor.vivid, cslL: CNSColor.CSLLightness _ CNSColor.medium ]; MakeColorPatch: PROCEDURE [handle: Handle] = { height: INTEGER = 50; width: INTEGER _ 400; instanceData: ColorPatchData _ NEW[ColorPatchDataRec]; handle.height2 _ handle.height2 + entryVSpace; handle.colorPatch.viewer _ ViewerOps.CreateViewer[ flavor: $ColorPatch, info: [ parent: handle.outer, wx: handle.rgb.blueText.wx + handle.rgb.blueText.ww + columnSpacing, wy: handle.height2, ww: width, wh: height, data: instanceData, scrollable: FALSE] ]; handle.height2 _ handle.height2 + height + entryVSpace; }; PaintColorPatch: ViewerClasses.PaintProc = { box: Graphics.Box _ Graphics.GetBounds[context]; patchData: ColorPatchData _ NARROW[self.data]; color: GraphicsBasic.Color _ GraphicsColor.RGBToColor[r: patchData.rgbR, g: patchData.rgbG, b: patchData.rgbB]; ColorMap.SetRGBColor[index: patchData.index, r: patchData.rgbR, g: patchData.rgbG, b: patchData.rgbB]; <> IF clear OR patchData.hasWrongIndex THEN { patchData.hasWrongIndex _ ColorMap.GetIndex[r: color.r, g: color.g, b: color.b] # patchData.index; Graphics.SetColor[context, color]; Graphics.DrawBox[context, box]; }; }; DestroyColorPatch: ViewerClasses.DestroyProc = {}; MakeOnOffButtons: PROCEDURE [handle: Handle] = { onButton, offButton: Buttons.Button; handle.height _ handle.height + entryVSpace; onButton _ Buttons.Create[ info: [ name: "Color On", wy: handle.height, wh: entryHeight, parent: handle.outer, border: TRUE ], clientData: handle, proc: TurnOnColorDisplay ]; offButton _ Buttons.Create[ info: [ name: "Color Off", wy: handle.height, wx: onButton.wx + onButton.ww + entryHSpace, wh: entryHeight, parent: handle.outer, border: TRUE ], clientData: handle, proc: TurnOffColorDisplay ]; handle.height _ handle.height + entryHeight + entryVSpace; }; TurnOnColorDisplay: Buttons.ButtonProc = { IF WindowManager.colorDisplayOn = FALSE THEN { sideToken: Rope.ROPE ~ UserProfile.Token["ColorDisplay.Side", "left"]; bppToken: Rope.ROPE ~ UserProfile.Token["ColorDisplay.BitsPerPoint", "8"]; WindowManager.StartColorViewers[ screenPos: SELECT TRUE FROM sideToken.Equal["left"] => left, sideToken.Equal["right"] => right, ENDCASE => left, bitsPerPixel: SELECT TRUE FROM bppToken.Equal["1"] => 1, bppToken.Equal["2"] => 2, bppToken.Equal["4"] => 4, bppToken.Equal["8"] => 8, bppToken.Equal["24"] => 24, ENDCASE => 8]; }; ColorMap.SetRGBColor[index: 1, r: 1.0, g: 1.0, b: 1.0] }; TurnOffColorDisplay: Buttons.ButtonProc = { WindowManager.StopColorViewers[]; }; ComputeFromRGB: Buttons.ButtonProc = { handle: Handle _ NARROW[clientData]; redValue: REAL _ GetColorValue[ViewerTools.GetContents[handle.rgb.redText]]; greenValue: REAL _ GetColorValue[ViewerTools.GetContents[handle.rgb.greenText]]; blueValue: REAL _ GetColorValue[ViewerTools.GetContents[handle.rgb.blueText]]; UpdateColorViewers[handle: handle, color: [rgb[redValue, greenValue, blueValue]]]; }; RedSliderUpdate: Sliders.SliderProc = { handle: Handle _ NARROW[clientData]; patchData: ColorPatchData _ NARROW[handle.colorPatch.viewer.data]; UpdateColorViewers[handle: handle, color: [rgb[value, patchData.rgbG, patchData.rgbB]]]; }; GreenSliderUpdate: Sliders.SliderProc = { handle: Handle _ NARROW[clientData]; patchData: ColorPatchData _ NARROW[handle.colorPatch.viewer.data]; UpdateColorViewers[handle: handle, color: [rgb[patchData.rgbR, value, patchData.rgbB]]]; }; BlueSliderUpdate: Sliders.SliderProc = { handle: Handle _ NARROW[clientData]; patchData: ColorPatchData _ NARROW[handle.colorPatch.viewer.data]; UpdateColorViewers[handle: handle, color: [rgb[patchData.rgbR, patchData.rgbG, value]]]; }; ComputeFromHSV: Buttons.ButtonProc = { handle: Handle _ NARROW[clientData]; hueContents: Rope.ROPE _ ViewerTools.GetContents[handle.hsv.hueText]; saturationContents: Rope.ROPE _ ViewerTools.GetContents[handle.hsv.saturationText]; valueContents: Rope.ROPE _ ViewerTools.GetContents[handle.hsv.valueText]; hueValue: REAL _ GetColorValue[hueContents]; saturationValue: REAL _ GetColorValue[saturationContents]; valueValue: REAL _ GetColorValue[valueContents]; UpdateColorViewers[handle: handle, color: [hsv[hueValue, saturationValue, valueValue]]]; }; HueSliderUpdate: Sliders.SliderProc = { handle: Handle _ NARROW[clientData]; patchData: ColorPatchData _ NARROW[handle.colorPatch.viewer.data]; UpdateColorViewers[handle: handle, color: [hsv[value, patchData.hsvS, patchData.hsvV]]]; }; SaturationSliderUpdate: Sliders.SliderProc = { handle: Handle _ NARROW[clientData]; patchData: ColorPatchData _ NARROW[handle.colorPatch.viewer.data]; UpdateColorViewers[handle: handle, color: [hsv[patchData.hsvH, value, patchData.hsvV]]]; }; ValueSliderUpdate: Sliders.SliderProc = { handle: Handle _ NARROW[clientData]; patchData: ColorPatchData _ NARROW[handle.colorPatch.viewer.data]; UpdateColorViewers[handle: handle, color: [hsv[patchData.hsvH, patchData.hsvS, value]]]; }; ComputeFromHSL: Buttons.ButtonProc = { handle: Handle _ NARROW[clientData]; hueContents: Rope.ROPE _ ViewerTools.GetContents[handle.hsl.hueText]; saturationContents: Rope.ROPE _ ViewerTools.GetContents[handle.hsl.saturationText]; lightnessContents: Rope.ROPE _ ViewerTools.GetContents[handle.hsl.lightnessText]; hueValue: REAL _ GetColorValue[hueContents]; saturationValue: REAL _ GetColorValue[saturationContents]; lightnessValue: REAL _ GetColorValue[lightnessContents]; UpdateColorViewers[handle: handle, color: [hsv[hueValue, saturationValue, lightnessValue]]]; }; HSLHueSliderUpdate: Sliders.SliderProc = { handle: Handle _ NARROW[clientData]; patchData: ColorPatchData _ NARROW[handle.colorPatch.viewer.data]; UpdateColorViewers[handle: handle, color: [hsl[value, patchData.hslS, patchData.hslL]]]; }; HSLSaturationSliderUpdate: Sliders.SliderProc = { handle: Handle _ NARROW[clientData]; patchData: ColorPatchData _ NARROW[handle.colorPatch.viewer.data]; UpdateColorViewers[handle: handle, color: [hsl[patchData.hslH, value, patchData.hslL]]]; }; HSLLightnessSliderUpdate: Sliders.SliderProc = { handle: Handle _ NARROW[clientData]; patchData: ColorPatchData _ NARROW[handle.colorPatch.viewer.data]; UpdateColorViewers[handle: handle, color: [hsl[patchData.hslH, patchData.hslS, value]]]; }; GetColorValue: PROCEDURE [rope: Rope.ROPE] RETURNS [value: REAL] = { 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]; }; 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.CNSToCSL[hue, saturation, lightness]; UpdateColorViewers[handle: handle, color: [csl[c, s, l]]]; }; <<>> CNSHueUpdate: ChoiceButtons.SelectionNotifierProc = { handle: Handle _ NARROW[clientdata]; patchData: ColorPatchData _ NARROW[handle.colorPatch.viewer.data]; hue, saturation, lightness: Rope.ROPE; c: CNSColor.CSLChroma; s: CNSColor.CSLSaturation; l: CNSColor.CSLLightness; [hue, saturation, lightness] _ CNSColor.CSLToCNS[patchData.cslC, patchData.cslS, patchData.cslL]; [c, s, l] _ CNSColor.CNSToCSL[ name, saturation, lightness]; UpdateColorViewers[handle: handle, color: [csl[c, s, l]]]; }; CNSSaturationUpdate: ChoiceButtons.SelectionNotifierProc = { handle: Handle _ NARROW[clientdata]; patchData: ColorPatchData _ NARROW[handle.colorPatch.viewer.data]; hue, saturation, lightness: Rope.ROPE; c: CNSColor.CSLChroma; s: CNSColor.CSLSaturation; l: CNSColor.CSLLightness; [hue, saturation, lightness] _ CNSColor.CSLToCNS[patchData.cslC, patchData.cslS, patchData.cslL]; [c, s, l] _ CNSColor.CNSToCSL[ hue, name, lightness]; UpdateColorViewers[handle: handle, color: [csl[c, s, l]]]; }; CNSLightnessUpdate: ChoiceButtons.SelectionNotifierProc = { handle: Handle _ NARROW[clientdata]; patchData: ColorPatchData _ NARROW[handle.colorPatch.viewer.data]; hue, saturation, lightness: Rope.ROPE; c: CNSColor.CSLChroma; s: CNSColor.CSLSaturation; l: CNSColor.CSLLightness; [hue, saturation, lightness] _ CNSColor.CSLToCNS[patchData.cslC, patchData.cslS, patchData.cslL]; [c, s, l] _ CNSColor.CNSToCSL[ hue, saturation, name]; UpdateColorViewers[handle: handle, color: [csl[c, s, l]]]; }; ColorSpecification: TYPE = RECORD [ color: SELECT type: ColorType FROM hsv => [h, s, v: REAL], hsl => [h, s, l: REAL], rgb => [r, g, b: REAL], cie => [x, y, Y: REAL], csl => [c: CNSColor.CSLChroma, s: CNSColor.CSLSaturation, l: CNSColor.CSLLightness], ENDCASE ]; ColorType: TYPE = {hsv, rgb, csl, hsl, 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.viewer.data]; h, s, v: REAL; r, g, b: REAL; hl,sl,l: REAL; chroma: CNSColor.CSLChroma; saturation: CNSColor.CSLSaturation; lightness: CNSColor.CSLLightness; cnsHue, cnsSaturation, cnsLightness: Rope.ROPE; WITH col: color SELECT FROM hsv => { h _ col.h; s _ col.s; v _ col.v; [r, g, b] _ CGColorWithCIE.HSVToRGB[h, s, v]; [hl, sl, l] _ CGColorWithCIE.RGBToHSL[r, g, b]; [chroma, saturation, lightness] _ CNSColor.HSLToCSL[hl, sl, l]; }; hsl => { hl _ col.h; sl _ col.s; l _ col.l; [r, g, b] _ CGColorWithCIE.HSLToRGB[hl, sl, l]; [h, s, v] _ CGColorWithCIE.RGBToHSV[r, g, b]; [chroma, saturation, lightness] _ CNSColor.HSLToCSL[hl, sl, l]; }; rgb => { r _ col.r; g _ col.g; b _ col.b; [h, s, v] _ CGColorWithCIE.RGBToHSV[ r, g, b ]; [hl, sl, l] _ CGColorWithCIE.RGBToHSL[r, g, b]; [chroma, saturation, lightness] _ CNSColor.HSLToCSL[hl, sl, l]; }; cie => { [r,g,b] _ CGColorWithCIE.CIEToRGB[col.x,col.y,col.Y]; r _ MIN[r,1]; g _ MIN[g,1]; b _ MIN[b,1]; --control precision problems r _ MAX[r,0]; g _ MAX[g,0]; b _ MAX[b,0]; [h, s, v] _ CGColorWithCIE.RGBToHSV[ r, g, b ]; [hl, sl, l] _ CGColorWithCIE.RGBToHSL[r, g, b]; [chroma, saturation, lightness] _ CNSColor.HSLToCSL[hl, sl, l]; }; csl => { chroma _ col.c; saturation _ col.s; lightness _ col.l; [hl, sl, l] _ CNSColor.CSLToHSL[ chroma, saturation, lightness]; [r, g, b] _ CGColorWithCIE.HSLToRGB[hl, sl, l]; [h, s, v] _ CGColorWithCIE.RGBToHSV[r, g, b]; } ENDCASE; patchData.rgbR _ UpdateColorValue[oldValue: patchData.rgbR, newValue: r, textViewer: handle.rgb.redText, sliderViewer: handle.rgb.redSlider]; patchData.rgbG _ UpdateColorValue[oldValue: patchData.rgbG, newValue: g, textViewer: handle.rgb.greenText, sliderViewer: handle.rgb.greenSlider]; patchData.rgbB _ UpdateColorValue[oldValue: patchData.rgbB, newValue: b, textViewer: handle.rgb.blueText, sliderViewer: handle.rgb.blueSlider]; patchData.hsvH _ UpdateColorValue[oldValue: patchData.hsvH, newValue: h, textViewer: handle.hsv.hueText, sliderViewer: handle.hsv.hueSlider]; patchData.hsvS _ UpdateColorValue[oldValue: patchData.hsvS, newValue: s, textViewer: handle.hsv.saturationText, sliderViewer: handle.hsv.saturationSlider]; patchData.hsvV _ UpdateColorValue[oldValue: patchData.hsvV, newValue: v, textViewer: handle.hsv.valueText, sliderViewer: handle.hsv.valueSlider]; patchData.hslH _ UpdateColorValue[oldValue: patchData.hslH, newValue: hl, textViewer: handle.hsl.hueText, sliderViewer: handle.hsl.hueSlider]; patchData.hslS _ UpdateColorValue[oldValue: patchData.hslS, newValue: sl, textViewer: handle.hsl.saturationText, sliderViewer: handle.hsl.saturationSlider]; patchData.hslL _ UpdateColorValue[oldValue: patchData.hslL, newValue: l, textViewer: handle.hsl.lightnessText, sliderViewer: handle.hsl.lightnessSlider]; [cnsHue, cnsSaturation, cnsLightness] _ CNSColor.CSLToCNS[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 color.type#cie THEN { x,y,Y: REAL; [x,y,Y] _ CGColorWithCIE.RGBToCIE[r,g,b]; CIEViewer.SetContents[handle.cie, x,y,Y]; }; ViewerOps.PaintViewer[viewer: handle.colorPatch.viewer, hint: client, clearClient: FALSE]; }; <<>> <> colorPatchClass: ViewerClasses.ViewerClass _ NEW[ViewerClasses.ViewerClassRec _ [paint: PaintColorPatch, destroy: DestroyColorPatch]]; <<>> <> Commander.Register[key: "ColorTool", proc: MakeColorTool, doc: "Create a color tool"]; <<>> <> ViewerOps.RegisterViewerClass[$ColorPatch, colorPatchClass]; END. <> <> <<>> <<>>