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<std::map<u32, Area>::iterator, bool> res =
187 areas_map.insert(std::make_pair(a->id, *a));
191 m_areas.push_back(&res.first->second);
196 void VectorAreaStore::reserve(size_t count)
198 m_areas.reserve(count);
201 bool VectorAreaStore::removeArea(u32 id)
203 std::map<u32, Area>::iterator itr = areas_map.find(id);
204 if (itr != areas_map.end()) {
205 size_t msiz = m_areas.size();
206 for (size_t i = 0; i < msiz; i++) {
207 Area * b = m_areas[i];
209 areas_map.erase(itr);
210 m_areas.erase(m_areas.begin() + i);
215 // we should never get here, it means we did find it in map,
216 // but not in the vector
221 void VectorAreaStore::getAreasForPosImpl(std::vector<Area *> *result, v3s16 pos)
223 size_t msiz = m_areas.size();
224 for (size_t i = 0; i < msiz; i++) {
225 Area *b = m_areas[i];
226 if (AST_CONTAINS_PT(b, pos)) {
227 result->push_back(b);
232 void VectorAreaStore::getAreasInArea(std::vector<Area *> *result,
233 v3s16 minedge, v3s16 maxedge, bool accept_overlap)
235 size_t msiz = m_areas.size();
236 for (size_t i = 0; i < msiz; i++) {
237 Area * b = m_areas[i];
238 if (accept_overlap ? AST_AREAS_OVERLAP(minedge, maxedge, b) :
239 AST_CONTAINS_AREA(minedge, maxedge, b)) {
240 result->push_back(b);
246 bool VectorAreaStore::forEach(bool (*callback)(void *args, Area *a), void *args) const
248 size_t msiz = m_areas.size();
249 for (size_t i = 0; i < msiz; i++) {
250 if (callback(args, m_areas[i])) {
260 static inline SpatialIndex::Region get_spatial_region(const v3s16 minedge,
263 const double p_low[] = {(double)minedge.X,
264 (double)minedge.Y, (double)minedge.Z};
265 const double p_high[] = {(double)maxedge.X, (double)maxedge.Y,
267 return SpatialIndex::Region(p_low, p_high, 3);
270 static inline SpatialIndex::Point get_spatial_point(const v3s16 pos)
272 const double p[] = {(double)pos.X, (double)pos.Y, (double)pos.Z};
273 return SpatialIndex::Point(p, 3);
277 bool SpatialAreaStore::insertArea(Area *a)
280 if (!areas_map.insert(std::make_pair(a->id, *a)).second)
283 m_tree->insertData(0, NULL, get_spatial_region(a->minedge, a->maxedge), a->id);
288 bool SpatialAreaStore::removeArea(u32 id)
290 std::map<u32, Area>::iterator itr = areas_map.find(id);
291 if (itr != areas_map.end()) {
292 Area *a = &itr->second;
293 bool result = m_tree->deleteData(get_spatial_region(a->minedge,
302 void SpatialAreaStore::getAreasForPosImpl(std::vector<Area *> *result, v3s16 pos)
304 VectorResultVisitor visitor(result, this);
305 m_tree->pointLocationQuery(get_spatial_point(pos), visitor);
308 void SpatialAreaStore::getAreasInArea(std::vector<Area *> *result,
309 v3s16 minedge, v3s16 maxedge, bool accept_overlap)
311 VectorResultVisitor visitor(result, this);
312 if (accept_overlap) {
313 m_tree->intersectsWithQuery(get_spatial_region(minedge, maxedge),
316 m_tree->containsWhatQuery(get_spatial_region(minedge, maxedge), visitor);
321 bool SpatialAreaStore::forEach(bool (*callback)(void *args, Area *a), void *args) const
323 // TODO ?? (this is only needed for serialisation, but libspatial has its own serialisation)
328 SpatialAreaStore::~SpatialAreaStore()
333 SpatialAreaStore::SpatialAreaStore()
336 SpatialIndex::StorageManager::createNewMemoryStorageManager();
337 SpatialIndex::id_type id;
338 m_tree = SpatialIndex::RTree::createNewRTree(
341 100, // Index capacity
342 100, // Leaf capacity
344 SpatialIndex::RTree::RV_RSTAR,