File: Scratchpad2dImpl.mesa
Author: Eric Bier on July 2, 1983 1:44 pm
Copyright © 1984 by Xerox Corporation. All rights reserved.
Last edited by Bier on July 30, 1984 10:41:41 pm PDT
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,
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 {
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.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;
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, editToolData: EditToolData]
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
Erase Normals CrossHairs NewLin NewRevo TestPoints Circle
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[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
];
CrossHairs NewLin NewRevo TestPoints
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
];
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]
]];
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
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;
};
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];
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;
this was a fix to float conversion
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];
Scratchpad2dUser.DrawLin[self, scratchpadData.scratchViewerData, red, FALSE, FALSE];
};
drawRevo =>{
SVScratchpadMonitor.NewMotion[controlPoint, NARROW[input.rest.first], scratchpadData];
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.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 {
Revo points must have positive x in scratchpad coords.
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];
Print current poly area.
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];
Print out current poly area
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 {
revo points must have positive x in scratchpad coords
oldPath, newPath: Path;
area: REAL;
poly: Polygon;
areaRope: Rope.ROPE;
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];
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];
areaRope ← IO.PutFR["Poly has area: %g",[real[area]]];
SVError.Append[areaRope, TRUE, 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 {
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 {
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.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 {
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.