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