» 50: Custom Datatypes
This site relies heavily on Javascript. You should enable it if you want the full experience. Learn more.

50: Custom Datatypes

helohelohelo,

here is the fifth in a series of blogposts about our forthcoming nextbigthing that we still call vvvv50 (50). If you haven't already done so please first read the previous issues with the titles:

  1. 50: That Next Big Thing. An Overview.
  2. 50: Colors
  3. 50: Properties
  4. 50: Generics
All information given here is still preliminary and subject to change.

Now please fasten your seat belt because this is a big one. Probably the single least asked for feature that could still become 50s unique selling point: Custom Datatypes.

There is a chance that for some of you it will now be enough to explain this in one sentence: In 50 a patch defines a datatype, horray! If that makes sense to you already, then cheers. There isn't much more to learn in this article.

Glad you're still reading. So you belong to the other group of people who are probably afraid that this may be the moment you're loosing it. It being your understanding of how it will be like to patch in the future? Fear not! "Custom Datatypes" only sounds abstract but is actually a very basic and intuitive concept. You'll understand and be happy to have them around soon, read on.

But before demonstrating how custom datatypes make our patchings easier with 50, lets first establish a problem in 45 to pave the road for the hype:

The demo scenario:

  • when the left mousebutton is pressed, particles (with random velocities) are added
  • when a particle reaches y < -1 it is being removed

In good old modular fashion here is how you could approach this:

A patch in 45 is merely a visual clue for the user

Step 1) A single particle

  • Create a patch called Particle that has a "Start Position" (2d), a "Velocity" (2d) and an "Initialize" input
  • Create a node of this Particle-patch, attach it to Mouse and Renderer and voila: A single particle moves as you click in the renderer
Left: the Particle patch that holds and manipulates the particles data in a framedelay loop. Right: a patch utilizing the Particle patch.
Click to enlarge.

Step 2) Multiple particles

  • Want multiple particles? Just spread them, right?. But how? If we'd just put a static randomspread to the Particles velocity input we'd get multiple moving particles, yes, but we'd not be able to remove individual ones once their y < -1
  • Intuitively we'd want to somehow create particles and put them into a spread as such. Only we don't really get hold of the particle itself. What we can get access to is its position and velocity properties. So lets put those in a spread
  • In order to be able to manage (ie. add/remove) individual particles we have to move the framedelay from inside the Particle patch (where we'd actually want it) to its parent patch. Now the Particle patch is degraded to a hull with the name "Particle" but it no longer holds the actual particla-data. That is bad.
Left: the Particle no longer holds the data. Right: the parent patch manages the particles data via InsertSlice and Select within a framedelay loop.
Click to enlarge.

The framedelay together with an InsertSlice and a Select allow us to easily (well,..) add and remove slices dynamically.

So far so 45. See the fact that we needed 2 framedelays here? One for Position and one for Velocity? Oh you can zip those two parameters into one spread, of course. But next you blink the boss demands the particle to have a Color and a Name and you already need 3 framedelays... See where this is going?

The general problem here is that, as mentioned in 50: That Next Big Thing. An Overview., 45 does not really understand subpatches. For 45 everything is just one big patch and when you carefully modularize the Particle patch, 45 does not get any of this. So Position, Velocity and any other parameters are just laying there but 45 does not understand that they all describe properties of a particle and actually belong together.

This is where in 45 we'd advise you to create dynamic plugins to join/split your custom datatype. Thats fine for a start, only it requires you to code. Also there are three contributions that I won't go into detail here but are worth mentioning in that respect: Message by velcrome, VObject by microdee and VVVV.Struct by woei & tonfilm.

So while you see there are ways in 45 to tackle such (still rather simplified) situations, they are all workarounds to that aforementioned inherent problem of 45 not knowing about subpatches. And we need you to be really frustrated about that, not about a missing grid or close-button in the UI. This is where we need you to chant in unison: "Go release the cat, we can't work like that".

Well, here we go:

A patch in 50 defines a datatype

What you really want to express when putting multiple properties in one patch is to wrap up all those properties into one entity that you can operate with. Which is exactly what happens in 50.

So just to make sure we got our terms straight, a quick recap:
Value, String, Color, Transform,... are all primitive datatypes we're used to.
Now when we create our own datatype we can think of it as a compound of multiple primitive ones. So for example our Particle could be a compound of:

  • Position (of type Vector2d)
  • Velocity (of type Vector2d)
  • Color (of type Color)
  • Name (of type String)

In other words: Those 4 properties (each of which has their own primitive datatype) together in a patch make our custom datatype called Particle. And of course this can be taken further. Think of a custom datatype called "Firework" that has a Spread<Particle> (meaning a spread of particles) and so on. So yes, datatypes can be compound and cascaded in any way. Apart from properties, datatypes also usually have one or more operations. Therefore the terms "patch" and "datatype" can be used synonymously.

To the point. We could of course exactly replicate Step 1) from above in 50 but we'll leave that up to your imagination and skip it here (as it would look exactly the same minus the framedelay). Instead we jump right into the definition of our custom datatype, which in its most simple form could look like this in 50:

