@cjhd/cj-decimal-quadtree 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,121 @@
1
+ import { Decimal } from '@cjhd/cj-decimal';
2
+ /**
3
+ * 包围盒
4
+ */
5
+ export interface IDecimalBox {
6
+ /** x最小值 */
7
+ x: Decimal;
8
+ /** y最小值 */
9
+ y: Decimal;
10
+ /** z最小值 */
11
+ z?: Decimal;
12
+ /** 宽度 */
13
+ width: Decimal;
14
+ /** 高度 */
15
+ height: Decimal;
16
+ /** 深度 */
17
+ depth?: Decimal;
18
+ }
19
+ /**
20
+ * AABB
21
+ */
22
+ export interface IDecimalAABB {
23
+ /**最小点坐标 */
24
+ min: {
25
+ x: Decimal;
26
+ y: Decimal;
27
+ z?: Decimal;
28
+ };
29
+ /**最大点坐标 */
30
+ max: {
31
+ x: Decimal;
32
+ y: Decimal;
33
+ z?: Decimal;
34
+ };
35
+ }
36
+ export interface IDecimalBody<T = unknown> {
37
+ readonly width: Decimal;
38
+ readonly height: Decimal;
39
+ readonly depth: Decimal;
40
+ readonly xMin: Decimal;
41
+ readonly yMin: Decimal;
42
+ readonly zMin: Decimal;
43
+ readonly xMid: Decimal;
44
+ readonly yMid: Decimal;
45
+ readonly zMid: Decimal;
46
+ readonly xMax: Decimal;
47
+ readonly yMax: Decimal;
48
+ readonly zMax: Decimal;
49
+ data: T;
50
+ }
51
+ export declare enum DecimalBodyEvent {
52
+ AABB_CHANGED = 0
53
+ }
54
+ export declare class DecimalBody<T = unknown> {
55
+ private _xMin;
56
+ private _yMin;
57
+ private _zMin;
58
+ private _xMid;
59
+ private _yMid;
60
+ private _zMid;
61
+ private _xMax;
62
+ private _yMax;
63
+ private _zMax;
64
+ private _width;
65
+ private _height;
66
+ private _depth;
67
+ get xMin(): Decimal;
68
+ get yMin(): Decimal;
69
+ get zMin(): Decimal;
70
+ get xMid(): Decimal;
71
+ get yMid(): Decimal;
72
+ get zMid(): Decimal;
73
+ get xMax(): Decimal;
74
+ get yMax(): Decimal;
75
+ get zMax(): Decimal;
76
+ get width(): Decimal;
77
+ get height(): Decimal;
78
+ get depth(): Decimal;
79
+ private _xMinRaw;
80
+ get xMinRaw(): number;
81
+ private _yMinRaw;
82
+ get yMinRaw(): number;
83
+ private _zMinRaw;
84
+ get zMinRaw(): number;
85
+ private _xMidRaw;
86
+ get xMidRaw(): number;
87
+ private _yMidRaw;
88
+ get yMidRaw(): number;
89
+ private _zMidRaw;
90
+ get zMidRaw(): number;
91
+ private _xMaxRaw;
92
+ get xMaxRaw(): number;
93
+ private _yMaxRaw;
94
+ get yMaxRaw(): number;
95
+ private _zMaxRaw;
96
+ get zMaxRaw(): number;
97
+ /**唯一ID */
98
+ id: number;
99
+ /**碰撞分组 */
100
+ group: number;
101
+ /**碰撞掩码 */
102
+ mask: number;
103
+ /**数据 */
104
+ data: T;
105
+ /** 事件: 替换原先的 new EventTarget() */
106
+ private readonly event;
107
+ constructor(id?: number);
108
+ static emit(body: DecimalBody, type: DecimalBodyEvent, ...args: any[]): void;
109
+ static on(body: DecimalBody, type: DecimalBodyEvent, listener: (body?: DecimalBody) => void, target?: unknown): void;
110
+ static once(body: DecimalBody, type: DecimalBodyEvent, listener: (body?: DecimalBody) => void, target?: unknown): void;
111
+ static off(body: DecimalBody, type: DecimalBodyEvent, listener: (body?: DecimalBody) => void, target?: unknown): void;
112
+ static targetOff(body: DecimalBody, target: unknown): void;
113
+ private syncRawBounds;
114
+ setID(id: number): this;
115
+ setGroup(group: number): this;
116
+ setMask(mask: number): this;
117
+ setData(data: T): this;
118
+ setBox(box: IDecimalBox): this;
119
+ setAABB(aabb: IDecimalAABB): this;
120
+ intersect(other: DecimalBody): boolean;
121
+ }
@@ -0,0 +1,61 @@
1
+ /**********************************************************************
2
+ * DecimalQuadTree.ts —— 定点数版四叉树
3
+ * -------------------------------------------------
4
+ * ① 完全沿用原 QuadTree.ts 的结构与接口,方便直接替换。
5
+ * ② 所有空间数值均改为 @cjhd/cj-decimal 的 Decimal。
6
+ * ③ 位操作 / Map Key 仍用 number,不影响 bit flag 与 ID。
7
+ * ④ 对重要逻辑步骤增加了更详细的中文注释,便于维护。
8
+ *********************************************************************/
9
+ import { Decimal } from '@cjhd/cj-decimal';
10
+ import { DecimalBody } from './DecimalBody';
11
+ export declare class DecimalQuadTree<T = unknown> {
12
+ private readonly quadTree;
13
+ private readonly nodeCache;
14
+ private readonly id2Node;
15
+ private readonly id2Tree;
16
+ /**
17
+ * 创建
18
+ * @param x 最小x
19
+ * @param y 最小y
20
+ * @param width 宽
21
+ * @param height 高
22
+ * @param maxLevels 最大深度(默认4)
23
+ */
24
+ constructor(x: Decimal, y: Decimal, width: Decimal, height: Decimal, maxLevels?: number);
25
+ private newNode;
26
+ private detach;
27
+ private recycle;
28
+ insert(body: DecimalBody<T>): number;
29
+ /**
30
+ * 移除一个碰撞体(函数签名)
31
+ */
32
+ remove(id: number): boolean;
33
+ /**
34
+ * 移除一个碰撞体
35
+ */
36
+ remove(body: DecimalBody<T>): boolean;
37
+ /** AABB 变化时重新放置节点 */
38
+ private refresh;
39
+ /**
40
+ * 检索与某个碰撞体相交的碰撞体数组
41
+ * @param id 碰撞体id
42
+ */
43
+ retrieve(id: number, out?: DecimalBody<T>[]): DecimalBody<T>[];
44
+ /**
45
+ * 检索与某个碰撞体相交的碰撞体数组
46
+ * @param body 碰撞体
47
+ */
48
+ retrieve(body: DecimalBody, out?: DecimalBody<T>[]): DecimalBody<T>[];
49
+ /**
50
+ * 检查某个碰撞体是否与其它至少一个碰撞体相交
51
+ * @param id 碰撞体id
52
+ */
53
+ check(id: number): boolean;
54
+ /**
55
+ * 检查某个碰撞体是否与其它至少一个碰撞体相交
56
+ * @param body 碰撞体
57
+ */
58
+ check(body: DecimalBody): boolean;
59
+ trigger(cb: (a: DecimalBody<T>, b: DecimalBody<T>) => any): void;
60
+ clear(): void;
61
+ }
@@ -0,0 +1,4 @@
1
+ /* CJ_RUNTIME_PROTECTED_JS */
2
+ "use strict";
3
+ var __cj_sa_b62742c0=["number"];
4
+ Object.defineProperty(exports,"__esModule",{value:!0}),exports.DecimalBody=exports.DecimalBodyEvent=void 0;const cj_decimal_1=require("@cjhd/cj-decimal"),cj_event_emitter_1=require("@cjhd/cj-event-emitter");class UUID{static create(){return++this.value>1e10&&(this.value=1),this.value}}var DecimalBodyEvent;UUID.value=0,function(i){i[i.AABB_CHANGED=0]="AABB_CHANGED"}(DecimalBodyEvent=exports.DecimalBodyEvent||(exports.DecimalBodyEvent={}));class DecimalBody{get xMin(){return this._xMin}get yMin(){return this._yMin}get zMin(){return this._zMin}get xMid(){return this._xMid}get yMid(){return this._yMid}get zMid(){return this._zMid}get xMax(){return this._xMax}get yMax(){return this._yMax}get zMax(){return this._zMax}get width(){return this._width}get height(){return this._height}get depth(){return this._depth}get xMinRaw(){return this._xMinRaw}get yMinRaw(){return this._yMinRaw}get zMinRaw(){return this._zMinRaw}get xMidRaw(){return this._xMidRaw}get yMidRaw(){return this._yMidRaw}get zMidRaw(){return this._zMidRaw}get xMaxRaw(){return this._xMaxRaw}get yMaxRaw(){return this._yMaxRaw}get zMaxRaw(){return this._zMaxRaw}constructor(i){this._xMin=new cj_decimal_1.Decimal,this._yMin=new cj_decimal_1.Decimal,this._zMin=new cj_decimal_1.Decimal,this._xMid=new cj_decimal_1.Decimal,this._yMid=new cj_decimal_1.Decimal,this._zMid=new cj_decimal_1.Decimal,this._xMax=new cj_decimal_1.Decimal,this._yMax=new cj_decimal_1.Decimal,this._zMax=new cj_decimal_1.Decimal,this._width=new cj_decimal_1.Decimal,this._height=new cj_decimal_1.Decimal,this._depth=new cj_decimal_1.Decimal,this._xMinRaw=0,this._yMinRaw=0,this._zMinRaw=0,this._xMidRaw=0,this._yMidRaw=0,this._zMidRaw=0,this._xMaxRaw=0,this._yMaxRaw=0,this._zMaxRaw=0,this.id=0,this.group=1,this.mask=1,this.data=void 0,this.event=new cj_event_emitter_1.EventEmitter,this.id=__cj_sa_b62742c0[0]==typeof i?i:UUID.create()}static emit(i,t,...e){i.event.emit(t,...e)}static on(i,t,e,s){i.event.on(t,e,s)}static once(i,t,e,s){i.event.once(t,e,s)}static off(i,t,e,s){i.event.off(t,e,s)}static targetOff(i,t){i.event.targetOff(t)}syncRawBounds(){this._xMinRaw=this._xMin.toBigInt(),this._yMinRaw=this._yMin.toBigInt(),this._zMinRaw=this._zMin.toBigInt(),this._xMidRaw=this._xMid.toBigInt(),this._yMidRaw=this._yMid.toBigInt(),this._zMidRaw=this._zMid.toBigInt(),this._xMaxRaw=this._xMax.toBigInt(),this._yMaxRaw=this._yMax.toBigInt(),this._zMaxRaw=this._zMax.toBigInt()}setID(i){return this.id=i,this}setGroup(i){return this.group=i,this}setMask(i){return this.mask=i,this}setData(i){return this.data=i,this}setBox(i){var t,e;return i?(this._xMin.copy(i.x),this._yMin.copy(i.y),this._zMin.copy(null!==(t=i.z)&&void 0!==t?t:cj_decimal_1.Decimal.ZERO),this._width.copy(i.width),this._height.copy(i.height),this._depth.copy(null!==(e=i.depth)&&void 0!==e?e:cj_decimal_1.Decimal.ZERO),this._xMax.copy(this._xMin).add(this._width),this._yMax.copy(this._yMin).add(this._height),this._zMax.copy(this._zMin).add(this._depth),this._xMid.copy(this._width).divInt(2).add(this._xMin),this._yMid.copy(this._height).divInt(2).add(this._yMin),this._zMid.copy(this._depth).divInt(2).add(this._zMin),this.syncRawBounds(),this.event.emit(DecimalBodyEvent.AABB_CHANGED,this),this):this}setAABB(i){var t,e;return i?(this._xMin.copy(i.min.x),this._yMin.copy(i.min.y),this._zMin.copy(null!==(t=i.min.z)&&void 0!==t?t:cj_decimal_1.Decimal.ZERO),this._xMax.copy(i.max.x),this._yMax.copy(i.max.y),this._zMax.copy(null!==(e=i.max.z)&&void 0!==e?e:cj_decimal_1.Decimal.ZERO),this._width.copy(this._xMax).sub(this._xMin),this._height.copy(this._yMax).sub(this._yMin),this._depth.copy(this._zMax).sub(this._zMin),this._xMid.copy(this._width).divInt(2).add(this._xMin),this._yMid.copy(this._height).divInt(2).add(this._yMin),this._zMid.copy(this._depth).divInt(2).add(this._zMin),this.syncRawBounds(),this.event.emit(DecimalBodyEvent.AABB_CHANGED,this),this):this}intersect(i){return!!(this.group&i.mask&&i.group&this.mask)&&!(this._xMinRaw>i._xMaxRaw||this._xMaxRaw<i._xMinRaw||this._yMinRaw>i._yMaxRaw||this._yMaxRaw<i._yMinRaw||this._zMinRaw>i._zMaxRaw||this._zMaxRaw<i._zMinRaw)}}exports.DecimalBody=DecimalBody;
@@ -0,0 +1,4 @@
1
+ /* CJ_RUNTIME_PROTECTED_JS */
2
+ "use strict";
3
+ var __cj_sa_eeef3e2e=["number"];
4
+ Object.defineProperty(exports,"__esModule",{value:!0}),exports.DecimalQuadTree=void 0;const DecimalBody_1=require("./DecimalBody"),DecimalQuadFlag=[1,2,4,8];class DecimalQuadBody extends DecimalBody_1.DecimalBody{static createWithAABB(e){const t=new DecimalQuadBody;return t.setAABB(e),t}static createWithBox(e){const t=new DecimalQuadBody;return t.setBox(e),t}constructor(){super()}contain(e){return this.xMinRaw<=e.xMinRaw&&this.xMaxRaw>=e.xMaxRaw&&this.yMinRaw<=e.yMinRaw&&this.yMaxRaw>=e.yMaxRaw}split(){const{xMin:e,yMin:t,xMax:i,yMax:a,xMid:s,yMid:r}=this;return[DecimalQuadBody.createWithAABB({min:{x:s,y:r},max:{x:i,y:a}}),DecimalQuadBody.createWithAABB({min:{x:e,y:r},max:{x:s,y:a}}),DecimalQuadBody.createWithAABB({min:{x:e,y:t},max:{x:s,y:r}}),DecimalQuadBody.createWithAABB({min:{x:s,y:t},max:{x:i,y:r}})]}}class DecimalQuadNode{constructor(e){this.value=null,this.last=null,this.next=null,this.value=e}concat(e){this.next=e,e&&(e.last=this)}disConcat(){this.last.concat(this.next),this.last=this.next=null}}class DecimalQuadList{constructor(){this.head=new DecimalQuadNode(null)}push(e){e.concat(this.head.next),this.head.concat(e)}clear(){this.head.next=null}}class DecimalQuadSubTree{constructor(e,t,i=0,a=null){this.quadBody=e,this.maxLevel=t,this.level=i,this.parent=a,this.quadList=new DecimalQuadList,this.children=[]}split(){if(this.children.length)return;const e=this.level+1;this.quadBody.split().forEach(t=>this.children.push(new DecimalQuadSubTree(t,this.maxLevel,e,this)))}getDecimalQuadFlags(e){let t=0;const i=e.xMinRaw<this.quadBody.xMidRaw,a=e.yMaxRaw>this.quadBody.yMidRaw,s=e.xMaxRaw>this.quadBody.xMidRaw,r=e.yMinRaw<this.quadBody.yMidRaw;return a&&s&&(t|=DecimalQuadFlag[0]),a&&i&&(t|=DecimalQuadFlag[1]),i&&r&&(t|=DecimalQuadFlag[2]),s&&r&&(t|=DecimalQuadFlag[3]),t}insert(e){if(this.level===this.maxLevel)return this.quadList.push(e),this;const t=DecimalQuadFlag.indexOf(this.getDecimalQuadFlags(e.value));return-1===t?(this.quadList.push(e),this):(this.children.length||this.split(),this.children[t].insert(e))}insertReverse(e){return this.quadBody.contain(e.value)?this.insert(e):this.parent?this.parent.insertReverse(e):this.insert(e)}retrieveSelf(e,t,i=!1){for(let a=this.quadList.head.next;a;a=a.next){const s=a.value;if(t!==s&&t.intersect(s)&&(e.push(s),!i))break}}retrieve(e,t,i=!1){if(this.retrieveSelf(e,t,i),!i&&e.length)return e;if(this.level===this.maxLevel||!this.children.length)return e;const a=this.getDecimalQuadFlags(t);for(let s=0;s<4&&!(a&DecimalQuadFlag[s]&&(this.children[s].retrieve(e,t,i),!i&&e.length));s++);return e}retrieveReverse(e,t,i=!1){return this.parent&&(this.parent.retrieveReverse(e,t,i),!i&&e.length)?e:this.retrieve(e,t,i)}clear(){this.children.forEach(e=>e.clear()),this.quadList.clear(),this.children.length=0}}class DecimalQuadTree{constructor(e,t,i,a,s=4){this.nodeCache=[],this.id2Node=new Map,this.id2Tree=new Map;const r=DecimalQuadBody.createWithBox({x:e,y:t,width:i,height:a});this.quadTree=new DecimalQuadSubTree(r,s)}newNode(e){var t;const i=null!==(t=this.nodeCache.pop())&&void 0!==t?t:new DecimalQuadNode(e);return i.value=e,i}detach(e){const t=this.id2Node.get(e);return t&&t.disConcat(),t}recycle(e){DecimalBody_1.DecimalBody.off(e.value,DecimalBody_1.DecimalBodyEvent.AABB_CHANGED,this.refresh,this),e.value=null,this.nodeCache.push(e)}insert(e){const t=this.newNode(e),i=this.quadTree.insert(t);return this.id2Node.set(e.id,t),this.id2Tree.set(e.id,i),DecimalBody_1.DecimalBody.on(e,DecimalBody_1.DecimalBodyEvent.AABB_CHANGED,this.refresh,this),e.id}remove(e){const t="number"==typeof e?e:e.id,i=this.detach(t);return!!i&&(this.id2Node.delete(t),this.id2Tree.delete(t),this.recycle(i),!0)}refresh(e){const t=this.detach(e.id);if(!t)return!1;const i=this.id2Tree.get(e.id).insertReverse(t);return this.id2Tree.set(e.id,i),!0}retrieve(e,t=[]){if("number"==typeof e){const i=this.id2Node.get(e);return i?this.id2Tree.get(e).retrieveReverse(t,i.value,!0):t}return this.quadTree.retrieve(t,e,!0)}check(e){if(__cj_sa_eeef3e2e[0]==typeof e){const t=this.id2Node.get(e);return!!t&&this.id2Tree.get(e).retrieveReverse([],t.value,!1).length>0}return this.quadTree.retrieve([],e,!1).length>0}trigger(e){const t=[],i=new Map;this.id2Node.forEach(a=>{t.length=0,this.id2Tree.get(a.value.id).retrieveReverse(t,a.value,!0),t.forEach(t=>{const s=a.value,r=t,d=s.id<r.id?s.id:r.id,h=s.id<r.id?r.id:s.id;i.get(d)!==h&&(i.set(d,h),e(s,r))})})}clear(){this.id2Node.forEach(e=>DecimalBody_1.DecimalBody.off(e.value,DecimalBody_1.DecimalBodyEvent.AABB_CHANGED,this.refresh,this)),this.quadTree.clear(),this.id2Node.clear(),this.id2Tree.clear(),this.nodeCache.length=0}}exports.DecimalQuadTree=DecimalQuadTree;
@@ -0,0 +1,4 @@
1
+ /* CJ_RUNTIME_PROTECTED_JS */
2
+ "use strict";
3
+ var __cj_sa_06ac20d3=["./assets/DecimalQuadTree"];
4
+ Object.defineProperty(exports,"__esModule",{value:!0}),exports.DecimalQuadTree=exports.DecimalBody=void 0;var DecimalBody_1=require("./assets/DecimalBody");Object.defineProperty(exports,"DecimalBody",{enumerable:!0,get:function(){return DecimalBody_1.DecimalBody}});var DecimalQuadTree_1=require(__cj_sa_06ac20d3[0]);Object.defineProperty(exports,"DecimalQuadTree",{enumerable:!0,get:function(){return DecimalQuadTree_1.DecimalQuadTree}});
package/index.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export { DecimalBody, type IDecimalAABB, type IDecimalBox } from './assets/DecimalBody';
2
+ export { DecimalQuadTree } from './assets/DecimalQuadTree';
package/package.json CHANGED
@@ -1,12 +1,9 @@
1
1
  {
2
2
  "name": "@cjhd/cj-decimal-quadtree",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "engine": ">=3.8.0",
5
5
  "description": "定点数四叉树模块",
6
- "main": "index.ts",
7
- "scripts": {
8
- "test": "echo \"Error: no test specified\" && exit 1"
9
- },
6
+ "main": "./dist/cocos/index.js",
10
7
  "repository": {
11
8
  "type": "git",
12
9
  "url": "https://gitee.com/cocos2d-zp/cococs-creator-frame-3d"
@@ -18,7 +15,30 @@
18
15
  "author": "超M <402879660@qq.com>",
19
16
  "license": "MIT",
20
17
  "dependencies": {
21
- "@cjhd/cj-decimal": "^1.0.0",
22
- "@cjhd/cj-event-emitter": "^1.0.0"
23
- }
18
+ "@cjhd/cj-decimal": "^1.0.1",
19
+ "@cjhd/cj-event-emitter": "^1.0.1"
20
+ },
21
+ "module": "./dist/cocos/index.js",
22
+ "browser": "./dist/cocos/index.js",
23
+ "types": "./index.d.ts",
24
+ "exports": {
25
+ ".": {
26
+ "types": "./index.d.ts",
27
+ "import": "./dist/cocos/index.js",
28
+ "browser": "./dist/cocos/index.js",
29
+ "default": "./dist/cocos/index.js"
30
+ },
31
+ "./assets/*": {
32
+ "import": "./dist/cocos/assets/*",
33
+ "browser": "./dist/cocos/assets/*",
34
+ "default": "./dist/cocos/assets/*"
35
+ }
36
+ },
37
+ "files": [
38
+ "dist/",
39
+ "**/*.d.ts",
40
+ "package.json",
41
+ "README.md",
42
+ "license/"
43
+ ]
24
44
  }
