diff --git a/01_Code/physical_computing_interface/demos/indexSimulation.html b/01_Code/physical_computing_interface/demos/indexSimulation.html
index 78f650c09b7aab408adc356372de13c43b25ac3c..531f9fd7016bde08ad57821ecd427197772b4ac7 100644
--- a/01_Code/physical_computing_interface/demos/indexSimulation.html
+++ b/01_Code/physical_computing_interface/demos/indexSimulation.html
@@ -12,6 +12,8 @@
     <!-- <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>
+    <!-- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"> -->
+
 
 </head>
         
@@ -88,7 +90,7 @@
     </div>
 
     <div class="footer1">
-        <strong>left-click</strong>: place voxel/orbit, <strong>right-click</strong>: radial menu
+        <strong>left-click</strong>: place voxel/orbit, <strong>right-click</strong>: radial menu with more options
     </div>
 
     <div class="footer2">
@@ -107,9 +109,8 @@
 
 <!-- libraries -->
 <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="../lib/cytoscape-cxtmenu1.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/graph/graph.js b/01_Code/physical_computing_interface/graph/graph.js
index 41f4a69d629325d5936268765cf59bb37b85671a..406ab5da98f96293a6e9627d65ee3bb00c286dd8 100644
--- a/01_Code/physical_computing_interface/graph/graph.js
+++ b/01_Code/physical_computing_interface/graph/graph.js
@@ -465,6 +465,7 @@ function initGraph(){
                 data:{
                     id: e.detail.id,
                     name: '[' +e.detail.x +"," +e.detail.y+"," +e.detail.z+']',
+                    type: "rigid",
                     parent: neighborhood,
                     code: "Math.max(...inValues)",
                     state:Math.floor(Math.random() * 100),
@@ -503,6 +504,16 @@ function initGraph(){
         
     }, false);
 
+
+    document.addEventListener('changeNodeColor', function (e) { 
+        var tgt=cy.$id('[' +e.detail.x +"," +e.detail.y+","+e.detail.z+']');
+        tgt._private.data.data.type= e.detail.data.type;
+        // console.log(tgt._private.data.data)
+        // console.log(e.detail.data.type)
+
+    }, false);
+
+
     //todo: change edge ID??
     function addEdges(x,y,z){
         x=parseInt(x);
diff --git a/01_Code/physical_computing_interface/lib/FileSaver.js b/01_Code/physical_computing_interface/lib/FileSaver.js
new file mode 100644
index 0000000000000000000000000000000000000000..500bf60a96b9ea2bf1a0d06a56c79455f5f2ca13
--- /dev/null
+++ b/01_Code/physical_computing_interface/lib/FileSaver.js
@@ -0,0 +1,143 @@
+/**
+ * Created by aghassaei on 5/25/15.
+ */
+
+
+define(['underscore', 'fileSaverLib', 'lattice', 'materials', 'ribbon', 'menuWrapper'], function(_, saveAs, lattice, materials, ribbon, menuWrapper){
+
+    function _saveFile(data, name, extension, noQuotes){
+//        require(['jsonFn'], function(JSONfn){
+//        console.log(data.toString());
+            var jsonString = JSON.stringify(data, null, '\t');
+
+            if (noQuotes){
+                jsonString = jsonString.replace(/"/g, '');
+            }
+
+            saveData(jsonString, name, extension);
+            if (data.assembler){
+                jsonString.replace(/\\"/g,"\uFFFF"); //U+ FFFF
+                jsonString = jsonString.replace(/\"([^"]+)\":/g,"$1:").replace(/\uFFFF/g,"\\\"");
+                var blob = new Blob([jsonString], {type: "text/plain;charset=utf-8"});
+                saveAs(blob, name + "-forAmanda" + extension);
+            }
+//        });
+    }
+
+    function saveData(data, name, extension){
+        var blob = new Blob([data], {type: "text/plain;charset=utf-8"});
+        saveAs(blob, name + extension);
+    }
+
+//    function save(name){
+//        if (!name || name == "" || name == undefined) name = "file";
+//        var data = {
+//            lattice:_getLatticeDataToSave(),
+////            assembler:_getAssemblerDataToSave()
+//        };
+//        _saveFile(data, name, ".json");
+//    }
+
+    function save(name){
+        if (!name || name == "" || name == undefined) name = "DM Assembly";
+        var data = {
+            assembly:_getLatticeDataToSave(),
+            materials:_getMaterialsDataToSave()
+        };
+        _saveFile(data, name, ".json");
+    }
+
+    function saveAssembler(){
+
+    }
+
+    function saveSequences(seqArray, name){
+        _saveFile(seqArray, name || "seqs", ".txt", true);
+    }
+
+    function saveMaterial(id, material){
+        var data = {materials:{}};
+        data.materials[id] = material || _getMaterialDataToSave(id);
+        _saveFile(data, data.materials[id].name, ".json");
+    }
+
+    function saveMachineConfig(data){
+        _saveFile(data, "Machine Config", ".json");
+    }
+
+    function _getAssemblerDataToSave(){
+        var assemblerData = _.omit(globals.cam.toJSON(), ["origin", "stock", "exporter", "appState", "lattice", "machine", "simLineNumber"]);
+        if (!globals.cam.get("editsMadeToProgram")) assemblerData.dataOut = "";
+        return assemblerData;
+    }
+
+    function _getLatticeDataToSave(){
+        return lattice.getSaveJSON();
+    }
+
+    function _getMaterialsDataToSave(){
+        var data = {};
+        _.each(_.keys(materials.list), function(key){
+            data[key] = _getMaterialDataToSave(key);
+        });
+        return data;
+    }
+
+    function _getMaterialDataToSave(id){
+        return _.omit(materials.list[id], "threeMaterial", "transparentMaterial");
+    }
+
+    function loadFile(data){//parsed json todo make this better - load composite
+        if (data.assembler) {
+            _loadAssembler(data.assembler);
+            return;
+        }
+        if (!data.materials){
+            console.warn("no material definitions in this file");
+            return;
+        }
+        _.each(_.keys(data.materials), function(key){
+            materials.setMaterial(key, data.materials[key]);
+        });
+        if (!data.assembly){
+            console.warn("no assembly in this file");
+            return;
+        }
+        lattice.clearCells();
+        var sparseCells = data.assembly.sparseCells;
+        _setData(lattice, _.omit(data.assembly, "sparseCells"));
+        if (sparseCells) lattice._reloadCells(sparseCells);
+        ribbon.render();
+        menuWrapper.render();
+    }
+
+    function _loadAssembler(data){
+        require(['cam'], function(cam){
+            cam.selectMachine(data);
+            console.log("loaded");
+        });
+    }
+
+    function _setData(object, data){
+        _.each(_.keys(data), function(key){
+            if (data[key] && data[key].x){//vector object
+                object.set(key, new THREE.Vector3(data[key].x, data[key].y, data[key].z), {silent:true});
+                return;
+            }
+            object.set(key, data[key], {silent:true});
+        });
+        object.trigger("change");
+    }
+
+
+    return {//return public methods
+//        save: save,
+        save: save,
+        saveData: saveData,
+        saveMaterial: saveMaterial,
+        saveMachineConfig: saveMachineConfig,
+//        saveAssembler: saveAssembler,
+        loadFile: loadFile,
+        saveSequences: saveSequences
+    }
+});
\ No newline at end of file
diff --git a/01_Code/physical_computing_interface/lib/cytoscape-cxtmenu1.js b/01_Code/physical_computing_interface/lib/cytoscape-cxtmenu1.js
new file mode 100644
index 0000000000000000000000000000000000000000..82b1440590fc4d367727f36736ecfc2893ef2d04
--- /dev/null
+++ b/01_Code/physical_computing_interface/lib/cytoscape-cxtmenu1.js
@@ -0,0 +1,849 @@
+(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 = 100; // defailt radius;
+  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(r, rs) {
+    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 = (r + rs) / 2 * Math.cos(midtheta);
+      var ry1 = (r + rs) / 2 * Math.sin(midtheta);
+
+      // Arbitrary multiplier to increase the sizing of the space 
+      // available for the item.
+      var width = 1 * Math.abs((r - rs) * Math.cos(midtheta));
+      var height = 1 * Math.abs((r - rs) * Math.sin(midtheta));
+      width = Math.max(width, height);
+
+      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': width + 'px',
+        width: width + 'px',
+        height: width + 'px',
+        marginLeft: rx1 - width / 2 + 'px',
+        marginTop: -ry1 - width / 2 + 'px'
+      });
+
+      var content = createElement({ class: 'cxtmenu-content' });
+
+      if (command.content instanceof HTMLElement) {
+        content.appendChild(command.content);
+      } else {
+        content.innerHTML = command.content;
+      }
+
+      setStyles(content, {
+        'width': width + 'px',
+        'height': width + '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(radius, rspotlight) {
+    redrawQueue.drawBg = [radius, rspotlight];
+  }
+
+  function drawBg(radius, rspotlight) {
+    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(radius + options.activePadding, radius + options.activePadding);
+      c2d.arc(radius + options.activePadding, radius + options.activePadding, radius, 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 = radius * Math.cos(theta1);
+      var ry1 = radius * Math.sin(theta1);
+      c2d.beginPath();
+      c2d.moveTo(radius + options.activePadding, radius + options.activePadding);
+      c2d.lineTo(radius + options.activePadding + rx1, radius + options.activePadding - ry1);
+      c2d.closePath();
+      c2d.stroke();
+
+      theta1 += dtheta;
+      theta2 += dtheta;
+    }
+
+    c2d.fillStyle = 'white';
+    c2d.globalCompositeOperation = 'destination-out';
+    c2d.beginPath();
+    c2d.arc(radius + options.activePadding, radius + options.activePadding, rspotlight + options.spotlightPadding, 0, Math.PI * 2, true);
+    c2d.closePath();
+    c2d.fill();
+
+    c2d.globalCompositeOperation = 'source-over';
+  }
+
+  function queueDrawCommands(rx, ry, radius, theta, rspotlight) {
+    redrawQueue.drawCommands = [rx, ry, radius, theta, rspotlight];
+  }
+
+  function drawCommands(rx, ry, radius, theta, rs) {
+    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(radius + options.activePadding, radius + options.activePadding);
+    c2d.arc(radius + options.activePadding, radius + options.activePadding, radius + options.activePadding, 2 * Math.PI - theta1, 2 * Math.PI - theta2, true);
+    c2d.closePath();
+    c2d.fill();
+
+    c2d.fillStyle = 'white';
+    c2d.globalCompositeOperation = 'destination-out';
+
+    var tx = radius + options.activePadding + rx / radius * (rs + options.spotlightPadding - options.indicatorSize / 4);
+    var ty = radius + options.activePadding + ry / radius * (rs + options.spotlightPadding - options.indicatorSize / 4);
+    var rot = Math.PI / 4 - theta;
+
+    c2d.translate(tx, ty);
+    c2d.rotate(rot);
+
+    // clear the indicator
+    // The indicator size (arrow) depends on the node size as well. If the indicator size is bigger and the rendered node size + padding, 
+    // use the rendered node size + padding as the indicator size.
+    var indicatorSize = options.indicatorSize > rs + options.spotlightPadding ? rs + options.spotlightPadding : options.indicatorSize;
+    c2d.beginPath();
+    c2d.fillRect(-indicatorSize / 2, -indicatorSize / 2, indicatorSize, 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(radius + options.activePadding, radius + 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 hoverOn = 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,
+            rs = void 0;
+        if (!isCy && ele && ele.isNode instanceof Function && ele.isNode() && !ele.isParent() && !options.atMouse) {
+          // If it's a node, the default spotlight radius for a node is the node width
+          rp = ele.renderedPosition();
+          rw = ele.renderedOuterWidth();
+          rh = ele.renderedOuterHeight();
+          rs = rw / 2;
+          // If adaptativeNodespotlightRadius is not enabled and min|maxSpotlighrRadius is defined, use those instead
+          rs = !options.adaptativeNodeSpotlightRadius && options.minSpotlightRadius ? Math.max(rs, options.minSpotlightRadius) : rs;
+          rs = !options.adaptativeNodeSpotlightRadius && options.maxSpotlightRadius ? Math.min(rs, options.maxSpotlightRadius) : rs;
+        } else {
+          // If it's the background or an edge, the spotlight radius is the min|maxSpotlightRadius
+          rp = e.renderedPosition || e.cyRenderedPosition;
+          rw = 1;
+          rh = 1;
+          rs = rw / 2;
+          rs = options.minSpotlightRadius ? Math.max(rs, options.minSpotlightRadius) : rs;
+          rs = options.maxSpotlightRadius ? Math.min(rs, options.maxSpotlightRadius) : rs;
+        }
+
+        offset = getOffset(container);
+
+        ctrx = rp.x;
+        ctry = rp.y;
+        r = rw / 2 + (options.menuRadius instanceof Function ? options.menuRadius(target) : Number(options.menuRadius));
+        containerSize = (r + options.activePadding) * 2;
+        updatePixelRatio();
+
+        setStyles(parent, {
+          width: containerSize + 'px',
+          height: containerSize + 'px',
+          display: 'block',
+          left: rp.x - r + 'px',
+          top: rp.y - r + 'px'
+        });
+        createMenuItems(r, rs);
+        queueDrawBg(r, rs);
+
+        activeCommandI = undefined;
+
+        inGesture = true;
+        gestureStartEvent = e;
+      }
+    }).on('cxtdrag tapdrag', options.selector, dragHandler = function dragHandler(e) {
+
+      if (!inGesture) {
+        return;
+      }
+      e.preventDefault(); // Otherwise, on mobile, the pull-down refresh gesture gets activated
+
+      var origE = e.originalEvent;
+      var isTouch = origE.touches && origE.touches.length > 0;
+
+      var pageX = (isTouch ? origE.touches[0].pageX : origE.pageX) - window.pageXOffset;
+      var pageY = (isTouch ? origE.touches[0].pageY : origE.pageY) - window.pageYOffset;
+
+      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);
+
+      var rw = void 0;
+      if (target && target.isNode instanceof Function && target.isNode() && !target.isParent() && !options.atMouse) {
+        // If it's a node, the default spotlight radius for a node is the node width
+        rw = target.renderedOuterWidth();
+        rs = rw / 2;
+        // If adaptativeNodespotlightRadius is not enabled and min|maxSpotlighrRadius is defined, use those instead
+        rs = !options.adaptativeNodeSpotlightRadius && options.minSpotlightRadius ? Math.max(rs, options.minSpotlightRadius) : rs;
+        rs = !options.adaptativeNodeSpotlightRadius && options.maxSpotlightRadius ? Math.min(rs, options.maxSpotlightRadius) : rs;
+      } else {
+        // If it's the background or an edge, the spotlight radius is the min|maxSpotlightRadius
+        rw = 1;
+        rs = rw / 2;
+        rs = options.minSpotlightRadius ? Math.max(rs, options.minSpotlightRadius) : rs;
+        rs = options.maxSpotlightRadius ? Math.min(rs, options.maxSpotlightRadius) : rs;
+      }
+
+      r = rw / 2 + (options.menuRadius instanceof Function ? options.menuRadius(target) : Number(options.menuRadius));
+      if (d < rs + options.spotlightPadding || typeof options.outsideMenuCancel === "number" && d > r + options.activePadding + options.outsideMenuCancel) {
+        //
+
+        queueDrawBg(r, rs);
+        return;
+      }
+      queueDrawBg(r, rs);
+
+      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, r, theta, rs);
+    }).on('tapdrag', dragHandler).on('mousemove', function () {
+      if (activeCommandI !== undefined) {
+        var hovered = commands[activeCommandI].hover;
+        if (hovered) {
+          if (hoverOn !== activeCommandI) {
+            hovered.apply(target, [target, gestureStartEvent]);
+          }
+          hoverOn = activeCommandI;
+        }
+      }
+    }).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;
+        }
+      }
+
+      hoverOn = 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: function menuRadius(ele) {
+    return 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
+      hover: function(ele){ // a function to execute when the command is hovered
+        console.log( ele.id() ) // `ele` holds the reference to the active element
+      },
+      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, will default to the node size if the node size is smaller than the indicator size, 
+  separatorWidth: 3, // the empty spacing in pixels between successive commands
+  spotlightPadding: 4, // extra spacing in pixels between the element and the spotlight
+  adaptativeNodeSpotlightRadius: false, // specify whether the spotlight radius should adapt to the node size
+  minSpotlightRadius: 24, // the minimum radius in pixels of the spotlight (ignored for the node if adaptativeNodeSpotlightRadius is enabled but still used for the edge & background)
+  maxSpotlightRadius: 38, // the maximum radius in pixels of the spotlight (ignored for the node if adaptativeNodeSpotlightRadius is enabled but still used for the edge & background)
+  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
+  outsideMenuCancel: false // if set to a number, this will cancel the command if the pointer is released outside of the spotlight, padded by the number given
+};
+
+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/setup/globals.js b/01_Code/physical_computing_interface/setup/globals.js
index 4a425114bcafaeeb2aa8764cdccc4a40e91e7fd7..fa9c3c279c0570fc0f3de179c0721dd53ba86b10 100644
--- a/01_Code/physical_computing_interface/setup/globals.js
+++ b/01_Code/physical_computing_interface/setup/globals.js
@@ -10,6 +10,7 @@ function globals(utils){
     this.color4= 0xfa6e70; //red/orange
     this.color5=0x380152; //purple
     this.color6=0x696767; //grey
+    this.color7=0x42f2f5; //light blue
     this.gridPresets={
         Cubic :  {
         
@@ -281,6 +282,7 @@ function globals(utils){
     this.timeline=[];
     this.selectedjson;
     this.editor;
+    this.globalJson={nodes:[]};
 
 }
 
diff --git a/01_Code/physical_computing_interface/threejs/grid.js b/01_Code/physical_computing_interface/threejs/grid.js
index beb8de935e4d9a87d31dd4df6eb46a96e3176764..3df50c660ce82896446ee3dd5eacdd34829155b5 100644
--- a/01_Code/physical_computing_interface/threejs/grid.js
+++ b/01_Code/physical_computing_interface/threejs/grid.js
@@ -37,6 +37,7 @@ function threejs(GLOBALS,utils,containerName,container1Name){
     var color1= "#ffffff"; //white
     var color11= "#ffffff8c"; //white transparent
     var color2= "#020227";  //dark blue
+    var color22= "#0202278c";  //dark blue transparent
     var color3= "#1c5c61"; //teal
     var color33= "#1c5c618c"; //teal transparent
     var color4= "#fa6e70"; //red/orange
@@ -64,23 +65,33 @@ function threejs(GLOBALS,utils,containerName,container1Name){
           
     });
 
-    this.cxtdefaults = {
+    this.cxtdefaultsNode = {
         selector: 'core',//'node, edge',
-        menuRadius: 50,
-        fillColor: color11, // the background colour of the menu
+        menuRadius: 70,
+        fillColor: color33, // 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
+        indicatorSize: 30, // 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
+        minSpotlightRadius: 20, // 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
+        itemColor: color2, // 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
         commands: [
+
+            {
+                // content: 'Select',
+                content: '<span class="fa fa-mouse-pointer fa-1x"></span>',
+                select: function(){
+                    // console.log( "Select");//todo move to globals
+                    GLOBALS.selectNode(three.selectedNodeID.x,three.selectedNodeID.y,three.selectedNodeID.z);
+
+                }
+            },
             {
                 //RUN/PLAY NODE
                 content: '<span class="fa fa-play fa-1x"></span>',
@@ -91,6 +102,7 @@ function threejs(GLOBALS,utils,containerName,container1Name){
             },
 
             {
+                // content: 'delete',
                 content: '<span class="fa fa-trash fa-1x"></span>',
                 select: function(){
                     GLOBALS.selectNode(three.selectedNodeID.x,three.selectedNodeID.y,three.selectedNodeID.z);
@@ -99,14 +111,164 @@ function threejs(GLOBALS,utils,containerName,container1Name){
                 },
                 // enabled: true
             },
+            {
+                // content: 'force',
+                content: '<span class="fa fa-down-long fa-1x"></span>',
+                select: function(){
+                    // console.log( "Select");//todo move to globals
+                    var data={
+                        color:GLOBALS.color4,
+                        type:"force"
+                    }
+                    GLOBALS.changeNodeColor(three.selectedNodeID.x,three.selectedNodeID.y,three.selectedNodeID.z,false,data);
+                    GLOBALS.selectNode(three.selectedNodeID.x,three.selectedNodeID.y,three.selectedNodeID.z);
 
+                }
+            },
             {
-                // content: 'Select',
-                content: '<span class="fa fa-mouse-pointer fa-1x"></span>',
+                // content: 'display',
+                content: '<span class="fa fa-lightbulb fa-1x"></span>',
+                select: function(){
+                    // console.log( "Select");//todo move to globals
+                    var data={
+                        color:GLOBALS.color7,
+                        type:"display"
+                    }
+                    GLOBALS.changeNodeColor(three.selectedNodeID.x,three.selectedNodeID.y,three.selectedNodeID.z,false,data);
+                    GLOBALS.selectNode(three.selectedNodeID.x,three.selectedNodeID.y,three.selectedNodeID.z);
+
+                }
+            },
+            {
+                // content: 'sensor',
+
+                content: '<span class="fa-regular fa-hand-point-up"></span>',
+                select: function(){
+                    // console.log( "Select");//todo move to globals
+                    var data={
+                        color:GLOBALS.color2,
+                        type:"sensor"
+                    }
+                    GLOBALS.changeNodeColor(three.selectedNodeID.x,three.selectedNodeID.y,three.selectedNodeID.z,false,data);
+                    GLOBALS.selectNode(three.selectedNodeID.x,three.selectedNodeID.y,three.selectedNodeID.z);
+
+                }
+            },
+            {
+                // content: 'microcontroller',
+                content: '<span class="fa fa-microchip fa-1x"></span>',
+                select: function(){
+                    // console.log( "Select");//todo move to globals
+                    var data={
+                        color:GLOBALS.color5,
+                        type:"microcontroller"
+                    }
+                    GLOBALS.changeNodeColor(three.selectedNodeID.x,three.selectedNodeID.y,three.selectedNodeID.z,false,data);
+                    GLOBALS.selectNode(three.selectedNodeID.x,three.selectedNodeID.y,three.selectedNodeID.z);
+
+
+                }
+            },
+            {
+                // content: 'frame',
+                content: '<span class="fa fa-cube fa-1x"></span>',
                 select: function(){
                     // console.log( "Select");//todo move to globals
+                    var data={
+                        color:GLOBALS.color3,
+                        type:"rigid"
+                    }
+                    GLOBALS.changeNodeColor(three.selectedNodeID.x,three.selectedNodeID.y,three.selectedNodeID.z,false,data);
                     GLOBALS.selectNode(three.selectedNodeID.x,three.selectedNodeID.y,three.selectedNodeID.z);
 
+
+                }
+            },
+
+            
+        ]
+    };
+
+    this.cxtdefaults = {
+        selector: 'core',//'node, edge',
+        outsideMenuCancel: 1,
+        menuRadius: 50,
+        fillColor: color33, // 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
+        indicatorColor: color33,
+        // 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: color2, // 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
+        commands: [
+            {
+                //upload
+                content: '<span class="fa fa-upload fa-1x"></span>',
+                select: function(){
+                    console.log( "upload design");//todo move to globals
+                    // GLOBALS.runNode(three.selectedNodeID.x,three.selectedNodeID.y,three.selectedNodeID.z);
+                    var input = document.createElement('input');
+                    input.type = 'file';
+                    input.onchange = e => { 
+                        var file = e.target.files[0]; 
+                        // console.log(file.name )
+                        // console.log(file.size)
+                        // console.log(file.type )
+                        // setting up the reader
+                        var reader = new FileReader();
+                        reader.readAsText(file,'UTF-8');
+
+                        // here we tell the reader what to do when it's done reading...
+                        reader.onload = readerEvent => {
+                            var content = readerEvent.target.result; // this is the content!
+                            const obj = JSON.parse(content)
+                            // console.log( obj );
+                            var nodes=obj.nodes;
+                            for(var c=0;c<nodes.length;c++){
+                                let node=nodes[c]
+                                var selectedIndex = GLOBALS.globalJson.nodes.findIndex(selected => selected.x == node.x&&selected.y == node.y&&selected.z == node.z);
+                                // console.log(selectedIndex)
+                                // console.log(GLOBALS.globalJson.nodes)
+                                if (selectedIndex > -1) {
+                                    // GLOBALS.globalJson.nodes.splice(selectedIndex, 1);
+                                    // console.log("node "+node.id+" already there.")
+                                    GLOBALS.changeNodeColor(node.x,node.y,node.z,false,node.data);
+
+                                }else{
+                                    GLOBALS.addNode(node.x,node.y,node.z,false,node.data);
+                                    GLOBALS.changeNodeColor(node.x,node.y,node.z,false,node.data);
+
+                                }
+
+                                
+                            }
+                        }
+                    }
+                     
+                    input.click();
+                }
+            },
+
+            
+            {
+                // content: 'Save',
+                content: '<span class="fa fa-save fa-1x"></span>',
+                select: function(){
+                    console.log( "save design");//todo move to globals
+
+                    var data=GLOBALS.globalJson
+
+                    var fileName = 'myDesign.json';
+
+                    downloadTextFile(JSON.stringify(data,undefined,2), fileName);
+
                 }
             }
         ]
@@ -115,10 +277,18 @@ function threejs(GLOBALS,utils,containerName,container1Name){
 
     // this.init();
 }
-
+function downloadTextFile(text, name) {
+    const a = document.createElement('a');
+    const type = name.split(".").pop();
+    a.href = URL.createObjectURL( new Blob([text], { type:`text/${type === "txt" ? "plain" : type}` }) );
+    a.download = name;
+    a.click();
+}
+  
 threejs.prototype.init=function() {
     this.camera = new THREE.PerspectiveCamera( 45, this.getWidth()/ this.getHeight()  , 1, 10000*this.voxelSpacing);
     this.camera.position.set( -this.gridSize*this.voxelSpacing*1.0, this.gridSize*this.voxelSpacing, this.gridSize*this.voxelSpacing/2.0 );
+
     this.camera.lookAt( this.gridSize/2, 0, this.gridSize/2 );
     this.scene = new THREE.Scene();
     this.scene.background = new THREE.Color( 0xffffff );
@@ -180,7 +350,7 @@ threejs.prototype.init=function() {
     var ambientLight = new THREE.AmbientLight( 0x606060 );
     this.scene.add( ambientLight );
     var directionalLight = new THREE.DirectionalLight( 0xffffff );
-    directionalLight.position.set( 1, 0.75, 0.5 ).normalize();
+    directionalLight.position.set( -1, 0.75, -0.5 ).normalize();
     this.scene.add( directionalLight );
     this.renderer = new THREE.WebGLRenderer( { antialias: true } );
     this.renderer.setPixelRatio( window.devicePixelRatio );
@@ -475,7 +645,7 @@ function onDocumentMouseDownThree( event ) {
                 
                 var obj=utils.getXYZfromName(intersect.object.name);
                 three.selectedNodeID=obj;
-                three.menu=three.cy.cxtmenu( three.cxtdefaults );
+                three.menu=three.cy.cxtmenu( three.cxtdefaultsNode );
 
 
                 [p_x ,p_y ,p_z ,s_x ,s_y,s_z,r_y]=utils.getTransforms(three.grid,obj.x, obj.y,obj.z);
@@ -498,6 +668,9 @@ function onDocumentMouseDownThree( event ) {
         }
         three.render();
     }
+    if(event.which==3 && !(intersects1.length > 0)&& !(intersects.length > 0)){
+        three.menu=three.cy.cxtmenu( three.cxtdefaults );
+    }
     
 }
 
@@ -541,11 +714,25 @@ document.addEventListener('addNode', function (e) {
     three.occupancyHelper[e.detail.x][e.detail.y][e.detail.z]=false;
 
     three.buildNeighborHelpers(three.grid, e.detail.x, e.detail.y,e.detail.z);
+
+    var obj=e.detail;
+    if (Object.entries(obj.data).length === 0) {
+        // is empty
+        obj.data={
+            color:GLOBALS.color3,
+            type:"rigid"
+        }
+        
+    } else {
+        // is not empty
+    }
+    GLOBALS.globalJson.nodes.push(obj)
+
     
 }, false);
 
 document.addEventListener('changeNodeColor', function (e) { 
-
+    var obj=e.detail;
     //deleteHelper Meshes
     var name='[' +e.detail.x +"," +e.detail.y+","+e.detail.z+']';
     var object = three.scene.getObjectByName( name );
@@ -553,7 +740,10 @@ document.addEventListener('changeNodeColor', function (e) {
     mat.color.setHex(e.detail.data.color);
     // three.cubeMaterial.color=GLOBALS.color5;
     object.material=mat;
-
+    
+    var selectedIndex = GLOBALS.globalJson.nodes.findIndex(selected => selected.x == obj.x&&selected.y == obj.y&&selected.z == obj.z);
+    
+    GLOBALS.globalJson.nodes[selectedIndex].data=obj.data
     
 }, false);
 
@@ -575,10 +765,22 @@ document.addEventListener('selectNode', function (e) {
     three.highlight.position.y=p_y;
     three.highlight.position.z=p_z;
 
+    var selected = GLOBALS.globalJson.nodes.filter(selected => selected.x == obj.x&&selected.y == obj.y&&selected.z == obj.z)[0];
+    // console.log(selected)
     
 }, false);
 
+document.addEventListener('removeNode', function (e) { 
+    var obj=e.detail;
 
+    var selectedIndex = GLOBALS.globalJson.nodes.findIndex(selected => selected.x == obj.x&&selected.y == obj.y&&selected.z == obj.z);
+    // console.log(selectedIndex)
+    // console.log(GLOBALS.globalJson.nodes)
+    if (selectedIndex > -1) {
+        GLOBALS.globalJson.nodes.splice(selectedIndex, 1);
+    }
+    // console.log(GLOBALS.globalJson.nodes)
+}, false);
 /////////////////////