فهرست منبع

Make ArcRotateCamera.input.pointers more GC friendly.
Renamed getOrAdd to getOrAddWithFactory and introduce a getOrAdd with a given value.

nockawa 9 سال پیش
والد
کامیت
71d1f3dfd7

+ 50 - 52
src/Cameras/Inputs/babylon.arcrotatecamera.input.pointers.js

@@ -21,9 +21,7 @@ var BABYLON;
             var _this = this;
             var _this = this;
             var engine = this.camera.getEngine();
             var engine = this.camera.getEngine();
             var cacheSoloPointer; // cache pointer object for better perf on camera rotation
             var cacheSoloPointer; // cache pointer object for better perf on camera rotation
-            var pointers = new BABYLON.StringDictionary();
-            var pointersOrder = new Array(2);
-            var curPointerCount = 0;
+            var pointA, pointB;
             var previousPinchDistance = 0;
             var previousPinchDistance = 0;
             this._pointerInput = function (p, s) {
             this._pointerInput = function (p, s) {
                 var evt = p.event;
                 var evt = p.event;
@@ -36,8 +34,13 @@ var BABYLON;
                     // Manage panning with right click
                     // Manage panning with right click
                     _this._isRightClick = evt.button === 2;
                     _this._isRightClick = evt.button === 2;
                     // manage pointers
                     // manage pointers
-                    cacheSoloPointer = pointers.add(evt.pointerId.toString(), { x: evt.clientX, y: evt.clientY, type: evt.pointerType });
-                    pointersOrder[curPointerCount++] = evt.pointerId;
+                    cacheSoloPointer = { x: evt.clientX, y: evt.clientY, pointerId: evt.pointerId, type: evt.pointerType };
+                    if (pointA === undefined) {
+                        pointA = cacheSoloPointer;
+                    }
+                    else if (pointB === undefined) {
+                        pointB = cacheSoloPointer;
+                    }
                     if (!noPreventDefault) {
                     if (!noPreventDefault) {
                         evt.preventDefault();
                         evt.preventDefault();
                     }
                     }
@@ -54,9 +57,7 @@ var BABYLON;
                     //but emptying completly pointers collection is required to fix a bug on iPhone : 
                     //but emptying completly pointers collection is required to fix a bug on iPhone : 
                     //when changing orientation while pinching camera, one pointer stay pressed forever if we don't release all pointers  
                     //when changing orientation while pinching camera, one pointer stay pressed forever if we don't release all pointers  
                     //will be ok to put back pointers.remove(evt.pointerId); when iPhone bug corrected
                     //will be ok to put back pointers.remove(evt.pointerId); when iPhone bug corrected
-                    pointers.clear();
-                    pointersOrder = new Array();
-                    curPointerCount = 0;
+                    pointA = pointB = undefined;
                     if (!noPreventDefault) {
                     if (!noPreventDefault) {
                         evt.preventDefault();
                         evt.preventDefault();
                     }
                     }
@@ -65,47 +66,46 @@ var BABYLON;
                     if (!noPreventDefault) {
                     if (!noPreventDefault) {
                         evt.preventDefault();
                         evt.preventDefault();
                     }
                     }
-                    switch (pointers.count) {
-                        case 1:
-                            if (_this.panningSensibility !== 0 && ((_this._isCtrlPushed && _this.camera._useCtrlForPanning) || (!_this.camera._useCtrlForPanning && _this._isRightClick))) {
-                                _this.camera.inertialPanningX += -(evt.clientX - cacheSoloPointer.x) / _this.panningSensibility;
-                                _this.camera.inertialPanningY += (evt.clientY - cacheSoloPointer.y) / _this.panningSensibility;
-                            }
-                            else {
-                                var offsetX = evt.clientX - cacheSoloPointer.x;
-                                var offsetY = evt.clientY - cacheSoloPointer.y;
-                                _this.camera.inertialAlphaOffset -= offsetX / _this.angularSensibilityX;
-                                _this.camera.inertialBetaOffset -= offsetY / _this.angularSensibilityY;
-                            }
-                            cacheSoloPointer.x = evt.clientX;
-                            cacheSoloPointer.y = evt.clientY;
-                            break;
-                        case 2:
-                            //if (noPreventDefault) { evt.preventDefault(); } //if pinch gesture, could be usefull to force preventDefault to avoid html page scroll/zoom in some mobile browsers
-                            var ed = pointers.get(evt.pointerId.toString());
-                            ed.x = evt.clientX;
-                            ed.y = evt.clientY;
-                            var direction = _this.pinchInwards ? 1 : -1;
-                            var p1 = pointers.get(pointersOrder[0].toString());
-                            var p2 = pointers.get(pointersOrder[1].toString());
-                            var distX = p1.x - p2.x;
-                            var distY = p1.y - p2.y;
-                            var pinchSquaredDistance = (distX * distX) + (distY * distY);
-                            if (previousPinchDistance === 0) {
-                                previousPinchDistance = pinchSquaredDistance;
-                                return;
-                            }
-                            if (pinchSquaredDistance !== previousPinchDistance) {
-                                _this.camera.inertialRadiusOffset += (pinchSquaredDistance - previousPinchDistance) / (_this.pinchPrecision * ((_this.angularSensibilityX + _this.angularSensibilityY) / 2) * direction);
-                                previousPinchDistance = pinchSquaredDistance;
-                            }
-                            break;
-                        default:
-                            var ed = pointers.get(evt.pointerId.toString());
-                            if (ed) {
-                                ed.x = evt.clientX;
-                                ed.y = evt.clientY;
-                            }
+                    // One button down
+                    if (pointA && pointB === undefined) {
+                        if (_this.panningSensibility !== 0 &&
+                            ((_this._isCtrlPushed && _this.camera._useCtrlForPanning) ||
+                                (!_this.camera._useCtrlForPanning && _this._isRightClick))) {
+                            _this.camera
+                                .inertialPanningX += -(evt.clientX - cacheSoloPointer.x) / _this.panningSensibility;
+                            _this.camera
+                                .inertialPanningY += (evt.clientY - cacheSoloPointer.y) / _this.panningSensibility;
+                        }
+                        else {
+                            var offsetX = evt.clientX - cacheSoloPointer.x;
+                            var offsetY = evt.clientY - cacheSoloPointer.y;
+                            _this.camera.inertialAlphaOffset -= offsetX / _this.angularSensibilityX;
+                            _this.camera.inertialBetaOffset -= offsetY / _this.angularSensibilityY;
+                        }
+                        cacheSoloPointer.x = evt.clientX;
+                        cacheSoloPointer.y = evt.clientY;
+                    }
+                    else if (pointA && pointB) {
+                        //if (noPreventDefault) { evt.preventDefault(); } //if pinch gesture, could be usefull to force preventDefault to avoid html page scroll/zoom in some mobile browsers
+                        var ed = (pointA.pointerId === evt.pointerId) ? pointA : pointB;
+                        ed.x = evt.clientX;
+                        ed.y = evt.clientY;
+                        var direction = _this.pinchInwards ? 1 : -1;
+                        var distX = pointA.x - pointB.x;
+                        var distY = pointA.y - pointB.y;
+                        var pinchSquaredDistance = (distX * distX) + (distY * distY);
+                        if (previousPinchDistance === 0) {
+                            previousPinchDistance = pinchSquaredDistance;
+                            return;
+                        }
+                        if (pinchSquaredDistance !== previousPinchDistance) {
+                            _this.camera
+                                .inertialRadiusOffset += (pinchSquaredDistance - previousPinchDistance) /
+                                (_this.pinchPrecision *
+                                    ((_this.angularSensibilityX + _this.angularSensibilityY) / 2) *
+                                    direction);
+                            previousPinchDistance = pinchSquaredDistance;
+                        }
                     }
                     }
                 }
                 }
             };
             };
