|
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:
Everything Is an Object
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.
Composite Objects and Recursivity
The GLG Toolkit provides a rich set of composite objects to
create various collections of objects. There is a Group
object which allows permanent 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.
Groups and Layers
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.
Reference Object
A Reference object provides a convenient way to use
an object several times in a drawing without replicating it.
All instances of 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 independent
dynamics.
The template object may be stored in a separate file, in which case the reference serves as a subdrawing. A subwindow object may be used to display loaded drawings in a separate window. 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.
Viewport Object and Window Hierarchies
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
"Native" interface objects, such as buttons, toggles, text and combo boxes, lists and scrollbars, are supported via 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 combo boxes on Windows, as HTML elements in JavaScript and as Motif widgets on Linux/UnixX. 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.
Interaction Handlers
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.
Objects are Resources
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.
Accessing Resources through Resource Hierarchies
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:
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:
Complex, unlimited hierarchies of resources can be created, as in the following example:
Resource Persistence
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.
The Tag Data Access Mechanism simplifies data connectivity to external process databases. While resources reflect the object hierarchy of the drawing, tags are global. Each tag defines a name of a data source variable that supplies data for the dynamic attribute the tag is attached to.
A tag is assigned to a dynamic attribute in the drawing. For example, a tag Voltage may be assigned to the Value resource of a dial widget, and the dial may be animated at run-time as follows:
An application can query a list of all tags defined in the
drawing and use this information to animate the drawing with
values obtained from the corresponding data sources, without
knowing the exact structure of the drawing. The use of tags
makes it possible to develop a generic viewer application
that can load and animate an arbitrary GLG drawing.
A Custom Tag Data Browser
may be integrated with the Graphics Builder or HMI
Configurator, so that the user can browse application data
sources and select tags from the list in the data browser.
Tag assignment may be done at design time in the editor, or at
run-time in the application code.
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").
Constrained Dynamics
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 chart, dials, meters, sliders and other input widget objects are examples of intricate objects with complex behavior built completely in the GLG Builder with no programming.
The process control as well as electrical switch and relay widgets provide another example of objects with a predefined animation behavior. Elaborate behavior may be added to custom graphical components to change their appearance depending on the input data and user interaction. The newly created custom components can be added to the editor's widget palettes to facilitate reuse.
Inherent Dynamics
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.
Automatic Damage Repair
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.
Data Driven Update
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 charts would update all of them on every polling iteration, moving even the charts that did not receive new data. A data-driven system, however, can update only the chart that received new data, leaving the others untouched.
Granular Update
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.
Custom Reusable Components
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 charts, gauges, knobs, sliders and so on.
Most existing systems use a "black box" approach for creating charts, dials and other widgets, with pre-programmed code defining the behavior of a particular widget. This approach requires writing code for each widget type, and limits widget customization to the features that were handled in the original widget code. Adding a new feature to the widget requires creating a new library, no matter how insignificant.
In the GLG Toolkit charts, dials and other widgets are just drawings -- collections of objects linked and constrained to one another -- no different from any other drawing. Each widget 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, ranges, etc.), but can add complex new features, modify a widget's layout, and behavior.
Resource-Driven Data Supply
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.
Customization
There is an unlimited amount of customization that may be done to any GLG drawing. Any attribute of any object inside of any widget can be changed, either in the editor or animated dynamically at run time. Additional objects can be added to a widget's drawing to provide custom annotations, or perform custom application-defined functions.
For example, you might add text annotations, change a widget's layout, add multiple pointers to a dial, or add extra axes to a chart. You could also use define connections between objects to specify custom behavior - for example, make an object blink when a corresponding data value goes out of range. All this would require custom and very extensive coding in models where widgets are implemented as black boxes.
Since all widgets 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.
Programming Components and Portability
An important feature of the GLG architecture is the simplicity of using a GLG drawing in a program. The GLG Standard API is quite small, comprising of only a handful of functions for displaying a drawing, animating it with data and handling user interaction. This shortens the learning curve, making it possible to display and animate a drawing with data with literally just a few GLG API functions. The GLG Intermediate and Extended APIs are also available for programs that need to change drawings at run time or create drawings dynamically based on configuration data.
The same set of API methods is available in a variety of environments, deployment platforms and programming languages. It is available as a Java or C# class library, C library, C++ class library, HTML5 JavaScript library, X11 widget, Java Bean, C# / .NET Control, Windows control or MFC class, ActiveX control, and on platforms ranging from Windows to Linux/UNIX, real-time and embedded ARM systems (Raspberry Pi, BeagleBone, NVIDIA Jetson Nano).
In addition to the platform-specific programming containers (such as platform-specific widgets and controls), the GLG Generic API provides cross-platform methods that may be used to display and animate a drawing in any programming environment. C/C++ programs that use the GLG Generic API to display the drawing can be moved to a different Windows or Linux/Unix platform by simply recompiling them on a target platform and linking with the GLG library for that platform.
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 deployment options, such as the JavaScript API, is limited to a small subset of functionality that was possible to expose, and rewriting the system in a new language, such as Java, C# or JavaScript is close to impossible due to the huge amount of old code that would have to be rewritten.
In GLG, all its functionality is defined in a relatively
small and generic object engine, and all custom functionality
(charts 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 prebuilt widgets and other
functionality provided in the drawings becomes immediately
available. As the result, it was possible to port the GLG
Toolkit to Java, C# or a HTML5 JavaScript for the Web in a
record time and without losing any functionality available to
GLG applications in C or C++. Once the graphics engine has
been ported, all chart and control widgets were immediately
available in the Java, C# / .NET or Web and Mobile
environments.
The small size of the GLG API makes it easy to support additional deployment options without loosing extended functionality. For example, GLG Java, C#, ActiveX and HTML% JavaScript components support all functionality available for C and C++.
The GLG JavaScript library makes it possible to deploy GLG
applications on a web page in any compliant web browser. The
GLG Graphics Server provides an alternative server-side web
deployment option with either ASP.NET or JSP. GLG drawings do
not require any change to deploy them on the web, and the
programming logic can also be reused between the desktop and
web versions of an application.
C++ Class Library
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 the eventual
needs of the system, and to make sure that the design
solutions envisioned for the initial solution will scale with
the project. Using the GLG Toolkit to solve your graphic
design problems will allow designers to concentrate on the
problem at hand instead of on their software tools.