clean code map
[myslice.git] / plugins / senslabmap / static / js / map.js
1 var Senslab = {
2   normalize: function(node) {
3     var info;
4
5     if (node.component_name) { // wsn430-11.devlille.iot-lab.info
6       info = node.component_name.split(".");
7     } /*else if (node.hrn) { // iotlab.a8-11\.devgrenoble\.iot-lab\.info
8       var info = node.hrn.split("\\.");
9       info[0] = info[0].split(".")[1];
10     }*/
11
12     if (info && info[2] == "iot-lab" && info[3] == "info") {
13       node.arch = info[0].split("-")[0];
14       node.id = info[0].split("-")[1];
15       node.site = info[1];
16       node.normalized = true;
17     }
18   }
19 };
20
21 Senslab.Map = function() {
22   var colors = {
23     "Alive": 0x7FFF00,
24     "Busy": 0x9943BE,
25     "Suspected": 0xFF3030,
26     "Selected": 0x0099CC
27   };
28
29   var archs = [
30     "wsn430",
31     "m3",
32     "a8"
33   ];
34   
35   function Map($container, options) {
36     this.width  = 600;
37     this.height = 400;
38     
39     this.distance = 50;
40     this.phi = -100;
41     this.theta = 0;
42     this.onRot = false;
43     
44     this.pointerDetectRay = new THREE.Raycaster();
45     this.pointerDetectRay.ray.direction.set(0, -1, 0);
46     this.projector = new THREE.Projector();
47     this.mouse2D = new THREE.Vector3(0, 0, 0);
48     
49     this.renderer = new THREE.CanvasRenderer();
50     this.renderer.setSize(this.width, this.height);
51     
52     this.camera = new THREE.PerspectiveCamera(75, this.width / this.height, 1, 10000);
53     
54     this.scene = new THREE.Scene();
55     this.scene.add(this.camera);
56     
57     this.updatePosition();
58     
59     var self = this;
60     
61     this.$nodeInputs = {};
62     
63     $.each(archs, function(i, arch) {
64       self.$nodeInputs[arch] = $("<input type='text' placeholder='" + arch + "'>")
65       .appendTo($container)
66       .change(function() {
67         self.updateSelected(arch, expand($(this).val()));
68         self.update();
69       });
70     });
71     
72     var $canvas = $(this.renderer.domElement)
73     .mousemove(function(e) {
74       self.mouse2D.x =  ((e.pageX - $canvas.offset().left) / $canvas.width()) * 2 - 1;
75       self.mouse2D.y = -((e.pageY - $canvas.offset().top) / $canvas.height()) * 2 + 1;
76       
77       if (self.onRot) {
78         self.theta -= e.pageX - self.mouse2D.pageX;
79         self.phi += e.pageY - self.mouse2D.pageY;
80         if (self.phi > 180)
81           self.phi = 180;
82         if (self.phi < -180)
83           self.phi = -180;
84         
85         self.mouse2D.pageX = e.pageX;
86         self.mouse2D.pageY = e.pageY;
87         
88         self.updatePosition();
89         self.update();
90       }
91     }).mousedown(function(e) {
92       e.preventDefault();
93       switch (e.which) {
94         case 1:
95           self.pointerDetectRay = self.projector.pickingRay(self.mouse2D.clone(), self.camera);
96           var intersects = self.pointerDetectRay.intersectObjects(self.scene.children);
97           if (intersects.length > 0) {
98             var node = intersects[0].object;
99             if (node.boot_state != "Suspected") {
100               setState(node, node.boot_state == "Alive" ? "Selected" : "Alive");
101               var $nodeInput = self.$nodeInputs[node.arch];
102               $nodeInput.val(factorize(self.getNodesId(node.arch)));
103               self.update();
104             }
105           }
106           break;
107         case 3:
108           self.mouse2D.pageX = e.pageX;
109           self.mouse2D.pageY = e.pageY;
110           self.onRot = true;
111           break;
112       }
113     }).mouseup(function(e) {
114       e.preventDefault();
115       switch (e.which) {
116         case 3:
117           self.onRot = false;
118           break;
119       }
120     }).mouseleave(function(e) {
121       self.onRot = false;
122     }).mousewheel(function(e, delta) {
123       e.preventDefault();
124       self.distance += delta * 5;
125       self.updatePosition();
126       self.update();
127     });
128     
129     $container.append($canvas);
130   }
131   
132   Map.prototype.getNodesId = function(arch) {
133     var allNodes = this.scene.children;
134     var nodes = [];
135     for (var i = 0; i < allNodes.length; ++i) {
136       if (allNodes[i].arch == arch && allNodes[i].boot_state == "Selected") {
137         nodes.push(allNodes[i].id);
138       }
139     }
140     return nodes;
141   }
142   
143   Map.prototype.addNodes = function(nodes) {
144     var center = getCenter(nodes);
145
146     nodes.sort(function(a, b) {
147       return a.id - b.id;
148     });
149     
150     for(var i = 0; i < nodes.length; ++i) {
151       var material = new THREE.ParticleCanvasMaterial({program: circle});
152       var particle = new THREE.Particle(material);
153       particle.id = parseInt(nodes[i].id);
154       particle.arch = nodes[i].arch;
155       particle.boot_state = nodes[i].boot_state;
156       particle.position.x = (nodes[i].x - center.x) * 10;
157       particle.position.y = (nodes[i].y - center.y) * 10;
158       particle.position.z = (nodes[i].z - center.z) * 10;
159       particle.scale.x = particle.scale.y = 1;
160       setColor(particle)
161       this.scene.add(particle);
162     }
163     this.update();
164   };
165   
166   Map.prototype.updateSelected = function(arch, selected) {
167     var nodes = this.scene.children;
168     for (var i = 0; i < nodes.length; ++i) {
169       if (nodes[i].arch == arch && nodes[i].boot_state != "Suspected") {
170         var node = nodes[i];
171         var state = $.inArray(node.id, selected) == -1 ? "Alive" : "Selected";
172         if (node.boot_state != state) {
173           setState(node, state);
174         }
175       }
176     }
177   }
178   
179   Map.prototype.updatePosition = function() {
180     this.camera.position.x = this.distance * Math.sin(this.theta * Math.PI / 360) * Math.cos(this.phi * Math.PI / 360);
181     this.camera.position.y = this.distance * Math.sin(this.phi * Math.PI / 360);
182     this.camera.position.z = this.distance * Math.cos(this.theta * Math.PI / 360) * Math.cos(this.phi * Math.PI / 360);
183     this.camera.lookAt(this.scene.position);
184     this.camera.updateMatrix();
185   };
186   
187   Map.prototype.update = function() {
188     this.renderer.render(this.scene, this.camera);
189   };
190
191   function setState(node, state) {
192     node.boot_state = state;
193     setColor(node);
194     notify(node);
195   }
196
197   function setColor(node) {
198     node.material.color.setHex(colors[node.boot_state] || colors["Selected"]);
199   }
200
201   function getCenter(nodes) {
202     var xmin = 0, ymin = 0, zmin = 0;
203     var xmax = 0, ymax = 0, zmax = 0;
204     
205     for (var i = 0; i < nodes.length; ++i) {
206       if (nodes[i].x > xmax) xmax = nodes[i].x;
207       if (nodes[i].x < xmin) xmin = nodes[i].x;
208       if (nodes[i].y > ymax) ymax = nodes[i].y;
209       if (nodes[i].y < ymin) ymin = nodes[i].y;
210       if (nodes[i].z > zmax) zmax = nodes[i].z;
211       if (nodes[i].z < zmin) zmin = nodes[i].z;
212     }
213     return {x: (xmax + xmin) / 2, y: (ymax + ymin) / 2, z: (zmax + zmin) / 2};
214   }
215
216   function factorize(nodes) {
217     var factorized = [];
218     var prev = 0;
219     var intervalStart = 0;
220     
221     for (var i = 0; i < nodes.length; ++i) {
222       if (intervalStart) {
223         if (nodes[i] == prev + 1) {
224           prev++;
225         } else {
226           factorized.push(intervalStart + "-" + prev);
227           intervalStart = 0;
228           prev = nodes[i];
229         }
230       } else {
231         if (nodes[i] == prev + 1) {
232           intervalStart = prev;
233           prev++;
234         } else {
235           prev && factorized.push(prev);
236           prev = nodes[i];
237         }
238       }
239     }
240     factorized.push(intervalStart ? intervalStart + "-" + prev : prev);
241     return factorized.join(",");
242   }
243   
244   function expand(input) {
245     var factorized = input.split(",");
246     var expanded = [];
247     for (var i = 0; i < factorized.length; ++i) {
248       var d = factorized[i].split("-");
249       if (d.length == 2) {
250         for (var j = parseInt(d[0]); j < parseInt(d[1]) + 1; j++) {
251           expanded.push(j);
252         }
253       } else {
254         expanded.push(parseInt(factorized[i]));
255       }
256     }
257     
258     expanded.sort(function(a, b) {
259       return a - b;
260     });
261     
262     for (var i = 1; i < expanded.length; i++) {
263       if (expanded[i] == expanded[i - 1]) {
264         expanded.splice(i--, 1);
265       }
266     }
267     return expanded;
268   }
269
270   function circle(context) {
271     context.beginPath();
272     context.arc(0, 0, 1, 0, Math.PI * 2, true);
273     context.closePath();
274     context.fill();
275   };
276
277   var notify = function(node) {
278     console.log("[Notify] node " + node.id + " is " + node.boot_state);
279   };
280   
281   return Map;
282 }();