Coder Social home page Coder Social logo

nodeeditorwinforms's Introduction

GitHub Nuget

Node Editor Winforms

Node Editor Winforms is a Windows Forms class library project, that provides nodal editor control for general usage - e.g. for sound processing applications, graphics editors, logic or control flow editors and many other. It contains Windows Forms user control that after referencing it to your project could be added via UI designer in Visual Studio.

Example of Node Editor usage in 3D application

Changelog

  • 2021.02.06 - Added ability to use string type parameters in node inputs and outputs directly, without need to create wrapper class
  • 2021.01.24 - Added ability to access nodes custom editors via CustomEditor property of NodeVisual
  • 2021.01.24 - Fixed method of resolving assemblies (which fixed for example usage of generic types in sockets, like List)
  • 2021.01.24 - Added support for customizing node width and height - via Node attribute parameters
  • 2016.08.03 - Added sample project (Math nodes sample) / Project refactoring
  • 2016.07.28 - Updated INodesContext implementation sample (missing event implementation)
  • 2016.06.22 - Changed various fragments of code to improve performance a little
  • 2016.03.27 - Fixed crash when the node method parameters was reordered. Added feature of sending feedback to the node graph from context.
  • 2016.03.21 - Added support for multiple execution path outputs per node, that implies ability to create more complex control flow nodes like loops, conditional nodes etc.

Features

  • Automatic context menu creation based on methods with Node attribute detected in INodeContext class instance
  • Nodes context menu could be hierarchical by providing additional menu parameter in Node attribute e.g. menu:"Filters/LFO"
  • Node graph could be executed or resolved automatically by calling Execute or Resolve method in NodesControl instance
  • Custom editors (other Winforms controls) inside nodes by specifying customEditor Node attribute parameter e.g. customEditor:typeof(MyUserControl)
  • Nodes could be selected, multi-selected by shift click or by dragging selection area around
  • Flexible connections attractive visually
  • Easy integration with property grid
  • Easy serialization / deserialization of entire node graph

Usage

Step 1 - Building

To use Node Editor first obtain source code from here, then open it in Visual Studio 2015 and finally build. The Release configuration may be considered when using in production - it really has much better rendering speed. Also good information here is that the Nodes Editor is written on top only .NET Framework 4.0 - no other third party libraries are involved. However you could use Node Editor with other third party libraries as well - it accepts types from other assemblies too ad node inputs/outputs.

Step 2 - Referencing to your project

Within the Winforms designer right click on the Toolbox and select Choose Items ... in context menu. In the Choose Toolbox Items dialog window choose .NET Framework Components tab page, then browse to the built library DLL. After accepting there should be User Control named NodesControl somewhere in the toolbox. Now you can easily drag-drop the control into your UI.

Step 3 - Adding & extending Node Editor context type

When you have NodesControl in your form or user control you should do first some minimal preparation. This step involves creating special context class that should implement INodesContext interface. In this context class you can put methods that will be exposed automatically as nodes by adding Node attribute to each method. Look at the example:

    // Our context class that implements INodeContext interface
    public class FContext : INodesContext
    {
        // This is implementation of INodesContext.
        // CurrentProcessingNode is the node that is being actually executed.
        public NodeVisual CurrentProcessingNode { get; set; }
        
        // Implementation of interface member: event FeedbackInfo
        public event Action<string, NodeVisual, FeedbackType, object, bool> FeedbackInfo;
        
        // Some your project specific methods, events, properties and so on
        public event Action<Model3D, Matrix> Placement = delegate { };
        public event Action<Bounding> ShowBounding = delegate { }; 
        public event Action Clear = delegate { };

        // Starter node - it has isExecutionInitiator as true, so it will have one execution path output
        // name is the node name displayed in node caption
        // menu is the path splitted by '/' character if you want to build hierarchical menu
        // description is a node description that will be sent in special event at some situations
        [Node(name:"Starter", menu:"General", isExecutionInitiator:true, description:"Node from which processing begins.")]
        public void Starter()
        {
            Clear();
        }

        // Node with one input (object obj)
        [Node(name:"Display Object", menu:"Debug", description:"Allows to show any output in popup message box.")]
        public void ShowMessage(object obj)
        {
            MessageBox.Show(obj.ToString(), "Nodes Debug: " + obj.GetType().Name, MessageBoxButtons.OK,
                MessageBoxIcon.Information);
        }

        // Node with custom editor that has two inputs (string path and bool useCage) and one output socket (out Model3D model)
        [Node(name: "Load Model", menu:"General", customEditor:typeof(NELoadModel3D), description:"Node that loads 3D model from disk and also textures together.")]
        public void LoadModel(string path, bool useCage, out Model3D model)
        {
            model = new Model3D();
            var fileNames = path.Split(';');
            InitAsset(fileNames, model.Asset);
            model.Asset.MakeObject(useCage);
        }
      }