@@ -118,9 +118,7 @@ var BABYLON;
             }
             }
             this._onLostFocus = function () {
             this._onLostFocus = function () {
                 //this._keys = [];
                 //this._keys = [];
-                pointers.clear();
-                pointersOrder = new Array();
-                curPointerCount = 0;
+                pointA = pointB = undefined;
                 previousPinchDistance = 0;
                 previousPinchDistance = 0;
                 cacheSoloPointer = null;
                 cacheSoloPointer = null;
             };
             };

+ 53 - 55
src/Cameras/Inputs/babylon.arcrotatecamera.input.pointers.ts

@@ -33,10 +33,8 @@ module BABYLON {
 
 
         public attachControl(element: HTMLElement, noPreventDefault?: boolean) {
         public attachControl(element: HTMLElement, noPreventDefault?: boolean) {
             var engine = this.camera.getEngine();
             var engine = this.camera.getEngine();
-            var cacheSoloPointer; // cache pointer object for better perf on camera rotation
-            var pointers = new StringDictionary<{ x: number, y: number, type: any }>();
-            var pointersOrder = new Array<number>(2);
-            var curPointerCount = 0;
+            var cacheSoloPointer: { x: number, y: number, pointerId: number, type: any }; // cache pointer object for better perf on camera rotation
+            var pointA: { x: number, y: number, pointerId: number, type: any }, pointB: { x: number, y: number, pointerId: number, type: any };
             var previousPinchDistance = 0;
             var previousPinchDistance = 0;
 
 
             this._pointerInput = (p, s) => {
             this._pointerInput = (p, s) => {
@@ -53,8 +51,13 @@ module BABYLON {
                     this._isRightClick = evt.button === 2;
                     this._isRightClick = evt.button === 2;
 
 
                     // manage pointers
                     // manage pointers
-                    cacheSoloPointer = pointers.add(evt.pointerId.toString(), { x: evt.clientX, y: evt.clientY, type: evt.pointerType });
-                    pointersOrder[curPointerCount++] = evt.pointerId;
+                    cacheSoloPointer = { x: evt.clientX, y: evt.clientY, pointerId: evt.pointerId, type: evt.pointerType };
+                    if (pointA === undefined) {
+                        pointA = cacheSoloPointer;
+                    }
+                    else if (pointB === undefined) {
+                        pointB = cacheSoloPointer;
+                    }
                     if (!noPreventDefault) {
                     if (!noPreventDefault) {
                         evt.preventDefault();
                         evt.preventDefault();
                     }
                     }
@@ -72,9 +75,7 @@ module BABYLON {
                     //but emptying completly pointers collection is required to fix a bug on iPhone : 
                     //but emptying completly pointers collection is required to fix a bug on iPhone : 
                     //when changing orientation while pinching camera, one pointer stay pressed forever if we don't release all pointers  
                     //when changing orientation while pinching camera, one pointer stay pressed forever if we don't release all pointers  
                     //will be ok to put back pointers.remove(evt.pointerId); when iPhone bug corrected
                     //will be ok to put back pointers.remove(evt.pointerId); when iPhone bug corrected
-                    pointers.clear();
-                    pointersOrder = new Array<number>();
-                    curPointerCount = 0;
+                    pointA = pointB = undefined;
 
 
                     if (!noPreventDefault) {
                     if (!noPreventDefault) {
                         evt.preventDefault();
                         evt.preventDefault();
@@ -84,49 +85,48 @@ module BABYLON {
                         evt.preventDefault();
                         evt.preventDefault();
                     }
                     }
 
 
-                    switch (pointers.count) {
-                        case 1: //normal camera rotation
-                            if (this.panningSensibility !== 0 && ((this._isCtrlPushed && this.camera._useCtrlForPanning) || (!this.camera._useCtrlForPanning && this._isRightClick))) {
-                                this.camera.inertialPanningX += -(evt.clientX - cacheSoloPointer.x) / this.panningSensibility;
-                                this.camera.inertialPanningY += (evt.clientY - cacheSoloPointer.y) / this.panningSensibility;
-                            } else {
-                                var offsetX = evt.clientX - cacheSoloPointer.x;
-                                var offsetY = evt.clientY - cacheSoloPointer.y;
-                                this.camera.inertialAlphaOffset -= offsetX / this.angularSensibilityX;
-                                this.camera.inertialBetaOffset -= offsetY / this.angularSensibilityY;
-                            }
-                            cacheSoloPointer.x = evt.clientX;
-                            cacheSoloPointer.y = evt.clientY;
-                            break;
-
-                        case 2: //pinch
-                            //if (noPreventDefault) { evt.preventDefault(); } //if pinch gesture, could be usefull to force preventDefault to avoid html page scroll/zoom in some mobile browsers
-                            var ed = pointers.get(evt.pointerId.toString());
-                            ed.x = evt.clientX;
-                            ed.y = evt.clientY;
-                            var direction = this.pinchInwards ? 1 : -1;
-                            var p1 = pointers.get(pointersOrder[0].toString());
-                            var p2 = pointers.get(pointersOrder[1].toString());
-                            var distX = p1.x - p2.x;
-                            var distY = p1.y - p2.y;
-                            var pinchSquaredDistance = (distX * distX) + (distY * distY);
-                            if (previousPinchDistance === 0) {
-                                previousPinchDistance = pinchSquaredDistance;
-                                return;
-                            }
-
-                            if (pinchSquaredDistance !== previousPinchDistance) {
-                                this.camera.inertialRadiusOffset += (pinchSquaredDistance - previousPinchDistance) / (this.pinchPrecision * ((this.angularSensibilityX + this.angularSensibilityY) / 2) * direction);
-                                previousPinchDistance = pinchSquaredDistance;
-                            }
-                            break;
-
-                        default:
-                            var ed = pointers.get(evt.pointerId.toString());
-                            if (ed) {
-                                ed.x = evt.clientX;
-                                ed.y = evt.clientY;
-                            }
+                    // One button down
+                    if (pointA && pointB === undefined) {
+                        if (this.panningSensibility !== 0 &&
+                        ((this._isCtrlPushed && this.camera._useCtrlForPanning) ||
+                        (!this.camera._useCtrlForPanning && this._isRightClick))) {
+                            this.camera
+                                .inertialPanningX += -(evt.clientX - cacheSoloPointer.x) / this.panningSensibility;
+                            this.camera
+                                .inertialPanningY += (evt.clientY - cacheSoloPointer.y) / this.panningSensibility;
+                        } else {
+                            var offsetX = evt.clientX - cacheSoloPointer.x;
+                            var offsetY = evt.clientY - cacheSoloPointer.y;
+                            this.camera.inertialAlphaOffset -= offsetX / this.angularSensibilityX;
+                            this.camera.inertialBetaOffset -= offsetY / this.angularSensibilityY;
+                        }
+                        cacheSoloPointer.x = evt.clientX;
+                        cacheSoloPointer.y = evt.clientY;
+                    }
+
+                   // Two buttons down: pinch
+                    else if (pointA && pointB) {
+                        //if (noPreventDefault) { evt.preventDefault(); } //if pinch gesture, could be usefull to force preventDefault to avoid html page scroll/zoom in some mobile browsers
+                        var ed = (pointA.pointerId===evt.pointerId) ? pointA : pointB;
+                        ed.x = evt.clientX;
+                        ed.y = evt.clientY;
+                        var direction = this.pinchInwards ? 1 : -1;
+                        var distX = pointA.x - pointB.x;
+                        var distY = pointA.y - pointB.y;
+                        var pinchSquaredDistance = (distX * distX) + (distY * distY);
+                        if (previousPinchDistance === 0) {
+                            previousPinchDistance = pinchSquaredDistance;
+                            return;
+                        }
+
+                        if (pinchSquaredDistance !== previousPinchDistance) {
+                            this.camera
+                                .inertialRadiusOffset += (pinchSquaredDistance - previousPinchDistance) /
+                                (this.pinchPrecision *
+                                    ((this.angularSensibilityX + this.angularSensibilityY) / 2) *
+                                    direction);
+                            previousPinchDistance = pinchSquaredDistance;
+                        }
                     }
                     }
                 }
                 }
             }
             }
@@ -143,9 +143,7 @@ module BABYLON {
 
 
             this._onLostFocus = () => {
             this._onLostFocus = () => {
                 //this._keys = [];
                 //this._keys = [];
-                pointers.clear();
-                pointersOrder = new Array<number>();
-                curPointerCount = 0;
+                pointA = pointB = undefined;
                 previousPinchDistance = 0;
                 previousPinchDistance = 0;
                 cacheSoloPointer = null;
                 cacheSoloPointer = null;
             };
             };

+ 38 - 3
src/Tools/babylon.stringDictionary.js

@@ -7,6 +7,7 @@ var BABYLON;
      */
      */
     var StringDictionary = (function () {
     var StringDictionary = (function () {
         function StringDictionary() {
         function StringDictionary() {
+            this._count = 0;
             this._data = {};
             this._data = {};
         }
         }
         /**
         /**
@@ -29,7 +30,7 @@ var BABYLON;
          * The factory will only be invoked if there's no data for the given key.
          * The factory will only be invoked if there's no data for the given key.
          * @return the value corresponding to the key.
          * @return the value corresponding to the key.
          */
          */
-        StringDictionary.prototype.getOrAdd = function (key, factory) {
+        StringDictionary.prototype.getOrAddWithFactory = function (key, factory) {
             var val = this.get(key);
             var val = this.get(key);
             if (val !== undefined) {
             if (val !== undefined) {
                 return val;
                 return val;
@@ -41,6 +42,20 @@ var BABYLON;
             return val;
             return val;
         };
         };
         /**
         /**
+         * Get a value from its key if present in the dictionary otherwise add it
+         * @param key the key to get the value from
+         * @param val if there's no such key/value pair in the dictionary add it with this value
+         * @return the value corresponding to the key
+         */
+        StringDictionary.prototype.getOrAdd = function (key, val) {
+            var val = this.get(key);
+            if (val !== undefined) {
+                return val;
+            }
+            this.add(key, val);
+            return val;
+        };
+        /**
          * Check if there's a given key in the dictionary
          * Check if there's a given key in the dictionary
          * @param key the key to check for
          * @param key the key to check for
          * @return true if the key is present, false otherwise
          * @return true if the key is present, false otherwise
@@ -59,16 +74,36 @@ var BABYLON;
                 return false;
                 return false;
             }
             }
             this._data[key] = value;
             this._data[key] = value;
+            ++this._count;
             return true;
             return true;
         };
         };
         /**
         /**
          * Remove a key/value from the dictionary.
          * Remove a key/value from the dictionary.
-         * For performance reason, calling this method won't tell you if there was the given key in the dictionary
          * @param key the key to remove
          * @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
          */
          */
         StringDictionary.prototype.remove = function (key) {
         StringDictionary.prototype.remove = function (key) {
-            delete this._data[key];
+            if (this.contains(key)) {
+                delete this._data[key];
+                --this._count;
+                return true;
+            }
+            return false;
+        };
+        /**
+         * Clear the whole content of the dictionary
+         */
+        StringDictionary.prototype.clear = function () {
+            this._data = {};
+            this._count = 0;
         };
         };
+        Object.defineProperty(StringDictionary.prototype, "count", {
+            get: function () {
+                return this._count;
+            },
+            enumerable: true,
+            configurable: true
+        });
         /**
         /**
          * Execute a callback on each key/val of the dictionary.
          * Execute a callback on each key/val of the dictionary.
          * Note that you can remove any element in this dictionary in the callback implementation
          * Note that you can remove any element in this dictionary in the callback implementation

+ 17 - 1
src/Tools/babylon.stringDictionary.ts

@@ -26,7 +26,7 @@
          * The factory will only be invoked if there's no data for the given key.
          * The factory will only be invoked if there's no data for the given key.
          * @return the value corresponding to the key.
          * @return the value corresponding to the key.
          */
          */
-        public getOrAdd(key: string, factory: (key: string) => T): T {
+        public getOrAddWithFactory(key: string, factory: (key: string) => T): T {
             var val = this.get(key);
             var val = this.get(key);
             if (val !== undefined) {
             if (val !== undefined) {
                 return val;
                 return val;
@@ -41,6 +41,22 @@
         }
         }
 
 
         /**
         /**
+         * Get a value from its key if present in the dictionary otherwise add it
+         * @param key the key to get the value from
+         * @param val if there's no such key/value pair in the dictionary add it with this value
+         * @return the value corresponding to the key
+         */
+        public getOrAdd(key: string, val: T): T {
+            var val = this.get(key);
+            if (val !== undefined) {
+                return val;
+            }
+
+            this.add(key, val);
+            return val;
+        }
+
+        /**
          * Check if there's a given key in the dictionary
          * Check if there's a given key in the dictionary
          * @param key the key to check for
          * @param key the key to check for
          * @return true if the key is present, false otherwise
          * @return true if the key is present, false otherwise