<<>> <> <> <> DIRECTORY Controls, ControlsPrivate, Convert, Imager, Real, Rope, ViewerClasses, ViewerOps, ViewerTools; ControlsFunctionImpl: CEDAR MONITOR IMPORTS ControlsPrivate, Convert, Imager, Real, Rope, ViewerOps, ViewerTools EXPORTS Controls, ControlsPrivate ~ BEGIN <> Context: TYPE ~ Imager.Context; Control: TYPE ~ Controls.Control; IntegerPair: TYPE ~ Controls.IntegerPair; IntegerSequence: TYPE ~ Controls.IntegerSequence; IntegerSequenceRep: TYPE ~ Controls.IntegerSequenceRep; Mouse: TYPE ~ Controls.Mouse; RealSequence: TYPE ~ Controls.RealSequence; RealSequenceRep: TYPE ~ Controls.RealSequenceRep; <> Function: TYPE ~ REF FunctionRep; FunctionRep: TYPE ~ RECORD [ vMul: REAL ¬ 0.0, -- (private) value multiplier screenValues: IntegerSequence ¬ NIL, -- (private) screen function values p0, p1: IntegerPair ¬ [] -- (private) mouse interpolates ]; <> NewFunction: PUBLIC PROC [control: Control] ~ { function: Function ¬ NEW[FunctionRep]; IF control.type # function THEN RETURN; control.functionRef ¬ function; IF control.values = NIL THEN { control.values ¬ NEW[RealSequenceRep[control.w]]; FOR i: NAT IN[0..control.values.length) DO control.values[i] ¬ control.min; ENDLOOP; }; IF control.values.length < CARD16[control.w] THEN { temp: RealSequence ¬ NEW[RealSequenceRep[control.w]]; FOR i: NAT IN[0..control.values.length) DO temp[i] ¬ control.values[i]; ENDLOOP; control.values ¬ temp; control.values.length ¬ control.w; }; function.screenValues ¬ NEW[IntegerSequenceRep[control.w]]; }; ResetFunction: PUBLIC PROC [control: Control, repaint: BOOL ¬ TRUE] ~ { IF control.type = function THEN { FOR i: NAT IN[0..control.values.length) DO control.values[i] ¬ control.min; ENDLOOP; IF repaint THEN RequestPaint[control, FALSE, NIL]; }; }; SetFunctionValues: PUBLIC PROC [ control: Control, values: RealSequence, repaint: BOOL ¬ TRUE] ~ { control.values ¬ values; IF repaint THEN RequestPaint[control, FALSE, NIL]; }; <<>> GetFunctionValues: PUBLIC PROC [control: Control] RETURNS [RealSequence] ~ { RETURN[control.values]; }; <> NotifyFunction: PUBLIC PROC [control: Control, mouse: Mouse] ~ { control.mouse ¬ mouse; SetInterpolates[control]; IF control.mouse.state # up THEN RequestPaint[control, FALSE, control] ELSE ControlsPrivate.MaybeForkControlProc[control]; }; SetInterpolates: PROC [control: Control] ~ { mouse: Mouse ¬ control.mouse; function: Function ¬ NARROW[control.functionRef]; SELECT mouse.state FROM down => SELECT mouse.button FROM left, middle => function.p0 ¬ function.p1 ¬ mouse.pos; right => { -- straight lines function.p0 ¬ function.p1; function.p1 ¬ mouse.pos; }; ENDCASE => NULL; held => SELECT mouse.button FROM left, middle => { -- freestyle function.p0 ¬ function.p1; function.p1 ¬ mouse.pos; }; right => -- straight lines function.p1 ¬ mouse.pos; ENDCASE => NULL; ENDCASE => NULL; }; <> RequestPaint: PROC [control: Control, clearClient: BOOL, whatChanged: REF ANY] ~ { ViewerOps.PaintViewer[control.viewer, client, clearClient, whatChanged]; IF control.status # NIL THEN { value: REAL ~ control.values[NARROW[control.functionRef, Function].p1.x]; ViewerTools.SetContents[control.status, Rope.Concat[Convert.FtoRope[value, 3], " "]]; ViewerOps.PaintViewer[control.status, client]; }; }; PaintFunction: PUBLIC ViewerClasses.PaintProc ~ { PaintUpdate: PROC ~ { UpdateOne: PROC [x: INTEGER, y, value: REAL] ~ { control.values[x] ¬ value; Imager.MaskRectangleI[context, x, function.screenValues[x] ¬ Real.Round[y], 1, 1]; }; y: REAL; x0, x1: INTEGER; IF function.p1.x > function.p0.x THEN {x0 ¬ function.p0.x; x1 ¬ function.p1.x; y ¬ function.p0.y} ELSE {x0 ¬ function.p1.x; x1 ¬ function.p0.x; y ¬ function.p1.y}; Imager.SetColor[context, Imager.white]; FOR x: NAT IN[x0..x1] DO Imager.MaskRectangleI[context, x, function.screenValues[x], 1, 1]; ENDLOOP; Imager.SetColor[context, Imager.black]; IF x0 = x1 THEN UpdateOne[x0, y, control.min+function.vMul*y] ELSE { value: REAL ¬ control.min+function.vMul*y; dy: REAL ¬ (function.p1.y-function.p0.y)/(function.p1.x-function.p0.x); dValue: REAL ¬ function.vMul*dy; FOR x: NAT IN[x0..x1] DO UpdateOne[x, y, value]; value ¬ value+dValue; y ¬ y+dy; ENDLOOP; }; }; PaintInit: PROC ~ { Imager.SetColor[context, Imager.black]; function.vMul ¬ IF self.wh = 0 THEN 1.0 ELSE (control.max-control.min)/(self.wh-3.0); FOR x: NAT IN[0..control.values.length) DO y: INTEGER ¬ function.screenValues[x] ¬ Real.Round[(control.values[x]-control.min)/function.vMul]; Imager.MaskRectangleI[context, x, y, 1, 1]; ENDLOOP; }; control: Control ¬ NARROW[self.data]; function: Function ¬ NARROW[control.functionRef]; IF function = NIL THEN RETURN; IF whatChanged = NIL THEN Imager.DoWithBuffer[context, PaintInit, 0, 0, self.ww, self.wh, Imager.white] ELSE PaintUpdate[]; }; END.