Я имел дело с аналогичной проблемой в this question. Я также написал ответ о том, как я решил свою проблему. Поэтому в основном я создал свой собственный контейнер, состоящий из нескольких частных, к которым вы можете обращаться по лобовым методам. В моем случае это перегруженный оператор() для прямого доступа 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. Надеюсь, это решает вашу проблему.
PS: Вместо vector<vector<vector...
вы можете использовать простой vector<unique_ptr<Object>>
, но в этом случае для оператора() вы бы вернуть что-то вроде _data[x + y * width + z * height * width]
, но я не совсем уверен, если это оно правильная формула для объекта из 3D матрицы на пос х/г/г. Для 2D-матрицы было бы _data[x + y * width]
EDIT: Реализация:
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);
Из того, что я понимаю, что вы хотите эффективный способ, чтобы найти объект на определенный должность? –
Точно. Я хотел бы иметь возможность сказать: link obj к объекту x + 1, z + 1 и т. Д. – Koosshh56
Самый простой способ, который до сих пор очень эффективен, - это std :: map. Используйте позицию как ключ и объект как значение. Однако, если у вас нет разреженной сетки (каждая позиция содержит объект), вы можете просто использовать многомерный массив. –