ColorTrixMapCommandsImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Bloomenthal, January 16, 1987 11:59:00 pm PST
DIRECTORY Args, CedarProcess, ColorTrixDispatch, ColorTrixMap, Commander, Controls, ImagerPixelMap, ImagerTerminal, Interminal, InterminalBackdoor, IO, Process, Random, Real, RealFns, Rope, Terminal, ViewerClasses;
ColorTrixMapCommandsImpl: CEDAR PROGRAM
IMPORTS Args, CedarProcess, ColorTrixDispatch, ColorTrixMap, Controls, ImagerTerminal, InterminalBackdoor, IO, Process, Random, Real, RealFns, Rope
~ BEGIN
OuterData: TYPE ~ Controls.OuterData;
Cmap:   TYPE ~ ColorTrixMap.Cmap;
ROPE:   TYPE ~ Rope.ROPE;
Miscellany
standardButtonList: Controls.ButtonList ← LIST[
["Quit", Controls.Quit],
["Restore", Restore],
["Restore&Quit", RestoreAndQuit]];
Destroy: Controls.DestroyProc ~ {
ColorTrixMap.ReleaseCmap[NARROW[NARROW[outerData, OuterData].data]];
};
Restore: Controls.ClickProc ~ {
ColorTrixMap.Write[NARROW[NARROW[clientData, OuterData].data]];
};
RestoreAndQuit: Controls.ClickProc ~ {
Controls.EndViewer[NARROW[parent, Controls.Viewer].parent];
Process.Pause[Process.MsecToTicks[100]];
ColorTrixMap.Write[NARROW[NARROW[clientData, OuterData].data]];
};
Commands
CmSave: PUBLIC Commander.CommandProc ~ {
arg: Rope.ROPE ← Args.GetRope[cmd];
IF arg = NIL THEN RETURN[$Failure, "Save requires a filename as argument"];
ColorTrixMap.Save[ColorTrixMap.Read[], arg];
}
;
CmLoad: PUBLIC Commander.CommandProc ~ {
arg: Rope.ROPE ← Args.GetRope[cmd];
IF arg = NIL THEN RETURN[$Failure, "Filename required as argument"];
IF NOT ColorTrixMap.Load[arg] THEN RETURN[$Failure, "Can't read file."];
};
CmMono: PUBLIC Commander.CommandProc ~ {ColorTrixMap.Mono[]};
Gammaler: Controls.ControlProc ~ {
IF control.mouse.button = right OR control.mouse.state = up
THEN ColorTrixMap.Gamma[control.value];
};
CmGamma: PUBLIC Commander.CommandProc ~ {
cmSave: Cmap ← ColorTrixMap.Read[];
arg: Args.Arg ← Args.ArgReal[cmd];
IF arg.ok
THEN ColorTrixMap.Gamma[arg.real]
ELSE [] ← Controls.OuterViewer[
name: "Gamma",
column: right,
buttons: standardButtonList,
controls: LIST[Controls.NewControl[name: "gamma", type: hSlider, min: 0.0, max: 5.0, init: 2.2, x: 60, y: 10, w: 200, h: 20, detents: LIST[[, 2.2]], proc: Gammaler]],
data: cmSave];
};
CmDither: PUBLIC Commander.CommandProc ~ {
ImagerTerminal.SetStandardColorMap[InterminalBackdoor.terminal];
};
CmRamp: PUBLIC Commander.CommandProc ~ {
args: ARRAY[0..8) OF Args.Arg;
FOR i: INT IN [0..8) DO
args[i] ← Args.ArgIntRange[cmd, i, 0, 255];
IF NOT args[i].ok THEN RETURN[$Failure, "bad arguments"];
ENDLOOP;
ColorTrixMap.Ramp[args[0].int, args[1].int, args[2].int, args[3].int, args[4].int, args[5].int, args[6].int, args[7].int];
};
CmPrint: PUBLIC Commander.CommandProc ~ {
cm: Cmap ← ColorTrixMap.Read[];
FOR i: INT IN [0..256) DO
cmd.out.PutF["%3g:\t%3g\t%3g\t%3g\n", IO.int[i], IO.int[cm[0][i]], IO.int[cm[1][i]], IO.int[cm[2][i]]];
ENDLOOP;
};
CmOnly: PUBLIC Commander.CommandProc ~ {
a: Rope.ROPE ← Args.GetRope[cmd];
IF a = NIL THEN RETURN[$Failure, "Only requires an argument"];
SELECT TRUE FROM
a.Equal["red"] => ColorTrixMap.PrimaryOnly[red];
a.Equal["green"] => ColorTrixMap.PrimaryOnly[green];
a.Equal["blue"] => ColorTrixMap.PrimaryOnly[blue];
ENDCASE => RETURN[$Failure, "argument one of: red, green, blue."];
};
CmTents: PUBLIC Commander.CommandProc ~ {
a: Args.Arg ← Args.ArgInt[cmd];
IF a.ok THEN ColorTrixMap.Tents[a.int] ELSE RETURN[$Failure, "argument needed."];
};
CmSin: PUBLIC Commander.CommandProc ~ {
a: Args.Arg;
v: ARRAY[0..2] OF REAL;
IF Args.NArgs[cmd] < 1 THEN RETURN[$Failure, "argument(s) needed."];
FOR i: NAT IN[0..2] DO
a ← Args.ArgReal[cmd, i];
IF NOT a.ok AND i = 0 THEN RETURN[$Failure, "bad argument."];
v[i] ← IF a.ok THEN a.real ELSE v[i-1];
ENDLOOP;
ColorTrixMap.Sin[v[0], v[1], v[2]];
};
CmGauss: PUBLIC Commander.CommandProc ~ {ColorTrixMap.Gauss[]};
CmColor: PUBLIC Commander.CommandProc ~ {
i, r, g, b: Args.Arg;
IF NOT (i ← Args.ArgIntRange[cmd, 0, 0, 255]).ok THEN RETURN[$Failure, "bad arguments"];
IF NOT (r ← Args.ArgIntRange[cmd, 1, 0, 255]).ok THEN RETURN[$Failure, "bad arguments"];
IF NOT (g ← Args.ArgIntRange[cmd, 2, 0, 255]).ok THEN RETURN[$Failure, "bad arguments"];
IF NOT (b ← Args.ArgIntRange[cmd, 3, 0, 255]).ok THEN RETURN[$Failure, "bad arguments"];
ColorTrixMap.WriteEntry[i.int, r.int, g.int, b.int];
};
Cycler: PROC [data: Controls.OuterData, cm: Cmap] ~ {
control: Controls.Control ← data.controls.first;
CedarProcess.SetPriority[background];
WHILE NOT data.destroyed DO
abs: REALABS[control.value];
n: INTMAX[1, Real.RoundI[abs]];
IF control.value = 0.0 THEN {Process.Pause[Process.MsecToTicks[100]]; LOOP};
ColorTrixMap.Write[ColorTrixMap.Cycle[cm, IF control.value > 0 THEN n ELSE -n]];
IF abs > 0.01 THEN Process.Pause[Process.MsecToTicks[Real.Round[100.0/abs]]];
ENDLOOP;
};
CmCycle: PUBLIC Commander.CommandProc ~ {
cmSave: Cmap ← ColorTrixMap.Read[];
cm: Cmap ← ColorTrixMap.Copy[cmSave];
outer: Controls.Viewer ← Controls.OuterViewer[
name: "Cycle",
column: right,
buttons: standardButtonList,
controls: LIST[Controls.NewControl[type: hSlider, report: FALSE, x: 60, y: 10, w: 200, h: 20, min: -25.0, max: 25.0, init: 0.0, detents: LIST[[, 0.0]]]],
data: cmSave];
TRUSTED {Process.Detach[FORK Cycler[NARROW[outer.data], cm]]};
};
NBitser: Controls.ControlProc ~ {
cm: Cmap ← ColorTrixMap.ObtainCmap[];
ColorTrixMap.Mono[cm];
ColorTrixMap.NBits[cm, Real.Round[control.value]];
ColorTrixMap.ReleaseCmap[cm];
};
CmNBits: PUBLIC Commander.CommandProc ~ {
arg: Args.Arg ← Args.ArgInt[cmd];
IF arg.ok
THEN ColorTrixMap.NBits[ColorTrixMap.Read[], arg.int]
ELSE [] ← Controls.OuterViewer[
name: "CmNBits",
column: right,
buttons: standardButtonList,
controls: LIST[Controls.NewControl[type: hSlider, x: 60, y: 10, w: 80, h: 20, min: 1.0, max: 8.0, init: 8.0, truncate: TRUE, proc: NBitser]],
data: ColorTrixMap.Read[]];
};
CmScale: PUBLIC Commander.CommandProc ~ {
a: Args.Arg ← Args.ArgReal[cmd];
IF NOT a.ok THEN RETURN[$Failure, "argument needed."];
ColorTrixMap.Scale[ColorTrixMap.Read[], a.real];
};
CmAdd: PUBLIC Commander.CommandProc ~ {
a: Args.Arg ← Args.ArgInt[cmd];
IF NOT a.ok THEN RETURN[$Failure, "argument needed."];
ColorTrixMap.Add[ColorTrixMap.Read[], a.int];
};
CmCompose: PUBLIC Commander.CommandProc ~ {
cm1, cm2: Cmap;
ok1: BOOL ← ColorTrixMap.Load[Args.GetRope[cmd, 0], cm1 ← ColorTrixMap.NewCmap[]];
ok2: BOOL ← ColorTrixMap.Load[Args.GetRope[cmd, 1], cm2 ← ColorTrixMap.NewCmap[]];
IF NOT ok1 OR NOT ok2 THEN RETURN[$Failure, "bad color map name(s)."];
ColorTrixMap.Compose[cm1, cm2];
};
CmInterp: PUBLIC Commander.CommandProc ~ {
cmLoad: Cmap ← ColorTrixMap.NewCmap[];
a: Args.Arg ← Args.ArgReal[cmd, 0];
IF NOT a.ok OR NOT ColorTrixMap.Load[Args.GetRope[cmd, 1], cmLoad]
THEN RETURN[$Failure, "bad argument(s)."];
ColorTrixMap.Interp[a.real, cmLoad, ColorTrixMap.Read[]];
};
CmScramble: PUBLIC Commander.CommandProc ~ {ColorTrixMap.Scramble[ColorTrixMap.Read[]]};
CmRandom: PUBLIC Commander.CommandProc ~ {ColorTrixMap.Rand[]};
Shifter: Controls.ControlProc ~ {
cm: Cmap ← ColorTrixMap.ObtainCmap[];
shift: INTEGER ← Real.Round[control.valuePrev]-Real.Round[control.value];
ColorTrixMap.Write[ColorTrixMap.Cycle[ColorTrixMap.Read[cm], shift]];
ColorTrixMap.ReleaseCmap[cm];
};
ShiftRestore: Controls.ClickProc ~ {
outerData: OuterData ← NARROW[clientData];
ColorTrixMap.Write[NARROW[outerData.data]];
Controls.Reset[outerData.controls.first];
};
CmShift: PUBLIC Commander.CommandProc ~ {
arg: Args.Arg ← Args.ArgInt[cmd];
cm: Cmap ← ColorTrixMap.Read[];
IF arg.ok
THEN ColorTrixMap.Write[ColorTrixMap.Cycle[ColorTrixMap.Read[], arg.int]]
ELSE [] ← Controls.OuterViewer[
name: "Shift",
column: right,
buttons: LIST[["Restore", ShiftRestore], ["Restore&Quit", RestoreAndQuit]],
destroyProc: Destroy,
controls: LIST[Controls.NewControl[type: dial, report: FALSE, x: 60, y: 10, w: 80, h: 80, min: 0.0, max: 255.0, init: 0.0, detents: LIST[[, 0.0]], proc: Shifter, data: cm]],
data: cm];
};
Speckler: PROC [data: Controls.OuterData] ~ {
RandInt: PROC RETURNS [INTEGER] ~ {RETURN[Random.ChooseInt[max: 255]]};
CedarProcess.SetPriority[background];
WHILE NOT data.destroyed DO
Process.Pause[Process.MsecToTicks[Real.Round[100.0*data.controls.first.value]]];
ColorTrixMap.WriteEntry[RandInt[], RandInt[], RandInt[], RandInt[]];
ENDLOOP;
};
CmSpeckle: PUBLIC Commander.CommandProc ~ {
cmSave: Cmap ← ColorTrixMap.Read[];
outer: Controls.Viewer ← Controls.OuterViewer[
name: "Speckle",
column: right,
buttons: standardButtonList,
controls: LIST[Controls.NewControl[
type: hSlider, report: FALSE, x: 60, y: 10, w: 200, h: 20, min: 1.0, max: 0.0, init: 0.5]],
data: cmSave];
TRUSTED {Process.Detach[FORK Speckler[NARROW[outer.data]]]};
};
Flasher: PROC [data: Controls.OuterData] ~ {
CedarProcess.SetPriority[background];
WHILE NOT data.destroyed DO
ColorTrixMap.Rand[];
Process.Pause[Process.MsecToTicks[Real.Round[1000.0*data.controls.first.value]]];
ENDLOOP;
};
CmFlash: PUBLIC Commander.CommandProc ~ {
cmSave: Cmap ← ColorTrixMap.Read[];
outer: ViewerClasses.Viewer ← Controls.OuterViewer[
name: "Flash",
column: right,
buttons: standardButtonList,
controls: LIST[Controls.NewControl[type: hSlider, report: FALSE, x: 60, y: 10, w: 200, h: 20, min: 1.0, max: 0.0, init: 0.5]],
data: cmSave];
TRUSTED {Process.Detach[FORK Flasher[NARROW[outer.data]]]};
};
Rippler: PROC [data: Controls.OuterData] ~ {
cmOut: Cmap ← ColorTrixMap.ObtainCmap[];
vals: ARRAY [0..2] OF REALALL[0.0];
cs: ARRAY [0..2] OF Controls.Control;
incs: ARRAY [0..2] OF INTEGERALL[1];
cs[0] ← data.controls.first;
cs[1] ← data.controls.rest.first;
cs[2] ← data.controls.rest.rest.first;
CedarProcess.SetPriority[background];
DO
FOR i: NAT IN[0..2] DO
IF vals[i] < 0.25 THEN incs[i] ← 1 ELSE IF vals[i] > 25.0 THEN incs[i] ← -1;
vals[i] ← vals[i]+incs[i]*cs[i].value;
ENDLOOP;
ColorTrixMap.Sin[vals[0], vals[1], vals[2], cmOut];
IF data.destroyed THEN EXIT;
ColorTrixMap.Write[cmOut];
ENDLOOP;
ColorTrixMap.ReleaseCmap[cmOut];
};
CmRipple: PUBLIC Commander.CommandProc ~ {
cmSave: Cmap ← ColorTrixMap.Read[];
outer: ViewerClasses.Viewer ← Controls.OuterViewer[
name: "Ripple",
column: right,
buttons: standardButtonList,
controls: LIST[
Controls.NewControl[type:hSlider,report:FALSE,x:60,y:10,w:200,h:20,min:0.,max:1.,init:0.5],
Controls.NewControl[type:hSlider,report:FALSE,x:60,y:40,w:200,h:20,min:0.,max:1.,init:0.5],
Controls.NewControl[type:hSlider,report:FALSE,x:60,y:70,w:200,h:20,min:0.,max:1.,init:0.5]],
data: cmSave];
TRUSTED {Process.Detach[FORK Rippler[NARROW[outer.data]]]};
};
rec: TYPE ~ RECORD [data: SEQUENCE num: NAT OF Cmap];
Blender: PROC [d: Controls.OuterData, cm: REF rec, ncm: NAT] ~ {
inc: INTEGER ← 1;
n0, n1: INTEGER ← 0;
WHILE NOT d.destroyed DO
Process.CheckForAbort[];
IF (n1 ← n0+inc) = -1 OR n1 = ncm THEN {inc ← -inc; n1 ← n0+inc};
FOR t: REAL ← 0, t+d.controls.first.value WHILE t <= 0.5*3.1415926535 DO
IF d.destroyed THEN EXIT;
ColorTrixMap.Interp[RealFns.Sin[t], cm.data[n0], cm.data[n1]];
ENDLOOP;
n0 ← n1;
ENDLOOP;
};
CmBlend: PUBLIC Commander.CommandProc ~ {
ncm: NAT ← 0;
outer: Controls.Viewer;
cmSave: Cmap ← ColorTrixMap.Read[];
cm: REF rec ← NEW[rec[Args.NArgs[cmd]]];
FOR a: NAT IN[0..Args.NArgs[cmd]) DO
Process.CheckForAbort[];
IF ColorTrixMap.Load[Args.GetRope[cmd, a], cm.data[ncm] ← ColorTrixMap.NewCmap[]]
THEN ncm ← ncm+1
ELSE cmd.out.PutF["Can't read %g\n", IO.rope[Args.GetRope[cmd, a]]];
ENDLOOP;
IF ncm = 0 THEN RETURN;
outer ← Controls.OuterViewer[
name: "Color Map Blender",
column: right,
buttons: standardButtonList,
controls: LIST[Controls.NewControl[type: hSlider, report: FALSE, x: 60, y: 10, w: 200, h: 20, min: 0.01, max: 1.0, init: 0.1]],
data: cmSave];
TRUSTED {Process.Detach[FORK Blender[NARROW[outer.data], cm, ncm]]};
};
Snaker: PROC [data: Controls.OuterData] ~ {
r, g, b: REAL;
cm: Cmap ← ColorTrixMap.Read[];
CedarProcess.SetPriority[background];
WHILE NOT data.destroyed DO
[r, g, b] ← GetSnake[];
[] ← ColorTrixMap.Cycle[cm, 1];
ColorTrixMap.SetEntry[cm, 1, Real.RoundI[255*r], Real.RoundI[255*g], Real.RoundI[255*b]];
ColorTrixMap.Write[cm];
ENDLOOP;
};
CmSnake: PUBLIC Commander.CommandProc ~ {
cmSave: Cmap ← ColorTrixMap.Read[];
outer: Controls.Viewer ← Controls.OuterViewer[
name: "Snake",
column: right,
buttons: standardButtonList,
data: cmSave];
TRUSTED {Process.Detach[FORK Snaker[NARROW[outer.data]]]};
};
r, g, b: Section;
Section: TYPE ~ RECORD [count: INTEGER ← 0, p, d: REAL ← 0.0];
GetSnake: PROC RETURNS [REAL, REAL, REAL] ~ {
RandomReal: PROC RETURNS [REAL] ~ {
RETURN[(1.0/1024.0)*REAL[Random.ChooseInt[min: 1, max: 1024]]];
};
NewSection: PROC [p: REAL] RETURNS[s: Section] ~ {
s.p ← p;
s.count ← Random.ChooseInt[min: 5, max: 30];
s.d ← (RandomReal[]-p)/REAL[s.count];
};
IF (r.count ← r.count-1) <= 0 THEN r ← NewSection[r.p];
IF (g.count ← g.count-1) <= 0 THEN g ← NewSection[g.p];
IF (b.count ← b.count-1) <= 0 THEN b ← NewSection[b.p];
r.p ← MAX[0.0, MIN[1.0, r.p+r.d]];
g.p ← MAX[0.0, MIN[1.0, g.p+g.d]];
b.p ← MAX[0.0, MIN[1.0, b.p+b.d]];
RETURN[r.p, g.p, b.p];
};
CmContours: PUBLIC Commander.CommandProc ~ {
ok: BOOL;
nCyclesArg, powerArg: Args.Arg;
twoPI: REAL ~ 2.0*3.1415926535;
halfPI: REAL ~ 0.5*3.1415926535;
[ok, nCyclesArg, powerArg] ← Args.ArgsGet[cmd, "-power%r-ncycles%r"];
IF ok THEN {
nCycles: REAL ~ IF nCyclesArg.ok THEN nCyclesArg.real ELSE 4.0;
power: REAL ~ 1.0/(IF powerArg.ok THEN powerArg.real ELSE 6.0);
FOR n: NAT IN [0..255] DO
cmapValue: INT;
t: REAL ~ REAL[n]/255.0;
intensity: REAL ← 0.5+0.5*RealFns.Sin[(nCycles*twoPI+halfPI)*RealFns.Power[t, power]];
intensity ← t+(1.0-t)*intensity;
cmapValue ← Real.RoundI[intensity*255.0];
ColorTrixMap.WriteEntry[n, cmapValue, cmapValue, cmapValue];
ENDLOOP;
ColorTrixMap.JustWritten[];
};
};
CmToOthers: PUBLIC Commander.CommandProc ~ {
source: Rope.ROPE ~ Args.GetRope[cmd];
i0dst, i1dst, isrc: INT ← -1;
SELECT TRUE FROM
Rope.Equal[source, "red", FALSE] => {isrc ← 0; i0dst ← 1; i1dst ← 2};
Rope.Equal[source, "grn", FALSE] => {isrc ← 1; i0dst ← 0; i1dst ← 2};
Rope.Equal[source, "blu", FALSE] => {isrc ← 2; i0dst ← 0; i1dst ← 1};
ENDCASE;
IF isrc # -1 THEN {
cmap: ColorTrixMap.Cmap ← ColorTrixMap.Read[];
FOR n: NAT IN [0..255] DO
cmap[i0dst][n] ← cmap[i1dst][n] ← cmap[isrc][n];
ENDLOOP;
ColorTrixMap.Write[cmap];
ColorTrixMap.JustWritten[];
};
};
Usage Messages
cmSaveUsage:   ROPE ~ "Cm Save <color map name>.";
cmLoadUsage:   ROPE ~ "Cm Load <color map name>.";
cmMonoUsage:   ROPE ~ "Cm Mono: set the color map to a linear ramp.";
cmGammaUsage:   ROPE ~ "Cm Gamma [value]: if no argument, interactive.";
cmDitherUsage:   ROPE ~ "Cm Dither: set color to Imager.StandardMap.";
cmRampUsage:   ROPE ~ "Cm Ramp <i0 r0 g0 b0 i1 r1 g1 b1>.";
cmPrintUsage:   ROPE ~ "Cm Print: print the color map contents.";
cmOnlyUsage:   ROPE ~ "Cm Only <red | green | blue>.";
cmTentsUsage:   ROPE ~ "Cm Tents <nTents>.";
cmSinUsage:    ROPE ~ "Cm Sin <nCycles>.";
cmGaussUsage:   ROPE ~ "Cm Gauss: put a gaussian in color map.";
cmColorUsage:   ROPE ~ "Cm Color <i r g b>: Set color map entry i.";
cmCycleUsage:   ROPE ~ "Cm Cycle: interactively cycle color map.";
cmNBitsUsage:   ROPE ~ "Cm NBits [nBits]: if no argument, interactive.";
cmScaleUsage:   ROPE ~ "Cm Scale <scale>.";
cmAddUsage:   ROPE ~ "Cm Add <offset>.";
cmComposeUsage:  ROPE ~ "Cm Compose <cmap1 cmap2>: result ← c2[c1].";
cmInterpUsage:   ROPE ~ "Cm Interp <t: REAL, cmap: ROPE>.";
cmScrambleUsage:  ROPE ~ "Cm Scramble: randomly interchange entries.";
cmRandomUsage:  ROPE ~ "Cm Random: randomize the color map.";
cmShiftUsage:   ROPE ~ "Cm Shift [n]: in no argument, interactive.";
cmSpeckleUsage:   ROPE ~ "Cm Speckle: randomly set single entries.";
cmFlashUsage:   ROPE ~ "Cm Flash: interactively randomize color map.";
cmBlendUsage:   ROPE ~ "Cm Blend <LIST of Cmap>: continuously blend.";
cmRippleUsage:   ROPE ~ "Cm Ripple: interactively ripple sine waves.";
cmSnakeUsage:   ROPE ~ "Cm Snake: snake through color space.";
cmToOthersUsage:  ROPE ~ "Cm ToOthers <red | grn | blu> one component to other two.";
cmContoursUsage:  ROPE ~ "Cm Contours [-power <real>] [-ncycles <real>] show contours.";
Start Code
ColorTrixDispatch.RegisterCmOp["Save",   CmSave,   cmSaveUsage];
ColorTrixDispatch.RegisterCmOp["Load",   CmLoad,   cmLoadUsage];
ColorTrixDispatch.RegisterCmOp["Mono",   CmMono,   cmMonoUsage];
ColorTrixDispatch.RegisterCmOp["Gamma",  CmGamma,  cmGammaUsage];
ColorTrixDispatch.RegisterCmOp["Dither",  CmDither,  cmDitherUsage];
ColorTrixDispatch.RegisterCmOp["Ramp",   CmRamp,   cmRampUsage];
ColorTrixDispatch.RegisterCmOp["Print",   CmPrint,   cmPrintUsage];
ColorTrixDispatch.RegisterCmOp["Only",   CmOnly,   cmOnlyUsage];
ColorTrixDispatch.RegisterCmOp["Tents",   CmTents,   cmTentsUsage];
ColorTrixDispatch.RegisterCmOp["Sin",   CmSin,   cmSinUsage];
ColorTrixDispatch.RegisterCmOp["Gauss",   CmGauss,   cmGaussUsage];
ColorTrixDispatch.RegisterCmOp["Color",   CmColor,   cmColorUsage];
ColorTrixDispatch.RegisterCmOp["Cycle",   CmCycle,   cmCycleUsage];
ColorTrixDispatch.RegisterCmOp["NBits",   CmNBits,   cmNBitsUsage];
ColorTrixDispatch.RegisterCmOp["Scale",   CmScale,   cmScaleUsage];
ColorTrixDispatch.RegisterCmOp["Add",   CmAdd,   cmAddUsage];
ColorTrixDispatch.RegisterCmOp["Compose",  CmCompose,  cmComposeUsage];
ColorTrixDispatch.RegisterCmOp["Interp",  CmInterp,  cmInterpUsage];
ColorTrixDispatch.RegisterCmOp["Scramble",  CmScramble,  cmScrambleUsage];
ColorTrixDispatch.RegisterCmOp["Random",  CmRandom,  cmRandomUsage];
ColorTrixDispatch.RegisterCmOp["Shift",   CmShift,   cmShiftUsage];
ColorTrixDispatch.RegisterCmOp["Speckle",  CmSpeckle,  cmSpeckleUsage];
ColorTrixDispatch.RegisterCmOp["Flash",   CmFlash,   cmFlashUsage];
ColorTrixDispatch.RegisterCmOp["Blend",   CmBlend,   cmBlendUsage];
ColorTrixDispatch.RegisterCmOp["Ripple",  CmRipple,  cmRippleUsage];
ColorTrixDispatch.RegisterCmOp["Snake",   CmSnake,   cmSnakeUsage];
ColorTrixDispatch.RegisterCmOp["ToOthers",  CmToOthers,  cmToOthersUsage];
ColorTrixDispatch.RegisterCmOp["Contours",  CmContours,  cmContoursUsage];
END.