In order to create a dynamic c# plugin doubleclick in a patch to open the nodebrowser. Type "Template" to get a list of templates which you can choose to start from. Choose one of the templates prefixed with d and press CTRL+Enter or CTRL+Click to clone the template.
In the dialog that now shows up choose a unique Name and Category for your node. Specifying a Version is optional, see NodeAndPinNaming. If the patch you're cloning in is not yet saved you'll also have to specify a path where to clone to. It is therefore always recommended to save the patch first because then the clone conveniently goes to a subdirectory of the patch. Press Clone to see your ready plugin appear in the patch. Rightclick the node to open the code-editor. Use the Project Explorer to add/remove documents or references to a plugin. |
[PluginInfo(Name = "Template", Category = "Value", Help = "A Template", Tags = "simple, basic", Author = "vvvv group", AutoEvaluate = false)] |
The PluginInfo attribute written above your plugin-class is used to describe a vvvv plugin.
Inside the () brackets in the attribute statement there is a comma separated list of Name=Value pairs. Only the Name and Category ones are mandatory, all others are optional. Here is a listing of all available PluginInfos. Regarding the AutoEvaluate setting see the Evaluate() section below. |
[Config("My Config Value")] public ISpread<double> FConfig; [Input("My Input String")] public ISpread<string> FInput; [Output("My Output Color")] public ISpread<RGBAColor> FOutput; Attribute examples//specify a default value for a pin [Input("Input", DefaultValue = 255.0)] //specify a not spreadable pin. //The pin has only one slice //and the user can't change that. [.., IsSingle = true)] //specify the pins visibilty. [.., Visibility = PinVisibility.Hidden)] //specify a pin to be of type filename //to show a file-dialog on rightclick. [.., StringType = StringType.Filename)] IDiffSpread example[Input("Write", IsBang=true)] IDiffSpread<bool> FDoWrite; ... if (FDoWrite.IsChanged) ... Spread of Spread example//create a two-dimensional spread //exposed in vvvv with a data and //a bin-size pin. [..] ISpread<ISpread<double>> FInput; PinGroup example//create a dynamic pingroup, where //the user can specify the pin count //via the Inspektor. [.., IsPinGroup = true)] ISpread<ISpread<double>> FInput; |
A Pin is being defined by 2 lines of code:
PinInfo attributeThe PinInfo attribute is used to describe a pin. The attribute immediately precedes the declaration of an input or output variable. Inside the () brackets in the attribute statement the first string argument is mandatory and specifies the pin's name. A further comma separated list of Name=Value pairs (that is optional) allows you to describe a subtype of the pins value by setting a Default-, Min-, MaxValue and more. Here is a listing of all available PinInfos. Variable declarationsIn most scenarios you'll want to declare your in- and outputs of the generic type ISpread<T> where for T you can specify any type like:
If you want to ask from an input if it has changed then simply use IDiffSpread<T> instead of ISpread<T> in the declaration and call .IsChanged whenever you need to know. Spread of SpreadIf you declare a variable as ISpread<ISpread<T>> then next to the actual pin (that holds the data) you get a free BinSize pin that allows you to specify how that data is structured into bins. If you still want to know if the input was changed, only the outer spread should be declared as IDiffSpread<T>. Alternatively you can also make this pin a PinGroup where instead of the BinSize pin you get a free PinCount config pin, see example to the left. Add/Remove pinsThere are 2 ways to do this at runtime:
Connection callbacksIn the unlikely case that you need to react to pin connect/disconnect events use Pin<T> instead of ISpread<T>, see Pin(T) Events. |
public void Evaluate(int SpreadMax) { FOutput.SliceCount = SpreadMax; for (int i = 0; i < SpreadMax; i++) FOutput[i] = FInput[i] * 2; } |
This is the heart of your plugin. The Evaluate() function is run once every frame if:
Here you do the main calculation of your plugin. If you're looking for a section to initialize some parts of your code (ie. run them only once), see the next section (Constructor). As an input parameter the function hands you the SpreadMax which is the maximum slicecount of all connected spreads on all inputs of the plugin. You will typically use this to set the slicecount of your output pins as shown in the code to the left. Please note: if ANY of your input pins is an ISpread<ISpread<...>>, you should evaluate SpreadMax yourself as you can see below. Like this a 2d-spread will only contribute to the spreadmax with its "bin count" which is what you typically want. |
SpreadMax = SpreadUtils.SpreadMax(FInput1, FInput2 /* state ALL your inputs here*/);
public MyNodeClassname() { //initialize stuff } public class MyNodeClassname: IPluginEvaluate, IPartImportsSatisfiedNotification { ... public void OnImportsSatisfied() { //access your in-/outputs here } |
You'll use a constructor if you have parts of your code that only need to run once. Note that a constructor is optional and therefore most of the templates come without one.
If you need to access any of the variables you defined as in- or outputs you'll have to take extra care and implement IPartImportsSatisfiedNotification as shown in the example to the left. For an actual usecase see the code of Template (Raw). |
public class MyNodeClassname: IPluginEvaluate, IDisposable { ... //called when the plugin gets //deleted public void Dispose() { //unsubscribe from events //and call dispose on resources //your plugin has created. } |
In case your plugin makes use of resources which need to be released when the plugin gets deleted you need to implement the IDisposable interface. In its Dispose method you can do all the necessary clean-up tasks. |
[Import()] public IPluginHost2 FPluginHost; [Import()] public IHDEHost FHDEHost; |
There are two entry points if you want to access some of the internals of vvvv:
|
using System; .. using VVVV.PluginInterfaces.V2; |
All entities in .NET can be reached via a global path, which is a combination of the namespace in which the entity is defined and the name of the entity. So e.g. VVVV.PluginInterfaces.V2.ISpread<bool> is the full path to the type ISpread<bool> which is defined in the namespace VVVV.PluginInterfaces.V2.
The using statements are there to be able to shorten your code by only saying ISpread<bool>. For that to work it is necessary to use the namespace (VVVV.PluginInterfaces.V2) once at the beginning of the code. |
[Import()] public ILogger Flogger; ... Flogger.Log(LogType.Debug, "foo"); |
The most basic way of debugging parts of your code is writing out log-messages that you can view in Renderer (TTY). You can do so by importing the ILogger and calling .Log() on it as shown in the example to the left.
BreakpointsIf you want to set breakpoints and step through your code line by line you'll need an IDE like SharpDevelop or VisualStudio and do the following:
Now when a breakpoint is hit the IDE stops vvvv and you can step through your lines of code. |
|
anonymous user login
~3d ago
~9d ago
~9d ago
~10d ago
~23d ago
~1mth ago
~1mth ago
~1mth ago
~1mth ago
~2mth ago