cf70e812cb4ff786bc73f87674bda0942e056655
[myslice.git] / plugins / senslabmap / static / js / viewer3D.js
1 \r
2 // Various global variables\r
3 var mouseX = 0, mouseY = 0,\r
4                         camera, scene, renderer, projector;\r
5 var sTestEventType = 'mousedown';\r
6 //var window3DWidth = window.innerWidth * 0.8;\r
7 //var window3DHeight=window.innerHeight*0.75;\r
8 var window3DWidth, window3DHeight;\r
9 var offX, offY;\r
10 // Camera parameters\r
11 phi = -100, theta = 0, distance = 150;\r
12 var rcount = 0;\r
13 // Text display\r
14 var info, nodelist, help;\r
15 \r
16 // graphical nodes\r
17 var objects = [];\r
18 \r
19 // list of selected nodes\r
20 var selectedNodes = [];\r
21 \r
22 var div3d, nodebox, infobox;\r
23 \r
24 function init() {\r
25     var particles, particle;\r
26 \r
27     // jordan : modifier document.getElementById\r
28     div3d = jQuery('#div3d');\r
29     nodebox = jQuery('#nodebox');\r
30     infobox = jQuery('#infobox');\r
31     titlebox = jQuery('#titlebox');\r
32     // offset also\r
33     var offset = div3d.offset();\r
34     offY = offset.top;\r
35     offX = offset.left;\r
36 \r
37     titlebox.innerHTML = 'Grenoble Site ' + nodes_gre.length + " nodes";\r
38     infobox.innerHTML = 'Node info : ';\r
39 \r
40     nodebox.value = "";\r
41 \r
42     camera = new THREE.PerspectiveCamera(75, window3DWidth / window3DHeight, 1, 10000);\r
43 \r
44     scene = new THREE.Scene();\r
45 \r
46     renderer = new THREE.CanvasRenderer();\r
47 \r
48     // jordan XXX div3d.appendChild(renderer.domElement);\r
49     div3d.append(renderer.domElement);\r
50 \r
51     window.addEventListener('resize', set3dsize, false);\r
52     scene.add(camera);\r
53     set3dsize();\r
54     \r
55 \r
56     var PI2 = Math.PI * 2;\r
57     var geometry = new THREE.Geometry();\r
58 \r
59     // let's find the center of the nodes\r
60     xmin = ymin = zmin = 0;\r
61     xmax = ymax = zmax = 0;\r
62 \r
63     for (var i = 0; i < nodes_gre.length; i++) {\r
64         if (nodes_gre[i][1] > xmax) xmax = nodes_gre[i][1];\r
65         if (nodes_gre[i][1] < xmin) xmin = nodes_gre[i][1];\r
66         if (nodes_gre[i][2] > ymax) ymax = nodes_gre[i][2];\r
67         if (nodes_gre[i][2] < ymin) ymin = nodes_gre[i][2];\r
68         if (nodes_gre[i][3] > zmax) zmax = nodes_gre[i][3];\r
69         if (nodes_gre[i][3] < zmin) zmin = nodes_gre[i][3];\r
70     }\r
71 \r
72     xcenter = (xmax + xmin) / 2;\r
73     ycenter = (ymax + ymin) / 2;\r
74     zcenter = (zmax + zmin) / 2;\r
75 \r
76     // display nodes as TREE particles\r
77     for (var i = 0; i < nodes_gre.length; i++) {\r
78         material = new THREE.ParticleCanvasMaterial({\r
79             color: 0xffffff,\r
80             program: function (context) {\r
81                 context.beginPath();\r
82                 context.arc(0, 0, 1, 0, PI2, true);\r
83                 context.closePath();\r
84                 context.fill();\r
85             }\r
86         });\r
87         particle = new THREE.Particle(material);\r
88         particle.name = nodes_gre[i][0];\r
89         particle.position.x = nodes_gre[i][1] - xcenter;\r
90         particle.position.y = nodes_gre[i][2] - ycenter;\r
91         particle.position.z = nodes_gre[i][3] - zcenter;\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     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 // TODO jordan\r
106     document.onmousedown = OnMouseDown;\r
107     document.onmouseup = OnMouseUp;\r
108     document.onmousemove = displayNodeInfo;\r
109     if (window.addEventListener)\r
110     /** DOMMouseScroll is for mozilla. */\r
111         window.addEventListener('DOMMouseScroll', wheel, false);\r
112     /** IE/Opera. */\r
113     window.onmousewheel = document.onmousewheel = wheel;\r
114     myrender();\r
115 }\r
116 \r
117 function set3dsize() {\r
118     // jordan\r
119     var offset = div3d.offset();\r
120     offY = offset.top;\r
121     offX = offset.left;\r
122     window3DWidth = div3d.width();\r
123     window3DHeight = div3d.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         if (selectedNodes.indexOf(objects[i].name) != -1) objects[i].material.color.setHex(0xff0000);\r
234         else objects[i].material.color.setHex(0xffffff);\r
235     renderer.render(scene, camera);\r
236 }\r
237 \r
238 // rigthbutton is used for rotations \r
239 function onDocumentMouseMoveRot(event) {\r
240     NewmouseX = event.clientX;\r
241     NewmouseY = event.clientY;\r
242     DeltaX = NewmouseX - mouseX;\r
243     DeltaY = NewmouseY - mouseY;\r
244 \r
245     mouseX = NewmouseX;\r
246     mouseY = NewmouseY;\r
247 \r
248     theta -= DeltaX;\r
249     phi += DeltaY;\r
250     if (phi > 180) phi = 180;\r
251     if (phi < -180) phi = -180;\r
252     myrender();\r
253 }\r
254 // mousewheel is used for zoom\r
255 function Zoom(delta) {\r
256     distance += delta * 5;\r
257     myrender();\r
258 }\r
259 \r
260 // return the graphical node under the mouse\r
261 function findNodeUnderMouse(event) {\r
262     var vector = new THREE.Vector3(((event.clientX - offX) / window3DWidth) * 2 - 1, -((event.clientY - offY) / window3DHeight) * 2 + 1, 0.5);\r
263     projector.unprojectVector(vector, camera);\r
264     var ray = new THREE.Ray(camera.position, vector.subSelf(camera.position).normalize());\r
265     var intersects = ray.intersectObjects(objects);\r
266     if (intersects.length > 0) {\r
267         return intersects[0];\r
268     } else return null;\r
269 }\r
270 \r
271 // display some info of the node under the mouse\r
272 function displayNodeInfo(e) {\r
273     obj = findNodeUnderMouse(e);\r
274     if (obj) infobox.innerHTML = 'Node info : number ' + obj.object.name + " with id " + nodes_gre[obj.object.id][4];\r
275     //    else infobox.innerHTML = e.clientX + "," + e.clientY + " - " + offX + "," + offY;\r
276 }\r
277 \r
278 // select/unselect nodes\r
279 function toggleNode(obj) {\r
280     nodeId = obj.object.name;\r
281     i = selectedNodes.indexOf(nodeId);\r
282     if (i == -1) selectedNodes.push(nodeId);\r
283     else selectedNodes.splice(i, 1);\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 != sTestEventType) 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