|
1 /* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* vim: set ts=2 et sw=2 tw=80: */ |
|
3 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 "use strict"; |
|
7 |
|
8 /** |
|
9 * Given the initialization data (sizes and information about |
|
10 * each DOM node) this worker sends back the arrays representing |
|
11 * vertices, texture coords, colors, indices and all the needed data for |
|
12 * rendering the DOM visualization mesh. |
|
13 * |
|
14 * Used in the TiltVisualization.Presenter object. |
|
15 */ |
|
16 self.onmessage = function TWC_onMessage(event) |
|
17 { |
|
18 let data = event.data; |
|
19 let maxGroupNodes = parseInt(data.maxGroupNodes); |
|
20 let style = data.style; |
|
21 let texWidth = data.texWidth; |
|
22 let texHeight = data.texHeight; |
|
23 let nodesInfo = data.nodesInfo; |
|
24 |
|
25 let mesh = { |
|
26 allVertices: [], |
|
27 groups: [], |
|
28 width: 0, |
|
29 height: 0 |
|
30 }; |
|
31 |
|
32 let vertices; |
|
33 let texCoord; |
|
34 let color; |
|
35 let stacksIndices; |
|
36 let wireframeIndices; |
|
37 let index; |
|
38 |
|
39 // seed the random function to get the same values each time |
|
40 // we're doing this to avoid ugly z-fighting with overlapping nodes |
|
41 self.random.seed(0); |
|
42 |
|
43 // go through all the dom nodes and compute the verts, texcoord etc. |
|
44 for (let n = 0, len = nodesInfo.length; n < len; n++) { |
|
45 |
|
46 // check if we need to start creating a new group |
|
47 if (n % maxGroupNodes === 0) { |
|
48 vertices = []; // recreate the arrays used to construct the 3D mesh data |
|
49 texCoord = []; |
|
50 color = []; |
|
51 stacksIndices = []; |
|
52 wireframeIndices = []; |
|
53 index = 0; |
|
54 } |
|
55 |
|
56 let info = nodesInfo[n]; |
|
57 let coord = info.coord; |
|
58 |
|
59 // calculate the stack x, y, z, width and height coordinates |
|
60 let z = coord.depth + coord.thickness; |
|
61 let y = coord.top; |
|
62 let x = coord.left; |
|
63 let w = coord.width; |
|
64 let h = coord.height; |
|
65 |
|
66 // the maximum texture size slices the visualization mesh where needed |
|
67 if (x + w > texWidth) { |
|
68 w = texWidth - x; |
|
69 } |
|
70 if (y + h > texHeight) { |
|
71 h = texHeight - y; |
|
72 } |
|
73 |
|
74 x += self.random.next(); |
|
75 y += self.random.next(); |
|
76 w -= self.random.next() * 0.1; |
|
77 h -= self.random.next() * 0.1; |
|
78 |
|
79 let xpw = x + w; |
|
80 let yph = y + h; |
|
81 let zmt = coord.depth; |
|
82 |
|
83 let xotw = x / texWidth; |
|
84 let yoth = y / texHeight; |
|
85 let xpwotw = xpw / texWidth; |
|
86 let yphoth = yph / texHeight; |
|
87 |
|
88 // calculate the margin fill color |
|
89 let fill = style[info.name] || style.highlight.defaultFill; |
|
90 |
|
91 let r = fill[0]; |
|
92 let g = fill[1]; |
|
93 let b = fill[2]; |
|
94 let g10 = r * 1.1; |
|
95 let g11 = g * 1.1; |
|
96 let g12 = b * 1.1; |
|
97 let g20 = r * 0.6; |
|
98 let g21 = g * 0.6; |
|
99 let g22 = b * 0.6; |
|
100 |
|
101 // compute the vertices |
|
102 vertices.push(x, y, z, /* front */ // 0 |
|
103 x, yph, z, // 1 |
|
104 xpw, yph, z, // 2 |
|
105 xpw, y, z, // 3 |
|
106 // we don't duplicate vertices for the left and right faces, because |
|
107 // they can be reused from the bottom and top faces; we do, however, |
|
108 // duplicate some vertices from front face, because it has custom |
|
109 // texture coordinates which are not shared by the other faces |
|
110 x, y, z, /* front */ // 4 |
|
111 x, yph, z, // 5 |
|
112 xpw, yph, z, // 6 |
|
113 xpw, y, z, // 7 |
|
114 x, y, zmt, /* back */ // 8 |
|
115 x, yph, zmt, // 9 |
|
116 xpw, yph, zmt, // 10 |
|
117 xpw, y, zmt); // 11 |
|
118 |
|
119 // compute the texture coordinates |
|
120 texCoord.push(xotw, yoth, |
|
121 xotw, yphoth, |
|
122 xpwotw, yphoth, |
|
123 xpwotw, yoth, |
|
124 -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0); |
|
125 |
|
126 // compute the colors for each vertex in the mesh |
|
127 color.push(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
|
128 g10, g11, g12, |
|
129 g10, g11, g12, |
|
130 g10, g11, g12, |
|
131 g10, g11, g12, |
|
132 g20, g21, g22, |
|
133 g20, g21, g22, |
|
134 g20, g21, g22, |
|
135 g20, g21, g22); |
|
136 |
|
137 let i = index; // number of vertex points, used to create the indices array |
|
138 let ip1 = i + 1; |
|
139 let ip2 = ip1 + 1; |
|
140 let ip3 = ip2 + 1; |
|
141 let ip4 = ip3 + 1; |
|
142 let ip5 = ip4 + 1; |
|
143 let ip6 = ip5 + 1; |
|
144 let ip7 = ip6 + 1; |
|
145 let ip8 = ip7 + 1; |
|
146 let ip9 = ip8 + 1; |
|
147 let ip10 = ip9 + 1; |
|
148 let ip11 = ip10 + 1; |
|
149 |
|
150 // compute the stack indices |
|
151 stacksIndices.unshift(i, ip1, ip2, i, ip2, ip3, |
|
152 ip8, ip9, ip5, ip8, ip5, ip4, |
|
153 ip7, ip6, ip10, ip7, ip10, ip11, |
|
154 ip8, ip4, ip7, ip8, ip7, ip11, |
|
155 ip5, ip9, ip10, ip5, ip10, ip6); |
|
156 |
|
157 // compute the wireframe indices |
|
158 if (coord.thickness !== 0) { |
|
159 wireframeIndices.unshift(i, ip1, ip1, ip2, |
|
160 ip2, ip3, ip3, i, |
|
161 ip8, i, ip9, ip1, |
|
162 ip11, ip3, ip10, ip2); |
|
163 } |
|
164 |
|
165 // there are 12 vertices in a stack representing a node |
|
166 index += 12; |
|
167 |
|
168 // set the maximum mesh width and height to calculate the center offset |
|
169 mesh.width = Math.max(w, mesh.width); |
|
170 mesh.height = Math.max(h, mesh.height); |
|
171 |
|
172 // check if we need to save the currently active group; this happens after |
|
173 // we filled all the "slots" in a group or there aren't any remaining nodes |
|
174 if (((n + 1) % maxGroupNodes === 0) || (n === len - 1)) { |
|
175 mesh.groups.push({ |
|
176 vertices: vertices, |
|
177 texCoord: texCoord, |
|
178 color: color, |
|
179 stacksIndices: stacksIndices, |
|
180 wireframeIndices: wireframeIndices |
|
181 }); |
|
182 mesh.allVertices = mesh.allVertices.concat(vertices); |
|
183 } |
|
184 } |
|
185 |
|
186 self.postMessage(mesh); |
|
187 close(); |
|
188 }; |
|
189 |
|
190 /** |
|
191 * Utility functions for generating random numbers using the Alea algorithm. |
|
192 */ |
|
193 self.random = { |
|
194 |
|
195 /** |
|
196 * The generator function, automatically created with seed 0. |
|
197 */ |
|
198 _generator: null, |
|
199 |
|
200 /** |
|
201 * Returns a new random number between [0..1) |
|
202 */ |
|
203 next: function RNG_next() |
|
204 { |
|
205 return this._generator(); |
|
206 }, |
|
207 |
|
208 /** |
|
209 * From http://baagoe.com/en/RandomMusings/javascript |
|
210 * Johannes Baagoe <baagoe@baagoe.com>, 2010 |
|
211 * |
|
212 * Seeds a random generator function with a set of passed arguments. |
|
213 */ |
|
214 seed: function RNG_seed() |
|
215 { |
|
216 let s0 = 0; |
|
217 let s1 = 0; |
|
218 let s2 = 0; |
|
219 let c = 1; |
|
220 |
|
221 if (arguments.length === 0) { |
|
222 return this.seed(+new Date()); |
|
223 } else { |
|
224 s0 = this.mash(" "); |
|
225 s1 = this.mash(" "); |
|
226 s2 = this.mash(" "); |
|
227 |
|
228 for (let i = 0, len = arguments.length; i < len; i++) { |
|
229 s0 -= this.mash(arguments[i]); |
|
230 if (s0 < 0) { |
|
231 s0 += 1; |
|
232 } |
|
233 s1 -= this.mash(arguments[i]); |
|
234 if (s1 < 0) { |
|
235 s1 += 1; |
|
236 } |
|
237 s2 -= this.mash(arguments[i]); |
|
238 if (s2 < 0) { |
|
239 s2 += 1; |
|
240 } |
|
241 } |
|
242 |
|
243 let random = function() { |
|
244 let t = 2091639 * s0 + c * 2.3283064365386963e-10; // 2^-32 |
|
245 s0 = s1; |
|
246 s1 = s2; |
|
247 return (s2 = t - (c = t | 0)); |
|
248 }; |
|
249 random.uint32 = function() { |
|
250 return random() * 0x100000000; // 2^32 |
|
251 }; |
|
252 random.fract53 = function() { |
|
253 return random() + |
|
254 (random() * 0x200000 | 0) * 1.1102230246251565e-16; // 2^-53 |
|
255 }; |
|
256 return (this._generator = random); |
|
257 } |
|
258 }, |
|
259 |
|
260 /** |
|
261 * From http://baagoe.com/en/RandomMusings/javascript |
|
262 * Johannes Baagoe <baagoe@baagoe.com>, 2010 |
|
263 */ |
|
264 mash: function RNG_mash(data) |
|
265 { |
|
266 let h, n = 0xefc8249d; |
|
267 |
|
268 for (let i = 0, data = data.toString(), len = data.length; i < len; i++) { |
|
269 n += data.charCodeAt(i); |
|
270 h = 0.02519603282416938 * n; |
|
271 n = h >>> 0; |
|
272 h -= n; |
|
273 h *= n; |
|
274 n = h >>> 0; |
|
275 h -= n; |
|
276 n += h * 0x100000000; // 2^32 |
|
277 } |
|
278 return (n >>> 0) * 2.3283064365386963e-10; // 2^-32 |
|
279 } |
|
280 }; |