Main - Overview - Documentation - FAQ - Media

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. 



Copyright © 2004-2011 Aoren LLC All rights reserved.
[email protected]