存储和访问对象位置向量

时间:2016-12-10 12:36:47

标签: c++ glm-math

我有一个班级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:
};

然后我有一个班级Linker,根据他们的位置“链接”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的网格。我想根据他们的位置关联Objects,而不仅仅是allObj[i+1],但我希望能够link Objectvec3 position = <0.0, 1.0, 0.0>一样:

Grid of Objects

我想对其他Object及其邻居做同样的事情。

我的循环现在创建了很少的Objects,但我可能需要稍后创建大量的。{/ p>

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

1 个答案:

答案 0 :(得分:2)

我在this question处理类似的问题。我还写了一个关于我如何解决问题的答案。所以基本上我已经创建了自己的容器,由多个私有容器组成,您可以通过公共方法访问它。在我的情况下,这些是重载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位置直接给出Linker对象。希望这能解决你的问题。

PS:而不是vector<vector<vector...你可以使用简单的vector<unique_ptr<Object>>,但在这种情况下,对于operator(),你会返回类似_data[x + y * width + z * height * width]的内容,但我不太确定它是pos x / y / z上3D矩阵的对象的正确公式。对于2D矩阵,它将是_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) });
}

写这篇文章的时候,我意识到我并不知道你的链接器到底是做什么的,以及将第i个对象与第(i + 1)个对象链接起来的目的是什么将转换为通过X / Y / Z获得它们而不是单个索引。

EDIT2:如果你想像图片一样链接这些对象,那么链接过程将如下所示:

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
      }

这会将每个对象链接到其直接相邻的对象。因此,例如3/4/5处的物体将链接到4/4 / 5,3 / 5/5和3/4/6。

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

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;
};

这将是所述类的用法,用于执行代码示例所做的一切:

// 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);