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