Construct

The basic building blocks of the Vision are
Objective-C
objects which are constructed from classes that all inherit from a
common ancestor base class called Construct. This base class
defines methods and data which allow the objects to be recognized by
and interact with the Vision server correctly. An object of the
Construct class itself is not very interesting in terms of interface
design and is never actually expected to be instantiated. People
who wish to design interface objects should create their own classes
which inherit from the Construct class. Methods such as - (void)
Draw:(DrawType)mode can be overridden to provide specific
functionality
that will be useful and interesting to those who wish to create
user interfaces. The Objective-C programming convention of
calling [super init] before performing
initializations in the init
function and calling [super dealloc] in
dealloc after cleanup must be
adhered to if you choose to override those methods. Methods and
data members can also be added to the new classes to customize the
kind of functionality the interface object designers desire for it.

Construct Trees And The World Object

Construct objects are stored in an acyclic tree
structure dictated
by the Construct tree organizational pointers described below that are
present in every Construct object. There are various
methods in the Construct class that can alter the Construct tree.
For the objects in a Construct tree to be serviced by the server,
the root of the tree must be added to a special Construct object called
"world". This object is of a special class which
is defined only internally by the Vision server. This
class inherits from the Construct class and thus would appear to be a
normal object that can be interacted with in the system. However,
the special class contains specific versions of organizational
methods which cause objects that are added to it to show NULL as their
parentConstruct and themselves as their rootConstruct. As far as
the Construct tree is concerned, not much has changed. The world
object however is now aware of the tree's existence and thus the tree
will be serviced (organization and drawing) by the server every
frame. You may keep a Construct tree separate and unconnected
from the world object and have other objects interact with them but
they will
not be
serviced by the Vision server.

rootConstruct
-
This is the root node for the Construct tree that
this object
is currently part of. If this is null then it means that this
Construct is not connected to the world Construct tree. If this
is of the value 0x1 it means that this Construct has been
encapsulated (see section below). If it has the value of 0x2 then
it is a descendant of an encapsulated Construct object.parentConstruct - This simply
points to this object's parent.siblingConstruct - This is the
next object in a linked list of
children of
this object's parentConstruct.childConstruct - This is the
head of a linked list of objects that
are
children of this object.

Below are two different representations of the same Construct
tree. One of them visualizes things in terms of the actual
organizational pointers and the other visualizes the objects
parent/child relationships.

Construct Tree Maintenance and Services

When Construct objects are hooked into the world
they then receive
basic maintenance and service automatically every frame from the
server. The first thing that happens to the world's Construct
tree
is that it is organized. Every object in the tree is passed a
- (void) Organize message in a depth first
order which tells the object
to
modify itself and its child objects to adapt to the new situation if
needed. This is performed in a depth first order to ensure that
before an object potentially modifies any of its children's state that
the children have already been organized themselves. The order
with which
sibling objects are sent the - (void) Organize
message is the order in
which
they reside in the siblingChild linked list headed by the
childConstruct of the overlying parentConstruct. This order is
determined by the value of the organizationFlag member variable the
objects when they are added as a child object to their parent.
This order can be altered after the fact or during addition of the
child object with special messages defined in the Construct base
class. The base definition of the - (void)
Organize method in the
Construct class is to simply cause all of the Behavior objects
currently
attached to the object to act on the object. Thus any class that
overrides - (void) Organize should be sure
to call the - (void) Organize
method of the
super class at some point. The reasoning behind this decision is
to allow classes the ability to choose whether the attached behaviors
(and consequently the - (void) Organize
methods of super classes) are enacted
before or after the class specific organization code. Whether a
Construct should implement organizational code as part of its -
(void) Organize
method or instead generalized out as a Behavior object instead is an
important
design decision. Sometimes it makes sense to tie the
organizational code to the specific class but when reasonable
should be pulled out into a Behavior class which can then be
applied to other objects.

After all of the objects in the world's
Construct
tree have been
organized it is time to draw them. The server sends a -
(void) Draw:(DrawType)mode message
to every object in the Construct tree in breadth first order so as to
ensure that the overlying parent objects are drawn first before their
children. Sibling Constructs are again (like in organization)
drawn in the order that they reside in the sibling list. Because
the order of drawing is designated by the Construct tree structure,
drawing may occur contrary to z-ordering. There is no object
based z-sorting occurring and so the burden of correct drawing order
and blending is put on the programmer. Typically the hierarchical
order of the Construct tree will coincide with desired drawing orders
and the drawing orders of specific child and sibling objects can be
customized by altering their position in the linked lists that connect
the Construct tree. Hopefully in the future, a fast, per pixel,
order independent translucency solution will arise either by software
or hardware that will allow programmers and designers to no longer
worry about such things as drawing order. The server handles any
necessary translations, rotations, scalings, OpenGL name loading (for
EventReceptor evaluation) automatically before the -
(void)Draw:(DrawType)mode message gets
sent. The power and convenience given by the server is very great
but there may be times where you might want to forego it in favor of
doing it yourself manually on encapsulated objects as described
below.

