mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2026-02-04 09:31:23 +01:00
fix: implement quadtree search for points within a radius (#1274)
This commit is contained in:
parent
f30ffd812e
commit
b223dc62da
1 changed files with 97 additions and 83 deletions
|
|
@ -206,13 +206,109 @@ export const findClosestCell = (x: number, y: number, radius = Infinity, packedG
|
||||||
return found ? found[2] : undefined;
|
return found ? found[2] : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Searches a quadtree for all points within a given radius
|
||||||
|
* Based on https://bl.ocks.org/lwthatcher/b41479725e0ff2277c7ac90df2de2b5e
|
||||||
|
* @param {number} x - The x coordinate of the search center
|
||||||
|
* @param {number} y - The y coordinate of the search center
|
||||||
|
* @param {number} radius - The search radius
|
||||||
|
* @param {Object} quadtree - The D3 quadtree to search
|
||||||
|
* @returns {Array} - An array of found data points within the radius
|
||||||
|
*/
|
||||||
|
export const findAllInQuadtree = (x: number, y: number, radius: number, quadtree: any) => {
|
||||||
|
const radiusSearchInit = (t: any, radius: number) => {
|
||||||
|
t.result = [];
|
||||||
|
(t.x0 = t.x - radius), (t.y0 = t.y - radius);
|
||||||
|
(t.x3 = t.x + radius), (t.y3 = t.y + radius);
|
||||||
|
t.radius = radius * radius;
|
||||||
|
};
|
||||||
|
|
||||||
|
const radiusSearchVisit = (t: any, d2: number) => {
|
||||||
|
t.node.data.scanned = true;
|
||||||
|
if (d2 < t.radius) {
|
||||||
|
do {
|
||||||
|
t.result.push(t.node.data);
|
||||||
|
t.node.data.selected = true;
|
||||||
|
} while ((t.node = t.node.next));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class Quad {
|
||||||
|
node: any;
|
||||||
|
x0: number;
|
||||||
|
y0: number;
|
||||||
|
x1: number;
|
||||||
|
y1: number;
|
||||||
|
constructor(node: any, x0: number, y0: number, x1: number, y1: number) {
|
||||||
|
this.node = node;
|
||||||
|
this.x0 = x0;
|
||||||
|
this.y0 = y0;
|
||||||
|
this.x1 = x1;
|
||||||
|
this.y1 = y1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const t: any = {x, y, x0: quadtree._x0, y0: quadtree._y0, x3: quadtree._x1, y3: quadtree._y1, quads: [], node: quadtree._root};
|
||||||
|
if (t.node) t.quads.push(new Quad(t.node, t.x0, t.y0, t.x3, t.y3));
|
||||||
|
radiusSearchInit(t, radius);
|
||||||
|
|
||||||
|
var i = 0;
|
||||||
|
while ((t.q = t.quads.pop())) {
|
||||||
|
i++;
|
||||||
|
|
||||||
|
// Stop searching if this quadrant can't contain a closer node.
|
||||||
|
if (
|
||||||
|
!(t.node = t.q.node) ||
|
||||||
|
(t.x1 = t.q.x0) > t.x3 ||
|
||||||
|
(t.y1 = t.q.y0) > t.y3 ||
|
||||||
|
(t.x2 = t.q.x1) < t.x0 ||
|
||||||
|
(t.y2 = t.q.y1) < t.y0
|
||||||
|
)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Bisect the current quadrant.
|
||||||
|
if (t.node.length) {
|
||||||
|
t.node.explored = true;
|
||||||
|
var xm: number = (t.x1 + t.x2) / 2,
|
||||||
|
ym: number = (t.y1 + t.y2) / 2;
|
||||||
|
|
||||||
|
t.quads.push(
|
||||||
|
new Quad(t.node[3], xm, ym, t.x2, t.y2),
|
||||||
|
new Quad(t.node[2], t.x1, ym, xm, t.y2),
|
||||||
|
new Quad(t.node[1], xm, t.y1, t.x2, ym),
|
||||||
|
new Quad(t.node[0], t.x1, t.y1, xm, ym)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Visit the closest quadrant first.
|
||||||
|
if ((t.i = (+(y >= ym) << 1) | +(x >= xm))) {
|
||||||
|
t.q = t.quads[t.quads.length - 1];
|
||||||
|
t.quads[t.quads.length - 1] = t.quads[t.quads.length - 1 - t.i];
|
||||||
|
t.quads[t.quads.length - 1 - t.i] = t.q;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Visit this point. (Visiting coincident points isn't necessary!)
|
||||||
|
else {
|
||||||
|
var dx = x - +quadtree._x.call(null, t.node.data),
|
||||||
|
dy = y - +quadtree._y.call(null, t.node.data),
|
||||||
|
d2 = dx * dx + dy * dy;
|
||||||
|
radiusSearchVisit(t, d2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return t.result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an array of packed cell indexes within a specified radius from given x and y coordinates
|
* Returns an array of packed cell indexes within a specified radius from given x and y coordinates
|
||||||
* @param {number} x - The x coordinate
|
* @param {number} x - The x coordinate
|
||||||
* @param {number} y - The y coordinate
|
* @param {number} y - The y coordinate
|
||||||
|
* @param {number} radius - The search radius
|
||||||
|
* @param {Object} packedGraph - The packed graph containing cells with quadtree
|
||||||
|
* @returns {number[]} - An array of cell indexes within the radius
|
||||||
*/
|
*/
|
||||||
export const findAllCellsInRadius = (x: number, y: number, radius: number, packedGraph: any): number[] => {
|
export const findAllCellsInRadius = (x: number, y: number, radius: number, packedGraph: any): number[] => {
|
||||||
const found = packedGraph.cells.q.findAll(x, y, radius);
|
// Use findAllInQuadtree directly instead of relying on prototype extension
|
||||||
|
const found = findAllInQuadtree(x, y, radius, packedGraph.cells.q);
|
||||||
return found.map((r: any) => r[2]);
|
return found.map((r: any) => r[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -325,88 +421,6 @@ export const isWater = (i: number, packedGraph: any) => {
|
||||||
return packedGraph.cells.h[i] < 20;
|
return packedGraph.cells.h[i] < 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const findAllInQuadtree = (x: number, y: number, radius: number, quadtree: any) => {
|
|
||||||
const radiusSearchInit = (t: any, radius: number) => {
|
|
||||||
t.result = [];
|
|
||||||
(t.x0 = t.x - radius), (t.y0 = t.y - radius);
|
|
||||||
(t.x3 = t.x + radius), (t.y3 = t.y + radius);
|
|
||||||
t.radius = radius * radius;
|
|
||||||
};
|
|
||||||
|
|
||||||
const radiusSearchVisit = (t: any, d2: number) => {
|
|
||||||
t.node.data.scanned = true;
|
|
||||||
if (d2 < t.radius) {
|
|
||||||
do {
|
|
||||||
t.result.push(t.node.data);
|
|
||||||
t.node.data.selected = true;
|
|
||||||
} while ((t.node = t.node.next));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class Quad {
|
|
||||||
node: any;
|
|
||||||
x0: number;
|
|
||||||
y0: number;
|
|
||||||
x1: number;
|
|
||||||
y1: number;
|
|
||||||
constructor(node: any, x0: number, y0: number, x1: number, y1: number) {
|
|
||||||
this.node = node;
|
|
||||||
this.x0 = x0;
|
|
||||||
this.y0 = y0;
|
|
||||||
this.x1 = x1;
|
|
||||||
this.y1 = y1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const t: any = {x, y, x0: quadtree._x0, y0: quadtree._y0, x3: quadtree._x1, y3: quadtree._y1, quads: [], node: quadtree._root};
|
|
||||||
if (t.node) t.quads.push(new Quad(t.node, t.x0, t.y0, t.x3, t.y3));
|
|
||||||
radiusSearchInit(t, radius);
|
|
||||||
|
|
||||||
var i = 0;
|
|
||||||
while ((t.q = t.quads.pop())) {
|
|
||||||
i++;
|
|
||||||
|
|
||||||
// Stop searching if this quadrant can’t contain a closer node.
|
|
||||||
if (
|
|
||||||
!(t.node = t.q.node) ||
|
|
||||||
(t.x1 = t.q.x0) > t.x3 ||
|
|
||||||
(t.y1 = t.q.y0) > t.y3 ||
|
|
||||||
(t.x2 = t.q.x1) < t.x0 ||
|
|
||||||
(t.y2 = t.q.y1) < t.y0
|
|
||||||
)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Bisect the current quadrant.
|
|
||||||
if (t.node.length) {
|
|
||||||
t.node.explored = true;
|
|
||||||
var xm: number = (t.x1 + t.x2) / 2,
|
|
||||||
ym: number = (t.y1 + t.y2) / 2;
|
|
||||||
|
|
||||||
t.quads.push(
|
|
||||||
new Quad(t.node[3], xm, ym, t.x2, t.y2),
|
|
||||||
new Quad(t.node[2], t.x1, ym, xm, t.y2),
|
|
||||||
new Quad(t.node[1], xm, t.y1, t.x2, ym),
|
|
||||||
new Quad(t.node[0], t.x1, t.y1, xm, ym)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Visit the closest quadrant first.
|
|
||||||
if ((t.i = (+(y >= ym) << 1) | +(x >= xm))) {
|
|
||||||
t.q = t.quads[t.quads.length - 1];
|
|
||||||
t.quads[t.quads.length - 1] = t.quads[t.quads.length - 1 - t.i];
|
|
||||||
t.quads[t.quads.length - 1 - t.i] = t.q;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Visit this point. (Visiting coincident points isn’t necessary!)
|
|
||||||
else {
|
|
||||||
var dx = x - +quadtree._x.call(null, t.node.data),
|
|
||||||
dy = y - +quadtree._y.call(null, t.node.data),
|
|
||||||
d2 = dx * dx + dy * dy;
|
|
||||||
radiusSearchVisit(t, d2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return t.result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// draw raster heightmap preview (not used in main generation)
|
// draw raster heightmap preview (not used in main generation)
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue