Skip to content

Commit

Permalink
Update the tree implementation to utilize static segment positions an…
Browse files Browse the repository at this point in the history
…d sizes. This will be necessary for future tree optimizations, allowing for updates instead of recreating the entire tree
  • Loading branch information
DrA1ex committed Oct 7, 2023
1 parent f96e143 commit 2c2c300
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 50 deletions.
4 changes: 3 additions & 1 deletion examples/common/settings/debug.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ export class DebugSettings extends SettingsBase {
.setName("Show tree leafs"),
showTreeSegments: Property.bool("debug_tree_segments", false)
.setName("Show tree segments"),
showTreeBoundaryCollision: Property.bool("debug_tree_collision", true)
showTreeSegmentsBoundary: Property.bool("debug_tree_segments_boundary", false)
.setName("Show tree segments boundary"),
showTreeBoundaryCollision: Property.bool("debug_tree_collision", false)
.setName("Show tree boundary collision"),

vectorArrowSize: Property.float("vector_arrow_size", 2)
Expand Down
18 changes: 11 additions & 7 deletions lib/misc/debug.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export class Debug {
this.debugTree = options?.debugTree ?? false;
this.showTreeLeafs = options?.showTreeLeafs ?? true;
this.showTreeSegments = options?.showTreeSegments ?? false;
this.showTreeSegmentsBoundary = options?.showTreeSegmentsBoundary ?? false;
this.showTreeBoundaryCollision = options?.showTreeBoundaryCollision ?? true;

this.vectorArrowSize = options?.vectorArrowSize ?? 2;
Expand Down Expand Up @@ -172,19 +173,19 @@ export class Debug {
#drawTreeDebug(ctx, tree) {
const self = this;

function _drawLeafBackground1(current) {
function _drawLeafSegmentRect(current) {
ctx.strokeRect(current.segmentBoundary.left, current.segmentBoundary.top, current.segmentBoundary.width, current.segmentBoundary.height);

for (const leaf of current.leafs) {
_drawLeafBackground1(leaf);
_drawLeafSegmentRect(leaf);
}
}

function _drawLeafBackground2(current) {
function _drawLeafSegmentBoundaryRect(current) {
ctx.strokeRect(current.boundary.left, current.boundary.top, current.boundary.width, current.boundary.height);

for (const leaf of current.leafs) {
_drawLeafBackground2(leaf);
_drawLeafSegmentBoundaryRect(leaf);
}
}

Expand Down Expand Up @@ -279,10 +280,13 @@ export class Debug {
ctx.save();

if (this.showTreeSegments) {
ctx.strokeStyle = "#e7ecff";
_drawLeafBackground1(tree.root);
ctx.strokeStyle = "silver";
_drawLeafBackground2(tree.root);
_drawLeafSegmentRect(tree.root);
}

if (this.showTreeSegmentsBoundary) {
ctx.strokeStyle = "#e7ecff";
_drawLeafSegmentBoundaryRect(tree.root);
}

ctx.font = "0.5rem serif";
Expand Down
77 changes: 36 additions & 41 deletions lib/physics/common/tree.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import {ObjectStack} from "../../misc/stack.js";
import {BoundaryBox} from "./boundary.js";

const EPSILON = 1e-9;

/** @type{ObjectStack<BoundaryBox>} */
const BoundaryBoxPool = new ObjectStack(() => BoundaryBox.empty());

class SpatialLeaf {
/** @type{SpatialLeaf[]} */
leafs = [];
Expand All @@ -23,14 +19,14 @@ class SpatialLeaf {
* @param {number} id
* @param {Body[]} items
* @param {BoundaryBox} boundary
* @param {BoundaryBox} [segmentBoundary=null]
* @param {Boolean} [containsActive=true]
* @param {BoundaryBox} segmentBoundary
* @param {Boolean} containsActive
*/
constructor(id, items, boundary, segmentBoundary = null, containsActive = true) {
constructor(id, items, boundary, segmentBoundary, containsActive) {
this.id = id;
this.items = items;
this.boundary = boundary;
this.segmentBoundary = segmentBoundary ?? boundary;
this.segmentBoundary = segmentBoundary;
this.containsActive = containsActive;
}

Expand All @@ -41,7 +37,6 @@ class SpatialLeaf {

export class SpatialTree {
#leafId = 0;
#savedStackPosition;

/** @type {SpatialLeaf} */
root = null;
Expand All @@ -55,62 +50,62 @@ export class SpatialTree {
*/
constructor(items, divider, maxCount) {
this.divider = divider;
this.maxCount = maxCount
this.maxCount = maxCount;

const boundary = BoundaryBox.fromBodies(items);
const segmentBoundary = this.#createLevelBoundary(boundary);

this.#savedStackPosition = BoundaryBoxPool.save();
this.root = new SpatialLeaf(this.#leafId++, items, BoundaryBox.fromBodies(items), segmentBoundary, true);

this.root = new SpatialLeaf(this.#leafId++, items, BoundaryBox.fromBodies(items, BoundaryBoxPool));
this.#fillLeaf(this.root, items, this.root.boundary);
this.#fillLeaf(this.root);
}

#fillLeaf(current) {
if (current.items.length <= this.maxCount) return;
#fillLeaf(leaf) {
if (leaf.items.length <= this.maxCount) return;

for (const segmentBoundary of this.#iterateSegments(current.segmentBoundary)) {
const leaf = this.#createLeaf(current, segmentBoundary, current.items);
if (leaf !== null) {
current.addLeaf(leaf)
}
for (const segment of this.#iterateSegments(leaf.segmentBoundary)) {
const child = this.#createLeaf(leaf, segment);
if (child) leaf.addLeaf(child);
}

for (const leaf of current.leafs) {
this.#fillLeaf(leaf);
for (const child of leaf.leafs) {
this.#fillLeaf(child);
}
}

#createLeaf(parent, segmentBoundary, items) {
#createLeaf(parent, segmentBoundary) {
const {items} = parent;
const filteredItems = items.filter(b => !b.static && SpatialTree.#isContainedByBoundary(b, segmentBoundary));
if (filteredItems.length <= 0) return null;

const boundaryBox = BoundaryBox.fromBodies(filteredItems, BoundaryBoxPool);
const boundaryBox = BoundaryBox.fromBodies(filteredItems);
const containsActive = parent.containsActive && filteredItems.some(b => b.active);

return new SpatialLeaf(this.#leafId++, filteredItems, boundaryBox, segmentBoundary, containsActive);
}

* #iterateSegments(boundary) {
if (boundary.width <= EPSILON && boundary.height <= EPSILON) {
return;
}
* #iterateSegments(segmentBoundary) {
const step = segmentBoundary.width / this.divider;
if (step <= EPSILON) return;

const xStep = boundary.width / this.divider;
const yStep = boundary.height / this.divider;
for (let i = 0; i < this.divider; i++) {
for (let j = 0; j < this.divider; j++) {
const left = segmentBoundary.left + i * step;
const top = segmentBoundary.top + j * step

for (let x = 0; x < this.divider; ++x) {
const left = boundary.left + x * xStep
const right = (x < this.divider - 1 ? left + xStep : boundary.right + EPSILON);

for (let y = 0; y < this.divider; ++y) {
const top = boundary.top + y * yStep;
const bottom = (y < this.divider - 1 ? top + yStep : boundary.bottom + EPSILON);

yield BoundaryBoxPool.get().update(left, right, top, bottom);
yield new BoundaryBox(left, left + step, top, top + step);
}
}
}

free() {
BoundaryBoxPool.restore(this.#savedStackPosition);
#createLevelBoundary(boundary) {
const maxDim = Math.max(...[
boundary.left, boundary.right, boundary.top, boundary.bottom
].map(v => Math.abs(v)));

const level = Math.ceil(Math.log(maxDim) / Math.log(this.divider));
const dim = Math.pow(this.divider, level);
return new BoundaryBox(-dim, dim, -dim, dim);
}

static #isContainedByBoundary(body, boundary) {
Expand Down
1 change: 0 additions & 1 deletion lib/physics/solver.js
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,6 @@ export class ImpulseBasedSolver {
#collectCollisions() {
let t = performance.now();

this.stepInfo.tree?.free();
this.stepInfo.tree = new SpatialTree(this.rigidBodies, this.treeDivider, this.treeMaxCount);
this.stepInfo.treeTime = performance.now() - t;

Expand Down

0 comments on commit 2c2c300

Please sign in to comment.