@woosh/meep-engine 2.71.0 → 2.73.0
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.
- package/build/bundle-worker-terrain.js +1 -1
- package/build/meep.cjs +36 -187
- package/build/meep.min.js +1 -1
- package/build/meep.module.js +36 -187
- package/package.json +3 -2
- package/src/core/collection/array/array_get_index_in_range.js +1 -1
- package/src/core/collection/array/array_swap.js +2 -2
- package/src/core/geom/3d/v3_compute_triangle_normal.js +7 -1
- package/src/core/geom/vec3/v3_normalize_array.js +27 -0
- package/src/engine/ecs/terrain/BufferedGeometryArraysBuilder.js +2 -2
- package/src/engine/graphics/ecs/camera/FrustumProjector.js +31 -182
- package/src/engine/graphics/geometry/buffered/ComputeNormals.js +11 -26
- package/src/engine/graphics/texture/virtual/v2/NOTES.md +27 -0
- package/src/engine/graphics/texture/virtual/v2/ShaderUsage.js +51 -26
- package/src/engine/graphics/texture/virtual/v2/SparseTexture.js +182 -0
- package/src/engine/graphics/texture/virtual/v2/TextureTile.js +31 -0
- package/src/engine/graphics/texture/virtual/v2/TileLoader.js +215 -0
- package/src/engine/graphics/texture/virtual/v2/UsageDebugView.js +64 -0
- package/src/engine/graphics/texture/virtual/v2/UsageMetadata.js +153 -0
- package/src/engine/graphics/texture/virtual/v2/UsagePyramidDebugView.js +252 -0
- package/src/engine/graphics/texture/virtual/v2/VirtualTextureManager.js +257 -0
- package/src/engine/graphics/texture/virtual/v2/compose_finger_print.js +13 -0
- package/src/engine/graphics/texture/virtual/v2/finger_print_to_tile_index.js +37 -0
- package/src/engine/graphics/texture/virtual/v2/prototype.js +122 -31
- package/src/engine/graphics/texture/virtual/v2/tile_index_to_finger_print.js +31 -0
- package/src/core/geom/2d/quad-tree/PointQuadTree.js +0 -478
- package/src/core/math/random/makeRangedRandom.js +0 -19
- package/src/engine/ecs/terrain/TerrainGeometryBuilder.js +0 -152
- package/src/engine/ecs/terrain/ecs/PromiseSamplerHeight.js +0 -66
- package/src/engine/ecs/terrain/ecs/TerrainClassifier.js +0 -125
- package/src/engine/graphics/clouds/MaterialTransformer.js +0 -0
- package/src/engine/graphics/clouds/TerrainCloudsPlugin.js +0 -0
- package/src/engine/graphics/clouds/cs_build_fragment_shader.js +0 -0
- package/src/engine/graphics/clouds/cs_build_vertex_shader.js +0 -0
- package/src/engine/graphics/ecs/camera/TiltCameraController.js +0 -69
- package/src/engine/graphics/ecs/camera/is_valid_distance_value.js +0 -11
- package/src/engine/graphics/texture/sampler/SampleTraverser.js +0 -165
|
@@ -1,56 +1,125 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
1
|
+
import { GUI } from "dat.gui";
|
|
2
|
+
import {
|
|
3
|
+
AmbientLight,
|
|
4
|
+
Clock,
|
|
5
|
+
DirectionalLight,
|
|
6
|
+
Mesh,
|
|
7
|
+
MeshStandardMaterial,
|
|
8
|
+
PerspectiveCamera,
|
|
9
|
+
Scene,
|
|
10
|
+
TextureLoader,
|
|
11
|
+
TorusGeometry,
|
|
12
|
+
Vector2,
|
|
13
|
+
WebGLRenderer
|
|
14
|
+
} from "three";
|
|
3
15
|
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
|
|
16
|
+
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
|
|
17
|
+
import EmptyView from "../../../../../view/elements/EmptyView.js";
|
|
18
|
+
import { AssetManager } from "../../../../asset/AssetManager.js";
|
|
19
|
+
import { GameAssetType } from "../../../../asset/GameAssetType.js";
|
|
20
|
+
import { ImageRGBADataLoader } from "../../../../asset/loaders/image/ImageRGBADataLoader.js";
|
|
21
|
+
import { SparseTexture } from "./SparseTexture.js";
|
|
22
|
+
import { UsageDebugView } from "./UsageDebugView.js";
|
|
23
|
+
import { VirtualTextureManager } from "./VirtualTextureManager.js";
|
|
24
|
+
|
|
25
|
+
let camera,
|
|
26
|
+
/**
|
|
27
|
+
* @type {WebGLRenderer}
|
|
28
|
+
*/
|
|
29
|
+
renderer,
|
|
30
|
+
clock,
|
|
31
|
+
/**
|
|
32
|
+
* @type {Scene}
|
|
33
|
+
*/
|
|
34
|
+
scene;
|
|
35
|
+
|
|
36
|
+
let mesh;
|
|
37
|
+
|
|
38
|
+
const am = new AssetManager();
|
|
39
|
+
am.registerLoader(GameAssetType.Image, new ImageRGBADataLoader());
|
|
40
|
+
am.startup();
|
|
41
|
+
|
|
42
|
+
const virtualTextureManager = new VirtualTextureManager();
|
|
43
|
+
virtualTextureManager.setTextureParameters(
|
|
44
|
+
// 2048,
|
|
45
|
+
16384,
|
|
46
|
+
128,
|
|
47
|
+
3
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
const sparseTexture = new SparseTexture();
|
|
51
|
+
sparseTexture.page_texture_size = [1024, 1024];
|
|
52
|
+
sparseTexture.tile_resolution = virtualTextureManager.tile_resolution;
|
|
53
|
+
sparseTexture.asset_manager = am;
|
|
54
|
+
|
|
55
|
+
console.log(sparseTexture);
|
|
56
|
+
|
|
57
|
+
// const TEXTURE_URL = "data/textures/utility/uv_map_reference.png";
|
|
58
|
+
// const TEXTURE_URL = "data/textures/utility/4096x4096TexelDensityTexture1.png";
|
|
59
|
+
// const TEXTURE_URL = "data/textures/utility/Lenna.png";
|
|
60
|
+
// const TEXTURE_URL = "data/textures/utility/TESTIMAGES/SAMPLING/8BIT/RGB/2448x2448/SRC/img_2448x2448_3x8bit_SRC_RGB_cards_a.png";
|
|
61
|
+
const TEXTURE_URL = "data/models/LowPolyTownshipSet/Town_Hall//diffuse_2048.png";
|
|
62
|
+
|
|
63
|
+
const container_view = new EmptyView();
|
|
64
|
+
//
|
|
65
|
+
const usageDebugView = new UsageDebugView();
|
|
66
|
+
usageDebugView.mip_levels = virtualTextureManager.max_mip_level;
|
|
67
|
+
container_view.addChild(usageDebugView);
|
|
68
|
+
|
|
4
69
|
|
|
5
|
-
|
|
70
|
+
// const usagePyramidDebugView = new UsagePyramidDebugView();
|
|
71
|
+
// usagePyramidDebugView.setImageURL(TEXTURE_URL);
|
|
72
|
+
//
|
|
73
|
+
// container_view.addChild(usagePyramidDebugView);
|
|
6
74
|
|
|
7
|
-
|
|
75
|
+
const options = {
|
|
76
|
+
spin: true
|
|
77
|
+
};
|
|
8
78
|
|
|
9
79
|
init();
|
|
10
80
|
animate();
|
|
11
81
|
|
|
82
|
+
|
|
83
|
+
function makeTorus() {
|
|
84
|
+
const size = 0.65;
|
|
85
|
+
mesh = new Mesh(new TorusGeometry(size, 0.3, 30, 30), new MeshStandardMaterial({
|
|
86
|
+
roughness: 0.4,
|
|
87
|
+
map: new TextureLoader().load(TEXTURE_URL)
|
|
88
|
+
}));
|
|
89
|
+
mesh.rotation.x = 0.3;
|
|
90
|
+
}
|
|
91
|
+
|
|
12
92
|
function init() {
|
|
13
93
|
|
|
14
94
|
const container = document.body;
|
|
15
95
|
|
|
16
|
-
|
|
17
|
-
camera.position.z = 4;
|
|
96
|
+
container.style.margin = "0";
|
|
18
97
|
|
|
19
|
-
|
|
98
|
+
container.appendChild(container_view.el);
|
|
99
|
+
container_view.link();
|
|
20
100
|
|
|
21
|
-
|
|
101
|
+
camera = new PerspectiveCamera(35, window.innerWidth / window.innerHeight, 0.1, 300);
|
|
102
|
+
camera.position.z = 4;
|
|
22
103
|
|
|
23
|
-
|
|
104
|
+
scene = new Scene();
|
|
24
105
|
|
|
25
|
-
|
|
26
|
-
"u_mt_params": { value: new THREE.Vector2(0.1, 6) },
|
|
27
|
-
/**
|
|
28
|
-
* Format: VT_WIDTH, VT_HEIGHT, VT_TILE_SIZE, VT_ID
|
|
29
|
-
* - VT_WIDTH : width of the virtual texture
|
|
30
|
-
* - VT_HEIGHT : height of the virtual texture
|
|
31
|
-
* - VT_TILE_SIZE : resolution of a single tile
|
|
32
|
-
* - VT_ID : multiple different virtual textures may be used, this identifies current texture
|
|
33
|
-
*/
|
|
34
|
-
"u_mt_tex": { value: new THREE.Vector4(16384, 16384, 256, 1) }
|
|
35
|
-
};
|
|
106
|
+
clock = new Clock();
|
|
36
107
|
|
|
37
|
-
const size = 0.65;
|
|
38
108
|
|
|
39
|
-
|
|
109
|
+
new GLTFLoader().load("data/models/LowPolyTownshipSet/Town_Hall/model.gltf", (gltf) => {
|
|
40
110
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
fragmentShader: fragment()
|
|
111
|
+
mesh = gltf.scene;
|
|
112
|
+
scene.add(mesh);
|
|
44
113
|
|
|
45
114
|
});
|
|
46
115
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
116
|
+
scene.add(new DirectionalLight(0xFFFFFF, 0.7));
|
|
117
|
+
scene.add(new AmbientLight(0xFFFFFF, 0.2));
|
|
118
|
+
|
|
50
119
|
|
|
51
120
|
//
|
|
52
121
|
|
|
53
|
-
renderer = new
|
|
122
|
+
renderer = new WebGLRenderer({ antialias: true });
|
|
54
123
|
renderer.setPixelRatio(window.devicePixelRatio);
|
|
55
124
|
container.appendChild(renderer.domElement);
|
|
56
125
|
renderer.autoClear = false;
|
|
@@ -67,6 +136,14 @@ function init() {
|
|
|
67
136
|
|
|
68
137
|
new OrbitControls(camera, renderer.domElement);
|
|
69
138
|
|
|
139
|
+
|
|
140
|
+
const gui = new GUI();
|
|
141
|
+
|
|
142
|
+
Object.keys(options).forEach(key => {
|
|
143
|
+
gui.add(options, key);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
|
|
70
147
|
}
|
|
71
148
|
|
|
72
149
|
function onWindowResize() {
|
|
@@ -92,10 +169,24 @@ function render() {
|
|
|
92
169
|
|
|
93
170
|
const delta = 5 * clock.getDelta();
|
|
94
171
|
|
|
95
|
-
|
|
96
|
-
|
|
172
|
+
if (options.spin && mesh !== undefined) {
|
|
173
|
+
mesh.rotation.y += 0.0125 * delta;
|
|
174
|
+
mesh.rotation.x += 0.05 * delta;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const view_port_size = renderer.getSize(new Vector2());
|
|
178
|
+
view_port_size.multiplyScalar(renderer.getPixelRatio());
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
virtualTextureManager.setViewportResolution(view_port_size.x, view_port_size.y);
|
|
182
|
+
virtualTextureManager.updateUsage(renderer, scene, camera);
|
|
183
|
+
sparseTexture.update_usage(virtualTextureManager.usage_metadata);
|
|
97
184
|
|
|
98
185
|
renderer.clear();
|
|
99
186
|
renderer.render(scene, camera)
|
|
100
187
|
|
|
188
|
+
usageDebugView.usage = virtualTextureManager.usage_metadata;
|
|
189
|
+
|
|
190
|
+
// usagePyramidDebugView.setTextureParameters(virtualTextureManager.texture_resolution, virtualTextureManager.tile_resolution);
|
|
191
|
+
// usagePyramidDebugView.usage = virtualTextureManager.usage_metadata;
|
|
101
192
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { split_by_2 } from "../../../../../core/geom/3d/morton/split_by_2.js";
|
|
2
|
+
import { compose_finger_print } from "./compose_finger_print.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
*
|
|
6
|
+
* @param {number} index
|
|
7
|
+
* @returns {number}
|
|
8
|
+
*/
|
|
9
|
+
export function tile_index_to_finger_print(index) {
|
|
10
|
+
for (let mip = 0; mip < 99; mip++) {
|
|
11
|
+
const resolution = 1 << mip;
|
|
12
|
+
|
|
13
|
+
const mip_mask = resolution - 1;
|
|
14
|
+
|
|
15
|
+
const index_offset = split_by_2(mip_mask);
|
|
16
|
+
|
|
17
|
+
if (index > index_offset) {
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const local_index = index - index_offset;
|
|
22
|
+
|
|
23
|
+
const y = (local_index / resolution) | 0;
|
|
24
|
+
const x = local_index % resolution;
|
|
25
|
+
|
|
26
|
+
return compose_finger_print(mip, x, y,);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// not found
|
|
30
|
+
return -1;
|
|
31
|
+
}
|
|
@@ -1,478 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Created by Alex on 31/10/2014.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
import { assert } from "../../../assert.js";
|
|
7
|
-
|
|
8
|
-
const maxElements = 12;
|
|
9
|
-
const minElements = 1;
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
*
|
|
13
|
-
* @param {*} obj
|
|
14
|
-
* @param {number} x
|
|
15
|
-
* @param {number} y
|
|
16
|
-
* @constructor
|
|
17
|
-
*/
|
|
18
|
-
const Element = function (obj, x, y) {
|
|
19
|
-
/**
|
|
20
|
-
*
|
|
21
|
-
* @type {number}
|
|
22
|
-
*/
|
|
23
|
-
this.x = x;
|
|
24
|
-
/**
|
|
25
|
-
*
|
|
26
|
-
* @type {number}
|
|
27
|
-
*/
|
|
28
|
-
this.y = y;
|
|
29
|
-
|
|
30
|
-
this.value = obj;
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
*
|
|
34
|
-
* @type {PointQuadTree|null}
|
|
35
|
-
*/
|
|
36
|
-
this.parentNode = null;
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
*
|
|
41
|
-
* @param {number} x
|
|
42
|
-
* @param {number} y
|
|
43
|
-
*/
|
|
44
|
-
Element.prototype.move = function (x, y) {
|
|
45
|
-
this.x = x;
|
|
46
|
-
this.y = y;
|
|
47
|
-
//check if new position is outside of the parent node
|
|
48
|
-
const parentNode = this.parentNode;
|
|
49
|
-
let node = parentNode;
|
|
50
|
-
while (x <= node.x0 || x > node.x1 || y <= node.y0 || y > node.y1) {
|
|
51
|
-
if (node.parentNode === null) {
|
|
52
|
-
//root
|
|
53
|
-
node.resizeToFit(x, y);
|
|
54
|
-
break;
|
|
55
|
-
}
|
|
56
|
-
//outside of the node
|
|
57
|
-
node = node.parentNode;
|
|
58
|
-
}
|
|
59
|
-
//found containing node
|
|
60
|
-
if (node === parentNode) {
|
|
61
|
-
//still inside the parent node
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
parentNode.removeElement(this);
|
|
65
|
-
node.insertElement(this);
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
Element.prototype.remove = function () {
|
|
69
|
-
assert.notEqual(this.parentNode, null, 'parentNode is null');
|
|
70
|
-
|
|
71
|
-
this.parentNode.removeElement(this);
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Prefer to use {@link QuadTreeNode} instead as that is more robust and has better performance
|
|
76
|
-
*/
|
|
77
|
-
class PointQuadTree {
|
|
78
|
-
/**
|
|
79
|
-
* @param {number} [x0]
|
|
80
|
-
* @param {number} [y0]
|
|
81
|
-
* @param {number} [x1]
|
|
82
|
-
* @param {number} [y1]
|
|
83
|
-
* @constructor
|
|
84
|
-
*/
|
|
85
|
-
constructor(x0 = 0, y0 = 0, x1 = 0, y1 = 0) {
|
|
86
|
-
//
|
|
87
|
-
this.x0 = x0;
|
|
88
|
-
this.y0 = y0;
|
|
89
|
-
this.x1 = x1;
|
|
90
|
-
this.y1 = y1;
|
|
91
|
-
|
|
92
|
-
this.parentNode = null;
|
|
93
|
-
|
|
94
|
-
this.setHalfSize(this.x0, this.y0, this.x1, this.y1);
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
this.elements = [];
|
|
98
|
-
this.numElements = 0;
|
|
99
|
-
|
|
100
|
-
this.tl = void 0;
|
|
101
|
-
this.tr = void 0;
|
|
102
|
-
this.bl = void 0;
|
|
103
|
-
this.br = void 0;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
reduce() {
|
|
107
|
-
if (this.isLeaf()) {
|
|
108
|
-
//leaf
|
|
109
|
-
this.bubbleElementsUp();
|
|
110
|
-
} else {
|
|
111
|
-
const tl = this.tl;
|
|
112
|
-
const tr = this.tr;
|
|
113
|
-
const bl = this.bl;
|
|
114
|
-
const br = this.br;
|
|
115
|
-
tl.reduce();
|
|
116
|
-
tr.reduce();
|
|
117
|
-
bl.reduce();
|
|
118
|
-
br.reduce();
|
|
119
|
-
//
|
|
120
|
-
if (tl.isLeaf() && tr.isLeaf() && bl.isLeaf() && br.isLeaf()
|
|
121
|
-
&& tl.numElements === 0 && tr.numElements === 0 && bl.numElements === 0 && br.numElements === 0) {
|
|
122
|
-
this.tl = void 0;
|
|
123
|
-
this.tr = void 0;
|
|
124
|
-
this.bl = void 0;
|
|
125
|
-
this.br = void 0;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
insertElement(element) {
|
|
131
|
-
if (this.numElements < maxElements) {
|
|
132
|
-
|
|
133
|
-
this.numElements++;
|
|
134
|
-
this.elements.push(element);
|
|
135
|
-
element.parentNode = this;
|
|
136
|
-
|
|
137
|
-
} else {
|
|
138
|
-
|
|
139
|
-
//check for split
|
|
140
|
-
if (this.tl === void 0) {
|
|
141
|
-
this.split();
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
//find suitable child to take element
|
|
145
|
-
const x = element.x;
|
|
146
|
-
const y = element.y;
|
|
147
|
-
|
|
148
|
-
if (x < this.hx) {
|
|
149
|
-
if (y < this.hy) {
|
|
150
|
-
this.tl.insertElement(element);
|
|
151
|
-
} else {
|
|
152
|
-
this.bl.insertElement(element);
|
|
153
|
-
}
|
|
154
|
-
} else {
|
|
155
|
-
if (y < this.hy) {
|
|
156
|
-
this.tr.insertElement(element);
|
|
157
|
-
} else {
|
|
158
|
-
this.br.insertElement(element);
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
clear() {
|
|
165
|
-
this.elemenets = [];
|
|
166
|
-
|
|
167
|
-
this.tl = undefined;
|
|
168
|
-
this.tr = undefined;
|
|
169
|
-
this.bl = undefined;
|
|
170
|
-
this.br = undefined;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
*
|
|
175
|
-
* @param {T} p
|
|
176
|
-
* @param {number} x
|
|
177
|
-
* @param {number} y
|
|
178
|
-
* @return {Element}
|
|
179
|
-
*/
|
|
180
|
-
insert(p, x, y) {
|
|
181
|
-
assert.isNumber(x, 'x');
|
|
182
|
-
assert.isNumber(y, 'y');
|
|
183
|
-
|
|
184
|
-
const element = new Element(p, x, y);
|
|
185
|
-
|
|
186
|
-
this.resizeToFit(x, y); //adjust size if needed
|
|
187
|
-
|
|
188
|
-
this.insertElement(element);
|
|
189
|
-
|
|
190
|
-
return element;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
traversePreOrder(visitor) {
|
|
194
|
-
const keepGoing = visitor(this);
|
|
195
|
-
if (keepGoing !== false && this.tl !== void 0) {
|
|
196
|
-
this.tl.traversePreOrder(visitor);
|
|
197
|
-
this.tr.traversePreOrder(visitor);
|
|
198
|
-
this.bl.traversePreOrder(visitor);
|
|
199
|
-
this.br.traversePreOrder(visitor);
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
absorbElement(element) {
|
|
204
|
-
this.elements.push(element);
|
|
205
|
-
this.numElements++;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
resizeToFit(x, y) {
|
|
209
|
-
let _x0 = this.x0,
|
|
210
|
-
_y0 = this.y0,
|
|
211
|
-
_x1 = this.x1,
|
|
212
|
-
_y1 = this.y1;
|
|
213
|
-
|
|
214
|
-
if (x < _x0) {
|
|
215
|
-
_x0 = x;
|
|
216
|
-
} else if (x > _x1) {
|
|
217
|
-
_x1 = x;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
if (y < _y0) {
|
|
221
|
-
_y0 = y;
|
|
222
|
-
} else if (y > _y1) {
|
|
223
|
-
_y1 = y;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
if (this.x0 !== _x0 || this.y0 !== _y0 || this.x1 !== _x1 || this.y1 !== _y1) {
|
|
227
|
-
this.resize(_x0, _y0, _x1, _y1);
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
isLeaf() {
|
|
233
|
-
return this.tl === void 0;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
setHalfSize(x0, y0, x1, y1) {
|
|
237
|
-
this.hx = (x1 + x0) / 2;
|
|
238
|
-
this.hy = (y1 + y0) / 2;
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
traverseRect(_x0, _y0, _x1, _y1, visitor) {
|
|
242
|
-
//check elements
|
|
243
|
-
for (let i = 0; i < this.numElements; i++) {
|
|
244
|
-
const element = this.elements[i];
|
|
245
|
-
const x = element.x;
|
|
246
|
-
const y = element.y;
|
|
247
|
-
if (x > _x0 && x < _x1 && y > _y0 && y < _y1) {
|
|
248
|
-
visitor(element);
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
if (!this.isLeaf()) {
|
|
252
|
-
//if we have children - check them
|
|
253
|
-
if (_x0 <= this.hx) {
|
|
254
|
-
if (_y0 <= this.hy) {
|
|
255
|
-
this.tl.traverseRect(_x0, _y0, _x1, _y1, visitor);
|
|
256
|
-
}
|
|
257
|
-
if (_y1 >= this.hy) {
|
|
258
|
-
this.bl.traverseRect(_x0, _y0, _x1, _y1, visitor);
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
if (_x1 >= this.hx) {
|
|
262
|
-
if (_y0 <= this.hy) {
|
|
263
|
-
this.tr.traverseRect(_x0, _y0, _x1, _y1, visitor);
|
|
264
|
-
}
|
|
265
|
-
if (_y1 >= this.hy) {
|
|
266
|
-
this.br.traverseRect(_x0, _y0, _x1, _y1, visitor);
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
traverse(visitor, thisArg) {
|
|
273
|
-
this.elements.forEach(visitor, thisArg);
|
|
274
|
-
if (this.tl !== void 0) {
|
|
275
|
-
this.tl.traverse(visitor, thisArg);
|
|
276
|
-
this.tr.traverse(visitor, thisArg);
|
|
277
|
-
this.bl.traverse(visitor, thisArg);
|
|
278
|
-
this.br.traverse(visitor, thisArg);
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
traverseCircle(cX, cY, r, visitor) {
|
|
283
|
-
this.traverseCircleSqr(cX, cY, r, r * r, visitor);
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
split() {
|
|
287
|
-
//generate children
|
|
288
|
-
const hx = this.hx;
|
|
289
|
-
const hy = this.hy;
|
|
290
|
-
|
|
291
|
-
this.tl = new PointQuadTree(this.x0, this.y0, hx, hy);
|
|
292
|
-
this.tr = new PointQuadTree(hx, this.y0, this.x1, hy);
|
|
293
|
-
this.bl = new PointQuadTree(this.x0, hy, hx, this.y1);
|
|
294
|
-
this.br = new PointQuadTree(hx, hy, this.x1, this.y1);
|
|
295
|
-
|
|
296
|
-
//set parent node
|
|
297
|
-
this.tl.parentNode = this;
|
|
298
|
-
this.tr.parentNode = this;
|
|
299
|
-
this.bl.parentNode = this;
|
|
300
|
-
this.br.parentNode = this;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
merge() {
|
|
304
|
-
//check if split at all
|
|
305
|
-
if (this.isLeaf()) {
|
|
306
|
-
return; //not split
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
//merge children
|
|
311
|
-
this.tl.traverse(this.absorbElement, this);
|
|
312
|
-
this.tr.traverse(this.absorbElement, this);
|
|
313
|
-
this.bl.traverse(this.absorbElement, this);
|
|
314
|
-
this.br.traverse(this.absorbElement, this);
|
|
315
|
-
//
|
|
316
|
-
this.tl = void 0;
|
|
317
|
-
this.tr = void 0;
|
|
318
|
-
this.bl = void 0;
|
|
319
|
-
this.br = void 0;
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
validateNode() {
|
|
323
|
-
if (this.hx !== (this.x0 + this.x1) / 2) {
|
|
324
|
-
return false;
|
|
325
|
-
}
|
|
326
|
-
if (this.hy !== (this.y0 + this.y1) / 2) {
|
|
327
|
-
return false;
|
|
328
|
-
}
|
|
329
|
-
if (!this.isLeaf()) {
|
|
330
|
-
if (this.tl.parentNode !== this
|
|
331
|
-
|| this.tr.parentNode !== this
|
|
332
|
-
|| this.bl.parentNode !== this
|
|
333
|
-
|| this.br.parentNode !== this) {
|
|
334
|
-
return false;
|
|
335
|
-
}
|
|
336
|
-
if (this.tl.x0 !== this.x0 || this.tl.x1 !== this.hx || this.tl.y0 !== this.y0 || this.tl.y1 !== this.hy) {
|
|
337
|
-
return false;
|
|
338
|
-
}
|
|
339
|
-
if (this.tr.x0 !== this.hx || this.tr.x1 !== this.x1 || this.tr.y0 !== this.y0 || this.tr.y1 !== this.hy) {
|
|
340
|
-
return false;
|
|
341
|
-
}
|
|
342
|
-
if (this.bl.x0 !== this.x0 || this.bl.x1 !== this.hx || this.bl.y0 !== this.hy || this.bl.y1 !== this.y1) {
|
|
343
|
-
return false;
|
|
344
|
-
}
|
|
345
|
-
if (this.br.x0 !== this.hx || this.br.x1 !== this.x1 || this.br.y0 !== this.hy || this.br.y1 !== this.y1) {
|
|
346
|
-
return false;
|
|
347
|
-
}
|
|
348
|
-
} else if (this.elements !== void 0) {
|
|
349
|
-
|
|
350
|
-
//check containment of elements
|
|
351
|
-
for (let i = 0; i < this.elements.length; i++) {
|
|
352
|
-
const e = this.elements[i];
|
|
353
|
-
if (e.x < this.x0 || e.x > this.x1 || e.y < this.y0 || e.y > this.y1) {
|
|
354
|
-
return false;
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
}
|
|
359
|
-
return true;
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
traverseCircleSqr(cX, cY, r, r2, visitor) {
|
|
363
|
-
for (let i = 0; i < this.numElements; i++) {
|
|
364
|
-
const element = this.elements[i];
|
|
365
|
-
const x = element.x;
|
|
366
|
-
const y = element.y;
|
|
367
|
-
const dx = cX - x;
|
|
368
|
-
const dy = cY - y;
|
|
369
|
-
const d2 = dx * dx + dy * dy;
|
|
370
|
-
if (d2 < r2) {
|
|
371
|
-
visitor(element);
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
if (cX - r < this.hx) {
|
|
376
|
-
if (cY - r < this.hy) {
|
|
377
|
-
this.tl.traverseCircleSqr(cX, cY, r, r2, visitor);
|
|
378
|
-
}
|
|
379
|
-
if (cY + r >= this.hy) {
|
|
380
|
-
this.bl.traverseCircleSqr(cX, cY, r, r2, visitor);
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
if (cX + r >= this.hx) {
|
|
384
|
-
if (cY - r < this.hy) {
|
|
385
|
-
this.tr.traverseCircleSqr(cX, cY, r, r2, visitor);
|
|
386
|
-
}
|
|
387
|
-
if (cY + r >= this.hy) {
|
|
388
|
-
this.br.traverseCircleSqr(cX, cY, r, r2, visitor);
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
resize(_x0, _y0, _x1, _y1) {
|
|
394
|
-
const parentNode = this.parentNode;
|
|
395
|
-
if (parentNode !== null) {
|
|
396
|
-
const w = _x1 - _x0;
|
|
397
|
-
const h = _y1 - _y0;
|
|
398
|
-
if (this === parentNode.tl) {
|
|
399
|
-
parentNode.resize(_x0, _y0, _x1 + w, _y1 + h);
|
|
400
|
-
} else if (this === parentNode.tr) {
|
|
401
|
-
parentNode.resize(_x0 - w, _y0, _x1, _y1 + h);
|
|
402
|
-
} else if (this === parentNode.bl) {
|
|
403
|
-
parentNode.resize(_x0, _y0 - h, _x1 + w, _y1);
|
|
404
|
-
} else if (this === parentNode.br) {
|
|
405
|
-
parentNode.resize(_x0 - w, _y0 - h, _x1, _y1);
|
|
406
|
-
} else {
|
|
407
|
-
throw new Error("Specified 'parent' does not own this node");
|
|
408
|
-
}
|
|
409
|
-
return;
|
|
410
|
-
}
|
|
411
|
-
this.x0 = this.x0 = _x0;
|
|
412
|
-
this.y0 = this.y0 = _y0;
|
|
413
|
-
this.x1 = this.x1 = _x1;
|
|
414
|
-
this.y1 = this.y1 = _y1;
|
|
415
|
-
|
|
416
|
-
this.setHalfSize(_x0, _y0, _x1, _y1);
|
|
417
|
-
|
|
418
|
-
this.merge();
|
|
419
|
-
//reinsert all elements
|
|
420
|
-
const l = this.numElements;
|
|
421
|
-
const els = this.elements;
|
|
422
|
-
this.elements = [];
|
|
423
|
-
this.numElements = 0;
|
|
424
|
-
for (let i = 0; i < l; i++) {
|
|
425
|
-
this.insertElement(els[i]);
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
removeElement(e) {
|
|
430
|
-
|
|
431
|
-
const i = this.elements.indexOf(e);
|
|
432
|
-
|
|
433
|
-
this.elements.splice(i, 1);
|
|
434
|
-
|
|
435
|
-
// reset parent
|
|
436
|
-
e.parentNode = null;
|
|
437
|
-
|
|
438
|
-
this.numElements--;
|
|
439
|
-
|
|
440
|
-
if (this.numElements < minElements) {
|
|
441
|
-
// number of elements in the current bucket is too small, attempt reduction
|
|
442
|
-
this.reduce();
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
bubbleElementsUp() {
|
|
448
|
-
let targetNode = this;
|
|
449
|
-
while (this.numElements > 0 && targetNode.parentNode !== null) {
|
|
450
|
-
targetNode = targetNode.parentNode;
|
|
451
|
-
const parentElements = targetNode.numElements;
|
|
452
|
-
const capacityLeft = maxElements - parentElements;
|
|
453
|
-
if (capacityLeft > 0) {
|
|
454
|
-
const transferNumber = Math.min(capacityLeft, this.numElements);
|
|
455
|
-
for (let i = this.numElements - transferNumber; i < this.numElements; i++) {
|
|
456
|
-
const element = this.elements[i];
|
|
457
|
-
targetNode.insertElement(element);
|
|
458
|
-
}
|
|
459
|
-
this.numElements -= transferNumber;
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
this.elements.length = this.numElements;
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
validate() {
|
|
466
|
-
let v = true;
|
|
467
|
-
this.traversePreOrder(function (node) {
|
|
468
|
-
let isValid = node.validateNode();
|
|
469
|
-
if (!isValid && v !== false) {
|
|
470
|
-
v = false;
|
|
471
|
-
}
|
|
472
|
-
return isValid;
|
|
473
|
-
});
|
|
474
|
-
return v;
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
export default PointQuadTree;
|