GGGravity.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Last edited by Bier on November 7, 1985 8:54:14 pm PST
Contents: Procedures which take a test point and map it to a point on a nearby set of objects.
DIRECTORY
GGModelTypes,
GGInterfaceTypes,
Imager;
Introduction
This module does not handle any of the feedback aspects associated with pulling one object into another. Instead, given a test point and a set of environment objects (points, lines, circles, and trajectories), GGGravity finds a slightly different position of the test object which would bring it into exact alignment with one or more environment objects.
GGGravity is intended to support a number of Gargoyle dragging features:
1) Selection. In this case we are not interested in the exact point which GGGravity but only in the name of the object deemed to be close. For selection, the environment consists only of visible objects in the Gargoyle scene.
2) Caret placement. We may wish to place the caret on a visible object or on an interesting alignment line. In this case, we care both about the nearest object (e.g. so we can record a touching relationship) and the new point. The environment will consist both of visible objects and of special alignment lines.
3) Intelligent dragging. For each vertex or edge of the objects being dragged, we can perform the gravity mapping, looking for directions in which to move the draggee. This will require arbitrating between multiple attractions. Otherwise, this is like caret placement.
4) Interactive rotation. Some of the alignments of interest during interactive rotation are point alignments. For these GGGravity works as for intelligent dragging. Also, GGGravity can be used in reverse: The environment can be built from the moving object. The test points are present in the scene.
GGGravity cooperates with GGAlign. GGAlign takes a Gargoyle scene and a test object and extracts a set of environment objects which the user might want to snap to (See GGAlign for more details).
Here is a description of the algorithms used to find a nearest object:
It is assumed that the client has already culled from the entire space of environment objects a subset which are currently of interest. Call this the object bag. The client also passes a point testPoint which is to be mapped to a nearby environment object (or left alone if none are near). We consider only environment objects with pass within a radius criticalR of the test point.
These are the only general assumptions. This module implements several specific gravity mappings which obey these assumptions. The main difference between the gravity functions is in their treatment of enviroment points versus environment lines. Some make it easier to select points of intersection (at the cost of making it hard or impossible to select points on the intersecting lines which are near the point of intersection). The mappings considered are:
StrictDistance. The nearest environment object (within criticalR) is chosen, and testPoint is mapped to the nearest point on that object. This makes points of intersection almost impossible to select (unless the intersecting curves end at the point as for a polygon vertex).
InnerCircle. Another radius innerR (innerR < criticalR) is chosen in which environment points are favored. In particular, testPoint is mapped to the nearest environment point within innerR, if any. If there are none, than the closest line within innerR is chosen. If there are none, then we fall back on StrictDistance. InnerCircle makes points easy to select even in environments full of lines. However, selecting points on lines near points can be impossible.
<OhWow>. A partial ordering on nearness of environment objects is defined this way: pt1 < pt2 if pt1 is closer to testPoint. pt1 < line1 (pt not on line1) if pt1 is closer to testPoint. line1 < line2 if line1 is closer to testPoint. pt1 < line1 (pt on line) if the angle between (pt1, testPoint) and line1 is greater than 45 degrees. Except for the last case, OhWow is the same as StrictDistance. However, this modification means that intersection points are easy to select (as for InnerCircle) and any point on any line can always be selected (as for StrictDistance). Not yet implemented (the other two types seem to be adequate).
Implementation
GGGravity builds a data structure which represents a number of points, lines, circles, and trajectories. It then answers the query "which objects are near this test point". GGGravity is optimized for the case where queries are more common than changes to the environment.
GGGravity: CEDAR DEFINITIONS =
BEGIN
Angle: TYPE = GGModelTypes.Angle;
BoundBox: TYPE = GGModelTypes.BoundBox;
Camera: TYPE = GGModelTypes.Camera;
Circle: TYPE = GGModelTypes.Circle;
Edge: TYPE = GGModelTypes.Edge;
GargoyleData: TYPE = GGInterfaceTypes.GargoyleData;
Line: TYPE = GGModelTypes.Line;
Segment: TYPE = GGModelTypes.Segment;
Sequence: TYPE = GGModelTypes.Sequence;
Traj: TYPE = GGModelTypes.Traj;
Point: TYPE = GGModelTypes.Point;
Vector: TYPE = GGModelTypes.Vector;
CreateObjectBag: PROC [] RETURNS [objectBag: ObjectBag];
ObjectBag: TYPE = REF ObjectBagObj;
ObjectBagObj: TYPE = RECORD [
seqs: LIST OF FeatureData,
slopeLines: LIST OF FeatureData,
symmetryLines: LIST OF FeatureData,
radiiCircles: LIST OF FeatureData,
distanceLines: LIST OF FeatureData,
vectors: LIST OF FeatureData,
intersectionPoints: LIST OF FeatureData
];
Snapping to Nearby Objects
Map: PROC [testPoint: Point, criticalR: REAL, environ: ObjectBag, gargoyleData: GargoyleData] RETURNS [resultPoint: Point, feature: FeatureData];
StrictDistance: PROC [testPoint: Point, criticalR: REAL, environ: ObjectBag] RETURNS [resultPoint: Point, feature: FeatureData];
InnerCircle: PROC [testPoint: Point, criticalR: REAL, innerR: REAL, environ: ObjectBag] RETURNS [resultPoint: Point, feature: FeatureData];
Sectors: PROC [testPoint: Point, criticalR: REAL, environ: ObjectBag] RETURNS [resultPoint: Point, feature: FeatureData];
Building the Object Bag
FeatureType: TYPE = {sequence, distanceLine, slopeLine, symmetryLine, radiiCircle, intersectionPoint};
ResultFeatureType: TYPE = {joint, segment, distanceLine, slopeLine, symmetryLine, radiiCircle, intersectionPoint};
FeatureData: TYPE = REF FeatureDataObj;
FeatureDataObj: TYPE = RECORD [
type: FeatureType,
tseq: Sequence, -- may be a whole trajectory
line: Line, -- "the" line for slopelines
circle: Circle, -- "the" circle for circles
line1Feature: FeatureData, -- the first line for an intersection point
line2Feature: FeatureData, -- the second line for an intersection point
degrees: REAL,
seg: Segment, -- the Segment which generated a colinearLine
point: Point, -- the data for an intersection point.
triggerPoints: LIST OF Point ← NIL, -- the points which trigger a slope line (for instance)
resultType: ResultFeatureType,
segNum: NAT, -- the joint or segment which was hit (input was type trajectory).
jointNum: NAT, -- the joint which was hit (or which generated a slopeLine)
traj: Traj, -- the Trajectory whose joint generated a slopeLine
visible: BOOLFALSE
];
SymmetryType: TYPE = {cyclic, mirror};
SymmetryGroup: TYPE = REF SymmetryGroupObj;
SymmetryGroupObj: TYPE = RECORD [
type: SymmetryType,
order: NAT
];
Joint Firing Rules: SlopeLine, Vector, Circle, Vertex
JointAddSlopeLine: PUBLIC PROC [degrees: REAL, direction: Vector, point: Point, jointNum: NAT, traj: Traj, objectBag: ObjectBag];
JointAddVector: PUBLIC PROC [vector: Vector, point: Point, jointNum: NAT, traj: Traj, objectBag: ObjectBag];
JointAddCircle: PUBLIC PROC [radius: REAL, point: Point, jointNum: NAT, traj: Traj, objectBag: ObjectBag];
JointAddPoint: PUBLIC PROC [point: Point, jointNum: NAT, traj: Traj, objectBag: ObjectBag];
Segment Firing Rules: FourAngleLines, ColinearLine, Segment
SegmentAddTwoAngleLines: PUBLIC PROC [degrees: REAL, p1, p2: Point, segNum, traj: Traj, objectBag: ObjectBag];
SegmentAddDistanceLines: PUBLIC PROC [distance: REAL, segNum: NAT, traj: Traj, objectBag: ObjectBag];
SegmentAddEdge: PUBLIC PROC [point: Point, jointNum: NAT, traj: Traj, objectBag: ObjectBag];
AddTrajectory: PUBLIC PROC [traj: Traj, objectBag: ObjectBag];
AddSequence: PUBLIC PROC [seq: Sequence, objectBag: ObjectBag];
Symmetry Firing Rules: SymmetryAddLines, SymmetryAddPoints
SymmetryAddLines: PUBLIC PROC [group: SymmetryGroup, entity: REF ANY, objectBag: ObjectBag];
SymmetryAddPoints: PUBLIC PROC [group: SymmetryGroup, entity: REF ANY, objectBag: ObjectBag];
Coordinate Frame Firing Rules: AddPoint AddXLine AddYLine:
CoordFrameAddPoint: PUBLIC PROC [point: Point, objectBag: ObjectBag];
CoordFrameAddXLine: PUBLIC PROC [xVal: REAL, objectBag: ObjectBag];
CoordFrameAddYLine: PUBLIC PROC [yVal: REAL, objectBag: ObjectBag];
FlushObjectBag: PROC [objectBag: ObjectBag];
Empties the bag.
Drawing Object Bags
AdjustVisibilityPoint: PROC [testPoint: Point, tolerance: REAL, objectBag: ObjectBag];
AdjustVisibilityBox: PROC [box: BoundBox, tolerance: REAL, objectBag: ObjectBag];
DrawObjectBag: PROC [dc: Imager.Context, objectBag: ObjectBag, camera: Camera, gargoyleData: GargoyleData];
Draws all objects which have been marked "visible" by AdjustVisibilityPoint or AdjustVisibilityBox.
DrawObjectBagRegardless: PROC [dc: Imager.Context, objectBag: ObjectBag, camera: Camera, gargoyleData: GargoyleData];
Draws all objects in the object bag regardless of how they have been marked.
END.