Parcourir la source

Major changes on Canvas2D and introducing GUI

 - Moved Canvas2D to a new folder location, make it a separate compiled .js file that wll contain everything which is Canvas2D related (so including the GUI lib)
 - Include the first commit of the GUI related files
nockawa il y a 8 ans
Parent
commit
676af8b1e7
60 fichiers modifiés avec 2873 ajouts et 1643 suppressions
  1. 47 0
      Canvas2D/config.json
  2. 48 0
      Canvas2D/gulp-addModuleExports.js
  3. 97 0
      Canvas2D/gulp-removeShaderComments.js
  4. 68 0
      Canvas2D/gulp-srcToVariable.js
  5. 124 0
      Canvas2D/gulpfile.js
  6. 28 0
      Canvas2D/package.json
  7. 65 0
      Canvas2D/readme.md
  8. 0 0
      Canvas2D/src/Engine/babylon.bounding2d.js
  9. 0 0
      Canvas2D/src/Engine/babylon.bounding2d.ts
  10. 2 2
      src/Canvas2d/babylon.brushes2d.js
  11. 0 0
      Canvas2D/src/Engine/babylon.brushes2d.ts
  12. 61 4
      src/Canvas2d/babylon.canvas2d.js
  13. 0 0
      Canvas2D/src/Engine/babylon.canvas2d.ts
  14. 3 3
      src/Canvas2d/babylon.canvas2dLayoutEngine.js
  15. 0 0
      Canvas2D/src/Engine/babylon.canvas2dLayoutEngine.ts
  16. 1 1
      src/Canvas2d/babylon.ellipse2d.js
  17. 0 0
      Canvas2D/src/Engine/babylon.ellipse2d.ts
  18. 1 1
      src/Canvas2d/babylon.group2d.js
  19. 0 0
      Canvas2D/src/Engine/babylon.group2d.ts
  20. 1 1
      src/Canvas2d/babylon.lines2d.js
  21. 0 0
      Canvas2D/src/Engine/babylon.lines2d.ts
  22. 0 0
      Canvas2D/src/Engine/babylon.modelRenderCache.js
  23. 0 0
      Canvas2D/src/Engine/babylon.modelRenderCache.ts
  24. 203 67
      src/Canvas2d/babylon.prim2dBase.js
  25. 0 0
      Canvas2D/src/Engine/babylon.prim2dBase.ts
  26. 1 1
      src/Canvas2d/babylon.rectangle2d.js
  27. 0 0
      Canvas2D/src/Engine/babylon.rectangle2d.ts
  28. 1 1
      src/Canvas2d/babylon.renderablePrim2d.js
  29. 0 0
      Canvas2D/src/Engine/babylon.renderablePrim2d.ts
  30. 1 1
      src/Canvas2d/babylon.shape2d.js
  31. 0 0
      Canvas2D/src/Engine/babylon.shape2d.ts
  32. 1146 0
      Canvas2D/src/Engine/babylon.smartPropertyPrim.js
  33. 0 0
      Canvas2D/src/Engine/babylon.smartPropertyPrim.ts
  34. 1 1
      src/Canvas2d/babylon.sprite2d.js
  35. 0 0
      Canvas2D/src/Engine/babylon.sprite2d.ts
  36. 12 2
      src/Canvas2d/babylon.text2d.js
  37. 0 0
      Canvas2D/src/Engine/babylon.text2d.ts
  38. 0 0
      Canvas2D/src/Engine/babylon.worldSpaceCanvas2dNode.js
  39. 0 0
      Canvas2D/src/Engine/babylon.worldSpaceCanvas2dNode.ts
  40. 0 0
      Canvas2D/src/GUI/babylon.gui.UIElement.ts
  41. 0 0
      Canvas2D/src/GUI/babylon.gui.button.ts
  42. 0 0
      Canvas2D/src/GUI/babylon.gui.control.ts
  43. 0 0
      Canvas2D/src/GUI/babylon.gui.label.ts
  44. 0 0
      Canvas2D/src/GUI/babylon.gui.window.ts
  45. 0 0
      Canvas2D/src/Shaders/ellipse2d.fragment.fx
  46. 0 0
      Canvas2D/src/Shaders/ellipse2d.vertex.fx
  47. 0 0
      Canvas2D/src/Shaders/lines2d.fragment.fx
  48. 0 0
      Canvas2D/src/Shaders/lines2d.vertex.fx
  49. 0 0
      Canvas2D/src/Shaders/rect2d.fragment.fx
  50. 0 0
      Canvas2D/src/Shaders/rect2d.vertex.fx
  51. 0 0
      Canvas2D/src/Shaders/sprite2d.fragment.fx
  52. 0 0
      Canvas2D/src/Shaders/sprite2d.vertex.fx
  53. 0 0
      Canvas2D/src/Shaders/text2d.fragment.fx
  54. 0 0
      Canvas2D/src/Shaders/text2d.vertex.fx
  55. 642 0
      Canvas2D/src/Tools/babylon.observable.ts
  56. 320 0
      Canvas2D/src/Tools/babylon.stringDictionary.ts
  57. 0 16
      Tools/Gulp/config.json
  58. 0 584
      src/Canvas2d/babylon.smartPropertyPrim.js
  59. 0 640
      src/Tools/babylon.observable.ts
  60. 0 318
      src/Tools/babylon.stringDictionary.ts

+ 47 - 0
Canvas2D/config.json

@@ -0,0 +1,47 @@
+{
+  "build": {
+    "filename": "babylon.canvas2d.max.js",
+    "minFilename": "babylon.canvas2d.js",
+    "declarationFilename": "babylon.canvas2d.d.ts",
+    "outputDirectory": "../dist/preview release",
+    "srcOutputDirectory": "src/"
+  },
+  "core": {
+    "typescript": [
+      "src/**/*.ts",
+      "../dist/preview release/babylon.d.ts", 
+      "!src/**/*.d.ts"
+    ],
+    "files": [   
+      "src/Tools/babylon.stringDictionary.js",
+      "src/Tools/babylon.observable.js",
+      "src/Engine/babylon.bounding2d.js",
+      "src/Engine/babylon.canvas2dLayoutEngine.js",
+      "src/Engine/babylon.brushes2d.js",
+      "src/Engine/babylon.smartPropertyPrim.js",
+      "src/Engine/babylon.prim2dBase.js",
+      "src/Engine/babylon.modelRenderCache.js",
+      "src/Engine/babylon.renderablePrim2d.js",
+      "src/Engine/babylon.shape2d.js",
+      "src/Engine/babylon.group2d.js",
+      "src/Engine/babylon.rectangle2d.js",
+      "src/Engine/babylon.ellipse2d.js",
+      "src/Engine/babylon.sprite2d.js",
+      "src/Engine/babylon.text2d.js",
+      "src/Engine/babylon.lines2d.js",
+      "src/Engine/babylon.canvas2d.js",
+      "src/Engine/babylon.worldSpaceCanvas2dNode.js",
+      "src/GUI/babylon.gui.UIElement.js",
+      "src/GUI/babylon.gui.control.js",
+      "src/GUI/babylon.gui.window.js",
+      "src/GUI/babylon.gui.label.js",
+      "src/GUI/babylon.gui.button.js"
+    ]
+  },
+  "shadersDirectories": [
+    {
+      "variable": "BABYLON.Effect.ShadersStore",
+      "files": "src/Shaders/*.fx"
+    }
+  ]
+}

+ 48 - 0
Canvas2D/gulp-addModuleExports.js

@@ -0,0 +1,48 @@
+var gutil = require('gulp-util');
+var through = require('through2');
+
+module.exports = function (varName) {
+    return through.obj(function (file, enc, cb) {
+
+        var moduleExportsAddition =
+          '\nif (((typeof window != "undefined" && window.module) || (typeof module != "undefined")) && typeof module.exports != "undefined") {\n' +
+          '    module.exports = ' + varName + ';\n' +
+          '};\n';
+
+        var extendsAddition =
+        'var __extends = (this && this.__extends) || function (d, b) {\n' +
+          'for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];\n' +
+          'function __() { this.constructor = d; }\n' +
+          '__.prototype = b.prototype;\n' +
+          'd.prototype = new __();\n' +
+        '};\n';
+
+        var decorateAddition =
+        'var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {\n' +
+            'var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;\n' +
+            'if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);\n' +
+            'else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;\n' +
+            'return c > 3 && r && Object.defineProperty(target, key, r), r;\n' +
+        '};\n';
+
+        if (file.isNull()) {
+            cb(null, file);
+            return;
+        }
+
+        if (file.isStream()) {
+            //streams not supported, no need for now.
+            return;
+        }
+
+        try {
+            file.contents = new Buffer(decorateAddition.concat(new Buffer(extendsAddition.concat(String(file.contents)).concat(moduleExportsAddition))));
+            this.push(file);
+
+        } catch (err) {
+            this.emit('error', new gutil.PluginError('gulp-add-module-exports', err, { fileName: file.path }));
+        }
+        cb();
+    });
+};
+

+ 97 - 0
Canvas2D/gulp-removeShaderComments.js

@@ -0,0 +1,97 @@
+'use strict';
+
+var through = require('through2');
+var PluginError = require('gulp-util').PluginError;
+var singleComment = 1;
+var multiComment = 2;
+
+function uncomment(str, opts) {
+    opts = opts || {};
+
+	var currentChar;
+	var nextChar;
+	var insideString = false;
+	var insideComment = 0;
+	var offset = 0;
+	var ret = '';
+
+    str = str.replace(/\r\n/g, '\n');
+    str = str.replace(/[ \f\t\v]+/g, ' ');
+    str = str.replace(/^\s*\n/gm, '');
+    str = str.replace(/ \+ /g, '+');
+    str = str.replace(/ \- /g, '-');
+    str = str.replace(/ \/ /g, '/');
+    str = str.replace(/ \* /g, '*');
+    str = str.replace(/ > /g, '>');
+    str = str.replace(/ < /g, '<');
+    str = str.replace(/ >= /g, '>=');
+    str = str.replace(/ <= /g, '<=');
+    str = str.replace(/ \+= /g, '+=');
+    str = str.replace(/ \-= /g, '-=');
+    str = str.replace(/ \/= /g, '/=');
+    str = str.replace(/ \*= /g, '*=');
+    str = str.replace(/ = /g, '=');
+    str = str.replace(/, /g, ',');
+    str = str.replace(/\n\n/g, '\n');
+    str = str.replace(/\n /g, '\n');
+    
+	for (var i = 0; i < str.length; i++) {
+		currentChar = str[i];
+		nextChar = str[i + 1];
+
+		if (!insideComment && currentChar === '"') {
+			var escaped = str[i - 1] === '\\' && str[i - 2] !== '\\';
+			if (!escaped) {
+				insideString = !insideString;
+			}
+		}
+
+		if (insideString) {
+			continue;
+		}
+
+		if (!insideComment && currentChar + nextChar === '//') {
+			ret += str.slice(offset, i);
+			offset = i;
+			insideComment = singleComment;
+			i++;
+		} else if (insideComment === singleComment && currentChar === '\n') {
+			insideComment = 0;
+			offset = i;
+		} else if (!insideComment && currentChar + nextChar === '/*') {
+			ret += str.slice(offset, i);
+			offset = i;
+			insideComment = multiComment;
+			i++;
+			continue;
+		} else if (insideComment === multiComment && currentChar + nextChar === '*/') {
+			i++;
+			insideComment = 0;
+			offset = i + 1;
+			continue;
+		}
+	}
+
+	return ret + (insideComment ? '' : str.substr(offset));
+}
+
+function gulpUncomment(options) {
+    return main(options, uncomment);
+}
+
+function main(options, func) {
+    return through.obj(function (file, enc, cb) {
+        if (file.isNull()) {
+            cb(null, file);
+            return;
+        }
+        if (file.isStream()) {
+            cb(new PluginError("Remove Shader Comments", "Streaming not supported."));
+        }
+        file.contents = new Buffer(func(file.contents.toString(), options));
+        this.push(file);
+        return cb();
+    });
+}
+
+module.exports = gulpUncomment;

+ 68 - 0
Canvas2D/gulp-srcToVariable.js

@@ -0,0 +1,68 @@
+var through = require('through2');
+var gutil = require('gulp-util');
+var PluginError = gutil.PluginError;
+var path = require('path');
+var File = gutil.File;
+
+// Consts
+const PLUGIN_NAME = 'gulp-srcToVariable';
+
+var srcToVariable = function srcToVariable(varName, asMap, namingCallback) {
+
+    var content;
+    var firstFile;
+
+    namingCallback = namingCallback || function (filename) { return filename; };
+
+    function bufferContents(file, enc, cb) {
+        // ignore empty files
+        if (file.isNull()) {
+            cb();
+            return;
+        }
+
+        // no stream support, only files.
+        if (file.isStream()) {
+            this.emit('error', new PluginError('gulp-concat', 'Streaming not supported'));
+            cb();
+            return;
+        }
+
+        // set first file if not already set
+        if (!firstFile) {
+            firstFile = file;
+        }
+
+        // construct concat instance
+        if (!content) {
+            content = "";
+        }
+        var name = namingCallback(file.relative);
+        content += varName + "['" + name + "'] = " + JSON.stringify(file.contents.toString()) + ";\r\n";
+        cb();
+    }
+
+    function endStream(cb) {
+        if (!firstFile || !content) {
+            cb();
+            return;
+        }
+
+        var joinedPath = path.join(firstFile.base, varName);
+
+        var joinedFile = new File({
+            cwd: firstFile.cwd,
+            base: firstFile.base,
+            path: joinedPath,
+            contents: new Buffer(content)
+        });
+
+        this.push(joinedFile);
+
+        cb();
+    }
+
+    return through.obj(bufferContents, endStream);
+}
+
+module.exports = srcToVariable;

+ 124 - 0
Canvas2D/gulpfile.js

@@ -0,0 +1,124 @@
+var gulp = require("gulp");
+var uglify = require("gulp-uglify");
+var typescript = require("gulp-typescript");
+var sourcemaps = require("gulp-sourcemaps");
+var srcToVariable = require("./gulp-srcToVariable");
+var addModuleExports = require("./gulp-addModuleExports");
+var merge2 = require("merge2");
+var concat = require("gulp-concat");
+var rename = require("gulp-rename");
+var cleants = require('gulp-clean-ts-extends');
+var changed = require('gulp-changed');
+var runSequence = require('run-sequence');
+var replace = require("gulp-replace");
+var uncommentShader = require("./gulp-removeShaderComments");
+var expect = require('gulp-expect-file');
+var optimisejs = require('gulp-optimize-js');
+
+var config = require("./config.json");
+
+var shadersStream;
+
+var extendsSearchRegex = /var\s__extends[\s\S]+?\};/g;
+var decorateSearchRegex = /var\s__decorate[\s\S]+?\};/g;
+
+//function to convert the shaders' filenames to variable names.
+function shadersName(filename) {
+    return filename.replace('.fragment', 'Pixel')
+        .replace('.vertex', 'Vertex')
+        .replace('.fx', 'Shader');
+}
+
+gulp.task("shaders", function (cb) {
+    shadersStream = config.shadersDirectories.map(function (shadersDef) {
+        return gulp.src(shadersDef.files).
+            pipe(expect.real({ errorOnFailure: true }, shadersDef.files)).
+            pipe(uncommentShader()).
+            pipe(srcToVariable("BABYLON.Effect.ShadersStore", true, shadersName)
+            );
+    });
+    cb();
+});
+
+/*
+Compiles all typescript files and creating a declaration file.
+*/
+gulp.task('typescript-compile', function () {
+    var tsResult = gulp.src(config.core.typescript).
+        pipe(typescript({
+            noExternalResolve: true,
+            target: 'ES5',
+            declarationFiles: true,
+            typescript: require('typescript'),
+            experimentalDecorators: true
+        }));
+    //If this gulp task is running on travis, file the build!
+    if (process.env.TRAVIS) {
+        var error = false;
+        tsResult.on('error', function () {
+            error = true;
+        }).on('end', function () {
+            if (error) {
+                console.log('Typescript compile failed');
+                process.exit(1);
+            }
+        });
+    }
+    return merge2([
+        tsResult.dts
+            .pipe(concat(config.build.declarationFilename))
+            .pipe(gulp.dest(config.build.outputDirectory)),
+        tsResult.js
+            .pipe(gulp.dest(config.build.srcOutputDirectory))
+    ])
+});
+
+gulp.task('typescript-sourcemaps', function () {
+    var tsResult = gulp.src(config.core.typescript)
+        .pipe(sourcemaps.init()) // sourcemaps init. currently redundant directory def, waiting for this - https://github.com/floridoo/gulp-sourcemaps/issues/111
+        .pipe(typescript({
+            noExternalResolve: true,
+            target: 'ES5',
+            declarationFiles: true,
+            typescript: require('typescript'),
+            experimentalDecorators: true
+        }));
+    return tsResult.js
+        .pipe(sourcemaps.write("./")) // sourcemaps are written.
+        .pipe(gulp.dest(config.build.srcOutputDirectory));
+});
+
+gulp.task("default", ["shaders"], function () {
+    return merge2(
+        gulp.src(config.core.files).        
+            pipe(expect.real({ errorOnFailure: true }, config.core.files)),
+        shadersStream)
+        .pipe(concat(config.build.filename))
+        .pipe(cleants())
+        .pipe(replace(extendsSearchRegex, ""))
+        .pipe(replace(decorateSearchRegex, ""))
+        .pipe(addModuleExports("BABYLON"))
+        .pipe(gulp.dest(config.build.outputDirectory))
+        .pipe(rename(config.build.minFilename))
+        .pipe(uglify())
+        .pipe(optimisejs())
+        .pipe(gulp.dest(config.build.outputDirectory));
+});
+
+gulp.task("typescript", function (cb) {
+    runSequence("typescript-compile", "default", cb);
+});
+
+/**
+ * Watch task, will call the default task if a js file is updated.
+ */
+gulp.task('watch', function () {
+    gulp.watch(config.core.typescript, ['default']);
+});
+
+/**
+ * Watch typescript task, will call the default typescript task if a typescript file is updated.
+ */
+gulp.task('watch-typescript', function () {
+    gulp.watch(config.core.typescript, ["typescript-compile", "build"]);
+});

+ 28 - 0
Canvas2D/package.json

@@ -0,0 +1,28 @@
+{
+  "name": "BabylonJS",
+  "version": "2.4.0",
+  "description": "Babylon.js is a 3D engine based on webgl and javascript",
+  "main": "",
+  "repository": { "url": "https://github.com/BabylonJS/Babylon.js/" },
+  "readme": "https://github.com/BabylonJS/Babylon.js/edit/master/readme.md",
+  "license": "(Apache-2.0)",
+  "devDependencies": {
+    "gulp": "^3.8.11",
+    "gulp-uglify": "~1.5.3",
+    "gulp-sourcemaps": "~1.5.2",
+    "typescript": "^1.7.5",
+    "gulp-typescript": "~2.13.0",
+    "through2": "~0.6.5",
+    "gulp-util": "~3.0.4",
+    "gulp-concat": "~2.5.2",
+    "merge2": "~0.3.5",
+    "gulp-rename": "~1.2.2",
+    "gulp-clean-ts-extends": "~0.1.1",
+    "gulp-changed": "~1.2.1",
+    "run-sequence": "~1.1.0",
+    "gulp-replace": "~0.5.3",
+    "gulp-content-to-variable": "^0.1.0",
+    "gulp-expect-file": "^0.0.7",
+    "gulp-optimize-js": "^1.0.2"
+  }
+}

+ 65 - 0
Canvas2D/readme.md

