<> <> <> <> <> DIRECTORY Containers, CoordSys, Graphics, Icons, Imager, ImagerBridge, Interminal, IO, Menus, PadGraphics, Rope, Scratchpad2d, Scratchpad2dUser, SV2d, SVError, SVInterfaceTypes, SVModelTypes, SVLines2d, SVPolygon2d, SVScratchpadMonitor, SVVector2d, SweepGeometry, TIPUser, ViewerClasses, ViewerOps; Scratchpad2dImpl: CEDAR PROGRAM IMPORTS Containers, CoordSys, Imager, ImagerBridge, Icons, IO, Menus, PadGraphics, Scratchpad2dUser, SVLines2d, SVScratchpadMonitor, SVPolygon2d, TIPUser, SVError, SVVector2d, ViewerOps EXPORTS Scratchpad2d SHARES ViewerClasses = BEGIN CoordSystem: TYPE = SVModelTypes.CoordSystem; EditToolData: TYPE = SVInterfaceTypes.EditToolData; ImagerProc: TYPE = SVInterfaceTypes.ImagerProc; Path: TYPE = SV2d.Path; Point2d: TYPE = SV2d.Point2d; PointPolyClass: TYPE = SVPolygon2d.PointPolyClass; Polygon: TYPE = SV2d.Polygon; TrigLineSeg: TYPE = SV2d.TrigLineSeg; Vector2d: TYPE = SV2d.Vector2d; Viewer: TYPE = ViewerClasses.Viewer; entryHeight: CARDINAL = 15; -- height of a line of items entryVSpace: CARDINAL = 3; -- vertical leading between lines entryHSpace: CARDINAL = 10; -- horizontal space between items on a line ScratchViewerData: TYPE = REF ScratchViewerDataObj; ScratchViewerDataObj: TYPE = SVInterfaceTypes.ScratchViewerDataObj; ScratchpadData: TYPE = REF ScratchpadDataObj; ScratchpadDataObj: TYPE = SVInterfaceTypes.ScratchpadDataObj; ScratchpadPaint: SAFE PROC [self: Viewer, context: Graphics.Context, whatChanged: REF ANY, clear: BOOL] = TRUSTED { <> <> scratchpadData: ScratchpadData; scratchViewerState: ScratchViewerData; IF whatChanged = NIL THEN {--we are being called by Window Manager scratchpadData _ NARROW[self.data]; Scratchpad2dUser.PlaceOrigin[self]; Scratchpad2dUser.DrawSceneButton[self, scratchpadData.scratchViewerData, red, FALSE, FALSE]; } ELSE { proc: ImagerProc; scratchViewerState _ NARROW[whatChanged]; scratchpadData _ NARROW[scratchViewerState.scratchpad.data]; proc _ scratchpadData.proc; ImagerBridge.SetViewFromGraphicsContext[scratchpadData.imager, context]; proc[scratchpadData.imager]; RETURN}; }; Init: PROC = { scratchpadClass: ViewerClasses.ViewerClass; <> scratchpadClass _ NEW[ViewerClasses.ViewerClassRec _ [ paint: ScratchpadPaint, notify: InputNotify, tipTable: TIPUser.InstantiateNewTIPTable["Scratchpad2d.TIP"] ]]; ViewerOps.RegisterViewerClass[$Scratchpad2d, scratchpadClass]; }; IntMax: PRIVATE PROC [a, b: INTEGER] RETURNS [max: INTEGER] = { RETURN[IF a>= b THEN a ELSE b]; }; CreateScratchpad: PUBLIC PROC [parent: ViewerClasses.Viewer, editToolData: EditToolData] RETURNS [container: Viewer, scratchpad: Viewer] = TRUSTED { <> <> < DESTROY SAVE NEWLINE ERASE>> <> world: CoordSystem; padMenu: Menus.Menu; scratchViewerData: ScratchViewerData _ NEW[ScratchViewerDataObj]; scratchpadData: ScratchpadData _ NEW[ScratchpadDataObj]; -- make the data object for the solid window <> padMenu _ Menus.CreateMenu[2]; Menus.AppendMenuEntry[ menu: padMenu, entry: Menus.CreateEntry[ name: "Erase", proc: Scratchpad2dUser.EraseButton, clientData: scratchViewerData, documentation: "erase the scratchpad" ], line: 0 ]; Menus.AppendMenuEntry[ menu: padMenu, entry: Menus.CreateEntry[ name: "Normals", proc: Scratchpad2dUser.NormalsButton, clientData: scratchViewerData, documentation: "show normals to all current segments" ], line: 0 ]; <> Menus.AppendMenuEntry[ menu: padMenu, entry: Menus.CreateEntry[ name: "CrossHairs", proc: Scratchpad2dUser.CrossHairsButton, clientData: scratchViewerData, documentation: "draw crosshairs to show current origin" ], line: 0 ]; Menus.AppendMenuEntry[ menu: padMenu, entry: Menus.CreateEntry[ name: "NewLin", proc: Scratchpad2dUser.NewLinButton, clientData: scratchViewerData, documentation: "prepare to draw the outline of a linear sweep" ], line: 0 ]; Menus.AppendMenuEntry[ menu: padMenu, entry: Menus.CreateEntry[ name: "NewRevo", proc: Scratchpad2dUser.NewRevoButton, clientData: scratchViewerData, documentation: "prepare to draw the outline of a revolute sweep" ], line: 0 ]; Menus.AppendMenuEntry[ menu: padMenu, entry: Menus.CreateEntry[ name: "TestPoints", proc: Scratchpad2dUser.TestPointsButton, clientData: scratchViewerData, documentation: "prepare to perform in-on-out test on mouse points" ], line: 0 ]; Menus.AppendMenuEntry[ menu: padMenu, entry: Menus.CreateEntry[ name: "Circle", proc: Scratchpad2dUser.CircleButton, clientData: scratchViewerData, documentation: "draw a circle centered on the origin" ], line: 0 ]; Menus.AppendMenuEntry[ menu: padMenu, entry: Menus.CreateEntry[ name: "Slice", proc: Scratchpad2dUser.DrawSliceButton, clientData: scratchViewerData, documentation: "Draw the current slice from the selected viewer" ], line: 1 ]; Menus.AppendMenuEntry[ menu: padMenu, entry: Menus.CreateEntry[ name: "Interpress", proc: Scratchpad2dUser.HardcopyButton, clientData: scratchViewerData, documentation: "Make an interpress document of this scene" ], line: 1 ]; Menus.AppendMenuEntry[ menu: padMenu, entry: Menus.CreateEntry[ name: "SliceRepeat", proc: Scratchpad2dUser.SliceRepeatButton, clientData: scratchViewerData, documentation: "Draw the current slice. Don't recompute tree" ], line: 1 ]; <> scratchViewerData.outer _ Containers.Create[[ name: "Scratchpad2d", menu: padMenu, iconic: TRUE, column: right,-- data is set by the Containers package scrollable: TRUE, icon: Icons.NewIconFromFile["SolidViews.icons", 1] ]]; scratchViewerData.editToolData _ editToolData; world _ CoordSys.CreateRoot["WORLD"]; scratchpadData.origin _ [200, 200]; -- will be updated every window manager paint scratchpadData.path _ SVPolygon2d.CreatePath[IntMax[SweepGeometry.maxMeshLen, SweepGeometry.maxLinesOfLat]]; scratchpadData.mode _ off; scratchpadData.scratchViewerData _ scratchViewerData; scratchpadData.imager _ Imager.Create[$LFDisplay]; Imager.ScaleT[scratchpadData.imager, 3.527778e-4]; -- units in screen dots <> scratchViewerData.scratchpad _ ViewerOps.CreateViewer[ flavor: $Scratchpad2d, info: [ parent: scratchViewerData.outer, wx: 0, wy: scratchViewerData.height, ww: scratchViewerData.outer.ww, wh: scratchViewerData.outer.wh,-- only initial values for ww and wh. They are constrained below data: scratchpadData,-- contains the current scene scrollable: FALSE ] ]; Containers.ChildXBound[scratchViewerData.outer, scratchViewerData.scratchpad]; Containers.ChildYBound[scratchViewerData.outer, scratchViewerData.scratchpad]; scratchViewerData.height _ scratchViewerData.height + scratchViewerData.scratchpad.wh; ViewerOps.PaintViewer[scratchViewerData.outer, all]; container _ scratchViewerData.outer; scratchpad _ scratchViewerData.scratchpad; }; -- end of CreateScratchpad GetPath: PUBLIC PROC [self: Viewer] RETURNS [path: Path] = { <> scratchpadData: ScratchpadData _ NARROW[self.data]; path _ scratchpadData.path; }; InputNotify: PUBLIC SAFE PROCEDURE [self: ViewerClasses.Viewer, input: LIST OF REF ANY] = TRUSTED { <> IF ISTYPE[input.first, TIPUser.TIPScreenCoords] THEN { controlPoint: Point2d; mousePlace: TIPUser.TIPScreenCoords _ NARROW[input.first]; scratchpadData: ScratchpadData _ NARROW[self.data]; context: Imager.Context _ scratchpadData.imager; p: Imager.Pair; p _ Imager.Transform[Imager.Invert[context.state.T], [mousePlace.mouseX, mousePlace.mouseY]]; controlPoint[1] _ p.x; controlPoint[2] _ p.y; <> IF ISTYPE[input.rest.first, ATOM] THEN SELECT input.rest.first FROM $DRAW => { scratchpadData: ScratchpadData _ NARROW[self.data]; SELECT scratchpadData.mode FROM off => RETURN; pointAt => TestPoint[controlPoint, scratchpadData]; drawLin =>{ AddLinPoint[controlPoint, scratchpadData]; Scratchpad2dUser.DrawSceneButton[self, scratchpadData.scratchViewerData, red, FALSE, FALSE]; }; drawRevo =>{ AddRevoPoint[controlPoint, scratchpadData]; Scratchpad2dUser.DrawSceneButton[self, scratchpadData.scratchViewerData, red, FALSE, FALSE]; }; ENDCASE => ERROR; }; $MOVE => { scratchpadData: ScratchpadData _ NARROW[self.data]; SELECT scratchpadData.mode FROM off => RETURN; pointAt => RETURN; drawLin =>{SVScratchpadMonitor.NewMotion[controlPoint, NARROW[input.rest.first], scratchpadData]; }; drawRevo =>{SVScratchpadMonitor.NewMotion[controlPoint, NARROW[input.rest.first], scratchpadData]; }; ENDCASE => ERROR; }; $MOVESTART => { scratchpadData: ScratchpadData _ NARROW[self.data]; SELECT scratchpadData.mode FROM off => RETURN; pointAt => RETURN; drawLin =>{SVScratchpadMonitor.NewMotion[controlPoint, NARROW[input.rest.first], scratchpadData]}; drawRevo =>{SVScratchpadMonitor.NewMotion[controlPoint, NARROW[input.rest.first], scratchpadData]}; ENDCASE => ERROR; }; $MOVEEND => { scratchpadData: ScratchpadData _ NARROW[self.data]; SELECT scratchpadData.mode FROM off => RETURN; pointAt => RETURN; drawLin =>{ SVScratchpadMonitor.NewMotion[controlPoint, NARROW[input.rest.first], scratchpadData]; <> }; drawRevo =>{ SVScratchpadMonitor.NewMotion[controlPoint, NARROW[input.rest.first], scratchpadData]; <> }; ENDCASE => ERROR; }; $ERASE => { scratchpadData: ScratchpadData _ NARROW[self.data]; SELECT scratchpadData.mode FROM off => RETURN; pointAt => RETURN; drawLin =>{DeleteLinPoint[controlPoint, scratchpadData]; Scratchpad2dUser.DrawSceneButton[self, scratchpadData.scratchViewerData, red, FALSE, FALSE]}; drawRevo =>{DeleteRevoPoint[controlPoint, scratchpadData]; Scratchpad2dUser.DrawSceneButton[self, scratchpadData.scratchViewerData, red, FALSE, FALSE]}; ENDCASE => ERROR; }; $SPLICE => { scratchpadData: ScratchpadData _ NARROW[self.data]; SELECT scratchpadData.mode FROM off => RETURN; pointAt => RETURN; drawLin =>{SpliceLinPoint[controlPoint, scratchpadData]; Scratchpad2dUser.DrawSceneButton[self, scratchpadData.scratchViewerData, red, FALSE, FALSE]}; drawRevo =>{SpliceRevoPoint[controlPoint, scratchpadData]; Scratchpad2dUser.DrawSceneButton[self, scratchpadData.scratchViewerData, red, FALSE, FALSE]}; ENDCASE => ERROR; }; ENDCASE => ERROR; }; -- end is a mouse point }; -- end of InputNotify TestPoint: PROC [screenPoint2d: Point2d, scratchpadData: ScratchpadData] = TRUSTED { poly: Polygon; inOnOut: PointPolyClass; inOnOutRope: Rope.ROPE; testRope: Rope.ROPE; padPoint: Point2d; poly _ SVPolygon2d.PathToPolygon[scratchpadData.path]; padPoint _ PadGraphics.ScreenToPad[screenPoint2d, scratchpadData.origin]; inOnOut _ SVPolygon2d.BoysePointInPoly[padPoint, poly]; SELECT inOnOut FROM in => inOnOutRope _ "in"; on => inOnOutRope _ "on"; out => inOnOutRope _ "out"; ENDCASE => ERROR; testRope _ IO.PutFR["Point: [%g, %g] is %g poly.",[real[padPoint[1]]], [real[padPoint[2]]], [rope[inOnOutRope]]]; SVError.Append[testRope, TRUE, TRUE]; }; -- end of TestPoint AddLinPoint: PROC [screenPoint2d: Point2d, scratchpadData: ScratchpadData] = TRUSTED { area: REAL; poly: Polygon; areaRope: Rope.ROPE; IF scratchpadData.path.len = SweepGeometry.maxMeshLen THEN {ComplainTooManyLinPoints[]; RETURN}; scratchpadData.path _ SVPolygon2d.AddPathPoint[scratchpadData.path, PadGraphics.ScreenToPad[screenPoint2d, scratchpadData.origin]]; poly _ SVPolygon2d.PathToPolygon[scratchpadData.path]; area _ SVPolygon2d.SignedArea[poly]; areaRope _ IO.PutFR["Poly has area: %g",[real[area]]]; SVError.Append[areaRope, TRUE, TRUE]; }; -- end of AddLinPoint AddRevoPoint: PROC [screenPoint2d: Point2d, scratchpadData: ScratchpadData] = TRUSTED { <> area: REAL; poly: Polygon; areaRope: Rope.ROPE; p: Point2d; p _ PadGraphics.ScreenToPad[screenPoint2d, scratchpadData.origin]; IF p[1] < 0 THEN p[1] _ -p[1]; IF scratchpadData.path.len = SweepGeometry.maxLinesOfLat THEN {ComplainTooManyRevoPoints[]; RETURN}; scratchpadData.path _ SVPolygon2d.AddPathPoint[scratchpadData.path, p]; poly _ SVPolygon2d.PathToPolygon[scratchpadData.path]; <> area _ SVPolygon2d.SignedArea[poly]; areaRope _ IO.PutFR["Poly has area: %g",[real[area]]]; SVError.Append[areaRope, TRUE, TRUE]; }; -- end of AddRevoPoint MoveLinStart: PUBLIC PROC [screenPoint2d: Point2d, scratchpadData: ScratchpadData] = TRUSTED { newPoint, oldPoint: Point2d; index: NAT; success: BOOL; newPoint _ PadGraphics.ScreenToPad[screenPoint2d, scratchpadData.origin]; [oldPoint, index, success] _ PointClosestTo[newPoint, scratchpadData]; IF NOT success THEN RETURN; scratchpadData.index _ index; MoveLinPoint[screenPoint2d, scratchpadData]; }; MoveRevoStart: PUBLIC PROC [screenPoint2d: Point2d, scratchpadData: ScratchpadData] = TRUSTED { newPoint, oldPoint: Point2d; index: NAT; success: BOOL; newPoint _ PadGraphics.ScreenToPad[screenPoint2d, scratchpadData.origin]; IF newPoint[1] < 0 THEN newPoint[1] _ -newPoint[1]; [oldPoint, index, success] _ PointClosestTo[newPoint, scratchpadData]; IF NOT success THEN RETURN; scratchpadData.index _ index; MoveRevoPoint[screenPoint2d, scratchpadData]; }; MoveLinPoint: PUBLIC PROC [screenPoint2d: Point2d, scratchpadData: ScratchpadData] = TRUSTED { oldPoly, newPoly: Polygon; area: REAL; areaRope: Rope.ROPE; newPoint: Point2d; DoDrawNeighborHood: PROC [dc: Imager.Context] = TRUSTED { Imager.SetColor[dc, Imager.white]; PadGraphics.DrawPolyNeighborHood[dc, oldPoly, scratchpadData.index, scratchpadData.origin]; Imager.SetColor[dc, Imager.black]; PadGraphics.DrawPolyNeighborHood[dc, newPoly, scratchpadData.index, scratchpadData.origin]; }; IF scratchpadData.path.len = 0 THEN RETURN;-- no points to move newPoint _ PadGraphics.ScreenToPad[screenPoint2d, scratchpadData.origin]; oldPoly _ SVPolygon2d.PathToPolygon[scratchpadData.path]; scratchpadData.path[scratchpadData.index] _ newPoint; newPoly _ SVPolygon2d.PathToPolygon[scratchpadData.path]; Scratchpad2dUser.Painter[DoDrawNeighborHood, scratchpadData.scratchViewerData]; <> area _ SVPolygon2d.SignedArea[newPoly]; areaRope _ IO.PutFR["Poly has area: %g",[real[area]]]; SVError.Append[areaRope, TRUE, TRUE]; }; -- end of MoveLinPoint MoveRevoPoint: PUBLIC PROC [screenPoint2d: Point2d, scratchpadData: ScratchpadData] = TRUSTED { <> oldPath, newPath: Path; <> <> <> newPoint: Point2d; DoDrawNeighborHood: PROC [dc: Imager.Context] = TRUSTED { Imager.SetColor[dc, Imager.white]; PadGraphics.DrawPathNeighborHood[dc, oldPath, scratchpadData.index+1, scratchpadData.origin]; PadGraphics.MirrorDrawPathNeighborHood[dc, oldPath, scratchpadData.index+1, scratchpadData.origin]; Imager.SetColor[dc, Imager.black]; PadGraphics.DrawPathNeighborHood[dc, newPath, scratchpadData.index+1, scratchpadData.origin]; PadGraphics.MirrorDrawPathNeighborHood[dc, newPath, scratchpadData.index+1, scratchpadData.origin]; }; IF scratchpadData.path.len = 0 THEN RETURN; -- no points to move newPoint _ PadGraphics.ScreenToPad[screenPoint2d, scratchpadData.origin]; IF newPoint[1] < 0 THEN newPoint[1] _ -newPoint[1]; oldPath _ SVPolygon2d.CreatePath[scratchpadData.path.len+2]; SVPolygon2d.PutPathPoint[oldPath, 0, [0, scratchpadData.path[0][2]]]; oldPath _ SVPolygon2d.PartPathGetsPartPath[scratchpadData.path, 0, oldPath, 1, scratchpadData.path.len]; SVPolygon2d.PutPathPoint[oldPath, scratchpadData.path.len+1, [0, scratchpadData.path[scratchpadData.path.len-1][2]]]; scratchpadData.path[scratchpadData.index] _ newPoint; newPath _ SVPolygon2d.CreatePath[scratchpadData.path.len+2]; SVPolygon2d.PutPathPoint[newPath, 0, [0, scratchpadData.path[0][2]]]; newPath _ SVPolygon2d.PartPathGetsPartPath[scratchpadData.path, 0, newPath, 1, scratchpadData.path.len]; SVPolygon2d.PutPathPoint[newPath, scratchpadData.path.len+1, [0, scratchpadData.path[scratchpadData.path.len-1][2]]]; Scratchpad2dUser.Painter[DoDrawNeighborHood, scratchpadData.scratchViewerData]; <> <> <> <> <> <> <> <> }; -- end of MoveRevoPoint DeleteLinPoint: PROC [screenPoint2d: Point2d, scratchpadData: ScratchpadData] = TRUSTED { newPoint, oldPoint: Point2d; index: NAT; success: BOOL; newPoint _ PadGraphics.ScreenToPad[screenPoint2d, scratchpadData.origin]; [oldPoint, index, success] _ PointClosestTo[newPoint, scratchpadData]; IF NOT success THEN RETURN; scratchpadData.path _ SVPolygon2d.DeletePathPoint[scratchpadData.path, index]; }; DeleteRevoPoint: PROC [screenPoint2d: Point2d, scratchpadData: ScratchpadData] = TRUSTED { newPoint, oldPoint: Point2d; index: NAT; success: BOOL; newPoint _ PadGraphics.ScreenToPad[screenPoint2d, scratchpadData.origin]; IF newPoint[1] < 0 THEN newPoint[1] _ -newPoint[1]; [oldPoint, index, success] _ PointClosestTo[newPoint, scratchpadData]; IF NOT success THEN RETURN; scratchpadData.path _ SVPolygon2d.DeletePathPoint[scratchpadData.path, index]; }; ComplainTooManyLinPoints: PRIVATE PROC [] = TRUSTED { errorRope: Rope.ROPE; errorRope _ IO.PutFR["Can't have more than %g points in a linear sweep", [integer[SweepGeometry.maxMeshLen]]]; SVError.Append[errorRope, TRUE, TRUE]; }; ComplainTooManyRevoPoints: PRIVATE PROC [] = TRUSTED { errorRope: Rope.ROPE; errorRope _ IO.PutFR["Can't have more than %g points in a revolute sweep", [integer[SweepGeometry.maxLinesOfLat]]]; SVError.Append[errorRope, TRUE, TRUE]; }; SpliceLinPoint: PROC [screenPoint2d: Point2d, scratchpadData: ScratchpadData] = TRUSTED { index: NAT; newPoint: Point2d; success: BOOL; poly: Polygon; IF scratchpadData.path.len = SweepGeometry.maxMeshLen THEN {ComplainTooManyLinPoints[]; RETURN}; newPoint _ PadGraphics.ScreenToPad[screenPoint2d, scratchpadData.origin]; poly _ SVPolygon2d.PathToPolygon[scratchpadData.path]; [index, success] _ EdgeClosestToInPoly[newPoint, poly]; IF NOT success THEN RETURN;-- less than 2 points in path scratchpadData.path _ SVPolygon2d.SplicePathPoint[scratchpadData.path, newPoint, index]; }; -- end of SpliceLinPoint SpliceRevoPoint: PROC [screenPoint2d: Point2d, scratchpadData: ScratchpadData] = TRUSTED { index: INTEGER; newPoint: Point2d; success: BOOL; newPath: Path; IF scratchpadData.path.len = SweepGeometry.maxLinesOfLat THEN {ComplainTooManyRevoPoints[]; RETURN}; newPoint _ PadGraphics.ScreenToPad[screenPoint2d, scratchpadData.origin]; IF newPoint[1] < 0 THEN newPoint[1] _ -newPoint[1]; newPath _ SVPolygon2d.CreatePath[scratchpadData.path.len+2]; SVPolygon2d.PutPathPoint[newPath, 0, [0, scratchpadData.path[0][2]]]; newPath _ SVPolygon2d.PartPathGetsPartPath[scratchpadData.path, 0, newPath, 1, scratchpadData.path.len]; SVPolygon2d.PutPathPoint[newPath, scratchpadData.path.len+1, [0, scratchpadData.path[scratchpadData.path.len - 1][2]]]; [index, success] _ EdgeClosestToInPath[newPoint, newPath]; index _ index - 1; -- since we have added a point onto the path IF NOT success THEN RETURN; -- less than 2 points in path scratchpadData.path _ SVPolygon2d.SplicePathPoint[scratchpadData.path, newPoint, index]; }; -- end of SplicePoint PointClosestTo: PROC [target: Point2d, scratchpadData: ScratchpadData] RETURNS [oldPoint: Point2d, index: NAT, success: BOOL] = TRUSTED { <> leastDistance, thisDistance: REAL; index _ 0; IF scratchpadData.path.len = 0 THEN {success _ FALSE; RETURN}; success _ TRUE; oldPoint _ scratchpadData.path[0]; leastDistance _ DistanceSquared[target, oldPoint]; FOR i: NAT IN[1..scratchpadData.path.len-1] DO thisDistance _ DistanceSquared[scratchpadData.path[i], target]; IF thisDistance < leastDistance THEN { leastDistance _ thisDistance; oldPoint _ scratchpadData.path[i]; index _ i;}; ENDLOOP; }; DistanceSquared: PROC [p1, p2: Point2d] RETURNS [d: REAL] = TRUSTED { d _ (p1[1]-p2[1])*(p1[1]-p2[1]) + (p1[2]-p2[2])*(p1[2]-p2[2]); }; -- end of DistanceSquared Abs: PRIVATE PROC [r: REAL] RETURNS [REAL] = TRUSTED { RETURN[IF r >=0 THEN r ELSE -r]; }; -- end of Abs DistanceToMidpoint: PRIVATE PROC [pt, edgeVert1, edgeVert2: Point2d] RETURNS [d: REAL] = TRUSTED { <> midpoint: Point2d; midpointToPt: Vector2d; midpoint _ SVVector2d.Scale[SVVector2d.Add[edgeVert2, edgeVert1], 0.5]; midpointToPt _ SVVector2d.Sub[pt, midpoint]; d _ SVVector2d.MagnitudeSquared[midpointToPt]; }; -- end of DistanceToEdge DistanceToEdge: PRIVATE PROC [pt, edgeVert1, edgeVert2: Point2d] RETURNS [d: REAL] = TRUSTED { seg: TrigLineSeg _ SVLines2d.CreateTrigLineSeg[edgeVert1, edgeVert2]; d _ SVLines2d.DistanceSquaredPointToTrigLineSeg[pt, seg]; }; EdgeClosestToInPath: PROC [target: Point2d, path: Path] RETURNS [index: NAT, success: BOOL] = TRUSTED { <> leastDistance, thisDistance: REAL; fromPoint, toPoint: Point2d; IF path.len < 2 THEN {success _ FALSE; RETURN}; -- no edges success _ TRUE; fromPoint _ path[0]; toPoint _ path[1]; index _ 0; leastDistance _ DistanceToEdge[target, fromPoint, toPoint]; fromPoint _ toPoint; FOR i: NAT IN[2..path.len) DO toPoint _ path[i]; thisDistance _ DistanceToEdge[target, fromPoint, toPoint]; IF thisDistance < leastDistance THEN { leastDistance _ thisDistance; index _ i-1;}; fromPoint _ toPoint; ENDLOOP; }; EdgeClosestToInPoly: PROC [target: Point2d, poly: Polygon] RETURNS [index: NAT, success: BOOL] = TRUSTED { <> leastDistance, thisDistance: REAL; fromPoint, toPoint: Point2d; iPlusOne: NAT; IF poly.len < 2 THEN {success _ FALSE; RETURN}; -- no edges success _ TRUE; fromPoint _ poly[0]; toPoint _ poly[1]; index _ 0; leastDistance _ DistanceToEdge[target, fromPoint, toPoint]; fromPoint _ toPoint; FOR i: NAT IN[1..poly.len-1] DO-- i is the number of fromPoint iPlusOne _ IF i = poly.len - 1 THEN 0 ELSE i+1; toPoint _ poly[iPlusOne]; thisDistance _ DistanceToEdge[target, fromPoint, toPoint]; IF thisDistance < leastDistance THEN { leastDistance _ thisDistance; index _ i;}; fromPoint _ toPoint; ENDLOOP; }; Init[]; END.