3 Copyright (C) 2015 est31 <mtest31@outlook.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "util/areastore.h"
21 #include "util/serialize.h"
22 #include "util/container.h"
25 #include <spatialindex/SpatialIndex.h>
26 #include <spatialindex/RTree.h>
27 #include <spatialindex/Point.h>
30 #define AST_SMALLER_EQ_AS(p, q) (((p).X <= (q).X) && ((p).Y <= (q).Y) && ((p).Z <= (q).Z))
32 #define AST_OVERLAPS_IN_DIMENSION(amine, amaxe, b, d) \
33 (!(((amine).d > (b)->maxedge.d) || ((amaxe).d < (b)->minedge.d)))
35 #define AST_CONTAINS_PT(a, p) (AST_SMALLER_EQ_AS((a)->minedge, (p)) && \
36 AST_SMALLER_EQ_AS((p), (a)->maxedge))
38 #define AST_CONTAINS_AREA(amine, amaxe, b) \
39 (AST_SMALLER_EQ_AS((amine), (b)->minedge) \
40 && AST_SMALLER_EQ_AS((b)->maxedge, (amaxe)))
42 #define AST_AREAS_OVERLAP(amine, amaxe, b) \
43 (AST_OVERLAPS_IN_DIMENSION((amine), (amaxe), (b), X) && \
44 AST_OVERLAPS_IN_DIMENSION((amine), (amaxe), (b), Y) && \
45 AST_OVERLAPS_IN_DIMENSION((amine), (amaxe), (b), Z))
48 AreaStore *AreaStore::getOptimalImplementation()
51 return new SpatialAreaStore();
53 return new VectorAreaStore();
57 u16 AreaStore::size() const
59 return areas_map.size();
62 const Area *AreaStore::getArea(u32 id) const
64 const Area *res = NULL;
65 std::map<u32, Area>::const_iterator itr = areas_map.find(id);
66 if (itr != areas_map.end()) {
73 Currently, serialisation is commented out. This is because of multiple reasons:
74 1. Why do we store the areastore into a file, why not into the database?
75 2. We don't use libspatial's serialisation, but we should, or perhaps not, because
76 it would remove the ability to switch. Perhaps write migration routines?
77 3. Various things need fixing, e.g. the size is serialized as
78 c++ implementation defined size_t
79 bool AreaStore::deserialize(std::istream &is)
84 u16 count_areas = readU16(is);
85 for (u16 i = 0; i < count_areas; i++) {
86 // deserialize an area
89 a.minedge = readV3S16(is);
90 a.maxedge = readV3S16(is);
91 a.datalen = readU16(is);
92 a.data = new char[a.datalen];
93 is.read((char *) a.data, a.datalen);
100 static bool serialize_area(void *ostr, Area *a)
102 std::ostream &os = *((std::ostream *) ostr);
104 writeV3S16(os, a->minedge);
105 writeV3S16(os, a->maxedge);
106 writeU16(os, a->datalen);
107 os.write(a->data, a->datalen);
113 void AreaStore::serialize(std::ostream &os) const
115 // write initial data
116 writeU8(os, 1); // serialisation version
117 writeU16(os, areas_map.size()); //DANGER: not platform independent
118 forEach(&serialize_area, &os);
123 void AreaStore::invalidateCache()
125 if (m_cache_enabled) {
126 m_res_cache.invalidate();
130 void AreaStore::setCacheParams(bool enabled, u8 block_radius, size_t limit)
132 m_cache_enabled = enabled;
133 m_cacheblock_radius = MYMAX(block_radius, 16);
134 m_res_cache.setLimit(MYMAX(limit, 20));
138 void AreaStore::cacheMiss(void *data, const v3s16 &mpos, std::vector<Area *> *dest)
140 AreaStore *as = (AreaStore *)data;
141 u8 r = as->m_cacheblock_radius;
143 // get the points at the edges of the mapblock
144 v3s16 minedge(mpos.X * r, mpos.Y * r, mpos.Z * r);
150 as->getAreasInArea(dest, minedge, maxedge, true);
152 /* infostream << "Cache miss with " << dest->size() << " areas, between ("
153 << minedge.X << ", " << minedge.Y << ", " << minedge.Z
155 << maxedge.X << ", " << maxedge.Y << ", " << maxedge.Z
156 << ")" << std::endl; // */
159 void AreaStore::getAreasForPos(std::vector<Area *> *result, v3s16 pos)
161 if (m_cache_enabled) {
162 v3s16 mblock = getContainerPos(pos, m_cacheblock_radius);
163 const std::vector<Area *> *pre_list = m_res_cache.lookupCache(mblock);
165 size_t s_p_l = pre_list->size();
166 for (size_t i = 0; i < s_p_l; i++) {
167 Area *b = (*pre_list)[i];
168 if (AST_CONTAINS_PT(b, pos)) {
169 result->push_back(b);
173 return getAreasForPosImpl(result, pos);
183 bool VectorAreaStore::insertArea(Area *a)
186 std::pair<AreaMap::iterator, bool> res =
187 areas_map.insert(std::make_pair(a->id, *a));
191 m_areas.push_back(&res.first->second);
196 bool VectorAreaStore::removeArea(u32 id)
198 AreaMap::iterator it = areas_map.find(id);
199 if (it == areas_map.end())
201 Area *a = &it->second;
202 for (std::vector<Area *>::iterator v_it = m_areas.begin();
203 v_it != m_areas.end(); ++v_it) {
214 void VectorAreaStore::getAreasForPosImpl(std::vector<Area *> *result, v3s16 pos)
216 for (size_t i = 0; i < m_areas.size(); ++i) {
217 Area *b = m_areas[i];
218 if (AST_CONTAINS_PT(b, pos)) {
219 result->push_back(b);
224 void VectorAreaStore::getAreasInArea(std::vector<Area *> *result,
225 v3s16 minedge, v3s16 maxedge, bool accept_overlap)
227 for (size_t i = 0; i < m_areas.size(); ++i) {
228 Area *b = m_areas[i];
229 if (accept_overlap ? AST_AREAS_OVERLAP(minedge, maxedge, b) :
230 AST_CONTAINS_AREA(minedge, maxedge, b)) {
231 result->push_back(b);
237 bool SimpleAreaStore::forEach(bool (*callback)(void *args, Area *a), void *args) const
239 for (size_t i = 0; i < m_areas.size(); ++i) {
240 if (callback(m_areas[i], arg)) {
250 static inline SpatialIndex::Region get_spatial_region(const v3s16 minedge,
253 const double p_low[] = {(double)minedge.X,
254 (double)minedge.Y, (double)minedge.Z};
255 const double p_high[] = {(double)maxedge.X, (double)maxedge.Y,
257 return SpatialIndex::Region(p_low, p_high, 3);
260 static inline SpatialIndex::Point get_spatial_point(const v3s16 pos)
262 const double p[] = {(double)pos.X, (double)pos.Y, (double)pos.Z};
263 return SpatialIndex::Point(p, 3);
267 bool SpatialAreaStore::insertArea(Area *a)
270 if (!areas_map.insert(std::make_pair(a->id, *a)).second)
273 m_tree->insertData(0, NULL, get_spatial_region(a->minedge, a->maxedge), a->id);
278 bool SpatialAreaStore::removeArea(u32 id)
280 std::map<u32, Area>::iterator itr = areas_map.find(id);
281 if (itr != areas_map.end()) {
282 Area *a = &itr->second;
283 bool result = m_tree->deleteData(get_spatial_region(a->minedge,
292 void SpatialAreaStore::getAreasForPosImpl(std::vector<Area *> *result, v3s16 pos)
294 VectorResultVisitor visitor(result, this);
295 m_tree->pointLocationQuery(get_spatial_point(pos), visitor);
298 void SpatialAreaStore::getAreasInArea(std::vector<Area *> *result,
299 v3s16 minedge, v3s16 maxedge, bool accept_overlap)
301 VectorResultVisitor visitor(result, this);
302 if (accept_overlap) {
303 m_tree->intersectsWithQuery(get_spatial_region(minedge, maxedge),
306 m_tree->containsWhatQuery(get_spatial_region(minedge, maxedge), visitor);
311 bool SpatialAreaStore::forEach(bool (*callback)(void *args, Area *a), void *args) const
313 // TODO ?? (this is only needed for serialisation, but libspatial has its own serialisation)
318 SpatialAreaStore::~SpatialAreaStore()
323 SpatialAreaStore::SpatialAreaStore()
326 SpatialIndex::StorageManager::createNewMemoryStorageManager();
327 SpatialIndex::id_type id;
328 m_tree = SpatialIndex::RTree::createNewRTree(
331 100, // Index capacity
332 100, // Leaf capacity
334 SpatialIndex::RTree::RV_RSTAR,