In this tutorial, we will discuss the various pin types, or in other words, the data type the modules are able to receive and output. Also, we’ll discuss the editor interfaces for these data types.
The first data type we were deal with is the Scalar. It means a simple numeric value. It’s the simplest data type but lots of basic functions can be demonstrated with it, so we’ll start here.
First I want to demonstrate a helper function that will be used extensively during the tutorial. It’s the so-called peeker. It’s a little window where we can watch the current value of the data coming out from a pin or traveling through a wire. It can be displayed by holding down the Control key and moving the mouse over an output pin of a module, a wire, or an input pin of a module. In each case, we can watch the data currently go in, out, or through. In the case of the Scalar type, it means displaying a simple number and also a bar that can be useful in the frequent cases when we watch a value fall in the 0 to 1 interval: levels, opacities, etc.
Sometimes it’s needed to watch a certain value continuously. For these cases, we can use the Peeker modules. Each data type peeker has a corresponding module in the system. Let’s search for the Scalar Peeker and add it to the compound. If we wire the desired scalar value into this module, it will output the peeker image on its Out pin. We can wire it to an output: now we can see it running on Output #1 continuously. But, of course, we usually don’t want to allocate a main output for this purpose, therefore we should send it to an internal channel that hasn't been assigned to any output. Let’s add a Channel Out module, and wire the peeker into it. Make the channel number for e.g. 35. Now if we select channel 35 on any Preview panel we can watch the peeker picture on it.
An input pin works this way: if a wire is connected to it, the data coming through that wire will apply. Here the Copy Scalar module receives data coming from the LFO module. If nothing is connected to a pin, a constant value applies. This constant value can be specified in the Pin Values panel. If we select a module, then in the Pin Values panel we can see the data editor fields for all the input pins of the module. Note that these constant values of the pins are often referred as “properties” in our tutorials. Also, the Pin Values panel sometimes is referred to as “Property Editor”. Let’s specify a value of 56.3 for the In pin. We can see that this 56.3 value applies on the input pin, and, since this is a Copy module, the same value appears on its output as well.
If a wire is connected to the pin, its corresponding editor field is grayed out and is disabled in the Pin Values editor. We still can see the constant value we specified before, but it cannot be edited and also doesn't apply to the pin. Instead, the value coming from the LFO applies.
Let’s examine the editor field itself. There are a few ways to edit the numeric value. One of them is obviously typing in the number. But we can conveniently increase or decrease the value by clicking on the field, holding the mouse button down, and moving the mouse up or down. By default, this method changes the value by 1/100ths. If we also hold down the Shift key, the step is 1/10ths, while holding down the Control key means whole number steps. Holding down shift and control together provides a 1/1000th step, this is the smallest step.
We can also change the value using the up and down arrows on the keyboard. The modifiers are the same. The default step is 1/100th, Shift means 1/10th, Control means 1, and Shift+Control means 1/1000h. But there is a special mode in this case. If we move the caret to a specific digit then using Alt with the arrows changes the value of that specific decimal place up or down. This way we can achieve steps of for e.g. 1/10000th or 100.
If the focus is not on the edit field itself but here on the name of the property, we can use left and right arrow keys and the Control, Shift combinations to achieve the same value changes as before. The benefit of this method is that we can easily adjust multiple properties without touching the mouse. We can use up and down arrow keys to move between the properties and the left and right arrow keys to adjust their values.
By right-clicking on the property name we can access a number of further operations. Reset gives the property its default value. Revert gives the value the property had at loading the compound. To demonstrate this I save the compound then close and reopen it. So now this is the load time value of the property. Let’s modify the value. Now by applying Revert we get the load time value of 400 back.
Property values can be copied. We can either use the Copy and Paste menu items, or we can use the usual Ctrl+C, Ctrl+V combinations, but in the latter case the focus must be on the property name. Let’s Copy the In value and Paste it into the Pulse Width property of the LFO module. These operations can be applied at the pin itself of on the module. By right-clicking the pin we get similar menu items.
As we’ve said before leading a wire into a pin overrides the constant value specified for the pin. But the current incoming value also can be “baked” into the constant value in order to keep it even after disconnecting the cable. This operation can be found in the right-click menu of the property, it’s named Capture Input. Let’s click it. We can see that the current incoming data is stored in the property as a constant value. If we remove the cable the lastly captured data remains the value of the pin.
Let’s talk about the Copy modules. For each data type, there’s a corresponding Copy module type in the system. It simply passes through the incoming data of the given type unmodified. This can be meaningful in several cases. For e.g. we expose an input pin, but we want to pass the data coming from the outside to multiple modules. This dashed line of the exposion, despite it looks like a cable, is not an actual data cable, just the visual designation of the exposion itself. So it can’t be split to be lead to another module. Let’s add another LFO module. All we can do is add a Copy Scalar module and insert it into the dashed line. What’s really happening here is that from now on the In pin of the Copy module is the one that is exposed, and the data is passed through the Copy module and reaches LFO via a data cable. And from now, of course, we can lead the signal from the Copy module to any number of modules.
Another application of a Copy module is when it is used for a data type conversion. Typical use case when we have a math module, for e.g. Multiply. These math modules usually have vector inputs and vector outputs so that they can be used universally, not only on scalars but on arbitrary-sized vectors as well. Suppose we want to expose one of the arguments of the multiplication in a way that it appears as a scalar from outside of the compound or on the Dashboard. For this, we can use the Copy Scalar module again. The scalar and vector data types are convertible into each other, so we can simply connect the Copy module to the argument pin of Multiply. Then we expose the input of the Copy module. This way the argument appears as a scalar value on the Dashboard.
Another conversion example is when we want to change a value between the 0 and 1 values, but we want it to appear as a switch instead of a numeric editor field. We can achieve this by using the Logical data type which will be discussed later. Let’s add a Copy Logical module, wire it into Multiply, and expose its input. Now we have an on/off switch on the Dashboard, and depending on its state a 0 or a 1 value arrives at the input of Multiply.
A scalar data is a 32-bit floating-point numeric value. This precision is not enough in certain situations. Therefore the system provides another scalar type: the Double Precision Scalar or Double for short which is a 64-bit floating-point value. Typically we find it as Position or Duration type output pins, for e.g. on the Video Player or in the Sequencer modules. This kind of data needs to work with multiple-hour intervals while maintaining the precision of little fractions of a second. This needs 64-bit precision.
Here is a Video Player loaded with a video file. Its Position output is a Double value which gives the current playing position. Suppose we want to apply some math operations on this value, e.g. we want to multiply it. If we use the previously demonstrated Multiply module then we have a problem. Multiply works with vector values, and the elements of a vector value are simple precision 32-bit scalars. Thus we lose the precision of the original Position value. To avoid this problem each math module has its double-precision counterpart. In this case, it's the Multiply Double module. By wiring the Position into it and performing the multiplication the original precision is kept.
In the system, each data type has its distinct color which is manifested both in the pin colors and the cable colors. The Double values are olive green while the simple Scalar values are black, this is how we can differentiate between them.
Our next data type is the Vector. It’s designated by the green color. Vector is essentially an arbitrary length sequence of numbers, including the zero-length sequence, so a vector can be empty. Vector is very often used throughout the system for various purposes. For e.g. a position of a point in the 3D space is described by a 3-element vector. A point in the 2D plane is represented by a 2-element vector etc. We can also use it to block forward an arbitrary amount of data in the form of a long sequence of numbers.
Let’s see how we can edit a vector value in the Pin Values editor. It can be different with different types of modules. Certain module types expect fixed-length vectors as input, and accordingly, the editor field has a fixed format as well. For e.g. this Translation module produces a translation transformation and expects a 3D offset value. So the corresponding editor field is a fixed 3-element one. The individual elements can be edited exactly the same way as in the Scalar editor.
Other modules can be fed with arbitrary length vectors. Obviously, the Copy module is one of them. In these cases, the property editor field has a little arrow button. By clicking it we can choose between several editing modes. The default editing mode is the Free Vector. It allows enumerating an arbitrary number of values separated by commas. In this case, we’ve created a 5-element vector. But if we exactly for sure that we’ll use this value as a 3D vector, we can switch to the 3D Vector editing mode, and we get an identical editor we’ve seen at the Translation module. We can also switch to 2D Vector mode etc.
It’s important to note that changing the editing mode also means the trimming of the vector at the end if it contains more elements than the number of editor fields. So if I now set the editor back to Free Vector mode we can see that only two elements remained. There’s a Scalar mode that is for editing single-element vectors that can be used as scalars in practice.
The value editors of the arithmetical modules like Multiply are in Scalar mode by default. The reason for this is that in practice we usually carry out operations between scalar values, or apply a scalar to a vector value coming from a cable.
Let’s examine the so-called binary operator modules that receive two arguments and output one result. These are the Add, Subtract, Multiply, Divide, etc. These modules carry out the operations element by element. For e.g. the Multiply module multiplies the first element of A with the first element of B, the second element with the second element, and so on. If A and B have different number of elements, the shorter one will be taken as a basis, the result will have that number of elements, and the remaining elements of the longer input will be lost.
But there’s an exception: if one of the arguments is a single-element one that is practically a scalar, that scalar is applied to all the elements of the other argument. This way we can, for e.g., multiply a 3D vector with a scalar value thus changing its length.
I would show some examples. But first I want to show a useful trick. Suppose we want to extract this vector value from here for further usage. By grabbing it at the property name we can drag’n’drop it into the Flow Editor. Upon this, a Copy module is created that contains the dragged value as a constant. Let’s wire it into Multiply, and multiply it by 2. We can see the result: it contains the doubled values element by element. But if we multiply by a 2D vector, let’s make it 2 and 3, the result will be 2-element, multiplied with 2 and 3 consecutively.
If an argument is empty, in the case of math modules the system considers it as zero. Let’s empty the B argument. To do that we have to switch to the Free Vector mode and delete all the numeric values. We can see that the result is three zeros since all the elements of A have been multiplied by zero.
Let’s see the so-called unary operator modules which receive a single argument only. In this case, the operation is simply carried out element by element. For e.g. this Cosine module currently takes a 3-element vector as input, thus its output is the vector of three cosine values.
Random module is an example of a module that’s capable of generating an arbitrary length vector. Make the Count 85 meaning that we’ll generate 85 random numbers. Specify a value range for the random generator, make it 2 to 10. Upon clicking Generate the output vector is filled with 85 random numbers. We can also see that the Peeker of the vector values is only capable of displaying the first 10 values and the number of the remaining elements.
The next data type is the Integer or the whole number in other words. It’s designated by the light gray color. We usually encounter it as selector-type pins. Here is a Switch Video module. It has an integer input pin specifying which video input is passed through and also has an integer output pin that simply reflects the current selection.
The editor field of the integer data type is very similar to the scalar editor. The main difference is that the base step is 1 when we increase or decrease the value with the mouse or the arrow keys. With holding down Shift the step is 10, and with holding down Control the step is 100. So we have a multiplier of 100 compared to the scalar editor.
An integer property can appear as a special data type called enumeration. For e.g. let’s add an LFO and examine its Waveform pin. In the Pin Values editor, it appears as a list of named values. Apart from that, it is a regular integer property. For e.g. if we select the Sawtooth item, the property takes on the value 3. We can see it in the peeker. Of course, we can also connect a cable to it thus controlling the Waveform of the LFO with another module.
For applying math operations on integers there are no separate module types, but we can use either the vector-based or the Double-based math modules, since these four data types: vector, scalar, double, and integer are mutually convertible into each other. The only thing we should note is that the Integer data type is a 32-bit integer. So if we wire it into a vector or a scalar input pin, a precision loss can occur, but only in the case of very large integer values. In these cases, we can trustfully use the Double math modules instead.
Let’s talk about the conversion between the four numeric data types precisely. If we connect a scalar value into a vector pin we’ll get a single-element vector. In the opposite direction - in this case, a 4-element vector - the first element of the vector is kept as a scalar value, the remaining elements are lost. If the vector is empty it will be considered as a zero value.
The integer and double types behave similarly when they are converted from or into a vector. But we should be also careful regarding the various types of precision loss. For e.g. if I wire this scalar - which has a fractional part - into an integer, only its whole part is kept. If we wire a double into a scalar a 64 bit to a 32-bit precision loss occurs. Let’s specify a value with lots of decimals. The double can store a lot of them but when it arrives to the scalar pin it loses a number of decimals. We also can lose some digits when converting a very large integer into a scalar. The value range of integers are from about -2 billion to 2 billion. Let’s specify a 10 digit number that is still in the allowed range. If we wire it into a scalar then firstly we can see that it is displayed as an exponential format because it’s a large number, secondly, we can clearly see that we lost two-digit precision at the end. While if we connect it into a double pin, the precision is kept, this is the reason that it is worth using the double math modules in the case of very large integers.