The Modelzilla user input/output machine layer - API Reference

The user input layer (the Modelzilla layer) builds upon the geometry layer and the graphics layer. It adds general a user input data model, and some user interface widgets for controlling the user input data. All other features are actually supporting systems for the general data model and user interface.

The Modelzilla database is so useful that we have considered splitting it of into separate package that would work independently of the 3D graphics application framework. However as currently written it is geared towards 3D graphics. Possibly the parts not specific to 3D graphics could be separated for use in other interactive software.

The Modelzilla database resembles embedded object-oriented database. It is a tree based, rather than a table based. The tree based system is more appropriate for a physical modeling system than a table based system. Tree based systems mirror the physical agglomerations that occur in the physical systems. In most cases properties of an object are best described by sub objects. But sometimes properties or effectors of an object are better left as independent objects that have no special attachment. Because of this, the Modelzilla database also supports arbitrary cross linking between tree nodes. For example, say we have a model of a person, and one property effecting him is the quantity of hair on his head. This property is best made a child node of the person, possibly enclosed by a scalp or a head node. Another example, say we have a person sitting on a chair, which is affecting him by not letting him fall to the floor. The chair is best kept as a separate node in the tree whose relationship to the person is kept via a cross link.

The basic idea of the user input database is the following: in a computer modeling system such as Modelzilla the user inputs information that is then used compute derived information, such as a geometry or an in memory representation of a molecule. The derived data is the output. So Modelzilla database is an input/output machine. Derived data is then often used as input to compute further derived data. The Modelzilla database facilitates the storage user input and facilitates the transfer down to derived data that is eventually displayed in a view-port.

