tentone 5 gadi atpakaļ
vecāks
revīzija
982c62764a

+ 177 - 33
build/escher.js

@@ -701,16 +701,14 @@
 	function UUID(){}
 
 	/**
-	 * Generate new random UUID v4 as string.
-	 *
-	 * http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/21963136#21963136
+	 * Generates a new random UUID v4 as string.
 	 *
 	 * @static
+	 * @return {string} UUID generated as string.
 	 */
 	UUID.generate = (function ()
 	{
 		var lut = [];
-
 		for(var i = 0; i < 256; i++)
 		{
 			lut[i] = (i < 16 ? "0" : "") + (i).toString(16);
@@ -745,48 +743,66 @@
 	{	
 		/**
 		 * UUID of the object.
+		 *
+		 * @type {string}
 		 */
 		this.uuid = UUID.generate(); 
 
 		/**
 		 * List of children objects attached to the object.
+		 *
+		 * @type {Object2D[]}
 		 */
 		this.children = [];
 
 		/**
 		 * Parent object, the object position is affected by its parent position.
+		 *
+		 * @type {Object2D}
 		 */
 		this.parent = null;
 
 		/**
 		 * Depth level in the object tree, objects with higher depth are drawn on top.
 		 *
-		 * The layer value is considered first. 
+		 * The layer value is considered first.
+		 *
+		 * @type {number}
 		 */
 		this.level = 0;
 
 		/**
 		 * Position of the object.
+		 *
+		 * @type {Vector2}
 		 */
 		this.position = new Vector2(0, 0);
 
 		/**
 		 * Origin of the object used as point of rotation.
+		 *
+		 * @type {Vector2}
 		 */
 		this.origin = new Vector2(0, 0);
 
 		/**
 		 * Scale of the object.
+		 *
+		 * @type {Vector2}
 		 */
 		this.scale = new Vector2(1, 1);
 
 		/**
 		 * Rotation of the object relative to its center.
+		 *
+		 * @type {number}
 		 */
 		this.rotation = 0.0;
 
 		/**
 		 * Indicates if the object is visible.
+		 *
+		 * @type {boolean}
 		 */
 		this.visible = true;
 
@@ -794,11 +810,15 @@
 		 * Layer of this object, objects are sorted by layer value.
 		 *
 		 * Lower layer value is draw first.
+		 *
+		 * @type {number}
 		 */
 		this.layer = 0;
 
 		/**
-		 * Local transformation matrix applied to the object. 
+		 * Local transformation matrix applied to the object.
+		 *
+		 * @type {Matrix}
 		 */
 		this.matrix = new Matrix();
 
@@ -806,6 +826,8 @@
 		 * Global transformation matrix multiplied by the parent matrix.
 		 *
 		 * Used to transform the object before projecting into screen coordinates.
+		 *
+		 * @type {Matrix}
 		 */
 		this.globalMatrix = new Matrix();
 
@@ -813,6 +835,8 @@
 		 * Inverse of the global matrix.
 		 *
 		 * Used to convert pointer input points into object coordinates.
+		 *
+		 * @type {Matrix}
 		 */
 		this.inverseGlobalMatrix = new Matrix();
 
@@ -820,18 +844,26 @@
 		 * Masks being applied to this object.
 		 *
 		 * Multiple masks can be used simultaneously.
+		 *
+		 * @type {Mask[]}
 		 */
 		this.masks = [];
 
 		/**
 		 * If true the matrix is updated before rendering the object.
+		 *
+		 * After the matrix is updated this attribute is automatically reset to false.
+		 *
+		 * @type {boolean}
 		 */
 		this.matrixNeedsUpdate = true;
 
 		/**
-		 * Indicates if its possible to drag the object around.
+		 * Draggable controls if its possible to drag the object around. Set this true to enable dragging events on this object.
+		 *
+		 * The onPointerDrag callback is used to update the state of the object while being dragged, by default it just updates the object position.
 		 *
-		 * If true the onPointerDrag callback is used to update the state of the object.
+		 * @type {boolean}
 		 */
 		this.draggable = false;
 
@@ -839,21 +871,29 @@
 		 * Indicates if this object uses pointer events.
 		 *
 		 * Can be set false to skip the pointer interaction events.
+		 *
+		 * @type {boolean}
 		 */
 		this.pointerEvents = true;
 
 		/**
-		 * Flag to indicate wheter this objet ignores the viewport transformation.
+		 * Flag to indicate whether this object ignores the viewport transformation.
+		 *
+		 * @type {boolean}
 		 */
 		this.ignoreViewport = false;
 
 		/**
 		 * Flag to indicate if the context of canvas should be saved before render.
+		 *
+		 * @type {boolean}
 		 */
 		this.saveContextState = true;
 
 		/**
 		 * Flag to indicate if the context of canvas should be restored after render.
+		 *
+		 * @type {boolean}
 		 */
 		this.restoreContextState = true;
 
@@ -861,11 +901,15 @@
 		 * Flag indicating if the pointer is inside of the element.
 		 *
 		 * Used to control object event.
+		 *
+		 * @type {boolean}
 		 */
 		this.pointerInside = false;
 
 		/**
 		 * Flag to indicate if the object is currently being dragged.
+		 *
+		 * @type {boolean}
 		 */
 		this.beingDragged = false;
 	}
@@ -890,7 +934,7 @@
 	/**
 	 * Get a object from its children list by its UUID.
 	 *
-	 * @param {String} uuid UUID of the object to get.
+	 * @param {string} uuid UUID of the object to get.
 	 * @return {Object2D} The object that has the UUID specified, null if the object was not found.
 	 */
 	Object2D.prototype.getChildByUUID = function(uuid)
@@ -934,11 +978,11 @@
 	/**
 	 * Remove object from the children list.
 	 *
-	 * @param {Object2D} object Object to be removed.
+	 * @param {Object2D} children Object to be removed.
 	 */
-	Object2D.prototype.remove = function(object)
+	Object2D.prototype.remove = function(children)
 	{
-		var index = this.children.indexOf(object);
+		var index = this.children.indexOf(children);
 		
 		if(index !== -1)
 		{
@@ -973,7 +1017,7 @@
 	/**
 	 * Update the transformation matrix of the object.
 	 *
-	 * @param {CanvasRenderingContext2D} context
+	 * @param {CanvasRenderingContext2D} context Canvas 2d drawing context.
 	 */
 	Object2D.prototype.updateMatrix = function(context)
 	{
@@ -988,7 +1032,7 @@
 			}
 
 			this.inverseGlobalMatrix = this.globalMatrix.getInverse();
-			//this.matrixNeedsUpdate = false;
+			this.matrixNeedsUpdate = false;
 		}
 	};
 
@@ -2565,7 +2609,7 @@
 	 */
 	Box2.prototype.containsPoint = function(point)
 	{
-		return point.x < this.min.x || point.x > this.max.x || point.y < this.min.y || point.y > this.max.y ? false : true;
+		return !(point.x < this.min.x || point.x > this.max.x || point.y < this.min.y || point.y > this.max.y);
 	};
 
 	/**
@@ -2589,7 +2633,7 @@
 	 */
 	Box2.prototype.intersectsBox = function(box)
 	{
-		return box.max.x < this.min.x || box.min.x > this.max.x || box.max.y < this.min.y || box.min.y > this.max.y ? false : true;
+		return !(box.max.x < this.min.x || box.min.x > this.max.x || box.max.y < this.min.y || box.min.y > this.max.y);
 	};
 
 	/**
@@ -2750,7 +2794,7 @@
 		/**
 		 * Box object containing the size of the object.
 		 */
-		this.box = new Box2(new Vector2(-50, -35), new Vector2(50, 35));
+		this.box = new Box2(new Vector2(-50, -50), new Vector2(50, 50));
 
 		/**
 		 * Style of the object border line.
@@ -3394,14 +3438,58 @@
 	{
 		Box.call(this);
 
+		this.draggable = true;
+
+		/**
+		 * List of inputs of the node.
+		 *
+		 * @type {NodeHook[]}
+		 */
 		this.inputs = [];
 
+		/**
+		 * List of outputs of the node.
+		 *
+		 * @type {NodeHook[]}
+		 */
 		this.outputs = [];
-
 	}
 
 	Node.prototype = Object.create(Box.prototype);
 
+	/**
+	 * Add input to this node.
+	 *
+	 * @param type
+	 */
+	Node.prototype.addInput = function(type)
+	{
+		var hook = new NodeHook(this, NodeHook.INPUT);
+		hook.type = type;
+		this.add(hook);
+	};
+
+	/**
+	 * Add output hook to this node.
+	 *
+	 * @param type
+	 */
+	Node.prototype.addOutput = function(type)
+	{
+		var hook = new NodeHook(this, NodeHook.OUTPUT);
+		hook.type = type;
+		this.add(hook);
+	};
+
+
+	Node.prototype.draw = function(context, viewport, canvas)
+	{
+		// Position the hooks in the box.
+		// TODO <ADD CODE HERE>
+
+		Box.prototype.draw.call(this, context, viewport, canvas);
+	};
+
 	/**
 	 * Node connector is used to connect a output of a node to a input of another node.
 	 *
@@ -3413,36 +3501,93 @@
 	{
 		BezierCurve.call(this);
 
-		// TODO <ADD CODE HERE>
+		/**
+		 * Origin hook that is attached to a node.
+		 *
+		 * @type {NodeHook}
+		 */
+		this.origin = null;
+
+		/**
+		 * Destination hook that is attached to a node.
+		 *
+		 * @type {NodeHook}
+		 */
+		this.destination = null;
 	}
 
 	NodeConnector.prototype = Object.create(BezierCurve.prototype);
 
 	/**
-	 * TODO
+	 * Represents a node hook point. Is attached to the node element and represented visually.
 	 *
-	 * @class NodeInput
+	 * Can be used as a node input, output or as a bidirectional connection.
+	 *
+	 * @class NodeHook
+	 * @param {Node} node Node of this hook.
+	 * @param {number} direction Direction of the hook.
 	 */
-	function NodeInput()
+	function NodeHook$1(node, direction)
 	{
 		Circle.call(this);
 
+		/**
+		 * If set true the hook can be connected to multiple hooks.
+		 *
+		 * @type {boolean}
+		 */
+		this.multiple = false;
+
+		/**
+		 * Direction of the node hook.
+		 *
+		 * @type {number}
+		 */
+		this.direction = direction;
+
+		/**
+		 * Type of hook. Hooks of the same type can be connected.
+		 *
+		 * @type {string}
+		 */
+		this.type = "";
+
+		/**
+		 * Node where this input element in attached.
+		 *
+		 * @type {Node}
+		 */
+		this.node = node;
 	}
 
-	NodeInput.prototype = Object.create(Circle.prototype);
+	/**
+	 * Input hook can only be connected to an output.
+	 *
+	 * Is used to read data from the output.
+	 *
+	 * @type {number}
+	 */
+	NodeHook$1.INPUT = 1;
 
 	/**
-	 * TODO
+	 * Output hook can only be connected to an input.
+	 *
+	 * Writes data to the output.
 	 *
-	 * @class NodeInput
+	 * @type {number}
 	 */
-	function NodeOutput()
-	{
-		Circle.call(this);
+	NodeHook$1.OUTPUT = 2;
 
-	}
+	/**
+	 * Bidirectional hook can be connected to any type of hook.
+	 *
+	 * Can be used to write and read from inputs and/or outputs.
+	 *
+	 * @type {number}
+	 */
+	NodeHook$1.BIDIRECTIONAL = 3;
 
-	NodeOutput.prototype = Object.create(Circle.prototype);
+	NodeHook$1.prototype = Object.create(Circle.prototype);
 
 	/**
 	 * Class contains helper functions to create editing object tools.
@@ -3569,8 +3714,7 @@
 	exports.MultiLineText = MultiLineText;
 	exports.Node = Node;
 	exports.NodeConnector = NodeConnector;
-	exports.NodeInput = NodeInput;
-	exports.NodeOutput = NodeOutput;
+	exports.NodeHook = NodeHook$1;
 	exports.Object2D = Object2D;
 	exports.Pattern = Pattern;
 	exports.Pointer = Pointer;

+ 11 - 15
examples/node.html

@@ -43,16 +43,16 @@
 		window.addOperatorBlock = function(symbol)
 		{
 			// Box
-			var box = new Escher.Box();
-			box.position.set(100, 100);
-			box.draggable = true;
-			group.add(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);
-			box.add(div);
+			node.add(div);
 
 			var text = document.createElement("div");
 			text.style.fontFamily = "Arial";
@@ -63,19 +63,15 @@
 
 
 
-
-		function NodeHook()
-		{
-			this.inputs = [];
-			this.outputs = [];
-		}
-
-
-
 		// Group to store other objects
 		var group = new Escher.Object2D();
 
-		// Line (connection)
+		var node = new Escher.Node();
+		node.position.set(100, 100);
+		node.draggable = true;
+		group.add(node);
+
+			// Line (connection)
 		//var line = new Escher.BezierCurve();
 		//line.from = box.position;
 		//line.to = circle.position;

+ 0 - 1
source/Escher.js

@@ -30,7 +30,6 @@ export {QuadraticCurve} from "./objects/QuadraticCurve.js";
 export {Node} from "./objects/node/Node.js";
 export {NodeConnector} from "./objects/node/NodeConnector.js";
 export {NodeHook} from "./objects/node/NodeHook.js";
-export {NodeOutput} from "./objects/node/NodeOutput.js";
 
 export {ViewportControls} from "./controls/ViewportControls.js";
 

+ 77 - 16
source/Object2D.js

@@ -15,60 +15,88 @@ function Object2D()
 {	
 	/**
 	 * UUID of the object.
+	 *
+	 * @type {string}
 	 */
 	this.uuid = UUID.generate(); 
 
 	/**
 	 * List of children objects attached to the object.
+	 *
+	 * @type {Object2D[]}
 	 */
 	this.children = [];
 
 	/**
 	 * Parent object, the object position is affected by its parent position.
+	 *
+	 * @type {Object2D}
 	 */
 	this.parent = null;
 
 	/**
 	 * Depth level in the object tree, objects with higher depth are drawn on top.
 	 *
-	 * The layer value is considered first. 
+	 * The layer value is considered first.
+	 *
+	 * @type {number}
 	 */
 	this.level = 0;
 
 	/**
 	 * Position of the object.
+	 *
+	 * The world position of the object is affected by its parent transform.
+	 *
+	 * @type {Vector2}
 	 */
 	this.position = new Vector2(0, 0);
 
 	/**
 	 * Origin of the object used as point of rotation.
+	 *
+	 * @type {Vector2}
 	 */
 	this.origin = new Vector2(0, 0);
 
 	/**
 	 * Scale of the object.
+	 *
+	 * The world scale of the object is affected by the parent transform.
+	 *
+	 * @type {Vector2}
 	 */
 	this.scale = new Vector2(1, 1);
 
 	/**
 	 * Rotation of the object relative to its center.
+	 *
+	 * The world rotation of the object is affected by the parent transform.
+	 *
+	 * @type {number}
 	 */
 	this.rotation = 0.0;
 
 	/**
 	 * Indicates if the object is visible.
+	 *
+	 * @type {boolean}
 	 */
 	this.visible = true;
 
 	/**
 	 * Layer of this object, objects are sorted by layer value.
 	 *
-	 * Lower layer value is draw first.
+	 * Lower layer value is draw first, higher layer value is drawn on top.
+	 *
+	 * @type {number}
 	 */
 	this.layer = 0;
 
 	/**
-	 * Local transformation matrix applied to the object. 
+	 * Local transformation matrix applied to the object.
+	 *
+	 * @type {Matrix}
 	 */
 	this.matrix = new Matrix();
 
@@ -76,6 +104,8 @@ function Object2D()
 	 * Global transformation matrix multiplied by the parent matrix.
 	 *
 	 * Used to transform the object before projecting into screen coordinates.
+	 *
+	 * @type {Matrix}
 	 */
 	this.globalMatrix = new Matrix();
 
@@ -83,25 +113,44 @@ function Object2D()
 	 * Inverse of the global matrix.
 	 *
 	 * Used to convert pointer input points into object coordinates.
+	 *
+	 * @type {Matrix}
 	 */
 	this.inverseGlobalMatrix = new Matrix();
 
 	/**
-	 * Masks being applied to this object.
+	 * Mask objects being applied to this object. Used to mask/subtract portions of this object when rendering.
 	 *
-	 * Multiple masks can be used simultaneously.
+	 * Multiple masks can be used simultaneously. Same mask might be reused for multiple objects.
+	 *
+	 * @type {Mask[]}
 	 */
 	this.masks = [];
 
 	/**
-	 * If true the matrix is updated before rendering the object.
+	 * Indicates if the transform matrix should be automatically updated every frame.
+	 *
+	 * Set this false for better performance. But if you do so dont forget to set matrixNeedsUpdate every time that a transform attribute is changed.
+	 *
+	 * @type {boolean}
+	 */
+	this.matrixAutoUpdate = true;
+
+	/**
+	 * Indicates if the matrix needs to be updated, should be set true after changes to the object position, scale or rotation.
+	 *
+	 * The matrix is updated before rendering the object, after the matrix is updated this attribute is automatically reset to false.
+	 *
+	 * @type {boolean}
 	 */
 	this.matrixNeedsUpdate = true;
 
 	/**
-	 * Indicates if its possible to drag the object around.
+	 * Draggable controls if its possible to drag the object around. Set this true to enable dragging events on this object.
 	 *
-	 * If true the onPointerDrag callback is used to update the state of the object.
+	 * The onPointerDrag callback is used to update the state of the object while being dragged, by default it just updates the object position.
+	 *
+	 * @type {boolean}
 	 */
 	this.draggable = false;
 
@@ -109,21 +158,29 @@ function Object2D()
 	 * Indicates if this object uses pointer events.
 	 *
 	 * Can be set false to skip the pointer interaction events.
+	 *
+	 * @type {boolean}
 	 */
 	this.pointerEvents = true;
 
 	/**
-	 * Flag to indicate wheter this objet ignores the viewport transformation.
+	 * Flag to indicate whether this object ignores the viewport transformation.
+	 *
+	 * @type {boolean}
 	 */
 	this.ignoreViewport = false;
 
 	/**
 	 * Flag to indicate if the context of canvas should be saved before render.
+	 *
+	 * @type {boolean}
 	 */
 	this.saveContextState = true;
 
 	/**
 	 * Flag to indicate if the context of canvas should be restored after render.
+	 *
+	 * @type {boolean}
 	 */
 	this.restoreContextState = true;
 
@@ -131,11 +188,15 @@ function Object2D()
 	 * Flag indicating if the pointer is inside of the element.
 	 *
 	 * Used to control object event.
+	 *
+	 * @type {boolean}
 	 */
 	this.pointerInside = false;
 
 	/**
 	 * Flag to indicate if the object is currently being dragged.
+	 *
+	 * @type {boolean}
 	 */
 	this.beingDragged = false;
 }
