├── .gitignore ├── python ├── aabb.i ├── setup.py └── hard_disc.py ├── dox └── mainpage.dox ├── LICENSE ├── demos ├── vmd.tcl ├── MersenneTwister.h └── hard_disc.cc ├── .travis.yml ├── Makefile ├── README.md └── src ├── AABB.h └── AABB.cc /.gitignore: -------------------------------------------------------------------------------- 1 | doc/ 2 | lib/ 3 | obj/ 4 | header-only/ 5 | demos/hard_disc 6 | python/build 7 | python/aabb.py 8 | python/aabb.pyc 9 | python/aabb_wrap.cxx 10 | python/_aabb.so 11 | python/trajectory.xyz 12 | .compiler_flags 13 | .check_python 14 | trajectory.xyz 15 | *.dSYM 16 | -------------------------------------------------------------------------------- /python/aabb.i: -------------------------------------------------------------------------------- 1 | %module aabb 2 | 3 | %{ 4 | #include "../src/AABB.h" 5 | %} 6 | 7 | %include "std_vector.i" 8 | 9 | namespace std { 10 | %template(VectorBool) vector; 11 | %template(VectorDouble) vector; 12 | %template(VectorUnsignedInt) vector; 13 | }; 14 | 15 | %include "exception.i" 16 | 17 | %exception { 18 | try { 19 | $action 20 | } 21 | catch (const std::invalid_argument& e) { 22 | SWIG_exception(SWIG_ValueError, e.what()); 23 | } 24 | } 25 | 26 | %include "../src/AABB.h" 27 | -------------------------------------------------------------------------------- /dox/mainpage.dox: -------------------------------------------------------------------------------- 1 | /** \mainpage AABBCC Documentation 2 | 3 | \section intro_sec Introduction 4 | AABBCC is a C++ implementation of dynamic axis-aligned bounding box (AABB) 5 | trees, based on code from the Box2D physics engine (http://www.box2d.org). 6 | 7 | AABB trees enable fast and efficient neighbour searches for objects of 8 | arbitrary size and geometry. In comparison to cell lists, AABB trees are 9 | particularly efficient when the system is comprised of particle species with 10 | markedly different sizes, or when the density is highly inhomegeneous. 11 | 12 | This implementation can handle both periodic and non-periodic simulation boxes. 13 | The simulation box can also be partially periodic along specific axes. 14 | */ 15 | -------------------------------------------------------------------------------- /python/setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """ 4 | setup.py file for the AABB.cc python interface. 5 | """ 6 | 7 | from distutils.core import setup, Extension 8 | 9 | aabb_module = Extension('_aabb', 10 | sources = ['aabb_wrap.cxx', '../src/AABB.cc'], 11 | extra_compile_args = ["-O3", "-std=c++11"], 12 | ) 13 | 14 | setup (name = 'aabb', 15 | author = 'Lester Hedges', 16 | author_email = 'lester.hedges+aabbcc@gmail.com', 17 | description = 'AABB.cc python wrapper', 18 | ext_modules = [aabb_module], 19 | py_modules = ['aabb'], 20 | url = 'http://github.com/lohedges/aabbcc', 21 | license = 'Zlib', 22 | ) 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The zlib/libpng License (Zlib) 2 | 3 | Copyright (c) 2009 Erin Catto http://www.box2d.org 4 | Copyright (c) 2016 Lester Hedges 5 | 6 | This software is provided 'as-is', without any express or implied 7 | warranty. In no event will the authors be held liable for any damages 8 | arising from the use of this software. 9 | 10 | Permission is granted to anyone to use this software for any purpose, 11 | including commercial applications, and to alter it and redistribute it 12 | freely, subject to the following restrictions: 13 | 14 | 1. The origin of this software must not be misrepresented; you must not 15 | claim that you wrote the original software. If you use this software 16 | in a product, an acknowledgment in the product documentation would be 17 | appreciated but is not required. 18 | 19 | 2. Altered source versions must be plainly marked as such, and must not be 20 | misrepresented as being the original software. 21 | 22 | 3. This notice may not be removed or altered from any source distribution. 23 | 24 | This code was adapted from parts of the Box2D Physics Engine, http://www.box2d.org 25 | -------------------------------------------------------------------------------- /demos/vmd.tcl: -------------------------------------------------------------------------------- 1 | light 0 on 2 | light 1 on 3 | light 2 off 4 | light 3 off 5 | axes location off 6 | stage location off 7 | display projection orthographic 8 | mol modstyle 0 0 VDW 1 30 9 | set sel [atomselect top "name X"] 10 | atomselect0 set radius 0.5 11 | color Name X yellow 12 | set sel [atomselect top "name H"] 13 | atomselect1 set radius 5 14 | color Name H blue 15 | display depthcue off 16 | set minx 0 17 | set maxx 125.33 18 | set miny 0 19 | set maxy 125.33 20 | set minz 0 21 | set maxz 0 22 | draw materials off 23 | draw color white 24 | draw line "$minx $miny $minz" "$maxx $miny $minz" 25 | draw line "$minx $miny $minz" "$minx $maxy $minz" 26 | draw line "$minx $miny $minz" "$minx $miny $maxz" 27 | draw line "$maxx $miny $minz" "$maxx $maxy $minz" 28 | draw line "$maxx $miny $minz" "$maxx $miny $maxz" 29 | draw line "$minx $maxy $minz" "$maxx $maxy $minz" 30 | draw line "$minx $maxy $minz" "$minx $maxy $maxz" 31 | draw line "$minx $miny $maxz" "$maxx $miny $maxz" 32 | draw line "$minx $miny $maxz" "$minx $maxy $maxz" 33 | draw line "$maxx $maxy $maxz" "$maxx $maxy $minz" 34 | draw line "$maxx $maxy $maxz" "$minx $maxy $maxz" 35 | draw line "$maxx $maxy $maxz" "$maxx $miny $maxz" 36 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: xenial 2 | language: cpp 3 | 4 | notifications: 5 | email: false 6 | 7 | matrix: 8 | include: 9 | 10 | # GCC builds. 11 | 12 | - os: linux 13 | addons: 14 | apt: 15 | packages: 16 | - g++-4.9 17 | - swig 18 | env: 19 | - MATRIX_EVAL="CC=gcc-4.9 && CXX=g++-4.9" 20 | 21 | - os: linux 22 | addons: 23 | apt: 24 | packages: 25 | - g++-5 26 | - swig 27 | env: 28 | - MATRIX_EVAL="CC=gcc-5 && CXX=g++-5" 29 | 30 | - os: linux 31 | addons: 32 | apt: 33 | sources: 34 | - ubuntu-toolchain-r-test 35 | packages: 36 | - g++-6 37 | - swig 38 | env: 39 | - MATRIX_EVAL="CC=gcc-6 && CXX=g++-6" 40 | 41 | - os: linux 42 | addons: 43 | apt: 44 | sources: 45 | - ubuntu-toolchain-r-test 46 | packages: 47 | - g++-7 48 | - swig 49 | env: 50 | - MATRIX_EVAL="CC=gcc-7 && CXX=g++-7" 51 | 52 | # Clang builds. 53 | 54 | - os: linux 55 | addons: 56 | apt: 57 | packages: 58 | - clang-4.0 59 | - swig 60 | env: 61 | - MATRIX_EVAL="CC=clang-4.0 && CXX=clang++-4.0" 62 | 63 | - os: linux 64 | addons: 65 | apt: 66 | packages: 67 | - clang-5.0 68 | - swig 69 | env: 70 | - MATRIX_EVAL="CC=clang-5.0 && CXX=clang++-5.0" 71 | 72 | - os: linux 73 | addons: 74 | apt: 75 | packages: 76 | - clang-6.0 77 | - swig 78 | env: 79 | - MATRIX_EVAL="CC=clang-6.0 && CXX=clang++-6.0" 80 | 81 | before_install: 82 | - eval "${MATRIX_EVAL}" 83 | 84 | script: 85 | - make devel 86 | -------------------------------------------------------------------------------- /demos/MersenneTwister.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2016 Lester Hedges 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program. If not, see . 16 | */ 17 | 18 | #ifndef _MERSENNETWISTER_H 19 | #define _MERSENNETWISTER_H 20 | 21 | #include 22 | 23 | /*! \file MersenneTwister.h 24 | \brief A C++11 implementation of a Mersenne-Twister 25 | random number generator class. 26 | */ 27 | 28 | //! Mersenne-Twister class. 29 | class MersenneTwister 30 | { 31 | public: 32 | //! Constructor. 33 | MersenneTwister() 34 | { 35 | // Get a hardware random number and seed the generator. 36 | seed = std::random_device{}(); 37 | generator.seed(seed); 38 | 39 | /* Note that seeding the generator with a single 32-bit integer 40 | only allows for 2^32 initial states. 41 | 42 | For a better RNG, consider using the Permuted Congruential Generator 43 | 44 | http://www.pcg-random.org 45 | 46 | For details on the pitfalls of seeding the C++11 mt19937 generator, 47 | see 48 | 49 | http://www.pcg-random.org/posts/cpp-seeding-surprises.html 50 | 51 | To enable good seeding with mt19937, consider using randutils.hpp 52 | 53 | https://gist.github.com/imneme/540829265469e673d045 54 | */ 55 | } 56 | 57 | //! Overloaded () operator. 58 | /*! \return A uniform random double in range [0-1]. */ 59 | double operator()() 60 | { 61 | return default_uniform_real_distribution(generator); 62 | } 63 | 64 | //! Generate a random integer between min and max (inclusive). 65 | /*! \param min 66 | The minium of the range. 67 | 68 | \param max 69 | The maxium of the range. 70 | 71 | \return 72 | The uniform random integer. 73 | */ 74 | int integer(int min, int max) 75 | { 76 | return std::uniform_int_distribution{min, max}(generator); 77 | } 78 | 79 | //! Generate a random number from a normal distribution with 80 | /*! zero mean and unit standard deviation. 81 | \return 82 | A random number drawn from the normal distribution. 83 | */ 84 | double normal() 85 | { 86 | return default_normal_distribution(generator); 87 | } 88 | 89 | //! Generate a random number from a normal distribution. 90 | /*! \param mean 91 | The mean of the the normal distribution. 92 | 93 | \param stdDev 94 | The standard deviation of the normal distribution. 95 | 96 | \return 97 | A random number drawn from the normal distribution. 98 | */ 99 | double normal(double mean, double stdDev) 100 | { 101 | return std::normal_distribution{mean, stdDev}(generator); 102 | } 103 | 104 | //! Get the random number generator seed. 105 | /*! \return seed 106 | The generator seed. 107 | */ 108 | unsigned int getSeed() 109 | { 110 | return seed; 111 | } 112 | 113 | //! Seed the random number generator. 114 | /*! \param seed_ 115 | The new seed. 116 | */ 117 | void setSeed(unsigned int seed_) 118 | { 119 | seed = seed_; 120 | generator.seed(seed); 121 | } 122 | 123 | private: 124 | /// The Mersenne-Twister generator. 125 | std::mt19937 generator; 126 | 127 | /// Default uniform_real distribution [0-1]. 128 | std::uniform_real_distribution default_uniform_real_distribution{0.0, 1.0}; 129 | 130 | /// Default normal distribution with zero mean and unit standard deviation. 131 | std::normal_distribution default_normal_distribution{0.0, 1.0}; 132 | 133 | /// The random number seed. 134 | unsigned int seed; 135 | }; 136 | 137 | #endif /* _MERSENNETWISTER_H */ 138 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # AABB.cc Makefile 2 | 3 | # Copyright (c) 2016 Lester Hedges 4 | 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 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 General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | 18 | ################################ INFO ####################################### 19 | 20 | # This Makefile can be used to build a CXX project library along with its 21 | # demos and documentation. For detailed information on using the Makefile 22 | # run make without a target, i.e. simply run make at your command prompt. 23 | # 24 | # Makefile style adapted from http://clarkgrubb.com/make-file-style-guide 25 | # Conventions: 26 | # - Environment and Makefile variables are in upper case, user 27 | # defined variables are in lower case. 28 | # - Variables are declared using the immediate assignment operator := 29 | 30 | ############################### MACROS ######################################## 31 | 32 | define colorecho 33 | @if hash tput 2> /dev/null; then \ 34 | if [[ -t 1 ]]; then \ 35 | tput setaf $1; \ 36 | echo $2; \ 37 | tput sgr0; \ 38 | else \ 39 | echo $2; \ 40 | fi \ 41 | else \ 42 | echo $2; \ 43 | fi 44 | endef 45 | 46 | define boldcolorecho 47 | @if hash tput 2> /dev/null; then \ 48 | if [[ -t 1 ]]; then \ 49 | tput bold; \ 50 | tput setaf $1; \ 51 | echo $2; \ 52 | tput sgr0; \ 53 | else \ 54 | echo $2; \ 55 | fi \ 56 | else \ 57 | echo $2; \ 58 | fi 59 | endef 60 | 61 | ############################## VARIABLES ###################################### 62 | 63 | # Set shell to bash. 64 | SHELL := bash 65 | 66 | # Suppress display of executed commands. 67 | .SILENT: 68 | 69 | # Default goal will print the help message. 70 | .DEFAULT_GOAL := help 71 | 72 | # Project name. 73 | project := aabb 74 | 75 | # Upper case project name (for use in library header file). 76 | project_upper := `echo $(project) | tr a-z A-Z` 77 | 78 | # C++ compiler. 79 | CXX := g++ 80 | 81 | # Installation path. 82 | PREFIX := /usr/local 83 | 84 | # Python version 85 | PYTHON := 2.7 86 | 87 | # External libraries. 88 | LIBS := 89 | 90 | # Path for source files. 91 | src_dir := src 92 | 93 | # Path for demo code. 94 | demo_dir := demos 95 | 96 | # Path for object files. 97 | obj_dir := obj 98 | 99 | # Path for the library. 100 | lib_dir := lib 101 | 102 | # Path for the python wrapper. 103 | python_dir := python 104 | 105 | # Path for the header-only library. 106 | header_only_dir := header-only 107 | 108 | # Generate library target name. 109 | library := $(lib_dir)/lib$(project).a 110 | 111 | # Header only library file. 112 | header_only_lib := $(header_only_dir)/$(project_upper).hpp 113 | 114 | # Install command. 115 | install_cmd := install 116 | 117 | # Install flags for executables. 118 | iflags_exec := -m 0755 119 | 120 | # Install flags for non-executable files. 121 | iflags := -m 0644 122 | 123 | # Git commit information. 124 | commit := $(shell git describe --abbrev=4 --dirty --always --tags 2> /dev/null) 125 | 126 | # Git branch information. 127 | branch := $(shell git rev-parse --abbrev-ref HEAD 2> /dev/null) 128 | 129 | # Python binary. 130 | python_binary := $(shell which python$(PYTHON)) 131 | 132 | # SWIG binary. 133 | swig_binary := $(shell which swig) 134 | 135 | # C++ compiler flags for development build. 136 | cxxflags_devel := -O0 -std=c++11 -g -Wall -Isrc -DCOMMIT=\"$(commit)\" -DBRANCH=\"$(branch)\" $(OPTFLAGS) 137 | 138 | # C++ compiler flags for release build. 139 | cxxflags_release := -O3 -std=c++11 -DNDEBUG -Isrc -DCOMMIT=\"$(commit)\" -DBRANCH=\"$(branch)\" $(OPTFLAGS) 140 | 141 | # Default to release build. 142 | CXXFLAGS := $(cxxflags_release) 143 | 144 | # The C++ header, source, object, and dependency files. 145 | headers := $(wildcard $(src_dir)/*.h) 146 | sources := $(wildcard $(src_dir)/*.cc) 147 | temp := $(patsubst %.cc,%.o,$(sources)) 148 | objects := $(subst $(src_dir),$(obj_dir),$(temp)) 149 | -include $(subst .o,.d,$(objects)) 150 | 151 | # Source files and executable names for demos. 152 | demo_sources := $(wildcard $(demo_dir)/*.cc) 153 | demos := $(patsubst %.cc,%,$(demo_sources)) 154 | 155 | # Doxygen files. 156 | dox_files := $(wildcard dox/*.dox) 157 | 158 | ############################### TARGETS ####################################### 159 | 160 | # Print help message. 161 | .PHONY: help 162 | help: 163 | $(call boldcolorecho, 4, "About") 164 | @echo " This Makefile can be used to build the $(project) library along with its" 165 | @echo " demos and documentation." 166 | @echo 167 | $(call boldcolorecho, 4, "Targets") 168 | @echo " help --> print this help message" 169 | @echo " build --> build library and demos (default=release)" 170 | @echo " devel --> build using development compiler flags (debug)" 171 | @echo " release --> build using release compiler flags (optmized)" 172 | @echo " python --> build the python wrapper" 173 | @echo " header-only --> create a header-only version of the library" 174 | @echo " doc --> generate source code documentation with doxygen" 175 | @echo " clean --> remove object and dependency files" 176 | @echo " clobber --> remove all files generated by make" 177 | @echo " install --> install library, demos, and documentation" 178 | @echo " uninstall --> uninstall library, demos, and documentation" 179 | 180 | # Set development compilation flags and build. 181 | devel: CXXFLAGS := $(cxxflags_devel) 182 | devel: build 183 | 184 | # Set release compilation flags and build. 185 | release: CXXFLAGS := $(cxxflags_release) 186 | release: build 187 | 188 | # Print compiler flags. 189 | devel release: 190 | $(call colorecho, 5, "--> CXXFLAGS: $(CXXFLAGS)") 191 | 192 | # Save compiler flags to file if they differ from previous build. 193 | # This target ensures that all object files are recompiled if the flags change. 194 | .PHONY: force 195 | .compiler_flags: force 196 | @echo '$(CXXFLAGS)' | cmp -s - $@ || echo '$(CXXFLAGS)' > $@ 197 | 198 | # Check that python and swig binaries are present. 199 | # This target ensures that all python demos are recompiled if the .check_python file changes. 200 | .PHONY: force 201 | .check_python: force 202 | @echo "Python found." | cmp -s - $@ || \ 203 | if [ "$(python_binary)" = "" ] || [ "$(swig_binary)" = "" ] ; then \ 204 | echo "Python not found."; \ 205 | exit 1; \ 206 | else echo "Python found."; \ 207 | fi > $@ 208 | 209 | # Compile object files. 210 | # Autodepenencies are handled using a recipe taken from 211 | # http://scottmcpeak.com/autodepend/autodepend.html 212 | $(obj_dir)/%.o: $(src_dir)/%.cc .compiler_flags 213 | $(call colorecho, 2, "--> Building CXX object $*.o") 214 | $(CXX) $(CXXFLAGS) -c -o $(obj_dir)/$*.o $(src_dir)/$*.cc 215 | $(CXX) -MM $(CXXFLAGS) $(src_dir)/$*.cc > $*.d 216 | @mv -f $*.d $*.d.tmp 217 | @sed -e 's|.*:|$(obj_dir)/$*.o:|' < $*.d.tmp > $(obj_dir)/$*.d 218 | @sed -e 's/.*://' -e 's/\\$$//' < $*.d.tmp | fmt -1 | \ 219 | sed -e 's/^ *//' -e 's/$$/:/' >> $(obj_dir)/$*.d 220 | @rm -f $*.d.tmp 221 | 222 | # Build the library and demos. 223 | .PHONY: build 224 | build: $(obj_dir) $(library) $(demos) python 225 | 226 | # Create output directory for object and dependency files. 227 | $(obj_dir): 228 | mkdir $(obj_dir) 229 | 230 | # Build the static library. 231 | $(library): $(objects) 232 | $(call colorecho, 1, "--> Linking CXX static library $(library)") 233 | mkdir -p $(lib_dir) 234 | ar rcs $@ $(objects) 235 | ranlib $@ 236 | 237 | # Compile demonstration code. 238 | $(demos): %: %.cc $(library) 239 | $(call colorecho, 1, "--> Linking CXX executable $@") 240 | $(CXX) $(CXXFLAGS) $@.cc $(library) $(LIBS) $(LDFLAGS) -o $@ 241 | 242 | # Build the python wrapper. 243 | .PHONY: python 244 | python: .check_python $(python_dir)/aabb.i $(python_dir)/setup.py 245 | $(call colorecho, 2, "--> Building Python wrapper") 246 | cd $(python_dir) ;\ 247 | $(swig_binary) -builtin -c++ -python aabb.i ;\ 248 | $(python_binary) setup.py -q build_ext --inplace 249 | 250 | # Create the header only library. 251 | .PHONY: header-only 252 | header-only: $(headers) $(sources) 253 | mkdir -p $(header_only_dir) 254 | head -n 499 src/AABB.h > $(header_only_lib) 255 | echo >> $(header_only_lib) 256 | tail +29 src/AABB.cc >> $(header_only_lib) 257 | echo >> $(header_only_lib) 258 | echo "#endif /* _AABB_H */" >> $(header_only_lib) 259 | 260 | # Build documentation using Doxygen. 261 | doc: $(headers) $(sources) $(dox_files) 262 | $(call colorecho, 4, "--> Generating CXX source documentation with Doxygen") 263 | doxygen dox/Doxyfile 264 | 265 | # Install the library and demos. 266 | .PHONY: install 267 | install: build doc 268 | $(call colorecho, 3, "--> Installing CXX static library $(library) to $(PREFIX)/lib") 269 | $(call colorecho, 3, "--> Installing Python wrapper to $(PREFIX)/lib/python$(PYTHON)") 270 | $(call colorecho, 3, "--> Installing CXX demos $(demos) to $(PREFIX)/share/$(project)-demos") 271 | $(call colorecho, 3, "--> Installing CXX Doxygen documentation to $(PREFIX)/share/doc/$(project)") 272 | $(install_cmd) -d $(iflags_exec) $(PREFIX)/lib 273 | $(install_cmd) -d $(iflags_exec) $(PREFIX)/lib/python$(PYTHON) 274 | $(install_cmd) -d $(iflags_exec) $(PREFIX)/include/$(project) 275 | $(install_cmd) -d $(iflags_exec) $(PREFIX)/share/$(project)-demos 276 | $(install_cmd) -d $(iflags_exec) $(PREFIX)/share/doc/$(project) 277 | $(install_cmd) $(iflags) $(library) $(PREFIX)/lib 278 | $(install_cmd) $(iflags) $(python_dir)/aabb.py $(PREFIX)/lib/python$(PYTHON) 279 | $(install_cmd) $(iflags_exec) $(python_dir)/_aabb.so $(PREFIX)/lib/python$(PYTHON) 280 | $(install_cmd) $(iflags) $(headers) $(PREFIX)/include/$(project) 281 | $(install_cmd) $(iflags) $(demo_sources) $(PREFIX)/share/$(project)-demos 282 | $(install_cmd) $(iflags_exec) $(demos) $(PREFIX)/share/$(project)-demos 283 | cp -r doc/html $(PREFIX)/share/doc/$(project) 284 | 285 | # Uninstall the library and demos. 286 | .PHONY: uninstall 287 | uninstall: 288 | $(call colorecho, 3, "--> Uninstalling CXX static library $(library) from $(PREFIX)/lib") 289 | $(call colorecho, 3, "--> Uninstalling Python wrapper from $(PREFIX)/lib/python$(PYTHON)") 290 | $(call colorecho, 3, "--> Uninstalling CXX demos $(demos) from $(PREFIX)/share/$(project)-demos") 291 | $(call colorecho, 3, "--> Uninstalling CXX Doxygen documentation from $(PREFIX)/share/doc/$(project)") 292 | rm -f $(PREFIX)/$(library) 293 | rm -f $(PREFIX)/lib/python$(PYTHON)/aabb.py 294 | rm -f $(PREFIX)/lib/python$(PYTHON)/_aabb.so 295 | rm -rf $(PREFIX)/include/$(project) 296 | rm -rf $(PREFIX)/share/$(project)-demos 297 | rm -rf $(PREFIX)/share/doc/$(project) 298 | 299 | # Clean up object and dependecy files. 300 | .PHONY: clean 301 | clean: 302 | $(call colorecho, 6, "--> Cleaning CXX object and dependency files") 303 | rm -rf $(obj_dir) 304 | 305 | # Clean up everything produced by make. 306 | .PHONY: clobber 307 | clobber: 308 | $(call colorecho, 6, "--> Cleaning all output files") 309 | rm -rf $(obj_dir) 310 | rm -rf $(lib_dir) 311 | rm -rf $(python_dir)/build 312 | rm -rf $(python_dir)/_aabb.* 313 | rm -rf $(python_dir)/aabb_wrap.cxx 314 | rm -rf $(python_dir)/aabb.py* 315 | rm -rf $(python_dir)/__pycache__ 316 | rm -rf $(header_only_dir) 317 | rm -rf doc 318 | rm -f $(demos) 319 | rm -rf $(demo_dir)/*dSYM 320 | rm -f .compiler_flags 321 | rm -f .check_python 322 | 323 | .PHONY: sandwich 324 | sandwich: 325 | if [ "$$(id -u)" != "0" ]; then \ 326 | echo " What? Make it yourself." ;\ 327 | else \ 328 | echo " ____" ;\ 329 | echo " .----------' '-." ;\ 330 | echo " / . ' . \\" ;\ 331 | echo " / ' . /|" ;\ 332 | echo " / . \ /" ;\ 333 | echo " / ' . . . || |" ;\ 334 | echo " /.___________ ' / //" ;\ 335 | echo " |._ '------'| /|" ;\ 336 | echo " '.............______.-' /" ;\ 337 | echo " |-. | /" ;\ 338 | echo " \`\"\"\"\"\"\"\"\"\"\"\"\"\"-.....-'" ;\ 339 | fi; \ 340 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AABB.cc 2 | 3 | Copyright © 2016-2018 [Lester Hedges](http://lesterhedges.net) 4 | 5 | [![Build Status](https://travis-ci.org/lohedges/aabbcc.svg?branch=master)](https://travis-ci.org/lohedges/aabbcc) 6 | 7 | Released under the [Zlib](http://zlib.net/zlib_license.html) license. 8 | 9 | ## About 10 | A C++ implementation of a dynamic bounding volume hierarchy 11 | ([BVH](https://en.wikipedia.org/wiki/Bounding_volume_hierarchy)) using 12 | axis-aligned bounding boxes ([AABBs](https://en.wikipedia.org/wiki/Minimum_bounding_box)). 13 | The data structure provides an efficient way of detecting potential overlap 14 | between objects of arbitrary shape and size and is commonly used in 15 | computer game engines for collision detection and ray tracing. 16 | 17 | Because of their speed and flexibility, AABB trees are also well suited 18 | to overlap detection in physics applications, such as molecular simulation. 19 | They are particularly helpful for systems where there is a large size disparity 20 | between particle species, or whenever the particle density is extremely 21 | inhomogeneous. In such situations, traditional neighbour finding tools, such 22 | as [cell lists](https://en.wikipedia.org/wiki/Cell_lists), can become extremely 23 | inefficient (both in terms of memory footprint, and search speed). A good 24 | overview of the pros and cons of various neighbour finding algorithms 25 | can be found [here](http://hoomd-blue.readthedocs.io/en/stable/nlist.html). 26 | (Note that this only discusses the cost of _querying_ different data 27 | structures, not the additional overhead of building them, or maintaining 28 | them as objects move around.) 29 | 30 | In statistical physics, a common means of approximating a bulk (infinite) 31 | system is through the use of [periodic boundary conditions](https://en.wikipedia.org/wiki/Periodic_boundary_conditions). 32 | Here, particles that are on opposite sides of the unit box can interact through 33 | its periodic image. This library supports periodic and non-periodic systems 34 | in an arbitrary number of dimensions (>= 2). Support is also provided for simulation 35 | boxes that are partially periodic, i.e. periodic along specific axes. At present, 36 | only orthorhombic simulation boxes are supported. 37 | 38 | The code in this library was adapted from parts of the [Box2D](http://www.box2d.org) 39 | physics engine. 40 | 41 | ## Installation 42 | A `Makefile` is included for building and installing the AABB library. 43 | 44 | To compile and install the library, documentation, python wrapper, and demos: 45 | 46 | ```bash 47 | make build 48 | make install 49 | ``` 50 | 51 | By default, the library installs to `/usr/local`. Therefore, you may need admin 52 | privileges for the final `make install` step above. An alternative is to change 53 | the install location: 54 | 55 | ```bash 56 | make PREFIX=MY_INSTALL_DIR install 57 | ``` 58 | If you would rather use a header-only version of the library in your application, 59 | simply run: 60 | 61 | ```bash 62 | make header-only 63 | ``` 64 | 65 | The resulting library header, `header-only/AABB.hpp`, can be directly included 66 | in your source code without the need for compiling and linking. 67 | 68 | Further details on using the Makefile can be found by running make without 69 | a target, i.e. 70 | 71 | ```bash 72 | make 73 | ``` 74 | 75 | ## Compiling and linking 76 | To use the library with a C/C++ code first include the library header file 77 | in the code. 78 | 79 | ```cpp 80 | #include 81 | ``` 82 | 83 | Then to compile, we can use something like the following: 84 | 85 | ```bash 86 | g++ example.cc -laabb 87 | ``` 88 | 89 | This assumes that we have used the default install location `/usr/local`. If 90 | we specify an install location, we would use a command more like the following: 91 | 92 | ```bash 93 | g++ example.cc -I/my/path/include -L/my/path/lib -laabb 94 | ``` 95 | 96 | ## Python wrapper 97 | A python wrapper can be built using: 98 | 99 | ```bash 100 | make python 101 | ``` 102 | 103 | You will require [python2.7](https://www.python.org/download/releases/2.7) 104 | (and the development files if your package manager separates them) and 105 | [SWIG](http://www.swig.org). To use the module you will need the python file 106 | `aabb.py` and the shared object `_aabb.so` from the `python` directory. 107 | If you wish to use a different version of python, simply override the 108 | `PYTHON` make variable on the command line, e.g. 109 | 110 | ```bash 111 | make PYTHON=3.5 python 112 | ``` 113 | 114 | (Note that you'll also need to update the shebang at the top of 115 | [hard_disc.py](python/hard_disc.py) to reflect your changes in order for the 116 | python demo to work.) 117 | 118 | ## Example 119 | Let's consider a two-component system of hard discs in two dimensions, where 120 | one species is much larger than the other. Making use of AABB trees, we can 121 | efficiently search for potential overlaps between discs by decomposing the 122 | system into its two constituent species and constructing a tree for each one. 123 | To test overlaps for any given disc, we simply query the two trees 124 | independently in order to find candidates. This decomposition ensures that 125 | each AABB tree has a well defined length scale, making it simple to construct 126 | and quick to query. 127 | 128 | The image below shows the example hard disc system (left) and the AABB tree 129 | structures for each species (middle and right). Each leaf node in a tree is 130 | the AABB of an individual disc. Moving up the tree, AABBs are grouped together 131 | into larger bounding volumes in a recursive fashion, leading to a single AABB 132 | enclosing all of the discs at the root. The box outline in the left-hand image 133 | shows the periodic boundary of the system. 134 | 135 | ![AABBs for a binary hard disc system.](https://raw.githubusercontent.com/lohedges/assets/master/aabbcc/images/aabb.png) 136 | 137 | To query overlaps between discs we start at the root node and traverse the 138 | tree. At each node we test whether the current AABB overlaps the AABB of the 139 | chosen disc. If so, we add the two children of the node to the stack of nodes 140 | to test. Whenever we reach a leaf node with which an overlap is found we record 141 | a potential overlap with that disc (we know that the AABBs of the discs overlap, 142 | but we need still need to check that discs themselves actually overlap). The 143 | animation below shows an example of such a query. The disc of interest is 144 | highlighted in green and the boundary of the periodic simulation box is shown 145 | in orange. At each stage of the search the AABB of the current node in the tree 146 | is shown in white. Leaf nodes that overlap with the trial disc are highlighted 147 | green. Note that the green leaf node on the right-hand edge of the simulation 148 | box overlaps through the periodic boundary. 149 | 150 |
151 | Querying an AABB tree for overlaps. 152 |
153 | 154 | You may be wondering why the AABBs shown in the previous animation are not 155 | the minimum enclosing bounding box for each disc. This is a trick that is 156 | used to avoid frequent updates of the AABB tree during dynamics (movement 157 | of the discs). Whenever an AABB changes position we need to delete it from 158 | the tree then reinsert the new one (at the updated position). This can be a 159 | costly operation. By "fattening" the AABBs a small amount it is possible to 160 | make many displacements of the objects before an update is triggered, i.e. 161 | when one of the discs moves outside of its fattened AABB. During dynamics it 162 | is also possible for the tree to become unbalanced, leading to increasingly 163 | inefficient queries. Here trees are balanced using a surface area heuristic 164 | and active balancing is handled via tree rotations. The animation below shows 165 | an example of a hard disc simulation. Dynamic AABB trees were used to maintain 166 | a configuration of non-overlapping discs throughout the trajectory. 167 | 168 |
169 | Dynamics using AABB trees for overlap tests. 170 |
171 | 172 | The code used to create the above animation can be found at 173 | `demos/hard_disc.cc`. When the library is built, you can run the demo 174 | and use [VMD](http://www.ks.uiuc.edu/Research/vmd/) to view the trajectory 175 | as follows: 176 | 177 | ```bash 178 | ./demos/hard_disc 179 | vmd trajectory.xyz -e demos/vmd.tcl 180 | ``` 181 | 182 | A python version of the demo can be found at `python/hard_disc.py`. This 183 | provides an example of how to use the python wrapper module. 184 | 185 | ## Usage 186 | There are several steps that go into building and using an AABB tree. Below 187 | are some examples showing how to use the various objects within the library. 188 | 189 | ### AABB 190 | This should be the minimum enclosing axis-aligned bounding box for an object 191 | in your simulation box. There is no need to fatten the AABB; this will be done 192 | when an object is inserted into the AABB tree. For example, to create an AABB 193 | for a two-dimensional disc we could do the following: 194 | 195 | ```cpp 196 | // Particle radius. 197 | double radius = 1.0; 198 | 199 | // Set the particle position. 200 | std::vector position({10, 10}); 201 | 202 | // Compute lower and upper AABB bounds. 203 | std::vector lowerBound({position[0] - radius, position[1] - radius}); 204 | std::vector upperBound({position[0] + radius, position[1] + radius}); 205 | 206 | // Create the AABB. 207 | aabb::AABB aabb(lowerBound, upperBound); 208 | ``` 209 | 210 | (While we refer to _particles_ in this example, in practice a particle could 211 | be any object, e.g. a sprite in a computer game.) 212 | 213 | ### Tree 214 | #### Initialising a tree 215 | To instantiate dynamic AABB trees for a periodic two-component system in 216 | two dimensions: 217 | 218 | ```cpp 219 | // Fattening factor. 220 | double fatten = 0.1; 221 | 222 | // Periodicity of the simulation box. 223 | std::vector periodicity({true, true}); 224 | 225 | // Size of the simulation box. 226 | std::vector boxSize({100, 100}); 227 | 228 | // Number of small discs. 229 | unsigned int nSmall = 100; 230 | 231 | // Number of large discs. 232 | unsigned int nLarge = 10; 233 | 234 | // Create the AABB trees. 235 | aabb::Tree treeSmall(2, fatten, periodicity, boxSize, nSmall); 236 | aabb::Tree treeLarge(2, fatten, periodicity, boxSize, nLarge); 237 | ``` 238 | 239 | Many of the arguments to the constructor of `Tree` are optional, see the 240 | [Doxygen](http://www.doxygen.nl) documentation for details. 241 | 242 | Note that both the periodicity and box size can be changed on-the-fly, e.g. 243 | for changing the box volume during a constant pressure simulation. See the 244 | `setPeriodicity` and `setBoxSize` methods for details. 245 | 246 | #### Inserting a particle 247 | To insert a particle (object) into the tree: 248 | 249 | ```cpp 250 | // Particle radius. 251 | double radius = 1.0; 252 | 253 | // Particle index (key). 254 | unsigned int index = 1; 255 | 256 | // Set the particle position. 257 | std::vector position({10.0, 10.0}); 258 | 259 | // Compute lower and upper AABB bounds. 260 | std::vector lowerBound({position[0] - radius, position[1] - radius}); 261 | std::vector upperBound({position[0] + radius, position[1] + radius}); 262 | 263 | // Insert particle into the tree. 264 | tree.insertParticle(index, position, lowerBound, upperBound); 265 | ``` 266 | 267 | Here `index` is a key that is used to create a map between particles and nodes 268 | in the AABB tree. The key should be unique to the particle and can take any value 269 | between 0 and `std::numeric_limits::max() - 1`. 270 | 271 | For spherical objects, the insertion can be simplified to: 272 | 273 | ```cpp 274 | tree.insertParticle(index, position, radius); 275 | ``` 276 | 277 | #### Removing a particle 278 | If you are performing simulations using the [grand canonical ensemble](https://en.wikipedia.org/wiki/Grand_canonical_ensemble) 279 | you may wish to remove particles from the tree. To do so: 280 | 281 | ```cpp 282 | tree.removeParticle(index); 283 | ``` 284 | 285 | where `index` is the key for the particle to be removed. (You'll need to 286 | keep track of the keys). 287 | 288 | #### Querying the tree 289 | You can query the tree for overlaps with a specific particle, or for overlaps 290 | with an arbitrary AABB object. The `query` method returns a vector containing 291 | the indices of the AABBs that overlap. You'll then need to test the objects 292 | enclosed by these AABBs for actual overlap with the particle of interest. 293 | (using your own overlap code). 294 | 295 | For a particle already in the tree: 296 | 297 | ```cpp 298 | // Query AABB overlaps for particle with key 10. 299 | std::vector particles = tree.query(10); 300 | ``` 301 | 302 | For an arbitrary AABB: 303 | 304 | ```cpp 305 | // Set the AABB bounds. 306 | std::vector lowerBound({5, 5}}; 307 | std::vector upperBound({10, 10}}; 308 | 309 | // Create the AABB. 310 | aabb::AABB aabb(lowerBound, upperBound); 311 | 312 | // Query the tree for overlap with the AABB. 313 | std::vector particles = tree.query(aabb); 314 | ``` 315 | 316 | ## Tests 317 | The AABB tree is self-testing if the library is compiled in development mode, i.e. 318 | 319 | ```bash 320 | make devel 321 | ``` 322 | 323 | ## Disclaimer 324 | Please be aware that this a working repository so the code should be used at 325 | your own risk. 326 | 327 | It would be great to hear from you if this library was of use in your research. 328 | 329 | Email bugs, comments, and suggestions to lester.hedges+aabbcc@gmail.com. 330 | -------------------------------------------------------------------------------- /python/hard_disc.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | 3 | # Copyright (c) 2016-2018 Lester Hedges 4 | # 5 | # This software is provided 'as-is', without any express or implied 6 | # warranty. In no event will the authors be held liable for any damages 7 | # arising from the use of this software. 8 | 9 | # Permission is granted to anyone to use this software for any purpose, 10 | # including commercial applications, and to alter it and redistribute it 11 | # freely, subject to the following restrictions: 12 | # 13 | # 1. The origin of this software must not be misrepresented; you must not 14 | # claim that you wrote the original software. If you use this software 15 | # in a product, an acknowledgment in the product documentation would be 16 | # appreciated but is not required. 17 | # 18 | # 2. Altered source versions must be plainly marked as such, and must not be 19 | # misrepresented as being the original software. 20 | # 21 | # 3. This notice may not be removed or altered from any source distribution. 22 | 23 | """An example showing how to use the AABB.cc Python wrapper.""" 24 | 25 | # Note: 26 | # SWIG allows us direct access to STL vectors in python. See aabb.i for 27 | # full details of the mappings. 28 | # 29 | # As an example, you can create a STL vector containing 10 doubles 30 | # as follows: 31 | # 32 | # doubleVector = aabb.VectorDouble(10) 33 | # 34 | # You can then access most of the usual member functions, e.g. to 35 | # print the size of the vector: 36 | # 37 | # print doubleVector.size() 38 | 39 | from __future__ import print_function 40 | 41 | import aabb 42 | import math 43 | import random 44 | 45 | # Test whether two discs overlap. 46 | def overlaps(position1, position2, periodicity, boxSize, cutOff): 47 | # Compute separation vector. 48 | separation = [0] * 2 49 | separation[0] = position1[0] - position2[0] 50 | separation[1] = position1[1] - position2[1] 51 | 52 | # Find minimum image separation. 53 | minimumImage(separation, periodicity, boxSize) 54 | 55 | # Squared distance between objects. 56 | rSqd = separation[0]*separation[0] + separation[1]*separation[1] 57 | 58 | if rSqd < cutOff: 59 | return True 60 | else: 61 | return False 62 | 63 | # Compute the minimum image separation vector between disc centres. 64 | def minimumImage(separation, periodicity, boxSize): 65 | for i in range(0, 2): 66 | if separation[i] < -0.5*boxSize[i]: 67 | separation[i] += periodicity[i]*boxSize[i] 68 | elif separation[i] >= 0.5*boxSize[i]: 69 | separation[i] -= periodicity[i]*boxSize[i] 70 | 71 | # Apply periodic boundary conditions. 72 | def periodicBoundaries(position, periodicity, boxSize): 73 | for i in range(0, 2): 74 | if position[i] < 0: 75 | position[i] += periodicity[i]*boxSize[i] 76 | elif position[i] >= boxSize[i]: 77 | position[i] -= periodicity[i]*boxSize[i] 78 | 79 | # Print current configuration to VMD trajectory file. 80 | def printVMD(fileName, positionsSmall, positionsLarge): 81 | with open(fileName, 'a') as trajectoryFile: 82 | trajectoryFile.write('%lu\n' % (len(positionsSmall) + len(positionsLarge))) 83 | trajectoryFile.write('\n') 84 | for i in range(0, len(positionsSmall)): 85 | trajectoryFile.write('0 %lf %lf 0\n' % (positionsSmall[i][0], positionsSmall[i][1])) 86 | for i in range(0, len(positionsLarge)): 87 | trajectoryFile.write('1 %lf %lf 0\n' % (positionsLarge[i][0], positionsLarge[i][1])) 88 | 89 | ############################################################# 90 | # Set parameters, initialise variables and objects. # 91 | ############################################################# 92 | 93 | nSweeps = 100000 # The number of Monte Carlo sweeps. 94 | sampleInterval = 100 # The number of sweeps per sample. 95 | nSmall = 1000 # The number of small particles. 96 | nLarge = 100 # The number of large particles. 97 | diameterSmall = 1 # The diameter of the small particles. 98 | diameterLarge = 10 # The diameter of the large particles. 99 | density = 0.1 # The system density 100 | maxDisp = 0.1 # Maximum trial displacement (in units of diameter). 101 | 102 | # Total particles. 103 | nParticles = nSmall + nLarge 104 | 105 | # Number of samples. 106 | nSamples = math.floor(nSweeps / sampleInterval) 107 | 108 | # Particle radii. 109 | radiusSmall = 0.5 * diameterSmall 110 | radiusLarge = 0.5 * diameterLarge 111 | 112 | # Output formatting flag. 113 | format = int(math.floor(math.log10(nSamples))) 114 | 115 | # Set the periodicity of the simulation box. 116 | periodicity = aabb.VectorBool(2) 117 | periodicity[0] = True 118 | periodicity[1] = True 119 | 120 | # Work out base length of the simulation box. 121 | baseLength = math.pow((math.pi*(nSmall*diameterSmall + nLarge*diameterLarge))/(4*density), 0.5) 122 | boxSize = aabb.VectorDouble(2) 123 | boxSize[0] = baseLength 124 | boxSize[1] = baseLength 125 | 126 | # Seed the random number generator. 127 | random.seed() 128 | 129 | # Initialise the AABB trees. 130 | treeSmall = aabb.Tree(2, maxDisp, periodicity, boxSize, nSmall) 131 | treeLarge = aabb.Tree(2, maxDisp, periodicity, boxSize, nLarge) 132 | 133 | # Initialise particle position vectors. 134 | positionsSmall = [[0 for i in range(2)] for j in range(nSmall)] 135 | positionsLarge = [[0 for i in range(2)] for j in range(nLarge)] 136 | 137 | ############################################################# 138 | # Generate the initial AABB trees. # 139 | ############################################################# 140 | 141 | # First the large particles. 142 | print('Inserting large particles into AABB tree ...') 143 | 144 | # Cut-off distance. 145 | cutOff = 2 * radiusLarge 146 | cutOff *= cutOff 147 | 148 | # Initialise the position vector. 149 | position = aabb.VectorDouble(2) 150 | 151 | # Initialise bounds vectors. 152 | lowerBound = aabb.VectorDouble(2) 153 | upperBound = aabb.VectorDouble(2) 154 | 155 | for i in range(0, nLarge): 156 | # Insert the first particle directly. 157 | if i == 0: 158 | # Generate a random particle position. 159 | position[0] = boxSize[0]*random.random() 160 | position[1] = boxSize[1]*random.random() 161 | 162 | # Check for overlaps. 163 | else: 164 | # Initialise the overlap flag. 165 | isOverlap = True 166 | 167 | while isOverlap: 168 | # Generate a random particle position. 169 | position[0] = boxSize[0]*random.random() 170 | position[1] = boxSize[1]*random.random() 171 | 172 | # Compute the lower and upper AABB bounds. 173 | lowerBound[0] = position[0] - radiusLarge 174 | lowerBound[1] = position[1] - radiusLarge 175 | upperBound[0] = position[0] + radiusLarge 176 | upperBound[1] = position[1] + radiusLarge 177 | 178 | # Generate the AABB. 179 | AABB = aabb.AABB(lowerBound, upperBound) 180 | 181 | # Query AABB overlaps. 182 | particles = treeLarge.query(AABB) 183 | 184 | # Flag as no overlap (yet). 185 | isOverlap = False 186 | 187 | # Test overlap. 188 | for j in range(0, len(particles)): 189 | if overlaps(position, positionsLarge[particles[j]], periodicity, boxSize, cutOff): 190 | isOverlap = True 191 | break 192 | 193 | # Insert the particle into the tree. 194 | treeLarge.insertParticle(i, position, radiusLarge) 195 | 196 | # Store the position. 197 | positionsLarge[i] = [position[0], position[1]] 198 | 199 | print('Tree generated!') 200 | 201 | # Now fill the gaps with the small particles. 202 | 203 | print('\nInserting small particles into AABB tree ...') 204 | 205 | for i in range(0, nSmall): 206 | # Initialise the overlap flag. 207 | isOverlap = True 208 | 209 | # Keep trying until there is no overlap. 210 | while isOverlap: 211 | # Set the cut-off. 212 | cutOff = radiusSmall + radiusLarge 213 | cutOff *= cutOff 214 | 215 | # Generate a random particle position. 216 | position[0] = boxSize[0]*random.random() 217 | position[1] = boxSize[1]*random.random() 218 | 219 | # Compute the lower and upper AABB bounds. 220 | lowerBound[0] = position[0] - radiusSmall 221 | lowerBound[1] = position[1] - radiusSmall 222 | upperBound[0] = position[0] + radiusSmall 223 | upperBound[1] = position[1] + radiusSmall 224 | 225 | # Generate the AABB. 226 | AABB = aabb.AABB(lowerBound, upperBound) 227 | 228 | # First query AABB overlaps with the large particles. 229 | particles = treeLarge.query(AABB) 230 | 231 | # Flag as no overlap (yet). 232 | isOverlap = False 233 | 234 | # Test overlap. 235 | for j in range(0, len(particles)): 236 | if overlaps(position, positionsLarge[particles[j]], periodicity, boxSize, cutOff): 237 | isOverlap = True 238 | break 239 | 240 | # Advance to next overlap test. 241 | if not isOverlap: 242 | # Set the cut-off. 243 | cutOff = radiusSmall + radiusSmall 244 | cutOff *= cutOff 245 | 246 | # No need to test the first particle. 247 | if i > 0: 248 | # Now query AABB overlaps with other small particles. 249 | particles = treeSmall.query(AABB) 250 | 251 | # Test overlap. 252 | for j in range(0, len(particles)): 253 | if overlaps(position, positionsSmall[particles[j]], periodicity, boxSize, cutOff): 254 | isOverlap = True 255 | break 256 | 257 | # Insert the particle into the tree. 258 | treeSmall.insertParticle(i, position, radiusSmall) 259 | 260 | # Store the position. 261 | positionsSmall[i] = [position[0], position[1]] 262 | 263 | print('Tree generated!') 264 | 265 | ############################################################# 266 | # Perform the dynamics, updating the tree as we go. # 267 | ############################################################# 268 | 269 | # Clear the trajectory file. 270 | open('trajectory.xyz', 'w').close() 271 | 272 | print('\nRunning dynamics ...') 273 | 274 | sampleFlag = 0 275 | nSampled = 0 276 | 277 | # Initialise the displacement vector. 278 | displacement = [0] * 2 279 | 280 | for i in range(0, nSweeps): 281 | for j in range(0, nParticles): 282 | # Choose a random particle. 283 | particle = random.randint(0, nParticles-1) 284 | 285 | # Determine the particle type 286 | if particle < nSmall: 287 | particleType = 0 288 | radius = radiusSmall 289 | displacement[0] = maxDisp*diameterSmall*(2*random.random() - 1) 290 | displacement[1] = maxDisp*diameterSmall*(2*random.random() - 1) 291 | position[0] = positionsSmall[particle][0] + displacement[0] 292 | position[1] = positionsSmall[particle][1] + displacement[1] 293 | else: 294 | particleType = 1 295 | particle -= nSmall 296 | radius = radiusLarge 297 | displacement[0] = maxDisp*diameterLarge*(2*random.random() - 1) 298 | displacement[1] = maxDisp*diameterLarge*(2*random.random() - 1) 299 | position[0] = positionsLarge[particle][0] + displacement[0] 300 | position[1] = positionsLarge[particle][1] + displacement[1] 301 | 302 | # Apply periodic boundary conditions. 303 | periodicBoundaries(position, periodicity, boxSize) 304 | 305 | # Compute the AABB bounds. 306 | lowerBound[0] = position[0] - radius 307 | lowerBound[1] = position[1] - radius 308 | upperBound[0] = position[0] + radius 309 | upperBound[1] = position[1] + radius 310 | 311 | # Generate the AABB. 312 | AABB = aabb.AABB(lowerBound, upperBound) 313 | 314 | # Query AABB overlaps with small particles. 315 | particles = treeSmall.query(AABB) 316 | 317 | # Flag as no overlap (yet). 318 | isOverlap = False 319 | 320 | # Set the cut-off 321 | cutOff = radius + radiusSmall 322 | cutOff *= cutOff 323 | 324 | # Test overlap. 325 | for k in range(0, len(particles)): 326 | # Don't test self overlap. 327 | if particleType == 1 or particles[k] != particle: 328 | if overlaps(position, positionsSmall[particles[k]], periodicity, boxSize, cutOff): 329 | isOverlap = True 330 | break 331 | 332 | # Advance to next overlap test. 333 | if not isOverlap: 334 | # Now query AABB overlaps with the large particles. 335 | particles = treeLarge.query(AABB) 336 | 337 | # Set the cut-off. 338 | cutOff = radius + radiusLarge 339 | cutOff *= cutOff 340 | 341 | # Test overlap. 342 | for k in range(0, len(particles)): 343 | # Don't test self overlap. 344 | if particleType == 0 or particles[k] != particle: 345 | if overlaps(position, positionsLarge[particles[k]], periodicity, boxSize, cutOff): 346 | isOverlap = True 347 | break 348 | 349 | # Accept the move. 350 | if not isOverlap: 351 | # Update the position and AABB tree. 352 | if particleType == 0: 353 | positionsSmall[particle] = [position[0], position[1]] 354 | treeSmall.updateParticle(particle, lowerBound, upperBound) 355 | else: 356 | positionsLarge[particle] = [position[0], position[1]] 357 | treeLarge.updateParticle(particle, lowerBound, upperBound) 358 | 359 | sampleFlag += 1 360 | 361 | # Print info to screen and append trajectory file. 362 | if sampleFlag == sampleInterval: 363 | sampleFlag = 0 364 | nSampled += 1 365 | 366 | printVMD('trajectory.xyz', positionsSmall, positionsLarge) 367 | 368 | if format == 1: 369 | print('Saved configuration %2d of %2d' % (nSampled, nSamples)) 370 | elif format == 2: 371 | print('Saved configuration %3d of %3d' % (nSampled, nSamples)) 372 | elif format == 3: 373 | print('Saved configuration %4d of %4d' % (nSampled, nSamples)) 374 | elif format == 4: 375 | print('Saved configuration %5d of %5d' % (nSampled, nSamples)) 376 | elif format == 5: 377 | print('Saved configuration %6d of %6d' % (nSampled, nSamples)) 378 | 379 | print('Done!') 380 | -------------------------------------------------------------------------------- /src/AABB.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2009 Erin Catto http://www.box2d.org 3 | Copyright (c) 2016-2018 Lester Hedges 4 | 5 | This software is provided 'as-is', without any express or implied 6 | warranty. In no event will the authors be held liable for any damages 7 | arising from the use of this software. 8 | 9 | Permission is granted to anyone to use this software for any purpose, 10 | including commercial applications, and to alter it and redistribute it 11 | freely, subject to the following restrictions: 12 | 13 | 1. The origin of this software must not be misrepresented; you must not 14 | claim that you wrote the original software. If you use this software 15 | in a product, an acknowledgment in the product documentation would be 16 | appreciated but is not required. 17 | 18 | 2. Altered source versions must be plainly marked as such, and must not be 19 | misrepresented as being the original software. 20 | 21 | 3. This notice may not be removed or altered from any source distribution. 22 | 23 | This code was adapted from parts of the Box2D Physics Engine, 24 | http://www.box2d.org 25 | */ 26 | 27 | #ifndef _AABB_H 28 | #define _AABB_H 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | /// Null node flag. 40 | const unsigned int NULL_NODE = 0xffffffff; 41 | 42 | namespace aabb 43 | { 44 | /*! \brief The axis-aligned bounding box object. 45 | 46 | Axis-aligned bounding boxes (AABBs) store information for the minimum 47 | orthorhombic bounding-box for an object. Support is provided for 48 | dimensions >= 2. (In 2D the bounding box is either a rectangle, 49 | in 3D it is a rectangular prism.) 50 | 51 | Class member functions provide functionality for merging AABB objects 52 | and testing overlap with other AABBs. 53 | */ 54 | class AABB 55 | { 56 | public: 57 | /// Constructor. 58 | AABB(); 59 | 60 | //! Constructor. 61 | /*! \param dimension 62 | The dimensionality of the system. 63 | */ 64 | AABB(unsigned int); 65 | 66 | //! Constructor. 67 | /*! \param lowerBound_ 68 | The lower bound in each dimension. 69 | 70 | \param upperBound_ 71 | The upper bound in each dimension. 72 | */ 73 | AABB(const std::vector&, const std::vector&); 74 | 75 | /// Compute the surface area of the box. 76 | double computeSurfaceArea() const; 77 | 78 | /// Get the surface area of the box. 79 | double getSurfaceArea() const; 80 | 81 | //! Merge two AABBs into this one. 82 | /*! \param aabb1 83 | A reference to the first AABB. 84 | 85 | \param aabb2 86 | A reference to the second AABB. 87 | */ 88 | void merge(const AABB&, const AABB&); 89 | 90 | //! Test whether the AABB is contained within this one. 91 | /*! \param aabb 92 | A reference to the AABB. 93 | 94 | \return 95 | Whether the AABB is fully contained. 96 | */ 97 | bool contains(const AABB&) const; 98 | 99 | //! Test whether the AABB overlaps this one. 100 | /*! \param aabb 101 | A reference to the AABB. 102 | 103 | \param touchIsOverlap 104 | Does touching constitute an overlap? 105 | 106 | \return 107 | Whether the AABB overlaps. 108 | */ 109 | bool overlaps(const AABB&, bool touchIsOverlap) const; 110 | 111 | //! Compute the centre of the AABB. 112 | /*! \returns 113 | The position vector of the AABB centre. 114 | */ 115 | std::vector computeCentre(); 116 | 117 | //! Set the dimensionality of the AABB. 118 | /*! \param dimension 119 | The dimensionality of the system. 120 | */ 121 | void setDimension(unsigned int); 122 | 123 | /// Lower bound of AABB in each dimension. 124 | std::vector lowerBound; 125 | 126 | /// Upper bound of AABB in each dimension. 127 | std::vector upperBound; 128 | 129 | /// The position of the AABB centre. 130 | std::vector centre; 131 | 132 | /// The AABB's surface area. 133 | double surfaceArea; 134 | }; 135 | 136 | /*! \brief A node of the AABB tree. 137 | 138 | Each node of the tree contains an AABB object which corresponds to a 139 | particle, or a group of particles, in the simulation box. The AABB 140 | objects of individual particles are "fattened" before they are stored 141 | to avoid having to continually update and rebalance the tree when 142 | displacements are small. 143 | 144 | Nodes are aware of their position within in the tree. The isLeaf member 145 | function allows the tree to query whether the node is a leaf, i.e. to 146 | determine whether it holds a single particle. 147 | */ 148 | struct Node 149 | { 150 | /// Constructor. 151 | Node(); 152 | 153 | /// The fattened axis-aligned bounding box. 154 | AABB aabb; 155 | 156 | /// Index of the parent node. 157 | unsigned int parent; 158 | 159 | /// Index of the next node. 160 | unsigned int next; 161 | 162 | /// Index of the left-hand child. 163 | unsigned int left; 164 | 165 | /// Index of the right-hand child. 166 | unsigned int right; 167 | 168 | /// Height of the node. This is 0 for a leaf and -1 for a free node. 169 | int height; 170 | 171 | /// The index of the particle that the node contains (leaf nodes only). 172 | unsigned int particle; 173 | 174 | //! Test whether the node is a leaf. 175 | /*! \return 176 | Whether the node is a leaf node. 177 | */ 178 | bool isLeaf() const; 179 | }; 180 | 181 | /*! \brief The dynamic AABB tree. 182 | 183 | The dynamic AABB tree is a hierarchical data structure that can be used 184 | to efficiently query overlaps between objects of arbitrary shape and 185 | size that lie inside of a simulation box. Support is provided for 186 | periodic and non-periodic boxes, as well as boxes with partial 187 | periodicity, e.g. periodic along specific axes. 188 | */ 189 | class Tree 190 | { 191 | public: 192 | //! Constructor (non-periodic). 193 | /*! \param dimension_ 194 | The dimensionality of the system. 195 | 196 | \param skinThickness_ 197 | The skin thickness for fattened AABBs, as a fraction 198 | of the AABB base length. 199 | 200 | \param nParticles 201 | The number of particles (for fixed particle number systems). 202 | 203 | \param touchIsOverlap 204 | Does touching count as overlapping in query operations? 205 | */ 206 | Tree(unsigned int dimension_= 3, double skinThickness_ = 0.05, 207 | unsigned int nParticles = 16, bool touchIsOverlap=true); 208 | 209 | //! Constructor (custom periodicity). 210 | /*! \param dimension_ 211 | The dimensionality of the system. 212 | 213 | \param skinThickness_ 214 | The skin thickness for fattened AABBs, as a fraction 215 | of the AABB base length. 216 | 217 | \param periodicity_ 218 | Whether the system is periodic in each dimension. 219 | 220 | \param boxSize_ 221 | The size of the simulation box in each dimension. 222 | 223 | \param nParticles 224 | The number of particles (for fixed particle number systems). 225 | 226 | \param touchIsOverlap 227 | Does touching count as overlapping in query operations? 228 | */ 229 | Tree(unsigned int, double, const std::vector&, const std::vector&, 230 | unsigned int nParticles = 16, bool touchIsOverlap=true); 231 | 232 | //! Set the periodicity of the simulation box. 233 | /*! \param periodicity_ 234 | Whether the system is periodic in each dimension. 235 | */ 236 | void setPeriodicity(const std::vector&); 237 | 238 | //! Set the size of the simulation box. 239 | /*! \param boxSize_ 240 | The size of the simulation box in each dimension. 241 | */ 242 | void setBoxSize(const std::vector&); 243 | 244 | //! Insert a particle into the tree (point particle). 245 | /*! \param index 246 | The index of the particle. 247 | 248 | \param position 249 | The position vector of the particle. 250 | 251 | \param radius 252 | The radius of the particle. 253 | */ 254 | void insertParticle(unsigned int, std::vector&, double); 255 | 256 | //! Insert a particle into the tree (arbitrary shape with bounding box). 257 | /*! \param index 258 | The index of the particle. 259 | 260 | \param lowerBound 261 | The lower bound in each dimension. 262 | 263 | \param upperBound 264 | The upper bound in each dimension. 265 | */ 266 | void insertParticle(unsigned int, std::vector&, std::vector&); 267 | 268 | /// Return the number of particles in the tree. 269 | unsigned int nParticles(); 270 | 271 | //! Remove a particle from the tree. 272 | /*! \param particle 273 | The particle index (particleMap will be used to map the node). 274 | */ 275 | void removeParticle(unsigned int); 276 | 277 | /// Remove all particles from the tree. 278 | void removeAll(); 279 | 280 | //! Update the tree if a particle moves outside its fattened AABB. 281 | /*! \param particle 282 | The particle index (particleMap will be used to map the node). 283 | 284 | \param position 285 | The position vector of the particle. 286 | 287 | \param radius 288 | The radius of the particle. 289 | 290 | \param alwaysReinsert 291 | Always reinsert the particle, even if it's within its old AABB (default:false) 292 | 293 | \return 294 | Whether the particle was reinserted. 295 | */ 296 | bool updateParticle(unsigned int, std::vector&, double, bool alwaysReinsert=false); 297 | 298 | //! Update the tree if a particle moves outside its fattened AABB. 299 | /*! \param particle 300 | The particle index (particleMap will be used to map the node). 301 | 302 | \param lowerBound 303 | The lower bound in each dimension. 304 | 305 | \param upperBound 306 | The upper bound in each dimension. 307 | 308 | \param alwaysReinsert 309 | Always reinsert the particle, even if it's within its old AABB (default: false) 310 | */ 311 | bool updateParticle(unsigned int, std::vector&, std::vector&, bool alwaysReinsert=false); 312 | 313 | //! Query the tree to find candidate interactions for a particle. 314 | /*! \param particle 315 | The particle index. 316 | 317 | \return particles 318 | A vector of particle indices. 319 | */ 320 | std::vector query(unsigned int); 321 | 322 | //! Query the tree to find candidate interactions for an AABB. 323 | /*! \param particle 324 | The particle index. 325 | 326 | \param aabb 327 | The AABB. 328 | 329 | \return particles 330 | A vector of particle indices. 331 | */ 332 | std::vector query(unsigned int, const AABB&); 333 | 334 | //! Query the tree to find candidate interactions for an AABB. 335 | /*! \param aabb 336 | The AABB. 337 | 338 | \return particles 339 | A vector of particle indices. 340 | */ 341 | std::vector query(const AABB&); 342 | 343 | //! Get a particle AABB. 344 | /*! \param particle 345 | The particle index. 346 | */ 347 | const AABB& getAABB(unsigned int); 348 | 349 | //! Get the height of the tree. 350 | /*! \return 351 | The height of the binary tree. 352 | */ 353 | unsigned int getHeight() const; 354 | 355 | //! Get the number of nodes in the tree. 356 | /*! \return 357 | The number of nodes in the tree. 358 | */ 359 | unsigned int getNodeCount() const; 360 | 361 | //! Compute the maximum balancance of the tree. 362 | /*! \return 363 | The maximum difference between the height of two 364 | children of a node. 365 | */ 366 | unsigned int computeMaximumBalance() const; 367 | 368 | //! Compute the surface area ratio of the tree. 369 | /*! \return 370 | The ratio of the sum of the node surface area to the surface 371 | area of the root node. 372 | */ 373 | double computeSurfaceAreaRatio() const; 374 | 375 | /// Validate the tree. 376 | void validate() const; 377 | 378 | /// Rebuild an optimal tree. 379 | void rebuild(); 380 | 381 | private: 382 | /// The index of the root node. 383 | unsigned int root; 384 | 385 | /// The dynamic tree. 386 | std::vector nodes; 387 | 388 | /// The current number of nodes in the tree. 389 | unsigned int nodeCount; 390 | 391 | /// The current node capacity. 392 | unsigned int nodeCapacity; 393 | 394 | /// The position of node at the top of the free list. 395 | unsigned int freeList; 396 | 397 | /// The dimensionality of the system. 398 | unsigned int dimension; 399 | 400 | /// Whether the system is periodic along at least one axis. 401 | bool isPeriodic; 402 | 403 | /// The skin thickness of the fattened AABBs, as a fraction of the AABB base length. 404 | double skinThickness; 405 | 406 | /// Whether the system is periodic along each axis. 407 | std::vector periodicity; 408 | 409 | /// The size of the system in each dimension. 410 | std::vector boxSize; 411 | 412 | /// The position of the negative minimum image. 413 | std::vector negMinImage; 414 | 415 | /// The position of the positive minimum image. 416 | std::vector posMinImage; 417 | 418 | /// A map between particle and node indices. 419 | std::unordered_map particleMap; 420 | 421 | /// Does touching count as overlapping in tree queries? 422 | bool touchIsOverlap; 423 | 424 | //! Allocate a new node. 425 | /*! \return 426 | The index of the allocated node. 427 | */ 428 | unsigned int allocateNode(); 429 | 430 | //! Free an existing node. 431 | /*! \param node 432 | The index of the node to be freed. 433 | */ 434 | void freeNode(unsigned int); 435 | 436 | //! Insert a leaf into the tree. 437 | /*! \param leaf 438 | The index of the leaf node. 439 | */ 440 | void insertLeaf(unsigned int); 441 | 442 | //! Remove a leaf from the tree. 443 | /*! \param leaf 444 | The index of the leaf node. 445 | */ 446 | void removeLeaf(unsigned int); 447 | 448 | //! Balance the tree. 449 | /*! \param node 450 | The index of the node. 451 | */ 452 | unsigned int balance(unsigned int); 453 | 454 | //! Compute the height of the tree. 455 | /*! \return 456 | The height of the entire tree. 457 | */ 458 | unsigned int computeHeight() const; 459 | 460 | //! Compute the height of a sub-tree. 461 | /*! \param node 462 | The index of the root node. 463 | 464 | \return 465 | The height of the sub-tree. 466 | */ 467 | unsigned int computeHeight(unsigned int) const; 468 | 469 | //! Assert that the sub-tree has a valid structure. 470 | /*! \param node 471 | The index of the root node. 472 | */ 473 | void validateStructure(unsigned int) const; 474 | 475 | //! Assert that the sub-tree has valid metrics. 476 | /*! \param node 477 | The index of the root node. 478 | */ 479 | void validateMetrics(unsigned int) const; 480 | 481 | //! Apply periodic boundary conditions. 482 | /* \param position 483 | The position vector. 484 | */ 485 | void periodicBoundaries(std::vector&); 486 | 487 | //! Compute minimum image separation. 488 | /*! \param separation 489 | The separation vector. 490 | 491 | \param shift 492 | The shift vector. 493 | 494 | \return 495 | Whether a periodic shift has been applied. 496 | */ 497 | bool minimumImage(std::vector&, std::vector&); 498 | }; 499 | } 500 | 501 | #endif /* _AABB_H */ 502 | -------------------------------------------------------------------------------- /demos/hard_disc.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2016-2018 Lester Hedges 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 20 | 3. This notice may not be removed or altered from any source distribution. 21 | */ 22 | 23 | #include 24 | #include 25 | 26 | #include "AABB.h" 27 | #include "MersenneTwister.h" 28 | 29 | #ifndef M_PI 30 | #define M_PI 3.1415926535897932384626433832795 31 | #endif 32 | 33 | /*! \file hard_disc.cpp 34 | 35 | An example showing the use of AABB trees for simulating the dynamics 36 | of a binary hard disc system where there is a large size asymmetry (10:1) 37 | between the particle species. 38 | */ 39 | 40 | // FUNCTION PROTOTYPES 41 | 42 | // Test whether two particles overlap. 43 | bool overlaps(std::vector&, std::vector&, const std::vector&, const std::vector&, double); 44 | 45 | // Compute the minimum image separation vector. 46 | void minimumImage(std::vector&, const std::vector&, const std::vector&); 47 | 48 | // Apply periodic boundary conditions. 49 | void periodicBoundaries(std::vector&, const std::vector&, const std::vector&); 50 | 51 | // Append a particle configuration to a VMD xyz file. 52 | void printVMD(const std::string&, const std::vector >&, const std::vector >&); 53 | 54 | // MAIN FUNCTION 55 | 56 | int main(int argc, char** argv) 57 | { 58 | // Print git commit info, if present. 59 | #ifdef COMMIT 60 | std::cout << "Git commit: " << COMMIT << "\n"; 61 | #endif 62 | 63 | // Print git branch info, if present. 64 | #ifdef BRANCH 65 | std::cout << "Git branch: " << BRANCH << "\n"; 66 | #endif 67 | 68 | /*****************************************************************/ 69 | /* Set parameters, initialise variables and objects. */ 70 | /*****************************************************************/ 71 | 72 | unsigned int nSweeps = 100000; // The number of Monte Carlo sweeps. 73 | unsigned int sampleInterval = 100; // The number of sweeps per sample. 74 | unsigned int nSmall = 1000; // The number of small particles. 75 | unsigned int nLarge = 100; // The number of large particles. 76 | double diameterSmall = 1; // The diameter of the small particles. 77 | double diameterLarge = 10; // The diameter of the large particles. 78 | double density = 0.1; // The system density. 79 | double maxDisp = 0.1; // Maximum trial displacement (in units of diameter). 80 | 81 | // Total particles. 82 | unsigned int nParticles = nSmall + nLarge; 83 | 84 | // Number of samples. 85 | unsigned int nSamples = nSweeps / sampleInterval; 86 | 87 | // Particle radii. 88 | double radiusSmall = 0.5*diameterSmall; 89 | double radiusLarge = 0.5*diameterLarge; 90 | 91 | // Output formatting flag. 92 | unsigned int format = std::floor(std::log10(nSamples)); 93 | 94 | // Set the periodicity of the simulation box. 95 | std::vector periodicity({true, true}); 96 | 97 | // Work out base length of simulation box. 98 | double baseLength = std::pow((M_PI*(nSmall*diameterSmall + nLarge*diameterLarge))/(4.0*density), 1.0/2.0); 99 | std::vector boxSize({baseLength, baseLength}); 100 | 101 | // Initialise the random number generator. 102 | MersenneTwister rng; 103 | 104 | // Initialise the AABB trees. 105 | aabb::Tree treeSmall(2, maxDisp, periodicity, boxSize, nSmall); 106 | aabb::Tree treeLarge(2, maxDisp, periodicity, boxSize, nLarge); 107 | 108 | // Initialise particle position vectors. 109 | std::vector > positionsSmall(nSmall, std::vector(boxSize.size())); 110 | std::vector > positionsLarge(nLarge, std::vector(boxSize.size())); 111 | 112 | /*****************************************************************/ 113 | /* Generate the initial AABB trees. */ 114 | /*****************************************************************/ 115 | 116 | // First the large particles. 117 | 118 | std::cout << "\nInserting large particles into AABB tree ...\n"; 119 | for (unsigned int i=0;i position(2); 123 | 124 | // Insert the first particle directly. 125 | if (i == 0) 126 | { 127 | // Generate a random particle position. 128 | position[0] = boxSize[0]*rng(); 129 | position[1] = boxSize[1]*rng(); 130 | } 131 | 132 | // Check for overlaps. 133 | else 134 | { 135 | // Initialise overlap flag. 136 | bool isOverlap = true; 137 | 138 | // Keep trying until there is no overlap. 139 | while (isOverlap) 140 | { 141 | // Generate a random particle position. 142 | position[0] = boxSize[0]*rng(); 143 | position[1] = boxSize[1]*rng(); 144 | 145 | // Compute lower and upper AABB bounds. 146 | std::vector lowerBound({position[0] - radiusLarge, position[1] - radiusLarge}); 147 | std::vector upperBound({position[0] + radiusLarge, position[1] + radiusLarge}); 148 | 149 | // Generate the AABB. 150 | aabb::AABB aabb(lowerBound, upperBound); 151 | 152 | // Query AABB overlaps. 153 | std::vector particles = treeLarge.query(aabb); 154 | 155 | // Flag as no overlap (yet). 156 | isOverlap = false; 157 | 158 | // Test overlap. 159 | for (unsigned int j=0;j position(2); 190 | 191 | // Initialise overlap flag. 192 | bool isOverlap = true; 193 | 194 | // Keep trying until there is no overlap. 195 | while (isOverlap) 196 | { 197 | // Generate a random particle position. 198 | position[0] = boxSize[0]*rng(); 199 | position[1] = boxSize[1]*rng(); 200 | 201 | // Compute lower and upper AABB bounds. 202 | std::vector lowerBound({position[0] - radiusSmall, position[1] - radiusSmall}); 203 | std::vector upperBound({position[0] + radiusSmall, position[1] + radiusSmall}); 204 | 205 | // Generate the AABB. 206 | aabb::AABB aabb(lowerBound, upperBound); 207 | 208 | // First query AABB overlaps with the large particles. 209 | std::vector particles = treeLarge.query(aabb); 210 | 211 | // Flag as no overlap (yet). 212 | isOverlap = false; 213 | 214 | // Test overlap. 215 | for (unsigned int j=0;j 0) 234 | { 235 | // Now query AABB overlaps with other small particles. 236 | particles = treeSmall.query(aabb); 237 | 238 | // Test overlap. 239 | for (unsigned int j=0;j displacement(2); 295 | std::vector position(2); 296 | std::vector lowerBound(2); 297 | std::vector upperBound(2); 298 | 299 | // Calculate the new particle position and displacement. 300 | if (particleType == 0) 301 | { 302 | displacement[0] = maxDisp*diameterSmall*(2.0*rng() - 1.0); 303 | displacement[1] = maxDisp*diameterSmall*(2.0*rng() - 1.0); 304 | position[0] = positionsSmall[particle][0] + displacement[0]; 305 | position[1] = positionsSmall[particle][1] + displacement[1]; 306 | } 307 | else 308 | { 309 | displacement[0] = maxDisp*diameterLarge*(2.0*rng() - 1.0); 310 | displacement[1] = maxDisp*diameterLarge*(2.0*rng() - 1.0); 311 | position[0] = positionsLarge[particle][0] + displacement[0]; 312 | position[1] = positionsLarge[particle][1] + displacement[1]; 313 | } 314 | 315 | // Apply periodic boundary conditions. 316 | periodicBoundaries(position, periodicity, boxSize); 317 | 318 | // Compute lower and upper AABB bounds. 319 | lowerBound[0] = position[0] - radius; 320 | lowerBound[1] = position[1] - radius; 321 | upperBound[0] = position[0] + radius; 322 | upperBound[1] = position[1] + radius; 323 | 324 | // Generate the AABB. 325 | aabb::AABB aabb(lowerBound, upperBound); 326 | 327 | // Query AABB overlaps with small particles. 328 | std::vector particles = treeSmall.query(aabb); 329 | 330 | // Flag as not overlapping (yet). 331 | bool isOverlap = false; 332 | 333 | // Test overlap. 334 | for (unsigned int k=0;k& position1, std::vector& position2, 420 | const std::vector& periodicity, const std::vector& boxSize, double cutOff) 421 | { 422 | // Calculate particle separation. 423 | std::vector separation; 424 | separation.push_back(position1[0] - position2[0]); 425 | separation.push_back(position1[1] - position2[1]); 426 | 427 | // Calculate minimum image separation. 428 | minimumImage(separation, periodicity, boxSize); 429 | 430 | double rSqd = separation[0]*separation[0] + separation[1]*separation[1]; 431 | 432 | if (rSqd < cutOff) return true; 433 | else return false; 434 | } 435 | 436 | void minimumImage(std::vector& separation, 437 | const std::vector& periodicity, const std::vector& boxSize) 438 | { 439 | for (unsigned int i=0;i<2;i++) 440 | { 441 | if (separation[i] < -0.5*boxSize[i]) 442 | { 443 | separation[i] += periodicity[i]*boxSize[i]; 444 | } 445 | else 446 | { 447 | if (separation[i] >= 0.5*boxSize[i]) 448 | { 449 | separation[i] -= periodicity[i]*boxSize[i]; 450 | } 451 | } 452 | } 453 | } 454 | 455 | void periodicBoundaries(std::vector& position, 456 | const std::vector& periodicity, const std::vector& boxSize) 457 | { 458 | for (unsigned int i=0;i<2;i++) 459 | { 460 | if (position[i] < 0) 461 | { 462 | position[i] += periodicity[i]*boxSize[i]; 463 | } 464 | else 465 | { 466 | if (position[i] >= boxSize[i]) 467 | { 468 | position[i] -= periodicity[i]*boxSize[i]; 469 | } 470 | } 471 | } 472 | } 473 | 474 | void printVMD(const std::string& fileName, const std::vector >& positionsSmall, 475 | const std::vector >& positionsLarge) 476 | { 477 | FILE *pFile; 478 | pFile = fopen(fileName.c_str(), "a"); 479 | 480 | fprintf(pFile, "%lu\n\n", positionsSmall.size() + positionsLarge.size()); 481 | for (unsigned int i=0;i 4 | 5 | This software is provided 'as-is', without any express or implied 6 | warranty. In no event will the authors be held liable for any damages 7 | arising from the use of this software. 8 | 9 | Permission is granted to anyone to use this software for any purpose, 10 | including commercial applications, and to alter it and redistribute it 11 | freely, subject to the following restrictions: 12 | 13 | 1. The origin of this software must not be misrepresented; you must not 14 | claim that you wrote the original software. If you use this software 15 | in a product, an acknowledgment in the product documentation would be 16 | appreciated but is not required. 17 | 18 | 2. Altered source versions must be plainly marked as such, and must not be 19 | misrepresented as being the original software. 20 | 21 | 3. This notice may not be removed or altered from any source distribution. 22 | 23 | This code was adapted from parts of the Box2D Physics Engine, 24 | http://www.box2d.org 25 | */ 26 | 27 | #include "AABB.h" 28 | 29 | namespace aabb 30 | { 31 | AABB::AABB() 32 | { 33 | } 34 | 35 | AABB::AABB(unsigned int dimension) 36 | { 37 | assert(dimension >= 2); 38 | 39 | lowerBound.resize(dimension); 40 | upperBound.resize(dimension); 41 | } 42 | 43 | AABB::AABB(const std::vector& lowerBound_, const std::vector& upperBound_) : 44 | lowerBound(lowerBound_), upperBound(upperBound_) 45 | { 46 | // Validate the dimensionality of the bounds vectors. 47 | if (lowerBound.size() != upperBound.size()) 48 | { 49 | throw std::invalid_argument("[ERROR]: Dimensionality mismatch!"); 50 | } 51 | 52 | // Validate that the upper bounds exceed the lower bounds. 53 | for (unsigned int i=0;i upperBound[i]) 57 | { 58 | throw std::invalid_argument("[ERROR]: AABB lower bound is greater than the upper bound!"); 59 | } 60 | } 61 | 62 | surfaceArea = computeSurfaceArea(); 63 | centre = computeCentre(); 64 | } 65 | 66 | double AABB::computeSurfaceArea() const 67 | { 68 | // Sum of "area" of all the sides. 69 | double sum = 0; 70 | 71 | // General formula for one side: hold one dimension constant 72 | // and multiply by all the other ones. 73 | for (unsigned int d1 = 0; d1 < lowerBound.size(); d1++) 74 | { 75 | // "Area" of current side. 76 | double product = 1; 77 | 78 | for (unsigned int d2 = 0; d2 < lowerBound.size(); d2++) 79 | { 80 | if (d1 == d2) 81 | continue; 82 | 83 | double dx = upperBound[d2] - lowerBound[d2]; 84 | product *= dx; 85 | } 86 | 87 | // Update the sum. 88 | sum += product; 89 | } 90 | 91 | return 2.0 * sum; 92 | } 93 | 94 | double AABB::getSurfaceArea() const 95 | { 96 | return surfaceArea; 97 | } 98 | 99 | void AABB::merge(const AABB& aabb1, const AABB& aabb2) 100 | { 101 | assert(aabb1.lowerBound.size() == aabb2.lowerBound.size()); 102 | assert(aabb1.upperBound.size() == aabb2.upperBound.size()); 103 | 104 | lowerBound.resize(aabb1.lowerBound.size()); 105 | upperBound.resize(aabb1.lowerBound.size()); 106 | 107 | for (unsigned int i=0;i upperBound[i]) return false; 125 | } 126 | 127 | return true; 128 | } 129 | 130 | bool AABB::overlaps(const AABB& aabb, bool touchIsOverlap) const 131 | { 132 | assert(aabb.lowerBound.size() == lowerBound.size()); 133 | 134 | bool rv = true; 135 | 136 | if (touchIsOverlap) 137 | { 138 | for (unsigned int i = 0; i < lowerBound.size(); ++i) 139 | { 140 | if (aabb.upperBound[i] < lowerBound[i] || aabb.lowerBound[i] > upperBound[i]) 141 | { 142 | rv = false; 143 | break; 144 | } 145 | } 146 | } 147 | else 148 | { 149 | for (unsigned int i = 0; i < lowerBound.size(); ++i) 150 | { 151 | if (aabb.upperBound[i] <= lowerBound[i] || aabb.lowerBound[i] >= upperBound[i]) 152 | { 153 | rv = false; 154 | break; 155 | } 156 | } 157 | } 158 | 159 | return rv; 160 | } 161 | 162 | std::vector AABB::computeCentre() 163 | { 164 | std::vector position(lowerBound.size()); 165 | 166 | for (unsigned int i=0;i= 2); 175 | 176 | lowerBound.resize(dimension); 177 | upperBound.resize(dimension); 178 | } 179 | 180 | Node::Node() 181 | { 182 | } 183 | 184 | bool Node::isLeaf() const 185 | { 186 | return (left == NULL_NODE); 187 | } 188 | 189 | Tree::Tree(unsigned int dimension_, 190 | double skinThickness_, 191 | unsigned int nParticles, 192 | bool touchIsOverlap_) : 193 | dimension(dimension_), isPeriodic(false), skinThickness(skinThickness_), 194 | touchIsOverlap(touchIsOverlap_) 195 | { 196 | // Validate the dimensionality. 197 | if ((dimension < 2)) 198 | { 199 | throw std::invalid_argument("[ERROR]: Invalid dimensionality!"); 200 | } 201 | 202 | // Initialise the periodicity vector. 203 | periodicity.resize(dimension); 204 | std::fill(periodicity.begin(), periodicity.end(), false); 205 | 206 | // Initialise the tree. 207 | root = NULL_NODE; 208 | nodeCount = 0; 209 | nodeCapacity = nParticles; 210 | nodes.resize(nodeCapacity); 211 | 212 | // Build a linked list for the list of free nodes. 213 | for (unsigned int i=0;i& periodicity_, 228 | const std::vector& boxSize_, 229 | unsigned int nParticles, 230 | bool touchIsOverlap_) : 231 | dimension(dimension_), skinThickness(skinThickness_), 232 | periodicity(periodicity_), boxSize(boxSize_), 233 | touchIsOverlap(touchIsOverlap_) 234 | { 235 | // Validate the dimensionality. 236 | if (dimension < 2) 237 | { 238 | throw std::invalid_argument("[ERROR]: Invalid dimensionality!"); 239 | } 240 | 241 | // Validate the dimensionality of the vectors. 242 | if ((periodicity.size() != dimension) || (boxSize.size() != dimension)) 243 | { 244 | throw std::invalid_argument("[ERROR]: Dimensionality mismatch!"); 245 | } 246 | 247 | // Initialise the tree. 248 | root = NULL_NODE; 249 | nodeCount = 0; 250 | nodeCapacity = nParticles; 251 | nodes.resize(nodeCapacity); 252 | 253 | // Build a linked list for the list of free nodes. 254 | for (unsigned int i=0;i& periodicity_) 280 | { 281 | periodicity = periodicity_; 282 | } 283 | 284 | void Tree::setBoxSize(const std::vector& boxSize_) 285 | { 286 | boxSize = boxSize_; 287 | } 288 | 289 | unsigned int Tree::allocateNode() 290 | { 291 | // Exand the node pool as needed. 292 | if (freeList == NULL_NODE) 293 | { 294 | assert(nodeCount == nodeCapacity); 295 | 296 | // The free list is empty. Rebuild a bigger pool. 297 | nodeCapacity *= 2; 298 | nodes.resize(nodeCapacity); 299 | 300 | // Build a linked list for the list of free nodes. 301 | for (unsigned int i=nodeCount;i& position, double radius) 338 | { 339 | // Make sure the particle doesn't already exist. 340 | if (particleMap.count(particle) != 0) 341 | { 342 | throw std::invalid_argument("[ERROR]: Particle already exists in tree!"); 343 | } 344 | 345 | // Validate the dimensionality of the position vector. 346 | if (position.size() != dimension) 347 | { 348 | throw std::invalid_argument("[ERROR]: Dimensionality mismatch!"); 349 | } 350 | 351 | // Allocate a new node for the particle. 352 | unsigned int node = allocateNode(); 353 | 354 | // AABB size in each dimension. 355 | std::vector size(dimension); 356 | 357 | // Compute the AABB limits. 358 | for (unsigned int i=0;i::value_type(particle, node)); 382 | 383 | // Store the particle index. 384 | nodes[node].particle = particle; 385 | } 386 | 387 | void Tree::insertParticle(unsigned int particle, std::vector& lowerBound, std::vector& upperBound) 388 | { 389 | // Make sure the particle doesn't already exist. 390 | if (particleMap.count(particle) != 0) 391 | { 392 | throw std::invalid_argument("[ERROR]: Particle already exists in tree!"); 393 | } 394 | 395 | // Validate the dimensionality of the bounds vectors. 396 | if ((lowerBound.size() != dimension) || (upperBound.size() != dimension)) 397 | { 398 | throw std::invalid_argument("[ERROR]: Dimensionality mismatch!"); 399 | } 400 | 401 | // Allocate a new node for the particle. 402 | unsigned int node = allocateNode(); 403 | 404 | // AABB size in each dimension. 405 | std::vector size(dimension); 406 | 407 | // Compute the AABB limits. 408 | for (unsigned int i=0;i upperBound[i]) 412 | { 413 | throw std::invalid_argument("[ERROR]: AABB lower bound is greater than the upper bound!"); 414 | } 415 | 416 | nodes[node].aabb.lowerBound[i] = lowerBound[i]; 417 | nodes[node].aabb.upperBound[i] = upperBound[i]; 418 | size[i] = upperBound[i] - lowerBound[i]; 419 | } 420 | 421 | // Fatten the AABB. 422 | for (unsigned int i=0;i::value_type(particle, node)); 438 | 439 | // Store the particle index. 440 | nodes[node].particle = particle; 441 | } 442 | 443 | unsigned int Tree::nParticles() 444 | { 445 | return particleMap.size(); 446 | } 447 | 448 | void Tree::removeParticle(unsigned int particle) 449 | { 450 | // Map iterator. 451 | std::unordered_map::iterator it; 452 | 453 | // Find the particle. 454 | it = particleMap.find(particle); 455 | 456 | // The particle doesn't exist. 457 | if (it == particleMap.end()) 458 | { 459 | throw std::invalid_argument("[ERROR]: Invalid particle index!"); 460 | } 461 | 462 | // Extract the node index. 463 | unsigned int node = it->second; 464 | 465 | // Erase the particle from the map. 466 | particleMap.erase(it); 467 | 468 | assert(node < nodeCapacity); 469 | assert(nodes[node].isLeaf()); 470 | 471 | removeLeaf(node); 472 | freeNode(node); 473 | } 474 | 475 | void Tree::removeAll() 476 | { 477 | // Iterator pointing to the start of the particle map. 478 | std::unordered_map::iterator it = particleMap.begin(); 479 | 480 | // Iterate over the map. 481 | while (it != particleMap.end()) 482 | { 483 | // Extract the node index. 484 | unsigned int node = it->second; 485 | 486 | assert(node < nodeCapacity); 487 | assert(nodes[node].isLeaf()); 488 | 489 | removeLeaf(node); 490 | freeNode(node); 491 | 492 | it++; 493 | } 494 | 495 | // Clear the particle map. 496 | particleMap.clear(); 497 | } 498 | 499 | bool Tree::updateParticle(unsigned int particle, std::vector& position, double radius, 500 | bool alwaysReinsert) 501 | { 502 | // Validate the dimensionality of the position vector. 503 | if (position.size() != dimension) 504 | { 505 | throw std::invalid_argument("[ERROR]: Dimensionality mismatch!"); 506 | } 507 | 508 | // AABB bounds vectors. 509 | std::vector lowerBound(dimension); 510 | std::vector upperBound(dimension); 511 | 512 | // Compute the AABB limits. 513 | for (unsigned int i=0;i& lowerBound, 524 | std::vector& upperBound, bool alwaysReinsert) 525 | { 526 | // Validate the dimensionality of the bounds vectors. 527 | if ((lowerBound.size() != dimension) && (upperBound.size() != dimension)) 528 | { 529 | throw std::invalid_argument("[ERROR]: Dimensionality mismatch!"); 530 | } 531 | 532 | // Map iterator. 533 | std::unordered_map::iterator it; 534 | 535 | // Find the particle. 536 | it = particleMap.find(particle); 537 | 538 | // The particle doesn't exist. 539 | if (it == particleMap.end()) 540 | { 541 | throw std::invalid_argument("[ERROR]: Invalid particle index!"); 542 | } 543 | 544 | // Extract the node index. 545 | unsigned int node = it->second; 546 | 547 | assert(node < nodeCapacity); 548 | assert(nodes[node].isLeaf()); 549 | 550 | // AABB size in each dimension. 551 | std::vector size(dimension); 552 | 553 | // Compute the AABB limits. 554 | for (unsigned int i=0;i upperBound[i]) 558 | { 559 | throw std::invalid_argument("[ERROR]: AABB lower bound is greater than the upper bound!"); 560 | } 561 | 562 | size[i] = upperBound[i] - lowerBound[i]; 563 | } 564 | 565 | // Create the new AABB. 566 | AABB aabb(lowerBound, upperBound); 567 | 568 | // No need to update if the particle is still within its fattened AABB. 569 | if (!alwaysReinsert && nodes[node].aabb.contains(aabb)) return false; 570 | 571 | // Remove the current leaf. 572 | removeLeaf(node); 573 | 574 | // Fatten the new AABB. 575 | for (unsigned int i=0;i Tree::query(unsigned int particle) 595 | { 596 | // Make sure that this is a valid particle. 597 | if (particleMap.count(particle) == 0) 598 | { 599 | throw std::invalid_argument("[ERROR]: Invalid particle index!"); 600 | } 601 | 602 | // Test overlap of particle AABB against all other particles. 603 | return query(particle, nodes[particleMap.find(particle)->second].aabb); 604 | } 605 | 606 | std::vector Tree::query(unsigned int particle, const AABB& aabb) 607 | { 608 | std::vector stack; 609 | stack.reserve(256); 610 | stack.push_back(root); 611 | 612 | std::vector particles; 613 | 614 | while (stack.size() > 0) 615 | { 616 | unsigned int node = stack.back(); 617 | stack.pop_back(); 618 | 619 | // Copy the AABB. 620 | AABB nodeAABB = nodes[node].aabb; 621 | 622 | if (node == NULL_NODE) continue; 623 | 624 | if (isPeriodic) 625 | { 626 | std::vector separation(dimension); 627 | std::vector shift(dimension); 628 | for (unsigned int i=0;i Tree::query(const AABB& aabb) 668 | { 669 | // Make sure the tree isn't empty. 670 | if (particleMap.size() == 0) 671 | { 672 | return std::vector(); 673 | } 674 | 675 | // Test overlap of AABB against all particles. 676 | return query(std::numeric_limits::max(), aabb); 677 | } 678 | 679 | const AABB& Tree::getAABB(unsigned int particle) 680 | { 681 | return nodes[particleMap[particle]].aabb; 682 | } 683 | 684 | void Tree::insertLeaf(unsigned int leaf) 685 | { 686 | if (root == NULL_NODE) 687 | { 688 | root = leaf; 689 | nodes[root].parent = NULL_NODE; 690 | return; 691 | } 692 | 693 | // Find the best sibling for the node. 694 | 695 | AABB leafAABB = nodes[leaf].aabb; 696 | unsigned int index = root; 697 | 698 | while (!nodes[index].isLeaf()) 699 | { 700 | // Extract the children of the node. 701 | unsigned int left = nodes[index].left; 702 | unsigned int right = nodes[index].right; 703 | 704 | double surfaceArea = nodes[index].aabb.getSurfaceArea(); 705 | 706 | AABB combinedAABB; 707 | combinedAABB.merge(nodes[index].aabb, leafAABB); 708 | double combinedSurfaceArea = combinedAABB.getSurfaceArea(); 709 | 710 | // Cost of creating a new parent for this node and the new leaf. 711 | double cost = 2.0 * combinedSurfaceArea; 712 | 713 | // Minimum cost of pushing the leaf further down the tree. 714 | double inheritanceCost = 2.0 * (combinedSurfaceArea - surfaceArea); 715 | 716 | // Cost of descending to the left. 717 | double costLeft; 718 | if (nodes[left].isLeaf()) 719 | { 720 | AABB aabb; 721 | aabb.merge(leafAABB, nodes[left].aabb); 722 | costLeft = aabb.getSurfaceArea() + inheritanceCost; 723 | } 724 | else 725 | { 726 | AABB aabb; 727 | aabb.merge(leafAABB, nodes[left].aabb); 728 | double oldArea = nodes[left].aabb.getSurfaceArea(); 729 | double newArea = aabb.getSurfaceArea(); 730 | costLeft = (newArea - oldArea) + inheritanceCost; 731 | } 732 | 733 | // Cost of descending to the right. 734 | double costRight; 735 | if (nodes[right].isLeaf()) 736 | { 737 | AABB aabb; 738 | aabb.merge(leafAABB, nodes[right].aabb); 739 | costRight = aabb.getSurfaceArea() + inheritanceCost; 740 | } 741 | else 742 | { 743 | AABB aabb; 744 | aabb.merge(leafAABB, nodes[right].aabb); 745 | double oldArea = nodes[right].aabb.getSurfaceArea(); 746 | double newArea = aabb.getSurfaceArea(); 747 | costRight = (newArea - oldArea) + inheritanceCost; 748 | } 749 | 750 | // Descend according to the minimum cost. 751 | if ((cost < costLeft) && (cost < costRight)) break; 752 | 753 | // Descend. 754 | if (costLeft < costRight) index = left; 755 | else index = right; 756 | } 757 | 758 | unsigned int sibling = index; 759 | 760 | // Create a new parent. 761 | unsigned int oldParent = nodes[sibling].parent; 762 | unsigned int newParent = allocateNode(); 763 | nodes[newParent].parent = oldParent; 764 | nodes[newParent].aabb.merge(leafAABB, nodes[sibling].aabb); 765 | nodes[newParent].height = nodes[sibling].height + 1; 766 | 767 | // The sibling was not the root. 768 | if (oldParent != NULL_NODE) 769 | { 770 | if (nodes[oldParent].left == sibling) nodes[oldParent].left = newParent; 771 | else nodes[oldParent].right = newParent; 772 | 773 | nodes[newParent].left = sibling; 774 | nodes[newParent].right = leaf; 775 | nodes[sibling].parent = newParent; 776 | nodes[leaf].parent = newParent; 777 | } 778 | // The sibling was the root. 779 | else 780 | { 781 | nodes[newParent].left = sibling; 782 | nodes[newParent].right = leaf; 783 | nodes[sibling].parent = newParent; 784 | nodes[leaf].parent = newParent; 785 | root = newParent; 786 | } 787 | 788 | // Walk back up the tree fixing heights and AABBs. 789 | index = nodes[leaf].parent; 790 | while (index != NULL_NODE) 791 | { 792 | index = balance(index); 793 | 794 | unsigned int left = nodes[index].left; 795 | unsigned int right = nodes[index].right; 796 | 797 | assert(left != NULL_NODE); 798 | assert(right != NULL_NODE); 799 | 800 | nodes[index].height = 1 + std::max(nodes[left].height, nodes[right].height); 801 | nodes[index].aabb.merge(nodes[left].aabb, nodes[right].aabb); 802 | 803 | index = nodes[index].parent; 804 | } 805 | } 806 | 807 | void Tree::removeLeaf(unsigned int leaf) 808 | { 809 | if (leaf == root) 810 | { 811 | root = NULL_NODE; 812 | return; 813 | } 814 | 815 | unsigned int parent = nodes[leaf].parent; 816 | unsigned int grandParent = nodes[parent].parent; 817 | unsigned int sibling; 818 | 819 | if (nodes[parent].left == leaf) sibling = nodes[parent].right; 820 | else sibling = nodes[parent].left; 821 | 822 | // Destroy the parent and connect the sibling to the grandparent. 823 | if (grandParent != NULL_NODE) 824 | { 825 | if (nodes[grandParent].left == parent) nodes[grandParent].left = sibling; 826 | else nodes[grandParent].right = sibling; 827 | 828 | nodes[sibling].parent = grandParent; 829 | freeNode(parent); 830 | 831 | // Adjust ancestor bounds. 832 | unsigned int index = grandParent; 833 | while (index != NULL_NODE) 834 | { 835 | index = balance(index); 836 | 837 | unsigned int left = nodes[index].left; 838 | unsigned int right = nodes[index].right; 839 | 840 | nodes[index].aabb.merge(nodes[left].aabb, nodes[right].aabb); 841 | nodes[index].height = 1 + std::max(nodes[left].height, nodes[right].height); 842 | 843 | index = nodes[index].parent; 844 | } 845 | } 846 | else 847 | { 848 | root = sibling; 849 | nodes[sibling].parent = NULL_NODE; 850 | freeNode(parent); 851 | } 852 | } 853 | 854 | unsigned int Tree::balance(unsigned int node) 855 | { 856 | assert(node != NULL_NODE); 857 | 858 | if (nodes[node].isLeaf() || (nodes[node].height < 2)) 859 | return node; 860 | 861 | unsigned int left = nodes[node].left; 862 | unsigned int right = nodes[node].right; 863 | 864 | assert(left < nodeCapacity); 865 | assert(right < nodeCapacity); 866 | 867 | int currentBalance = nodes[right].height - nodes[left].height; 868 | 869 | // Rotate right branch up. 870 | if (currentBalance > 1) 871 | { 872 | unsigned int rightLeft = nodes[right].left; 873 | unsigned int rightRight = nodes[right].right; 874 | 875 | assert(rightLeft < nodeCapacity); 876 | assert(rightRight < nodeCapacity); 877 | 878 | // Swap node and its right-hand child. 879 | nodes[right].left = node; 880 | nodes[right].parent = nodes[node].parent; 881 | nodes[node].parent = right; 882 | 883 | // The node's old parent should now point to its right-hand child. 884 | if (nodes[right].parent != NULL_NODE) 885 | { 886 | if (nodes[nodes[right].parent].left == node) nodes[nodes[right].parent].left = right; 887 | else 888 | { 889 | assert(nodes[nodes[right].parent].right == node); 890 | nodes[nodes[right].parent].right = right; 891 | } 892 | } 893 | else root = right; 894 | 895 | // Rotate. 896 | if (nodes[rightLeft].height > nodes[rightRight].height) 897 | { 898 | nodes[right].right = rightLeft; 899 | nodes[node].right = rightRight; 900 | nodes[rightRight].parent = node; 901 | nodes[node].aabb.merge(nodes[left].aabb, nodes[rightRight].aabb); 902 | nodes[right].aabb.merge(nodes[node].aabb, nodes[rightLeft].aabb); 903 | 904 | nodes[node].height = 1 + std::max(nodes[left].height, nodes[rightRight].height); 905 | nodes[right].height = 1 + std::max(nodes[node].height, nodes[rightLeft].height); 906 | } 907 | else 908 | { 909 | nodes[right].right = rightRight; 910 | nodes[node].right = rightLeft; 911 | nodes[rightLeft].parent = node; 912 | nodes[node].aabb.merge(nodes[left].aabb, nodes[rightLeft].aabb); 913 | nodes[right].aabb.merge(nodes[node].aabb, nodes[rightRight].aabb); 914 | 915 | nodes[node].height = 1 + std::max(nodes[left].height, nodes[rightLeft].height); 916 | nodes[right].height = 1 + std::max(nodes[node].height, nodes[rightRight].height); 917 | } 918 | 919 | return right; 920 | } 921 | 922 | // Rotate left branch up. 923 | if (currentBalance < -1) 924 | { 925 | unsigned int leftLeft = nodes[left].left; 926 | unsigned int leftRight = nodes[left].right; 927 | 928 | assert(leftLeft < nodeCapacity); 929 | assert(leftRight < nodeCapacity); 930 | 931 | // Swap node and its left-hand child. 932 | nodes[left].left = node; 933 | nodes[left].parent = nodes[node].parent; 934 | nodes[node].parent = left; 935 | 936 | // The node's old parent should now point to its left-hand child. 937 | if (nodes[left].parent != NULL_NODE) 938 | { 939 | if (nodes[nodes[left].parent].left == node) nodes[nodes[left].parent].left = left; 940 | else 941 | { 942 | assert(nodes[nodes[left].parent].right == node); 943 | nodes[nodes[left].parent].right = left; 944 | } 945 | } 946 | else root = left; 947 | 948 | // Rotate. 949 | if (nodes[leftLeft].height > nodes[leftRight].height) 950 | { 951 | nodes[left].right = leftLeft; 952 | nodes[node].left = leftRight; 953 | nodes[leftRight].parent = node; 954 | nodes[node].aabb.merge(nodes[right].aabb, nodes[leftRight].aabb); 955 | nodes[left].aabb.merge(nodes[node].aabb, nodes[leftLeft].aabb); 956 | 957 | nodes[node].height = 1 + std::max(nodes[right].height, nodes[leftRight].height); 958 | nodes[left].height = 1 + std::max(nodes[node].height, nodes[leftLeft].height); 959 | } 960 | else 961 | { 962 | nodes[left].right = leftRight; 963 | nodes[node].left = leftLeft; 964 | nodes[leftLeft].parent = node; 965 | nodes[node].aabb.merge(nodes[right].aabb, nodes[leftLeft].aabb); 966 | nodes[left].aabb.merge(nodes[node].aabb, nodes[leftRight].aabb); 967 | 968 | nodes[node].height = 1 + std::max(nodes[right].height, nodes[leftLeft].height); 969 | nodes[left].height = 1 + std::max(nodes[node].height, nodes[leftRight].height); 970 | } 971 | 972 | return left; 973 | } 974 | 975 | return node; 976 | } 977 | 978 | unsigned int Tree::computeHeight() const 979 | { 980 | return computeHeight(root); 981 | } 982 | 983 | unsigned int Tree::computeHeight(unsigned int node) const 984 | { 985 | assert(node < nodeCapacity); 986 | 987 | if (nodes[node].isLeaf()) return 0; 988 | 989 | unsigned int height1 = computeHeight(nodes[node].left); 990 | unsigned int height2 = computeHeight(nodes[node].right); 991 | 992 | return 1 + std::max(height1, height2); 993 | } 994 | 995 | unsigned int Tree::getHeight() const 996 | { 997 | if (root == NULL_NODE) return 0; 998 | return nodes[root].height; 999 | } 1000 | 1001 | unsigned int Tree::getNodeCount() const 1002 | { 1003 | return nodeCount; 1004 | } 1005 | 1006 | unsigned int Tree::computeMaximumBalance() const 1007 | { 1008 | unsigned int maxBalance = 0; 1009 | for (unsigned int i=0; i nodeIndices(nodeCount); 1064 | unsigned int count = 0; 1065 | 1066 | for (unsigned int i=0;i 1) 1081 | { 1082 | double minCost = std::numeric_limits::max(); 1083 | int iMin = -1, jMin = -1; 1084 | 1085 | for (unsigned int i=0;i& position) 1193 | { 1194 | for (unsigned int i=0;i= boxSize[i]) 1203 | { 1204 | position[i] -= boxSize[i]; 1205 | } 1206 | } 1207 | } 1208 | } 1209 | 1210 | bool Tree::minimumImage(std::vector& separation, std::vector& shift) 1211 | { 1212 | bool isShifted = false; 1213 | 1214 | for (unsigned int i=0;i= posMinImage[i]) 1225 | { 1226 | separation[i] -= periodicity[i]*boxSize[i]; 1227 | shift[i] = -periodicity[i]*boxSize[i]; 1228 | isShifted = true; 1229 | } 1230 | } 1231 | } 1232 | 1233 | return isShifted; 1234 | } 1235 | } 1236 | --------------------------------------------------------------------------------