存储和访问对象位置向量

Storing and accessing Object position vector

我有一个 class Object,它有一个 vec3 属性来存储它的位置

class Object{
public:
  Object();
  ~Object();
  glm::vec3 position;
  virtual float getX();  //these methods get the x, y and z value of the vec3 position
  virtual float getY();
  virtual float getZ();

private:
};

然后我有一个 class Linker,根据他们的位置 "link" Objects

class Linker
{
    Object *obj;
public:
    Linker(Object *obj);
    virtual void link(Object *o);  //this method should perform actions based on Object position
};

在我的 main.cpp 中,我创建了一些 Objects 并将它们存储在 std::vector

static vector<unique_ptr<Object>> allObj;
static vector<Linker> allLinkers;

unique_ptr<Object> createNewObj(int x, int y, int z) {
    unique_ptr<Object> obj(new Object());
    obj->position(vec3(x, y, z));
    return obj;
    }

void createPattern()
{
    for (int x = 0; x < 3; x++)
    {
        for (int z = 0; z < 3; z++)
        {
             allObj.push_back(createNewObj(x, 1.0f, z));
        }
    }

    for (auto &p : allObj) {
        Linker lnkr = Linker(p);
        //EDIT
        allLinkers.push_back(lnkr);
    }
}

void linkPattern()
{
    for (int i = 0; i < allObj.size() - 1; i++)
    {
        auto p = allObj[i+1];    //this is where my problem comes up
        allLinkers[i].link(p);   //takes the linker of the first object and "links" it with the second
    }
}

createPattern() 中的嵌套循环创建了 Objects 的网格。我想 link Objects 基于他们的位置,而不仅仅是 allObj[i+1],但我希望能够 link Objectvec3 position = <0.0, 1.0, 0.0>喜欢:

我想对所有其他 Object 及其邻居也这样做。

我的循环目前创建的很少 Objects,但我以后可能需要创建大量它们。

在这种情况下,std::vector 是存储 Objects 的最佳方式吗? 有没有办法存储它们以便我可以通过它们的位置直接访问它们?

我在 中处理过类似的问题。我还写了一个关于我如何解决问题的答案。所以基本上我已经创建了我自己的容器,其中包含多个私有容器,您可以通过公共方法访问这些容器。在我的例子中,这些被重载 operator() 用于直接 X/Y 访问。在您的情况下,保存数据的基本结构将是唯一指针的复合向量,对于直接访问,您可以重载 operator()(unsigned x, unsigned y, unsigned z) 看起来像这样:

class Grid {
public:
   Object& operator()(unsigned x, unsigned y, unsigned z) noexcept {
      return *_data[z][y][x];
   }

   // same method returning const reference(read-only)
   const Object& operator()(unsigned x, unsigned y, unsigned z) const noexcept {
      return *_data[z][y][x];
   }

   /* Safer but throws std::out_of_range exception, which you should handle
   Object& operator()(unsigned x, unsigned y, unsigned z) {
      return *_data.at(z).at(y).at(z);
   }
   */
private:
   vector<vector<vector<unique_ptr<Object> > > > _data;
}

这样您就可以直接通过 X/Y/Z 位置给出链接器对象。希望这能解决您的问题。

P.S.: 你可以使用简单的 vector<unique_ptr<Object>> 而不是 vector<vector<vector... 但在那种情况下,对于 operator() 你会 return 像 _data[x + y * width + z * height * width]但我不太确定这是否是 pos x/y/z 上 3D 矩阵对象的正确公式。对于二维矩阵,它将是 _data[x + y * width]

编辑:实施:

class Grid {
public:
   // Constructor fills the Grid with Objects created from theirs default constructor
   Grid(unsigned width, unsigned height, unsigned depth)
      : _width(width), _height(height), _depth(depth) {
      _data.resize(depth);
      for (unsigned i = 0; i < depth; ++i) {
         _data[i].resize(height);
         for (unsigned j = 0; i < height; ++i)
            _data[i][j].push_back(make_unique<Object>()); 
            // Calls a consturctor of Object
            // If you don't plan on filling every single position you can instead fill it with nullptr to save memory
      }
   }

   Object& operator()(unsigned x, unsigned y, unsigned z) {
      return *_data[z][y][x];
   }

   unsigned size() { return _width * _height * _depth; }    
   unsigned width() { return _width; }    
   unsigned height() { return _height; }    
   unsigned depth() { return _depth; }

private:
   vector<vector<vector<unique_ptr<Object> > > > _data;
   unsigned _width;
   unsigned _height;
   unsigned _depth;
}

static Grid allObj(width, height, depth);
static vector<Linker> allLinkers;

unique_ptr<Object> createNewObj(int x, int y, int z) {
    unique_ptr<Object> obj(new Object());
    obj->position(vec3(x, y, z));
    return obj;
    }

