https://github.com/benmaier/barneshuttree
Simple and adaptable C++ Barnes-Hut-tree implementation for 2D, based on openFrameworks but de facto stand-alone
https://github.com/benmaier/barneshuttree
Last synced: 3 months ago
JSON representation
Simple and adaptable C++ Barnes-Hut-tree implementation for 2D, based on openFrameworks but de facto stand-alone
- Host: GitHub
- URL: https://github.com/benmaier/barneshuttree
- Owner: benmaier
- License: mit
- Created: 2022-07-24T21:53:08.000Z (almost 3 years ago)
- Default Branch: main
- Last Pushed: 2022-12-05T10:31:28.000Z (over 2 years ago)
- Last Synced: 2024-10-11T11:07:06.849Z (8 months ago)
- Language: C++
- Homepage:
- Size: 231 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# BarnesHutTree
Simple C++-11 implementation for 2D Barnes-Hut trees. Based on openFrameworks syntax, but usable without openFrameworks. Implemented for `float`, but easily adaptable to `double` by replacing types (specifically didn't use templates to be more beginner-friendly).
Implemented after ["The Barnes-Hut Algorithm"](http://arborjs.org/docs/barnes-hut) by Tom Ventimiglia, Kevin Wayne and Christian Swinehart, which is an adaption of ["Barnes-Hut Galaxy Simulator"](https://www.cs.princeton.edu/courses/archive/fall03/cs126/assignments/barnes-hut.html) by Tom Ventimiglin and Kevin Wayne.
```cpp
#include
#include
#includeint main(){
int n = 1000;
vector < ofVec2f > positions;
for(int i=0; i positions
positions.push_back(ofVec2f(2.4, 6.0));
positions.push_back(ofVec2f(1.4, 3.1));BarnesHutTree tree(positions);
```### Construct tree from positions and masses
```cpp
vector positions;
positions.push_back(ofVec2f(2.4, 6.0));
positions.push_back(ofVec2f(1.4, 3.1));vector masses;
masses.push_back(1.3);
masses.push_back(3.1);
BarnesHutTree tree(positions, masses);
```### Construct tree, insert positions afterwards
```cpp
BarnesHutTree tree(box);
tree.insert_positions(positions);
```### Construct tree, insert positions and masses afterwards
```cpp
BarnesHutTree tree(box);
tree.insert_positions(positions);
tree.insert_positions_and_masses(positions, masses);
```### Compute force on position
```cpp
ofVec2f force; // 0-vector
ofVec2f loc(0.2, 0.4);
tree.compute_force(loc, force); // defaults to theta = 0.5
```### Compute force with controlled theta
Referring to theta as [defined here](http://arborjs.org/docs/barnes-hut).
```cpp
float theta = 0.3;
tree.compute_force(loc, force, theta);
```### Scale the force
#### Gravitational attractive force
```cpp
ofVec2f force; // 0-vector
float G = 1.f;
float loc_mass = 2.f;
tree.compute_force(loc, force, theta);force = force * G * loc_mass;
```#### Electro-static repulsive force
```cpp
ofVec2f force; // 0-vector
float Z = -1.f;
float loc_charge = 2.f;
tree.compute_force(loc, force, theta);force = force * Z * loc_charge;
```### Access a tree node's subtrees
```cpp
tree.subtrees.trees // this is a vector < BarnesHutTree * >
// and empty subtrees are NULL
```### Check if tree node is internal or leaf or empty
```cpp
tree.is_leaf();
tree.is_internal_node();
tree.is_empty();
```### Access a tree node's box (geometry)
```cpp
tree.geom;tree.geom.get_bottom_left();
tree.geom.get_top_right();
tree.geom.get_top_left();
tree.geom.width();
tree.geom.height();
```### Tree string representation
```cpp
#include
#include...
ostringstream ss;
tree.get_tree_str(ss)
cout << ss;
```Output:
```
+- CM = 0.465625, 0.499375; M = 8; n = 8
| +- (nw) 0 (0.2, 0.76)
| +- (ne) CM = 0.638333, 0.795; M = 3; n = 3
| | +- (nw) CM = 0.5775, 0.8275; M = 2; n = 2
| | | +- (ne) 1 (0.635, 0.885)
| | | +- (sw) 2 (0.52, 0.77)
| | +- (se) 3 (0.76, 0.73)
| +- (se) 7 (0.635, 0.12)
| +- (sw) CM = 0.325, 0.243333; M = 3; n = 3
| | +- (ne) 4 (0.48, 0.48)
| | +- (se) 6 (0.395, 0.15)
| | +- (sw) 5 (0.1, 0.1)
```## Examples
### Simple tree construction
See [01\_tree\_example](./01_tree_example/main.cpp).
```cpp
# include
# include
# includeusing namespace std;
int main(int argc, char *argv[]){
// coordinates of points in http://arborjs.org/docs/barnes-hut,
// or https://www.cs.princeton.edu/courses/archive/fall03/cs126/assignments/barnes-hut.html
// respectively
vector < ofVec2f > points;points.push_back(ofVec2f(0.2, 0.76));
points.push_back(ofVec2f(0.5+1/8.0+0.01, 7/8.0 + 0.01));
points.push_back(ofVec2f(0.5 + 0.02, 0.75 + 0.02));
points.push_back(ofVec2f(0.75 + 0.01, 0.75 - 0.02));
points.push_back(ofVec2f(0.5 - 0.02, 0.5 - 0.02));
points.push_back(ofVec2f(0.1, 0.1));
points.push_back(ofVec2f(3/8.0+0.02, 0.15));
points.push_back(ofVec2f(5/8.+0.01, 0.12));Extent box(0.f, // origin x
0.f, // origin y
1.f, // width
1.f // height
);BarnesHutTree tree(box);
float mass = 1.f;
int id = 0;for(auto &point: points){
tree.insert(point,mass,id);
++id;
}
}
```### Simple tree traversal and data access
See [01\_tree\_example](./01_tree_example/main.cpp).
```cpp
void scan_tree(BarnesHutTree* node){if (node->is_leaf()){
cout << "this node is a leaf and carries data with id = " << node->this_id
<< " and position = " << *(node->this_pos)
<< endl << endl;
}
if (node->is_internal_node()){
if (node->parent == NULL)
cout << "this node is the root node" << endl;cout << "this node is an internal node with " << (node->subtrees).occupied_trees
<< " occupied quadrants";
if ((node->subtrees).occupied_trees > 0){
cout << " (";
int i = 0;
for(auto &subtree: node->subtrees.trees){
if (subtree != NULL)
cout << i << ", ";
++i;
}
cout << ")" << endl;
}
cout << "center of mass = " << node->center_of_mass << endl;
cout << "total mass = " << node->total_mass << endl;
cout << endl;for(auto &subtree: node->subtrees.trees){
if (subtree != NULL)
scan_tree(subtree);
}
}
}
```### Compute force
See [02\_force\_example](./02_force_example/main.cpp).
```cpp
void compute_force(
ofVec2f &force,
const ofVec2f &pos,
BarnesHutTree* tree,
float theta,
const float &mass,
const float &gravitational_constant
)
{
if (tree == NULL) return;auto _r = tree->this_pos;
if (tree->is_leaf())
{
ofVec2f d = *(tree->this_pos) - pos;
float norm = d.length();
if (norm > 0)
force += gravitational_constant * mass * (tree->total_mass) * d/pow(norm,3);
}
else
{
ofVec2f __r = tree->center_of_mass;
ofVec2f d = (__r) - pos;
float s = sqrt(tree->geom.width() * tree->geom.height()); // geometric mean of box dimensions
float norm = d.length();
if ((s/norm) < theta)
force += gravitational_constant * mass * (tree->total_mass) * d/pow(norm,3);
else
for(auto &subtree: tree->subtrees.trees){
if (subtree != NULL){
compute_force(force, pos, subtree, theta, mass, gravitational_constant);
}
}
}
}
```### Generate data to draw tree as boxes
See [07\_draw\_boxes](./07_draw_boxes/main.cpp).
```cpp
void scan_tree(BarnesHutTree* node){if (node->is_leaf()){
cout << (*(node->this_pos)).x << ","
<< (*(node->this_pos)).y << ","
<< "0,0" << endl;
}
if (node->is_internal_node() || node->is_leaf()){
cout << (node->geom).left() << ","
<< (node->geom).bottom() << ","
<< (node->geom).width() << ","
<< (node->geom).height()
<< endl;for(auto &subtree: (node->subtrees).trees){
if (subtree != NULL)
scan_tree(subtree);
}
}
}```
Then generate csv-data as
```cpp
cout << "x,y,w,h" << endl;
scan_tree(&tree);
```Plot with Python script in [07\_draw\_boxes](./07_draw_boxes/main.cpp).

### Test the accuracy and duration of force computation depending on theta
See [05\_theta\_scan\_statistics](./05_theta_scan_statistics/).
Testing (i) how accurate and (ii) how fast the computation of force is on a single point,
depending on the `theta`-parameter.
### ofVec2f
See [00\_vector\_example](./00_vector_example//main.cpp).
```cpp
# include
# includeusing namespace std;
int main(int argc, char *argv[]){
ofVec2f zero(0.f, 0.f);
ofVec2f vec(1.f, 1.5f);
ofVec2f tri(3.f, 4.f);cout << "ofVec2f zero(); zero = " << zero << endl;
cout << "ofVec2f vec(1.f, 1.5f); vec = " << vec << endl;
vec += ofVec2f(3.f, 0.5f);
cout << "vec += ofVec2f(3.f, 0.5f); vec = " << vec << endl;
vec = vec * 2;
cout << "vec = vec * 2; vec = " << vec << endl;
vec = (1/2.0) * vec;
cout << "vec = (1/2.0) * vec; vec = " << vec << endl;
vec = vec / 2.f;
cout << "vec = vec / 2.f; vec = " << vec << endl;
cout << "ofVec2f tri(3.f, 4.f); tri.length() = " << tri.length() << endl;
}
```Output:
```
ofVec2f zero(); zero = 0, 0
ofVec2f vec(1.f, 1.5f); vec = 1, 1.5
vec += ofVec2f(3.f, 0.5f); vec = 4, 2
vec = vec * 2; vec = 8, 4
vec = (1/2.0) * vec; vec = 4, 2
vec = vec / 2.f; vec = 2, 1
ofVec2f tri(3.f, 4.f); tri.length() = 5
```