-- Scene.mesa: Viewers for geometrical drawings
-- last modified by Stolfi - September 13, 1983 11:26 am
-- To do: everything
DIRECTORY
ScenePrivate USING [Object, Thing, Group, BoundingBox],
Graphics USING [Context],
Rope USING [ROPE],
ViewerClasses USING [Viewer],
Menus USING [MouseButton, MenuLine];
Scene: CEDAR DEFINITIONS
= BEGIN OPEN SPr: ScenePrivate, Gr: Graphics;
-- SCENES - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SceneViewer: TYPE = VClasses.Viewer;
-- A SceneViewer is a dynamic viewer for displaying a dynamic collection of dynamic graphical objects in a dinamic style (so that you can get dynamic bugs). The implementation of Scenes provides the following facilites on top those of Cedar Graphics:
-- Addition, deletion, and modification of individual objects (atomic or clusters).
-- Automatic computation of bounding boxes for object clusters.
-- Automatic incremental repainting when the viewer or the object tree are modified.
-- Client-specified painting order for the objects.
-- Automatic scaling and centering to fit current viewer's dimensions.
-- Synchonization primitives for controlling access to object trees.
-- Simplified facilities for property list mangement, menu composition, and mouse input.
-- A SceneViewer has the following client-visible attributes:
-- an object tree, a display file in the form of an ordered tree whose nodes are client-defined graphical objects.
-- a collection of client-defined menu and mouse actions to be invoked when specific menu or mouse clicks occur.
-- An object tree is normaly examined an modified by two or more concurent processes. The scene implementation provides internal locking mechanisms to prevent one process from modifying an object or a tree while others are examining it. The client must therefore acquire these locks before examining or modifying any link or fields in the object trees
-- Furthermore, the scene implementation assumes that the links and fields in the tree structures always satisfy a collection of internal invariants (for example, the parent of the children of X must be X, the root of a tree must have no siblings, and so forth). If a process modifies any object or tree, it must restore these invariants before releasing the abovementioned locks.
-- The procedures in this interface are all ``nice'' in both respects. They automatically acquire the necessary locks, so each can be thought of as being indivisible. Moreover, when they modify any objects or trees, they will automatically restore the data structure invariants before exiting, and will repaint the affected objects on the attached scene viewers.
-- The same precautions must be observed by the client when examining or modifying the client-defined parameter records of Things. Most situations should be covered by the procedures ModifyThing, ModifyAllThings, and EnumerateThings, with minimal hassle.
-- SCENE CREATION - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CreateSceneViewer: PROC [info: ViewerClasses.ViewerRec]
RETURNS [scene: SceneViewer];
-- Creates a new SceneViewer with empty object tree. The info gives the default parameters for the viewer. Forks also an independent paint/menu/mouse process whose task is to repaint the scene as necessary, and to peform mouse and menu actions that have been enqueued.
-- To set the object tree, the client should use the SetTree procedure below.
Destroy: PROC [scene: SceneViewer];
-- Destroys the scene viewer. Its current object tree, if any, becomes detached.
GetTree: PROC [scene: SceneViewer] RETURNS [tree: Tree];
-- Obtains the object tree currently attached to the given scene. Returns NIL if the scene viewer has been destroyed.
SetTree: PROC [scene: SceneViewer, tree: Tree];
-- Replaces the object tree currently attached to the given scene by the given new tree, and repaints the viewer. The current object tree of the scene becomes detached. If the scene viewer has been destroyed, the operation is a no-op.
GetSceneViewer: PROC [object: OBject] RETURNS [scene: SceneViewer];
-- Obtains the scene viewer to which the object is currently attached. Returns NIL if none.
-- SAFE PROCEDURES - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-- The procedures in this section are (I hope) deadlock-free, and thus may be called at any time.
Alive: PROC [scene: SceneViewer] RETURNS [BOOL];
-- Returns FALSE if the SceneViewer has been destroyed for any reason.
Repaint: PROC [scene: SceneViewer, box: BoundingBox ← infiniteBox];
-- Repaints the scene within the specified box.
-- Actually, the repaint request will be put in an internal queue and will be performed by the scene's server process when the scene becomes available.
-- OBJECTS - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Object: TYPE = SPr.Object;
Group: TYPE = SPr.Thing;
Thing: TYPE = SPr.Group;
-- Objects come in two flavors: things and groups. A thing is an atomic object, like a point, line, polygon, polyhedron, polytope, polynomial, polymer, polyp, polyphemus, polyrrhine, in fact an arbitrary client-defined entity. A group consists of the superposition of zero or more other children objects, in a specific painting order.
-- At any time each object is a child of at most one group, its parent. Therefore, all objects existing at any moment constitute a collection of disjoint ``object trees''. The ``root'' of an object X is its most remote ancestor, the one we reach by following parent links while they exist. The ``object tree containing X'', or simply ``the tree of X'' consists of that root object and everything that hangs from it. The ``subtree of X'' consists of just X and its descendants.
-- At any moment an object tree can be either ``attached'' to exactly one SceneViewer, or ``detached'' from all of them. By extension, we say that an object X (not necessarily a root) is attached to a SceneViewer or detached if the same holds for the tree containing X.
-- Objects can be moved from one tree to another. Adding, deleting, or moving a group object automatically carries along all its descendants.
-- An Object (of both types) also has a list of properties which is used to store several oddball parameters for the object. Some of them have their meaning fixed by this interface (like $Color, $LineWidth, $Background); the Client can define others if it so desires.
-- In addition, a thing Object contains:
-- a client-defined parameter record containing geometrical and other parameters of the thing, like coordinates, coefficients, etc.
-- a pointer to the thing's class, a record containing client-defined procedures that know how to paint the thing on the screen.
Tree: TYPE = Object; -- The root of an object tree. Has no parents or siblings.
-- OBJECT TREE MANIPULATION - - - - - - - - - - - - - - - - - - - - - - - - -
-- All the operations in this section can be safely used by several processes operating on the same scene(s). The procedures acquire the neccessary locks in the proper order.
-- The procedures also will repaint automatically the portions of the viewer's image affected by them, and restore the internal data structure invariants.
MakeThing: PROC [class: ThingClass, parms: REF] RETURNS [thing: Thing];
-- Allocates a new thing with given class and parms record. The thing is returned as a detached root.
MakeGroup: PROC RETURNS [group: Group];
-- Allocates a new group as a detached root with no descendants.
Remove: PROC [object: Object] RETURNS [oldParent: Group, oldNext: Object];
-- The object is disconnected (with all its descendants) from its current parent. The subtree rooted at the object becomes a detached tree by itself.
-- If the object was attached to a scene, the affected portion of the image is repainted.
-- The procedure returns the old parent node (oldParent) of the object, and the old sibling (oldNext) that immediately followed it in the painting order. If the object was the last (topmost) child of oldParent, returns oldNext=NIL.
-- The object must not be a root.
Add: PROC [object: Object, newParent: Group, newNext: Object ← NIL];
-- Adds object (with its descendants) one of the children of newParent, immediately before (below) the specified child object newNext. If newNext= NIL, object is inserted as the last (topmost) child of newParent. The given newNext (if not NIL) must be a child of newParent.
-- The object to be added must be a detached root, and the newParent cannot be NIL.
-- If the newParent is attached to a scene, the affected portion of the image will be repainted.
MoveObject: PROC [object: Object, newParent: Group, newPrev: Object ← NIL]
RETURNS [oldParent: Group, oldNext: Object];
-- An indivisible operation equivalent to

