UNPKG

shadow-function

Version:

ioing lib - shadow Function, worker Function

338 lines (308 loc) 9.11 kB
//Region is used to label a rect which world axis aligned. var Region = function () { this._minX = 0; this._minY = 0; this._maxX = 0; this._maxY = 0; this._width = 0; this._height = 0; this._area = 0; //this.moved = false; }; var regionProto = Region.prototype; var regionPool = []; function regionCreate() { var region = regionPool.pop(); if (!region) { region = new Region(); } return region; } function regionRelease(region) { regionPool.push(region); } regionProto.setTo = function (minX, minY, maxX, maxY) { this._minX = minX; this._minY = minY; this._maxX = maxX; this._maxY = maxY; this.updateArea(); return this; }; //convert region to int values which is fast for clipping regionProto.intValues = function () { this._minX = Math.floor(this._minX); this._minY = Math.floor(this._minY); this._maxX = Math.ceil(this._maxX); this._maxY = Math.ceil(this._maxY); this.updateArea(); }; //update the area of region regionProto.updateArea = function () { this._width = this._maxX - this._minX; this._height = this._maxY - this._minY; this._area = this._width * this._height; }; //merge two region into one regionProto.union = function (target) { if(this._width <= 0 || this._height <= 0) { this.setTo(target._minX, target._minY, target._maxX, target._maxY); return; } if (this._minX > target._minX) { this._minX = target._minX; } if (this._minY > target._minY) { this._minY = target._minY; } if (this._maxX < target._maxX) { this._maxX = target._maxX; } if (this._maxY < target._maxY) { this._maxY = target._maxY; } this.updateArea(); }; //regionProto.intersect = function (target) { // if (this._minX < target._minX) { // this._minX = target._minX; // } // if (this._maxX > target._maxX) { // this._maxX = target._maxX; // } // if (this._minX >= this._maxX) { // this.setEmpty(); // return; // } // if (this._minY < target._minY) { // this._minY = target._minY; // } // // if (this._maxY > target._maxY) { // this._maxY = target._maxY; // } // if (this._minY >= this._maxY) { // this.setEmpty(); // return; // } // this.updateArea(); //}; //set region to empty regionProto.setEmpty = function () { this._minX = 0; this._minY = 0; this._maxX = 0; this._maxY = 0; this._width = 0; this._height = 0; this._area = 0; }; regionProto.isEmpty = function () { return this._width <= 0 || this._height <= 0; }; //check whether two region is intersects or not regionProto.intersects = function (target) { if (this._width <= 0 || this._height <= 0 || target._width <= 0 || target._height <= 0) { return false; } var max = this._minX > target._minX ? this._minX : target._minX; var min = this._maxX < target._maxX ? this._maxX : target._maxX; if (max > min) { return false; } max = this._minY > target._minY ? this._minY : target._minY; min = this._maxY < target._maxY ? this._maxY : target._maxY; return max <= min; }; //update region by a rotated bounds regionProto.updateRegion = function (bounds, matrix) { if (bounds.width == 0 || bounds.height == 0) { this.setEmpty(); return; } var m = matrix; var a = m.a; var b = m.b; var c = m.c; var d = m.d; var tx = m.tx; var ty = m.ty; var x = bounds.x; var y = bounds.y; var xMax = x + bounds.width; var yMax = y + bounds.height; var minX, minY, maxX, maxY; if (a == 1.0 && b == 0.0 && c == 0.0 && d == 1.0) { minX = x + tx - 1; minY = y + ty - 1; maxX = xMax + tx + 1; maxY = yMax + ty + 1; } else { var x0 = a * x + c * y + tx; var y0 = b * x + d * y + ty; var x1 = a * xMax + c * y + tx; var y1 = b * xMax + d * y + ty; var x2 = a * xMax + c * yMax + tx; var y2 = b * xMax + d * yMax + ty; var x3 = a * x + c * yMax + tx; var y3 = b * x + d * yMax + ty; var tmp = 0; if (x0 > x1) { tmp = x0; x0 = x1; x1 = tmp; } if (x2 > x3) { tmp = x2; x2 = x3; x3 = tmp; } minX = (x0 < x2 ? x0 : x2) - 1; maxX = (x1 > x3 ? x1 : x3) + 1; if (y0 > y1) { tmp = y0; y0 = y1; y1 = tmp; } if (y2 > y3) { tmp = y2; y2 = y3; y3 = tmp; } minY = (y0 < y2 ? y0 : y2) - 1; maxY = (y1 > y3 ? y1 : y3) + 1; } this._minX = minX; this._minY = minY; this._maxX = maxX; this._maxY = maxY; this._width = maxX - minX; this._height = maxY - minY; this._area = this._width * this._height; }; //get the area of the unioned region of r1 and r2 function unionArea(r1, r2) { var minX = r1._minX < r2._minX ? r1._minX : r2._minX; var minY = r1._minY < r2._minY ? r1._minY : r2._minY; var maxX = r1._maxX > r2._maxX ? r1._maxX : r2._maxX; var maxY = r1._maxY > r2._maxY ? r1._maxY : r2._maxY; return (maxX - minX) * (maxY - minY); } //DirtyRegion is used to collect the dirty area which need to be rerendered in canvas //there may be many small regions which is dirty, the dirty region will merge it into several big one to optimise performance var DirtyRegion = function() { this.dirtyList = []; this.hasClipRect = false; this.clipWidth = 0; this.clipHeight = 0; this.clipArea = 0; this.clipRectChanged = false; }; var dirtyRegionProto = DirtyRegion.prototype; //clip rect, regions will not be considered if it is outside dirtyRegionProto.setClipRect = function(width, height) { this.hasClipRect = true; this.clipRectChanged = true; this.clipWidth = Math.ceil(width); this.clipHeight = Math.ceil(height); this.clipArea = this.clipWidth * this.clipHeight; }; //add a new region which is dirty (need to be rendered) dirtyRegionProto.addRegion = function(target) { var minX = target._minX, minY = target._minY, maxX = target._maxX, maxY = target._maxY; if (this.hasClipRect) { if (minX < 0) { minX = 0; } if (minY < 0) { minY = 0; } if (maxX > this.clipWidth) { maxX = this.clipWidth; } if (maxY > this.clipHeight) { maxY = this.clipHeight; } } if (minX >= maxX || minY >= maxY) { return false; } if (this.clipRectChanged) { return true; } var dirtyList = this.dirtyList; var region = regionCreate(); dirtyList.push(region.setTo(minX, minY, maxX, maxY)); this.mergeDirtyList(dirtyList); return true; }; //clear all the dirty regions dirtyRegionProto.clear = function() { var dirtyList = this.dirtyList; var length = dirtyList.length; for (var i = 0; i < length; i++) { regionRelease(dirtyList[i]); } dirtyList.length = 0; }; //get the merged dirty regions dirtyRegionProto.getDirtyRegions = function() { var dirtyList = this.dirtyList; if (this.clipRectChanged) { this.clipRectChanged = false; this.clear(); var region = regionCreate(); dirtyList.push(region.setTo(0, 0, this.clipWidth, this.clipHeight)); } else { while (this.mergeDirtyList(dirtyList)) { } } var numDirty = this.dirtyList.length; if (numDirty > 0) { for (var i = 0; i < numDirty; i++) { this.dirtyList[i].intValues(); } } return this.dirtyList; }; //merge the small dirty regions into bigger region, to improve the performance of dirty regions dirtyRegionProto.mergeDirtyList = function(dirtyList) { var length = dirtyList.length; if (length < 2) { return false; } var hasClipRect = this.hasClipRect; var bestDelta = length > 3 ? Number.POSITIVE_INFINITY : 0; var mergeA = 0; var mergeB = 0; var totalArea = 0; for (var i = 0; i < length - 1; i++) { var regionA = dirtyList[i]; hasClipRect && (totalArea += regionA.area); for (var j = i + 1; j < length; j++) { var regionB = dirtyList[j]; var delta = unionArea(regionA, regionB) - regionA.area - regionB.area; if (bestDelta > delta) { mergeA = i; mergeB = j; bestDelta = delta; } } } //if the area of dirty region exceed 95% of the screen, skip the following dirty regions merge if (hasClipRect && (totalArea / this.clipArea) > 0.95) { this.clipRectChanged = true; } if (mergeA != mergeB) { var region = dirtyList[mergeB]; dirtyList[mergeA].union(region); regionRelease(region); dirtyList.splice(mergeB, 1); return true; } return false; }; cc.Region = Region; cc.DirtyRegion = DirtyRegion;