Encapsulation

Organizing Construct objects into Construct trees is
how most
functionality in the application interface will be implemented because
it leverages a lot of powerful capability from the server and its
servicing routines. There are some times however when it would be
useful to service Construct objects without them being subject to all
of the services and organization of an object in a Construct
tree. One example of this would be the indented
Hierarchy object. In a Hierarchy object's -
(void) Organize
method there
is code that visually orders and organizes all of its child Construct
objects into a list. However, if it is an indented list there
would be value in being able to treat one object differently by
indenting all child objects except for the one that is supposed to
represent the overarching container or heading of the list. This
can be performed on a single child object but it would require an extra
Construct member variable to distinguish it from the other children and
it would require special cases to be coded into sorting and other
routines that act on all the children. To avoid those pitfalls we
use encapsulation. Instead of trying to make one of the children
special we can pull it out from the child objects altogether and
instead keep a specific reference of it in the Hierarchy object, set
its organizational pointers to special values (described below) and not
even keep it in the Construct tree at all. What this means is
that it will be kept separate from all routines that act on the child
objects and we can save adding memory to all Construct objects.
However, this also means that it will not get serviced by the system
automatically like the other child objects which means that the
Hierarchy object is now responsible for organizing, drawing, releasing
and any other needed maintenance. This allows for very tight
control and optimization of the maintenance of objects that are
encapsulated with the tradeoff of more code on the part of the
programmer (but not as much or as complex code as it would take to
write special cases). Because the maintenance of encapsulated
objects lies with their encapsulator (unless specified otherwise with
special flags) they are for most purposes the same one object.
Construct object pointers do not necessarily denote that those objects
are encapsulated and functionality-wise they are no different than
pointers to encapsulated objects. The only difference is in how
the class treats them.

Another good example of encapsulation
that
illustrates this concept is
that of the PanelLabel
object. You have the classes Panel and Label which display a
rectangular plane and text respectively. Because it is useful to
have text set against a contrasting background, putting a Label object
infront
of or more ideally as a child of a Panel object is a common object
arrangement. But it is tedious to build and describe that
situation over and over and it would be better to create another class
that would provide that combined functionality. Thus the
PanelLabel class is conceived. In the diagram are shown
three possible ways to
organize both pieces of functionality.

PanelLabelIntegrated

First is a class (noted as
PanelLabelIntegrated in the diagram) that simple inherits from the
Construct base class and has the functionality of both a Panel and
Label copied into its code. This has the advantage of only
producing the
overhead of only one object but the great disadvantage of not being
independently upgradeable or customizable. This means that as the
Panel and Label classes are improved and given more functionality those
improvements will not extend to the PanelLabel object unless they are
also copied in and adapted to it. It also does not allow for
future subclasses of Panel or Label to be taken advantage of
either. There can only be the hard-coded combination that the
PanelLabel
class originally contains.

PanelPartiallyIntegrated

The next example brings some
flexibility
to the table at the cost of more overhead. This time
PanelLabel (marked PanelPartiallyIntegrated in the diagram) inherits
from the Panel class (although it could just as well have been the
Label class) and contains a Label object pointer ("Text" in the
diagram) that is meant to point to an encapsulated Label object.
In this example the Label object can be switched out at runtime to take
advantage of a Label subclasses which might be more suited to the
program
design. Also, any improvement that the Label class undergoes will
automatically be leveraged without any recoding of the PanelLabel
class. However the same flexibility does not extend to any future
improvements/subclasses having to do with the Panel class.

PanelLabel

The
last example is how the PanelLabel class is actually implemented.
It takes the flexibility in the second example with the Label aspect
and implements it with the Panel aspect as well. The PanelLabel
class inherits directly from the Construct base class and now has two
encapsulated object pointers. Now it can take advantage of any
future advancements in both the Panel and the Label classes as well as
any superclasses that are introduced. As far as the rest of
system is concerned all three of those objects are functionally the
same object and will be treated as such in things like EventReceptors
if specified. This flexibility comes at the cost of potentially
three full object overheads. However, the PanelLabel may choose
to only partially implement the maintenance overhead that it now
manually controls in its two encapsulated objects which can lower the
total overhead. In some respects this emulates the idea of
multiple inheritance.

When combining the functionality of more
than
one existing Construct class care must be taken in the design
decisions of how exactly to implement the combination. Most of
the time however, the flexibility and power gained by the third
PanelLabel example is well worth the overhead, especially down the road
when it can transparently take advantage of future advancements.
The ideas of encapsulation might seem complicated but as your designs
become more complicated encapsulation helps solve a lot of
problems.

Interacting With Construct Objects

There are many ways with which you can
interact with
Construct
Objects. You can change their variable values by calling instance
methods on them or in cases where you can have direct object memory
access you can alter their values directly. You can also assign
Timers, EventReceptors, Behaviors or other structures to them which
can alter data values and/or execute callback messages on
objects. You typically assign those structures to your objects
when you instantiate them and unless they are released beforehand they
will automatically be released by the server if objects they interact
with are also released.