DIRECTORY CIETriangle, Imager USING [MaskVector, MaskStrokeTrajectory, black, white, SetColor, MaskRectangle], ImagerPath USING [MoveTo, LineTo, Trajectory], InputFocus USING [ReleaseButtons, CaptureButtons], LinearSystem USING [Matrix2, Column2, Solve2], Process USING [Milliseconds, SetTimeout, MsecToTicks, Detach], Rope USING [ROPE], TIPUser USING [TIPScreenCoords, InstantiateNewTIPTable], ViewerClasses USING [Viewer, ViewerClass, ViewerClassRec, ViewerRec, PaintProc, GetProc, SetProc, NotifyProc], ViewerOps USING [CreateViewer, PaintViewer, MouseInViewer, RegisterViewerClass, SetViewer], WindowManager USING [RestoreCursor]; CIETriangleImpl: CEDAR MONITOR IMPORTS Imager, ImagerPath, InputFocus, LinearSystem, Process, TIPUser, ViewerOps, WindowManager EXPORTS CIETriangle = BEGIN OPEN CIETriangle; SliderState: TYPE = {inputDisabled, inputEnabled}; Reason: TYPE = {move, set, abort}; Line: TYPE=RECORD[a,b,c: REAL, vx,vy: REAL]; Vector: TYPE=RECORD[x,y: REAL]; --vx and vy are the opposite vertex. used for PinToLine TriangleData: TYPE = REF TriangleDataRec; TriangleDataRec: TYPE = RECORD [ proc: CIETriangleProc, lineGR: Line, --in viewer coordinates lineRB: Line, lineBG: Line, path: ImagerPath.Trajectory, scale: REAL, --cie to viewer locX,locY: REAL, --value in viewer coordinates oldX, oldY: REAL _ 0, --old locX, old locY mouseX, mouseY: REAL _ 0, --position of mouse in triangle (mouse values) savedX, savedY: REAL _ 0, --postion for abort (mouse values) reason: Reason _ move, -- reason for calling client proc oldReason: Reason _ move, -- reason for calling client proc the last time state: SliderState _ inputDisabled -- indicates whether mouse input is being accepted ]; Create: PUBLIC PROCEDURE [info: ViewerClasses.ViewerRec _ [], xr,yr,xg,yg,xb,yb,xw,yw, scale: REAL, proc: CIETriangleProc] RETURNS [cieTriangle: Viewer] = { path: ImagerPath.Trajectory _ ImagerPath.MoveTo[[xr*scale,yr*scale]]; path _ ImagerPath.LineTo[path, [xg*scale,yg*scale]]; path _ ImagerPath.LineTo[path, [xb*scale,yb*scale]]; info.data _ NEW[TriangleDataRec _ [ proc: proc, scale: scale, lineGR: MakeLine[scale, xg,yg,xr-xg,yr-yg, xb,yb], lineRB: MakeLine[scale, xr,yr,xb-xr,yb-yr, xg,yg], lineBG: MakeLine[scale, xb,yb,xg-xb,yg-yb, xr,yr], locX: xw*scale, locY: yw*scale, path: path ]]; info.scrollable _ FALSE; info.border _ TRUE; cieTriangle_ ViewerOps.CreateViewer[flavor: $CIETriangle, info: info]; }; Reinitialize: PUBLIC PROCEDURE [cieTriangle: Viewer, xr,yr,xg,yg,xb,yb,xw,yw: REAL] = { data: TriangleData _ NARROW[cieTriangle.data]; data.path _ ImagerPath.MoveTo[[xr*data.scale,yr*data.scale]]; data.path _ ImagerPath.LineTo[data.path, [xg*data.scale,yg*data.scale]]; data.path _ ImagerPath.LineTo[data.path, [xb*data.scale,yb*data.scale]]; data.lineGR _ MakeLine[data.scale, xg,yg,xr-xg,yr-yg, xb,yb]; data.lineRB _ MakeLine[data.scale, xr,yr,xb-xr,yb-yr, xg,yg]; data.lineBG _ MakeLine[data.scale, xb,yb,xg-xb,yg-yb, xr,yr]; data.locX _ xw*data.scale; data.locY _ yw*data.scale; ViewerOps.PaintViewer[viewer: cieTriangle, hint: client, clearClient: TRUE, whatChanged: $newValue]; data.proc[self: cieTriangle, x: data.locX/data.scale, y: data.locY/data.scale]; }; GetContents: PUBLIC PROCEDURE [cieTriangle: Viewer] RETURNS [x,y: REAL] = { data: TriangleData _ NARROW[cieTriangle.data]; RETURN[x: data.locX/data.scale, y: data.locY/data.scale]; }; SetContents: PUBLIC PROCEDURE [cieTriangle: Viewer, x,y: REAL] = { data: REF Vector _ NEW[Vector _ [x:x, y:y]]; ViewerOps.SetViewer[viewer: cieTriangle, data: data]; }; MakeLine: PROCEDURE [scale: REAL, fromX,fromY,dx,dy, vx, vy: REAL _ 0] RETURNS [Line] = { line: Line _ [a: -dy, b: dx, c: 0, vx: scale* vx, vy: scale*vy]; line.c _ -scale*(line.a*fromX+line.b*fromY); RETURN[line]; }; debug: BOOLEAN _ TRUE; CIETrianglePaint: PRIVATE ViewerClasses.PaintProc = { w: REAL = 2; data: TriangleData _ NARROW[self.data]; drawBox: PROC [cx,cy: REAL] = { Imager.MaskRectangle[context, [cx-w, cy-w, 2*w, 2*w]]; }; SELECT whatChanged FROM $newValue => { Imager.SetColor[context, Imager.white]; drawBox[cx: data.oldX, cy: data.oldY]; }; ENDCASE => NULL; Imager.SetColor[context, Imager.black]; Imager.MaskRectangle[context, [data.locX-w, data.locY-w, 2*w, 2*w]]; Imager.MaskStrokeTrajectory[context: context, trajectory: data.path, closed: TRUE]; IF debug THEN { DrawLine: PROC [line: Line] = { Imager.MaskVector[context, [0, -line.c/line.b], [data.scale, -(line.c+data.scale*line.a)/line.b]]; }; DrawLine[data.lineGR]; DrawLine[data.lineRB]; DrawLine[data.lineBG]; }; data.oldX _ data.locX; data.oldY _ data.locY; }; CIETriangleGet: PRIVATE ViewerClasses.GetProc = { triangleData: TriangleData _ NARROW[self.data]; vec: REF Vector _ NEW[Vector _ [x: triangleData.locX/triangleData.scale, y: triangleData.locY/triangleData.scale]]; RETURN[vec]; }; CIETriangleSet: PRIVATE ENTRY ViewerClasses.SetProc = { vec: REF Vector _ NARROW[data]; triangleData: TriangleData _ NARROW[self.data]; SetTriangleValue[triangleData, vec.x*triangleData.scale, vec.y*triangleData.scale]; ViewerOps.PaintViewer[viewer: self, hint: client, clearClient: FALSE, whatChanged: $newValue]; }; SetTriangleValue: PRIVATE PROCEDURE [data: TriangleData, x,y: REAL] = { flag: CARDINAL _ 0; Left: PROC[line: Line] RETURNS [BOOLEAN] = { value: REAL _ line.a*x+line.b*y+line.c; RETURN[value >= 0]; }; PinToLine: PROC[line: Line] = TRUSTED { pline: Line _ MakeLine[scale: 1, fromX: x, fromY: y, dx: line.vx-x, dy: line.vy-y]; matrix: LinearSystem.Matrix2 _ [[line.a,line.b],[pline.a,pline.b]]; column: LinearSystem.Column2 _ [-line.c,-pline.c]; point: LinearSystem.Column2 _LinearSystem.Solve2[matrix,column]; data.locX _ point[1]; data.locY _ point[2]; }; PinToVertex: PROC[line1,line2: Line] = TRUSTED { matrix: LinearSystem.Matrix2 _ [[line1.a,line1.b],[line2.a,line2.b]]; column: LinearSystem.Column2 _ [-line1.c,-line2.c]; point: LinearSystem.Column2 _LinearSystem.Solve2[matrix,column]; data.locX _ point[1]; data.locY _ point[2]; }; IF Left[data.lineGR] THEN flag _ 1; IF Left[data.lineRB] THEN flag _ flag+2; IF Left[data.lineBG] THEN flag _ flag+4; SELECT flag FROM 0 => {data.locX _ x; data.locY _ y}; 1 => PinToLine[data.lineGR]; 2 => PinToLine[data.lineRB]; 4 => PinToLine[data.lineBG]; 3 => PinToVertex[data.lineGR, data.lineRB]; 6 => PinToVertex[data.lineRB, data.lineBG]; 5 => PinToVertex[data.lineBG, data.lineGR]; ENDCASE => ERROR; }; CIETriangleNotify: PRIVATE ViewerClasses.NotifyProc = TRUSTED { ENABLE UNWIND => { InputFocus.ReleaseButtons[]; WindowManager.RestoreCursor[] }; v: ViewerClasses.Viewer; c: BOOLEAN; mouse: TIPUser.TIPScreenCoords; mouseX, mouseY: REAL; data: TriangleData _ NARROW[self.data]; FOR list: LIST OF REF ANY _ input, list.rest UNTIL list = NIL DO WITH list.first SELECT FROM x: ATOM => SELECT x FROM $Enable => { InputFocus.CaptureButtons[proc: CIETriangleNotify, tip: cieTriangleClass.tipTable, viewer: self]; data.state _ inputEnabled; data.savedX _ data.locX; data.savedY _ data.locX; mouseX _ mouse.mouseX; mouseY _ mouse.mouseY; SetNewTriangleValue[data: data, mouseX: mouseX, mouseY: mouseY, reason: move]; CreateSliderProcess[self]; }; $Abort => { InputFocus.ReleaseButtons[]; data.state _ inputDisabled; SetNewTriangleValue[data: data, mouseX: data.savedX, mouseY: data.savedY, reason: abort]; sliderProcessData.state _ dying; }; $Move => IF data.state = inputEnabled THEN { [v, c] _ ViewerOps.MouseInViewer[mouse]; IF v = self AND c THEN {mouseX _ mouse.mouseX; mouseY _ mouse.mouseY} ELSE {mouseX _ data.mouseX; mouseY _ data.mouseY}; SetNewTriangleValue[data: data, mouseX: mouseX, mouseY: mouseY, reason: move]; }; $Set => IF data.state = inputEnabled THEN { reason: Reason _ set; InputFocus.ReleaseButtons[]; data.state _ inputDisabled; [v, c] _ ViewerOps.MouseInViewer[mouse]; IF v = self AND c THEN {mouseX _ mouse.mouseX; mouseY _ mouse.mouseY} ELSE { mouseX _ data.savedX; mouseY _ data.savedY; reason _ abort; }; SetNewTriangleValue[data: data, mouseX: mouseX, mouseY: mouseY, reason: reason]; sliderProcessData.state _ dying; }; ENDCASE => NULL; z: TIPUser.TIPScreenCoords => mouse _ z; ENDCASE => ERROR; ENDLOOP; }; SetNewTriangleValue: PRIVATE ENTRY PROCEDURE [data: TriangleData, mouseX,mouseY: REAL, reason: Reason] = { data.mouseX _ mouseX; data.mouseY _ mouseY; data.reason _ reason; }; SliderProcessData: TYPE = REF SliderProcessDataRec; SliderProcessDataRec: TYPE = RECORD [ triangle: Viewer _ NIL, timerCondition: CONDITION, state: SliderProcessState _ alive ]; SliderProcessState: TYPE = {alive, dying}; sliderProcessData: SliderProcessData _ NIL; CreateSliderProcess: PRIVATE PROCEDURE [triangle: Viewer] = { sliderProcessData _ NEW[SliderProcessDataRec]; sliderProcessData.triangle _ triangle; TRUSTED {Process.Detach[FORK SliderProcess[sliderProcessData]]}; }; SliderProcess: PRIVATE PROCEDURE [myData: SliderProcessData] = { UpdateSliderValue: PROCEDURE [self: Viewer] = { data: TriangleData _ NARROW[self.data]; notifyClient: BOOLEAN; reason: Reason; x,y: REAL; [notifyClient, reason, x,y] _ UpdateSlider[self]; IF data.proc # NIL AND notifyClient THEN data.proc[self: self, x: x/data.scale, y: y/data.scale]; }; timerPeriod: Process.Milliseconds = 40; WHILE myData.state = alive DO UpdateSliderValue[myData.triangle]; Wait[myData, timerPeriod]; ENDLOOP; UpdateSliderValue[myData.triangle]; }; UpdateSlider: PRIVATE ENTRY PROCEDURE [self: Viewer] RETURNS [notifyClient: BOOLEAN, reason: Reason, x,y: REAL] = { data: TriangleData _ NARROW[self.data]; notifyClient _ FALSE; IF data.mouseX # data.oldX OR data.mouseY # data.oldY THEN { notifyClient _ TRUE; SetTriangleValue[data, data.mouseX, data.mouseY]; IF data.locX # data.oldX OR data.locY # data.oldY THEN { notifyClient _ TRUE; ViewerOps.PaintViewer[viewer: self, hint: client, clearClient: FALSE, whatChanged: $newValue]; }; }; IF data.reason # data.oldReason THEN notifyClient _ TRUE; data.oldReason _ data.reason; RETURN[notifyClient, data.reason, data.locX, data.locY]; }; Wait: PRIVATE ENTRY PROCEDURE [data: SliderProcessData, milliseconds: Process.Milliseconds] = { TRUSTED {Process.SetTimeout[@data.timerCondition, Process.MsecToTicks[milliseconds]]}; WAIT data.timerCondition; }; cieTriangleClass: ViewerClasses.ViewerClass _ NEW[ViewerClasses.ViewerClassRec _ [ paint: CIETrianglePaint, get: CIETriangleGet, set: CIETriangleSet, notify: CIETriangleNotify, tipTable: TIPUser.InstantiateNewTIPTable["Slider.tip"], cursor: crossHairsCircle ]]; ViewerOps.RegisterViewerClass[$CIETriangle, cieTriangleClass]; END. ŒCIETriangleImpl.mesa Written by Maureen Stone on August 15, 1985 11:07:00 pm PDT Last Edited by: Beach, February 13, 1984 1:45:05 pm PST reinitializes the phosphor chromaticities returns a value in 0..1 takes a value in 0..1 --Utilities ViewerProcs x,y are viewer coordinates process for smooth interaction Initialization Register the CIETriangle class of viewer with the Window Manager Κ ]˜šœ™Jšœ;™;J™7J™—šΟk ˜ Jšœ ˜ JšœœK˜WJšœ œ˜.Jšœ œ"˜2Jšœ œ˜.Jšœœ1˜>Jšœœœ˜Jšœœ+˜8Jšœœ[˜nJšœ œL˜[Jšœœ˜$J˜—šœ œ˜JšœY˜`Jšœ˜—J˜Jšœœ ˜Jšœ œ!˜2Jšœœ˜"Jš œœœœ œ˜,JšœœœœΟc8˜YJšœœœ˜)šœœœ˜ Jšœ˜Jšœž˜%J˜ J˜ J˜Jšœœž˜Jšœ œž˜/Jšœ œž˜*Jšœœž.˜IJšœœž"˜=Jšœž!˜˜>Jšœ˜J˜—…—(ώ4η