Next thing is to create our context object and put it in our NodesControl:

  var context = new FContext();
  nodesControl1.Context = context;

Now you have right setup and during application runtime, there context menu will appear after right clicking on NodesControl.

Serialization

NodesControl has byte[] Serialize() and Deserialize(byte[] data) methods that allow you to save and load node graph state. However you should design your classes that you use as inputs/outputs of nodes with some additional code due to proper serialization and deserialization.

  • The class for node input/output should have [Serializable] attribute
  • It is good to have also [TypeConverter(typeof(ExpandableObjectConverter))] attribute in order to work properly with property grids
  • The class should implement ISerializable interface
  • The class should has public constructor and private one, that has parameters: (SerializationInfo info, StreamingContext ctx)
  • The class should implement GetObjectData method of ISerializable

This is example of such a class:

    [Serializable]
    [TypeConverter(typeof(ExpandableObjectConverter))]
    public class Vector3W : ISerializable
    {
        public Vector3 Value;

        public float X { get { return Value.X; } set { Value.X = value; } }
        public float Y { get { return Value.Y; } set { Value.Y = value; } }
        public float Z { get { return Value.Z; } set { Value.Z = value; } }

        public override string ToString()
        {
            return Value.ToString();
        }

        public Vector3W()
        {
            
        }

        private Vector3W(SerializationInfo info, StreamingContext ctx)
        {
            X = info.GetSingle("X");
            Y = info.GetSingle("Y");
            Z = info.GetSingle("Z");
        }

        [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.AddValue("X", X);
            info.AddValue("Y", Y);
            info.AddValue("Z", Z);
        }
    }

If you are unable to follow this rules (e.g. you would to use third party library types) you should write wrapper for that classes, that meets the above conditions.

Custom Node Editors

Custom node editor can be any object that is subclass of System.Windows.Forms.Control class. It is maintained automatically (created and handled) by NodesControl. You can specify custom editor for each node by giving its type as customEditor parameter of Node attribute. Keep in mind that custom editor will get NodeVisual object on its Tag property, so you can easily interact with node state.

NodeVisual object has a method named GetNodeContext() by which you can obtain current node state - which is dynamic object, so you can just type member name and if it is not present, the member will be created (property). Node state is a set of properties related to its inputs and outputs.

Here is an example, inside your UserControl class:

    var node = (Tag as NodeVisual);
    dynamic context = node.GetNodeContext();
    var myVariable = context.model;
    var myVariable2 = context.transform;

Execution / Resolving

To start execution just simply call nodesControl1.Execute() (without parameters), which will call the starter node (the node you had marked as isExecutionInitiator:true). After that, the graph will execute through execution path (yellow connections). While the execution process is running, each node that is being actually executed will be put into the CurrentProcessingNode property of your context.

To resolve any node just call nodesControl1.Resolve(yourNodeHere) where yourNodeHere is NodeVisual class object (it must be node that comes from graph).

Resolving differs from execution, that it not need execution path to be provided. It just resolve a node with all its dependant nodes.

User Interaction

  • Right click on NodesControl to bring context menu with nodes available to put
  • Click on node to select it, shift-click to multi select
  • Ctrl+LMB on connection socket to unpin it and drag elsewhere

Credits & Contact

Programming: Mariusz Komorowski - @komorra86

License

This piece of software is licensed under MIT License

nodeeditorwinforms's People

Contributors

komorra avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

nodeeditorwinforms's Issues

