From 038ee0bdf1c32ab7fb1d4e42a0448a770ec7a463 Mon Sep 17 00:00:00 2001 From: Alexander Gugel Date: Mon, 27 Jul 2015 17:06:50 +0200 Subject: [PATCH 01/29] refact: Refactor geometries This refactor solves a number of issues ans subtle bugs that are present in the current implementation. No breaking changes have been made. * Usage of defaultGeometry: Previously a single defaultGeometry has been used for all Meshes that didn't have a geometry set on them. Since it was possible to retrieve and mutate the geometry, mutating the geometry of one Mesh would have resulted into unexpected results (since the geometry was shared among all Meshes). * Primitives inherit from Geometry: Although the methods were documented as constructors, they actually instantiated a new Geometry and returned it. This resulted into confusing `instanceof` and `constructor` checks. The new keyword is still optional, there this is not a breaking change. * Deprecate Mesh#setGeometry's geometry register: Accepting geometries as strings is problematic, since it means that **all** geometries will be required and included in the bundled file (as pointed out on phabricator). Currently a console.warn is used for printing the deprecation notice. --- renderers/Compositor.js | 3 ++- webgl-geometries/Geometry.js | 4 ++-- webgl-geometries/primitives/Box.js | 7 +++++- webgl-geometries/primitives/Circle.js | 7 +++++- webgl-geometries/primitives/Cylinder.js | 7 +++++- webgl-geometries/primitives/GeodesicSphere.js | 17 +++++++++----- webgl-geometries/primitives/Icosahedron.js | 22 +++++++++++-------- webgl-geometries/primitives/ParametricCone.js | 9 ++++++-- webgl-geometries/primitives/Plane.js | 7 +++++- webgl-geometries/primitives/Sphere.js | 7 +++++- webgl-geometries/primitives/Tetrahedron.js | 7 +++++- webgl-geometries/primitives/Torus.js | 11 +++++++--- webgl-geometries/primitives/Triangle.js | 12 ++++++---- webgl-geometries/test/Primitives.spec.js | 8 +++---- webgl-renderables/Mesh.js | 18 ++++++++++++--- 15 files changed, 106 insertions(+), 40 deletions(-) diff --git a/renderers/Compositor.js b/renderers/Compositor.js index 386c00de..789a007d 100644 --- a/renderers/Compositor.js +++ b/renderers/Compositor.js @@ -270,7 +270,8 @@ Compositor.prototype.giveSizeFor = function giveSizeFor(iterator, commands) { if (context) { var size = context.getRootSize(); this.sendResize(selector, size); - } else { + } + else { this.getOrSetContext(selector); } }; diff --git a/webgl-geometries/Geometry.js b/webgl-geometries/Geometry.js index 1d492bf8..2079279a 100644 --- a/webgl-geometries/Geometry.js +++ b/webgl-geometries/Geometry.js @@ -52,11 +52,11 @@ function Geometry(options) { if (this.options.buffers) { var len = this.options.buffers.length; - for (var i = 0; i < len;) { + for (var i = 0; i < len; i++) { this.spec.bufferNames.push(this.options.buffers[i].name); this.spec.bufferValues.push(this.options.buffers[i].data); this.spec.bufferSpacings.push(this.options.buffers[i].size || this.DEFAULT_BUFFER_SIZE); - this.spec.invalidations.push(i++); + this.spec.invalidations.push(i); } } } diff --git a/webgl-geometries/primitives/Box.js b/webgl-geometries/primitives/Box.js index 860d4fba..8a87c8e3 100644 --- a/webgl-geometries/primitives/Box.js +++ b/webgl-geometries/primitives/Box.js @@ -52,6 +52,8 @@ var boxData = [ * @return {Object} constructed geometry */ function BoxGeometry(options) { + if (!(this instanceof BoxGeometry)) return new BoxGeometry(options); + options = options || {}; var vertices = []; @@ -87,7 +89,10 @@ function BoxGeometry(options) { { name: 'indices', data: indices, size: 1 } ]; - return new Geometry(options); + Geometry.call(this, options); } +BoxGeometry.prototype = Object.create(Geometry.prototype); +BoxGeometry.prototype.constructor = BoxGeometry; + module.exports = BoxGeometry; diff --git a/webgl-geometries/primitives/Circle.js b/webgl-geometries/primitives/Circle.js index 9ef43694..c93c03f8 100644 --- a/webgl-geometries/primitives/Circle.js +++ b/webgl-geometries/primitives/Circle.js @@ -40,6 +40,8 @@ var GeometryHelper = require('../GeometryHelper'); * @return {Object} constructed geometry */ function Circle (options) { + if (!(this instanceof Circle)) return new Circle(options); + options = options || {}; var detail = options.detail || 30; var buffers = getCircleBuffers(detail, true); @@ -58,9 +60,12 @@ function Circle (options) { { name: 'indices', data: buffers.indices, size: 1 } ]; - return new Geometry(options); + Geometry.call(this, options); } +Circle.prototype = Object.create(Geometry.prototype); +Circle.prototype.constructor = Circle; + function getCircleTexCoords (vertices) { var textureCoords = []; var nFaces = vertices.length / 3; diff --git a/webgl-geometries/primitives/Cylinder.js b/webgl-geometries/primitives/Cylinder.js index fae1cf59..7ed4a269 100644 --- a/webgl-geometries/primitives/Cylinder.js +++ b/webgl-geometries/primitives/Cylinder.js @@ -41,6 +41,8 @@ var GeometryHelper = require('../GeometryHelper'); * @return {Object} constructed geometry */ function Cylinder (options) { + if (!(this instanceof Cylinder)) return new Cylinder(options); + options = options || {}; var radius = options.radius || 1; var detail = options.detail || 15; @@ -63,9 +65,12 @@ function Cylinder (options) { { name: 'indices', data: buffers.indices, size: 1 } ]; - return new Geometry(options); + Geometry.call(this, options); } +Cylinder.prototype = Object.create(Geometry.prototype); +Cylinder.prototype.constructor = Cylinder; + /** * Function used in iterative construction of parametric primitive. * diff --git a/webgl-geometries/primitives/GeodesicSphere.js b/webgl-geometries/primitives/GeodesicSphere.js index c2938624..af8a95e3 100644 --- a/webgl-geometries/primitives/GeodesicSphere.js +++ b/webgl-geometries/primitives/GeodesicSphere.js @@ -1,18 +1,18 @@ /** * The MIT License (MIT) - * + * * Copyright (c) 2015 Famous Industries Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -36,10 +36,12 @@ var GeometryHelper = require('../GeometryHelper'); * * @param {Object} options Parameters that alter the * vertex buffers of the generated geometry. - * + * * @return {Object} constructed geometry */ function GeodesicSphere (options) { + if (!(this instanceof GeodesicSphere)) return new GeodesicSphere(options); + var t = (1 + Math.sqrt(5)) * 0.5; var vertices = [ @@ -72,7 +74,10 @@ function GeodesicSphere (options) { { name: 'indices', data: indices, size: 1 } ]; - return new Geometry(options); + Geometry.call(this, options); } +GeodesicSphere.prototype = Object.create(Geometry.prototype); +GeodesicSphere.prototype.constructor = GeodesicSphere; + module.exports = GeodesicSphere; diff --git a/webgl-geometries/primitives/Icosahedron.js b/webgl-geometries/primitives/Icosahedron.js index d83c4e52..9dbcef21 100644 --- a/webgl-geometries/primitives/Icosahedron.js +++ b/webgl-geometries/primitives/Icosahedron.js @@ -1,18 +1,18 @@ /** * The MIT License (MIT) - * + * * Copyright (c) 2015 Famous Industries Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -36,12 +36,13 @@ var GeometryHelper = require('../GeometryHelper'); * * @param {Object} options Parameters that alter the * vertex buffers of the generated geometry. - * + * * @return {Object} constructed geometry */ -function Icosahedron( options ) -{ - options = options || {}; +function Icosahedron(options) { + if (!(this instanceof Icosahedron)) return new Icosahedron(options); + + options = options || {}; var t = ( 1 + Math.sqrt( 5 ) ) / 2; var vertices = [ @@ -70,7 +71,10 @@ function Icosahedron( options ) { name: 'indices', data: indices, size: 1 } ]; - return new Geometry(options); + Geometry.call(this, options); } +Icosahedron.prototype = Object.create(Geometry.prototype); +Icosahedron.prototype.constructor = Icosahedron; + module.exports = Icosahedron; diff --git a/webgl-geometries/primitives/ParametricCone.js b/webgl-geometries/primitives/ParametricCone.js index 4448c2f8..fa7e779e 100644 --- a/webgl-geometries/primitives/ParametricCone.js +++ b/webgl-geometries/primitives/ParametricCone.js @@ -40,7 +40,9 @@ var GeometryHelper = require('../GeometryHelper'); * @return {Object} constructed geometry */ function ParametricCone (options) { - options = options || {}; + if (!(this instanceof ParametricCone)) return new ParametricCone(options); + + options = options || {}; var detail = options.detail || 15; var radius = options.radius || 1 / Math.PI; @@ -61,9 +63,12 @@ function ParametricCone (options) { { name: 'indices', data: buffers.indices, size: 1 } ]; - return new Geometry(options); + Geometry.call(this, options); } +ParametricCone.prototype = Object.create(Geometry.prototype); +ParametricCone.prototype.constructor = ParametricCone; + /** * function used in iterative construction of parametric primitive. * diff --git a/webgl-geometries/primitives/Plane.js b/webgl-geometries/primitives/Plane.js index 7c7cbeca..6d610ff6 100644 --- a/webgl-geometries/primitives/Plane.js +++ b/webgl-geometries/primitives/Plane.js @@ -40,6 +40,8 @@ var GeometryHelper = require('../GeometryHelper'); * @return {Object} constructed geometry */ function Plane(options) { + if (!(this instanceof Plane)) return new Plane(options); + options = options || {}; var detailX = options.detailX || options.detail || 1; var detailY = options.detailY || options.detail || 1; @@ -83,7 +85,10 @@ function Plane(options) { { name: 'indices', data: indices, size: 1 } ]; - return new Geometry(options); + Geometry.call(this, options); } +Plane.prototype = Object.create(Geometry.prototype); +Plane.prototype.constructor = Plane; + module.exports = Plane; diff --git a/webgl-geometries/primitives/Sphere.js b/webgl-geometries/primitives/Sphere.js index d9cca9c9..da1489b8 100644 --- a/webgl-geometries/primitives/Sphere.js +++ b/webgl-geometries/primitives/Sphere.js @@ -40,6 +40,8 @@ var GeometryHelper = require('../GeometryHelper'); * @return {Object} constructed geometry */ function ParametricSphere (options) { + if (!(this instanceof ParametricSphere)) return new ParametricSphere(options); + options = options || {}; var detail = options.detail || 10; var detailX = options.detailX || detail; @@ -59,9 +61,12 @@ function ParametricSphere (options) { { name: 'indices', data: buffers.indices, size: 1 } ]; - return new Geometry(options); + Geometry.call(this, options); } +ParametricSphere.prototype = Object.create(Geometry.prototype); +ParametricSphere.prototype.constructor = ParametricSphere; + /** * Function used in iterative construction of parametric primitive. * diff --git a/webgl-geometries/primitives/Tetrahedron.js b/webgl-geometries/primitives/Tetrahedron.js index 152c06bd..452b389c 100644 --- a/webgl-geometries/primitives/Tetrahedron.js +++ b/webgl-geometries/primitives/Tetrahedron.js @@ -40,6 +40,8 @@ var GeometryHelper = require('../GeometryHelper'); * @return {Object} constructed geometry */ function Tetrahedron(options) { + if (!(this instanceof Tetrahedron)) return new Tetrahedron(options); + var textureCoords = []; var normals = []; var detail; @@ -95,7 +97,10 @@ function Tetrahedron(options) { { name: 'indices', data: indices, size: 1 } ]; - return new Geometry(options); + Geometry.call(this, options); } +Tetrahedron.prototype = Object.create(Geometry.prototype); +Tetrahedron.prototype.constructor = Tetrahedron; + module.exports = Tetrahedron; diff --git a/webgl-geometries/primitives/Torus.js b/webgl-geometries/primitives/Torus.js index 0f76572f..81c7ac25 100644 --- a/webgl-geometries/primitives/Torus.js +++ b/webgl-geometries/primitives/Torus.js @@ -41,8 +41,10 @@ var GeometryHelper = require('../GeometryHelper'); */ function Torus(options) { - options = options || {}; - var detail = options.detail || 30; + if (!(this instanceof Torus)) return new Torus(options); + + options = options || {}; + var detail = options.detail || 30; var holeRadius = options.holeRadius || 0.80; var tubeRadius = options.tubeRadius || 0.20; @@ -59,9 +61,12 @@ function Torus(options) { { name: 'indices', data: buffers.indices, size: 1 } ]; - return new Geometry(options); + Geometry.call(this, options); } +Torus.prototype = Object.create(Geometry.prototype); +Torus.prototype.constructor = Torus; + /** * function used in iterative construction of parametric primitive. * diff --git a/webgl-geometries/primitives/Triangle.js b/webgl-geometries/primitives/Triangle.js index 2e70fdc9..0bb0b491 100644 --- a/webgl-geometries/primitives/Triangle.js +++ b/webgl-geometries/primitives/Triangle.js @@ -40,6 +40,8 @@ var GeometryHelper = require('../GeometryHelper'); * @return {Object} constructed geometry */ function Triangle (options) { + if (!(this instanceof Triangle)) return new Triangle(options); + options = options || {}; var detail = options.detail || 1; var normals = []; @@ -57,11 +59,10 @@ function Triangle (options) { 1, -1, 0 ]; - while(--detail) GeometryHelper.subdivide(indices, vertices, textureCoords); + while (--detail) GeometryHelper.subdivide(indices, vertices, textureCoords); - if (options.backface !== false) { + if (options.backface !== false) GeometryHelper.addBackfaceTriangles(vertices, indices); - } normals = GeometryHelper.computeNormals(vertices, indices); @@ -72,7 +73,10 @@ function Triangle (options) { { name: 'indices', data: indices, size: 1 } ]; - return new Geometry(options); + Geometry.call(this, options); } +Triangle.prototype = Object.create(Geometry.prototype); +Triangle.prototype.constructor = Triangle; + module.exports = Triangle; diff --git a/webgl-geometries/test/Primitives.spec.js b/webgl-geometries/test/Primitives.spec.js index d2f2d0fd..98f0609f 100644 --- a/webgl-geometries/test/Primitives.spec.js +++ b/webgl-geometries/test/Primitives.spec.js @@ -58,17 +58,18 @@ test('Primitives', function(t) { } t.end(); }); - + t.test('Primitives.optionsParameter', function(t) { for (var name in primitives) { var primitive = new primitives[name]({type:'POINTS'}); t.ok(primitive instanceof Geometry, 'should be an instance of a static geometry'); + t.ok(primitive instanceof primitives[name], 'should be an instance of its constructor function'); t.notEquals(primitive.spec.bufferNames.indexOf('a_texCoord'), -1, 'should contain a texCoord buffer'); t.notEquals(primitive.spec.bufferNames.indexOf('a_normals'), -1, 'should contain a normal buffer'); t.notEquals(primitive.spec.bufferNames.indexOf('a_pos'), -1, 'should contain a pos buffer'); - + t.equals(primitive.spec.type, 'POINTS', 'draw type should be passed through'); if (name !== 'Circle') { @@ -77,8 +78,7 @@ test('Primitives', function(t) { } t.end(); }); - + t.end(); }); - diff --git a/webgl-renderables/Mesh.js b/webgl-renderables/Mesh.js index accb1d1d..5396fe77 100644 --- a/webgl-renderables/Mesh.js +++ b/webgl-renderables/Mesh.js @@ -23,10 +23,14 @@ */ 'use strict'; + +// TODO This will be removed once `Mesh#setGeometry` no longer accepts +// geometries defined by name. var Geometry = require('../webgl-geometries'); + var Commands = require('../core/Commands'); var TransformSystem = require('../core/TransformSystem'); -var defaultGeometry = new Geometry.Plane(); +var Plane = require('../webgl-geometries/primitives/Plane'); /** * The Mesh class is responsible for providing the API for how @@ -53,7 +57,7 @@ function Mesh (node, options) { this._requestingUpdate = false; this._inDraw = false; this.value = { - geometry: defaultGeometry, + geometry: Plane(), drawOptions: null, color: null, expressions: {}, @@ -108,7 +112,15 @@ Mesh.prototype.getDrawOptions = function getDrawOptions () { Mesh.prototype.setGeometry = function setGeometry (geometry, options) { if (typeof geometry === 'string') { if (!Geometry[geometry]) throw 'Invalid geometry: "' + geometry + '".'; - else geometry = new Geometry[geometry](options); + else { + console.warn( + 'Mesh#setGeometry using the geometry registry is deprecated!\n' + + 'Instantiate the geometry directly via `new ' + geometry + + '(options)` instead!' + ); + + geometry = new Geometry[geometry](options); + } } if (this.value.geometry !== geometry || this._inDraw) { From e2904b47d1a840c6c21e826dd25e05c7bc0965ac Mon Sep 17 00:00:00 2001 From: Alexander Gugel Date: Mon, 27 Jul 2015 14:08:56 +0200 Subject: [PATCH 02/29] refact: Use switch; No longer hardcode gl constants --- webgl-renderers/Program.js | 54 ++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 29 deletions(-) diff --git a/webgl-renderers/Program.js b/webgl-renderers/Program.js index 80e28f79..6de8b37b 100644 --- a/webgl-renderers/Program.js +++ b/webgl-renderers/Program.js @@ -31,8 +31,6 @@ var vertexWrapper = require('../webgl-shaders').vertex; var fragmentWrapper = require('../webgl-shaders').fragment; var Debug = require('./Debug'); -var VERTEX_SHADER = 35633; -var FRAGMENT_SHADER = 35632; var identityMatrix = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]; var header = 'precision mediump float;\n'; @@ -183,28 +181,27 @@ Program.prototype.registerMaterial = function registerMaterial(name, material) { this.registeredMaterials[material._id] |= mask; - if (type === 'float') { - this.definitionFloat.push(material.defines); - this.definitionFloat.push('float fa_' + material._id + '() {\n ' + compiled.glsl + ' \n}'); - this.applicationFloat.push('if (int(abs(ID)) == ' + material._id + ') return fa_' + material._id + '();'); - } - - if (type === 'vec3') { - this.definitionVec3.push(material.defines); - this.definitionVec3.push('vec3 fa_' + material._id + '() {\n ' + compiled.glsl + ' \n}'); - this.applicationVec3.push('if (int(abs(ID.x)) == ' + material._id + ') return fa_' + material._id + '();'); - } - - if (type === 'vec4') { - this.definitionVec4.push(material.defines); - this.definitionVec4.push('vec4 fa_' + material._id + '() {\n ' + compiled.glsl + ' \n}'); - this.applicationVec4.push('if (int(abs(ID.x)) == ' + material._id + ') return fa_' + material._id + '();'); - } - - if (type === 'vert') { - this.definitionVert.push(material.defines); - this.definitionVert.push('vec3 fa_' + material._id + '() {\n ' + compiled.glsl + ' \n}'); - this.applicationVert.push('if (int(abs(ID.x)) == ' + material._id + ') return fa_' + material._id + '();'); + switch (type) { + case 'float': + this.definitionFloat.push(material.defines); + this.definitionFloat.push('float fa_' + material._id + '() {\n ' + compiled.glsl + ' \n}'); + this.applicationFloat.push('if (int(abs(ID)) == ' + material._id + ') return fa_' + material._id + '();'); + break; + case 'vec3': + this.definitionVec3.push(material.defines); + this.definitionVec3.push('vec3 fa_' + material._id + '() {\n ' + compiled.glsl + ' \n}'); + this.applicationVec3.push('if (int(abs(ID.x)) == ' + material._id + ') return fa_' + material._id + '();'); + break; + case 'vec4': + this.definitionVec4.push(material.defines); + this.definitionVec4.push('vec4 fa_' + material._id + '() {\n ' + compiled.glsl + ' \n}'); + this.applicationVec4.push('if (int(abs(ID.x)) == ' + material._id + ') return fa_' + material._id + '();'); + break; + case 'vert': + this.definitionVert.push(material.defines); + this.definitionVert.push('vec3 fa_' + material._id + '() {\n ' + compiled.glsl + ' \n}'); + this.applicationVert.push('if (int(abs(ID.x)) == ' + material._id + ') return fa_' + material._id + '();'); + break; } return this.resetProgram(); @@ -250,9 +247,8 @@ Program.prototype.resetProgram = function resetProgram() { fragmentHeader.push('uniform sampler2D u_textures[7];\n'); - if (this.applicationVert.length) { + if (this.applicationVert.length) vertexHeader.push('uniform sampler2D u_textures[7];\n'); - } for(i = 0; i < this.uniformNames.length; i++) { name = this.uniformNames[i]; @@ -290,12 +286,12 @@ Program.prototype.resetProgram = function resetProgram() { this.gl.attachShader( program, - this.compileShader(this.gl.createShader(VERTEX_SHADER), vertexSource) + this.compileShader(this.gl.createShader(this.gl.VERTEX_SHADER), vertexSource) ); this.gl.attachShader( program, - this.compileShader(this.gl.createShader(FRAGMENT_SHADER), fragmentSource) + this.compileShader(this.gl.createShader(this.gl.FRAGMENT_SHADER), fragmentSource) ); this.gl.linkProgram(program); @@ -386,7 +382,7 @@ Program.prototype.setUniforms = function (uniformNames, uniformValue) { name = uniformNames[i]; value = uniformValue[i]; - // Retreive the cached location of the uniform, + // Retrieve the cached location of the uniform, // requesting a new location from the WebGL context // if it does not yet exist. From dcd7773fdc35d5efdb2c33be82d420634ce4e4ec Mon Sep 17 00:00:00 2001 From: Myles Borins Date: Mon, 27 Jul 2015 00:11:30 -0700 Subject: [PATCH 03/29] [chore] update deps --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index c8537c0b..3aa9740f 100644 --- a/package.json +++ b/package.json @@ -35,17 +35,17 @@ "author": "Famous", "license": "MIT", "devDependencies": { - "browserify": "^10.2.1", + "browserify": "^11.0.0", "colors": "^1.1.0", - "eslint": "^0.21.2", + "eslint": "^0.24.1", "glob": "^5.0.10", "istanbul": "^0.3.15", "rewire": "^2.3.3", "sinon": "^1.14.1", "smokestack": "^3.2.2", "tap-closer": "^1.0.0", - "tape": "^4.0.0", "tap-spec": "^4.0.0", + "tape": "^4.0.0", "uglify-js": "^2.4.17" }, "dependencies": { From 34b3848a7ed3bfdd85a8e8b615aeacdcb5d33068 Mon Sep 17 00:00:00 2001 From: Alexander Gugel Date: Thu, 23 Jul 2015 17:54:31 +0200 Subject: [PATCH 04/29] doc: Fix Node#requestUpdate doc --- core/Node.js | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/core/Node.js b/core/Node.js index eed38ad0..119a08ec 100644 --- a/core/Node.js +++ b/core/Node.js @@ -1,4 +1,4 @@ -/** +/* * The MIT License (MIT) * * Copyright (c) 2015 Famous Industries Inc. @@ -372,13 +372,18 @@ Node.prototype.getParent = function getParent () { * next frame (if no update during this frame has been scheduled already). * If the node is currently being updated (which means one of the requesters * invoked requestsUpdate while being updated itself), an update will be - * scheduled on the next frame. + * scheduled on the next frame by falling back to the `requestUpdateOnNextTick` + * function. + * + * Components request their `onUpdate` method to be called during the next + * frame using this method. * * @method requestUpdate * - * @param {Object} requester If the requester has an `onUpdate` method, it - * will be invoked during the next update phase of - * the node. + * @param {Number} requester Id of the component (as returned by + * {@link Node#addComponent}) to be updated. The + * component's `onUpdate` method will be invoked + * during the next update cycle. * * @return {Node} this */ @@ -393,16 +398,25 @@ Node.prototype.requestUpdate = function requestUpdate (requester) { }; /** - * Schedules an update on the next tick. Similarily to - * {@link Node#requestUpdate}, `requestUpdateOnNextTick` schedules the node's - * `onUpdate` function to be invoked on the frame after the next invocation on + * Schedules an update on the next tick. + * + * This method is similar to {@link Node#requestUpdate}, but schedules an + * update on the **next** frame. It schedules the node's `onUpdate` function + * to be invoked on the frame after the next invocation on * the node's onUpdate function. * + * The primary use-case for this method is to request an update while being in + * an update phase (e.g. because an animation is still active). Most of the + * time, {@link Node#requestUpdate} is sufficient, since it automatically + * falls back to {@link Node#requestUpdateOnNextTick} when being invoked during + * the update phase. + * * @method requestUpdateOnNextTick * - * @param {Object} requester If the requester has an `onUpdate` method, it - * will be invoked during the next update phase of - * the node. + * @param {Number} requester Id of the component (as returned by + * {@link Node#addComponent}) to be updated. The + * component's `onUpdate` method will be invoked + * during the next update cycle. * * @return {Node} this */ From 891a57ef62597ce8124cd9d8140028d1d07ac3c4 Mon Sep 17 00:00:00 2001 From: Alexander Gugel Date: Thu, 23 Jul 2015 10:44:15 +0200 Subject: [PATCH 05/29] fix: Support sparse arrays in radixSort Previously wasn't an issue, since: 1. There were no registries 2. Meshes were never being removed --- webgl-renderers/WebGLRenderer.js | 30 +++++++++++++++---------- webgl-renderers/radixSort.js | 2 +- webgl-renderers/test/radixSort.spec.js | 31 +++++++++++++------------- 3 files changed, 34 insertions(+), 29 deletions(-) diff --git a/webgl-renderers/WebGLRenderer.js b/webgl-renderers/WebGLRenderer.js index 8c175579..f3ecb805 100644 --- a/webgl-renderers/WebGLRenderer.js +++ b/webgl-renderers/WebGLRenderer.js @@ -76,7 +76,9 @@ function WebGLRenderer(canvas, compositor) { gl.enable(gl.CULL_FACE); gl.cullFace(gl.BACK); - this.meshRegistry = new Registry(); + this.meshRegistry = {}; + this.meshRegistryKeys = []; + this.cutoutRegistry = new Registry(); this.lightRegistry = new Registry(); @@ -217,7 +219,8 @@ WebGLRenderer.prototype.createMesh = function createMesh(path) { visible: true }; - this.meshRegistry.register(path, mesh); + this.meshRegistry[path] = mesh; + this.meshRegistryKeys.push(path); return mesh; }; @@ -284,7 +287,7 @@ WebGLRenderer.prototype.getOrSetCutout = function getOrSetCutout(path) { * @return {undefined} undefined */ WebGLRenderer.prototype.setMeshVisibility = function setMeshVisibility(path, visibility) { - var mesh = this.meshRegistry.get(path) || this.createMesh(path); + var mesh = this.meshRegistry[path] || this.createMesh(path); mesh.visible = visibility; }; @@ -298,7 +301,10 @@ WebGLRenderer.prototype.setMeshVisibility = function setMeshVisibility(path, vis * @return {undefined} undefined */ WebGLRenderer.prototype.removeMesh = function removeMesh(path) { - this.meshRegistry.unregister(path); + delete this.meshRegistry[path]; + var index = this.meshRegistryKeys.indexOf(path); + + if (index !== -1) this.meshRegistryKeys.splice(index, 1); }; /** @@ -336,7 +342,7 @@ WebGLRenderer.prototype.setCutoutUniform = function setCutoutUniform(path, unifo * @return {WebGLRenderer} this */ WebGLRenderer.prototype.setMeshOptions = function(path, options) { - var mesh = this.meshRegistry.get(path) || this.createMesh(path); + var mesh = this.meshRegistry[path] || this.createMesh(path); mesh.options = options; return this; @@ -414,7 +420,7 @@ WebGLRenderer.prototype.setLightColor = function setLightColor(path, r, g, b) { * @return {WebGLRenderer} this */ WebGLRenderer.prototype.handleMaterialInput = function handleMaterialInput(path, name, material) { - var mesh = this.meshRegistry.get(path) || this.createMesh(path); + var mesh = this.meshRegistry[path] || this.createMesh(path); material = compileMaterial(material, mesh.textures.length); // Set uniforms to enable texture! @@ -450,7 +456,7 @@ WebGLRenderer.prototype.handleMaterialInput = function handleMaterialInput(path, * @return {undefined} undefined */ WebGLRenderer.prototype.setGeometry = function setGeometry(path, geometry, drawType, dynamic) { - var mesh = this.meshRegistry.get(path) || this.createMesh(path); + var mesh = this.meshRegistry[path] || this.createMesh(path); mesh.geometry = geometry; mesh.drawType = drawType; @@ -471,7 +477,7 @@ WebGLRenderer.prototype.setGeometry = function setGeometry(path, geometry, drawT * @return {undefined} undefined */ WebGLRenderer.prototype.setMeshUniform = function setMeshUniform(path, uniformName, uniformValue) { - var mesh = this.meshRegistry.get(path) || this.createMesh(path); + var mesh = this.meshRegistry[path] || this.createMesh(path); var index = mesh.uniformKeys.indexOf(uniformName); @@ -517,7 +523,7 @@ WebGLRenderer.prototype.draw = function draw(renderState) { this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT); this.textureManager.update(time); - this.meshRegistryKeys = sorter(this.meshRegistry.getKeys(), this.meshRegistry.getKeyToValue()); + this.meshRegistryKeys = sorter(this.meshRegistryKeys, this.meshRegistry); this.setGlobalUniforms(renderState); this.drawCutouts(); @@ -538,10 +544,10 @@ WebGLRenderer.prototype.drawMeshes = function drawMeshes() { var buffers; var mesh; - var meshes = this.meshRegistry.getValues(); + var paths = this.meshRegistryKeys; - for(var i = 0; i < meshes.length; i++) { - mesh = meshes[i]; + for(var i = 0; i < paths.length; i++) { + mesh = this.meshRegistry[paths[i]]; if (!mesh) continue; diff --git a/webgl-renderers/radixSort.js b/webgl-renderers/radixSort.js index a005f2e7..7d7d123a 100644 --- a/webgl-renderers/radixSort.js +++ b/webgl-renderers/radixSort.js @@ -107,7 +107,7 @@ function radixSort(list, registry) { div = floatToInt(comp(list, registry, i)); out[++buckets[div & radixMask]] = mutator(list, registry, i, div ^= div >> 31 | 0x80000000); } - + swap = out; out = list; list = swap; diff --git a/webgl-renderers/test/radixSort.spec.js b/webgl-renderers/test/radixSort.spec.js index 524d6e61..ddac1dc2 100644 --- a/webgl-renderers/test/radixSort.spec.js +++ b/webgl-renderers/test/radixSort.spec.js @@ -1,18 +1,18 @@ /** * The MIT License (MIT) - * + * * Copyright (c) 2015 Famous Industries Inc. - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -27,28 +27,27 @@ var test = require('tape'); var radixSort = require('../radixSort'); test('radixSort', function(t) { - var registry = {}; - var meshList = []; - while (meshList.length < 1e3) { - var path = Math.random(); - registry[path] = mockMesh(); - meshList[meshList.length] = path; - } - radixSort(meshList, registry); + t.test('dense array', function(t) { + var registry = {}; + var meshList = []; + while (meshList.length < 1e3) { + var path = Math.random(); + registry[path] = mockMesh(); + meshList[meshList.length] = path; + } - t.test('sort', function(t) { - t.equals(checkSorted(meshList), true, 'should be sorted by depth'); + radixSort(meshList, registry); + t.ok(checkSorted(meshList), 'should be sorted by depth'); t.end(); }); - t.end(); }); function checkSorted (list, registry){ var a, b; - for (var i= 0; i < test.length - 1; i++) { + for (var i = 0; i < test.length - 1; i++) { a = list[i].uniformValues[1][14]; b = list[i+1].uniformValues[1][14]; if (a < b) return false; From 782be3d643f4ad99c5baa6afbe87f7b0726a6672 Mon Sep 17 00:00:00 2001 From: Alexander Gugel Date: Fri, 17 Jul 2015 15:48:03 +0200 Subject: [PATCH 06/29] refact: Remove obsolete data clone In the current implementation a new typed array is being instantiated on every subData call using `concat`. This should be factored out by sending the data as a transferable ArrayBuffer through postMessage, resulting into a zero-copy transfer. For the time being, we can at least remove the obsolete cloning of the array using concat. --- webgl-renderers/Buffer.js | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/webgl-renderers/Buffer.js b/webgl-renderers/Buffer.js index 981cdc75..a6bf69da 100644 --- a/webgl-renderers/Buffer.js +++ b/webgl-renderers/Buffer.js @@ -56,15 +56,9 @@ function Buffer(target, type, gl) { */ Buffer.prototype.subData = function subData() { var gl = this.gl; - var data = []; - - // to prevent against maximum call-stack issue. - for (var i = 0, chunk = 10000; i < this.data.length; i += chunk) - data = Array.prototype.concat.apply(data, this.data.slice(i, i + chunk)); - this.buffer = this.buffer || gl.createBuffer(); gl.bindBuffer(this.target, this.buffer); - gl.bufferData(this.target, new this.type(data), gl.STATIC_DRAW); + gl.bufferData(this.target, new this.type(this.data), gl.STATIC_DRAW); }; module.exports = Buffer; From 19e22ed5084a0f262716fb2ff4fa75fcc7bac12e Mon Sep 17 00:00:00 2001 From: Alexander Gugel Date: Wed, 15 Jul 2015 11:59:12 +0200 Subject: [PATCH 07/29] fix: Return gl context in getWebGLContext if available * throws an error instead of console.error when WebGL is not supported otherwise this.gl is undefined and the errors would be pretty confusing for users in either case) * WebGLRenderer#getContext returns the previously retrieved GL context if available --- webgl-renderers/WebGLRenderer.js | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/webgl-renderers/WebGLRenderer.js b/webgl-renderers/WebGLRenderer.js index f3ecb805..1f419a59 100644 --- a/webgl-renderers/WebGLRenderer.js +++ b/webgl-renderers/WebGLRenderer.js @@ -64,7 +64,7 @@ function WebGLRenderer(canvas, compositor) { this.canvas = canvas; this.compositor = compositor; - var gl = this.gl = this.getWebGLContext(this.canvas); + var gl = this.getWebGLContext(this.canvas); gl.clearColor(0.0, 0.0, 0.0, 0.0); gl.polygonOffset(0.1, 0.1); @@ -149,24 +149,17 @@ function WebGLRenderer(canvas, compositor) { * @return {Object} WebGLContext WebGL context */ WebGLRenderer.prototype.getWebGLContext = function getWebGLContext(canvas) { + if (this.gl) return this.gl; + var names = ['webgl', 'experimental-webgl', 'webkit-3d', 'moz-webgl']; - var context; - for (var i = 0, len = names.length; i < len; i++) { - try { - context = canvas.getContext(names[i]); - } - catch (error) { - console.error('Error creating WebGL context: ' + error.toString()); - } - if (context) return context; - } + for (var i = 0, len = names.length; i < len && !this.gl; i++) + this.gl = canvas.getContext(names[i]); - if (!context) { - console.error('Could not retrieve WebGL context. Please refer to https://www.khronos.org/webgl/ for requirements'); - return false; - } + if (!this.gl) + throw new Error('Could not retrieve WebGL context. Please refer to https://www.khronos.org/webgl/ for requirements'); + return this.gl; }; /** From 6b64c68331d8c6c6e07c3fb2eb18797d6ce98330 Mon Sep 17 00:00:00 2001 From: Alexander Gugel Date: Tue, 7 Jul 2015 19:34:24 +0200 Subject: [PATCH 08/29] breaks: Emit Dispatch#dispatch events on source node This change is intended to make the behavior of Dispatch#dispatch more intuitive behavior by dispatching the event on the original "source node" (the passed in node). --- core/Dispatch.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/Dispatch.js b/core/Dispatch.js index 6ef97706..930d7241 100644 --- a/core/Dispatch.js +++ b/core/Dispatch.js @@ -339,7 +339,7 @@ Dispatch.prototype.dispatch = function dispatch (path, event, payload) { if (!node) return; - this.addChildrenToQueue(node); + this._queue.push(node); var child; while ((child = this.breadthFirstNext())) From 961014e6bd9cc89c32655d6ce7df2ff857a24928 Mon Sep 17 00:00:00 2001 From: Alexander Gugel Date: Tue, 7 Jul 2015 15:03:55 +0200 Subject: [PATCH 09/29] fix: Fix Dispatch#dispatch bug Dispatch#dispatch was not calling onReceive on the nodes' components. --- core/Dispatch.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/core/Dispatch.js b/core/Dispatch.js index 930d7241..1ef38a88 100644 --- a/core/Dispatch.js +++ b/core/Dispatch.js @@ -336,16 +336,28 @@ Dispatch.prototype.dispatch = function dispatch (path, event, payload) { if (!event) throw new Error('dispatch requires an event name as it\'s second argument'); var node = this._nodes[path]; - + if (!node) return; + payload.node = node; this._queue.push(node); + var child; + var components; + var i; + var len; - while ((child = this.breadthFirstNext())) + while ((child = this.breadthFirstNext())) { if (child && child.onReceive) child.onReceive(event, payload); + components = child.getComponents(); + + for (i = 0, len = components.length ; i < len ; i++) + if (components[i] && components[i].onReceive) + components[i].onReceive(event, payload); + } + }; /** From c8251f12d6032870cc8da6ebd5228629a213368a Mon Sep 17 00:00:00 2001 From: Alexander Gugel Date: Wed, 17 Jun 2015 19:39:11 +0200 Subject: [PATCH 10/29] break: Make opacity propagate --- core/Opacity.js | 128 ++++++++ core/OpacitySystem.js | 196 +++++++++++ core/test/opacity/Opacity.api.js | 15 + core/test/opacity/Opacity.spec.js | 304 ++++++++++++++++++ core/test/opacity/Opacity.stub.js | 12 + core/test/opacitySystem/OpacitySystem.api.js | 15 + core/test/opacitySystem/OpacitySystem.spec.js | 0 core/test/opacitySystem/OpacitySystem.stub.js | 12 + 8 files changed, 682 insertions(+) create mode 100644 core/Opacity.js create mode 100644 core/OpacitySystem.js create mode 100644 core/test/opacity/Opacity.api.js create mode 100644 core/test/opacity/Opacity.spec.js create mode 100644 core/test/opacity/Opacity.stub.js create mode 100644 core/test/opacitySystem/OpacitySystem.api.js create mode 100644 core/test/opacitySystem/OpacitySystem.spec.js create mode 100644 core/test/opacitySystem/OpacitySystem.stub.js diff --git a/core/Opacity.js b/core/Opacity.js new file mode 100644 index 00000000..93519eac --- /dev/null +++ b/core/Opacity.js @@ -0,0 +1,128 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2015 Famous Industries Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +'use strict'; + +function Opacity (parent) { + this.local = 1; + this.global = 1; + this.opacity = 1; + this.parent = parent ? parent : null; + this.breakPoint = false; +} + +Opacity.WORLD_CHANGED = 1; +Opacity.LOCAL_CHANGED = 2; + +Opacity.prototype.reset = function reset () { + this.parent = null; + this.breakPoint = false; +}; + +Opacity.prototype.setParent = function setParent (parent) { + this.parent = parent; +}; + +Opacity.prototype.getParent = function getParent () { + return this.parent; +}; + +Opacity.prototype.setBreakPoint = function setBreakPoint () { + this.breakPoint = true; +}; + +Opacity.prototype.isBreakPoint = function isBreakPoint () { + return this.breakPoint; +}; + +Opacity.prototype.getLocalOpacity = function getLocalOpacity () { + return this.local; +}; + +Opacity.prototype.getWorldOpacity = function getWorldOpacity () { + if (!this.isBreakPoint()) + throw new Error('This opacity is not calculating world transforms'); + return this.global; +}; + +Opacity.prototype.calculate = function calculate (node) { + if (!this.parent || this.parent.isBreakPoint()) + return this.fromNode(node); + else return this.fromNodeWithParent(node); +}; + +Opacity.prototype.getOpacity = function getOpacity () { + return this.opacity; +}; + +Opacity.prototype.setOpacity = function setOpacity (opacity) { + this.opacity = opacity; +}; + +Opacity.prototype.calculateWorldOpacity = function calculateWorldOpacity () { + var nearestBreakPoint = this.parent; + + while (nearestBreakPoint && !nearestBreakPoint.isBreakPoint()) + nearestBreakPoint = nearestBreakPoint.parent; + + if (nearestBreakPoint) { + this.global = nearestBreakPoint.getWorldOpacity() * this.local; + } + else { + this.global = this.local; + return false; + } +}; + +Opacity.prototype.fromNode = function fromNode () { + var changed = 0; + + if (this.isBreakPoint() && this.calculateWorldOpacity()) + changed |= Opacity.WORLD_CHANGED; + + if (this.opacity !== this.local) + changed |= Opacity.LOCAL_CHANGED; + + this.local = this.opacity; + + return changed; +}; + +Opacity.prototype.fromNodeWithParent = function fromNodeWithParent () { + var oldLocalOpacity = this.getLocalOpacity(); + + var changed = 0; + + this.localOpacity = this.parent.getLocalOpacity() * oldLocalOpacity; + + if (this.isBreakPoint() && this.calculateWorldOpacity()) + changed |= Opacity.WORLD_CHANGED; + + if (oldLocalOpacity !== this.localOpacity) + changed |= Opacity.LOCAL_CHANGED; + + return changed; +}; + +module.exports = Opacity; diff --git a/core/OpacitySystem.js b/core/OpacitySystem.js new file mode 100644 index 00000000..fd041869 --- /dev/null +++ b/core/OpacitySystem.js @@ -0,0 +1,196 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2015 Famous Industries Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +'use strict'; + +var PathUtils = require('./Path'); +var Opacity = require('./Opacity'); +var Dispatch = require('./Dispatch'); +var PathStore = require('./PathStore'); + +/** + * The opacity class is responsible for calculating the opacity of a particular + * node from the data on the node and its parent + * + * @constructor {OpacitySystem} + */ +function OpacitySystem () { + this.pathStore = new PathStore(); +} + +/** + * registers a new Opacity for the given path. This opacity will be updated + * when the OpacitySystem updates. + * + * @method registerOpacityAtPath + * @return {void} + * + * @param {String} path for the opacity to be registered to. + */ +OpacitySystem.prototype.registerOpacityAtPath = function registerOpacityAtPath (path) { + if (!PathUtils.depth(path)) return this.pathStore.insert(path, new Opacity()); + + var parent = this.pathStore.get(PathUtils.parent(path)); + + if (!parent) throw new Error( + 'No parent opacity registered at expected path: ' + PathUtils.parent(path) + ); + this.pathStore.insert(path, new Opacity(parent)); +}; + +/** + * deregisters a opacity registered at the given path. + * + * @method deregisterOpacityAtPath + * @return {void} + * + * @param {String} path at which to register the opacity + */ +OpacitySystem.prototype.deregisterOpacityAtPath = function deregisterOpacityAtPath (path) { + this.pathStore.remove(path); +}; + +/** + * Method which will make the opacity currently stored at the given path a breakpoint. + * A opacity being a breakpoint means that both a local and world opacity will be calculated + * for that point. The local opacity being the concatinated opacity of all ancestor opacites up + * until the nearest breakpoint, and the world being the concatinated opacity of all ancestor opacites. + * This method throws if no opacity is at the provided path. + * + * @method + * + * @param {String} path The path at which to turn the opacity into a breakpoint + * + * @return {undefined} undefined + */ +OpacitySystem.prototype.makeBreakPointAt = function makeBreakPointAt (path) { + var opacity = this.pathStore.get(path); + if (!opacity) throw new Error('No opacity Registered at path: ' + path); + opacity.setBreakPoint(); +}; + +/** + * Returns the instance of the opacity class associated with the given path, + * or undefined if no opacity is associated. + * + * @method + * + * @param {String} path The path to lookup + * + * @return {Opacity | undefined} the opacity at that path is available, else undefined. + */ +OpacitySystem.prototype.get = function get (path) { + return this.pathStore.get(path); +}; + +/** + * onUpdate is called when the opacity system requires an update. + * It traverses the opacity array and evaluates the necessary opacites + * in the scene graph with the information from the corresponding node + * in the scene graph + * + * @method onUpdate + */ +OpacitySystem.prototype.onUpdate = function onUpdate () { + var opacites = this.pathStore.getItems(); + var paths = this.pathStore.getPaths(); + var opacity; + var changed; + var node; + var components; + + for (var i = 0, len = opacites.length ; i < len ; i++) { + node = Dispatch.getNode(paths[i]); + if (!node) continue; + components = node.getComponents(); + opacity = opacites[i]; + if ((changed = opacity.from(node))) { + opacityChanged(node, components, opacity); + if (changed & Opacity.LOCAL_CHANGED) localOpacityChanged(node, components, opacity.getLocalOpacity()); + if (changed & Opacity.WORLD_CHANGED) worldOpacityChanged(node, components, opacity.getWorldOpacity()); + } + } +}; + +/** + * Private method to call when either the Local or World Opacity changes. + * Triggers 'onOpacityChange' methods on the node and all of the node's components + * + * @method + * @private + * + * @param {Node} node the node on which to trigger a change event if necessary + * @param {Array} components the components on which to trigger a change event if necessary + * @param {Opacity} opacity the opacity class that changed + * + * @return {undefined} undefined + */ +function opacityChanged (node, components, opacity) { + if (node.onOpacityChange) node.onOpacityChange(opacity); + for (var i = 0, len = components.length ; i < len ; i++) + if (components[i] && components[i].onOpacityChange) + components[i].onOpacityChange(opacity); +} + +/** + * Private method to call when the local opacity changes. Triggers 'onLocalOpacityChange' methods + * on the node and all of the node's components + * + * @method + * @private + * + * @param {Node} node the node on which to trigger a change event if necessary + * @param {Array} components the components on which to trigger a change event if necessary + * @param {Array} opacity the local opacity + * + * @return {undefined} undefined + */ +function localOpacityChanged (node, components, opacity) { + if (node.onLocalOpacityChange) node.onLocalOpacityChange(opacity); + for (var i = 0, len = components.length ; i < len ; i++) + if (components[i] && components[i].onLocalOpacityChange) + components[i].onLocalOpacityChange(opacity); +} + +/** + * Private method to call when the world opacity changes. Triggers 'onWorldOpacityChange' methods + * on the node and all of the node's components + * + * @method + * @private + * + * @param {Node} node the node on which to trigger a change event if necessary + * @param {Array} components the components on which to trigger a change event if necessary + * @param {Array} opacity the world opacity + * + * @return {undefined} undefined + */ +function worldOpacityChanged (node, components, opacity) { + if (node.onWorldOpacityChange) node.onWorldOpacityChange(opacity); + for (var i = 0, len = components.length ; i < len ; i++) + if (components[i] && components[i].onWorldOpacityChange) + components[i].onWorldOpacityChange(opacity); +} + +module.exports = new OpacitySystem(); diff --git a/core/test/opacity/Opacity.api.js b/core/test/opacity/Opacity.api.js new file mode 100644 index 00000000..f19d00d9 --- /dev/null +++ b/core/test/opacity/Opacity.api.js @@ -0,0 +1,15 @@ +module.exports = [ + 'reset', + 'setParent', + 'getParent', + 'setBreakPoint', + 'isBreakPoint', + 'getLocalOpacity', + 'getWorldOpacity', + 'calculate', + 'getOpacity', + 'setOpacity', + 'calculateWorldOpacity', + 'fromNode', + 'fromNodeWithParent' +]; diff --git a/core/test/opacity/Opacity.spec.js b/core/test/opacity/Opacity.spec.js new file mode 100644 index 00000000..abaaba3c --- /dev/null +++ b/core/test/opacity/Opacity.spec.js @@ -0,0 +1,304 @@ +'use strict'; + +var test = require('tape'); +var api = require('./Opacity.api'); +var Opacity = require('../../Opacity'); +var OpacityStub = require('./Opacity.stub'); +var NodeStub = require('../node/Node.stub'); +var sinon = require('sinon'); + +function createTestNode () { + var node = new NodeStub(); + // node.getSize.returns([100, 100, 100]); + // node.getParent.returns({ getSize: sinon.stub().returns([200, 200, 200]) }); + return node; +} + +test('Opacity class', function (t) { + + t.test('Opacity constructor' , function (t) { + + t.ok(Opacity, 'There should be a transform module'); + t.equal(Opacity.constructor, Function, 'Opacity should be a function'); + + t.doesNotThrow(function () { + return new Opacity(); + }, 'Opacity should be callable with new'); + + t.doesNotThrow(function () { + return new Opacity(new OpacityStub()); + }, 'Opacity should be callable with new and another transform as an argument'); + + t.equal((new Opacity()).constructor, Opacity, 'Opacity should be a constructor function'); + + var transform = new Opacity(); + + api.forEach(function (method) { + t.ok( + transform[method] && transform[method].constructor === Function, + 'Opacity should have a ' + method + ' method' + ); + }); + + t.equal(Opacity.WORLD_CHANGED, 1, 'Opacity should have a static property WORLD_CHANGED that equals 1'); + t.equal(Opacity.LOCAL_CHANGED, 2, 'Opacity should have a static property LOCAL_CHANGED that equals 2'); + + var parent = new OpacityStub(); + transform = new Opacity(parent); + + t.equal(transform.getParent(), parent, 'Opacity constructor should have its parent set to the first argument'); + + t.notOk(transform.isBreakPoint(), 'Transforms should not be a breakpoint by default'); + + t.end(); + }); + + t.test('no breakpoints', function (t) { + + var opacities = []; + + for (var i = 0; i < 5; i++) + opacities.push(new Opacity(opacities[i - 1])); + + + for (i = 0; i < opacities.length; i++) + opacities[i].setOpacity(0.5); + + + for (i = 0; i < opacities.length; i++) + console.log(opacities[i]) + + + + t.end(); + }); + // + // t.test('getParent method', function (t) { + // + // var transform = new Opacity('hello'); + // t.doesNotThrow(function () { + // transform.getParent(); + // }, 'transform should be callable'); + // + // t.equal(transform.getParent(), 'hello', 'getParent should return the value passed to the constructor'); + // transform.setParent('bye'); + // t.equal(transform.getParent(), 'bye', 'getParent should return the value passed to the setParent method'); + // + // t.end(); + // }); + // + // t.test('setBreakPoint', function (t) { + // + // var transform = new Opacity(); + // + // // sanity check + // if (transform.isBreakPoint()) + // throw new Error('Opacity should not be a breakpoint by default.' + + // ' isBreakPoint or the constructor might be broken'); + // + // t.doesNotThrow( function () { + // transform.setBreakPoint(); + // }, 'setBreakPoint should be callable'); + // + // t.ok(transform.isBreakPoint(), 'after calling setBreakpoint, a transform should be a breakpoint'); + // + // t.end(); + // }); + // + // t.test('isBreakPoint', function (t) { + // + // var transform = new Opacity(); + // + // t.doesNotThrow(function () { + // t.notOk(transform.isBreakPoint(), 'transforms are not a breakpoint when they are first instantiated'); + // }, 'isBreakPoint should be callable if the transform is not a breakpoint'); + // + // transform.setBreakPoint(); + // + // t.doesNotThrow(function () { + // t.ok(transform.isBreakPoint(), 'isBreakPoint should return true when the transform is a breakpoint'); + // }, 'isBreakPoint should be callable if the transform is a breakpoint'); + // + // transform.reset(); + // + // t.end(); + // }); + // + // t.test('reset method', function (t) { + // var parent = new OpacityStub(); + // var transform = new Opacity(parent); + // transform.setBreakPoint(); + // + // // sanity check + // if (parent !== transform.getParent() || !transform.isBreakPoint()) + // throw new Error('transform.getParent or isBreakPoint is not functioning correctly'); + // + // t.doesNotThrow(transform.reset.bind(transform), 'reset should be callable without arguments'); + // + // api.forEach(function (method) { + // t.notOk(parent[method].called, 'No calls should be made on the parent during reset'); + // }); + // + // var a = 2; + // while (a--) { + // t.ok(transform.getParent() == null, 'after being reset, transform should not have a parent'); + // t.notOk(transform.isBreakPoint(), 'after being reset, transform should not be a breakpoint'); + // transform = new Opacity(); + // transform.reset(); + // } + // + // t.end(); + // }); + // + // t.test('getLocalTransform method', function (t) { + // var transform = new Opacity(); + // t.doesNotThrow(function () { + // t.deepEqual(transform.getLocalTransform(), new Float32Array([ + // 1, 0, 0, 0, + // 0, 1, 0, 0, + // 0, 0, 1, 0, + // 0, 0, 0, 1]), 'transform.getLocalTransform should return' + + // ' identity matrix after instantiation'); + // }, 'getLocalTransform should be callable'); + // + // t.end(); + // }); + // + // t.test('getWorldTransform method', function (t) { + // var transform = new Opacity(); + // + // // sanity check + // if (transform.isBreakPoint()) throw new Error('transform is reporting itself to be ' + + // 'a breakpoint after instantiation. isBreakPoint ' + + // 'or the constructor might be broken'); + // + // t.throws(transform.getWorldTransform.bind(transform), 'getWorldTransform should throw if ' + + // 'the transform isn\'t a breakpoint'); + // + // transform.setBreakPoint(); + // + // t.doesNotThrow(function () { + // t.deepEqual(transform.getWorldTransform(), new Float32Array([ + // 1, 0, 0, 0, + // 0, 1, 0, 0, + // 0, 0, 1, 0, + // 0, 0, 0, 1]), 'transform.getWorldTransform should return' + + // ' identity matrix after instantiation'); + // }, 'getWorldTransform should not throw if the transform is a breakpoint'); + // + // t.end(); + // }); + // + // t.test('calculate method', function (t) { + // var transform = new Opacity(); + // + // t.doesNotThrow(function () { + // transform.calculate(createTestNode()); + // t.deepEqual(transform.getLocalTransform(), new Float32Array([ + // 1, 0, 0, 0, + // 0, 1, 0, 0, + // 0, 0, 1, 0, + // 0, 0, 0, 1]), 'transform.getLocalTransform should return' + + // ' identity matrix with no vectors changed'); + // }, '.calculate should be callable'); + // + // t.end(); + // }); + // + // t.test('setPosition method', function (t) { + // var transform = new Opacity(); + // + // t.doesNotThrow( function () { + // transform.setPosition(0); + // t.deepEqual(transform.getPosition(), new Float32Array([0, 0, 0]), 'transform should not change from zero when a dimension is passed ' + + // 'null, undefined, or zero'); + // transform.setPosition(0, 0); + // t.deepEqual(transform.getPosition(), new Float32Array([0, 0, 0]), 'transform should not change from zero when a dimension is passed ' + + // 'null, undefined, or zero'); + // transform.setPosition(0, 0, 0); + // t.deepEqual(transform.getPosition(), new Float32Array([0, 0, 0]), 'transform should not change from zero when a dimension is passed ' + + // 'null, undefined, or zero'); + // transform.setPosition(null, 0, 0); + // t.deepEqual(transform.getPosition(), new Float32Array([0, 0, 0]), 'transform should not change from zero when a dimension is passed ' + + // 'null, undefined, or zero'); + // transform.setPosition(null, null, 0); + // t.deepEqual(transform.getPosition(), new Float32Array([0, 0, 0]), 'transform should not change from zero when a dimension is passed ' + + // 'null, undefined, or zero'); + // transform.setPosition(null, null, null); + // t.deepEqual(transform.getPosition(), new Float32Array([0, 0, 0]), 'transform should not change from zero when a dimension is passed ' + + // 'null, undefined, or zero'); + // transform.setPosition(null, 0); + // t.deepEqual(transform.getPosition(), new Float32Array([0, 0, 0]), 'transform should not change from zero when a dimension is passed ' + + // 'null, undefined, or zero'); + // + // transform.setPosition(0, 1); + // + // t.deepEqual(transform.getPosition(), new Float32Array([0, 1, 0]), 'transform should set the value properly for the given dimension'); + // + // }, 'transform should be callable with any number of arguments'); + // + // transform.setPosition(1, 2, 3); + // + // t.deepEqual(transform.getPosition(), new Float32Array([1, 2, 3]), 'transform should set the values returned by getPosition'); + // + // transform.setPosition(); + // + // t.deepEqual(transform.getPosition(), new Float32Array([1, 2, 3]), 'undefined arguments should not change the values stored'); + // + // transform.setPosition(null, null, null); + // + // t.deepEqual(transform.getPosition(), new Float32Array([1, 2, 3]), 'null arguments should not change the values stored'); + // + // transform.setPosition(0, 0, 0); + // + // t.deepEqual(transform.getPosition(), new Float32Array([0, 0, 0]), 'zero should successfully set the position back to zero'); + // + // var node = createTestNode(); + // + // transform.setPosition(3, 3, 3); + // + // transform.calculate(node); + // + // t.deepEqual(transform.getLocalTransform(), new Float32Array([ + // 1, 0, 0, 0, + // 0, 1, 0, 0, + // 0, 0, 1, 0, + // 3, 3, 3, 1]), 'position should change the ' + + // 'result of the calculated matrix'); + // + // + // t.end(); + // }); + // + // t.test('setRotation method', function (t) { + // + // // todo + // + // t.end(); + // }); + // + // t.test('setScale method', function (t) { + // + // t.end(); + // }); + // + // t.test('setAlign method', function (t) { + // + // t.end(); + // }); + // + // t.test('setMountPoint method', function (t) { + // + // t.end(); + // }); + // + // t.test('setOrigin method', function (t) { + // + // t.end(); + // }); + // + // t.test('calculateWorldMatrix', function (t) { + // + // t.end(); + // }); +}); diff --git a/core/test/opacity/Opacity.stub.js b/core/test/opacity/Opacity.stub.js new file mode 100644 index 00000000..86eb8de4 --- /dev/null +++ b/core/test/opacity/Opacity.stub.js @@ -0,0 +1,12 @@ +'use strict'; + +var api = require('./Opacity.api'); +var sinon = require('sinon'); + +function Opacity (parent) { + api.forEach(function (method) { + this[method] = sinon.stub(); + }.bind(this)); +} + +module.exports = Opacity; diff --git a/core/test/opacitySystem/OpacitySystem.api.js b/core/test/opacitySystem/OpacitySystem.api.js new file mode 100644 index 00000000..f19d00d9 --- /dev/null +++ b/core/test/opacitySystem/OpacitySystem.api.js @@ -0,0 +1,15 @@ +module.exports = [ + 'reset', + 'setParent', + 'getParent', + 'setBreakPoint', + 'isBreakPoint', + 'getLocalOpacity', + 'getWorldOpacity', + 'calculate', + 'getOpacity', + 'setOpacity', + 'calculateWorldOpacity', + 'fromNode', + 'fromNodeWithParent' +]; diff --git a/core/test/opacitySystem/OpacitySystem.spec.js b/core/test/opacitySystem/OpacitySystem.spec.js new file mode 100644 index 00000000..e69de29b diff --git a/core/test/opacitySystem/OpacitySystem.stub.js b/core/test/opacitySystem/OpacitySystem.stub.js new file mode 100644 index 00000000..c1d2b23f --- /dev/null +++ b/core/test/opacitySystem/OpacitySystem.stub.js @@ -0,0 +1,12 @@ +'use strict'; + +var api = require('./OpacitySystem.api'); +var sinon = require('sinon'); + +var OpacitySystem = {}; + +api.forEach(function (method) { + OpacitySystem[method] = sinon.stub(); +}); + +module.exports = OpacitySystem; From 4071b7bd315688ef98cc2d8e74a6d603e770c23e Mon Sep 17 00:00:00 2001 From: Alexander Gugel Date: Thu, 18 Jun 2015 11:05:38 +0200 Subject: [PATCH 11/29] feat: Add Opacity and OpacitySystem to Node --- core/Node.js | 33 ++++++++++++++++----------------- core/Opacity.js | 19 +++++++++++-------- 2 files changed, 27 insertions(+), 25 deletions(-) diff --git a/core/Node.js b/core/Node.js index 119a08ec..6dd484c5 100644 --- a/core/Node.js +++ b/core/Node.js @@ -29,7 +29,9 @@ var SizeSystem = require('./SizeSystem'); var Dispatch = require('./Dispatch'); var TransformSystem = require('./TransformSystem'); +var OpacitySystem = require('./OpacitySystem'); var Size = require('./Size'); +var Opacity = require('./Opacity'); var Transform = require('./Transform'); /** @@ -76,7 +78,6 @@ function Node () { this._mounted = false; this._shown = true; this._updater = null; - this._opacity = 1; this._UIEvents = []; this._updateQueue = []; @@ -96,6 +97,7 @@ function Node () { this._transformID = null; this._sizeID = null; + this._opacityID = null; if (!this.constructor.NO_DEFAULT_COMPONENTS) this._init(); } @@ -117,6 +119,7 @@ Node.NO_DEFAULT_COMPONENTS = false; Node.prototype._init = function _init () { this._transformID = this.addComponent(new Transform()); this._sizeID = this.addComponent(new Size()); + this._opacityID = this.addComponent(new Opacity()); }; /** @@ -473,7 +476,11 @@ Node.prototype.isShown = function isShown () { * @return {Number} Relative opacity of the node. */ Node.prototype.getOpacity = function getOpacity () { - return this._opacity; + if (this.constructor.INIT_DEFAULT_COMPONENTS) + return this.getComponent(this._opacityID).getOpacity(); + else if (this.isMounted()) + return TransformSystem.get(this.getLocation()).getOpacity(); + else throw new Error('This node does not have access to an opacity component'); }; /** @@ -1062,28 +1069,20 @@ Node.prototype.setScale = function setScale (x, y, z) { /** * Sets the value of the opacity of this node. All of the node's - * components will have onOpacityChange called on them/ + * components will have onOpacityChange called on them. * * @method * - * @param {Number} val Value of the opacity. 1 is the default. + * @param {Number} val=1 Value of the opacity. 1 is the default. * * @return {Node} this */ Node.prototype.setOpacity = function setOpacity (val) { - if (val !== this._opacity) { - this._opacity = val; - if (!this._requestingUpdate) this._requestUpdate(); - - var i = 0; - var list = this._components; - var len = list.length; - var item; - for (; i < len ; i++) { - item = list[i]; - if (item && item.onOpacityChange) item.onOpacityChange(val); - } - } + if (this.constructor.INIT_DEFAULT_COMPONENTS) + this.getComponent(this._opacityID).setOpacity(val); + else if (this.isMounted()) + SizeSystem.get(this.getLocation()).setOpacity(val); + else throw new Error('This node does not have access to an opacity component'); return this; }; diff --git a/core/Opacity.js b/core/Opacity.js index 93519eac..df89d795 100644 --- a/core/Opacity.js +++ b/core/Opacity.js @@ -83,6 +83,8 @@ Opacity.prototype.setOpacity = function setOpacity (opacity) { Opacity.prototype.calculateWorldOpacity = function calculateWorldOpacity () { var nearestBreakPoint = this.parent; + var previousGlobal = this.global; + while (nearestBreakPoint && !nearestBreakPoint.isBreakPoint()) nearestBreakPoint = nearestBreakPoint.parent; @@ -91,35 +93,36 @@ Opacity.prototype.calculateWorldOpacity = function calculateWorldOpacity () { } else { this.global = this.local; - return false; } + + return previousGlobal !== this.global; }; Opacity.prototype.fromNode = function fromNode () { var changed = 0; - if (this.isBreakPoint() && this.calculateWorldOpacity()) - changed |= Opacity.WORLD_CHANGED; - if (this.opacity !== this.local) changed |= Opacity.LOCAL_CHANGED; this.local = this.opacity; + if (this.isBreakPoint() && this.calculateWorldOpacity()) + changed |= Opacity.WORLD_CHANGED; + return changed; }; Opacity.prototype.fromNodeWithParent = function fromNodeWithParent () { - var oldLocalOpacity = this.getLocalOpacity(); - var changed = 0; - this.localOpacity = this.parent.getLocalOpacity() * oldLocalOpacity; + var previousLocal = this.local; + + this.local = this.parent.getLocalOpacity() * this.opacity; if (this.isBreakPoint() && this.calculateWorldOpacity()) changed |= Opacity.WORLD_CHANGED; - if (oldLocalOpacity !== this.localOpacity) + if (previousLocal !== this.local) changed |= Opacity.LOCAL_CHANGED; return changed; From 31748853dd71a17825d5983a563dbc1c26f4c7ab Mon Sep 17 00:00:00 2001 From: Alexander Gugel Date: Thu, 18 Jun 2015 11:06:11 +0200 Subject: [PATCH 12/29] feat: Use local opacity in DOMElement --- dom-renderables/DOMElement.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dom-renderables/DOMElement.js b/dom-renderables/DOMElement.js index 05f9d12b..64634afc 100644 --- a/dom-renderables/DOMElement.js +++ b/dom-renderables/DOMElement.js @@ -172,6 +172,7 @@ DOMElement.prototype.onMount = function onMount(node, id) { this._UIEvents = node.getUIEvents().slice(0); TransformSystem.makeBreakPointAt(node.getLocation()); this.onSizeModeChange.apply(this, node.getSizeMode()); + OpacitySystem.makeBreakPointAt(node.getLocation()); this.draw(); this.setAttribute('data-fa-path', node.getLocation()); }; @@ -291,6 +292,8 @@ DOMElement.prototype.onSizeChange = function onSizeChange(x, y) { * @return {DOMElement} this */ DOMElement.prototype.onOpacityChange = function onOpacityChange(opacity) { + opacity = opacity.getLocalOpacity(); + return this.setProperty('opacity', opacity); }; @@ -468,6 +471,7 @@ DOMElement.prototype.init = function init () { this._changeQueue.push(Commands.INIT_DOM, this._tagName); this._initialized = true; this.onTransformChange(TransformSystem.get(this._node.getLocation())); + this.onOpacityChange(OpacitySystem.get(this._node.getLocation())); var size = this._node.getSize(); this.onSizeChange(size[0], size[1]); if (!this._requestingUpdate) this._requestUpdate(); From ec5fc8edcd390e01287452e77b1c331711cf586f Mon Sep 17 00:00:00 2001 From: Alexander Gugel Date: Thu, 18 Jun 2015 11:07:20 +0200 Subject: [PATCH 13/29] test: Add test case for Opacity#calculate --- core/test/opacity/Opacity.api.js | 26 ++ core/test/opacity/Opacity.spec.js | 409 ++++++++++++------------------ core/test/opacity/Opacity.stub.js | 24 ++ 3 files changed, 211 insertions(+), 248 deletions(-) diff --git a/core/test/opacity/Opacity.api.js b/core/test/opacity/Opacity.api.js index f19d00d9..dea43344 100644 --- a/core/test/opacity/Opacity.api.js +++ b/core/test/opacity/Opacity.api.js @@ -1,3 +1,29 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2015 Famous Industries Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +'use strict'; + module.exports = [ 'reset', 'setParent', diff --git a/core/test/opacity/Opacity.spec.js b/core/test/opacity/Opacity.spec.js index abaaba3c..92dd1ec5 100644 --- a/core/test/opacity/Opacity.spec.js +++ b/core/test/opacity/Opacity.spec.js @@ -1,3 +1,27 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2015 Famous Industries Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + 'use strict'; var test = require('tape'); @@ -7,17 +31,9 @@ var OpacityStub = require('./Opacity.stub'); var NodeStub = require('../node/Node.stub'); var sinon = require('sinon'); -function createTestNode () { - var node = new NodeStub(); - // node.getSize.returns([100, 100, 100]); - // node.getParent.returns({ getSize: sinon.stub().returns([200, 200, 200]) }); - return node; -} - test('Opacity class', function (t) { t.test('Opacity constructor' , function (t) { - t.ok(Opacity, 'There should be a transform module'); t.equal(Opacity.constructor, Function, 'Opacity should be a function'); @@ -53,252 +69,149 @@ test('Opacity class', function (t) { t.end(); }); - t.test('no breakpoints', function (t) { - + t.test('calculate', function (t) { var opacities = []; - for (var i = 0; i < 5; i++) - opacities.push(new Opacity(opacities[i - 1])); - + var opacity; + + for (var i = 0; i < 5; i++) { + opacity = new Opacity(opacities[i - 1]); + + opacities.push(opacity); + opacity.setOpacity(0.5); + } + + + t.comment('Root Opacity (no breakpoint): 0.5'); + + t.equal( + opacities[0].calculate(), Opacity.LOCAL_CHANGED & ~Opacity.WORLD_CHANGED, + 'Calculating the root opacity should only change the local opacity' + ); + t.equal( + opacities[0].getLocalOpacity(), 0.5, + 'The root local opacity should be set to 0.5' + ); + + t.equal( + opacities[0].getOpacity(), 0.5, + 'The root opacity should still be set to 0.5 after calculation' + ); + t.throws( + function() { + opacities[0].getWorldOpacity(); + }, + /not calculating world transforms/, + 'Attempting to get the world opacity of the root opacity should throw an error, since no breakpoint has been set on it' + ); + + + t.comment('2nd Opacity (no breakpoint): 0.5'); + + t.equal( + opacities[1].calculate(), Opacity.LOCAL_CHANGED & ~Opacity.WORLD_CHANGED, + 'Calculating the 2nd opacity (child of root) should only change the local opacity, since no breakpoint has been set on it' + ); + t.equal( + opacities[1].getLocalOpacity(), 0.25, + 'The 2nd opacity should have been multiplied with the root opacity' + ); + + t.equal( + opacities[1].getOpacity(), 0.5, + 'The 2nd opacity should still be set to 0.5 after calculation' + ); + t.throws( + function() { + opacities[1].getWorldOpacity(); + }, + /not calculating world transforms/, + 'Attempting to get the world opacity of the 2nd opacity should throw an error, since no breakpoint has been set on it' + ); + + + t.comment('3rd Opacity (no breakpoint): 0.5'); + + t.equal( + opacities[2].calculate(), Opacity.LOCAL_CHANGED & ~Opacity.WORLD_CHANGED, + 'Calculating the 3rd opacity should only change the local opacity, since no breakpoint has been set on it' + ); + t.equal( + opacities[2].getLocalOpacity(), 0.125, + 'The 3rd opacity should have been multiplied with the root and 2nd opacity' + ); + + t.equal( + opacities[2].getOpacity(), 0.5, + 'The 3rd opacity should still be set to 0.5 after calculation' + ); + t.throws( + function() { + opacities[2].getWorldOpacity(); + }, + /not calculating world transforms/, + 'Attempting to get the world opacity of the 3rd opacity should throw an error, since no breakpoint has been set on it' + ); + + + t.comment('4th Opacity (breakpoint): 0.5'); + + opacities[3].setBreakPoint(); + t.equal( + opacities[3].calculate(), Opacity.LOCAL_CHANGED | Opacity.WORLD_CHANGED, + 'Calculating the 4th opacity should have checked if the world opacity changed, since it has a breakpoint set on it' + ); + + t.equal( + opacities[3].isBreakPoint(), true, + 'The 4th opacity should still have breakpoint after calculation' + ); + t.equal( + opacities[3].getLocalOpacity(), 0.0625, + 'The 4th local opacity should have been multiplied with the root, 2nd and 3rd opacity' + ); + t.equal( + opacities[3].getOpacity(), 0.5, + 'The 4th opacity should still be set to 0.5 after calculation' + ); + t.doesNotThrow(function() { + opacities[3].getWorldOpacity(); + }, 'Attempting to calculate the world on the 4th opacity should not throw an error, since a breakpoint has been set on it'); + + t.equal( + opacities[3].getWorldOpacity(), 0.0625, + 'The 4th world opacity should be have been multiplied with all previous opacities' + ); + + + t.comment('5th Opacity (no breakpoint): 0.5') + + t.equal( + opacities[4].calculate(), Opacity.LOCAL_CHANGED & ~Opacity.WORLD_CHANGED, + 'Calculating the 5th opacity should only change the local opacity, since no breakpoint has been set on it' + ); + t.equal( + opacities[4].getLocalOpacity(), 0.5, + 'The 5th local opacity should be equivalent to the opacity set on it, it should not have been multiplied, since the 4th node has a breakpoint set on it' + ); + + t.equal( + opacities[4].getOpacity(), 0.5, + 'The 5th opacity should still be set to 0.5 after calculation' + ); + + t.throws( + function() { + opacities[4].getWorldOpacity(); + }, + /not calculating world transforms/, + 'Attempting to get the world opacity of the 4th opacity should throw an error, since no breakpoint has been set on it' + ); for (i = 0; i < opacities.length; i++) - opacities[i].setOpacity(0.5); - - - for (i = 0; i < opacities.length; i++) - console.log(opacities[i]) - + console.log(JSON.stringify(opacities[i])); t.end(); }); - // - // t.test('getParent method', function (t) { - // - // var transform = new Opacity('hello'); - // t.doesNotThrow(function () { - // transform.getParent(); - // }, 'transform should be callable'); - // - // t.equal(transform.getParent(), 'hello', 'getParent should return the value passed to the constructor'); - // transform.setParent('bye'); - // t.equal(transform.getParent(), 'bye', 'getParent should return the value passed to the setParent method'); - // - // t.end(); - // }); - // - // t.test('setBreakPoint', function (t) { - // - // var transform = new Opacity(); - // - // // sanity check - // if (transform.isBreakPoint()) - // throw new Error('Opacity should not be a breakpoint by default.' + - // ' isBreakPoint or the constructor might be broken'); - // - // t.doesNotThrow( function () { - // transform.setBreakPoint(); - // }, 'setBreakPoint should be callable'); - // - // t.ok(transform.isBreakPoint(), 'after calling setBreakpoint, a transform should be a breakpoint'); - // - // t.end(); - // }); - // - // t.test('isBreakPoint', function (t) { - // - // var transform = new Opacity(); - // - // t.doesNotThrow(function () { - // t.notOk(transform.isBreakPoint(), 'transforms are not a breakpoint when they are first instantiated'); - // }, 'isBreakPoint should be callable if the transform is not a breakpoint'); - // - // transform.setBreakPoint(); - // - // t.doesNotThrow(function () { - // t.ok(transform.isBreakPoint(), 'isBreakPoint should return true when the transform is a breakpoint'); - // }, 'isBreakPoint should be callable if the transform is a breakpoint'); - // - // transform.reset(); - // - // t.end(); - // }); - // - // t.test('reset method', function (t) { - // var parent = new OpacityStub(); - // var transform = new Opacity(parent); - // transform.setBreakPoint(); - // - // // sanity check - // if (parent !== transform.getParent() || !transform.isBreakPoint()) - // throw new Error('transform.getParent or isBreakPoint is not functioning correctly'); - // - // t.doesNotThrow(transform.reset.bind(transform), 'reset should be callable without arguments'); - // - // api.forEach(function (method) { - // t.notOk(parent[method].called, 'No calls should be made on the parent during reset'); - // }); - // - // var a = 2; - // while (a--) { - // t.ok(transform.getParent() == null, 'after being reset, transform should not have a parent'); - // t.notOk(transform.isBreakPoint(), 'after being reset, transform should not be a breakpoint'); - // transform = new Opacity(); - // transform.reset(); - // } - // - // t.end(); - // }); - // - // t.test('getLocalTransform method', function (t) { - // var transform = new Opacity(); - // t.doesNotThrow(function () { - // t.deepEqual(transform.getLocalTransform(), new Float32Array([ - // 1, 0, 0, 0, - // 0, 1, 0, 0, - // 0, 0, 1, 0, - // 0, 0, 0, 1]), 'transform.getLocalTransform should return' + - // ' identity matrix after instantiation'); - // }, 'getLocalTransform should be callable'); - // - // t.end(); - // }); - // - // t.test('getWorldTransform method', function (t) { - // var transform = new Opacity(); - // - // // sanity check - // if (transform.isBreakPoint()) throw new Error('transform is reporting itself to be ' + - // 'a breakpoint after instantiation. isBreakPoint ' + - // 'or the constructor might be broken'); - // - // t.throws(transform.getWorldTransform.bind(transform), 'getWorldTransform should throw if ' + - // 'the transform isn\'t a breakpoint'); - // - // transform.setBreakPoint(); - // - // t.doesNotThrow(function () { - // t.deepEqual(transform.getWorldTransform(), new Float32Array([ - // 1, 0, 0, 0, - // 0, 1, 0, 0, - // 0, 0, 1, 0, - // 0, 0, 0, 1]), 'transform.getWorldTransform should return' + - // ' identity matrix after instantiation'); - // }, 'getWorldTransform should not throw if the transform is a breakpoint'); - // - // t.end(); - // }); - // - // t.test('calculate method', function (t) { - // var transform = new Opacity(); - // - // t.doesNotThrow(function () { - // transform.calculate(createTestNode()); - // t.deepEqual(transform.getLocalTransform(), new Float32Array([ - // 1, 0, 0, 0, - // 0, 1, 0, 0, - // 0, 0, 1, 0, - // 0, 0, 0, 1]), 'transform.getLocalTransform should return' + - // ' identity matrix with no vectors changed'); - // }, '.calculate should be callable'); - // - // t.end(); - // }); - // - // t.test('setPosition method', function (t) { - // var transform = new Opacity(); - // - // t.doesNotThrow( function () { - // transform.setPosition(0); - // t.deepEqual(transform.getPosition(), new Float32Array([0, 0, 0]), 'transform should not change from zero when a dimension is passed ' + - // 'null, undefined, or zero'); - // transform.setPosition(0, 0); - // t.deepEqual(transform.getPosition(), new Float32Array([0, 0, 0]), 'transform should not change from zero when a dimension is passed ' + - // 'null, undefined, or zero'); - // transform.setPosition(0, 0, 0); - // t.deepEqual(transform.getPosition(), new Float32Array([0, 0, 0]), 'transform should not change from zero when a dimension is passed ' + - // 'null, undefined, or zero'); - // transform.setPosition(null, 0, 0); - // t.deepEqual(transform.getPosition(), new Float32Array([0, 0, 0]), 'transform should not change from zero when a dimension is passed ' + - // 'null, undefined, or zero'); - // transform.setPosition(null, null, 0); - // t.deepEqual(transform.getPosition(), new Float32Array([0, 0, 0]), 'transform should not change from zero when a dimension is passed ' + - // 'null, undefined, or zero'); - // transform.setPosition(null, null, null); - // t.deepEqual(transform.getPosition(), new Float32Array([0, 0, 0]), 'transform should not change from zero when a dimension is passed ' + - // 'null, undefined, or zero'); - // transform.setPosition(null, 0); - // t.deepEqual(transform.getPosition(), new Float32Array([0, 0, 0]), 'transform should not change from zero when a dimension is passed ' + - // 'null, undefined, or zero'); - // - // transform.setPosition(0, 1); - // - // t.deepEqual(transform.getPosition(), new Float32Array([0, 1, 0]), 'transform should set the value properly for the given dimension'); - // - // }, 'transform should be callable with any number of arguments'); - // - // transform.setPosition(1, 2, 3); - // - // t.deepEqual(transform.getPosition(), new Float32Array([1, 2, 3]), 'transform should set the values returned by getPosition'); - // - // transform.setPosition(); - // - // t.deepEqual(transform.getPosition(), new Float32Array([1, 2, 3]), 'undefined arguments should not change the values stored'); - // - // transform.setPosition(null, null, null); - // - // t.deepEqual(transform.getPosition(), new Float32Array([1, 2, 3]), 'null arguments should not change the values stored'); - // - // transform.setPosition(0, 0, 0); - // - // t.deepEqual(transform.getPosition(), new Float32Array([0, 0, 0]), 'zero should successfully set the position back to zero'); - // - // var node = createTestNode(); - // - // transform.setPosition(3, 3, 3); - // - // transform.calculate(node); - // - // t.deepEqual(transform.getLocalTransform(), new Float32Array([ - // 1, 0, 0, 0, - // 0, 1, 0, 0, - // 0, 0, 1, 0, - // 3, 3, 3, 1]), 'position should change the ' + - // 'result of the calculated matrix'); - // - // - // t.end(); - // }); - // - // t.test('setRotation method', function (t) { - // - // // todo - // - // t.end(); - // }); - // - // t.test('setScale method', function (t) { - // - // t.end(); - // }); - // - // t.test('setAlign method', function (t) { - // - // t.end(); - // }); - // - // t.test('setMountPoint method', function (t) { - // - // t.end(); - // }); - // - // t.test('setOrigin method', function (t) { - // - // t.end(); - // }); - // - // t.test('calculateWorldMatrix', function (t) { - // - // t.end(); - // }); }); diff --git a/core/test/opacity/Opacity.stub.js b/core/test/opacity/Opacity.stub.js index 86eb8de4..79e57470 100644 --- a/core/test/opacity/Opacity.stub.js +++ b/core/test/opacity/Opacity.stub.js @@ -1,3 +1,27 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2015 Famous Industries Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + 'use strict'; var api = require('./Opacity.api'); From ecd64803360195eccf7790c78a3221f5f8a102a4 Mon Sep 17 00:00:00 2001 From: Alexander Gugel Date: Thu, 18 Jun 2015 11:34:24 +0200 Subject: [PATCH 14/29] fix: Correctly register and retrieve Opacity --- core/Node.js | 15 +++++++++++---- core/OpacitySystem.js | 12 ++++++------ core/Scene.js | 2 ++ core/test/opacity/Opacity.spec.js | 4 ---- dom-renderables/DOMElement.js | 2 +- 5 files changed, 20 insertions(+), 15 deletions(-) diff --git a/core/Node.js b/core/Node.js index 6dd484c5..079c9451 100644 --- a/core/Node.js +++ b/core/Node.js @@ -247,7 +247,7 @@ Node.prototype.getValue = function getValue () { showState: { mounted: this.isMounted(), shown: this.isShown(), - opacity: this.getOpacity() || null + opacity: 1 }, offsets: { mountPoint: [0, 0, 0], @@ -275,6 +275,9 @@ Node.prototype.getValue = function getValue () { if (value.location) { var transform = TransformSystem.get(this.getId()); var size = SizeSystem.get(this.getId()); + var opacity = SizeSystem.get(this.getId()); + + value.spec.showState.opacity = opacity.getOpacity(); for (i = 0 ; i < 3 ; i++) { value.spec.offsets.mountPoint[i] = transform.offsets.mountPoint[i]; @@ -322,7 +325,8 @@ Node.prototype.getComputedValue = function getComputedValue () { location: this.getId(), computedValues: { transform: this.isMounted() ? TransformSystem.get(this.getLocation()).getLocalTransform() : null, - size: this.isMounted() ? SizeSystem.get(this.getLocation()).get() : null + size: this.isMounted() ? SizeSystem.get(this.getLocation()).get() : null, + opacity: this.isMounted() ? OpacitySystem.get(this.getLocation()).get() : null }, children: [] }; @@ -479,7 +483,7 @@ Node.prototype.getOpacity = function getOpacity () { if (this.constructor.INIT_DEFAULT_COMPONENTS) return this.getComponent(this._opacityID).getOpacity(); else if (this.isMounted()) - return TransformSystem.get(this.getLocation()).getOpacity(); + return OpacitySystem.get(this.getLocation()).getOpacity(); else throw new Error('This node does not have access to an opacity component'); }; @@ -1081,7 +1085,7 @@ Node.prototype.setOpacity = function setOpacity (val) { if (this.constructor.INIT_DEFAULT_COMPONENTS) this.getComponent(this._opacityID).setOpacity(val); else if (this.isMounted()) - SizeSystem.get(this.getLocation()).setOpacity(val); + OpacitySystem.get(this.getLocation()).setOpacity(val); else throw new Error('This node does not have access to an opacity component'); return this; }; @@ -1266,10 +1270,12 @@ Node.prototype.mount = function mount (path) { if (!this.constructor.NO_DEFAULT_COMPONENTS){ TransformSystem.registerTransformAtPath(path, this.getComponent(this._transformID)); + OpacitySystem.registerOpacityAtPath(path, this.getComponent(this._opacityID)); SizeSystem.registerSizeAtPath(path, this.getComponent(this._sizeID)); } else { TransformSystem.registerTransformAtPath(path); + OpacitySystem.registerOpacityAtPath(path); SizeSystem.registerSizeAtPath(path); } Dispatch.mount(path, this); @@ -1295,6 +1301,7 @@ Node.prototype.dismount = function dismount () { TransformSystem.deregisterTransformAtPath(path); SizeSystem.deregisterSizeAtPath(path); + OpacitySystem.deregisterOpacityAtPath(path); Dispatch.dismount(path); if (!this._requestingUpdate) this._requestUpdate(); diff --git a/core/OpacitySystem.js b/core/OpacitySystem.js index fd041869..c26d2753 100644 --- a/core/OpacitySystem.js +++ b/core/OpacitySystem.js @@ -74,8 +74,8 @@ OpacitySystem.prototype.deregisterOpacityAtPath = function deregisterOpacityAtPa /** * Method which will make the opacity currently stored at the given path a breakpoint. * A opacity being a breakpoint means that both a local and world opacity will be calculated - * for that point. The local opacity being the concatinated opacity of all ancestor opacites up - * until the nearest breakpoint, and the world being the concatinated opacity of all ancestor opacites. + * for that point. The local opacity being the concatinated opacity of all ancestor opacities up + * until the nearest breakpoint, and the world being the concatinated opacity of all ancestor opacities. * This method throws if no opacity is at the provided path. * * @method @@ -106,25 +106,25 @@ OpacitySystem.prototype.get = function get (path) { /** * onUpdate is called when the opacity system requires an update. - * It traverses the opacity array and evaluates the necessary opacites + * It traverses the opacity array and evaluates the necessary opacities * in the scene graph with the information from the corresponding node * in the scene graph * * @method onUpdate */ OpacitySystem.prototype.onUpdate = function onUpdate () { - var opacites = this.pathStore.getItems(); + var opacities = this.pathStore.getItems(); var paths = this.pathStore.getPaths(); var opacity; var changed; var node; var components; - for (var i = 0, len = opacites.length ; i < len ; i++) { + for (var i = 0, len = opacities.length ; i < len ; i++) { node = Dispatch.getNode(paths[i]); if (!node) continue; components = node.getComponents(); - opacity = opacites[i]; + opacity = opacities[i]; if ((changed = opacity.from(node))) { opacityChanged(node, components, opacity); if (changed & Opacity.LOCAL_CHANGED) localOpacityChanged(node, components, opacity.getLocalOpacity()); diff --git a/core/Scene.js b/core/Scene.js index 5e7061a7..2162e128 100644 --- a/core/Scene.js +++ b/core/Scene.js @@ -30,6 +30,7 @@ var Node = require('./Node'); var Dispatch = require('./Dispatch'); var Commands = require('./Commands'); var TransformSystem = require('./TransformSystem'); +var OpacitySystem = require('./OpacitySystem'); var SizeSystem = require('./SizeSystem'); /** @@ -147,6 +148,7 @@ Scene.prototype.mount = function mount (path) { this._mounted = true; this._parent = this; TransformSystem.registerTransformAtPath(path); + OpacitySystem.registerOpacityAtPath(path); SizeSystem.registerSizeAtPath(path); }; diff --git a/core/test/opacity/Opacity.spec.js b/core/test/opacity/Opacity.spec.js index 92dd1ec5..d6b4ccbf 100644 --- a/core/test/opacity/Opacity.spec.js +++ b/core/test/opacity/Opacity.spec.js @@ -208,10 +208,6 @@ test('Opacity class', function (t) { 'Attempting to get the world opacity of the 4th opacity should throw an error, since no breakpoint has been set on it' ); - for (i = 0; i < opacities.length; i++) - console.log(JSON.stringify(opacities[i])); - - t.end(); }); }); diff --git a/dom-renderables/DOMElement.js b/dom-renderables/DOMElement.js index 64634afc..09ba6624 100644 --- a/dom-renderables/DOMElement.js +++ b/dom-renderables/DOMElement.js @@ -26,6 +26,7 @@ var CallbackStore = require('../utilities/CallbackStore'); var TransformSystem = require('../core/TransformSystem'); +var OpacitySystem = require('../core/OpacitySystem'); var Commands = require('../core/Commands'); var Size = require('../core/Size'); @@ -79,7 +80,6 @@ function DOMElement(node, options) { this._callbacks = new CallbackStore(); this.setProperty('display', node.isShown() ? 'block' : 'none'); - this.onOpacityChange(node.getOpacity()); if (!options) return; From 61d3f571c8aae406c8f737663d3ae0d2bd963573 Mon Sep 17 00:00:00 2001 From: Alexander Gugel Date: Thu, 18 Jun 2015 11:59:20 +0200 Subject: [PATCH 15/29] fix: Correctly register opacities in OpacitySystem --- core/FamousEngine.js | 2 ++ core/Node.js | 2 +- core/OpacitySystem.js | 18 +++++++++++------- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/core/FamousEngine.js b/core/FamousEngine.js index 82766e7c..e75cfa88 100644 --- a/core/FamousEngine.js +++ b/core/FamousEngine.js @@ -32,6 +32,7 @@ var UIManager = require('../renderers/UIManager'); var Compositor = require('../renderers/Compositor'); var RequestAnimationFrameLoop = require('../render-loops/RequestAnimationFrameLoop'); var TransformSystem = require('./TransformSystem'); +var OpacitySystem = require('./OpacitySystem'); var SizeSystem = require('./SizeSystem'); var Commands = require('./Commands'); @@ -158,6 +159,7 @@ FamousEngine.prototype._update = function _update () { SizeSystem.update(); TransformSystem.update(); + OpacitySystem.update(); while (nextQueue.length) queue.unshift(nextQueue.pop()); diff --git a/core/Node.js b/core/Node.js index 079c9451..50ec77b9 100644 --- a/core/Node.js +++ b/core/Node.js @@ -275,7 +275,7 @@ Node.prototype.getValue = function getValue () { if (value.location) { var transform = TransformSystem.get(this.getId()); var size = SizeSystem.get(this.getId()); - var opacity = SizeSystem.get(this.getId()); + var opacity = OpacitySystem.get(this.getId()); value.spec.showState.opacity = opacity.getOpacity(); diff --git a/core/OpacitySystem.js b/core/OpacitySystem.js index c26d2753..d0a6d380 100644 --- a/core/OpacitySystem.js +++ b/core/OpacitySystem.js @@ -48,15 +48,18 @@ function OpacitySystem () { * * @param {String} path for the opacity to be registered to. */ -OpacitySystem.prototype.registerOpacityAtPath = function registerOpacityAtPath (path) { - if (!PathUtils.depth(path)) return this.pathStore.insert(path, new Opacity()); +OpacitySystem.prototype.registerOpacityAtPath = function registerOpacityAtPath (path, opacity) { + if (!PathUtils.depth(path)) return this.pathStore.insert(path, opacity ? opacity : new Opacity()); var parent = this.pathStore.get(PathUtils.parent(path)); if (!parent) throw new Error( 'No parent opacity registered at expected path: ' + PathUtils.parent(path) ); - this.pathStore.insert(path, new Opacity(parent)); + + if (opacity) opacity.setParent(parent); + + this.pathStore.insert(path, opacity ? opacity : new Opacity(parent)); }; /** @@ -105,14 +108,14 @@ OpacitySystem.prototype.get = function get (path) { }; /** - * onUpdate is called when the opacity system requires an update. + * update is called when the opacity system requires an update. * It traverses the opacity array and evaluates the necessary opacities * in the scene graph with the information from the corresponding node * in the scene graph * - * @method onUpdate + * @method update */ -OpacitySystem.prototype.onUpdate = function onUpdate () { +OpacitySystem.prototype.update = function update () { var opacities = this.pathStore.getItems(); var paths = this.pathStore.getPaths(); var opacity; @@ -125,7 +128,8 @@ OpacitySystem.prototype.onUpdate = function onUpdate () { if (!node) continue; components = node.getComponents(); opacity = opacities[i]; - if ((changed = opacity.from(node))) { + + if ((changed = opacity.calculate())) { opacityChanged(node, components, opacity); if (changed & Opacity.LOCAL_CHANGED) localOpacityChanged(node, components, opacity.getLocalOpacity()); if (changed & Opacity.WORLD_CHANGED) worldOpacityChanged(node, components, opacity.getWorldOpacity()); From 3366ba2ae9086fdf47c52b3584c8416db5ed73e6 Mon Sep 17 00:00:00 2001 From: Alexander Gugel Date: Thu, 18 Jun 2015 13:42:21 +0200 Subject: [PATCH 16/29] fix: Request global opacity in Mesh --- webgl-renderables/Mesh.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/webgl-renderables/Mesh.js b/webgl-renderables/Mesh.js index 5396fe77..302f21cd 100644 --- a/webgl-renderables/Mesh.js +++ b/webgl-renderables/Mesh.js @@ -31,6 +31,7 @@ var Geometry = require('../webgl-geometries'); var Commands = require('../core/Commands'); var TransformSystem = require('../core/TransformSystem'); var Plane = require('../webgl-geometries/primitives/Plane'); +var OpacitySystem = require('../core/OpacitySystem'); /** * The Mesh class is responsible for providing the API for how @@ -508,6 +509,7 @@ Mesh.prototype.onMount = function onMount (node, id) { this._id = id; TransformSystem.makeCalculateWorldMatrixAt(node.getLocation()); + OpacitySystem.makeBreakPointAt(node.getLocation()); this.draw(); }; @@ -613,7 +615,7 @@ Mesh.prototype.onOpacityChange = function onOpacityChange (opacity) { if (this._initialized) { this._changeQueue.push(Commands.GL_UNIFORMS); this._changeQueue.push('u_opacity'); - this._changeQueue.push(opacity); + this._changeQueue.push(opacity.getWorldOpacity()); } this._requestUpdate(); @@ -656,9 +658,9 @@ Mesh.prototype._requestUpdate = function _requestUpdate () { Mesh.prototype.init = function init () { this._initialized = true; this.onTransformChange(TransformSystem.get(this._node.getLocation())); + this.onOpacityChange(OpacitySystem.get(this._node.getLocation())); var size = this._node.getSize(); this.onSizeChange(size[0], size[1], size[2]); - this.onOpacityChange(this._node.getOpacity()); this._requestUpdate(); }; From 50244d3413b75f8a31e639c67421d13110bc8222 Mon Sep 17 00:00:00 2001 From: Alexander Gugel Date: Thu, 18 Jun 2015 23:25:12 +0200 Subject: [PATCH 17/29] test: Group tests instead of t.comment --- core/test/opacity/Opacity.spec.js | 255 +++++++++++++++--------------- 1 file changed, 129 insertions(+), 126 deletions(-) diff --git a/core/test/opacity/Opacity.spec.js b/core/test/opacity/Opacity.spec.js index d6b4ccbf..6f54eaf4 100644 --- a/core/test/opacity/Opacity.spec.js +++ b/core/test/opacity/Opacity.spec.js @@ -81,133 +81,136 @@ test('Opacity class', function (t) { opacity.setOpacity(0.5); } + t.test('Root Opacity (no breakpoint): 0.5', function(t) { + t.equal( + opacities[0].calculate(), Opacity.LOCAL_CHANGED & ~Opacity.WORLD_CHANGED, + 'Calculating the root opacity should only change the local opacity' + ); + t.equal( + opacities[0].getLocalOpacity(), 0.5, + 'The root local opacity should be set to 0.5' + ); - t.comment('Root Opacity (no breakpoint): 0.5'); - - t.equal( - opacities[0].calculate(), Opacity.LOCAL_CHANGED & ~Opacity.WORLD_CHANGED, - 'Calculating the root opacity should only change the local opacity' - ); - t.equal( - opacities[0].getLocalOpacity(), 0.5, - 'The root local opacity should be set to 0.5' - ); - - t.equal( - opacities[0].getOpacity(), 0.5, - 'The root opacity should still be set to 0.5 after calculation' - ); - t.throws( - function() { - opacities[0].getWorldOpacity(); - }, - /not calculating world transforms/, - 'Attempting to get the world opacity of the root opacity should throw an error, since no breakpoint has been set on it' - ); - - - t.comment('2nd Opacity (no breakpoint): 0.5'); - - t.equal( - opacities[1].calculate(), Opacity.LOCAL_CHANGED & ~Opacity.WORLD_CHANGED, - 'Calculating the 2nd opacity (child of root) should only change the local opacity, since no breakpoint has been set on it' - ); - t.equal( - opacities[1].getLocalOpacity(), 0.25, - 'The 2nd opacity should have been multiplied with the root opacity' - ); - - t.equal( - opacities[1].getOpacity(), 0.5, - 'The 2nd opacity should still be set to 0.5 after calculation' - ); - t.throws( - function() { - opacities[1].getWorldOpacity(); - }, - /not calculating world transforms/, - 'Attempting to get the world opacity of the 2nd opacity should throw an error, since no breakpoint has been set on it' - ); - - - t.comment('3rd Opacity (no breakpoint): 0.5'); - - t.equal( - opacities[2].calculate(), Opacity.LOCAL_CHANGED & ~Opacity.WORLD_CHANGED, - 'Calculating the 3rd opacity should only change the local opacity, since no breakpoint has been set on it' - ); - t.equal( - opacities[2].getLocalOpacity(), 0.125, - 'The 3rd opacity should have been multiplied with the root and 2nd opacity' - ); - - t.equal( - opacities[2].getOpacity(), 0.5, - 'The 3rd opacity should still be set to 0.5 after calculation' - ); - t.throws( - function() { - opacities[2].getWorldOpacity(); - }, - /not calculating world transforms/, - 'Attempting to get the world opacity of the 3rd opacity should throw an error, since no breakpoint has been set on it' - ); - - - t.comment('4th Opacity (breakpoint): 0.5'); - - opacities[3].setBreakPoint(); - t.equal( - opacities[3].calculate(), Opacity.LOCAL_CHANGED | Opacity.WORLD_CHANGED, - 'Calculating the 4th opacity should have checked if the world opacity changed, since it has a breakpoint set on it' - ); - - t.equal( - opacities[3].isBreakPoint(), true, - 'The 4th opacity should still have breakpoint after calculation' - ); - t.equal( - opacities[3].getLocalOpacity(), 0.0625, - 'The 4th local opacity should have been multiplied with the root, 2nd and 3rd opacity' - ); - t.equal( - opacities[3].getOpacity(), 0.5, - 'The 4th opacity should still be set to 0.5 after calculation' - ); - t.doesNotThrow(function() { - opacities[3].getWorldOpacity(); - }, 'Attempting to calculate the world on the 4th opacity should not throw an error, since a breakpoint has been set on it'); - - t.equal( - opacities[3].getWorldOpacity(), 0.0625, - 'The 4th world opacity should be have been multiplied with all previous opacities' - ); - - - t.comment('5th Opacity (no breakpoint): 0.5') - - t.equal( - opacities[4].calculate(), Opacity.LOCAL_CHANGED & ~Opacity.WORLD_CHANGED, - 'Calculating the 5th opacity should only change the local opacity, since no breakpoint has been set on it' - ); - t.equal( - opacities[4].getLocalOpacity(), 0.5, - 'The 5th local opacity should be equivalent to the opacity set on it, it should not have been multiplied, since the 4th node has a breakpoint set on it' - ); - - t.equal( - opacities[4].getOpacity(), 0.5, - 'The 5th opacity should still be set to 0.5 after calculation' - ); - - t.throws( - function() { - opacities[4].getWorldOpacity(); - }, - /not calculating world transforms/, - 'Attempting to get the world opacity of the 4th opacity should throw an error, since no breakpoint has been set on it' - ); + t.equal( + opacities[0].getOpacity(), 0.5, + 'The root opacity should still be set to 0.5 after calculation' + ); + t.throws( + function() { + opacities[0].getWorldOpacity(); + }, + /not calculating world transforms/, + 'Attempting to get the world opacity of the root opacity should throw an error, since no breakpoint has been set on it' + ); - t.end(); + t.end(); + }); + + t.test('2nd Opacity (no breakpoint): 0.5', function(t) { + t.equal( + opacities[1].calculate(), Opacity.LOCAL_CHANGED & ~Opacity.WORLD_CHANGED, + 'Calculating the 2nd opacity (child of root) should only change the local opacity, since no breakpoint has been set on it' + ); + t.equal( + opacities[1].getLocalOpacity(), 0.25, + 'The 2nd opacity should have been multiplied with the root opacity' + ); + + t.equal( + opacities[1].getOpacity(), 0.5, + 'The 2nd opacity should still be set to 0.5 after calculation' + ); + t.throws( + function() { + opacities[1].getWorldOpacity(); + }, + /not calculating world transforms/, + 'Attempting to get the world opacity of the 2nd opacity should throw an error, since no breakpoint has been set on it' + ); + + t.end(); + }); + + t.test('3rd Opacity (no breakpoint): 0.5', function(t) { + t.equal( + opacities[2].calculate(), Opacity.LOCAL_CHANGED & ~Opacity.WORLD_CHANGED, + 'Calculating the 3rd opacity should only change the local opacity, since no breakpoint has been set on it' + ); + t.equal( + opacities[2].getLocalOpacity(), 0.125, + 'The 3rd opacity should have been multiplied with the root and 2nd opacity' + ); + + t.equal( + opacities[2].getOpacity(), 0.5, + 'The 3rd opacity should still be set to 0.5 after calculation' + ); + t.throws( + function() { + opacities[2].getWorldOpacity(); + }, + /not calculating world transforms/, + 'Attempting to get the world opacity of the 3rd opacity should throw an error, since no breakpoint has been set on it' + ); + + t.end(); + }); + + t.test('4th Opacity (breakpoint): 0.5', function(t) { + opacities[3].setBreakPoint(); + t.equal( + opacities[3].calculate(), Opacity.LOCAL_CHANGED | Opacity.WORLD_CHANGED, + 'Calculating the 4th opacity should have checked if the world opacity changed, since it has a breakpoint set on it' + ); + + t.equal( + opacities[3].isBreakPoint(), true, + 'The 4th opacity should still have breakpoint after calculation' + ); + t.equal( + opacities[3].getLocalOpacity(), 0.0625, + 'The 4th local opacity should have been multiplied with the root, 2nd and 3rd opacity' + ); + t.equal( + opacities[3].getOpacity(), 0.5, + 'The 4th opacity should still be set to 0.5 after calculation' + ); + t.doesNotThrow(function() { + opacities[3].getWorldOpacity(); + }, 'Attempting to calculate the world on the 4th opacity should not throw an error, since a breakpoint has been set on it'); + + t.equal( + opacities[3].getWorldOpacity(), 0.0625, + 'The 4th world opacity should be have been multiplied with all previous opacities' + ); + + t.end(); + }); + + t.test('5th Opacity (no breakpoint): 0.5', function(t) { + t.equal( + opacities[4].calculate(), Opacity.LOCAL_CHANGED & ~Opacity.WORLD_CHANGED, + 'Calculating the 5th opacity should only change the local opacity, since no breakpoint has been set on it' + ); + t.equal( + opacities[4].getLocalOpacity(), 0.5, + 'The 5th local opacity should be equivalent to the opacity set on it, it should not have been multiplied, since the 4th node has a breakpoint set on it' + ); + + t.equal( + opacities[4].getOpacity(), 0.5, + 'The 5th opacity should still be set to 0.5 after calculation' + ); + + t.throws( + function() { + opacities[4].getWorldOpacity(); + }, + /not calculating world transforms/, + 'Attempting to get the world opacity of the 4th opacity should throw an error, since no breakpoint has been set on it' + ); + + t.end(); + }); }); }); From 705ba67b35bbef983cb730cf9b40b0b1b66e5066 Mon Sep 17 00:00:00 2001 From: Alexander Gugel Date: Mon, 29 Jun 2015 15:46:00 +0200 Subject: [PATCH 18/29] fix: Fix Node#setOpacity, Node#getOpacity --- core/Node.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/Node.js b/core/Node.js index 50ec77b9..ec995513 100644 --- a/core/Node.js +++ b/core/Node.js @@ -480,7 +480,7 @@ Node.prototype.isShown = function isShown () { * @return {Number} Relative opacity of the node. */ Node.prototype.getOpacity = function getOpacity () { - if (this.constructor.INIT_DEFAULT_COMPONENTS) + if (!this.constructor.NO_DEFAULT_COMPONENTS) return this.getComponent(this._opacityID).getOpacity(); else if (this.isMounted()) return OpacitySystem.get(this.getLocation()).getOpacity(); @@ -1082,7 +1082,7 @@ Node.prototype.setScale = function setScale (x, y, z) { * @return {Node} this */ Node.prototype.setOpacity = function setOpacity (val) { - if (this.constructor.INIT_DEFAULT_COMPONENTS) + if (!this.constructor.NO_DEFAULT_COMPONENTS) this.getComponent(this._opacityID).setOpacity(val); else if (this.isMounted()) OpacitySystem.get(this.getLocation()).setOpacity(val); From e53f69f94781c5903be684f0ef2b6953b11e62be Mon Sep 17 00:00:00 2001 From: Alexander Gugel Date: Tue, 30 Jun 2015 12:14:46 +0200 Subject: [PATCH 19/29] doc: Fix OpacitySystem docs --- core/OpacitySystem.js | 8 +++++--- core/test/opacity/Opacity.spec.js | 2 -- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/OpacitySystem.js b/core/OpacitySystem.js index d0a6d380..e6e52e6e 100644 --- a/core/OpacitySystem.js +++ b/core/OpacitySystem.js @@ -44,9 +44,10 @@ function OpacitySystem () { * when the OpacitySystem updates. * * @method registerOpacityAtPath - * @return {void} * - * @param {String} path for the opacity to be registered to. + * @param {String} path path for the opacity to be registered to. + * @param {Opacity} [opacity] opacity to register. + * @return {undefined} undefined */ OpacitySystem.prototype.registerOpacityAtPath = function registerOpacityAtPath (path, opacity) { if (!PathUtils.depth(path)) return this.pathStore.insert(path, opacity ? opacity : new Opacity()); @@ -63,7 +64,7 @@ OpacitySystem.prototype.registerOpacityAtPath = function registerOpacityAtPath ( }; /** - * deregisters a opacity registered at the given path. + * Deregisters a opacity registered at the given path. * * @method deregisterOpacityAtPath * @return {void} @@ -114,6 +115,7 @@ OpacitySystem.prototype.get = function get (path) { * in the scene graph * * @method update + * @return {undefined} undefined */ OpacitySystem.prototype.update = function update () { var opacities = this.pathStore.getItems(); diff --git a/core/test/opacity/Opacity.spec.js b/core/test/opacity/Opacity.spec.js index 6f54eaf4..b884556e 100644 --- a/core/test/opacity/Opacity.spec.js +++ b/core/test/opacity/Opacity.spec.js @@ -28,8 +28,6 @@ var test = require('tape'); var api = require('./Opacity.api'); var Opacity = require('../../Opacity'); var OpacityStub = require('./Opacity.stub'); -var NodeStub = require('../node/Node.stub'); -var sinon = require('sinon'); test('Opacity class', function (t) { From 39c0e72fc5e9633587908070626fc214d3edc38e Mon Sep 17 00:00:00 2001 From: Alexander Gugel Date: Tue, 14 Jul 2015 10:50:45 +0200 Subject: [PATCH 20/29] fix: Implement Opacity#setCalculateWorldOpacity Equivalent of f6b1dde0ce34d0e952b6c36d77ee30d6387bcbc9 Needed for nesting DOMElements within Meshes --- core/Opacity.js | 19 ++++++++++++++++--- core/OpacitySystem.js | 15 +++++++++++++++ webgl-renderables/Mesh.js | 2 +- 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/core/Opacity.js b/core/Opacity.js index df89d795..83fb1bcf 100644 --- a/core/Opacity.js +++ b/core/Opacity.js @@ -30,6 +30,7 @@ function Opacity (parent) { this.opacity = 1; this.parent = parent ? parent : null; this.breakPoint = false; + this.calculatingWorldOpacity = false; } Opacity.WORLD_CHANGED = 1; @@ -50,6 +51,18 @@ Opacity.prototype.getParent = function getParent () { Opacity.prototype.setBreakPoint = function setBreakPoint () { this.breakPoint = true; + this.calculatingWorldOpacity = true; +}; + +/** + * Set this node to calculate the world opacity. + * + * @method + * + * @return {undefined} undefined + */ +Opacity.prototype.setCalculateWorldOpacity = function setCalculateWorldOpacity () { + this.calculatingWorldOpacity = true; }; Opacity.prototype.isBreakPoint = function isBreakPoint () { @@ -61,7 +74,7 @@ Opacity.prototype.getLocalOpacity = function getLocalOpacity () { }; Opacity.prototype.getWorldOpacity = function getWorldOpacity () { - if (!this.isBreakPoint()) + if (!this.isBreakPoint() && !this.calculatingWorldOpacity) throw new Error('This opacity is not calculating world transforms'); return this.global; }; @@ -106,7 +119,7 @@ Opacity.prototype.fromNode = function fromNode () { this.local = this.opacity; - if (this.isBreakPoint() && this.calculateWorldOpacity()) + if (this.calculatingWorldOpacity && this.calculateWorldOpacity()) changed |= Opacity.WORLD_CHANGED; return changed; @@ -119,7 +132,7 @@ Opacity.prototype.fromNodeWithParent = function fromNodeWithParent () { this.local = this.parent.getLocalOpacity() * this.opacity; - if (this.isBreakPoint() && this.calculateWorldOpacity()) + if (this.calculatingWorldOpacity && this.calculateWorldOpacity()) changed |= Opacity.WORLD_CHANGED; if (previousLocal !== this.local) diff --git a/core/OpacitySystem.js b/core/OpacitySystem.js index e6e52e6e..219fed04 100644 --- a/core/OpacitySystem.js +++ b/core/OpacitySystem.js @@ -94,6 +94,21 @@ OpacitySystem.prototype.makeBreakPointAt = function makeBreakPointAt (path) { opacity.setBreakPoint(); }; +/** + * Method that will make the opacity at this location calculate a world opacity. + * + * @method + * + * @param {String} path The path at which to make the opacity calculate a world matrix + * + * @return {undefined} undefined + */ +OpacitySystem.prototype.makeCalculateWorldOpacityAt = function makeCalculateWorldOpacityAt (path) { + var opacity = this.pathStore.get(path); + if (!opacity) throw new Error('No opacity opacity at path: ' + path); + opacity.setCalculateWorldOpacity(); +}; + /** * Returns the instance of the opacity class associated with the given path, * or undefined if no opacity is associated. diff --git a/webgl-renderables/Mesh.js b/webgl-renderables/Mesh.js index 302f21cd..57f0eb63 100644 --- a/webgl-renderables/Mesh.js +++ b/webgl-renderables/Mesh.js @@ -509,7 +509,7 @@ Mesh.prototype.onMount = function onMount (node, id) { this._id = id; TransformSystem.makeCalculateWorldMatrixAt(node.getLocation()); - OpacitySystem.makeBreakPointAt(node.getLocation()); + OpacitySystem.makeCalculateWorldOpacityAt(node.getLocation()); this.draw(); }; From 79401a3adef6fed2b789c9e60b47c085091eb055 Mon Sep 17 00:00:00 2001 From: Alexander Gugel Date: Tue, 7 Jul 2015 11:28:57 +0200 Subject: [PATCH 21/29] fix: Fix bug resulting from single queue in Dispatch (#365) --- core/Dispatch.js | 45 ++++++++++++----------------- core/test/dispatch/Dispatch.api.js | 3 -- core/test/dispatch/Dispatch.spec.js | 18 ++---------- 3 files changed, 21 insertions(+), 45 deletions(-) diff --git a/core/Dispatch.js b/core/Dispatch.js index 1ef38a88..0a61d0a3 100644 --- a/core/Dispatch.js +++ b/core/Dispatch.js @@ -38,7 +38,7 @@ var PathUtils = require('./Path'); function Dispatch () { this._nodes = {}; // a container for constant time lookup of nodes - this._queue = []; // The queue is used for two purposes + // The queue is used for two purposes // 1. It is used to list indicies in the // Nodes path which are then used to lookup // a node in the scene graph. @@ -73,24 +73,14 @@ Dispatch.prototype._setUpdater = function _setUpdater (updater) { * * @param {Node} node from which to add children to the queue */ -Dispatch.prototype.addChildrenToQueue = function addChildrenToQueue (node) { +function addChildrenToQueue (node, queue) { var children = node.getChildren(); var child; for (var i = 0, len = children.length ; i < len ; i++) { child = children[i]; - if (child) this._queue.push(child); + if (child) queue.push(child); } -}; - -/** - * Returns the next item in the Dispatch's queue. - * - * @method next - * @return {Node} next node in the queue - */ -Dispatch.prototype.next = function next () { - return this._queue.shift(); -}; +} /** * Returns the next node in the queue, but also adds its children to @@ -100,12 +90,12 @@ Dispatch.prototype.next = function next () { * @method breadthFirstNext * @return {Node | undefined} the next node in the traversal if one exists */ -Dispatch.prototype.breadthFirstNext = function breadthFirstNext () { - var child = this._queue.shift(); +function breadthFirstNext (queue) { + var child = queue.shift(); if (!child) return void 0; - this.addChildrenToQueue(child); + addChildrenToQueue(child, queue); return child; -}; +} /** * Calls the onMount method for the node at a given path and @@ -254,13 +244,13 @@ Dispatch.prototype.show = function show (path) { if (components[i] && components[i].onShow) components[i].onShow(); + var queue = []; - this.addChildrenToQueue(node); + addChildrenToQueue(node, queue); var child; - while ((child = this.breadthFirstNext())) + while ((child = breadthFirstNext(queue))) this.show(child.getLocation()); - }; /** @@ -288,11 +278,12 @@ Dispatch.prototype.hide = function hide (path) { if (components[i] && components[i].onHide) components[i].onHide(); + var queue = []; - this.addChildrenToQueue(node); + addChildrenToQueue(node, queue); var child; - while ((child = this.breadthFirstNext())) + while ((child = breadthFirstNext(queue))) this.hide(child.getLocation()); }; @@ -308,8 +299,7 @@ Dispatch.prototype.hide = function hide (path) { Dispatch.prototype.lookupNode = function lookupNode (location) { if (!location) throw new Error('lookupNode must be called with a path'); - this._queue.length = 0; - var path = this._queue; + var path = []; _splitTo(location, path); @@ -340,14 +330,15 @@ Dispatch.prototype.dispatch = function dispatch (path, event, payload) { if (!node) return; payload.node = node; - this._queue.push(node); + + var queue = [node]; var child; var components; var i; var len; - while ((child = this.breadthFirstNext())) { + while ((child = breadthFirstNext(queue))) { if (child && child.onReceive) child.onReceive(event, payload); diff --git a/core/test/dispatch/Dispatch.api.js b/core/test/dispatch/Dispatch.api.js index c7de920f..4e54ba9d 100644 --- a/core/test/dispatch/Dispatch.api.js +++ b/core/test/dispatch/Dispatch.api.js @@ -1,9 +1,6 @@ module.exports = [ '_setUpdater', - 'addChildrenToQueue', - 'next', - 'breadthFirstNext', 'mount', 'dismount', 'getNode', diff --git a/core/test/dispatch/Dispatch.spec.js b/core/test/dispatch/Dispatch.spec.js index 62db096c..9e22e553 100644 --- a/core/test/dispatch/Dispatch.spec.js +++ b/core/test/dispatch/Dispatch.spec.js @@ -39,7 +39,7 @@ test('Dispatch singleton', function (t) { var testUpdater = 'a'; t.doesNotThrow( - Dispatch._setUpdater.bind(Dispatch, testUpdater), + Dispatch._setUpdater.bind(Dispatch, testUpdater), '._setUpdater should be callable' ); @@ -60,10 +60,10 @@ test('Dispatch singleton', function (t) { Dispatch.mount('body/0', stub2); t.notOk(stub2._setUpdater.getCall(0).calledWith(testUpdater), 'Nodes mounted with the Dispatch ' + - 'should have their updaters set to ' + + 'should have their updaters set to ' + 'the dispatch\'s current updater and not a previous one'); - t.ok(stub2._setUpdater.getCall(0).calledWith(testUpdater2), 'Nodes mounted with the Dispatch ' + + t.ok(stub2._setUpdater.getCall(0).calledWith(testUpdater2), 'Nodes mounted with the Dispatch ' + 'should have their updaters set to ' + 'the dispatch\'s current updater'); @@ -74,18 +74,6 @@ test('Dispatch singleton', function (t) { t.end(); }); - t.test('.addChildrenToQueue method', function (t) { - t.end(); - }); - - t.test('.next method', function (t) { - t.end(); - }); - - t.test('.breadthFirstNext method', function (t) { - t.end(); - }); - t.test('.mount method', function (t) { t.end(); }); From a231ee6b23105cad50c38d0d6d30151fe413b2a9 Mon Sep 17 00:00:00 2001 From: Alexander Gugel Date: Tue, 7 Jul 2015 11:38:25 +0200 Subject: [PATCH 22/29] doc: Fix JSDoc --- core/Dispatch.js | 70 +++++++++++++++++++++++++----------------------- 1 file changed, 37 insertions(+), 33 deletions(-) diff --git a/core/Dispatch.js b/core/Dispatch.js index 0a61d0a3..d779ac90 100644 --- a/core/Dispatch.js +++ b/core/Dispatch.js @@ -64,39 +64,6 @@ Dispatch.prototype._setUpdater = function _setUpdater (updater) { for (var key in this._nodes) this._nodes[key]._setUpdater(updater); }; -/** - * Enque the children of a node within the dispatcher. Does not clear - * the dispatchers queue first. - * - * @method addChildrenToQueue - * @return {void} - * - * @param {Node} node from which to add children to the queue - */ -function addChildrenToQueue (node, queue) { - var children = node.getChildren(); - var child; - for (var i = 0, len = children.length ; i < len ; i++) { - child = children[i]; - if (child) queue.push(child); - } -} - -/** - * Returns the next node in the queue, but also adds its children to - * the end of the queue. Continually calling this method will result - * in a breadth first traversal of the render tree. - * - * @method breadthFirstNext - * @return {Node | undefined} the next node in the traversal if one exists - */ -function breadthFirstNext (queue) { - var child = queue.shift(); - if (!child) return void 0; - addChildrenToQueue(child, queue); - return child; -} - /** * Calls the onMount method for the node at a given path and * properly registers all of that nodes children to their proper @@ -422,4 +389,41 @@ function _splitTo (string, target) { return target; } +/** + * Enque the children of a node within the dispatcher. Does not clear + * the dispatchers queue first. + * + * @method addChildrenToQueue + * + * @param {Node} node from which to add children to the queue + * @param {Array} queue the queue used for retrieving the new child from + * + * @return {void} + */ +function addChildrenToQueue (node, queue) { + var children = node.getChildren(); + var child; + for (var i = 0, len = children.length ; i < len ; i++) { + child = children[i]; + if (child) queue.push(child); + } +} + +/** + * Returns the next node in the queue, but also adds its children to + * the end of the queue. Continually calling this method will result + * in a breadth first traversal of the render tree. + * + * @method breadthFirstNext + * @param {Array} queue the queue used for retrieving the new child from + * @return {Node | undefined} the next node in the traversal if one exists + */ +function breadthFirstNext (queue) { + var child = queue.shift(); + if (!child) return void 0; + addChildrenToQueue(child, queue); + return child; +} + + module.exports = new Dispatch(); From e26334373dc5b1a964719610fef681c3b1f7c075 Mon Sep 17 00:00:00 2001 From: Alexander Gugel Date: Tue, 7 Jul 2015 11:44:26 +0200 Subject: [PATCH 23/29] perf: Reuse queue --- core/Dispatch.js | 42 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/core/Dispatch.js b/core/Dispatch.js index d779ac90..2352f82d 100644 --- a/core/Dispatch.js +++ b/core/Dispatch.js @@ -211,13 +211,15 @@ Dispatch.prototype.show = function show (path) { if (components[i] && components[i].onShow) components[i].onShow(); - var queue = []; + var queue = allocQueue(); addChildrenToQueue(node, queue); var child; while ((child = breadthFirstNext(queue))) this.show(child.getLocation()); + + deallocQueue(queue); }; /** @@ -245,7 +247,7 @@ Dispatch.prototype.hide = function hide (path) { if (components[i] && components[i].onHide) components[i].onHide(); - var queue = []; + var queue = allocQueue(); addChildrenToQueue(node, queue); var child; @@ -253,6 +255,7 @@ Dispatch.prototype.hide = function hide (path) { while ((child = breadthFirstNext(queue))) this.hide(child.getLocation()); + deallocQueue(queue); }; /** @@ -266,13 +269,16 @@ Dispatch.prototype.hide = function hide (path) { Dispatch.prototype.lookupNode = function lookupNode (location) { if (!location) throw new Error('lookupNode must be called with a path'); - var path = []; + var path = allocQueue(); _splitTo(location, path); for (var i = 0, len = path.length ; i < len ; i++) path[i] = this._nodes[path[i]]; + path.length = 0; + deallocQueue(path); + return path[path.length - 1]; }; @@ -298,7 +304,8 @@ Dispatch.prototype.dispatch = function dispatch (path, event, payload) { payload.node = node; - var queue = [node]; + var queue = allocQueue(); + queue.push(node); var child; var components; @@ -316,6 +323,7 @@ Dispatch.prototype.dispatch = function dispatch (path, event, payload) { components[i].onReceive(event, payload); } + deallocQueue(queue); }; /** @@ -361,6 +369,32 @@ Dispatch.prototype.dispatchUIEvent = function dispatchUIEvent (path, event, payl } }; +var queues = []; + +/** + * Helper method used for allocating a new queue or reusing a previously freed + * one if possible. + * + * @private + * + * @return {Array} allocated queue. + */ +function allocQueue() { + return queues.pop() || []; +} + +/** + * Helper method used for freeing a previously allocated queue. + * + * @private + * + * @param {Array} queue the queue to be relased to the pool. + * @return {undefined} undefined + */ +function deallocQueue(queue) { + queues.push(queue); +} + /** * _splitTo is a private method which takes a path and splits it at every '/' * pushing the result into the supplied array. This is a destructive change. From 4e67678d13bf8784b70289d43c48f7f968f59a93 Mon Sep 17 00:00:00 2001 From: Alexander Gugel Date: Tue, 7 Jul 2015 14:54:41 +0200 Subject: [PATCH 24/29] test: Test basic Dispatch.dispatch --- core/test/dispatch/Dispatch.spec.js | 52 ++++++++++++++++++++++++++--- 1 file changed, 47 insertions(+), 5 deletions(-) diff --git a/core/test/dispatch/Dispatch.spec.js b/core/test/dispatch/Dispatch.spec.js index 9e22e553..675a3484 100644 --- a/core/test/dispatch/Dispatch.spec.js +++ b/core/test/dispatch/Dispatch.spec.js @@ -1,14 +1,11 @@ 'use strict'; -var rewire = require('rewire'); var api = require('./Dispatch.api'); -var Dispatch = rewire('../../../core/Dispatch'); -var PathUtilsStub = require('../path/Path.stub'); +var Dispatch = require('../../../core/Dispatch'); var NodeStub = require('../node/Node.stub'); +var Node = require('../../../core/Node'); var test = require('tape'); -Dispatch.__set__('PathUtils', PathUtilsStub); - test('Dispatch singleton', function (t) { t.test('Dispatch object', function (t) { @@ -99,6 +96,51 @@ test('Dispatch singleton', function (t) { }); t.test('.dispatch method', function (t) { + var nodes = { + 'path': new Node(), + 'path/1': new Node(), + 'path/1/2': new Node(), + 'path/1/1': new Node(), + 'path/1/2/3': new Node(), + 'path/1/2/4': new Node() + }; + + var received = []; + + function onReceive(actualEvent, actualPayload) { + t.equal(actualEvent, expectedEvent, 'Node should receive '); + t.equal(actualPayload, expectedPayload); + + received.push(this.__path__); + } + + nodes.path.addChild(nodes['path/1']); + nodes['path/1'].addChild(nodes['path/1/2']); + nodes['path/1'].addChild(nodes['path/1/1']); + nodes['path/1/2'].addChild(nodes['path/1/2/3']); + nodes['path/1/2'].addChild(nodes['path/1/2/4']); + + for (var path in nodes) { + nodes[path].__path__ = path; + nodes[path].onReceive = onReceive; + Dispatch.mount(path, nodes[path]); + } + + var expectedEvent = 'event'; + var expectedPayload = { some: 'payload' }; + + Dispatch.dispatch('path/1', expectedEvent, expectedPayload); + + t.equal( + received.indexOf('path/1'), -1, + 'path/1 should not receive event dispatched via Dispatch.dispatch("path/1")' + ); + + t.deepEqual( + received, ['path/1/2', 'path/1/1', 'path/1/2/3', 'path/1/2/4'], + 'Dispatch.dispatch should trigger event first on parent, then on children' + ); + t.end(); }); From 4324e17466eb580cb83c2f51c12d4bf0b189e6ab Mon Sep 17 00:00:00 2001 From: Alexander Gugel Date: Tue, 7 Jul 2015 19:29:51 +0200 Subject: [PATCH 25/29] chore: Fix typo --- core/test/dispatch/Dispatch.spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/test/dispatch/Dispatch.spec.js b/core/test/dispatch/Dispatch.spec.js index 675a3484..ed0e3de7 100644 --- a/core/test/dispatch/Dispatch.spec.js +++ b/core/test/dispatch/Dispatch.spec.js @@ -108,8 +108,8 @@ test('Dispatch singleton', function (t) { var received = []; function onReceive(actualEvent, actualPayload) { - t.equal(actualEvent, expectedEvent, 'Node should receive '); - t.equal(actualPayload, expectedPayload); + t.equal(actualEvent, expectedEvent, 'Node should receive expected event'); + t.equal(actualPayload, expectedPayload, 'Node should receive expected payload'); received.push(this.__path__); } From 2c55477137dcf8ab654a614f04ba8afa1c20ea19 Mon Sep 17 00:00:00 2001 From: Alexander Gugel Date: Wed, 8 Jul 2015 11:25:00 +0200 Subject: [PATCH 26/29] test: Add test case for conflicting events in Dispatch queue --- core/test/dispatch/Dispatch.spec.js | 173 +++++++++++++++++++++------- 1 file changed, 130 insertions(+), 43 deletions(-) diff --git a/core/test/dispatch/Dispatch.spec.js b/core/test/dispatch/Dispatch.spec.js index ed0e3de7..f00b8a79 100644 --- a/core/test/dispatch/Dispatch.spec.js +++ b/core/test/dispatch/Dispatch.spec.js @@ -96,52 +96,139 @@ test('Dispatch singleton', function (t) { }); t.test('.dispatch method', function (t) { - var nodes = { - 'path': new Node(), - 'path/1': new Node(), - 'path/1/2': new Node(), - 'path/1/1': new Node(), - 'path/1/2/3': new Node(), - 'path/1/2/4': new Node() - }; - - var received = []; - - function onReceive(actualEvent, actualPayload) { - t.equal(actualEvent, expectedEvent, 'Node should receive expected event'); - t.equal(actualPayload, expectedPayload, 'Node should receive expected payload'); - - received.push(this.__path__); - } - nodes.path.addChild(nodes['path/1']); - nodes['path/1'].addChild(nodes['path/1/2']); - nodes['path/1'].addChild(nodes['path/1/1']); - nodes['path/1/2'].addChild(nodes['path/1/2/3']); - nodes['path/1/2'].addChild(nodes['path/1/2/4']); - - for (var path in nodes) { - nodes[path].__path__ = path; - nodes[path].onReceive = onReceive; - Dispatch.mount(path, nodes[path]); - } + var nodes; - var expectedEvent = 'event'; - var expectedPayload = { some: 'payload' }; - - Dispatch.dispatch('path/1', expectedEvent, expectedPayload); - - t.equal( - received.indexOf('path/1'), -1, - 'path/1 should not receive event dispatched via Dispatch.dispatch("path/1")' - ); + t.test('setup', function (t) { + nodes = { + 'path': new Node(), + 'path/1': new Node(), + 'path/1/2': new Node(), + 'path/1/1': new Node(), + 'path/1/2/3': new Node(), + 'path/1/2/4': new Node() + }; + + nodes.path.addChild(nodes['path/1']); + nodes['path/1'].addChild(nodes['path/1/2']); + nodes['path/1'].addChild(nodes['path/1/1']); + nodes['path/1/2'].addChild(nodes['path/1/2/3']); + nodes['path/1/2'].addChild(nodes['path/1/2/4']); + + for (var path in nodes) { + nodes[path].__path__ = path; + Dispatch.mount(path, nodes[path]); + } + + t.end(); + }); - t.deepEqual( - received, ['path/1/2', 'path/1/1', 'path/1/2/3', 'path/1/2/4'], - 'Dispatch.dispatch should trigger event first on parent, then on children' - ); - - t.end(); + t.test('basic', function (t) { + + var received; + var expectedEvent; + var expectedPayload; + + t.test('setup', function (t) { + received = []; + + expectedEvent = 'event'; + expectedPayload = { some: 'payload' }; + + function onReceive(actualEvent, actualPayload) { + t.equal(actualEvent, expectedEvent, 'Node should receive expected event'); + t.equal(actualPayload, expectedPayload, 'Node should receive expected payload'); + + received.push(this.__path__); + } + + for (var path in nodes) { + nodes[path].onReceive = onReceive; + } + + t.end(); + }); + + t.test('exec', function (t) { + Dispatch.dispatch('path/1', expectedEvent, expectedPayload); + + t.equal( + received.indexOf('path/1'), -1, + 'path/1 should not receive event dispatched via Dispatch.dispatch("path/1")' + ); + + t.deepEqual( + received, ['path/1/2', 'path/1/1', 'path/1/2/3', 'path/1/2/4'], + 'Dispatch.dispatch should trigger event first on parent, then on children' + ); + + t.end(); + }); + + t.test('teardown', function (t) { + for (var path in nodes) { + nodes[path].onReceive = null; + } + + t.end(); + }); + }); + + t.test('conflicting events', function (t) { + var received; + + t.test('setup', function (t) { + received = []; + + function onReceive(event, payload) { + received.push([this.__path__, event, payload]); + } + + for (var path in nodes) { + nodes[path].onReceive = onReceive; + } + + nodes['path/1/1'].onReceive = function (event, payload) { + received.push([this.__path__, event, payload]); + Dispatch.dispatch('path/1/2', 'event2', {payload: 2}); + }; + + t.end(); + }); + + t.test('exec', function (t) { + Dispatch.dispatch('path', 'event1', {payload: 1}); + + t.equal( + received.length, 7, + 'Dispatching an event while processing a previous event should result into the correct number of events being received by the nodes' + ); + + t.deepEqual( + received, + [ + [ 'path/1', 'event1', { payload: 1 } ], + [ 'path/1/2', 'event1', { payload: 1 } ], + [ 'path/1/1', 'event1', { payload: 1 } ], + [ 'path/1/2/3', 'event2', { payload: 2 } ], + [ 'path/1/2/4', 'event2', { payload: 2 } ], + [ 'path/1/2/3', 'event1', { payload: 1 } ], + [ 'path/1/2/4', 'event1', { payload: 1 } ] + ], + 'Dispatching the second event should traverse the scene graph in the correctly' + ); + + t.end(); + }); + + t.test('teardown', function (t) { + for (var path in nodes) { + nodes[path].onReceive = null; + } + + t.end(); + }); + }); }); t.test('.dispatchUIEvents method', function (t) { From 6b56cb5b892249f8a759acfd4d512e1f4d2fdff4 Mon Sep 17 00:00:00 2001 From: Alexander Gugel Date: Thu, 23 Jul 2015 10:56:58 +0200 Subject: [PATCH 27/29] test: Isolate Dispatch using rewire --- core/test/dispatch/Dispatch.spec.js | 58 +++++++++++++++-------------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/core/test/dispatch/Dispatch.spec.js b/core/test/dispatch/Dispatch.spec.js index f00b8a79..48b680b9 100644 --- a/core/test/dispatch/Dispatch.spec.js +++ b/core/test/dispatch/Dispatch.spec.js @@ -1,11 +1,15 @@ 'use strict'; var api = require('./Dispatch.api'); -var Dispatch = require('../../../core/Dispatch'); +var rewire = require('rewire'); +var Dispatch = rewire('../../../core/Dispatch'); var NodeStub = require('../node/Node.stub'); +var PathUtilsStub = require('../path/Path.stub'); var Node = require('../../../core/Node'); var test = require('tape'); +Dispatch.__set__('PathUtils', PathUtilsStub); + test('Dispatch singleton', function (t) { t.test('Dispatch object', function (t) { @@ -14,7 +18,7 @@ test('Dispatch singleton', function (t) { t.equal(typeof Dispatch, 'object', 'Dispatch should be an object'); t.end(); }); - + t.test('Dispatch should conform to its public api', function (t) { api.forEach(function (method) { @@ -34,7 +38,7 @@ test('Dispatch singleton', function (t) { t.test('._setUpdater method', function (t) { var testUpdater = 'a'; - + t.doesNotThrow( Dispatch._setUpdater.bind(Dispatch, testUpdater), '._setUpdater should be callable' @@ -96,9 +100,9 @@ test('Dispatch singleton', function (t) { }); t.test('.dispatch method', function (t) { - + var nodes; - + t.test('setup', function (t) { nodes = { 'path': new Node(), @@ -108,7 +112,7 @@ test('Dispatch singleton', function (t) { 'path/1/2/3': new Node(), 'path/1/2/4': new Node() }; - + nodes.path.addChild(nodes['path/1']); nodes['path/1'].addChild(nodes['path/1/2']); nodes['path/1'].addChild(nodes['path/1/1']); @@ -119,16 +123,16 @@ test('Dispatch singleton', function (t) { nodes[path].__path__ = path; Dispatch.mount(path, nodes[path]); } - + t.end(); }); - + t.test('basic', function (t) { - + var received; var expectedEvent; var expectedPayload; - + t.test('setup', function (t) { received = []; @@ -138,17 +142,17 @@ test('Dispatch singleton', function (t) { function onReceive(actualEvent, actualPayload) { t.equal(actualEvent, expectedEvent, 'Node should receive expected event'); t.equal(actualPayload, expectedPayload, 'Node should receive expected payload'); - + received.push(this.__path__); } for (var path in nodes) { nodes[path].onReceive = onReceive; } - + t.end(); }); - + t.test('exec', function (t) { Dispatch.dispatch('path/1', expectedEvent, expectedPayload); @@ -156,27 +160,27 @@ test('Dispatch singleton', function (t) { received.indexOf('path/1'), -1, 'path/1 should not receive event dispatched via Dispatch.dispatch("path/1")' ); - + t.deepEqual( received, ['path/1/2', 'path/1/1', 'path/1/2/3', 'path/1/2/4'], 'Dispatch.dispatch should trigger event first on parent, then on children' ); - + t.end(); }); - + t.test('teardown', function (t) { for (var path in nodes) { nodes[path].onReceive = null; } - + t.end(); }); }); - + t.test('conflicting events', function (t) { var received; - + t.test('setup', function (t) { received = []; @@ -187,23 +191,23 @@ test('Dispatch singleton', function (t) { for (var path in nodes) { nodes[path].onReceive = onReceive; } - + nodes['path/1/1'].onReceive = function (event, payload) { received.push([this.__path__, event, payload]); Dispatch.dispatch('path/1/2', 'event2', {payload: 2}); }; - + t.end(); }); - + t.test('exec', function (t) { Dispatch.dispatch('path', 'event1', {payload: 1}); - + t.equal( received.length, 7, 'Dispatching an event while processing a previous event should result into the correct number of events being received by the nodes' ); - + t.deepEqual( received, [ @@ -217,15 +221,15 @@ test('Dispatch singleton', function (t) { ], 'Dispatching the second event should traverse the scene graph in the correctly' ); - + t.end(); }); - + t.test('teardown', function (t) { for (var path in nodes) { nodes[path].onReceive = null; } - + t.end(); }); }); From 622461886b5162f8dc42f91483b04d758b507d72 Mon Sep 17 00:00:00 2001 From: Alexander Gugel Date: Fri, 31 Jul 2015 17:29:43 +0200 Subject: [PATCH 28/29] test: Fix source node dispatch test case --- core/test/dispatch/Dispatch.spec.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/core/test/dispatch/Dispatch.spec.js b/core/test/dispatch/Dispatch.spec.js index 48b680b9..3f5327cc 100644 --- a/core/test/dispatch/Dispatch.spec.js +++ b/core/test/dispatch/Dispatch.spec.js @@ -157,12 +157,12 @@ test('Dispatch singleton', function (t) { Dispatch.dispatch('path/1', expectedEvent, expectedPayload); t.equal( - received.indexOf('path/1'), -1, - 'path/1 should not receive event dispatched via Dispatch.dispatch("path/1")' + received.indexOf('path/1'), 0, + 'path/1 should receive event dispatched via Dispatch.dispatch("path/1")' ); t.deepEqual( - received, ['path/1/2', 'path/1/1', 'path/1/2/3', 'path/1/2/4'], + received, ['path/1', 'path/1/2', 'path/1/1', 'path/1/2/3', 'path/1/2/4'], 'Dispatch.dispatch should trigger event first on parent, then on children' ); @@ -204,16 +204,22 @@ test('Dispatch singleton', function (t) { Dispatch.dispatch('path', 'event1', {payload: 1}); t.equal( - received.length, 7, + received.length, 9, 'Dispatching an event while processing a previous event should result into the correct number of events being received by the nodes' ); + received.forEach(function (node) { + delete node[2].node; + }); + t.deepEqual( received, [ + [ 'path', 'event1', { payload: 1 } ], [ 'path/1', 'event1', { payload: 1 } ], [ 'path/1/2', 'event1', { payload: 1 } ], [ 'path/1/1', 'event1', { payload: 1 } ], + [ 'path/1/2', 'event2', { payload: 2 } ], [ 'path/1/2/3', 'event2', { payload: 2 } ], [ 'path/1/2/4', 'event2', { payload: 2 } ], [ 'path/1/2/3', 'event1', { payload: 1 } ], From 4e1990c61de6f9ff8092bc31cafd663c895eb63e Mon Sep 17 00:00:00 2001 From: Joseph Orbegoso Pea Date: Tue, 28 Jul 2015 14:24:04 -0700 Subject: [PATCH 29/29] Require GLSL files directly as a workaround for Webpack until we get glslify working with Webpack. --- package.json | 4 ++-- webgl-shaders/index.js | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 3aa9740f..b87089dc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { - "name": "famous", - "version": "0.7.1", + "name": "infamous-engine", + "version": "0.7.1-webpack.0", "description": "", "main": "index.js", "scripts": { diff --git a/webgl-shaders/index.js b/webgl-shaders/index.js index dfb87204..ea848433 100644 --- a/webgl-shaders/index.js +++ b/webgl-shaders/index.js @@ -24,11 +24,12 @@ 'use strict'; -var glslify = require('glslify'); +var VertexShader = require('./VertexShader.glsl') +var FragmentShader = require('./FragmentShader.glsl') var shaders = { - vertex: glslify('./VertexShader.glsl'), - fragment: glslify('./FragmentShader.glsl') + vertex: VertexShader, + fragment: FragmentShader }; module.exports = shaders;