Reading the patch:
In the (still rather rudimentary) listing on the left side you see that this patch has two properties called "Position" and "Velocity" and it has two operations called "create" and "update".

We've mentioned operations before, they are sections in a patch that can be independently executed. In 45 every patch has only one operation, therefore we never gave it a name, but think of it like "update" or "evaluate", ie. the operation that is called once every frame. In 50 a patch can have any amount of operations that can be executed any amount of times per frame (just dropping that here...more on it in a later posting).

So here the "create" operation is used to set initial values for a particles Position and Velocity properties. In further executions of "update" the velocity is always added to the position and the result is again stored in the "Position" property for the computation of the next frame. Also the result is being returned to the caller of the "update" operation as you can see via the little quad in the bar labeled "update".

Next: How do we create an instance?

Instantiating a custom datatype

ad step 1)
Lets again start with placing just a single particle in the patch:

By simply creating a "Particle" node in a patch, what actually happens in 50 is that we create a static instance of the type "Particle". Static meaning that it is instantiated when the program starts. The particle executes its "create" operation once and then repeats executing the "update" operation every frame until we delete the node or stop the program. We can of course create multiple Particle nodes next to each other and have multiple static instances.

Give it a velocity and it moves..done.

Creating dynamic instances

ad step 2)
The challenge now is to create multiple instances of Particles dynamically. But don't think 45 style SetPatch (VVVV), where you'd tell vvvv to actually create a Particle node and place it in a patch... no. This is where the 50 magic chimes in:

Instead of placing the "Particle" node as such in a patch we can also just call individual of its operations. Booom. In the patch below notice the "* Particle" node. This one is short for Particle.Create(), ie. calling the particles "create" operation to create and return a single instance. And then spot the double-decker ".update Particle" which is (not so short) for Particle.Update() that is called in the "For Each" region, ie. it is called once for each particle in the list.

Reading the patch:
When the left mouse button is pressed a new particle is added to the spread with the current mouse position as start position and a random velocity. Remember from the last posting about generics that the ".add Spread" is not specific to the particle! Then for each particle in the spread the update-operation is called so the particles move. Also for each particle a circle is created. The select node only takes particles that are still alive (ie. have an y < 500) and writes them back to the spread of particles. Finally all the circles are drawn in the renderer.

If you now once more compare the 45 and 50 approaches here is what you should note:

  • in 45 the data of the particle is mixed together in a patch with the parts that manage the particlesystem (ie. add/remove particles). That works ok-ish in this simple example but is hell in larger scenarios.
  • in 50 the data and functionality of the particle are conveniently wrapped inside a particle patch and another patch around the particle, which we could call ParticleSystem, simply manages multiple instances of particles. So when changing functionality in the particle nothing has to be changed in the ParticleSystem. This is big!

You still here?
yawnsmiley... let us digest this information for a while. I think it is enough for this time. Still more to come..
goodnite.

Appreciate what you just read? We appreciate a click: /downloads|vvvv?.

joreg, Sunday, Mar 1st 2015 Digg | Tweet | Delicious 10 comments  
fibo 02/03/2015 - 22:36

The Great Spirit Spread.

ggml 04/03/2015 - 09:36

where does the definition reside ?

gregsn 04/03/2015 - 18:03

@ggml: for now the patch itself is the definition.

  • it defines the data type via its used properties.
  • it defines the operations (like create and update).
  • it defines the data types of the operations' inputs and outputs via their usage.

(..)

By that you don't have to switch between different views (e.g. patch view for operation body = data flow and some definition view for data type definition & operation signature).
That said, for more complex types we might add a definition view later on.

Does this answer your question?

velcrome 04/03/2015 - 21:22

