[pal] Bring back refined upstream version of superliminal rtree index

Turns out this index is MUCH (magnitudes) faster for use in pal. So
grab an updated version of the upstream library and place in external libs,
and use this for indices in pal.

(we should probably investigate whether this is faster for snapping and
other index use too!)
This commit is contained in:
Nyall Dawson 2019-12-14 12:09:46 +10:00
parent 7464290ae3
commit da5cd52162
22 changed files with 2352 additions and 27 deletions

0
external/rtree/.Rhistory vendored Normal file
View File

26
external/rtree/CMakeLists.txt vendored Normal file
View File

@ -0,0 +1,26 @@
# CMake version check
cmake_minimum_required(VERSION 3.10.0)
# Check build directory
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR)
message(FATAL_ERROR "Do not build in-source. Please remove CMakeCache.txt and the CMakeFiles/ directory. Then build out-of-source.")
endif()
# RTree project
project(RTree
VERSION 0.1.0)
# Project build options
option(RTREE_BUILD_TESTS "Build test programs" OFF)
# RTree as header only library
add_library(RTree INTERFACE)
target_include_directories(RTree
INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include)
target_compile_features(RTree
INTERFACE cxx_std_11)
# Tests
if (RTREE_BUILD_TESTS)
add_subdirectory(tests)
endif()

56
external/rtree/CMakeSettings.json vendored Normal file
View File

@ -0,0 +1,56 @@
{
"configurations": [
{
"name": "x64-Debug",
"generator": "Ninja",
"configurationType": "Debug",
"inheritEnvironments": [
"msvc_x64_x64"
],
"buildRoot": "${projectDir}\\build\\${name}",
"installRoot": "${projectDir}\\install\\${name}",
"cmakeCommandArgs": "-Wno-dev -DRTREE_BUILD_TESTS=ON",
"buildCommandArgs": "-v",
"ctestCommandArgs": ""
},
{
"name": "x64-Release",
"generator": "Ninja",
"configurationType": "RelWithDebInfo",
"inheritEnvironments": [
"msvc_x64_x64"
],
"buildRoot": "${projectDir}\\build\\${name}",
"installRoot": "${projectDir}\\install\\${name}",
"cmakeCommandArgs": "-Wno-dev -DRTREE_BUILD_TESTS=ON",
"buildCommandArgs": "-v",
"ctestCommandArgs": ""
},
{
"name": "x86-Debug",
"generator": "Ninja",
"configurationType": "Debug",
"inheritEnvironments": [
"msvc_x86"
],
"buildRoot": "${projectDir}\\build\\${name}",
"installRoot": "${projectDir}\\install\\${name}",
"cmakeCommandArgs": "-Wno-dev -DRTREE_BUILD_TESTS=ON",
"buildCommandArgs": "-v",
"ctestCommandArgs": ""
},
{
"name": "x86-Release",
"generator": "Ninja",
"configurationType": "RelWithDebInfo",
"inheritEnvironments": [
"msvc_x86"
],
"buildRoot": "${projectDir}\\build\\${name}",
"installRoot": "${projectDir}\\install\\${name}",
"cmakeCommandArgs": "-Wno-dev -DRTREE_BUILD_TESTS=ON",
"buildCommandArgs": "-v",
"ctestCommandArgs": ""
}
]
}

84
external/rtree/README.md vendored Normal file
View File

