c sharp test

This commit is contained in:
Javi Agenjo
2019-07-19 19:41:05 +02:00
committed by GitHub
parent 2a18d14e02
commit b42fad51d2
5 changed files with 2256 additions and 0 deletions

604
csharp/LiteGraph.cs Normal file
View File

@@ -0,0 +1,604 @@
using System;
using System.Collections;
using System.Collections.Generic;
//using System.Diagnostics;
using UnityEngine; //for debug messages
using SimpleJSON; //to parse the JSON
namespace LiteGraph
{
public enum DataType { NONE, ENUM, NUMBER, STRING, BOOL, VEC2, VEC3 };
public struct vec2 {
float x;
float y;
};
public struct vec3
{
float x;
float y;
float z;
};
//not used yet...
public class MutableType {
public DataType type;
public bool data_bool;
public float data_number;
public string data_string;
public vec2 data_vec2;
public vec3 data_vec3;
public bool AsBool { get { return data_bool; } }
public int AsEnum { get { return (int)data_number; } }
public float AsFloat { get { return data_number; } }
public string AsString { get { return data_string; } }
public vec2 AsVec2 { get { return data_vec2; } }
public vec3 AsVec3 { get { return data_vec3; } }
public void Set(bool v) { type = DataType.BOOL; data_bool = v; }
public void Set(int v) { type = DataType.ENUM; data_number = v; }
public void Set(float v) { type = DataType.NUMBER; data_number = v; }
public void Set(string v) { type = DataType.STRING; data_string = v; }
public void Set(vec2 v) { type = DataType.VEC2; data_vec2 = v; }
public void Set(vec3 v) { type = DataType.VEC3; data_vec3 = v; }
public override string ToString() {
switch (type)
{
case DataType.NONE: return "";
case DataType.ENUM: return data_number.ToString();
case DataType.BOOL: return data_bool.ToString();
case DataType.NUMBER: return data_number.ToString();
case DataType.STRING: return data_string;
case DataType.VEC2: return data_vec2.ToString();
case DataType.VEC3: return data_vec3.ToString();
}
return "";
}
};
//to store connection info
public class LLink {
public int id;
public int origin_id;
public int origin_slot;
public int target_id;
public int target_slot;
public DataType data_type;
public bool data_bool;
public float data_float;
public string data_string;
public vec2 data_vec2;
public vec3 data_vec3;
public LLink(int id, DataType type, int origin_id, int origin_slot, int target_id, int target_slot)
{
this.id = id;
this.data_type = type;
this.origin_id = origin_id;
this.origin_slot = origin_slot;
this.target_id = target_id;
this.target_slot = target_slot;
}
public void setData(bool data)
{
data_type = DataType.BOOL;
data_bool = data;
}
public void setData(float data)
{
data_type = DataType.NUMBER;
data_float = data;
}
public void setData(string data)
{
data_type = DataType.STRING;
data_string = data;
}
public void setData(vec2 data)
{
data_type = DataType.VEC2;
data_vec2 = data;
}
public void setData(vec3 data)
{
data_type = DataType.VEC3;
data_vec3 = data;
}
}
//to store slot info
public class LSlot
{
public int num;
public string name;
public DataType type;
public LLink link = null; //for input slots
public List<LLink> links = new List<LLink>(); //for output slots
public LSlot(string name, DataType type)
{
this.name = name;
this.type = type;
}
}
//the node base class
public class LGraphNode {
public int id = -1;
public LGraph graph = null;
public int order = -1;
public List<LSlot> inputs = new List<LSlot>();
public List<LSlot> outputs = new List<LSlot>();
public LGraphNode()
{
}
public virtual LSlot addInput(string name, DataType type = DataType.NONE)
{
LSlot slot = new LSlot(name, type);
slot.num = inputs.Count;
inputs.Add(slot);
return slot;
}
public virtual LSlot addOutput(string name, DataType type = DataType.NONE)
{
LSlot slot = new LSlot(name, type);
slot.num = outputs.Count;
outputs.Add(slot);
return slot;
}
public virtual bool getInputData(int slot_num, bool default_value)
{
LLink link = inputs[slot_num].link;
if (link != null)
return link.data_bool;
return default_value;
}
public virtual float getInputData(int slot_num, float default_value )
{
LLink link = inputs[slot_num].link;
if (link != null)
return link.data_float;
return default_value;
}
public virtual string getInputData(int slot_num, string default_value)
{
LLink link = inputs[slot_num].link;
if (link != null)
return link.data_string;
return default_value;
}
public virtual vec2 getInputData(int slot_num, vec2 default_value)
{
LLink link = inputs[slot_num].link;
if (link != null)
return link.data_vec2;
return default_value;
}
public virtual vec3 getInputData(int slot_num, vec3 default_value)
{
LLink link = inputs[slot_num].link;
if (link != null)
return link.data_vec3;
return default_value;
}
public virtual void setOutputData(int slot_num, bool v)
{
if (inputs.Count <= slot_num)
return;
LSlot slot = outputs[slot_num];
if (slot == null)
return;
for (int i = 0; i < slot.links.Count; ++i)
{
LLink link = slot.links[i];
if (link == null)
return;
link.setData(v);
}
}
public virtual void setOutputData(int slot_num, float v)
{
if (outputs.Count <= slot_num)
return;
LSlot slot = outputs[slot_num];
if (slot == null)
return;
for (int i = 0; i < slot.links.Count; ++i)
{
LLink link = slot.links[i];
if (link == null)
return;
link.setData(v);
}
}
public virtual void setOutputData(int slot_num, string v)
{
if (outputs.Count <= slot_num)
return;
LSlot slot = outputs[slot_num];
if (slot == null)
return;
for (int i = 0; i < slot.links.Count; ++i)
{
LLink link = slot.links[i];
if (link == null)
return;
link.setData(v);
}
}
public virtual void setOutputData(int slot_num, vec2 v)
{
if (outputs.Count <= slot_num)
return;
LSlot slot = outputs[slot_num];
if (slot == null)
return;
for (int i = 0; i < slot.links.Count; ++i)
{
LLink link = slot.links[i];
if (link == null)
return;
link.setData(v);
}
}
public virtual void setOutputData(int slot_num, vec3 v)
{
if (outputs.Count <= slot_num)
return;
LSlot slot = outputs[slot_num];
if (slot == null)
return;
for (int i = 0; i < slot.links.Count; ++i)
{
LLink link = slot.links[i];
if (link == null)
return;
link.setData(v);
}
}
public virtual void transferData(int input_slot_num, int output_slot_num)
{
LLink input_link = inputs[input_slot_num].link;
if (input_link == null)
return;
if (outputs.Count <= output_slot_num)
return;
LSlot slot = outputs[output_slot_num];
if (slot == null)
return;
for (int i = 0; i < slot.links.Count; ++i)
{
LLink link = slot.links[i];
if (link == null)
return;
switch(input_link.data_type)
{
case DataType.BOOL: link.setData(input_link.data_bool); break;
case DataType.NUMBER: link.setData(input_link.data_float); break;
case DataType.STRING: link.setData(input_link.data_string); break;
case DataType.VEC2: link.setData(input_link.data_vec2); break;
case DataType.VEC3: link.setData(input_link.data_vec3); break;
}
}
}
public virtual bool connect(int origin_slot, LGraphNode target, int target_slot)
{
if(graph == null)
throw (new Exception("node does not belong to a graph"));
if (graph != target.graph)
throw (new Exception("nodes do not belong to same graph") );
LSlot origin_slot_info = this.outputs[origin_slot];
LSlot target_slot_info = target.inputs[target_slot];
if (origin_slot_info == null || target_slot_info == null)
return false;
if(origin_slot_info.type != target_slot_info.type && (origin_slot_info.type != DataType.NONE && target_slot_info.type != DataType.NONE) )
throw (new Exception("connecting incompatible types"));
int id = graph.last_link_id++;
LLink link = new LLink(id, origin_slot_info.type, this.id, origin_slot, target.id, target_slot);
graph.links.Add(link);
origin_slot_info.links.Add(link);
target_slot_info.link = link;
graph.sortByExecutionOrder();
return true;
}
public virtual void onExecute()
{
}
public virtual void configure(JSONNode json_node)
{
this.id = json_node["id"].AsInt;
this.order = json_node["order"].AsInt;
//inputs
var json_inputs = json_node["inputs"];
if (json_inputs != null)
{
JSONNode.Enumerator it = json_inputs.GetEnumerator();
int i = 0;
while(it.MoveNext())
{
JSONNode json_slot = it.Current;
string str_type = json_slot["type"];
DataType type = DataType.NONE;
if (str_type != null && Globals.stringToDataType.ContainsKey(str_type))
type = Globals.stringToDataType[str_type];
LSlot slot = null;
if (inputs.Count > i)
slot = inputs[i];
if(slot == null)
slot = this.addInput( json_slot["name"], type );
JSONNode json_link = json_slot["link"];
if (json_link != null)
slot.link = graph.links_by_id[json_link.AsInt];
++i;
}
}
//outputs
var json_outputs = json_node["outputs"];
if (json_outputs != null)
{
JSONNode.Enumerator it = json_outputs.GetEnumerator();
int i = 0;
while (it.MoveNext())
{
JSONNode json_slot = it.Current;
string str_type = json_slot["type"];
DataType type = DataType.NONE;
if (str_type != null && Globals.stringToDataType.ContainsKey(str_type))
type = Globals.stringToDataType[str_type];
LSlot slot = null;
if (outputs.Count > i)
slot = outputs[i];
if (slot == null)
slot = this.addOutput(json_slot["name"], type);
JSONNode json_links = json_slot["links"];
if(json_links != null)
{
JSONNode.Enumerator it2 = json_links.GetEnumerator();
while (it2.MoveNext())
{
JSONNode json_link_id = it2.Current;
LLink link = graph.links_by_id[json_link_id.AsInt];
if (link != null)
slot.links.Add(link);
else
Debug.LogError("Link ID not found!: " + json_link_id);
}
}
++i;
}
}
//custom data (properties)
this.onConfigure(json_node);
}
public virtual void onConfigure(JSONNode json_node)
{
//overwrite this one
}
}
//namespace to store global litegraph data
public class Globals
{
static public Dictionary<string, DataType> stringToDataType = new Dictionary<string, DataType> { { "NONE", DataType.NONE }, { "", DataType.NONE }, { "ENUM", DataType.ENUM }, {"NUMBER",DataType.NUMBER }, { "number", DataType.NUMBER }, { "BOOLEAN", DataType.BOOL }, { "boolean", DataType.BOOL }, { "STRING", DataType.STRING }, { "string", DataType.STRING }, { "VEC2", DataType.VEC2 } };
static public Dictionary<string, Func<LGraphNode>> node_types = new Dictionary<string, Func<LGraphNode>>();
static public void registerType(string name, Func<LGraphNode> ctor )
{
node_types.Add(name, ctor);
}
static public LGraphNode createNodeType(string name)
{
if (!node_types.ContainsKey(name))
{
Debug.Log("Node type not found: " + name);
return null;
}
Func<LGraphNode> ctor = node_types[name];
return ctor();
}
};
//one graph
public class LGraph
{
public List<LGraphNode> nodes = new List<LGraphNode>();
public Dictionary<int, LGraphNode> nodes_by_id = new Dictionary<int, LGraphNode>();
public List<LGraphNode> nodes_in_execution_order = new List<LGraphNode>();
public List<LLink> links = new List<LLink>();
public Dictionary<int,LLink> links_by_id = new Dictionary<int, LLink>();
public bool has_errors = false;
public int last_node_id = 0;
public int last_link_id = 0;
public double time = 0; //time in seconds
public Dictionary<string, float> outputs = new Dictionary<string, float>();
// Start is called before the first frame update
public LGraph()
{
}
public void add(LGraphNode node)
{
if (node.graph != null)
throw ( new Exception("already has graph") );
node.graph = this;
node.id = last_node_id++;
node.order = node.id;
nodes.Add(node);
nodes_by_id.Add(node.id, node);
}
public void clear()
{
has_errors = false;
nodes.Clear();
nodes_by_id.Clear();
links.Clear();
links_by_id.Clear();
nodes_in_execution_order.Clear();
last_link_id = 0;
last_node_id = 0;
outputs.Clear();
}
// Update is called once per frame
public void runStep(float dt = 0)
{
for (int i = 0; i < nodes_in_execution_order.Count; ++i)
{
LGraphNode node = nodes_in_execution_order[i];
node.onExecute();
}
time += dt;
}
public void configure(string data)
{
sortByExecutionOrder();
}
public void sortByExecutionOrder()
{
nodes_in_execution_order = nodes.GetRange(0, nodes.Count);
nodes_in_execution_order.Sort(delegate (LGraphNode a, LGraphNode b)
{
return a.order - b.order;
});
}
public void fromJSONText(string text)
{
clear();
var root = JSON.Parse(text);
last_node_id = root["last_node_id"].AsInt;
last_link_id = root["last_link_id"].AsInt;
var json_links = root["links"];
for (int i = 0; i < json_links.Count; ++i)
{
var json_node = json_links[i];
int id = json_node[0].AsInt;
int origin_id = json_node[1].AsInt;
int origin_slot = json_node[2].AsInt;
int target_id = json_node[3].AsInt;
int target_slot = json_node[4].AsInt;
JSONNode json_type = json_node[5];
DataType type = DataType.NONE;
if(json_type != null && json_type.Value != "0" && Globals.stringToDataType.ContainsKey(json_type) )
type = Globals.stringToDataType[ json_type ];
LLink link = new LLink(id, type, origin_id, origin_slot, target_id, target_slot);
links.Add(link);
links_by_id[link.id] = link;
}
var json_nodes = root["nodes"];
for (int i = 0; i < json_nodes.Count; ++i)
{
var json_node = json_nodes[i];
string node_type = json_node["type"];
Debug.Log(node_type);
LGraphNode node = LiteGraph.Globals.createNodeType(node_type);
if (node == null)
{
Debug.Log("Error: node type not found: " + node_type);
has_errors = true;
continue;
}
node.graph = this;
nodes.Add(node);
node.configure(json_node);
}
sortByExecutionOrder();
}
public float getOutput(string name, float def_value)
{
if (!outputs.ContainsKey(name))
return def_value;
return outputs[name];
}
}
public class Main
{
public static void Init()
{
Main.loadNodes();
}
public static void loadNodes()
{
Globals.registerType(WatchNode.type, () => new WatchNode() );
Globals.registerType(RandomNumberNode.type, () => new RandomNumberNode());
Globals.registerType(ConstNumberNode.type, () => new ConstNumberNode());
Globals.registerType(TimeNode.type, () => new TimeNode());
Globals.registerType(ConditionNode.type, () => new ConditionNode());
Globals.registerType(GraphOutputNode.type, () => new GraphOutputNode());
Globals.registerType(GateNode.type, () => new GateNode());
}
public static void test()
{
Debug.Log("Testing Graph...");
LGraph graph = new LGraph();
LGraphNode node1 = LiteGraph.Globals.createNodeType("math/rand");
graph.add(node1);
LGraphNode node2 = LiteGraph.Globals.createNodeType("basic/watch");
graph.add(node2);
node1.connect(0,node2,0);
for(int i = 0; i < 100; ++i)
graph.runStep();
}
}
}

