浏览代码

Canvas 2D: minor improvments

Uniformizing the creation methods by introducing position/x/y and size/width/height. Creation code is also cleaner now.

Alignment feature on its way...still WIP
nockawa 9 年之前
父节点
当前提交
16113275a8

+ 24 - 11
src/Canvas2d/babylon.canvas2d.ts

@@ -55,16 +55,28 @@
          * @param scene the Scene that owns the Canvas
          * Options:
          *  - id: a text identifier, for information purpose only
-         *  - pos: the position of the canvas, relative from the bottom/left of the scene's viewport
-         *  - size: the Size of the canvas. If null two behaviors depend on the cachingStrategy: if it's CACHESTRATEGY_CACHECANVAS then it will always auto-fit the rendering device, in all the other modes it will fit the content of the Canvas
-         *  - cachingStrategy: either CACHESTRATEGY_TOPLEVELGROUPS, CACHESTRATEGY_ALLGROUPS, CACHESTRATEGY_CANVAS, CACHESTRATEGY_DONTCACHE. Please refer to their respective documentation for more information. Default is Canvas2D.CACHESTRATEGY_TOPLEVELGROUPS
+         *  - pos: the position of the canvas, relative from the bottom/left of the scene's viewport. Alternatively you can set the x and y properties directly. Default value is [0, 0]
+         *  - size: the Size of the canvas. Alternatively the width and height properties can be set. If null two behaviors depend on the cachingStrategy: if it's CACHESTRATEGY_CACHECANVAS then it will always auto-fit the rendering device, in all the other modes it will fit the content of the Canvas
+         *  - cachingStrategy: either CACHESTRATEGY_TOPLEVELGROUPS, CACHESTRATEGY_ALLGROUPS, CACHESTRATEGY_CANVAS, CACHESTRATEGY_DONTCACHE. Please refer to their respective documentation for more information. Default is Canvas2D.CACHESTRATEGY_DONTCACHE
          *  - enableInteraction: if true the pointer events will be listened and rerouted to the appropriate primitives of the Canvas2D through the Prim2DBase.onPointerEventObservable observable property.
+         *  - isVisible: true if the canvas must be visible, false for hidden. Default is true.
+         *  - marginTop/Left/Right/Bottom: define the margin for the corresponding edge, if all of them are null, margin is not used in layout computing. Default Value is null for each.
+         *  - hAlighment: define horizontal alignment of the Canvas, alignment is optional, default value null: no alignment.
+         *  - vAlighment: define horizontal alignment of the Canvas, alignment is optional, default value null: no alignment.
          */
