ColorSweepImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Greene, September 16, 1986 6:15:56 pm PDT
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] ~ {
Trapaziods are easy to paint directly, the following is a slightly inacurate, and extremely inefficient way of painting them.
traj: Trajectory;
lowX: INTMIN[lineLeft.above.x, lineLeft.below.x];
highX: INTMAX[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: INTIS[my.mouse.mouseX];
cY: INTIS[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.