Skip to content

Commit f28a355

Browse files
committed
update BVH
1 parent ba3733c commit f28a355

File tree

2 files changed

+43
-8
lines changed

2 files changed

+43
-8
lines changed

packages/geometry/__tests__/BoundingVolumeHierarchy.test.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,6 @@ describe('BoundingVolumeHierarchy', () => {
3636
width: 25,
3737
height: 25,
3838
});
39-
40-
expect(BVH.intersections(bvh, shapes[0], S.boundingBox(shapes[0])).length).toBe(0);
4139
});
4240

4341
const shapes = [S.fromValues(0, 0, 10, 10), S.fromValues(15, 15, 10, 10), S.fromValues(5, 5, 10, 10)];
@@ -56,14 +54,16 @@ describe('BoundingVolumeHierarchy', () => {
5654
test('intersection', () => {
5755
const bvh = BVH.fromShapes([...shapes]);
5856

59-
const c1 = BVH.intersections(bvh, shapes[0], S.boundingBox(shapes[0]));
60-
expect(c1.length).toBe(1);
57+
const c1 = BVH.intersections(bvh, S.boundingBox(shapes[0]));
58+
expect(c1.length).toBe(2);
59+
expect(c1).toContain(shapes[0]);
6160
expect(c1).toContain(shapes[2]);
6261

63-
const c2 = BVH.intersections(bvh, shapes[2], S.boundingBox(shapes[2]));
64-
expect(c2.length).toBe(2);
62+
const c2 = BVH.intersections(bvh, S.boundingBox(shapes[2]));
63+
expect(c2.length).toBe(3);
6564
expect(c2).toContain(shapes[0]);
6665
expect(c2).toContain(shapes[1]);
66+
expect(c2).toContain(shapes[2]);
6767
});
6868

6969
describe('nearest neighbor', () => {

packages/geometry/src/BoundingVolumeHierarchy.ts

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,13 +76,44 @@ export function fromShapes(shapes: Array<S.Shape2D>): BVHNode<S.Shape2D> {
7676
return constructBVHTree(leafNodes, 0, leafNodes.length - 1);
7777
}
7878

79-
export function intersections<T>(root: BVHNode<T>, value: T, rect: R.Rect2D): T[] {
79+
export function fromElements(elements: Element[]): BVHNode<Element> {
80+
if (elements.length === 0) {
81+
return {
82+
value: new Element(),
83+
aabb: R.fromValues(),
84+
mortonCode: -1,
85+
isLeaf: true,
86+
left: null,
87+
right: null,
88+
};
89+
}
90+
91+
const leafNodes: BVHLeafNode<Element>[] = elements.map((el) => {
92+
const aabb = el.getBoundingClientRect();
93+
94+
return {
95+
value: el,
96+
aabb,
97+
mortonCode: V.mortonCode(R.center(aabb)),
98+
isLeaf: true,
99+
left: null,
100+
right: null,
101+
};
102+
});
103+
104+
// Rectangles sorted in order of their morton codes.
105+
leafNodes.sort((a, b) => a.mortonCode - b.mortonCode);
106+
107+
return constructBVHTree(leafNodes, 0, leafNodes.length - 1);
108+
}
109+
110+
export function intersections<T>(root: BVHNode<T>, rect: R.Rect2D): T[] {
80111
const stack = [root];
81112
let node: BVHNode<T> | undefined;
82113
const collisions: T[] = [];
83114

84115
while ((node = stack.pop())) {
85-
if (value === node.value || !R.intersects(rect, node.aabb)) continue;
116+
if (!R.intersects(rect, node.aabb)) continue;
86117

87118
if (node.isLeaf) {
88119
collisions.push(node.value);
@@ -114,6 +145,10 @@ export function depthFirstTraverse<T>(root: BVHNode<T>, cb: (node: BVHNode<T>) =
114145
}
115146
}
116147

148+
export function proximal<T>(root: BVHNode<T>, rect: R.Rect2D, proximity: number): T[] {
149+
return intersections(root, R.expand(rect, proximity));
150+
}
151+
117152
export function nearestShape(root: BVHNode<S.Shape2D>, shape: S.Shape2D, direction?: V.Vector2): S.Shape2D | undefined {
118153
const stack = [root];
119154
let node: BVHNode<S.Shape2D> | undefined;

0 commit comments

Comments
 (0)