File: Scratchpad2dImpl.mesa
Author: Eric Bier on July 2, 1983 1:44 pm
Last edited by Bier on July 2, 1983 2:53 pm
Contents: Definitions for a new type of viewer designed to support 2d editting. Most (or perhaps all) three dimensional shapes in the SolidViews three dimensional illustrator will be constructable from a set of 2d entities, hence, the Scratchpad2d window may be a principle source of shape creation until that time when shapes can be drawn on planes which are in the 3d scene.
DIRECTORY
Containers,
CoordSys,
Graphics,
GraphicsColor,
Icons,
Interminal,
IO,
Matrix3d,
Menus,
MessageWindow,
PadGraphics,
Rope,
Scratchpad2d,
Scratchpad2dUser,
SV2d,
SVLines2d,
SVPolygon2d,
SVScratchpadMonitor,
SVVector2d,
SweepGeometry,
TIPUser,
ViewerClasses,
ViewerOps;
Scratchpad2dImpl: CEDAR PROGRAM
IMPORTS Containers, CoordSys, Graphics, Icons, IO, Matrix3d, Menus, MessageWindow, PadGraphics, Scratchpad2dUser, SVLines2d, SVScratchpadMonitor, SVPolygon2d, TIPUser, SVVector2d, ViewerOps
EXPORTS Scratchpad2d
SHARES ViewerClasses =
BEGIN
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[];
END.