i am officially excited. you know, this is like a vision finally becoming elegant: patching oop dataflow visually.

my timing is propably a little early for this, but I have three and a half questions:

  1. what is Circle doing exactly?
  2. this ingenious foreach-block, does it behave lazy, like linq? other way to ask: do you cache the nodes as expression trees?
  3. will it be possible to attach custom attributes to class members (e.g. fields)? this is necessary for serialisation, tests, etc. can this be modified during runtime?
velcrome 04/03/2015 - 21:37

i cannot resist the fourth

4. do you have any plans for patch inheritance and polymorphy?

ggml 04/03/2015 - 21:45

@gregsn: yes, thank you

tonfilm 05/03/2015 - 05:44

@velcrome

1. the circle is an instance of a datatype which implements the interface IRenderable. the renderer expects this interface and calls the render method on it. not final though...

2. the foreach region is virtually the same as a foreach loop in C# no lazy linq magic, strict execution. but it is able to manage a state per iteration (e.g. the circle).

3. user attributes are not yet implemented but we discussed it and it will most likely be realized in a later version.

4. this is a big topic. the facts are:

  • ad hoc polymorphism is in development in the form of type classes
  • parametric polymorphism is supported very well and elegant, see the generics article
  • inclusion polymorphism (or subtyping) in the form of inheritance will not be present in the first version
  • subtyping in the form of interfaces (as mentioned in 1.) is in development, no guarantees until node15

if you look up polymorphism on wikipedia you will find exactly these three types of polymorphism.

subtyping has actually two main use cases in object oriented programming. one is re-using functionality from a supertype and the other is handling a bunch of instances of different types in a common way. the first case is the more important one i think and often one wants even multiple inheritance to get the functionality of different super types. the second case is probably better handled using interfaces.

so what we found is that subtyping in the form of inheritance is handy, but might not even be that important in a visual language since you can instantiate your 'supertype' easily in a patch of your 'subtype' and exposing its operations or properties is a matter of seconds. as opposed to a textual language where writing a method signature is a tedious thing. this also avoids virtual calls and the optimizer of the compiler can do its thing.

interfaces are cool though and we are working on it... :)

for a non-programmer/patcher-only person this might sound a bit abstract, but when saying we are working on it, it actually means that we want to find implementations of these topics which feel natural and appropriate for a visual language. with generics it already works quite well. so in the end you will just patch along without knowing that you do top-of-the-pops computer science stuff.

sebl 05/03/2015 - 17:55
tonfilm said
the circle is an instance of a datatype which implements the interface IRenderable

interesting point. is that node (still) handwritten or already patched? if the latter is the case, how is the interface implemented?

though this is not finished...

vux 05/03/2015 - 22:16

Out of interest,

what about performance?
vvvv45 style patch, simulation + rendering vs vvvv50

numbers about framerate for different particle count would be pretty interesting.

gregsn 06/03/2015 - 15:19

@sebl: though not finished and still under discussion - yes, it is patched and yes it is just a patch that reports to implement the interface "IRenderable". It does so by actually defining a "render" operation (in addition to the create and update operations you are used to). Anyway it might not make it in the first release. Let's see.

@vux: we are busy on polishing details that hardly can be changed once it is out (especially semantic details of the features we want to commit to and the selection of those features...). So yeah. We have no numbers for you yet. Sorry.

  • 1

anonymous user login

Shoutbox

~2d ago

joreg: @microdee: yep i think the site takes user submissions..

~2d ago

microdee: @joreg: it's missing FlareTic... ;)

~2d ago

joreg: @motzi yeah, welcome to my life..

~2d ago

motzi: @joreg: i'm tempted to look into PraxisLIVE :)

~2d ago

joreg: @u7: no worries, we'll still be here when you everyone comes back...

~2d ago

u7angel: @joreg, are u trying to get rid of the community ? :)

~2d ago

joreg: bored of #vvvv? try some alternatives: https://alternativeto.net/software/vvvv/

~4d ago

tonfilm: @beyon you can also use F# together with #vl, see: #fsharp #dotnet #visualprogramming https://github.com/vvvv/VL.DemoLib/tree/master/src/VL.FSharpDemoLib

~4d ago

beyon: tonfilm: Ok,currently using F# though but maybe I should look into sharpdx vs xenko math

~5d ago

tonfilm: @beyon #vl also fills these gaps with huge range of collision detection math from #sharpdx which is basically the same as #xenko's