tentone 5 anni fa
parent
commit
42664bcc4f

+ 1 - 0
README.md

@@ -19,6 +19,7 @@
   - [PDF & TIFF (External libraries)](https://tentone.github.io/escher.js/examples/pdftiff)
   - [Masks](https://tentone.github.io/escher.js/examples/mask)
   - [Snake Game](https://tentone.github.io/escher.js/examples/snake)
+  - [Node Graph](https://tentone.github.io/escher.js/examples/node)
   - [Stress test](https://tentone.github.io/escher.js/examples/stress)
 - There is also available API documentation containing implementation details about all the internal components of the framework and detailed functionality descriptions.
   - [API Documentation](https://tentone.github.io/escher.js/docs/)

+ 42 - 7
build/escher.js

@@ -3672,6 +3672,8 @@
 
 	NodeConnector.prototype.destroy = function()
 	{
+		BezierCurve.prototype.destroy.call(this);
+
 		if(this.outputSocket !== null)
 		{
 			this.outputSocket.connector = null;
@@ -3681,8 +3683,6 @@
 		{
 			this.inputSocket.connector = null;
 		}
-
-		BezierCurve.prototype.destroy.call(this);
 	};
 
 	NodeConnector.prototype.onUpdate = function()
@@ -3751,6 +3751,8 @@
 		/**
 		 * Type of data available from this socket, only hooks of the same type can be connected.
 		 *
+		 * Should directly store data type as text
+		 *
 		 * @type {string}
 		 */
 		this.type = type !== undefined ? type : "";
@@ -3874,6 +3876,15 @@
 		return this.direction !== socket.direction && this.type === socket.type;
 	};
 
+	NodeSocket.prototype.destroy = function()
+	{
+		Circle.prototype.destroy.call(this);
+
+		if(this.connector !== null)
+		{
+			this.connector.destroy();
+		}
+	};
 
 	NodeSocket.prototype.onPointerDragStart = function(pointer, viewport)
 	{
@@ -3958,6 +3969,13 @@
 
 	Node.prototype = Object.create(RoundedBox.prototype);
 
+	/**
+	 * This method should be used for the node to register their socket inputs/outputs.
+	 *
+	 * It is called automatically after the node is added to the node graph to create sockets.
+	 */
+	Node.prototype.registerSockets = null;
+
 	/**
 	 * Add input to this node, can be connected to other nodes to receive data.
 	 *
@@ -3988,6 +4006,21 @@
 		return socket;
 	};
 
+	Node.prototype.destroy = function()
+	{
+		RoundedBox.prototype.destroy.call(this);
+
+		for(var i = 0; i < this.inputs.length; i++)
+		{
+			this.inputs[i].destroy();
+		}
+
+		for(var i = 0; i < this.outputs.length; i++)
+		{
+			this.outputs[i].destroy();
+		}
+	};
+
 	Node.prototype.onUpdate = function()
 	{
 		var height = this.box.max.y - this.box.min.y;
@@ -4023,8 +4056,6 @@
 	function NodeGraph()
 	{
 		Object2D.call(this);
-
-		// TODO <ADD CODE HERE>
 	}
 
 	NodeGraph.prototype = Object.create(Object2D.prototype);
@@ -4034,10 +4065,10 @@
 	 *
 	 * Automatically finds an empty space as close as possible to other nodes to add this new node.
 	 *
-	 * @param {Function} NodeConstructor Constructor of the node type to be created.
+	 * @param {Node} node Node object to be added.
 	 * @return {Node} Node created (already added to the graph).
 	 */
-	NodeGraph.prototype.createNode = function(NodeConstructor)
+	NodeGraph.prototype.addNode = function(node)
 	{
 		// Check available position on screen.
 		var x = 0, y = 0;
@@ -4054,10 +4085,14 @@
 		}
 
 		// Create and add new node
-		var node = new NodeConstructor();
 		node.position.set(x + 300, y / 2.0);
 		this.add(node);
 
+		if(node.registerSockets !== null)
+		{
+			node.registerSockets();
+		}
+
 		return node;
 	};
 

+ 56 - 20
examples/node.html

@@ -15,7 +15,7 @@
 		<div style="position:absolute; width:60px; height:50px; top:50px; left:10px; text-align:center; z-index:10; cursor: pointer;" onclick="window.addOperatorBlock('-');">-</div>
 		<div style="position:absolute; width:60px; height:50px; top:100px; left:10px; text-align:center; z-index:10; cursor: pointer;" onclick="window.addOperatorBlock('*');">x</div>
 		<div style="position:absolute; width:60px; height:50px; top:150px; left:10px; text-align:center; z-index:10; cursor: pointer;" onclick="window.addOperatorBlock('/');">/</div>
-		<div style="position:absolute; width:60px; height:50px; top:200px; left:10px; text-align:center; z-index:10; cursor: pointer;" onclick="window.addConstantBlock(prompt('Input value'));">Num</div>
+		<div style="position:absolute; width:60px; height:50px; top:200px; left:10px; text-align:center; z-index:10; cursor: pointer;" onclick="window.addInputBlock();">Num</div>
 	</div>
 
 	<!-- Code -->
@@ -42,35 +42,71 @@
 
 		window.addOperatorBlock = function(symbol)
 		{
-			// Box
-			var node = new Escher.Node();
-			node.position.set(100, 100);
-			node.draggable = true;
-			group.add(node);
-			
-			// DOM
-			var div = new Escher.DOM(canvas.parentElement);
-			div.size.set(100, 50);
-			div.origin.set(50, 25);
-			node.add(div);
-
-			var text = document.createElement("div");
-			text.style.fontFamily = "Arial";
-			text.style.textAlign = "center";
-			text.innerHTML = symbol;
-			div.element.appendChild(text);
+			graph.addNode(new OperationNode(symbol));
 		};
 
 
+		window.addInputBlock = function(symbol)
+		{
+			graph.addNode(new NumberInputNode());
+		};
+
+		class OperationNode extends Escher.Node
+		{
+			constructor(operation)
+			{
+				super();
+
+				this.operation = operation;
+
+				this.text = new Escher.Text();
+				this.text.text = operation;
+				this.text.font = "35px Arial";
+				this.text.layer = 2;
+
+				this.add(this.text);
+			}
+
+			registerSockets()
+			{
+				this.addInput("number", "a");
+				this.addInput("number", "b");
+				this.addOutput("number", "r");
+			}
+		}
+
+		class NumberInputNode extends Escher.Node
+		{
+			constructor()
+			{
+				super();
+
+				this.div = new Escher.DOM(canvas.parentElement);
+				this.div.size.set(100, 50);
+				this.div.origin.set(50, 25);
+				this.add(this.div);
+
+				this.input = document.createElement("input");
+				this.input.type = "number";
+				this.input.style.fontFamily = "Arial";
+				this.input.style.textAlign = "center";
+				this.div.element.appendChild(this.input);
+			}
+
+			registerSockets()
+			{
+				this.addOutput("number", "v");
+			}
+		}
 
 		// Group to store other objects
 		var graph = new Escher.NodeGraph();
 
-		var a = graph.createNode(Escher.Node);
+		var a = graph.addNode(new Escher.Node());
 		a.addInput("test", "a");
 		var aa = a.addOutput("test", "a");
 
-		var b = graph.createNode(Escher.Node);
+		var b = graph.addNode(new Escher.Node());
 		var bb = b.addInput("test", "a");
 		b.addInput("test", "b");
 		b.addInput("test", "c");

+ 0 - 0
examples/diagram.html → examples/playground.html


+ 22 - 1
source/objects/node/Node.js

@@ -1,4 +1,3 @@
-import {Box} from "../Box";
 import {NodeSocket} from "./NodeSocket";
 import {RoundedBox} from "../RoundedBox";
 
@@ -34,6 +33,13 @@ function Node()
 
 Node.prototype = Object.create(RoundedBox.prototype);
 
+/**
+ * This method should be used for the node to register their socket inputs/outputs.
+ *
+ * It is called automatically after the node is added to the node graph to create sockets.
+ */
+Node.prototype.registerSockets = null;
+
 /**
  * Add input to this node, can be connected to other nodes to receive data.
  *
@@ -64,6 +70,21 @@ Node.prototype.addOutput = function(type, name)
 	return socket;
 };
 
+Node.prototype.destroy = function()
+{
+	RoundedBox.prototype.destroy.call(this);
+
+	for(var i = 0; i < this.inputs.length; i++)
+	{
+		this.inputs[i].destroy();
+	}
+
+	for(var i = 0; i < this.outputs.length; i++)
+	{
+		this.outputs[i].destroy();
+	}
+};
+
 Node.prototype.onUpdate = function()
 {
 	var height = this.box.max.y - this.box.min.y;

+ 2 - 2
source/objects/node/NodeConnector.js

@@ -32,6 +32,8 @@ NodeConnector.prototype = Object.create(BezierCurve.prototype);
 
 NodeConnector.prototype.destroy = function()
 {
+	BezierCurve.prototype.destroy.call(this);
+
 	if(this.outputSocket !== null)
 	{
 		this.outputSocket.connector = null;
@@ -41,8 +43,6 @@ NodeConnector.prototype.destroy = function()
 	{
 		this.inputSocket.connector = null;
 	}
-
-	BezierCurve.prototype.destroy.call(this);
 };
 
 NodeConnector.prototype.onUpdate = function()

+ 7 - 5
source/objects/node/NodeGraph.js

@@ -12,8 +12,6 @@ import {Object2D} from "../../Object2D";
 function NodeGraph()
 {
 	Object2D.call(this);
-
-	// TODO <ADD CODE HERE>
 }
 
 NodeGraph.prototype = Object.create(Object2D.prototype);
@@ -23,10 +21,10 @@ NodeGraph.prototype = Object.create(Object2D.prototype);
  *
  * Automatically finds an empty space as close as possible to other nodes to add this new node.
  *
- * @param {Function} NodeConstructor Constructor of the node type to be created.
+ * @param {Node} node Node object to be added.
  * @return {Node} Node created (already added to the graph).
  */
-NodeGraph.prototype.createNode = function(NodeConstructor)
+NodeGraph.prototype.addNode = function(node)
 {
 	// Check available position on screen.
 	var x = 0, y = 0;
@@ -43,10 +41,14 @@ NodeGraph.prototype.createNode = function(NodeConstructor)
 	}
 
 	// Create and add new node
-	var node = new NodeConstructor();
 	node.position.set(x + 300, y / 2.0);
 	this.add(node);
 
+	if(node.registerSockets !== null)
+	{
+		node.registerSockets();
+	}
+
 	return node;
 };
 

+ 11 - 0
source/objects/node/NodeSocket.js

@@ -32,6 +32,8 @@ function NodeSocket(node, direction, type, name)
 	/**
 	 * Type of data available from this socket, only hooks of the same type can be connected.
 	 *
+	 * Should directly store data type as text
+	 *
 	 * @type {string}
 	 */
 	this.type = type !== undefined ? type : "";
@@ -155,6 +157,15 @@ NodeSocket.prototype.isCompatible = function(socket)
 	return this.direction !== socket.direction && this.type === socket.type;
 };
 
+NodeSocket.prototype.destroy = function()
+{
+	Circle.prototype.destroy.call(this);
+
+	if(this.connector !== null)
+	{
+		this.connector.destroy();
+	}
+};
 
 NodeSocket.prototype.onPointerDragStart = function(pointer, viewport)
 {