Space and time 1
Clock and persistence
Last updated
Clock and persistence
Last updated
This is the first part of the “simulated design” series which focuses on contemporary modeling approaches beyond the generative and parametric paradigms.
We will review concepts of modeling with entities that break out of the constructive geometry box ie. working exclusively with metric units of space and direct linkages between information. We will develop a physics simulation engine involving beyond positions, masses, velocities and forces updating overtime.
Visual programming is not conducive for defining our own semantic entities beyond geometries such as points, curves and surfaces, packaged in containers such as lists and tables; we lack high-level tools capable of supporting the description of custom objects. Nevertheless, it is important to appreciate that concepts often transcend the medium and its limitations.
The objective of this session is to introduce the idea of integrating the aspect of time in geometric constructions. This is a prerequisite for being able to achieve any form of simulation. In this session we will break out of the directed acyclic graph logic.
The graphs we create are automatically kept up-to-date. This forbids creating cyclic dependencies among nodes. As a result we cannot increment a value over time because the new value depends on a previous, thus forming a closed loop. But we need such a capability in order to express the notion of time passing.
The approach we take is somewhat unorthodox but makes sense. We will store data outside the graph using one custom component and then read them back using another. We thus create an invisible wire through computer memory that allows us to break the cyclic barrier.
The approach we will take is general and aims to convey some broader ideas of data persistence and serialization. We will save and load information from a storage which persists after our graph is no longer running. In addition, we will convert or pack and unpack aka serialize data between text and numerical forms.
We could have used the file save and load components but we wish to run animation sequences rather rapidly, so going through the disk is too slow. We need to store the data in memory. A convenient way to achieve this while being able to visually inspect memory content is by using the CAD application’s “notes” panel, where we can freely store text descriptions about the model.
These two lines of python import the CAD library and request our supplied text data value stored in the CAD documents notes. Using the “notes” command we can show the relevant panel and visualize the data transmitted from the visual programming environment. The “text” node is used for making sure the data stored is as text.
The opposite of the above component aims to load the data from the CAD document notes into the visual programming environment. The code is pretty much the exact symmetric of the one seen earlier. We need to use the “number” component here to convert the text data back to numeric values.
We observe that while the storage component writes data immediately to the notes’ memory location, the retrieval component does not update automatically. It needs to be triggered using here a “push button” component; the push button does nothing more than forcing the component to re-evaluate.
This disconnect makes sense because there is no wire connecting the storage and retrieval components. How would one notify another? Nevertheless, this is exactly what we wanted to achieve: transmit data values without wired connections!
The final piece of the puzzle is the “timer” component (“timer” has been replaced by “trigger” in Rhino 7). Instead of manually triggering the retrieval component, we can let the “timer/trigger” periodically force re-evaluation of the component such that we keep data in sync. The interval between the clock’s ticks controls the speed of update or lag between values.
We can now put together a proof of concept using the components developed: the most basic incrementing integer clock. As you can see we have achieved the goal of having values update over time without cyclic wired relations between components.
Before we develop something more complex we first improve the clock’s behaviour by introducing two features: (a) resetting the clock values back to zero and (b) allowing for manual steps forward using a push button. The first one solves the problem of manually setting the notes contents back to zero, while the second feature will help us incrementally test and debug our graphs.
To achieve resetting we deploy the conditional selection logic, using the “gate filter” component, presented in the control flow session. If the reset push button is pressed, the gate selects the initial value “-1”, which increments to “0” and gets stored. While the button is not held down, the gate uses the value loaded from memory.
The manual update logic is based also on a simple boolean logic idea. We use an OR gate to trigger updates either when the reset is pressed or the single step forward button is pressed. The result is used as trigger to force the retrieval component to load the last stored value. (Alternatively, if using triggers in Rhino 7, we can just switch the trigger to ‘Manual mode’ and step forward by pressing the play button).
Finally, we can package the above logic into a custom cluster component so we wont have to be exposed to the internals. Packaging logic into “clusters” ie. user-defined components is pretty simple. We just group the nodes we wish to encapsulate and select the cluster option in the context menu.
A “cluster” represents a functional block we can always open, update and close, using the appropriate context menu. You will notice that there are a few special “input” and “output” components automatically generated inside the cluster definition. We may always add or remove additional inputs and outputs. In addition, the vertical location of the input and output parameters defines their order as they appear in the cluster wire connector lists.
Our clock component accepts a pair of boolean values for resetting and manual updates and outputs an integer representing a clock tick. We can build more complex time-aware logic around this base functionality.