<> <> <> DIRECTORY Buttons USING [Button, ButtonProc, Create], ChoiceButtons USING [BuildEnumTypeSelection, ButtonList, EnumTypeRef,SelectionNotifierProc], CIEViewer, CIETriangle, CGColorWithCIE, Containers USING [Container, Create], Convert USING [RealFromRope], IO USING [PutFR, real], MessageWindow USING [Append], Rope USING [ROPE, Equal], Sliders, VFonts USING[CharWidth], ViewerClasses USING [Viewer, ViewerRec], ViewerOps USING [PaintViewer], ViewerTools USING [MakeNewTextViewer, GetContents, SetContents, InhibitUserEdits]; CIEViewerImpl: CEDAR PROGRAM IMPORTS Buttons, ChoiceButtons, CIETriangle, Containers, Convert, CGColorWithCIE, IO, MessageWindow, Rope, Sliders, VFonts, ViewerOps, ViewerTools EXPORTS CIEViewer = BEGIN OPEN CIEViewer; CIEInit: TYPE=RECORD[xr,yr,xg,yg, xb,yb, whiteY: REAL]; globalData: CIEData; --hack. Need to hang this on the viewer somehow CIEData: TYPE = REF CIEDataRec; CIEDataRec: TYPE = RECORD [ cieInit: CIEInit, triangle: CIETriangle.Viewer _ NIL, --the triangular slider, which is a viewer slider: Sliders.Slider, --for Y xText: ViewerClasses.Viewer _ NIL, yText: ViewerClasses.Viewer _ NIL, YText: ViewerClasses.Viewer _ NIL, zText: ViewerClasses.Viewer _ NIL, x,y,Y: REAL, --values for viewers. Cache for compare that prevents flicker initChoices: ChoiceButtons.EnumTypeRef _ NIL, YTrack: ChoiceButtons.EnumTypeRef _ NIL, yTrack: BOOLEAN, --cached value of YTrack. Controls behavior of Y slider maxY: REAL, --maximum luminance for this hue proc: CIEProc, clientData: REF ANY _ NIL ]; CIEInitChoices: ChoiceButtons.ButtonList _ LIST["Long", "Normal"]; YTrackChoices: ChoiceButtons.ButtonList _ LIST["Track Y", "Clip Y"]; ComputeFromCIE: Buttons.ButtonProc = { data: CIEData _ NARROW[clientData]; xValue: REAL _ GetColorValue[ViewerTools.GetContents[data.xText]]; yValue: REAL _ GetColorValue[ViewerTools.GetContents[data.yText]]; YValue: REAL _ GetColorValue[ViewerTools.GetContents[data.YText]]; UpdateXY[data,xValue,yValue]; UpdateY[data, YValue]; data.proc[self: data.slider.parent, x: xValue, y: yValue, Y: YValue, clientData: data.clientData]; }; CIEInitUpdate: ChoiceButtons.SelectionNotifierProc = { data: CIEData _ NARROW[clientdata]; x,y, Y: REAL; xr,yr,xg,yg,xb,yb: REAL; IF Rope.Equal[name,"Long"] THEN { CGColorWithCIE.InitDefaultCIE[long]; [xr: xr, yr: yr, xg: xg, yg: yg, xb: xb, yb: yb] _ CGColorWithCIE.GetDefaultValues[long]; }; IF Rope.Equal[name,"Normal"] THEN { CGColorWithCIE.InitDefaultCIE[normal]; [xr: xr, yr: yr, xg: xg, yg: yg, xb: xb, yb: yb] _ CGColorWithCIE.GetDefaultValues[normal]; }; [x,y,Y] _ CGColorWithCIE.RGBToCIE[r: 1, g: 1, b: 1]; --set it to white CIETriangle.Reinitialize[cieTriangle: data.triangle, xr: xr, yr: yr, xg: xg, yg: yg, xb: xb, yb: yb, xw: x, yw: y]; UpdateXY[data, x,y]; UpdateY[data,Y]; }; YTrackUpdate: ChoiceButtons.SelectionNotifierProc = { data: CIEData _ NARROW[clientdata]; IF Rope.Equal[name,"Track Y"] THEN { data.yTrack _ TRUE; UpdateY[data,data.maxY]; }; IF Rope.Equal[name,"Clip Y"] THEN data.yTrack _ FALSE; }; <> Create: PUBLIC PROCEDURE [info: ViewerClasses.ViewerRec _ [], sliderWidth, sliderHeight: CARDINAL, proc: CIEProc, clientData: REF ANY _ NIL, maxY, initR, initG, initB: REAL _ 1] RETURNS [cieViewer: Viewer] = { button: Buttons.Button; leftX: CARDINAL _ 0; topY: CARDINAL _ 0; xInit,yInit,YInit: REAL; entryHSpace: CARDINAL _ 10; entryVSpace: CARDINAL _ 8; xr,yr,xg,yg,xb,yb,xw,yw,Yw: REAL; triangleSize: CARDINAL _ sliderHeight; entryHeight: CARDINAL = 15; valueEntryWidth: CARDINAL = 8 * VFonts.CharWidth['0]; -- eight digits worth of width data: CIEData; CGColorWithCIE.InitDefaultCIE[long]; [xr: xr, yr: yr, xg: xg, yg: yg, xb: xb, yb: yb] _ CGColorWithCIE.GetDefaultValues[long]; [xInit,yInit,YInit] _ CGColorWithCIE.RGBToCIE[initR,initG,initB]; data_ NEW[CIEDataRec _ [ clientData: clientData, proc: proc, cieInit: [xr: xr, yr: yr, xg: xg, yg: yg, xb: xb, yb: yb, whiteY: Yw], yTrack: TRUE, x: xInit, y: yInit, Y: YInit, maxY: maxY]]; cieViewer _ Containers.Create[info: info]; button _ Buttons.Create[ info: [ name: "CIE", wx: leftX, wy: topY, wh: entryHeight, parent: cieViewer, border: TRUE ], clientData: data, proc: ComputeFromCIE ]; leftX _ button.wx + button.ww + entryHSpace; data.YText _ ViewerTools.MakeNewTextViewer[ [ parent: cieViewer, wx: leftX, wy: topY, ww: valueEntryWidth, wh: entryHeight, data: Format[YInit], scrollable: FALSE, border: FALSE ] ]; data.slider _ Sliders.Create[ info: [ parent: cieViewer, wx: leftX, wy: topY + entryHeight + entryVSpace, ww: sliderWidth, wh: sliderHeight ], clientData: data, sliderProc: CIESetY, filterProc: CIEFilterY ]; leftX _ leftX+valueEntryWidth+2*entryHSpace; data.triangle _ CIETriangle.Create[ info: [ parent: cieViewer, wx: leftX, wy: topY + entryHeight + entryVSpace, ww: triangleSize, wh: triangleSize, border: TRUE ], xr: xr, yr: yr, xg: xg, yg: yg, xb: xb, yb: yb, xw: xw, yw: yw, scale: triangleSize, proc: NewXY ]; data.xText _ ViewerTools.MakeNewTextViewer[ [ parent: cieViewer, wx: leftX, wy: topY, ww: valueEntryWidth, wh: entryHeight, data: Format[xInit], scrollable: FALSE, border: FALSE ] ]; leftX _ leftX+valueEntryWidth+entryHSpace; data.yText _ ViewerTools.MakeNewTextViewer[ [ parent: cieViewer, wx: leftX, wy: topY, ww: valueEntryWidth, wh: entryHeight, data: Format[yInit], scrollable: FALSE, border: FALSE ] ]; leftX _ leftX+valueEntryWidth+entryHSpace; data.zText _ ViewerTools.MakeNewTextViewer[ [ parent: cieViewer, wx: leftX, wy: topY, ww: valueEntryWidth, wh: entryHeight, data: Format[1-(xInit+yInit)], scrollable: FALSE, border: FALSE ] ]; ViewerTools.InhibitUserEdits[data.zText]; topY _ topY+2*entryHeight+sliderHeight+entryHSpace; data.initChoices _ ChoiceButtons.BuildEnumTypeSelection[ viewer: cieViewer, x: button.wx + button.ww + entryHSpace, y: topY, title: "Init:", buttonNames: CIEInitChoices, default: "Long", borderOnButtons: TRUE, notifyClientProc: CIEInitUpdate, clientdata: data, style: menuSelection, allInOneRow: TRUE ]; data.initChoices _ ChoiceButtons.BuildEnumTypeSelection[ viewer: cieViewer, x: data.initChoices.nextx+entryHSpace, y: topY, title: "Y Tracking:", buttonNames: YTrackChoices, default: "Track Y", borderOnButtons: TRUE, notifyClientProc: YTrackUpdate, clientdata: data, style: menuSelection, allInOneRow: TRUE ]; globalData _ data; --temporary hack }; GetContents: PUBLIC PROCEDURE [cieViewer: Viewer] RETURNS [x,y, Y: REAL] = { data: CIEData _ globalData; [x,y] _ CIETriangle.GetContents[data.triangle]; Y _ Sliders.GetContents[data.slider]; }; SetContents: PUBLIC PROCEDURE [cieViewer: Viewer, x,y,Y: REAL] = { data: CIEData _ globalData; IF NOT (x=0 AND y=0) AND NOT (x=data.x AND y=data.y) THEN UpdateXY[data,x,y]; IF Y#data.Y THEN UpdateY[data,Y]; }; Reinitialize: PUBLIC PROCEDURE [cieViewer: Viewer, xr,yr,xg,yg,xb,yb,whiteY: REAL] = { <> cieData: CIEData _ globalData; xw,yw,Yw: REAL; cieData.cieInit _ [xr: xr, yr: yr, xg: xg, yg: yg, xb: xb, yb: yb, whiteY: whiteY]; CGColorWithCIE.InitCIE[xr: xr, yr: yr, xg: xg, yg: yg, xb: xb, yb: yb, whiteY: whiteY]; [xw,yw,Yw] _ CGColorWithCIE.RGBToCIE[1,1,1]; CIETriangle.Reinitialize[cieTriangle: cieData.triangle, xr: xr, yr: yr, xg: xg, yg: yg, xb: xb, yb: yb, xw: xw, yw: yw]; UpdateY[cieData, Yw]; ViewerOps.PaintViewer[viewer: cieViewer, hint: all]; cieData.proc[self: cieViewer, x: xw, y: yw, Y: Yw, clientData: cieData.clientData]; }; UpdateY: PROC[cieData: CIEData, Y: REAL] = { Y _ CIEFilterY[value: Y, clientData: cieData]; --should the slider do this automatically? Sliders.SetContents[slider: cieData.slider, contents: Y]; Y _ Sliders.GetContents[slider: cieData.slider]; --may be clipped by filter proc ViewerTools.SetContents[viewer: cieData.YText, contents: Format[Y]]; }; UpdateXY: PROC [cieData: CIEData, x,y: REAL] = { CIETriangle.SetContents[cieTriangle: cieData.triangle, x: x, y: y]; [x,y] _ CIETriangle.GetContents[cieData.triangle]; --may be clipped to triangle cieData.maxY _ CGColorWithCIE.GetMaxY[x,y]; UpdateXYText[cieData, x, y]; }; UpdateXYText: PROC [cieData: CIEData, x,y: REAL] = { ViewerTools.SetContents[viewer: cieData.xText, contents: Format[x]]; ViewerTools.SetContents[viewer: cieData.yText, contents: Format[y]]; ViewerTools.SetContents[viewer: cieData.zText, contents: Format[1-(x+y)]]; }; Format: PROC[v: REAL] RETURNS [Rope.ROPE] = { RETURN[IO.PutFR["%-5f", IO.real[v]]]; }; NewXY: CIETriangle.CIETriangleProc = { cieData: CIEData _ globalData; maxY: REAL; IF x=cieData.x AND y=cieData.y THEN RETURN; maxY _ CGColorWithCIE.GetMaxY[x,y]; IF cieData.yTrack OR maxY < Sliders.GetContents[cieData.slider] THEN UpdateY[cieData,maxY]; UpdateXYText[cieData,x,y]; cieData.maxY _ maxY; cieData.x _ x; cieData.y _ y; cieData.proc[self: NARROW[self.parent], x: x, y: y, Y: Sliders.GetContents[cieData.slider], clientData: cieData.clientData]; }; CIESetY: Sliders.SliderProc = { data: CIEData _ NARROW[clientData]; x,y: REAL; IF value=data.Y THEN RETURN; data.Y _ value; ViewerTools.SetContents[viewer: data.YText, contents: Format[value]]; [x,y] _ CIETriangle.GetContents[data.triangle]; data.proc[x: x, y: y, Y: value, self: slider.parent, clientData: data.clientData]; }; CIEFilterY: Sliders.FilterProc = { data: CIEData _ NARROW[clientData]; RETURN[MIN[value,data.maxY]]; }; 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]; }; END.