@ -0,0 +1,84 @@
# R-Trees: A Dynamic Index Structure for Spatial Searching
## Description
A C++ templated version of [this](http://www.superliminal.com/sources/sources.htm)
RTree algorithm.
The code it now generally compatible with the STL and Boost C++ libraries.
## Usage
```cpp
#include <RTree.h>
// ...
RTree<Foo*, double, 3> tree;
double min[3] = {0., 0., 0.};
double max[3] = {1., 1., 1.};
Foo* bar = new Foo();
tree.Insert(min, max, bar);
```
Provides search in and iteration over the tree. For examples see
[Test.cpp](https://github.com/nushoin/RTree/blob/master/Test.cpp)
## Testing
Run `make` to build and `make test` to run the tests. The RTree itself is
a single header file and can be included without compiling.
## Authors
- 1983 Original algorithm and test code by Antonin Guttman and Michael Stonebraker, UC Berkely
- 1994 ANCI C ported from original test code by Melinda Green - melinda@superliminal.com
- 1995 Sphere volume fix for degeneracy problem submitted by Paul Brook
- 2004 Templated C++ port by Greg Douglas
- 2011 Modified the container to support more data types, by Yariv Barkan
- 2017 Modified Search to take C++11 function to allow lambdas and added const qualifier, by Gero Mueller
## License
Original code was taken from http://www.superliminal.com/sources/sources.htm
and is stored as git revision 0. This revision is entirely free for all
uses. Enjoy!
Due to restrictions on public domain in certain jurisdictions, code
contributed by Yariv Barkan is released in these jurisdictions under the
BSD, MIT or the GPL - you may choose one or more, whichever that suits you
best.
In jurisdictions where public domain property is recognized, the user of
this software may choose to accept it either 1) as public domain, 2) under
the conditions of the BSD, MIT or GPL or 3) any combination of public
domain and one or more of these licenses.
Thanks [Baptiste Lepilleur](http://jsoncpp.sourceforge.net/LICENSE) for the
licensing idea.
## Recent Change Log
### 31 Jan 2018
- Added copy constructor
- Callback function is now `std::function`
### 05 Apr 2014
- Added tests
### 02 Sep 2011
- Modified the container to support more data types. The code it now generally
compatible with the STL and Boost C++ libraries.
### 05 Jan 2010
- Fixed Iterator GetFirst() - Previous fix was not incomplete
### 03 Dec 2009
- Added Iteartor GetBounds()
- Added Iterator usage to simple test
- Fixed Iterator GetFirst() - Thanks Mathew Riek
- Minor updates for MSVC 2005/08 compilers

1331
external/rtree/include/RTree.h vendored Normal file

File diff suppressed because it is too large Load Diff

3
external/rtree/source.txt vendored Normal file
View File

@ -0,0 +1,3 @@
Taken from https://github.com/DevHwan/RTree, at the last commit before c++17 support was required
(https://github.com/DevHwan/RTree/commit/b6688880dfced02879f98dcb24dd8db0d09efce7)

46
external/rtree/tests/CMakeLists.txt vendored Normal file
View File

@ -0,0 +1,46 @@
# CMake version check
cmake_minimum_required(VERSION 3.10.0)
# Default Test
add_executable(Test_RTree
Test.cpp)
add_dependencies(Test_RTree
RTree)
target_include_directories(Test_RTree
PRIVATE ${CMAKE_CURRENT_LIST_DIR})
target_link_libraries(Test_RTree
PRIVATE RTree)
if(MSVC)
target_compile_options(Test_RTree
PRIVATE /permissive- /sdl)
target_compile_definitions(Test_RTree
PRIVATE _CRT_SECURE_NO_WARNINGS)
endif()
# Memory Test file
add_executable(Test_Memory_RTree
MemoryTest.cpp)
target_include_directories(Test_Memory_RTree
PRIVATE ${CMAKE_CURRENT_LIST_DIR})
target_link_libraries(Test_Memory_RTree
PRIVATE RTree)
if(MSVC)
target_compile_options(Test_Memory_RTree
PRIVATE /permissive- /sdl)
target_compile_definitions(Test_Memory_RTree
PRIVATE _CRT_SECURE_NO_WARNINGS)
endif()
# Bad Data Test file
add_executable(Test_BadData_RTree
TestBadData.cpp)
target_include_directories(Test_BadData_RTree
PRIVATE ${CMAKE_CURRENT_LIST_DIR})
target_link_libraries(Test_BadData_RTree
PRIVATE RTree)
if(MSVC)
target_compile_options(Test_BadData_RTree
PRIVATE /permissive- /sdl)
target_compile_definitions(Test_BadData_RTree
PRIVATE _CRT_SECURE_NO_WARNINGS)
endif()

246
external/rtree/tests/MemoryTest.cpp vendored Normal file
View File

@ -0,0 +1,246 @@
//
// MemoryTest.cpp
//
// This demonstrates a use of RTree
//
// RTree
#include <RTree.h>
#include <stdio.h>
#include <memory.h>
#ifdef WIN32
#include <crtdbg.h>
#endif //WIN32
#include <random>
// Use CRT Debug facility to dump memory leaks on app exit
#ifdef WIN32
// These two are for MSVS 2005 security consciousness until safe std lib funcs are available
#pragma warning(disable : 4996) // Deprecated functions
#define _CRT_SECURE_NO_DEPRECATE // Allow old unsecure standard library functions, Disable some 'warning C4996 - function was deprecated'
// The following macros set and clear, respectively, given bits
// of the C runtime library debug flag, as specified by a bitmask.
#ifdef _DEBUG
#define SET_CRT_DEBUG_FIELD(a) \
_CrtSetDbgFlag((a) | _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG))
#define CLEAR_CRT_DEBUG_FIELD(a) \
_CrtSetDbgFlag(~(a) & _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG))
#else
#define SET_CRT_DEBUG_FIELD(a) ((void) 0)
#define CLEAR_CRT_DEBUG_FIELD(a) ((void) 0)
#endif
#endif //WIN32
//
// Get a random float b/n two values
// The returned value is >= min && < max (exclusive of max)
//
static std::random_device sRandomDevice;
static std::mt19937 sMT(sRandomDevice());
static float RandFloat(float a_min, float a_max)
{
std::uniform_real_distribution<float> distribution(a_min, a_max);
return distribution(sMT);
}
/// Simplify handling of 3 dimensional coordinate
struct Vec3
{
/// Default constructor
Vec3() noexcept = default;
/// Construct from three elements
constexpr Vec3(float a_x, float a_y, float a_z) noexcept
: fVal{ a_x, a_y, a_z }
{
}
/// Add two vectors and return result
Vec3 operator+ (const Vec3& a_other) const
{
return Vec3(fVal[0] + a_other.fVal[0],
fVal[1] + a_other.fVal[1],
fVal[2] + a_other.fVal[2]);
}
float fVal[3] = { 0.0f, }; ///< 3 float components for axes or dimensions
};
static bool BoxesIntersect(const Vec3& a_boxMinA, const Vec3& a_boxMaxA,
const Vec3& a_boxMinB, const Vec3& a_boxMaxB)
{
if (a_boxMinA.fVal[0] > a_boxMaxB.fVal[0] || a_boxMaxA.fVal[0] < a_boxMinB.fVal[0])
return false;
if (a_boxMinA.fVal[1] > a_boxMaxB.fVal[1] || a_boxMaxA.fVal[1] < a_boxMinB.fVal[1])
return false;
if (a_boxMinA.fVal[2] > a_boxMaxB.fVal[2] || a_boxMaxA.fVal[2] < a_boxMinB.fVal[2])
return false;
return true;
}
/// A user type to test with, instead of a simple type such as an 'int'
struct SomeThing
{
SomeThing()
{
++s_outstandingAllocs;
}
~SomeThing()
{
--s_outstandingAllocs;
}
int m_creationCounter; ///< Just a number for identifying within test program
Vec3 m_min, m_max; ///< Minimal bounding rect, values must be known and constant in order to remove from RTree
static int s_outstandingAllocs; ///< Count how many outstanding objects remain
};
/// Init static
int SomeThing::s_outstandingAllocs = 0;
/// A callback function to obtain query results in this implementation
bool QueryResultCallback(SomeThing* a_data)
{
printf("search found %d\n", a_data->m_creationCounter);
return true;
}
int main(int argc, char* argv[])
{
constexpr const int NUM_OBJECTS = 40; // Number of objects in test set
constexpr const int FRAC_OBJECTS = 4;
static_assert(NUM_OBJECTS > FRAC_OBJECTS, "NUM_OBJECTS must be bigger than FRAC_OBJECTS for this test");
constexpr const float MAX_WORLDSIZE = 10.0f;
constexpr const float FRAC_WORLDSIZE = MAX_WORLDSIZE / 2;
// typedef the RTree useage just for conveniance with iteration
using SomeThingTree = RTree<SomeThing*, float, 3>;
SomeThing* thingArray[NUM_OBJECTS * 2] = { nullptr, }; // Store objects in another container to test with, sized larger than we need
// Create intance of RTree
SomeThingTree tree;
// Add some nodes
int counter = 0;
for (int index = 0; index < NUM_OBJECTS; ++index)
{
auto newThing = new SomeThing{};
newThing->m_creationCounter = counter++;
newThing->m_min = Vec3(RandFloat(-MAX_WORLDSIZE, MAX_WORLDSIZE), RandFloat(-MAX_WORLDSIZE, MAX_WORLDSIZE), RandFloat(-MAX_WORLDSIZE, MAX_WORLDSIZE));
Vec3 extent = Vec3(RandFloat(0, FRAC_WORLDSIZE), RandFloat(0, FRAC_WORLDSIZE), RandFloat(0, FRAC_WORLDSIZE));
newThing->m_max = newThing->m_min + extent;
thingArray[counter - 1] = newThing;
tree.Insert(newThing->m_min.fVal, newThing->m_max.fVal, newThing);
printf("inserting %d\n", newThing->m_creationCounter);
}
printf("tree count = %d\n", tree.Count());
int numToDelete = NUM_OBJECTS / FRAC_OBJECTS;
int numToStep = FRAC_OBJECTS;
// Delete some nodes
for (int index = 0; index < NUM_OBJECTS; index += numToStep)
{
auto curThing = thingArray[index];
if (curThing)
{
tree.Remove(curThing->m_min.fVal, curThing->m_max.fVal, curThing);
printf("removing %d\n", curThing->m_creationCounter);
delete curThing;
thingArray[index] = nullptr;
}
}
printf("tree count = %d\n", tree.Count());
// Add some more nodes
for (int index = 0; index < numToDelete; ++index)
{
auto newThing = new SomeThing{};
newThing->m_creationCounter = counter++;
newThing->m_min = Vec3(RandFloat(-MAX_WORLDSIZE, MAX_WORLDSIZE), RandFloat(-MAX_WORLDSIZE, MAX_WORLDSIZE), RandFloat(-MAX_WORLDSIZE, MAX_WORLDSIZE));
Vec3 extent = Vec3(RandFloat(0, FRAC_WORLDSIZE), RandFloat(0, FRAC_WORLDSIZE), RandFloat(0, FRAC_WORLDSIZE));
newThing->m_max = newThing->m_min + extent;
thingArray[counter - 1] = newThing;
tree.Insert(newThing->m_min.fVal, newThing->m_max.fVal, newThing);
printf("inserting %d\n", newThing->m_creationCounter);
}
printf("tree count = %d\n", tree.Count());
Vec3 searchMin(0, 0, 0);
Vec3 searchMax(FRAC_WORLDSIZE, FRAC_WORLDSIZE, FRAC_WORLDSIZE);
tree.Search(searchMin.fVal, searchMax.fVal, &QueryResultCallback);
// NOTE: Even better than just dumping text, it would be nice to render the
// tree contents and search result for visualization.
// List values. Iterator is NOT delete safe
SomeThingTree::Iterator it;
for (tree.GetFirst(it); !tree.IsNull(it); tree.GetNext(it))
{
SomeThing* curThing = tree.GetAt(it);
if (BoxesIntersect(searchMin, searchMax, curThing->m_min, curThing->m_max))
{
printf("brute found %d\n", curThing->m_creationCounter);
}
}
// Delete our nodes, NOTE, we are NOT deleting the tree nodes, just our data
// of course the tree will now contain invalid pointers that must not be used any more.
for (tree.GetFirst(it); !tree.IsNull(it); tree.GetNext(it))
{
SomeThing* removeElem = tree.GetAt(it);
if (removeElem)
{
printf("deleting %d\n", removeElem->m_creationCounter);
delete removeElem;
}
}
// Remove all contents (This would have happened automatically during destructor)
tree.RemoveAll();
if (SomeThing::s_outstandingAllocs > 0)
{
printf("Memory leak!\n");
printf("s_outstandingAllocs = %d\n", SomeThing::s_outstandingAllocs);
}
else
{
printf("No memory leaks detected by app\n");
}
#ifdef WIN32
// Use CRT Debug facility to dump memory leaks on app exit
SET_CRT_DEBUG_FIELD(_CRTDBG_LEAK_CHECK_DF);
#endif //WIN32
return 0;
}

