The power and flexibility of any modern software tool depends on its internal architecture. The cleanness and expressive capability of the internal model defines many of the external characteristics of a software tool -- its performance, ease of use, and adaptability.
While many software packages rely on complex structures with deep hierarchies of objects to achieve relatively simple program behavior, the GLG Toolkit, a graphics toolkit for creating complex animated drawings and user interfaces, is built around a different model. Using a small number of easy to learn concepts and objects, the GLG Toolkit can achieve very complex program behavior by allowing the same simple objects to be used and reused in many ways. Instead of defining a rigid hierarchy of objects to use with a limited interconnection capability, the Toolkit provides a small number of objects and object actions with unlimited interconnecting capabilities. This allows users to build complex drawing structures from simple components.
The GLG model shortens the initial learning curve, since you can start experimenting with GLG objects right away. Later you can start building more and more complex drawings by creating complex connections as you go along.
The organizing philosophy of the Generic Logic Toolkit might be described as one of "relentless abstraction." A small number of data objects are used for a large number of tasks, making object reuse and generalization a central organizing principle of the system -- instead of just a useful feature. This provides a tremendous flexibility to the architecture, allowing it to apply equally well to simple and complex tasks.
The Generic Logic Toolkit is organized around a is based on the following design principles, described in detail further down on this page:
The object model is the central idea of the GLG Toolkit -- everything in the Toolkit is represented as a variant (subclass) of a generic data object. Drawing primitives, windows, motion dynamic, object attributes, messages and internal structures are represented as objects. As an example, a color (three values representing red, green, and blue) is represented with the same data structure as a point in three-dimensional space (three values representing X, Y, and Z coordinates).
Attribute Objects and Constraints
The object model is taken one step up compared to the conventional implementation of graphical systems: not only graphical primitives are objects, but their attributes as well. That is, a rectangle is represented as a primitive object, and so is the color that describes its fill.
An object can be used for two or more purposes. For example, the fill colors of two rectangles can be specified with the same color object. After such a link -- or "constraint" -- is made, changes to the color of one rectangle automatically affect the other. Accumulating simple primitive objects and constraints between them allow a designer to build these simple elements up into complex data structures representing intricate graphical concepts.
The GLG Toolkit provides a rich set of composite
objects to create
various collections of objects. There is a Group object
or temporary grouping of other objects, a Viewport
object that contains other objects and provides a drawing surface for
them, a Series object which takes a "template" and generates a
number of copies, and others.
Composite objects may be used recursively to create hierarchies of objects of arbitrary complexity. For example, one group may contain other groups and viewports, or one series object may be used as a template for another series object. There is no limit for the depth of the object hierarchy in the GLG model.
Composite objects enable a user to manipulate a
set of objects as
one entity. For example, you may change or constrain attributes of all
the objects in a group, transform all the objects in a group, or just
use the group as a temporary editing tool.
To implement layers, objects may be placed
inside a group.
The layer can be toggled by simply setting the group's visibility. To
implement sub-layers, the groups may be nested.
More complex layering functionality may be achieved by using constraints. For example, a network monitoring application may need to display network nodes and toggle the nodes' labels on or off. Instead of separating labels from the nodes and placing them into a separate layer (which would make them harder to manage), the application can keep labels inside the nodes and constrain the labels' visibility attributes. The application can then switch all labels on and off by setting a single label Visibility resource. A combination of the two layering techniques may be used to satisfy any custom layering requirements.
A Reference object provides a convenient
way to use an
object several times in a drawing without replicating it. All instances
a reference object in the drawing can be modified by editing a single
template object. This simplifies editing drawings with a large number
of identical objects. Individual references may have constrained or
The template object may be stored in a separate file, in which case the reference serves as a subdrawing. The DrawingFile attribute of the reference object controls the drawing to use as a template, which can be changed dynamically. A list transformation with several filenames may be attached to the DrawingFile attribute to implement subdrawing dynamics: changing the index parameter of the list will change the drawing displayed by the reference object.
In most graphical systems, only drawing primitives are handled as objects. The windows in which these objects appear are handled in a special way, usually programmatically, and the operations defined for objects can't be applied to the windows.
In the GLG Toolkit, a window is handled as an object -- a viewport object is an encapsulation of a native (to the windowing environment you are working in) window. The Toolkit makes no distinction between graphical primitive objects, such as polygons and circles, and a viewport object. In the same way as with other objects, a viewport object may be part of a group or placed inside another viewport. This enables a user to interactively create hierarchies of windows and easily embed drawings inside other drawings in the GLG drawing editor, also called the Graphics Builder.
"Native" interface objects, such as buttons, toggles, and scrollbars are supported with the Window Type attribute of a viewport. If this attribute is set, a viewport object renders a native control or widget defined by the type attribute. This adds the extra convenience of placing native buttons and sliders right in the drawing. These native objects are supported cross-platform; they appear as Windows buttons and sliders on Windows, and as Motif widgets on UNIX. This cross-platform capability is automatic and does not have to be programmed. Thus the same drawing may be deployed in different environments with a native look and feel.
An Interaction Handler object may be attached to a viewport object to provide a predefined behavior and handle interactive aspects of buttons, sliders, knobs and other interface elements represented by a viewport. The behavior of interaction handlers may be modified by defining resources that control the handler's logic. Interaction handlers also allow reacting to the user input programmatically.
Resources provide a convenient way to access objects in a drawing hierarchy. A resource is simply an object's name, arranged in a hierarchy of names. For example, the point at the corner of a rectangle in a window might be called something like this:
This would indicate a window called $Widget containing a rectangle called Rect, one of whose corner points is named Corner. Since not all objects need to have names, this name hierarchy can be much simpler than the actual object hierarchy of the drawing. The rectangle in the above example could be replaced by a complex composite object, and still use the same name for the corner point. Most often, you would use resources to change the attributes of an object. The words are often used interchangeably; a resource is just an attribute that has a place in the resource hierarchy of names.
Resources can be easily accessed by calling the GetResource and SetResource functions, which are the only functions required to query or change any attribute or data value in the Toolkit.
The GetResource and SetResource functions can be universally applied to any GLG object, which means that you can access or change any graphical attribute of any object in the drawing with no complex interface to learn. You don't have to call different functions to access different parts of various objects.
One of the most important questions for an application programmer is "How would I supply data to my application?". A resource based access provides a simple answer: drawing data is modified by setting resources.
Resources are accessed by names, and resource names are simply names of objects in the drawing. An object name is used to identify an object and is supplied as a parameter to the resource access functions. Attribute objects have default names for convenience. For example, the following function call:
SetDResource( polygon, "LineWidth", 5. );
may be used to change the line width of a polygon. The letter D in the function name identifies the Double (floating point scalar) resource type. In this example, LineWidth is the default name of the objects' line width attribute.
Resources can be organized hierarchically, allowing access to the resources of different objects in the same way files are accessed in a file system. The following example sets the FillColor of two polygons named "Polygon1" and "Polygon2" using hierarchical resource names:
SetGResource( drawing, "Polygon1/FillColor", 1., 0., 0. ); // red
SetGResource( drawing, "Polygon2/FillColor", 0., 1., 0. ); // green
Complex, unlimited hierarchies of resources can be created, as in the following example:
// Setting some variable in the system to 50.
SetDResource( drawing, "Component1/SubComponent2/Part3/Variable1", 50. );
Using a conventional graphical system to display data from an external source creates a problem of "data connectivity". In a conventional drawing data are external to the object and have to be reconnected every time a new copy of an object in the drawing is created, otherwise the new instance of the object will share the same data with the original object.
A GLG drawing, on the other hand, is simply a sophisticated data structure, and actually contains the data it displays. Resources are part of the data structure of a GLG drawing and are permanently attached to it. If an object is copied, the copy will get its own resources attached in an identical way, saving time which would otherwise be spent to set up resource and data connection hierarchy. Any constraints and resource hierarchies are preserved and always stay with the object. Just give the new object instance a unique name, and you can start accessing it's resources right away.
Transformational Approach to Motion Dynamics
A transformation object can be attached to an object to move, rotate, scale or transform that object in some way. Transformations are not the only way to define an animation in a GLG drawing, but they are a simple and intuitive way to do so.
Changing the attributes of an object's transformations moves that object in the way defined for that transformation. There are translation, rotation, and other types of dynamics. For example, when you change an angle of the object's rotate transformation to rotate it, all necessary graphical updates -- transforming object points and redrawing the object at a new position -- will be handled by the Toolkit automatically.
The attributes of the transformation object, which control how the attached object is changed are inherently dynamic and can be accessed as resources. You can control these resources in precisely the same way you control the resources of any other GLG object. For example, a rotation object can be attached to a polygon to rotate it. The rotation transformation has two attributes, the angle of rotation and the center around which the rotation is defined. You can control these two attributes dynamically by changing their value with the GetResource and SetResource functions, just the same way you would control the fill color of the polygon or the position of one of its vertices.
All transformation objects operate in 3-dimensional space. Object rotation, for example, may be done around any of the X, Y or Z axes. Several transformations may be attached to any object or set of objects, allowing you to create complex 2D and 3D animations.
A transformation object may be applied not only to drawing objects such as polygons, circles, rectangles and so on, but also to an object's individual points or even to object attributes. For example, one point of a polygon may move while other points stay unmoved, or a rectangle's color could "rotate" through three-dimensional color space. On a more practical level, an indicator animation could change color as it moved to show passage of different levels (for a fuel gauge, red could indicate "almost empty" and green for "full").
The attributes of a transformation object can be constrained, in the same way any other object's attributes can be constrained. A constraint links a transformation attribute to another drawing attribute, making the two operate as one. For example, after constraining the rotational centers of several transformation objects to one another, moving that single point moves the whole thing. (And that point itself might be linked to another GLG object.) A complex model of a spoked wagon wheel might contain many objects, each of which might be linked to a rotation transformation to make the wheel spin. If the centers of all these transformations are linked, then moving the entire wheel is as simple as moving the rotational center. (To accomplish the same effect, you could also link all the wheel components to the same rotation transformation object. With a powerful architecture like GLG's, there may be more than one way to model any particular problem.)
Modeling Complex Behavior
The combination of graphical primitive objects, transformation objects, the mechanism of constraints and unlimited recursivity enables a user to create very complex drawings from a small number of relatively simple components. The simple elements -- drawing primitive objects, containers, references, transformations and constraints -- can be combined endlessly to model virtually any graphical system. Since there are no limitations on how these components can be interconnected, your creative capabilities aren't limited either.
The GLG ready-made graph and input widget objects are examples of intricate objects with complex behavior built completely in the GLG Builder with no programming.
A GLG object is inherently dynamic. Unlike other graphical systems where dynamic features must be tediously and explicitly applied, all attributes of a GLG object are dynamic by definition.
Consider the dynamics of color or line width attributes, for example. In order to animate these attributes in other graphical systems, you would have to attach a "dynamic" object with a complex connection to the data to the attribute. With a GLG drawing, you can programmatically change the fill color or line width attribute of an object by accessing them as resources, and the object appearance will change. All necessary display update and repair will be handled automatically by the Toolkit.
The ability to change attributes directly is especially important when you don't know in advance what attribute you'll be changing dynamically, or when you have a large number of dynamic attributes. Inherent dynamic architecture saves a lot of the editing and attaching dynamic objects you would need to do with other systems.
When anything is changed in a GLG drawing, all the necessary graphical updates and redrawing of damaged objects are handled automatically by the Toolkit. There are no artificial erase or repair methods to setup and no code to write.
The update will happen automatically, performing all necessary optimizations and utilizing double buffering for smoother updates. Double buffering may be disabled for individual viewport objects and drawings if desired.
Updating a GLG drawing is data driven and happens only when the drawing's data changes. There is no idle polling to waste CPU cycles.
This structure provides fine-grained drawing control, allowing you to update individual objects in a complex drawing, leaving others unchanged.
For example, a system based on a polling loop displaying several graphs would update all of them on every polling iteration, moving even the graphs that did not receive new data. A data-driven system, however, can update only the graph that received new data, leaving the others untouched.
You can also fine-tune the update granularity by updating after every change, after every several changes, or after updating the whole drawing. Contrast this with a poll-based system, which must always update the whole drawing on each loop iteration, unless complex programming is utilized.
An important feature of the GLG Toolkit is that it is easily used to create drawing components that can be used in other, more complex drawings. A drawing designer can easily define these components, and define simply-named resources that make them easy to use in other drawings. These drawings can later be reused in different designs and applications. For example, you can create a library of objects for process control applications, such as valves, tanks, gauges, etc. These objects will also store any data, dynamics and resource hierarchies. To reuse an object, simply load it into your drawing.
GLG offers extensive libraries of predefined components for use in drawings, such as graphs, gauges, knobs, sliders and so on.
Most existing systems use a "black box" approach for creating graphs, with pre-programmed code defining the behavior of a particular graph. This approach requires writing code for different graphs, and limits graph customization to the features that were handled in the original graph code. Adding a new feature to the graph requires creating a new library, no matter how insignificant.
In the GLG Toolkit graphs are just drawings -- collections of objects linked and constrained to one another -- no different from any other drawing. The graph behavior is defined by the structure of the drawing, which can be modified and customized by the user with no programming. The end user can not only do simple editing (changing colors, graph ranges), but can add complex new features and modify the graph behavior to edit graph layout, add new titles, annotations and axes, modify scrolling behavior and add new graph functionality.
Data is supplied to a graph by simply calling the same resource-setting functions that are used for any other animation. You can supply data values and labels, and let the graph handle scrolling, or you can control every component of a graph individually, providing data for individual data samples and graph labels.
There is an unlimited amount of customization that may be done to any GLG drawing. Attributes of individual labels and data samples may be dynamically changed for additional application-specific annotation, composite graphs can be created combining several graphs in one, and additional objects may be added to the graph's drawing to perform custom application-defined functions. For example, you might add text annotation to data samples, or make an object blink when a corresponding data sample value goes out of range. All this would require custom and very extensive coding in models where graphs are implemented as black boxes.
Since graphs are just drawings, modifying existing or adding new functionality does not require a new library -- just a new drawing created (or modified) with the GLG Builder, with no recompilation or relinking required.
An important feature of the GLG architecture is the simplicity of using a GLG drawing in a program. The GLG API is quite small, comprising only a handful of function calls. This has important ramifications for portability. A very small API makes it simple to create bindings for different environments in which the GLG Toolkit can be used. Currently, the Toolkit can be used with native or platform specific API, in Java, C#, C, C++, MFC, VB.NET and other environments. It is available as a Java or C# class library, C library, C++ class library, widget, Java Bean, C# Control, Window class or ActiveX control, and on platforms ranging from Linux/UNIX to Windows and real-time systems.
In older systems, where animation requires more than just a resource change, there is generally either a huge programming API necessary to set up and drive the animation, or a separate closed run-time engine with its own environment. This makes upgrading to new technology extremely hard, and limits the portability of design solutions between platforms. Often, functionality is sacrificed in the name of portability, or portability considerations are forced onto the system implementers using these graphical systems. Introducing new components such as the ActiveX control is limited to a small subset of functionality that was possible to expose, and rewriting the system in Java or C# is close to impossible due to the huge amount of old code that would have to be rewritten in Java.
In GLG, all its functionality is defined in a
relatively small and
generic object engine, and all custom functionality (graphs or
controls, for example) is encoded in the drawing by the way the objects
in the drawing are connected. As soon as the object engine is ported,
all graphs and other functionality provided in the drawings becomes
immediately available. As the result, it was possible to port the GLG
Toolkit to Java and C# in a record time and without losing any
available to GLG applications in C or C++. Once the graphics engine has
been ported, all graphs and controls widgets were immediately available
in the Java and C# / .NET environments.
The small size of the GLG API makes it easy to
deployment options without loosing extended functionality. For example,
GLG Java, C# and ActiveX components support all functionality available
C and C++.
The GLG Graphics Server deployment options enable
web deployment of
GLG applications with either ASP.NET or JSP. GLG drawings do not
require any change to deploy them
on the web, and API calls require only minor syntax change to convert
them to either Java or C#.
The GLG Library is used to load GLG drawings created using the Builder into your application and to animate them with application data.
In addition to a C library, the GLG Toolkit provides C++ API for accessing and manipulating GLG objects as C++ objects. You can derive new classes from GLG objects, inheriting GLG functionality.
If you use the C++ API, not only the top level drawings are C++ objects, but objects inside the drawings are C++ objects as well. You can use the GetResourceObject method of a GLG object and it will return a C++ object representing the GLG drawing object. For example, you can obtain a C++ object representing an object in the drawing or an attribute of a graphical object.
The GLG C++ class library frees you from keeping track of objects by handling object referencing and dereferencing automatically. Objects are automatically referenced when used and automatically dereferenced or destroyed when the object goes out of scope.
The GLG Toolkit provides several types of C and C++ interfaces. A native platform-specific interface may be used to utilize a native X/Motif or Windows programming environment. A generic cross-platform interface may be used to allow the same source code to be compiled and run on any platform without modifications. On Windows, MFC classes are used as a native C++ API.
The small size of the GLG C and C++ class library API allows you to start using all its features quickly. You can use supplied C++ demos and examples as templates for creating your application. The source code for the C++ class library is supplied for your convenience.
Broadly speaking, there are two approaches to the graphics system design:
Interface-driven systems are programmed around a convenient user interface and have a user interface for every action that was foreseen in the original design. However, things that were not accounted for are impossible to do without programming, so these systems generally grow over time as code is added to handle user requests.
As a system grows bigger, however, the internal architecture becomes fragmented. Individual extensions grow in different directions, do not work together and become more and more difficult to maintain. If a user tries to work around missing functionality in an application program, it often turns out that only certain configurations can be supported. The system becomes less and less flexible for further development, release cycles grow longer, new releases contain more and more bug fixes and fewer and fewer new features.
Things are quite different for a model-driven system, where the purity of the internal model and architecture is the primary concern. For a sufficiently rich model, most new features can be mapped onto the internal architecture, with additional programming only occasionally necessary. Model extensions are kept to minimum by careful consideration and finding a general architecture which will handle not only the requested feature, but also a whole spectrum of related features. The user interface for the new feature is added only when the details of a new architecture extension have been finalized.
The most significant drawback of the model-driven approach is that the user interface may be more complex compared to the interface of the interface-driven systems. A user may need to follow a several-step procedure of attaching objects and arranging object hierarchies, in cases where the interface-driven systems provide a precoded functionality available at a mouse click. For a sufficiently rich architecture, there may be more than one way to solve a particular design problem, which can be confusing to users.
However, while systems built using a model driven approach -- such as the GLG Toolkit -- may be more difficult to use at the beginning, they win quickly as the user develops bigger and more complex applications which require more and more features and flexibility.
In any large system, it is important to consider
of the system, and to make sure that the design solutions envisioned
the initial solution will scale with the project. Using the GLG Toolkit
solve your graphic design problems will allow designers to concentrate
the problem at hand instead of on their software tools.