Преглед изворни кода

Canvas2D: improving intersection, perf and fixes

Intersection in Rectangle2D is now considering the round Radius.

Improving perf of pointer handler by preparing the Canvas state outside of it.
Improving perf of BoundingInfo2D.transformToRef (but a lot more left is still to be down)

Typo in the PrepareRender2DContext class is now fixed
nockawa пре 9 година
родитељ
комит
3cee205322

+ 11 - 5
src/Canvas2d/babylon.bounding2d.ts

@@ -135,6 +135,8 @@
             return r;
         }
 
+        private static _transform: Array<Vector2> = new Array<Vector2>(Vector2.Zero(), Vector2.Zero(), Vector2.Zero(), Vector2.Zero());
+
         /**
          * Transform this BoundingInfo2D with a given matrix and store the result in an existing BoundingInfo2D instance.
          * This is a GC friendly version, try to use it as much as possible, specially if your transformation is inside a loop, allocate the result object once for good outside of the loop and use it every time.
@@ -143,11 +145,15 @@
          */
         public transformToRef(matrix: Matrix, result: BoundingInfo2D) {
             // Construct a bounding box based on the extent values
-            let p = new Array<Vector2>(4);
-            p[0] = new Vector2(this.center.x + this.extent.x, this.center.y + this.extent.y);
-            p[1] = new Vector2(this.center.x + this.extent.x, this.center.y - this.extent.y);
-            p[2] = new Vector2(this.center.x - this.extent.x, this.center.y - this.extent.y);
-            p[3] = new Vector2(this.center.x - this.extent.x, this.center.y + this.extent.y);
+            let p = BoundingInfo2D._transform;
+            p[0].x = this.center.x + this.extent.x;
+            p[0].y = this.center.y + this.extent.y;
+            p[1].x = this.center.x + this.extent.x;
+            p[1].y = this.center.y - this.extent.y;
+            p[2].x = this.center.x - this.extent.x;
+            p[2].y = this.center.y - this.extent.y;
+            p[3].x = this.center.x - this.extent.x;
+            p[3].y = this.center.y + this.extent.y;
 
             // Transform the four points of the bounding box with the matrix
             for (let i = 0; i < 4; i++) {

+ 1 - 3
src/Canvas2d/babylon.canvas2d.ts

@@ -453,8 +453,6 @@
                 return;
             }
 
-            this._updateCanvasState();
-
             this.intersect(ii);
 
             this._previousIntersectionList = this._actualIntersectionList;
@@ -902,7 +900,7 @@
                 }
             }
 
-            var context = new PreapreRender2DContext();
+            var context = new PrepareRender2DContext();
 
             ++this._globalTransformProcessStep;
             this.updateGlobalTransVis(false);

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

@@ -205,7 +205,7 @@
 
         public _renderCachedCanvas() {
             this.updateGlobalTransVis(true);
-            let context = new PreapreRender2DContext();
+            let context = new PrepareRender2DContext();
             this._prepareGroupRender(context);
             this._groupRender();
         }
@@ -231,7 +231,7 @@
         }
 
         // Method called only on renderable groups to prepare the rendering
