michael@0: /* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=2 et sw=2 tw=80: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: "use strict"; michael@0: michael@0: /** michael@0: * Given the initialization data (sizes and information about michael@0: * each DOM node) this worker sends back the arrays representing michael@0: * vertices, texture coords, colors, indices and all the needed data for michael@0: * rendering the DOM visualization mesh. michael@0: * michael@0: * Used in the TiltVisualization.Presenter object. michael@0: */ michael@0: self.onmessage = function TWC_onMessage(event) michael@0: { michael@0: let data = event.data; michael@0: let maxGroupNodes = parseInt(data.maxGroupNodes); michael@0: let style = data.style; michael@0: let texWidth = data.texWidth; michael@0: let texHeight = data.texHeight; michael@0: let nodesInfo = data.nodesInfo; michael@0: michael@0: let mesh = { michael@0: allVertices: [], michael@0: groups: [], michael@0: width: 0, michael@0: height: 0 michael@0: }; michael@0: michael@0: let vertices; michael@0: let texCoord; michael@0: let color; michael@0: let stacksIndices; michael@0: let wireframeIndices; michael@0: let index; michael@0: michael@0: // seed the random function to get the same values each time michael@0: // we're doing this to avoid ugly z-fighting with overlapping nodes michael@0: self.random.seed(0); michael@0: michael@0: // go through all the dom nodes and compute the verts, texcoord etc. michael@0: for (let n = 0, len = nodesInfo.length; n < len; n++) { michael@0: michael@0: // check if we need to start creating a new group michael@0: if (n % maxGroupNodes === 0) { michael@0: vertices = []; // recreate the arrays used to construct the 3D mesh data michael@0: texCoord = []; michael@0: color = []; michael@0: stacksIndices = []; michael@0: wireframeIndices = []; michael@0: index = 0; michael@0: } michael@0: michael@0: let info = nodesInfo[n]; michael@0: let coord = info.coord; michael@0: michael@0: // calculate the stack x, y, z, width and height coordinates michael@0: let z = coord.depth + coord.thickness; michael@0: let y = coord.top; michael@0: let x = coord.left; michael@0: let w = coord.width; michael@0: let h = coord.height; michael@0: michael@0: // the maximum texture size slices the visualization mesh where needed michael@0: if (x + w > texWidth) { michael@0: w = texWidth - x; michael@0: } michael@0: if (y + h > texHeight) { michael@0: h = texHeight - y; michael@0: } michael@0: michael@0: x += self.random.next(); michael@0: y += self.random.next(); michael@0: w -= self.random.next() * 0.1; michael@0: h -= self.random.next() * 0.1; michael@0: michael@0: let xpw = x + w; michael@0: let yph = y + h; michael@0: let zmt = coord.depth; michael@0: michael@0: let xotw = x / texWidth; michael@0: let yoth = y / texHeight; michael@0: let xpwotw = xpw / texWidth; michael@0: let yphoth = yph / texHeight; michael@0: michael@0: // calculate the margin fill color michael@0: let fill = style[info.name] || style.highlight.defaultFill; michael@0: michael@0: let r = fill[0]; michael@0: let g = fill[1]; michael@0: let b = fill[2]; michael@0: let g10 = r * 1.1; michael@0: let g11 = g * 1.1; michael@0: let g12 = b * 1.1; michael@0: let g20 = r * 0.6; michael@0: let g21 = g * 0.6; michael@0: let g22 = b * 0.6; michael@0: michael@0: // compute the vertices michael@0: vertices.push(x, y, z, /* front */ // 0 michael@0: x, yph, z, // 1 michael@0: xpw, yph, z, // 2 michael@0: xpw, y, z, // 3 michael@0: // we don't duplicate vertices for the left and right faces, because michael@0: // they can be reused from the bottom and top faces; we do, however, michael@0: // duplicate some vertices from front face, because it has custom michael@0: // texture coordinates which are not shared by the other faces michael@0: x, y, z, /* front */ // 4 michael@0: x, yph, z, // 5 michael@0: xpw, yph, z, // 6 michael@0: xpw, y, z, // 7 michael@0: x, y, zmt, /* back */ // 8 michael@0: x, yph, zmt, // 9 michael@0: xpw, yph, zmt, // 10 michael@0: xpw, y, zmt); // 11 michael@0: michael@0: // compute the texture coordinates michael@0: texCoord.push(xotw, yoth, michael@0: xotw, yphoth, michael@0: xpwotw, yphoth, michael@0: xpwotw, yoth, michael@0: -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0); michael@0: michael@0: // compute the colors for each vertex in the mesh michael@0: color.push(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, michael@0: g10, g11, g12, michael@0: g10, g11, g12, michael@0: g10, g11, g12, michael@0: g10, g11, g12, michael@0: g20, g21, g22, michael@0: g20, g21, g22, michael@0: g20, g21, g22, michael@0: g20, g21, g22); michael@0: michael@0: let i = index; // number of vertex points, used to create the indices array michael@0: let ip1 = i + 1; michael@0: let ip2 = ip1 + 1; michael@0: let ip3 = ip2 + 1; michael@0: let ip4 = ip3 + 1; michael@0: let ip5 = ip4 + 1; michael@0: let ip6 = ip5 + 1; michael@0: let ip7 = ip6 + 1; michael@0: let ip8 = ip7 + 1; michael@0: let ip9 = ip8 + 1; michael@0: let ip10 = ip9 + 1; michael@0: let ip11 = ip10 + 1; michael@0: michael@0: // compute the stack indices michael@0: stacksIndices.unshift(i, ip1, ip2, i, ip2, ip3, michael@0: ip8, ip9, ip5, ip8, ip5, ip4, michael@0: ip7, ip6, ip10, ip7, ip10, ip11, michael@0: ip8, ip4, ip7, ip8, ip7, ip11, michael@0: ip5, ip9, ip10, ip5, ip10, ip6); michael@0: michael@0: // compute the wireframe indices michael@0: if (coord.thickness !== 0) { michael@0: wireframeIndices.unshift(i, ip1, ip1, ip2, michael@0: ip2, ip3, ip3, i, michael@0: ip8, i, ip9, ip1, michael@0: ip11, ip3, ip10, ip2); michael@0: } michael@0: michael@0: // there are 12 vertices in a stack representing a node michael@0: index += 12; michael@0: michael@0: // set the maximum mesh width and height to calculate the center offset michael@0: mesh.width = Math.max(w, mesh.width); michael@0: mesh.height = Math.max(h, mesh.height); michael@0: michael@0: // check if we need to save the currently active group; this happens after michael@0: // we filled all the "slots" in a group or there aren't any remaining nodes michael@0: if (((n + 1) % maxGroupNodes === 0) || (n === len - 1)) { michael@0: mesh.groups.push({ michael@0: vertices: vertices, michael@0: texCoord: texCoord, michael@0: color: color, michael@0: stacksIndices: stacksIndices, michael@0: wireframeIndices: wireframeIndices michael@0: }); michael@0: mesh.allVertices = mesh.allVertices.concat(vertices); michael@0: } michael@0: } michael@0: michael@0: self.postMessage(mesh); michael@0: close(); michael@0: }; michael@0: michael@0: /** michael@0: * Utility functions for generating random numbers using the Alea algorithm. michael@0: */ michael@0: self.random = { michael@0: michael@0: /** michael@0: * The generator function, automatically created with seed 0. michael@0: */ michael@0: _generator: null, michael@0: michael@0: /** michael@0: * Returns a new random number between [0..1) michael@0: */ michael@0: next: function RNG_next() michael@0: { michael@0: return this._generator(); michael@0: }, michael@0: michael@0: /** michael@0: * From http://baagoe.com/en/RandomMusings/javascript michael@0: * Johannes Baagoe , 2010 michael@0: * michael@0: * Seeds a random generator function with a set of passed arguments. michael@0: */ michael@0: seed: function RNG_seed() michael@0: { michael@0: let s0 = 0; michael@0: let s1 = 0; michael@0: let s2 = 0; michael@0: let c = 1; michael@0: michael@0: if (arguments.length === 0) { michael@0: return this.seed(+new Date()); michael@0: } else { michael@0: s0 = this.mash(" "); michael@0: s1 = this.mash(" "); michael@0: s2 = this.mash(" "); michael@0: michael@0: for (let i = 0, len = arguments.length; i < len; i++) { michael@0: s0 -= this.mash(arguments[i]); michael@0: if (s0 < 0) { michael@0: s0 += 1; michael@0: } michael@0: s1 -= this.mash(arguments[i]); michael@0: if (s1 < 0) { michael@0: s1 += 1; michael@0: } michael@0: s2 -= this.mash(arguments[i]); michael@0: if (s2 < 0) { michael@0: s2 += 1; michael@0: } michael@0: } michael@0: michael@0: let random = function() { michael@0: let t = 2091639 * s0 + c * 2.3283064365386963e-10; // 2^-32 michael@0: s0 = s1; michael@0: s1 = s2; michael@0: return (s2 = t - (c = t | 0)); michael@0: }; michael@0: random.uint32 = function() { michael@0: return random() * 0x100000000; // 2^32 michael@0: }; michael@0: random.fract53 = function() { michael@0: return random() + michael@0: (random() * 0x200000 | 0) * 1.1102230246251565e-16; // 2^-53 michael@0: }; michael@0: return (this._generator = random); michael@0: } michael@0: }, michael@0: michael@0: /** michael@0: * From http://baagoe.com/en/RandomMusings/javascript michael@0: * Johannes Baagoe , 2010 michael@0: */ michael@0: mash: function RNG_mash(data) michael@0: { michael@0: let h, n = 0xefc8249d; michael@0: michael@0: for (let i = 0, data = data.toString(), len = data.length; i < len; i++) { michael@0: n += data.charCodeAt(i); michael@0: h = 0.02519603282416938 * n; michael@0: n = h >>> 0; michael@0: h -= n; michael@0: h *= n; michael@0: n = h >>> 0; michael@0: h -= n; michael@0: n += h * 0x100000000; // 2^32 michael@0: } michael@0: return (n >>> 0) * 2.3283064365386963e-10; // 2^-32 michael@0: } michael@0: };