所以我得到的是Grid类和Tile类。 ATM网格包含Tiles(vector<vector<Tile>>
)的二维向量。这些Tiles包含有关x,y和z的信息(这是一个自上而下的地图)和f.e.侵蚀率等。
我的问题在于我需要通过x / y坐标有效地访问这些图块,从所有z坐标中找到具有中值(或其他0到1值,中值为0.5)值的图块(设置海)水平)并且从最高z到最低点遍历所有这些(用于创建侵蚀图。
你会建议最好的数据结构是什么,所以我可以有效地做我上面列出的所有内容,如果我后来发现我需要它,也许还有别的东西。现在我只是创建一个临时排序的结构或地图来做事情,将所有的瓷砖复制到它并使用它,这真的很慢。
我考虑过的选项是没有直接访问权限的地图,也总是按照x / y难以选择的方式进行分类。
然后是一个允许直接访问的向量,但如果我要对切片进行排序,直接访问将毫无意义,因为向量中的Tile的位置将与它的x + y *宽度相同。
这是一个小样本代码:
Class Grid {
public:
Class Tile {
unsigned x;
unsigned y;
float z; // used for drawing height map
static float seaLevel; // static value for all the tiles
unsigned erosionLevel; //used for drawing erosion map
void setSeaLevel(float pos) {
// set seaLevel to z of tile on pos from 0 to 1 in tile grid
}
void generateErosionMap() {
// loop thorugh all tiles from highest z to lowest z and set their erosion
}
void draw() {
// loop through all tiles by their x/y and draw them
}
vector<vector<Tile>> tileGrid;
}
答案 0 :(得分:1)
C ++库提供了一组基本容器。每个容器都经过优化,以便以特定方式进行访问。
当您需要能够以不同方式最佳地访问同一组数据时,执行此操作的方法是将多个容器组合在一起,所有容器都引用相同的基础数据,每个容器用于定位以特定方式单个数据块。
以我们的两个要求为例:
根据X和Y坐标找到Grid
对象,
通过z坐标以单调递增或递减的顺序迭代所有Grid
。
我们可以通过使用简单的二维向量来实现第一个要求:
typedef std::vector<std::vector<std::shared_ptr<Grid>>> lookup_by_xy_t;
lookup_by_xy_t lookup_by_xy;
从表面上看,这是相当明显的。但请注意,向量不会存储实际的Grid
,而是存储std::shared_ptr
这些对象。如果您不熟悉std::shared_ptr
,请阅读它们,并了解它们是什么。
这是非常基本的:你构建一个新的Grid
:
auto g = std::make_shared<Grid>( /* arguments to Grid's constructor */);
// Any additional initialization...
//
// g->foo(); g->bar=4;
//
// etc...
并将其插入查找向量中:
lookup_by_xy[g->x][g->y]=g;
现在,我们处理您的第二个要求:能够通过z
坐标迭代所有这些对象:
typedef std::multimap<double, std::shared_ptr<Grid>> lookup_by_z_t;
lookup_by_z_t lookup_by_z;
这假设您的z
坐标是double
。默认情况下,multimap
将根据密钥从最低到最高密钥以严格的弱顺序迭代其内容。您可以向后迭代地图,或者使用适当的比较类与multimap,从最高值到最低值对其键进行排序。
现在,只需将相同的std::shared_ptr
插入此查找容器中:
lookup_by_z.insert(std::make_pair(g->z, g));
现在,您可以通过其x / y坐标找到每个Grid
对象,或者通过z坐标迭代所有对象。二维向量和多图都包含shared_ptr
个相同的Grid
个对象。任何一个都可以用来访问它们。
根据需要简单地创建其他容器,以不同的方式访问相同的底层对象。
现在,当然,所有这些额外的框架确实在动态内存分配和每个容器本身的开销方面施加了一些额外的开销。天下没有免费的午餐。如果原始数据量成为问题,则可能需要自定义分配器。
答案 1 :(得分:0)
所以在我的大学上提出这个问题并得到更深入的解释之后,我已经找到了这个解决方案 如果您需要一个需要各种访问方法的数据结构(例如在我的情况下通过x / y直接访问,通过排序z进行线性访问等),最佳解决方案是让您拥有自己的类来处理它。同样使用 shared_ptr 比uniqu_ptr慢得多,除非必要,否则不应使用 。所以在我的情况下,实现看起来像这样:
#ifndef TILE_GRID_H
#define TILE_GRID_H
#include "Tile.h"
#include <memory>
#include <vector>
using Matrix = std::vector<std::vector<std::unique_ptr<Tile>>>;
using Sorted = std::vector<Tile*>;
class TileGrid {
public:
TileGrid(unsigned w, unsigned h) : width(w), height(h) {
// Resize _dA to desired size
_directAccess.resize(height);
for (unsigned j = 0; j < height; ++j)
for (unsigned i = 0; i < width; ++i)
_directAccess[j].push_back(std::make_unique<Tile>(i, j));
// Link _sZ to _dA
for (auto& i : _directAccess)
for (auto& j : i)
_sortedZ.push_back(j.get());
}
// Sorts the data by it's z value
void sortZ() {
std::sort(_sortedZ.begin(), _sortedZ.end(), [](Tile* a, Tile* b) { return b->z < a->z; });
}
// Operator to read directly from this container
Tile& operator()(unsigned x, unsigned y) {
return *_directAccess[y][x];
}
// Operator returning i-th position from sorted tiles (in my case used for setting sea level)
Tile& operator()(float level) {
level = fmax(fmin(level, 1), 0);
return *_sortedZ[width * height * level];
}
// Iterators
auto begin() { return _sortedZ.begin(); }
auto end() { return _sortedZ.end(); }
auto rbegin() { return _sortedZ.rbegin(); }
auto rend() { return _sortedZ.rend(); }
const unsigned width; // x dimensoin
const unsigned height; // y dimension
private:
Matrix _directAccess;
Sorted _sortedZ;
};
#endif // TILE_GRID_H
您也可以使用模板,但在我的情况下,我只需要这个用于Tile类。如您所见,主_directAccess矩阵包含所有unique_ptr,而_sortedZ仅包含存储在_dA中的数据的原始指针。由于这些指针与一个类绑定,并且所有指针同时被删除,因此速度更快,也更安全。我还添加了重载()运算符来访问数据并重用_sortedZ向量中的迭代器。同样,宽度和高度是const只是因为这个数据结构的预期用途(不可调整大小,不可移动的瓷砖等)。
如果您对要改进的内容有任何疑问或建议,请随时发表评论。