@@ -0,0 +1,65 @@
+Build Babylon.canvas2d.js with Gulp
+====================
+
+More info about [Canvas2D](http://doc.babylonjs.com/overviews/Canvas2D_Home)
+
+Build Babylon.canvas2d.js with [gulp](http://gulpjs.com/ "gulp") and npm ([nodejs](http://nodejs.org/ "nodejs")), easy and cross-platform
+
+(Paths in this file are relative to this file location.)
+
+# How to use it
+
+### First install gulp :
+```
+npm install -g gulp
+```
+
+### Install some dependencies :
+```
+npm install
+```
+
+### Update dependencies if necessary :
+```
+npm update
+```
+
+## From the javascript source
+### Build Babylon.canvas2d.js from the javascript files:
+
+```
+gulp
+```
+Will be generated :
+- babylon.canvas2d.js
+- babylon.canvas2d.max.js (unminified)
+
+### Build Babylon.canvas2d.js when you save a javascript file:
+```
+gulp watch
+```
+
+## From the typescript source
+### Build Babylon.canvas2d.js from the typescript files:
+
+```
+gulp typescript
+```
+Will be generated :
+- babylon.canvas2d.js
+- babylon.canvas2d.d.ts
+- babylon.canvas2d.max.js (unminified)
+
+Be aware that all js files content will be overwrite.
+
+### Build Babylon.canvas2d.js when you save a typescript file:
+```
+gulp watch-typescript
+```
+
+### Compile all the typscript files to their javascript respective files including declaration file
+```
+gulp typescript-compile
+```
+
+Be aware that all js files content will be overwritten.

src/Canvas2d/babylon.bounding2d.js → Canvas2D/src/Engine/babylon.bounding2d.js


src/Canvas2d/babylon.bounding2d.ts → Canvas2D/src/Engine/babylon.bounding2d.ts


+ 2 - 2
src/Canvas2d/babylon.brushes2d.js

@@ -79,7 +79,7 @@ var BABYLON;
             return this._color.toHexString();
         };
         SolidColorBrush2D = __decorate([
-            BABYLON.className("SolidColorBrush2D")
+            BABYLON.className("SolidColorBrush2D", "BABYLON")
         ], SolidColorBrush2D);
         return SolidColorBrush2D;
     }(LockableBase));
@@ -204,7 +204,7 @@ var BABYLON;
             return "C1:" + color1 + ";C2:" + color2 + ";T:" + translation.toString() + ";R:" + rotation + ";S:" + scale + ";";
         };
         GradientColorBrush2D = __decorate([
-            BABYLON.className("GradientColorBrush2D")
+            BABYLON.className("GradientColorBrush2D", "BABYLON")
         ], GradientColorBrush2D);
         return GradientColorBrush2D;
     }(LockableBase));

src/Canvas2d/babylon.brushes2d.ts → Canvas2D/src/Engine/babylon.brushes2d.ts


+ 61 - 4
src/Canvas2d/babylon.canvas2d.js

@@ -86,6 +86,7 @@ var BABYLON;
             this._updateLocalTransformCounter = new BABYLON.PerfCounter();
             this._updateGlobalTransformCounter = new BABYLON.PerfCounter();
             this._boundingInfoRecomputeCounter = new BABYLON.PerfCounter();
+            this._uid = null;
             this._cachedCanvasGroup = null;
             this._profileInfoText = null;
             BABYLON.Prim2DBase._isCanvasInit = false;
@@ -487,6 +488,9 @@ var BABYLON;
             this._actualIntersectionList = ii.intersectedPrimitives;
             this._previousOverPrimitive = this._actualOverPrimitive;
             this._actualOverPrimitive = ii.topMostIntersectedPrimitive;
+            if ((!this._actualOverPrimitive && !this._previousOverPrimitive) || !(this._actualOverPrimitive && this._previousOverPrimitive && this._actualOverPrimitive.prim === this._previousOverPrimitive.prim)) {
+                this.onPropertyChanged("overPrim", this._previousOverPrimitive ? this._previousOverPrimitive.prim : null, this._actualOverPrimitive ? this._actualOverPrimitive.prim : null);
+            }
             this._intersectionRenderId = this.scene.getRenderId();
         };
         // Based on the previousIntersectionList and the actualInstersectionList we can determined which primitives are being hover state or loosing it
@@ -739,6 +743,35 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(Canvas2D.prototype, "uid", {
+            /**
+             * return a unique identifier for the Canvas2D
+             */
+            get: function () {
+                if (!this._uid) {
+                    this._uid = BABYLON.Tools.RandomId();
+                }
+                return this._uid;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Canvas2D.prototype, "renderObservable", {
+            /**
+             * And observable called during the Canvas rendering process.
+             * This observable is called twice per render, each time with a different mask:
+             *  - 1: before render is executed
+             *  - 2: after render is executed
+             */
+            get: function () {
+                if (!this._renderObservable) {
+                    this._renderObservable = new BABYLON.Observable();
+                }
+                return this._renderObservable;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(Canvas2D.prototype, "cachingStrategy", {
             /**
              * Accessor of the Caching Strategy used by this Canvas.
@@ -901,6 +934,16 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(Canvas2D.prototype, "overPrim", {
+            /**
+             * Return
+             */
+            get: function () {
+                return this._actualOverPrimitive ? this._actualOverPrimitive.prim : null;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(Canvas2D.prototype, "_engineData", {
             /**
              * Access the babylon.js' engine bound data, do not invoke this method, it's for internal purpose only
@@ -1132,6 +1175,9 @@ var BABYLON;
          */
         Canvas2D.prototype._render = function () {
             this._initPerfMetrics();
+            if (this._renderObservable && this._renderObservable.hasObservers()) {
+                this._renderObservable.notifyObservers(this, Canvas2D.RENDEROBSERVABLE_PRE);
+            }
             this._updateCanvasState(false);
             this._updateTrackedNodes();
             // Nothing to do is the Canvas is not visible
@@ -1144,7 +1190,7 @@ var BABYLON;
             this._updateCanvasState(false);
             if (this._primPointerInfo.canvasPointerPos) {
                 this._updateIntersectionList(this._primPointerInfo.canvasPointerPos, false, false);
-                this._updateOverStatus(false); // TODO this._primPointerInfo may not be up to date!
+                this._updateOverStatus(false);
             }
             this.engine.setState(false);
             this._groupRender();
@@ -1160,6 +1206,9 @@ var BABYLON;
             }
             this._fetchPerfMetrics();
             this._updateProfileCanvas();
+            if (this._renderObservable && this._renderObservable.hasObservers()) {
+                this._renderObservable.notifyObservers(this, Canvas2D.RENDEROBSERVABLE_POST);
+            }
         };
         /**
          * Internal method that allocate a cache for the given group.
@@ -1385,6 +1434,14 @@ var BABYLON;
          * Note that you can't use this strategy for WorldSpace Canvas, they need at least a top level group caching.
          */
         Canvas2D.CACHESTRATEGY_DONTCACHE = 4;
+        /**
+         * Observable Mask to be notified before rendering is made
+         */
+        Canvas2D.RENDEROBSERVABLE_PRE = 1;
+        /**
+         * Observable Mask to be notified after rendering is made
+         */
+        Canvas2D.RENDEROBSERVABLE_POST = 2;
         Canvas2D._INSTANCES = [];
         Canvas2D._zMinDelta = 1 / (Math.pow(2, 24) - 1);
         Canvas2D._interInfo = new BABYLON.IntersectInfo2D();
@@ -1400,7 +1457,7 @@ var BABYLON;
         Canvas2D._solidColorBrushes = new BABYLON.StringDictionary();
         Canvas2D._gradientColorBrushes = new BABYLON.StringDictionary();
         Canvas2D = __decorate([
-            BABYLON.className("Canvas2D")
+            BABYLON.className("Canvas2D", "BABYLON")
         ], Canvas2D);
         return Canvas2D;
     }(BABYLON.Group2D));
@@ -1492,7 +1549,7 @@ var BABYLON;
             }, BABYLON.Prim2DBase.isVisibleProperty.flagId);
         }
         WorldSpaceCanvas2D = __decorate([
-            BABYLON.className("WorldSpaceCanvas2D")
+            BABYLON.className("WorldSpaceCanvas2D", "BABYLON")
         ], WorldSpaceCanvas2D);
         return WorldSpaceCanvas2D;
     }(Canvas2D));
@@ -1535,7 +1592,7 @@ var BABYLON;
             _super.call(this, scene, settings);
         }
         ScreenSpaceCanvas2D = __decorate([
-            BABYLON.className("ScreenSpaceCanvas2D")
+            BABYLON.className("ScreenSpaceCanvas2D", "BABYLON")
         ], ScreenSpaceCanvas2D);
         return ScreenSpaceCanvas2D;
     }(Canvas2D));

src/Canvas2d/babylon.canvas2d.ts → Canvas2D/src/Engine/babylon.canvas2d.ts


+ 3 - 3
src/Canvas2d/babylon.canvas2dLayoutEngine.js

@@ -35,7 +35,7 @@ var BABYLON;
             return true;
         };
         LayoutEngineBase = __decorate([
-            BABYLON.className("LayoutEngineBase")
+            BABYLON.className("LayoutEngineBase", "BABYLON")
         ], LayoutEngineBase);
         return LayoutEngineBase;
     }());
@@ -79,7 +79,7 @@ var BABYLON;
         });
         CanvasLayoutEngine.Singleton = new CanvasLayoutEngine();
         CanvasLayoutEngine = __decorate([
-            BABYLON.className("CanvasLayoutEngine")
+            BABYLON.className("CanvasLayoutEngine", "BABYLON")
         ], CanvasLayoutEngine);
         return CanvasLayoutEngine;
     }(LayoutEngineBase));
@@ -176,7 +176,7 @@ var BABYLON;
         StackPanelLayoutEngine.dstOffset = BABYLON.Vector2.Zero();
         StackPanelLayoutEngine.dstArea = BABYLON.Size.Zero();
         StackPanelLayoutEngine = __decorate([
-            BABYLON.className("StackPanelLayoutEngine")
+            BABYLON.className("StackPanelLayoutEngine", "BABYLON")
         ], StackPanelLayoutEngine);
         return StackPanelLayoutEngine;
     }(LayoutEngineBase));

src/Canvas2d/babylon.canvas2dLayoutEngine.ts → Canvas2D/src/Engine/babylon.canvas2dLayoutEngine.ts


+ 1 - 1
src/Canvas2d/babylon.ellipse2d.js

@@ -347,7 +347,7 @@ var BABYLON;
             BABYLON.modelLevelProperty(BABYLON.Shape2D.SHAPE2D_PROPCOUNT + 2, function (pi) { return Ellipse2D.subdivisionsProperty = pi; })
         ], Ellipse2D.prototype, "subdivisions", null);
         Ellipse2D = __decorate([
-            BABYLON.className("Ellipse2D")
+            BABYLON.className("Ellipse2D", "BABYLON")
         ], Ellipse2D);
         return Ellipse2D;
     }(BABYLON.Shape2D));

src/Canvas2d/babylon.ellipse2d.ts → Canvas2D/src/Engine/babylon.ellipse2d.ts


+ 1 - 1
src/Canvas2d/babylon.group2d.js

@@ -852,7 +852,7 @@ var BABYLON;
             BABYLON.instanceLevelProperty(BABYLON.Prim2DBase.PRIM2DBASE_PROPCOUNT + 2, function (pi) { return Group2D.actualSizeProperty = pi; })
         ], Group2D.prototype, "actualSize", null);
         Group2D = __decorate([
-            BABYLON.className("Group2D")
+            BABYLON.className("Group2D", "BABYLON")
         ], Group2D);
         return Group2D;
     }(BABYLON.Prim2DBase));

src/Canvas2d/babylon.group2d.ts → Canvas2D/src/Engine/babylon.group2d.ts


+ 1 - 1
src/Canvas2d/babylon.lines2d.js

@@ -1172,7 +1172,7 @@ var BABYLON;
             BABYLON.modelLevelProperty(BABYLON.Shape2D.SHAPE2D_PROPCOUNT + 5, function (pi) { return Lines2D.endCapProperty = pi; })
         ], Lines2D.prototype, "endCap", null);
         Lines2D = __decorate([
-            BABYLON.className("Lines2D")
+            BABYLON.className("Lines2D", "BABYLON")
         ], Lines2D);
         return Lines2D;
     }(BABYLON.Shape2D));

src/Canvas2d/babylon.lines2d.ts → Canvas2D/src/Engine/babylon.lines2d.ts


src/Canvas2d/babylon.modelRenderCache.js → Canvas2D/src/Engine/babylon.modelRenderCache.js


src/Canvas2d/babylon.modelRenderCache.ts → Canvas2D/src/Engine/babylon.modelRenderCache.ts


+ 203 - 67
src/Canvas2d/babylon.prim2dBase.js

@@ -304,7 +304,7 @@ var BABYLON;
                     return;
                 }
                 this._horizontal = value;
-                this._changedCallback();
+                this.onChangeCallback();
             },
             enumerable: true,
             configurable: true
@@ -321,11 +321,16 @@ var BABYLON;
                     return;
                 }
                 this._vertical = value;
-                this._changedCallback();
+                this.onChangeCallback();
             },
             enumerable: true,
             configurable: true
         });
+        PrimitiveAlignment.prototype.onChangeCallback = function () {
+            if (this._changedCallback) {
+                this._changedCallback();
+            }
+        };
         /**
          * Set the horizontal alignment from a string value.
          * @param text can be either: 'left','right','center','stretch'
@@ -374,37 +379,58 @@ var BABYLON;
          */
         PrimitiveAlignment.prototype.fromString = function (value) {
             var m = value.trim().split(",");
-            for (var _i = 0, m_1 = m; _i < m_1.length; _i++) {
-                var v = m_1[_i];
-                v = v.toLocaleLowerCase().trim();
-                // Horizontal
-                var i = v.indexOf("h:");
-                if (i === -1) {
-                    i = v.indexOf("horizontal:");
-                }
-                if (i !== -1) {
-                    v = v.substr(v.indexOf(":") + 1);
-                    this.setHorizontal(v);
-                    continue;
-                }
-                // Vertical
-                i = v.indexOf("v:");
-                if (i === -1) {
-                    i = v.indexOf("vertical:");
-                }
-                if (i !== -1) {
-                    v = v.substr(v.indexOf(":") + 1);
-                    this.setVertical(v);
-                    continue;
+            if (m.length === 1) {
+                this.setHorizontal(m[0]);
+                this.setVertical(m[0]);
+            }
+            else {
+                for (var _i = 0, m_1 = m; _i < m_1.length; _i++) {
+                    var v = m_1[_i];
+                    v = v.toLocaleLowerCase().trim();
+                    // Horizontal
+                    var i = v.indexOf("h:");
+                    if (i === -1) {
+                        i = v.indexOf("horizontal:");
+                    }
+                    if (i !== -1) {
+                        v = v.substr(v.indexOf(":") + 1);
+                        this.setHorizontal(v);
+                        continue;
+                    }
+                    // Vertical
+                    i = v.indexOf("v:");
+                    if (i === -1) {
+                        i = v.indexOf("vertical:");
+                    }
+                    if (i !== -1) {
+                        v = v.substr(v.indexOf(":") + 1);
+                        this.setVertical(v);
+                        continue;
+                    }
                 }
             }
         };
+        PrimitiveAlignment.prototype.copyFrom = function (pa) {
+            this._horizontal = pa._horizontal;
+            this._vertical = pa._vertical;
+            this.onChangeCallback();
+        };
+        Object.defineProperty(PrimitiveAlignment.prototype, "isDefault", {
+            get: function () {
+                return this.horizontal === PrimitiveAlignment.AlignLeft && this.vertical === PrimitiveAlignment.AlignBottom;
+            },
+            enumerable: true,
+            configurable: true
+        });
         PrimitiveAlignment._AlignLeft = 1;
         PrimitiveAlignment._AlignTop = 1; // Same as left
         PrimitiveAlignment._AlignRight = 2;
         PrimitiveAlignment._AlignBottom = 2; // Same as right
         PrimitiveAlignment._AlignCenter = 3;
         PrimitiveAlignment._AlignStretch = 4;
+        PrimitiveAlignment = __decorate([
+            BABYLON.className("PrimitiveAlignment", "BABYLON")
+        ], PrimitiveAlignment);
         return PrimitiveAlignment;
     }());
     BABYLON.PrimitiveAlignment = PrimitiveAlignment;
@@ -452,7 +478,7 @@ var BABYLON;
                 this._setStringValue(m[0], 1, false);
                 this._setStringValue(m[0], 2, false);
                 this._setStringValue(m[0], 3, false);
-                this._changedCallback();
+                this.onChangeCallback();
                 return;
             }
             var res = false;
@@ -472,7 +498,7 @@ var BABYLON;
                 this._flags |= PrimitiveThickness.Pixel << 8;
             if ((this._flags & 0xF000) === 0)
                 this._flags |= PrimitiveThickness.Pixel << 12;
-            this._changedCallback();
+            this.onChangeCallback();
         };
         /**
          * Set the thickness from multiple string
@@ -488,7 +514,7 @@ var BABYLON;
             this._setStringValue(left, 1, false);
             this._setStringValue(right, 2, false);
             this._setStringValue(bottom, 3, false);
-            this._changedCallback();
+            this.onChangeCallback();
             return this;
         };
         /**
@@ -504,7 +530,7 @@ var BABYLON;
             this._pixels[1] = left;
             this._pixels[2] = right;
             this._pixels[3] = bottom;
-            this._changedCallback();
+            this.onChangeCallback();
             return this;
         };
         /**
@@ -517,9 +543,18 @@ var BABYLON;
             this._pixels[1] = margin;
             this._pixels[2] = margin;
             this._pixels[3] = margin;
-            this._changedCallback();
+            this.onChangeCallback();
             return this;
         };
+        PrimitiveThickness.prototype.copyFrom = function (pt) {
+            this._clear();
+            for (var i = 0; i < 4; i++) {
+                this._pixels[i] = pt._pixels[i];
+                this._percentages[i] = pt._percentages[i];
+            }
+            this._flags = pt._flags;
+            this.onChangeCallback();
+        };
         /**
          * Set all edges in auto
          */
@@ -530,7 +565,7 @@ var BABYLON;
             this._pixels[1] = 0;
             this._pixels[2] = 0;
             this._pixels[3] = 0;
-            this._changedCallback();
+            this.onChangeCallback();
             return this;
         };
         PrimitiveThickness.prototype._clear = function () {
@@ -574,7 +609,7 @@ var BABYLON;
                 this._setType(index, PrimitiveThickness.Auto);
                 this._pixels[index] = 0;
                 if (emitChanged) {
-                    this._changedCallback();
+                    this.onChangeCallback();
                 }
             }
             else if (v === "inherit") {
@@ -584,7 +619,7 @@ var BABYLON;
                 this._setType(index, PrimitiveThickness.Inherit);
                 this._pixels[index] = null;
                 if (emitChanged) {
-                    this._changedCallback();
+                    this.onChangeCallback();
                 }
             }
             else {
@@ -602,7 +637,7 @@ var BABYLON;
                     }
                     this._percentages[index] = number_1;
                     if (emitChanged) {
-                        this._changedCallback();
+                        this.onChangeCallback();
                     }
                     return true;
                 }
@@ -625,7 +660,7 @@ var BABYLON;
                 this._pixels[index] = number;
                 this._setType(index, PrimitiveThickness.Pixel);
                 if (emitChanged) {
-                    this._changedCallback();
+                    this.onChangeCallback();
                 }
                 return true;
             }
