新增子模块与文件迁移
This commit is contained in:
116
source/src/Utils/Collections/FastList.ts
Normal file
116
source/src/Utils/Collections/FastList.ts
Normal file
@@ -0,0 +1,116 @@
|
||||
module es {
|
||||
/**
|
||||
* 围绕一个数组的非常基本的包装,当它达到容量时自动扩展。
|
||||
* 注意,在迭代时应该这样直接访问缓冲区,但使用FastList.length字段。
|
||||
*
|
||||
* @tutorial
|
||||
* for( var i = 0; i <= list.length; i++ )
|
||||
* var item = list.buffer[i];
|
||||
*/
|
||||
export class FastList<T> {
|
||||
/**
|
||||
* 直接访问后备缓冲区。
|
||||
* 不要使用buffer.Length! 使用FastList.length
|
||||
*/
|
||||
public buffer: T[];
|
||||
|
||||
/**
|
||||
* 直接访问缓冲区内填充项的长度。不要改变。
|
||||
*/
|
||||
public length: number = 0;
|
||||
|
||||
constructor(size: number = 5) {
|
||||
this.buffer = new Array(size);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空列表并清空缓冲区中的所有项目
|
||||
*/
|
||||
public clear() {
|
||||
this.buffer.length = 0;
|
||||
this.length = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 和clear的工作原理一样,只是它不会将缓冲区中的所有项目清空。
|
||||
*/
|
||||
public reset() {
|
||||
this.length = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将该项目添加到列表中
|
||||
* @param item
|
||||
*/
|
||||
public add(item: T) {
|
||||
if (this.length == this.buffer.length)
|
||||
this.buffer.length = Math.max(this.buffer.length << 1, 10);
|
||||
this.buffer[this.length++] = item;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从列表中删除该项目
|
||||
* @param item
|
||||
*/
|
||||
public remove(item: T){
|
||||
let comp = EqualityComparer.default<T>();
|
||||
for (let i = 0; i < this.length; ++i){
|
||||
if (comp.equals(this.buffer[i], item)){
|
||||
this.removeAt(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从列表中删除给定索引的项目。
|
||||
* @param index
|
||||
*/
|
||||
public removeAt(index: number){
|
||||
if (index >= this.length)
|
||||
throw new Error("index超出范围!");
|
||||
|
||||
this.length --;
|
||||
new linq.List(this.buffer).removeAt(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查项目是否在FastList中
|
||||
* @param item
|
||||
*/
|
||||
public contains(item: T){
|
||||
let comp = EqualityComparer.default<T>();
|
||||
for (let i = 0; i < this.length; ++ i){
|
||||
if (comp.equals(this.buffer[i], item))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果缓冲区达到最大,将分配更多的空间来容纳额外的ItemCount。
|
||||
* @param additionalItemCount
|
||||
*/
|
||||
public ensureCapacity(additionalItemCount: number = 1){
|
||||
if (this.length + additionalItemCount >= this.buffer.length)
|
||||
this.buffer.length = Math.max(this.buffer.length << 1, this.length + additionalItemCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加数组中的所有项目
|
||||
* @param array
|
||||
*/
|
||||
public addRange(array: T[]){
|
||||
for (let item of array)
|
||||
this.add(item);
|
||||
}
|
||||
|
||||
/**
|
||||
* 对缓冲区中的所有项目进行排序,长度不限。
|
||||
*/
|
||||
public sort(comparer: IComparer<T>){
|
||||
this.buffer.sort(comparer.compare);
|
||||
}
|
||||
}
|
||||
}
|
||||
317
source/src/Utils/Collections/FasterDictionary.ts
Normal file
317
source/src/Utils/Collections/FasterDictionary.ts
Normal file
@@ -0,0 +1,317 @@
|
||||
module es {
|
||||
/**
|
||||
* 创建这个字典的原因只有一个:
|
||||
* 我需要一个能让我直接以数组的形式对值进行迭代的字典,而不需要生成一个数组或使用迭代器。
|
||||
* 对于这个目标是比标准字典快N倍。
|
||||
* Faster dictionary在大部分操作上也比标准字典快,但差别可以忽略不计。
|
||||
* 唯一较慢的操作是在添加时调整内存大小,因为与标准数组相比,这个实现需要使用两个单独的数组。
|
||||
*/
|
||||
export class FasterDictionary<TKey, TValue> {
|
||||
public _values: TValue[];
|
||||
public _valuesInfo: FastNode[];
|
||||
public _buckets: number[];
|
||||
public _freeValueCellIndex: number = 0;
|
||||
public _collisions: number = 0;
|
||||
|
||||
constructor(size: number = 1) {
|
||||
this._valuesInfo = new Array(size);
|
||||
this._values = new Array(size);
|
||||
this._buckets = new Array(HashHelpers.getPrime(size));
|
||||
}
|
||||
|
||||
public getValuesArray(count: {value: number}): TValue[] {
|
||||
count.value = this._freeValueCellIndex;
|
||||
|
||||
return this._values;
|
||||
}
|
||||
|
||||
public get valuesArray(): TValue[] {
|
||||
return this._values;
|
||||
}
|
||||
|
||||
public get count(): number {
|
||||
return this._freeValueCellIndex;
|
||||
}
|
||||
|
||||
public add(key: TKey, value: TValue) {
|
||||
if (!this.addValue(key, value, {value: 0}))
|
||||
throw new Error("key 已经存在")
|
||||
}
|
||||
|
||||
public addValue(key: TKey, value: TValue, indexSet: {value: number}) {
|
||||
let hash = HashHelpers.getHashCode(key);
|
||||
let bucketIndex = FasterDictionary.reduce(hash, this._buckets.length);
|
||||
|
||||
if (this._freeValueCellIndex == this._values.length) {
|
||||
let expandPrime = HashHelpers.expandPrime(this._freeValueCellIndex);
|
||||
|
||||
this._values.length = expandPrime;
|
||||
this._valuesInfo.length = expandPrime;
|
||||
}
|
||||
|
||||
// buckets值-1表示它是空的
|
||||
let valueIndex = NumberExtension.toNumber(this._buckets[bucketIndex]) - 1;
|
||||
|
||||
if (valueIndex == -1) {
|
||||
// 在最后一个位置创建信息节点,并填入相关信息
|
||||
this._valuesInfo[this._freeValueCellIndex] = new FastNode(key, hash);
|
||||
} else {
|
||||
{
|
||||
let currentValueIndex = valueIndex;
|
||||
do {
|
||||
// 必须检查键是否已经存在于字典中
|
||||
if (this._valuesInfo[currentValueIndex].hashcode == hash &&
|
||||
this._valuesInfo[currentValueIndex].key == key) {
|
||||
// 键已经存在,只需将其值替换掉即可
|
||||
this._values[currentValueIndex] = value;
|
||||
indexSet.value = currentValueIndex;
|
||||
return false;
|
||||
}
|
||||
|
||||
currentValueIndex = this._valuesInfo[currentValueIndex].previous;
|
||||
}
|
||||
while (currentValueIndex != -1); // -1表示没有更多的值与相同的哈希值的键
|
||||
}
|
||||
|
||||
this._collisions++;
|
||||
// 创建一个新的节点,该节点之前的索引指向当前指向桶的节点
|
||||
this._valuesInfo[this._freeValueCellIndex] = new FastNode(key, hash, valueIndex);
|
||||
// 更新现有单元格的下一个单元格指向新的单元格,旧的单元格 -> 新的单元格 -> 旧的单元格 <- 下一个单元格
|
||||
this._valuesInfo[valueIndex].next = this._freeValueCellIndex;
|
||||
}
|
||||
// 重要的是:新的节点总是被桶单元格指向的那个节点,所以我可以假设被桶指向的那个节点总是最后添加的值(next = -1)
|
||||
// item与这个bucketIndex将指向最后创建的值
|
||||
// TODO: 如果相反,我假设原来的那个是bucket中的那个,我就不需要在这里更新bucket了
|
||||
this._buckets[bucketIndex] = (this._freeValueCellIndex + 1);
|
||||
|
||||
this._values[this._freeValueCellIndex] = value;
|
||||
indexSet.value = this._freeValueCellIndex;
|
||||
|
||||
this._freeValueCellIndex++;
|
||||
|
||||
if (this._collisions > this._buckets.length) {
|
||||
// 我们需要更多的空间和更少的碰撞
|
||||
this._buckets = new Array(HashHelpers.expandPrime(this._collisions));
|
||||
this._collisions = 0;
|
||||
// 我们需要得到目前存储的所有值的哈希码,并将它们分布在新的桶长上
|
||||
for (let newValueIndex = 0; newValueIndex < this._freeValueCellIndex; newValueIndex++) {
|
||||
// 获取原始哈希码,并根据新的长度找到新的bucketIndex
|
||||
bucketIndex = FasterDictionary.reduce(this._valuesInfo[newValueIndex].hashcode, this._buckets.length);
|
||||
// bucketsIndex可以是-1或下一个值。
|
||||
// 如果是-1意味着没有碰撞。
|
||||
// 如果有碰撞,我们创建一个新节点,它的上一个指向旧节点。
|
||||
// 旧节点指向新节点,新节点指向旧节点,旧节点指向新节点,现在bucket指向新节点,这样我们就可以重建linkedlist.
|
||||
// 获取当前值Index,如果没有碰撞,则为-1。
|
||||
let existingValueIndex = NumberExtension.toNumber(this._buckets[bucketIndex]) - 1;
|
||||
// 将bucket索引更新为共享bucketIndex的当前项目的索引(最后找到的总是bucket中的那个)
|
||||
this._buckets[bucketIndex] = newValueIndex + 1;
|
||||
if (existingValueIndex != -1) {
|
||||
// 这个单元格已经指向了新的bucket list中的一个值,这意味着有一个碰撞,出了问题
|
||||
this._collisions++;
|
||||
// bucket将指向这个值,所以新的值将使用以前的索引
|
||||
this._valuesInfo[newValueIndex].previous = existingValueIndex;
|
||||
this._valuesInfo[newValueIndex].next = -1;
|
||||
// 并将之前的下一个索引更新为新的索引
|
||||
this._valuesInfo[existingValueIndex].next = newValueIndex;
|
||||
} else {
|
||||
// 什么都没有被索引,桶是空的。我们需要更新之前的 next 和 previous 的值。
|
||||
this._valuesInfo[newValueIndex].next = -1;
|
||||
this._valuesInfo[newValueIndex].previous = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public remove(key: TKey): boolean {
|
||||
let hash = FasterDictionary.hash(key);
|
||||
let bucketIndex = FasterDictionary.reduce(hash, this._buckets.length);
|
||||
|
||||
// 找桶
|
||||
let indexToValueToRemove = NumberExtension.toNumber(this._buckets[bucketIndex]) - 1;
|
||||
|
||||
// 第一部分:在bucket list中寻找实际的键,如果找到了,我就更新bucket list,使它不再指向要删除的单元格。
|
||||
while (indexToValueToRemove != -1) {
|
||||
if (this._valuesInfo[indexToValueToRemove].hashcode == hash &&
|
||||
this._valuesInfo[indexToValueToRemove].key == key) {
|
||||
// 如果找到了密钥,并且桶直接指向了要删除的节点
|
||||
if (this._buckets[bucketIndex] - 1 == indexToValueToRemove){
|
||||
if (this._valuesInfo[indexToValueToRemove].next != -1)
|
||||
throw new Error("如果 bucket 指向单元格,那么 next 必须不存在。");
|
||||
|
||||
// 如果前一个单元格存在,它的下一个指针必须被更新!
|
||||
//<---迭代顺序
|
||||
// B(ucket总是指向最后一个)
|
||||
// ------- ------- -------
|
||||
// 1 | | | | 2 | | | 3 | //bucket不能有下一个,只能有上一个。
|
||||
// ------- ------- -------
|
||||
//--> 插入
|
||||
let value = this._valuesInfo[indexToValueToRemove].previous;
|
||||
this._buckets[bucketIndex] = value + 1;
|
||||
}else{
|
||||
if (this._valuesInfo[indexToValueToRemove].next == -1)
|
||||
throw new Error("如果 bucket 指向另一个单元格,则 NEXT 必须存在");
|
||||
}
|
||||
|
||||
FasterDictionary.updateLinkedList(indexToValueToRemove, this._valuesInfo);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
indexToValueToRemove = this._valuesInfo[indexToValueToRemove].previous;
|
||||
}
|
||||
|
||||
if (indexToValueToRemove == -1)
|
||||
return false; // 未找到
|
||||
|
||||
this._freeValueCellIndex --; // 少了一个需要反复计算的值
|
||||
|
||||
// 第二部分
|
||||
// 这时节点指针和水桶会被更新,但_values数组会被更新仍然有要删除的值
|
||||
// 这个字典的目标是能够做到像数组一样对数值进行迭代,所以数值数组必须始终是最新的
|
||||
|
||||
// 如果要删除的单元格是列表中的最后一个,我们可以执行较少的操作(不需要交换),否则我们要将最后一个值的单元格移到要删除的值上。
|
||||
if (indexToValueToRemove != this._freeValueCellIndex){
|
||||
// 我们可以将两个数组的最后一个值移到要删除的数组中。
|
||||
// 为了做到这一点,我们需要确保 bucket 指针已经更新了
|
||||
// 首先我们在桶列表中找到指向要移动的单元格的指针的索引
|
||||
let movingBucketIndex = FasterDictionary.reduce(this._valuesInfo[this._freeValueCellIndex].hashcode, this._buckets.length);
|
||||
|
||||
// 如果找到了键,并且桶直接指向要删除的节点,现在必须指向要移动的单元格。
|
||||
if (this._buckets[movingBucketIndex] - 1 == this._freeValueCellIndex)
|
||||
this._buckets[movingBucketIndex] = (indexToValueToRemove + 1);
|
||||
|
||||
// 否则意味着有多个键具有相同的哈希值(碰撞),所以我们需要更新链接列表和它的指针
|
||||
let next = this._valuesInfo[this._freeValueCellIndex].next;
|
||||
let previous = this._valuesInfo[this._freeValueCellIndex].previous;
|
||||
|
||||
// 现在它们指向最后一个值被移入的单元格
|
||||
if (next != -1)
|
||||
this._valuesInfo[next].previous = indexToValueToRemove;
|
||||
if (previous != -1)
|
||||
this._valuesInfo[previous].next = indexToValueToRemove;
|
||||
|
||||
// 最后,实际上是移动值
|
||||
this._valuesInfo[indexToValueToRemove] = this._valuesInfo[this._freeValueCellIndex];
|
||||
this._values[indexToValueToRemove] = this._values[this._freeValueCellIndex];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public trim(){
|
||||
let expandPrime = HashHelpers.expandPrime(this._freeValueCellIndex);
|
||||
|
||||
if (expandPrime < this._valuesInfo.length){
|
||||
this._values.length = expandPrime;
|
||||
this._valuesInfo.length = expandPrime;
|
||||
}
|
||||
}
|
||||
|
||||
public clear(){
|
||||
if (this._freeValueCellIndex == 0) return;
|
||||
|
||||
this._freeValueCellIndex = 0;
|
||||
this._buckets.length = 0;
|
||||
this._values.length = 0;
|
||||
this._valuesInfo.length = 0;
|
||||
}
|
||||
|
||||
public fastClear(){
|
||||
if (this._freeValueCellIndex == 0) return;
|
||||
|
||||
this._freeValueCellIndex = 0;
|
||||
|
||||
this._buckets.length = 0;
|
||||
this._valuesInfo.length = 0;
|
||||
}
|
||||
|
||||
public containsKey(key: TKey){
|
||||
if (this.tryFindIndex(key, {value: 0})){
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public tryGetValue(key: TKey): TValue {
|
||||
let findIndex = {value: 0};
|
||||
if (this.tryFindIndex(key, findIndex)){
|
||||
return this._values[findIndex.value];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public tryFindIndex(key: TKey, findIndex: {value: number}){
|
||||
// 我把所有的索引都用偏移量+1来存储,这样在bucket list中0就意味着实际上不存在
|
||||
// 当读取时,偏移量必须再偏移-1才是真实的
|
||||
// 这样我就避免了将数组初始化为-1
|
||||
let hash = FasterDictionary.hash(key);
|
||||
let bucketIndex = FasterDictionary.reduce(hash, this._buckets.length);
|
||||
|
||||
let valueIndex = NumberExtension.toNumber(this._buckets[bucketIndex]) - 1;
|
||||
|
||||
// 即使我们找到了一个现有的值,我们也需要确定它是我们所要求的值
|
||||
while (valueIndex != -1){
|
||||
if (this._valuesInfo[valueIndex].hashcode == hash && this._valuesInfo[valueIndex].key == key){
|
||||
findIndex.value = valueIndex;
|
||||
return true;
|
||||
}
|
||||
|
||||
valueIndex = this._valuesInfo[valueIndex].previous;
|
||||
}
|
||||
|
||||
findIndex.value = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
public getDirectValue(index: number): TValue {
|
||||
return this._values[index];
|
||||
}
|
||||
|
||||
public getIndex(key: TKey): number {
|
||||
let findIndex = {value: 0};
|
||||
if (this.tryFindIndex(key, findIndex))
|
||||
return findIndex.value;
|
||||
|
||||
throw new Error("未找到key");
|
||||
}
|
||||
|
||||
public static updateLinkedList(index: number, valuesInfo: FastNode[]){
|
||||
let next = valuesInfo[index].next;
|
||||
let previous = valuesInfo[index].previous;
|
||||
|
||||
if (next != -1)
|
||||
valuesInfo[next].previous = previous;
|
||||
if (previous != -1)
|
||||
valuesInfo[previous].next = next;
|
||||
}
|
||||
|
||||
public static hash(key) {
|
||||
return HashHelpers.getHashCode(key);
|
||||
}
|
||||
|
||||
public static reduce(x: number, n: number) {
|
||||
if (x >= n)
|
||||
return x % n;
|
||||
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
export class FastNode {
|
||||
readonly key;
|
||||
readonly hashcode: number;
|
||||
previous: number;
|
||||
next: number;
|
||||
|
||||
constructor(key, hash: number, previousNode: number = -1) {
|
||||
this.key = key;
|
||||
this.hashcode = hash;
|
||||
this.previous = previousNode;
|
||||
this.next = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,297 +1,297 @@
|
||||
class ArrayUtils {
|
||||
/**
|
||||
* 执行冒泡排序
|
||||
* @param ary
|
||||
*/
|
||||
public static bubbleSort(ary: number[]): void {
|
||||
let isExchange: Boolean = false;
|
||||
for (let i: number = 0; i < ary.length; i++) {
|
||||
isExchange = false;
|
||||
for (let j: number = ary.length - 1; j > i; j--) {
|
||||
if (ary[j] < ary[j - 1]) {
|
||||
let temp: number = ary[j];
|
||||
ary[j] = ary[j - 1];
|
||||
ary[j - 1] = temp;
|
||||
isExchange = true;
|
||||
}
|
||||
}
|
||||
if (!isExchange)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行插入排序
|
||||
* @param ary
|
||||
*/
|
||||
public static insertionSort(ary: number[]): void {
|
||||
let len: number = ary.length;
|
||||
for (let i: number = 1; i < len; i++) {
|
||||
let val: number = ary[i];
|
||||
for (var j: number = i; j > 0 && ary[j - 1] > val; j--) {
|
||||
ary[j] = ary[j - 1];
|
||||
}
|
||||
ary[j] = val;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行二分搜索
|
||||
* @param ary 搜索的数组(必须排序过)
|
||||
* @param value 需要搜索的值
|
||||
* @returns 返回匹配结果的数组索引
|
||||
*/
|
||||
public static binarySearch(ary: number[], value: number): number {
|
||||
let startIndex: number = 0;
|
||||
let endIndex: number = ary.length;
|
||||
let sub: number = (startIndex + endIndex) >> 1;
|
||||
while (startIndex < endIndex) {
|
||||
if (value <= ary[sub]) endIndex = sub;
|
||||
else if (value >= ary[sub]) startIndex = sub + 1;
|
||||
sub = (startIndex + endIndex) >> 1;
|
||||
}
|
||||
if (ary[startIndex] == value) return startIndex;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 返回匹配项的索引
|
||||
* @param ary
|
||||
* @param num
|
||||
*/
|
||||
public static findElementIndex(ary: any[], num: any): any {
|
||||
let len: number = ary.length;
|
||||
for (let i: number = 0; i < len; ++i) {
|
||||
if (ary[i] == num)
|
||||
return i;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回数组中最大值的索引
|
||||
* @param ary
|
||||
*/
|
||||
public static getMaxElementIndex(ary: number[]): number {
|
||||
let matchIndex: number = 0;
|
||||
let len: number = ary.length;
|
||||
for (let j: number = 1; j < len; j++) {
|
||||
if (ary[j] > ary[matchIndex])
|
||||
matchIndex = j;
|
||||
}
|
||||
return matchIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回数组中最小值的索引
|
||||
* @param ary
|
||||
*/
|
||||
public static getMinElementIndex(ary: number[]): number {
|
||||
let matchIndex: number = 0;
|
||||
let len: number = ary.length;
|
||||
for (let j: number = 1; j < len; j++) {
|
||||
if (ary[j] < ary[matchIndex])
|
||||
matchIndex = j;
|
||||
}
|
||||
return matchIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回一个"唯一性"数组
|
||||
* @param ary 需要唯一性的数组
|
||||
* @returns 唯一性的数组
|
||||
*
|
||||
* @tutorial
|
||||
* 比如: [1, 2, 2, 3, 4]
|
||||
* 返回: [1, 2, 3, 4]
|
||||
*/
|
||||
public static getUniqueAry(ary: number[]): number[] {
|
||||
let uAry: number[] = [];
|
||||
let newAry: number[] = [];
|
||||
let count = ary.length;
|
||||
for (let i: number = 0; i < count; ++i) {
|
||||
let value: number = ary[i];
|
||||
if (uAry.indexOf(value) == -1) uAry.push(value);
|
||||
}
|
||||
|
||||
count = uAry.length;
|
||||
for (let i: number = count - 1; i >= 0; --i) {
|
||||
newAry.unshift(uAry[i]);
|
||||
}
|
||||
return newAry;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 返回2个数组中不同的部分
|
||||
* 比如数组A = [1, 2, 3, 4, 6]
|
||||
* 数组B = [0, 2, 1, 3, 4]
|
||||
* 返回[6, 0]
|
||||
* @param aryA
|
||||
* @param aryB
|
||||
* @return
|
||||
*/
|
||||
public static getDifferAry(aryA: number[], aryB: number[]): number[] {
|
||||
aryA = this.getUniqueAry(aryA);
|
||||
aryB = this.getUniqueAry(aryB);
|
||||
let ary: number[] = aryA.concat(aryB);
|
||||
let uObj: Object = {};
|
||||
let newAry: number[] = [];
|
||||
let count: number = ary.length;
|
||||
for (let j: number = 0; j < count; ++j) {
|
||||
if (!uObj[ary[j]]) {
|
||||
uObj[ary[j]] = {};
|
||||
uObj[ary[j]].count = 0;
|
||||
uObj[ary[j]].key = ary[j];
|
||||
uObj[ary[j]].count++;
|
||||
} else {
|
||||
if (uObj[ary[j]] instanceof Object) {
|
||||
uObj[ary[j]].count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (let i in uObj) {
|
||||
if (uObj[i].count != 2) {
|
||||
newAry.unshift(uObj[i].key);
|
||||
}
|
||||
}
|
||||
return newAry;
|
||||
}
|
||||
|
||||
/**
|
||||
* 交换数组元素
|
||||
* @param array 目标数组
|
||||
* @param index1 交换后的索引
|
||||
* @param index2 交换前的索引
|
||||
*/
|
||||
public static swap(array: any[], index1: number, index2: number): void {
|
||||
let temp: any = array[index1];
|
||||
array[index1] = array[index2];
|
||||
array[index2] = temp;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 清除列表
|
||||
* @param ary
|
||||
*/
|
||||
public static clearList(ary: any[]): void {
|
||||
if (!ary) return;
|
||||
let length: number = ary.length;
|
||||
for (let i: number = length - 1; i >= 0; i -= 1) {
|
||||
ary.splice(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 克隆一个数组
|
||||
* @param ary 需要克隆的数组
|
||||
* @return 克隆的数组
|
||||
*/
|
||||
public static cloneList(ary: any[]): any[] {
|
||||
if (!ary) return null;
|
||||
return ary.slice(0, ary.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断2个数组是否相同
|
||||
* @param ary1 数组1
|
||||
* @param ary2 数组2
|
||||
*/
|
||||
public static equals(ary1: number[], ary2: number[]): Boolean {
|
||||
if (ary1 == ary2) return true;
|
||||
let length: number = ary1.length;
|
||||
if (length != ary2.length) return false;
|
||||
while (length--) {
|
||||
if (ary1[length] != ary2[length])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据索引插入元素,索引和索引后的元素都向后移动一位
|
||||
* @param ary
|
||||
* @param index 插入索引
|
||||
* @param value 插入的元素
|
||||
* @returns 插入的元素 未插入则返回空
|
||||
*/
|
||||
public static insert(ary: any[], index: number, value: any): any {
|
||||
if (!ary) return null;
|
||||
let length: number = ary.length;
|
||||
if (index > length) index = length;
|
||||
if (index < 0) index = 0;
|
||||
if (index == length) ary.push(value); //插入最后
|
||||
else if (index == 0) ary.unshift(value); //插入头
|
||||
else {
|
||||
for (let i: number = length - 1; i >= index; i -= 1) {
|
||||
ary[i + 1] = ary[i];
|
||||
}
|
||||
ary[index] = value;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 打乱数组 Fisher–Yates shuffle
|
||||
* @param list
|
||||
*/
|
||||
public static shuffle<T>(list: T[]) {
|
||||
let n = list.length;
|
||||
while (n > 1) {
|
||||
n--;
|
||||
let k = RandomUtils.randint(0, n + 1);
|
||||
let value: T = list[k];
|
||||
list[k] = list[n];
|
||||
list[n] = value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果项目已经在列表中,返回false,如果成功添加,返回true
|
||||
* @param list
|
||||
* @param item
|
||||
*/
|
||||
public static addIfNotPresent<T>(list: T[], item: T) {
|
||||
if (new linq.List(list).contains(item))
|
||||
return false;
|
||||
|
||||
list.push(item);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回列表中的最后一项。列表中至少应该有一个项目
|
||||
* @param list
|
||||
*/
|
||||
public static lastItem<T>(list: T[]) {
|
||||
return list[list.length - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* 从列表中随机获取一个项目。不清空检查列表!
|
||||
* @param list
|
||||
*/
|
||||
public static randomItem<T>(list: T[]) {
|
||||
return list[RandomUtils.randint(0, list.length - 1)];
|
||||
}
|
||||
|
||||
/**
|
||||
* 从列表中随机获取物品。不清空检查列表,也不验证列表数是否大于项目数。返回的List可以通过ListPool.free放回池中
|
||||
* @param list
|
||||
* @param itemCount 从列表中返回的随机项目的数量
|
||||
*/
|
||||
public static randomItems<T>(list: T[], itemCount: number){
|
||||
let set = new Set<T>();
|
||||
while (set.size != itemCount) {
|
||||
let item = this.randomItem(list);
|
||||
if (!set.has(item))
|
||||
set.add(item);
|
||||
}
|
||||
|
||||
let items = es.ListPool.obtain<T>();
|
||||
set.forEach(value => items.push(value));
|
||||
return items;
|
||||
}
|
||||
class ArrayUtils {
|
||||
/**
|
||||
* 执行冒泡排序
|
||||
* @param ary
|
||||
*/
|
||||
public static bubbleSort(ary: number[]): void {
|
||||
let isExchange: Boolean = false;
|
||||
for (let i: number = 0; i < ary.length; i++) {
|
||||
isExchange = false;
|
||||
for (let j: number = ary.length - 1; j > i; j--) {
|
||||
if (ary[j] < ary[j - 1]) {
|
||||
let temp: number = ary[j];
|
||||
ary[j] = ary[j - 1];
|
||||
ary[j - 1] = temp;
|
||||
isExchange = true;
|
||||
}
|
||||
}
|
||||
if (!isExchange)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行插入排序
|
||||
* @param ary
|
||||
*/
|
||||
public static insertionSort(ary: number[]): void {
|
||||
let len: number = ary.length;
|
||||
for (let i: number = 1; i < len; i++) {
|
||||
let val: number = ary[i];
|
||||
for (var j: number = i; j > 0 && ary[j - 1] > val; j--) {
|
||||
ary[j] = ary[j - 1];
|
||||
}
|
||||
ary[j] = val;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行二分搜索
|
||||
* @param ary 搜索的数组(必须排序过)
|
||||
* @param value 需要搜索的值
|
||||
* @returns 返回匹配结果的数组索引
|
||||
*/
|
||||
public static binarySearch(ary: number[], value: number): number {
|
||||
let startIndex: number = 0;
|
||||
let endIndex: number = ary.length;
|
||||
let sub: number = (startIndex + endIndex) >> 1;
|
||||
while (startIndex < endIndex) {
|
||||
if (value <= ary[sub]) endIndex = sub;
|
||||
else if (value >= ary[sub]) startIndex = sub + 1;
|
||||
sub = (startIndex + endIndex) >> 1;
|
||||
}
|
||||
if (ary[startIndex] == value) return startIndex;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 返回匹配项的索引
|
||||
* @param ary
|
||||
* @param num
|
||||
*/
|
||||
public static findElementIndex(ary: any[], num: any): any {
|
||||
let len: number = ary.length;
|
||||
for (let i: number = 0; i < len; ++i) {
|
||||
if (ary[i] == num)
|
||||
return i;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回数组中最大值的索引
|
||||
* @param ary
|
||||
*/
|
||||
public static getMaxElementIndex(ary: number[]): number {
|
||||
let matchIndex: number = 0;
|
||||
let len: number = ary.length;
|
||||
for (let j: number = 1; j < len; j++) {
|
||||
if (ary[j] > ary[matchIndex])
|
||||
matchIndex = j;
|
||||
}
|
||||
return matchIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回数组中最小值的索引
|
||||
* @param ary
|
||||
*/
|
||||
public static getMinElementIndex(ary: number[]): number {
|
||||
let matchIndex: number = 0;
|
||||
let len: number = ary.length;
|
||||
for (let j: number = 1; j < len; j++) {
|
||||
if (ary[j] < ary[matchIndex])
|
||||
matchIndex = j;
|
||||
}
|
||||
return matchIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回一个"唯一性"数组
|
||||
* @param ary 需要唯一性的数组
|
||||
* @returns 唯一性的数组
|
||||
*
|
||||
* @tutorial
|
||||
* 比如: [1, 2, 2, 3, 4]
|
||||
* 返回: [1, 2, 3, 4]
|
||||
*/
|
||||
public static getUniqueAry(ary: number[]): number[] {
|
||||
let uAry: number[] = [];
|
||||
let newAry: number[] = [];
|
||||
let count = ary.length;
|
||||
for (let i: number = 0; i < count; ++i) {
|
||||
let value: number = ary[i];
|
||||
if (uAry.indexOf(value) == -1) uAry.push(value);
|
||||
}
|
||||
|
||||
count = uAry.length;
|
||||
for (let i: number = count - 1; i >= 0; --i) {
|
||||
newAry.unshift(uAry[i]);
|
||||
}
|
||||
return newAry;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 返回2个数组中不同的部分
|
||||
* 比如数组A = [1, 2, 3, 4, 6]
|
||||
* 数组B = [0, 2, 1, 3, 4]
|
||||
* 返回[6, 0]
|
||||
* @param aryA
|
||||
* @param aryB
|
||||
* @return
|
||||
*/
|
||||
public static getDifferAry(aryA: number[], aryB: number[]): number[] {
|
||||
aryA = this.getUniqueAry(aryA);
|
||||
aryB = this.getUniqueAry(aryB);
|
||||
let ary: number[] = aryA.concat(aryB);
|
||||
let uObj: Object = {};
|
||||
let newAry: number[] = [];
|
||||
let count: number = ary.length;
|
||||
for (let j: number = 0; j < count; ++j) {
|
||||
if (!uObj[ary[j]]) {
|
||||
uObj[ary[j]] = {};
|
||||
uObj[ary[j]].count = 0;
|
||||
uObj[ary[j]].key = ary[j];
|
||||
uObj[ary[j]].count++;
|
||||
} else {
|
||||
if (uObj[ary[j]] instanceof Object) {
|
||||
uObj[ary[j]].count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (let i in uObj) {
|
||||
if (uObj[i].count != 2) {
|
||||
newAry.unshift(uObj[i].key);
|
||||
}
|
||||
}
|
||||
return newAry;
|
||||
}
|
||||
|
||||
/**
|
||||
* 交换数组元素
|
||||
* @param array 目标数组
|
||||
* @param index1 交换后的索引
|
||||
* @param index2 交换前的索引
|
||||
*/
|
||||
public static swap(array: any[], index1: number, index2: number): void {
|
||||
let temp: any = array[index1];
|
||||
array[index1] = array[index2];
|
||||
array[index2] = temp;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 清除列表
|
||||
* @param ary
|
||||
*/
|
||||
public static clearList(ary: any[]): void {
|
||||
if (!ary) return;
|
||||
let length: number = ary.length;
|
||||
for (let i: number = length - 1; i >= 0; i -= 1) {
|
||||
ary.splice(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 克隆一个数组
|
||||
* @param ary 需要克隆的数组
|
||||
* @return 克隆的数组
|
||||
*/
|
||||
public static cloneList(ary: any[]): any[] {
|
||||
if (!ary) return null;
|
||||
return ary.slice(0, ary.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断2个数组是否相同
|
||||
* @param ary1 数组1
|
||||
* @param ary2 数组2
|
||||
*/
|
||||
public static equals(ary1: number[], ary2: number[]): Boolean {
|
||||
if (ary1 == ary2) return true;
|
||||
let length: number = ary1.length;
|
||||
if (length != ary2.length) return false;
|
||||
while (length--) {
|
||||
if (ary1[length] != ary2[length])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据索引插入元素,索引和索引后的元素都向后移动一位
|
||||
* @param ary
|
||||
* @param index 插入索引
|
||||
* @param value 插入的元素
|
||||
* @returns 插入的元素 未插入则返回空
|
||||
*/
|
||||
public static insert(ary: any[], index: number, value: any): any {
|
||||
if (!ary) return null;
|
||||
let length: number = ary.length;
|
||||
if (index > length) index = length;
|
||||
if (index < 0) index = 0;
|
||||
if (index == length) ary.push(value); //插入最后
|
||||
else if (index == 0) ary.unshift(value); //插入头
|
||||
else {
|
||||
for (let i: number = length - 1; i >= index; i -= 1) {
|
||||
ary[i + 1] = ary[i];
|
||||
}
|
||||
ary[index] = value;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 打乱数组 Fisher–Yates shuffle
|
||||
* @param list
|
||||
*/
|
||||
public static shuffle<T>(list: T[]) {
|
||||
let n = list.length;
|
||||
while (n > 1) {
|
||||
n--;
|
||||
let k = RandomUtils.randint(0, n + 1);
|
||||
let value: T = list[k];
|
||||
list[k] = list[n];
|
||||
list[n] = value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果项目已经在列表中,返回false,如果成功添加,返回true
|
||||
* @param list
|
||||
* @param item
|
||||
*/
|
||||
public static addIfNotPresent<T>(list: T[], item: T) {
|
||||
if (new linq.List(list).contains(item))
|
||||
return false;
|
||||
|
||||
list.push(item);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回列表中的最后一项。列表中至少应该有一个项目
|
||||
* @param list
|
||||
*/
|
||||
public static lastItem<T>(list: T[]) {
|
||||
return list[list.length - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* 从列表中随机获取一个项目。不清空检查列表!
|
||||
* @param list
|
||||
*/
|
||||
public static randomItem<T>(list: T[]) {
|
||||
return list[RandomUtils.randint(0, list.length - 1)];
|
||||
}
|
||||
|
||||
/**
|
||||
* 从列表中随机获取物品。不清空检查列表,也不验证列表数是否大于项目数。返回的List可以通过ListPool.free放回池中
|
||||
* @param list
|
||||
* @param itemCount 从列表中返回的随机项目的数量
|
||||
*/
|
||||
public static randomItems<T>(list: T[], itemCount: number){
|
||||
let set = new Set<T>();
|
||||
while (set.size != itemCount) {
|
||||
let item = this.randomItem(list);
|
||||
if (!set.has(item))
|
||||
set.add(item);
|
||||
}
|
||||
|
||||
let items = es.ListPool.obtain<T>();
|
||||
set.forEach(value => items.push(value));
|
||||
return items;
|
||||
}
|
||||
}
|
||||
@@ -1,134 +1,134 @@
|
||||
module es{
|
||||
export class Base64Utils {
|
||||
private static _keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
|
||||
|
||||
/**
|
||||
* 判断是否原生支持Base64位解析
|
||||
*/
|
||||
static get nativeBase64() {
|
||||
return (typeof (window.atob) === "function");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 解码
|
||||
* @param input
|
||||
*/
|
||||
static decode(input:string): string {
|
||||
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
|
||||
|
||||
if (this.nativeBase64) {
|
||||
return window.atob(input);
|
||||
} else {
|
||||
var output: any = [], chr1: number, chr2: number, chr3: number, enc1: number, enc2: number, enc3: number, enc4: number, i: number = 0;
|
||||
|
||||
while (i < input.length) {
|
||||
enc1 = this._keyStr.indexOf(input.charAt(i++));
|
||||
enc2 = this._keyStr.indexOf(input.charAt(i++));
|
||||
enc3 = this._keyStr.indexOf(input.charAt(i++));
|
||||
enc4 = this._keyStr.indexOf(input.charAt(i++));
|
||||
|
||||
chr1 = (enc1 << 2) | (enc2 >> 4);
|
||||
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
|
||||
chr3 = ((enc3 & 3) << 6) | enc4;
|
||||
|
||||
output.push(String.fromCharCode(chr1));
|
||||
|
||||
if (enc3 !== 64) {
|
||||
output.push(String.fromCharCode(chr2));
|
||||
}
|
||||
if (enc4 !== 64) {
|
||||
output.push(String.fromCharCode(chr3));
|
||||
}
|
||||
}
|
||||
|
||||
output = output.join("");
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 编码
|
||||
* @param input
|
||||
*/
|
||||
static encode(input:string): string {
|
||||
input = input.replace(/\r\n/g, "\n");
|
||||
if (this.nativeBase64) {
|
||||
window.btoa(input);
|
||||
} else {
|
||||
var output: any = [], chr1: number, chr2: number, chr3: number, enc1: number, enc2: number, enc3: number, enc4: number, i: number = 0;
|
||||
while (i < input.length) {
|
||||
chr1 = input.charCodeAt(i++);
|
||||
chr2 = input.charCodeAt(i++);
|
||||
chr3 = input.charCodeAt(i++);
|
||||
|
||||
enc1 = chr1 >> 2;
|
||||
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
|
||||
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
|
||||
enc4 = chr3 & 63;
|
||||
|
||||
if (isNaN(chr2)) {
|
||||
enc3 = enc4 = 64;
|
||||
} else if (isNaN(chr3)) {
|
||||
enc4 = 64;
|
||||
}
|
||||
|
||||
output.push(this._keyStr.charAt(enc1));
|
||||
output.push(this._keyStr.charAt(enc2));
|
||||
output.push(this._keyStr.charAt(enc3));
|
||||
output.push(this._keyStr.charAt(enc4));
|
||||
}
|
||||
|
||||
output = output.join("");
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 解析Base64格式数据
|
||||
* @param input
|
||||
* @param bytes
|
||||
*/
|
||||
static decodeBase64AsArray(input: string, bytes: number): Uint32Array {
|
||||
bytes = bytes || 1;
|
||||
|
||||
var dec = Base64Utils.decode(input), i, j, len;
|
||||
var ar: Uint32Array = new Uint32Array(dec.length / bytes);
|
||||
|
||||
for (i = 0, len = dec.length / bytes; i < len; i++) {
|
||||
ar[i] = 0;
|
||||
for (j = bytes - 1; j >= 0; --j) {
|
||||
ar[i] += dec.charCodeAt((i * bytes) + j) << (j << 3);
|
||||
}
|
||||
}
|
||||
return ar;
|
||||
}
|
||||
|
||||
/**
|
||||
* 暂时不支持
|
||||
* @param data
|
||||
* @param decoded
|
||||
* @param compression
|
||||
* @private
|
||||
*/
|
||||
static decompress(data: string, decoded: any, compression: string): any {
|
||||
throw new Error("GZIP/ZLIB compressed TMX Tile Map not supported!");
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析csv数据
|
||||
* @param input
|
||||
*/
|
||||
static decodeCSV(input: string): Array<number> {
|
||||
var entries: Array<any> = input.replace("\n", "").trim().split(",");
|
||||
|
||||
var result:Array<number> = [];
|
||||
for (var i:number = 0; i < entries.length; i++) {
|
||||
result.push(+entries[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
module es{
|
||||
export class Base64Utils {
|
||||
private static _keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
|
||||
|
||||
/**
|
||||
* 判断是否原生支持Base64位解析
|
||||
*/
|
||||
static get nativeBase64() {
|
||||
return (typeof (window.atob) === "function");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 解码
|
||||
* @param input
|
||||
*/
|
||||
static decode(input:string): string {
|
||||
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
|
||||
|
||||
if (this.nativeBase64) {
|
||||
return window.atob(input);
|
||||
} else {
|
||||
var output: any = [], chr1: number, chr2: number, chr3: number, enc1: number, enc2: number, enc3: number, enc4: number, i: number = 0;
|
||||
|
||||
while (i < input.length) {
|
||||
enc1 = this._keyStr.indexOf(input.charAt(i++));
|
||||
enc2 = this._keyStr.indexOf(input.charAt(i++));
|
||||
enc3 = this._keyStr.indexOf(input.charAt(i++));
|
||||
enc4 = this._keyStr.indexOf(input.charAt(i++));
|
||||
|
||||
chr1 = (enc1 << 2) | (enc2 >> 4);
|
||||
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
|
||||
chr3 = ((enc3 & 3) << 6) | enc4;
|
||||
|
||||
output.push(String.fromCharCode(chr1));
|
||||
|
||||
if (enc3 !== 64) {
|
||||
output.push(String.fromCharCode(chr2));
|
||||
}
|
||||
if (enc4 !== 64) {
|
||||
output.push(String.fromCharCode(chr3));
|
||||
}
|
||||
}
|
||||
|
||||
output = output.join("");
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 编码
|
||||
* @param input
|
||||
*/
|
||||
static encode(input:string): string {
|
||||
input = input.replace(/\r\n/g, "\n");
|
||||
if (this.nativeBase64) {
|
||||
window.btoa(input);
|
||||
} else {
|
||||
var output: any = [], chr1: number, chr2: number, chr3: number, enc1: number, enc2: number, enc3: number, enc4: number, i: number = 0;
|
||||
while (i < input.length) {
|
||||
chr1 = input.charCodeAt(i++);
|
||||
chr2 = input.charCodeAt(i++);
|
||||
chr3 = input.charCodeAt(i++);
|
||||
|
||||
enc1 = chr1 >> 2;
|
||||
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
|
||||
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
|
||||
enc4 = chr3 & 63;
|
||||
|
||||
if (isNaN(chr2)) {
|
||||
enc3 = enc4 = 64;
|
||||
} else if (isNaN(chr3)) {
|
||||
enc4 = 64;
|
||||
}
|
||||
|
||||
output.push(this._keyStr.charAt(enc1));
|
||||
output.push(this._keyStr.charAt(enc2));
|
||||
output.push(this._keyStr.charAt(enc3));
|
||||
output.push(this._keyStr.charAt(enc4));
|
||||
}
|
||||
|
||||
output = output.join("");
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 解析Base64格式数据
|
||||
* @param input
|
||||
* @param bytes
|
||||
*/
|
||||
static decodeBase64AsArray(input: string, bytes: number): Uint32Array {
|
||||
bytes = bytes || 1;
|
||||
|
||||
var dec = Base64Utils.decode(input), i, j, len;
|
||||
var ar: Uint32Array = new Uint32Array(dec.length / bytes);
|
||||
|
||||
for (i = 0, len = dec.length / bytes; i < len; i++) {
|
||||
ar[i] = 0;
|
||||
for (j = bytes - 1; j >= 0; --j) {
|
||||
ar[i] += dec.charCodeAt((i * bytes) + j) << (j << 3);
|
||||
}
|
||||
}
|
||||
return ar;
|
||||
}
|
||||
|
||||
/**
|
||||
* 暂时不支持
|
||||
* @param data
|
||||
* @param decoded
|
||||
* @param compression
|
||||
* @private
|
||||
*/
|
||||
static decompress(data: string, decoded: any, compression: string): any {
|
||||
throw new Error("GZIP/ZLIB compressed TMX Tile Map not supported!");
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析csv数据
|
||||
* @param input
|
||||
*/
|
||||
static decodeCSV(input: string): Array<number> {
|
||||
var entries: Array<any> = input.replace("\n", "").trim().split(",");
|
||||
|
||||
var result:Array<number> = [];
|
||||
for (var i:number = 0; i < entries.length; i++) {
|
||||
result.push(+entries[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,133 +1,133 @@
|
||||
class RandomUtils {
|
||||
/**
|
||||
* 在 start 与 stop之间取一个随机整数,可以用step指定间隔, 但不包括较大的端点(start与stop较大的一个)
|
||||
* 如
|
||||
* this.randrange(1, 10, 3)
|
||||
* 则返回的可能是 1 或 4 或 7 , 注意 这里面不会返回10,因为是10是大端点
|
||||
*
|
||||
* @param start
|
||||
* @param stop
|
||||
* @param step
|
||||
* @return 假设 start < stop, [start, stop) 区间内的随机整数
|
||||
*
|
||||
*/
|
||||
public static randrange(start: number, stop: number, step: number = 1): number {
|
||||
if (step == 0)
|
||||
throw new Error('step 不能为 0');
|
||||
|
||||
let width: number = stop - start;
|
||||
if (width == 0)
|
||||
throw new Error('没有可用的范围(' + start + ',' + stop + ')');
|
||||
if (width < 0)
|
||||
width = start - stop;
|
||||
|
||||
let n: number = Math.floor((width + step - 1) / step);
|
||||
return Math.floor(this.random() * n) * step + Math.min(start, stop);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回a 到 b直间的随机整数,包括 a 和 b
|
||||
* @param a
|
||||
* @param b
|
||||
* @return [a, b] 直接的随机整数
|
||||
*
|
||||
*/
|
||||
public static randint(a: number, b: number): number {
|
||||
a = Math.floor(a);
|
||||
b = Math.floor(b);
|
||||
if (a > b)
|
||||
a++;
|
||||
else
|
||||
b++;
|
||||
return this.randrange(a, b);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回 a - b之间的随机数,不包括 Math.max(a, b)
|
||||
* @param a
|
||||
* @param b
|
||||
* @return 假设 a < b, [a, b)
|
||||
*/
|
||||
public static randnum(a: number, b: number): number {
|
||||
return this.random() * (b - a) + a;
|
||||
}
|
||||
|
||||
/**
|
||||
* 打乱数组
|
||||
* @param array
|
||||
* @return
|
||||
*/
|
||||
public static shuffle(array: any[]): any[] {
|
||||
array.sort(this._randomCompare);
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从序列中随机取一个元素
|
||||
* @param sequence 可以是 数组、 vector,等只要是有length属性,并且可以用数字索引获取元素的对象,
|
||||
* 另外,字符串也是允许的。
|
||||
* @return 序列中的某一个元素
|
||||
*
|
||||
*/
|
||||
public static choice(sequence: any): any {
|
||||
if (!sequence.hasOwnProperty("length"))
|
||||
throw new Error('无法对此对象执行此操作');
|
||||
let index: number = Math.floor(this.random() * sequence.length);
|
||||
if (sequence instanceof String)
|
||||
return String(sequence).charAt(index);
|
||||
else
|
||||
return sequence[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* 对列表中的元素进行随机采æ ?
|
||||
* <pre>
|
||||
* this.sample([1, 2, 3, 4, 5], 3) // Choose 3 elements
|
||||
* [4, 1, 5]
|
||||
* </pre>
|
||||
* @param sequence
|
||||
* @param num
|
||||
* @return
|
||||
*
|
||||
*/
|
||||
public static sample(sequence: any[], num: number): any[] {
|
||||
let len: number = sequence.length;
|
||||
if (num <= 0 || len < num)
|
||||
throw new Error("采样数量不够");
|
||||
|
||||
let selected: any[] = [];
|
||||
let indices: any[] = [];
|
||||
for (let i: number = 0; i < num; i++) {
|
||||
let index: number = Math.floor(this.random() * len);
|
||||
while (indices.indexOf(index) >= 0)
|
||||
index = Math.floor(this.random() * len);
|
||||
|
||||
selected.push(sequence[index]);
|
||||
indices.push(index);
|
||||
}
|
||||
|
||||
return selected;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回 0.0 - 1.0 之间的随机数,等同于 Math.random()
|
||||
* @return Math.random()
|
||||
*
|
||||
*/
|
||||
public static random(): number {
|
||||
return Math.random();
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算概率
|
||||
* @param chance 概率
|
||||
* @return
|
||||
*/
|
||||
public static boolean(chance: number = .5): boolean {
|
||||
return (this.random() < chance) ? true : false;
|
||||
}
|
||||
|
||||
private static _randomCompare(a: Object, b: Object): number {
|
||||
return (this.random() > .5) ? 1 : -1;
|
||||
}
|
||||
class RandomUtils {
|
||||
/**
|
||||
* 在 start 与 stop之间取一个随机整数,可以用step指定间隔, 但不包括较大的端点(start与stop较大的一个)
|
||||
* 如
|
||||
* this.randrange(1, 10, 3)
|
||||
* 则返回的可能是 1 或 4 或 7 , 注意 这里面不会返回10,因为是10是大端点
|
||||
*
|
||||
* @param start
|
||||
* @param stop
|
||||
* @param step
|
||||
* @return 假设 start < stop, [start, stop) 区间内的随机整数
|
||||
*
|
||||
*/
|
||||
public static randrange(start: number, stop: number, step: number = 1): number {
|
||||
if (step == 0)
|
||||
throw new Error('step 不能为 0');
|
||||
|
||||
let width: number = stop - start;
|
||||
if (width == 0)
|
||||
throw new Error('没有可用的范围(' + start + ',' + stop + ')');
|
||||
if (width < 0)
|
||||
width = start - stop;
|
||||
|
||||
let n: number = Math.floor((width + step - 1) / step);
|
||||
return Math.floor(this.random() * n) * step + Math.min(start, stop);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回a 到 b直间的随机整数,包括 a 和 b
|
||||
* @param a
|
||||
* @param b
|
||||
* @return [a, b] 直接的随机整数
|
||||
*
|
||||
*/
|
||||
public static randint(a: number, b: number): number {
|
||||
a = Math.floor(a);
|
||||
b = Math.floor(b);
|
||||
if (a > b)
|
||||
a++;
|
||||
else
|
||||
b++;
|
||||
return this.randrange(a, b);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回 a - b之间的随机数,不包括 Math.max(a, b)
|
||||
* @param a
|
||||
* @param b
|
||||
* @return 假设 a < b, [a, b)
|
||||
*/
|
||||
public static randnum(a: number, b: number): number {
|
||||
return this.random() * (b - a) + a;
|
||||
}
|
||||
|
||||
/**
|
||||
* 打乱数组
|
||||
* @param array
|
||||
* @return
|
||||
*/
|
||||
public static shuffle(array: any[]): any[] {
|
||||
array.sort(this._randomCompare);
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从序列中随机取一个元素
|
||||
* @param sequence 可以是 数组、 vector,等只要是有length属性,并且可以用数字索引获取元素的对象,
|
||||
* 另外,字符串也是允许的。
|
||||
* @return 序列中的某一个元素
|
||||
*
|
||||
*/
|
||||
public static choice(sequence: any): any {
|
||||
if (!sequence.hasOwnProperty("length"))
|
||||
throw new Error('无法对此对象执行此操作');
|
||||
let index: number = Math.floor(this.random() * sequence.length);
|
||||
if (sequence instanceof String)
|
||||
return String(sequence).charAt(index);
|
||||
else
|
||||
return sequence[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* 对列表中的元素进行随机采æ ?
|
||||
* <pre>
|
||||
* this.sample([1, 2, 3, 4, 5], 3) // Choose 3 elements
|
||||
* [4, 1, 5]
|
||||
* </pre>
|
||||
* @param sequence
|
||||
* @param num
|
||||
* @return
|
||||
*
|
||||
*/
|
||||
public static sample(sequence: any[], num: number): any[] {
|
||||
let len: number = sequence.length;
|
||||
if (num <= 0 || len < num)
|
||||
throw new Error("采样数量不够");
|
||||
|
||||
let selected: any[] = [];
|
||||
let indices: any[] = [];
|
||||
for (let i: number = 0; i < num; i++) {
|
||||
let index: number = Math.floor(this.random() * len);
|
||||
while (indices.indexOf(index) >= 0)
|
||||
index = Math.floor(this.random() * len);
|
||||
|
||||
selected.push(sequence[index]);
|
||||
indices.push(index);
|
||||
}
|
||||
|
||||
return selected;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回 0.0 - 1.0 之间的随机数,等同于 Math.random()
|
||||
* @return Math.random()
|
||||
*
|
||||
*/
|
||||
public static random(): number {
|
||||
return Math.random();
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算概率
|
||||
* @param chance 概率
|
||||
* @return
|
||||
*/
|
||||
public static boolean(chance: number = .5): boolean {
|
||||
return (this.random() < chance) ? true : false;
|
||||
}
|
||||
|
||||
private static _randomCompare(a: Object, b: Object): number {
|
||||
return (this.random() > .5) ? 1 : -1;
|
||||
}
|
||||
}
|
||||
220
source/src/Utils/Extensions/RectangleExt.ts
Normal file
220
source/src/Utils/Extensions/RectangleExt.ts
Normal file
@@ -0,0 +1,220 @@
|
||||
module es {
|
||||
export class RectangleExt {
|
||||
/**
|
||||
* 获取指定边的位置
|
||||
* @param rect
|
||||
* @param edge
|
||||
*/
|
||||
public static getSide(rect: Rectangle, edge: Edge) {
|
||||
switch (edge) {
|
||||
case Edge.top:
|
||||
return rect.top;
|
||||
case Edge.bottom:
|
||||
return rect.bottom;
|
||||
case es.Edge.left:
|
||||
return rect.left;
|
||||
case Edge.right:
|
||||
return rect.right;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算两个矩形的并集。结果将是一个包含其他两个的矩形。
|
||||
* @param first
|
||||
* @param point
|
||||
*/
|
||||
public static union(first: Rectangle, point: Vector2) {
|
||||
let rect = new Rectangle(point.x, point.y, 0, 0);
|
||||
let result = new Rectangle();
|
||||
result.x = Math.min(first.x, rect.x);
|
||||
result.y = Math.min(first.y, rect.y);
|
||||
result.width = Math.max(first.right, rect.right) - result.x;
|
||||
result.height = Math.max(first.bottom, rect.bottom) - result.y;
|
||||
return result;
|
||||
}
|
||||
|
||||
public static getHalfRect(rect: Rectangle, edge: Edge) {
|
||||
switch (edge) {
|
||||
case Edge.top:
|
||||
return new Rectangle(rect.x, rect.y, rect.width, rect.height / 2);
|
||||
case Edge.bottom:
|
||||
return new Rectangle(rect.x, rect.y + rect.height / 2, rect.width, rect.height / 2);
|
||||
case Edge.left:
|
||||
return new Rectangle(rect.x, rect.y, rect.width / 2, rect.height);
|
||||
case Edge.right:
|
||||
return new Rectangle(rect.x + rect.width / 2, rect.y, rect.width / 2, rect.height);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取矩形的一部分,其宽度/高度的大小位于矩形的边缘,但仍然包含在其中。
|
||||
* @param rect
|
||||
* @param edge
|
||||
* @param size
|
||||
*/
|
||||
public static getRectEdgePortion(rect: Rectangle, edge: Edge, size: number = 1) {
|
||||
switch (edge) {
|
||||
case es.Edge.top:
|
||||
return new Rectangle(rect.x, rect.y, rect.width, size);
|
||||
case Edge.bottom:
|
||||
return new Rectangle(rect.x, rect.y + rect.height - size, rect.width, size);
|
||||
case Edge.left:
|
||||
return new Rectangle(rect.x, rect.y, size, rect.height);
|
||||
case Edge.right:
|
||||
return new Rectangle(rect.x + rect.width - size, rect.y, size, rect.height);
|
||||
}
|
||||
}
|
||||
|
||||
public static expandSide(rect: Rectangle, edge: Edge, amount: number) {
|
||||
amount = Math.abs(amount);
|
||||
|
||||
switch (edge) {
|
||||
case Edge.top:
|
||||
rect.y -= amount;
|
||||
rect.height += amount;
|
||||
break;
|
||||
case es.Edge.bottom:
|
||||
rect.height += amount;
|
||||
break;
|
||||
case Edge.left:
|
||||
rect.x -= amount;
|
||||
rect.width += amount;
|
||||
break;
|
||||
case Edge.right:
|
||||
rect.width += amount;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static contract(rect: Rectangle, horizontalAmount, verticalAmount) {
|
||||
rect.x += horizontalAmount;
|
||||
rect.y += verticalAmount;
|
||||
rect.width -= horizontalAmount * 2;
|
||||
rect.height -= verticalAmount * 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* 给定多边形的点,计算其边界
|
||||
* @param points
|
||||
*/
|
||||
public static boundsFromPolygonVector(points: Vector2[]) {
|
||||
// 我们需要找到最小/最大的x/y值。
|
||||
let minX = Number.POSITIVE_INFINITY;
|
||||
let minY = Number.POSITIVE_INFINITY;
|
||||
let maxX = Number.NEGATIVE_INFINITY;
|
||||
let maxY = Number.NEGATIVE_INFINITY;
|
||||
|
||||
for (let i = 0; i < points.length; i ++) {
|
||||
let pt = points[i];
|
||||
|
||||
if (pt.x < minX)
|
||||
minX = pt.x;
|
||||
if (pt.x > maxX)
|
||||
maxX = pt.x;
|
||||
|
||||
if (pt.y < minY)
|
||||
minY = pt.y;
|
||||
if (pt.y > maxY)
|
||||
maxY = pt.y;
|
||||
}
|
||||
|
||||
return this.fromMinMaxVector(new Vector2(minX, minY), new Vector2(maxX, maxY));
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个给定最小/最大点(左上角,右下角)的矩形
|
||||
* @param min
|
||||
* @param max
|
||||
*/
|
||||
public static fromMinMaxVector(min: Vector2, max: Vector2) {
|
||||
return new Rectangle(min.x, min.y, max.x - min.x, max.y - min.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回一个跨越当前边界和提供的delta位置的Bounds
|
||||
* @param rect
|
||||
* @param deltaX
|
||||
* @param deltaY
|
||||
*/
|
||||
public static getSweptBroadphaseBounds(rect: Rectangle, deltaX: number, deltaY: number){
|
||||
let broadphasebox = Rectangle.empty;
|
||||
|
||||
broadphasebox.x = deltaX > 0 ? rect.x : rect.x + deltaX;
|
||||
broadphasebox.y = deltaY > 0 ? rect.y : rect.y + deltaY;
|
||||
broadphasebox.width = deltaX > 0 ? deltaX + rect.width : rect.width - deltaX;
|
||||
broadphasebox.height = deltaY > 0 ? deltaY + rect.height : rect.height - deltaY;
|
||||
|
||||
return broadphasebox;
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果矩形发生碰撞,返回true
|
||||
* moveX和moveY将返回b1为避免碰撞而必须移动的移动量
|
||||
* @param rect
|
||||
* @param other
|
||||
* @param moveX
|
||||
* @param moveY
|
||||
*/
|
||||
public collisionCheck(rect: Rectangle, other: Rectangle, moveX: Ref<number>, moveY: Ref<number>) {
|
||||
moveX.value = moveY.value = 0;
|
||||
|
||||
let l = other.x - (rect.x + rect.width);
|
||||
let r = (other.x + other.width) - rect.x;
|
||||
let t = other.y - (rect.y + rect.height);
|
||||
let b = (other.y + other.height) - rect.y;
|
||||
|
||||
// 检验是否有碰撞
|
||||
if (l > 0 || r < 0 || t > 0 || b < 0)
|
||||
return false;
|
||||
|
||||
// 求两边的偏移量
|
||||
moveX.value = Math.abs(l) < r ? l : r;
|
||||
moveY.value = Math.abs(t) < b ? t : b;
|
||||
|
||||
// 只使用最小的偏移量
|
||||
if (Math.abs(moveX.value) < Math.abs(moveY.value))
|
||||
moveY.value = 0;
|
||||
else
|
||||
moveX.value = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算两个矩形之间有符号的交点深度
|
||||
* @param rectA
|
||||
* @param rectB
|
||||
* @returns 两个相交的矩形之间的重叠量。
|
||||
* 这些深度值可以是负值,取决于矩形相交的边。
|
||||
* 这允许调用者确定正确的推送对象的方向,以解决碰撞问题。
|
||||
* 如果矩形不相交,则返回Vector2.zero。
|
||||
*/
|
||||
public static getIntersectionDepth(rectA: Rectangle, rectB: Rectangle) {
|
||||
// 计算半尺寸
|
||||
let halfWidthA = rectA.width / 2;
|
||||
let halfHeightA = rectA.height / 2;
|
||||
let halfWidthB = rectB.width / 2;
|
||||
let halfHeightB = rectB.height / 2;
|
||||
|
||||
// 计算中心
|
||||
let centerA = new Vector2(rectA.left + halfWidthA, rectA.top + halfHeightA);
|
||||
let centerB = new Vector2(rectB.left + halfWidthB, rectB.top + halfHeightB);
|
||||
|
||||
// 计算当前中心间的距离和最小非相交距离
|
||||
let distanceX = centerA.x - centerB.x;
|
||||
let distanceY = centerA.y - centerB.y;
|
||||
let minDistanceX = halfWidthA + halfWidthB;
|
||||
let minDistanceY = halfHeightA + halfHeightB;
|
||||
|
||||
// 如果我们根本不相交,则返回(0,0)
|
||||
if (Math.abs(distanceX) >= minDistanceX || Math.abs(distanceY) >= minDistanceY)
|
||||
return Vector2.zero;
|
||||
|
||||
// 计算并返回交叉点深度
|
||||
let depthX = distanceX > 0 ? minDistanceX - distanceX : -minDistanceX - distanceX;
|
||||
let depthY = distanceY > 0 ? minDistanceY - distanceY : -minDistanceY - distanceY;
|
||||
|
||||
return new Vector2(depthX, depthY);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
class WebGLUtils {
|
||||
/**
|
||||
* 获取webgl context
|
||||
*/
|
||||
public static getContext() {
|
||||
const canvas = document.getElementsByTagName('canvas')[0];
|
||||
return canvas.getContext('2d');
|
||||
}
|
||||
class WebGLUtils {
|
||||
/**
|
||||
* 获取webgl context
|
||||
*/
|
||||
public static getContext() {
|
||||
const canvas = document.getElementsByTagName('canvas')[0];
|
||||
return canvas.getContext('2d');
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
///<reference path="../LinkList.ts" />
|
||||
///<reference path="../Collections/LinkList.ts" />
|
||||
module es {
|
||||
/**
|
||||
* 类,它可以计算出一个网格,表示从给定的一组遮挡物的原点可以看到哪些区域。使用方法如下。
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
module es {
|
||||
export class RectangleExt {
|
||||
/**
|
||||
* 获取指定边的位置
|
||||
* @param rect
|
||||
* @param edge
|
||||
*/
|
||||
public static getSide(rect: Rectangle, edge: Edge) {
|
||||
switch (edge) {
|
||||
case Edge.top:
|
||||
return rect.top;
|
||||
case Edge.bottom:
|
||||
return rect.bottom;
|
||||
case es.Edge.left:
|
||||
return rect.left;
|
||||
case Edge.right:
|
||||
return rect.right;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算两个矩形的并集。结果将是一个包含其他两个的矩形。
|
||||
* @param first
|
||||
* @param point
|
||||
*/
|
||||
public static union(first: Rectangle, point: Vector2) {
|
||||
let rect = new Rectangle(point.x, point.y, 0, 0);
|
||||
let result = new Rectangle();
|
||||
result.x = Math.min(first.x, rect.x);
|
||||
result.y = Math.min(first.y, rect.y);
|
||||
result.width = Math.max(first.right, rect.right) - result.x;
|
||||
result.height = Math.max(first.bottom, rect.bottom) - result.y;
|
||||
return result;
|
||||
}
|
||||
|
||||
public static getHalfRect(rect: Rectangle, edge: Edge) {
|
||||
switch (edge) {
|
||||
case Edge.top:
|
||||
return new Rectangle(rect.x, rect.y, rect.width, rect.height / 2);
|
||||
case Edge.bottom:
|
||||
return new Rectangle(rect.x, rect.y + rect.height / 2, rect.width, rect.height / 2);
|
||||
case Edge.left:
|
||||
return new Rectangle(rect.x, rect.y, rect.width / 2, rect.height);
|
||||
case Edge.right:
|
||||
return new Rectangle(rect.x + rect.width / 2, rect.y, rect.width / 2, rect.height);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取矩形的一部分,其宽度/高度的大小位于矩形的边缘,但仍然包含在其中。
|
||||
* @param rect
|
||||
* @param edge
|
||||
* @param size
|
||||
*/
|
||||
public static getRectEdgePortion(rect: Rectangle, edge: Edge, size: number = 1) {
|
||||
switch (edge) {
|
||||
case es.Edge.top:
|
||||
return new Rectangle(rect.x, rect.y, rect.width, size);
|
||||
case Edge.bottom:
|
||||
return new Rectangle(rect.x, rect.y + rect.height - size, rect.width, size);
|
||||
case Edge.left:
|
||||
return new Rectangle(rect.x, rect.y, size, rect.height);
|
||||
case Edge.right:
|
||||
return new Rectangle(rect.x + rect.width - size, rect.y, size, rect.height);
|
||||
}
|
||||
}
|
||||
|
||||
public static expandSide(rect: Rectangle, edge: Edge, amount: number) {
|
||||
amount = Math.abs(amount);
|
||||
|
||||
switch (edge) {
|
||||
case Edge.top:
|
||||
rect.y -= amount;
|
||||
rect.height += amount;
|
||||
break;
|
||||
case es.Edge.bottom:
|
||||
rect.height += amount;
|
||||
break;
|
||||
case Edge.left:
|
||||
rect.x -= amount;
|
||||
rect.width += amount;
|
||||
break;
|
||||
case Edge.right:
|
||||
rect.width += amount;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static contract(rect: Rectangle, horizontalAmount, verticalAmount) {
|
||||
rect.x += horizontalAmount;
|
||||
rect.y += verticalAmount;
|
||||
rect.width -= horizontalAmount * 2;
|
||||
rect.height -= verticalAmount * 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user