<> <> <> DIRECTORY Buttons USING [Button, ButtonProc, Create], Commander USING [CommandProc, Register], Containers USING [Container, Create], Imager USING [Context, Color, ScaleT, MaskFillTrajectory, MaskBox, SetColor], ImagerColor USING [ColorFromRGB, RGB, HSV, RGBFromHSV], ImagerPath USING[LineTo, MoveTo, Trajectory], IO USING [GetReal, RIS], Menus USING[CreateMenu, AppendMenuEntry, CreateEntry, Menu, ClickProc], Real USING [Float, Round], RealFns USING [AlmostZero], Rope USING [ROPE], Sweep, TIPUser USING [TIPScreenCoords, InstantiateNewTIPTable], Vector2 USING [VEC], ViewerClasses USING [ViewerClass, ViewerClassRec, Viewer, PaintProc, NotifyProc], ViewerOps USING [CreateViewer, RegisterViewerClass, PaintViewer], ViewerTools USING [MakeNewTextViewer, SetSelection, SetContents, GetContents]; ColorSweepImpl: CEDAR MONITOR LOCKS my USING my: State IMPORTS Buttons, Containers, Commander, IO, Imager, ImagerPath, ImagerColor, Menus, Real, RealFns, Sweep, TIPUser, ViewerOps, ViewerTools = BEGIN OPEN Sweep; AddRGB: PROC [a, b: ImagerColor.RGB] RETURNS [ImagerColor.RGB] ~ INLINE { RETURN[[a.R + b.R, a.G + b.G, a.B + b.B]] }; NegRGB: PROC [a: ImagerColor.RGB] RETURNS [ImagerColor.RGB] ~ INLINE { RETURN[[- a.R, -a.G, -a.B]]; }; MulRGB: PROC [a: ImagerColor.RGB, s: REAL] RETURNS [ImagerColor.RGB] ~ INLINE { RETURN[[s * a.R, s * a.G, s * a.B]]; }; ZeroTest: PROC [a: ImagerColor.RGB] RETURNS [BOOLEAN] ~ INLINE { OPEN RealFns; RETURN[AlmostZero[a.R, -8] AND AlmostZero[a.G, -8] AND AlmostZero[a.B, -8]]; }; Scale: INT _ 10; State: TYPE = REF StateRec; StateRec: TYPE = MONITORED RECORD [ outer: Containers.Container _ NIL, menu: Menus.Menu, blendInput, palette, inner: ViewerClasses.Viewer, mouse: TIPUser.TIPScreenCoords, input: Graph _ NewGraph[], blend: REAL, newColor: ImagerColor.RGB _ [0.0, 0.0, 0.0], preMulNewColor: ImagerColor.RGB, output: Graph]; Change: TYPE = REF ChangeRec; ChangeRec: TYPE = RECORD[doc: Rope.ROPE]; allFlag: Change = NIL; sampleColor: Change = NEW[ChangeRec _ ["Paint Sample Square"]]; ShowColor: Commander.CommandProc = { my: State _ NEW[StateRec]; blendButton: Buttons.Button; my.menu _ Menus.CreateMenu[]; my.menu.AppendMenuEntry[Menus.CreateEntry["Clear", ClearProc, my]]; my.menu.AppendMenuEntry[Menus.CreateEntry["Blend", BlendProc, my]]; my.outer _ Containers.Create[[ name: "Color", menu: my.menu, scrollable: FALSE]]; blendButton _ Buttons.Create[ info: [name: "Enter Blend Factor:", wx: 10, wy: 10, wh: 15, parent: my.outer, border: FALSE], proc: ForceBlend, clientData: my]; my.blendInput _ ViewerTools.MakeNewTextViewer[[ parent: my.outer, wx: blendButton.wx + blendButton.ww + 20, wy: 10, wh: 15, ww: 500, scrollable: FALSE, border: FALSE]]; ViewerTools.SetContents[my.blendInput, "1.0"]; my.palette _ ViewerOps.CreateViewer[ flavor: $Palette, info: [wx:5, wy: 30, wh: 400, ww: 50, parent: my.outer, data: my]]; my.inner _ ViewerOps.CreateViewer[ flavor: $ShowColor, info: [wx: 60, wy: 30, wh: 400, ww: 575, parent: my.outer, data: my]]; ViewerOps.PaintViewer[viewer: my.inner, hint: all]; ViewerOps.PaintViewer[viewer: my.palette, hint: all]; }; ForceBlend: Buttons.ButtonProc -- [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE] -- = { my: State _ NARROW[clientData]; ViewerTools.SetSelection[my.blendInput]; }; ClearProc: Menus.ClickProc = { my: State _ NARROW[clientData]; ClearLocked[my]; ViewerOps.PaintViewer[viewer: my.inner, hint: all]; }; ClearLocked: ENTRY PROC[my: State] ~ { ENABLE UNWIND => NULL; DestroyGraph[my.input]; my.input _ NewGraph[]; DestroyGraph[my.output]; my.output _ NIL; }; BlendProc: Menus.ClickProc = { my: State _ NARROW[clientData]; BlendLocked[my]; ViewerOps.PaintViewer[viewer: my.inner, hint: all, whatChanged: allFlag]; }; ColorLine: TYPE = REF ColorLineRec; ColorLineRec: TYPE = RECORD [ deltaColor: ImagerColor.RGB _ [0.0, 0.0, 0.0], deltaWind: INTEGER _ 0 ]; windPlusOne: ColorLine = NEW[ColorLineRec _ [deltaWind: 1]]; ColorRegion: TYPE = REF ColorRegionRec; ColorRegionRec: TYPE = RECORD[ oldColor: ImagerColor.RGB _ [1.0, 1.0, 1.0], newColor: ImagerColor.RGB _ [1.0, 1.0, 1.0], wind: INTEGER]; colorInfinityRegion: ColorRegion = NEW[ColorRegionRec _ [oldColor: [1.0, 1.0, 1.0], wind: 0]]; ColorCopy: CopyLineProc = { sI: ColorLine _ NARROW[stateIn]; RETURN[NEW[ColorLineRec _ [deltaColor: sI.deltaColor, deltaWind: sI.deltaWind]]]; }; ColorCombine: CombineLineProc = { s1: ColorLine _ NARROW[state1]; s2: ColorLine _ NARROW[state2]; sO: ColorLine _ NEW[ColorLineRec _ [ deltaColor: AddRGB[s1.deltaColor, s2.deltaColor], deltaWind: s1.deltaWind + s2.deltaWind]]; RETURN[sO]; }; ColorFlip: FlipLineProc = { sI: ColorLine _ NARROW[stateIn]; sO: ColorLine _ NEW[ColorLineRec _ [ deltaColor: NegRGB[sI.deltaColor], deltaWind: - sI.deltaWind]]; RETURN[sO]; }; BlendLocked: ENTRY PROC[my: State] ~ { ENABLE UNWIND => NULL; ColorStart: StartRegionProc = { rP: ColorRegion _ NARROW[regionPrevious]; lR: ColorLine _ NARROW[lineRight.state]; rC: ColorRegion _ NEW[ColorRegionRec _ [oldColor: AddRGB[rP.oldColor, lR.deltaColor], wind: rP.wind + lR.deltaWind]]; rC.newColor _ IF rC.wind = 0 THEN rC.oldColor ELSE AddRGB[MulRGB[rC.oldColor, my.blend], my.preMulNewColor]; FixLineState[rC, lR, rP]; RETURN[rC]; }; ColorStop: StopRegionProc = { rC:ColorRegion _ NARROW[regionCenter]; IF ZeroTest[NARROW[lineLeft.state, ColorLine].deltaColor] THEN RemoveLineFromEndPoints[lineLeft]; }; ColorSplit: SplitRegionProc = { FixLineState[NARROW[regionRight], NARROW[lineLeft.state], NARROW[regionPrevious]]; RETURN[regionRight]; }; ColorMerge: MergeRegionProc = { rR: ColorRegion _ NARROW[regionRight]; IF ZeroTest[NARROW[lineRight.state, ColorLine].deltaColor] THEN RemoveLineFromEndPoints[lineRight]; RETURN[regionRight]; }; ColorLineChange: LineChangeRegionProc = { rC: ColorRegion _ NARROW[regionCenter]; IF side = left THEN { IF ZeroTest[NARROW[lineOld.state, ColorLine].deltaColor] THEN RemoveLineFromEndPoints[lineOld]; } ELSE FixLineState[rC, NARROW[lineNew.state], NARROW[regionPrevious]]; }; FixLineState: PROC [regionLeft: ColorRegion, lineCenter: ColorLine, regionRight: ColorRegion] ~ { lineCenter.deltaWind _ 0; lineCenter.deltaColor _ AddRGB[regionLeft.newColor, NegRGB[regionRight.newColor]]; }; DestroyGraph[my.output]; my.output _ Intersect[my.input, ColorCopy, ColorCombine, ColorFlip]; my.blend _ 1.0 - RealFromRope[ViewerTools.GetContents[my.blendInput]]; IF my.blend > 1.0 THEN my.blend _ 1.0; IF my.blend < 0.0 THEN my.blend _ 0.0; my.preMulNewColor _ MulRGB[my.newColor, (1.0 - my.blend)]; my.output _ Sweep[my.output, colorInfinityRegion, ColorStart, ColorStop, ColorSplit, ColorMerge, ColorLineChange]; my.output _ StraightenLines[my.output]; my.input _ CopyGraph[my.output, ColorCopy]; }; RealFromRope: PROC [rawRope: Rope.ROPE] RETURNS [REAL] = INLINE { OPEN IO; RETURN [GetReal[RIS[rawRope]]] }; ShowColorPaint: ViewerClasses.PaintProc = { my:State _ NARROW[self.data]; context.ScaleT[40.0/Scale]; PaintLocked[my, context, whatChanged]; }; TrapRegion: TYPE = REF TrapRegionRec; TrapRegionRec: TYPE = RECORD [ color: ImagerColor.RGB, lastPointSeen: Point _ NIL, lineLeft, lineRight: Line _ NIL ]; trapInfinityRegion: TrapRegion = NEW[TrapRegionRec _ [color: [1.0, 1.0, 1.0]]]; PaintLocked: ENTRY PROC [my: State, context: Imager.Context, whatChanged: REF ANY] ~ { OPEN Imager, ImagerPath; ENABLE UNWIND => NULL; TrapStart: StartRegionProc = { rP: TrapRegion _ NARROW[regionPrevious]; RETURN[NEW[TrapRegionRec _ [color: AddRGB[rP.color, NARROW[lineRight.state, ColorLine].deltaColor], lastPointSeen: lineLeft.above, lineLeft: lineLeft, lineRight: lineRight]]]; }; TrapStop: StopRegionProc = { rC: TrapRegion _ NARROW[regionCenter]; IF rC # trapInfinityRegion THEN { PaintTrap[rC.color, rC.lastPointSeen.y, lineLeft.below.y, lineLeft, lineRight]; rC.lineLeft _ rC.lineRight _ NIL; }; }; TrapSplit: SplitRegionProc = { rR: TrapRegion _ NARROW[regionRight]; rL: TrapRegion; point: Point _ lineRight.above; IF rR # trapInfinityRegion THEN { PaintTrap[rR.color, rR.lastPointSeen.y, point.y, rR.lineLeft, rR.lineRight]; rR.lastPointSeen _ point; rL _ NEW[TrapRegionRec _ [color: rR.color, lastPointSeen: point, lineLeft: rR.lineLeft, lineRight: lineLeft]]; rR.lineLeft _ lineRight; RETURN[rL]; } ELSE RETURN[rR]; }; TrapMerge: MergeRegionProc = { rL: TrapRegion _ NARROW[regionLeft]; rR: TrapRegion _ NARROW[regionRight]; point: Point _ lineLeft.below; IF (rL # trapInfinityRegion) AND (rR # trapInfinityRegion) THEN { PaintTrap[rL.color, rL.lastPointSeen.y, point.y, rL.lineLeft, rL.lineRight]; PaintTrap[rR.color, rR.lastPointSeen.y, point.y, rR.lineLeft, rR.lineRight]; rR.lastPointSeen _ point; rR.lineLeft _ rL.lineLeft; rL.lineLeft _ rL.lineRight _ NIL; }; RETURN[IF rL = trapInfinityRegion THEN rL ELSE rR]; }; TrapLineChange: LineChangeRegionProc = { rC: TrapRegion _ NARROW[regionCenter]; point: Point _ lineNew.above; IF (rC # trapInfinityRegion) THEN { PaintTrap[rC.color, rC.lastPointSeen.y, point.y, rC.lineLeft, rC.lineRight]; rC.lastPointSeen _ point; IF side = left THEN rC.lineLeft _ lineNew ELSE rC.lineRight _ lineNew; }; }; PaintTrap: PROC [c: ImagerColor.RGB, aboveY, belowY: INT, lineLeft, lineRight: Line] ~ { <> traj: Trajectory; lowX: INT _ MIN[lineLeft.above.x, lineLeft.below.x]; highX: INT _ MAX[lineRight.above.x, lineRight.below.x]; top: Line _ NEW[LineRec _ [above: NEW[PointRec _ [x: highX, y: aboveY]], below: NEW[PointRec _ [x: lowX, y: aboveY]] ]]; bottom: Line _ NEW[LineRec _ [above: NEW[PointRec _ [x: highX, y: belowY]], below: NEW[PointRec _ [x: lowX, y: belowY]] ]]; topLeft, topRight, bottomLeft, bottomRight: Point; IF aboveY = belowY THEN RETURN; [topLeft, ] _ PairIntersection[top, lineLeft]; [topRight, ] _ PairIntersection[top, lineRight]; [bottomLeft, ] _ PairIntersection[bottom, lineLeft]; [bottomRight, ] _ PairIntersection[bottom, lineRight]; traj _ MoveTo[Vfp[topLeft]].LineTo[VfpR[topRight]].LineTo[VfpR[bottomRight]].LineTo[Vfp[bottomLeft]]; context.SetColor[ImagerColor.ColorFromRGB[c]]; MaskFillTrajectory[context, traj]; }; my.output _ Sweep[my.output, trapInfinityRegion, TrapStart, TrapStop, TrapSplit, TrapMerge, TrapLineChange]; }; Vfp: PROC [p: Point] RETURNS [Vector2.VEC] ~ INLINE { RETURN[[Real.Float[p.x], Real.Float[p.y]]]; }; VfpR: PROC [p: Point] RETURNS [Vector2.VEC] ~ INLINE { RETURN[[Real.Float[p.x+1], Real.Float[p.y]]]; }; BrushComing: ViewerClasses.NotifyProc ~ { my: State _NARROW[self.data]; BrushComingLocked[my, input]; }; BrushComingLocked: ENTRY PROC[my: State, input: LIST OF REF ANY] ~ { ENABLE UNWIND => NULL; FOR list: LIST OF REF ANY _ input, list.rest UNTIL list = NIL DO WITH list.first SELECT FROM a: ATOM => SELECT a FROM $RedBrush => AddBrush[my, redBrush]; $YellowBrush => AddBrush[my, yellowBrush]; $BlueBrush => AddBrush[my, blueBrush]; ENDCASE => NULL; z: TIPUser.TIPScreenCoords => my.mouse _ z; ENDCASE => ERROR; ENDLOOP; }; DeltaPoint: TYPE = RECORD[dX, dY: INT]; redBrush: LIST OF DeltaPoint _ LIST[ [-5,-5], [-5, 5], [5,5], [5, -5] ]; yellowBrush: LIST OF DeltaPoint _ LIST[ [0,-5], [-5, 0], [0,5], [5, 0] ]; blueBrush: LIST OF DeltaPoint _ LIST[ [-5,0],[-4, 3],[-3,4], [0,5], [3,4], [4,3], [5, 0],[4, -3],[3,-4], [0,-5], [-3,-4], [-4,-3] ]; boarder: INT _ 5; AddBrush: INTERNAL PROC [my: State, brush: LIST OF DeltaPoint] ~ { cX: INT _ IS[my.mouse.mouseX]; cY: INT _ IS[my.mouse.mouseY]; sX, sY: INT; factor: INT _ Scale/10; brushBoarder: INT _ factor * boarder; IF cX < brushBoarder THEN cX _ brushBoarder; IF cY < brushBoarder THEN cY _ brushBoarder; [sX, sY] _ brush.first; my.input _ my.input.NewPoint[cX+factor*sX, cY+factor*sY]; brush _ brush.rest; UNTIL brush = NIL DO my.input _ my.input.LineTo[cX+factor*brush.first.dX, cY+factor*brush.first.dY, ColorCopy[windPlusOne], ColorFlip]; brush _ brush.rest; ENDLOOP; my.input _ my.input.LineTo[cX+factor*sX, cY+factor*sY, ColorCopy[windPlusOne], ColorFlip]; }; IS: PROC [i: INT] RETURNS [o: INT] ~ INLINE { RETURN[Real.Round[i*Scale/40.0]]; }; PaletteNotify: ViewerClasses.NotifyProc ~ { my: State _ NARROW[self.data]; PaletteNotifyLocked[my, input]; ViewerOps.PaintViewer[viewer: my.palette, hint: all, clearClient: FALSE, whatChanged: sampleColor]; }; PalettePaint: ViewerClasses.PaintProc = { my:State _ NARROW[self.data]; x, y: INTEGER; IF whatChanged = NIL THEN { FOR hx: INTEGER IN [0..25) DO FOR hy: INTEGER IN [0..175) DO x _ 2*hx; y _ 2*hy; context.SetColor[ImagerColor.ColorFromRGB[RGBFromCoords[x, y]]]; context.MaskBox[[x, y, x+2, y+2]]; ENDLOOP; ENDLOOP; }; context.SetColor[ImagerColor.ColorFromRGB[my.newColor]]; context.MaskBox[[0, 350, 50, 400]]; }; RGBFromCoords: PROC [x, y: INTEGER] RETURNS [rgb: ImagerColor.RGB] ~ { hsv: ImagerColor.HSV _ [y/350.0, 0.5 + x/100.0, 1.0]; RETURN[ImagerColor.RGBFromHSV[hsv]]; }; PaletteNotifyLocked: ENTRY PROC[my: State, input: LIST OF REF ANY] ~ { ENABLE UNWIND => NULL; FOR list: LIST OF REF ANY _ input, list.rest UNTIL list = NIL DO WITH list.first SELECT FROM z: TIPUser.TIPScreenCoords => { IF z.mouseY > 350 THEN z.mouseY _ 350; my.newColor _ RGBFromCoords[z.mouseX, z.mouseY] }; ENDCASE => ERROR; ENDLOOP; }; displayerClass: ViewerClasses.ViewerClass _ NEW[ViewerClasses.ViewerClassRec _ [paint: ShowColorPaint, notify: BrushComing, tipTable: TIPUser.InstantiateNewTIPTable["ColorSweep.tip"]]]; ViewerOps.RegisterViewerClass[$ShowColor, displayerClass]; displayerClass _ NEW[ViewerClasses.ViewerClassRec _ [paint: PalettePaint, notify: PaletteNotify, tipTable: TIPUser.InstantiateNewTIPTable["Palette.tip"]]]; ViewerOps.RegisterViewerClass[$Palette, displayerClass]; Commander.Register[key: "ShowColor", proc: ShowColor, doc: "To Debug Color"]; END.