GGConventions.tioga
Author: Eric Bier on January 5, 1986 4:17:06 pm PST
Last edited by Bier on January 30, 1986 5:23:20 pm PST
This is a centralized place to put lore about Gargoyle. In particular, the history behind certain data structures, the invariants maintained by certain modules, and a list of kludges known to be in force. This is also a good place to keep lists -- e.g. the list of all procedures which have to be rewritten if data structure X changes.
Conventions
The Scene: Our First Opaque Type
Scenes are an opaque type implemented by GGObjectsImpl.mesa. This guarantees that only GGObjectsImpl touches the entity list. Hence, we can change this data structure as we please to make commands like AddOutline run fast.
The Caret: Chairs and Attractors
The original idea between chairs and attractors was to build up a data structure of what was touching what. The caret would act as a kind of soldering iron. When a dragging operaton completed, the object that moved with the caret (the chair) and the object that the caret snapped to (the attractor) would be listed as touching. This use has been abandoned. We didn't find enough use for the touching data structure to warrant the effort needed to maintain it. Unfortunately, chairs and attractors have been used for other purposes so we can't just get rid of them. Here are their uses:
Chair
Heuristics: During add, the chair trajectory was automatically hot. The chair should no longer be used for this. The new joint is simply selected and the same heuristics are used as for drag.
Close: The Close command closes the chair trajectory. Now that Add works by selecting a joint, Close could just work on the selected trajectories.
GGEventImplA.DescribeCaretObject describes the chair. It can be removed when chairs are no more.
Adding: The caret to be extended is the trajectory that has the caret sitting on its end. I don't know of a good way to get rid of this use of the chair. When writing a routine that may modify a trajectory that the caret is sitting on, it is safest to put GGCaret.SitOn[caret, nothing] in that routine.
GGEventImplB.DeleteCaretSegment is used to abort Add, and to "BackSpace" over segments. These uses cannot be removed until Add works differently.
GGRefreshImpl.ChairIsNotAttractor is a mystery to me (Bier). I'll ask Ken about it.
Conclusion: Chairs should be used for adding segment to trajectories, only. I know of no reason that the caret should sit on a slice.
Attractor
GGEventImplC.AddControlPoint. The trajectory that most recently attracted the caret gets a new control point added to it. This is ambiguous because of the difficulty of getting the caret to snap to just what you want it to.
GGEventImplC.AddJoint. The trajectory that most recently attracted the caret gets a new joint added to it. This is ambiguous because of the difficulty of getting the caret to snap to just what you want it to.
GGRefreshImpl.ChairIsNotAttractor is a mystery to me (Bier). I'll ask Ken about it.
Conclusion: Attractors aren't being used for much either. We may do well to try to phase them out so we don't have to maintain them everywhere.
Color
Setting the color. Color is part of the Imager state. If it changes unexpectedly, an incorrect picture can result. In Gargoyle, the color is never changed by a call to a GGShapes procedure. It is changed by GGRefresh.DrawOutline and GGRefresh.DrawTraj. Callers of GGShapes procedures (e.g. GGGravityImpl.DrawObjectBagRegardless must set the color before calling these procedures.
Names
Variable names refering to trajectories use the abbreviation "traj". Procedure names spell out Trajectory in full. GGSelect.DeselectTraj and SelectTraj should be fixed.
Bounding Boxes
The bounding boxes of outlines, segments, trajectories, and slices are kept correct. The bounding box of a sequence is used as temporary storage only. The box stored with each segment (and hence each trajectory) adds enough space to allow for control points.
The segment operations (e.g. transform, endPointMoved, and controlPointMoved) update bounding boxes. Segment boundBoxes are only accessed via the segment.class.boundBox proc (this is now true).
Non-standard slice types must see to it that their boundboxes are big enough to allow for drawing them in selected and non-selected configurations.
Slice BoundBoxes. slice.boundBox should not be accessed directly. Access should be through the slice.getBoundBox proc. Slices that wish to keep their boundboxes up to date at all times should update them after the following types of changes: MakeSlice, TransformSlice, SetStrokeWidth, and any addition or deletion of parts, such as AddSegment, AddHole, DeleteSequence.
NEEDED: A convention about who should copy boundBoxes and who can use the originals.
Sequences
A sequence is a description of a subset of the joints and segments of a trajectory as it was when the sequence was made.
If the number of segments or joints in a trajectory is changed, all sequences describing that trajectory become obsolete, and should be discarded. The procedure GGSelect.ReselectTraj can be used to update the sequences on selection lists. When sequences are stored on persistent trigger lists by GGAlignImpl, then these sequences will have to be updated as well.
The operations which change a trajectory in this was include: GGObjects.AddSegment, GGObjects.DeleteSegment, and GGEventImplA.Delete (which should be moved to GGObjects). Planned operations which splice in segments are also in this category (e.g. Weld).
Selections
Selections are the Vietnam of Gargoyle. Here are the conventions I believe should be in force (November 16, 1986, Bier):
Descriptors only. Two kinds of objects are on the selected list: SliceDescriptor and OutlineDescriptor.
No empty descriptors. If a descriptor is on the selected list, one or more of its components are selected. That is, no empty descriptors should be on the list.
Setting bits. The GGSelect operations IsSelectedInPart and IsSelectedInFull take advantage of a set of flags stored in the objects themselves. In particular, slices, joints, segments, and control points have selectedInFull bits. The selectedInPart field of trajectories does not appear to be in use. I will consider whether to remove it all together.
Control Points
A segment can be normal selected in one of five ways:
1) In entirety (both joints, the segment, and all control points).
2) One or both endjoints only.
3) One or more control points only.
4) Control points and endjoints.
5) Not at all.
There are 8 selection combinations for joints (J), the segment (S), and the control points (C):
(none), (J), (S), (C), (JS), (JC), (SC), (JSC)
The 3 possibilities in bold are illegal. An easy rule to remember is that S -> C and S -> J. That is, if the segment is selected then both of its joints, and all of its control points must be selected as well. GGSelect should enforce this convention.]
Only one possibility is illegal for hot selections:
(none), (J), (S), (C), (JS), (JC), (SC), (JSC)
The possibility in bold is illegal. An easy rule to remember is that S -> C. That is, if the segment is selected then all of its control points must be selected as well. GGSelect should enforce this convention. Changed by Bier on July 14, 1986, to allow control points to be made cold even when the adjacent segment is hot.
startProc, duringProc, endProc, continueProc and the mouse position.
There are currently 4 interesting events for interactive actions.
1) When the mouse button first goes down, immediate action should be taken to update the screen so the user can see, even before he moves the mouse, that an operation is beginning.
2) When the mouse moves, objects will move.
3) When the operation is terminated by letting go of all buttons. Unfortunately, the mouse position at this time is not likely to be particularly relevant. Many operations stop all motion as soon as any buttons go up. Hence, the mouse position from the last duringProc must be saved. This conflicts with any attempts to allow the SlackProcess to remove all duringProcs, because at least the last duringProc must be done. To get smooth motion anyway, the endProc can do no further motion.
4) When the continueProc is called, a new action is beginning. The mouse position is no longer relevant to the old operation. Again, the mouse position (or the mapPoint, or just the transform) must be saved from the previous duringProc. The new operation then uses the current mouse position.
This is true for adding, and dragging.