203
csharp/LiteGraphNodes.cs Normal file
View File

@@ -0,0 +1,203 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using SimpleJSON;
namespace LiteGraph
{
public class ConstNumberNode : LGraphNode
{
public static string type = "basic/const";
public float value = 0;
public ConstNumberNode()
{
this.addOutput("out", DataType.NUMBER );
}
override public void onExecute()
{
this.setOutputData(0, value );
}
override public void onConfigure( JSONNode o)
{
JSONNode json_properties = o["properties"];
value = json_properties["value"].AsFloat;
}
}
public class RandomNumberNode : LGraphNode
{
public static string type = "math/rand";
float min_value = 0;
float max_value = 1;
System.Random random = new System.Random();
public RandomNumberNode()
{
this.addOutput("out", DataType.NUMBER );
}
override public void onExecute()
{
this.setOutputData(0, (float)(min_value + random.NextDouble() * (max_value - min_value)) );
}
override public void onConfigure(JSONNode o)
{
JSONNode json_properties = o["properties"];
min_value = json_properties["min"].AsFloat;
max_value = json_properties["max"].AsFloat;
}
}
public class GraphOutputNode : LGraphNode
{
public static string type = "graph/output";
string name = "";
DataType datatype = DataType.NUMBER;
public GraphOutputNode()
{
this.addInput("out", datatype);
}
override public void onExecute()
{
switch (datatype)
{
case DataType.NUMBER:
float v = this.getInputData(0, 0);
graph.outputs[name] = v;
break;
}
}
override public void onConfigure(JSONNode o)
{
JSONNode json_properties = o["properties"];
name = json_properties["name"];
if (json_properties.HasKey("type"))
{
string str_type = json_properties["type"];
if (Globals.stringToDataType.ContainsKey(str_type))
{
datatype = Globals.stringToDataType[str_type];
this.inputs[0].type = datatype;
}
}
}
}
public class ConditionNode : LGraphNode
{
public static string type = "math/condition";
public enum OPERATION { NONE,GREATER,LOWER,EQUAL,NEQUAL,GEQUAL,LEQUAL,OR,AND };
static public Dictionary<string, OPERATION> strToOperation = new Dictionary<string, OPERATION> { { "NONE", OPERATION.NONE }, { ">", OPERATION.GREATER }, { "<", OPERATION.LOWER }, { "==", OPERATION.EQUAL }, { "!=", OPERATION.NEQUAL }, { "<=", OPERATION.LEQUAL }, { ">=", OPERATION.GEQUAL }, { "&&", OPERATION.AND }, { "||", OPERATION.OR } };
public float A = 0;
public float B = 0;
public OPERATION OP = OPERATION.EQUAL;
public ConditionNode()
{
this.addOutput("A", DataType.NUMBER );
this.addOutput("B", DataType.NUMBER );
this.addOutput("out", DataType.BOOL );
}
override public void onExecute()
{
float A = this.getInputData(0,this.A);
float B = this.getInputData(1,this.B);
bool v = false;
switch(OP)
{
case OPERATION.NONE: v = false; break;
case OPERATION.GREATER: v = A > B; break;
case OPERATION.LOWER: v = A < B; break;
case OPERATION.EQUAL: v = A == B; break;
case OPERATION.NEQUAL: v = A != B; break;
case OPERATION.GEQUAL: v = A >= B; break;
case OPERATION.LEQUAL: v = A <= B; break;
case OPERATION.OR: v = (A != 0) || (B != 0); break;
case OPERATION.AND: v = (A != 0) && (B != 0); break;
}
this.setOutputData(0, v);
}
override public void onConfigure(JSONNode o)
{
JSONNode json_properties = o["properties"];
A = json_properties["A"].AsFloat;
B = json_properties["B"].AsFloat;
string op = json_properties["OP"];
if (strToOperation.ContainsKey(op))
OP = strToOperation[op];
else
Debug.Log("Wrong operation type: " + op);
}
}
public class GateNode : LGraphNode
{
public static string type = "math/gate";
public GateNode()
{
this.addInput("v", DataType.BOOL);
this.addInput("A");
this.addInput("B");
this.addOutput("out");
}
override public void onExecute()
{
bool v = this.getInputData(0, true);
this.transferData(v ? 1 : 2, 0);
}
}
public class TimeNode : LGraphNode
{
public static string type = "basic/time";
public TimeNode()
{
this.addOutput("in ms", DataType.NUMBER );
this.addOutput("in sec", DataType.NUMBER);
}
override public void onExecute()
{
this.setOutputData(0, (float)(graph.time * 1000));
this.setOutputData(1, (float)graph.time);
}
}
public class WatchNode : LGraphNode
{
public static string type = "basic/watch";
public WatchNode()
{
this.addInput("in", DataType.NUMBER);
}
override public void onExecute()
{
float v = this.getInputData(0, 0);
//Debug.Log("Watch: " + v.ToString());
}
}
}

