Implemented tangible.find. Octrees are more or less done! Yay!

This commit is contained in:
2022-07-14 00:57:11 -04:00
parent 69efb12c26
commit 4735c1fd7d
6 changed files with 466 additions and 114 deletions

View File

@@ -260,6 +260,9 @@ public:
// It is only relevant during the scan.
const PlaneScan *scan_config_;
IdVector *scan_result_;
util::XYZ scan_lo_;
util::XYZ scan_hi_;
util::XYZ scan_invradius_;
int scan_bbxlo_[9], scan_bbxhi_[9];
int scan_bbylo_[9], scan_bbyhi_[9];
int scan_bbzlo_[9], scan_bbzhi_[9];
@@ -462,10 +465,32 @@ public:
}
}
void calculate_search_bboxes(const PlaneScan &scan) {
// The final filtering step sometimes uses the inverse of the
// radius. In the case that the radius is 0, we want to use a huge
// number for the inverse radius, but not infinity, because using infinity
// would result in the final filtering step calculating (inf*0).
// In the case that the radius is infinite, we want to use zero for the
// inverse radius.
static float inverse_radius(float f) {
if (f == 0) return std::numeric_limits<float>::max();
if (std::isinf(f)) return 0;
return 1.0f / f;
}
// Given a PlaneScan, calculate the search bboxes,
// and all the other related configuration data.
void calculate_search_bboxes(const PlaneScan &sc) {
scan_config_ = &sc;
scan_lo_ = sc.center_ - sc.radius_;
scan_hi_ = sc.center_ + sc.radius_;
scan_invradius_.x = inverse_radius(sc.radius_.x);
scan_invradius_.y = inverse_radius(sc.radius_.y);
scan_invradius_.z = inverse_radius(sc.radius_.z);
// Convert the scan's bounding box to integral coordinates.
NodeInfo bblo(scale_, scan.lo_.x, scan.lo_.y, scan.lo_.z);
NodeInfo bbhi(scale_, scan.hi_.x, scan.hi_.y, scan.hi_.z);
NodeInfo bblo(scale_, scan_lo_.x, scan_lo_.y, scan_lo_.z);
NodeInfo bbhi(scale_, scan_hi_.x, scan_hi_.y, scan_hi_.z);
// Calculate the bounding box at each level of the tree.
NodeID ibblo = bblo.node;
@@ -503,37 +528,37 @@ public:
return oss.str();
}
static inline void scan_push_id(int64_t id, int64_t special, IdVector *result) {
if (id != special) {
static inline void scan_push_id(int64_t id, int64_t near, IdVector *result) {
if (id != near) {
result->push_back(id);
}
}
static void scan_planeitem(PlaneItem *pi, const PlaneScan &scan, IdVector *result) {
switch (scan.shape_) {
void scan_planeitem(PlaneItem *pi) {
switch (scan_config_->shape_) {
case PlaneScan::BOX: {
if ((pi->x() >= scan.lo_.x) && (pi->x() <= scan.hi_.x) &&
(pi->y() >= scan.lo_.y) && (pi->y() <= scan.hi_.y) &&
(pi->z() >= scan.lo_.z) && (pi->z() <= scan.hi_.z)) {
scan_push_id(pi->id(), scan.special_, result);
if ((pi->x() >= scan_lo_.x) && (pi->x() <= scan_hi_.x) &&
(pi->y() >= scan_lo_.y) && (pi->y() <= scan_hi_.y) &&
(pi->z() >= scan_lo_.z) && (pi->z() <= scan_hi_.z)) {
scan_push_id(pi->id(), scan_config_->near_, scan_result_);
}
break;
}
case PlaneScan::SPHERE: {
float dx = (pi->x() - scan.center_.x) * scan.invradius_.x;
float dy = (pi->y() - scan.center_.y) * scan.invradius_.y;
float dz = (pi->z() - scan.center_.z) * scan.invradius_.z;
float dx = (pi->x() - scan_config_->center_.x) * scan_invradius_.x;
float dy = (pi->y() - scan_config_->center_.y) * scan_invradius_.y;
float dz = (pi->z() - scan_config_->center_.z) * scan_invradius_.z;
if (dx*dx + dy*dy + dz*dz <= 1.0) {
scan_push_id(pi->id(), scan.special_, result);
scan_push_id(pi->id(), scan_config_->near_, scan_result_);
}
break;
}
case PlaneScan::CYLINDER: {
if ((pi->z() >= scan.lo_.z) && (pi->z() <= scan.hi_.z)) {
float dx = (pi->x() - scan.center_.x) * scan.invradius_.x;
float dy = (pi->y() - scan.center_.y) * scan.invradius_.y;
if ((pi->z() >= scan_lo_.z) && (pi->z() <= scan_hi_.z)) {
float dx = (pi->x() - scan_config_->center_.x) * scan_invradius_.x;
float dy = (pi->y() - scan_config_->center_.y) * scan_invradius_.y;
if (dx*dx + dy*dy <= 1.0) {
scan_push_id(pi->id(), scan.special_, result);
scan_push_id(pi->id(), scan_config_->near_, scan_result_);
}
}
break;
@@ -557,7 +582,7 @@ public:
PlaneItem *pi = first;
while (true) {
PlaneItem *next = pi->next_;
scan_planeitem(pi, *scan_config_, scan_result_);
scan_planeitem(pi);
if (next == first) break;
pi = next;
}
@@ -594,11 +619,10 @@ public:
}
// Scan a planetree.
void scan(const PlaneScan &sc, IdVector *result, std::ostream *debug) {
scan_config_ = &sc;
scan_result_ = result;
void scan(const PlaneScan &sc, IdVector *result, std::ostream *debug) {
calculate_search_bboxes(sc);
scan_result_ = result;
// We must only call 'scan_node' on nodes that actually exist.
// So we check if the tree is empty, and if so, we don't scan the
// root node.
@@ -758,9 +782,9 @@ PlaneMap::~PlaneMap() {}
IdVector PlaneMap::scan(const PlaneScan &sc) const {
IdVector result;
int startpos = 0;
if (sc.special_ != 0) {
if (!sc.omit_special_) {
result.push_back(sc.special_);
if (sc.near_ != 0) {
if (sc.include_near_) {
result.push_back(sc.near_);
startpos = 1;
}
}
@@ -789,12 +813,12 @@ eng::string PlaneMap::outliers_debug_string(const eng::string &plane) {
return PlaneTree::get(this, plane)->outliers_debug_string();
}
eng::string PlaneMap::search_bboxes_debug_string(const eng::string &plane, const PlaneScan &scan) {
return PlaneTree::get(this, plane)->search_bboxes_debug_string(scan);
eng::string PlaneMap::search_bboxes_debug_string(const PlaneScan &scan) {
return PlaneTree::get(this, scan.plane_)->search_bboxes_debug_string(scan);
}
eng::string PlaneMap::scan_steps_debug_string(const eng::string &plane, const PlaneScan &scan) {
return PlaneTree::get(this, plane)->scan_steps_debug_string(scan);
eng::string PlaneMap::scan_steps_debug_string(const PlaneScan &scan) {
return PlaneTree::get(this, scan.plane_)->scan_steps_debug_string(scan);
}
void PlaneMap::untrack_all() {
@@ -803,12 +827,188 @@ void PlaneMap::untrack_all() {
}
}
static eng::string tdb(PlaneMap &pm) {
return pm.tree_debug_string("p");
eng::string PlaneScan::configure(const LuaStack &LS0, LuaSlot config) {
LuaVar val, vx, vy, vz;
LuaStack LS(LS0.state(), val, vx, vy, vz);
if (!LS.istable(config)) {
return "scan configuration is not a table";
}
bool have_plane = false;
bool have_center = false;
bool have_radius = false;
bool have_shape = false;
bool have_near = false;
int parameters = 0;
LS.rawget(val, config, "plane");
if (!LS.isnil(val)) {
if (!LS.isstring(val)) {
return "scan configuration: 'plane' must be a string";
}
plane_ = LS.ckstring(val);
have_plane = true;
parameters += 1;
}
LS.rawget(vx, config, "centerx");
LS.rawget(vy, config, "centery");
LS.rawget(vz, config, "centerz");
if ((!LS.isnil(vx)) || (!LS.isnil(vy)) || (!LS.isnil(vz))) {
if (!LS.isnumber(vx)) {
return "scan configuration: 'centerx' must be a number";
}
if (!LS.isnumber(vy)) {
return "scan configuration: 'centery' must be a number";
}
if (!LS.isnumber(vz)) {
return "scan configuration: 'centerz' must be a number";
}
center_.x = LS.cknumber(vx);
center_.y = LS.cknumber(vy);
center_.z = LS.cknumber(vz);
have_center = true;
parameters += 3;
}
LS.rawget(val, config, "radius");
if (!LS.isnil(val)) {
if (!LS.isnumber(val)) {
return "scan configuration: 'radius' must be a number";
}
radius_.x = LS.cknumber(val);
radius_.y = radius_.z = radius_.x;
have_radius = true;
parameters += 1;
}
LS.rawget(vx, config, "radiusx");
LS.rawget(vy, config, "radiusy");
LS.rawget(vz, config, "radiusz");
if ((!LS.isnil(vx)) || (!LS.isnil(vy)) || (!LS.isnil(vz))) {
if (!LS.isnumber(vx)) {
return "scan configuration: 'radiusx' must be a number";
}
if (!LS.isnumber(vy)) {
return "scan configuration: 'radiusy' must be a number";
}
if (!LS.isnumber(vz)) {
return "scan configuration: 'radiusz' must be a number";
}
if (have_radius) {
return "scan configuration: specified both 'radius' and 'radiusx'";
}
radius_.x = LS.cknumber(vx);
radius_.y = LS.cknumber(vy);
radius_.z = LS.cknumber(vz);
have_radius = true;
parameters += 3;
}
LS.rawget(val, config, "shape");
if (!LS.isnil(val)) {
if (!LS.isstring(val)) {
return "scan configuration: 'shape' must be a string";
}
eng::string shape = LS.ckstring(val);
if (shape == "box") {
shape_ = BOX;
} else if (shape == "sphere") {
shape_ = SPHERE;
} else if (shape == "cylinder") {
shape_ = CYLINDER;
} else {
return util::ss("scan configuration: unknown shape ", shape);
}
have_shape = true;
parameters += 1;
}
LS.rawget(val, config, "near");
if (!LS.isnil(val)) {
int64_t id = LS.tanid(val);
if (id == 0) {
return "scan configuration: 'near' must be a tangible";
}
if (have_center) {
return "scan configuration: specified both 'center' and 'near'";
}
if (have_plane) {
return "scan configuration: specified both 'plane' and 'near'";
}
near_ = id;
have_center = true;
have_plane = true;
have_near = true;
parameters += 1;
}
LS.rawget(val, config, "include");
if (!LS.isnil(val)) {
if (!LS.isboolean(val)) {
return "scan configuration: 'include' must be a boolean";
}
if (!have_near) {
return "scan configuration: 'include' specified without 'near'";
}
include_near_ = LS.ckboolean(val);
parameters += 1;
}
LS.rawget(val, config, "wholeplane");
if (!LS.isnil(val)) {
if (!LS.isstring(val)) {
return "scan configuration: 'wholeplane' must be a string";
}
if (have_plane || have_center || have_radius || have_shape) {
return "scan configuration: do not specify plane, center, shape, or radius with 'wholeplane'";
}
plane_ = LS.ckstring(val);
set_whole_plane();
have_plane = true;
have_center = true;
have_radius = true;
have_shape = true;
parameters += 1;
}
if (lua_nkeys(LS.state(), config.index()) != parameters) {
return "scan configuration: unrecognized parameters in table";
}
if (!have_plane) {
return "scan configuration: did not specify plane";
}
if (!have_radius) {
return "scan configuration: did not specify radius";
}
if (!have_center) {
return "scan configuration: did not specify center";
}
if (!have_shape) {
shape_ = SPHERE; // default value.
}
return "";
}
static eng::string odb(PlaneMap &pm) {
return pm.outliers_debug_string("p");
eng::string PlaneScan::debug_string() const {
eng::ostringstream oss;
oss << "plane:" << plane_ << " center:" << center_ << " radius:" << radius_;
if (shape_ == BOX) oss << " shape:box";
else if (shape_ == SPHERE) oss << " shape:sphere";
else if (shape_ == CYLINDER) oss << " shape:cylinder";
else oss << " shape:unknown";
if (near_ != 0) {
oss << " near:" << near_ << " include:" << include_near_;
}
if (omit_nowhere_) {
oss << " omit_nowhere:true";
}
if (!sorted_) {
oss << " sorted:false";
}
return oss.str();
}
// The default radius is set such that float coordinates map directly to
@@ -835,7 +1035,7 @@ LuaDefine(unittests_planemap, "", "some unit tests") {
// Test track.
pi123.set_pos("p", 0x38, 0x16, 0x87);
pi123.track(&pm);
LuaAssertStrEq(L, tdb(pm),
LuaAssertStrEq(L, pm.tree_debug_string("p"),
"|L8:root"
"| L6:8,8,8"
"| L4:80,80,80"
@@ -844,14 +1044,14 @@ LuaDefine(unittests_planemap, "", "some unit tests") {
// Test untrack.
pi123.untrack();
LuaAssertStrEq(L, tdb(pm),
LuaAssertStrEq(L, pm.tree_debug_string("p"),
"|L8:root");
// Track two items at a time, not in the same cell.
pi456.set_pos("p", 0x12, 0x17, 0xAC);
pi123.track(&pm);
pi456.track(&pm);
LuaAssertStrEq(L, tdb(pm),
LuaAssertStrEq(L, pm.tree_debug_string("p"),
"|L8:root"
"| L6:8,8,8"
"| L4:80,80,80"
@@ -862,7 +1062,7 @@ LuaDefine(unittests_planemap, "", "some unit tests") {
// Move one of the items into the same cell as the other.
pi456.set_xyz(0x38, 0x16, 0x87);
LuaAssertStrEq(L, tdb(pm),
LuaAssertStrEq(L, pm.tree_debug_string("p"),
"|L8:root"
"| L6:8,8,8"
"| L4:80,80,80"
@@ -871,7 +1071,7 @@ LuaDefine(unittests_planemap, "", "some unit tests") {
// Move item 456 back out of the cell.
pi456.set_xyz(0x27, 0x11, 0x31);
LuaAssertStrEq(L, tdb(pm),
LuaAssertStrEq(L, pm.tree_debug_string("p"),
"|L8:root"
"| L6:8,8,8"
"| L4:80,80,80"
@@ -882,7 +1082,7 @@ LuaDefine(unittests_planemap, "", "some unit tests") {
// Move item 123 to follow 456.
pi123.set_xyz(0x27, 0x11, 0x31);
LuaAssertStrEq(L, tdb(pm),
LuaAssertStrEq(L, pm.tree_debug_string("p"),
"|L8:root"
"| L6:8,8,8"
"| L4:80,80,80"
@@ -894,8 +1094,8 @@ LuaDefine(unittests_planemap, "", "some unit tests") {
// Move item 456 close to, but not quite on the positive edge.
pi123.untrack();
pi456.set_xyz(0x23, 0x7FFE, 0x27);
LuaAssertStrEq(L, odb(pm), "total:1 justout:0 wayout:0");
LuaAssertStrEq(L, tdb(pm),
LuaAssertStrEq(L, pm.outliers_debug_string("p"), "total:1 justout:0 wayout:0");
LuaAssertStrEq(L, pm.tree_debug_string("p"),
"|L8:root"
"| L6:8,f,8"
"| L4:80,ff,80"
@@ -904,8 +1104,8 @@ LuaDefine(unittests_planemap, "", "some unit tests") {
// Move item 456 so that it's on the positive edge, but not an outlier.
pi456.set_xyz(0x23, 0x7FFF, 0x27);
LuaAssertStrEq(L, odb(pm), "total:1 justout:0 wayout:0");
LuaAssertStrEq(L, tdb(pm),
LuaAssertStrEq(L, pm.outliers_debug_string("p"), "total:1 justout:0 wayout:0");
LuaAssertStrEq(L, pm.tree_debug_string("p"),
"|L8:root"
"| L6:8,f,8"
"| L4:80,ff,80"
@@ -914,8 +1114,8 @@ LuaDefine(unittests_planemap, "", "some unit tests") {
// Move item 456 so that it's even closer to the positive edge, but not an outlier.
pi456.set_xyz(0x23, 0x7FFF + 0.99, 0x27);
LuaAssertStrEq(L, odb(pm), "total:1 justout:0 wayout:0");
LuaAssertStrEq(L, tdb(pm),
LuaAssertStrEq(L, pm.outliers_debug_string("p"), "total:1 justout:0 wayout:0");
LuaAssertStrEq(L, pm.tree_debug_string("p"),
"|L8:root"
"| L6:8,f,8"
"| L4:80,ff,80"
@@ -924,8 +1124,8 @@ LuaDefine(unittests_planemap, "", "some unit tests") {
// Move item 456 so that it's just barely a positive outlier.
pi456.set_xyz(0x23, 0x8000, 0x27);
LuaAssertStrEq(L, odb(pm), "total:1 justout:1 wayout:0");
LuaAssertStrEq(L, tdb(pm),
LuaAssertStrEq(L, pm.outliers_debug_string("p"), "total:1 justout:1 wayout:0");
LuaAssertStrEq(L, pm.tree_debug_string("p"),
"|L8:root"
"| L6:8,f,8"
"| L4:80,ff,80"
@@ -934,8 +1134,8 @@ LuaDefine(unittests_planemap, "", "some unit tests") {
// Move item 456 so that it's considerably past the positive edge.
pi456.set_xyz(0x23, 0x8048, 0x27);
LuaAssertStrEq(L, odb(pm), "total:1 justout:1 wayout:0");
LuaAssertStrEq(L, tdb(pm),
LuaAssertStrEq(L, pm.outliers_debug_string("p"), "total:1 justout:1 wayout:0");
LuaAssertStrEq(L, pm.tree_debug_string("p"),
"|L8:root"
"| L6:8,f,8"
"| L4:80,ff,80"
@@ -944,8 +1144,8 @@ LuaDefine(unittests_planemap, "", "some unit tests") {
// Move item 456 so that it's way past the positive edge.
pi456.set_xyz(0x23, 0x83748, 0x27);
LuaAssertStrEq(L, odb(pm), "total:1 justout:0 wayout:1");
LuaAssertStrEq(L, tdb(pm),
LuaAssertStrEq(L, pm.outliers_debug_string("p"), "total:1 justout:0 wayout:1");
LuaAssertStrEq(L, pm.tree_debug_string("p"),
"|L8:root"
"| L6:8,f,8"
"| L4:80,ff,80"
@@ -954,8 +1154,8 @@ LuaDefine(unittests_planemap, "", "some unit tests") {
// Move item 456 close to, but not quite on the negative edge.
pi456.set_xyz(0x23, -0x7fff, 0x27);
LuaAssertStrEq(L, odb(pm), "total:1 justout:0 wayout:0");
LuaAssertStrEq(L, tdb(pm),
LuaAssertStrEq(L, pm.outliers_debug_string("p"), "total:1 justout:0 wayout:0");
LuaAssertStrEq(L, pm.tree_debug_string("p"),
"|L8:root"
"| L6:8,0,8"
"| L4:80,00,80"
@@ -964,8 +1164,8 @@ LuaDefine(unittests_planemap, "", "some unit tests") {
// Move item 456 so that it's on the negative edge, but not an outlier.
pi456.set_xyz(0x23, -0x7fff - 0.5, 0x27);
LuaAssertStrEq(L, odb(pm), "total:1 justout:0 wayout:0");
LuaAssertStrEq(L, tdb(pm),
LuaAssertStrEq(L, pm.outliers_debug_string("p"), "total:1 justout:0 wayout:0");
LuaAssertStrEq(L, pm.tree_debug_string("p"),
"|L8:root"
"| L6:8,0,8"
"| L4:80,00,80"
@@ -974,8 +1174,8 @@ LuaDefine(unittests_planemap, "", "some unit tests") {
// Move item 456 so that it's just barely a negative outlier.
pi456.set_xyz(0x23, -0x8000, 0x27);
LuaAssertStrEq(L, odb(pm), "total:1 justout:1 wayout:0");
LuaAssertStrEq(L, tdb(pm),
LuaAssertStrEq(L, pm.outliers_debug_string("p"), "total:1 justout:1 wayout:0");
LuaAssertStrEq(L, pm.tree_debug_string("p"),
"|L8:root"
"| L6:8,0,8"
"| L4:80,00,80"
@@ -984,8 +1184,8 @@ LuaDefine(unittests_planemap, "", "some unit tests") {
// Move item 456 so that it's significantly past the negative edge.
pi456.set_xyz(0x23, -0x8048, 0x27);
LuaAssertStrEq(L, odb(pm), "total:1 justout:1 wayout:0");
LuaAssertStrEq(L, tdb(pm),
LuaAssertStrEq(L, pm.outliers_debug_string("p"), "total:1 justout:1 wayout:0");
LuaAssertStrEq(L, pm.tree_debug_string("p"),
"|L8:root"
"| L6:8,0,8"
"| L4:80,00,80"
@@ -994,8 +1194,8 @@ LuaDefine(unittests_planemap, "", "some unit tests") {
// Move item 456 so that it's way past the negative edge.
pi456.set_xyz(0x23, -0x83048, 0x27);
LuaAssertStrEq(L, odb(pm), "total:1 justout:0 wayout:1");
LuaAssertStrEq(L, tdb(pm),
LuaAssertStrEq(L, pm.outliers_debug_string("p"), "total:1 justout:0 wayout:1");
LuaAssertStrEq(L, pm.tree_debug_string("p"),
"|L8:root"
"| L6:8,0,8"
"| L4:80,00,80"
@@ -1005,14 +1205,14 @@ LuaDefine(unittests_planemap, "", "some unit tests") {
// Test the calculation of search bboxes.
// The two corners are deliberately not in low-high order.
scan.clear();
scan.set_bbox_given_two_corners(util::XYZ(0x23, 0x97, 0x103),
util::XYZ(0x309, 0x412, 0x27));
LuaAssertStrEq(L, pm.search_bboxes_debug_string("p", scan),
scan.set_plane("p");
scan.set_bbox_given_center_radius(util::XYZ(0x23, 0x97, 0x103), 2.0f);
LuaAssertStrEq(L, pm.search_bboxes_debug_string(scan),
"|Level 8 0,0,0 - 0,0,0"
"|Level 6 8,8,8 - 8,8,8"
"|Level 4 80,80,80 - 83,84,81"
"|Level 2 802,809,802 - 830,841,810"
"|Level 0 8023,8097,8027 - 8309,8412,8103");
"|Level 4 80,80,81 - 80,80,81"
"|Level 2 802,809,810 - 802,809,810"
"|Level 0 8021,8095,8101 - 8025,8099,8105");
// TESTS OF SCANNING
@@ -1025,8 +1225,9 @@ LuaDefine(unittests_planemap, "", "some unit tests") {
// right on the one object. Check the bboxes to make
// sure they only include the one cell.
scan.clear();
scan.set_plane("p");
scan.set_bbox_given_center_radius(util::XYZ(0x12, 0x34, 0x45), 0.0);
LuaAssertStrEq(L, pm.search_bboxes_debug_string("p", scan),
LuaAssertStrEq(L, pm.search_bboxes_debug_string(scan),
"|Level 8 0,0,0 - 0,0,0"
"|Level 6 8,8,8 - 8,8,8"
"|Level 4 80,80,80 - 80,80,80"
@@ -1034,7 +1235,7 @@ LuaDefine(unittests_planemap, "", "some unit tests") {
"|Level 0 8012,8034,8045 - 8012,8034,8045");
// Run the scan with radius zero. It should find the one object.
LuaAssertStrEq(L, pm.scan_steps_debug_string("p", scan),
LuaAssertStrEq(L, pm.scan_steps_debug_string(scan),
"|L8:root"
"| L7:2,2,2"
"| L6:8,8,8"
@@ -1050,8 +1251,9 @@ LuaDefine(unittests_planemap, "", "some unit tests") {
// bboxes as before, since a half-unit isn't enough to shift
// from one cell to the next.
scan.clear();
scan.set_plane("p");
scan.set_bbox_given_center_radius(util::XYZ(0x12 + 0.5, 0x34, 0x45), 0.0);
LuaAssertStrEq(L, pm.search_bboxes_debug_string("p", scan),
LuaAssertStrEq(L, pm.search_bboxes_debug_string(scan),
"|Level 8 0,0,0 - 0,0,0"
"|Level 6 8,8,8 - 8,8,8"
"|Level 4 80,80,80 - 80,80,80"
@@ -1062,7 +1264,7 @@ LuaDefine(unittests_planemap, "", "some unit tests") {
// from the object. It should encounter the cell containing the
// one object, but the object should get removed in the final
// filtering step.
LuaAssertStrEq(L, pm.scan_steps_debug_string("p", scan),
LuaAssertStrEq(L, pm.scan_steps_debug_string(scan),
"|L8:root"
"| L7:2,2,2"
"| L6:8,8,8"
@@ -1077,8 +1279,9 @@ LuaDefine(unittests_planemap, "", "some unit tests") {
// Next, expand the scan radius to huge. Examine the bboxes
// to make sure they cover the entire PlaneTree.
scan.clear();
scan.set_plane("p");
scan.set_bbox_given_center_radius(util::XYZ(0x12, 0x34, 0x45), 100000.0);
LuaAssertStrEq(L, pm.search_bboxes_debug_string("p", scan),
LuaAssertStrEq(L, pm.search_bboxes_debug_string(scan),
"|Level 8 0,0,0 - 0,0,0"
"|Level 6 0,0,0 - f,f,f"
"|Level 4 00,00,00 - ff,ff,ff"
@@ -1088,7 +1291,7 @@ LuaDefine(unittests_planemap, "", "some unit tests") {
// Walk the tree using the expansive search bboxes. It should
// find the one object, and it should still only traverse the same
// cells, because those are the only cells that exist.
LuaAssertStrEq(L, pm.scan_steps_debug_string("p", scan),
LuaAssertStrEq(L, pm.scan_steps_debug_string(scan),
"|L8:root"
"| L7:2,2,2"
"| L6:8,8,8"
@@ -1104,7 +1307,7 @@ LuaDefine(unittests_planemap, "", "some unit tests") {
// using the expansive search. It should find both objects.
pi456.set_pos("p", 0x14, 0x35, 0x30);
pi456.track(&pm);
LuaAssertStrEq(L, pm.scan_steps_debug_string("p", scan),
LuaAssertStrEq(L, pm.scan_steps_debug_string(scan),
"|L8:root"
"| L7:2,2,2"
"| L6:8,8,8"
@@ -1120,13 +1323,66 @@ LuaDefine(unittests_planemap, "", "some unit tests") {
"| L0:8012,8034,8045 123"
"|Result: 123,456");
// We're going to test that sphere of radius 0.0 works. This is an important
// test because the sphere calculation involves calculating the inverse of
// the radius, which has to be special-cased when radius is zero. We need
// special-case code for this. In this test case, we set up a scan with two
// objects in the same cell, just a smidge apart. Use a sphere scan with
// radius zero, centered right on object 123 (but missing object 456).
pm.untrack_all();
pi123.set_pos("p", 0x12 + 0.1, 0x34, 0x45);
pi456.set_pos("p", 0x12 + 0.2, 0x34, 0x45);
pi123.track(&pm);
pi456.track(&pm);
scan.clear();
scan.set_plane("p");
scan.set_shape(PlaneScan::SPHERE);
scan.set_bbox_given_center_radius(util::XYZ(0x12 + 0.1, 0x34, 0x45), 0.0);
LuaAssertStrEq(L, pm.scan_steps_debug_string(scan),
"|L8:root"
"| L7:2,2,2"
"| L6:8,8,8"
"| L5:20,20,20"
"| L4:80,80,80"
"| L3:200,200,201"
"| L2:801,803,804"
"| L1:2004,200d,2011"
"| L0:8012,8034,8045 123,456"
"|Result: 123");
// We're going to test that 'whole plane' searches work.
// These use an infinite radius.
pm.untrack_all();
pi123.set_pos("p", 0x12, 0x34, 0x45);
pi123.track(&pm);
scan.clear();
scan.set_plane("p");
scan.set_whole_plane();
LuaAssertStrEq(L, pm.search_bboxes_debug_string(scan),
"|Level 8 0,0,0 - 0,0,0"
"|Level 6 0,0,0 - f,f,f"
"|Level 4 00,00,00 - ff,ff,ff"
"|Level 2 000,000,000 - fff,fff,fff"
"|Level 0 0000,0000,0000 - ffff,ffff,ffff");
LuaAssertStrEq(L, pm.scan_steps_debug_string(scan),
"|L8:root"
"| L7:2,2,2"
"| L6:8,8,8"
"| L5:20,20,20"
"| L4:80,80,80"
"| L3:200,200,201"
"| L2:801,803,804"
"| L1:2004,200d,2011"
"| L0:8012,8034,8045 123"
"|Result: 123");
// Set up a tree with a single object that's outside
// the tree's bounding box (an outlier). Print the tree
// to verify that the object ended up on the edge.
pm.untrack_all();
pi123.set_pos("p", 0x100000, 0x16, 0x23);
pi123.track(&pm);
LuaAssertStrEq(L, tdb(pm),
LuaAssertStrEq(L, pm.tree_debug_string("p"),
"|L8:root"
"| L6:f,8,8"
"| L4:ff,80,80"
@@ -1138,8 +1394,9 @@ LuaDefine(unittests_planemap, "", "some unit tests") {
// contains the outlier. It also contains a few other cells
// because the radius is nonzero.
scan.clear();
scan.set_plane("p");
scan.set_bbox_given_center_radius(util::XYZ(0x100000, 0x16, 0x23), 0.2);
LuaAssertStrEq(L, pm.search_bboxes_debug_string("p", scan),
LuaAssertStrEq(L, pm.search_bboxes_debug_string(scan),
"|Level 8 0,0,0 - 0,0,0"
"|Level 6 f,8,8 - f,8,8"
"|Level 4 ff,80,80 - ff,80,80"
@@ -1147,7 +1404,7 @@ LuaDefine(unittests_planemap, "", "some unit tests") {
"|Level 0 ffff,8015,8022 - ffff,8016,8023");
// Confirm that the scan finds the outlier.
LuaAssertStrEq(L, pm.scan_steps_debug_string("p", scan),
LuaAssertStrEq(L, pm.scan_steps_debug_string(scan),
"|L8:root"
"| L7:3,2,2"
"| L6:f,8,8"