@@ -639,7 +674,7 @@ var BABYLON;
             this._setType(index, PrimitiveThickness.Pixel);
             this._pixels[index] = value;
             if (emitChanged) {
-                this._changedCallback();
+                this.onChangeCallback();
             }
         };
         PrimitiveThickness.prototype._setPercentage = function (value, index, emitChanged) {
@@ -653,7 +688,7 @@ var BABYLON;
             this._setType(index, PrimitiveThickness.Percentage);
             this._percentages[index] = value;
             if (emitChanged) {
-                this._changedCallback();
+                this.onChangeCallback();
             }
         };
         PrimitiveThickness.prototype._getStringValue = function (index) {
@@ -937,6 +972,13 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(PrimitiveThickness.prototype, "isDefault", {
+            get: function () {
+                return this._flags === 0x1111;
+            },
+            enumerable: true,
+            configurable: true
+        });
         PrimitiveThickness.prototype._computePixels = function (index, sourceArea, emitChanged) {
             var type = this._getType(index, false);
             if (type === PrimitiveThickness.Inherit) {
@@ -949,6 +991,11 @@ var BABYLON;
             var pixels = ((index === 0 || index === 3) ? sourceArea.height : sourceArea.width) * this._percentages[index];
             this._pixels[index] = pixels;
             if (emitChanged) {
+                this.onChangeCallback();
+            }
+        };
+        PrimitiveThickness.prototype.onChangeCallback = function () {
+            if (this._changedCallback) {
                 this._changedCallback();
             }
         };
@@ -1143,6 +1190,9 @@ var BABYLON;
         PrimitiveThickness.Inherit = 0x2;
         PrimitiveThickness.Percentage = 0x4;
         PrimitiveThickness.Pixel = 0x8;
+        PrimitiveThickness = __decorate([
+            BABYLON.className("PrimitiveThickness", "BABYLON")
+        ], PrimitiveThickness);
         return PrimitiveThickness;
     }());
     BABYLON.PrimitiveThickness = PrimitiveThickness;
@@ -1235,7 +1285,6 @@ var BABYLON;
             this._padding = null;
             this._marginAlignment = null;
             this._id = settings.id;
-            this.propertyChanged = new BABYLON.Observable();
             this._children = new Array();
             this._localTransform = new BABYLON.Matrix();
             this._globalTransform = null;
@@ -1480,6 +1529,10 @@ var BABYLON;
             get: function () {
                 return this.actualPosition.x;
             },
+            set: function (val) {
+                this._actualPosition.x = val;
+                this._triggerPropertyChanged(Prim2DBase.actualPositionProperty, this._actualPosition);
+            },
             enumerable: true,
             configurable: true
         });
@@ -1490,6 +1543,10 @@ var BABYLON;
             get: function () {
                 return this.actualPosition.y;
             },
+            set: function (val) {
+                this._actualPosition.y = val;
+                this._triggerPropertyChanged(Prim2DBase.actualPositionProperty, this._actualPosition);
+            },
             enumerable: true,
             configurable: true
         });
@@ -1608,14 +1665,15 @@ var BABYLON;
                 return this.size.width;
             },
             set: function (value) {
+                if (this.size && this.size.width === value) {
+                    return;
+                }
                 if (!this.size) {
                     this.size = new BABYLON.Size(value, 0);
-                    return;
                 }
-                if (this.size.width === value) {
-                    return;
+                else {
+                    this.size.width = value;
                 }
-                this.size.width = value;
                 this._triggerPropertyChanged(Prim2DBase.sizeProperty, value);
                 this._positioningDirty();
             },
@@ -1634,14 +1692,15 @@ var BABYLON;
                 return this.size.height;
             },
             set: function (value) {
+                if (this.size && this.size.height === value) {
+                    return;
+                }
                 if (!this.size) {
                     this.size = new BABYLON.Size(0, value);
-                    return;
                 }
-                if (this.size.height === value) {
-                    return;
+                else {
+                    this.size.height = value;
                 }
-                this.size.height = value;
                 this._triggerPropertyChanged(Prim2DBase.sizeProperty, value);
                 this._positioningDirty();
             },
@@ -1692,6 +1751,34 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(Prim2DBase.prototype, "actualWidth", {
+            /**
+             * Shortcut to actualSize.width
+             */
+            get: function () {
+                return this.actualSize.width;
+            },
+            set: function (val) {
+                this._actualSize.width = val;
+                this._triggerPropertyChanged(Prim2DBase.actualSizeProperty, this._actualSize);
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Prim2DBase.prototype, "actualHeight", {
+            /**
+             * Shortcut to actualPosition.height
+             */
+            get: function () {
+                return this.actualSize.width;
+            },
+            set: function (val) {
+                this._actualSize.height = val;
+                this._triggerPropertyChanged(Prim2DBase.actualPositionProperty, this._actualSize);
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(Prim2DBase.prototype, "actualZOffset", {
             get: function () {
                 if (this._manualZOrder != null) {
@@ -1819,12 +1906,18 @@ var BABYLON;
                 }
                 return this._margin;
             },
+            set: function (value) {
+                this.margin.copyFrom(value);
+            },
             enumerable: true,
             configurable: true
         });
         Object.defineProperty(Prim2DBase.prototype, "_hasMargin", {
+            /**
+             * Check for both margin and marginAlignment, return true if at least one of them is specified with a non default value
+             */
             get: function () {
-                return (this._margin !== null) || (this._marginAlignment !== null);
+                return (this._margin !== null && !this._margin.isDefault) || (this._marginAlignment !== null && !this._marginAlignment.isDefault);
             },
             enumerable: true,
             configurable: true
@@ -1842,12 +1935,15 @@ var BABYLON;
                 }
                 return this._padding;
             },
+            set: function (value) {
+                this.padding.copyFrom(value);
+            },
             enumerable: true,
             configurable: true
         });
         Object.defineProperty(Prim2DBase.prototype, "_hasPadding", {
             get: function () {
-                return this._padding !== null;
+                return this._padding !== null && !this._padding.isDefault;
             },
             enumerable: true,
             configurable: true
@@ -1860,6 +1956,19 @@ var BABYLON;
                 }
                 return this._marginAlignment;
             },
+            set: function (value) {
+                this.marginAlignment.copyFrom(value);
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Prim2DBase.prototype, "_hasMarginAlignment", {
+            /**
+             * Check if there a marginAlignment specified (non null and not default)
+             */
+            get: function () {
+                return (this._marginAlignment !== null && !this._marginAlignment.isDefault);
+            },
             enumerable: true,
             configurable: true
         });
@@ -2708,12 +2817,13 @@ var BABYLON;
             // We know have to :
             //  1. Determine the PaddingArea and the ActualPosition based on the margin/marginAlignment properties, which will also set the size property of the primitive
             //  2. Determine the contentArea based on the padding property.
+            var isSizeAuto = this.isSizeAuto;
             // Auto Create PaddingArea if there's no actualSize on width&|height to allocate the whole content available to the paddingArea where the actualSize is null
-            if (!this._hasMargin && (this.actualSize.width == null || this.actualSize.height == null)) {
-                if (this.actualSize.width == null) {
+            if (!this._hasMarginAlignment && (isSizeAuto || (this.actualSize.width == null || this.actualSize.height == null))) {
+                if (isSizeAuto || this.actualSize.width == null) {
                     this.marginAlignment.horizontal = PrimitiveAlignment.AlignStretch;
                 }
-                if (this.actualSize.height == null) {
+                if (isSizeAuto || this.actualSize.height == null) {
                     this.marginAlignment.vertical = PrimitiveAlignment.AlignStretch;
                 }
             }
@@ -2722,7 +2832,6 @@ var BABYLON;
                 this.margin.computeWithAlignment(this.layoutArea, this.size || this.actualSize, this.marginAlignment, this._marginOffset, Prim2DBase._size);
                 this.actualSize = Prim2DBase._size.clone();
             }
-            var isSizeAuto = this.isSizeAuto;
             if (this._hasPadding) {
                 // Two cases from here: the size of the Primitive is Auto, its content can't be shrink, so me resize the primitive itself
                 if (isSizeAuto) {
@@ -2966,7 +3075,7 @@ var BABYLON;
         Prim2DBase.prototype._getActualSizeFromContentToRef = function (primSize, newPrimSize) {
             newPrimSize.copyFrom(primSize);
         };
-        Prim2DBase.PRIM2DBASE_PROPCOUNT = 16;
+        Prim2DBase.PRIM2DBASE_PROPCOUNT = 24;
         Prim2DBase._bigInt = Math.pow(2, 30);
         Prim2DBase._nullPosition = BABYLON.Vector2.Zero();
         Prim2DBase.boundinbBoxReentrency = false;
@@ -2989,49 +3098,76 @@ var BABYLON;
             BABYLON.instanceLevelProperty(1, function (pi) { return Prim2DBase.actualPositionProperty = pi; }, false, false, true)
         ], Prim2DBase.prototype, "actualPosition", null);
         __decorate([
-            BABYLON.dynamicLevelProperty(2, function (pi) { return Prim2DBase.positionProperty = pi; }, false, false, true)
+            BABYLON.instanceLevelProperty(BABYLON.SmartPropertyPrim.SMARTPROPERTYPRIM_PROPCOUNT + 1, function (pi) { return Prim2DBase.actualXProperty = pi; }, false, false, true)
+        ], Prim2DBase.prototype, "actualX", null);
+        __decorate([
+            BABYLON.instanceLevelProperty(BABYLON.SmartPropertyPrim.SMARTPROPERTYPRIM_PROPCOUNT + 2, function (pi) { return Prim2DBase.actualYProperty = pi; }, false, false, true)
+        ], Prim2DBase.prototype, "actualY", null);
+        __decorate([
+            BABYLON.dynamicLevelProperty(BABYLON.SmartPropertyPrim.SMARTPROPERTYPRIM_PROPCOUNT + 3, function (pi) { return Prim2DBase.positionProperty = pi; }, false, false, true)
         ], Prim2DBase.prototype, "position", null);
         __decorate([
-            BABYLON.dynamicLevelProperty(3, function (pi) { return Prim2DBase.sizeProperty = pi; }, false, true)
+            BABYLON.dynamicLevelProperty(BABYLON.SmartPropertyPrim.SMARTPROPERTYPRIM_PROPCOUNT + 4, function (pi) { return Prim2DBase.xProperty = pi; }, false, false, true)
+        ], Prim2DBase.prototype, "x", null);
+        __decorate([
+            BABYLON.dynamicLevelProperty(BABYLON.SmartPropertyPrim.SMARTPROPERTYPRIM_PROPCOUNT + 5, function (pi) { return Prim2DBase.yProperty = pi; }, false, false, true)
+        ], Prim2DBase.prototype, "y", null);
+        __decorate([
+            BABYLON.dynamicLevelProperty(BABYLON.SmartPropertyPrim.SMARTPROPERTYPRIM_PROPCOUNT + 6, function (pi) { return Prim2DBase.sizeProperty = pi; }, false, true)
         ], Prim2DBase.prototype, "size", null);
         __decorate([
-            BABYLON.instanceLevelProperty(4, function (pi) { return Prim2DBase.rotationProperty = pi; }, false, true)
+            BABYLON.dynamicLevelProperty(BABYLON.SmartPropertyPrim.SMARTPROPERTYPRIM_PROPCOUNT + 7, function (pi) { return Prim2DBase.widthProperty = pi; }, false, true)
+        ], Prim2DBase.prototype, "width", null);
+        __decorate([
+            BABYLON.dynamicLevelProperty(BABYLON.SmartPropertyPrim.SMARTPROPERTYPRIM_PROPCOUNT + 8, function (pi) { return Prim2DBase.heightProperty = pi; }, false, true)
+        ], Prim2DBase.prototype, "height", null);
+        __decorate([
+            BABYLON.instanceLevelProperty(BABYLON.SmartPropertyPrim.SMARTPROPERTYPRIM_PROPCOUNT + 9, function (pi) { return Prim2DBase.rotationProperty = pi; }, false, true)
         ], Prim2DBase.prototype, "rotation", null);
         __decorate([
-            BABYLON.instanceLevelProperty(5, function (pi) { return Prim2DBase.scaleProperty = pi; }, false, true)
+            BABYLON.instanceLevelProperty(BABYLON.SmartPropertyPrim.SMARTPROPERTYPRIM_PROPCOUNT + 10, function (pi) { return Prim2DBase.scaleProperty = pi; }, false, true)
         ], Prim2DBase.prototype, "scale", null);
         __decorate([
-            BABYLON.dynamicLevelProperty(6, function (pi) { return Prim2DBase.originProperty = pi; }, false, true)
+            BABYLON.dynamicLevelProperty(BABYLON.SmartPropertyPrim.SMARTPROPERTYPRIM_PROPCOUNT + 11, function (pi) { return Prim2DBase.actualSizeProperty = pi; }, false, true)
+        ], Prim2DBase.prototype, "actualSize", null);
+        __decorate([
+            BABYLON.dynamicLevelProperty(BABYLON.SmartPropertyPrim.SMARTPROPERTYPRIM_PROPCOUNT + 12, function (pi) { return Prim2DBase.actualWidthProperty = pi; }, false, true)
+        ], Prim2DBase.prototype, "actualWidth", null);
+        __decorate([
+            BABYLON.dynamicLevelProperty(BABYLON.SmartPropertyPrim.SMARTPROPERTYPRIM_PROPCOUNT + 13, function (pi) { return Prim2DBase.actualHeightProperty = pi; }, false, true)
+        ], Prim2DBase.prototype, "actualHeight", null);
+        __decorate([
+            BABYLON.dynamicLevelProperty(BABYLON.SmartPropertyPrim.SMARTPROPERTYPRIM_PROPCOUNT + 14, function (pi) { return Prim2DBase.originProperty = pi; }, false, true)
         ], Prim2DBase.prototype, "origin", null);
         __decorate([
-            BABYLON.dynamicLevelProperty(7, function (pi) { return Prim2DBase.levelVisibleProperty = pi; })
+            BABYLON.dynamicLevelProperty(BABYLON.SmartPropertyPrim.SMARTPROPERTYPRIM_PROPCOUNT + 15, function (pi) { return Prim2DBase.levelVisibleProperty = pi; })
         ], Prim2DBase.prototype, "levelVisible", null);
         __decorate([
-            BABYLON.instanceLevelProperty(8, function (pi) { return Prim2DBase.isVisibleProperty = pi; })
+            BABYLON.instanceLevelProperty(BABYLON.SmartPropertyPrim.SMARTPROPERTYPRIM_PROPCOUNT + 16, function (pi) { return Prim2DBase.isVisibleProperty = pi; })
         ], Prim2DBase.prototype, "isVisible", null);
         __decorate([
-            BABYLON.instanceLevelProperty(9, function (pi) { return Prim2DBase.zOrderProperty = pi; })
+            BABYLON.instanceLevelProperty(BABYLON.SmartPropertyPrim.SMARTPROPERTYPRIM_PROPCOUNT + 17, function (pi) { return Prim2DBase.zOrderProperty = pi; })
         ], Prim2DBase.prototype, "zOrder", null);
         __decorate([
-            BABYLON.dynamicLevelProperty(10, function (pi) { return Prim2DBase.marginProperty = pi; })
+            BABYLON.dynamicLevelProperty(BABYLON.SmartPropertyPrim.SMARTPROPERTYPRIM_PROPCOUNT + 18, function (pi) { return Prim2DBase.marginProperty = pi; })
         ], Prim2DBase.prototype, "margin", null);
         __decorate([
-            BABYLON.dynamicLevelProperty(11, function (pi) { return Prim2DBase.paddingProperty = pi; })
+            BABYLON.dynamicLevelProperty(BABYLON.SmartPropertyPrim.SMARTPROPERTYPRIM_PROPCOUNT + 19, function (pi) { return Prim2DBase.paddingProperty = pi; })
         ], Prim2DBase.prototype, "padding", null);
         __decorate([
-            BABYLON.dynamicLevelProperty(12, function (pi) { return Prim2DBase.marginAlignmentProperty = pi; })
+            BABYLON.dynamicLevelProperty(BABYLON.SmartPropertyPrim.SMARTPROPERTYPRIM_PROPCOUNT + 20, function (pi) { return Prim2DBase.marginAlignmentProperty = pi; })
         ], Prim2DBase.prototype, "marginAlignment", null);
         __decorate([
-            BABYLON.instanceLevelProperty(13, function (pi) { return Prim2DBase.opacityProperty = pi; })
+            BABYLON.instanceLevelProperty(BABYLON.SmartPropertyPrim.SMARTPROPERTYPRIM_PROPCOUNT + 21, function (pi) { return Prim2DBase.opacityProperty = pi; })
         ], Prim2DBase.prototype, "opacity", null);
         __decorate([
-            BABYLON.instanceLevelProperty(14, function (pi) { return Prim2DBase.scaleXProperty = pi; }, false, true)
+            BABYLON.instanceLevelProperty(BABYLON.SmartPropertyPrim.SMARTPROPERTYPRIM_PROPCOUNT + 22, function (pi) { return Prim2DBase.scaleXProperty = pi; }, false, true)
         ], Prim2DBase.prototype, "scaleX", null);
         __decorate([
-            BABYLON.instanceLevelProperty(15, function (pi) { return Prim2DBase.scaleYProperty = pi; }, false, true)
+            BABYLON.instanceLevelProperty(BABYLON.SmartPropertyPrim.SMARTPROPERTYPRIM_PROPCOUNT + 23, function (pi) { return Prim2DBase.scaleYProperty = pi; }, false, true)
         ], Prim2DBase.prototype, "scaleY", null);
         Prim2DBase = __decorate([
-            BABYLON.className("Prim2DBase")
+            BABYLON.className("Prim2DBase", "BABYLON")
         ], Prim2DBase);
         return Prim2DBase;
     }(BABYLON.SmartPropertyPrim));

src/Canvas2d/babylon.prim2dBase.ts → Canvas2D/src/Engine/babylon.prim2dBase.ts


+ 1 - 1
src/Canvas2d/babylon.rectangle2d.js

@@ -445,7 +445,7 @@ var BABYLON;
             BABYLON.instanceLevelProperty(BABYLON.Shape2D.SHAPE2D_PROPCOUNT + 3, function (pi) { return Rectangle2D.roundRadiusProperty = pi; })
         ], Rectangle2D.prototype, "roundRadius", null);
         Rectangle2D = __decorate([
-            BABYLON.className("Rectangle2D")
+            BABYLON.className("Rectangle2D", "BABYLON")
         ], Rectangle2D);
         return Rectangle2D;
     }(BABYLON.Shape2D));

src/Canvas2d/babylon.rectangle2d.ts → Canvas2D/src/Engine/babylon.rectangle2d.ts


+ 1 - 1
src/Canvas2d/babylon.renderablePrim2d.js

@@ -885,7 +885,7 @@ var BABYLON;
             BABYLON.dynamicLevelProperty(BABYLON.Prim2DBase.PRIM2DBASE_PROPCOUNT + 1, function (pi) { return RenderablePrim2D.isTransparentProperty = pi; })
         ], RenderablePrim2D.prototype, "isTransparent", null);
         RenderablePrim2D = __decorate([
-            BABYLON.className("RenderablePrim2D")
+            BABYLON.className("RenderablePrim2D", "BABYLON")
         ], RenderablePrim2D);
         return RenderablePrim2D;
     }(BABYLON.Prim2DBase));

src/Canvas2d/babylon.renderablePrim2d.ts → Canvas2D/src/Engine/babylon.renderablePrim2d.ts


+ 1 - 1
src/Canvas2d/babylon.shape2d.js

@@ -182,7 +182,7 @@ var BABYLON;
             BABYLON.instanceLevelProperty(BABYLON.RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 3, function (pi) { return Shape2D.borderThicknessProperty = pi; })
         ], Shape2D.prototype, "borderThickness", null);
         Shape2D = __decorate([
-            BABYLON.className("Shape2D")
+            BABYLON.className("Shape2D", "BABYLON")
         ], Shape2D);
         return Shape2D;
     }(BABYLON.RenderablePrim2D));

src/Canvas2d/babylon.shape2d.ts → Canvas2D/src/Engine/babylon.shape2d.ts


Fichier diff supprimé car celui-ci est trop grand
+ 1146 - 0
Canvas2D/src/Engine/babylon.smartPropertyPrim.js


src/Canvas2d/babylon.smartPropertyPrim.ts → Canvas2D/src/Engine/babylon.smartPropertyPrim.ts


+ 1 - 1
src/Canvas2d/babylon.sprite2d.js

@@ -478,7 +478,7 @@ var BABYLON;
             BABYLON.instanceLevelProperty(BABYLON.RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 7, function (pi) { return Sprite2D.spriteScaleFactorProperty = pi; })
         ], Sprite2D.prototype, "spriteScaleFactor", null);
         Sprite2D = __decorate([
-            BABYLON.className("Sprite2D")
+            BABYLON.className("Sprite2D", "BABYLON")
         ], Sprite2D);
         return Sprite2D;
     }(BABYLON.RenderablePrim2D));

src/Canvas2d/babylon.sprite2d.ts → Canvas2D/src/Engine/babylon.sprite2d.ts


+ 12 - 2
src/Canvas2d/babylon.text2d.js

@@ -234,6 +234,9 @@ var BABYLON;
                 return this._text;
             },
             set: function (value) {
+                if (!value) {
+                    value = "";
+                }
                 this._text = value;
                 this._textSize = null; // A change of text will reset the TextSize which will be recomputed next time it's used
                 this._size = null;
@@ -257,6 +260,13 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(Text2D.prototype, "isSizeAuto", {
+            get: function () {
+                return false;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(Text2D.prototype, "actualSize", {
             /**
              * Get the actual size of the Text2D primitive
@@ -276,7 +286,7 @@ var BABYLON;
              */
             get: function () {
                 if (!this._textSize) {
-                    if (this.owner) {
+                    if (this.owner && this._text) {
                         var newSize = this.fontTexture.measureText(this._text, this._tabulationSize);
                         if (!newSize.equals(this._textSize)) {
                             this.onPrimitivePropertyDirty(BABYLON.Prim2DBase.sizeProperty.flagId);
@@ -455,7 +465,7 @@ var BABYLON;
             BABYLON.instanceLevelProperty(BABYLON.RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 4, function (pi) { return Text2D.sizeProperty = pi; })
         ], Text2D.prototype, "size", null);
         Text2D = __decorate([
-            BABYLON.className("Text2D")
+            BABYLON.className("Text2D", "BABYLON")
         ], Text2D);
         return Text2D;
     }(BABYLON.RenderablePrim2D));

src/Canvas2d/babylon.text2d.ts → Canvas2D/src/Engine/babylon.text2d.ts


src/Canvas2d/babylon.worldSpaceCanvas2dNode.js → Canvas2D/src/Engine/babylon.worldSpaceCanvas2dNode.js


src/Canvas2d/babylon.worldSpaceCanvas2dNode.ts → Canvas2D/src/Engine/babylon.worldSpaceCanvas2dNode.ts


src/GUI/babylon.gui.UIElement.ts → Canvas2D/src/GUI/babylon.gui.UIElement.ts


src/GUI/babylon.gui.button.ts → Canvas2D/src/GUI/babylon.gui.button.ts


src/GUI/babylon.gui.control.ts → Canvas2D/src/GUI/babylon.gui.control.ts


src/GUI/babylon.gui.label.ts → Canvas2D/src/GUI/babylon.gui.label.ts


src/GUI/babylon.gui.window.ts → Canvas2D/src/GUI/babylon.gui.window.ts


src/Shaders/ellipse2d.fragment.fx → Canvas2D/src/Shaders/ellipse2d.fragment.fx


src/Shaders/ellipse2d.vertex.fx → Canvas2D/src/Shaders/ellipse2d.vertex.fx


src/Shaders/lines2d.fragment.fx → Canvas2D/src/Shaders/lines2d.fragment.fx


src/Shaders/lines2d.vertex.fx → Canvas2D/src/Shaders/lines2d.vertex.fx


src/Shaders/rect2d.fragment.fx → Canvas2D/src/Shaders/rect2d.fragment.fx


src/Shaders/rect2d.vertex.fx → Canvas2D/src/Shaders/rect2d.vertex.fx


src/Shaders/sprite2d.fragment.fx → Canvas2D/src/Shaders/sprite2d.fragment.fx


src/Shaders/sprite2d.vertex.fx → Canvas2D/src/Shaders/sprite2d.vertex.fx


src/Shaders/text2d.fragment.fx → Canvas2D/src/Shaders/text2d.fragment.fx


src/Shaders/text2d.vertex.fx → Canvas2D/src/Shaders/text2d.vertex.fx


+ 642 - 0
Canvas2D/src/Tools/babylon.observable.ts

@@ -0,0 +1,642 @@
+module BABYLON {
+
+    /**
+     * The purpose of this class is to provide a base implementation of the IPropertyChanged interface for the user to avoid rewriting a code needlessly.
+     * Typical use of this class is to check for equality in a property set(), then call the onPropertyChanged method if values are different after the new value is set. The protected method will notify observers of the change.
+     * Remark: onPropertyChanged detects reentrant code and acts in a way to make sure everything is fine, fast and allocation friendly (when there no reentrant code which should be 99% of the time)
+     */
+    export abstract class PropertyChangedBase implements IPropertyChanged {
+
+        /**
+         * Protected method to call when there's a change of value in a property set
+         * @param propName the name of the concerned property
+         * @param oldValue its old value
+         * @param newValue its new value
+         * @param mask an optional observable mask
+         */
+        protected onPropertyChanged<T>(propName: string, oldValue: T, newValue: T, mask?: number) {
+            if (this.propertyChanged.hasObservers()) {
+
+                let pci = PropertyChangedBase.calling ? new PropertyChangedInfo() : PropertyChangedBase.pci;
+
+                pci.oldValue = oldValue;
+                pci.newValue = newValue;
+                pci.propertyName = propName;
+
+                try {
+                    PropertyChangedBase.calling = true;
+                    this.propertyChanged.notifyObservers(pci, mask);
+                } finally {
+                    PropertyChangedBase.calling = false;
+                }
+            }
+        }
+
+        /**
+         * An observable that is triggered when a property (using of the XXXXLevelProperty decorator) has its value changing.
+         * You can add an observer that will be triggered only for a given set of Properties using the Mask feature of the Observable and the corresponding Prim2DPropInfo.flagid value (e.g. Prim2DBase.positionProperty.flagid|Prim2DBase.rotationProperty.flagid to be notified only about position or rotation change)
+         */
+        public get propertyChanged(): Observable<PropertyChangedInfo> {
+            if (!this._propertyChanged) {
+                this._propertyChanged = new Observable<PropertyChangedInfo>();
+            }
+            return this._propertyChanged;
+        }
+
+        public _propertyChanged: Observable<PropertyChangedInfo> = null;
+        private static pci = new PropertyChangedInfo();
+        private static calling: boolean = false;
+    }
+
+    /**
+     * Class for the ObservableArray.onArrayChanged observable
+     */
+    export class ArrayChanged<T> {
+        constructor() {
+            this.action = 0;
+            this.newItems = new Array<{index: number, value: T }>();
+            this.removedItems = new Array<{ index: number, value: T }>();
+            this.changedItems = new Array<{ index: number, value: T }>();
+            this.newStartingIndex = -1;
+            this.removedStartingIndex = -1;
+        }
+
+        /**
+         * Contain the action that were made on the ObservableArray, it's one of the ArrayChanged.xxxAction members.
+         * Note the action's value can be used in the "mask" field of the Observable to only be notified about given action(s)
+         */
+        public action: number;
+
+        /**
+         * Only valid if the action is newItemsAction
+         */
+        public newItems: { index: number, value: T }[];
+
+        /**
+         * Only valid if the action is removedItemsAction
+         */
+        public removedItems: { index: number, value: T }[];
+
+        /**
+         * Only valid if the action is changedItemAction
+         */
+        public changedItems: { index: number, value: T }[];
+
+        /**
+         * Get the index of the first item inserted
+         */
+        public newStartingIndex: number;
+
+        /**
+         * Get the index of the first item removed
+         */
+        public removedStartingIndex: number;
+
+        /**
+         * Get the index of the first changed item
+         */
+        public changedStartingIndex: number;
+
+        /**
+         * The content of the array was totally cleared
+         */
+        public static get clearAction() {
+            return ArrayChanged._clearAction;
+        }
+
+        /**
+         * A new item was added, the newItems field contains the key/value pairs
+         */
+        public static get newItemsAction() {
+            return ArrayChanged._newItemsAction;
+        }
+
+        /**
+         * An existing item was removed, the removedKey field contains its key
+         */
+        public static get removedItemsAction() {
+            return ArrayChanged._removedItemsAction;
+        }
+
+        /**
+         * One or many items in the array were changed, the 
+         */
+        public static get changedItemAction() {
+            return ArrayChanged._changedItemAction;
+        }
+
+        /**
+         * The array's content was totally changed
+         * Depending on the method that used this mode the ChangedArray object may contains more information
+         */
+        public static get replacedArrayAction() {
+            return ArrayChanged._replacedArrayAction;
+        }
+
+        /**
+         * The length of the array changed
+         */
+        public static get lengthChangedAction() {
+            return ArrayChanged._lengthChangedAction;
+        }
+
+        private static _clearAction            = 0x1;
+        private static _newItemsAction         = 0x2;
+        private static _removedItemsAction     = 0x4;
+        private static _replacedArrayAction    = 0x8;
+        private static _lengthChangedAction    = 0x10;
+        private static _changedItemAction      = 0x20;
+
+        clear() {
+            this.action = 0;
+            this.newItems.splice(0);
+            this.removedItems.splice(0);
+            this.changedItems.splice(0);
+            this.removedStartingIndex = this.removedStartingIndex = this.changedStartingIndex = 0;
+        }
+    }
+
+    export class OAWatchedObjectChangedInfo<T> {
+        object: T;
+        propertyChanged: PropertyChangedInfo;
+    }
+
+    /**
+     * This class mimics the Javascript Array and TypeScript Array<T> classes, adding new features concerning the Observable pattern.
+     * 
+     */
+    export class ObservableArray<T> extends PropertyChangedBase {
+        /**
+         * Create an Observable Array.
+         * @param watchObjectsPropertyChange
+         * @param array and optional array that will be encapsulated by this ObservableArray instance. That's right, it's NOT a copy!
+         */
+        constructor(watchObjectsPropertyChange: boolean, array?: Array<T>) {
+            super();
+            this._array = (array!=null) ? array : new Array<T>();
+            this.dci = new ArrayChanged<T>();
+            this._callingArrayChanged = false;
+            this._arrayChanged = null;
+
+            this._callingWatchedObjectChanged = false;
+            this._watchObjectsPropertyChange = watchObjectsPropertyChange;
+            this._watchedObjectList = this._watchObjectsPropertyChange ? new StringDictionary<Observer<PropertyChangedInfo>>() : null;
+            this._woci = new OAWatchedObjectChangedInfo<T>();
+        }
+
+        /**
+          * Gets or sets the length of the array. This is a number one higher than the highest element defined in an array.
+          */
+        get length(): number {
+            return this._array.length;
+        }
+
+        set length(value: number) {
+            if (value === this._array.length) {
+                return;
+            }
+
+            let oldLength = this._array.length;
+            this._array.length = value;
+
+            this.onPropertyChanged("length", oldLength, this._array.length);
+        }
+
+        getAt(index: number): T {
+            return this._array[index];
+        }
+
+        setAt(index: number, value: T): boolean {
+            if (index < 0) {
+                return false;
+            }
+
+            let insertion = (index >= this._array.length) || this._array[index] === undefined;
+            let oldLength = 0;
+            if (insertion) {
+                oldLength = this._array.length;
+            } else if (this._watchObjectsPropertyChange) {
+                this._removeWatchedElement(this._array[index]);
+            }
+
+            this._array[index] = value;
+
+            if (this._watchObjectsPropertyChange) {
+                this._addWatchedElement(value);
+            }
+
+            if (insertion) {
+                this.onPropertyChanged("length", oldLength, this._array.length);
+            }
+
+            let ac = this.getArrayChangedObject();
+            if (ac) {
+                ac.action = insertion ? ArrayChanged.newItemsAction : ArrayChanged.changedItemAction;
+                if (insertion) {
+                    ac.newItems.splice(0, ac.newItems.length, { index: index, value: value });
+                    ac.newStartingIndex = index;
+                    ac.changedItems.splice(0);
+                } else {
+                    ac.newItems.splice(0);
+                    ac.changedStartingIndex = index;
+                    ac.changedItems.splice(0, ac.changedItems.length, { index: index, value: value });
+                }
+                ac.removedItems.splice(0);
+                ac.removedStartingIndex = -1;
+                this.callArrayChanged(ac);
+            }
+        }
+
+        /**
+          * Returns a string representation of an array.
+          */
+        toString(): string {
+            return this._array.toString();
+        }
+
+        toLocaleString(): string {
+            return this._array.toLocaleString();
+        }
+
+        /**
+          * Appends new elements to an array, and returns the new length of the array.
+          * @param items New elements of the Array.
+          */
+        push(...items: T[]): number {
+            let oldLength = this._array.length;
+            let n = this._array.push(...items);
+
+            if (this._watchObjectsPropertyChange) {
+                this._addWatchedElement(...items);
+            }
+
+            this.onPropertyChanged("length", oldLength, this._array.length);
+            let ac = this.getArrayChangedObject();
+            if (ac) {
+                ac.action = ArrayChanged.newItemsAction;
+                ac.newStartingIndex = oldLength;
+                this.feedNotifArray(ac.newItems, oldLength, ...items);
+                this.callArrayChanged(ac);
+            }
+
+            return n;
+        }
+
+        /**
+          * Removes the last element from an array and returns it.
+          */
+        pop(): T {
+            let firstRemove = this._array.length - 1;
+            let res = this._array.pop();
+
+            if (res && this._watchObjectsPropertyChange) {
+                this._removeWatchedElement(res);
+            }
+
+            if (firstRemove !== -1) {
+                this.onPropertyChanged("length", this._array.length + 1, this._array.length);
+
+                let ac = this.getArrayChangedObject();
+                if (ac) {
+                    ac.action = ArrayChanged.removedItemsAction;
+                    ac.removedStartingIndex = firstRemove;
+                    this.feedNotifArray(ac.removedItems, firstRemove, res);
+                }
+            }
+
+            return res;
+        }
+
+        /**
+          * Combines two or more arrays.
+          * @param items Additional items to add to the end of array1.
+          */
+        concat(...items: T[]): ObservableArray<T> {
+            return new ObservableArray<T>(this._watchObjectsPropertyChange, this._array.concat(...items));
+        }
+
+        /**
+          * Adds all the elements of an array separated by the specified separator string.
+          * @param separator A string used to separate one element of an array from the next in the resulting String. If omitted, the array elements are separated with a comma.
+          */
+        join(separator?: string): string {
+            return this._array.join(separator);
+        }
+
+        /**
+          * Reverses the elements in an Array.
+         * The arrayChanged action is 
+          */
+        reverse(): T[] {
+            let res = this._array.reverse();
+
+            let ac = this.getArrayChangedObject();
+            ac.action = ArrayChanged.replacedArrayAction;
+
+            return res;
+        }
+
+        /**
+          * Removes the first element from an array and returns it, shift all subsequents element one element before.
+         * The ArrayChange action is replacedArrayAction, the whole array changes and must be reevaluate as such, the removed element is in removedItems.
+         * 
+          */
+        shift(): T {
+            let oldLength = this._array.length;
+            let res = this._array.shift();
+
+            if (this._watchedObjectChanged && res!=null) {
+                this._removeWatchedElement(res);
+            }
+
+            if (oldLength !== 0) {
+                this.onPropertyChanged("length", oldLength, this._array.length);
+
+                let ac = this.getArrayChangedObject();
+                if (ac) {
+                    ac.action = ArrayChanged.replacedArrayAction;
+                    ac.removedItems.splice(0, ac.removedItems.length, { index: 0, value: res });
+                    ac.newItems.splice(0);
+                    ac.changedItems.splice(0);
+                    ac.removedStartingIndex = 0;
+                    this.callArrayChanged(ac);
+                }
+            }
+
+            return res;
+        }
+
+        /** 
+          * Returns a section of an array.
+          * @param start The beginning of the specified portion of the array.
+          * @param end The end of the specified portion of the array.
+          */
+        slice(start?: number, end?: number): ObservableArray<T> {
+            return new ObservableArray<T>(this._watchObjectsPropertyChange, this._array.slice(start, end));
+        }
+
+        /**
+          * Sorts an array.
+          * @param compareFn The name of the function used to determine the order of the elements. If omitted, the elements are sorted in ascending, ASCII character order.
+         * On the contrary of the Javascript Array's implementation, this method returns nothing
+          */
+        sort(compareFn?: (a: T, b: T) => number): void {
+            let oldLength = this._array.length;
+
+            this._array.sort(compareFn);
+
+            if (oldLength !== 0) {
+                let ac = this.getArrayChangedObject();
+                if (ac) {
+                    ac.clear();
+                    ac.action = ArrayChanged.replacedArrayAction;
+                    this.callArrayChanged(ac);
+                }
+            }
+        }
+
+        /**
+          * Removes elements from an array and, if necessary, inserts new elements in their place, returning the deleted elements.
+          * @param start The zero-based location in the array from which to start removing elements.
+          * @param deleteCount The number of elements to remove.
+          * @param items Elements to insert into the array in place of the deleted elements.
+          */
+        splice(start: number, deleteCount: number, ...items: T[]): T[] {
+            let oldLength = this._array.length;
+
+            if (this._watchObjectsPropertyChange) {
+                for (let i = start; i < start+deleteCount; i++) {
+                    let val = this._array[i];
+                    if (this._watchObjectsPropertyChange && val != null) {
+                        this._removeWatchedElement(val);
+                    }
+                }
+            }
+
+            let res = this._array.splice(start, deleteCount, ...items);
+
+            if (this._watchObjectsPropertyChange) {
+                this._addWatchedElement(...items);
+            }
+
+            if (oldLength !== this._array.length) {
+                this.onPropertyChanged("length", oldLength, this._array.length);
+            }
+
+            let ac = this.getArrayChangedObject();
+            if (ac) {
+                ac.clear();
+                ac.action = ArrayChanged.replacedArrayAction;
+                this.callArrayChanged(ac);
+            }
+
+            return res;
+        }
+
+        /**
+          * Inserts new elements at the start of an array.
+          * @param items  Elements to insert at the start of the Array.
+          * The ChangedArray action is replacedArrayAction, newItems contains the list of the added items
+          */
+        unshift(...items: T[]): number {
+            let oldLength = this._array.length;
+            
+            let res = this._array.unshift(...items);
+
+            if (this._watchObjectsPropertyChange) {
+                this._addWatchedElement(...items);
+            }
+
+            this.onPropertyChanged("length", oldLength, this._array.length);
+            let ac = this.getArrayChangedObject();
+            if (ac) {
+                ac.clear();
+                ac.action = ArrayChanged.replacedArrayAction;
+                ac.newStartingIndex = 0,
+                this.feedNotifArray(ac.newItems, 0, ...items);
+                this.callArrayChanged(ac);
+            }
+
+            return res;
+        }
+
+        /**
+          * Returns the index of the first occurrence of a value in an array.
+          * @param searchElement The value to locate in the array.
+          * @param fromIndex The array index at which to begin the search. If fromIndex is omitted, the search starts at index 0.
+          */
+        indexOf(searchElement: T, fromIndex?: number): number {
+            return this._array.indexOf(searchElement, fromIndex);
+        }
+
+        /**
+          * Returns the index of the last occurrence of a specified value in an array.
+          * @param searchElement The value to locate in the array.
+          * @param fromIndex The array index at which to begin the search. If fromIndex is omitted, the search starts at the last index in the array.
+          */
+        lastIndexOf(searchElement: T, fromIndex?: number): number {
+            return this._array.lastIndexOf(searchElement, fromIndex);
+        }
+
+        /**
+          * Determines whether all the members of an array satisfy the specified test.
+          * @param callbackfn A function that accepts up to three arguments. The every method calls the callbackfn function for each element in array1 until the callbackfn returns false, or until the end of the array.
+          * @param thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.
+          */
+        every(callbackfn: (value: T, index: number, array: T[]) => boolean, thisArg?: any): boolean {
+            return this._array.every(callbackfn, thisArg);
+        }
+
+        /**
+          * Determines whether the specified callback function returns true for any element of an array.
+          * @param callbackfn A function that accepts up to three arguments. The some method calls the callbackfn function for each element in array1 until the callbackfn returns true, or until the end of the array.
+          * @param thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.
+          */
+        some(callbackfn: (value: T, index: number, array: T[]) => boolean, thisArg?: any): boolean {
+            return this._array.some(callbackfn, thisArg);
+        }
+
+        /**
+          * Performs the specified action for each element in an array.
+          * @param callbackfn  A function that accepts up to three arguments. forEach calls the callbackfn function one time for each element in the array. 
+          * @param thisArg  An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.
+          */
+        forEach(callbackfn: (value: T, index: number, array: T[]) => void, thisArg?: any): void {
+            return this._array.forEach(callbackfn, thisArg);
+        }
+
+        /**
+          * Calls a defined callback function on each element of an array, and returns an array that contains the results.
+          * @param callbackfn A function that accepts up to three arguments. The map method calls the callbackfn function one time for each element in the array. 
+          * @param thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.
+          */
+        map<U>(callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): U[] {
+            return this._array.map(callbackfn, thisArg);
+        }
+
+        /**
+          * Returns the elements of an array that meet the condition specified in a callback function. 
+          * @param callbackfn A function that accepts up to three arguments. The filter method calls the callbackfn function one time for each element in the array. 
+          * @param thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.
+          */
+        filter(callbackfn: (value: T, index: number, array: T[]) => boolean, thisArg?: any): T[] {
+            return this._array.filter(callbackfn, thisArg);
+        }
+
+        /**
+          * Calls the specified callback function for all the elements in an array. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function.
+          * @param callbackfn A function that accepts up to four arguments. The reduce method calls the callbackfn function one time for each element in the array.
+          * @param initialValue If initialValue is specified, it is used as the initial value to start the accumulation. The first call to the callbackfn function provides this value as an argument instead of an array value.
+          */
+        reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue?: T): T {
+            return this._array.reduce(callbackfn);
+        }
+
+        /** 
+          * Calls the specified callback function for all the elements in an array, in descending order. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function.
+          * @param callbackfn A function that accepts up to four arguments. The reduceRight method calls the callbackfn function one time for each element in the array. 
+          * @param initialValue If initialValue is specified, it is used as the initial value to start the accumulation. The first call to the callbackfn function provides this value as an argument instead of an array value.
+          */
+        reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue?: T): T {
+            return this._array.reduceRight(callbackfn);
+        }
+
+        get arrayChanged(): Observable<ArrayChanged<T>> {
+            if (!this._arrayChanged) {
+                this._arrayChanged = new Observable<ArrayChanged<T>>();
+            }
+            return this._arrayChanged;
+        }
+
+        protected getArrayChangedObject(): ArrayChanged<T> {
+            if (this._arrayChanged && this._arrayChanged.hasObservers()) {
+                let ac = this._callingArrayChanged ? new ArrayChanged<T>() : this.dci;
+                return ac;
+            }
+            return null;
+        }
+
+        protected feedNotifArray(array: { index: number, value: T }[], startindIndex: number, ...items: T[]) {
+            array.splice(0);
+            for (let i = 0; i < items.length; i++) {
+                let value = this._array[i + startindIndex];
+                if (value !== undefined) {
+                    array.push({ index: i + startindIndex, value: value });
+                }
+            }
+        }
+
+        protected callArrayChanged(ac: ArrayChanged<T>) {
+            try {
+                this._callingArrayChanged = true;
+                this.arrayChanged.notifyObservers(ac, ac.action);
+            } finally {
+                this._callingArrayChanged = false;
+            }
+        }
+
+        get watchedObjectChanged(): Observable<OAWatchedObjectChangedInfo<T>> {
+            if (!this._watchedObjectChanged) {
+                this._watchedObjectChanged = new Observable<OAWatchedObjectChangedInfo<T>>();
+            }
+            return this._watchedObjectChanged;
+        }
+
+        private _addWatchedElement(...items: T[]) {
+            for (let curItem of items) {
+                if (curItem["propertyChanged"]) {
+                    let key = curItem["__ObsArrayObjID__"] as string;
+
+                    // The object may already be part of another ObsArray, so there already be a valid ID
+                    if (!key) {
+                        key = Tools.RandomId();
+                        curItem["__ObsArrayObjID__"] = key;
+                    }
+
+                    this._watchedObjectList.add(key, (<IPropertyChanged><any>curItem).propertyChanged.add((e, d) => {
+                        this.onWatchedObjectChanged(key, curItem, e);
+                    }));
+                }
+            }
+        }
+
+        private _removeWatchedElement(...items: T[]) {
+            for (let curItem of items) {
+                let key = curItem["__ObsArrayObjID__"] as string;
+                if (key != null) {
+                    let observer = this._watchedObjectList.getAndRemove(key);
+                    (<IPropertyChanged><any>curItem).propertyChanged.remove(observer);
+                }
+            }
+        }
+
+        protected onWatchedObjectChanged(key: string, object: T, propChanged: PropertyChangedInfo) {
+            if (this._watchedObjectChanged && this._watchedObjectChanged.hasObservers()) {
+
+                let woci = this._callingWatchedObjectChanged ? new OAWatchedObjectChangedInfo<T>() : this._woci;
+                woci.object = object;
+                woci.propertyChanged = propChanged;
+
+                try {
+                    this._callingWatchedObjectChanged = true;
+                    this.watchedObjectChanged.notifyObservers(woci);
+                } finally {
+                    this._callingWatchedObjectChanged = false;
+                }
+            }
+        }
+
+        private _array: Array<T>;
+
+        private _arrayChanged: Observable<ArrayChanged<T>>;
+        private dci = new ArrayChanged<T>();
+        private _callingArrayChanged: boolean = false;
+
+        private _watchedObjectChanged: Observable<OAWatchedObjectChangedInfo<T>>;
+        private _woci: OAWatchedObjectChangedInfo<T>;
+        private _callingWatchedObjectChanged: boolean;
+        private _watchObjectsPropertyChange: boolean;
+        private _watchedObjectList: StringDictionary<Observer<PropertyChangedInfo>>;
+
+    }
+}

+ 320 - 0
Canvas2D/src/Tools/babylon.stringDictionary.ts

@@ -0,0 +1,320 @@
+module BABYLON {
+
+    /**
+     * Class for the ObservableStringDictionary.onDictionaryChanged observable
+     */
+    export class DictionaryChanged<T> {
+        /**
+         * Contain the action that were made on the dictionary, it's one of the DictionaryChanged.xxxAction members.
+         * Note the action's value can be used in the "mask" field of the Observable to only be notified about given action(s)
+         */
+        public action: number;
+
+        /**
+         * Only valid if the action is newItemAction
+         */
+        public newItem: { key: string, value: T }
+
+        /**
+         * Only valid if the action is removedItemAction
+         */
+        public removedKey: string;
+
+        /**
+         * Only valid if the action is itemValueChangedAction
+         */
+        public changedItem: { key: string, oldValue: T, newValue: T }
+
+        /**
+         * The content of the dictionary was totally cleared
+         */
+        public static get clearAction() {
+            return DictionaryChanged._clearAction;
+        }
+
+        /**
+         * A new item was added, the newItem field contains the key/value pair
+         */
+        public static get newItemAction() {
+            return DictionaryChanged._newItemAction;
+        }
+
+        /**
+         * An existing item was removed, the removedKey field contains its key
+         */
+        public static get removedItemAction() {
+            return DictionaryChanged._removedItemAction;
+        }
+
+        /**
+         * An existing item had a value change, the changedItem field contains the key/value
+         */
+        public static get itemValueChangedAction() {
+            return DictionaryChanged._itemValueChangedAction;
+        }
+
+        /**
+         * The dictionary's content was reset and replaced by the content of another dictionary.
+         * DictionaryChanged<T> contains no further information about this action
+         */
+        public static get replacedAction() {
+            return DictionaryChanged._replacedAction;
+        }
+
+        private static _clearAction            = 0x1;
+        private static _newItemAction          = 0x2;
+        private static _removedItemAction      = 0x4;
+        private static _itemValueChangedAction = 0x8;
+        private static _replacedAction         = 0x10;
+    }
+
+    export class OSDWatchedObjectChangedInfo<T> {
+        key: string;
+        object: T;
+        propertyChanged: PropertyChangedInfo;
+    }
+
+    export class ObservableStringDictionary<T> extends StringDictionary<T> implements IPropertyChanged {
+
+        constructor(watchObjectsPropertyChange: boolean) {
+            super();
+
+            this._propertyChanged = null;
+            this._dictionaryChanged = null;
+            this.dci = new DictionaryChanged<T>();
+            this._callingDicChanged = false;
+            this._watchedObjectChanged = null;
+            this._callingWatchedObjectChanged = false;
+            this._woci = new OSDWatchedObjectChangedInfo<T>();
+            this._watchObjectsPropertyChange = watchObjectsPropertyChange;
+            this._watchedObjectList = this._watchObjectsPropertyChange ? new StringDictionary<Observer<PropertyChangedInfo>>() : null;
+        }
+
+        /**
+         * This will clear this dictionary and copy the content from the 'source' one.
+         * If the T value is a custom object, it won't be copied/cloned, the same object will be used
+         * @param source the dictionary to take the content from and copy to this dictionary
+         */
+        public copyFrom(source: StringDictionary<T>) {
+            let oldCount = this.count;
+            // Don't rely on this class' implementation for clear/add otherwise tons of notification will be thrown
+            super.clear();
+            source.forEach((t, v) => this._add(t, v, false, this._watchObjectsPropertyChange));
+            this.onDictionaryChanged(DictionaryChanged.replacedAction, null, null, null);
+            this.onPropertyChanged("count", oldCount, this.count);
+        }
+
+        /**
+         * Get a value from its key or add it if it doesn't exist.
+         * This method will ensure you that a given key/data will be present in the dictionary.
+         * @param key the given key to get the matching value from
+         * @param factory the factory that will create the value if the key is not present in the dictionary.
+         * The factory will only be invoked if there's no data for the given key.
+         * @return the value corresponding to the key.
+         */
+        public getOrAddWithFactory(key: string, factory: (key: string) => T): T {
+            let val = super.getOrAddWithFactory(key, k => {
+                let v = factory(key);
+                this._add(key, v, true, this._watchObjectsPropertyChange);
+                return v;
+            });
+
+            return val;
+        }
+
+        /**
+         * Add a new key and its corresponding value
+         * @param key the key to add
+         * @param value the value corresponding to the key
+         * @return true if the operation completed successfully, false if we couldn't insert the key/value because there was already this key in the dictionary
+         */
+        public add(key: string, value: T): boolean {
+            return this._add(key, value, true, true);
+        }
+
+        public getAndRemove(key: string): T {
+            let val = super.get(key);
+            this._remove(key, true, val);
+            return val;
+        }
+
+        private _add(key: string, value: T, fireNotif: boolean, registerWatcher: boolean): boolean {
+            if (super.add(key, value)) {
+                if (fireNotif) {
+                    this.onDictionaryChanged(DictionaryChanged.newItemAction, { key: key, value: value }, null, null);
+                    this.onPropertyChanged("count", this.count - 1, this.count);
+                }
+                if (registerWatcher) {
+                    this._addWatchedElement(key, value);
+                }
+                return true;
+            }
+            return false;
+        }
+
+        private _addWatchedElement(key: string, el: T) {
+            if (el["propertyChanged"]) {
+                this._watchedObjectList.add(key, (<IPropertyChanged><any>el).propertyChanged.add((e, d) => {
+                    this.onWatchedObjectChanged(key, el, e);
+                }));
+            }            
+        }
+
+        private _removeWatchedElement(key: string, el: T) {
+            let observer = this._watchedObjectList.getAndRemove(key);
+            (<IPropertyChanged><any>el).propertyChanged.remove(observer);
+        }
+
+        public set(key: string, value: T): boolean {
+            let oldValue = this.get(key);
+            if (this._watchObjectsPropertyChange) {
+                this._removeWatchedElement(key, oldValue);
+            }
+
+            if (super.set(key, value)) {
+                this.onDictionaryChanged(DictionaryChanged.itemValueChangedAction, null, null, { key: key, oldValue: oldValue, newValue: value });
+                this._addWatchedElement(key, value);
+                return true;
+            }
+
+            return false;
+        }
+
+        /**
+         * Remove a key/value from the dictionary.
+         * @param key the key to remove
+         * @return true if the item was successfully deleted, false if no item with such key exist in the dictionary
+         */
+        public remove(key: string): boolean {
+            return this._remove(key, true);
+        }
+
+        private _remove(key: string, fireNotif: boolean, element?: T): boolean {
+            if (!element) {
+                element = this.get(key);
+            }
+
+            if (!element) {
+                return false;
+            }
+
+            if (super.remove(key) === undefined) {
+                return false;
+            }
+
+            this.onDictionaryChanged(DictionaryChanged.removedItemAction, null, key, null);
+            this.onPropertyChanged("count", this.count + 1, this.count);
+
+            if (this._watchObjectsPropertyChange) {
+                this._removeWatchedElement(key, element);
+            }
+
+            return true;
+        }
+
+        /**
+         * Clear the whole content of the dictionary
+         */
+        public clear() {
+            this._watchedObjectList.forEach((k, v) => {
+                let el = this.get(k);
+                this._removeWatchedElement(k, el);
+            });
+            this._watchedObjectList.clear();
+
+            let oldCount = this.count;
+            super.clear();
+            this.onDictionaryChanged(DictionaryChanged.clearAction, null, null, null);
+            this.onPropertyChanged("count", oldCount, 0);
+        }
+
+        get propertyChanged(): Observable<PropertyChangedInfo> {
+            if (!this._propertyChanged) {
+                this._propertyChanged = new Observable<PropertyChangedInfo>();
+            }
+            return this._propertyChanged;
+        }
+
+        protected onPropertyChanged<T>(propName: string, oldValue: T, newValue: T, mask?: number) {
+            if (this._propertyChanged && this._propertyChanged.hasObservers()) {
+
+                let pci = ObservableStringDictionary.callingPropChanged ? new PropertyChangedInfo() : ObservableStringDictionary.pci;
+
+                pci.oldValue = oldValue;
+                pci.newValue = newValue;
+                pci.propertyName = propName;
+
+                try {
+                    ObservableStringDictionary.callingPropChanged = true;
+                    this.propertyChanged.notifyObservers(pci, mask);
+                } finally {
+                    ObservableStringDictionary.callingPropChanged = false;
+                }
+            }
+        }
+
+        get dictionaryChanged(): Observable<DictionaryChanged<T>> {
+            if (!this._dictionaryChanged) {
+                this._dictionaryChanged = new Observable<DictionaryChanged<T>>();
+            }
+            return this._dictionaryChanged;
+        }
+
+        protected onDictionaryChanged(action: number, newItem: { key: string, value: T }, removedKey: string, changedItem: { key: string, oldValue: T, newValue: T }) {
+            if (this._dictionaryChanged && this._dictionaryChanged.hasObservers()) {
+
+                let dci = this._callingDicChanged ? new DictionaryChanged<T>() : this.dci;
+
+                dci.action = action;
+                dci.newItem = newItem;
+                dci.removedKey = removedKey;
+                dci.changedItem = changedItem;
+
+                try {
+                    this._callingDicChanged = true;
+                    this.dictionaryChanged.notifyObservers(dci, action);
+                } finally {
+                    this._callingDicChanged = false;
+                }
+            }
+        }
+
+        get watchedObjectChanged(): Observable<OSDWatchedObjectChangedInfo<T>> {
+            if (!this._watchedObjectChanged) {
+                this._watchedObjectChanged = new Observable<OSDWatchedObjectChangedInfo<T>>();
+            }
+            return this._watchedObjectChanged;
+        }
+
+        protected onWatchedObjectChanged(key: string, object: T, propChanged: PropertyChangedInfo) {
+            if (this._watchedObjectChanged && this._watchedObjectChanged.hasObservers()) {
+
+                let woci = this._callingWatchedObjectChanged ? new OSDWatchedObjectChangedInfo<T>() : this._woci;
+                woci.key = key;
+                woci.object = object;
+                woci.propertyChanged = propChanged;
+
+                try {
+                    this._callingWatchedObjectChanged = true;
+                    this.watchedObjectChanged.notifyObservers(woci);
+                } finally {
+                    this._callingWatchedObjectChanged = false;
+                }
+            }
+        }
+
+        private _propertyChanged: Observable<PropertyChangedInfo>;
+        private static pci = new PropertyChangedInfo();
+        private static callingPropChanged: boolean = false;
+
+        private _dictionaryChanged: Observable<DictionaryChanged<T>>;
+        private dci: DictionaryChanged<T>;
+        private _callingDicChanged: boolean;
+
+        private _watchedObjectChanged: Observable<OSDWatchedObjectChangedInfo<T>>;
+        private _woci: OSDWatchedObjectChangedInfo<T>;
+        private _callingWatchedObjectChanged: boolean;
+        private _watchObjectsPropertyChange: boolean;
+        private _watchedObjectList: StringDictionary<Observer<PropertyChangedInfo>>;
+    }
+}

+ 0 - 16
Tools/Gulp/config.json

@@ -159,22 +159,6 @@
       "../../src/Tools/babylon.dynamicFloatArray.js",
       "../../src/Materials/Textures/babylon.fontTexture.js",
       "../../src/Materials/Textures/babylon.mapTexture.js",
-      "../../src/Canvas2d/babylon.bounding2d.js",
-      "../../src/Canvas2d/babylon.canvas2dLayoutEngine.js",
-      "../../src/Canvas2d/babylon.brushes2d.js",
-      "../../src/Canvas2d/babylon.smartPropertyPrim.js",
-      "../../src/Canvas2d/babylon.prim2dBase.js",
-      "../../src/Canvas2d/babylon.modelRenderCache.js",
-      "../../src/Canvas2d/babylon.renderablePrim2d.js",
-      "../../src/Canvas2d/babylon.shape2d.js",
-      "../../src/Canvas2d/babylon.group2d.js",
-      "../../src/Canvas2d/babylon.rectangle2d.js",
-      "../../src/Canvas2d/babylon.ellipse2d.js",
-      "../../src/Canvas2d/babylon.sprite2d.js",
-      "../../src/Canvas2d/babylon.text2d.js",
-      "../../src/Canvas2d/babylon.lines2d.js",
-      "../../src/Canvas2d/babylon.canvas2d.js",
-      "../../src/Canvas2d/babylon.worldSpaceCanvas2dNode.js",
       "../../src/Materials/babylon.shaderMaterial.js",
       "../../src/Tools/babylon.tools.dds.js",
       "../../src/Physics/Plugins/babylon.cannonJSPlugin.js",

+ 0 - 584
src/Canvas2d/babylon.smartPropertyPrim.js

@@ -1,584 +0,0 @@
-var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
-    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
-    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
-    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
-    return c > 3 && r && Object.defineProperty(target, key, r), r;
-};
-var BABYLON;
-(function (BABYLON) {
-    var Prim2DClassInfo = (function () {
-        function Prim2DClassInfo() {
-        }
-        return Prim2DClassInfo;
-    }());
-    BABYLON.Prim2DClassInfo = Prim2DClassInfo;
-    var Prim2DPropInfo = (function () {
-        function Prim2DPropInfo() {
-        }
-        Prim2DPropInfo.PROPKIND_MODEL = 1;
-        Prim2DPropInfo.PROPKIND_INSTANCE = 2;
-        Prim2DPropInfo.PROPKIND_DYNAMIC = 3;
-        return Prim2DPropInfo;
-    }());
-    BABYLON.Prim2DPropInfo = Prim2DPropInfo;
-    /**
-     * Custom type of the propertyChanged observable
-     */
-    var PropertyChangedInfo = (function () {
-        function PropertyChangedInfo() {
-        }
-        return PropertyChangedInfo;
-    }());
-    BABYLON.PropertyChangedInfo = PropertyChangedInfo;
-    var ClassTreeInfo = (function () {
-        function ClassTreeInfo(baseClass, type, classContentFactory) {
-            this._baseClass = baseClass;
-            this._type = type;
-            this._subClasses = new Array();
-            this._levelContent = new BABYLON.StringDictionary();
-            this._classContentFactory = classContentFactory;
-        }
-        Object.defineProperty(ClassTreeInfo.prototype, "classContent", {
-            get: function () {
-                if (!this._classContent) {
-                    this._classContent = this._classContentFactory(this._baseClass ? this._baseClass.classContent : null);
-                }
-                return this._classContent;
-            },
-            enumerable: true,
-            configurable: true
-        });
-        Object.defineProperty(ClassTreeInfo.prototype, "type", {
-            get: function () {
-                return this._type;
-            },
-            enumerable: true,
-            configurable: true
-        });
-        Object.defineProperty(ClassTreeInfo.prototype, "levelContent", {
-            get: function () {
-                return this._levelContent;
-            },
-            enumerable: true,
-            configurable: true
-        });
-        Object.defineProperty(ClassTreeInfo.prototype, "fullContent", {
-            get: function () {
-                if (!this._fullContent) {
-                    var dic_1 = new BABYLON.StringDictionary();
-                    var curLevel = this;
-                    while (curLevel) {
-                        curLevel.levelContent.forEach(function (k, v) { return dic_1.add(k, v); });
-                        curLevel = curLevel._baseClass;
-                    }
-                    this._fullContent = dic_1;
-                }
-                return this._fullContent;
-            },
-            enumerable: true,
-            configurable: true
-        });
-        ClassTreeInfo.prototype.getLevelOf = function (type) {
-            // Are we already there?
-            if (type === this._type) {
-                return this;
-            }
-            var baseProto = Object.getPrototypeOf(type);
-            var curProtoContent = this.getOrAddType(Object.getPrototypeOf(baseProto), baseProto);
-            if (!curProtoContent) {
-                this.getLevelOf(baseProto);
-            }
-            return this.getOrAddType(baseProto, type);
-        };
-        ClassTreeInfo.prototype.getOrAddType = function (baseType, type) {
-            // Are we at the level corresponding to the baseType?
-            // If so, get or add the level we're looking for
-            if (baseType === this._type) {
-                for (var _i = 0, _a = this._subClasses; _i < _a.length; _i++) {
-                    var subType = _a[_i];
-                    if (subType.type === type) {
-                        return subType.node;
-                    }
-                }
-                var node = new ClassTreeInfo(this, type, this._classContentFactory);
-                var info = { type: type, node: node };
-                this._subClasses.push(info);
-                return info.node;
-            }
-            // Recurse down to keep looking for the node corresponding to the baseTypeName
-            for (var _b = 0, _c = this._subClasses; _b < _c.length; _b++) {
-                var subType = _c[_b];
-                var info = subType.node.getOrAddType(baseType, type);
-                if (info) {
-                    return info;
-                }
-            }
-            return null;
-        };
-        ClassTreeInfo.get = function (type) {
-            var dic = type["__classTreeInfo"];
-            if (!dic) {
-                return null;
-            }
-            return dic.getLevelOf(type);
-        };
-        ClassTreeInfo.getOrRegister = function (type, classContentFactory) {
-            var dic = type["__classTreeInfo"];
-            if (!dic) {
-                dic = new ClassTreeInfo(null, type, classContentFactory);
-                type["__classTreeInfo"] = dic;
-            }
-            return dic;
-        };
-        return ClassTreeInfo;
-    }());
-    BABYLON.ClassTreeInfo = ClassTreeInfo;
-    var SmartPropertyPrim = (function () {
-        function SmartPropertyPrim() {
-            this._flags = 0;
-            this._modelKey = null;
-            this._instanceDirtyFlags = 0;
-            this._levelBoundingInfo = new BABYLON.BoundingInfo2D();
-            this.animations = new Array();
-        }
-        Object.defineProperty(SmartPropertyPrim.prototype, "isDisposed", {
-            /**
-             * Check if the object is disposed or not.
-             * @returns true if the object is dispose, false otherwise.
-             */
-            get: function () {
-                return this._isFlagSet(SmartPropertyPrim.flagIsDisposed);
-            },
-            enumerable: true,
-            configurable: true
-        });
-        /**
-         * Disposable pattern, this method must be overloaded by derived types in order to clean up hardware related resources.
-         * @returns false if the object is already dispose, true otherwise. Your implementation must call super.dispose() and check for a false return and return immediately if it's the case.
-         */
-        SmartPropertyPrim.prototype.dispose = function () {
-            if (this.isDisposed) {
-                return false;
-            }
-            // Don't set to null, it may upset somebody...
-            this.animations.splice(0);
-            this._setFlags(SmartPropertyPrim.flagIsDisposed);
-            return true;
-        };
-        /**
-         * Returns as a new array populated with the Animatable used by the primitive. Must be overloaded by derived primitives.
-         * Look at Sprite2D for more information
-         */
-        SmartPropertyPrim.prototype.getAnimatables = function () {
-            return new Array();
-        };
-        Object.defineProperty(SmartPropertyPrim.prototype, "modelKey", {
-            /**
-             * Property giving the Model Key associated to the property.
-             * This value is constructed from the type of the primitive and all the name/value of its properties declared with the modelLevelProperty decorator
-             * @returns the model key string.
-             */
-            get: function () {
-                var _this = this;
-                // No need to compute it?
-                if (!this._isFlagSet(SmartPropertyPrim.flagModelDirty) && this._modelKey) {
-                    return this._modelKey;
-                }
-                var modelKey = "Class:" + BABYLON.Tools.getClassName(this) + ";";
-                var propDic = this.propDic;
-                propDic.forEach(function (k, v) {
-                    if (v.kind === Prim2DPropInfo.PROPKIND_MODEL) {
-                        var propVal = _this[v.name];
-                        // Special case, array, this WON'T WORK IN ALL CASES, all entries have to be of the same type and it must be a BJS well known one
-                        if (propVal && propVal.constructor === Array) {
-                            var firstVal = propVal[0];
-                            if (!firstVal) {
-                                propVal = 0;
-                            }
-                            else {
-                                propVal = BABYLON.Tools.hashCodeFromStream(BABYLON.Tools.arrayOrStringFeeder(propVal));
-                            }
-                        }
-                        modelKey += v.name + ":" + ((propVal != null) ? ((v.typeLevelCompare) ? BABYLON.Tools.getClassName(propVal) : propVal.toString()) : "[null]") + ";";
-                    }
-                });
-                this._clearFlags(SmartPropertyPrim.flagModelDirty);
-                this._modelKey = modelKey;
-                return modelKey;
-            },
-            enumerable: true,
-            configurable: true
-        });
-        Object.defineProperty(SmartPropertyPrim.prototype, "isDirty", {
-            /**
-             * States if the Primitive is dirty and should be rendered again next time.
-             * @returns true is dirty, false otherwise
-             */
-            get: function () {
-                return (this._instanceDirtyFlags !== 0) || this._areSomeFlagsSet(SmartPropertyPrim.flagModelDirty | SmartPropertyPrim.flagPositioningDirty | SmartPropertyPrim.flagLayoutDirty);
-            },
-            enumerable: true,
-            configurable: true
-        });
-        Object.defineProperty(SmartPropertyPrim.prototype, "propDic", {
-            /**
-             * Access the dictionary of properties metadata. Only properties decorated with XXXXLevelProperty are concerned
-             * @returns the dictionary, the key is the property name as declared in Javascript, the value is the metadata object
-             */
-            get: function () {
-                if (!this._propInfo) {
-                    var cti = ClassTreeInfo.get(Object.getPrototypeOf(this));
-                    if (!cti) {
-                        throw new Error("Can't access the propDic member in class definition, is this class SmartPropertyPrim based?");
-                    }
-                    this._propInfo = cti.fullContent;
-                }
-                return this._propInfo;
-            },
-            enumerable: true,
-            configurable: true
-        });
-        SmartPropertyPrim._createPropInfo = function (target, propName, propId, dirtyBoundingInfo, dirtyParentBoundingBox, typeLevelCompare, kind) {
-            var dic = ClassTreeInfo.getOrRegister(target, function () { return new Prim2DClassInfo(); });
-            var node = dic.getLevelOf(target);
-            var propInfo = node.levelContent.get(propId.toString());
-            if (propInfo) {
-                throw new Error("The ID " + propId + " is already taken by another property declaration named: " + propInfo.name);
-            }
-            // Create, setup and add the PropInfo object to our prop dictionary
-            propInfo = new Prim2DPropInfo();
-            propInfo.id = propId;
-            propInfo.flagId = Math.pow(2, propId);
-            propInfo.kind = kind;
-            propInfo.name = propName;
-            propInfo.dirtyBoundingInfo = dirtyBoundingInfo;
-            propInfo.dirtyParentBoundingInfo = dirtyParentBoundingBox;
-            propInfo.typeLevelCompare = typeLevelCompare;
-            node.levelContent.add(propName, propInfo);
-            return propInfo;
-        };
-        SmartPropertyPrim._checkUnchanged = function (curValue, newValue) {
-            // Nothing to nothing: nothing to do!
-            if ((curValue === null && newValue === null) || (curValue === undefined && newValue === undefined)) {
-                return true;
-            }
-            // Check value unchanged
-            if ((curValue != null) && (newValue != null)) {
-                if (typeof (curValue.equals) == "function") {
-                    if (curValue.equals(newValue)) {
-                        return true;
-                    }
-                }
-                else {
-                    if (curValue === newValue) {
-                        return true;
-                    }
-                }
-            }
-            return false;
-        };
-        SmartPropertyPrim.prototype._triggerPropertyChanged = function (propInfo, newValue) {
-            if (this.isDisposed) {
-                return;
-            }
-            if (!propInfo) {
-                return;
-            }
-            this._handlePropChanged(undefined, newValue, propInfo.name, propInfo, propInfo.typeLevelCompare);
-        };
-        SmartPropertyPrim.prototype._boundingBoxDirty = function () {
-            this._setFlags(SmartPropertyPrim.flagLevelBoundingInfoDirty);
-            // Escalate the dirty flag in the instance hierarchy, stop when a renderable group is found or at the end
-            if (this instanceof BABYLON.Prim2DBase) {
-                var curprim = this;
-                while (curprim) {
-                    curprim._setFlags(SmartPropertyPrim.flagBoundingInfoDirty);
-                    if (curprim.isSizeAuto) {
-                        curprim.onPrimitivePropertyDirty(BABYLON.Prim2DBase.sizeProperty.flagId);
-                        curprim._setFlags(SmartPropertyPrim.flagPositioningDirty);
-                    }
-                    if (curprim instanceof BABYLON.Group2D) {
-                        if (curprim.isRenderableGroup) {
-                            break;
-                        }
-                    }
-                    curprim = curprim.parent;
-                }
-            }
-        };
-        SmartPropertyPrim.prototype._handlePropChanged = function (curValue, newValue, propName, propInfo, typeLevelCompare) {
-            // If the property change also dirty the boundingInfo, update the boundingInfo dirty flags
-            if (propInfo.dirtyBoundingInfo) {
-                this._boundingBoxDirty();
-            }
-            else if (propInfo.dirtyParentBoundingInfo) {
-                var p = this._parent;
-                if (p != null) {
-                    p._boundingBoxDirty();
-                }
-            }
-            // Trigger property changed
-            var info = SmartPropertyPrim.propChangedInfo;
-            info.oldValue = curValue;
-            info.newValue = newValue;
-            info.propertyName = propName;
-            var propMask = propInfo.flagId;
-            this.propertyChanged.notifyObservers(info, propMask);
-            // If the property belong to a group, check if it's a cached one, and dirty its render sprite accordingly
-            if (this instanceof BABYLON.Group2D) {
-                this.handleGroupChanged(propInfo);
-            }
-            // Check for parent layout dirty
-            if (this instanceof BABYLON.Prim2DBase) {
-                var p = this._parent;
-                if (p != null && p.layoutEngine && (p.layoutEngine.layoutDirtyOnPropertyChangedMask & propInfo.flagId) !== 0) {
-                    p._setLayoutDirty();
-                }
-            }
-            // For type level compare, if there's a change of type it's a change of model, otherwise we issue an instance change
-            var instanceDirty = false;
-            if (typeLevelCompare && curValue != null && newValue != null) {
-                var cvProto = curValue.__proto__;
-                var nvProto = newValue.__proto__;
-                instanceDirty = (cvProto === nvProto);
-            }
-            // Set the dirty flags
-            if (!instanceDirty && (propInfo.kind === Prim2DPropInfo.PROPKIND_MODEL)) {
-                if (!this.isDirty) {
-                    this._setFlags(SmartPropertyPrim.flagModelDirty);
-                }
-            }
-            else if (instanceDirty || (propInfo.kind === Prim2DPropInfo.PROPKIND_INSTANCE) || (propInfo.kind === Prim2DPropInfo.PROPKIND_DYNAMIC)) {
-                this.onPrimitivePropertyDirty(propMask);
-            }
-        };
-        SmartPropertyPrim.prototype.onPrimitivePropertyDirty = function (propFlagId) {
-            this.onPrimBecomesDirty();
-            this._instanceDirtyFlags |= propFlagId;
-        };
-        SmartPropertyPrim.prototype.handleGroupChanged = function (prop) {
-        };
-        /**
-         * Check if a given set of properties are dirty or not.
-         * @param flags a ORed combination of Prim2DPropInfo.flagId values
-         * @return true if at least one property is dirty, false if none of them are.
-         */
-        SmartPropertyPrim.prototype.checkPropertiesDirty = function (flags) {
-            return (this._instanceDirtyFlags & flags) !== 0;
-        };
-        /**
-         * Clear a given set of properties.
-         * @param flags a ORed combination of Prim2DPropInfo.flagId values
-         * @return the new set of property still marked as dirty
-         */
-        SmartPropertyPrim.prototype.clearPropertiesDirty = function (flags) {
-            this._instanceDirtyFlags &= ~flags;
-            return this._instanceDirtyFlags;
-        };
-        SmartPropertyPrim.prototype._resetPropertiesDirty = function () {
-            this._instanceDirtyFlags = 0;
-            this._clearFlags(SmartPropertyPrim.flagPrimInDirtyList | SmartPropertyPrim.flagNeedRefresh);
-        };
-        Object.defineProperty(SmartPropertyPrim.prototype, "levelBoundingInfo", {
-            /**
-             * Retrieve the boundingInfo for this Primitive, computed based on the primitive itself and NOT its children
-             */
-            get: function () {
-                if (this._isFlagSet(SmartPropertyPrim.flagLevelBoundingInfoDirty)) {
-                    this.updateLevelBoundingInfo();
-                    this._clearFlags(SmartPropertyPrim.flagLevelBoundingInfoDirty);
-                }
-                return this._levelBoundingInfo;
-            },
-            enumerable: true,
-            configurable: true
-        });
-        /**
-         * This method must be overridden by a given Primitive implementation to compute its boundingInfo
-         */
-        SmartPropertyPrim.prototype.updateLevelBoundingInfo = function () {
-        };
-        /**
-         * Property method called when the Primitive becomes dirty
-         */
-        SmartPropertyPrim.prototype.onPrimBecomesDirty = function () {
-        };
-        SmartPropertyPrim._hookProperty = function (propId, piStore, typeLevelCompare, dirtyBoundingInfo, dirtyParentBoundingBox, kind) {
-            return function (target, propName, descriptor) {
-                var propInfo = SmartPropertyPrim._createPropInfo(target, propName, propId, dirtyBoundingInfo, dirtyParentBoundingBox, typeLevelCompare, kind);
-                if (piStore) {
-                    piStore(propInfo);
-                }
-                var getter = descriptor.get, setter = descriptor.set;
-                // Overload the property setter implementation to add our own logic
-                descriptor.set = function (val) {
-                    // check for disposed first, do nothing
-                    if (this.isDisposed) {
-                        return;
-                    }
-                    var curVal = getter.call(this);
-                    if (SmartPropertyPrim._checkUnchanged(curVal, val)) {
-                        return;
-                    }
-                    // Cast the object we're working one
-                    var prim = this;
-                    // Change the value
-                    setter.call(this, val);
-                    // Notify change, dirty flags update
-                    prim._handlePropChanged(curVal, val, propName, propInfo, typeLevelCompare);
-                };
-            };
-        };
-        /**
-         * Add an externally attached data from its key.
-         * This method call will fail and return false, if such key already exists.
-         * If you don't care and just want to get the data no matter what, use the more convenient getOrAddExternalDataWithFactory() method.
-         * @param key the unique key that identifies the data
-         * @param data the data object to associate to the key for this Engine instance
-         * @return true if no such key were already present and the data was added successfully, false otherwise
-         */
-        SmartPropertyPrim.prototype.addExternalData = function (key, data) {
-            if (!this._externalData) {
-                this._externalData = new BABYLON.StringDictionary();
-            }
-            return this._externalData.add(key, data);
-        };
-        /**
-         * Get an externally attached data from its key
-         * @param key the unique key that identifies the data
-         * @return the associated data, if present (can be null), or undefined if not present
-         */
-        SmartPropertyPrim.prototype.getExternalData = function (key) {
-            if (!this._externalData) {
-                return null;
-            }
-            return this._externalData.get(key);
-        };
-        /**
-         * Get an externally attached data from its key, create it using a factory if it's not already present
-         * @param key the unique key that identifies the data
-         * @param factory the factory that will be called to create the instance if and only if it doesn't exists
-         * @return the associated data, can be null if the factory returned null.
-         */
-        SmartPropertyPrim.prototype.getOrAddExternalDataWithFactory = function (key, factory) {
-            if (!this._externalData) {
-                this._externalData = new BABYLON.StringDictionary();
-            }
-            return this._externalData.getOrAddWithFactory(key, factory);
-        };
-        /**
-         * Remove an externally attached data from the Engine instance
-         * @param key the unique key that identifies the data
-         * @return true if the data was successfully removed, false if it doesn't exist
-         */
-        SmartPropertyPrim.prototype.removeExternalData = function (key) {
-            if (!this._externalData) {
-                return false;
-            }
-            return this._externalData.remove(key);
-        };
-        /**
-         * Check if a given flag is set
-         * @param flag the flag value
-         * @return true if set, false otherwise
-         */
-        SmartPropertyPrim.prototype._isFlagSet = function (flag) {
-            return (this._flags & flag) !== 0;
-        };
-        /**
-         * Check if all given flags are set
-         * @param flags the flags ORed
-         * @return true if all the flags are set, false otherwise
-         */
-        SmartPropertyPrim.prototype._areAllFlagsSet = function (flags) {
-            return (this._flags & flags) === flags;
-        };
-        /**
-         * Check if at least one flag of the given flags is set
-         * @param flags the flags ORed
-         * @return true if at least one flag is set, false otherwise
-         */
-        SmartPropertyPrim.prototype._areSomeFlagsSet = function (flags) {
-            return (this._flags & flags) !== 0;
-        };
-        /**
-         * Clear the given flags
-         * @param flags the flags to clear
-         */
-        SmartPropertyPrim.prototype._clearFlags = function (flags) {
-            this._flags &= ~flags;
-        };
-        /**
-         * Set the given flags to true state
-         * @param flags the flags ORed to set
-         * @return the flags state before this call
-         */
-        SmartPropertyPrim.prototype._setFlags = function (flags) {
-            var cur = this._flags;
-            this._flags |= flags;
-            return cur;
-        };
-        /**
-         * Change the state of the given flags
-         * @param flags the flags ORed to change
-         * @param state true to set them, false to clear them
-         */
-        SmartPropertyPrim.prototype._changeFlags = function (flags, state) {
-            if (state) {
-                this._flags |= flags;
-            }
-            else {
-                this._flags &= ~flags;
-            }
-        };
-        SmartPropertyPrim.propChangedInfo = new PropertyChangedInfo();
-        SmartPropertyPrim.flagIsDisposed = 0x0000001; // set if the object is already disposed
-        SmartPropertyPrim.flagLevelBoundingInfoDirty = 0x0000002; // set if the primitive's level bounding box (not including children) is dirty
-        SmartPropertyPrim.flagModelDirty = 0x0000004; // set if the model must be changed
-        SmartPropertyPrim.flagLayoutDirty = 0x0000008; // set if the layout must be computed
-        SmartPropertyPrim.flagLevelVisible = 0x0000010; // set if the primitive is set as visible for its level only
-        SmartPropertyPrim.flagBoundingInfoDirty = 0x0000020; // set if the primitive's overall bounding box (including children) is dirty
-        SmartPropertyPrim.flagIsPickable = 0x0000040; // set if the primitive can be picked during interaction
-        SmartPropertyPrim.flagIsVisible = 0x0000080; // set if the primitive is concretely visible (use the levelVisible of parents)
-        SmartPropertyPrim.flagVisibilityChanged = 0x0000100; // set if there was a transition between visible/hidden status
-        SmartPropertyPrim.flagPositioningDirty = 0x0000200; // set if the primitive positioning must be computed
-        SmartPropertyPrim.flagTrackedGroup = 0x0000400; // set if the group2D is tracking a scene node
-        SmartPropertyPrim.flagWorldCacheChanged = 0x0000800; // set if the cached bitmap of a world space canvas changed
-        SmartPropertyPrim.flagChildrenFlatZOrder = 0x0001000; // set if all the children (direct and indirect) will share the same Z-Order
-        SmartPropertyPrim.flagZOrderDirty = 0x0002000; // set if the Z-Order for this prim and its children must be recomputed
-        SmartPropertyPrim.flagActualOpacityDirty = 0x0004000; // set if the actualOpactity should be recomputed
-        SmartPropertyPrim.flagPrimInDirtyList = 0x0008000; // set if the primitive is in the primDirtyList
-        SmartPropertyPrim.flagIsContainer = 0x0010000; // set if the primitive is a container
-        SmartPropertyPrim.flagNeedRefresh = 0x0020000; // set if the primitive wasn't successful at refresh
-        SmartPropertyPrim.flagActualScaleDirty = 0x0040000; // set if the actualScale property needs to be recomputed
-        SmartPropertyPrim.flagDontInheritParentScale = 0x0080000; // set if the actualScale must not use its parent's scale to be computed
-        SmartPropertyPrim.flagGlobalTransformDirty = 0x0100000; // set if the global transform must be recomputed due to a local transform change
-        SmartPropertyPrim.flagLayoutBoundingInfoDirty = 0x0100000; // set if the layout bounding info is dirty
-        SmartPropertyPrim = __decorate([
-            BABYLON.className("SmartPropertyPrim")
-        ], SmartPropertyPrim);
-        return SmartPropertyPrim;
-    }());
-    BABYLON.SmartPropertyPrim = SmartPropertyPrim;
-    function modelLevelProperty(propId, piStore, typeLevelCompare, dirtyBoundingInfo, dirtyParentBoundingBox) {
-        if (typeLevelCompare === void 0) { typeLevelCompare = false; }
-        if (dirtyBoundingInfo === void 0) { dirtyBoundingInfo = false; }
-        if (dirtyParentBoundingBox === void 0) { dirtyParentBoundingBox = false; }
-        return SmartPropertyPrim._hookProperty(propId, piStore, typeLevelCompare, dirtyBoundingInfo, dirtyParentBoundingBox, Prim2DPropInfo.PROPKIND_MODEL);
-    }
-    BABYLON.modelLevelProperty = modelLevelProperty;
-    function instanceLevelProperty(propId, piStore, typeLevelCompare, dirtyBoundingInfo, dirtyParentBoundingBox) {
-        if (typeLevelCompare === void 0) { typeLevelCompare = false; }
-        if (dirtyBoundingInfo === void 0) { dirtyBoundingInfo = false; }
-        if (dirtyParentBoundingBox === void 0) { dirtyParentBoundingBox = false; }
-        return SmartPropertyPrim._hookProperty(propId, piStore, typeLevelCompare, dirtyBoundingInfo, dirtyParentBoundingBox, Prim2DPropInfo.PROPKIND_INSTANCE);
-    }
-    BABYLON.instanceLevelProperty = instanceLevelProperty;
-    function dynamicLevelProperty(propId, piStore, typeLevelCompare, dirtyBoundingInfo, dirtyParentBoundingBox) {
-        if (typeLevelCompare === void 0) { typeLevelCompare = false; }
-        if (dirtyBoundingInfo === void 0) { dirtyBoundingInfo = false; }
-        if (dirtyParentBoundingBox === void 0) { dirtyParentBoundingBox = false; }
-        return SmartPropertyPrim._hookProperty(propId, piStore, typeLevelCompare, dirtyBoundingInfo, dirtyParentBoundingBox, Prim2DPropInfo.PROPKIND_DYNAMIC);
-    }
-    BABYLON.dynamicLevelProperty = dynamicLevelProperty;
-})(BABYLON || (BABYLON = {}));

+ 0 - 640
src/Tools/babylon.observable.ts

@@ -184,644 +184,4 @@
          */
         propertyChanged: Observable<PropertyChangedInfo>;
     }
-
-    /**
-     * The purpose of this class is to provide a base implementation of the IPropertyChanged interface for the user to avoid rewriting a code needlessly.
-     * Typical use of this class is to check for equality in a property set(), then call the onPropertyChanged method if values are different after the new value is set. The protected method will notify observers of the change.
-     * Remark: onPropertyChanged detects reentrant code and acts in a way to make sure everything is fine, fast and allocation friendly (when there no reentrant code which should be 99% of the time)
-     */
-    export abstract class PropertyChangedBase implements IPropertyChanged {
-
-        /**
-         * Protected method to call when there's a change of value in a property set
-         * @param propName the name of the concerned property
-         * @param oldValue its old value
-         * @param newValue its new value
-         * @param mask an optional observable mask
-         */
-        protected onPropertyChanged<T>(propName: string, oldValue: T, newValue: T, mask?: number) {
-            if (this.propertyChanged.hasObservers()) {
-
-                let pci = PropertyChangedBase.calling ? new PropertyChangedInfo() : PropertyChangedBase.pci;
-
-                pci.oldValue = oldValue;
-                pci.newValue = newValue;
-                pci.propertyName = propName;
-
-                try {
-                    PropertyChangedBase.calling = true;
-                    this.propertyChanged.notifyObservers(pci, mask);
-                } finally {
-                    PropertyChangedBase.calling = false;
-                }
-            }
-        }
-
-        /**
-         * An observable that is triggered when a property (using of the XXXXLevelProperty decorator) has its value changing.
-         * You can add an observer that will be triggered only for a given set of Properties using the Mask feature of the Observable and the corresponding Prim2DPropInfo.flagid value (e.g. Prim2DBase.positionProperty.flagid|Prim2DBase.rotationProperty.flagid to be notified only about position or rotation change)
-         */
-        public get propertyChanged(): Observable<PropertyChangedInfo> {
-            if (!this._propertyChanged) {
-                this._propertyChanged = new Observable<PropertyChangedInfo>();
-            }
-            return this._propertyChanged;
-        }
-
-        public _propertyChanged: Observable<PropertyChangedInfo> = null;
-        private static pci = new PropertyChangedInfo();
-        private static calling: boolean = false;
-    }
-
-    /**
-     * Class for the ObservableArray.onArrayChanged observable
-     */
-    export class ArrayChanged<T> {
-        constructor() {
-            this.action = 0;
-            this.newItems = new Array<{index: number, value: T }>();
-            this.removedItems = new Array<{ index: number, value: T }>();
-            this.changedItems = new Array<{ index: number, value: T }>();
-            this.newStartingIndex = -1;
-            this.removedStartingIndex = -1;
-        }
-
-        /**
-         * Contain the action that were made on the ObservableArray, it's one of the ArrayChanged.xxxAction members.
-         * Note the action's value can be used in the "mask" field of the Observable to only be notified about given action(s)
-         */
-        public action: number;
-
-        /**
-         * Only valid if the action is newItemsAction
-         */
-        public newItems: { index: number, value: T }[];
-
-        /**
-         * Only valid if the action is removedItemsAction
-         */
-        public removedItems: { index: number, value: T }[];
-
-        /**
-         * Only valid if the action is changedItemAction
-         */
-        public changedItems: { index: number, value: T }[];
-
-        /**
-         * Get the index of the first item inserted
-         */
-        public newStartingIndex: number;
-
-        /**
-         * Get the index of the first item removed
-         */
-        public removedStartingIndex: number;
-
-        /**
-         * Get the index of the first changed item
-         */
-        public changedStartingIndex: number;
-
-        /**
-         * The content of the array was totally cleared
-         */
-        public static get clearAction() {
-            return ArrayChanged._clearAction;
-        }
-
-        /**
-         * A new item was added, the newItems field contains the key/value pairs
-         */
-        public static get newItemsAction() {
-            return ArrayChanged._newItemsAction;
-        }
-
-        /**
-         * An existing item was removed, the removedKey field contains its key
-         */
-        public static get removedItemsAction() {
-            return ArrayChanged._removedItemsAction;
-        }
-
-        /**
-         * One or many items in the array were changed, the 
-         */
-        public static get changedItemAction() {
-            return ArrayChanged._changedItemAction;
-        }
-
-        /**
-         * The array's content was totally changed
-         * Depending on the method that used this mode the ChangedArray object may contains more information
-         */
-        public static get replacedArrayAction() {
-            return ArrayChanged._replacedArrayAction;
-        }
-
-        /**
-         * The length of the array changed
-         */
-        public static get lengthChangedAction() {
-            return ArrayChanged._lengthChangedAction;
-        }
-
-        private static _clearAction            = 0x1;
-        private static _newItemsAction         = 0x2;
-        private static _removedItemsAction     = 0x4;
-        private static _replacedArrayAction    = 0x8;
-        private static _lengthChangedAction    = 0x10;
-        private static _changedItemAction      = 0x20;
-
-        clear() {
-            this.action = 0;
-            this.newItems.splice(0);
-            this.removedItems.splice(0);
-            this.changedItems.splice(0);
-            this.removedStartingIndex = this.removedStartingIndex = this.changedStartingIndex = 0;
-        }
-    }
-
-    export class OAWatchedObjectChangedInfo<T> {
-        object: T;
-        propertyChanged: PropertyChangedInfo;
-    }
-
-    /**
-     * This class mimics the Javascript Array and TypeScript Array<T> classes, adding new features concerning the Observable pattern.
-     * 
-     */
-    export class ObservableArray<T> extends PropertyChangedBase {
-        /**
-         * Create an Observable Array.
-         * @param watchObjectsPropertyChange
-         * @param array and optional array that will be encapsulated by this ObservableArray instance. That's right, it's NOT a copy!
-         */
-        constructor(watchObjectsPropertyChange: boolean, array?: Array<T>) {
-            super();
-            this._array = (array!=null) ? array : new Array<T>();
-            this.dci = new ArrayChanged<T>();
-            this._callingArrayChanged = false;
-            this._arrayChanged = null;
-
-            this._callingWatchedObjectChanged = false;
-            this._watchObjectsPropertyChange = watchObjectsPropertyChange;
-            this._watchedObjectList = this._watchObjectsPropertyChange ? new StringDictionary<Observer<PropertyChangedInfo>>() : null;
-            this._woci = new OAWatchedObjectChangedInfo<T>();
-        }
-
-        /**
-          * Gets or sets the length of the array. This is a number one higher than the highest element defined in an array.
-          */
-        get length(): number {
-            return this._array.length;
-        }
-
-        set length(value: number) {
-            if (value === this._array.length) {
-                return;
-            }
-
-            let oldLength = this._array.length;
-            this._array.length = value;
-
-            this.onPropertyChanged("length", oldLength, this._array.length);
-        }
-
-        getAt(index: number): T {
-            return this._array[index];
-        }
-
-        setAt(index: number, value: T): boolean {
-            if (index < 0) {
-                return false;
-            }
-
-            let insertion = (index >= this._array.length) || this._array[index] === undefined;
-            let oldLength = 0;
-            if (insertion) {
-                oldLength = this._array.length;
-            } else if (this._watchObjectsPropertyChange) {
-                this._removeWatchedElement(this._array[index]);
-            }
-
-            this._array[index] = value;
-
-            if (this._watchObjectsPropertyChange) {
-                this._addWatchedElement(value);
-            }
-
-            if (insertion) {
-                this.onPropertyChanged("length", oldLength, this._array.length);
-            }
-
-            let ac = this.getArrayChangedObject();
-            if (ac) {
-                ac.action = insertion ? ArrayChanged.newItemsAction : ArrayChanged.changedItemAction;
-                if (insertion) {
-                    ac.newItems.splice(0, ac.newItems.length, { index: index, value: value });
-                    ac.newStartingIndex = index;
-                    ac.changedItems.splice(0);
-                } else {
-                    ac.newItems.splice(0);
-                    ac.changedStartingIndex = index;
-                    ac.changedItems.splice(0, ac.changedItems.length, { index: index, value: value });
-                }
-                ac.removedItems.splice(0);
-                ac.removedStartingIndex = -1;
-                this.callArrayChanged(ac);
-            }
-        }
-
-        /**
-          * Returns a string representation of an array.
-          */
-        toString(): string {
-            return this._array.toString();
-        }
-
-        toLocaleString(): string {
-            return this._array.toLocaleString();
-        }
-
-        /**
-          * Appends new elements to an array, and returns the new length of the array.
-          * @param items New elements of the Array.
-          */
-        push(...items: T[]): number {
-            let oldLength = this._array.length;
-            let n = this._array.push(...items);
-
-            if (this._watchObjectsPropertyChange) {
-                this._addWatchedElement(...items);
-            }
-
-            this.onPropertyChanged("length", oldLength, this._array.length);
-            let ac = this.getArrayChangedObject();
-            if (ac) {
-                ac.action = ArrayChanged.newItemsAction;
-                ac.newStartingIndex = oldLength;
-                this.feedNotifArray(ac.newItems, oldLength, ...items);
-                this.callArrayChanged(ac);
-            }
-
-            return n;
-        }
-
-        /**
-          * Removes the last element from an array and returns it.
-          */
-        pop(): T {
-            let firstRemove = this._array.length - 1;
-            let res = this._array.pop();
-
-            if (res && this._watchObjectsPropertyChange) {
-                this._removeWatchedElement(res);
-            }
-
-            if (firstRemove !== -1) {
-                this.onPropertyChanged("length", this._array.length + 1, this._array.length);
-
-                let ac = this.getArrayChangedObject();
-                if (ac) {
-                    ac.action = ArrayChanged.removedItemsAction;
-                    ac.removedStartingIndex = firstRemove;
-                    this.feedNotifArray(ac.removedItems, firstRemove, res);
-                }
-            }
-
-            return res;
-        }
-
-        /**
-          * Combines two or more arrays.
-          * @param items Additional items to add to the end of array1.
-          */
-        concat(...items: T[]): ObservableArray<T> {
-            return new ObservableArray<T>(this._watchObjectsPropertyChange, this._array.concat(...items));
-        }
-
-        /**
-          * Adds all the elements of an array separated by the specified separator string.
-          * @param separator A string used to separate one element of an array from the next in the resulting String. If omitted, the array elements are separated with a comma.
-          */
-        join(separator?: string): string {
-            return this._array.join(separator);
-        }
-
-        /**
-          * Reverses the elements in an Array.
-         * The arrayChanged action is 
-          */
-        reverse(): T[] {
-            let res = this._array.reverse();
-
-            let ac = this.getArrayChangedObject();
-            ac.action = ArrayChanged.replacedArrayAction;
-
-            return res;
-        }
-
-        /**
-          * Removes the first element from an array and returns it, shift all subsequents element one element before.
-         * The ArrayChange action is replacedArrayAction, the whole array changes and must be reevaluate as such, the removed element is in removedItems.
-         * 
-          */
-        shift(): T {
-            let oldLength = this._array.length;
-            let res = this._array.shift();
-
-            if (this._watchedObjectChanged && res!=null) {
-                this._removeWatchedElement(res);
-            }
-
-            if (oldLength !== 0) {
-                this.onPropertyChanged("length", oldLength, this._array.length);
-
-                let ac = this.getArrayChangedObject();
-                if (ac) {
-                    ac.action = ArrayChanged.replacedArrayAction;
-                    ac.removedItems.splice(0, ac.removedItems.length, { index: 0, value: res });
-                    ac.newItems.splice(0);
-                    ac.changedItems.splice(0);
-                    ac.removedStartingIndex = 0;
-                    this.callArrayChanged(ac);
-                }
-            }
-
-            return res;
-        }
-
-        /** 
-          * Returns a section of an array.
-          * @param start The beginning of the specified portion of the array.
-          * @param end The end of the specified portion of the array.
-          */
-        slice(start?: number, end?: number): ObservableArray<T> {
-            return new ObservableArray<T>(this._watchObjectsPropertyChange, this._array.slice(start, end));
-        }
-
-        /**
-          * Sorts an array.
-          * @param compareFn The name of the function used to determine the order of the elements. If omitted, the elements are sorted in ascending, ASCII character order.
-         * On the contrary of the Javascript Array's implementation, this method returns nothing
-          */
-        sort(compareFn?: (a: T, b: T) => number): void {
-            let oldLength = this._array.length;
-
-            this._array.sort(compareFn);
-
-            if (oldLength !== 0) {
-                let ac = this.getArrayChangedObject();
-                if (ac) {
-                    ac.clear();
-                    ac.action = ArrayChanged.replacedArrayAction;
-                    this.callArrayChanged(ac);
-                }
-            }
-        }
-
-        /**
-          * Removes elements from an array and, if necessary, inserts new elements in their place, returning the deleted elements.
-          * @param start The zero-based location in the array from which to start removing elements.
-          * @param deleteCount The number of elements to remove.
-          * @param items Elements to insert into the array in place of the deleted elements.
-          */
-        splice(start: number, deleteCount: number, ...items: T[]): T[] {
-            let oldLength = this._array.length;
-
-            if (this._watchObjectsPropertyChange) {
-                for (let i = start; i < start+deleteCount; i++) {
-                    let val = this._array[i];
-                    if (this._watchObjectsPropertyChange && val != null) {
-                        this._removeWatchedElement(val);
-                    }
-                }
-            }
-
-            let res = this._array.splice(start, deleteCount, ...items);
-
-            if (this._watchObjectsPropertyChange) {
-                this._addWatchedElement(...items);
-            }
-
-            if (oldLength !== this._array.length) {
-                this.onPropertyChanged("length", oldLength, this._array.length);
-            }
-
-            let ac = this.getArrayChangedObject();
-            if (ac) {
-                ac.clear();
-                ac.action = ArrayChanged.replacedArrayAction;
-                this.callArrayChanged(ac);
-            }
-
-            return res;
-        }
-
-        /**
-          * Inserts new elements at the start of an array.
-          * @param items  Elements to insert at the start of the Array.
-          * The ChangedArray action is replacedArrayAction, newItems contains the list of the added items
-          */
-        unshift(...items: T[]): number {
-            let oldLength = this._array.length;
-            
-            let res = this._array.unshift(...items);
-
-            if (this._watchObjectsPropertyChange) {
-                this._addWatchedElement(...items);
-            }
-
-            this.onPropertyChanged("length", oldLength, this._array.length);
-            let ac = this.getArrayChangedObject();
-            if (ac) {
-                ac.clear();
-                ac.action = ArrayChanged.replacedArrayAction;
-                ac.newStartingIndex = 0,
-                this.feedNotifArray(ac.newItems, 0, ...items);
-                this.callArrayChanged(ac);
-            }
-
-            return res;
-        }
-
-        /**
-          * Returns the index of the first occurrence of a value in an array.
-          * @param searchElement The value to locate in the array.
-          * @param fromIndex The array index at which to begin the search. If fromIndex is omitted, the search starts at index 0.
-          */
-        indexOf(searchElement: T, fromIndex?: number): number {
-            return this._array.indexOf(searchElement, fromIndex);
-        }
-
-        /**
-          * Returns the index of the last occurrence of a specified value in an array.
-          * @param searchElement The value to locate in the array.
-          * @param fromIndex The array index at which to begin the search. If fromIndex is omitted, the search starts at the last index in the array.
-          */
-        lastIndexOf(searchElement: T, fromIndex?: number): number {
-            return this._array.lastIndexOf(searchElement, fromIndex);
-        }
-
-        /**
-          * Determines whether all the members of an array satisfy the specified test.
-          * @param callbackfn A function that accepts up to three arguments. The every method calls the callbackfn function for each element in array1 until the callbackfn returns false, or until the end of the array.
-          * @param thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.
-          */
-        every(callbackfn: (value: T, index: number, array: T[]) => boolean, thisArg?: any): boolean {
-            return this._array.every(callbackfn, thisArg);
-        }
-
-        /**
-          * Determines whether the specified callback function returns true for any element of an array.
-          * @param callbackfn A function that accepts up to three arguments. The some method calls the callbackfn function for each element in array1 until the callbackfn returns true, or until the end of the array.
-          * @param thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.
-          */
-        some(callbackfn: (value: T, index: number, array: T[]) => boolean, thisArg?: any): boolean {
-            return this._array.some(callbackfn, thisArg);
-        }
-
-        /**
-          * Performs the specified action for each element in an array.
-          * @param callbackfn  A function that accepts up to three arguments. forEach calls the callbackfn function one time for each element in the array. 
-          * @param thisArg  An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.
-          */
-        forEach(callbackfn: (value: T, index: number, array: T[]) => void, thisArg?: any): void {
-            return this._array.forEach(callbackfn, thisArg);
-        }
-
-        /**
-          * Calls a defined callback function on each element of an array, and returns an array that contains the results.
-          * @param callbackfn A function that accepts up to three arguments. The map method calls the callbackfn function one time for each element in the array. 
-          * @param thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.
-          */
-        map<U>(callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): U[] {
-            return this._array.map(callbackfn, thisArg);
-        }
-
-        /**
-          * Returns the elements of an array that meet the condition specified in a callback function. 
-          * @param callbackfn A function that accepts up to three arguments. The filter method calls the callbackfn function one time for each element in the array. 
-          * @param thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.
-          */
-        filter(callbackfn: (value: T, index: number, array: T[]) => boolean, thisArg?: any): T[] {
-            return this._array.filter(callbackfn, thisArg);
-        }
-
-        /**
-          * Calls the specified callback function for all the elements in an array. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function.
-          * @param callbackfn A function that accepts up to four arguments. The reduce method calls the callbackfn function one time for each element in the array.
-          * @param initialValue If initialValue is specified, it is used as the initial value to start the accumulation. The first call to the callbackfn function provides this value as an argument instead of an array value.
-          */
-        reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue?: T): T {
-            return this._array.reduce(callbackfn);
-        }
-
-        /** 
-          * Calls the specified callback function for all the elements in an array, in descending order. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function.
-          * @param callbackfn A function that accepts up to four arguments. The reduceRight method calls the callbackfn function one time for each element in the array. 
-          * @param initialValue If initialValue is specified, it is used as the initial value to start the accumulation. The first call to the callbackfn function provides this value as an argument instead of an array value.
-          */
-        reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue?: T): T {
-            return this._array.reduceRight(callbackfn);
-        }
-
-        get arrayChanged(): Observable<ArrayChanged<T>> {
-            if (!this._arrayChanged) {
-                this._arrayChanged = new Observable<ArrayChanged<T>>();
-            }
-            return this._arrayChanged;
-        }
-
-        protected getArrayChangedObject(): ArrayChanged<T> {
-            if (this._arrayChanged && this._arrayChanged.hasObservers()) {
-                let ac = this._callingArrayChanged ? new ArrayChanged<T>() : this.dci;
-                return ac;
-            }
-            return null;
-        }
-
-        protected feedNotifArray(array: { index: number, value: T }[], startindIndex: number, ...items: T[]) {
-            array.splice(0);
-            for (let i = 0; i < items.length; i++) {
-                let value = this._array[i + startindIndex];
-                if (value !== undefined) {
-                    array.push({ index: i + startindIndex, value: value });
-                }
-            }
-        }
-
-        protected callArrayChanged(ac: ArrayChanged<T>) {
-            try {
-                this._callingArrayChanged = true;
-                this.arrayChanged.notifyObservers(ac, ac.action);
-            } finally {
-                this._callingArrayChanged = false;
-            }
-        }
-
-        get watchedObjectChanged(): Observable<OAWatchedObjectChangedInfo<T>> {
-            if (!this._watchedObjectChanged) {
-                this._watchedObjectChanged = new Observable<OAWatchedObjectChangedInfo<T>>();
-            }
-            return this._watchedObjectChanged;
-        }
-
-        private _addWatchedElement(...items: T[]) {
-            for (let curItem of items) {
-                if (curItem["propertyChanged"]) {
-                    let key = curItem["__ObsArrayObjID__"] as string;
-
-                    // The object may already be part of another ObsArray, so there already be a valid ID
-                    if (!key) {
-                        key = Tools.RandomId();
-                        curItem["__ObsArrayObjID__"] = key;
-                    }
-
-                    this._watchedObjectList.add(key, (<IPropertyChanged><any>curItem).propertyChanged.add((e, d) => {
-                        this.onWatchedObjectChanged(key, curItem, e);
-                    }));
-                }
-            }
-        }
-
-        private _removeWatchedElement(...items: T[]) {
-            for (let curItem of items) {
-                let key = curItem["__ObsArrayObjID__"] as string;
-                if (key != null) {
-                    let observer = this._watchedObjectList.getAndRemove(key);
-                    (<IPropertyChanged><any>curItem).propertyChanged.remove(observer);
-                }
-            }
-        }
-
-        protected onWatchedObjectChanged(key: string, object: T, propChanged: PropertyChangedInfo) {
-            if (this._watchedObjectChanged && this._watchedObjectChanged.hasObservers()) {
-
-                let woci = this._callingWatchedObjectChanged ? new OAWatchedObjectChangedInfo<T>() : this._woci;
-                woci.object = object;
-                woci.propertyChanged = propChanged;
-
-                try {
-                    this._callingWatchedObjectChanged = true;
-                    this.watchedObjectChanged.notifyObservers(woci);
-                } finally {
-                    this._callingWatchedObjectChanged = false;
-                }
-            }
-        }
-
-        private _array: Array<T>;
-
-        private _arrayChanged: Observable<ArrayChanged<T>>;
-        private dci = new ArrayChanged<T>();
-        private _callingArrayChanged: boolean = false;
-
-        private _watchedObjectChanged: Observable<OAWatchedObjectChangedInfo<T>>;
-        private _woci: OAWatchedObjectChangedInfo<T>;
-        private _callingWatchedObjectChanged: boolean;
-        private _watchObjectsPropertyChange: boolean;
-        private _watchedObjectList: StringDictionary<Observer<PropertyChangedInfo>>;
-
-    }
 }