AssemblyResolver can't find the Assembly [NodesControl.cs]

I found an issue in the AssemblyResolver here:
https://github.com/komorra/NodeEditorWinforms/blob/master/NodeEditor/NodesControl.cs#L368

x => x.GetName() == assemblyName this compares two different physical identities of assemblies, which returns 'null' as a result.

To resolve this issue you just need to compare their Assembly.FullNames like this:

x => x.GetName().ToString() == assemblyName.FullName;


I was trying to connect two identical List sockets, but the connection failed. After applying the above mentioned fix, it is possible to work with generic Lists in the NodeGraph, as the AssemblyResolver will find the correct connected Assembly.

Are you accepting pull request? Then I could do it for you if wished.

BTW: NodeEditorWinforms is very nice. You did a great job. Thank you!

Eto.Forms variant?

This is fantastic. I wondered whether you'd considered a version based on something like Eto.Forms, where it might be possible to use this on multiple platforms.

Wpf Version ?

Great NodeEditor. Thanks a lot.

Do you have any plans to port this to WPF ?

Adding combo box to a standary node

Hi there,
thanks for sharing this great project. I am using it like you said as a signal routing patcher for an osc/midi/dmx system for our band. I have sucsessfully added and expanded to it. However I have a couple of issues and I need help. To make things easier I have taken your original code and made a very minor change but I still have the same issue. I made a minor change to the Serialise function to enable me to save .nds with filename, its now a void function and return commented out. If I add a modified module with a combo box save and reload the nds file the app dies in .also how to I access combo box ie read or write entry.

    public void Deserialize(byte[] data)
    {
        using (var br = new BinaryReader(new MemoryStream(data)))
        {
            var ident = br.ReadString();
        //unable to read past end of file


       //modified node added combo box
   [Node("Value", "Input", "Basic", "Allows to output a simple value.",false,false, typeof(ComboBox))]
    public void InputValue(float inValue, out float outValue)
    {
        outValue = inValue;
    }


    public void Serialize(string fn)
    {

        using (BinaryWriter bw = new BinaryWriter(File.Open(fn, FileMode.Create)))

        {
            bw.Write("NodeSystemP"); //recognization string
            bw.Write(1000); //version
            bw.Write(graph.Nodes.Count);
            foreach (var node in graph.Nodes)
            {
                SerializeNode(bw, node);
            }
            bw.Write(graph.Connections.Count);
            foreach (var connection in graph.Connections)
            {
                bw.Write(connection.OutputNode.GUID);
                bw.Write(connection.OutputSocketName);

                bw.Write(connection.InputNode.GUID);
                bw.Write(connection.InputSocketName);
                bw.Write(0); //additional data size per connection
            }
            bw.Write(0); //additional data size per graph
            // return (bw.BaseStream as MemoryStream).ToArray();
        }
    }

Error when "out string" is used

Hello,

If we use string parameter as output we get error on control.
pasted image at 2016_12_28 09_50 am

What can we do about it?

Code Example:
[Node("XML Data", "Input", "Basic", "Allows to output a XML value.",false)] public void InputXml(string inValue, out string outValue) { outValue = inValue; string strOut = ""; //MessageBox("strOut"); }

Loop with conditions exemple ?

Hello, my project is to make a packet editor my game.

I need to make something like :
1 - Walk to a Point.
2 - Attack Monster
3 - ( Condition : If Life < 50% )
3.1 - Walk to a Point
3.2 - Wait x second
4 -LOOP BACK to 1-

Your project look very great and simple and i should be able to adapt it to my situation. Unfortunately there is no "complex" example of how to use it and I'm struggling by understand it by myself.

I read that i should use "ExecutionPath" from another issue (#15 (comment)), but the example isn't reproducible.

Any help would be appreciate !
Thank you.

Direct value in the node?

Thank you for this
Is there an example how to add an input slider or number for example and other inputs like text, file path ...etc directly from the node?

output string parameter

Hello, thanks for the work this is great !

I encountered an issue when trying to create a simple "String Value" node, based on the MathSample :

[Node("String Value", "Input", "Basic", "Allows to output a simple string value.", false)]
public void InputStringValue(string inValue, out string outValue)
{
    outValue = inValue;
}

When creating the node, I have this exception :

System.MissingMethodException: 'Constructor on type 'System.String' not found.'

This breaks into NodeVisual.cs line 193, because the tested out type is not 'String' but 'String&'.

I worked around it by replacing the test this way :

var p = output.ParameterType.Name.TrimEnd('&').ToLower() == "string"

I thought this would require your attention for a proper fix :)

Autoscroll Doesn't Seem to work

Great library. I found it very quick and generally easy to use.
One thing that doesn't seem to work, though is the AutoScroll property of the control. If you make the control anchored inside a form, and then you change the size of the form, its easy to lose your nodes by shrinking the control. It seems like there's no good way to navigate the control "canvas". Normally, this would be handled by the autoscroll property. Unless, I'm maybe using it wrong.

Difference between yellow and black connections

Hi,
If I understand correctly the yellow connections between nodes are the order of execution, since they go from Exit of one node to Enter of the next node.
However what are the black connections and how are thy executed, I see that the nodes with black connections don't have exit and enter?

Great job....but how to....

Hi, really love what you did...but I need some guidance on how to develop nodes with complex logic.
For instance I would like to make a node that takes an INT as input and it output TRUE/FALSE is the value is above a set number. The number must be entered in a text box on the node. How do I add the text box or other components to a node?

Thx!!!

Zooming and mouse control enhancements

First of all, great work on this! I've been willing to include this into one of my projects, and I am thinking to contribute a little bit with the following:

  • Zooming: Allow the consumer to set the zooming level (zoom on and out). Setting default to 1f for 100% zooming level. I'm thinking it would be good if the control does it automatically for you when you use the wheel but also allow you invalidate it and set it manually throw a ZoomLevel property.

  • Move through the control using mouse drag: When you press Alt + Click, you enter now into Drag mode, where the cursor is changed to a grabbing hand and allow you to move thorugh the graph without scrolling. Stop pressing Alt and you will stop this behaviour.

Let me know if the project is open to contributions and how.

Thank you.

Serialize User Control ?

Hi there, this is awesome!
I need a little guidance, if I have a node which shows an User control with a textbox, it is possible to serialize and deserialize that textbox value?
Thanks!
Rodo

Example of creating a node through code

I'd like to setup the control so that it starts with a 'Starter' node by default every time the control is loaded. I assume I need to set nodesControl1.Context.CurrentProcessingNode to a new Node but it isn't clear through the NodeVisual class how this is done.

Straightforward example to add customized nodes?

Hi, Thanks for making this project public, I did try the example and it is perfect for generic usage.

Although I am having hard time to figure out exactly how do you create new nodes. Ideally I would like to have some guidance example, that show how did you make the nodes for the various operations; and what is the minimum viable code to actually get just the "work area" where to add nodes; and how to make nodes Input and Output.

The example that you made is really helpful but it is taking me some time to get to the basics; I know that you may not have time, but something step by step, that show starting from the inclusion of the control on the form, creation of a couple of different node types and how they interact with each other; would really be the fastest way to dive into your project.

Thanks for your efforts!

Test example?

Any chance of a runnable test example? As it stands there's bits missing from the example usage code to get it to do anything:
Error CS0535 'FContext' does not implement interface member 'INodesContext.FeedbackInfo' etc.

Proposal and Question

Hi, first of all I want to thank you for this great library, I am using it in my project and have expanded it to use Direct2D for graphics rendering (among other things). I have only one question:
How can I make or create a node that has multiple outputs?
For example, fork a Boolean value.
I can not find ways to do it at the moment, any help will be greatly appreciated, it is more if you want it, I can give you my expanded code.

Custom editor help

Fantastic project thankyou for sharing your code)
please help me .

PVE4 FE NODE EDIOR (screen shot)
Example parameter module shows
a slider and a combo box how can this be acheived
or otjer multiple components
is it possible for an example
how to change code below ?

[Node("IsGreater",customEditor:typeof(NumericUpDown))]
public void IsGreater(int input, out bool output)
{
var userThreshold = (int)(CurrentProcessingNode.CustomEditor as NumericUpDown).Value;
output = input > userThreshold;
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.