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: BOOL _ TRUE; 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; }; 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; [lowerGraphEnd, numberVertices ] _ CEG.GraphFromFile["///Users/Arnon.pa/Combiner/LowerGraph", clientDataFromRope, out]; EG.vertexIndex _ MAX[maxVertexIndex, numberVertices]; -- don't repeat indices [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; [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]; }; 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 ] ]; }; 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; 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: BOOL _ TRUE, showVerticesOnly: BOOL _ FALSE, labelVertices: BOOL _ TRUE ] = { 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.VEC _ EG.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] = { vAdjList: EG.AdjacencyList _ v.adjacentVertices; lastVertex, nextVertex: CEG.ConvexEuclideanGraph; done: BOOL; vToNext, NextTov: EG.Adjacency; v.visited _ visitedValue; -- record that we've visited this vertex 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.VEC _ EG.ImagerVecFromPoint[v.coordinates]; p2: Imager.VEC _ EG.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]; }; }; 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]; point: CP.Point; clearVal: BOOL _ TRUE; selfData: CombinerViewerData _ NARROW[self.data]; 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 = { 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]; 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:"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:"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]; }; globalClientData: REF; interpressIndex: CARDINAL _ 1; showClientData: BOOL _ TRUE; -- global variables for control of CEG.ConvexEuclideanGraph display format showVerticesOnly: BOOL _ FALSE; labelVertices: BOOL _ TRUE; fillDraw: BOOL _ TRUE; showGraphs: BOOL _ TRUE; lowerGraphEnd: CP.DBID; -- The convention is that if the lower structure exists, then lowerGraphEnd is one of its outline vertices. lowerGraphRegions: CP.RegionGenerator; 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; out: IO.STREAM; verbosity: Verbosity _ NEW[CEG.VerbosityRec _ [in: NIL, out: NIL] ]; Color: TYPE = REF ImagerColor.RGB; clientDataFromRope: CEG.ClientDataFromRope ~ { char: CHAR; dataStream: IO.STREAM _ IO.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] ] ] }; RegisterCombinerViewerClass[]; globalClientData _ NIL; lowerGraphEnd _ CP.CreateDatabase[]; lowerGraphRegions _ NIL; upperGraphStart _ upperGraphEnd _ CP.CreateDatabase[]; upperGraphOpen _ FALSE; [] _ ViewerOps.CreateViewer[flavor: $CombinerViewer, info: [iconic:FALSE, column: color] ]; [] _ Random.Create[range: 0, seed: -1]; END. ‚CombinePolyTester.mesa Last Edited by: Arnon, June 20, 1985 4:46:23 pm PDT Pier, June 2, 1986 2:46:16 pm PDT 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. out.PutF["\n\nVertexVerify lowerGraphEnd"]; maxVertexIndex _ EG.VertexVerifyIOGraph["///Users/Arnon.pa/Combiner/LowerGraph", out]; out.PutF["\n\nVertexVerify upperGraphEnd"]; maxVertexIndex _ EG.VertexVerifyIOGraph["///Users/Arnon.pa/Combiner/UpperGraph", out]; out.PutF["\n"]; out.PutF["\n\nVertexVerify lowerGraphEnd"]; maxVertexIndex _ EG.VertexVerifyIOGraph["///Users/Arnon.pa/Combiner/LowerGraph", out]; 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]; }; 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] ]; Generate 20 random points, compute convex hull, Does Depth First Search. Assumes context color already set. Assumes two or more vertices Record that we've visited v Recursive calls for adjacencies of v Get an interpress context from the rope, "display" only the visible objects x, y: RN.RatNum; x _ RN.RatNumFromREAL[(1.0 / selfData.scale) * xy.mouseX - selfData.x]; y _ RN.RatNumFromREAL[ (1.0 / selfData.scale) * xy.mouseY - selfData.y]; [self: Viewer, context: Imager.Context, whatChanged: REF, clear: BOOL] See ImagerInterpress to expand to write Interpress master, /indigo/peach for how to use Versatec context.ScaleT[0.0254/72]; -- conversion from meters to inches not needed???? Menus.AppendMenuEntry[menu:menu, entry:Menus.CreateEntry [name:"RandTest", proc: RandomTest], line:1]; 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]; **** Top level variables Combiner Log title logTitle: Rope.ROPE; Global objects (Lower and Upper) Graph display options Lower Graph objects Upper Graph objects in: IO.STREAM; Client data type Client data procedures **** End of Top level variables 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). 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 ʉ˜šœ™J™3Icode™!J™—šÏk ˜ Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ ˜ Jšœ˜Jšœ ˜ Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ ˜ Jšœ ˜ Jšœ ˜ Jšœ˜Jšœ˜J˜Kšœ˜KšœÏc˜ K˜K˜J˜—Ihead2šœœ˜ JšœœÇœ˜â˜Jšœ˜ J˜—Jšœœœœ œœœœ˜oJ˜Jšœœž'˜Jšœ'˜'Jšœœž*˜BJšœ?œœ˜WJ˜J˜—šŸœ˜"Jšœ œN˜[J˜J˜—šŸœ˜"Jšœœ˜ž˜™Kšœ*˜*Kšœœœ1ž˜bKšœ œ˜ Kšœ˜—K˜K˜—šŸœœ$œ˜XKšœœœ˜ž˜™Kšœ*˜*Kšœœœ,ž˜]Kšœ˜Kšœ˜—K˜K˜—šŸœœ$œ˜OJšœ$˜$Jšœ œ˜-šœ œ˜Kšœœ˜#Kšœ2˜2Kšœ,˜,Kšœœœ,ž˜]Kšœ˜Kšœ˜—K˜K˜—K˜šŸ œœœGœœœœœœ˜®J•commentTRUEšœ<™J˜—Jšœ#˜#Jšœœ˜)J˜1šœœ˜Jšœ!˜!Jšœ9˜9J˜—J˜—J˜—Jšœ˜Jšœ˜—Jšœ˜J™—šŸœ˜'Jšœœ˜˜>Jšœ œ"˜3Jšœœ˜ Jšœ>˜>Jšœ œ"˜3Jšœœ˜Jšœ>˜>Jšœ œ'˜8Jšœ œ œ!˜AJšœ ˜J˜J˜—šœœ˜.Jšœœ˜ Jšœ œ˜šœœ˜K˜—š˜Kšœ ˜ —Kšœ˜ K˜J˜—šœœ˜&Jšœ˜Jšœœ ˜Jšœ œœ œœœ œœœž1˜ŒJšœ œ˜Jšœ œ˜šœœ˜*Kšœ˜#Kšœ ˜#—K˜K˜—šœœ ž*˜>Jšœ ˜ Jšœœ ˜Jš œœœœœ˜'Jšœœ ˜šœœ˜$Kšœ˜Kšœ˜—K˜J˜—šœœ˜,Jšœœ˜0Jšœœ˜,Kšœœ˜Jšœœœœ ˜,Jš œ œœœž:˜gšœœœ˜NJšœœ œ"ž˜T—Kšœœ%˜.Kšœœ%˜0Kšœœ%˜/Jšœœ œ"˜=J˜J˜—J˜Jšž™J™Jšœ˜J˜JšœŸ œY™âJšœœ˜Jšœœ˜%Jšœœ˜Jšœ$œ˜8Jšœœ˜JšœCœ˜[Jšœ#™#J™Jšž™Jšœ œ™$Jšœ3™3Jšœ(™(J˜Jšœ™Jšœ'˜'J˜Jšœ˜˜J™——…—tœŸ§