+ 0 - 318
src/Tools/babylon.stringDictionary.ts

@@ -172,322 +172,4 @@
         private _count = 0;
         private _data = {};
     }
-
-    /**
-     * Class for the ObservableStringDictionary.onDictionaryChanged observable
-     */
-    export class DictionaryChanged<T> {
-        /**
-         * Contain the action that were made on the dictionary, it's one of the DictionaryChanged.xxxAction members.
-         * Note the action's value can be used in the "mask" field of the Observable to only be notified about given action(s)
-         */
-        public action: number;
-
-        /**
-         * Only valid if the action is newItemAction
-         */
-        public newItem: { key: string, value: T }
-
-        /**
-         * Only valid if the action is removedItemAction
-         */
-        public removedKey: string;
-
-        /**
-         * Only valid if the action is itemValueChangedAction
-         */
-        public changedItem: { key: string, oldValue: T, newValue: T }
-
-        /**
-         * The content of the dictionary was totally cleared
-         */
-        public static get clearAction() {
-            return DictionaryChanged._clearAction;
-        }
-
-        /**
-         * A new item was added, the newItem field contains the key/value pair
-         */
-        public static get newItemAction() {
-            return DictionaryChanged._newItemAction;
-        }
-
-        /**
-         * An existing item was removed, the removedKey field contains its key
-         */
-        public static get removedItemAction() {
-            return DictionaryChanged._removedItemAction;
-        }
-
-        /**
-         * An existing item had a value change, the changedItem field contains the key/value
-         */
-        public static get itemValueChangedAction() {
-            return DictionaryChanged._itemValueChangedAction;
-        }
-
-        /**
-         * The dictionary's content was reset and replaced by the content of another dictionary.
-         * DictionaryChanged<T> contains no further information about this action
-         */
-        public static get replacedAction() {
-            return DictionaryChanged._replacedAction;
-        }
-
-        private static _clearAction            = 0x1;
-        private static _newItemAction          = 0x2;
-        private static _removedItemAction      = 0x4;
-        private static _itemValueChangedAction = 0x8;
-        private static _replacedAction         = 0x10;
-    }
-
-    export class OSDWatchedObjectChangedInfo<T> {
-        key: string;
-        object: T;
-        propertyChanged: PropertyChangedInfo;
-    }
-
-    export class ObservableStringDictionary<T> extends StringDictionary<T> implements IPropertyChanged {
-
-        constructor(watchObjectsPropertyChange: boolean) {
-            super();
-
-            this._propertyChanged = null;
-            this._dictionaryChanged = null;
-            this.dci = new DictionaryChanged<T>();
-            this._callingDicChanged = false;
-            this._watchedObjectChanged = null;
-            this._callingWatchedObjectChanged = false;
-            this._woci = new OSDWatchedObjectChangedInfo<T>();
-            this._watchObjectsPropertyChange = watchObjectsPropertyChange;
-            this._watchedObjectList = this._watchObjectsPropertyChange ? new StringDictionary<Observer<PropertyChangedInfo>>() : null;
-        }
-
-        /**
-         * This will clear this dictionary and copy the content from the 'source' one.
-         * If the T value is a custom object, it won't be copied/cloned, the same object will be used
-         * @param source the dictionary to take the content from and copy to this dictionary
-         */
-        public copyFrom(source: StringDictionary<T>) {
-            let oldCount = this.count;
-            // Don't rely on this class' implementation for clear/add otherwise tons of notification will be thrown
-            super.clear();
-            source.forEach((t, v) => this._add(t, v, false, this._watchObjectsPropertyChange));
-            this.onDictionaryChanged(DictionaryChanged.replacedAction, null, null, null);
-            this.onPropertyChanged("count", oldCount, this.count);
-        }
-
-        /**
-         * Get a value from its key or add it if it doesn't exist.
-         * This method will ensure you that a given key/data will be present in the dictionary.
-         * @param key the given key to get the matching value from
-         * @param factory the factory that will create the value if the key is not present in the dictionary.
-         * The factory will only be invoked if there's no data for the given key.
-         * @return the value corresponding to the key.
-         */
-        public getOrAddWithFactory(key: string, factory: (key: string) => T): T {
-            let val = super.getOrAddWithFactory(key, k => {
-                let v = factory(key);
-                this._add(key, v, true, this._watchObjectsPropertyChange);
-                return v;
-            });
-
-            return val;
-        }
-
-        /**
-         * Add a new key and its corresponding value
-         * @param key the key to add
-         * @param value the value corresponding to the key
-         * @return true if the operation completed successfully, false if we couldn't insert the key/value because there was already this key in the dictionary
-         */
-        public add(key: string, value: T): boolean {
-            return this._add(key, value, true, true);
-        }
-
-        public getAndRemove(key: string): T {
-            let val = super.get(key);
-            this._remove(key, true, val);
-            return val;
-        }
-
-        private _add(key: string, value: T, fireNotif: boolean, registerWatcher: boolean): boolean {
-            if (super.add(key, value)) {
-                if (fireNotif) {
-                    this.onDictionaryChanged(DictionaryChanged.newItemAction, { key: key, value: value }, null, null);
-                    this.onPropertyChanged("count", this.count - 1, this.count);
-                }
-                if (registerWatcher) {
-                    this._addWatchedElement(key, value);
-                }
-                return true;
-            }
-            return false;
-        }
-
-        private _addWatchedElement(key: string, el: T) {
-            if (el["propertyChanged"]) {
-                this._watchedObjectList.add(key, (<IPropertyChanged><any>el).propertyChanged.add((e, d) => {
-                    this.onWatchedObjectChanged(key, el, e);
-                }));
-            }            
-        }
-
-        private _removeWatchedElement(key: string, el: T) {
-            let observer = this._watchedObjectList.getAndRemove(key);
-            (<IPropertyChanged><any>el).propertyChanged.remove(observer);
-        }
-
-        public set(key: string, value: T): boolean {
-            let oldValue = this.get(key);
-            if (this._watchObjectsPropertyChange) {
-                this._removeWatchedElement(key, oldValue);
-            }
-
-            if (super.set(key, value)) {
-                this.onDictionaryChanged(DictionaryChanged.itemValueChangedAction, null, null, { key: key, oldValue: oldValue, newValue: value });
-                this._addWatchedElement(key, value);
-                return true;
-            }
-
-            return false;
-        }
-
-        /**
-         * Remove a key/value from the dictionary.
-         * @param key the key to remove
-         * @return true if the item was successfully deleted, false if no item with such key exist in the dictionary
-         */
-        public remove(key: string): boolean {
-            return this._remove(key, true);
-        }
-
-        private _remove(key: string, fireNotif: boolean, element?: T): boolean {
-            if (!element) {
-                element = this.get(key);
-            }
-
-            if (!element) {
-                return false;
-            }
-
-            if (super.remove(key) === undefined) {
-                return false;
-            }
-
-            this.onDictionaryChanged(DictionaryChanged.removedItemAction, null, key, null);
-            this.onPropertyChanged("count", this.count + 1, this.count);
-
-            if (this._watchObjectsPropertyChange) {
-                this._removeWatchedElement(key, element);
-            }
-
-            return true;
-        }
-
-        /**
-         * Clear the whole content of the dictionary
-         */
-        public clear() {
-            this._watchedObjectList.forEach((k, v) => {
-                let el = this.get(k);
-                this._removeWatchedElement(k, el);
-            });
-            this._watchedObjectList.clear();
-
-            let oldCount = this.count;
-            super.clear();
-            this.onDictionaryChanged(DictionaryChanged.clearAction, null, null, null);
-            this.onPropertyChanged("count", oldCount, 0);
-        }
-
-        get propertyChanged(): Observable<PropertyChangedInfo> {
-            if (!this._propertyChanged) {
-                this._propertyChanged = new Observable<PropertyChangedInfo>();
-            }
-            return this._propertyChanged;
-        }
-
-        protected onPropertyChanged<T>(propName: string, oldValue: T, newValue: T, mask?: number) {
-            if (this._propertyChanged && this._propertyChanged.hasObservers()) {
-
-                let pci = ObservableStringDictionary.callingPropChanged ? new PropertyChangedInfo() : ObservableStringDictionary.pci;
-
-                pci.oldValue = oldValue;
-                pci.newValue = newValue;
-                pci.propertyName = propName;
-
-                try {
-                    ObservableStringDictionary.callingPropChanged = true;
-                    this.propertyChanged.notifyObservers(pci, mask);
-                } finally {
-                    ObservableStringDictionary.callingPropChanged = false;
-                }
-            }
-        }
-
-        get dictionaryChanged(): Observable<DictionaryChanged<T>> {
-            if (!this._dictionaryChanged) {
-                this._dictionaryChanged = new Observable<DictionaryChanged<T>>();
-            }
-            return this._dictionaryChanged;
-        }
-
-        protected onDictionaryChanged(action: number, newItem: { key: string, value: T }, removedKey: string, changedItem: { key: string, oldValue: T, newValue: T }) {
-            if (this._dictionaryChanged && this._dictionaryChanged.hasObservers()) {
-
-                let dci = this._callingDicChanged ? new DictionaryChanged<T>() : this.dci;
-
-                dci.action = action;
-                dci.newItem = newItem;
-                dci.removedKey = removedKey;
-                dci.changedItem = changedItem;
-
-                try {
-                    this._callingDicChanged = true;
-                    this.dictionaryChanged.notifyObservers(dci, action);
-                } finally {
-                    this._callingDicChanged = false;
-                }
-            }
-        }
-
-        get watchedObjectChanged(): Observable<OSDWatchedObjectChangedInfo<T>> {
-            if (!this._watchedObjectChanged) {
-                this._watchedObjectChanged = new Observable<OSDWatchedObjectChangedInfo<T>>();
-            }
-            return this._watchedObjectChanged;
-        }
-
-        protected onWatchedObjectChanged(key: string, object: T, propChanged: PropertyChangedInfo) {
-            if (this._watchedObjectChanged && this._watchedObjectChanged.hasObservers()) {
-
-                let woci = this._callingWatchedObjectChanged ? new OSDWatchedObjectChangedInfo<T>() : this._woci;
-                woci.key = key;
-                woci.object = object;
-                woci.propertyChanged = propChanged;
-
-                try {
-                    this._callingWatchedObjectChanged = true;
-                    this.watchedObjectChanged.notifyObservers(woci);
-                } finally {
-                    this._callingWatchedObjectChanged = false;
-                }
-            }
-        }
-
-        private _propertyChanged: Observable<PropertyChangedInfo>;
-        private static pci = new PropertyChangedInfo();
-        private static callingPropChanged: boolean = false;
-
-        private _dictionaryChanged: Observable<DictionaryChanged<T>>;
-        private dci: DictionaryChanged<T>;
-        private _callingDicChanged: boolean;
-
-        private _watchedObjectChanged: Observable<OSDWatchedObjectChangedInfo<T>>;
-        private _woci: OSDWatchedObjectChangedInfo<T>;
-        private _callingWatchedObjectChanged: boolean;
-        private _watchObjectsPropertyChange: boolean;
-        private _watchedObjectList: StringDictionary<Observer<PropertyChangedInfo>>;
-    }
 }