function FSS(container, output){ FSS = { FRONT: 0, BACK: 1, DOUBLE: 2, SVGNS: "http://www.w3.org/2000/svg" }; FSS.Array = "function" === typeof Float32Array ? Float32Array: Array; FSS.Utils = { isNumber: function(b) { return ! isNaN(parseFloat(b)) && isFinite(b) } }; (function() { for (var b = 0, d = ["ms", "moz", "webkit", "o"], h = 0; h < d.length && !window.requestAnimationFrame; ++h) window.requestAnimationFrame = window[d[h] + "RequestAnimationFrame"], window.cancelAnimationFrame = window[d[h] + "CancelAnimationFrame"] || window[d[h] + "CancelRequestAnimationFrame"]; window.requestAnimationFrame || (window.requestAnimationFrame = function(d, h) { var n = (new Date).getTime(), s = Math.max(0, 16 - (n - b)), k = window.setTimeout(function() { d(n + s) }, s); b = n + s; return k }); window.cancelAnimationFrame || (window.cancelAnimationFrame = function(b) { clearTimeout(b) }) })(); Math.PIM2 = 2 * Math.PI; Math.PID2 = Math.PI / 2; Math.randomInRange = function(b, d) { return b + (d - b) * Math.random() }; Math.clamp = function(b, d, h) { b = Math.max(b, d); return b = Math.min(b, h) }; FSS.Vector3 = { create: function(b, d, h) { var g = new FSS.Array(3); this.set(g, b, d, h); return g }, clone: function(b) { var d = this.create(); this.copy(d, b); return d }, set: function(b, d, h, g) { b[0] = d || 0; b[1] = h || 0; b[2] = g || 0; return this }, setX: function(b, d) { b[0] = d || 0; return this }, setY: function(b, d) { b[1] = d || 0; return this }, setZ: function(b, d) { b[2] = d || 0; return this }, copy: function(b, d) { b[0] = d[0]; b[1] = d[1]; b[2] = d[2]; return this }, add: function(b, d) { b[0] += d[0]; b[1] += d[1]; b[2] += d[2]; return this }, addVectors: function(b, d, h) { b[0] = d[0] + h[0]; b[1] = d[1] + h[1]; b[2] = d[2] + h[2]; return this }, addScalar: function(b, d) { b[0] += d; b[1] += d; b[2] += d; return this }, subtract: function(b, d) { b[0] -= d[0]; b[1] -= d[1]; b[2] -= d[2]; return this }, subtractVectors: function(b, d, h) { b[0] = d[0] - h[0]; b[1] = d[1] - h[1]; b[2] = d[2] - h[2]; return this }, subtractScalar: function(b, d) { b[0] -= d; b[1] -= d; b[2] -= d; return this }, multiply: function(b, d) { b[0] *= d[0]; b[1] *= d[1]; b[2] *= d[2]; return this }, multiplyVectors: function(b, d, h) { b[0] = d[0] * h[0]; b[1] = d[1] * h[1]; b[2] = d[2] * h[2]; return this }, multiplyScalar: function(b, d) { b[0] *= d; b[1] *= d; b[2] *= d; return this }, divide: function(b, d) { b[0] /= d[0]; b[1] /= d[1]; b[2] /= d[2]; return this }, divideVectors: function(b, d, h) { b[0] = d[0] / h[0]; b[1] = d[1] / h[1]; b[2] = d[2] / h[2]; return this }, divideScalar: function(b, d) { 0 !== d ? (b[0] /= d, b[1] /= d, b[2] /= d) : (b[0] = 0, b[1] = 0, b[2] = 0); return this }, cross: function(b, d) { var h = b[0], g = b[1], l = b[2]; b[0] = g * d[2] - l * d[1]; b[1] = l * d[0] - h * d[2]; b[2] = h * d[1] - g * d[0]; return this }, crossVectors: function(b, d, h) { b[0] = d[1] * h[2] - d[2] * h[1]; b[1] = d[2] * h[0] - d[0] * h[2]; b[2] = d[0] * h[1] - d[1] * h[0]; return this }, min: function(b, d) { b[0] < d && (b[0] = d); b[1] < d && (b[1] = d); b[2] < d && (b[2] = d); return this }, max: function(b, d) { b[0] > d && (b[0] = d); b[1] > d && (b[1] = d); b[2] > d && (b[2] = d); return this }, clamp: function(b, d, h) { this.min(b, d); this.max(b, h); return this }, limit: function(b, d, h) { var g = this.length(b); null !== d && g < d ? this.setLength(b, d) : null !== h && g > h && this.setLength(b, h); return this }, dot: function(b, d) { return b[0] * d[0] + b[1] * d[1] + b[2] * d[2] }, normalise: function(b) { return this.divideScalar(b, this.length(b)) }, negate: function(b) { return this.multiplyScalar(b, -1) }, distanceSquared: function(b, d) { var h = b[0] - d[0], g = b[1] - d[1], l = b[2] - d[2]; return h * h + g * g + l * l }, distance: function(b, d) { return Math.sqrt(this.distanceSquared(b, d)) }, lengthSquared: function(b) { return b[0] * b[0] + b[1] * b[1] + b[2] * b[2] }, length: function(b) { return Math.sqrt(this.lengthSquared(b)) }, setLength: function(b, d) { var h = this.length(b); 0 !== h && d !== h && this.multiplyScalar(b, d / h); return this } }; FSS.Vector4 = { create: function(b, d, h, g) { g = new FSS.Array(4); this.set(g, b, d, h); return g }, set: function(b, d, h, g, l) { b[0] = d || 0; b[1] = h || 0; b[2] = g || 0; b[3] = l || 0; return this }, setX: function(b, d) { b[0] = d || 0; return this }, setY: function(b, d) { b[1] = d || 0; return this }, setZ: function(b, d) { b[2] = d || 0; return this }, setW: function(b, d) { b[3] = d || 0; return this }, add: function(b, d) { b[0] += d[0]; b[1] += d[1]; b[2] += d[2]; b[3] += d[3]; return this }, multiplyVectors: function(b, d, h) { b[0] = d[0] * h[0]; b[1] = d[1] * h[1]; b[2] = d[2] * h[2]; b[3] = d[3] * h[3]; return this }, multiplyScalar: function(b, d) { b[0] *= d; b[1] *= d; b[2] *= d; b[3] *= d; return this }, min: function(b, d) { b[0] < d && (b[0] = d); b[1] < d && (b[1] = d); b[2] < d && (b[2] = d); b[3] < d && (b[3] = d); return this }, max: function(b, d) { b[0] > d && (b[0] = d); b[1] > d && (b[1] = d); b[2] > d && (b[2] = d); b[3] > d && (b[3] = d); return this }, clamp: function(b, d, h) { this.min(b, d); this.max(b, h); return this } }; FSS.Color = function(b, d) { this.rgba = FSS.Vector4.create(); this.hex = b || "#000000"; this.opacity = FSS.Utils.isNumber(d) ? d: 1; this.set(this.hex, this.opacity) }; FSS.Color.prototype = { set: function(b, d) { b = b.replace("#", ""); var h = b.length / 3; this.rgba[0] = parseInt(b.substring(0 * h, 1 * h), 16) / 255; this.rgba[1] = parseInt(b.substring(1 * h, 2 * h), 16) / 255; this.rgba[2] = parseInt(b.substring(2 * h, 3 * h), 16) / 255; this.rgba[3] = FSS.Utils.isNumber(d) ? d: this.rgba[3]; return this }, hexify: function(b) { b = Math.ceil(255 * b).toString(16); 1 === b.length && (b = "0" + b); return b }, format: function() { var b = this.hexify(this.rgba[0]), d = this.hexify(this.rgba[1]), h = this.hexify(this.rgba[2]); return this.hex = "#" + b + d + h } }; FSS.Object = function() { this.position = FSS.Vector3.create() }; FSS.Object.prototype = { setPosition: function(b, d, h) { FSS.Vector3.set(this.position, b, d, h); return this } }; FSS.Light = function(b, d) { FSS.Object.call(this); this.ambient = new FSS.Color(b || "#FFFFFF"); this.diffuse = new FSS.Color(d || "#FFFFFF"); this.ray = FSS.Vector3.create() }; FSS.Light.prototype = Object.create(FSS.Object.prototype); FSS.Vertex = function(b, d, h) { this.position = FSS.Vector3.create(b, d, h) }; FSS.Vertex.prototype = { setPosition: function(b, d, h) { FSS.Vector3.set(this.position, b, d, h); return this } }; FSS.Triangle = function(b, d, h) { this.a = b || new FSS.Vertex; this.b = d || new FSS.Vertex; this.c = h || new FSS.Vertex; this.vertices = [this.a, this.b, this.c]; this.u = FSS.Vector3.create(); this.v = FSS.Vector3.create(); this.centroid = FSS.Vector3.create(); this.normal = FSS.Vector3.create(); this.color = new FSS.Color; this.polygon = document.createElementNS(FSS.SVGNS, "polygon"); this.polygon.setAttributeNS(null, "stroke-linejoin", "round"); this.polygon.setAttributeNS(null, "stroke-miterlimit", "1"); this.polygon.setAttributeNS(null, "stroke-width", "1"); this.computeCentroid(); this.computeNormal() }; FSS.Triangle.prototype = { computeCentroid: function() { this.centroid[0] = this.a.position[0] + this.b.position[0] + this.c.position[0]; this.centroid[1] = this.a.position[1] + this.b.position[1] + this.c.position[1]; this.centroid[2] = this.a.position[2] + this.b.position[2] + this.c.position[2]; FSS.Vector3.divideScalar(this.centroid, 3); return this }, computeNormal: function() { FSS.Vector3.subtractVectors(this.u, this.b.position, this.a.position); FSS.Vector3.subtractVectors(this.v, this.c.position, this.a.position); FSS.Vector3.crossVectors(this.normal, this.u, this.v); FSS.Vector3.normalise(this.normal); return this } }; FSS.Geometry = function() { this.vertices = []; this.triangles = []; this.dirty = !1 }; FSS.Geometry.prototype = { update: function() { if (this.dirty) { var b, d; for (b = this.triangles.length - 1; 0 <= b; b--) d = this.triangles[b], d.computeCentroid(), d.computeNormal(); this.dirty = !1 } return this } }; FSS.Plane = function(b, d, h, g) { FSS.Geometry.call(this); this.width = b || 100; this.height = d || 100; this.segments = h || 4; this.slices = g || 4; this.segmentWidth = this.width / this.segments; this.sliceHeight = this.height / this.slices; var l, n, s; h = []; l = -0.5 * this.width; n = 0.5 * this.height; for (b = 0; b <= this.segments; b++) for (h.push([]), d = 0; d <= this.slices; d++) g = new FSS.Vertex(l + b * this.segmentWidth, n - d * this.sliceHeight), h[b].push(g), this.vertices.push(g); for (b = 0; b < this.segments; b++) for (d = 0; d < this.slices; d++) g = h[b + 0][d + 0], l = h[b + 0][d + 1], n = h[b + 1][d + 0], s = h[b + 1][d + 1], t0 = new FSS.Triangle(g, l, n), t1 = new FSS.Triangle(n, l, s), this.triangles.push(t0, t1) }; FSS.Plane.prototype = Object.create(FSS.Geometry.prototype); FSS.Material = function(b, d) { this.ambient = new FSS.Color(b || "#444444"); this.diffuse = new FSS.Color(d || "#FFFFFF"); this.slave = new FSS.Color }; FSS.Mesh = function(b, d) { FSS.Object.call(this); this.geometry = b || new FSS.Geometry; this.material = d || new FSS.Material; this.side = FSS.FRONT; this.visible = !0 }; FSS.Mesh.prototype = Object.create(FSS.Object.prototype); FSS.Mesh.prototype.update = function(b, d) { var h, g, l, n, s; this.geometry.update(); if (d) for (h = this.geometry.triangles.length - 1; 0 <= h; h--) { g = this.geometry.triangles[h]; FSS.Vector4.set(g.color.rgba); for (l = b.length - 1; 0 <= l; l--) n = b[l], FSS.Vector3.subtractVectors(n.ray, n.position, g.centroid), FSS.Vector3.normalise(n.ray), s = FSS.Vector3.dot(g.normal, n.ray), this.side === FSS.FRONT ? s = Math.max(s, 0) : this.side === FSS.BACK ? s = Math.abs(Math.min(s, 0)) : this.side === FSS.DOUBLE && (s = Math.max(Math.abs(s), 0)), FSS.Vector4.multiplyVectors(this.material.slave.rgba, this.material.ambient.rgba, n.ambient.rgba), FSS.Vector4.add(g.color.rgba, this.material.slave.rgba), FSS.Vector4.multiplyVectors(this.material.slave.rgba, this.material.diffuse.rgba, n.diffuse.rgba), FSS.Vector4.multiplyScalar(this.material.slave.rgba, s), FSS.Vector4.add(g.color.rgba, this.material.slave.rgba); FSS.Vector4.clamp(g.color.rgba, 0, 1) } return this }; FSS.Scene = function() { this.meshes = []; this.lights = [] }; FSS.Scene.prototype = { add: function(b) { b instanceof FSS.Mesh && !~this.meshes.indexOf(b) ? this.meshes.push(b) : b instanceof FSS.Light && !~this.lights.indexOf(b) && this.lights.push(b); return this }, remove: function(b) { b instanceof FSS.Mesh && ~this.meshes.indexOf(b) ? this.meshes.splice(this.meshes.indexOf(b), 1) : b instanceof FSS.Light && ~this.lights.indexOf(b) && this.lights.splice(this.lights.indexOf(b), 1); return this } }; FSS.Renderer = function() { this.halfHeight = this.halfWidth = this.height = this.width = 0 }; FSS.Renderer.prototype = { setSize: function(b, d) { if (this.width !== b || this.height !== d) return this.width = b, this.height = d, this.halfWidth = 0.5 * this.width, this.halfHeight = 0.5 * this.height, this }, clear: function() { return this }, render: function(b) { return this } }; FSS.CanvasRenderer = function() { FSS.Renderer.call(this); this.element = document.createElement("canvas"); this.element.style.display = "block"; this.context = this.element.getContext("2d"); this.setSize(this.element.width, this.element.height) }; FSS.CanvasRenderer.prototype = Object.create(FSS.Renderer.prototype); FSS.CanvasRenderer.prototype.setSize = function(b, d) { FSS.Renderer.prototype.setSize.call(this, b, d); this.element.width = b; this.element.height = d; this.context.setTransform(1, 0, 0, -1, this.halfWidth, this.halfHeight); return this }; FSS.CanvasRenderer.prototype.clear = function() { FSS.Renderer.prototype.clear.call(this); this.context.clearRect( - this.halfWidth, -this.halfHeight, this.width, this.height); return this }; FSS.CanvasRenderer.prototype.render = function(b) { FSS.Renderer.prototype.render.call(this, b); var d, h, g, l, n; this.clear(); this.context.lineJoin = "round"; this.context.lineWidth = 1; for (d = b.meshes.length - 1; 0 <= d; d--) if (h = b.meshes[d], h.visible) for (h.update(b.lights, !0), g = h.geometry.triangles.length - 1; 0 <= g; g--) l = h.geometry.triangles[g], n = l.color.format(), this.context.beginPath(), this.context.moveTo(l.a.position[0], l.a.position[1]), this.context.lineTo(l.b.position[0], l.b.position[1]), this.context.lineTo(l.c.position[0], l.c.position[1]), this.context.closePath(), this.context.strokeStyle = n, this.context.fillStyle = n, this.context.stroke(), this.context.fill(); return this }; FSS.WebGLRenderer = function() { FSS.Renderer.call(this); this.element = document.createElement("canvas"); this.element.style.display = "block"; this.lights = this.vertices = null; this.gl = this.getContext(this.element, { preserveDrawingBuffer: !1, premultipliedAlpha: !0, antialias: !0, stencil: !0, alpha: !0 }); if (this.unsupported = !this.gl) return "WebGL is not supported by your browser."; this.gl.clearColor(0, 0, 0, 0); this.gl.enable(this.gl.DEPTH_TEST); this.setSize(this.element.width, this.element.height) }; FSS.WebGLRenderer.prototype = Object.create(FSS.Renderer.prototype); FSS.WebGLRenderer.prototype.getContext = function(b, d) { var h = !1; try { if (! (h = b.getContext("experimental-webgl", d))) throw "Error creating WebGL context."; } catch(g) { // console.error(g); } return h }; FSS.WebGLRenderer.prototype.setSize = function(b, d) { FSS.Renderer.prototype.setSize.call(this, b, d); if (!this.unsupported) return this.element.width = b, this.element.height = d, this.gl.viewport(0, 0, b, d), this }; FSS.WebGLRenderer.prototype.clear = function() { FSS.Renderer.prototype.clear.call(this); if (!this.unsupported) return this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT), this }; FSS.WebGLRenderer.prototype.render = function(b) { FSS.Renderer.prototype.render.call(this, b); if (!this.unsupported) { var d, h, g, l, n, s, k, m; s = !1; var r = b.lights.length, u, E, y, z = 0; this.clear(); if (this.lights !== r) if (this.lights = r, 0 < this.lights) this.buildProgram(r); else return; if (this.program) { for (d = b.meshes.length - 1; 0 <= d; d--) h = b.meshes[d], h.geometry.dirty && (s = !0), h.update(b.lights, !1), z += 3 * h.geometry.triangles.length; if (s || this.vertices !== z) for (k in this.vertices = z, this.program.attributes) { s = this.program.attributes[k]; s.data = new FSS.Array(z * s.size); u = 0; for (d = b.meshes.length - 1; 0 <= d; d--) for (h = b.meshes[d], g = 0, l = h.geometry.triangles.length; g < l; g++) for (n = h.geometry.triangles[g], E = 0, y = n.vertices.length; E < y; E++) { vertex = n.vertices[E]; switch (k) { case "side": this.setBufferData(u, s, h.side); break; case "position": this.setBufferData(u, s, vertex.position); break; case "centroid": this.setBufferData(u, s, n.centroid); break; case "normal": this.setBufferData(u, s, n.normal); break; case "ambient": this.setBufferData(u, s, h.material.ambient.rgba); break; case "diffuse": this.setBufferData(u, s, h.material.diffuse.rgba) } u++ } this.gl.bindBuffer(this.gl.ARRAY_BUFFER, s.buffer); this.gl.bufferData(this.gl.ARRAY_BUFFER, s.data, this.gl.DYNAMIC_DRAW); this.gl.enableVertexAttribArray(s.location); this.gl.vertexAttribPointer(s.location, s.size, this.gl.FLOAT, !1, 0, 0) } this.setBufferData(0, this.program.uniforms.resolution, [this.width, this.height, this.width]); for (s = r - 1; 0 <= s; s--) d = b.lights[s], this.setBufferData(s, this.program.uniforms.lightPosition, d.position), this.setBufferData(s, this.program.uniforms.lightAmbient, d.ambient.rgba), this.setBufferData(s, this.program.uniforms.lightDiffuse, d.diffuse.rgba); for (m in this.program.uniforms) switch (s = this.program.uniforms[m], d = s.location, b = s.data, s.structure) { case "3f": this.gl.uniform3f(d, b[0], b[1], b[2]); break; case "3fv": this.gl.uniform3fv(d, b); break; case "4fv": this.gl.uniform4fv(d, b) } } this.gl.drawArrays(this.gl.TRIANGLES, 0, this.vertices); return this } }; FSS.WebGLRenderer.prototype.setBufferData = function(b, d, h) { if (FSS.Utils.isNumber(h)) d.data[b * d.size] = h; else for (var g = h.length - 1; 0 <= g; g--) d.data[b * d.size + g] = h[g] }; FSS.WebGLRenderer.prototype.buildProgram = function(b) { if (!this.unsupported) { var d = FSS.WebGLRenderer.VS(b), h = FSS.WebGLRenderer.FS(b), g = d + h; if (!this.program || this.program.code !== g) { var l = this.gl.createProgram(), d = this.buildShader(this.gl.VERTEX_SHADER, d), h = this.buildShader(this.gl.FRAGMENT_SHADER, h); this.gl.attachShader(l, d); this.gl.attachShader(l, h); this.gl.linkProgram(l); if (!this.gl.getProgramParameter(l, this.gl.LINK_STATUS)) return b = this.gl.getError(), l = this.gl.getProgramParameter(l, this.gl.VALIDATE_STATUS), console.error("Could not initialise shader.\nVALIDATE_STATUS: " + l + "\nERROR: " + b), null; this.gl.deleteShader(h); this.gl.deleteShader(d); l.code = g; l.attributes = { side: this.buildBuffer(l, "attribute", "aSide", 1, "f"), position: this.buildBuffer(l, "attribute", "aPosition", 3, "v3"), centroid: this.buildBuffer(l, "attribute", "aCentroid", 3, "v3"), normal: this.buildBuffer(l, "attribute", "aNormal", 3, "v3"), ambient: this.buildBuffer(l, "attribute", "aAmbient", 4, "v4"), diffuse: this.buildBuffer(l, "attribute", "aDiffuse", 4, "v4") }; l.uniforms = { resolution: this.buildBuffer(l, "uniform", "uResolution", 3, "3f", 1), lightPosition: this.buildBuffer(l, "uniform", "uLightPosition", 3, "3fv", b), lightAmbient: this.buildBuffer(l, "uniform", "uLightAmbient", 4, "4fv", b), lightDiffuse: this.buildBuffer(l, "uniform", "uLightDiffuse", 4, "4fv", b) }; this.program = l; this.gl.useProgram(this.program); return l } } }; FSS.WebGLRenderer.prototype.buildShader = function(b, d) { if (!this.unsupported) { var h = this.gl.createShader(b); this.gl.shaderSource(h, d); this.gl.compileShader(h); return this.gl.getShaderParameter(h, this.gl.COMPILE_STATUS) ? h: (console.error(this.gl.getShaderInfoLog(h)), null) } }; FSS.WebGLRenderer.prototype.buildBuffer = function(b, d, h, g, l, n) { l = { buffer: this.gl.createBuffer(), size: g, structure: l, data: null }; switch (d) { case "attribute": l.location = this.gl.getAttribLocation(b, h); break; case "uniform": l.location = this.gl.getUniformLocation(b, h) } n && (l.data = new FSS.Array(n * g)); return l }; FSS.WebGLRenderer.VS = function(b) { return ["precision mediump float;", "#define LIGHTS " + b, "attribute float aSide;\nattribute vec3 aPosition;\nattribute vec3 aCentroid;\nattribute vec3 aNormal;\nattribute vec4 aAmbient;\nattribute vec4 aDiffuse;\nuniform vec3 uResolution;\nuniform vec3 uLightPosition[LIGHTS];\nuniform vec4 uLightAmbient[LIGHTS];\nuniform vec4 uLightDiffuse[LIGHTS];\nvarying vec4 vColor;\nvoid main() {\nvColor = vec4(0.0);\nvec3 position = aPosition / uResolution * 2.0;\nfor (int i = 0; i < LIGHTS; i++) {\nvec3 lightPosition = uLightPosition[i];\nvec4 lightAmbient = uLightAmbient[i];\nvec4 lightDiffuse = uLightDiffuse[i];\nvec3 ray = normalize(lightPosition - aCentroid);\nfloat illuminance = dot(aNormal, ray);\nif (aSide == 0.0) {\nilluminance = max(illuminance, 0.0);\n} else if (aSide == 1.0) {\nilluminance = abs(min(illuminance, 0.0));\n} else if (aSide == 2.0) {\nilluminance = max(abs(illuminance), 0.0);\n}\nvColor += aAmbient * lightAmbient;\nvColor += aDiffuse * lightDiffuse * illuminance;\n}\nvColor = clamp(vColor, 0.0, 1.0);\ngl_Position = vec4(position, 1.0);\n}"].join("\n") }; FSS.WebGLRenderer.FS = function(b) { return "precision mediump float;\nvarying vec4 vColor;\nvoid main() {\ngl_FragColor = vColor;\n}" }; FSS.SVGRenderer = function() { FSS.Renderer.call(this); this.element = document.createElementNS(FSS.SVGNS, "svg"); this.element.setAttribute("xmlns", FSS.SVGNS); this.element.setAttribute("version", "1.1"); this.element.style.display = "block"; this.setSize(300, 150) }; FSS.SVGRenderer.prototype = Object.create(FSS.Renderer.prototype); FSS.SVGRenderer.prototype.setSize = function(b, d) { FSS.Renderer.prototype.setSize.call(this, b, d); this.element.setAttribute("width", b); this.element.setAttribute("height", d); return this }; FSS.SVGRenderer.prototype.clear = function() { FSS.Renderer.prototype.clear.call(this); for (var b = this.element.childNodes.length - 1; 0 <= b; b--) this.element.removeChild(this.element.childNodes[b]); return this }; FSS.SVGRenderer.prototype.render = function(b) { FSS.Renderer.prototype.render.call(this, b); var d, h, g, l, n, s; for (d = b.meshes.length - 1; 0 <= d; d--) if (h = b.meshes[d], h.visible) for (h.update(b.lights, !0), g = h.geometry.triangles.length - 1; 0 <= g; g--) l = h.geometry.triangles[g], l.polygon.parentNode !== this.element && this.element.appendChild(l.polygon), n = this.formatPoint(l.a) + " ", n += this.formatPoint(l.b) + " ", n += this.formatPoint(l.c), s = this.formatStyle(l.color.format()), l.polygon.setAttributeNS(null, "points", n), l.polygon.setAttributeNS(null, "style", s); return this }; FSS.SVGRenderer.prototype.formatPoint = function(b) { return this.halfWidth + b.position[0] + "," + (this.halfHeight - b.position[1]) }; FSS.SVGRenderer.prototype.formatStyle = function(b) { return b = "fill:" + b + ";" + ("stroke:" + b + ";") }; /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) || function() { function b() { B.remove(G); D.clear(); H = new FSS.Plane(l.width * D.width, l.height * D.height, l.segments, l.slices); J = new FSS.Material(l.ambient, l.diffuse); G = new FSS.Mesh(H, J); B.add(G); var b, d; for (b = H.vertices.length - 1; 0 <= b; b--) d = H.vertices[b], d.anchor = FSS.Vector3.clone(d.position), d.step = FSS.Vector3.create(Math.randomInRange(0.2, 1), Math.randomInRange(0.2, 1), Math.randomInRange(0.2, 1)), d.time = Math.randomInRange(0, Math.PIM2) } function d(d, g) { D.setSize(d, g); FSS.Vector3.set(E, D.halfWidth, D.halfHeight); b() } function h() { r = Date.now() - u; var b, d, k, m, s, z = l.depth / 2; FSS.Vector3.copy(n.bounds, E); FSS.Vector3.multiplyScalar(n.bounds, n.xyScalar); FSS.Vector3.setZ(y, n.zOffset); n.autopilot && (b = Math.sin(n.step[0] * r * n.speed), d = Math.cos(n.step[1] * r * n.speed), FSS.Vector3.set(y, n.bounds[0] * b, n.bounds[1] * d, n.zOffset)); for (b = B.lights.length - 1; 0 <= b; b--) d = B.lights[b], FSS.Vector3.setZ(d.position, n.zOffset), k = Math.clamp(FSS.Vector3.distanceSquared(d.position, y), n.minDistance, n.maxDistance), k = n.gravity * d.mass / k, FSS.Vector3.subtractVectors(d.force, y, d.position), FSS.Vector3.normalise(d.force), FSS.Vector3.multiplyScalar(d.force, k), FSS.Vector3.set(d.acceleration), FSS.Vector3.add(d.acceleration, d.force), FSS.Vector3.add(d.velocity, d.acceleration), FSS.Vector3.multiplyScalar(d.velocity, n.dampening), FSS.Vector3.limit(d.velocity, n.minLimit, n.maxLimit), FSS.Vector3.add(d.position, d.velocity); for (m = H.vertices.length - 1; 0 <= m; m--) s = H.vertices[m], b = Math.sin(s.time + s.step[0] * r * l.speed), d = Math.cos(s.time + s.step[1] * r * l.speed), k = Math.sin(s.time + s.step[2] * r * l.speed), FSS.Vector3.set(s.position, l.xRange * H.segmentWidth * b, l.yRange * H.sliceHeight * d, l.zRange * z * k - z), FSS.Vector3.add(s.position, s.anchor); H.dirty = !0; g(); requestAnimationFrame(h) } function g() { D.render(B); if (n.draw) { var b, d, g, h; for (b = B.lights.length - 1; 0 <= b; b--) switch (h = B.lights[b], d = h.position[0], g = h.position[1], m.renderer) { case s: D.context.lineWidth = 0.5; D.context.beginPath(); D.context.arc(d, g, 10, 0, Math.PIM2); D.context.strokeStyle = h.ambientHex; D.context.stroke(); D.context.beginPath(); D.context.arc(d, g, 4, 0, Math.PIM2); D.context.fillStyle = h.diffuseHex; D.context.fill(); break; case k: d += D.halfWidth, g = D.halfHeight - g, h.core.setAttributeNS(null, "fill", h.diffuseHex), h.core.setAttributeNS(null, "cx", d), h.core.setAttributeNS(null, "cy", g), D.element.appendChild(h.core), h.ring.setAttributeNS(null, "stroke", h.ambientHex), h.ring.setAttributeNS(null, "cx", d), h.ring.setAttributeNS(null, "cy", g), D.element.appendChild(h.ring) } } } var l = { width: 1.8, height: 1.8, depth: 10, segments: 16, slices: 8, xRange: 0.8, yRange: 0.1, zRange: 1, ambient: "#010101", diffuse: "#ffffff", speed: 6E-4, opacity: 0.5 }, n = { count: 2, xyScalar: 1, zOffset: 100, ambient: "#ffffff", diffuse: "#2d2d2d", speed: 0.001, gravity: 800, dampening: 0.95, minLimit: 10, maxLimit: null, minDistance: 20, maxDistance: 400, autopilot: !1, draw: !1, bounds: FSS.Vector3.create(), step: FSS.Vector3.create(Math.randomInRange(0.2, 1), Math.randomInRange(0.2, 1), Math.randomInRange(0.2, 1)) }, s = "canvas", k = "svg", m = { renderer: s }, r, u = Date.now(), E = FSS.Vector3.create(), y = FSS.Vector3.create(), z = document.getElementById(container || "container"); document.getElementById("controls"); var X = document.getElementById(output || "output"); document.getElementById("ui"); var D, B, G, H, J, R, C, S; R = new FSS.WebGLRenderer; C = new FSS.CanvasRenderer; S = new FSS.SVGRenderer; (function(b) { D && X.removeChild(D.element); switch (b) { case "webgl": D = R; break; case s: D = C; break; case k: D = S } D.setSize(z.offsetWidth, z.offsetHeight); X.appendChild(D.element) })(m.renderer); B = new FSS.Scene; b(); (function() { var b, d; for (b = B.lights.length - 1; 0 <= b; b--) d = B.lights[b], B.remove(d); D.clear(); for (b = 0; b < n.count; b++) d = new FSS.Light(n.ambient, n.diffuse), d.ambientHex = d.ambient.format(), d.diffuseHex = d.diffuse.format(), B.add(d), d.mass = Math.randomInRange(0.5, 1), d.velocity = FSS.Vector3.create(), d.acceleration = FSS.Vector3.create(), d.force = FSS.Vector3.create(), d.ring = document.createElementNS(FSS.SVGNS, "circle"), d.ring.setAttributeNS(null, "stroke", d.ambientHex), d.ring.setAttributeNS(null, "stroke-width", "0.5"), d.ring.setAttributeNS(null, "fill", "none"), d.ring.setAttributeNS(null, "r", "10"), d.core = document.createElementNS(FSS.SVGNS, "circle"), d.core.setAttributeNS(null, "fill", d.diffuseHex), d.core.setAttributeNS(null, "r", "4") })(); window.addEventListener("resize", function(b) { d(z.offsetWidth, z.offsetHeight); g() }); z.addEventListener("click", function(b) { FSS.Vector3.set(y, b.x, D.height - b.y); FSS.Vector3.subtract(y, E); n.autopilot = !n.autopilot; (void 0).updateDisplay() }); z.addEventListener("mousemove", function(b) { FSS.Vector3.set(y, b.x, D.height - b.y); FSS.Vector3.subtract(y, E) }); d(z.offsetWidth, z.offsetHeight); h() } (); }