Add modif senslabmap
[myslice.git] / plugins / senslabmap / static / js / viewer3D.js
1 // Various global variables\r
2 var mouseX = 0, mouseY = 0, camera, scene, renderer, projector;\r
3 \r
4 // Camera parameters\r
5 var phi = -100, theta = 0, distance = 130;\r
6 \r
7 // graphical nodes\r
8 var objects = [];\r
9 \r
10 // list of selected nodes\r
11 var selectedNodes = [];\r
12 \r
13 var div3d, nodebox, infobox;\r
14 \r
15 function onResize() {\r
16   renderer.setSize(div3d.offsetWidth, div3d.offsetHeight);\r
17   camera.aspect = div3d.offsetWidth / div3d.offsetHeight;\r
18   camera.updateProjectionMatrix();\r
19   myrender();\r
20 }\r
21 \r
22 function getCenter(nodes) {\r
23   var xmin = 0, ymin = 0, zmin = 0;\r
24   var xmax = 0, ymax = 0, zmax = 0;\r
25   \r
26   for (var i = 0; i < nodes.length; i++) {\r
27     if (nodes[i][1] > xmax) xmax = nodes[i][1];\r
28     if (nodes[i][1] < xmin) xmin = nodes[i][1];\r
29     if (nodes[i][2] > ymax) ymax = nodes[i][2];\r
30     if (nodes[i][2] < ymin) ymin = nodes[i][2];\r
31     if (nodes[i][3] > zmax) zmax = nodes[i][3];\r
32     if (nodes[i][3] < zmin) zmin = nodes[i][3];\r
33   }\r
34   return {x: (xmax + xmin) / 2, y: (ymax + ymin) / 2, z: (zmax + zmin) / 2};\r
35 }\r
36 \r
37 function debugaxis(axisLength) {\r
38   var v = function(x, y, z) {\r
39     return new THREE.Vertex(new THREE.Vector3(x, y, z));\r
40   }\r
41   \r
42   var createAxis = function (p1, p2, color) {\r
43     var lineGeometry = new THREE.Geometry();\r
44     var lineMat = new THREE.LineBasicMaterial({ color: color, lineWidth: 2 });\r
45     lineGeometry.vertices.push(p1, p2);\r
46     var line = new THREE.Line(lineGeometry, lineMat);\r
47     scene.add(line);\r
48   }\r
49   \r
50   createAxis(v(0, 0, 0), v(axisLength, 0, 0), 0xFF0000);\r
51   createAxis(v(0, 0, 0), v(0, axisLength, 0), 0x00FF00);\r
52   createAxis(v(0, 0, 0), v(0, 0, axisLength), 0x0000FF);\r
53 };\r
54 \r
55 function drawNodes(nodes) {\r
56   var geometry = new THREE.Geometry();\r
57   var center = getCenter(nodes);\r
58   \r
59   onResize();\r
60   for (var i = 0; i < nodes.length; i++) {\r
61     var material = new THREE.ParticleCanvasMaterial({\r
62       color: 0xffffff,\r
63       program: function (context) {\r
64         context.beginPath();\r
65         context.arc(0, 0, 1, 0, Math.PI * 2, true);\r
66         context.closePath();\r
67         context.fill();\r
68       }\r
69     });\r
70     var particle = new THREE.Particle(material);\r
71     particle.name = nodes[i][0];\r
72     particle.position.x = nodes[i][1] - center.x;\r
73     particle.position.y = nodes[i][2] - center.y;\r
74     particle.position.z = nodes[i][3] - center.z;\r
75     particle.uid = nodes[i][4];\r
76     particle.state = nodes[i][5];\r
77     particle.position.multiplyScalar(10);\r
78     particle.scale.x = particle.scale.y = 1;\r
79     scene.add(particle);\r
80     \r
81     var vertex = new THREE.Vertex(particle.position);\r
82     geometry.vertices.push(vertex);\r
83     objects.push(particle)\r
84     \r
85     debugaxis(10);\r
86     myrender();\r
87   }\r
88 }\r
89 \r
90 function init(nodes) {\r
91   div3d = document.getElementById('div3d');\r
92   nodebox = document.getElementById('nodebox');\r
93   infobox = document.getElementById('infobox');\r
94   \r
95   infobox.innerHTML = 'Node info : ';\r
96   nodebox.value = "";\r
97   \r
98   camera = new THREE.PerspectiveCamera(75, div3d.offsetWidth / div3d.offsetHeight, 1, 10000);\r
99   scene = new THREE.Scene();\r
100   renderer = new THREE.CanvasRenderer();\r
101   projector = new THREE.Projector();\r
102   \r
103   div3d.appendChild(renderer.domElement);\r
104   \r
105   window.addEventListener('resize', onResize, false);\r
106   scene.add(camera);\r
107   \r
108   nodebox.onkeyup = parseNodebox;\r
109   \r
110   div3d.onmousedown = OnMouseDown;\r
111   div3d.onmouseup = OnMouseUp;\r
112   div3d.onmousemove = displayNodeInfo;\r
113   if (div3d.addEventListener) {\r
114     div3d.addEventListener('DOMMouseScroll', wheel, false); // mozilla\r
115   }\r
116   div3d.onmousewheel = wheel; // IE & Opera\r
117 }\r
118 \r
119 function sortfunction(a, b) {\r
120   return (a - b) //causes an array to be sorted numerically and ascending\r
121 }\r
122 \r
123 // factorize the expanded list in selectedNode to produce dash intervals\r
124 // 1,2,3,5,9  -> 1-3,5,9\r
125 function factorize() {\r
126   var fact = [];\r
127   var previous = 0;\r
128   var intervalStart = 0;\r
129   selectedNodes.sort(sortfunction);\r
130   for (j = 0; j < selectedNodes.length; j++) {\r
131     if (intervalStart) {\r
132       // we are in an interval\r
133       if (selectedNodes[j] == previous + 1) {\r
134         // interval grows\r
135         previous += 1;\r
136       } else {\r
137         // end of interval\r
138         fact.push(intervalStart + "-" + previous);\r
139         intervalStart = 0;\r
140         previous = selectedNodes[j];\r
141       }\r
142     } else {\r
143       // we are not in an interval\r
144       if (selectedNodes[j] == previous + 1) {\r
145         // let's begin an interval\r
146         intervalStart = previous;\r
147         previous += 1;\r
148       } else {\r
149         if (previous) {\r
150           fact.push(previous);\r
151         }\r
152         previous = selectedNodes[j];\r
153       }\r
154     }\r
155   } // for end\r
156   // at the end of the loop, two cases\r
157   // we were still in an interval, then add it to the result\r
158   // we were not in an interval, then add the last selectednode (previous)\r
159   if (intervalStart) {\r
160     fact.push(intervalStart + "-" + previous);\r
161   } else if (previous) {\r
162     fact.push(previous);\r
163   }\r
164   return fact;\r
165 }\r
166 \r
167 // expand a list of nodes containing dash intervals\r
168 // 1-3,5,9 -> 1,2,3,5,9\r
169 function expand(input) {\r
170   var factorized = input.split(",");\r
171   var expanded = [];\r
172   for (var i = 0; i < factorized.length; i++) {\r
173     var d = factorized[i].split("-");\r
174     if (d.length == 2) {\r
175       for (var j = parseInt(d[0]); j < parseInt(d[1]) + 1; j++) {\r
176         expanded.push(j);\r
177       }\r
178     } else {\r
179       expanded.push(parseInt(factorized[i]));\r
180     }\r
181   }\r
182   expanded.sort(sortfunction);\r
183   for (var i = 1; i < expanded.length; i++) {\r
184     if (expanded[i] == expanded[i - 1]) {\r
185       expanded.splice(i--, 1);\r
186     }\r
187   }\r
188   return expanded;\r
189 }\r
190 \r
191 function parseNodebox() {\r
192   selectedNodes = expand(nodebox.value);\r
193   myrender();\r
194 }\r
195 \r
196 // set the camera position according two angles theta and phi and the distance\r
197 // and display the scene\r
198 function myrender() {\r
199   camera.position.x = distance * Math.sin(theta * Math.PI / 360) * Math.cos(phi * Math.PI / 360);\r
200   camera.position.y = distance * Math.sin(this.phi * Math.PI / 360);\r
201   camera.position.z = distance * Math.cos(this.theta * Math.PI / 360) * Math.cos(this.phi * Math.PI / 360);\r
202   camera.lookAt(scene.position);\r
203   camera.updateMatrix();\r
204   \r
205   for (i = 0; i < objects.length; i++) {\r
206     if (selectedNodes.indexOf(objects[i].name) != -1) {\r
207       objects[i].material.color.setHex(0x0099CC);\r
208     } else {\r
209       if (objects[i].state == "Busy") {\r
210         objects[i].material.color.setHex(0x9943BE);\r
211       } else if (objects[i].state == "Alive") {\r
212         objects[i].material.color.setHex(0x7FFF00);\r
213       } else {\r
214         objects[i].material.color.setHex(0xFF3030);\r
215       }\r
216     }\r
217   }\r
218   renderer.render(scene, camera);\r
219 }\r
220 \r
221 // rigthbutton is used for rotations\r
222 function onDocumentMouseMoveRot(event) {\r
223   var NewmouseX = event.clientX;\r
224   var NewmouseY = event.clientY;\r
225   var DeltaX = NewmouseX - mouseX;\r
226   var DeltaY = NewmouseY - mouseY;\r
227   \r
228   mouseX = NewmouseX;\r
229   mouseY = NewmouseY;\r
230   \r
231   theta -= DeltaX;\r
232   phi += DeltaY;\r
233   if (phi > 180) phi = 180;\r
234   if (phi < -180) phi = -180;\r
235   myrender();\r
236 }\r
237 // mousewheel is used for zoom\r
238 function Zoom(delta) {\r
239   distance += delta * 5;\r
240   myrender();\r
241 }\r
242 \r
243 // return the graphical node under the mouse\r
244 function findNodeUnderMouse(event) {\r
245   var x, y, z;\r
246   \r
247   event.preventDefault();\r
248   \r
249   x = ((event.clientX - div3d.offsetLeft) / div3d.offsetWidth) * 2 - 1;\r
250   y = -((event.clientY - div3d.offsetTop) / div3d.offsetHeight) * 2 + 1;\r
251   z = 0.5;\r
252   \r
253   var vector = new THREE.Vector3(x, y, z);\r
254   projector.unprojectVector(vector, camera);\r
255   \r
256   var ray = new THREE.Ray(camera.position, vector.subSelf(camera.position).normalize());\r
257   var intersects = ray.intersectObjects(objects);\r
258   \r
259   if (intersects.length > 0) {\r
260     return intersects[0];\r
261   } else {\r
262     return null;\r
263   }\r
264 }\r
265 \r
266 // display some info of the node under the mouse\r
267 function displayNodeInfo(e) {\r
268   obj = findNodeUnderMouse(e);\r
269   if (obj) {\r
270     infobox.innerHTML = 'Node info : number ' + obj.object.name + " with id " + obj.object.uid;\r
271   }\r
272 }\r
273 \r
274 // select/unselect nodes\r
275 function toggleNode(obj) {\r
276   var nodeId = obj.object.name;\r
277   var index = selectedNodes.indexOf(nodeId);\r
278   \r
279   if (index == -1) {\r
280     selectedNodes.push(nodeId);\r
281   } else {\r
282     selectedNodes.splice(index, 1);\r
283   }\r
284   nodebox.value = factorize().join(",");\r
285   myrender();\r
286 }\r
287 \r
288 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\r
289 // Mouse handling functions\r
290 //\r
291 function OnMouseDown(e) {\r
292   var clickType = 'LEFT';\r
293   if (e.type != 'mousedown') return true\r
294     if (e.which) {\r
295       if (e.which == 3) clickType = 'RIGHT';\r
296       if (e.which == 2) clickType = 'MIDDLE';\r
297       //infobox.innerHTML = clickType + " - Cam Pos = " + camera.position.x + "," + camera.position.y + "," + camera.position.z;\r
298     }\r
299     mouseX = e.clientX;\r
300   mouseY = e.clientY;\r
301   if (clickType == 'RIGHT') {\r
302     document.onmousemove = onDocumentMouseMoveRot;\r
303   }\r
304   if (clickType == 'LEFT') {\r
305     obj = findNodeUnderMouse(e);\r
306     if (obj != null) toggleNode(obj);\r
307   }\r
308 }\r
309 \r
310 function OnMouseUp(e) {\r
311   document.onmousemove = displayNodeInfo;\r
312 }\r
313 \r
314 // This function was taken here : http://www.adomas.org/javascript-mouse-wheel/\r
315 // Event handler for mouse wheel event.\r
316 \r
317 function wheel(event) {\r
318   var delta = 0;\r
319   if (!event) /* For IE. */\r
320     event = window.event;\r
321   if (event.wheelDelta) { /* IE/Opera. */\r
322     delta = event.wheelDelta / 120;\r
323   } else if (event.detail) { /** Mozilla case. */\r
324     /** In Mozilla, sign of delta is different than in IE.\r
325      * Also, delta is multiple of 3.\r
326      */\r
327     delta = -event.detail / 3;\r
328   }\r
329   /** If delta is nonzero, handle it.\r
330    * Basically, delta is now positive if wheel was scrolled up,\r
331    * and negative, if wheel was scrolled down.\r
332    */\r
333   if (delta)\r
334     Zoom(delta);\r
335   /** Prevent default actions caused by mouse wheel.\r
336    * That might be ugly, but we handle scrolls somehow\r
337    * anyway, so don't bother here..\r
338    */\r
339   if (event.preventDefault)\r
340     event.preventDefault();\r
341   event.returnValue = false;\r
342 }\r