CoordSystem: TYPE = REF CoordSysObj;
CoordSysObj: TYPE = CoordSys.CoordSysObj;
Path: TYPE = REF PathObj;
PathObj: TYPE = SV2d.PathObj;
PointPolyClass: TYPE = SVPolygon2d.PointPolyClass;
Polygon: TYPE = REF PolygonObj;
PolygonObj: TYPE = SV2d.PolygonObj;
TrigLineSeg: TYPE = SV2d.TrigLineSeg;
Vector2d: TYPE = SVVector2d.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 = Scratchpad2dUser.ScratchViewerDataObj;
TextSectionData: TYPE = REF TextSectionDataObj;
TextSectionDataObj: TYPE = Scratchpad2dUser.TextSectionDataObj;
ScratchpadData: TYPE = REF ScratchpadDataObj;
ScratchpadDataObj: TYPE = Scratchpad2dUser.ScratchpadDataObj;
ScratchpadPaint:
SAFE
PROC [self: Viewer, context: Graphics.Context,
whatChanged: REF ANY, clear: BOOL] = TRUSTED {
whatChanged is a ScratchViewerData
self is a Scratchpad
scratchpadData: ScratchpadData;
scratchViewerState: ScratchViewerData;
IF whatChanged =
NIL
THEN {
--we are being called by Window Manager
scratchpadData ← NARROW[self.data];
Scratchpad2dUser.PlaceOrigin[self];
Scratchpad2dUser.DrawScene[self, scratchpadData.scratchViewerData, red, FALSE, FALSE];
}
ELSE {
proc: PROC [Graphics.Context];
scratchViewerState ← NARROW[whatChanged];
scratchpadData ← NARROW[scratchViewerState.scratchpad.data];
proc ← scratchpadData.proc;
proc[context];
RETURN};
};
Init:
PROC = {
scratchpadClass: ViewerClasses.ViewerClass;
I register the new Scratchpad class.
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]
RETURNS [container: Viewer, scratchpad: Viewer] =
TRUSTED {
Parent is the SolidViewer tool.
Here we create the Container and the top menu which looks like this:
CLOSE GROW <-> DESTROY SAVE NEWLINE ERASE
NewLin NewRevo TestPoints
world: CoordSystem;
padMenu: Menus.Menu;
scratchViewerData: ScratchViewerData ← NEW[ScratchViewerDataObj];
scratchpadData: ScratchpadData ← NEW[ScratchpadDataObj]; -- make the data object for the solid window
construct the menu
padMenu ← Menus.CreateMenu[1];
Menus.AppendMenuEntry[
menu: padMenu,
entry: Menus.CreateEntry[
name: "Erase",
proc: Scratchpad2dUser.Erase,
clientData: scratchViewerData,
documentation: "erase the scratchpad"
]
];
Menus.AppendMenuEntry[
menu: padMenu,
entry: Menus.CreateEntry[
name: "Normals",
proc: Scratchpad2dUser.Normals,
clientData: scratchViewerData,
documentation: "show normals to all current segments"
]
];
CrossHairs NewLin NewRevo TestPoints
Menus.AppendMenuEntry[
menu: padMenu,
entry: Menus.CreateEntry[
name: "CrossHairs",
proc: Scratchpad2dUser.CrossHairs,
clientData: scratchViewerData,
documentation: "draw crosshairs to show current origin"
]
];
Menus.AppendMenuEntry[
menu: padMenu,
entry: Menus.CreateEntry[
name: "NewLin",
proc: Scratchpad2dUser.NewLin,
clientData: scratchViewerData,
documentation: "prepare to draw the outline of a linear sweep"
]
];
Menus.AppendMenuEntry[
menu: padMenu,
entry: Menus.CreateEntry[
name: "NewRevo",
proc: Scratchpad2dUser.NewRevo,
clientData: scratchViewerData,
documentation: "prepare to draw the outline of a revolute sweep"
]
];
Menus.AppendMenuEntry[
menu: padMenu,
entry: Menus.CreateEntry[
name: "TestPoints",
proc: Scratchpad2dUser.TestPoints,
clientData: scratchViewerData,
documentation: "prepare to perform in-on-out test on mouse points"
]
];
Menus.AppendMenuEntry[
menu: padMenu,
entry: Menus.CreateEntry[
name: "Circle",
proc: Scratchpad2dUser.Circle,
clientData: scratchViewerData,
documentation: "draw a circle centered on the origin"
]
];
now make the outer container
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]
]];
world ← CoordSys.CreateCoordSys["WORLD", Matrix3d.Identity[],NIL];
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;
Now build the draw section.
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] = {
Self is a scratchpad
scratchpadData: ScratchpadData ← NARROW[self.data];
path ← scratchpadData.path;
};
Point2d: TYPE = Matrix3d.Point2d;
InputNotify: PUBLIC SAFE PROCEDURE [self: ViewerClasses.Viewer, input: LIST OF REF ANY] =
TRUSTED {
Self is a Scratchpad2d
IF
ISTYPE[input.first, TIPUser.TIPScreenCoords]
THEN {
controlPoint: Point2d;
mousePlace: TIPUser.TIPScreenCoords ← NARROW[input.first];
controlPoint[1] ← mousePlace.mouseX; -- this is a fix to float conversion
controlPoint[2] ← mousePlace.mouseY;
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.DrawLin[self, scratchpadData.scratchViewerData, red, FALSE, FALSE];
};
drawRevo =>{
AddRevoPoint[controlPoint, scratchpadData];
Scratchpad2dUser.DrawRevo[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 =>{Scratchpad2dUser.DrawLin[self, scratchpadData.scratchViewerData, red, FALSE, FALSE]};
drawRevo =>{Scratchpad2dUser.DrawRevo[self, scratchpadData.scratchViewerData, red, FALSE, FALSE]};
ENDCASE => ERROR;
};
$ERASE => {
scratchpadData: ScratchpadData ← NARROW[self.data];
SELECT scratchpadData.mode
FROM
off => RETURN;
pointAt => RETURN;
drawLin =>{DeleteLinPoint[controlPoint, scratchpadData];
Scratchpad2dUser.DrawLin[self, scratchpadData.scratchViewerData, red, FALSE, FALSE]};
drawRevo =>{DeleteRevoPoint[controlPoint, scratchpadData];
Scratchpad2dUser.DrawRevo[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.DrawLin[self, scratchpadData.scratchViewerData, red, FALSE, FALSE]};
drawRevo =>{SpliceRevoPoint[controlPoint, scratchpadData];
Scratchpad2dUser.DrawRevo[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;
testStream: IO.STREAM;
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;
testStream ← IO.CreateOutputStreamToRope[];
testStream.PutF["Point: [%g, %g] is %g poly.",[real[padPoint[1]]], [real[padPoint[2]]],
[rope[inOnOutRope]]];
testRope ← IO.GetOutputStreamRope[testStream];
MessageWindow.Append[testRope, TRUE];
}; -- end of TestPoint
AddLinPoint:
PROC [screenPoint2d: Point2d, scratchpadData: ScratchpadData] =
TRUSTED {
area: REAL;
poly: Polygon;
areaRope: Rope.ROPE;
areaStream: IO.STREAM;
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];
areaStream ← IO.CreateOutputStreamToRope[];
areaStream.PutF["Poly has area: %g",[real[area]]];
areaRope ← IO.GetOutputStreamRope[areaStream];
MessageWindow.Append[areaRope, TRUE];
}; -- end of AddLinPoint
AddRevoPoint:
PROC [screenPoint2d: Point2d, scratchpadData: ScratchpadData] =
TRUSTED {
Revo points must have positive x in scratchpad coords.
area: REAL;
poly: Polygon;
areaRope: Rope.ROPE;
areaStream: IO.STREAM;
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];
Print current poly area.
area ← SVPolygon2d.SignedArea[poly];
areaStream ← IO.CreateOutputStreamToRope[];
areaStream.PutF["Poly has area: %g",[real[area]]];
areaRope ← IO.GetOutputStreamRope[areaStream];
MessageWindow.Append[areaRope, 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;
areaStream: IO.STREAM;
newPoint: Point2d;
DoDrawNeighborHood:
PROC [dc: Graphics.Context] =
TRUSTED {
Graphics.SetColor[dc, GraphicsColor.white];
PadGraphics.DrawPolyNeighborHood[dc, oldPoly, scratchpadData.index, scratchpadData.origin];
Graphics.SetColor[dc, GraphicsColor.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];
Print out current poly area
area ← SVPolygon2d.SignedArea[newPoly];
areaStream ← IO.CreateOutputStreamToRope[];
areaStream.PutF["Poly has area: %g",[real[area]]];
areaRope ← IO.GetOutputStreamRope[areaStream];
MessageWindow.Append[areaRope, TRUE];
}; -- end of MoveLinPoint
MoveRevoPoint:
PUBLIC
PROC [screenPoint2d: Point2d, scratchpadData: ScratchpadData] =
TRUSTED {
revo points must have positive x in scratchpad coords
oldPath, newPath: Path;
area: REAL;
poly: Polygon;
areaRope: Rope.ROPE;
areaStream: IO.STREAM;
newPoint: Point2d;
DoDrawNeighborHood:
PROC [dc: Graphics.Context] =
TRUSTED {
Graphics.SetColor[dc, GraphicsColor.white];
PadGraphics.DrawPathNeighborHood[dc, oldPath, scratchpadData.index+1, scratchpadData.origin];
PadGraphics.MirrorDrawPathNeighborHood[dc, oldPath, scratchpadData.index+1, scratchpadData.origin];
Graphics.SetColor[dc, GraphicsColor.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];
Print out current poly area
poly ← SVPolygon2d.CreatePoly[scratchpadData.path.len+2];
poly[0] ← [0, scratchpadData.path[0][2]];
poly[scratchpadData.path.len + 1] ← [0, scratchpadData.path[scratchpadData.path.len -1][2]];
poly ← SVPolygon2d.PartPolyGetsPartPath[scratchpadData.path, 0, poly, 1, scratchpadData.path.len];
area ← SVPolygon2d.SignedArea[poly];
areaStream ← IO.CreateOutputStreamToRope[];
areaStream.PutF["Poly has area: %g",[real[area]]];
areaRope ← IO.GetOutputStreamRope[areaStream];
MessageWindow.Append[areaRope, TRUE];
}; -- 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 {
errorStream: IO.STREAM;
errorRope: Rope.ROPE;
errorStream ← IO.CreateOutputStreamToRope[];
errorStream.PutF["Can't have more than %g points in a linear sweep",
[integer[SweepGeometry.maxMeshLen]]];
errorRope ← IO.GetOutputStreamRope[errorStream];
MessageWindow.Append[errorRope, TRUE];
};
ComplainTooManyRevoPoints:
PRIVATE
PROC [] =
TRUSTED {
errorStream: IO.STREAM;
errorRope: Rope.ROPE;
errorStream ← IO.CreateOutputStreamToRope[];
errorStream.PutF["Can't have more than %g points in a revolute sweep",
[integer[SweepGeometry.maxLinesOfLat]]];
errorRope ← IO.GetOutputStreamRope[errorStream];
MessageWindow.Append[errorRope, 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 {
Returns success ← FALSE if there are no points.
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 {
Use distance squared to midpoint as a heuristic for being close to an edge.
midpoint: Point2d;
midpointToPt: Vector2d;
midpoint ← SVVector2d.Scale[SVVector2d.Sum[edgeVert2, edgeVert1], 0.5];
midpointToPt ← SVVector2d.Difference[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 {
Index will be 0 if edge between 0 and 1 is closest and poly.len - 2 if edge between poly.len - 2 and poly.len - 1 is closest, etc.
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 {
Index will be 0 if edge between 0 and 1 is closest and poly.len - 1 if edge between poly.len - 1 and 0 is closest, etc.
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[];