[oldParent, oldNext] ← RemoveObject [object];
AddObject [object, newParent, newNext]
-- PROPERTY LIST - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Scope: SPr.Scope; -- {local, root, path}
GetProp: PROC [object: Object, key: Atom, scope: Scope ← path] RETURNS [value: REF];
-- Gets the value of the property named key for the given object. Returns NIL if the key is not found.
-- If scope=local, only the node's own property list is searched for the key. If scope=root, only the property listy in the root node of the object's tree is searched. If scope=path, the property lists of all nodes in the path from the object up to the root (including these two), will be searched in sequence for the key, until a non-NIL value for it is found.
SetProp: PROC [object: Object, key: Atom, value: REF];
-- Sets the value of the property named key for the given object. Removes the property if the value is NIL.
-- If the object is attached to a scene, the affected portion of the image will be repainted.
-- OBJECT ENUMERATION AND MODIFICATION - - - - - - - - - - - - - - - - - - - - - -
ModifyThing: PROC
[thing: Thing, Modifier: ThingModifier];
-- Modifies the the parameter record of the given thing as specified by the client-provided Modifier procedure. Will acquire the necessary locks, and restore the data structure invariants before exiting.
-- If the thing is attached to a scene, the affected part of the image will be repainted.
ModifyAllThings: PROC
[object: Object, Modifier: ThingModifier];
-- Equivalent to ModifyThings[thing, Modifier] applied to all things that are descendant of the the given object. Will acquire the necessary locks for the duration of the traversal.
-- If the object is attached to a scene, the affected part of the image will be repainted.
ThingModifier: TYPE = PROC [thing: Thing, context: Gr.Context]
RETURNS [changed: BOOL ← TRUE]
-- A client-written procedure for modifying the parameter record or the class of a thing. It is called by ModifyThing and ModifyAllThings after these have acquired the necessary locks.
-- If the thing is attached to a scene, the scene's graphics context (appropriately scaled and clipped to the thing's current bounding box) will be passed to the client's ThingModifier. The ThingModifier can draw at will on this contex, for example to show debugging information. Since ModifyThing and ModifyAllThings repaint the affected portion of the image on exit, such scribblings are ephemeral. If the thing is detached or the viewer is iconic, a null context is passed instead.
-- If the ThingModifier decides not to modify the thing it receives, it may optionally warn ModifyThing and ModifyAllThings of this fact by returning changed=FALSE. This information may enable them to save some time in the repainting and the restoration of the data structure invriants. This is useful mainly when the ThingModifier changes only a few of the things enumerated by ModifyAllThings.
-- Moreover, when repainting the scene and restoring the data structure invariants, ModifyThing and ModifyAllThings assume the ThingModifier has modified ONLY the thing that was passed to it, and ONLY the class and parameter record of that thing. A ThingModifier should not touch any other field or link in any object tree. In particular, if the class or parameter records of the thing are shared with any other things, in any tree, their CONTENTS cannot be touched; the ThingModifier can at most change the links to them.
-- CAUTION: ModifyThing and ModifyAllThings acquire an internal monitor lock before calling the client's ThingModifier procedure. Since the same lock is acquired by practically all procedures from this interface (including GetProp), these procedures CANNOT be used inside a ThingModifier, or a self-deadlock will result. The properties of the thing can be examined from inside a ThingModifier by calling SceneInternal.GetProp instead of Scene.GetProp above.
ExamineAllThings: PROC
[object: Object, Examiner: ThingExaminer, getContext: BOOL ← TRUE];
-- Applies the client-given Examiner to all things that are descendant of the the given object. Will acquire the necessary locks for the duration of the traversal.
-- If getContext is true and the node's tree is attached to a scene, EnumerateAllThings will also pass to the Examiner the scene viewer's context (scaled and clipped to each thing's bounding box). In any case, ExamineAllThings will NOT repaint the scene automatically.
ThingExaminer: TYPE = PROC [thing: Thing, context: Gr.Context];
-- A client-written procedure for examining the parameter record or the class of a thing. It is called by EnumerateAllThings after the latter has acquired the necessary locks.
-- ExamineAllThings may pass the scene's context (appropriately scaled and clipped to the thing's current bounding box) to the ThingExaminer. The ThingExaminer can draw at will on this contex, for example to show debugging information. Those scribblings are ephemeral: they will be erased by any subsequent repaints. If the thingb is detached, the viewer is iconic, or the getContext parameter to ExamineAllThings is false, a null context is passed instead.
-- CAUTION: ExamineAllThings acquires an internal monitor lock before calling the client's ThingExaminer procedure. Since the same lock is acquired by practically all procedures from this interface (including GetProp), these procedures CANNOT be used inside a ThingExaminer, or a self-deadlock will result. The properties of the thing can be examined from inside a ThingExaminer by calling SceneInternal.GetProp instead of Scene.GetProp above.
-- Moreover, a ThingExaminer should not modify the thing pased to it, or in fact any other object in any tree.
-- MENU AND MOUSE PROCEDURES - - - - - - - - - - - - - - - - - - - - - - - - -
MouseButton: TYPE = Menus.MouseButton; -- {red, yellow, blue}
UpOrDown: TYPE = {down, move, up}; -- move means mouse moves with button down.
ClickClass: TYPE = RECORD -- type of menu or mouse click
[name: ROPE ← NIL, -- menu entry name, NIL for mouse clicks
button: MouseButton ← red,
shift, ctrl: BOOLFALSE, -- always FALSE for menu entries
kind: UpOrDown ← down -- always down for menu entries
];
-- A ClickClass value specifies the kind of a menu or mouse event (except for the ouse coordinates).
ClickProc: TYPE = PROC
[scene: SceneViewer, context: Gr.Context,
class: ClickClass, x, y: REAL, data: REF ANY];
-- ClickProcs are client-written procedures to be attached to specific menu entries or mouse event classes in a SceneViewer.
-- When one such event happens, the attached ClickProc is applied to the scene viewer, the viewer's graphics context (if available), and the event class. The client may also attach to a specific event class an arbitrary data record, that is also passed to the corresponding ClickProc.
-- The client can also specify whether the monitor lock (that protects the scene and its tree) should be automatically acquired before calling the ClickProc. If the client refuses this service, the ClickProc itself should acquire the lock before examining or modifying the scene or its tree. The self-synchronized procedures in this interface are of course one way to do that. Conversely, if the client asks for the lock to be acquired before the ClickProc is called (which is the default), the latter should not use any procedure from this interface, under penalty of a self-deadlock.
-- The context will be passed to the ClickProc only if the latter is called with the lock held and the viewer is not iconic.
-- ClickProcs attached to mouse events will be called with the coordinates x,y, of the mouse event, in the client coordinate system. For menu events, x and y will be 0.0.
AddMouseAction: PROC
[scene: SceneViewer, class: ClickType, Proc: ClickProc,
data: REFNIL, access: Access];
-- Attaches the given Proc and data record to mouse events of the the specified class. After this call, whenever the specified event occurs in the scene viewer, the Proc will be called, with the specified access rights already acquired.
AddMenuEntry: PROC
[scene: SceneViewer, class: ClickType, Proc: ClickProc,
data: REFNIL, access: Access, line: Menus.MenuLine ← 1];
-- Adds a new menu entry with given class, Proc and data. After this call, whenever the menu entry is activated by the user, the Proc will be called, with the specified access rights to the scene and its tree already acquired.
AddPropertyButton: PROC [scene: SceneViewer, key: ATOM, line: Menus.MenuLine ← 1];
-- Adds a new menu entry with name = Atom.GetPName[key].
-- When the user red-clicks that menu entry, the key property of the scene's root object is set to $TRUE; when the right button is used, the key property is set to NIL. The entire drawing is repainted in each case.

END.

Edited on August 13, 1983 4:56 am, by Stolfi
changes to: Everything!
Edited on September 12, 1983 6:42 pm, by Stolfi
changes to: SceneViewer, ModifyThing, ThingModifier, ThingExaminer, AddMouseAction, DIRECTORY, Scene, Object, Group, Thing