53
csharp/LiteGraphTest.cs Normal file
View File

@@ -0,0 +1,53 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using LiteGraph;
public class LiteGraphTest : MonoBehaviour
{
public TextAsset graph_file = null;
private LGraph graph = null;
public bool graph_has_errors = false;
public float output_value = 0;
// Start is called before the first frame update
void Start()
{
System.Diagnostics.Debug.WriteLine("Test!");
LiteGraph.Main.Init();
graph = new LGraph();
if (!graph_file)
{
Debug.Log("Testing Base Graph...");
LGraphNode node1 = LiteGraph.Globals.createNodeType("math/rand");
graph.add(node1);
LGraphNode node2 = LiteGraph.Globals.createNodeType("basic/watch");
graph.add(node2);
node1.connect(0, node2, 0);
}
else {
Debug.Log("Testing File Graph...");
string text = graph_file.text;
graph.fromJSONText(text);
}
graph_has_errors = graph.has_errors;
}
// Update is called once per frame
void Update()
{
if(graph != null)
graph.runStep( Time.deltaTime );
output_value = graph.getOutput("output",0);
}
}

1395
csharp/SimpleJSON.cs Normal file

File diff suppressed because it is too large Load Diff

1
csharp/graph.JSON Normal file
View File

@@ -0,0 +1 @@
{"last_node_id":15,"last_link_id":26,"nodes":[{"id":5,"type":"graph/output","pos":[1265,331],"size":[180,60],"flags":{"collapsed":false},"order":4,"mode":0,"inputs":[{"name":"","type":"number","link":24}],"properties":{"name":"output","type":"number"}},{"id":13,"type":"basic/watch","pos":[1228,213],"size":{"0":140,"1":26},"flags":{},"order":5,"mode":0,"inputs":[{"name":"value","type":0,"link":25,"label":"0.000"}],"properties":{}},{"id":9,"type":"math/condition","pos":[754,261],"size":[180,61],"flags":{},"order":2,"mode":0,"inputs":[{"name":"A","type":"number","link":9},{"name":"B","type":"number","link":10}],"outputs":[{"name":"true","type":"boolean","links":[21]},{"name":"false","type":"boolean","links":null}],"properties":{"A":6.957165000028908,"B":4,"OP":"<"}},{"id":7,"type":"basic/time","pos":[406,262],"size":{"0":140,"1":46},"flags":{},"order":0,"mode":0,"outputs":[{"name":"in ms","type":"number","links":null},{"name":"in sec","type":"number","links":[9,22]}],"properties":{}},{"id":10,"type":"basic/const","pos":[298,560],"size":{"0":140,"1":26},"flags":{},"order":1,"mode":0,"outputs":[{"name":"value","type":"number","links":[10,23],"label":"4.000"}],"properties":{"value":4}},{"id":15,"type":"math/gate","pos":[961,483],"size":{"0":140,"1":66},"flags":{},"order":3,"mode":0,"inputs":[{"name":"v","type":"boolean","link":21},{"name":"A","type":0,"link":22},{"name":"B","type":0,"link":23}],"outputs":[{"name":"out","links":[24,25]}],"properties":{}}],"links":[[9,7,1,9,0,"number"],[10,10,0,9,1,"number"],[21,9,0,15,0,"boolean"],[22,7,1,15,1,0],[23,10,0,15,2,0],[24,15,0,5,0,"number"],[25,15,0,13,0,0]],"groups":[],"config":{},"version":0.4}