ContoursTestImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Bloomenthal, September 17, 1986 4:25:55 pm PDT
DIRECTORY ColorTrixBasics, Commander, Controls, Contours, Draw2d, ImagerPixelMap, IO, Real;
ContoursTestImpl: CEDAR PROGRAM
IMPORTS
~ BEGIN
OPEN Contours;
Types
PixelMap:    TYPE ~ ImagerPixelMap.PixelMap;
Spot:     TYPE ~ RECORD [x, y: INTEGER];
SpotSequence:  TYPE ~ REF SpotSequenceRec;
SpotSequenceRec: TYPE ~ RECORD [
length:      NAT ← 0,
elememnt:     SEQUENCE maxLength: NAT OF Spot
        ];
Commands
ProgramData:  TYPE ~ REF ProgramDataRec;
ProgramDataRec: TYPE ~ RECORD [
outer:    Viewer ← NIL,      -- the outer (parent, top level) viewer
contour:   Control ← NIL,      -- the contour control
color:    CARDINAL ← 200,
cd:    Context ← NIL,
out:    STREAMNIL
];
ContoursFill: Commander.CommandProc ~ {
p: ProgramData ← NEW[ProgramDataRec];
p.contour ← Controls.NewControl[type: contour, w: 160, proc: Negate, data: p];
p.outer ← Controls.OuterViewer[
name: "Contours Fill", controls: LIST[p.contour], buttons: LIST[["Clear", Clear]], data: p];
p.cd ← ColorTrixBasics.InitCd[gray];
p.out ← cmd.out;
};
Clear: Controls.ClickProc ~ {
p: ProgramData ← NARROW[NARROW[clientData, Controls.OuterData].data];
Controls.Reset[p.contour];
};
Commander.Register["///Commands/ContoursFill", ContoursFill, "Fill a contour\n"];
END.
..
NegateSegment: PROC [pm: PixelMap, s0, s1: Spot] ~ {
x: REAL ← s0.x;
xMax: INTEGER ← pm.fMin+pm.fSize;
dx: REALIF s0.y # s1.y THEN (s1.x-s0.x)/(s1.y-s0.y) ELSE 1.0;
color: CARDINALIF dx < 0 THEN 100 ELSE p.color;
FOR y: INTEGER IN [s0.y..s1.y) DO
xx: INTEGER ← Real.RoundI[x];
dest: PixelMap ← ImagerPixelMap.SetWindow[p.pm, [y, xx, 1, xMax-xx]];
ImagerPixelMap.Transfer[dest, p.pm, [null, complement]];
x ← x+dx;
ENDLOOP;
};
Fill: Controls.ControlProc ~ {
Presumes contour is clockwise and its coordinates are  [-1..1].
p: ProgramData ← NARROW[control.data];
contour: Contour ← Contours.FromControl[p.contour];
SpotsStart: PROC [spots: SpotSequence] RETURNS [INTEGER] ~ {
start, ymin: INTEGER ← 10000;
FOR n: NAT IN [0..spots.length) DO
IF spots[n].y < ymin THEN {ymin ← spots[n].y; start ← n};
ENDLOOP;
RETURN[start];
};
FloodSegment: PROC [pm: PixelMap, s0, s1: Spot] ~ {
x: REAL ← s0.x;
xMax: INTEGER ← pm.fMin+pm.fSize;
dx: REALIF s0.y # s1.y THEN (s1.x-s0.x)/(s1.y-s0.y) ELSE 1.0;
color: CARDINALIF dx < 0 THEN 100 ELSE p.color;
FOR y: INTEGER IN [s0.y..s1.y] DO
xx: INTEGER ← Real.RoundI[x];
ImagerPixelMap.Fill[pm, [y, xx, 1, xMax-xx], color];
x ← x+dx;
ENDLOOP;
};
IF contour.closed THEN {
spots: SpotSequence ← PmSpotsFromContour[contour, p.pm];
n, start: INTEGER ← SpotsStart[spots];
s0, s1: Spot ← spots[start];
ImagerPixelMap.Fill[p.pm, [p.pm.sMin, p.pm.fMin, p.pm.sSize, p.pm.fSize], 60];
DO
s0 ← s1;
s1 ← spots[n ← (n+1) MOD spots.length];
FloodSegment[p.pm, s0, s1];
IF n = start THEN EXIT;
ENDLOOP;
};
};
PmSpotsFromContour: PROC [contour: Contour, pm: PixelMap, invert: BOOLTRUE]
RETURNS [SpotSequence] ~ {
spots: SpotSequence ← NEW[SpotSequenceRec[contour.pairs.length]];
scale: REAL(MIN[pm.sSize, pm.fSize]-1)/2.0;
xOffset: REAL ← pm.fMin+scale-1.0;
yOffset: REAL ← pm.sMin+scale-1.0;
xMin: INTEGER ← 10000;
spots.length ← spots.maxLength;
FOR n: NAT IN [0..spots.length) DO
spots[n] ← [
Real.RoundI[xOffset+scale*contour.pairs[n].x],
Real.RoundI[yOffset+scale*contour.pairs[n].y]];
ENDLOOP;
IF invert THEN FOR n: NAT IN [0..spots.length) DO
spots[n].y ← pm.sMin+pm.sSize-spots[n].y;
ENDLOOP;
RETURN[spots];
};
Negate: Controls.ControlProc ~ {
Presumes contour is clockwise and its coordinates are  [-1..1].
p: ProgramData ← NARROW[control.data];
yLast: INTEGER ← -1;
xMax: INTEGER ← p.pm.fMin+p.pm.fSize;
contour: Contour ← Contours.FromControl[p.contour];
NegateSegment: PROC [pm: PixelMap, s0, s1: Spot] ~ {
NegateToEnd: PROC [x, y: INTEGER] ~ {
IF y # yLast THEN {
dest: PixelMap ← ImagerPixelMap.SetWindow[pm, [y, x, 1, xMax-x]];
ImagerPixelMap.Transfer[dest, pm, [null, complement]];
yLast ← y;
};
};
IF s0.y = s1.y
THEN NegateToEnd[s0.x, s0.y]
ELSE {
Dot: Draw2d.PixelProc ~ {NegateToEnd[x, y]};
Draw2d.DoWithLine[s0.x, s0.y, s1.x, s1.y, Dot];
};
};
IF contour.closed THEN {
spots: SpotSequence ← PmSpotsFromContour[contour, p.pm];
s0, s1: Spot ← spots[spots.length-1];
ColorTrixBasics.FillPm[p.pm, 100];
FOR n: NAT IN [0..spots.length) DO
s0 ← s1;
s1 ← spots[n];
ColorTrixBasics.PutLine[p.pm, s0.x, s0.y, s1.x, s1.y, 200];
NegateSegment[p.pm, s0, s1];
ENDLOOP;
};
};
Negate: Controls.ControlProc ~ {
Presumes contour is clockwise and its coordinates are  [-1..1].
p: ProgramData ← NARROW[control.data];
yLast: INTEGER ← -1;
xMax: INTEGER ← p.pm.fMin+p.pm.fSize;
contour: Contour ← Contours.FromControl[p.contour];
NegateSegment: PROC [pm: PixelMap, s0, s1: Spot] ~ {
NegateToEnd: PROC [x, y: INTEGER] ~ {
IF y # yLast THEN {
dest: PixelMap ← ImagerPixelMap.SetWindow[pm, [y, x, 1, xMax-x]];
ImagerPixelMap.Transfer[dest, pm, [null, complement]];
yLast ← y;
};
};
IF s0.y = s1.y
THEN NegateToEnd[s0.x, s0.y]
ELSE {
Dot: Draw2d.PixelProc ~ {NegateToEnd[x, y]};
Draw2d.DoWithLine[s0.x, s0.y, s1.x, s1.y, Dot];
};
};
IF contour.closed THEN {
spots: SpotSequence ← PmSpotsFromContour[contour, p.pm];
s0, s1: Spot ← spots[spots.length-1];
ColorTrixBasics.FillPm[p.pm, 100];
FOR n: NAT IN [0..spots.length) DO
s0 ← s1;
s1 ← spots[n];
ColorTrixBasics.PutLine[p.pm, s0.x, s0.y, s1.x, s1.y, 200];
NegateSegment[p.pm, s0, s1];
ENDLOOP;
};
};