Skip to content

Commit 1bcb4c0

Browse files
committed
improve ChunkQuadTreeNode
1 parent 259ec48 commit 1bcb4c0

File tree

3 files changed

+74
-24
lines changed

3 files changed

+74
-24
lines changed

apps/lookbook/src/demos/quadtree-playground/QuadTreeVisualization.ts

+51-7
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,20 @@ export class QuadTreeVisualization {
5353
// this.ctx.fillRect(chunk.left, chunk.top, chunk.right - chunk.left, chunk.bottom - chunk.top);
5454
// }
5555

56-
this.renderChunkFrame(root, 0, 0, width, height);
56+
const stats = {
57+
leafs: 0,
58+
noLeafs: 0,
59+
totalFrames: 0,
60+
maxChunksPerFrame: 0,
61+
averageChunksPerFrame: 0,
62+
};
63+
64+
this.renderChunkFrame(root, 0, 0, width, height, stats);
65+
66+
stats.totalFrames = stats.leafs + stats.noLeafs;
67+
stats.averageChunksPerFrame = Math.round(stats.averageChunksPerFrame / stats.totalFrames);
68+
69+
console.log('render chunks stats', stats);
5770

5871
this.canvasStage.needsUpdate = true;
5972
}
@@ -64,11 +77,20 @@ export class QuadTreeVisualization {
6477
top: number,
6578
right: number,
6679
bottom: number,
80+
stats: {leafs: number; noLeafs: number; totalFrames: number; maxChunksPerFrame: number; averageChunksPerFrame: number},
6781
) {
6882
if (chunk.isLeaf) {
6983
this.ctx.fillStyle = 'rgba(255, 0, 66, 0.5)';
84+
stats.leafs++;
7085
} else {
7186
this.ctx.fillStyle = 'rgba(255, 255, 66, 0.25)';
87+
stats.noLeafs++;
88+
}
89+
90+
stats.averageChunksPerFrame += chunk.chunks.length;
91+
92+
if (chunk.chunks.length > stats.maxChunksPerFrame) {
93+
stats.maxChunksPerFrame = chunk.chunks.length;
7294
}
7395

7496
for (const c of chunk.chunks) {
@@ -84,14 +106,14 @@ export class QuadTreeVisualization {
84106
this.ctx.lineTo(chunk.originX, bottom);
85107
this.ctx.stroke();
86108

87-
chunk.nodes.northWest && this.renderChunkFrame(chunk.nodes.northWest, left, top, chunk.originX, chunk.originY);
88-
chunk.nodes.northEast && this.renderChunkFrame(chunk.nodes.northEast, chunk.originX, top, right, chunk.originY);
89-
chunk.nodes.southWest && this.renderChunkFrame(chunk.nodes.southWest, left, chunk.originY, chunk.originX, bottom);
90-
chunk.nodes.southEast && this.renderChunkFrame(chunk.nodes.southEast, chunk.originX, chunk.originY, right, bottom);
109+
chunk.nodes.northWest && this.renderChunkFrame(chunk.nodes.northWest, left, top, chunk.originX, chunk.originY, stats);
110+
chunk.nodes.northEast && this.renderChunkFrame(chunk.nodes.northEast, chunk.originX, top, right, chunk.originY, stats);
111+
chunk.nodes.southWest && this.renderChunkFrame(chunk.nodes.southWest, left, chunk.originY, chunk.originX, bottom, stats);
112+
chunk.nodes.southEast && this.renderChunkFrame(chunk.nodes.southEast, chunk.originX, chunk.originY, right, bottom, stats);
91113
}
92114
}
93115

94-
makeRandomQuadTree(count = 100, maxChunkNodes = 2) {
116+
makeRandomQuadTree(randomCount = 100, sortedCount = 100, maxChunkNodes = 2) {
95117
const WIDTH = 1000;
96118
const HEIGHT = 600;
97119

@@ -101,7 +123,7 @@ export class QuadTreeVisualization {
101123

102124
const chunks: NumberDataChunk2D[] = [];
103125

104-
for (let i = 0; i < count; i++) {
126+
for (let i = 0; i < randomCount; i++) {
105127
const w = Math.ceil(Math.max(MIN_SIZE, Math.random() * MAX_SIZE));
106128
const h = Math.ceil(Math.max(MIN_SIZE, Math.random() * MAX_SIZE));
107129
const x = Math.round(Math.random() * (WIDTH - MARGIN * 2) + MARGIN) - w / 2;
@@ -116,6 +138,28 @@ export class QuadTreeVisualization {
116138
);
117139
}
118140

141+
const minDimension = Math.min(WIDTH, HEIGHT);
142+
const rows = Math.ceil(Math.sqrt(sortedCount));
143+
const cellSize = Math.ceil(minDimension / rows);
144+
145+
const centerX = (WIDTH - rows * cellSize) * -0.5;
146+
const centerY = (HEIGHT - rows * cellSize) * -0.5;
147+
148+
for (let i = 0; i < sortedCount; i++) {
149+
const w = Math.ceil(cellSize + (Math.random() - 1) * cellSize * 0.25);
150+
const h = Math.ceil(cellSize + (Math.random() - 1) * cellSize * 0.25);
151+
const y = Math.floor(i / rows) * cellSize - centerY + (Math.random() - 1) * cellSize * 0.25;
152+
const x = (i % rows) * cellSize - centerX + (Math.random() - 1) * cellSize * 0.25;
153+
chunks.push(
154+
new NumberDataChunk2D({
155+
x,
156+
y,
157+
width: w,
158+
height: h,
159+
}),
160+
);
161+
}
162+
119163
const root = new ChunkQuadTreeNode<NumberDataChunk2D>(chunks);
120164

121165
root.subdivide(maxChunkNodes);

apps/lookbook/src/pages/demos/quadtree-playground.astro

+15-12
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,15 @@ import {title} from './_quadtree-playground.json';
1414
</style>
1515

1616
<script>
17-
import {Display, type DisplayEventArgs} from '@spearwolf/twopoint5d';
17+
import {ChunkQuadTreeNode, Display, type DisplayEventArgs} from '@spearwolf/twopoint5d';
1818
import GUI from 'lil-gui';
1919
import {QuadTreeVisualization} from '~demos/quadtree-playground/QuadTreeVisualization';
2020

2121
// -----------------------------
2222

23-
// -----------------------------
24-
2523
const display = new Display(document.getElementById('twopoint5d')!);
2624

27-
const visual = new QuadTreeVisualization(display.renderer, 256, 256);
28-
29-
// visual.canvasStage.on('resize', () => {
30-
// visual.render();
31-
// });
25+
const visual = new QuadTreeVisualization(display.renderer, 1024, 1024);
3226

3327
visual.makeRandomQuadTree();
3428

@@ -49,15 +43,24 @@ import {title} from './_quadtree-playground.json';
4943
const gui = new GUI({title: 'quadtree playground'});
5044

5145
const actions = {
52-
chunkCount: 100,
53-
maxChunkNodes: 5,
46+
randomChunkCount: 100,
47+
sortedChunkCount: 100,
48+
maxChunkNodes: 10,
49+
intersectDistanceFactor: ChunkQuadTreeNode.IntersectDistanceFactor,
50+
beforeAfterDeltaFactor: ChunkQuadTreeNode.BeforeAfterDeltaFactor,
5451

5552
makeRandomQuadTree: () => {
56-
visual.makeRandomQuadTree(actions.chunkCount, actions.maxChunkNodes);
53+
ChunkQuadTreeNode.IntersectDistanceFactor = actions.intersectDistanceFactor;
54+
ChunkQuadTreeNode.BeforeAfterDeltaFactor = actions.beforeAfterDeltaFactor;
55+
56+
visual.makeRandomQuadTree(actions.randomChunkCount, actions.sortedChunkCount, actions.maxChunkNodes);
5757
},
5858
};
5959

60-
gui.add(actions, 'chunkCount', 1, 1000, 1).name('chunk count');
60+
gui.add(actions, 'randomChunkCount', 0, 1000, 1).name('random placed chunks');
61+
gui.add(actions, 'sortedChunkCount', 0, 1000, 1).name('sorted grid chunks');
6162
gui.add(actions, 'maxChunkNodes', 2, 30, 1).name('max chunk nodes');
63+
gui.add(actions, 'intersectDistanceFactor', 0.1, 10).name('intersect distance factor');
64+
gui.add(actions, 'beforeAfterDeltaFactor', 0.1, 10).name('before after delta factor');
6265
gui.add(actions, 'makeRandomQuadTree').name('make random chunks');
6366
</script>

packages/twopoint5d/src/tiled-maps/chunk-quad-tree/ChunkQuadTreeNode.ts

+8-5
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,6 @@ interface IChunkAxis {
1818
noSubdivide: boolean;
1919
}
2020

21-
const INTERSECT_DISTANCE_FACTOR = Math.PI;
22-
const BEFORE_AFTER_DELTA_FACTOR = Math.PI;
23-
2421
type AABBPropKey = 'top' | 'right' | 'bottom' | 'left';
2522

2623
const calcAxis = (chunks: IDataChunk2D[], beforeKey: AABBPropKey, afterKey: AABBPropKey, chunk: IDataChunk2D): IChunkAxis => {
@@ -48,12 +45,15 @@ const calcAxis = (chunks: IDataChunk2D[], beforeKey: AABBPropKey, afterKey: AABB
4845
const intersectCount = intersectChunks.length;
4946
const afterCount = afterChunks.length;
5047
const beforeDistance = Math.abs(0.5 - beforeCount / chunksCount);
51-
const intersectDistance = Math.abs(intersectCount / chunksCount) * INTERSECT_DISTANCE_FACTOR;
48+
const intersectDistance = Math.abs(intersectCount / chunksCount) * ChunkQuadTreeNode.IntersectDistanceFactor;
5249
const afterDistance = Math.abs(0.5 - afterCount / chunksCount);
5350

5451
return {
5552
distance:
56-
beforeDistance + intersectDistance + afterDistance + Math.abs(afterDistance - beforeDistance) * BEFORE_AFTER_DELTA_FACTOR,
53+
beforeDistance +
54+
intersectDistance +
55+
afterDistance +
56+
Math.abs(afterDistance - beforeDistance) * ChunkQuadTreeNode.BeforeAfterDeltaFactor,
5757
noSubdivide:
5858
(beforeCount === 0 && intersectCount === 0) ||
5959
(beforeCount === 0 && afterCount === 0) ||
@@ -86,6 +86,9 @@ const findAxis = (chunks: IDataChunk2D[], beforeKey: AABBPropKey, afterKey: AABB
8686
* With `findChunks*()` all chunks in a certain area are found.
8787
*/
8888
export class ChunkQuadTreeNode<ChunkType extends IDataChunk2D> {
89+
static IntersectDistanceFactor = Math.PI;
90+
static BeforeAfterDeltaFactor = Math.PI;
91+
8992
//
9093
// -y
9194
//

0 commit comments

Comments
 (0)