149
external/rtree/tests/Test.cpp vendored Normal file
View File

@ -0,0 +1,149 @@
//
// Test.cpp
//
// This is a direct port of the C version of the RTree test program.
//
// RTree
#include <RTree.h>
#include <iostream>
using namespace std;
typedef int ValueType;
struct Rect
{
Rect() noexcept = default;
constexpr Rect(int a_minX, int a_minY, int a_maxX, int a_maxY) noexcept
: fMin{ a_minX, a_minY }
, fMax{ a_maxX, a_maxY }
{
}
int fMin[2] = { 0, };
int fMax[2] = { 0, };
};
struct Rect rects[] =
{
Rect(0, 0, 2, 2), // xmin, ymin, xmax, ymax (for 2 dimensional RTree)
Rect(5, 5, 7, 7),
Rect(8, 5, 9, 6),
Rect(7, 1, 9, 2),
};
constexpr const auto nrects = sizeof(rects) / sizeof(rects[0]);
Rect search_rect(6, 4, 10, 6); // search will find above rects that this one overlaps
bool MySearchCallback(ValueType id)
{
cout << "Hit data rect " << id << "\n";
return true; // keep going
}
int main()
{
using MyTree = RTree<ValueType, int, 2, float>;
static_assert(std::is_same<MyTree::ElementType, int>::value, "ElementType must match");
static_assert(std::is_same<MyTree::Element, int[2]>::value, "Element must match");
static_assert(std::is_same<MyTree::ElementTypeReal, float>::value, "ElementTypeReal must match");
static_assert(MyTree::kNumDimensions == 2, "Dimension must match");
MyTree tree;
cout << "nrects = " << nrects << "\n";
for (size_t i = 0; i < nrects; i++)
{
tree.Insert(rects[i].fMin, rects[i].fMax, static_cast<int>(i)); // Note, all values including zero are fine in this version
}
const auto nhits = tree.Search(search_rect.fMin, search_rect.fMax, MySearchCallback);
cout << "Search resulted in " << nhits << " hits\n";
// Iterator test
int itIndex = 0;
MyTree::Iterator it;
for (tree.GetFirst(it);
!tree.IsNull(it);
tree.GetNext(it))
{
int value = tree.GetAt(it);
int boundsMin[2] = { 0,0 };
int boundsMax[2] = { 0,0 };
it.GetBounds(boundsMin, boundsMax);
cout << "it[" << itIndex++ << "] " << value << " = (" << boundsMin[0] << "," << boundsMin[1] << "," << boundsMax[0] << "," << boundsMax[1] << ")\n";
}
// Iterator test, alternate syntax
itIndex = 0;
tree.GetFirst(it);
while (!it.IsNull())
{
int value = *it;
++it;
cout << "it[" << itIndex++ << "] " << value << "\n";
}
// test copy constructor
MyTree copy = tree;
// Iterator test
itIndex = 0;
for (copy.GetFirst(it);
!copy.IsNull(it);
copy.GetNext(it))
{
int value = copy.GetAt(it);
int boundsMin[2] = { 0,0 };
int boundsMax[2] = { 0,0 };
it.GetBounds(boundsMin, boundsMax);
cout << "it[" << itIndex++ << "] " << value << " = (" << boundsMin[0] << "," << boundsMin[1] << "," << boundsMax[0] << "," << boundsMax[1] << ")\n";
}
// Iterator test, alternate syntax
itIndex = 0;
copy.GetFirst(it);
while (!it.IsNull())
{
int value = *it;
++it;
cout << "it[" << itIndex++ << "] " << value << "\n";
}
return 0;
// Output:
//
// nrects = 4
// Hit data rect 1
// Hit data rect 2
// Search resulted in 2 hits
// it[0] 0 = (0,0,2,2)
// it[1] 1 = (5,5,7,7)
// it[2] 2 = (8,5,9,6)
// it[3] 3 = (7,1,9,2)
// it[0] 0
// it[1] 1
// it[2] 2
// it[3] 3
// it[0] 0 = (0,0,2,2)
// it[1] 1 = (5,5,7,7)
// it[2] 2 = (8,5,9,6)
// it[3] 3 = (7,1,9,2)
// it[0] 0
// it[1] 1
// it[2] 2
// it[3] 3
}

