The configuration has these read/write fields: Number Field name Field type Field value 1 Atom to create Atom DesignAtom//C Give the number of a field to edit, or press 0 to exit: 1 This slot implements the protocol Atom. The currently chosen factory is DesignAtom. Do you want to change the factory and reset to the new factory's default configuration? n Editing the configuration... The configuration has these read/write fields: Number Field name Field type Field value 1 Atomic Number Positive Integer 6 2 Material ColorSlotValue (0.588235, 0.588235, 0.588235, 0.750000) 3 Radius Positive Float 1.548000 Give the number of a field to edit, or press 0 to exit: 1 The old value of the slot is 6. What do you want the new value to be? 1 The configuration has these read/write fields: Number Field name Field type Field value 1 Atomic Number Positive Integer 1 2 Material ColorSlotValue (0.588235, 0.588235, 0.588235, 0.750000) 3 Radius Positive Float 1.548000 Give the number of a field to edit, or press 0 to exit: 0 The configuration has these read/write fields: Number Field name Field type Field value 1 Atom to create Atom DesignAtom//H Give the number of a field to edit, or press 0 to exit: 0
Similarly, there are several different kinds of names in the plugin architecture. We haven't yet described what sorts of things have these names, but it's important to say what the namespaces are at first and to make it clear that they are entirely different from each other. The namespaces in the list have absolutely nothing to do with each other.
All of the namespaces listed below happen at run time. Thus they are definitely unconnected (except by convention) with names that the C++ compiler uses directly: class names, variable names, label names, file names, etc., since all of those names happen at compile time and generally are not available at run time.
Chemistry/DesignAtom.cpp
that is a Factory that generates DesignAtom's.
Note that this source file does not provide any .h
files
for use by other parts of Fungimol. All use of DesignAtom's is
by invoking the factory and by manipulating the base class BoringAtom.
It's important to be clear on the difference between the Factory and the Configurable it makes. For example, the C++ class DesignAtom is a subclass of Configurable; it has a bunch of methods that deal with the physics of how DesignAtom's should work in the scene. The C++ class DesignAtomFactory is a subclass of Factory; it has methods that describe how to build a DesignAtom.
Unfortunately for the purposes of generating a clear explanation, the C++ class DesignAtomFactory has a getName() method which returns the factory name "DesignAtom". Recall the discussion of namespaces above; the result from the getName() method is a factory name, but the name of the C++ class DesignAtomFactory is a C++ name, so only convention connects the two. All of the occurrences of the word "DesignAtom" in the terminal dialogue below ultimately came from this string, and the terminal dialogue would be less clear if they all said "DesignAtomFactory".
For example, the Configuration that is given to DesignAtomFactory to
make a DesignAtom is an instance of the C++ class
DesignAtomConfiguration, which is (indirectly) a subclass of
Configuration. In the dialogue above, the
bold part of the dialogue is editing a DesignAtomConfiguration.
DesignAtomConfiguration is defined in the source code in
Chemistry/DesignAtomConfiguration.h
and
Chemistry/DesignAtomConfiguration.cpp
.
An ActionConfiguration differs from other Configurations only in that
it has a field that the Factory can use to fetch a top-level object
representing the entire system. If ac is an instance of the C++ class
ActionConfiguration, then ac->getTopLevel() is an instance of the
class TopLevel decared in TopLevel/TopLevel.h
. The
Factory can call methods on a TopLevel to do whatever it needs to do
to the system, including getting and then manipulating the SceneGraph
which has all of the objects in the scene.
Actually, "Action" is a subprotocol of two other protocols, "KeyboardMouseAction" and "SpaceOrbAction". "KeyboardMouseAction" is a protcol for Actions that only make sense when invoked in response to an event from the keyboard or the mouse, and "SpaceOrbAction" is a protocol for Actions that only make sense when invoked in response to an event from the Space Orb. Factories in the generic "Action" protcol should not pay attention to any input events, so they can work when invoked because of keyboard events, mouse events, Space Orb events, or events from some other device which does not yet have a device driver plugged into Fungimol.
The sample plugin below defines a new Action.
Plugins/Hi.cpp
,
and we include the entire source file here. You may find this code
easier to read if you first read about the coding conventions used. You may
also want to read comments in the header files that are included.
To compile the sample plugin, give this command:// This file is in the public domain. Tim Freeman 23 Feb 2000. #include "ActionConfiguration.h" #include "TypedFactory.h" #include "Action.h" #include "FactoryTable.h" #include "MemoryUtil.h" #include "String.h" #include "SP.h" #include "Factory.h" #include "Float.h" #include "Dynavec.h" #include "Vec3.h" #include "TopLevel.h" #include "SceneGraph.h" #include "LinkManager.h" #include "BoringAtom.h" namespace { // This sample plugin defines a command that inserts "Hi!" into the scene, // spelling out the letters with the default DesignAtom. // Dot represents one atom in the figure we're going to add. struct Dot { // x and y coordinates of the atom, in an arbitrary coordinate system. int x, y; // The index in dotArray of the atom we want to link this atom to, or // -1 if none. We interpret hiArray from the beginning, so linkTo had // better be less than or equal than the index of this dot. int linkTo; }; // Here's an ASCII drawing of figure we're going to add: // //y=4 8 10 14 18 //y=3 7 9 17 //y=2 2 3 4 13 16 //y=1 1 5 12 //y=0 0 6 11 15 // // x=0 1 2 3 4 5 6 // // And here it is as a sequence of Dot's: const Dot hiArray [] = { {0, 0, -1}, // 0 {0, 1, 0}, // 1 {0, 2, 1}, // 2 {1, 2, 2}, // 3 {2, 2, 3}, // 4 {2, 1, 4}, // 5 {2, 0, 5}, // 6 {0, 3, 2}, // 7 {0, 4, 7}, // 8 {2, 3, 4}, // 9 {2, 4, 9}, // 10 {4, 0, -1}, // 11 {4, 1, 11}, // 12 {4, 2, 12}, // 13 {4, 4, -1}, // 14 {6, 0, -1}, // 15 {6, 2, -1}, // 16 {6, 3, 16}, // 17 {6, 4, 17}};// 18 // Multiply all dimensions by this much. 1.4 Angstroms is a plausible bond // length between two Carbon atoms. const Float bondLength=1.4; class Hi : public TypedFactory <ActionConfiguration, Action> { public: // Give the factory (that is, the command) a name so we can look it up // later. Hi () : TypedFactory <ActionConfiguration, Action> ("Hi") {} // Specify the default configuration for this command. Nothing interesting // here. SP<ActionConfiguration> typedDefaultConfiguration () const { return NEW (ActionConfiguration ()); } SP<Action> makeIt (ActionConfiguration *conf) const { // Get the TopLevel so we can get the SceneGraph from it. SP<TopLevel> const top = conf->getTopLevel (); // Get the SceneGraph so we can add atoms to it. SP<SceneGraph> const sg = top->getSceneGraph (); // Get the LinkManager so we can create links between the atoms. SP<LinkManager> const lm = sg->getLinkManager (); // Get the factory that makes DesignAtom's. SP<Factory> const factory = FactoryTable::load ("Atom", "DesignAtom"); // Make one. We only need one DesignAtom because the position is stored // outside of the DesignAtom itself. DesignAtom has no header file and // it is a subclass of BoringAtom which does have a header file, so da is // a BoringAtom. SP<BoringAtom> const da = dynamic_cast <BoringAtom *> (&*factory->makeIt (factory->defaultConfiguration ())); assert (da); // We'll hold the state in the plausibleState vector. Dynavec <Float> plausibleState; // Put a plausible state into the plausibleState vector. For // BoringAtom's, this state has zero position and zero velocity. da->plausibleState (plausibleState); // A list of the indices of the atoms we've added, so we can link them // together properly. Dynavec <int> atomIndices; // Vectors for the x and y coordinates const Vec3 dx = Vec3 (bondLength, 0, 0); const Vec3 dy = Vec3 (0, bondLength, 0); for (int i = 0; i < sizeof (hiArray) / sizeof (Dot); i++) { const Dot &d = hiArray [i]; BoringAtom::setCenter (&*plausibleState, d.x*dx+d.y*dy); // Add the new atom, remembering its index. const int index = sg->addObject (da, plausibleState); // Save the index so we can make links later. atomIndices.push (index); if (-1 != d.linkTo) { // Add the link. An individual link is a directed edge, but bonds // between atoms are undirected, so we have to do it both ways. lm->addNewLink (index, atomIndices [d.linkTo]); lm->addNewLink (atomIndices [d.linkTo], index); } } // Generate the return value. SP<Action> result = NEW (Action ()); // Give a success message. result->setMessage ("Hi!"); return result; } }; // Register this factory as an Action. bool useless = (FactoryTable::store ("Action", NEW (Hi ())), true); }
Theg++ Hi.cpp -I/usr/include/fungimol -fpic -shared -o Hi.so
-fpic -shared
is required to create a loadable object
file, and the -I/usr/include/fungimol
is required for the
#include
's at the beginning of the plugin.
Before running the new Action you must first load the plugin.
The
command for loading plugins is not bound to any key, so you must run
it with the command for executing any command, which is bound to
meta-semicolon. If Hi.so
is in Fungimol's current
directory, the dialogue in the text window is something like the
following, except the number "21" may be different depending on your
version of Fungimol:
Note that the path name had to be specified asThe configuration has these read/write fields: Number Field name Field type Field value 1 Command to execute Action DoNothing Give the number of a field to edit, or press 0 to exit: 1 This slot implements the protocol Action. The currently chosen factory is DoNothing. Do you want to change the factory and reset to the new factory's default configuration? y The available factories are: Number Name ... 21 LoadPlugin ... What is the number of the factory you want? 21 Editing the configuration... The configuration has these read/write fields: Number Field name Field type Field value 1 Shared object file name String Unspecified.so Give the number of a field to edit, or press 0 to exit: 1 The old value of the slot is Unspecified.so. What do you want the new value to be? ./Hi.so The configuration has these read/write fields: Number Field name Field type Field value 1 Shared object file name String ./Hi.so Give the number of a field to edit, or press 0 to exit: 0 The configuration has these read/write fields: Number Field name Field type Field value 1 Command to execute Action LoadPlugin Give the number of a field to edit, or press 0 to exit: 0
./Hi.so
.
The dynamic linker did not accept a simple Hi.so
,
probably because I did not have .
on my
LD_LIBRARY_PATH
.
Next, we will run the plugin. First, delete the default elements in the scene graph, as described in the basic editing section of the tutorial. Then, use meta-semicolon to invoke the new plugin:
Note that the success messageThe configuration has these read/write fields: Number Field name Field type Field value 1 Command to execute Action LoadPlugin Give the number of a field to edit, or press 0 to exit: 1 This slot implements the protocol Action. The currently chosen factory is LoadPlugin. Do you want to change the factory and reset to the new factory's default configuration? y The available factories are: Number Name ... 19 Hi ... What is the number of the factory you want? 19 Editing the configuration... The configuration has no fields that can be changed. Nothing to edit, so we're done. The configuration has these read/write fields: Number Field name Field type Field value 1 Command to execute Action Hi Give the number of a field to edit, or press 0 to exit: 0 Hi!
Hi
was printed immedately
after the command was run. If time is stopped, the result looks like this:
If time was started, the figure will immediately rearrange itself to look like this:![]()
To bind the new command to a key, we need to edit the configuration of the keyboard dispatcher. The keyboard command for editing the top-level configuration is "c", and then the following grievously long terminal dialogue can happen to bind the "Hi!" Action to the keyboard key "h":![]()
After going through all this, you can delete the original "Hi!" from the scene, and then press "h" to get a new one. It ought to be possible to write a plugin that binds keys and can significantly shorten this dialogue. I have not done this since I have been editing my default keybindings directly into the device driver plugin atThe configuration has these read/write fields: Number Field name Field type Field value 1 Scene Generator SceneLoader VectorSceneLoader 2 Scene Graph SceneGraph SubsetStorageScene 3 Timesteps Per Frame Positive Integer 5 4 Top Level TopLevel XTopLevel Give the number of a field to edit, or press 0 to exit: 4 This slot implements the protocol TopLevel. The currently chosen factory is XTopLevel. Do you want to change the factory and reset to the new factory's default configuration? n Editing the configuration... The configuration has these read/write fields: Number Field name Field type Field value 1 Busy device bug threshhold Positive Integer 100 2 Input Devices Vector of InputDevice Vector of length 2 3 Object Drawer ObjectDrawer XObjectDrawer 4 Sleep between polls (microseconds) Positive Integer 50000 5 Use MIT Shared Memory Extension Boolean true Give the number of a field to edit, or press 0 to exit: 2 The vector currently has these contents: Position Value 1 Keyboard and Mouse 2 Space Orb Configuration These options are available: f,0 Finish editing this vector e Edit an element of the vector <slot number> Edit the given slot of the vector p Print out the vector again Which of these do you want to do? 1 This slot implements the protocol InputDevice. The currently chosen factory is Useless (this string should not be seen). Editing the configuration... The configuration has these read/write fields: Number Field name Field type Field value 1 Key and Button Dispatch Table KeyboardMouseAction KeyboardMouseDispatcher Give the number of a field to edit, or press 0 to exit: 1 This slot implements the protocol KeyboardMouseAction. The currently chosen factory is KeyboardMouseDispatcher. Do you want to change the factory and reset to the new factory's default configuration? n Editing the configuration... The configuration has these read/write fields: Number Field name Field type Field value 1 Bindings when Control is down Vector of KeyBinding Vector of length 9 2 Bindings when Meta or Alt is down Vector of KeyBinding Vector of length 1 3 Bindings when Shift is down Vector of KeyBinding Vector of length 7 4 Bindings with no shift keys Vector of KeyBinding Vector of length 16 Give the number of a field to edit, or press 0 to exit: 4 The default value for new elements has the type KeyBinding and the value Mouse motion is bound to DoNothing. The vector currently has these contents: Position Value 1 b is bound to InferBonds ... 16 i is bound to SelectionInvisible These options are available: f,0 Finish editing this vector d edit the Default value i Insert a copy of the default value into the vector l deLete an element from the vector e Edit an element of the vector <slot number> Edit the given slot of the vector p Print out the vector again Which of these do you want to do? i Note that the vector starts with element 1. Enter 0 if you have changed your mind and do not want to insert anything. Where you want the new default element to appear? 17 The default value for new elements has the type KeyBinding and the value Mouse motion is bound to DoNothing. The vector currently has these contents: Position Value 1 b is bound to InferBonds ... 16 i is bound to SelectionInvisible 17 Mouse motion is bound to DoNothing These options are available: f,0 Finish editing this vector d edit the Default value i Insert a copy of the default value into the vector l deLete an element from the vector e Edit an element of the vector <slot number> Edit the given slot of the vector p Print out the vector again Which of these do you want to do? 17 This slot implements the protocol KeyBinding. The currently chosen factory is KeyBinding. Editing the configuration... The configuration has these read/write fields: Number Field name Field type Field value 1 Key Button Mouse motion 2 What it should do KeyboardMouseAction DoNothing Give the number of a field to edit, or press 0 to exit: 1 The old value of the button is Mouse motion. What do you want the new value to be? h The configuration has these read/write fields: Number Field name Field type Field value 1 Key Button h 2 What it should do KeyboardMouseAction DoNothing Give the number of a field to edit, or press 0 to exit: 2 This slot implements the protocol KeyboardMouseAction. The currently chosen factory is DoNothing. Do you want to change the factory and reset to the new factory's default configuration? y The available factories are: Number Name ... 24 Hi ... What is the number of the factory you want? 24 Editing the configuration... The configuration has no fields that can be changed. Nothing to edit, so we're done. The configuration has these read/write fields: Number Field name Field type Field value 1 Key Button h 2 What it should do KeyboardMouseAction Hi Give the number of a field to edit, or press 0 to exit: 0 The default value for new elements has the type KeyBinding and the value Mouse motion is bound to DoNothing. The vector currently has these contents: Position Value ... 17 h is bound to Hi These options are available: f,0 Finish editing this vector d edit the Default value i Insert a copy of the default value into the vector l deLete an element from the vector e Edit an element of the vector <slot number> Edit the given slot of the vector p Print out the vector again Which of these do you want to do? 0 The configuration has these read/write fields: Number Field name Field type Field value 1 Bindings when Control is down Vector of KeyBinding Vector of length 9 2 Bindings when Meta or Alt is down Vector of KeyBinding Vector of length 1 3 Bindings when Shift is down Vector of KeyBinding Vector of length 7 4 Bindings with no shift keys Vector of KeyBinding Vector of length 17 Give the number of a field to edit, or press 0 to exit: 0 The configuration has these read/write fields: Number Field name Field type Field value 1 Key and Button Dispatch Table KeyboardMouseAction KeyboardMouseDispatcher Give the number of a field to edit, or press 0 to exit: 0 The vector currently has these contents: Position Value 1 Keyboard and Mouse 2 Space Orb Configuration These options are available: f,0 Finish editing this vector e Edit an element of the vector <slot number> Edit the given slot of the vector p Print out the vector again Which of these do you want to do? 0 The configuration has these read/write fields: Number Field name Field type Field value 1 Busy device bug threshhold Positive Integer 100 2 Input Devices Vector of InputDevice Vector of length 2 3 Object Drawer ObjectDrawer XObjectDrawer 4 Sleep between polls (microseconds) Positive Integer 50000 5 Use MIT Shared Memory Extension Boolean true Give the number of a field to edit, or press 0 to exit: 0 The configuration has these read/write fields: Number Field name Field type Field value 1 Scene Generator SceneLoader VectorSceneLoader 2 Scene Graph SceneGraph SubsetStorageScene 3 Timesteps Per Frame Positive Integer 5 4 Top Level TopLevel XTopLevel Give the number of a field to edit, or press 0 to exit: 0
Input/KeyboardMouse.cpp
.