void createPattern()
{
    // no need for inserting because Objects are created on allObj creation

    // changed the iterator based range for to normal for loops
    for (unsigned k = 0; k < allObj.depth - 1; ++k)
       for (unsigned j = 0; j < allObj.height - 1; ++j)
          for (unsigned i = 0; i < allObj.width - 1; ++i)
             Linker.push_back({ allObj(i, j, k) });
}

在写这篇文章的时候我意识到我真的不知道你的 linker 到底是做什么的以及 link 使用 (i+) 第 i 个对象的目的是什么1)-th 对象以及它将如何转换为通过 X/Y/Z 而不是单个索引获取它们。

EDIT2:如果您想要 link 这些对象,如图所示,那么 linking 过程将如下所示:

for (unsigned k = 0; k < allObj.depth - 1; ++k)
   for (unsigned j = 0; j < allObj.height - 1; ++j)
      for (unsigned i = 0; i < allObj.width - 1; ++i) {
         auto p = allObj(i + 1, j, k);
         allLinkers[i].link(p);
         p = allObj(i, j + 1, k);
         allLinkers[i].link(p);
         p = allObj(i, j, k + 1);
         allLinkers[i].link(p);
         // and continue with whatever else you want to link
         // as you can see this is quite unefective so maybe modifying link method
         // so it takes no parameters and automatically links all neighbouring objects would be better
      }

这将 link 每个对象与其直接相邻的对象。因此,例如 3/4/5 处的对象将 linked 为 4/4/5、3/5/5 和 3/4/6。

EDIT3:简化了程序结构。将所有功能放入网格 class。这是代码:

class Grid {
public:
    // Create a grid with set width, height and depth
    Grid(unsigned width, unsigned height, unsigned depth)
            : _width(width), _height(height), _depth(depth) {

        // This replaces the createPattern function
        // Creates both objects and linkers
        for (unsigned i = 0; i < size(); ++i) {
            _objects.push_back(make_unique<Object>());
            _linkers.push_back({ _objects[i].get() });
        }

        // This replaces the linkPattern function
        // Does the linking exactly as shown on the picture
        for (unsigned i = 0; i < size(); ++i) {
            _linkers[i].link(&object(_objects[i]->getX(), _objects[i]->getY(), _objects[i]->getZ() + 1));
            _linkers[i].link(&object(_objects[i]->() + 1, _objects[i]->getY(), _objects[i]->getZ()));
            _linkers[i].link(&object(_objects[i]->getX() + 1, _objects[i]->getY(), _objects[i]->getZ() + 1));
        }
    }

    // Direct access operator
    Object& object(unsigned x, unsigned y, unsigned z) noexcept {
        return *_objects[x + y * _width + z * _height * _width];
    }

    // another possible implementation of Direct access operator
    // checks if element you want is 'in range'
    // NOTE: may throw std::out_of_range
    const Object& operator()(unsigned x, unsigned y, unsigned z) const {
        size_t position = x + y * _width + z * _height * _width;
        if (position >= _objects.size() || x > _width || y > _height || z > _depth)
            throw std::out_of_range("index is out of range");
        return *_objects[x + y * _width + z * _height * _width];
    }

    // Direct access for linkers
    Linker& linker(unsigned x, unsigned y, unsigned z) noexcept {
        return _linkers[x + y * _width + z * _height * _width];
    }

    // Getters
    constexpr unsigned size() const noexcept { return _width * _height * _depth; }
    constexpr unsigned width() const noexcept { return _width; }
    constexpr unsigned height() const noexcept { return _height; }
    constexpr unsigned depth() const noexcept { return _depth; }

    // Iterators - using ranged for would loop throught all the Objects from _objects

    using iterator = std::vector<unique_ptr<Object> >::iterator;
    using const_iterator = std::vector<unique_ptr<Object> >::const_iterator;
    using reverse_iterator = std::vector<unique_ptr<Object> >::reverse_iterator;
    using const_reverse_iterator = std::vector<unique_ptr<Object> >::const_reverse_iterator;

    iterator begin() noexcept { return _objects.begin(); }
    const_iterator begin() const noexcept { return _objects.begin(); }

    iterator end() noexcept { return _objects.end(); }
    const_iterator end() const noexcept { return _objects.end(); }

    reverse_iterator rbegin() noexcept { return _objects.rbegin(); }
    const_reverse_iterator rbegin() const noexcept { return _objects.rbegin(); }

    reverse_iterator rend() noexcept { return _objects.rend(); }
    const_reverse_iterator rend() const noexcept { return _objects.rend(); }

private:
    vector<Linker> _linkers;
    vector<unique_ptr<Object> > _objects;
    const unsigned _width;
    const unsigned _height;
    const unsigned _depth;
};

这就是所说 class 执行您的代码示例所做的一切的用法:

// The grid containing all the objects and linkers
Grid allObj(3, 1, 3);

// You can access objects like this
allObj.object(x, y, z);

// or like this (returns const& (read-only))
allObj(x, y, z);

// Likewise the linker
allObj.linker(x, y, z);