(In this documentation we're going to try to use a convention that "information" is a higher level idea with connotations towards direct human accessibility, wheras "data" is a lower level idea of this in the experimental case taken directly from observations of nature, or in the modeling case where the transfer goes the other way, the data is computed to try resemble observations of nature from the input information.)

Does this wonderful database hinder the creativity of the application developer? The answer of course is no. The application developer uses the database by extending generic data nodes with nodes designed to store his own specialized user input data, and nodes designed to compute his own specialized derived data. The database supports the application developer by allowing his code to interoperate with data from other applications. It allows the user to express relationships between models from different applications. Without the database the system would be a mess to use.

The decision of what information is stored in the database simple. Information that is directly controlled by the user should be the database. Most of the time, the application developer will need to store some information that only his application knows about in his own special place. This could be out side the database, or within a data node extension. For example, our GridZilla application has a data node that holds volumetric data. It keeps a file name as user input data, and reads the volumetric data in the process of computing its derived data. It keeps this data by assigning it to its internal fields that are separate from the database. Other data nodes defined by GridZilla know about this type of node and can ask it for its volumetric data. Derived data is often passed around between specialized nodes via specialized functions like GridNode.getVolumeData() in GridZilla. However it is possible to register that a node is a source of a certain type of derived data. This allows other nodes to request the data without knowing the data source node's exact type.


Storing your data

Some Known Issues

First of all we know that it is rather presumptuous to assume that any application can store all of its user input data in the Modelzilla database. Doing this and a convenient way may not always be possible Sometimes an application may need to store data in its own the specialized mechanism, possibly referenced by an application specific subclass of a Modelzilla data node.

A statement of fact, although not a recommendation, is that it is possible for the application developer to go around the Modelzilla database, and add geometries directly to the ViewerCore layer. But this is a bad idea. This would make geometries or operations and application builds or does inoperable with other applications. The rouge application's geometries would be in the view-port but the user would have no handle to them from other applications. The data nodes are the main organizational feature of application interoperability. As an example, an application that computes a molecular structure and makes a geometrical depiction of it and just dumps the geometry in a viewport is not useful to a user who wants to use our GridZilla application to map electrostatic potential data onto its surface. The user would have no way of selecting the molecular depiction and inputting it into GridZilla surface coloring operator.

We see two main reasons for an application developer to develop application that uses the Modelzilla framework.

The first reason is to save to development effort. The first two layers of the Modelzilla framework, the geometry layer and the graphics layer, make a nice library for doing 3D graphics that is embeddable within any application. The third layer, the database, is also a nice library for storing user input data assuming its organization is appropriate for the specialized application. It is easy to use for all of the applications we have developed so far, but we are not assuming that this will be true for any application.

The second reason is to enable integration for your application(s) with other physical modeling applications. We have discussed this enough. Unfortunately, for full integration the application developer is forced to use a rather presumptuous database. This is because the Modelzilla database is serving both as a library to be used by applications, and the interface allowing applications to plug in to the inter-operation framework Conceivably there should be a separate set of classes or interfaces for achieving this goal.

Such a set of classes and interfaces would describe abstract functions to:

In the future we're hoping to separate the data storage mechanism and the application interoperation mechanism. In this improved system the application developer could define his own data structures that aren't linked to the database by extending ModelNodes.

Important classes

A Modelzilla application mainly uses these classes

Assembing ModelNodes

The Modelzilla database is a tree of ModelNodes (found in the package harmonic.mz.data). There is no single database object that all data is inserted into. Instead, the database is simply the sum total of the assemblage of ModelNodes. There is class called DataManager that functions as a overseer of the ModelNodes, but application code largely just uses the ModelNodes and ModelNode subclasses directly.

Because we wanted it to be possible for class already extending other classes to be members of the database, the interface DataNode is really the minimum requirement. The ModelNode class is simply a default concrete implementation of DataNode. SimpleNode is another concrete implementation of DataNode, that is smaller and simpler than ModelNode. The general data storage and transfer mechanisms expect only a DataNode, for the most part. This is a rather large interface, and in practice all of the existing application modules subclass ModelNode for their storage, except for VincentVanMol, which builds molecular data over the SimpleNode.

Note: This system may change significantly. We are considering separating the "structure" and the "payload". In the new system, the specific data would still be kept under a ModelNode, which would be the payload of a TreeNode. This would simplify the DataNode interface. Or conceivably the payload wouldn't have to implement or extend any particular interface at all. A good design might be to have a third object that handles the communication between the payload and the general database system. This would enable special data objects (such as molecular data) from other independent libraries to live within the Modelzilla database while at the same time live in their own systems. It could be called DataNodeAdapter.

Individual pieces of user input data are to be stored in extensions of ModelNode. The package mz.data.fields has many examples of extensions to ModelNode. For example, ScalarField holds a single scalar value. It keeps this value internally as a Java primitive float. It has methods for returning the scalar value, and assigning the scalar value. When the value is reassigned via the access functions, the ScalarField class calls ModelNode.valChanged(), which is a convenient function that causes a NodeEvent.VALUE_CHANGED event to be sent to ModelNode event listeners. The ScalarField also knows how to build a user interface widget to control its value, it knows how to represent itself as XML, and it knows how to record its state for undo/redo.

But it is a very simple data node. The ScalarField class does not have the capability of computing derived data. It simply holds a scalar value. The ScalarField class is also totally self contained in is user specification; it does not accept input from other nodes. Its entirely is defined by that single scalar value. (Actually this is not strictly true. Any ModelNode can be linked by user to another ModelNode with the same type, but this is rather a separate idea.) In other words, the ScalarField is a leaf.

A ModelNode may be composed of other ModelNodes. The most common case of composing a ModelNode is declaring that is always has a fixed set of child nodes, where each of these children has a fixed name and type. But this is just like a JAVA field declared in a class. Because of this similarity, the function ModelNode.addField() is used to add a child nodes of guaranteed name and existence. Typically when adding a field node, it is convenient to also assign it to a real JAVA primitive field for convenient access to it from the parent node. Field nodes are typically added to the parent nodes in the parent node's constructor function, guaranteeing that they are always there. The user is not allowed to remove field nodes. Field nodes are represented differently in the Modelzilla tree/network editor. Fields are also stored differently in the MVG files. Field nodes are the mechanical equivalent of simply keeping user input data and an ad hoc Java primitive. (But of course keeping the data in a smart object such as a ModelNode instead of a primitive has the advantage user select-ability, participation in automatic file saving, change event notification, etc.)

As an example of using field nodes, in the CADZilla application there is a ModelNode that holds the user specifications for a circle, called CircleFromCenterRadiusShape. In the constructor, this class adds a center and a radius as ModelNode fields like this:

	addField("center", center = new ScalarField());
	addField("radius", radius = new PointField());
Possibly the classes in mz.data.fields should be renamed to end in Node instead of Field. There is nothing special at all about a class that ends in Field. They do not have to be added a node as a field. This is just the way they are most commonly used.

If a node is going have the ability to compute derived data, it must override the function ModelNode.validate(). The CircleFromCenterRadiusShape is a very simple example of a node that computes derived data. Based on the values of the center and radius fields that it keeps, it computes a polygon object that is the graphical representation of a circle. It does this in its validate() function. Because the CircleFromCenterRadiusShape is an extension of ShapeNode, (which is an extension of ModelNode), it uses ShapeNode.addGeometry() to store the polygon representation of a circle that it computes. This method keeps geometries in standard place that is looked at by the method that retrieves derived data of geometrical nature (ShapeNode.getGeom()). Using this function to store geometry also has the effect that the geometry is automatically added to the Viewports the CircleFromCenterRadiusShape is specified to be visible in, and will be removed and updated according to changes in the ShapeNode.

Notice that the CircleFromCenterRadiusShape shape is the important object here. The polygonal geometry that it generates is nothing but a byproduct of its validation function. The user does not care about the polygonal geometry. They cannot select it, they can not delete it, and they directly change it. It is simply the CircleFromCenterRadiusShape representation in a geometrical world. When the user clicks on the polygonal geometry in the viewport, it is actually the CircleFromCenterRadiusShape that is selected. A simpler organization would have CircleFromCenterRadiusShape actually extending the geometry. Years of experience in software design have led us to believe that this really is not the right way to do it. Such an organization would establish a one-to-one correspondence between the geometry and the object in the user input data model. It would be impossible for the user selectable shape generate more than one geometry. It also means the CircleFromCenterRadiusShape would not be able to extend the other data types oriented towards storing user input, like our ModelNode.

Often a ModelNode needs to accept a variable set of ModelNodes as input. Very often the user needs to say a certain node does its job based on the values of this node and that node. In fact this is the main feature that allows application interoperability through the database. A node that requires input from other nodes, the identity of which is to be specified by the user, holds these nodes in NodeSetField objects. The NodeSetField class is a ModelNode extension specialized to hold a list of other nodes. It can hold the list of other nodes as formal children, such that it their exclusive parent. It can also hold the list of nodes as a list that is independent of the child array, so that the list the nodes can be left in their original parents. This is how the database implements cross linking.

An example of a ModelNode that uses the NodeSetField to hold an arbitrary list of user selected nodes is the GeneralSectionShape defined in GridZilla. This operator node treats any input shape as a geometrical sectioning device into a grid of volumetric data. One of its applications is can map electrostatic potential data onto a molecular surface. It requires both a reference to a ModelNode that holds volumetric data, and a reference to a ShapeNode that has geometry to use for the section. In its constructor function it calls:

	addField("grid", grid = new NodeSetField());
	addField("section", section = new NodeSetField());
	addField("colorMap", colorMap = new NodeSetField());
In its validate() function, and whose purpose is to compute a geometry based on input data, it uses:
	Grid grid = (Grid)this.grid.getSingleSelection();
	ShapeNode[] sections = this.section.getShapeSelection();
	ColorMap colorMap = (ColorMap)this.colorMap.getSingleSelction();
to extract the user input from the database. Notice that it is keeping convenient references to the NodeSetFields as JAVA primitive fields.

class ModelNode

Important functions:

addField() and variations

Add an expected property as a child node.

addNode() and variations

Add a node of variable existance.

addNodeListener()

Add a listener to call about events pertaining to this node.

addToCurrent()

Add this node to current database group node.

at()

Return child at index.

nodeCount()

Return number of child nodes.

deepCopy()

Recusivly copy children

createControlWidget()

Override to create control widget to let user control this node iteractively.

getName()

Return this node's name.

getNodeChildren

Return this node's children.

getNodeParent

Return this node's parent.

getSelect

Return if this node is selected by user.

getTransformable

Return object to apply coord system transformations to.

getValue()

Return value represented by this node as object independant of database.

isField()

Return whether this node is a field.

purgeFinal()

Override to purge memory when node is removed for ever.

set()

Assign this node to given node.

validate()

Compute derived data.

class ShapeNode

The ShapeNode class is an extension of ModelNode intended to be extended by applications that subclass nodes having the purpose of generating geometry. The previous examples have shown the utility of this class already. One open question in the API is whether not it should be necessary to extend ShapeNode to be able to produce a derived data geometrical nature. Of course is not really necessary, but many piece of code in various application modules are expecting that if a node has geometry to offer it will be of the type ShapeNode.

The ShapeNode class adds in its constructor function four field nodes. All ShapeNodes have:

It seems that for any shape, these are useful quantities for the user to be able to change, so they come with the ShapeNode of a free of charge.

The two most important functions in the ShapeNode are the functions for storing computed geometries and retrieving computed geometries. A shape node may add any number of geometries. The functions are:

addGeometry()

This function of wraps the given geometry in PaintStruct, assigns PaintStruct.coordSystem to the coordinate system of this ShapeNode, and gives the PaintStruct default rendering attributes.

addPainter()

This is the lower level version of the addGeometry() function. It allows the application developer to instantiate his own PaintStruct, which could be a subclass of PaintStruct, and allows him to assign his own rendering attributes and local transformation matrix.

getGeom()

This function and its variants allow code that is using a ShapeNode as input to retrieve the geometries and make computations based on them. The function takes an argument that describes the types of geometry to retrieve. The types are declared in ShapeNode case static-final fields:

Validation

Validation is the process that makes the whole database work. It is the power behind the input/output machine. Validation is what we call the process of computing derived data from user input data. Validation is the process of matching the external state to the internal state. An object that doesn't appear as it currently specified is invalid. Validation re-computes its appearance to match its specification. A data node may have some derived data that is computed from user input data. The derived data would fall under the category of the external state data. ("External" state refers to the external appearance of derived information that is geometrical.) Often the internal of information should be allowed change many times without re- computing of the external information. This happens all the time in response to actions taken by the user. For example the user moves the mouse around a view-port which results in the changing of the database value every time the mouse motion event is handled. Suppose the external state of this node is geometry that appears in the view-port and it is not computed every time the mouse is moved. When the user finally stops moving the mouse, the node is asked to validate, which in this case computes geometry.

In other words, the validation system is asynchronous. When a data value changes, a change notification is propagated to all data nodes directly or indirectly care about this data. These change notification can result in a data node being notified of changes more than once in response to a single action by the user. If we're not careful, is easily possible to re-compute the same derived data hundreds of times between redraws. The Modelzilla database handles the problem with an asynchronous invalidation/validation scheme. During change notifications, nodes that need to be recomputed are placed in an invalid node queue. A node is free to record precisely what part of its derived data as become invalid. Then, just before a redraw, the validation scheduler asks each invalid node to validate. This is implemented in the ValidationManager class. ValidationManager has a validation scheduling system that first validates a node that are input to other invalid nodes, in a recursive fashion. When the validation is complete, the resultant geometrical models should be ready to view the view-port.

Asynchronous updating works like this:
Many invalidations....
invalidate()
invalidate()
invalidate()
invalidate()
invalidate()
invalidate()
invalidate()
invalidate()
One validate....
validate()

To invalidate a node, when a piece of information in data node depends on is changed, call ModelNode.invalidate() or ValidationManager.addInvalid(DataNode node). If the piece of information is registered as input to the first node, then these functions will be called automatically. Otherwise, if some piece of internal information that an application keeps separately from the database is changed, such as a primitive field member of the model node subclass, or some central piece of information that many elements of your application are looking during the course of their computations, you will need to manually invalidate nodes that needs to be recomputed.

To validate a node, override ModelNode.validate() in your application defined ModelNode subclass. (Or implement DataNode.validate().). An application defined data node can keep track of exactly what part of the ModelNode has become invalid and only re-compute this part. This would be an incremental validation system. Our VincentVanMol application uses this technique a lot.


File Saving

Sub trees of the Modelzilla database can be stored and retrieved from an XML file. These are the files with the MVG extension. These files store the current state of the program. Our applications properly store all user input in the database. Because of this, users of our applications can hit the "Save Button" to produce and MVG file (Modelzilla Virtual Graphics), come back a week and open the file and resume exactly where they left off. This ability is crucial for a complex model that took days of refinement to get right.

How does this work? When the user opens an MVG file, this file is parsed and the database is rebuilt. All other state information not stored within the database, if its state should return what was when the user saved the file, should be computed from information in the database. Because after all, the entire state of the program should be due to user input. The database causes the data nodes to compute the derived data that the user cares about.

All ModelNode subclasses must offer no argument constructor functions. This enables them to be instantiated while loading a database file. (An MVG file.) It is not possible to completely build a model node's user specification its no-arg constructor function. The node is first built, and then field values are filled in both interactively and when opening files.

An application module is free to save and open its own specialized files. Modelzilla does not try to do this for the application developer. The saving of an MVG file can be made to trigger the saving of other more specialized files. The opening of an MVG file can trigger the opening of other more specialized files. Or the application can have its own independent functions to make this happen. The MVG files are supposed to save the overlying situation. Our VincentVanMol application for example does not save the molecular information to an MVG file. It saves the molecular state information to a standard molecular file format like PDB. However VincentVanMol's job is to make pictures of proteins, and use input regarding the graphical renditions it allows to be stored to the MVG files. When the users opens an MVG file that VincentVanMol took part in building, the validation of the top molecule node in the database causes VincentVanMol to open a PDB file and fill in the molecular information it self.


Initializing an application module

class Module

This is the base class an application extends to do initialization and cleanup. (It is in the package harmonic.mz.function). The only two functions you really need to override are:

installInFramework()

This is where an application should build its user interface.

removeGUI()

Override this to remove the application's interface when the user wants to unload the application.

Causing an application (a Module class extension) to load programmatically Use MZ.modules().loadModule("full.qualified.class.name"). This can be used when an application module has a dependency on another application module. The standard controls application's class name is "harmonic.mz_controls.MZ_Controls". An application that wanted only these controls plus its own could have a main function like this:

	public void main(String[] args){
		new MZ();
		MZ.modules().loadModule("harmonic.mz_controls.MZ_Controls");
		MZ.modules().loadModule("my module name");
	}

Letting the user load your application

There is a directory called mod_stubs under the Modelzilla installation. If you create a file similar to the ones that are already there, and supply information about your applications name and the name of its Module class, the user will see your application in the applications list and can load it when they need it.

The -m app_class_name command line option will startup an application module strait away


Building an application's user interface

This is largely out of the domain of the Modezilla API. The application that uses Modelzilla can define its own user interface that is totally separate from the Modelzilla API. One of the guiding principals in Modelzilla's development was that it wouldn't try to make the user interface for you. An application's user interface can be put in one or more floating frames, or it can be added it to a special area Modelzilla has for application user interfaces. The class GUIManager, the single instance of which is accessible from MZ.gui() defines a function addModulePanel() for adding an application's user interface to the special area. But we don't care if you don't use the standard area.

A user interface is responsible for controlling the ModelNodes an application defines and uses. Generally it takes input from the JAVA components it builds itself.

Modelzilla does come with some widgets you can use for controlling the values of ModelNodes. You can insert these within your GUI if you wish. The NodeValuesDialog is the most useful.


Notes regarding possible changes


Changes to data model

Modelzilla has no support for a node to generate other nodes as its output.  This ability might 
create a whole new level
of flexibility by letting the user select "output nodes".  Things like geometry and molecular 
information could be wrapped
in a general output node.  

Some interface allowing any object to be selectable and imputable into database.  Reduces 
difficulty of making existing applications "friends with Modelzilla".


Making it smaller and simpler, to enable regular app-mods run on web

We have a problem in that Modelzilla applications can run on the web because the framework 
is too large and has dependencies a large amount of 3rd party code.  But application code really 
uses directly only a small amount of the framework anyway.  So splitting the use input layer of 
the framework into the core classes and a separate jar file for the periphery, so that the core has 
no dependency on the periphery, might solve this inconvenience.  It also really simplifies the 
"core API".  

A simplified Modelzilla would have only a means allowing applications to flow data from one to 
another, and a way for the user
to control this.

Keeping
ModelNode, ShapeNode, everything in mz.data.
nodes in mz.data.fields
mz.undo package
mz.manager package

Only the ability to keep data in the database, the validation system, the selection system.  Can't 
get rid of mz.data.fields because these
are so useful for storing data.  Can't get rid of mz.undo because it is used within application 
code, and it's small anyway.  Managers are too important too.  

Can get rid of file saving/opening code.  Entries ? not so sure.  Needed heavly by CAD-Zilla 
and St Module. 

Removing 
	mz.file
	mz.docnode
	mz.collaboration
	mz.entries
	mz.visuals
	mz.imageencoder
	maybe some of mz.gui
	things in mz.util pertaining to file saving
	any jython dependancy

Removing from external libs
Removing file stuff enables many things to go away from external libs.
JAI stuff goes ? that's jai_core, jai_codec, activation .jar mail, mailapi, pop3, smtp
XML stuff goes ? xerces
regex is now part of Java, so gnu-regexp goes
Nothing left!  Reduced from 2.5 MB to 0 MB.

MZ then costs about 300 KB
ViewerCore about 130 KB
Geometry about 160KB

Removed classes are kept in a jar called mz_full.jar.  This is all code that a regular application 
should not care about, but
provides file saving abilities and other tricks that very useful.  

A simplified ?basic controls? module would be used that would have no dependency on 
periphery ? maybe just one panel of buttons for things like rotations and undo.

Enables regular application modules to run on the web!