diff --git a/01_Code/physical_computing_interface/app.js b/01_Code/physical_computing_interface/app.js
index d6db620243ecc29c09405f7796aeedb3ea278d69..2cb95555a4847bd58d64501628db0a82acec0165 100644
--- a/01_Code/physical_computing_interface/app.js
+++ b/01_Code/physical_computing_interface/app.js
@@ -7,6 +7,7 @@ var utils= new utilities();
 var GLOBALS=new globals(utils);
 
 
+
 var three=new threejs(GLOBALS,utils,'webgl','threejs');
 three.init();
 
@@ -18,3 +19,4 @@ assembler.run();
 
 
 
+
diff --git a/01_Code/physical_computing_interface/graph/graph.js b/01_Code/physical_computing_interface/graph/graph.js
index 165b3e19eb3fd1f4b8c3aa97d849b76949fc738a..84f6bf915e5cb7d3ad324dbc99d0b34ceb1ea0c1 100644
--- a/01_Code/physical_computing_interface/graph/graph.js
+++ b/01_Code/physical_computing_interface/graph/graph.js
@@ -6,7 +6,9 @@ function initGraph(){
     var color11= "#ffffff8c"; //white transparent
     var color2= "#020227";  //dark blue
     var color3= "#1c5c61"; //teal
+    var color33= "#1c5c618c"; //teal transparent
     var color4= "#fa6e70"; //red/orange
+    var color44= "#fa6e708c"; //red/orange
     var color5="#380152"; //purple
     var color6="#696767"; //grey
     var font= "consolas";//'font-family'
@@ -63,7 +65,13 @@ function initGraph(){
                 style: {
                     'width': 2,
                     'line-color': color3,
-                    'curve-style': 'straight'
+                    'curve-style': 'bezier', //straight
+                    'target-arrow-color': color3,
+                    'target-arrow-shape': 'vee',
+                    'content': 'data(name)',
+                    'color': color6,
+                    'font-family':font,
+                    "font-size": 6
                 }
             },
 
@@ -83,6 +91,7 @@ function initGraph(){
                 }
             }
         ],
+
         elements: {
             nodes: [
             //   {
@@ -159,10 +168,104 @@ function initGraph(){
             //   { data: { source: 'app', target: 'api', name: 'use' }, classes: 'app' },
             //   { data: { source: 'app', target: 'ext', name: 'register' }, classes: 'app' }
             ]
-            },
+        },
+        
 
                 
     });
+    //////////////////////////
+
+    cy.cxtmenu({
+        selector: 'node',//'node, edge',
+        menuRadius: 50,
+        fillColor: color11, // the background colour of the menu
+        activeFillColor: color44, // the colour used to indicate the selected command
+        // activePadding: 20, // additional size in pixels for the active command
+        indicatorSize: 20, // the size in pixels of the pointer to the active command
+        // separatorWidth: 3, // the empty spacing in pixels between successive commands
+        spotlightPadding: 4, // extra spacing in pixels between the element and the spotlight
+        minSpotlightRadius: 15, // the minimum radius in pixels of the spotlight
+        maxSpotlightRadius: 10, // the maximum radius in pixels of the spotlight
+        // openMenuEvents: 'cxttapstart taphold', // space-separated cytoscape events that will open the menu; only `cxttapstart` and/or `taphold` work here
+        itemColor: color6, // the colour of text in the command's content
+        // itemTextShadowColor: 'transparent', // the text shadow colour of the command's content
+        zIndex: 2*9999, // the z-index of the ui div
+        // atMouse: false // draw menu at mouse position
+
+        commands: [
+            {
+                content: '<span class="fa fa-play fa-1x"></span>',
+                select: function(ele){
+                    console.log( ele.id() );
+                }
+            },
+
+            {
+                content: '<span class="fa fa-trash fa-1x"></span>',
+                select: function(ele){
+                    console.log( ele.data('name') );
+                    var tgt = ele; 
+                    // console.log(tgt._private.data.id);
+                    var pos=utils.getXYZfromName(tgt._private.data.id);
+                    if(tgt._private.data.name=="N/A"){
+                        tgt.remove();
+
+                    }else{
+                        GLOBALS.removeNode(pos.x,pos.y,pos.z);
+                    }
+                },
+                // enabled: true
+            },
+
+            {
+                // content: 'Select',
+                content: '<span class="fa fa-mouse-pointer fa-1x"></span>',
+                select: function(ele){
+                    console.log( ele.id());
+                    // var nodes = this;
+                    // ele.trigger('tap');//TODO FIX SELECT
+                    
+                }
+            }
+        ]
+    });
+
+    cy.cxtmenu({
+        selector: 'core',
+        menuRadius: 50,
+        fillColor: color11, // the background colour of the menu
+        activeFillColor: color44, // the colour used to indicate the selected command
+        // activePadding: 20, // additional size in pixels for the active command
+        indicatorSize: 20, // the size in pixels of the pointer to the active command
+        // separatorWidth: 3, // the empty spacing in pixels between successive commands
+        spotlightPadding: 4, // extra spacing in pixels between the element and the spotlight
+        minSpotlightRadius: 15, // the minimum radius in pixels of the spotlight
+        maxSpotlightRadius: 10, // the maximum radius in pixels of the spotlight
+        // openMenuEvents: 'cxttapstart taphold', // space-separated cytoscape events that will open the menu; only `cxttapstart` and/or `taphold` work here
+        itemColor: color6, // the colour of text in the command's content
+        // itemTextShadowColor: 'transparent', // the text shadow colour of the command's content
+        zIndex: 2*9999, // the z-index of the ui div
+        // atMouse: false // draw menu at mouse position
+
+
+        commands: [
+            {
+                content: '<span class="fa fa-expand-arrows-alt fa-1.5x"></span>',
+                select: function(){
+                    // console.log( 'bg1' );
+                    api.expandAll();
+                }
+            },
+
+            {
+                content: '<span class="fa fa-compress-arrows-alt fa-1.5x"></span>',
+                select: function(){
+                    api.collapseAll();
+                }
+            }
+        ]
+    });
+    /////////////////////////
 
     addLayerNodes(GLOBALS.gridSize/2);
 
@@ -266,7 +369,7 @@ function initGraph(){
             // data: { id: 'new' + Math.round( Math.random() * 100 ) },
             data: { 
                 id: e.detail.id,
-                name: '[' +e.detail.x +"," +e.detail.y+']',
+                name: 0,
                 parent: ''+e.detail.z,
                 data:{
                     id: e.detail.id,
@@ -274,6 +377,8 @@ function initGraph(){
                     parent: ''+e.detail.z,
                     code: "sum(x+y/2)",
                     neighbors:[],
+                    inputs:[],
+                    outputs:[],
                 }
             },
             position: {
@@ -284,6 +389,7 @@ function initGraph(){
 
         api.expandAll();
         addEdges(e.detail.x,e.detail.y,e.detail.z);
+        // addEvents(e.detail.x,e.detail.y,e.detail.z);
         // api.expandAll();
         
     }, false);
@@ -305,15 +411,18 @@ function initGraph(){
                 
                 tgt._private.data.data.neighbors.push('[' +x +"," +y+","+z+']');
                 cy.add([
-                    { group: "edges", data: { source: '[' +x +"," +y+","+z+']', target: '[' +i1 +"," +j1+","+k1+']',
-                    id:"blaaa"+'[' +x +"," +y+","+z+']'+'[' +i1 +"," +j1+","+k1+']'}}
+                    { group: "edges",data: { name:0, source: '[' +x +"," +y+","+z+']', target: '[' +i1 +"," +j1+","+k1+']',
+                    id:'[' +x +"," +y+","+z+']'+'to[' +i1 +"," +j1+","+k1+']'}}
+                ]);
+                cy.add([
+                    { group: "edges",data: { name:0, target: '[' +x +"," +y+","+z+']', source: '[' +i1 +"," +j1+","+k1+']',
+                    id:'[' +i1 +"," +j1+","+k1+']'+'to[' +x +"," +y+","+z+']'}}
                 ]);
             }
         }
     }
 
     //remove node