@@ -1,249 +0,0 @@
1
- import { Decimal } from '@cjhd/cj-decimal';
2
- import { EventEmitter } from '@cjhd/cj-event-emitter';
3
-
4
- class UUID {
5
- private static value = 0;
6
- public static create() {
7
- if (++this.value > 1E10) {
8
- this.value = 1;
9
- }
10
- return this.value;
11
- }
12
- }
13
-
14
- /**
15
- * 包围盒
16
- */
17
- export interface IDecimalBox {
18
- /** x最小值 */
19
- x: Decimal;
20
- /** y最小值 */
21
- y: Decimal;
22
- /** z最小值 */
23
- z?: Decimal;
24
- /** 宽度 */
25
- width: Decimal;
26
- /** 高度 */
27
- height: Decimal;
28
- /** 深度 */
29
- depth?: Decimal;
30
- }
31
-
32
- /**
33
- * AABB
34
- */
35
- export interface IDecimalAABB {
36
- /**最小点坐标 */
37
- min: { x: Decimal; y: Decimal; z?: Decimal; };
38
- /**最大点坐标 */
39
- max: { x: Decimal; y: Decimal; z?: Decimal; };
40
- }
41
-
42
- export interface IDecimalBody<T = unknown> {
43
- readonly width: Decimal;
44
- readonly height: Decimal;
45
- readonly depth: Decimal;
46
-
47
- readonly xMin: Decimal;
48
- readonly yMin: Decimal;
49
- readonly zMin: Decimal;
50
-
51
- readonly xMid: Decimal;
52
- readonly yMid: Decimal;
53
- readonly zMid: Decimal;
54
-
55
- readonly xMax: Decimal;
56
- readonly yMax: Decimal;
57
- readonly zMax: Decimal;
58
-
59
- data: T;
60
- }
61
-
62
- export enum DecimalBodyEvent {
63
- AABB_CHANGED
64
- }
65
-
66
- export class DecimalBody<T = unknown> {
67
- private _xMin: Decimal = new Decimal();
68
- private _yMin: Decimal = new Decimal();
69
- private _zMin: Decimal = new Decimal();
70
-
71
- private _xMid: Decimal = new Decimal();
72
- private _yMid: Decimal = new Decimal();
73
- private _zMid: Decimal = new Decimal();
74
-
75
- private _xMax: Decimal = new Decimal();
76
- private _yMax: Decimal = new Decimal();
77
- private _zMax: Decimal = new Decimal();
78
-
79
- private _width: Decimal = new Decimal();
80
- private _height: Decimal = new Decimal();
81
- private _depth: Decimal = new Decimal();
82
-
83
- public get xMin() { return this._xMin; }
84
- public get yMin() { return this._yMin; }
85
- public get zMin() { return this._zMin; }
86
-
87
- public get xMid() { return this._xMid; }
88
- public get yMid() { return this._yMid; }
89
- public get zMid() { return this._zMid; }
90
-
91
- public get xMax() { return this._xMax; }
92
- public get yMax() { return this._yMax; }
93
- public get zMax() { return this._zMax; }
94
-
95
- public get width() { return this._width; }
96
- public get height() { return this._height; }
97
- public get depth() { return this._depth; }
98
-
99
- // Quadtree 宽相位比较缓存。只在 setBox/setAABB 同步,用于减少 Decimal 比较调用。
100
- private _xMinRaw = 0;
101
- public get xMinRaw(): number { return this._xMinRaw; }
102
- private _yMinRaw = 0;
103
- public get yMinRaw(): number { return this._yMinRaw; }
104
- private _zMinRaw = 0;
105
- public get zMinRaw(): number { return this._zMinRaw; }
106
- private _xMidRaw = 0;
107
- public get xMidRaw(): number { return this._xMidRaw; }
108
- private _yMidRaw = 0;
109
- public get yMidRaw(): number { return this._yMidRaw; }
110
- private _zMidRaw = 0;
111
- public get zMidRaw(): number { return this._zMidRaw; }
112
- private _xMaxRaw = 0;
113
- public get xMaxRaw(): number { return this._xMaxRaw; }
114
- private _yMaxRaw = 0;
115
- public get yMaxRaw(): number { return this._yMaxRaw; }
116
- private _zMaxRaw = 0;
117
- public get zMaxRaw(): number { return this._zMaxRaw; }
118
-
119
- /**唯一ID */
120
- public id: number = 0;
121
-
122
- /**碰撞分组 */
123
- public group: number = 1;
124
-
125
- /**碰撞掩码 */
126
- public mask: number = 1;
127
-
128
- /**数据 */
129
- public data: T = undefined;
130
-
131
- /** 事件: 替换原先的 new EventTarget() */
132
- private readonly event = new EventEmitter();
133
-
134
- constructor(id?: number) {
135
- this.id = typeof id === 'number' ? id : UUID.create();
136
- }
137
-
138
- public static emit(body: DecimalBody, type: DecimalBodyEvent, ...args: any[]) {
139
- body.event.emit(type, ...args);
140
- }
141
-
142
- public static on(body: DecimalBody, type: DecimalBodyEvent, listener: (body?: DecimalBody) => void, target?: unknown) {
143
- body.event.on(type, listener, target);
144
- }
145
-
146
- public static once(body: DecimalBody, type: DecimalBodyEvent, listener: (body?: DecimalBody) => void, target?: unknown) {
147
- body.event.once(type, listener, target);
148
- }
149
-
150
- public static off(body: DecimalBody, type: DecimalBodyEvent, listener: (body?: DecimalBody) => void, target?: unknown) {
151
- body.event.off(type, listener, target);
152
- }
153
-
154
- public static targetOff(body: DecimalBody, target: unknown) {
155
- body.event.targetOff(target);
156
- }
157
-
158
- private syncRawBounds() {
159
- this._xMinRaw = this._xMin.toBigInt();
160
- this._yMinRaw = this._yMin.toBigInt();
161
- this._zMinRaw = this._zMin.toBigInt();
162
- this._xMidRaw = this._xMid.toBigInt();
163
- this._yMidRaw = this._yMid.toBigInt();
164
- this._zMidRaw = this._zMid.toBigInt();
165
- this._xMaxRaw = this._xMax.toBigInt();
166
- this._yMaxRaw = this._yMax.toBigInt();
167
- this._zMaxRaw = this._zMax.toBigInt();
168
- }
169
-
170
- public setID(id: number) {
171
- this.id = id;
172
- return this;
173
- }
174
-
175
- public setGroup(group: number) {
176
- this.group = group;
177
- return this;
178
- }
179
-
180
- public setMask(mask: number) {
181
- this.mask = mask;
182
- return this;
183
- }
184
-
185
- public setData(data: T) {
186
- this.data = data;
187
- return this;
188
- }
189
-
190
- public setBox(box: IDecimalBox) {
191
- if (!box) return this;
192
-
193
- // 不能直接 this._xMin = box.x;Decimal 是可变对象,后续 add/sub 会污染输入对象和其它字段。
194
- this._xMin.copy(box.x);
195
- this._yMin.copy(box.y);
196
- this._zMin.copy(box.z ?? Decimal.ZERO);
197
-
198
- this._width.copy(box.width);
199
- this._height.copy(box.height);
200
- this._depth.copy(box.depth ?? Decimal.ZERO);
201
-
202
- this._xMax.copy(this._xMin).add(this._width);
203
- this._yMax.copy(this._yMin).add(this._height);
204
- this._zMax.copy(this._zMin).add(this._depth);
205
-
206
- this._xMid.copy(this._width).divInt(2).add(this._xMin);
207
- this._yMid.copy(this._height).divInt(2).add(this._yMin);
208
- this._zMid.copy(this._depth).divInt(2).add(this._zMin);
209
-
210
- this.syncRawBounds();
211
- this.event.emit(DecimalBodyEvent.AABB_CHANGED, this);
212
- return this;
213
- }
214
-
215
- public setAABB(aabb: IDecimalAABB) {
216
- if (!aabb) return this;
217
-
218
- this._xMin.copy(aabb.min.x);
219
- this._yMin.copy(aabb.min.y);
220
- this._zMin.copy(aabb.min.z ?? Decimal.ZERO);
221
-
222
- this._xMax.copy(aabb.max.x);
223
- this._yMax.copy(aabb.max.y);
224
- this._zMax.copy(aabb.max.z ?? Decimal.ZERO);
225
-
226
- this._width.copy(this._xMax).sub(this._xMin);
227
- this._height.copy(this._yMax).sub(this._yMin);
228
- this._depth.copy(this._zMax).sub(this._zMin);
229
-
230
- this._xMid.copy(this._width).divInt(2).add(this._xMin);
231
- this._yMid.copy(this._height).divInt(2).add(this._yMin);
232
- this._zMid.copy(this._depth).divInt(2).add(this._zMin);
233
-
234
- this.syncRawBounds();
235
- this.event.emit(DecimalBodyEvent.AABB_CHANGED, this);
236
- return this;
237
- }
238
-
239
- public intersect(other: DecimalBody): boolean {
240
- if (!(this.group & other.mask && other.group & this.mask)) return false;
241
-
242
- return !(this._xMinRaw > other._xMaxRaw ||
243
- this._xMaxRaw < other._xMinRaw ||
244
- this._yMinRaw > other._yMaxRaw ||
245
- this._yMaxRaw < other._yMinRaw ||
246
- this._zMinRaw > other._zMaxRaw ||
247
- this._zMaxRaw < other._zMinRaw);
248
- }
249
- }
@@ -1,9 +0,0 @@
1
- {
2
- "ver": "4.0.24",
3
- "importer": "typescript",
4
- "imported": true,
5
- "uuid": "400b7e6e-2a3e-41aa-8c2b-520f83528983",
6
- "files": [],
7
- "subMetas": {},
8
- "userData": {}
9
- }
@@ -1,297 +0,0 @@
1
- /**********************************************************************
2
- * DecimalQuadTree.ts —— 定点数版四叉树
3
- * -------------------------------------------------
4
- * ① 完全沿用原 QuadTree.ts 的结构与接口,方便直接替换。
5
- * ② 所有空间数值均改为 @cjhd/cj-decimal 的 Decimal。
6
- * ③ 位操作 / Map Key 仍用 number,不影响 bit flag 与 ID。
7
- * ④ 对重要逻辑步骤增加了更详细的中文注释,便于维护。
8
- *********************************************************************/
9
-
10
- import { Decimal } from '@cjhd/cj-decimal';
11
- import { DecimalBody, DecimalBodyEvent, IDecimalAABB, IDecimalBox } from './DecimalBody';
12
-
13
- /* ───── 每个象限的位标识(保持 number,便于位运算)───── */
14
- const DecimalQuadFlag = [1 << 0, 1 << 1, 1 << 2, 1 << 3];
15
-
16
- /* ════════════════════ 1. QuadBody ════════════════════ */
17
- /**
18
- * 区域包围盒,继承自 DecimalBody,仅添加辅助工厂与拆分逻辑
19
- */
20
- class DecimalQuadBody<T = unknown> extends DecimalBody<T> {
21
- /* ----------- 辅助工厂保持原命名 ----------- */
22
- // static createWithRect<T = unknown>(rect: Rect) {
23
- // const b = new DecimalQuadBody<T>(); b.setRect(rect); return b;
24
- // }
25
- static createWithAABB<T = unknown>(aabb: IDecimalAABB) {
26
- const b = new DecimalQuadBody<T>(); b.setAABB(aabb); return b;
27
- }
28
- static createWithBox<T = unknown>(box: IDecimalBox) {
29
- const b = new DecimalQuadBody<T>(); b.setBox(box); return b;
30
- }
31
-
32
- protected constructor() { super(); }
33
-
34
- /** 判断 `this` 是否完整包裹 `other`(含边界) */
35
- contain(other: DecimalBody) {
36
- return this.xMinRaw <= other.xMinRaw &&
37
- this.xMaxRaw >= other.xMaxRaw &&
38
- this.yMinRaw <= other.yMinRaw &&
39
- this.yMaxRaw >= other.yMaxRaw;
40
- }
41
-
42
- /** 将当前矩形分割为四个象限 AABB,返回对应 QuadBody */
43
- split() {
44
- const { xMin, yMin, xMax, yMax, xMid, yMid } = this;
45
- return [
46
- DecimalQuadBody.createWithAABB({ min: { x: xMid, y: yMid }, max: { x: xMax, y: yMax } }),
47
- DecimalQuadBody.createWithAABB({ min: { x: xMin, y: yMid }, max: { x: xMid, y: yMax } }),
48
- DecimalQuadBody.createWithAABB({ min: { x: xMin, y: yMin }, max: { x: xMid, y: yMid } }),
49
- DecimalQuadBody.createWithAABB({ min: { x: xMid, y: yMin }, max: { x: xMax, y: yMid } }),
50
- ];
51
- }
52
- }
53
-
54
- /* ════════════════════ 2. 链表节点 / 列表 ════════════════════ */
55
- class DecimalQuadNode<T> {
56
- value: DecimalBody<T> = null!; // 真正存储的 Body
57
- last: DecimalQuadNode<T> = null!; // 前驱
58
- next: DecimalQuadNode<T> = null!; // 后继
59
- constructor(v: DecimalBody<T>) { this.value = v; }
60
-
61
- /** 把自己接在 next 前面 */
62
- concat(n: DecimalQuadNode<T> | null) { this.next = n; if (n) n.last = this; }
63
- /** 从链表摘除自己 */
64
- disConcat() { this.last.concat(this.next); this.last = this.next = null!; }
65
- }
66
- class DecimalQuadList<T> {
67
- /** 哨兵节点,简化插入/清空逻辑 */
68
- readonly head = new DecimalQuadNode<T>(null!);
69
-
70
- push(node: DecimalQuadNode<T>) {
71
- node.concat(this.head.next);
72
- this.head.concat(node);
73
- }
74
- clear() { this.head.next = null!; }
75
- }
76
-
77
- /* ════════════════════ 3. QuadSubTree 递归子树 ════════════════════ */
78
- class DecimalQuadSubTree<T> {
79
- /* === 基本属性 === */
80
- private readonly quadList = new DecimalQuadList<T>(); // 存放落在当前节点、且不能继续下分的元素
81
- private readonly children: DecimalQuadSubTree<T>[] = []; // 4 子象限
82
- constructor(
83
- private quadBody: DecimalQuadBody, // 当前节点的空间范围
84
- private readonly maxLevel: number,
85
- private readonly level = 0,
86
- private readonly parent: DecimalQuadSubTree<T> | null = null,
87
- ) { }
88
-
89
- /* ---------- 内部:延迟分裂 ---------- */
90
- private split() {
91
- if (this.children.length) return; // 已分裂
92
- const lvl = this.level + 1;
93
- this.quadBody.split().forEach(qb =>
94
- this.children.push(new DecimalQuadSubTree<T>(qb, this.maxLevel, lvl, this)));
95
- }
96
-
97
- /* ---------- 内部:计算 body 与哪些象限相交 ---------- */
98
- private getDecimalQuadFlags(body: DecimalBody): number {
99
- let f = 0;
100
- const startLeft = body.xMinRaw < this.quadBody.xMidRaw;
101
- const startTop = body.yMaxRaw > this.quadBody.yMidRaw;
102
- const endRight = body.xMaxRaw > this.quadBody.xMidRaw;
103
- const endBottom = body.yMinRaw < this.quadBody.yMidRaw;
104
-
105
- if (startTop && endRight) f |= DecimalQuadFlag[0]; // 第1象限
106
- if (startTop && startLeft) f |= DecimalQuadFlag[1]; // 第2象限
107
- if (startLeft && endBottom) f |= DecimalQuadFlag[2]; // 第3象限
108
- if (endRight && endBottom) f |= DecimalQuadFlag[3]; // 第4象限
109
- return f;
110
- }
111
-
112
- /* ---------- 插入 ---------- */
113
- insert(node: DecimalQuadNode<T>): DecimalQuadSubTree<T> {
114
- // 最深层:直接挂本节点
115
- if (this.level === this.maxLevel) { this.quadList.push(node); return this; }
116
-
117
- const idx = DecimalQuadFlag.indexOf(this.getDecimalQuadFlags(node.value));
118
- // idx == -1 → 跨象限,挂在当前节点
119
- if (idx === -1) { this.quadList.push(node); return this; }
120
-
121
- // 单象限 → 递归插入
122
- if (!this.children.length) this.split();
123
- return this.children[idx].insert(node);
124
- }
125
-
126
- /** 从叶往根插:若 body 已不在旧节点范围,就向上找能包裹它的新父节点 */
127
- insertReverse(node: DecimalQuadNode<T>): DecimalQuadSubTree<T> {
128
- if (this.quadBody.contain(node.value)) return this.insert(node);
129
- return this.parent ? this.parent.insertReverse(node) : this.insert(node);
130
- }
131
-
132
- /* ---------- 检索 ---------- */
133
- /** 仅查本节点链表 */
134
- private retrieveSelf(out: DecimalBody<T>[], body: DecimalBody, multi = false) {
135
- for (let n = this.quadList.head.next; n; n = n.next) {
136
- const other = n.value;
137
- if (body === other || !body.intersect(other)) continue;
138
- out.push(other);
139
- if (!multi) break; // 只找一个
140
- }
141
- }
142
-
143
- /** 向下递归检索 */
144
- retrieve(out: DecimalBody<T>[], body: DecimalBody, multi = false): DecimalBody<T>[] {
145
- this.retrieveSelf(out, body, multi);
146
- if (!multi && out.length) return out;
147
-
148
- if (this.level === this.maxLevel || !this.children.length) return out;
149
-
150
- const flags = this.getDecimalQuadFlags(body);
151
- for (let i = 0; i < 4; i++) {
152
- if (!(flags & DecimalQuadFlag[i])) continue;
153
- this.children[i].retrieve(out, body, multi);
154
- if (!multi && out.length) break;
155
- }
156
- return out;
157
- }
158
-
159
- /** 先向上查父节点,再向下递归(更少无关遍历) */
160
- retrieveReverse(out: DecimalBody<T>[], body: DecimalBody<T>, multi = false) {
161
- if (this.parent) {
162
- this.parent.retrieveReverse(out, body, multi);
163
- if (!multi && out.length) return out;
164
- }
165
- return this.retrieve(out, body, multi);
166
- }
167
-
168
- /* ---------- 清空 ---------- */
169
- clear() { this.children.forEach(c => c.clear()); this.quadList.clear(); this.children.length = 0; }
170
- }
171
-
172
- /* ════════════════════ 4. 顶层 DecimalQuadTree ════════════════════ */
173
- export class DecimalQuadTree<T = unknown> {
174
- /* 主要结构映射 */
175
- private readonly quadTree: DecimalQuadSubTree<T>;
176
- private readonly nodeCache: DecimalQuadNode<T>[] = [];
177
- private readonly id2Node = new Map<number, DecimalQuadNode<T>>();
178
- private readonly id2Tree = new Map<number, DecimalQuadSubTree<T>>();
179
-
180
- /**
181
- * 创建
182
- * @param x 最小x
183
- * @param y 最小y
184
- * @param width 宽
185
- * @param height 高
186
- * @param maxLevels 最大深度(默认4)
187
- */
188
- constructor(x: Decimal, y: Decimal, width: Decimal, height: Decimal, maxLevels = 4) {
189
- const bounds = DecimalQuadBody.createWithBox({ x, y, width, height });
190
- this.quadTree = new DecimalQuadSubTree<T>(bounds, maxLevels);
191
- }
192
-
193
- /* ---------- 内部节点复用 ---------- */
194
- private newNode(body: DecimalBody<T>) {
195
- const n = this.nodeCache.pop() ?? new DecimalQuadNode<T>(body);
196
- n.value = body; return n;
197
- }
198
- private detach(id: number) {
199
- const n = this.id2Node.get(id); if (n) n.disConcat(); return n;
200
- }
201
- private recycle(n: DecimalQuadNode<T>) {
202
- DecimalBody.off(n.value, DecimalBodyEvent.AABB_CHANGED, this.refresh, this);
203
- n.value = null!; this.nodeCache.push(n);
204
- }
205
-
206
- /* ---------- 插入 & 移除 & 刷新 ---------- */
207
- insert(body: DecimalBody<T>) {
208
- const node = this.newNode(body);
209
- const tree = this.quadTree.insert(node);
210
- this.id2Node.set(body.id, node);
211
- this.id2Tree.set(body.id, tree);
212
- DecimalBody.on(body, DecimalBodyEvent.AABB_CHANGED, this.refresh, this);
213
- return body.id;
214
- }
215
- /**
216
- * 移除一个碰撞体(函数签名)
217
- */
218
- remove(id: number): boolean;
219
- /**
220
- * 移除一个碰撞体
221
- */
222
- remove(body: DecimalBody<T>): boolean;
223
- remove(target: number | DecimalBody<T>) {
224
- const id = typeof target === 'number' ? target : target.id;
225
- const n = this.detach(id); if (!n) return false;
226
- this.id2Node.delete(id); this.id2Tree.delete(id); this.recycle(n); return true;
227
- }
228
- /** AABB 变化时重新放置节点 */
229
- private refresh(body: DecimalBody<T>) {
230
- const n = this.detach(body.id); if (!n) return false;
231
- const newTree = this.id2Tree.get(body.id)!.insertReverse(n);
232
- this.id2Tree.set(body.id, newTree); return true;
233
- }
234
-
235
- /**
236
- * 检索与某个碰撞体相交的碰撞体数组
237
- * @param id 碰撞体id
238
- */
239
- public retrieve(id: number, out?: DecimalBody<T>[]): DecimalBody<T>[];
240
- /**
241
- * 检索与某个碰撞体相交的碰撞体数组
242
- * @param body 碰撞体
243
- */
244
- public retrieve(body: DecimalBody, out?: DecimalBody<T>[]): DecimalBody<T>[];
245
- public retrieve(query: number | DecimalBody, out: DecimalBody<T>[] = []) {
246
- if (typeof query === 'number') {
247
- const n = this.id2Node.get(query); if (!n) return out;
248
- return this.id2Tree.get(query)!.retrieveReverse(out, n.value, true);
249
- } else {
250
- return this.quadTree.retrieve(out, query, true);
251
- }
252
- }
253
-
254
- /**
255
- * 检查某个碰撞体是否与其它至少一个碰撞体相交
256
- * @param id 碰撞体id
257
- */
258
- public check(id: number): boolean;
259
- /**
260
- * 检查某个碰撞体是否与其它至少一个碰撞体相交
261
- * @param body 碰撞体
262
- */
263
- public check(body: DecimalBody): boolean;
264
- public check(query: number | DecimalBody) {
265
- if (typeof query === 'number') {
266
- const n = this.id2Node.get(query);
267
- if (!n) return false;
268
- return this.id2Tree.get(query)!.retrieveReverse([], n.value, false).length > 0;
269
- } else {
270
- return this.quadTree.retrieve([], query, false).length > 0;
271
- }
272
- }
273
-
274
- /* ---------- 全局碰撞触发 ---------- */
275
- trigger(cb: (a: DecimalBody<T>, b: DecimalBody<T>) => any) {
276
- const tmp: DecimalBody<T>[] = []; // 复用数组,减少 GC
277
- const seen = new Map<number, number>(); // 记录已触发对,避免重复
278
- this.id2Node.forEach(n => {
279
- tmp.length = 0;
280
- this.id2Tree.get(n.value.id)!.retrieveReverse(tmp, n.value, true);
281
- tmp.forEach(o => {
282
- const a = n.value, b = o;
283
- const small = a.id < b.id ? a.id : b.id, big = a.id < b.id ? b.id : a.id;
284
- if (seen.get(small) === big) return; // 已触发
285
- seen.set(small, big);
286
- cb(a, b);
287
- });
288
- });
289
- }
290
-
291
- /* ---------- 清空 ---------- */
292
- clear() {
293
- this.id2Node.forEach(n => DecimalBody.off(n.value, DecimalBodyEvent.AABB_CHANGED, this.refresh, this));
294
- this.quadTree.clear();
295
- this.id2Node.clear(); this.id2Tree.clear(); this.nodeCache.length = 0;
296
- }
297
- }
@@ -1,9 +0,0 @@
1
- {
2
- "ver": "4.0.24",
3
- "importer": "typescript",
4
- "imported": true,
5
- "uuid": "abba3c07-5cee-4d44-b1f5-e633926d2bad",
6
- "files": [],
7
- "subMetas": {},
8
- "userData": {}
9
- }
package/assets.meta DELETED
@@ -1,9 +0,0 @@
1
- {
2
- "ver": "1.2.0",
3
- "importer": "directory",
4
- "imported": true,
5
- "uuid": "96f83186-0b02-4d07-bc68-df1a2404fea3",
6
- "files": [],
7
- "subMetas": {},
8
- "userData": {}
9
- }
package/index.ts DELETED
@@ -1,3 +0,0 @@
1
- // 负责导出assets下的模块,如: export { default } from './assets/xxx.ts'
2
- export {DecimalBody, type IDecimalAABB, type IDecimalBox} from './assets/DecimalBody';
3
- export {DecimalQuadTree} from './assets/DecimalQuadTree';
package/index.ts.meta DELETED
@@ -1,9 +0,0 @@
1
- {
2
- "ver": "4.0.24",
3
- "importer": "typescript",
4
- "imported": true,
5
- "uuid": "142d51e6-0ee0-4bd9-b684-f75f22cce4dd",
6
- "files": [],
7
- "subMetas": {},
8
- "userData": {}
9
- }
@@ -1,11 +0,0 @@
1
- {
2
- "ver": "2.0.1",
3
- "importer": "json",
4
- "imported": true,
5
- "uuid": "b959cec9-adef-4c64-86de-c0ef8918c7a3",
6
- "files": [
7
- ".json"
8
- ],
9
- "subMetas": {},
10
- "userData": {}
11
- }
package/package.json.meta DELETED
@@ -1,11 +0,0 @@
1
- {
2
- "ver": "2.0.1",
3
- "importer": "json",
4
- "imported": true,
5
- "uuid": "d2f7b651-5fc1-42a9-87c9-e0ed50c7993d",
6
- "files": [
7
- ".json"
8
- ],
9
- "subMetas": {},
10
- "userData": {}
11
- }
File without changes