Change directory structure
This commit is contained in:
247
luprex/cpp/core/planemap.hpp
Normal file
247
luprex/cpp/core/planemap.hpp
Normal file
@@ -0,0 +1,247 @@
|
||||
//////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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, SPHERE };
|
||||
private:
|
||||
// The plane to scan.
|
||||
eng::string plane_;
|
||||
|
||||
// The bounding box of the scan.
|
||||
util::XYZ center_, radius_;
|
||||
|
||||
// If you scan a cylinder or SPHERE, 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 near ID, if nonzero, is either PREPENDED to the
|
||||
// results, or OMITTED from the results, depending on include_near_.
|
||||
int64_t near_;
|
||||
bool include_near_;
|
||||
|
||||
// If this is true, items on the nowhere plane are not scanned.
|
||||
bool omit_nowhere_;
|
||||
|
||||
public:
|
||||
void clear() {
|
||||
plane_ = "";
|
||||
shape_ = BOX;
|
||||
sorted_ = true;
|
||||
near_ = 0;
|
||||
include_near_ = false;
|
||||
omit_nowhere_ = false;
|
||||
radius_ = center_ = util::XYZ();
|
||||
}
|
||||
PlaneScan() { clear(); }
|
||||
|
||||
// Convert a lua table into a scan configuration.
|
||||
//
|
||||
// If there is an error in the configuration,
|
||||
// throws a lua error message.
|
||||
//
|
||||
// Caution: if this detects the configuration flag 'near',
|
||||
// then it stores the near ID. However, it doesn't fetch
|
||||
// the center and plane. It is the caller's responsibility
|
||||
// to check if 'near' has been set, and if so, store a center
|
||||
// and plane. This is admittedly ugly, but it eliminates
|
||||
// a dependency on the world module.
|
||||
//
|
||||
void configure(LuaKeywordParser &kw);
|
||||
|
||||
void set_bbox_given_center_radius(const util::XYZ ¢er, float r) {
|
||||
set_center(center);
|
||||
set_radius(r);
|
||||
}
|
||||
|
||||
void set_whole_plane() {
|
||||
shape_ = BOX;
|
||||
center_ = util::XYZ();
|
||||
radius_.x = std::numeric_limits<float>::infinity();
|
||||
radius_.y = radius_.z = radius_.x;
|
||||
}
|
||||
|
||||
void set_center(const util::XYZ ¢er) { center_ = center; }
|
||||
void set_radius(const util::XYZ &radius) { radius_ = radius; }
|
||||
void set_radius(float f) { radius_.x = radius_.y = radius_.z = f; }
|
||||
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_near(int64_t id, bool inc) { near_ = id; include_near_ = inc; }
|
||||
void set_omit_nowhere(bool b) { omit_nowhere_ = b; }
|
||||
|
||||
int64_t near() const { return near_; }
|
||||
|
||||
// Reveal the contents of the PlaneScan as a string.
|
||||
eng::string debug_string() const;
|
||||
};
|
||||
|
||||
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);
|
||||
eng::string outliers_debug_string(const eng::string &plane);
|
||||
eng::string search_bboxes_debug_string(const PlaneScan &scan);
|
||||
eng::string scan_steps_debug_string(const PlaneScan &scan);
|
||||
|
||||
// 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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user