-    //TODO: UPDATE EDGES WHEN NODE IS REMOVED, CREATE UPDATE NEIGHBORS FUNCTION
     cy.on('cxttap', 'node', function( evt ){
         var tgt = evt.target || evt.cyTarget; // 3.x || 2.x
         // console.log(tgt._private.data.id);
@@ -363,37 +472,37 @@ function initGraph(){
     var api = cy.expandCollapse('get');
     var elements = null;
 
-    document.getElementById("collapseRecursively").addEventListener("click", function () {
-        api.collapseRecursively(cy.$(":selected"));
-    });
+    // document.getElementById("collapseRecursively").addEventListener("click", function () {
+    //     api.collapseRecursively(cy.$(":selected"));
+    // });
 
-    document.getElementById("expandRecursively").addEventListener("click", function () {
-        api.expandRecursively(cy.$(":selected"));
-    });
+    // document.getElementById("expandRecursively").addEventListener("click", function () {
+    //     api.expandRecursively(cy.$(":selected"));
+    // });
 
-    document.getElementById("expandAllAndRemove").addEventListener("click", function () {
-        api.expandAll();
-        elements = cy.elements().remove();
-    });
+    // document.getElementById("expandAllAndRemove").addEventListener("click", function () {
+    //     api.expandAll();
+    //     elements = cy.elements().remove();
+    // });
 
-    document.getElementById("loadInCollapsedState").addEventListener("click", function () {
-        if (elements){
-            cy.add(elements);
-            api.collapseAll();
-            elements = null;
-        }
-        else{
-            console.warn("Remove elements first by clicking on 'Expand all and remove' button.");
-        }
-    });
+    // document.getElementById("loadInCollapsedState").addEventListener("click", function () {
+    //     if (elements){
+    //         cy.add(elements);
+    //         api.collapseAll();
+    //         elements = null;
+    //     }
+    //     else{
+    //         console.warn("Remove elements first by clicking on 'Expand all and remove' button.");
+    //     }
+    // });
 
-    document.getElementById("collapseAll").addEventListener("click", function () {
-        api.collapseAll();
-    });
+    // document.getElementById("collapseAll").addEventListener("click", function () {
+    //     api.collapseAll();
+    // });
 
-    document.getElementById("expandAll").addEventListener("click", function () {
-        api.expandAll();
-    });
+    // document.getElementById("expandAll").addEventListener("click", function () {
+    //     api.expandAll();
+    // });
 
     // });
 
diff --git a/01_Code/physical_computing_interface/graph/style.css b/01_Code/physical_computing_interface/graph/style.css
index 3a080dc977f292125a463520111c4ed68f1f0c1a..64e86da8a27b23c5578b1df433797207b11d6da6 100644
--- a/01_Code/physical_computing_interface/graph/style.css
+++ b/01_Code/physical_computing_interface/graph/style.css
@@ -1,5 +1,5 @@
 body { 
-  font: 14px helvetica neue, helvetica, arial, sans-serif;
+  font: 8px helvetica neue, helvetica, arial, sans-serif;
 }
 
 #cy {
@@ -8,4 +8,13 @@ body {
   position: absolute;
   left: 0;
   top: 0;
+}
+
+/* you can set the disabled style how you like on the text/icon */
+.cxtmenu-disabled {
+  opacity: 0.1;
+}
+/* you can set the disabled style how you like on the text/icon */
+.cxtmenu {
+  opacity: 0.1;
 }
\ No newline at end of file
diff --git a/01_Code/physical_computing_interface/index.html b/01_Code/physical_computing_interface/index.html
index 6d9261ba523dfa7532a5600332bb9ed3897a89a7..7ead2497cdf597ced975b3c874d313ca3e5607d5 100644
--- a/01_Code/physical_computing_interface/index.html
+++ b/01_Code/physical_computing_interface/index.html
@@ -4,6 +4,10 @@
     <title>Physical Computing Interface</title>
     <link rel="stylesheet" type="text/css" href="style.css" media="screen"/>
     <link rel="stylesheet" type="text/css" href="./lib/jsoneditor/jsoneditor.css" >
+    <!-- <link href="https://unpkg.com/font-awesome@5.8.0/css/font-awesome.min.css" rel="stylesheet" type="text/css" /> -->
+    <!-- <link href="//netdna.bootstrapcdn.com/font-awesome/3.2.1/css/font-awesome.css" rel="stylesheet"> -->
+    <script src="https://kit.fontawesome.com/99c302ff33.js" crossorigin="anonymous"></script>
+
 </head>
         
 <body>
@@ -28,16 +32,14 @@
                 <i> Graph</i>
             </div>
             <div id=jsondiveditor>
-                    <p>
+                    <!-- <p>
                         <button class="button" id="expandAll">Expand all</button> 
                         <button class="button" id="collapseAll">Collapse all</button>            
-                        <!-- <br/> -->
                         <button class="button" id="collapseRecursively">Collapse selected </button> 
                         <button class="button" id="expandRecursively">Expand selected </button>
-                        <!-- <br/> -->
                         <button class="button" id="expandAllAndRemove">Expand all and remove</button>
                         <button class="button" id="loadInCollapsedState">Load all collapsed</button>
-                    </p>
+                    </p> -->
                 
                 <div id="cy"></div>
             </div>
@@ -92,6 +94,7 @@
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
 
 <script src="./lib/cytoscape.min.js"></script>
+<script src="./lib/cytoscape-cxtmenu.js"></script>
 <script src="https://unpkg.com/layout-base/layout-base.js"></script>
 <script src="https://unpkg.com/cose-base/cose-base.js"></script>
 <script src="https://unpkg.com/cytoscape-cose-bilkent/cytoscape-cose-bilkent.js"></script>
diff --git a/01_Code/physical_computing_interface/lib/cytoscape-cxtmenu copy.js b/01_Code/physical_computing_interface/lib/cytoscape-cxtmenu copy.js
new file mode 100644
index 0000000000000000000000000000000000000000..4294ea67bfd92cadb71705056492661cfbb8da86
--- /dev/null
+++ b/01_Code/physical_computing_interface/lib/cytoscape-cxtmenu copy.js	
@@ -0,0 +1,795 @@
+(function webpackUniversalModuleDefinition(root, factory) {
+	if(typeof exports === 'object' && typeof module === 'object')
+		module.exports = factory();
+	else if(typeof define === 'function' && define.amd)
+		define([], factory);
+	else if(typeof exports === 'object')
+		exports["cytoscapeCxtmenu"] = factory();
+	else
+		root["cytoscapeCxtmenu"] = factory();
+})(this, function() {
+return /******/ (function(modules) { // webpackBootstrap
+/******/ 	// The module cache
+/******/ 	var installedModules = {};
+/******/
+/******/ 	// The require function
+/******/ 	function __webpack_require__(moduleId) {
+/******/
+/******/ 		// Check if module is in cache
+/******/ 		if(installedModules[moduleId]) {
+/******/ 			return installedModules[moduleId].exports;
+/******/ 		}
+/******/ 		// Create a new module (and put it into the cache)
+/******/ 		var module = installedModules[moduleId] = {
+/******/ 			i: moduleId,
+/******/ 			l: false,
+/******/ 			exports: {}
+/******/ 		};
+/******/
+/******/ 		// Execute the module function
+/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
+/******/
+/******/ 		// Flag the module as loaded
+/******/ 		module.l = true;
+/******/
+/******/ 		// Return the exports of the module
+/******/ 		return module.exports;
+/******/ 	}
+/******/
+/******/
+/******/ 	// expose the modules object (__webpack_modules__)
+/******/ 	__webpack_require__.m = modules;
+/******/
+/******/ 	// expose the module cache
+/******/ 	__webpack_require__.c = installedModules;
+/******/
+/******/ 	// identity function for calling harmony imports with the correct context
+/******/ 	__webpack_require__.i = function(value) { return value; };
+/******/
+/******/ 	// define getter function for harmony exports
+/******/ 	__webpack_require__.d = function(exports, name, getter) {
+/******/ 		if(!__webpack_require__.o(exports, name)) {
+/******/ 			Object.defineProperty(exports, name, {
+/******/ 				configurable: false,
+/******/ 				enumerable: true,
+/******/ 				get: getter
+/******/ 			});
+/******/ 		}
+/******/ 	};
+/******/
+/******/ 	// getDefaultExport function for compatibility with non-harmony modules
+/******/ 	__webpack_require__.n = function(module) {
+/******/ 		var getter = module && module.__esModule ?
+/******/ 			function getDefault() { return module['default']; } :
+/******/ 			function getModuleExports() { return module; };
+/******/ 		__webpack_require__.d(getter, 'a', getter);
+/******/ 		return getter;
+/******/ 	};
+/******/
+/******/ 	// Object.prototype.hasOwnProperty.call
+/******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
+/******/
+/******/ 	// __webpack_public_path__
+/******/ 	__webpack_require__.p = "";
+/******/
+/******/ 	// Load entry module and return exports
+/******/ 	return __webpack_require__(__webpack_require__.s = 4);
+/******/ })
+/************************************************************************/
+/******/ ([
+/* 0 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+var defaults = __webpack_require__(2);
+var assign = __webpack_require__(1);
+
+var _require = __webpack_require__(3),
+    removeEles = _require.removeEles,
+    setStyles = _require.setStyles,
+    createElement = _require.createElement,
+    getPixelRatio = _require.getPixelRatio,
+    getOffset = _require.getOffset;
+
+var cxtmenu = function cxtmenu(params) {
+  var options = assign({}, defaults, params);
+  var cy = this;
+  var container = cy.container();
+  var target = void 0;
+
+  var data = {
+    options: options,
+    handlers: [],
+    container: createElement({ class: 'cxtmenu' })
+  };
+
+  var wrapper = data.container;
+  var parent = createElement();
+  var canvas = createElement({ tag: 'canvas' });
+  var commands = [];
+  var c2d = canvas.getContext('2d');
+  var r = options.menuRadius;
+  var containerSize = (r + options.activePadding) * 2;
+  var activeCommandI = void 0;
+  var offset = void 0;
+
+  container.insertBefore(wrapper, container.firstChild);
+  wrapper.appendChild(parent);
+  parent.appendChild(canvas);
+
+  setStyles(wrapper, {
+    position: 'absolute',
+    zIndex: options.zIndex,
+    userSelect: 'none',
+    pointerEvents: 'none' // prevent events on menu in modern browsers
+  });
+
+  // prevent events on menu in legacy browsers
+  ['mousedown', 'mousemove', 'mouseup', 'contextmenu'].forEach(function (evt) {
+    wrapper.addEventListener(evt, function (e) {
+      e.preventDefault();
+
+      return false;
+    });
+  });
+
+  setStyles(parent, {
+    display: 'none',
+    width: containerSize + 'px',
+    height: containerSize + 'px',
+    position: 'absolute',
+    zIndex: 1,
+    marginLeft: -options.activePadding + 'px',
+    marginTop: -options.activePadding + 'px',
+    userSelect: 'none'
+  });
+
+  canvas.width = containerSize;
+  canvas.height = containerSize;
+
+  function createMenuItems() {
+    removeEles('.cxtmenu-item', parent);
+    var dtheta = 2 * Math.PI / commands.length;
+    var theta1 = Math.PI / 2;
+    var theta2 = theta1 + dtheta;
+
+    for (var i = 0; i < commands.length; i++) {
+      var command = commands[i];
+
+      var midtheta = (theta1 + theta2) / 2;
+      var rx1 = 0.66 * r * Math.cos(midtheta);
+      var ry1 = 0.66 * r * Math.sin(midtheta);
+
+      var item = createElement({ class: 'cxtmenu-item' });
+      setStyles(item, {
+        color: options.itemColor,
+        cursor: 'default',
+        display: 'table',
+        'text-align': 'center',
+        //background: 'red',
+        position: 'absolute',
+        'text-shadow': '-1px -1px 2px ' + options.itemTextShadowColor + ', 1px -1px 2px ' + options.itemTextShadowColor + ', -1px 1px 2px ' + options.itemTextShadowColor + ', 1px 1px 1px ' + options.itemTextShadowColor,
+        left: '50%',
+        top: '50%',
+        'min-height': r * 0.66 + 'px',
+        width: r * 0.66 + 'px',
+        height: r * 0.66 + 'px',
+        marginLeft: rx1 - r * 0.33 + 'px',
+        marginTop: -ry1 - r * 0.33 + 'px'
+      });
+
+      var content = createElement({ class: 'cxtmenu-content' });
+
+      if (command.content instanceof HTMLElement) {
+        content.appendChild(command.content);
+      } else {
+        content.innerHTML = command.content;
+      }
+
+      setStyles(content, {
+        'width': r * 0.66 + 'px',
+        'height': r * 0.66 + 'px',
+        'vertical-align': 'middle',
+        'display': 'table-cell'
+      });
+
+      setStyles(content, command.contentStyle || {});
+
+      if (command.disabled === true || command.enabled === false) {
+        content.setAttribute('class', 'cxtmenu-content cxtmenu-disabled');
+      }
+
+      parent.appendChild(item);
+      item.appendChild(content);
+
+      theta1 += dtheta;
+      theta2 += dtheta;
+    }
+  }
+
+  function queueDrawBg(rspotlight) {
+    redrawQueue.drawBg = [rspotlight];
+  }
+
+  function drawBg(rspotlight) {
+    rspotlight = rspotlight !== undefined ? rspotlight : rs;
+
+    c2d.globalCompositeOperation = 'source-over';
+
+    c2d.clearRect(0, 0, containerSize, containerSize);
+
+    // draw background items
+    c2d.fillStyle = options.fillColor;
+    var dtheta = 2 * Math.PI / commands.length;
+    var theta1 = Math.PI / 2;
+    var theta2 = theta1 + dtheta;
+
+    for (var index = 0; index < commands.length; index++) {
+      var command = commands[index];
+
+      if (command.fillColor) {
+        c2d.fillStyle = command.fillColor;
+      }
+      c2d.beginPath();
+      c2d.moveTo(r + options.activePadding, r + options.activePadding);
+      c2d.arc(r + options.activePadding, r + options.activePadding, r, 2 * Math.PI - theta1, 2 * Math.PI - theta2, true);
+      c2d.closePath();
+      c2d.fill();
+
+      theta1 += dtheta;
+      theta2 += dtheta;
+
+      c2d.fillStyle = options.fillColor;
+    }
+
+    // draw separators between items
+    c2d.globalCompositeOperation = 'destination-out';
+    c2d.strokeStyle = 'white';
+    c2d.lineWidth = options.separatorWidth;
+    theta1 = Math.PI / 2;
+    theta2 = theta1 + dtheta;
+
+    for (var i = 0; i < commands.length; i++) {
+      var rx1 = r * Math.cos(theta1);
+      var ry1 = r * Math.sin(theta1);
+      c2d.beginPath();
+      c2d.moveTo(r + options.activePadding, r + options.activePadding);
+      c2d.lineTo(r + options.activePadding + rx1, r + options.activePadding - ry1);
+      c2d.closePath();
+      c2d.stroke();
+
+      theta1 += dtheta;
+      theta2 += dtheta;
+    }
+
+    c2d.fillStyle = 'white';
+    c2d.globalCompositeOperation = 'destination-out';
+    c2d.beginPath();
+    c2d.arc(r + options.activePadding, r + options.activePadding, rspotlight + options.spotlightPadding, 0, Math.PI * 2, true);
+    c2d.closePath();
+    c2d.fill();
+
+    c2d.globalCompositeOperation = 'source-over';
+  }
+
+  function queueDrawCommands(rx, ry, theta) {
+    redrawQueue.drawCommands = [rx, ry, theta];
+  }
+
+  function drawCommands(rx, ry, theta) {
+    var dtheta = 2 * Math.PI / commands.length;
+    var theta1 = Math.PI / 2;
+    var theta2 = theta1 + dtheta;
+
+    theta1 += dtheta * activeCommandI;
+    theta2 += dtheta * activeCommandI;
+
+    c2d.fillStyle = options.activeFillColor;
+    c2d.strokeStyle = 'black';
+    c2d.lineWidth = 1;
+    c2d.beginPath();
+    c2d.moveTo(r + options.activePadding, r + options.activePadding);
+    c2d.arc(r + options.activePadding, r + options.activePadding, r + options.activePadding, 2 * Math.PI - theta1, 2 * Math.PI - theta2, true);
+    c2d.closePath();
+    c2d.fill();
+
+    c2d.fillStyle = 'white';
+    c2d.globalCompositeOperation = 'destination-out';
+
+    var tx = r + options.activePadding + rx / r * (rs + options.spotlightPadding - options.indicatorSize / 4);
+    var ty = r + options.activePadding + ry / r * (rs + options.spotlightPadding - options.indicatorSize / 4);
+    var rot = Math.PI / 4 - theta;
+
+    c2d.translate(tx, ty);
+    c2d.rotate(rot);
+
+    // clear the indicator
+    c2d.beginPath();
+    c2d.fillRect(-options.indicatorSize / 2, -options.indicatorSize / 2, options.indicatorSize, options.indicatorSize);
+    c2d.closePath();
+    c2d.fill();
+
+    c2d.rotate(-rot);
+    c2d.translate(-tx, -ty);
+
+    // c2d.setTransform( 1, 0, 0, 1, 0, 0 );
+
+    // clear the spotlight
+    c2d.beginPath();
+    c2d.arc(r + options.activePadding, r + options.activePadding, rs + options.spotlightPadding, 0, Math.PI * 2, true);
+    c2d.closePath();
+    c2d.fill();
+
+    c2d.globalCompositeOperation = 'source-over';
+  }
+
+  function updatePixelRatio() {
+    var pxr = getPixelRatio();
+    var w = containerSize;
+    var h = containerSize;
+
+    canvas.width = w * pxr;
+    canvas.height = h * pxr;
+
+    canvas.style.width = w + 'px';
+    canvas.style.height = h + 'px';
+
+    c2d.setTransform(1, 0, 0, 1, 0, 0);
+    c2d.scale(pxr, pxr);
+  }
+
+  var redrawing = true;
+  var redrawQueue = {};
+
+  var raf = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.msRequestAnimationFrame || function (fn) {
+    return setTimeout(fn, 16);
+  };
+
+  var redraw = function redraw() {
+    if (redrawQueue.drawBg) {
+      drawBg.apply(null, redrawQueue.drawBg);
+    }
+
+    if (redrawQueue.drawCommands) {
+      drawCommands.apply(null, redrawQueue.drawCommands);
+    }
+
+    redrawQueue = {};
+
+    if (redrawing) {
+      raf(redraw);
+    }
+  };
+
+  // kick off
+  updatePixelRatio();
+  redraw();
+
+  var ctrx = void 0,
+      ctry = void 0,
+      rs = void 0;
+
+  var bindings = {
+    on: function on(events, selector, fn) {
+
+      var _fn = fn;
+      if (selector === 'core') {
+        _fn = function _fn(e) {
+          if (e.cyTarget === cy || e.target === cy) {
+            // only if event target is directly core
+            return fn.apply(this, [e]);
+          }
+        };
+      }
+
+      data.handlers.push({
+        events: events,
+        selector: selector,
+        fn: _fn
+      });
+
+      if (selector === 'core') {
+        cy.on(events, _fn);
+      } else {
+        cy.on(events, selector, _fn);
+      }
+
+      return this;
+    }
+  };
+
+  function addEventListeners() {
+    var grabbable = void 0;
+    var inGesture = false;
+    var dragHandler = void 0;
+    var zoomEnabled = void 0;
+    var panEnabled = void 0;
+    var boxEnabled = void 0;
+    var gestureStartEvent = void 0;
+
+    var restoreZoom = function restoreZoom() {
+      if (zoomEnabled) {
+        cy.userZoomingEnabled(true);
+      }
+    };
+
+    var restoreGrab = function restoreGrab() {
+      if (grabbable) {
+        target.grabify();
+      }
+    };
+
+    var restorePan = function restorePan() {
+      if (panEnabled) {
+        cy.userPanningEnabled(true);
+      }
+    };
+
+    var restoreBoxSeln = function restoreBoxSeln() {
+      if (boxEnabled) {
+        cy.boxSelectionEnabled(true);
+      }
+    };
+
+    var restoreGestures = function restoreGestures() {
+      restoreGrab();
+      restoreZoom();
+      restorePan();
+      restoreBoxSeln();
+    };
+
+    window.addEventListener('resize', updatePixelRatio);
+
+    bindings.on('resize', function () {
+      updatePixelRatio();
+    }).on(options.openMenuEvents, options.selector, function (e) {
+      target = this; // Remember which node the context menu is for
+      var ele = this;
+      var isCy = this === cy;
+
+      if (inGesture) {
+        parent.style.display = 'none';
+
+        inGesture = false;
+
+        restoreGestures();
+      }
+
+      if (typeof options.commands === 'function') {
+        var res = options.commands(target);
+        if (res.then) {
+          res.then(function (_commands) {
+            commands = _commands;
+            openMenu();
+          });
+        } else {
+          commands = res;
+          openMenu();
+        }
+      } else {
+        commands = options.commands;
+        openMenu();
+      }
+
+      function openMenu() {
+        if (!commands || commands.length === 0) {
+          return;
+        }
+
+        zoomEnabled = cy.userZoomingEnabled();
+        cy.userZoomingEnabled(false);
+
+        panEnabled = cy.userPanningEnabled();
+        cy.userPanningEnabled(false);
+
+        boxEnabled = cy.boxSelectionEnabled();
+        cy.boxSelectionEnabled(false);
+
+        grabbable = target.grabbable && target.grabbable();
+        if (grabbable) {
+          target.ungrabify();
+        }
+
+        var rp = void 0,
+            rw = void 0,
+            rh = void 0;
+        if (!isCy && ele.isNode() && !ele.isParent() && !options.atMouse) {
+          rp = ele.renderedPosition();
+          rw = ele.renderedWidth();
+          rh = ele.renderedHeight();
+        } else {
+          rp = e.renderedPosition || e.cyRenderedPosition;
+          rw = 1;
+          rh = 1;
+        }
+
+        offset = getOffset(container);
+
+        ctrx = rp.x;
+        ctry = rp.y;
+
+        createMenuItems();
+
+        setStyles(parent, {
+          display: 'block',
+          left: rp.x - r + 'px',
+          top: rp.y - r + 'px'
+        });
+
+        rs = Math.max(rw, rh) / 2;
+        rs = Math.max(rs, options.minSpotlightRadius);
+        rs = Math.min(rs, options.maxSpotlightRadius);
+
+        queueDrawBg();
+
+        activeCommandI = undefined;
+
+        inGesture = true;
+        gestureStartEvent = e;
+      }
+    }).on('cxtdrag tapdrag', options.selector, dragHandler = function dragHandler(e) {
+
+      if (!inGesture) {
+        return;
+      }
+
+      var origE = e.originalEvent;
+      var isTouch = origE.touches && origE.touches.length > 0;
+
+      var pageX = (isTouch ? origE.touches[0].pageX : origE.pageX) - window.scrollX;
+      var pageY = (isTouch ? origE.touches[0].pageY : origE.pageY) - window.scrollY;
+
+      activeCommandI = undefined;
+
+      var dx = pageX - offset.left - ctrx;
+      var dy = pageY - offset.top - ctry;
+
+      if (dx === 0) {
+        dx = 0.01;
+      }
+
+      var d = Math.sqrt(dx * dx + dy * dy);
+      var cosTheta = (dy * dy - d * d - dx * dx) / (-2 * d * dx);
+      var theta = Math.acos(cosTheta);
+
+      if (d < rs + options.spotlightPadding) {
+        queueDrawBg();
+        return;
+      }
+
+      queueDrawBg();
+
+      var rx = dx * r / d;
+      var ry = dy * r / d;
+
+      if (dy > 0) {
+        theta = Math.PI + Math.abs(theta - Math.PI);
+      }
+
+      var dtheta = 2 * Math.PI / commands.length;
+      var theta1 = Math.PI / 2;
+      var theta2 = theta1 + dtheta;
+
+      for (var i = 0; i < commands.length; i++) {
+        var command = commands[i];
+
+        var inThisCommand = theta1 <= theta && theta <= theta2 || theta1 <= theta + 2 * Math.PI && theta + 2 * Math.PI <= theta2;
+
+        if (command.disabled === true || command.enabled === false) {
+          inThisCommand = false;
+        }
+
+        if (inThisCommand) {
+          activeCommandI = i;
+          break;
+        }
+
+        theta1 += dtheta;
+        theta2 += dtheta;
+      }
+
+      queueDrawCommands(rx, ry, theta);
+    }).on('tapdrag', dragHandler).on('cxttapend tapend', function () {
+      parent.style.display = 'none';
+
+      if (activeCommandI !== undefined) {
+        var select = commands[activeCommandI].select;
+
+        if (select) {
+          select.apply(target, [target, gestureStartEvent]);
+          activeCommandI = undefined;
+        }
+      }
+
+      inGesture = false;
+
+      restoreGestures();
+    });
+  }
+
+  function removeEventListeners() {
+    var handlers = data.handlers;
+
+    for (var i = 0; i < handlers.length; i++) {
+      var h = handlers[i];
+
+      if (h.selector === 'core') {
+        cy.off(h.events, h.fn);
+      } else {
+        cy.off(h.events, h.selector, h.fn);
+      }
+    }
+
+    window.removeEventListener('resize', updatePixelRatio);
+  }
+
+  function destroyInstance() {
+    redrawing = false;
+
+    removeEventListeners();
+
+    wrapper.remove();
+  }
+
+  addEventListeners();
+
+  return {
+    destroy: function destroy() {
+      destroyInstance();
+    }
+  };
+};
+
+module.exports = cxtmenu;
+
+/***/ }),
+/* 1 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+// Simple, internal Object.assign() polyfill for options objects etc.
+
+module.exports = Object.assign != null ? Object.assign.bind(Object) : function (tgt) {
+  for (var _len = arguments.length, srcs = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
+    srcs[_key - 1] = arguments[_key];
+  }
+
+  srcs.filter(function (src) {
+    return src != null;
+  }).forEach(function (src) {
+    Object.keys(src).forEach(function (k) {
+      return tgt[k] = src[k];
+    });
+  });
+
+  return tgt;
+};
+
+/***/ }),
+/* 2 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+var defaults = {
+  menuRadius: 100, // the radius of the circular menu in pixels
+  selector: 'node', // elements matching this Cytoscape.js selector will trigger cxtmenus
+  commands: [// an array of commands to list in the menu or a function that returns the array
+    /*
+    { // example command
+      fillColor: 'rgba(200, 200, 200, 0.75)', // optional: custom background color for item
+      content: 'a command name' // html/text content to be displayed in the menu
+      contentStyle: {}, // css key:value pairs to set the command's css in js if you want
+      select: function(ele){ // a function to execute when the command is selected
+        console.log( ele.id() ) // `ele` holds the reference to the active element
+      },
+      enabled: true // whether the command is selectable
+    }
+    */
+  ], // function( ele ){ return [ /*...*/ ] }, // example function for commands
+  fillColor: 'rgba(0, 0, 0, 0.75)', // the background colour of the menu
+  activeFillColor: 'rgba(1, 105, 217, 0.75)', // the colour used to indicate the selected command
+  activePadding: 20, // additional size in pixels for the active command
+  indicatorSize: 24, // the size in pixels of the pointer to the active command
+  separatorWidth: 3, // the empty spacing in pixels between successive commands
+  spotlightPadding: 4, // extra spacing in pixels between the element and the spotlight
+  minSpotlightRadius: 24, // the minimum radius in pixels of the spotlight
+  maxSpotlightRadius: 38, // the maximum radius in pixels of the spotlight
+  openMenuEvents: 'cxttapstart taphold', // space-separated cytoscape events that will open the menu; only `cxttapstart` and/or `taphold` work here
+  itemColor: 'white', // the colour of text in the command's content
+  itemTextShadowColor: 'transparent', // the text shadow colour of the command's content
+  zIndex: 9999, // the z-index of the ui div
+  atMouse: false // draw menu at mouse position
+};
+
+module.exports = defaults;
+
+/***/ }),
+/* 3 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+var removeEles = function removeEles(query) {
+  var ancestor = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : document;
+
+  var els = ancestor.querySelectorAll(query);
+
+  for (var i = 0; i < els.length; i++) {
+    var el = els[i];
+
+    el.parentNode.removeChild(el);
+  }
+};
+
+var setStyles = function setStyles(el, style) {
+  var props = Object.keys(style);
+
+  for (var i = 0, l = props.length; i < l; i++) {
+    el.style[props[i]] = style[props[i]];
+  }
+};
+
+var createElement = function createElement(options) {
+  options = options || {};
+
+  var el = document.createElement(options.tag || 'div');
+
+  el.className = options.class || '';
+
+  if (options.style) {
+    setStyles(el, options.style);
+  }
+
+  return el;
+};
+
+var getPixelRatio = function getPixelRatio() {
+  return window.devicePixelRatio || 1;
+};
+
+var getOffset = function getOffset(el) {
+  var offset = el.getBoundingClientRect();
+
+  return {
+    left: offset.left + document.body.scrollLeft + parseFloat(getComputedStyle(document.body)['padding-left']) + parseFloat(getComputedStyle(document.body)['border-left-width']),
+    top: offset.top + document.body.scrollTop + parseFloat(getComputedStyle(document.body)['padding-top']) + parseFloat(getComputedStyle(document.body)['border-top-width'])
+  };
+};
+
+module.exports = { removeEles: removeEles, setStyles: setStyles, createElement: createElement, getPixelRatio: getPixelRatio, getOffset: getOffset };
+
+/***/ }),
+/* 4 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+var cxtmenu = __webpack_require__(0);
+
+// registers the extension on a cytoscape lib ref
+var register = function register(cytoscape) {
+  if (!cytoscape) {
+    return;
+  } // can't register if cytoscape unspecified
+
+  cytoscape('core', 'cxtmenu', cxtmenu); // register with cytoscape.js
+};
+
+if (typeof cytoscape !== 'undefined') {
+  // expose to global cytoscape (i.e. window.cytoscape)
+  register(cytoscape);
+}
+
+module.exports = register;
+
+/***/ })
+/******/ ]);
+});
\ No newline at end of file
diff --git a/01_Code/physical_computing_interface/lib/cytoscape-cxtmenu.js b/01_Code/physical_computing_interface/lib/cytoscape-cxtmenu.js
new file mode 100644
index 0000000000000000000000000000000000000000..4294ea67bfd92cadb71705056492661cfbb8da86
--- /dev/null
+++ b/01_Code/physical_computing_interface/lib/cytoscape-cxtmenu.js
@@ -0,0 +1,795 @@
+(function webpackUniversalModuleDefinition(root, factory) {
+	if(typeof exports === 'object' && typeof module === 'object')
+		module.exports = factory();
+	else if(typeof define === 'function' && define.amd)
+		define([], factory);
+	else if(typeof exports === 'object')
+		exports["cytoscapeCxtmenu"] = factory();
+	else
+		root["cytoscapeCxtmenu"] = factory();
+})(this, function() {
+return /******/ (function(modules) { // webpackBootstrap
+/******/ 	// The module cache
+/******/ 	var installedModules = {};
+/******/
+/******/ 	// The require function
+/******/ 	function __webpack_require__(moduleId) {
+/******/
+/******/ 		// Check if module is in cache
+/******/ 		if(installedModules[moduleId]) {
+/******/ 			return installedModules[moduleId].exports;
+/******/ 		}
+/******/ 		// Create a new module (and put it into the cache)
+/******/ 		var module = installedModules[moduleId] = {
+/******/ 			i: moduleId,
+/******/ 			l: false,
+/******/ 			exports: {}
+/******/ 		};
+/******/
+/******/ 		// Execute the module function
+/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
+/******/
+/******/ 		// Flag the module as loaded
+/******/ 		module.l = true;
+/******/
+/******/ 		// Return the exports of the module
+/******/ 		return module.exports;
+/******/ 	}
+/******/
+/******/
+/******/ 	// expose the modules object (__webpack_modules__)
+/******/ 	__webpack_require__.m = modules;
+/******/
+/******/ 	// expose the module cache
+/******/ 	__webpack_require__.c = installedModules;
+/******/
+/******/ 	// identity function for calling harmony imports with the correct context
+/******/ 	__webpack_require__.i = function(value) { return value; };
+/******/
+/******/ 	// define getter function for harmony exports
+/******/ 	__webpack_require__.d = function(exports, name, getter) {
+/******/ 		if(!__webpack_require__.o(exports, name)) {
+/******/ 			Object.defineProperty(exports, name, {
+/******/ 				configurable: false,
+/******/ 				enumerable: true,
+/******/ 				get: getter
+/******/ 			});
+/******/ 		}
+/******/ 	};
+/******/
+/******/ 	// getDefaultExport function for compatibility with non-harmony modules
+/******/ 	__webpack_require__.n = function(module) {
+/******/ 		var getter = module && module.__esModule ?
+/******/ 			function getDefault() { return module['default']; } :
+/******/ 			function getModuleExports() { return module; };
+/******/ 		__webpack_require__.d(getter, 'a', getter);
+/******/ 		return getter;
+/******/ 	};
+/******/
+/******/ 	// Object.prototype.hasOwnProperty.call
+/******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
+/******/
+/******/ 	// __webpack_public_path__
+/******/ 	__webpack_require__.p = "";
+/******/
+/******/ 	// Load entry module and return exports
+/******/ 	return __webpack_require__(__webpack_require__.s = 4);
+/******/ })
+/************************************************************************/
+/******/ ([
+/* 0 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+var defaults = __webpack_require__(2);
+var assign = __webpack_require__(1);
+
+var _require = __webpack_require__(3),
+    removeEles = _require.removeEles,
+    setStyles = _require.setStyles,
+    createElement = _require.createElement,
+    getPixelRatio = _require.getPixelRatio,
+    getOffset = _require.getOffset;
+
+var cxtmenu = function cxtmenu(params) {
+  var options = assign({}, defaults, params);
+  var cy = this;
+  var container = cy.container();
+  var target = void 0;
+
+  var data = {
+    options: options,
+    handlers: [],
+    container: createElement({ class: 'cxtmenu' })
+  };
+
+  var wrapper = data.container;
+  var parent = createElement();
+  var canvas = createElement({ tag: 'canvas' });
+  var commands = [];
+  var c2d = canvas.getContext('2d');
+  var r = options.menuRadius;
+  var containerSize = (r + options.activePadding) * 2;
+  var activeCommandI = void 0;
+  var offset = void 0;
+
+  container.insertBefore(wrapper, container.firstChild);
+  wrapper.appendChild(parent);
+  parent.appendChild(canvas);
+
+  setStyles(wrapper, {
+    position: 'absolute',
+    zIndex: options.zIndex,
+    userSelect: 'none',
+    pointerEvents: 'none' // prevent events on menu in modern browsers
+  });
+
+  // prevent events on menu in legacy browsers
+  ['mousedown', 'mousemove', 'mouseup', 'contextmenu'].forEach(function (evt) {
+    wrapper.addEventListener(evt, function (e) {
+      e.preventDefault();
+
+      return false;
+    });
+  });
+
+  setStyles(parent, {
+    display: 'none',
+    width: containerSize + 'px',
+    height: containerSize + 'px',
+    position: 'absolute',
+    zIndex: 1,
+    marginLeft: -options.activePadding + 'px',
+    marginTop: -options.activePadding + 'px',
+    userSelect: 'none'
+  });
+
+  canvas.width = containerSize;
+  canvas.height = containerSize;
+
+  function createMenuItems() {
+    removeEles('.cxtmenu-item', parent);
+    var dtheta = 2 * Math.PI / commands.length;
+    var theta1 = Math.PI / 2;
+    var theta2 = theta1 + dtheta;
+
+    for (var i = 0; i < commands.length; i++) {
+      var command = commands[i];
+
+      var midtheta = (theta1 + theta2) / 2;
+      var rx1 = 0.66 * r * Math.cos(midtheta);
+      var ry1 = 0.66 * r * Math.sin(midtheta);
+
+      var item = createElement({ class: 'cxtmenu-item' });
+      setStyles(item, {
+        color: options.itemColor,
+        cursor: 'default',
+        display: 'table',
+        'text-align': 'center',
+        //background: 'red',
+        position: 'absolute',
+        'text-shadow': '-1px -1px 2px ' + options.itemTextShadowColor + ', 1px -1px 2px ' + options.itemTextShadowColor + ', -1px 1px 2px ' + options.itemTextShadowColor + ', 1px 1px 1px ' + options.itemTextShadowColor,
+        left: '50%',
+        top: '50%',
+        'min-height': r * 0.66 + 'px',
+        width: r * 0.66 + 'px',
+        height: r * 0.66 + 'px',
+        marginLeft: rx1 - r * 0.33 + 'px',
+        marginTop: -ry1 - r * 0.33 + 'px'
+      });
+
+      var content = createElement({ class: 'cxtmenu-content' });
+
+      if (command.content instanceof HTMLElement) {
+        content.appendChild(command.content);
+      } else {
+        content.innerHTML = command.content;
+      }
+
+      setStyles(content, {
+        'width': r * 0.66 + 'px',
+        'height': r * 0.66 + 'px',
+        'vertical-align': 'middle',
+        'display': 'table-cell'
+      });
+
+      setStyles(content, command.contentStyle || {});
+
+      if (command.disabled === true || command.enabled === false) {
+        content.setAttribute('class', 'cxtmenu-content cxtmenu-disabled');
+      }
+
+      parent.appendChild(item);
+      item.appendChild(content);
+
+      theta1 += dtheta;
+      theta2 += dtheta;
+    }
+  }
+
+  function queueDrawBg(rspotlight) {
+    redrawQueue.drawBg = [rspotlight];
+  }
+
+  function drawBg(rspotlight) {
+    rspotlight = rspotlight !== undefined ? rspotlight : rs;
+
+    c2d.globalCompositeOperation = 'source-over';
+
+    c2d.clearRect(0, 0, containerSize, containerSize);
+
+    // draw background items
+    c2d.fillStyle = options.fillColor;
+    var dtheta = 2 * Math.PI / commands.length;
+    var theta1 = Math.PI / 2;
+    var theta2 = theta1 + dtheta;
+
+    for (var index = 0; index < commands.length; index++) {
+      var command = commands[index];
+
+      if (command.fillColor) {
+        c2d.fillStyle = command.fillColor;
+      }
+      c2d.beginPath();
+      c2d.moveTo(r + options.activePadding, r + options.activePadding);
+      c2d.arc(r + options.activePadding, r + options.activePadding, r, 2 * Math.PI - theta1, 2 * Math.PI - theta2, true);
+      c2d.closePath();
+      c2d.fill();
+
+      theta1 += dtheta;
+      theta2 += dtheta;
+
+      c2d.fillStyle = options.fillColor;
+    }
+
+    // draw separators between items
+    c2d.globalCompositeOperation = 'destination-out';
+    c2d.strokeStyle = 'white';
+    c2d.lineWidth = options.separatorWidth;
+    theta1 = Math.PI / 2;
+    theta2 = theta1 + dtheta;
+
+    for (var i = 0; i < commands.length; i++) {
+      var rx1 = r * Math.cos(theta1);
+      var ry1 = r * Math.sin(theta1);
+      c2d.beginPath();
+      c2d.moveTo(r + options.activePadding, r + options.activePadding);
+      c2d.lineTo(r + options.activePadding + rx1, r + options.activePadding - ry1);
+      c2d.closePath();
+      c2d.stroke();
+
+      theta1 += dtheta;
+      theta2 += dtheta;
+    }
+
+    c2d.fillStyle = 'white';
+    c2d.globalCompositeOperation = 'destination-out';
+    c2d.beginPath();
+    c2d.arc(r + options.activePadding, r + options.activePadding, rspotlight + options.spotlightPadding, 0, Math.PI * 2, true);
+    c2d.closePath();
+    c2d.fill();
+
+    c2d.globalCompositeOperation = 'source-over';
+  }
+
+  function queueDrawCommands(rx, ry, theta) {
+    redrawQueue.drawCommands = [rx, ry, theta];
+  }
+
+  function drawCommands(rx, ry, theta) {
+    var dtheta = 2 * Math.PI / commands.length;
+    var theta1 = Math.PI / 2;
+    var theta2 = theta1 + dtheta;
+
+    theta1 += dtheta * activeCommandI;
+    theta2 += dtheta * activeCommandI;
+
+    c2d.fillStyle = options.activeFillColor;
+    c2d.strokeStyle = 'black';
+    c2d.lineWidth = 1;
+    c2d.beginPath();
+    c2d.moveTo(r + options.activePadding, r + options.activePadding);
+    c2d.arc(r + options.activePadding, r + options.activePadding, r + options.activePadding, 2 * Math.PI - theta1, 2 * Math.PI - theta2, true);
+    c2d.closePath();
+    c2d.fill();
+
+    c2d.fillStyle = 'white';
+    c2d.globalCompositeOperation = 'destination-out';
+
+    var tx = r + options.activePadding + rx / r * (rs + options.spotlightPadding - options.indicatorSize / 4);
+    var ty = r + options.activePadding + ry / r * (rs + options.spotlightPadding - options.indicatorSize / 4);
+    var rot = Math.PI / 4 - theta;
+
+    c2d.translate(tx, ty);
+    c2d.rotate(rot);
+
+    // clear the indicator
+    c2d.beginPath();
+    c2d.fillRect(-options.indicatorSize / 2, -options.indicatorSize / 2, options.indicatorSize, options.indicatorSize);
+    c2d.closePath();
+    c2d.fill();
+
+    c2d.rotate(-rot);
+    c2d.translate(-tx, -ty);
+
+    // c2d.setTransform( 1, 0, 0, 1, 0, 0 );
+
+    // clear the spotlight
+    c2d.beginPath();
+    c2d.arc(r + options.activePadding, r + options.activePadding, rs + options.spotlightPadding, 0, Math.PI * 2, true);
+    c2d.closePath();
+    c2d.fill();
+
+    c2d.globalCompositeOperation = 'source-over';
+  }
+
+  function updatePixelRatio() {
+    var pxr = getPixelRatio();
+    var w = containerSize;
+    var h = containerSize;
+
+    canvas.width = w * pxr;
+    canvas.height = h * pxr;
+
+    canvas.style.width = w + 'px';
+    canvas.style.height = h + 'px';
+
+    c2d.setTransform(1, 0, 0, 1, 0, 0);
+    c2d.scale(pxr, pxr);
+  }
+
+  var redrawing = true;
+  var redrawQueue = {};
+
+  var raf = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.msRequestAnimationFrame || function (fn) {
+    return setTimeout(fn, 16);
+  };
+
+  var redraw = function redraw() {
+    if (redrawQueue.drawBg) {
+      drawBg.apply(null, redrawQueue.drawBg);
+    }
+
+    if (redrawQueue.drawCommands) {
+      drawCommands.apply(null, redrawQueue.drawCommands);
+    }
+
+    redrawQueue = {};
+
+    if (redrawing) {
+      raf(redraw);
+    }
+  };
+
+  // kick off
+  updatePixelRatio();
+  redraw();
+
+  var ctrx = void 0,
+      ctry = void 0,
+      rs = void 0;
+
+  var bindings = {
+    on: function on(events, selector, fn) {
+
+      var _fn = fn;
+      if (selector === 'core') {
+        _fn = function _fn(e) {
+          if (e.cyTarget === cy || e.target === cy) {
+            // only if event target is directly core
+            return fn.apply(this, [e]);
+          }
+        };
+      }
+
+      data.handlers.push({
+        events: events,
+        selector: selector,
+        fn: _fn
+      });
+
+      if (selector === 'core') {
+        cy.on(events, _fn);
+      } else {
+        cy.on(events, selector, _fn);
+      }
+
+      return this;
+    }
+  };
+
+  function addEventListeners() {
+    var grabbable = void 0;
+    var inGesture = false;
+    var dragHandler = void 0;
+    var zoomEnabled = void 0;
+    var panEnabled = void 0;
+    var boxEnabled = void 0;
+    var gestureStartEvent = void 0;
+
+    var restoreZoom = function restoreZoom() {
+      if (zoomEnabled) {
+        cy.userZoomingEnabled(true);
+      }
+    };
+
+    var restoreGrab = function restoreGrab() {
+      if (grabbable) {
+        target.grabify();
+      }
+    };
+
+    var restorePan = function restorePan() {
+      if (panEnabled) {
+        cy.userPanningEnabled(true);
+      }
+    };
+
+    var restoreBoxSeln = function restoreBoxSeln() {
+      if (boxEnabled) {
+        cy.boxSelectionEnabled(true);
+      }
+    };
+
+    var restoreGestures = function restoreGestures() {
+      restoreGrab();
+      restoreZoom();
+      restorePan();
+      restoreBoxSeln();
+    };
+
+    window.addEventListener('resize', updatePixelRatio);
+
+    bindings.on('resize', function () {
+      updatePixelRatio();
+    }).on(options.openMenuEvents, options.selector, function (e) {
+      target = this; // Remember which node the context menu is for
+      var ele = this;
+      var isCy = this === cy;
+
+      if (inGesture) {
+        parent.style.display = 'none';
+
+        inGesture = false;
+
+        restoreGestures();
+      }
+
+      if (typeof options.commands === 'function') {
+        var res = options.commands(target);
+        if (res.then) {
+          res.then(function (_commands) {
+            commands = _commands;
+            openMenu();
+          });
+        } else {
+          commands = res;
+          openMenu();
+        }
+      } else {
+        commands = options.commands;
+        openMenu();
+      }
+
+      function openMenu() {
+        if (!commands || commands.length === 0) {
+          return;
+        }
+
+        zoomEnabled = cy.userZoomingEnabled();
+        cy.userZoomingEnabled(false);
+
+        panEnabled = cy.userPanningEnabled();
+        cy.userPanningEnabled(false);
+
+        boxEnabled = cy.boxSelectionEnabled();
+        cy.boxSelectionEnabled(false);
+
+        grabbable = target.grabbable && target.grabbable();
+        if (grabbable) {
+          target.ungrabify();
+        }
+
+        var rp = void 0,
+            rw = void 0,
+            rh = void 0;
+        if (!isCy && ele.isNode() && !ele.isParent() && !options.atMouse) {
+          rp = ele.renderedPosition();
+          rw = ele.renderedWidth();
+          rh = ele.renderedHeight();
+        } else {
+          rp = e.renderedPosition || e.cyRenderedPosition;
+          rw = 1;
+          rh = 1;
+        }
+
+        offset = getOffset(container);
+
+        ctrx = rp.x;
+        ctry = rp.y;
+
+        createMenuItems();
+
+        setStyles(parent, {
+          display: 'block',
+          left: rp.x - r + 'px',
+          top: rp.y - r + 'px'
+        });
+
+        rs = Math.max(rw, rh) / 2;
+        rs = Math.max(rs, options.minSpotlightRadius);
+        rs = Math.min(rs, options.maxSpotlightRadius);
+
+        queueDrawBg();
+
+        activeCommandI = undefined;
+
+        inGesture = true;
+        gestureStartEvent = e;
+      }
+    }).on('cxtdrag tapdrag', options.selector, dragHandler = function dragHandler(e) {
+
+      if (!inGesture) {
+        return;
+      }
+
+      var origE = e.originalEvent;
+      var isTouch = origE.touches && origE.touches.length > 0;
+
+      var pageX = (isTouch ? origE.touches[0].pageX : origE.pageX) - window.scrollX;
+      var pageY = (isTouch ? origE.touches[0].pageY : origE.pageY) - window.scrollY;
+
+      activeCommandI = undefined;
+
+      var dx = pageX - offset.left - ctrx;
+      var dy = pageY - offset.top - ctry;
+
+      if (dx === 0) {
+        dx = 0.01;
+      }
+
+      var d = Math.sqrt(dx * dx + dy * dy);
+      var cosTheta = (dy * dy - d * d - dx * dx) / (-2 * d * dx);
+      var theta = Math.acos(cosTheta);
+
+      if (d < rs + options.spotlightPadding) {
+        queueDrawBg();
+        return;
+      }
+
+      queueDrawBg();
+
+      var rx = dx * r / d;
+      var ry = dy * r / d;
+
+      if (dy > 0) {
+        theta = Math.PI + Math.abs(theta - Math.PI);
+      }
+
+      var dtheta = 2 * Math.PI / commands.length;
+      var theta1 = Math.PI / 2;
+      var theta2 = theta1 + dtheta;
+
+      for (var i = 0; i < commands.length; i++) {
+        var command = commands[i];
+
+        var inThisCommand = theta1 <= theta && theta <= theta2 || theta1 <= theta + 2 * Math.PI && theta + 2 * Math.PI <= theta2;
+
+        if (command.disabled === true || command.enabled === false) {
+          inThisCommand = false;
+        }
+
+        if (inThisCommand) {
+          activeCommandI = i;
+          break;
+        }
+
+        theta1 += dtheta;
+        theta2 += dtheta;
+      }
+
+      queueDrawCommands(rx, ry, theta);
+    }).on('tapdrag', dragHandler).on('cxttapend tapend', function () {
+      parent.style.display = 'none';
+
+      if (activeCommandI !== undefined) {
+        var select = commands[activeCommandI].select;
+
+        if (select) {
+          select.apply(target, [target, gestureStartEvent]);
+          activeCommandI = undefined;
+        }
+      }
+
+      inGesture = false;
+
+      restoreGestures();
+    });
+  }
+
+  function removeEventListeners() {
+    var handlers = data.handlers;
+
+    for (var i = 0; i < handlers.length; i++) {
+      var h = handlers[i];
+
+      if (h.selector === 'core') {
+        cy.off(h.events, h.fn);
+      } else {
+        cy.off(h.events, h.selector, h.fn);
+      }
+    }
+
+    window.removeEventListener('resize', updatePixelRatio);
+  }
+
+  function destroyInstance() {
+    redrawing = false;
+
+    removeEventListeners();
+
+    wrapper.remove();
+  }
+
+  addEventListeners();
+
+  return {
+    destroy: function destroy() {
+      destroyInstance();
+    }
+  };
+};
+
+module.exports = cxtmenu;
+
+/***/ }),
+/* 1 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+// Simple, internal Object.assign() polyfill for options objects etc.
+
+module.exports = Object.assign != null ? Object.assign.bind(Object) : function (tgt) {
+  for (var _len = arguments.length, srcs = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
+    srcs[_key - 1] = arguments[_key];
+  }
+
+  srcs.filter(function (src) {
+    return src != null;
+  }).forEach(function (src) {
+    Object.keys(src).forEach(function (k) {
+      return tgt[k] = src[k];
+    });
+  });
+
+  return tgt;
+};
+
+/***/ }),
+/* 2 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+var defaults = {
+  menuRadius: 100, // the radius of the circular menu in pixels
+  selector: 'node', // elements matching this Cytoscape.js selector will trigger cxtmenus
+  commands: [// an array of commands to list in the menu or a function that returns the array
+    /*
+    { // example command
+      fillColor: 'rgba(200, 200, 200, 0.75)', // optional: custom background color for item
+      content: 'a command name' // html/text content to be displayed in the menu
+      contentStyle: {}, // css key:value pairs to set the command's css in js if you want
+      select: function(ele){ // a function to execute when the command is selected
+        console.log( ele.id() ) // `ele` holds the reference to the active element
+      },
+      enabled: true // whether the command is selectable
+    }
+    */
+  ], // function( ele ){ return [ /*...*/ ] }, // example function for commands
+  fillColor: 'rgba(0, 0, 0, 0.75)', // the background colour of the menu
+  activeFillColor: 'rgba(1, 105, 217, 0.75)', // the colour used to indicate the selected command
+  activePadding: 20, // additional size in pixels for the active command
+  indicatorSize: 24, // the size in pixels of the pointer to the active command
+  separatorWidth: 3, // the empty spacing in pixels between successive commands
+  spotlightPadding: 4, // extra spacing in pixels between the element and the spotlight
+  minSpotlightRadius: 24, // the minimum radius in pixels of the spotlight
+  maxSpotlightRadius: 38, // the maximum radius in pixels of the spotlight
+  openMenuEvents: 'cxttapstart taphold', // space-separated cytoscape events that will open the menu; only `cxttapstart` and/or `taphold` work here
+  itemColor: 'white', // the colour of text in the command's content
+  itemTextShadowColor: 'transparent', // the text shadow colour of the command's content
+  zIndex: 9999, // the z-index of the ui div
+  atMouse: false // draw menu at mouse position
+};
+
+module.exports = defaults;
+
+/***/ }),
+/* 3 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+var removeEles = function removeEles(query) {
+  var ancestor = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : document;
+
+  var els = ancestor.querySelectorAll(query);
+
+  for (var i = 0; i < els.length; i++) {
+    var el = els[i];
+
+    el.parentNode.removeChild(el);
+  }
+};
+
+var setStyles = function setStyles(el, style) {
+  var props = Object.keys(style);
+
+  for (var i = 0, l = props.length; i < l; i++) {
+    el.style[props[i]] = style[props[i]];
+  }
+};
+
+var createElement = function createElement(options) {
+  options = options || {};
+
+  var el = document.createElement(options.tag || 'div');
+
+  el.className = options.class || '';
+
+  if (options.style) {
+    setStyles(el, options.style);
+  }
+
+  return el;
+};
+
+var getPixelRatio = function getPixelRatio() {
+  return window.devicePixelRatio || 1;
+};
+
+var getOffset = function getOffset(el) {
+  var offset = el.getBoundingClientRect();
+
+  return {
+    left: offset.left + document.body.scrollLeft + parseFloat(getComputedStyle(document.body)['padding-left']) + parseFloat(getComputedStyle(document.body)['border-left-width']),
+    top: offset.top + document.body.scrollTop + parseFloat(getComputedStyle(document.body)['padding-top']) + parseFloat(getComputedStyle(document.body)['border-top-width'])
+  };
+};
+
+module.exports = { removeEles: removeEles, setStyles: setStyles, createElement: createElement, getPixelRatio: getPixelRatio, getOffset: getOffset };
+
+/***/ }),
+/* 4 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+var cxtmenu = __webpack_require__(0);
+
+// registers the extension on a cytoscape lib ref
+var register = function register(cytoscape) {
+  if (!cytoscape) {
+    return;
+  } // can't register if cytoscape unspecified
+
+  cytoscape('core', 'cxtmenu', cxtmenu); // register with cytoscape.js
+};
+
+if (typeof cytoscape !== 'undefined') {
+  // expose to global cytoscape (i.e. window.cytoscape)
+  register(cytoscape);
+}
+
+module.exports = register;
+
+/***/ })
+/******/ ]);
+});
\ No newline at end of file
diff --git a/01_Code/physical_computing_interface/threejs/grid.js b/01_Code/physical_computing_interface/threejs/grid.js
index 2b93828e34d0e739270eaed39f82b74e16fa417b..84d94d488fa03121a6a0334f7d7c05d45b570294 100644
--- a/01_Code/physical_computing_interface/threejs/grid.js
+++ b/01_Code/physical_computing_interface/threejs/grid.js
@@ -29,6 +29,79 @@ function threejs(GLOBALS,utils,containerName,container1Name){
     this.grid=GLOBALS.grid;
     this.cubicGrid=GLOBALS.gridPresets.Cubic;
     this.occupancy=GLOBALS.occupancy;
+    this.selectedNodeID;
+    this.menu;
+
+    var color1= "#ffffff"; //white
+    var color11= "#ffffff8c"; //white transparent
+    var color2= "#020227";  //dark blue
+    var color3= "#1c5c61"; //teal
+    var color33= "#1c5c618c"; //teal transparent
+    var color4= "#fa6e70"; //red/orange
+    var color44= "#fa6e708c"; //red/orange
+    var color5="#380152"; //purple
+    var color6="#696767"; //grey
+    
+    this.cy =  cytoscape({
+        container: document.getElementById(container1Name),
+    
+        ready: function(){
+        },
+        style: [ 
+        ],
+    
+        elements: {
+            nodes: [
+            ],
+            edges: [
+                ]
+        },
+          
+    });
+
+    this.cxtdefaults = {
+        selector: 'core',//'node, edge',
+        menuRadius: 50,
+        fillColor: color11, // the background colour of the menu
+        activeFillColor: color44, // the colour used to indicate the selected command
+        // activePadding: 20, // additional size in pixels for the active command
+        indicatorSize: 20, // the size in pixels of the pointer to the active command
+        // separatorWidth: 3, // the empty spacing in pixels between successive commands
+        spotlightPadding: 4, // extra spacing in pixels between the element and the spotlight
+        minSpotlightRadius: 15, // the minimum radius in pixels of the spotlight
+        maxSpotlightRadius: 10, // the maximum radius in pixels of the spotlight
+        // openMenuEvents: 'cxttapstart taphold', // space-separated cytoscape events that will open the menu; only `cxttapstart` and/or `taphold` work here
+        itemColor: color6, // the colour of text in the command's content
+        // itemTextShadowColor: 'transparent', // the text shadow colour of the command's content
+        zIndex: 2*9999, // the z-index of the ui div
+        // atMouse: false // draw menu at mouse position
+        commands: [
+            {
+                content: '<span class="fa fa-play fa-1x"></span>',
+                select: function(){
+                    console.log( "play");//todo move to globals
+                }
+            },
+
+            {
+                content: '<span class="fa fa-trash fa-1x"></span>',
+                select: function(){
+                    GLOBALS.removeNode(three.selectedNodeID.x,three.selectedNodeID.y,three.selectedNodeID.z);
+                    
+                },
+                // enabled: true
+            },
+
+            {
+                // content: 'Select',
+                content: '<span class="fa fa-mouse-pointer fa-1x"></span>',
+                select: function(){
+                    console.log( "Select");//todo move to globals
+                }
+            }
+        ]
+    };
+
     // this.init();
 }
 