111
external/rtree/tests/TestBadData.cpp vendored Normal file
View File

@ -0,0 +1,111 @@
//
// TestBadData.cpp
//
// RTree
#include <RTree.h>
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
using namespace std;
typedef int ValueType;
typedef long long CoordType;
struct Rect
{
Rect() noexcept = default;
constexpr Rect(CoordType a_minX, CoordType a_minY, CoordType a_maxX, CoordType a_maxY) noexcept
: fMin{ a_minX, a_minY }
, fMax{ a_maxX, a_maxY }
{
}
CoordType fMin[2] = { 0, };
CoordType fMax[2] = { 0, };
};
bool MySearchCallback(ValueType id)
{
cout << "Hit data rect " << id << "\n";
return true; // keep going
}
int main(int argc, char* argv[])
{
if (argc < 2) {
std::cout << "Usage: " << argv[0] << " inFile\n";
return -1;
}
using RectVector = std::vector<Rect>;
RectVector rectVector;
// read the data
{
ifstream inFile(argv[1]);
if (!inFile.is_open()) {
std::cerr << "Can't open input file\n";
return -1;
}
while (!inFile.eof()) {
// security and robustness be damned
CoordType xmin, ymin, xmax, ymax;
string dummy;
inFile >> xmin >> ymin >> xmax >> ymax;
cout << xmin << " " << ymin << " " << xmax << " " << ymax << "\n";
rectVector.emplace_back(xmin, ymin, xmin + xmax, ymin + ymax);
}
}
using MyTree = RTree<ValueType, CoordType, 2, float>;
MyTree tree;
int i, nhits;
cout << "number of rectangles is " << rectVector.size() << "\n";
for (i = 0; i < rectVector.size(); i++)
{
tree.Insert(rectVector[i].fMin, rectVector[i].fMax, i); // Note, all values including zero are fine in this version
}
Rect search_rect(6, 4, 10, 6);
nhits = tree.Search(search_rect.fMin, search_rect.fMax, MySearchCallback);
cout << "Search resulted in " << nhits << " hits\n";
// Iterator test
int itIndex = 0;
MyTree::Iterator it;
for (tree.GetFirst(it);
!tree.IsNull(it);
tree.GetNext(it))
{
int value = tree.GetAt(it);
CoordType boundsMin[2] = { 0,0 };
CoordType boundsMax[2] = { 0,0 };
it.GetBounds(boundsMin, boundsMax);
cout << "it[" << itIndex++ << "] " << value << " = (" << boundsMin[0] << "," << boundsMin[1] << "," << boundsMax[0] << "," << boundsMax[1] << ")\n";
}
// Iterator test, alternate syntax
itIndex = 0;
tree.GetFirst(it);
while (!it.IsNull())
{
CoordType value = *it;
++it;
cout << "it[" << itIndex++ << "] " << value << "\n";
}
return 0;
}