-        protected _prepareGroupRender(context: PreapreRender2DContext) {
+        protected _prepareGroupRender(context: PrepareRender2DContext) {
             let sortedDirtyList: Prim2DBase[] = null;
 
             // Update the Global Transformation and visibility status of the changed primitives

+ 5 - 5
src/Canvas2d/babylon.prim2dBase.ts

@@ -1,6 +1,6 @@
 module BABYLON {
 
-    export class PreapreRender2DContext {
+    export class PrepareRender2DContext {
         constructor() {
             this.forceRefreshPrimitive = false;
         }
@@ -799,7 +799,7 @@
         }
 
         /**
-         * Get the boundingInfo associated to the primitive.
+         * Get the boundingInfo associated to the primitive and its children.
          * The value is supposed to be always up to date
          */
         public get boundingInfo(): BoundingInfo2D {
@@ -998,15 +998,15 @@
             return this._visibilityChanged || this._modelDirty || (this._instanceDirtyFlags !== 0) || (this._globalTransformProcessStep !== this._globalTransformStep);
         }
 
-        public _prepareRender(context: PreapreRender2DContext) {
+        public _prepareRender(context: PrepareRender2DContext) {
             this._prepareRenderPre(context);
             this._prepareRenderPost(context);
         }
 
-        public _prepareRenderPre(context: PreapreRender2DContext) {
+        public _prepareRenderPre(context: PrepareRender2DContext) {
         }
 
-        public _prepareRenderPost(context: PreapreRender2DContext) {
+        public _prepareRenderPost(context: PrepareRender2DContext) {
             // Don't recurse if it's a renderable group, the content will be processed by the group itself
             if (this instanceof Group2D) {
                 var self: any = this;

+ 68 - 2
src/Canvas2d/babylon.rectangle2d.ts

@@ -203,14 +203,80 @@
             this.notRounded = value === 0;
         }
 
+        private static _i0 = Vector2.Zero();
+        private static _i1 = Vector2.Zero();
+        private static _i2 = Vector2.Zero();
+
         protected levelIntersect(intersectInfo: IntersectInfo2D): boolean {
             // If we got there it mean the boundingInfo intersection succeed, if the rectangle has not roundRadius, it means it succeed!
             if (this.notRounded) {
                 return true;
             }
 
-            // Well, for now we neglect the area where the pickPosition could be outside due to the roundRadius...
-            // TODO make REAL intersection test here!
+            // If we got so far it means the bounding box at least passed, so we know it's inside the bounding rectangle, but it can be outside the roundedRectangle.
+            // The easiest way is to check if the point is inside on of the four corners area (a little square of roundRadius size at the four corners)
+            // If it's the case for one, check if the mouse is located in the quarter that we care about (the one who is visible) then finally make a distance check with the roundRadius radius to see if it's inside the circle quarter or outside.
+
+            // First let remove the origin out the equation, to have the rectangle with an origin at bottom/left
+            let o = this.origin;
+            let size = this.size;
+            Rectangle2D._i0.x = intersectInfo._localPickPosition.x + (size.width * o.x);
+            Rectangle2D._i0.y = intersectInfo._localPickPosition.y + (size.height * o.y);
+
+            let rr = this.roundRadius;
+            let rrs = rr * rr;
+
+            // Check if the point is in the bottom/left quarter area
+            Rectangle2D._i1.x = rr;
+            Rectangle2D._i1.y = rr;
+            if (Rectangle2D._i0.x <= Rectangle2D._i1.x && Rectangle2D._i0.y <= Rectangle2D._i1.y) {
+                // Compute the intersection point in the quarter local space
+                Rectangle2D._i2.x = Rectangle2D._i0.x - Rectangle2D._i1.x;
+                Rectangle2D._i2.y = Rectangle2D._i0.y - Rectangle2D._i1.y;
+
+                // It's a hit if the squared distance is less/equal to the squared radius of the round circle
+                return Rectangle2D._i2.lengthSquared() <= rrs;
+            }
+
+            // Check if the point is in the top/left quarter area
+            Rectangle2D._i1.x = rr;
+            Rectangle2D._i1.y = size.height - rr;
+            if (Rectangle2D._i0.x <= Rectangle2D._i1.x && Rectangle2D._i0.y >= Rectangle2D._i1.y) {
+                // Compute the intersection point in the quarter local space
+                Rectangle2D._i2.x = Rectangle2D._i0.x - Rectangle2D._i1.x;
+                Rectangle2D._i2.y = Rectangle2D._i0.y - Rectangle2D._i1.y;
+
+                // It's a hit if the squared distance is less/equal to the squared radius of the round circle
+                return Rectangle2D._i2.lengthSquared() <= rrs;
+            }
+
+            // Check if the point is in the top/right quarter area
+            Rectangle2D._i1.x = size.width - rr;
+            Rectangle2D._i1.y = size.height - rr;
+            if (Rectangle2D._i0.x >= Rectangle2D._i1.x && Rectangle2D._i0.y >= Rectangle2D._i1.y) {
+                // Compute the intersection point in the quarter local space
+                Rectangle2D._i2.x = Rectangle2D._i0.x - Rectangle2D._i1.x;
+                Rectangle2D._i2.y = Rectangle2D._i0.y - Rectangle2D._i1.y;
+
+                // It's a hit if the squared distance is less/equal to the squared radius of the round circle
+                return Rectangle2D._i2.lengthSquared() <= rrs;
+            }
+
+
+            // Check if the point is in the bottom/right quarter area
+            Rectangle2D._i1.x = size.width - rr;
+            Rectangle2D._i1.y = rr;
+            if (Rectangle2D._i0.x >= Rectangle2D._i1.x && Rectangle2D._i0.y <= Rectangle2D._i1.y) {
+                // Compute the intersection point in the quarter local space
+                Rectangle2D._i2.x = Rectangle2D._i0.x - Rectangle2D._i1.x;
+                Rectangle2D._i2.y = Rectangle2D._i0.y - Rectangle2D._i1.y;
+
+                // It's a hit if the squared distance is less/equal to the squared radius of the round circle
+                return Rectangle2D._i2.lengthSquared() <= rrs;
+            }
+
+            // At any other locations the point is guarantied to be inside
+
             return true;
         }
 

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

@@ -385,7 +385,7 @@
             return true;
         }
 
-        public _prepareRenderPre(context: PreapreRender2DContext) {
+        public _prepareRenderPre(context: PrepareRender2DContext) {
             super._prepareRenderPre(context);
 
             // If the model changed and we have already an instance, we must remove this instance from the obsolete model