@@ -67,11 +140,7 @@ threejs.prototype.init=function() {
 
     this.raycaster = new THREE.Raycaster();
     this.mouse = new THREE.Vector2();
-    // var geometry = new THREE.PlaneBufferGeometry( 1000, 1000 );
-    // geometry.rotateX( - Math.PI / 2 );
-    // plane = new THREE.Mesh( geometry, new THREE.MeshBasicMaterial( { visible: false } ) );
-    // scene.add( plane );
-    // objects.push( plane );
+    
     
     this.helperPosition = new Float32Array( 1 * 3 );
     var c = new THREE.Color( this.color1 );
@@ -110,7 +179,7 @@ threejs.prototype.init=function() {
     this.container.addEventListener( 'mousemove', onDocumentMouseMoveThree, false );
     this.container.addEventListener( 'mousedown', onDocumentMouseDownThree, false );
     // window.addEventListener( 'keydown', onDocumentKeyDown, false );
-    // window.addEventListener( 'keyup', onDocumentKeyUp, false );
+    window.addEventListener( 'mouseup', onDocumentKeyUpThree, false );
     //
     window.addEventListener( 'mouseup', onWindowResizeThree, false );
 
@@ -216,23 +285,6 @@ threejs.prototype.buildHelperSnap=function(grid,x,y,z){
     this.plane.position.y=p_y;
     this.plane.position.z=p_z;
 
-    // plane.scale.x=grid.xScale*0.95;
-    // plane.scale.z=grid.yScale*0.95;
-
-    // plane.position.x=0.0;
-    // plane.position.y=0.0;
-    // plane.position.z=0.0;
-
-    // plane.position.x+=grid.voxelSpacing*grid.xScale*(x );
-    // plane.position.z+=grid.voxelSpacing*grid.yScale*(y );
-    // plane.position.y+=grid.voxelSpacing*grid.zScale*(z );
-
-    // if(y%2!=0){
-    //     plane.position.x+=grid.voxelSpacing*grid.xScale*(grid.xLineOffset);
-    //     plane.position.z+=grid.voxelSpacing*grid.yScale*(grid.yLineOffset);
-
-    // }
-    // plane.position.y-=grid.voxelSpacing*grid.zScale/2.0;
     this.plane.name="p["+parseInt(x) +","+parseInt(y) +","+parseInt(z) +"]";
     
     this.helperPosition[0]=this.plane.position.x;
@@ -285,7 +337,11 @@ threejs.prototype.helperAt=function(x,y,z){
 };
 
 threejs.prototype.updateHelperMeshesAfterRemove=function(grid,x,y,z){
-    this.buildHelperSnap(grid,x,y,z);
+    // this.buildHelperSnap(grid,x,y,z);
+    this.occupancy[x][y][z]=false;
+    if(z<1){
+        this.buildHelperSnap(grid,x,y,z);
+    }
     var list=this.utils.getNeighborsList(grid,x,y,z); //TODO ENCLOSE
     
     for(var i=0;i<list.length;i++){
@@ -295,14 +351,20 @@ threejs.prototype.updateHelperMeshesAfterRemove=function(grid,x,y,z){
             // buildHelperSnap(grid,x1,y1,z1);
             var name='[' +x1+"," +y1+","+z1+']';
             var object = this.scene.getObjectByName( 'p'+name );
-            this.scene.remove( object );
-            this.objects.splice( this.objects.indexOf( object ), 1 );
-            object = this.scene.getObjectByName( 's'+name );
-            this.scene.remove( object );
-            this.occupancyHelper[x1][y1][z1]=false;
+            if(object!==undefined){
+                this.scene.remove( object );
+                this.objects.splice( this.objects.indexOf( object ), 1 );
+
+                object = this.scene.getObjectByName( 's'+name );
+                this.scene.remove( object );
+                this.occupancyHelper[x1][y1][z1]=false;
+
+            }
+            
+
         }
     }
-    var list=this.utils.getNeighborsList(this.cubicGrid,x,y,z); 
+    list=this.utils.getNeighborsList(this.cubicGrid,x,y,z); 
     for(var i=0;i<list.length;i++){
         var x1,y1,z1;
         [x1,y1,z1]=list[i];
@@ -391,13 +453,27 @@ function onDocumentMouseDownThree( event ) {
                 var intersect = intersects1[ 0 ];
                 
                 var obj=utils.getXYZfromName(intersect.object.name);
-                GLOBALS.removeNode(obj.x,obj.y,obj.z);
+                three.selectedNodeID=obj;
+                three.menu=three.cy.cxtmenu( three.cxtdefaults );
+                
+                
+                
         }
         three.render();
     }
     
 }
 
+function onDocumentKeyUpThree(event){
+    if(event.which==3){
+
+        if(three.menu!==undefined){
+            three.menu.destroy();
+        }
+    }
+
+}
+
 document.addEventListener('removeNode', function (e) { 
     var name='[' +e.detail.x +"," +e.detail.y+","+e.detail.z+']';
     var object = three.scene.getObjectByName( name );
@@ -415,6 +491,7 @@ document.addEventListener('addNode', function (e) {
     var object = three.scene.getObjectByName( 'p'+name );
     three.scene.remove( object );
     three.objects.splice( three.objects.indexOf( object ), 1 );
+
     object = three.scene.getObjectByName( 's'+name );
     three.scene.remove( object );
     three.occupancyHelper[e.detail.x][e.detail.y][e.detail.z]=false;