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