174
external/rtree/tests/baddata.txt vendored Normal file
View File

@ -0,0 +1,174 @@
20041225182812 20041225182812 32743 32743
20050120182105 20050120182105 32743 32743
20050120181004 20050120181004 32743 32743
20050120185604 20050120185604 32743 32743
20050120191706 20050120192143 32743 32743
20050129054438 20050129054455 32743 32743
20050125004242 20050127004348 32743 32743
20050127080821 20050127080821 32743 32743
20050103232047 20050103232047 32743 32743
20050103231608 20050103231953 32743 32743
20050103232124 20050103233547 32743 32743
20050103234758 20050103234758 32743 32743
20050113072936 20050113072936 32743 32743
20050105014304 20050111225505 32743 32743
20050117025915 20050118054001 32743 32743
20050118064200 20050119020157 32743 32743
20041225182812 20041225182812 32743 32743
20041225182812 20041225182812 32743 32743
20041231081821 20041231081821 32743 32743
20041231084441 20050101210045 32743 32743
20041231053402 20041231053402 32743 32743
20041227225557 20041228062229 32743 32743
20041227003512 20041227003512 32743 32743
20041227004805 20041227074924 32743 32743
20041227080309 20041227080309 32743 32743
20041227075241 20041227075241 32743 32743
20041227000445 20041227000931 32743 32743
20041227003006 20041227003006 32743 32743
20050120181004 20050120182105 32743 32743
20050120183502 20050120183502 32743 32743
20050101213828 20050113072936 32743 32743
20050117025915 20050119020157 32743 32743
20050119201652 20050120105309 32743 32743
20050120185604 20050120185604 32743 32743
20050120191706 20050120192143 32743 32743
20050129054438 20050130172429 32743 32743
20050125004242 20050127004348 32743 32743
20050127080821 20050127080821 32743 32743
20041225182812 20041225182812 32743 32743
20041225182812 20041225182812 32743 32743
20050120182105 20050120182105 32743 32743
20050120181004 20050120181004 32743 32743
20050120185604 20050120185604 32743 32743
20050120191706 20050120192143 32743 32743
20050129054438 20050129054455 32743 32743
20050125004242 20050127004348 32743 32743
20050127080821 20050127080821 32743 32743
20050103232047 20050103232047 32743 32743
20050103231608 20050103231953 32743 32743
20050103232124 20050103233547 32743 32743
20050103234758 20050103234758 32743 32743
20050113072936 20050113072936 32743 32743
20050105014304 20050111225505 32743 32743
20050117025915 20050118054001 32743 32743
20050118064200 20050119020157 32743 32743
20041225182812 20041225182812 32743 32743
20041225182812 20041225182812 32743 32743
20041227000445 20041227080309 32743 32743
20041227225557 20050102160402 32743 32743
20050103024822 20050113072936 32743 32743
20050117025915 20050119020157 32743 32743
20050120073145 20050120181004 32743 32743
20050120182105 20050120182105 32743 32743
20050120185604 20050120192143 32743 32743
20050125004242 20050130172429 32743 32743
20041227225454 20041227225454 32743 32743
20050119201652 20050119201652 32743 32743
20050120183502 20050120183502 32743 32743
20050120212256 20050120212256 32743 32743
20050122001605 20050122001605 32743 32743
20050120182105 20050120182105 32743 32743
20050120181004 20050120181004 32743 32743
20050120185604 20050120185604 32743 32743
20050120191706 20050120192143 32743 32743
20050129054438 20050130172429 32743 32743
20050125004242 20050127004348 32743 32743
20050127080821 20050127080821 32743 32743
20041231081821 20050101210045 32743 32743
20041227225557 20041231053402 32743 32743
20041227003512 20041227074924 32743 32743
20041227075241 20041227080309 32743 32743
20041227003340 20041227003340 32743 32743
20041227000445 20041227003006 32743 32743
20050103224855 20050103234758 32743 32743
20050105014304 20050113072936 32743 32743
20050117025915 20050119020157 32743 32743
20050101213828 20050101213828 32743 32743
20050102160402 20050102160402 32743 32743
20050103024822 20050103025321 32743 32743
20041225182812 20041225182812 32743 32743
20050120105309 20050120105309 32743 32743
20050120073145 20050120073145 32743 32743
20041225182812 20041225182812 32743 32743
20050103232124 20050103232503 32743 32743
20050103232710 20050103232710 32743 32743
20050105034455 20050106034834 32743 32743
20050106215124 20050111225505 32743 32743
20050106035210 20050106035210 32743 32743
20050117213917 20050118025128 32743 32743
20050118025641 20050118025641 32743 32743
20050118065543 20050118065543 32743 32743
20050119020157 20050119020157 32743 32743
20050118064200 20050118064200 32743 32743
20050118064228 20050118064228 32743 32743
20050120181004 20050120182105 32743 32743
20050120185604 20050120192143 32743 32743
20050125004242 20050129054455 32743 32743
20041225182812 20041225182812 32743 32743
20041225182812 20041225182812 32743 32743
20041225182812 20041225182812 32743 32743
20041225182812 20041225182812 32743 32743
20050120182105 20050120182105 32743 32743
20050120181004 20050120181004 32743 32743
20050120185604 20050120185604 32743 32743
20050120191706 20050120192143 32743 32743
20050129054438 20050129054455 32743 32743
20050125004242 20050127004348 32743 32743
20050127080821 20050127080821 32743 32743
20050103232047 20050103232047 32743 32743
20050103231608 20050103231953 32743 32743
20050103232124 20050103233547 32743 32743
20050103234758 20050103234758 32743 32743
20050113072936 20050113072936 32743 32743
20050105014304 20050111225505 32743 32743
20050117025915 20050118054001 32743 32743
20050118064200 20050119020157 32743 32743
20041225182812 20041225182812 32743 32743
20041225182812 20041225182812 32743 32743
20050120191706 20050120191706 32743 32743
20050120192143 20050120192143 32743 32743
20050129054438 20050129054438 32743 32743
20050129054455 20050130172429 32743 32743
20050125004242 20050125035134 32743 32743
20050126112156 20050127004348 32743 32743
20041227225454 20050101210045 32743 32743
20041227000445 20041227080309 32743 32743
20050101213828 20050101213828 32743 32743
20050102160402 20050102160402 32743 32743
20050103224855 20050113072936 32743 32743
20050117025915 20050119020157 32743 32743
20050103024822 20050103025321 32743 32743
20050119201652 20050119201652 32743 32743
20050120073145 20050120105309 32743 32743
20041225182812 20041225182812 32743 32743
20041225182812 20041225182812 32743 32743
20050120191706 20050120191706 32743 32743
20050120192143 20050120192143 32743 32743
20050129054438 20050129054438 32743 32743
20050129054455 20050130172429 32743 32743
20050125004242 20050125035134 32743 32743
20050126112156 20050127004348 32743 32743
20041227225454 20050101210045 32743 32743
20041227000445 20041227080309 32743 32743
20050101213828 20050101213828 32743 32743
20050102160402 20050102160402 32743 32743
20050103224855 20050113072936 32743 32743
20050117025915 20050119020157 32743 32743
20050103024822 20050103025321 32743 32743
20050119201652 20050119201652 32743 32743
20050120073145 20050120105309 32743 32743
20041225182812 20041225182812 32743 32743
20041225182812 20041225182812 32743 32743
20050120191706 20050120191706 32743 32743
20050120192143 20050120192143 32743 32743
20050129054438 20050129054438 32743 32743
20050129054455 20050130172429 32743 32743
20050125004242 20050125035134 32743 32743
20050126112156 20050127004348 32743 32743
20041227225454 20050101210045 32743 32743
20041227000445 20041227080309 32743 32743
20050101213828 20050101213828 32743 32743
20050102160402 20050102160402 32743 32743
20050103224855 20050113072936 32743 32743
20050117025915 20050119020157 32743 32743