-        static CreateScreenSpace(scene: Scene, options: { id?: string, pos?: Vector2, origin?: Vector2, size?: Size, cachingStrategy?: number, enableInteraction?: boolean }): Canvas2D {
+        static CreateScreenSpace(scene: Scene, options: { id?: string, x?: number, y?: number, position?: Vector2, origin?: Vector2, width?: number, height?: number, size?: Size, cachingStrategy?: number, enableInteraction?: boolean, isVisible?: boolean, marginTop?: number, marginLeft?: number, marginRight?: number, marginBottom?: number, hAlignment?: number, vAlignment?: number }): Canvas2D {
             let c = new Canvas2D();
-            c.setupCanvas(scene, options && options.id || null, options && options.size || null, true, options && options.cachingStrategy || Canvas2D.CACHESTRATEGY_TOPLEVELGROUPS, options && options.enableInteraction || true);
-            c.position = options && options.pos || Vector2.Zero();
-            c.origin = options && options.origin || Vector2.Zero();
+
+            if (!options) {
+                c.setupCanvas(scene, null, null, true, Canvas2D.CACHESTRATEGY_DONTCACHE, true, Vector2.Zero(), true, null, null, null, null, null, null);
+                c.position = Vector2.Zero();
+            } else { 
+                let pos = options.position || new Vector2(options.x || 0, options.y || 0);
+                let size = (!options.size && !options.width && !options.height) ? null : (options.size || (new Size(options.width || 0, options.height || 0)));
+
+                c.setupCanvas(scene, options.id || null, size, true, options.cachingStrategy || Canvas2D.CACHESTRATEGY_DONTCACHE, options.enableInteraction || true, options.origin || Vector2.Zero(), options.isVisible || true, options.marginTop, options.marginLeft, options.marginRight, options.marginBottom, options.hAlignment || Prim2DBase.HAlignLeft, options.vAlignment || Prim2DBase.VAlignTop);
+                c.position = pos;
+            }
 
             return c;
         }
@@ -83,8 +95,9 @@
          * TIPS: if you want a renderScaleFactor independent reference of frame, create a child Group2D in the Canvas with position 0,0 and size set to null, then set its scale property to the same amount than the renderScaleFactor, put all your primitive inside using coordinates regarding the size property you pick for the Canvas and you'll be fine.
          * - sideOrientation: Unexpected behavior occur if the value is different from Mesh.DEFAULTSIDE right now, so please use this one, which is the default.
          * - cachingStrategy Must be CACHESTRATEGY_CANVAS for now, which is the default.
+         * - isVisible: true if the canvas must be visible, false for hidden. Default is true.
          */
-        static CreateWorldSpace(scene: Scene, size: Size, options: { id?: string, position?: Vector3, rotation?: Quaternion, renderScaleFactor?: number, sideOrientation?: number, cachingStrategy?: number, enableInteraction?: boolean}): Canvas2D {
+        static CreateWorldSpace(scene: Scene, size: Size, options: { id?: string, position?: Vector3, rotation?: Quaternion, renderScaleFactor?: number, sideOrientation?: number, cachingStrategy?: number, enableInteraction?: boolean, isVisible?: boolean}): Canvas2D {
 
             let cs = options && options.cachingStrategy || Canvas2D.CACHESTRATEGY_CANVAS;
 
@@ -99,7 +112,7 @@
             let id = options && options.id || null;
             let rsf = options && options.renderScaleFactor || 1;
             let c = new Canvas2D();
-            c.setupCanvas(scene, id, new Size(size.width * rsf, size.height * rsf), false, cs, options && options.enableInteraction || true);
+            c.setupCanvas(scene, id, new Size(size.width * rsf, size.height * rsf), false, cs, options && options.enableInteraction || true, Vector2.Zero(), options && options.isVisible || true, null, null, null, null, null, null);
 
             let plane = new WorldSpaceCanvas2D(id, scene, c);
             let vertexData = VertexData.CreatePlane({ width: size.width / 2, height: size.height / 2, sideOrientation: options && options.sideOrientation || Mesh.DEFAULTSIDE });
@@ -119,7 +132,7 @@
             return c;
         }
 
-        protected setupCanvas(scene: Scene, name: string, size: Size, isScreenSpace: boolean, cachingstrategy: number, enableInteraction: boolean) {
+        protected setupCanvas(scene: Scene, name: string, size: Size, isScreenSpace: boolean, cachingstrategy: number, enableInteraction: boolean, origin: Vector2, isVisible: boolean, marginTop: number, marginLeft: number, marginRight: number, marginBottom: number, hAlign: number, vAlign: number) {
             let engine = scene.getEngine();
             this._fitRenderingDevice = !size;
             if (!size) {
@@ -131,7 +144,7 @@
             this._capturedPointers = new StringDictionary<Prim2DBase>();
             this._pickStartingPosition = Vector2.Zero();
 
-            this.setupGroup2D(this, null, name, Vector2.Zero(), null, size, this._cachingStrategy===Canvas2D.CACHESTRATEGY_ALLGROUPS ? Group2D.GROUPCACHEBEHAVIOR_DONTCACHEOVERRIDE : Group2D.GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY);
+            this.setupGroup2D(this, null, name, Vector2.Zero(), origin, size, isVisible, this._cachingStrategy===Canvas2D.CACHESTRATEGY_ALLGROUPS ? Group2D.GROUPCACHEBEHAVIOR_DONTCACHEOVERRIDE : Group2D.GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY, marginTop, marginLeft, marginRight, marginBottom, hAlign, vAlign);
 
             this._hierarchyLevelMaxSiblingCount = 100;
             this._hierarchyDepthOffset = 0;

+ 23 - 13
src/Canvas2d/babylon.ellipse2d.ts

@@ -181,8 +181,8 @@
             BoundingInfo2D.CreateFromSizeToRef(this.size, this._levelBoundingInfo, this.origin);
         }
 
-        protected setupEllipse2D(owner: Canvas2D, parent: Prim2DBase, id: string, position: Vector2, origin: Vector2, size: Size, subdivisions: number=64, fill?: IBrush2D, border?: IBrush2D, borderThickness: number = 1) {
-            this.setupShape2D(owner, parent, id, position, origin, true, fill, border, borderThickness);
+        protected setupEllipse2D(owner: Canvas2D, parent: Prim2DBase, id: string, position: Vector2, origin: Vector2, size: Size, subdivisions: number, fill: IBrush2D, border: IBrush2D, borderThickness: number, isVisible: boolean, marginTop: number, marginLeft: number, marginRight: number, marginBottom: number, vAlignment: number, hAlignment: number) {
+            this.setupShape2D(owner, parent, id, position, origin, isVisible, fill, border, borderThickness, marginTop, marginLeft, marginRight, marginBottom, hAlignment, vAlignment);
             this.size = size;
             this.subdivisions = subdivisions;
         }
@@ -192,28 +192,38 @@
          * @param parent the parent primitive, must be a valid primitive (or the Canvas)
          * options:
          *  - id: a text identifier, for information purpose
-         *  - x: the X position relative to its parent, default is 0
-         *  - y: the Y position relative to its parent, default is 0
+         *  - position: the X & Y positions relative to its parent. Alternatively the x and y properties can be set. Default is [0;0]
          *  - origin: define the normalized origin point location, default [0.5;0.5]
-         *  - width: the width of the ellipse, default is 10
-         *  - height: the height of the ellipse, default is 10
+         *  - size: the size of the group. Alternatively the width and height properties can be set. Default will be [10;10].
          *  - subdivision: the number of subdivision to create the ellipse perimeter, default is 64.
          *  - fill: the brush used to draw the fill content of the ellipse, you can set null to draw nothing (but you will have to set a border brush), default is a SolidColorBrush of plain white.
          *  - border: the brush used to draw the border of the ellipse, you can set null to draw nothing (but you will have to set a fill brush), default is null.
          *  - borderThickness: the thickness of the drawn border, default is 1.
+         *  - isVisible: true if the primitive must be visible, false for hidden. Default is true.
+         *  - marginTop/Left/Right/Bottom: define the margin for the corresponding edge, if all of them are null, margin is not used in layout computing. Default Value is null for each.
+         *  - hAlighment: define horizontal alignment of the Canvas, alignment is optional, default value null: no alignment.
+         *  - vAlighment: define horizontal alignment of the Canvas, alignment is optional, default value null: no alignment.
          */
-        public static Create(parent: Prim2DBase, options: { id?: string, x?: number, y?: number, origin?: Vector2, width?: number, height?: number, subdivisions?: number, fill?: IBrush2D, border?: IBrush2D, borderThickness?: number }): Ellipse2D {
+        public static Create(parent: Prim2DBase, options: { id?: string, position?: Vector2, x?: number, y?: number, origin?: Vector2, size?: Size, width?: number, height?: number, subdivisions?: number, fill?: IBrush2D, border?: IBrush2D, borderThickness?: number, isVisible?: boolean, marginTop?: number, marginLeft?: number, marginRight?: number, marginBottom?: number, vAlignment?: number, hAlignment?: number}): Ellipse2D {
             Prim2DBase.CheckParent(parent);
 
-            let fill: IBrush2D;
-            if (options && options.fill !== undefined) {
-                fill = options.fill;
+            let ellipse = new Ellipse2D();
+
+            if (!options) {
+                ellipse.setupEllipse2D(parent.owner, parent, null, Vector2.Zero(), null, new Size(10, 10), 64, Canvas2D.GetSolidColorBrushFromHex("#FFFFFFFF"), null, 1, true, null, null, null, null, null, null);
             } else {
-                fill = Canvas2D.GetSolidColorBrushFromHex("#FFFFFFFF");
+                let fill: IBrush2D;
+                if (!options.fill) {
+                    fill = Canvas2D.GetSolidColorBrushFromHex("#FFFFFFFF");
+                } else {
+                    fill = options.fill;
+                }
+                let pos = options.position || new Vector2(options.x || 0, options.y || 0);
+                let size = options.size || (new Size(options.width || 10, options.height || 10));
+
+                ellipse.setupEllipse2D(parent.owner, parent, options.id || null, pos, options.origin || null, size, options.subdivisions || 64, fill, options.border || null, options.borderThickness || 1, options.isVisible || true, options.marginTop || null, options.marginLeft || null, options.marginRight || null, options.marginBottom || null, options.vAlignment || null, options.hAlignment || null);
             }
 
-            let ellipse = new Ellipse2D();
-            ellipse.setupEllipse2D(parent.owner, parent, options && options.id || null, new Vector2(options && options.x || 0, options && options.y || 0), options && options.origin || null, new Size(options && options.width || 10, options && options.height || 10), options && options.subdivisions || 64, fill, options && options.border || null, options && options.borderThickness || 1);
             return ellipse;
         }
 

+ 21 - 7
src/Canvas2d/babylon.group2d.ts

@@ -37,22 +37,36 @@
          * @param parent the parent primitive, must be a valid primitive (or the Canvas)
          * options:
          *  - id a text identifier, for information purpose
-         *  - position: the X & Y positions relative to its parent, default is [0;0]
+         *  - position: the X & Y positions relative to its parent. Alternatively the x and y properties can be set. Default is [0;0]
          *  - origin: define the normalized origin point location, default [0.5;0.5]
-         *  - size: the size of the group, if null the size will be computed from its content, default is null.
+         *  - size: the size of the group. Alternatively the width and height properties can be set. If null the size will be computed from its content, default is null.
          *  - cacheBehavior: Define how the group should behave regarding the Canvas's cache strategy, default is Group2D.GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY
+         *  - isVisible: true if the group must be visible, false for hidden. Default is true.
+         *  - marginTop/Left/Right/Bottom: define the margin for the corresponding edge, if all of them are null, margin is not used in layout computing. Default Value is null for each.
+         *  - hAlighment: define horizontal alignment of the Canvas, alignment is optional, default value null: no alignment.
+         *  - vAlighment: define horizontal alignment of the Canvas, alignment is optional, default value null: no alignment.
          */
-        static CreateGroup2D(parent: Prim2DBase, options: { id?: string, position?: Vector2; origin?: Vector2, size?: Size, cacheBehavior?: number}): Group2D {
+        static CreateGroup2D(parent: Prim2DBase, options: { id?: string, position?: Vector2, x?: number, y?: number, origin?: Vector2, size?: Size, width?: number, height?: number, cacheBehavior?: number, isVisible?: boolean, marginTop?: number, marginLeft?: number, marginRight?: number, marginBottom?: number, vAlignment?: number, hAlignment?: number}): Group2D {
             Prim2DBase.CheckParent(parent);
+
+
             var g = new Group2D();
-            g.setupGroup2D(parent.owner, parent, options && options.id || null, options && options.position || Vector2.Zero(), options && options.origin || null, options && options.size || null, options && options.cacheBehavior || Group2D.GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY);
 
+            if (!options) {
+                g.setupGroup2D(parent.owner, parent, null, Vector2.Zero(), null, null, true, Group2D.GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY, null, null, null, null, null, null);
+            } else {
+                let pos = options.position || new Vector2(options.x || 0, options.y || 0);
+                let size = (!options.size && !options.width && !options.height) ? null : (options.size || (new Size(options.width || 0, options.height || 0)));
+                
+                g.setupGroup2D(parent.owner, parent, options.id || null, pos, options.origin || null, size, options.isVisible || true, options.cacheBehavior || Group2D.GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY, options.marginTop, options.marginLeft, options.marginRight, options.marginBottom, options.hAlignment || Prim2DBase.HAlignLeft, options.vAlignment || Prim2DBase.VAlignTop);
+            }
+       
             return g;
         }
 
         static _createCachedCanvasGroup(owner: Canvas2D): Group2D {
             var g = new Group2D();
-            g.setupGroup2D(owner, null, "__cachedCanvasGroup__", Vector2.Zero(), null);
+            g.setupGroup2D(owner, null, "__cachedCanvasGroup__", Vector2.Zero(), Vector2.Zero(), null, true, Group2D.GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY, null, null, null, null, null, null);
             g.origin = Vector2.Zero();
             return g;
             
@@ -108,9 +122,9 @@
             return true;
         }
 
-        protected setupGroup2D(owner: Canvas2D, parent: Prim2DBase, id: string, position: Vector2, origin: Vector2, size?: Size, cacheBehavior: number = Group2D.GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY) {
+        protected setupGroup2D(owner: Canvas2D, parent: Prim2DBase, id: string, position: Vector2, origin: Vector2, size: Size, isVisible: boolean, cacheBehavior: number, marginTop: number, marginLeft: number, marginRight: number, marginBottom: number, hAlign: number, vAlign: number) {
             this._cacheBehavior = cacheBehavior;
-            this.setupPrim2DBase(owner, parent, id, position, origin);
+            this.setupPrim2DBase(owner, parent, id, position, origin, isVisible, marginTop, marginLeft, marginRight, marginBottom , hAlign, vAlign);
             this.size = size;
             this._viewportPosition = Vector2.Zero();
         }

+ 21 - 11
src/Canvas2d/babylon.lines2d.ts

@@ -265,8 +265,8 @@
             BoundingInfo2D.CreateFromSizeToRef(this.size, this._levelBoundingInfo, this.origin);
         }
 
-        protected setupLines2D(owner: Canvas2D, parent: Prim2DBase, id: string, position: Vector2, origin: Vector2, points: Vector2[], fillThickness: number, startCap: number, endCap: number, fill: IBrush2D, border: IBrush2D, borderThickness: number, closed: boolean) {
-            this.setupShape2D(owner, parent, id, position, origin, true, fill, border, borderThickness);
+        protected setupLines2D(owner: Canvas2D, parent: Prim2DBase, id: string, position: Vector2, origin: Vector2, points: Vector2[], fillThickness: number, startCap: number, endCap: number, fill: IBrush2D, border: IBrush2D, borderThickness: number, closed: boolean, isVisible: boolean, marginTop: number, marginLeft: number, marginRight: number, marginBottom: number, vAlignment: number, hAlignment: number) {
+            this.setupShape2D(owner, parent, id, position, origin, isVisible, fill, border, borderThickness, marginTop, marginLeft, marginRight, marginBottom, hAlignment, vAlignment);
             this.fillThickness = fillThickness;
             this.startCap = startCap;
             this.endCap = endCap;
@@ -283,8 +283,7 @@
          * @param points an array that describe the points to use to draw the line, must contain at least two entries.
          * options:
          *  - id a text identifier, for information purpose
-         *  - x: the X position relative to its parent, default is 0
-         *  - y: the Y position relative to its parent, default is 0
+         *  - position: the X & Y positions relative to its parent. Alternatively the x and y properties can be set. Default is [0;0]
          *  - origin: define the normalized origin point location, default [0.5;0.5]
          *  - fillThickness: the thickness of the fill part of the line, can be null to draw nothing (but a border brush must be given), default is 1.
          *  - closed: if false the lines are said to be opened, the first point and the latest DON'T connect. if true the lines are said to be closed, the first and last point will be connected by a line. For instance you can define the 4 points of a rectangle, if you set closed to true a 4 edges rectangle will be drawn. If you set false, only three edges will be drawn, the edge formed by the first and last point won't exist. Default is false.
@@ -293,19 +292,30 @@
          *  - fill: the brush used to draw the fill content of the lines, you can set null to draw nothing (but you will have to set a border brush), default is a SolidColorBrush of plain white.
          *  - border: the brush used to draw the border of the lines, you can set null to draw nothing (but you will have to set a fill brush), default is null.
          *  - borderThickness: the thickness of the drawn border, default is 1.
+         *  - isVisible: true if the primitive must be visible, false for hidden. Default is true.
+         *  - marginTop/Left/Right/Bottom: define the margin for the corresponding edge, if all of them are null, margin is not used in layout computing. Default Value is null for each.
+         *  - hAlighment: define horizontal alignment of the Canvas, alignment is optional, default value null: no alignment.
+         *  - vAlighment: define horizontal alignment of the Canvas, alignment is optional, default value null: no alignment.
          */
-        public static Create(parent: Prim2DBase, points: Vector2[], options: { id?: string, x?: number, y?: number, origin?: Vector2, fillThickness?: number, closed?: boolean, startCap?: number, endCap?: number, fill?: IBrush2D, border?: IBrush2D, borderThickness?: number }): Lines2D {
+        public static Create(parent: Prim2DBase, points: Vector2[], options: { id?: string, position?: Vector2, x?: number, y?: number, origin?: Vector2, fillThickness?: number, closed?: boolean, startCap?: number, endCap?: number, fill?: IBrush2D, border?: IBrush2D, borderThickness?: number, isVisible?: boolean, marginTop?: number, marginLeft?: number, marginRight?: number, marginBottom?: number, vAlignment?: number, hAlignment?: number }): Lines2D {
             Prim2DBase.CheckParent(parent);
 
-            let fill: IBrush2D;
-            if (options && options.fill !== undefined) {
-                fill = options.fill;
+            let lines = new Lines2D();
+
+            if (!options) {
+                lines.setupLines2D(parent.owner, parent, null, Vector2.Zero(), null, points, 1, 0, 0, Canvas2D.GetSolidColorBrushFromHex("#FFFFFFFF"), null, 1, false, true, null, null, null, null, null, null);
             } else {
-                fill = Canvas2D.GetSolidColorBrushFromHex("#FFFFFFFF");
+                let fill: IBrush2D;
+                if (!options.fill) {
+                    fill = Canvas2D.GetSolidColorBrushFromHex("#FFFFFFFF");
+                } else {
+                    fill = options.fill;
+                }
+                let pos = options.position || new Vector2(options.x || 0, options.y || 0);
+
+                lines.setupLines2D(parent.owner, parent, options.id || null, pos, options.origin || null, points, options.fillThickness || 1, options.startCap || 0, options.endCap || 0, fill, options.border || null, options.borderThickness || 1, options.closed || false, options.isVisible || true, options.marginTop || null, options.marginLeft || null, options.marginRight || null, options.marginBottom || null, options.vAlignment || null, options.hAlignment || null);                
             }
 
-            let lines = new Lines2D();
-            lines.setupLines2D(parent.owner, parent, options && options.id || null, new Vector2(options && options.x || 0, options && options.y || 0), options && options.origin || null, points, options && options.fillThickness || 1, options && options.startCap || 0, options && options.endCap || 0, fill, options && options.border || null, options && options.borderThickness || 0, options && options.closed || false);
             return lines;
         }
 

+ 164 - 4
src/Canvas2d/babylon.prim2dBase.ts

@@ -222,6 +222,75 @@
         }
     }
 
+    export class PrimitiveMargin {
+        constructor(owner: Prim2DBase) {
+            this._owner = owner;
+            this._left = this._top = this._bottom = this.right = 0;
+        }
+
+        public get top(): number {
+            return this._top;
+        }
+
+        public set top(value: number) {
+            if (value === this._top) {
+                return;
+            }
+
+            this._top = value;
+            this._owner._marginChanged();
+        }
+
+        public get left(): number {
+            return this._left;
+        }
+
+        public set left(value: number) {
+            if (value === this._left) {
+                return;
+            }
+
+            this._left = value;
+            this._owner._marginChanged();
+        }
+
+        public get right(): number {
+            return this._right;
+        }
+
+        public set right(value: number) {
+            if (value === this._right) {
+                return;
+            }
+
+            this._right = value;
+            this._owner._marginChanged();
+        }
+
+        public get bottom(): number {
+            return this._bottom;
+        }
+
+        public set bottom(value: number) {
+            if (value === this._bottom) {
+                return;
+            }
+
+            this._bottom = value;
+            this._owner._marginChanged();
+        }
+
+        private _owner: Prim2DBase;
+        private _top: number;
+        private _left: number;
+        private _right: number;
+        private _bottom: number;
+
+        static Zero(owner: Prim2DBase): PrimitiveMargin {
+            return new PrimitiveMargin(owner);
+        }
+    }
+
     /**
      * Main class used for the Primitive Intersection API
      */
@@ -294,13 +363,31 @@
      * Base class for a Primitive of the Canvas2D feature
      */
     export class Prim2DBase extends SmartPropertyPrim {
-        static PRIM2DBASE_PROPCOUNT: number = 10;
-
-        protected setupPrim2DBase(owner: Canvas2D, parent: Prim2DBase, id: string, position: Vector2, origin: Vector2, isVisible: boolean = true) {
+        static PRIM2DBASE_PROPCOUNT: number = 12;
+
+        public static get HAlignLeft():    number { return Prim2DBase._hAlignLeft;   }
+        public static get HAlignCenter():  number { return Prim2DBase._hAlignCenter; }
+        public static get HAlignRight():   number { return Prim2DBase._hAlignRight;  }
+        public static get HAlignStretch(): number { return Prim2DBase._hAlignStretch;}
+        public static get VAlignTop():     number { return Prim2DBase._vAlignTop;    }
+        public static get VAlignCenter():  number { return Prim2DBase._vAlignCenter; }
+        public static get VAlignBottom():  number { return Prim2DBase._vAlignBottom; }
+        public static get VAlignStretch(): number { return Prim2DBase._vAlignStretch;}
+
+        protected setupPrim2DBase(owner: Canvas2D, parent: Prim2DBase, id: string, position: Vector2, origin: Vector2, isVisible: boolean, marginTop?: number, marginLeft?: number, marginRight?: number, marginBottom?: number, vAlignment?: number, hAlignment?: number) {
             if (!(this instanceof Group2D) && !(this instanceof Sprite2D && id !== null && id.indexOf("__cachedSpriteOfGroup__") === 0) && (owner.cachingStrategy === Canvas2D.CACHESTRATEGY_TOPLEVELGROUPS) && (parent === owner)) {
                 throw new Error("Can't create a primitive with the canvas as direct parent when the caching strategy is TOPLEVELGROUPS. You need to create a Group below the canvas and use it as the parent for the primitive");
             }
 
+            let m: PrimitiveMargin = null;
+            if (marginTop || marginLeft || marginRight || marginBottom) {
+                m = new PrimitiveMargin(this);
+                m.top    = marginTop    || 0;
+                m.left   = marginLeft   || 0;
+                m.right  = marginRight  || 0;
+                m.bottom = marginBottom || 0;
+            }
+
             this.setupSmartPropertyPrim();
             this._pointerEventObservable = new Observable<PrimitivePointerInfo>();
             this._isPickable = true;
@@ -334,9 +421,11 @@
             this.scale = 1;
             this.levelVisible = isVisible;
             this.origin = origin || new Vector2(0.5, 0.5);
+            this.margin = m;
+            this.hAlignment = hAlignment;
+            this.vAlignment = vAlignment;
         }
 
-
         public get actionManager(): ActionManager {
             if (!this._actionManager) {
                 this._actionManager = new ActionManager(this.owner.scene);
@@ -423,6 +512,21 @@
          */
         public static zOrderProperty: Prim2DPropInfo;
 
+        /**
+         * Metadata of the margin property
+         */
+        public static marginProperty: Prim2DPropInfo;
+
+        /**
+         * Metadata of the vAlignment property
+         */
+        public static vAlignmentProperty: Prim2DPropInfo;
+
+        /**
+         * Metadata of the hAlignment property
+         */
+        public static hAlignmentProperty: Prim2DPropInfo;
+
         @instanceLevelProperty(1, pi => Prim2DBase.positionProperty = pi, false, true)
         /**
          * Position of the primitive, relative to its parent.
@@ -525,6 +629,46 @@
             this._zOrder = value;
         }
 
+        @dynamicLevelProperty(8, pi => Prim2DBase.marginProperty = pi)
+        /**
+         * You can get/set a margin on the primitive through this property
+         * @returns the margin object, if there was none, a default one is created and returned
+         */
+        public get margin(): PrimitiveMargin {
+            if (!this._margin) {
+                this._margin = new PrimitiveMargin(this);
+            }
+            return this._margin;
+        }
+
+        public set margin(value: PrimitiveMargin) {
+            this._margin = value;
+        }
+
+        @dynamicLevelProperty(9, pi => Prim2DBase.hAlignmentProperty = pi)
+        /**
+         * You can get/set the horizontal alignment through this property
+         */
+        public get hAlignment(): number {
+            return this._hAlignment;
+        }
+
+        public set hAlignment(value: number) {
+            this._hAlignment = value;
+        }
+
+        @dynamicLevelProperty(10, pi => Prim2DBase.vAlignmentProperty = pi)
+        /**
+         * You can get/set the vertical alignment through this property
+         */
+        public get vAlignment(): number {
+            return this._vAlignment;
+        }
+
+        public set vAlignment(value: number) {
+            this._vAlignment = value;
+        }
+
         /**
          * Define if the Primitive can be subject to intersection test or not (default is true)
          */
@@ -762,6 +906,10 @@
             }
         }
 
+        public _marginChanged() {
+            
+        }
+
         public _needPrepare(): boolean {
             return this._visibilityChanged || this._modelDirty || (this._instanceDirtyFlags !== 0) || (this._globalTransformProcessStep !== this._globalTransformStep);
         }
@@ -876,6 +1024,15 @@
             }
         }
 
+        private static _hAlignLeft    = 1;
+        private static _hAlignCenter  = 2;
+        private static _hAlignRight   = 3;
+        private static _hAlignStretch = 4;
+        private static _vAlignTop     = 1;
+        private static _vAlignCenter  = 2;
+        private static _vAlignBottom  = 3;
+        private static _vAlignStretch = 4;
+
         private _owner: Canvas2D;
         private _parent: Prim2DBase;
         private _actionManager: ActionManager;
@@ -885,6 +1042,9 @@
         protected _hierarchyDepthOffset: number;
         protected _siblingDepthOffset: number;
         private _zOrder: number;
+        private _margin: PrimitiveMargin;
+        private _hAlignment: number;
+        private _vAlignment: number;
         private _levelVisible: boolean;
         public _pointerEventObservable: Observable<PrimitivePointerInfo>;
         public _boundingInfoDirty: boolean;

+ 15 - 13
src/Canvas2d/babylon.rectangle2d.ts

@@ -195,8 +195,8 @@
             BoundingInfo2D.CreateFromSizeToRef(this.size, this._levelBoundingInfo, this.origin);
         }
 
-        protected setupRectangle2D(owner: Canvas2D, parent: Prim2DBase, id: string, position: Vector2, origin: Vector2, size: Size, roundRadius = 0, fill?: IBrush2D, border?: IBrush2D, borderThickness: number = 1) {
-            this.setupShape2D(owner, parent, id, position, origin, true, fill, border, borderThickness);
+        protected setupRectangle2D(owner: Canvas2D, parent: Prim2DBase, id: string, position: Vector2, origin: Vector2, size: Size, roundRadius, fill: IBrush2D, border: IBrush2D, borderThickness: number, isVisible: boolean, marginTop: number, marginLeft: number, marginRight: number, marginBottom: number, vAlignment: number, hAlignment: number) {
+            this.setupShape2D(owner, parent, id, position, origin, isVisible, fill, border, borderThickness, marginTop, marginLeft, marginRight, marginBottom, hAlignment, vAlignment);
             this.size = size;
             this.notRounded = !roundRadius;
             this.roundRadius = roundRadius;
@@ -207,29 +207,31 @@
          * @param parent the parent primitive, must be a valid primitive (or the Canvas)
          * options:
          *  - id a text identifier, for information purpose
-         *  - x: the X position relative to its parent, default is 0
-         *  - y: the Y position relative to its parent, default is 0
+         *  - position: the X & Y positions relative to its parent. Alternatively the x and y properties can be set. Default is [0;0]
          *  - origin: define the normalized origin point location, default [0.5;0.5]
-         *  - width: the width of the rectangle, default is 10
-         *  - height: the height of the rectangle, default is 10
+         *  - size: the size of the group. Alternatively the width and height properties can be set. Default will be [10;10].
          *  - roundRadius: if the rectangle has rounded corner, set their radius, default is 0 (to get a sharp rectangle).
          *  - fill: the brush used to draw the fill content of the ellipse, you can set null to draw nothing (but you will have to set a border brush), default is a SolidColorBrush of plain white.
          *  - border: the brush used to draw the border of the ellipse, you can set null to draw nothing (but you will have to set a fill brush), default is null.
          *  - borderThickness: the thickness of the drawn border, default is 1.
+         *  - isVisible: true if the primitive must be visible, false for hidden. Default is true.
+         *  - marginTop/Left/Right/Bottom: define the margin for the corresponding edge, if all of them are null, margin is not used in layout computing. Default Value is null for each.
+         *  - hAlighment: define horizontal alignment of the Canvas, alignment is optional, default value null: no alignment.
+         *  - vAlighment: define horizontal alignment of the Canvas, alignment is optional, default value null: no alignment.
          */
-        public static Create(parent: Prim2DBase, options: { id?: string, x?: number, y?: number, origin?: Vector2, width?: number, height?: number, roundRadius?: number, fill?: IBrush2D, border?: IBrush2D, borderThickness?: number}): Rectangle2D {
+        public static Create(parent: Prim2DBase, options: { id?: string, position?: Vector2, x?: number, y?: number, origin?: Vector2, size?: Size, width?: number, height?: number, roundRadius?: number, fill?: IBrush2D, border?: IBrush2D, borderThickness?: number, isVisible?: boolean, marginTop?: number, marginLeft?: number, marginRight?: number, marginBottom?: number, vAlignment?: number, hAlignment?: number}): Rectangle2D {
             Prim2DBase.CheckParent(parent);
 
             let rect = new Rectangle2D();
-            rect.setupRectangle2D(parent.owner, parent, options && options.id || null, new Vector2(options && options.x || 0, options && options.y || 0), options && options.origin || null, new Size(options && options.width || 10, options && options.height || 10), options && options.roundRadius || 0);
 
-            if (options && options.fill !== undefined) {
-                rect.fill = options.fill;
+            if (!options) {
+                rect.setupRectangle2D(parent.owner, parent, null, Vector2.Zero(), null, new Size(10, 10), 0, Canvas2D.GetSolidColorBrushFromHex("#FFFFFFFF"), null, 1, true, null, null, null, null, null, null);
             } else {
-                rect.fill = Canvas2D.GetSolidColorBrushFromHex("#FFFFFFFF");                
+                let pos = options.position || new Vector2(options.x || 0, options.y || 0);
+                let size = options.size || (new Size(options.width || 10, options.height || 10));
+
+                rect.setupRectangle2D(parent.owner, parent, options.id || null, pos, options.origin || null, size, options.roundRadius || 0, options.fill || Canvas2D.GetSolidColorBrushFromHex("#FFFFFFFF"), options.border || null, options.borderThickness || 1, options.isVisible || true, options.marginTop || null, options.marginLeft || null, options.marginRight || null, options.marginBottom || null, options.vAlignment || null, options.hAlignment || null);
             }
-            rect.border = options && options.border || null;
-            rect.borderThickness = options && options.borderThickness || 1;
             return rect;
         }
 

+ 2 - 2
src/Canvas2d/babylon.renderablePrim2d.ts

@@ -343,8 +343,8 @@
             this._isTransparent = value;
         }
 
-        setupRenderablePrim2D(owner: Canvas2D, parent: Prim2DBase, id: string, position: Vector2, origin: Vector2, isVisible: boolean) {
-            this.setupPrim2DBase(owner, parent, id, position, origin);
+        setupRenderablePrim2D(owner: Canvas2D, parent: Prim2DBase, id: string, position: Vector2, origin: Vector2, isVisible: boolean, marginTop: number, marginLeft: number, marginRight: number, marginBottom: number, hAlign: number, vAlign: number) {
+            this.setupPrim2DBase(owner, parent, id, position, origin, isVisible, marginTop, marginLeft, marginRight, marginBottom, hAlign, vAlign);
             this._isTransparent = false;
         }
 

+ 3 - 2
src/Canvas2d/babylon.shape2d.ts

@@ -44,8 +44,9 @@
             this._borderThickness = value;
         }
 
-        setupShape2D(owner: Canvas2D, parent: Prim2DBase, id: string, position: Vector2, origin: Vector2, isVisible: boolean, fill: IBrush2D, border: IBrush2D, borderThickness: number = 1.0) {
-            this.setupRenderablePrim2D(owner, parent, id, position, origin, isVisible);
+        setupShape2D(owner: Canvas2D, parent: Prim2DBase, id: string, position: Vector2, origin: Vector2, isVisible: boolean, fill: IBrush2D, border: IBrush2D, borderThickness: number, marginTop: number, marginLeft: number, marginRight: number, marginBottom: number, vAlignment: number, hAlignment: number) {
+
+            this.setupRenderablePrim2D(owner, parent, id, position, origin, isVisible, marginTop, marginLeft, marginRight, marginBottom, hAlignment || Prim2DBase.HAlignLeft, vAlignment || Prim2DBase.VAlignTop);
             this.border = border;
             this.fill = fill;
             this.borderThickness = borderThickness;

+ 16 - 7
src/Canvas2d/babylon.sprite2d.ts

@@ -179,8 +179,8 @@
             return true;
         }
 
-        protected setupSprite2D(owner: Canvas2D, parent: Prim2DBase, id: string, position: Vector2, origin: Vector2, texture: Texture, spriteSize: Size, spriteLocation: Vector2, invertY: boolean) {
-            this.setupRenderablePrim2D(owner, parent, id, position, origin, true);
+        protected setupSprite2D(owner: Canvas2D, parent: Prim2DBase, id: string, position: Vector2, origin: Vector2, texture: Texture, spriteSize: Size, spriteLocation: Vector2, invertY: boolean, isVisible: boolean, marginTop: number, marginLeft: number, marginRight: number, marginBottom: number, vAlignment: number, hAlignment: number) {
+            this.setupRenderablePrim2D(owner, parent, id, position, origin, isVisible, marginTop, marginLeft, marginRight, marginBottom, hAlignment, vAlignment);
             this.texture = texture;
             this.texture.wrapU = Texture.CLAMP_ADDRESSMODE;
             this.texture.wrapV = Texture.CLAMP_ADDRESSMODE;
@@ -202,25 +202,34 @@
          * @param texture the texture that stores the sprite to render
          * options:
          *  - id a text identifier, for information purpose
-         *  - x: the X position relative to its parent, default is 0
-         *  - y: the Y position relative to its parent, default is 0
+         *  - position: the X & Y positions relative to its parent. Alternatively the x and y properties can be set. Default is [0;0]
          *  - origin: define the normalized origin point location, default [0.5;0.5]
          *  - spriteSize: the size of the sprite, if null the size of the given texture will be used, default is null.
          *  - spriteLocation: the location in the texture of the top/left corner of the Sprite to display, default is null (0,0)
          *  - invertY: if true the texture Y will be inverted, default is false.
+         *  - isVisible: true if the sprite must be visible, false for hidden. Default is true.
+         *  - marginTop/Left/Right/Bottom: define the margin for the corresponding edge, if all of them are null, margin is not used in layout computing. Default Value is null for each.
+         *  - hAlighment: define horizontal alignment of the Canvas, alignment is optional, default value null: no alignment.
+         *  - vAlighment: define horizontal alignment of the Canvas, alignment is optional, default value null: no alignment.
          */
-        public static Create(parent: Prim2DBase, texture: Texture, options: { id?: string, x?: number, y?: number, origin?: Vector2, spriteSize?: Size, spriteLocation?: Vector2, invertY?: boolean}): Sprite2D {
+        public static Create(parent: Prim2DBase, texture: Texture, options: { id?: string, position?: Vector2, x?: number, y?: number, origin?: Vector2, spriteSize?: Size, spriteLocation?: Vector2, invertY?: boolean, isVisible?: boolean, marginTop?: number, marginLeft?: number, marginRight?: number, marginBottom?: number, vAlignment?: number, hAlignment?: number}): Sprite2D {
             Prim2DBase.CheckParent(parent);
 
             let sprite = new Sprite2D();
-            sprite.setupSprite2D(parent.owner, parent, options && options.id || null, new Vector2(options && options.x || 0, options && options.y || 0), options && options.origin || null, texture, options && options.spriteSize || null, options && options.spriteLocation || null, options && options.invertY || false);
+            if (!options) {
+                sprite.setupSprite2D(parent.owner, parent, null, Vector2.Zero(), null, texture, null, null, false, true, null, null, null, null, null, null);
+            } else {
+                let pos = options.position || new Vector2(options.x || 0, options.y || 0);
+                sprite.setupSprite2D(parent.owner, parent, options.id || null, pos, options.origin || null, texture, options.spriteSize || null, options.spriteLocation || null, options.invertY || false, options.isVisible || true, options.marginTop || null, options.marginLeft || null, options.marginRight || null, options.marginBottom || null, options.vAlignment || null, options.hAlignment || null);
+            }
+
             return sprite;
         }
 
         static _createCachedCanvasSprite(owner: Canvas2D, texture: MapTexture, size: Size, pos: Vector2): Sprite2D {
 
             let sprite = new Sprite2D();
-            sprite.setupSprite2D(owner, null, "__cachedCanvasSprite__", new Vector2(0, 0), null, texture, size, pos, false);
+            sprite.setupSprite2D(owner, null, "__cachedCanvasSprite__", new Vector2(0, 0), null, texture, size, pos, false, true, null, null, null, null, null, null);
 
             return sprite;
         }

+ 14 - 37
src/Canvas2d/babylon.text2d.ts

@@ -107,15 +107,6 @@
         public static defaultFontColorProperty: Prim2DPropInfo;
         public static textProperty: Prim2DPropInfo;
         public static areaSizeProperty: Prim2DPropInfo;
-        public static vAlignProperty: Prim2DPropInfo;
-        public static hAlignProperty: Prim2DPropInfo;
-
-        public static TEXT2D_VALIGN_TOP = 1;
-        public static TEXT2D_VALIGN_CENTER = 2;
-        public static TEXT2D_VALIGN_BOTTOM = 3;
-        public static TEXT2D_HALIGN_LEFT = 1;
-        public static TEXT2D_HALIGN_CENTER = 2;
-        public static TEXT2D_HALIGN_RIGHT = 3;
 
         @modelLevelProperty(RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 1, pi => Text2D.fontProperty = pi, false, true)
         public get fontName(): string {
@@ -158,24 +149,6 @@
             this._areaSize = value;
         }
 
-        @instanceLevelProperty(RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 5, pi => Text2D.vAlignProperty = pi)
-        public get vAlign(): number {
-            return this._vAlign;
-        }
-
-        public set vAlign(value: number) {
-            this._vAlign = value;
-        }
-
-        @instanceLevelProperty(RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 6, pi => Text2D.hAlignProperty = pi)
-        public get hAlign(): number {
-            return this._hAlign;
-        }
-
-        public set hAlign(value: number) {
-            this._hAlign = value;
-        }
-
         public get actualSize(): Size {
             if (this.areaSize) {
                 return this.areaSize;
@@ -216,15 +189,13 @@
             BoundingInfo2D.CreateFromSizeToRef(this.actualSize, this._levelBoundingInfo, this.origin);
         }
 
-        protected setupText2D(owner: Canvas2D, parent: Prim2DBase, id: string, position: Vector2, origin: Vector2, fontName: string, text: string, areaSize: Size, defaultFontColor: Color4, vAlign, hAlign, tabulationSize: number) {
-            this.setupRenderablePrim2D(owner, parent, id, position, origin, true);
+        protected setupText2D(owner: Canvas2D, parent: Prim2DBase, id: string, position: Vector2, origin: Vector2, fontName: string, text: string, areaSize: Size, defaultFontColor: Color4, tabulationSize: number, isVisible: boolean, marginTop: number, marginLeft: number, marginRight: number, marginBottom: number, vAlignment: number, hAlignment: number) {
+            this.setupRenderablePrim2D(owner, parent, id, position, origin, isVisible, marginTop, marginLeft, marginRight, marginBottom, hAlignment, vAlignment);
 
             this.fontName = fontName;
             this.defaultFontColor = defaultFontColor;
             this.text = text;
             this.areaSize = areaSize;
-            this.vAlign = vAlign;
-            this.hAlign = hAlign;
             this._tabulationSize = tabulationSize;
             this._isTransparent = true;
         }
@@ -235,21 +206,27 @@
          * @param text the text to display
          * Options:
          *  - id a text identifier, for information purpose
-         *  - x: the X position relative to its parent, default is 0
-         *  - y: the Y position relative to its parent, default is 0
+         *  - position: the X & Y positions relative to its parent. Alternatively the x and y properties can be set. Default is [0;0]
          *  - origin: define the normalized origin point location, default [0.5;0.5]
          *  - fontName: the name/size/style of the font to use, following the CSS notation. Default is "12pt Arial".
          *  - defaultColor: the color by default to apply on each letter of the text to display, default is plain white.
          *  - areaSize: the size of the area in which to display the text, default is auto-fit from text content.
-         *  - vAlign: vertical alignment (areaSize must be specified), default is Text2D.TEXT2D_VALIGN_CENTER
-         *  - hAlign: horizontal alignment (areaSize must be specified), default is Text2D.TEXT2D_HALIGN_CENTER
          *  - tabulationSize: number of space character to insert when a tabulation is encountered, default is 4
+         *  - isVisible: true if the text must be visible, false for hidden. Default is true.
+         *  - marginTop/Left/Right/Bottom: define the margin for the corresponding edge, if all of them are null, margin is not used in layout computing. Default Value is null for each.
+         *  - hAlighment: define horizontal alignment of the Canvas, alignment is optional, default value null: no alignment.
+         *  - vAlighment: define horizontal alignment of the Canvas, alignment is optional, default value null: no alignment.
          */
-        public static Create(parent: Prim2DBase, text: string, options?: { id?: string, x?: number, y?: number, origin?:Vector2, fontName?: string, defaultFontColor?: Color4, areaSize?: Size, vAlign?: number, hAlign?: number, tabulationSize?: number}): Text2D {
+        public static Create(parent: Prim2DBase, text: string, options?: { id?: string, position?: Vector2, x?: number, y?: number, origin?: Vector2, fontName?: string, defaultFontColor?: Color4, areaSize?: Size, tabulationSize?: number, isVisible?: boolean, marginTop?: number, marginLeft?: number, marginRight?: number, marginBottom?: number, hAlignment?: number, vAlignment?: number}): Text2D {
             Prim2DBase.CheckParent(parent);
 
             let text2d = new Text2D();
-            text2d.setupText2D(parent.owner, parent, options && options.id || null, new Vector2(options && options.x || 0, options && options.y || 0), options && options.origin || null, options && options.fontName || "12pt Arial", text, options && options.areaSize, options && options.defaultFontColor || new Color4(1, 1, 1, 1), options && options.vAlign || Text2D.TEXT2D_VALIGN_CENTER, options && options.hAlign || Text2D.TEXT2D_HALIGN_CENTER, options && options.tabulationSize || 4);
+            if (!options) {
+                text2d.setupText2D(parent.owner, parent, null, Vector2.Zero(), null, "12pt Arial", text, null, new Color4(1,1,1,1), 4, true, null, null, null, null, null, null);
+            } else {
+                let pos = options.position || new Vector2(options.x || 0, options.y || 0);
+                text2d.setupText2D(parent.owner, parent, options.id || null, pos, options.origin || null, options.fontName || "12pt Arial", text, options.areaSize, options.defaultFontColor || new Color4(1, 1, 1, 1), options.tabulationSize || 4, options.isVisible || true, options.marginTop || null, options.marginLeft || null, options.marginRight || null, options.marginBottom || null, options.vAlignment || null, options.hAlignment || null);
+            }
             return text2d;
         }