CombinePolyTester.mesa
Last Edited by: Arnon, June 20, 1985 4:46:23 pm PDT
Pier, June 2, 1986 2:46:16 pm PDT
DIRECTORY
RatNums,
EuclideanGraphs,
ConvexEuclideanGraphs,
ConvexCombiner,
CombinePoly,
ViewerClasses,
ViewerOps,
MessageWindow,
Rope,
Menus,
Imager,
ImagerColor,
ImagerFont ,
ImagerPath,
ImagerInterpress,
TIPUser,
Convert,
IO,
ViewerIO,
Real,
Random;
CombinePolyTester: CEDAR PROGRAM
IMPORTS RatNums, EuclideanGraphs, ConvexEuclideanGraphs, ConvexCombiner, CombinePoly, ViewerOps, Menus, MessageWindow, Rope, Imager, ImagerColor, ImagerFont, ImagerPath, ImagerInterpress, TIPUser, Convert, IO, Real, Random
SHARES Imager
= BEGIN OPEN RN: RatNums, EG: EuclideanGraphs, CEG: ConvexEuclideanGraphs, CC: ConvexCombiner, CP: CombinePoly;
DOTWIDTH : INT = 10; -- diameter of vertices in edge drawing
SEGMENTWIDTH: INT = 2; -- width of segment portion in edge drawing
timesRomanBI: Imager.Font ~ ImagerFont.Scale[ImagerFont.Find["xerox/pressfonts/timesroman-brr"], 10.]; --- why does scale of 10. work?
CombinerViewer: TYPE = ViewerClasses.Viewer;
CombinerViewerData: TYPE = REF CombinerViewerDataRec;
CombinerViewerDataRec: TYPE = RECORD [
lowerGraphEnd: CP.DBID, -- for CEG.ConvexEuclideanGraph = EG.Vertex, assumed to be a vertex on the (convex) outline of the structure. We try to follow the rule that only known valid structures are ever attached to a CombinerViewer, since the Paint proc will look here when the viewer is to be repainted.
upperGraphEnd: CP.PolyID, -- for CEG.ConvexEuclideanGraph = EG.Vertex, assumed to be a vertex on the (convex) outline of the structure. We try to follow the rule that only known valid structures are ever attached to a CombinerViewer.
lowerGraphRegions: CP.RegionGenerator,
x, y: REAL, -- specify starting location in viewer window for drawing a figure
scale: REAL ← 1.0 -- current scale for Imager
];
Verbosity: TYPE = CEG.Verbosity;
UpperPolygonStart: Menus.MenuProc = {
self: CombinerViewer ← NARROW[parent, ViewerClasses.Viewer];
saveVertexIndex ← EG.vertexIndex;
upperGraphOpen ← TRUE;
pointCount ← 0;
MessageWindow.Append[
message : "Enter your polygon",
clearFirst: TRUE];
};
UpperPolygonDone: Menus.MenuProc = {
self: CombinerViewer ← NARROW[parent, ViewerClasses.Viewer];
selfData: CombinerViewerData ← NARROW[self.data];
upperGraphprev: CEG.ConvexEuclideanGraph;
outline: BOOLTRUE;
IF (NOT upperGraphOpen) THEN {
MessageWindow.Append[
message : "No upperGraphEnd is open now",
clearFirst: TRUE];
MessageWindow.Blink;
RETURN;
};
IF NOT CEG.ThreeOrMoreVertices[upperGraphEnd] THEN {
MessageWindow.Append[
message : "Input upperGraphEnd has fewer than three vertices; reenter",
clearFirst: TRUE];
MessageWindow.Blink;
upperGraphEnd ← NIL;
EG.vertexIndex ← saveVertexIndex;
upperGraphOpen ← FALSE;
RETURN;
};
Test that the polygon is convex; first need to get preceding vertex Note that the call to SpecialPreviousOutlineVertex amounts to an assumption that the polygon has at least three vertices. ConvexPolygon( or SpecialPreviousOutlineVertex?) will loop if < three vertices.
upperGraphprev ← CEG.SpecialPreviousOutlineVertex[upperGraphEnd];
IF NOT CEG.ConvexPolygon[upperGraphprev, upperGraphEnd, outline] THEN {
MessageWindow.Append[
message : "Input upperGraphEnd is not convex; reenter",
clearFirst: TRUE];
MessageWindow.Blink;
upperGraphEnd ← NIL;
EG.vertexIndex ← saveVertexIndex;
upperGraphOpen ← FALSE;
};
upperGraphOpen ← FALSE;
};
SetUpperPolygonClientData: Menus.MenuProc = {
self: CombinerViewer ← NARROW[parent, ViewerClasses.Viewer];
IF NOT CEG.ThreeOrMoreVertices[upperGraphEnd] THEN {
MessageWindow.Append[
message : "Current upperGraphEnd has fewer than three vertices",
clearFirst: TRUE];
MessageWindow.Blink;
RETURN;
};
upperGraphStart ← CEG.SpecialPreviousOutlineVertex[upperGraphEnd];
CEG.SetPolygonClientData[upperGraphStart, upperGraphEnd, globalClientData];
ViewerOps.PaintViewer[viewer: self, hint: client, whatChanged: NIL, clearClient: TRUE];
};
RunCombiner: Menus.MenuProc = {
self: CombinerViewer ← NARROW[parent, ViewerClasses.Viewer];
selfData: CombinerViewerData ← NARROW[self.data];
IF upperGraphOpen THEN {
MessageWindow.Append[
message : "You have an open upperGraphEnd",
clearFirst: TRUE];
MessageWindow.Blink;
RETURN;
};
CEG.DumpGraph[ lowerGraphEnd, ropeFromClientData, "///Users/Arnon.pa/Combiner/LowerGraph"];
CEG.DumpGraph[ upperGraphEnd, ropeFromClientData, "///Users/Arnon.pa/Combiner/UpperGraph"];
upperGraphStart ← CEG.SpecialPreviousOutlineVertex[upperGraphEnd];
lowerGraphEnd ← CP.PolygonIntoDatabase[upperGraphEnd, lowerGraphEnd, clientDataCombiner, clientDataEqual ];
upperGraphEnd ← NIL;
selfData.lowerGraphEnd ← lowerGraphEnd; --update Viewer data fields (InitViewer actions)
selfData.upperGraphEnd ← upperGraphEnd;
ViewerOps.PaintViewer[viewer: self, hint: client, whatChanged: NIL, clearClient: TRUE];
};
RunCleaner: Menus.MenuProc = {
self: CombinerViewer ← NARROW[parent, ViewerClasses.Viewer];
CC.SimpleCleaner[lowerGraphEnd, clientDataEqual, verbosity];
ViewerOps.PaintViewer[viewer: self, hint: client, whatChanged: NIL, clearClient: TRUE];
};
GetLowerRegions: Menus.MenuProc = {
self: CombinerViewer ← NARROW[parent, ViewerClasses.Viewer];
selfData: CombinerViewerData ← NARROW[self.data];
IF NOT CEG.ThreeOrMoreVertices[lowerGraphEnd] THEN {
MessageWindow.Append[
message : "lowerGraphEnd has fewer than three vertices; can't do regions yet",
clearFirst: TRUE];
MessageWindow.Blink;
RETURN;
};
lowerGraphRegions ← CP.MaximalRegions[lowerGraphEnd, isA];
selfData.lowerGraphRegions ← lowerGraphRegions; -- InitViewer actions
showGraphs ← FALSE;
ViewerOps.PaintViewer[viewer: self, hint: client, whatChanged: NIL, clearClient: TRUE];
};
DumpBothGraphs: Menus.MenuProc = {
IF lowerGraphEnd = NIL OR upperGraphEnd = NIL THEN {
MessageWindow.Append[
message : "You don't have a lowerGraphEnd and a upperGraphEnd polygon",
clearFirst: TRUE];
MessageWindow.Blink;
RETURN;
};
CEG.DumpGraph[ lowerGraphEnd, ropeFromClientData, "///Users/Arnon.pa/Combiner/LowerGraph"];
CEG.DumpGraph[ upperGraphEnd, ropeFromClientData, "///Users/Arnon.pa/Combiner/UpperGraph"];
};
ReadBothGraphs: Menus.MenuProc = {
self: CombinerViewer ← NARROW[parent, ViewerClasses.Viewer];
selfData: CombinerViewerData ← NARROW[self.data];
numberVertices: CARDINAL;
maxVertexIndex: CARDINAL ← 0;
out.PutF["\n\nVertexVerify lowerGraphEnd"];
maxVertexIndex ← EG.VertexVerifyIOGraph["///Users/Arnon.pa/Combiner/LowerGraph", out];
[lowerGraphEnd, numberVertices ] ← CEG.GraphFromFile["///Users/Arnon.pa/Combiner/LowerGraph", clientDataFromRope, out];
EG.vertexIndex ← MAX[maxVertexIndex, numberVertices]; -- don't repeat indices
out.PutF["\n\nVertexVerify upperGraphEnd"];
maxVertexIndex ← EG.VertexVerifyIOGraph["///Users/Arnon.pa/Combiner/UpperGraph", out];
out.PutF["\n"];
[upperGraphEnd, numberVertices ] ← CEG.GraphFromFile["///Users/Arnon.pa/Combiner/UpperGraph", clientDataFromRope, out];
EG.vertexIndex ← MAX[ EG.vertexIndex + numberVertices, maxVertexIndex];
EG.vertexIndex ← EG.vertexIndex + 1; -- set next vertex to be allocated
selfData.lowerGraphEnd ← lowerGraphEnd; -- InitViewer actions
selfData.upperGraphEnd ← upperGraphEnd;
upperGraphOpen ← FALSE; -- Override other input action in progress
ViewerOps.PaintViewer[viewer: self, hint: client, whatChanged: NIL, clearClient: TRUE];
};
DumpLowerGraph: Menus.MenuProc = {
CEG.DumpGraph[ lowerGraphEnd, ropeFromClientData, "///Users/Arnon.pa/Combiner/LowerGraph"];
};
ReadLowerGraph: Menus.MenuProc = {
self: CombinerViewer ← NARROW[parent, ViewerClasses.Viewer];
selfData: CombinerViewerData ← NARROW[self.data];
numberVertices: CARDINAL;
maxVertexIndex: CARDINAL ← 0;
out.PutF["\n\nVertexVerify lowerGraphEnd"];
maxVertexIndex ← EG.VertexVerifyIOGraph["///Users/Arnon.pa/Combiner/LowerGraph", out];
[lowerGraphEnd, numberVertices ] ← CEG.GraphFromFile["///Users/Arnon.pa/Combiner/LowerGraph", clientDataFromRope, out];
EG.vertexIndex ← MAX[maxVertexIndex, numberVertices]; -- don't repeat indices
EG.vertexIndex ← EG.vertexIndex + 1; -- next index to be assigned to a vertex
selfData.lowerGraphEnd ← lowerGraphEnd;
ViewerOps.PaintViewer[viewer: self, hint: client, whatChanged: NIL, clearClient: TRUE];
};
Reset: Menus.MenuProc = {
self: CombinerViewer ← NARROW[parent, ViewerClasses.Viewer];
selfData: CombinerViewerData ← NARROW[self.data];
globalClientData ← NIL;
lowerGraphEnd ← CP.CreateDatabase[];
lowerGraphRegions ← NIL;
upperGraphStart ← upperGraphEnd ← CP.CreateDatabase[];
upperGraphOpen ← FALSE;
selfData.lowerGraphEnd ← lowerGraphEnd; -- repeat InitViewer actions
selfData.upperGraphEnd ← upperGraphEnd;
selfData.lowerGraphRegions ← lowerGraphRegions;
ViewerOps.PaintViewer[viewer: self, hint: client, whatChanged: NIL, clearClient: TRUE];
};
StateGeometry: Menus.MenuProc = {
self: CombinerViewer ← NARROW[parent, ViewerClasses.Viewer];
showClientData ← NOT showClientData;
ViewerOps.PaintViewer[viewer: self, hint: client, whatChanged: NIL, clearClient: TRUE];
};
EdgesVertices: Menus.MenuProc = {
self: CombinerViewer ← NARROW[parent, ViewerClasses.Viewer];
showVerticesOnly ← NOT showVerticesOnly;
labelVertices ← NOT labelVertices;
ViewerOps.PaintViewer[viewer: self, hint: client, whatChanged: NIL, clearClient: TRUE];
};
VertexLabels: Menus.MenuProc = {
self: CombinerViewer ← NARROW[parent, ViewerClasses.Viewer];
labelVertices ← NOT labelVertices;
ViewerOps.PaintViewer[viewer: self, hint: client, whatChanged: NIL, clearClient: TRUE];
};
FillNoFill: Menus.MenuProc = {
self: CombinerViewer ← NARROW[parent, ViewerClasses.Viewer];
fillDraw ← NOT fillDraw;
ViewerOps.PaintViewer[viewer: self, hint: client, whatChanged: NIL, clearClient: TRUE];
};
GraphsRegions: Menus.MenuProc = {
self: CombinerViewer ← NARROW[parent, ViewerClasses.Viewer];
showGraphs ← NOT showGraphs;
ViewerOps.PaintViewer[viewer: self, hint: client, whatChanged: NIL, clearClient: TRUE];
};
Zoom: Menus.MenuProc = {
self: CombinerViewer ← NARROW[parent, ViewerClasses.Viewer];
selfData: CombinerViewerData ← NARROW[self.data];
IF mouseButton=red THEN {
selfData.scale ← selfData.scale*2;
selfData.x ← selfData.x - (1.0 / selfData.scale) * 300.0;
selfData.y ← selfData.y - (1.0 / selfData.scale) * 350.0
}
ELSE {
selfData.x ← selfData.x + (1.0 / selfData.scale) * 300.0;
selfData.y ← selfData.y + (1.0 / selfData.scale) * 350.0;
selfData.scale ← selfData.scale/2
};
ViewerOps.PaintViewer[viewer: self, hint: client, whatChanged: NIL, clearClient: TRUE];
};
Horizontal: Menus.MenuProc = {
self: CombinerViewer ← NARROW[parent, ViewerClasses.Viewer];
selfData: CombinerViewerData ← NARROW[self.data];
IF mouseButton=red THEN selfData.x ← selfData.x - (1.0 / selfData.scale) * 100.0 ELSE
selfData.x ← selfData.x + (1.0 / selfData.scale) * 100.0;
ViewerOps.PaintViewer[viewer: self, hint: client, whatChanged: NIL, clearClient: TRUE];
};
Vertical: Menus.MenuProc = {
self: CombinerViewer ← NARROW[parent, ViewerClasses.Viewer];
selfData: CombinerViewerData ← NARROW[self.data];
IF mouseButton=red THEN selfData.y ← selfData.y + (1.0 / selfData.scale) * 100.0 ELSE
selfData.y ← selfData.y - (1.0 / selfData.scale) * 100.0;
ViewerOps.PaintViewer[viewer: self, hint: client, whatChanged: NIL, clearClient: TRUE];
};
StepGlobalState: Menus.MenuProc = {
color: Color ← NARROW[globalClientData];
newrgb: ImagerColor.RGB;
IF mouseButton=red THEN {
newrgb.R ← color.R - 0.1;
IF newrgb.R <= 0.0 THEN newrgb.R ← 1.0;
newrgb.G ← color.G;
newrgb.B ← color.B;
}
ELSE IF mouseButton=yellow THEN {
newrgb.G ← color.G - 0.1;
IF newrgb.G <= 0.0 THEN newrgb.G ← 1.0;
newrgb.R ← color.R;
newrgb.B ← color.B;
}
ELSE IF mouseButton=blue THEN {
newrgb.B ← color.B - 0.1;
IF newrgb.B <= 0.0 THEN newrgb.B ← 1.0;
newrgb.R ← color.R;
newrgb.G ← color.G;
}
ELSE ERROR;
globalClientData ← NEW[ImagerColor.RGB ← [R: newrgb.R, G: newrgb.G, B: newrgb.B ] ];
since globalClientData is a REF, need NEW[newrgb] to avoid changing previous versions.
out.PutF["\n StepInputColor sets globalClientData to %g, %g, %g", IO.real[newrgb.R], IO.real[newrgb.G], IO.real[newrgb.B] ];
};
GlobalStateRed: Menus.MenuProc = {
globalClientData ← NEW[ImagerColor.RGB ← [R: 1.0, G: 0.0, B: 0.0 ] ];
};
GlobalStateGreen: Menus.MenuProc = {
globalClientData ← NEW[ImagerColor.RGB ← [R: 0.0, G: 1.0, B: 0.0 ] ];
};
GlobalStateBlue: Menus.MenuProc = {
globalClientData ← NEW[ImagerColor.RGB ← [R: 0.0, G: 0.0, B: 1.0 ] ];
};
GlobalStateWhite: Menus.MenuProc = {
globalClientData ← NEW[ImagerColor.RGB ← [R: 1.0, G: 1.0, B: 1.0 ] ];
};
GlobalStateBlack: Menus.MenuProc = {
globalClientData ← NEW[ImagerColor.RGB ← [R: 0.0, G: 0.0, B: 0.0 ] ];
};
RandomTest: Menus.MenuProc = {
DO
DoConvexHull[parent];
RunCombiner[parent];
RunCleaner[parent];
ENDLOOP;
};
DoConvexHull: Menus.MenuProc ~ {
self: CombinerViewer ← NARROW[parent, ViewerClasses.Viewer];
selfData: CombinerViewerData ← NARROW[self.data];
L, LEnd, M: EG.PointList;
x, y: REAL;
xRat, yRat: RN.RatNum;
myClientData: REF;
Generate 20 random points, compute convex hull,
L ← NIL;
FOR I: CARDINAL IN [1..20] DO
x ← Real.Float [Random.ChooseInt[min:1, max:600]];
y ← Real.Float [Random.ChooseInt[min:1, max:400]];
xRat ← RN.RatNumFromREAL[(1.0 / selfData.scale) * x - selfData.x];
yRat ← RN.RatNumFromREAL[ (1.0 / selfData.scale) * y - selfData.y];
IF L = NIL THEN
L ← LEnd ← CONS[ [xRat, yRat], NIL]
ELSE {
LEnd.rest ← CONS[ [xRat,yRat], NIL];
LEnd ← LEnd.rest;
};
ENDLOOP;
M ← CEG.ConvexHull[L];
upperGraphStart ← upperGraphEnd ← NIL;
myClientData ← NEW[ImagerColor.RGB
[R: Real.Float [Random.ChooseInt[min:1, max:100]] / 100.,
G: Real.Float [Random.ChooseInt[min:1, max:100]] / 100.,
B: Real.Float [Random.ChooseInt[min:1, max:100]] / 100. ] ];
WHILE M#NIL DO
[upperGraphStart, upperGraphEnd] ← CEG.AddVertexToPolygon[upperGraphStart, upperGraphEnd, EG.MakePointFromRatNums[M.first.x, M.first.y], myClientData];
M ← M.rest;
ENDLOOP;
selfData.upperGraphEnd ← upperGraphEnd;
ViewerOps.PaintViewer[viewer: self, hint: client, whatChanged: NIL, clearClient: TRUE];
};
TrajectoryFromPointGenerator: PROC [pointGen: CP.PointGenerator] RETURNS [traj: ImagerPath.Trajectory] ~ {
pointAndDone: CP.PointAndDone ← CP.NextPoint[pointGen];
point, firstPoint: CP.Point ← pointAndDone.point;
vec: Imager.VEC ← [point.x, point.y ];
IF pointAndDone.done THEN RETURN[NIL];
traj ← ImagerPath.MoveTo[vec];
pointAndDone ← CP.NextPoint[pointGen];
WHILE NOT pointAndDone.done DO
point ← pointAndDone.point;
vec ← [point.x, point.y ];
traj ← traj.LineTo[vec];
pointAndDone ← CP.NextPoint[pointGen];
ENDLOOP;
vec ← [firstPoint.x, firstPoint.y ]; -- edge from last to first vertex
traj ← traj.LineTo[vec];
};
FillDrawRegionGenerator: PROC [context: Imager.Context, regions: CP.RegionGenerator] ~ {
region: CP.OutlineHolesDataAndDone ← CP.NextRegion[regions];
WHILE NOT region.done DO
color: Color ← NARROW[region.data];
regionOutline: Imager.Trajectory ← TrajectoryFromPointGenerator[region.outline];
IF color # NIL THEN context.SetColor[ImagerColor.ColorFromRGB[color^]] ELSE context.SetColor[ImagerColor.ColorFromRGB[[0.0, 0.0, 0.0]]]; -- black for NIL
context.MaskFillTrajectory[regionOutline];
IF region.holes#NIL THEN FillDrawRegionGenerator[context, region.holes]; -- recursively draw holes
region ← CP.NextRegion[regions];
ENDLOOP;
};
LineDrawRegionGenerator: PROC [context: Imager.Context, regions: CP.RegionGenerator] ~ {
region: CP.OutlineHolesDataAndDone ← CP.NextRegion[regions];
Imager.SetStrokeEnd[context, round];
Imager.SetStrokeWidth[context, SEGMENTWIDTH];
WHILE NOT region.done DO
regionOutline: Imager.Trajectory ← TrajectoryFromPointGenerator[region.outline];
context.MaskStrokeTrajectory[regionOutline];
IF region.holes#NIL THEN LineDrawRegionGenerator[context, region.holes]; -- recursively draw holes
region ← CP.NextRegion[regions];
ENDLOOP;
};
FillDrawRegionList: PROC [context: Imager.Context, regions: CEG.RegionList] ~ {
WHILE regions # NIL DO
region: CEG.Region ← regions.first;
color: Color ← NARROW[region.clientData];
regionOutline: Imager.Trajectory ← region.outline;
IF color # NIL THEN context.SetColor[ImagerColor.ColorFromRGB[color^]] ELSE context.SetColor[ImagerColor.ColorFromRGB[[0.0, 0.0, 0.0]]]; -- black for NIL
context.MaskFillTrajectory[regionOutline];
IF region.holes#NIL THEN FillDrawRegionList[context, region.holes]; -- recursively draw holes
regions ← regions.rest;
ENDLOOP;
};
LineDrawRegionList: PROC [context: Imager.Context, regions: CEG.RegionList] ~ {
Imager.SetStrokeEnd[context, round];
Imager.SetStrokeWidth[context, SEGMENTWIDTH];
WHILE regions # NIL DO
region: CEG.Region ← regions.first;
regionOutline: Imager.Trajectory ← region.outline;
context.MaskStrokeTrajectory[regionOutline];
IF region.holes#NIL THEN LineDrawRegionList[context, region.holes]; -- recursively draw holes
regions ← regions.rest;
ENDLOOP;
};
LineDrawGraph: PUBLIC PROC[context: Imager.Context, v: CEG.ConvexEuclideanGraph, showClientData: BOOLTRUE, showVerticesOnly: BOOLFALSE, labelVertices: BOOLTRUE ] = {
Does Depth First Search. Assumes context color already set.
visitedValue: BOOL;
IF v = NIL THEN RETURN; -- no vertices
Imager.SetStrokeEnd[context, round];
Imager.SetFont[context, timesRomanBI];
IF v.adjacentVertices = NIL THEN { -- one vertex
p1: Imager.VECEG.ImagerVecFromPoint[v.coordinates];
Imager.SetStrokeWidth[context, DOTWIDTH];
Imager.MaskStrokeTrajectory[context, ImagerPath.MoveTo[ p1 ] ];
IF labelVertices THEN {
context.SetXY[[p1.x, p1.y + 7.]];
context.ShowRope[Convert.RopeFromCard[v.index]];
};
}
ELSE { -- two or more vertices
visitedValue ← v.visited;
LineDrawGraphSubr[context, v, showClientData, showVerticesOnly, labelVertices, NOT visitedValue]; -- toggle visitedValue
};
};
LineDrawGraphSubr: PROC[context: Imager.Context, v: CEG.ConvexEuclideanGraph, showClientData: BOOL, showVerticesOnly: BOOL, labelVertices: BOOL, visitedValue: BOOL] = {
Assumes two or more vertices
vAdjList: EG.AdjacencyList ← v.adjacentVertices;
lastVertex, nextVertex: CEG.ConvexEuclideanGraph;
done: BOOL;
vToNext, NextTov: EG.Adjacency;
Record that we've visited v
v.visited ← visitedValue; -- record that we've visited this vertex
Recursive calls for adjacencies of v
lastVertex ← vAdjList.first.vertex;
vAdjList ← vAdjList.rest; -- shift past marker vertex (i.e. first iteration of loop begins after it)
done ← FALSE;
WHILE NOT done DO
nextVertex ← vAdjList.first.vertex;
IF nextVertex = lastVertex THEN done ← TRUE; -- test for last vertex to be processed
IF (nextVertex.visited # visitedValue) THEN
LineDrawGraphSubr[ context, nextVertex, showClientData, showVerticesOnly, labelVertices, visitedValue ]
ELSE {
[vToNext, NextTov] ← EG.FindAdjacency[ v, nextVertex ];
IF NOT showClientData OR NOT clientDataEqual[CEG.GetEdgeClientData[vToNext], CEG.GetEdgeClientData[NextTov] ] THEN {
p1: Imager.VECEG.ImagerVecFromPoint[v.coordinates];
p2: Imager.VECEG.ImagerVecFromPoint[nextVertex.coordinates];
trajectory: Imager.Trajectory ← ImagerPath.MoveTo[p1];
Imager.SetStrokeWidth[context, DOTWIDTH];
Imager.MaskStrokeTrajectory[context, trajectory];
IF labelVertices THEN {
context.SetXY[[p1.x, p1.y + 7.]];
context.ShowRope[Convert.RopeFromCard[v.index]];
};
IF NOT showVerticesOnly THEN {
Imager.SetStrokeWidth[context, SEGMENTWIDTH];
Imager.MaskVector [context, p1, p2 ]; -- Draw [v, nextVertex]
};
trajectory ← ImagerPath.MoveTo[p2];
Imager.SetStrokeWidth[context, DOTWIDTH];
Imager.MaskStrokeTrajectory[context, trajectory];
IF labelVertices THEN {
context.SetXY[[p2.x, p2.y + 7.]];
context.ShowRope[Convert.RopeFromCard[nextVertex.index]];
};
};
};
vAdjList ← vAdjList.rest;
ENDLOOP;
};
ObjectsToInterpress: Menus.MenuProc = {
self: CombinerViewer ← NARROW[parent, ViewerClasses.Viewer];
selfData: CombinerViewerData ← NARROW[self.data];
action: PROC [context: Imager.Context] = {
v, w: CEG.ConvexEuclideanGraph;
Imager.ScaleT[context, Imager.metersPerPoint];
Imager.SetPriorityImportant[context, TRUE];
context.ScaleT[selfData.scale];
context.TranslateT[[selfData.x, selfData.y]];
w ← selfData.lowerGraphEnd;
IF NOT CEG.ThreeOrMoreVertices[w] THEN {
context.SetColor[ImagerColor.ColorFromAtom[$Black]];
LineDrawGraph[context: context, v: w, showClientData: FALSE, showVerticesOnly: FALSE, labelVertices: FALSE]
}
ELSE {
v ← CEG.SpecialPreviousOutlineVertex[w];
FillDrawRegionList[context, CEG.InternalPolygons[ v, w ] ];
CEG.ClearEdgeVisitedFields[v, w];
};
w ← selfData.upperGraphEnd;
IF NOT CEG.ThreeOrMoreVertices[w] THEN {
context.SetColor[ImagerColor.ColorFromAtom[$Black]];
LineDrawGraph[context: context, v: w, showClientData: FALSE, showVerticesOnly: FALSE, labelVertices: FALSE]
}
ELSE {
v ← CEG.SpecialPreviousOutlineVertex[w];
FillDrawRegionList[context, CEG.InternalPolygons[ v, w ] ];
CEG.ClearEdgeVisitedFields[v, w];
};
};
Get an interpress context from the rope, "display" only the visible objects
ip: ImagerInterpress.Ref ← ImagerInterpress.Create[Rope.Cat["///Users/Arnon.pa/Combiner/Art", Convert.RopeFromCard[interpressIndex], ".ip"] ];
interpressIndex ← interpressIndex + 1;
ImagerInterpress.DoPage[ip, action];
ImagerInterpress.Close[ip];
};
InitViewer: ViewerClasses.InitProc -- [self: ViewerClasses.Viewer] -- = {
size: REAL = 100.0;
self.data ← NEW[CombinerViewerDataRec ← [
lowerGraphEnd: NIL,
upperGraphEnd: NIL,
lowerGraphRegions: NIL,
x: 0.0,
y: 0.0
]];
self.name ← "TestBasicCombiner";
};
Notify: ViewerClasses.NotifyProc -- [self:ViewerClasses.Viewer,input:LIST OF REF ANY] -- ={
xy: TIPUser.TIPScreenCoords ← NARROW[input.first];
x, y: RN.RatNum;
point: CP.Point;
clearVal: BOOLTRUE;
selfData: CombinerViewerData ← NARROW[self.data];
x ← RN.RatNumFromREAL[(1.0 / selfData.scale) * xy.mouseX - selfData.x];
y ← RN.RatNumFromREAL[ (1.0 / selfData.scale) * xy.mouseY - selfData.y];
point.x ← (1.0 / selfData.scale) * xy.mouseX - selfData.x;
point.y ← (1.0 / selfData.scale) * xy.mouseY - selfData.y;
SELECT input.rest.first FROM
$RedRelease => {
IF upperGraphOpen THEN {
IF pointCount < 2 THEN {
IF pointCount = 0 THEN {
firstPoint ← point;
pointCount ← 1;
}
ELSE {
upperGraphEnd ← CP.CreatePolygon[firstPoint, point, globalClientData];
selfData.upperGraphEnd ← upperGraphEnd;
pointCount ← 2;
}
}
ELSE {
CP.AddPointToPolygon[point, upperGraphEnd];
selfData.upperGraphEnd ← upperGraphEnd;
pointCount ← pointCount + 1;
};
ViewerOps.PaintViewer[viewer: self, hint: client, whatChanged: NIL, clearClient: TRUE];
};
};
ENDCASE => ERROR;
};
Paint: ViewerClasses.PaintProc = {
[self: Viewer, context: Imager.Context, whatChanged: REF, clear: BOOL]
See ImagerInterpress to expand to write Interpress master, /indigo/peach for how to use Versatec
FillDrawViewerGraphs: PROC ~ {
v, w: CEG.ConvexEuclideanGraph;
context.ScaleT[selfData.scale];
context.TranslateT[[selfData.x, selfData.y]];
w ← selfData.lowerGraphEnd;
IF NOT CEG.ThreeOrMoreVertices[w] THEN {
context.SetColor[ImagerColor.ColorFromAtom[$Black]];
LineDrawGraph[context: context, v: w, showClientData: FALSE, showVerticesOnly: FALSE, labelVertices: FALSE]
}
ELSE {
v ← CEG.SpecialPreviousOutlineVertex[w];
FillDrawRegionList[context, CEG.InternalPolygons[v, w] ];
CEG.ClearEdgeVisitedFields[v, w];
};
w ← selfData.upperGraphEnd;
IF NOT CEG.ThreeOrMoreVertices[w] THEN {
context.SetColor[ImagerColor.ColorFromAtom[$Black]];
LineDrawGraph[context: context, v: w, showClientData: FALSE, showVerticesOnly: FALSE, labelVertices: FALSE]
}
ELSE {
v ← CEG.SpecialPreviousOutlineVertex[w];
FillDrawRegionList[context, CEG.InternalPolygons[v, w] ];
CEG.ClearEdgeVisitedFields[v, w];
};
};
FillDrawLowerRegions: PROC ~ {
L: CP.RegionGenerator;
context.ScaleT[selfData.scale];
context.TranslateT[[selfData.x, selfData.y]];
L ← selfData.lowerGraphRegions;
IF L#NIL THEN {
v, w: CEG.ConvexEuclideanGraph;
w ← selfData.lowerGraphEnd;
v ← CEG.SpecialPreviousOutlineVertex[w];
FillDrawRegionGenerator[context, L];
CEG.ClearEdgeVisitedFields[v, w];
};
};
LineDrawViewerGraphs: PROC ~ {
rgb: ImagerColor.RGB;
context.ScaleT[selfData.scale];
context.TranslateT[[selfData.x, selfData.y]];
rgb ← [1.0, 0.0, 0.0]; -- red
context.SetColor[ImagerColor.ColorFromRGB[rgb]];
LineDrawGraph[context: context, v: selfData.lowerGraphEnd, showClientData: showClientData, showVerticesOnly: showVerticesOnly, labelVertices: labelVertices];
rgb ← [0.0, 1.0, 0.0]; -- green
context.SetColor[ImagerColor.ColorFromRGB[rgb]];
LineDrawGraph[context: context, v: selfData.upperGraphEnd, showClientData: showClientData, showVerticesOnly: showVerticesOnly, labelVertices: labelVertices];
};
LineDrawLowerRegions: PROC ~ {
w: CP.RegionGenerator;
context.ScaleT[selfData.scale];
context.TranslateT[[selfData.x, selfData.y]];
w ← selfData.lowerGraphRegions;
IF w#NIL THEN {
context.SetColor[ImagerColor.ColorFromAtom[$Black]];
LineDrawRegionGenerator[context, w];
};
};
selfData: CombinerViewerData ← NARROW[self.data];
context.ScaleT[0.0254/72]; -- conversion from meters to inches not needed????
SELECT whatChanged FROM
NIL => {
IF showGraphs THEN {
IF fillDraw THEN context.DoSaveAll[FillDrawViewerGraphs] ELSE context.DoSaveAll[LineDrawViewerGraphs];
}
ELSE {
IF fillDraw THEN context.DoSaveAll[FillDrawLowerRegions] ELSE context.DoSaveAll[LineDrawLowerRegions];
};
};
ENDCASE => ERROR;
};
RegisterCombinerViewerClass: PROC ~ {
menu: Menus.Menu ← Menus.CreateMenu[4];
combinerViewerClass: ViewerClasses.ViewerClass;
Menus.AppendMenuEntry[menu:menu, entry:Menus.CreateEntry [name:"Reset", proc: Reset], line:0];
Menus.AppendMenuEntry[menu:menu, entry:Menus.CreateEntry [name:"DumpLower ", proc: DumpLowerGraph], line:0];
Menus.AppendMenuEntry[menu:menu, entry:Menus.CreateEntry [name:"LoadLower ", proc: ReadLowerGraph], line:0];
Menus.AppendMenuEntry[menu:menu, entry:Menus.CreateEntry [name:"DumpBoth ", proc: DumpBothGraphs], line:0];
Menus.AppendMenuEntry[menu:menu, entry:Menus.CreateEntry [name:"LoadBoth ", proc: ReadBothGraphs], line:0];
Menus.AppendMenuEntry[menu:menu, entry:Menus.CreateEntry [name:"IP ", proc: ObjectsToInterpress], line:0];
Menus.AppendMenuEntry[menu:menu, entry:Menus.CreateEntry [name:"UppStart ", proc: UpperPolygonStart], line:1];
Menus.AppendMenuEntry[menu:menu, entry:Menus.CreateEntry [name:"UppDone ", proc: UpperPolygonDone], line:1];
Menus.AppendMenuEntry[menu:menu, entry:Menus.CreateEntry [name:"RandHull", proc: DoConvexHull], line:1];
Menus.AppendMenuEntry[menu:menu, entry:Menus.CreateEntry [name:"Combine ", proc: RunCombiner], line:1];
Menus.AppendMenuEntry[menu:menu, entry:Menus.CreateEntry [name:"Clean ", proc: RunCleaner], line:1];
Menus.AppendMenuEntry[menu:menu, entry:Menus.CreateEntry [name:"GetLowRegions ", proc: GetLowerRegions], line:1];
Menus.AppendMenuEntry[menu:menu, entry:Menus.CreateEntry [name:"RandTest", proc: RandomTest], line:1];
Menus.AppendMenuEntry[menu:menu, entry:Menus.CreateEntry [name:"Red", proc: GlobalStateRed], line:2];
Menus.AppendMenuEntry[menu:menu, entry:Menus.CreateEntry [name:"Green", proc: GlobalStateGreen], line:2];
Menus.AppendMenuEntry[menu:menu, entry:Menus.CreateEntry [name:"Blue", proc: GlobalStateBlue], line:2];
Menus.AppendMenuEntry[menu:menu, entry:Menus.CreateEntry [name:"White", proc: GlobalStateWhite], line:2];
Menus.AppendMenuEntry[menu:menu, entry:Menus.CreateEntry [name:"Black", proc: GlobalStateBlack], line:2];
Menus.AppendMenuEntry[menu:menu, entry:Menus.CreateEntry [name:"Step", proc: StepGlobalState], line:2];
Menus.AppendMenuEntry[menu:menu, entry:Menus.CreateEntry [name:"SetUppState ", proc: SetUpperPolygonClientData], line:2];
Menus.AppendMenuEntry[menu:menu, entry:Menus.CreateEntry [name:"StateGeometry ", proc: StateGeometry], line:3];
Menus.AppendMenuEntry[menu:menu, entry:Menus.CreateEntry [name:"EdgesVertices ", proc: EdgesVertices], line:3];
Menus.AppendMenuEntry[menu:menu, entry:Menus.CreateEntry [name:"VertexLabels ", proc: VertexLabels], line:3];
Menus.AppendMenuEntry[menu:menu, entry:Menus.CreateEntry [name:"FillNoFill ", proc: FillNoFill], line:3];
Menus.AppendMenuEntry[menu:menu, entry:Menus.CreateEntry [name:"GraphsRegions ", proc: GraphsRegions], line:3];
Menus.AppendMenuEntry[menu:menu, entry:Menus.CreateEntry [name:"Zoom ", proc: Zoom], line:3];
Menus.AppendMenuEntry[menu:menu, entry:Menus.CreateEntry [name:"Horizontal ", proc: Horizontal], line:3];
Menus.AppendMenuEntry[menu:menu, entry:Menus.CreateEntry [name:"Vertical ", proc: Vertical], line:3];
combinerViewerClass ← NEW[ViewerClasses.ViewerClassRec ← [
flavor: $CombinerViewer,
init: InitViewer,
notify: Notify,
tipTable: TIPUser.InstantiateNewTIPTable["ConvexCombiner.tip"],
paint: Paint,
menu: menu
]];
ViewerOps.RegisterViewerClass[flavor: $CombinerViewer, class: combinerViewerClass];
};
**** Top level variables
Combiner Log title
logTitle: Rope.ROPE;
Global objects
globalClientData: REF;
interpressIndex: CARDINAL ← 1;
(Lower and Upper) Graph display options
showClientData: BOOLTRUE; -- global variables for control of CEG.ConvexEuclideanGraph display format
showVerticesOnly: BOOLFALSE;
labelVertices: BOOLTRUE;
fillDraw: BOOLTRUE;
showGraphs: BOOLTRUE;
Lower Graph objects
lowerGraphEnd: CP.DBID; -- The convention is that if the lower structure exists, then lowerGraphEnd is one of its outline vertices.
lowerGraphRegions: CP.RegionGenerator;
Upper Graph objects
upperGraphStart, upperGraphEnd: CP.PolyID; -- The convention is that if the upper structure exists, then upperGraphEnd is one of its outline vertices
upperGraphOpen: BOOLEAN; -- true when an upperGraphEnd (which currently must be a polygon) is being read in
saveVertexIndex: INT; -- for backup if unacceptable upperGraphEnd read
pointCount: CARDINAL;
firstPoint: CP.Point;
in: IO.STREAM;
out: IO.STREAM;
verbosity: Verbosity ← NEW[CEG.VerbosityRec ← [in: NIL, out: NIL] ];
Client data type
Color: TYPE = REF ImagerColor.RGB;
Client data procedures
clientDataFromRope: CEG.ClientDataFromRope ~ {
char: CHAR;
dataStream: IO.STREAMIO.RIS[rope];
red, green, blue: REAL;
clientData: Color;
DataFromRopeFail: PUBLIC ERROR [subclass: ATOM ← $Unspecified] = CODE;
char ← dataStream.GetChar[];
IF char # '( THEN DataFromRopeFail[$LeftParenExpected];
red ← IO.GetReal[ dataStream];
[]← dataStream.SkipWhitespace[]; char ← dataStream.GetChar[];
IF char # '/ THEN DataFromRopeFail[$SlashExpected];
green ← IO.GetReal[ dataStream];
[]← dataStream.SkipWhitespace[]; char ← dataStream.GetChar[];
IF char # '/ THEN DataFromRopeFail[$SlashExpected];
blue ← IO.GetReal[ dataStream];
[]← dataStream.SkipWhitespace[]; char ← dataStream.GetChar[];
IF char # ') THEN DataFromRopeFail[$RightParenExpected];
clientData ← NEW[ImagerColor.RGB ← [R: red, G: green, B: blue] ];
RETURN[clientData];
};
ropeFromClientData: CEG.RopeFromClientData ~ {
clientData: Color ← NARROW[ref];
out: Rope.ROPE;
IF clientData = NIL THEN
out ← Rope.Concat[out, "NIL"]
ELSE
out ← Rope.Cat[out, "(", Convert.RopeFromReal[clientData.R], "/", Rope.Cat[Convert.RopeFromReal[clientData.G], "/", Convert.RopeFromReal[clientData.B], ")"] ];
RETURN[out];
};
clientDataEqual: CP.RegionGlueProc ~ {
color1, color2: Color;
tol: REAL ← 0.005;
IF clientData1=NIL OR clientData2=NIL THEN RETURN[clientData1=NIL AND clientData2 = NIL]; -- all ClientDataEqual's should contain this test
color1 ← NARROW[clientData1];
color2 ← NARROW[clientData2];
RETURN[ ABS[color1.R - color2.R] < tol AND
ABS[ color1.G - color2.G] < tol AND
ABS[ color1.B - color2.B ] < tol ];
};
isA: CP.IsAProc ~ { -- This one differentiates red and not red
color: Color;
tol: REAL ← 0.005;
IF clientData = NIL THEN RETURN[FALSE];
color ← NARROW[clientData];
RETURN[ ABS[color.R - 1.0] < tol AND
ABS[ color.G - 0.0] < tol AND
ABS[ color.B - 0.0 ] < tol ];
};
clientDataCombiner: CP.RegionOverlapProc = {
currentColor: Color ← NARROW[currentClientdata];
inputColor: Color ← NARROW[inputClientdata];
red, green, blue: REAL;
IF currentColor=NIL THEN RETURN[inputColor];
IF inputColor=NIL THEN RETURN[currentColor]; -- all ClientDataCombiner's should contain these two lines
IF inputColor.R > 0.995 AND inputColor.G < 0.005 AND inputColor.B < 0.005 THEN
RETURN[ NEW[ImagerColor.RGB ← [R: 1.0, G: 0.0, B: 0.0] ] ]; -- red is positive area
red ← MIN[currentColor.R + inputColor.R, 1.0];
green ← MIN[currentColor.G + inputColor.G, 1.0];
blue ← MIN[currentColor.B + inputColor.B, 1.0];
RETURN[ NEW[ImagerColor.RGB ← [R: red, G: green, B: blue] ] ]
};
**** End of Top level variables
RegisterCombinerViewerClass[];
Step through Reset procedure, creating CombinerViewer along the way (in place of painting the existing viewer), and relying on InitViewer to set CEG.ConvexEuclideanGraph fields of viewer (in place of setting them explicitly).
globalClientData ← NIL;
lowerGraphEnd ← CP.CreateDatabase[];
lowerGraphRegions ← NIL;
upperGraphStart ← upperGraphEnd ← CP.CreateDatabase[];
upperGraphOpen ← FALSE;
[] ← ViewerOps.CreateViewer[flavor: $CombinerViewer, info: [iconic:FALSE, column: color] ];
End of Step through Reset procedure
Create Combiner Log
logTitle ← IO.PutFR["Combiner Log"];
[in, out] ← ViewerIO.CreateViewerStreams[logTitle];
verbosity.in ← in; verbosity.out ← out;
Initialize random numbers
[] ← Random.Create[range: 0, seed: -1];
END.