Files
integration/luprex/core/cpp/planemap.hpp
2022-07-11 02:32:12 -04:00

230 lines
7.2 KiB
C++

//////////////////////////////////////////////////////////////
//
// PLANEMAP
//
// This module defines two classes: PlaneMap, and PlaneItem. A
// PlaneItem is an object that has a plane (a string) and an
// XYZ position. A PlaneMap keeps track of potentially thousands
// of PlaneItem objects, allowing you to search for PlaneItems
// by scanning a geographic region.
//
// CLASS SPRITE DERIVES FROM PLANEITEM
//
// A PlaneMap records the positions of PlaneItems. The intent is
// that class Sprite should derive from PlaneItem. That way, the
// PlaneMap can record the positions of sprites.
//
// When you put derived types like Sprite into a PlaneMap, the
// PlaneMap will work fine. However, when you scan the PlaneMap,
// it will give return PlaneItem pointers, not Sprite pointers.
//
// If you're sure that the PlaneMap contains only sprites, then
// you can use static_cast to convert those PlaneItem pointers to
// Sprite pointers. Sadly, there's no simple way to avoid the casting.
//
// THE SCANNABLE REGION IS FINITE
//
// A plane is a rectangle, with a finite size: if you stray more
// than 80,000,000 meters from the origin, then that PlaneItem
// is beyond the scannable region.
//
// There's nothing stopping you from moving a PlaneItem outside
// the scannable region. It is not an error to do so.
// However, if you do move a PlaneItem outside the scannable
// region, then that PlaneItem will not show up for scan_radius.
//
// PLANES CANNOT BE DESTROYED BUT THEY CAN BE UNINHABITED
//
// You can't "create" or "destroy" planes. Instead, we say
// that a plane is "uninhabited" until you put a PlaneItem
// there. Initially, all possible planes exist, but they're
// all uninhabited until you put a PlaneItem there. Planes
// that are uninhabited take no memory.
//
// THE NOWHERE PLANE
//
// If you use the literal "nowhere" as a plane name, a special case is
// triggered: you're "nowhere." Radius scans can never find items
// whose plane is "nowhere." The same also applies when you use the
// empty string as a plane name.
//
// THE Z COORDINATE IS IGNORED
//
// Class PlaneItem stores X, Y, and Z. The Z coordinate is
// ignored for all plane-scanning operations. In other words,
// planes are 2D. The only reason we store a Z coordinate
// is for the consistency of storing the model's X, Y, and Z
// all in the same place.
//
// MEMORY MANAGEMENT
//
// PlaneMaps do not own PlaneItems. This is a deliberate choice.
// We assume that sprites will be owned by the sprite ID table.
// So therefore, the PlaneMaps shouldn't own the sprites.
//
// So instead, we use this rule: a PlaneMap has a function 'track'
// that causes it to start tracking the location of a PlaneItem.
// However, the PlaneMap still doesn't own the PlaneItem.
// If you destroy a PlaneMap, all the PlaneItems
// will automatically be untracked, but they won't be deleted.
//
//////////////////////////////////////////////////////////////
#ifndef PLANEMAP_HPP
#define PLANEMAP_HPP
#include "wrap-vector.hpp"
#include "wrap-map.hpp"
#include "wrap-string.hpp"
#include "wrap-bytell-hash-map.hpp"
#include "util.hpp"
#include "luastack.hpp"
#include <cstdint>
#include <memory>
#include <string_view>
class PlaneMap;
class PlaneTree;
class PlaneScan : public eng::nevernew {
public:
friend class PlaneMap;
friend class PlaneTree;
enum Shape { BOX, CYLINDER, SPHEROID };
private:
// The plane to scan.
eng::string plane_;
// The bounding box of the scan.
util::XYZ lo_, hi_, center_;
// If you scan a cylinder or spheroid, it actually
// scans the bounding box first, then clips out
// the parts that aren't correct.
Shape shape_;
// When true, the items are sorted by ID.
// WARNING: setting this to false can create
// nondeterminism. Scans by lua should always be sorted.
bool sorted_;
// The special ID, if nonzero, is either PREPENDED to the
// results, or OMITTED from the results, depending on omit_special.
int64_t special_;
bool omit_special_;
// If this is true, items on the nowhere plane are not scanned.
bool omit_nowhere_;
private:
// Derived variables. These are populated by PlaneTree::scan.
uint64_t bblo_;
uint64_t bbhi_;
public:
PlaneScan() : plane_(""), shape_(BOX), sorted_(true), special_(0), omit_special_(0), omit_nowhere_(false) {}
// Convert a lua table into a scan configuration.
void configure(LuaStack &LS, LuaSlot slot);
// Set the bounding box given two corners.
void set_bbox_given_two_corners(const util::XYZ &a, const util::XYZ &b) {
lo_.x = std::min(a.x, b.x);
lo_.y = std::min(a.y, b.y);
lo_.z = std::min(a.z, b.z);
hi_.x = std::max(a.x, b.x);
hi_.y = std::max(a.y, b.y);
hi_.z = std::max(a.z, b.z);
center_ = (lo_ + hi_) * 0.5;
}
// Set the bounding box given a center and a radius.
void set_bbox_given_center_radius(const util::XYZ &center, float r) {
util::XYZ offset(r, r, r);
lo_ = center - offset;
hi_ = center + offset;
center_ = center;
}
void set_plane(std::string_view p) { plane_ = p; }
void set_shape(Shape s) { shape_ = s; }
void set_sorted(bool s) { sorted_ = s; }
void set_special(int64_t id, bool omit) { special_ = id; omit_special_ = omit; }
void set_omit_nowhere(bool b) { omit_nowhere_ = b; }
};
class PlaneItem : public eng::nevernew {
friend class PlaneTree;
friend class PlaneMap;
private:
PlaneTree *tree_;
PlaneItem *prev_;
PlaneItem *next_;
int64_t id_;
eng::string plane_;
float x_, y_, z_;
public:
PlaneItem();
~PlaneItem();
// You may modify the ID at any time.
void set_id(int64_t id) { id_ = id; }
int64_t id() const { return id_; }
const eng::string &plane() const { return plane_; }
const float x() const { return x_; }
const float y() const { return y_; }
const float z() const { return z_; }
void track(PlaneMap *pmap);
void untrack() { track(nullptr); }
void set_pos(const eng::string &plane, float x, float y, float z);
void set_xyz(float x, float y, float z);
};
class PlaneMap : public eng::nevernew {
friend class PlaneItem;
friend class PlaneTree;
public:
using IdVector = util::IdVector;
private:
float default_radius_;
eng::map<eng::string, std::unique_ptr<PlaneTree>> planes_;
public:
// No special code is needed for construction or destruction.
PlaneMap();
~PlaneMap();
// The 'insert' and 'remove' operators are inside class
// PlaneItem. See PlaneItem::track and PlaneItem::untrack.
// Scan the PlaneMap for items, return their IDs.
IdVector scan(const PlaneScan &s) const;
// Set the default radius for all planes.
// Maybe we'll make it adaptive some day.
void set_default_radius(float r) { default_radius_ = r; }
// Return a debug string for the specified plane.
// This is for unit testing.
eng::string tree_debug_string(const eng::string &plane) const;
eng::string outliers_debug_string(const eng::string &plane) const;
// Untrack all planeitems. This is for unit testing.
void untrack_all();
private:
// unit testing stuff.
friend int lfn_unittests_planemap(lua_State *L);
};
#endif // PLANEMAP_HPP