View File

@ -89,7 +89,7 @@ astyleit() {
for f in "$@"; do
case "$f" in
src/plugins/grass/qtermwidget/*|external/o2/*|external/qt-unix-signals/*|external/astyle/*|external/kdbush/*|external/poly2tri/*|external/wintoast/*|external/qt3dextra-headers/*|python/ext-libs/*|ui_*.py|*.astyle|tests/testdata/*|editors/*)
src/plugins/grass/qtermwidget/*|external/o2/*|external/qt-unix-signals/*|external/rtree/*|external/astyle/*|external/kdbush/*|external/poly2tri/*|external/wintoast/*|external/qt3dextra-headers/*|python/ext-libs/*|ui_*.py|*.astyle|tests/testdata/*|editors/*)
echo -ne "$f skipped $elcr"
continue
;;

View File

@ -1337,6 +1337,7 @@ INCLUDE_DIRECTORIES(
${CMAKE_SOURCE_DIR}/external/kdbush/include
${CMAKE_SOURCE_DIR}/external/nmea
${CMAKE_SOURCE_DIR}/external/poly2tri
${CMAKE_SOURCE_DIR}/external/rtree/include
)
INCLUDE_DIRECTORIES(SYSTEM

View File

@ -112,7 +112,7 @@ void CostCalculator::addObstacleCostPenalty( LabelPosition *lp, FeaturePart *obs
lp->setCost( lp->cost() + obstacleCost );
}
void CostCalculator::setPolygonCandidatesCost( std::size_t nblp, std::vector< std::unique_ptr< LabelPosition > > &lPos, QgsGenericSpatialIndex<FeaturePart> *obstacles, double bbx[4], double bby[4] )
void CostCalculator::setPolygonCandidatesCost( std::size_t nblp, std::vector< std::unique_ptr< LabelPosition > > &lPos, PalRtree<FeaturePart> *obstacles, double bbx[4], double bby[4] )
{
double normalizer;
// compute raw cost
@ -157,7 +157,7 @@ void CostCalculator::setPolygonCandidatesCost( std::size_t nblp, std::vector< st
}
}
void CostCalculator::setCandidateCostFromPolygon( LabelPosition *lp, QgsGenericSpatialIndex<FeaturePart> *obstacles, double bbx[4], double bby[4] )
void CostCalculator::setCandidateCostFromPolygon( LabelPosition *lp, PalRtree<FeaturePart> *obstacles, double bbx[4], double bby[4] )
{
PolygonCostCalculator *pCost = new PolygonCostCalculator( lp );
@ -190,7 +190,7 @@ void CostCalculator::setCandidateCostFromPolygon( LabelPosition *lp, QgsGenericS
delete pCost;
}
std::size_t CostCalculator::finalizeCandidatesCosts( Feats *feat, std::size_t max_p, QgsGenericSpatialIndex<FeaturePart> *obstacles, double bbx[4], double bby[4] )
std::size_t CostCalculator::finalizeCandidatesCosts( Feats *feat, std::size_t max_p, PalRtree<FeaturePart> *obstacles, double bbx[4], double bby[4] )
{
// If candidates list is smaller than expected
if ( max_p > feat->candidates.size() )

View File

@ -18,6 +18,7 @@
#define SIP_NO_FILE
#include <QList>
#include "palrtree.h"
/**
* \class pal::CostCalculator
@ -40,13 +41,13 @@ namespace pal
static void addObstacleCostPenalty( pal::LabelPosition *lp, pal::FeaturePart *obstacle, Pal *pal );
//! Calculates the costs for polygon label candidates
static void setPolygonCandidatesCost( std::size_t nblp, std::vector<std::unique_ptr<pal::LabelPosition> > &lPos, QgsGenericSpatialIndex< FeaturePart > *obstacles, double bbx[4], double bby[4] );
static void setPolygonCandidatesCost( std::size_t nblp, std::vector<std::unique_ptr<pal::LabelPosition> > &lPos, PalRtree< FeaturePart > *obstacles, double bbx[4], double bby[4] );
//! Sets cost to the smallest distance between lPos's centroid and a polygon stored in geometry field
static void setCandidateCostFromPolygon( LabelPosition *lp, QgsGenericSpatialIndex< FeaturePart > *obstacles, double bbx[4], double bby[4] );
static void setCandidateCostFromPolygon( LabelPosition *lp, PalRtree< FeaturePart > *obstacles, double bbx[4], double bby[4] );
//! Sort candidates by costs, skip the worse ones, evaluate polygon candidates
static std::size_t finalizeCandidatesCosts( Feats *feat, std::size_t max_p, QgsGenericSpatialIndex< FeaturePart > *obstacles, double bbx[4], double bby[4] );
static std::size_t finalizeCandidatesCosts( Feats *feat, std::size_t max_p, PalRtree< FeaturePart > *obstacles, double bbx[4], double bby[4] );
/**
* Sorts label candidates in ascending order of cost

View File

@ -428,7 +428,7 @@ void LabelPosition::setHasHardObstacleConflict( bool conflicts )
nextPart->setHasHardObstacleConflict( conflicts );
}
void LabelPosition::removeFromIndex( QgsGenericSpatialIndex<LabelPosition> &index )
void LabelPosition::removeFromIndex( PalRtree<LabelPosition> &index )
{
double amin[2];
double amax[2];
@ -436,7 +436,7 @@ void LabelPosition::removeFromIndex( QgsGenericSpatialIndex<LabelPosition> &inde
index.remove( this, QgsRectangle( amin[0], amin[1], amax[0], amax[1] ) );
}
void LabelPosition::insertIntoIndex( QgsGenericSpatialIndex<LabelPosition> &index )
void LabelPosition::insertIntoIndex( PalRtree<LabelPosition> &index )
{
double amin[2];
double amax[2];

View File

@ -35,7 +35,7 @@
#include "qgis_core.h"
#include "pointset.h"
#include "qgsgenericspatialindex.h"
#include "palrtree.h"
#include <fstream>
namespace pal
@ -288,12 +288,12 @@ namespace pal
/**
* Removes the label position from the specified \a index.
*/
void removeFromIndex( QgsGenericSpatialIndex<LabelPosition> &index );
void removeFromIndex( PalRtree<LabelPosition> &index );
/**
* Inserts the label position into the specified \a index.
*/
void insertIntoIndex( QgsGenericSpatialIndex<LabelPosition> &index );
void insertIntoIndex( PalRtree<LabelPosition> &index );
/**
* The offset of the anchor point in x direction.

View File

@ -47,10 +47,6 @@ class QgsLabelFeature;
namespace pal
{
/// @cond PRIVATE
template<class DATATYPE, class ELEMTYPE, int NUMDIMS, class ELEMTYPEREAL, int TMAXNODES, int TMINNODES> class RTree;
/// @endcond
class FeaturePart;
class Pal;

View File

@ -40,6 +40,7 @@
#include "pointset.h"
#include "internalexception.h"
#include "util.h"
#include "palrtree.h"
#include <cfloat>
#include <list>
@ -84,7 +85,7 @@ Layer *Pal::addLayer( QgsAbstractLabelProvider *provider, const QString &layerNa
std::unique_ptr<Problem> Pal::extract( const QgsRectangle &extent, const QgsGeometry &mapBoundary )
{
// to store obstacles
std::unique_ptr< QgsGenericSpatialIndex< FeaturePart > > obstacles = qgis::make_unique< QgsGenericSpatialIndex< FeaturePart > >();
PalRtree< FeaturePart > obstacles;
std::vector< FeaturePart * > allObstacleParts;
std::unique_ptr< Problem > prob = qgis::make_unique< Problem >();
@ -153,7 +154,7 @@ std::unique_ptr<Problem> Pal::extract( const QgsRectangle &extent, const QgsGeom
for ( int i = 0; i < featurePart->getNumSelfObstacles(); i++ )
{
FeaturePart *selfObstacle = featurePart->getSelfObstacle( i );
obstacles->insert( selfObstacle, selfObstacle->boundingBox() );
obstacles.insert( selfObstacle, selfObstacle->boundingBox() );
allObstacleParts.emplace_back( selfObstacle );
if ( !featurePart->getSelfObstacle( i )->getHoleOf() )
@ -215,7 +216,7 @@ std::unique_ptr<Problem> Pal::extract( const QgsRectangle &extent, const QgsGeom
break; // do not continue searching
// insert into obstacles
obstacles->insert( obstaclePart, obstaclePart->boundingBox() );
obstacles.insert( obstaclePart, obstaclePart->boundingBox() );
allObstacleParts.emplace_back( obstaclePart );
obstacleCount++;
}
@ -301,7 +302,7 @@ std::unique_ptr<Problem> Pal::extract( const QgsRectangle &extent, const QgsGeom
}
// sort candidates by cost, skip less interesting ones, calculate polygon costs (if using polygons)
max_p = CostCalculator::finalizeCandidatesCosts( feat.get(), max_p, obstacles.get(), bbx, bby );
max_p = CostCalculator::finalizeCandidatesCosts( feat.get(), max_p, &obstacles, bbx, bby );
if ( isCanceled() )
return nullptr;
@ -411,8 +412,6 @@ std::unique_ptr<Problem> Pal::extract( const QgsRectangle &extent, const QgsGeom
prob->mNbOverlap = nbOverlaps;
}
obstacles.reset();
return prob;
}

100
src/core/pal/palrtree.h Normal file
View File

@ -0,0 +1,100 @@
/***************************************************************************
parlrtree.h
------------------------
Date : December 2019
Copyright : (C) 2019 by Nyall Dawson
Email : nyall dot dawson at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "RTree.h"
#include "qgsrectangle.h"
#ifndef QGSPALRTREE_H
#define QGSPALRTREE_H
#define SIP_NO_FILE
/**
* \ingroup core
* \class PalRtree
*
* A rtree spatial index for use in the pal labeling engine.
*
* \note Not available in Python bindings.
* \since QGIS 3.12
*/
template <typename T>
class PalRtree : public RTree<T *, float, 2, float>
{
public:
/**
* Inserts new \a data into the spatial index, with the specified \a bounds.
*
* Ownership of \a data is not transferred, and it is the caller's responsibility to ensure that
* it exists for the lifetime of the spatial index.
*/
void insert( T *data, const QgsRectangle &bounds )
{
this->Insert(
{
static_cast< float >( bounds.xMinimum() ),
static_cast< float >( bounds.yMinimum() )
},
{
static_cast< float>( bounds.xMaximum() ),
static_cast< float >( bounds.yMaximum() )
},
data );
}
/**
* Removes existing \a data from the spatial index, with the specified \a bounds.
*
* \a data is not deleted, and it is the caller's responsibility to ensure that
* it is appropriately cleaned up.
*/
void remove( T *data, const QgsRectangle &bounds )
{
this->Remove(
{
static_cast< float >( bounds.xMinimum() ),
static_cast< float >( bounds.yMinimum() )
},
{
static_cast< float>( bounds.xMaximum() ),
static_cast< float >( bounds.yMaximum() )
},
data );
}
/**
* Performs an intersection check against the index, for data intersecting the specified \a bounds.
*
* The \a callback function will be called once for each matching data object encountered.
*/
bool intersects( const QgsRectangle &bounds, const std::function< bool( T *data )> &callback ) const
{
this->Search(
{
static_cast< float >( bounds.xMinimum() ),
static_cast< float >( bounds.yMinimum() )
},
{
static_cast< float>( bounds.xMaximum() ),
static_cast< float >( bounds.yMaximum() )
},
callback );
return true;
}
};
#endif

View File

@ -132,7 +132,7 @@ void Problem::reduce()
delete[] ok;
}
void ignoreLabel( const LabelPosition *lp, PriorityQueue &list, QgsGenericSpatialIndex< LabelPosition > &candidatesIndex )
void ignoreLabel( const LabelPosition *lp, PriorityQueue &list, PalRtree< LabelPosition > &candidatesIndex )
{
if ( list.isIn( lp->getId() ) )
{

View File

@ -36,7 +36,9 @@
#include "qgis_core.h"
#include <list>
#include <QList>
#include "qgsgenericspatialindex.h"
#include "palrtree.h"
#include <memory>
#include <vector>
namespace pal
{
@ -142,7 +144,7 @@ namespace pal
/**
* Returns the index containing all label candidates.
*/
QgsGenericSpatialIndex<LabelPosition> &allCandidatesIndex() { return mAllCandidatesIndex; }
PalRtree< LabelPosition > &allCandidatesIndex() { return mAllCandidatesIndex; }
private:
@ -183,8 +185,8 @@ namespace pal
std::vector< std::unique_ptr< LabelPosition > > mLabelPositions;
QgsGenericSpatialIndex<LabelPosition> mAllCandidatesIndex;
QgsGenericSpatialIndex<LabelPosition> mActiveCandidatesIndex;
PalRtree<LabelPosition> mAllCandidatesIndex;
PalRtree<LabelPosition> mActiveCandidatesIndex;
std::vector< std::unique_ptr< LabelPosition > > mPositionsWithNoCandidates;