新增maxRectsBinPack
This commit is contained in:
9
.gitmodules
vendored
9
.gitmodules
vendored
@@ -4,3 +4,12 @@
|
|||||||
[submodule "demo/laya_demo"]
|
[submodule "demo/laya_demo"]
|
||||||
path = demo/laya_demo
|
path = demo/laya_demo
|
||||||
url = https://github.com/esengine/ecs-laya-demo.git
|
url = https://github.com/esengine/ecs-laya-demo.git
|
||||||
|
[submodule "extensions/ecs-star"]
|
||||||
|
path = extensions/ecs-star
|
||||||
|
url = https://github.com/esengine/ecs-astar
|
||||||
|
[submodule "extensions/behaviourTree-ai"]
|
||||||
|
path = extensions/behaviourTree-ai
|
||||||
|
url = https://github.com/esengine/BehaviourTree-ai
|
||||||
|
[submodule "engine_support/egret"]
|
||||||
|
path = engine_support/egret
|
||||||
|
url = https://github.com/esengine/egret-ecs-ext.git
|
||||||
|
|||||||
Submodule demo/egret_demo updated: 678dd72ede...4d55df721a
Submodule demo/laya_demo updated: e8df98d44e...53efc1f443
1
engine_support/egret
Submodule
1
engine_support/egret
Submodule
Submodule engine_support/egret added at 4f4208f8b6
1
extensions/behaviourTree-ai
Submodule
1
extensions/behaviourTree-ai
Submodule
Submodule extensions/behaviourTree-ai added at 052527601d
1
extensions/ecs-star
Submodule
1
extensions/ecs-star
Submodule
Submodule extensions/ecs-star added at bed93ef0e0
16
source/bin/framework.d.ts
vendored
16
source/bin/framework.d.ts
vendored
@@ -3575,6 +3575,22 @@ declare module es {
|
|||||||
tickCoroutine(coroutine: CoroutineImpl): boolean;
|
tickCoroutine(coroutine: CoroutineImpl): boolean;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
declare module es {
|
||||||
|
class MaxRectsBinPack {
|
||||||
|
binWidth: number;
|
||||||
|
binHeight: number;
|
||||||
|
allowRotations: boolean;
|
||||||
|
usedRectangles: Rectangle[];
|
||||||
|
freeRectangles: Rectangle[];
|
||||||
|
constructor(width: number, height: number, rotations?: boolean);
|
||||||
|
init(width: number, height: number, rotations?: boolean): void;
|
||||||
|
insert(width: number, height: number): Rectangle;
|
||||||
|
findPositionForNewNodeBestAreaFit(width: number, height: number, bestAreaFit: Ref<number>, bestShortSideFit: Ref<number>): Rectangle;
|
||||||
|
splitFreeNode(freeNode: Rectangle, usedNode: Rectangle): boolean;
|
||||||
|
pruneFreeList(): void;
|
||||||
|
isContainedIn(a: Rectangle, b: Rectangle): boolean;
|
||||||
|
}
|
||||||
|
}
|
||||||
declare class ArrayUtils {
|
declare class ArrayUtils {
|
||||||
/**
|
/**
|
||||||
* 执行冒泡排序
|
* 执行冒泡排序
|
||||||
|
|||||||
@@ -8995,6 +8995,145 @@ var es;
|
|||||||
}(es.GlobalManager));
|
}(es.GlobalManager));
|
||||||
es.CoroutineManager = CoroutineManager;
|
es.CoroutineManager = CoroutineManager;
|
||||||
})(es || (es = {}));
|
})(es || (es = {}));
|
||||||
|
var es;
|
||||||
|
(function (es) {
|
||||||
|
var MaxRectsBinPack = /** @class */ (function () {
|
||||||
|
function MaxRectsBinPack(width, height, rotations) {
|
||||||
|
if (rotations === void 0) { rotations = true; }
|
||||||
|
this.binWidth = 0;
|
||||||
|
this.binHeight = 0;
|
||||||
|
this.usedRectangles = [];
|
||||||
|
this.freeRectangles = [];
|
||||||
|
this.init(width, height, rotations);
|
||||||
|
}
|
||||||
|
MaxRectsBinPack.prototype.init = function (width, height, rotations) {
|
||||||
|
if (rotations === void 0) { rotations = true; }
|
||||||
|
this.binWidth = width;
|
||||||
|
this.binHeight = height;
|
||||||
|
this.allowRotations = rotations;
|
||||||
|
var n = new es.Rectangle();
|
||||||
|
n.x = 0;
|
||||||
|
n.y = 0;
|
||||||
|
n.width = width;
|
||||||
|
n.height = height;
|
||||||
|
this.usedRectangles.length = 0;
|
||||||
|
this.freeRectangles.length = 0;
|
||||||
|
this.freeRectangles.push(n);
|
||||||
|
};
|
||||||
|
MaxRectsBinPack.prototype.insert = function (width, height) {
|
||||||
|
var newNode = new es.Rectangle();
|
||||||
|
var score1 = new es.Ref(0);
|
||||||
|
var score2 = new es.Ref(0);
|
||||||
|
newNode = this.findPositionForNewNodeBestAreaFit(width, height, score1, score2);
|
||||||
|
if (newNode.height == 0)
|
||||||
|
return newNode;
|
||||||
|
var numRectanglesToProcess = this.freeRectangles.length;
|
||||||
|
for (var i = 0; i < numRectanglesToProcess; ++i) {
|
||||||
|
if (this.splitFreeNode(this.freeRectangles[i], newNode)) {
|
||||||
|
new linq.List(this.freeRectangles).removeAt(i);
|
||||||
|
--i;
|
||||||
|
--numRectanglesToProcess;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.pruneFreeList();
|
||||||
|
this.usedRectangles.push(newNode);
|
||||||
|
return newNode;
|
||||||
|
};
|
||||||
|
MaxRectsBinPack.prototype.findPositionForNewNodeBestAreaFit = function (width, height, bestAreaFit, bestShortSideFit) {
|
||||||
|
var bestNode = new es.Rectangle();
|
||||||
|
bestAreaFit.value = Number.MAX_VALUE;
|
||||||
|
for (var i = 0; i < this.freeRectangles.length; ++i) {
|
||||||
|
var areaFit = this.freeRectangles[i].width * this.freeRectangles[i].height - width * height;
|
||||||
|
// 试着将长方形放在直立(非翻转)的方向
|
||||||
|
if (this.freeRectangles[i].width >= width && this.freeRectangles[i].height >= height) {
|
||||||
|
var leftoverHoriz = Math.abs(this.freeRectangles[i].width - width);
|
||||||
|
var leftoverVert = Math.abs(this.freeRectangles[i].height - height);
|
||||||
|
var shortSideFit = Math.min(leftoverHoriz, leftoverVert);
|
||||||
|
if (areaFit < bestAreaFit.value || (areaFit == bestAreaFit.value && shortSideFit < bestShortSideFit.value)) {
|
||||||
|
bestNode.x = this.freeRectangles[i].x;
|
||||||
|
bestNode.y = this.freeRectangles[i].y;
|
||||||
|
bestNode.width = width;
|
||||||
|
bestNode.height = height;
|
||||||
|
bestShortSideFit.value = shortSideFit;
|
||||||
|
bestAreaFit.value = areaFit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.allowRotations && this.freeRectangles[i].width >= height && this.freeRectangles[i].height >= width) {
|
||||||
|
var leftoverHoriz = Math.abs(this.freeRectangles[i].width - height);
|
||||||
|
var leftoverVert = Math.abs(this.freeRectangles[i].height - width);
|
||||||
|
var shortSideFit = Math.min(leftoverHoriz, leftoverVert);
|
||||||
|
if (areaFit < bestAreaFit.value || (areaFit == bestAreaFit.value && shortSideFit < bestShortSideFit.value)) {
|
||||||
|
bestNode.x = this.freeRectangles[i].x;
|
||||||
|
bestNode.y = this.freeRectangles[i].y;
|
||||||
|
bestNode.width = height;
|
||||||
|
bestNode.height = width;
|
||||||
|
bestShortSideFit.value = shortSideFit;
|
||||||
|
bestAreaFit.value = areaFit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bestNode;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
MaxRectsBinPack.prototype.splitFreeNode = function (freeNode, usedNode) {
|
||||||
|
// 用SAT测试长方形是否均匀相交
|
||||||
|
if (usedNode.x >= freeNode.x + freeNode.width || usedNode.x + usedNode.width <= freeNode.x ||
|
||||||
|
usedNode.y >= freeNode.y + freeNode.height || usedNode.y + usedNode.height <= freeNode.y)
|
||||||
|
return false;
|
||||||
|
if (usedNode.x < freeNode.x + freeNode.width && usedNode.x + usedNode.width > freeNode.x) {
|
||||||
|
// 在使用过的节点的上边新建一个节点
|
||||||
|
if (usedNode.y > freeNode.y && usedNode.y < freeNode.y + freeNode.height) {
|
||||||
|
var newNode = freeNode;
|
||||||
|
newNode.height = usedNode.y - newNode.y;
|
||||||
|
this.freeRectangles.push(newNode);
|
||||||
|
}
|
||||||
|
// 在使用过的节点的底边新建节点
|
||||||
|
if (usedNode.y + usedNode.height < freeNode.y + freeNode.height) {
|
||||||
|
var newNode = freeNode;
|
||||||
|
newNode.y = usedNode.y + usedNode.height;
|
||||||
|
newNode.height = freeNode.y + freeNode.height - (usedNode.y + usedNode.height);
|
||||||
|
this.freeRectangles.push(newNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (usedNode.y < freeNode.y + freeNode.height && usedNode.y + usedNode.height > freeNode.y) {
|
||||||
|
// 在使用过的节点的左侧新建节点
|
||||||
|
if (usedNode.x > freeNode.x && usedNode.x < freeNode.x + freeNode.width) {
|
||||||
|
var newNode = freeNode;
|
||||||
|
newNode.width = usedNode.x - newNode.x;
|
||||||
|
this.freeRectangles.push(newNode);
|
||||||
|
}
|
||||||
|
// 在使用过的节点右侧新建节点
|
||||||
|
if (usedNode.x + usedNode.width < freeNode.x + freeNode.width) {
|
||||||
|
var newNode = freeNode;
|
||||||
|
newNode.x = usedNode.x + usedNode.width;
|
||||||
|
newNode.width = freeNode.x + freeNode.width - (usedNode.x + usedNode.width);
|
||||||
|
this.freeRectangles.push(newNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
MaxRectsBinPack.prototype.pruneFreeList = function () {
|
||||||
|
for (var i = 0; i < this.freeRectangles.length; ++i)
|
||||||
|
for (var j = i + 1; j < this.freeRectangles.length; ++j) {
|
||||||
|
if (this.isContainedIn(this.freeRectangles[i], this.freeRectangles[j])) {
|
||||||
|
new linq.List(this.freeRectangles).removeAt(i);
|
||||||
|
--i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (this.isContainedIn(this.freeRectangles[j], this.freeRectangles[i])) {
|
||||||
|
new linq.List(this.freeRectangles).removeAt(j);
|
||||||
|
--j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
MaxRectsBinPack.prototype.isContainedIn = function (a, b) {
|
||||||
|
return a.x >= b.x && a.y >= b.y
|
||||||
|
&& a.x + a.width <= b.x + b.width
|
||||||
|
&& a.y + a.height <= b.y + b.height;
|
||||||
|
};
|
||||||
|
return MaxRectsBinPack;
|
||||||
|
}());
|
||||||
|
es.MaxRectsBinPack = MaxRectsBinPack;
|
||||||
|
})(es || (es = {}));
|
||||||
var ArrayUtils = /** @class */ (function () {
|
var ArrayUtils = /** @class */ (function () {
|
||||||
function ArrayUtils() {
|
function ArrayUtils() {
|
||||||
}
|
}
|
||||||
|
|||||||
2
source/bin/framework.min.js
vendored
2
source/bin/framework.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -12,7 +12,7 @@ gulp.task('buildJs', () => {
|
|||||||
.js.pipe(inject.replace('var es;', ''))
|
.js.pipe(inject.replace('var es;', ''))
|
||||||
.pipe(inject.prepend('window.es = {};\n'))
|
.pipe(inject.prepend('window.es = {};\n'))
|
||||||
.pipe(inject.replace('var __extends =', 'window.__extends ='))
|
.pipe(inject.replace('var __extends =', 'window.__extends ='))
|
||||||
.pipe(minify({ext: {min: ".min.js"}}))
|
.pipe(minify({ ext: { min: ".min.js" } }))
|
||||||
.pipe(gulp.dest('./bin'));
|
.pipe(gulp.dest('./bin'));
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -26,13 +26,18 @@ gulp.task("buildDts", ["buildJs"], () => {
|
|||||||
gulp.task("copy", ["buildDts"], () => {
|
gulp.task("copy", ["buildDts"], () => {
|
||||||
return gulp.src('bin/**/*')
|
return gulp.src('bin/**/*')
|
||||||
.pipe(gulp.dest('../demo/egret_demo/libs/framework/'))
|
.pipe(gulp.dest('../demo/egret_demo/libs/framework/'))
|
||||||
|
.pipe(gulp.dest('../extensions/behaviourTree-ai/egret-demo/libs/framework/'))
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('build', ['copy'], ()=>{
|
gulp.task('build', ['copy'], () => {
|
||||||
return merge([
|
return merge([
|
||||||
gulp.src('bin/*.js')
|
gulp.src('bin/*.js')
|
||||||
.pipe(gulp.dest('../demo/laya_demo/bin/libs/')),
|
.pipe(gulp.dest('../demo/laya_demo/bin/libs/')),
|
||||||
gulp.src('bin/*.ts')
|
gulp.src('bin/*.ts')
|
||||||
.pipe(gulp.dest('../demo/laya_demo/libs/'))
|
.pipe(gulp.dest('../demo/laya_demo/libs/')),
|
||||||
|
gulp.src('bin/framework.d.ts')
|
||||||
|
.pipe(gulp.dest('../extensions/behaviourTree-ai/source/lib/'))
|
||||||
|
.pipe(gulp.dest('../extensions/ecs-star/lib/'))
|
||||||
|
.pipe(gulp.dest('../engine_support/egret/lib/'))
|
||||||
])
|
])
|
||||||
});
|
});
|
||||||
162
source/src/Utils/DynamticAtlas/MaxRectsBinPack.ts
Normal file
162
source/src/Utils/DynamticAtlas/MaxRectsBinPack.ts
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
module es {
|
||||||
|
export class MaxRectsBinPack {
|
||||||
|
public binWidth: number = 0;
|
||||||
|
public binHeight: number = 0;
|
||||||
|
public allowRotations: boolean;
|
||||||
|
|
||||||
|
public usedRectangles: Rectangle[] = [];
|
||||||
|
public freeRectangles: Rectangle[] = [];
|
||||||
|
|
||||||
|
constructor(width: number, height: number, rotations: boolean = true) {
|
||||||
|
this.init(width, height, rotations);
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(width: number, height: number, rotations: boolean = true) {
|
||||||
|
this.binWidth = width;
|
||||||
|
this.binHeight = height;
|
||||||
|
this.allowRotations = rotations;
|
||||||
|
|
||||||
|
let n = new Rectangle();
|
||||||
|
n.x = 0;
|
||||||
|
n.y = 0;
|
||||||
|
n.width = width;
|
||||||
|
n.height = height;
|
||||||
|
|
||||||
|
this.usedRectangles.length = 0;
|
||||||
|
|
||||||
|
this.freeRectangles.length = 0;
|
||||||
|
this.freeRectangles.push(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
public insert(width: number, height: number): Rectangle {
|
||||||
|
let newNode = new Rectangle();
|
||||||
|
let score1 = new Ref(0);
|
||||||
|
let score2 = new Ref(0);
|
||||||
|
newNode = this.findPositionForNewNodeBestAreaFit(width, height, score1, score2);
|
||||||
|
|
||||||
|
if (newNode.height == 0)
|
||||||
|
return newNode;
|
||||||
|
|
||||||
|
let numRectanglesToProcess = this.freeRectangles.length;
|
||||||
|
for (let i = 0; i < numRectanglesToProcess; ++i) {
|
||||||
|
if (this.splitFreeNode(this.freeRectangles[i], newNode)) {
|
||||||
|
new linq.List(this.freeRectangles).removeAt(i);
|
||||||
|
--i;
|
||||||
|
--numRectanglesToProcess;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.pruneFreeList();
|
||||||
|
|
||||||
|
this.usedRectangles.push(newNode);
|
||||||
|
return newNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public findPositionForNewNodeBestAreaFit(width: number, height: number, bestAreaFit: Ref<number>, bestShortSideFit: Ref<number>) {
|
||||||
|
let bestNode = new Rectangle();
|
||||||
|
|
||||||
|
bestAreaFit.value = Number.MAX_VALUE;
|
||||||
|
|
||||||
|
for (let i = 0; i < this.freeRectangles.length; ++i) {
|
||||||
|
let areaFit = this.freeRectangles[i].width * this.freeRectangles[i].height - width * height;
|
||||||
|
|
||||||
|
// 试着将长方形放在直立(非翻转)的方向
|
||||||
|
if (this.freeRectangles[i].width >= width && this.freeRectangles[i].height >= height) {
|
||||||
|
let leftoverHoriz = Math.abs(this.freeRectangles[i].width - width);
|
||||||
|
let leftoverVert = Math.abs(this.freeRectangles[i].height - height);
|
||||||
|
let shortSideFit = Math.min(leftoverHoriz, leftoverVert);
|
||||||
|
|
||||||
|
if (areaFit < bestAreaFit.value || (areaFit == bestAreaFit.value && shortSideFit < bestShortSideFit.value)) {
|
||||||
|
bestNode.x = this.freeRectangles[i].x;
|
||||||
|
bestNode.y = this.freeRectangles[i].y;
|
||||||
|
bestNode.width = width;
|
||||||
|
bestNode.height = height;
|
||||||
|
bestShortSideFit.value = shortSideFit;
|
||||||
|
bestAreaFit.value = areaFit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.allowRotations && this.freeRectangles[i].width >= height && this.freeRectangles[i].height >= width) {
|
||||||
|
let leftoverHoriz = Math.abs(this.freeRectangles[i].width - height);
|
||||||
|
let leftoverVert = Math.abs(this.freeRectangles[i].height - width);
|
||||||
|
let shortSideFit = Math.min(leftoverHoriz, leftoverVert);
|
||||||
|
|
||||||
|
if (areaFit < bestAreaFit.value || (areaFit == bestAreaFit.value && shortSideFit < bestShortSideFit.value)) {
|
||||||
|
bestNode.x = this.freeRectangles[i].x;
|
||||||
|
bestNode.y = this.freeRectangles[i].y;
|
||||||
|
bestNode.width = height;
|
||||||
|
bestNode.height = width;
|
||||||
|
bestShortSideFit.value = shortSideFit;
|
||||||
|
bestAreaFit.value = areaFit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bestNode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public splitFreeNode(freeNode: Rectangle, usedNode: Rectangle) {
|
||||||
|
// 用SAT测试长方形是否均匀相交
|
||||||
|
if (usedNode.x >= freeNode.x + freeNode.width || usedNode.x + usedNode.width <= freeNode.x ||
|
||||||
|
usedNode.y >= freeNode.y + freeNode.height || usedNode.y + usedNode.height <= freeNode.y)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (usedNode.x < freeNode.x + freeNode.width && usedNode.x + usedNode.width > freeNode.x) {
|
||||||
|
// 在使用过的节点的上边新建一个节点
|
||||||
|
if (usedNode.y > freeNode.y && usedNode.y < freeNode.y + freeNode.height) {
|
||||||
|
let newNode = freeNode;
|
||||||
|
newNode.height = usedNode.y - newNode.y;
|
||||||
|
this.freeRectangles.push(newNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在使用过的节点的底边新建节点
|
||||||
|
if (usedNode.y + usedNode.height < freeNode.y + freeNode.height) {
|
||||||
|
let newNode = freeNode;
|
||||||
|
newNode.y = usedNode.y + usedNode.height;
|
||||||
|
newNode.height = freeNode.y + freeNode.height - (usedNode.y + usedNode.height);
|
||||||
|
this.freeRectangles.push(newNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (usedNode.y < freeNode.y + freeNode.height && usedNode.y + usedNode.height > freeNode.y) {
|
||||||
|
// 在使用过的节点的左侧新建节点
|
||||||
|
if (usedNode.x > freeNode.x && usedNode.x < freeNode.x + freeNode.width) {
|
||||||
|
let newNode = freeNode;
|
||||||
|
newNode.width = usedNode.x - newNode.x;
|
||||||
|
this.freeRectangles.push(newNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在使用过的节点右侧新建节点
|
||||||
|
if (usedNode.x + usedNode.width < freeNode.x + freeNode.width) {
|
||||||
|
let newNode = freeNode;
|
||||||
|
newNode.x = usedNode.x + usedNode.width;
|
||||||
|
newNode.width = freeNode.x + freeNode.width - (usedNode.x + usedNode.width);
|
||||||
|
this.freeRectangles.push(newNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public pruneFreeList() {
|
||||||
|
for (let i = 0; i < this.freeRectangles.length; ++i)
|
||||||
|
for (let j = i + 1; j < this.freeRectangles.length; ++j) {
|
||||||
|
if (this.isContainedIn(this.freeRectangles[i], this.freeRectangles[j])) {
|
||||||
|
new linq.List(this.freeRectangles).removeAt(i);
|
||||||
|
--i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (this.isContainedIn(this.freeRectangles[j], this.freeRectangles[i])) {
|
||||||
|
new linq.List(this.freeRectangles).removeAt(j);
|
||||||
|
--j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public isContainedIn(a: Rectangle, b: Rectangle) {
|
||||||
|
return a.x >= b.x && a.y >= b.y
|
||||||
|
&& a.x + a.width <= b.x + b.width
|
||||||
|
&& a.y + a.height <= b.y + b.height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user