@@ -160,7 +221,7 @@ Object2D.prototype.traverse = function(callback)
 /**
  * Get a object from its children list by its UUID.
  *
- * @param {String} uuid UUID of the object to get.
+ * @param {string} uuid UUID of the object to get.
  * @return {Object2D} The object that has the UUID specified, null if the object was not found.
  */
 Object2D.prototype.getChildByUUID = function(uuid)
@@ -204,11 +265,11 @@ Object2D.prototype.add = function(object)
 /**
  * Remove object from the children list.
  *
- * @param {Object2D} object Object to be removed.
+ * @param {Object2D} children Object to be removed.
  */
-Object2D.prototype.remove = function(object)
+Object2D.prototype.remove = function(children)
 {
-	var index = this.children.indexOf(object);
+	var index = this.children.indexOf(children);
 	
 	if(index !== -1)
 	{
@@ -243,11 +304,11 @@ Object2D.prototype.isInside = function(point)
 /**
  * Update the transformation matrix of the object.
  *
- * @param {CanvasRenderingContext2D} context
+ * @param {CanvasRenderingContext2D} context Canvas 2d drawing context.
  */
 Object2D.prototype.updateMatrix = function(context)
 {
-	if(this.matrixNeedsUpdate)
+	if(this.matrixAutoUpdate || this.matrixNeedsUpdate)
 	{
 		this.matrix.compose(this.position.x, this.position.y, this.scale.x, this.scale.y, this.origin.x, this.origin.y, this.rotation);
 		this.globalMatrix.copy(this.matrix);
@@ -258,7 +319,7 @@ Object2D.prototype.updateMatrix = function(context)
 		}
 
 		this.inverseGlobalMatrix = this.globalMatrix.getInverse()
-		//this.matrixNeedsUpdate = false;
+		this.matrixNeedsUpdate = false;
 	}
 };
 

+ 28 - 8
source/math/Matrix.js

@@ -1,12 +1,12 @@
 import {Vector2} from "./Vector2.js";
 
 /**
- * 2D 3x2 transformation matrix, applied to the canvas elements.
+ * 2D 3x2 transformation matrix, used to represent linear geometric transformations over objects.
  *
- * The values of the matrix are stored in a numeric array.
+ * The values of the matrix are stored in a numeric array and can be applied to the canvas or DOM elements.
  *
  * @class
- * @param {array} [values]
+ * @param {number[]} values Array of matrix values by row, needs to have exactly 6 values.
  */
 function Matrix(values)
 {
@@ -23,7 +23,7 @@ function Matrix(values)
 /**
  * Copy the content of another matrix and store in this one.
  *
- * @param {Matrix} mat
+ * @param {Matrix} mat Matrix to copy values from.
  */
 Matrix.prototype.copy = function(mat)
 {
@@ -32,6 +32,8 @@ Matrix.prototype.copy = function(mat)
 
 /**
  * Create a new matrix object with a copy of the content of this one.
+ *
+ * @return {Matrix} Copy of this matrix.
  */
 Matrix.prototype.clone = function()
 {
@@ -39,7 +41,7 @@ Matrix.prototype.clone = function()
 };
 
 /**
- * Reset this matrix to indentity.
+ * Reset this matrix to identity.
  */
 Matrix.prototype.identity = function()
 {
@@ -116,6 +118,8 @@ Matrix.prototype.compose = function(px, py, sx, sy, ox, oy, a)
 /**
  * Apply translation to this matrix.
  *
+ * Adds position over the transformation already stored in the matrix.
+ *
  * @param {number} x
  * @param {number} y
  */
@@ -128,7 +132,7 @@ Matrix.prototype.translate = function(x, y)
 /**
  * Apply rotation to this matrix.
  *
- * @param {number} angle Angle in radians.
+ * @param {number} rad Angle to rotate the matrix in radians.
  */
 Matrix.prototype.rotate = function(rad)
 {
@@ -173,7 +177,7 @@ Matrix.prototype.setPosition = function(x, y)
 };
 
 /**
- * Get the scale from the transformation matrix.
+ * Extract the scale from the transformation matrix.
  *
  * @return {Vector2} Scale of the matrix transformation.
  */
@@ -183,7 +187,7 @@ Matrix.prototype.getScale = function()
 };
 
 /**
- * Get the position from the transformation matrix.
+ * Extract the position from the transformation matrix.
  *
  * @return {Vector2} Position of the matrix transformation.
  */
@@ -202,6 +206,8 @@ Matrix.prototype.skew = function(radianX, radianY)
 
 /**
  * Get the matrix determinant.
+ *
+ * @return {number} Determinant of this matrix.
  */
 Matrix.prototype.determinant = function()
 {
@@ -210,6 +216,8 @@ Matrix.prototype.determinant = function()
 
 /**
  * Get the inverse matrix.
+ *
+ * @return {Matrix} New matrix instance containing the inverse matrix.
  */
 Matrix.prototype.getInverse = function()
 {
@@ -220,6 +228,9 @@ Matrix.prototype.getInverse = function()
 
 /**
  * Transform a point using this matrix.
+ *
+ * @param {Vector2} p Point to be transformed.
+ * @return {Vector2} Transformed point.
  */
 Matrix.prototype.transformPoint = function(p)
 {
@@ -231,6 +242,8 @@ Matrix.prototype.transformPoint = function(p)
 
 /**
  * Set a canvas context to use this transformation.
+ *
+ * @param {CanvasRenderingContext2D} context Canvas context to apply this matrix transform.
  */
 Matrix.prototype.setContextTransform = function(context)
 {
@@ -239,12 +252,19 @@ Matrix.prototype.setContextTransform = function(context)
 
 /**
  * Transform on top of the current context transformation.
+ *
+ * @param {CanvasRenderingContext2D} context Canvas context to apply this matrix transform.
  */
 Matrix.prototype.tranformContext = function(context)
 {
 	context.transform(this.m[0], this.m[1], this.m[2], this.m[3], this.m[4], this.m[5]);
 };
 
+/**
+ * Generate a CSS transform string that can be applied to the transform style of any DOM element.
+ *
+ * @returns {string} CSS transform matrix.
+ */
 Matrix.prototype.cssTransform = function()
 {
 	return "matrix(" + this.m[0] + "," + this.m[1] + "," + this.m[2] + "," + this.m[3] + "," + this.m[4] + "," + this.m[5] + ")";

+ 30 - 2
source/objects/node/Node.js

@@ -13,6 +13,8 @@ function Node()
 {
 	Box.call(this);
 
+	this.draggable = true;
+
 	/**
 	 * List of inputs of the node.
 	 *
@@ -30,11 +32,37 @@ function Node()
 
 Node.prototype = Object.create(Box.prototype);
 
-Node.prototype.draw = function(context, viewport, canvas)
+/**
+ * Add input to this node.
+ *
+ * @param type
+ */
+Node.prototype.addInput = function(type)
 {
-	Box.prototype.draw.call(this, context, viewport, canvas);
+	var hook = new NodeHook(this, NodeHook.INPUT);
+	hook.type = type;
+	this.add(hook);
+};
+
+/**
+ * Add output hook to this node.
+ *
+ * @param type
+ */
+Node.prototype.addOutput = function(type)
+{
+	var hook = new NodeHook(this, NodeHook.OUTPUT);
+	hook.type = type;
+	this.add(hook);
+};
+
 
+Node.prototype.draw = function(context, viewport, canvas)
+{
+	// Position the hooks in the box.
 	// TODO <ADD CODE HERE>
+
+	Box.prototype.draw.call(this, context, viewport, canvas);
 };
 
 export {Node};

+ 44 - 3
source/objects/node/NodeHook.js

@@ -7,26 +7,67 @@ import {Node} from "./Node";
  * Can be used as a node input, output or as a bidirectional connection.
  *
  * @class NodeHook
+ * @param {Node} node Node of this hook.
+ * @param {number} direction Direction of the hook.
  */
-function NodeHook(node)
+function NodeHook(node, direction)
 {
 	Circle.call(this);
 
+	/**
+	 * If set true the hook can be connected to multiple hooks.
+	 *
+	 * @type {boolean}
+	 */
+	this.multiple = false;
+
 	/**
 	 * Direction of the node hook.
+	 *
+	 * @type {number}
+	 */
+	this.direction = direction;
+
+	/**
+	 * Type of hook. Hooks of the same type can be connected.
+	 *
+	 * @type {string}
 	 */
-	this.direction = NodeHook.INPUT;
+	this.type = "";
 
 	/**
 	 * Node where this input element in attached.
 	 *
 	 * @type {Node}
 	 */
-	this.node = null;
+	this.node = node;
 }
 
+/**
+ * Input hook can only be connected to an output.
+ *
+ * Is used to read data from the output.
+ *
+ * @type {number}
+ */
 NodeHook.INPUT = 1;
+
+/**
+ * Output hook can only be connected to an input.
+ *
+ * Writes data to the output.
+ *
+ * @type {number}
+ */
 NodeHook.OUTPUT = 2;
+
+/**
+ * Bidirectional hook can be connected to any type of hook.
+ *
+ * Can be used to write and read from inputs and/or outputs.
+ *
+ * @type {number}
+ */
 NodeHook.BIDIRECTIONAL = 3;
 
 NodeHook.prototype = Object.create(Circle.prototype);