From ef307394b3e1a492e755a476dc4e839c1d531d64 Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Fri, 15 Dec 2023 19:54:13 -0800 Subject: [PATCH 01/36] Add the Voro++ Library --- src/manifold/CMakeLists.txt | 2 +- src/third_party/CMakeLists.txt | 5 + src/third_party/voro/LICENSE | 39 + src/third_party/voro/src/c_loops.cc | 150 ++ src/third_party/voro/src/c_loops.hh | 456 ++++ src/third_party/voro/src/cell.cc | 2714 +++++++++++++++++++++ src/third_party/voro/src/cell.hh | 553 +++++ src/third_party/voro/src/common.cc | 138 ++ src/third_party/voro/src/common.hh | 32 + src/third_party/voro/src/config.hh | 123 + src/third_party/voro/src/container.cc | 551 +++++ src/third_party/voro/src/container.hh | 732 ++++++ src/third_party/voro/src/container_prd.cc | 776 ++++++ src/third_party/voro/src/container_prd.hh | 655 +++++ src/third_party/voro/src/pre_container.cc | 236 ++ src/third_party/voro/src/pre_container.hh | 162 ++ src/third_party/voro/src/rad_option.hh | 158 ++ src/third_party/voro/src/unitcell.cc | 232 ++ src/third_party/voro/src/unitcell.hh | 79 + src/third_party/voro/src/v_base.cc | 118 + src/third_party/voro/src/v_base.hh | 88 + src/third_party/voro/src/v_base_wl.cc | 79 + src/third_party/voro/src/v_compute.cc | 1006 ++++++++ src/third_party/voro/src/v_compute.hh | 149 ++ src/third_party/voro/src/wall.cc | 132 + src/third_party/voro/src/wall.hh | 118 + src/third_party/voro/src/worklist.hh | 32 + 27 files changed, 9514 insertions(+), 1 deletion(-) create mode 100644 src/third_party/voro/LICENSE create mode 100644 src/third_party/voro/src/c_loops.cc create mode 100644 src/third_party/voro/src/c_loops.hh create mode 100644 src/third_party/voro/src/cell.cc create mode 100644 src/third_party/voro/src/cell.hh create mode 100644 src/third_party/voro/src/common.cc create mode 100644 src/third_party/voro/src/common.hh create mode 100644 src/third_party/voro/src/config.hh create mode 100644 src/third_party/voro/src/container.cc create mode 100644 src/third_party/voro/src/container.hh create mode 100644 src/third_party/voro/src/container_prd.cc create mode 100644 src/third_party/voro/src/container_prd.hh create mode 100644 src/third_party/voro/src/pre_container.cc create mode 100644 src/third_party/voro/src/pre_container.hh create mode 100644 src/third_party/voro/src/rad_option.hh create mode 100644 src/third_party/voro/src/unitcell.cc create mode 100644 src/third_party/voro/src/unitcell.hh create mode 100644 src/third_party/voro/src/v_base.cc create mode 100644 src/third_party/voro/src/v_base.hh create mode 100644 src/third_party/voro/src/v_base_wl.cc create mode 100644 src/third_party/voro/src/v_compute.cc create mode 100644 src/third_party/voro/src/v_compute.hh create mode 100644 src/third_party/voro/src/wall.cc create mode 100644 src/third_party/voro/src/wall.hh create mode 100644 src/third_party/voro/src/worklist.hh diff --git a/src/manifold/CMakeLists.txt b/src/manifold/CMakeLists.txt index 72cff48d7..e7a5749b7 100644 --- a/src/manifold/CMakeLists.txt +++ b/src/manifold/CMakeLists.txt @@ -20,7 +20,7 @@ add_library(${PROJECT_NAME} ${SOURCE_FILES}) target_include_directories(${PROJECT_NAME} PUBLIC ${PROJECT_SOURCE_DIR}/include) target_link_libraries(${PROJECT_NAME} PUBLIC utilities cross_section - PRIVATE collider polygon ${MANIFOLD_INCLUDE} Clipper2 quickhull + PRIVATE collider polygon ${MANIFOLD_INCLUDE} Clipper2 quickhull voro++ ) target_compile_options(${PROJECT_NAME} PRIVATE ${MANIFOLD_FLAGS}) diff --git a/src/third_party/CMakeLists.txt b/src/third_party/CMakeLists.txt index 6ef64e832..1103b7c8d 100644 --- a/src/third_party/CMakeLists.txt +++ b/src/third_party/CMakeLists.txt @@ -7,3 +7,8 @@ add_subdirectory(clipper2/CPP) add_library(quickhull quickhull/QuickHull.cpp) target_include_directories(quickhull PUBLIC quickhull) target_compile_features(quickhull PUBLIC cxx_std_17) + +file(GLOB VORO_SOURCES voro/src/*.cc) +file(GLOB NOT_VORO_SOURCES voro/src/v_base_wl.cc src/voro++.cc) +list(REMOVE_ITEM VORO_SOURCES ${NOT_VORO_SOURCES}) +add_library(voro++ STATIC ${VORO_SOURCES}) diff --git a/src/third_party/voro/LICENSE b/src/third_party/voro/LICENSE new file mode 100644 index 000000000..7b05ace52 --- /dev/null +++ b/src/third_party/voro/LICENSE @@ -0,0 +1,39 @@ +Voro++ Copyright (c) 2008, The Regents of the University of California, through +Lawrence Berkeley National Laboratory (subject to receipt of any required +approvals from the U.S. Dept. of Energy). All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +(1) Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +(2) Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +(3) Neither the name of the University of California, Lawrence Berkeley +National Laboratory, U.S. Dept. of Energy nor the names of its contributors may +be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +You are under no obligation whatsoever to provide any bug fixes, patches, or +upgrades to the features, functionality or performance of the source code +("Enhancements") to anyone; however, if you choose to make your Enhancements +available either publicly, or directly to Lawrence Berkeley National +Laboratory, without imposing a separate written license agreement for such +Enhancements, then you hereby grant the following license: a non-exclusive, +royalty-free perpetual license to install, use, modify, prepare derivative +works, incorporate into other computer software, distribute, and sublicense +such enhancements or derivative works thereof, in binary and source code form. diff --git a/src/third_party/voro/src/c_loops.cc b/src/third_party/voro/src/c_loops.cc new file mode 100644 index 000000000..86d3e4478 --- /dev/null +++ b/src/third_party/voro/src/c_loops.cc @@ -0,0 +1,150 @@ +// Voro++, a 3D cell-based Voronoi library +// +// Author : Chris H. Rycroft (Harvard University / LBL) +// Email : chr@alum.mit.edu +// Date : August 30th 2011 + +/** \file c_loops.cc + * \brief Function implementations for the loop classes. */ + +#include "c_loops.hh" + +namespace voro { + +/** Initializes a c_loop_subset object to scan over all particles within a + * given sphere. + * \param[in] (vx,vy,vz) the position vector of the center of the sphere. + * \param[in] r the radius of the sphere. + * \param[in] bounds_test whether to do detailed bounds checking. If this is + * false then the class will loop over all particles in + * blocks that overlap the given sphere. If it is true, + * the particle will only loop over the particles which + * actually lie within the sphere. + * \return True if there is any valid point to loop over, false otherwise. */ +void c_loop_subset::setup_sphere(double vx,double vy,double vz,double r,bool bounds_test) { + if(bounds_test) {mode=sphere;v0=vx;v1=vy;v2=vz;v3=r*r;} else mode=no_check; + ai=step_int((vx-ax-r)*xsp); + bi=step_int((vx-ax+r)*xsp); + aj=step_int((vy-ay-r)*ysp); + bj=step_int((vy-ay+r)*ysp); + ak=step_int((vz-az-r)*zsp); + bk=step_int((vz-az+r)*zsp); + setup_common(); +} + +/** Initializes the class to loop over all particles in a rectangular subgrid + * of blocks. + * \param[in] (ai_,bi_) the subgrid range in the x-direction, inclusive of both + * ends. + * \param[in] (aj_,bj_) the subgrid range in the y-direction, inclusive of both + * ends. + * \param[in] (ak_,bk_) the subgrid range in the z-direction, inclusive of both + * ends. + * \return True if there is any valid point to loop over, false otherwise. */ +void c_loop_subset::setup_intbox(int ai_,int bi_,int aj_,int bj_,int ak_,int bk_) { + ai=ai_;bi=bi_;aj=aj_;bj=bj_;ak=ak_;bk=bk_; + mode=no_check; + setup_common(); +} + +/** Sets up all of the common constants used for the loop. + * \return True if there is any valid point to loop over, false otherwise. */ +void c_loop_subset::setup_common() { + if(!xperiodic) { + if(ai<0) {ai=0;if(bi<0) bi=0;} + if(bi>=nx) {bi=nx-1;if(ai>=nx) ai=nx-1;} + } + if(!yperiodic) { + if(aj<0) {aj=0;if(bj<0) bj=0;} + if(bj>=ny) {bj=ny-1;if(aj>=ny) aj=ny-1;} + } + if(!zperiodic) { + if(ak<0) {ak=0;if(bk<0) bk=0;} + if(bk>=nz) {bk=nz-1;if(ak>=nz) ak=nz-1;} + } + ci=ai;cj=aj;ck=ak; + di=i=step_mod(ci,nx);apx=px=step_div(ci,nx)*sx; + dj=j=step_mod(cj,ny);apy=py=step_div(cj,ny)*sy; + dk=k=step_mod(ck,nz);apz=pz=step_div(ck,nz)*sz; + inc1=di-step_mod(bi,nx); + inc2=nx*(ny+dj-step_mod(bj,ny))+inc1; + inc1+=nx; + ijk=di+nx*(dj+ny*dk); + q=0; +} + +/** Starts the loop by finding the first particle within the container to + * consider. + * \return True if there is any particle to consider, false otherwise. */ +bool c_loop_subset::start() { + while(co[ijk]==0) {if(!next_block()) return false;} + while(mode!=no_check&&out_of_bounds()) { + q++; + while(q>=co[ijk]) {q=0;if(!next_block()) return false;} + } + return true; +} + +/** Initializes the class to loop over all particles in a rectangular box. + * \param[in] (xmin,xmax) the minimum and maximum x coordinates of the box. + * \param[in] (ymin,ymax) the minimum and maximum y coordinates of the box. + * \param[in] (zmin,zmax) the minimum and maximum z coordinates of the box. + * \param[in] bounds_test whether to do detailed bounds checking. If this is + * false then the class will loop over all particles in + * blocks that overlap the given box. If it is true, the + * particle will only loop over the particles which + * actually lie within the box. + * \return True if there is any valid point to loop over, false otherwise. */ +void c_loop_subset::setup_box(double xmin,double xmax,double ymin,double ymax,double zmin,double zmax,bool bounds_test) { + if(bounds_test) {mode=box;v0=xmin;v1=xmax;v2=ymin;v3=ymax;v4=zmin;v5=zmax;} else mode=no_check; + ai=step_int((xmin-ax)*xsp); + bi=step_int((xmax-ax)*xsp); + aj=step_int((ymin-ay)*ysp); + bj=step_int((ymax-ay)*ysp); + ak=step_int((zmin-az)*zsp); + bk=step_int((zmax-az)*zsp); + setup_common(); +} + +/** Computes whether the current point is out of bounds, relative to the + * current loop setup. + * \return True if the point is out of bounds, false otherwise. */ +bool c_loop_subset::out_of_bounds() { + double *pp=p[ijk]+ps*q; + if(mode==sphere) { + double fx(*pp+px-v0),fy(pp[1]+py-v1),fz(pp[2]+pz-v2); + return fx*fx+fy*fy+fz*fz>v3; + } else { + double f(*pp+px);if(fv1) return true; + f=pp[1]+py;if(fv3) return true; + f=pp[2]+pz;return fv5; + } +} + +/** Returns the next block to be tested in a loop, and updates the periodicity + * vector if necessary. */ +bool c_loop_subset::next_block() { + if(i + c_loop_base(c_class &con) : nx(con.nx), ny(con.ny), nz(con.nz), + nxy(con.nxy), nxyz(con.nxyz), ps(con.ps), + p(con.p), id(con.id), co(con.co) {} + /** Returns the position vector of the particle currently being + * considered by the loop. + * \param[out] (x,y,z) the position vector of the particle. */ + inline void pos(double &x,double &y,double &z) { + double *pp=p[ijk]+ps*q; + x=*(pp++);y=*(pp++);z=*pp; + } + /** Returns the ID, position vector, and radius of the particle + * currently being considered by the loop. + * \param[out] pid the particle ID. + * \param[out] (x,y,z) the position vector of the particle. + * \param[out] r the radius of the particle. If no radius + * information is available the default radius + * value is returned. */ + inline void pos(int &pid,double &x,double &y,double &z,double &r) { + pid=id[ijk][q]; + double *pp=p[ijk]+ps*q; + x=*(pp++);y=*(pp++);z=*pp; + r=ps==3?default_radius:*(++pp); + } + /** Returns the x position of the particle currently being + * considered by the loop. */ + inline double x() {return p[ijk][ps*q];} + /** Returns the y position of the particle currently being + * considered by the loop. */ + inline double y() {return p[ijk][ps*q+1];} + /** Returns the z position of the particle currently being + * considered by the loop. */ + inline double z() {return p[ijk][ps*q+2];} + /** Returns the ID of the particle currently being considered + * by the loop. */ + inline int pid() {return id[ijk][q];} +}; + +/** \brief Class for looping over all of the particles in a container. + * + * This is one of the simplest loop classes, that scans the computational + * blocks in order, and scans all the particles within each block in order. */ +class c_loop_all : public c_loop_base { + public: + /** The constructor copies several necessary constants from the + * base container class. + * \param[in] con the container class to use. */ + template + c_loop_all(c_class &con) : c_loop_base(con) {} + /** Sets the class to consider the first particle. + * \return True if there is any particle to consider, false + * otherwise. */ + inline bool start() { + i=j=k=ijk=q=0; + while(co[ijk]==0) if(!next_block()) return false; + return true; + } + /** Finds the next particle to test. + * \return True if there is another particle, false if no more + * particles are available. */ + inline bool inc() { + q++; + if(q>=co[ijk]) { + q=0; + do { + if(!next_block()) return false; + } while(co[ijk]==0); + } + return true; + } + private: + /** Updates the internal variables to find the next + * computational block with any particles. + * \return True if another block is found, false if there are + * no more blocks. */ + inline bool next_block() { + ijk++; + i++; + if(i==nx) { + i=0;j++; + if(j==ny) { + j=0;k++; + if(ijk==nxyz) return false; + } + } + return true; + } +}; + +/** \brief Class for looping over a subset of particles in a container. + * + * This class can loop over a subset of particles in a certain geometrical + * region within the container. The class can be set up to loop over a + * rectangular box or sphere. It can also rectangular group of internal + * computational blocks. */ +class c_loop_subset : public c_loop_base { + public: + /** The current mode of operation, determining whether tests + * should be applied to particles to ensure they are within a + * certain geometrical object. */ + c_loop_subset_mode mode; + /** The constructor copies several necessary constants from the + * base container class. + * \param[in] con the container class to use. */ + template + c_loop_subset(c_class &con) : c_loop_base(con), ax(con.ax), ay(con.ay), az(con.az), + sx(con.bx-ax), sy(con.by-ay), sz(con.bz-az), xsp(con.xsp), ysp(con.ysp), zsp(con.zsp), + xperiodic(con.xperiodic), yperiodic(con.yperiodic), zperiodic(con.zperiodic) {} + void setup_sphere(double vx,double vy,double vz,double r,bool bounds_test=true); + void setup_box(double xmin,double xmax,double ymin,double ymax,double zmin,double zmax,bool bounds_test=true); + void setup_intbox(int ai_,int bi_,int aj_,int bj_,int ak_,int bk_); + bool start(); + /** Finds the next particle to test. + * \return True if there is another particle, false if no more + * particles are available. */ + inline bool inc() { + do { + q++; + while(q>=co[ijk]) {q=0;if(!next_block()) return false;} + } while(mode!=no_check&&out_of_bounds()); + return true; + } + private: + const double ax,ay,az,sx,sy,sz,xsp,ysp,zsp; + const bool xperiodic,yperiodic,zperiodic; + double px,py,pz,apx,apy,apz; + double v0,v1,v2,v3,v4,v5; + int ai,bi,aj,bj,ak,bk; + int ci,cj,ck,di,dj,dk,inc1,inc2; + inline int step_mod(int a,int b) {return a>=0?a%b:b-1-(b-1-a)%b;} + inline int step_div(int a,int b) {return a>=0?a/b:-1+(a+1)/b;} + inline int step_int(double a) {return a<0?int(a)-1:int(a);} + void setup_common(); + bool next_block(); + bool out_of_bounds(); +}; + +/** \brief Class for looping over all of the particles specified in a + * pre-assembled particle_order class. + * + * The particle_order class can be used to create a specific order of particles + * within the container. This class can then loop over these particles in this + * order. The class is particularly useful in cases where the ordering of the + * output must match the ordering of particles as they were inserted into the + * container. */ +class c_loop_order : public c_loop_base { + public: + /** A reference to the ordering class to use. */ + particle_order &vo; + /** A pointer to the current position in the ordering class. */ + int *cp; + /** A pointer to the end position in the ordering class. */ + int *op; + /** The constructor copies several necessary constants from the + * base class, and sets up a reference to the ordering class to + * use. + * \param[in] con the container class to use. + * \param[in] vo_ the ordering class to use. */ + template + c_loop_order(c_class &con,particle_order &vo_) + : c_loop_base(con), vo(vo_), nx(con.nx), nxy(con.nxy) {} + /** Sets the class to consider the first particle. + * \return True if there is any particle to consider, false + * otherwise. */ + inline bool start() { + cp=vo.o;op=vo.op; + if(cp!=op) { + ijk=*(cp++);decode(); + q=*(cp++); + return true; + } else return false; + } + /** Finds the next particle to test. + * \return True if there is another particle, false if no more + * particles are available. */ + inline bool inc() { + if(cp==op) return false; + ijk=*(cp++);decode(); + q=*(cp++); + return true; + } + private: + /** The number of computational blocks in the x direction. */ + const int nx; + /** The number of computational blocks in a z-slice. */ + const int nxy; + /** Takes the current block index and computes indices in the + * x, y, and z directions. */ + inline void decode() { + k=ijk/nxy; + int ijkt=ijk-nxy*k; + j=ijkt/nx; + i=ijkt-j*nx; + } +}; + +/** \brief A class for looping over all particles in a container_periodic or + * container_periodic_poly class. + * + * Since the container_periodic and container_periodic_poly classes have a + * fundamentally different memory organization, the regular loop classes cannot + * be used with them. */ +class c_loop_all_periodic : public c_loop_base { + public: + /** The constructor copies several necessary constants from the + * base periodic container class. + * \param[in] con the periodic container class to use. */ + template + c_loop_all_periodic(c_class &con) : c_loop_base(con), ey(con.ey), ez(con.ez), wy(con.wy), wz(con.wz), + ijk0(nx*(ey+con.oy*ez)), inc2(2*nx*con.ey+1) {} + /** Sets the class to consider the first particle. + * \return True if there is any particle to consider, false + * otherwise. */ + inline bool start() { + i=0; + j=ey; + k=ez; + ijk=ijk0; + q=0; + while(co[ijk]==0) if(!next_block()) return false; + return true; + } + /** Finds the next particle to test. + * \return True if there is another particle, false if no more + * particles are available. */ + inline bool inc() { + q++; + if(q>=co[ijk]) { + q=0; + do { + if(!next_block()) return false; + } while(co[ijk]==0); + } + return true; + } + private: + /** The lower y index (inclusive) of the primary domain within + * the block structure. */ + int ey; + /** The lower y index (inclusive) of the primary domain within + * the block structure. */ + int ez; + /** The upper y index (exclusive) of the primary domain within + * the block structure. */ + int wy; + /** The upper z index (exclusive) of the primary domain within + * the block structure. */ + int wz; + /** The index of the (0,0,0) block within the block structure. + */ + int ijk0; + /** A value to increase ijk by when the z index is increased. + */ + int inc2; + /** Updates the internal variables to find the next + * computational block with any particles. + * \return True if another block is found, false if there are + * no more blocks. */ + inline bool next_block() { + i++; + if(i==nx) { + i=0;j++; + if(j==wy) { + j=ey;k++; + if(k==wz) return false; + ijk+=inc2; + } else ijk++; + } else ijk++; + return true; + } +}; + +/** \brief Class for looping over all of the particles specified in a + * pre-assembled particle_order class, for use with container_periodic classes. + * + * The particle_order class can be used to create a specific order of particles + * within the container. This class can then loop over these particles in this + * order. The class is particularly useful in cases where the ordering of the + * output must match the ordering of particles as they were inserted into the + * container. */ +class c_loop_order_periodic : public c_loop_base { + public: + /** A reference to the ordering class to use. */ + particle_order &vo; + /** A pointer to the current position in the ordering class. */ + int *cp; + /** A pointer to the end position in the ordering class. */ + int *op; + /** The constructor copies several necessary constants from the + * base class, and sets up a reference to the ordering class to + * use. + * \param[in] con the container class to use. + * \param[in] vo_ the ordering class to use. */ + template + c_loop_order_periodic(c_class &con,particle_order &vo_) + : c_loop_base(con), vo(vo_), nx(con.nx), oxy(con.nx*con.oy) {} + /** Sets the class to consider the first particle. + * \return True if there is any particle to consider, false + * otherwise. */ + inline bool start() { + cp=vo.o;op=vo.op; + if(cp!=op) { + ijk=*(cp++);decode(); + q=*(cp++); + return true; + } else return false; + } + /** Finds the next particle to test. + * \return True if there is another particle, false if no more + * particles are available. */ + inline bool inc() { + if(cp==op) return false; + ijk=*(cp++);decode(); + q=*(cp++); + return true; + } + private: + /** The number of computational blocks in the x direction. */ + const int nx; + /** The number of computational blocks in a z-slice. */ + const int oxy; + /** Takes the current block index and computes indices in the + * x, y, and z directions. */ + inline void decode() { + k=ijk/oxy; + int ijkt=ijk-oxy*k; + j=ijkt/nx; + i=ijkt-j*nx; + } +}; + +} + +#endif diff --git a/src/third_party/voro/src/cell.cc b/src/third_party/voro/src/cell.cc new file mode 100644 index 000000000..81e37dad8 --- /dev/null +++ b/src/third_party/voro/src/cell.cc @@ -0,0 +1,2714 @@ +// Voro++, a 3D cell-based Voronoi library +// +// Author : Chris H. Rycroft (Harvard University / LBL) +// Email : chr@alum.mit.edu +// Date : August 30th 2011 + +/** \file cell.cc + * \brief Function implementations for the voronoicell and related classes. */ + +#include +#include + +#include "config.hh" +#include "common.hh" +#include "cell.hh" + +namespace voro { + +/** Constructs a Voronoi cell and sets up the initial memory. */ +voronoicell_base::voronoicell_base(double max_len_sq) : + current_vertices(init_vertices), current_vertex_order(init_vertex_order), + current_delete_size(init_delete_size), current_delete2_size(init_delete2_size), + current_xsearch_size(init_xsearch_size), + ed(new int*[current_vertices]), nu(new int[current_vertices]), + mask(new unsigned int[current_vertices]), + pts(new double[current_vertices<<2]), tol(tolerance*max_len_sq), + tol_cu(tol*sqrt(tol)), big_tol(big_tolerance_fac*tol), mem(new int[current_vertex_order]), + mec(new int[current_vertex_order]), + mep(new int*[current_vertex_order]), ds(new int[current_delete_size]), + stacke(ds+current_delete_size), ds2(new int[current_delete2_size]), + stacke2(ds2+current_delete2_size), xse(new int[current_xsearch_size]), + stacke3(xse+current_xsearch_size), maskc(0) { + int i; + for(i=0;i=0;i--) if(mem[i]>0) delete [] mep[i]; + delete [] xse; + delete [] ds2;delete [] ds; + delete [] mep;delete [] mec; + delete [] mem;delete [] pts; + delete [] mask; + delete [] nu;delete [] ed; +} + +/** Ensures that enough memory is allocated prior to carrying out a copy. + * \param[in] vc a reference to the specialized version of the calling class. + * \param[in] vb a pointered to the class to be copied. */ +template +void voronoicell_base::check_memory_for_copy(vc_class &vc,voronoicell_base* vb) { + while(current_vertex_ordercurrent_vertex_order) add_memory_vorder(vc); + for(int i=0;imec[i]) add_memory(vc,i); + while(current_verticesp) add_memory_vertices(vc); +} + +/** Copies the vertex and edge information from another class. The routine + * assumes that enough memory is available for the copy. + * \param[in] vb a pointer to the class to copy. */ +void voronoicell_base::copy(voronoicell_base* vb) { + int i,j; + p=vb->p;up=0; + for(i=0;imec[i]; + for(j=0;jmep[i][j]; + for(j=0;jnu[i]; + for(i=0;i<(p<<2);i++) pts[i]=vb->pts[i]; +} + +/** Copies the information from another voronoicell class into this + * class, extending memory allocation if necessary. + * \param[in] c the class to copy. */ +void voronoicell_neighbor::operator=(voronoicell &c) { + voronoicell_base *vb=((voronoicell_base*) &c); + check_memory_for_copy(*this,vb);copy(vb); + int i,j; + for(i=0;i +void voronoicell_base::add_memory(vc_class &vc,int i) { + int s=(i<<1)+1; + if(mem[i]==0) { + vc.n_allocate(i,init_n_vertices); + mep[i]=new int[init_n_vertices*s]; + mem[i]=init_n_vertices; +#if VOROPP_VERBOSE >=2 + fprintf(stderr,"Order %d vertex memory created\n",i); +#endif + } else { + int j=0,k,*l; + mem[i]<<=1; + if(mem[i]>max_n_vertices) voro_fatal_error("Point memory allocation exceeded absolute maximum",VOROPP_MEMORY_ERROR); +#if VOROPP_VERBOSE >=2 + fprintf(stderr,"Order %d vertex memory scaled up to %d\n",i,mem[i]); +#endif + l=new int[s*mem[i]]; + int m=0; + vc.n_allocate_aux1(i); + while(j=0) { + ed[k]=l+j; + vc.n_set_to_aux1_offset(k,m); + } else { + int *dsp; + for(dsp=ds2;dsp=3 + fputs("Relocated dangling pointer",stderr); +#endif + } + for(k=0;k +void voronoicell_base::add_memory_vertices(vc_class &vc) { + int i=(current_vertices<<1),j,**pp,*pnu; + unsigned int* pmask; + if(i>max_vertices) voro_fatal_error("Vertex memory allocation exceeded absolute maximum",VOROPP_MEMORY_ERROR); +#if VOROPP_VERBOSE >=2 + fprintf(stderr,"Vertex memory scaled up to %d\n",i); +#endif + double *ppts; + pp=new int*[i]; + for(j=0;j +void voronoicell_base::add_memory_vorder(vc_class &vc) { + int i=(current_vertex_order<<1),j,*p1,**p2; + if(i>max_vertex_order) voro_fatal_error("Vertex order memory allocation exceeded absolute maximum",VOROPP_MEMORY_ERROR); +#if VOROPP_VERBOSE >=2 + fprintf(stderr,"Vertex order memory scaled up to %d\n",i); +#endif + p1=new int[i]; + for(j=0;jmax_delete_size) voro_fatal_error("Delete stack 1 memory allocation exceeded absolute maximum",VOROPP_MEMORY_ERROR); +#if VOROPP_VERBOSE >=2 + fprintf(stderr,"Delete stack 1 memory scaled up to %d\n",current_delete_size); +#endif + int *dsn=new int[current_delete_size],*dsnp=dsn,*dsp=ds; + while(dspmax_delete2_size) voro_fatal_error("Delete stack 2 memory allocation exceeded absolute maximum",VOROPP_MEMORY_ERROR); +#if VOROPP_VERBOSE >=2 + fprintf(stderr,"Delete stack 2 memory scaled up to %d\n",current_delete2_size); +#endif + int *dsn=new int[current_delete2_size],*dsnp=dsn,*dsp=ds2; + while(dspmax_xsearch_size) voro_fatal_error("Extra search stack memory allocation exceeded absolute maximum",VOROPP_MEMORY_ERROR); +#if VOROPP_VERBOSE >=2 + fprintf(stderr,"Extra search stack memory scaled up to %d\n",current_xsearch_size); +#endif + int *dsn=new int[current_xsearch_size],*dsnp=dsn,*dsp=xse; + while(dspl) break; + } + if(ls==nu[lp]) if(definite_max(lp,ls,l,u,uw)) { + up=lp; + return false; + } + + while(uw==0) { + //if(++count>=p) failsafe_find(lp,ls,us,l,u); + + // Test all the neighbors of the current point + // and find the one which is closest to the + // plane + vs=ed[lp][nu[lp]+ls];lp=up;l=u; + for(ls=0;lsl) break; + } + if(ls==nu[lp]&&definite_max(lp,ls,l,u,uw)) { + up=lp; + return false; + } + } + us=ed[lp][nu[lp]+ls]; + return true; +} + +/** Checks whether a particular point lp is a definite maximum, searching + * through any possible minor non-convexities, for a better maximum. + * \param[in] (x,y,z) the normal vector to the plane. */ +bool voronoicell_base::definite_max(int &lp,int &ls,double &l,double &u,unsigned int &uw) { + int tp=lp,ts,qp=0; + unsigned int qw; + double q; + + // Check to see whether point up is a well-defined maximum. Otherwise + // any neighboring vertices of up that are marginal need to be + // followed, to see if they lead to a better maximum. + for(ts=0;tsl-big_tol) break; + } + if(ts==nu[tp]) return true; + + // The point tp is marginal, so it will be necessary to do the + // flood-fill search. Mark the point tp and the point qp, and search + // any remaining neighbors of the point tp. + int *stackp=ds+1; + flip(lp); + flip(qp); + *ds=qp; + ts++; + while(tsl-big_tol) { + if(stackp==stacke) add_memory_ds(); + *(stackp++)=up; + flip(up); + } + ts++; + } + + // Consider additional marginal points, starting with the original + // point qp + int *spp=ds; + while(sppl) { + flip(lp); + lp=tp; + ls=ts; + m_test(lp,l); + up=qp; + uw=qw; + u=q; + while(stackp>ds) flip(*(--stackp)); + return false; + } + + // The point is marginal and therefore must also be + // considered + if(q>l-big_tol) { + if(stackp==stacke) { + int nn=stackp-spp; + add_memory_ds(); + spp=stackp-nn; + } + *(stackp++)=qp; + flip(qp); + } + } + } + + // Reset markers and return false + flip(lp); + while(stackp>ds) flip(*(--stackp)); + return true; +} + +inline bool voronoicell_base::search_downward(unsigned int &lw,int &lp,int &ls,int &us,double &l,double &u) { + int vs; + + // The test point is outside of the cutting space + for(us=0;usl) break; + } + if(us==nu[up]) if(definite_min(lp,us,l,u,lw)) return false; + + while(lw==2) { + //if(++count>=p) failsafe_find(lp,ls,us,l,u); + + // Test all the neighbors of the current point + // and find the one which is closest to the + // plane + vs=ed[up][nu[up]+us];up=lp;u=l; + for(us=0;usl) break; + } + if(us==nu[up]&&definite_min(lp,us,l,u,lw)) return false; + } + ls=ed[up][nu[up]+us]; + return true; +} + +bool voronoicell_base::definite_min(int &lp,int &us,double &l,double &u,unsigned int &lw) { + int tp=up,ts,qp=0; + unsigned int qw; + double q; + + // Check to see whether point up is a well-defined maximum. Otherwise + // any neighboring vertices of up that are marginal need to be + // followed, to see if they lead to a better maximum. + for(ts=0;tsds) flip(*(--stackp)); + return false; + } + + // The point is marginal and therefore must also be + // considered + if(qds) flip(*(--stackp)); + return true; +} + +/** Cuts the Voronoi cell by a particle whose center is at a separation of + * (x,y,z) from the cell center. The value of rsq should be initially set to + * \f$x^2+y^2+z^2\f$. + * \param[in] vc a reference to the specialized version of the calling class. + * \param[in] (x,y,z) the normal vector to the plane. + * \param[in] rsq the distance along this vector of the plane. + * \param[in] p_id the plane ID (for neighbor tracking only). + * \return False if the plane cut deleted the cell entirely, true otherwise. */ +template +bool voronoicell_base::nplane(vc_class &vc,double x,double y,double z,double rsq,int p_id) { + int i,j,lp=up,cp,qp,*dsp; + int us=0,ls=0; + unsigned int uw,lw; + int *edp,*edd;stackp=ds; + double u,l=0;up=0; + + // Initialize the safe testing routine + px=x;py=y;pz=z;prsq=rsq; + maskc+=4; + if(maskc<4) reset_mask(); + + uw=m_test(up,u); + if(uw==2) { + if(!search_downward(lw,lp,ls,us,l,u)) return false; + if(lw==1) {up=lp;lp=-1;} + } else if(uw==0) { + if(!search_upward(uw,lp,ls,us,l,u)) return true; + if(uw==1) lp=-1; + } else { + lp=-1; + } + + // Set stack pointers + stackp=ds;stackp2=ds2;stackp3=xse; + + // Store initial number of vertices + int op=p; + + if(create_facet(vc,lp,ls,l,us,u,p_id)) return false; + int k=0;int xtra=0; + while(xse+k=op) continue; + + if(uw==0) { + if(u>-big_tol&&ed[up][nu[up]<<1]!=-1) { + ed[up][nu[up]<<1]=-1; + if(stackp3==stacke3) add_memory_xse(); + *(stackp3++)=up; + } + } else if(uw==1) { + + // This is a possible facet starting + // from a vertex on the cutting plane + if(create_facet(vc,-1,0,0,0,u,p_id)) return false; + } else { + + // This is a new facet + us=ed[lp][nu[lp]+ls]; + m_test(lp,l); + if(create_facet(vc,lp,ls,l,us,u,p_id)) return false; + } + } + xtra++; + } + + // Reset back pointers on extra search stack + for(dsp=xse;dspds) { + --p; + while(ed[p][nu[p]]==-1) { + j=nu[p]; + edp=ed[p];edd=(mep[j]+((j<<1)+1)*--mec[j]); + while(edp0) voro_fatal_error("Zero order vertex formed",VOROPP_INTERNAL_ERROR); + + // Collapse any order 2 vertices and exit + return collapse_order2(vc); +} + +/** Creates a new facet. + * \return True if cell deleted, false otherwise. */ +template +bool voronoicell_base::create_facet(vc_class &vc,int lp,int ls,double l,int us,double u,int p_id) { + int i,j,k,qp,qs,iqs,cp,cs,rp,*edp,*edd; + unsigned int lw,qw; + bool new_double_edge=false,double_edge=false; + double q,r; + + // We're about to add the first point of the new facet. In either + // routine, we have to add a point, so first check there's space for + // it. + if(p==current_vertices) add_memory_vertices(vc); + + if(lp==-1) { + + // We want to be strict about reaching the conclusion that the + // cell is entirely within the cutting plane. It's not enough + // to find a vertex that has edges which are all inside or on + // the plane. If the vertex has neighbors that are also on the + // plane, we should check those too. + if(!search_for_outside_edge(up)) return true; + + // The search algorithm found a point which is on the cutting + // plane. We leave that point in place, and create a new one at + // the same location. + pts[(p<<2)]=pts[(up<<2)]; + pts[(p<<2)+1]=pts[(up<<2)+1]; + pts[(p<<2)+2]=pts[(up<<2)+2]; + + // Search for a collection of edges of the test vertex which + // are outside of the cutting space. Begin by testing the + // zeroth edge. + i=0; + lp=*ed[up]; + lw=m_testx(lp,l); + if(lw!=0) { + + // The first edge is either inside the cutting space, + // or lies within the cutting plane. Test the edges + // sequentially until we find one that is outside. + unsigned int rw=lw; + do { + i++; + + // If we reached the last edge with no luck + // then all of the vertices are inside + // or on the plane, so the cell is completely + // deleted + if(i==nu[up]) return true; + lp=ed[up][i]; + lw=m_testx(lp,l); + } while (lw!=0); + j=i+1; + + // We found an edge outside the cutting space. Keep + // moving through these edges until we find one that's + // inside or on the plane. + while(j=current_vertex_order) add_memory_vorder(vc); + if(mec[nu[p]]==mem[nu[p]]) add_memory(vc,nu[p]); + vc.n_set_pointer(p,nu[p]); + ed[p]=mep[nu[p]]+((nu[p]<<1)+1)*mec[nu[p]]++; + ed[p][nu[p]<<1]=p; + + // Copy the edges of the original vertex into the new + // one. Delete the edges of the original vertex, and + // update the relational table. + us=cycle_down(i,up); + while(i=current_vertex_order) add_memory_vorder(vc); + if(mec[nu[p]]==mem[nu[p]]) add_memory(vc,nu[p]); + + // Copy the edges of the original vertex into the new + // one. Delete the edges of the original vertex, and + // update the relational table. + vc.n_set_pointer(p,nu[p]); + ed[p]=mep[nu[p]]+((nu[p]<<1)+1)*mec[nu[p]]++; + ed[p][nu[p]<<1]=p; + us=i++; + while(i0) k+=nu[j]; + } else { + if(j>0) { + + // This vertex was visited before, so + // count those vertices to the ones we + // already have. + k+=nu[j]; + + // The only time when we might make a + // duplicate edge is if the point we're + // going to move to next is also a + // marginal point, so test for that + // first. + if(lw==1) { + + // Now see whether this marginal point + // has been visited before. + i=-ed[lp][nu[lp]<<1]; + if(i>0) { + + // Now see if the last edge of that other + // marginal point actually ends up here. + if(ed[i][nu[i]-1]==j) { + new_double_edge=true; + k-=1; + } else new_double_edge=false; + } else { + + // That marginal point hasn't been visited + // before, so we probably don't have to worry + // about duplicate edges, except in the + // case when that's the way into the end + // of the facet, because that way always creates + // an edge. + if(j==rp&&lp==up&&ed[qp][nu[qp]+qs]==us) { + new_double_edge=true; + k-=1; + } else new_double_edge=false; + } + } else new_double_edge=false; + } else { + + // The vertex hasn't been visited + // before, but let's see if it's + // marginal + if(lw==1) { + + // If it is, we need to check + // for the case that it's a + // small branch, and that we're + // heading right back to where + // we came from + i=-ed[lp][nu[lp]<<1]; + if(i==cp) { + new_double_edge=true; + k-=1; + } else new_double_edge=false; + } else new_double_edge=false; + } + } + + // k now holds the number of edges of the new vertex + // we are forming. Add memory for it if it doesn't exist + // already. + while(k>=current_vertex_order) add_memory_vorder(vc); + if(mec[k]==mem[k]) add_memory(vc,k); + + // Now create a new vertex with order k, or augment + // the existing one + if(j>0) { + + // If we're augmenting a vertex but we don't + // actually need any more edges, just skip this + // routine to avoid memory confusion + if(nu[j]!=k) { + + // Allocate memory and copy the edges + // of the previous instance into it + vc.n_set_aux1(k); + edp=mep[k]+((k<<1)+1)*mec[k]++; + i=0; + while(i +inline bool voronoicell_base::collapse_order2(vc_class &vc) { + if(!collapse_order1(vc)) return false; + int a,b,i,j,k,l; + while(mec[2]>0) { + + // Pick a order 2 vertex and read in its edges + i=--mec[2]; + j=mep[2][5*i];k=mep[2][5*i+1]; + if(j==k) { +#if VOROPP_VERBOSE >=1 + fputs("Order two vertex joins itself",stderr); +#endif + return false; + } + + // Scan the edges of j to see if joins k + for(l=0;l +bool voronoicell_base::collapse_order1(vc_class &vc) { + int i,j,k; + while(mec[1]>0) { + up=0; +#if VOROPP_VERBOSE >=1 + fputs("Order one collapse\n",stderr); +#endif + i=--mec[1]; + j=mep[1][3*i];k=mep[1][3*i+1]; + i=mep[1][3*i+2]; + if(!delete_connection(vc,j,k,false)) return false; + --p; + if(up==i) up=0; + if(p!=i) { + if(up==p) up=i; + pts[i<<2]=pts[p<<2]; + pts[(i<<2)+1]=pts[(p<<2)+1]; + pts[(i<<2)+2]=pts[(p<<2)+2]; + for(k=0;k +bool voronoicell_base::delete_connection(vc_class &vc,int j,int k,bool hand) { + int q=hand?k:cycle_up(k,j); + int i=nu[j]-1,l,*edp,*edd,m; +#if VOROPP_VERBOSE >=1 + if(i<1) { + fputs("Zero order vertex formed\n",stderr); + return false; + } +#endif + if(mec[i]==mem[i]) add_memory(vc,i); + vc.n_set_aux1(i); + for(l=0;l=0) { + ed[i][j]=-1-k; + l=cycle_up(ed[i][nu[i]+j],k); + vx=pts[k<<2]-*pts; + vy=pts[(k<<2)+1]-pts[1]; + vz=pts[(k<<2)+2]-pts[2]; + m=ed[k][l];ed[k][l]=-1-m; + while(m!=i) { + n=cycle_up(ed[k][nu[k]+l],m); + wx=pts[(m<<2)]-*pts; + wy=pts[(m<<2)+1]-pts[1]; + wz=pts[(m<<2)+2]-pts[2]; + vol+=ux*vy*wz+uy*vz*wx+uz*vx*wy-uz*vy*wx-uy*vx*wz-ux*vz*wy; + k=m;l=n;vx=wx;vy=wy;vz=wz; + m=ed[k][l];ed[k][l]=-1-m; + } + } + } + } + reset_edges(); + return vol*fe; +} + +/** Calculates the contributions to the Minkowski functionals for this Voronoi cell. + * \param[in] r the radius to consider. + * \param[out] ar the area functional. + * \param[out] vo the volume functional. */ +void voronoicell_base::minkowski(double r,double &ar,double &vo) { + int i,j,k,l,m,n; + ar=vo=0;r*=2; + for(i=1;i=0) { + ed[i][j]=-1-k; + l=cycle_up(ed[i][nu[i]+j],k); + m=ed[k][l];ed[k][l]=-1-m; + while(m!=i) { + n=cycle_up(ed[k][nu[k]+l],m); + minkowski_contrib(i,k,m,r,ar,vo); + k=m;l=n; + m=ed[k][l];ed[k][l]=-1-m; + } + } + } + vo*=0.125; + ar*=0.25; + reset_edges(); +} + +inline void voronoicell_base::minkowski_contrib(int i,int k,int m,double r,double &ar,double &vo) { + double ix=pts[4*i],iy=pts[4*i+1],iz=pts[4*i+2], + kx=pts[4*k],ky=pts[4*k+1],kz=pts[4*k+2], + mx=pts[4*m],my=pts[4*m+1],mz=pts[4*m+2], + ux=kx-ix,uy=ky-iy,uz=kz-iz,vx=mx-kx,vy=my-ky,vz=mz-kz, + e1x=uz*vy-uy*vz,e1y=ux*vz-uz*vx,e1z=uy*vx-ux*vy,e2x,e2y,e2z, + wmag=e1x*e1x+e1y*e1y+e1z*e1z; + if(wmag0.5) { + e2x=-e1y;e2y=e1x;e2z=0; + } else if(fabs(e1y)>0.5) { + e2x=0;e2y=-e1z;e2z=e1y; + } else { + e2x=e1z;e2y=0;e2z=-e1x; + } + wmag=1/sqrt(e2x*e2x+e2y*e2y+e2z*e2z); + e2x*=wmag;e2y*=wmag;e2z*=wmag; + + // Compute third orthonormal vector + double e3x=e1z*e2y-e1y*e2z, + e3y=e1x*e2z-e1z*e2x, + e3z=e1y*e2x-e1x*e2y, + x0=e1x*ix+e1y*iy+e1z*iz; + if(x0 &v) { + double solid_angle; + std::vector normalized(3*p); + v.clear(); + int i,j,k,l,m,n; + for (i=0;i=0) { + solid_angle=0; + ed[i][j]=-1-k; + l=cycle_up(ed[i][nu[i]+j],k); + m=ed[k][l];ed[k][l]=-1-m; + while(m!=i) { + n=cycle_up(ed[k][nu[k]+l],m); + solid_angle+=calculate_solid_angle(&normalized[3*i],&normalized[3*k],&normalized[3*m]); + k=m;l=n; + m=ed[k][l];ed[k][l]=-1-m; + } + v.push_back(solid_angle); + } + } + reset_edges(); +} + +/** Calculates the areas of each face of the Voronoi cell and prints the + * results to an output stream. + * \param[out] v the vector to store the results in. */ +void voronoicell_base::face_areas(std::vector &v) { + double area; + v.clear(); + int i,j,k,l,m,n; + double ux,uy,uz,vx,vy,vz,wx,wy,wz; + for(i=1;i=0) { + area=0; + ed[i][j]=-1-k; + l=cycle_up(ed[i][nu[i]+j],k); + m=ed[k][l];ed[k][l]=-1-m; + while(m!=i) { + n=cycle_up(ed[k][nu[k]+l],m); + ux=pts[4*k]-pts[4*i]; + uy=pts[4*k+1]-pts[4*i+1]; + uz=pts[4*k+2]-pts[4*i+2]; + vx=pts[4*m]-pts[4*i]; + vy=pts[4*m+1]-pts[4*i+1]; + vz=pts[4*m+2]-pts[4*i+2]; + wx=uy*vz-uz*vy; + wy=uz*vx-ux*vz; + wz=ux*vy-uy*vx; + area+=sqrt(wx*wx+wy*wy+wz*wz); + k=m;l=n; + m=ed[k][l];ed[k][l]=-1-m; + } + v.push_back(0.125*area); + } + } + reset_edges(); +} + +/** Calculates the total surface area of the Voronoi cell. + * \return The computed area. */ +double voronoicell_base::surface_area() { + double area=0; + int i,j,k,l,m,n; + double ux,uy,uz,vx,vy,vz,wx,wy,wz; + for(i=1;i=0) { + ed[i][j]=-1-k; + l=cycle_up(ed[i][nu[i]+j],k); + m=ed[k][l];ed[k][l]=-1-m; + while(m!=i) { + n=cycle_up(ed[k][nu[k]+l],m); + ux=pts[4*k]-pts[4*i]; + uy=pts[4*k+1]-pts[4*i+1]; + uz=pts[4*k+2]-pts[4*i+2]; + vx=pts[4*m]-pts[4*i]; + vy=pts[4*m+1]-pts[4*i+1]; + vz=pts[4*m+2]-pts[4*i+2]; + wx=uy*vz-uz*vy; + wy=uz*vx-ux*vz; + wz=ux*vy-uy*vx; + area+=sqrt(wx*wx+wy*wy+wz*wz); + k=m;l=n; + m=ed[k][l];ed[k][l]=-1-m; + } + } + } + reset_edges(); + return 0.125*area; +} + +/** Calculates the centroid of the Voronoi cell, by decomposing the cell into + * tetrahedra extending outward from the zeroth vertex. + * \param[out] (cx,cy,cz) references to floating point numbers in which to + * pass back the centroid vector. */ +void voronoicell_base::centroid(double &cx,double &cy,double &cz) { + double tvol,vol=0;cx=cy=cz=0; + int i,j,k,l,m,n; + double ux,uy,uz,vx,vy,vz,wx,wy,wz; + for(i=1;i=0) { + ed[i][j]=-1-k; + l=cycle_up(ed[i][nu[i]+j],k); + vx=pts[4*k]-*pts; + vy=pts[4*k+1]-pts[1]; + vz=pts[4*k+2]-pts[2]; + m=ed[k][l];ed[k][l]=-1-m; + while(m!=i) { + n=cycle_up(ed[k][nu[k]+l],m); + wx=pts[4*m]-*pts; + wy=pts[4*m+1]-pts[1]; + wz=pts[4*m+2]-pts[2]; + tvol=ux*vy*wz+uy*vz*wx+uz*vx*wy-uz*vy*wx-uy*vx*wz-ux*vz*wy; + vol+=tvol; + cx+=(wx+vx-ux)*tvol; + cy+=(wy+vy-uy)*tvol; + cz+=(wz+vz-uz)*tvol; + k=m;l=n;vx=wx;vy=wy;vz=wz; + m=ed[k][l];ed[k][l]=-1-m; + } + } + } + } + reset_edges(); + if(vol>tol_cu) { + vol=0.125/vol; + cx=cx*vol+0.5*(*pts); + cy=cy*vol+0.5*pts[1]; + cz=cz*vol+0.5*pts[2]; + } else cx=cy=cz=0; +} + +/** Computes the maximum radius squared of a vertex from the center of the + * cell. It can be used to determine when enough particles have been testing an + * all planes that could cut the cell have been considered. + * \return The maximum radius squared of a vertex.*/ +double voronoicell_base::max_radius_squared() { + double r,s,*ptsp=pts+4,*ptse=pts+(p<<2); + r=*pts*(*pts)+pts[1]*pts[1]+pts[2]*pts[2]; + while(ptspr) r=s; + } + return r; +} + +/** Calculates the total edge distance of the Voronoi cell. + * \return A floating point number holding the calculated distance. */ +double voronoicell_base::total_edge_distance() { + int i,j,k; + double dis=0,dx,dy,dz; + for(i=0;ii) { + dx=pts[k<<2]-pts[i<<2]; + dy=pts[(k<<2)+1]-pts[(i<<2)+1]; + dz=pts[(k<<2)+2]-pts[(i<<2)+2]; + dis+=sqrt(dx*dx+dy*dy+dz*dz); + } + } + return 0.5*dis; +} + +/** Outputs the edges of the Voronoi cell in POV-Ray format to an open file + * stream, displacing the cell by given vector. + * \param[in] (x,y,z) a displacement vector to be added to the cell's position. + * \param[in] fp a file handle to write to. */ +void voronoicell_base::draw_pov(double x,double y,double z,FILE* fp) { + int i,j,k;double *ptsp=pts,*pt2; + char posbuf1[128],posbuf2[128]; + for(i=0;i,r}\n",posbuf1); + for(j=0;j,<%s>,r}\n",posbuf1,posbuf2); + } + } + } +} + +/** Outputs the edges of the Voronoi cell in gnuplot format to an output stream. + * \param[in] (x,y,z) a displacement vector to be added to the cell's position. + * \param[in] fp a file handle to write to. */ +void voronoicell_base::draw_gnuplot(double x,double y,double z,FILE *fp) { + int i,j,k,l,m; + for(i=1;i=0) { + fprintf(fp,"%g %g %g\n",x+0.5*pts[i<<2],y+0.5*pts[(i<<2)+1],z+0.5*pts[(i<<2)+2]); + l=i;m=j; + do { + ed[k][ed[l][nu[l]+m]]=-1-l; + ed[l][m]=-1-k; + l=k; + fprintf(fp,"%g %g %g\n",x+0.5*pts[k<<2],y+0.5*pts[(k<<2)+1],z+0.5*pts[(k<<2)+2]); + } while (search_edge(l,m,k)); + fputs("\n\n",fp); + } + } + reset_edges(); +} + +inline bool voronoicell_base::search_edge(int l,int &m,int &k) { + for(m=0;m=0) return true; + } + return false; +} + +/** Outputs the Voronoi cell in the POV mesh2 format, described in section + * 1.3.2.2 of the POV-Ray documentation. The mesh2 output consists of a list of + * vertex vectors, followed by a list of triangular faces. The routine also + * makes use of the optional inside_vector specification, which makes the mesh + * object solid, so that the POV-Ray Constructive Solid Geometry (CSG) can be + * applied. + * \param[in] (x,y,z) a displacement vector to be added to the cell's position. + * \param[in] fp a file handle to write to. */ +void voronoicell_base::draw_pov_mesh(double x,double y,double z,FILE *fp) { + int i,j,k,l,m,n; + double *ptsp=pts; + fprintf(fp,"mesh2 {\nvertex_vectors {\n%d\n",p); + for(i=0;i\n",x+*ptsp*0.5,y+ptsp[1]*0.5,z+ptsp[2]*0.5); + fprintf(fp,"}\nface_indices {\n%d\n",(p-2)<<1); + for(i=1;i=0) { + ed[i][j]=-1-k; + l=cycle_up(ed[i][nu[i]+j],k); + m=ed[k][l];ed[k][l]=-1-m; + while(m!=i) { + n=cycle_up(ed[k][nu[k]+l],m); + fprintf(fp,",<%d,%d,%d>\n",i,k,m); + k=m;l=n; + m=ed[k][l];ed[k][l]=-1-m; + } + } + } + fputs("}\ninside_vector <0,0,1>\n}\n",fp); + reset_edges(); +} + +/** Several routines in the class that gather cell-based statistics internally + * track their progress by flipping edges to negative so that they know what + * parts of the cell have already been tested. This function resets them back + * to positive. When it is called, it assumes that every edge in the routine + * should have already been flipped to negative, and it bails out with an + * internal error if it encounters a positive edge. */ +inline void voronoicell_base::reset_edges() { + int i,j; + for(i=0;i=0) voro_fatal_error("Edge reset routine found a previously untested edge",VOROPP_INTERNAL_ERROR); + ed[i][j]=-1-ed[i][j]; + } +} + +/** Checks to see if a given vertex is inside, outside or within the test + * plane. If the point is far away from the test plane, the routine immediately + * returns whether it is inside or outside. If the routine is close the the + * plane and within the specified tolerance, then the special check_marginal() + * routine is called. + * \param[in] n the vertex to test. + * \param[out] ans the result of the scalar product used in evaluating the + * location of the point. + * \return -1 if the point is inside the plane, 1 if the point is outside the + * plane, or 0 if the point is within the plane. */ +inline unsigned int voronoicell_base::m_test(int n,double &ans) { + if(mask[n]>=maskc) { + ans=pts[4*n+3]; + return mask[n]&3; + } else return m_calc(n,ans); +} + +unsigned int voronoicell_base::m_calc(int n,double &ans) { + double *pp=pts+4*n; + ans=*(pp++)*px; + ans+=*(pp++)*py; + ans+=*(pp++)*pz-prsq; + *pp=ans; + unsigned int maskr=ans<-tol?0:(ans>tol?2:1); + mask[n]=maskc|maskr; + return maskr; +} + +/** Checks to see if a given vertex is inside, outside or within the test + * plane. If the point is far away from the test plane, the routine immediately + * returns whether it is inside or outside. If the routine is close the the + * plane and within the specified tolerance, then the special check_marginal() + * routine is called. + * \param[in] n the vertex to test. + * \param[out] ans the result of the scalar product used in evaluating the + * location of the point. + * \return -1 if the point is inside the plane, 1 if the point is outside the + * plane, or 0 if the point is within the plane. */ +inline unsigned int voronoicell_base::m_testx(int n,double &ans) { + unsigned int maskr; + if(mask[n]>=maskc) { + ans=pts[4*n+3]; + maskr=mask[n]&3; + } else maskr=m_calc(n,ans); + if(maskr==0&&ans>-big_tol&&ed[n][nu[n]<<1]!=-1) { + ed[n][nu[n]<<1]=-1; + if(stackp3==stacke3) add_memory_xse(); + *(stackp3++)=n; + } + return maskr; +} + +/** This routine calculates the unit normal vectors for every face. + * \param[out] v the vector to store the results in. */ +void voronoicell_base::normals(std::vector &v) { + int i,j,k; + v.clear(); + for(i=1;i=0) normals_search(v,i,j,k); + } + reset_edges(); +} + +/** This inline routine is called by normals(). It attempts to construct a + * single normal vector that is associated with a particular face. It first + * traces around the face, trying to find two vectors along the face edges + * whose vector product is above the numerical tolerance. It then constructs + * the normal vector using this product. If the face is too small, and none of + * the vector products are large enough, the routine may return (0,0,0) as the + * normal vector. + * \param[in] v the vector to store the results in. + * \param[in] i the initial vertex of the face to test. + * \param[in] j the index of an edge of the vertex. + * \param[in] k the neighboring vertex of i, set to ed[i][j]. */ +inline void voronoicell_base::normals_search(std::vector &v,int i,int j,int k) { + ed[i][j]=-1-k; + int l=cycle_up(ed[i][nu[i]+j],k),m; + double ux,uy,uz,vx,vy,vz,wx,wy,wz,wmag; + do { + m=ed[k][l];ed[k][l]=-1-m; + ux=pts[4*m]-pts[4*k]; + uy=pts[4*m+1]-pts[4*k+1]; + uz=pts[4*m+2]-pts[4*k+2]; + + // Test to see if the length of this edge is above the tolerance + if(ux*ux+uy*uy+uz*uz>tol) { + while(m!=i) { + l=cycle_up(ed[k][nu[k]+l],m); + k=m;m=ed[k][l];ed[k][l]=-1-m; + vx=pts[4*m]-pts[4*k]; + vy=pts[4*m+1]-pts[4*k+1]; + vz=pts[4*m+2]-pts[4*k+2]; + + // Construct the vector product of this edge with + // the previous one + wx=uz*vy-uy*vz; + wy=ux*vz-uz*vx; + wz=uy*vx-ux*vy; + wmag=wx*wx+wy*wy+wz*wz; + + // Test to see if this vector product of the + // two edges is above the tolerance + if(wmag>tol) { + + // Construct the normal vector and print it + wmag=1/sqrt(wmag); + v.push_back(wx*wmag); + v.push_back(wy*wmag); + v.push_back(wz*wmag); + + // Mark all of the remaining edges of this + // face and exit + while(m!=i) { + l=cycle_up(ed[k][nu[k]+l],m); + k=m;m=ed[k][l];ed[k][l]=-1-m; + } + return; + } + } + v.push_back(0); + v.push_back(0); + v.push_back(0); + return; + } + l=cycle_up(ed[k][nu[k]+l],m); + k=m; + } while (k!=i); + v.push_back(0); + v.push_back(0); + v.push_back(0); +} + +/** Returns the number of faces of a computed Voronoi cell. + * \return The number of faces. */ +int voronoicell_base::number_of_faces() { + int i,j,k,l,m,s=0; + for(i=1;i=0) { + s++; + ed[i][j]=-1-k; + l=cycle_up(ed[i][nu[i]+j],k); + do { + m=ed[k][l]; + ed[k][l]=-1-m; + l=cycle_up(ed[k][nu[k]+l],m); + k=m; + } while (k!=i); + + } + } + reset_edges(); + return s; +} + +/** Returns a vector of the vertex orders. + * \param[out] v the vector to store the results in. */ +void voronoicell_base::vertex_orders(std::vector &v) { + v.resize(p); + for(int i=0;i0) { + fprintf(fp,"%d",*nu); + for(int *nup=nu+1;nup &v) { + v.resize(3*p); + double *ptsp=pts; + for(int i=0;i<3*p;i+=3) { + v[i]=*(ptsp++)*0.5; + v[i+1]=*(ptsp++)*0.5; + v[i+2]=*ptsp*0.5;ptsp+=2; + } +} + +/** Outputs the vertex vectors using the local coordinate system. + * \param[out] fp the file handle to write to. */ +void voronoicell_base::output_vertices(FILE *fp) { + if(p>0) { + fprintf(fp,"(%g,%g,%g)",*pts*0.5,pts[1]*0.5,pts[2]*0.5); + for(double *ptsp=pts+4;ptsp &v) { + v.resize(3*p); + double *ptsp=pts; + for(int i=0;i<3*p;i+=3) { + v[i]=x+*(ptsp++)*0.5; + v[i+1]=y+*(ptsp++)*0.5; + v[i+2]=z+*ptsp*0.5;ptsp+=2; + } +} + +/** Outputs the vertex vectors using the global coordinate system. + * \param[out] fp the file handle to write to. + * \param[in] (x,y,z) the position vector of the particle in the global + * coordinate system. */ +void voronoicell_base::output_vertices(double x,double y,double z,FILE *fp) { + if(p>0) { + fprintf(fp,"(%g,%g,%g)",x+*pts*0.5,y+pts[1]*0.5,z+pts[2]*0.5); + for(double *ptsp=pts+4;ptsp &v) { + v.clear(); + int i,j,k,l,m; + double dx,dy,dz,perim; + for(i=1;i=0) { + dx=pts[k<<2]-pts[i<<2]; + dy=pts[(k<<2)+1]-pts[(i<<2)+1]; + dz=pts[(k<<2)+2]-pts[(i<<2)+2]; + perim=sqrt(dx*dx+dy*dy+dz*dz); + ed[i][j]=-1-k; + l=cycle_up(ed[i][nu[i]+j],k); + do { + m=ed[k][l]; + dx=pts[m<<2]-pts[k<<2]; + dy=pts[(m<<2)+1]-pts[(k<<2)+1]; + dz=pts[(m<<2)+2]-pts[(k<<2)+2]; + perim+=sqrt(dx*dx+dy*dy+dz*dz); + ed[k][l]=-1-m; + l=cycle_up(ed[k][nu[k]+l],m); + k=m; + } while (k!=i); + v.push_back(0.5*perim); + } + } + reset_edges(); +} + +/** For each face, this routine outputs a bracketed sequence of numbers + * containing a list of all the vertices that make up that face. + * \param[out] v the vector to store the results in. */ +void voronoicell_base::face_vertices(std::vector &v) { + int i,j,k,l,m,vp(0),vn; + v.clear(); + for(i=1;i=0) { + v.push_back(0); + v.push_back(i); + ed[i][j]=-1-k; + l=cycle_up(ed[i][nu[i]+j],k); + do { + v.push_back(k); + m=ed[k][l]; + ed[k][l]=-1-m; + l=cycle_up(ed[k][nu[k]+l],m); + k=m; + } while (k!=i); + vn=v.size(); + v[vp]=vn-vp-1; + vp=vn; + } + } + reset_edges(); +} + +/** Outputs a list of the number of edges in each face. + * \param[out] v the vector to store the results in. */ +void voronoicell_base::face_orders(std::vector &v) { + int i,j,k,l,m,q; + v.clear(); + for(i=1;i=0) { + q=1; + ed[i][j]=-1-k; + l=cycle_up(ed[i][nu[i]+j],k); + do { + q++; + m=ed[k][l]; + ed[k][l]=-1-m; + l=cycle_up(ed[k][nu[k]+l],m); + k=m; + } while (k!=i); + v.push_back(q);; + } + } + reset_edges(); +} + +/** Computes the number of edges that each face has and outputs a frequency + * table of the results. + * \param[out] v the vector to store the results in. */ +void voronoicell_base::face_freq_table(std::vector &v) { + int i,j,k,l,m,q; + v.clear(); + for(i=1;i=0) { + q=1; + ed[i][j]=-1-k; + l=cycle_up(ed[i][nu[i]+j],k); + do { + q++; + m=ed[k][l]; + ed[k][l]=-1-m; + l=cycle_up(ed[k][nu[k]+l],m); + k=m; + } while (k!=i); + if((unsigned int) q>=v.size()) v.resize(q+1,0); + v[q]++; + } + } + reset_edges(); +} + +/** This routine tests to see whether the cell intersects a plane by starting + * from the guess point up. If up intersects, then it immediately returns true. + * Otherwise, it calls the plane_intersects_track() routine. + * \param[in] (x,y,z) the normal vector to the plane. + * \param[in] rsq the distance along this vector of the plane. + * \return False if the plane does not intersect the plane, true if it does. */ +bool voronoicell_base::plane_intersects(double x,double y,double z,double rsq) { + double g=x*pts[up<<2]+y*pts[(up<<2)+1]+z*pts[(up<<2)+2]; + if(g>3,mp=1; + double m; + while(cag) { + if(m>rsq) return true; + g=m;up=mp; + } + ca+=mp++; + } + return plane_intersects_track(x,y,z,rsq,g); + } + return true; +} + +/* This routine tests to see if a cell intersects a plane, by tracing over the + * cell from vertex to vertex, starting at up. It is meant to be called either + * by plane_intersects() or plane_intersects_track(), when those routines + * cannot immediately resolve the case. + * \param[in] (x,y,z) the normal vector to the plane. + * \param[in] rsq the distance along this vector of the plane. + * \param[in] g the distance of up from the plane. + * \return False if the plane does not intersect the plane, true if it does. */ +inline bool voronoicell_base::plane_intersects_track(double x,double y,double z,double rsq,double g) { + + for(int tp=0;tprsq) return true; + return false; +/* + int ls,us,lp; + double l,u; + unsigned int uw; + + // Initialize the safe testing routine + px=x;py=y;pz=z;prsq=rsq; + maskc+=4; + if(maskc<4) reset_mask(); + + return search_upward(uw,lp,ls,us,l,u); +}*/ + /* + int count=0,ls,us,tp; + double t; + // The test point is outside of the cutting space + for(us=0;usg) { + ls=ed[up][nu[up]+us]; + up=tp; + while (t=p) { +#if VOROPP_VERBOSE >=1 + fputs("Bailed out of convex calculation",stderr); +#endif + for(tp=0;tprsq) return true; + return false; + } + + // Test all the neighbors of the current point + // and find the one which is closest to the + // plane + for(us=0;ust) break; + } + if(us==ls) { + us++; + while(ust) break; + us++; + } + if(us==nu[up]) return false; + } + ls=ed[up][nu[up]+us];up=tp;t=g; + } + return true; + } + } + return false;*/ +} + +/** Counts the number of edges of the Voronoi cell. + * \return the number of edges. */ +int voronoicell_base::number_of_edges() { + int edges=0,*nup=nu; + while(nup>1; +} + +/** Outputs a custom string of information about the Voronoi cell. The string + * of information follows a similar style as the C printf command, and detailed + * information about its format is available at + * http://math.lbl.gov/voro++/doc/custom.html. + * \param[in] format the custom string to print. + * \param[in] i the ID of the particle associated with this Voronoi cell. + * \param[in] (x,y,z) the position of the particle associated with this Voronoi + * cell. + * \param[in] r a radius associated with the particle. + * \param[in] fp the file handle to write to. */ +void voronoicell_base::output_custom(const char *format,int i,double x,double y,double z,double r,FILE *fp) { + char *fmp=(const_cast(format)); + std::vector vi; + std::vector vd; + while(*fmp!=0) { + if(*fmp=='%') { + fmp++; + switch(*fmp) { + + // Particle-related output + case 'i': fprintf(fp,"%d",i);break; + case 'x': fprintf(fp,"%g",x);break; + case 'y': fprintf(fp,"%g",y);break; + case 'z': fprintf(fp,"%g",z);break; + case 'q': fprintf(fp,"%g %g %g",x,y,z);break; + case 'r': fprintf(fp,"%g",r);break; + + // Vertex-related output + case 'w': fprintf(fp,"%d",p);break; + case 'p': output_vertices(fp);break; + case 'P': output_vertices(x,y,z,fp);break; + case 'o': output_vertex_orders(fp);break; + case 'm': fprintf(fp,"%g",0.25*max_radius_squared());break; + + // Edge-related output + case 'g': fprintf(fp,"%d",number_of_edges());break; + case 'E': fprintf(fp,"%g",total_edge_distance());break; + case 'e': face_perimeters(vd);voro_print_vector(vd,fp);break; + + // Face-related output + case 's': fprintf(fp,"%d",number_of_faces());break; + case 'F': fprintf(fp,"%g",surface_area());break; + case 'A': { + face_freq_table(vi); + voro_print_vector(vi,fp); + } break; + case 'a': face_orders(vi);voro_print_vector(vi,fp);break; + case 'f': face_areas(vd);voro_print_vector(vd,fp);break; + case 't': { + face_vertices(vi); + voro_print_face_vertices(vi,fp); + } break; + case 'l': normals(vd); + voro_print_positions(vd,fp); + break; + case 'n': neighbors(vi); + voro_print_vector(vi,fp); + break; + + // Volume-related output + case 'v': fprintf(fp,"%g",volume());break; + case 'c': { + double cx,cy,cz; + centroid(cx,cy,cz); + fprintf(fp,"%g %g %g",cx,cy,cz); + } break; + case 'C': { + double cx,cy,cz; + centroid(cx,cy,cz); + fprintf(fp,"%g %g %g",x+cx,y+cy,z+cz); + } break; + + // End-of-string reached + case 0: fmp--;break; + + // The percent sign is not part of a + // control sequence + default: putc('%',fp);putc(*fmp,fp); + } + } else putc(*fmp,fp); + fmp++; + } + fputs("\n",fp); +} + +/** This initializes the class to be a rectangular box. It calls the base class + * initialization routine to set up the edge and vertex information, and then + * sets up the neighbor information, with initial faces being assigned ID + * numbers from -1 to -6. + * \param[in] (xmin,xmax) the minimum and maximum x coordinates. + * \param[in] (ymin,ymax) the minimum and maximum y coordinates. + * \param[in] (zmin,zmax) the minimum and maximum z coordinates. */ +void voronoicell_neighbor::init(double xmin,double xmax,double ymin,double ymax,double zmin,double zmax) { + init_base(xmin,xmax,ymin,ymax,zmin,zmax); + int *q=mne[3]; + *q=-5;q[1]=-3;q[2]=-1; + q[3]=-5;q[4]=-2;q[5]=-3; + q[6]=-5;q[7]=-1;q[8]=-4; + q[9]=-5;q[10]=-4;q[11]=-2; + q[12]=-6;q[13]=-1;q[14]=-3; + q[15]=-6;q[16]=-3;q[17]=-2; + q[18]=-6;q[19]=-4;q[20]=-1; + q[21]=-6;q[22]=-2;q[23]=-4; + *ne=q;ne[1]=q+3;ne[2]=q+6;ne[3]=q+9; + ne[4]=q+12;ne[5]=q+15;ne[6]=q+18;ne[7]=q+21; +} + +/** This initializes the class to be an octahedron. It calls the base class + * initialization routine to set up the edge and vertex information, and then + * sets up the neighbor information, with the initial faces being assigned ID + * numbers from -1 to -8. + * \param[in] l The distance from the octahedron center to a vertex. Six + * vertices are initialized at (-l,0,0), (l,0,0), (0,-l,0), + * (0,l,0), (0,0,-l), and (0,0,l). */ +void voronoicell_neighbor::init_octahedron(double l) { + init_octahedron_base(l); + int *q=mne[4]; + *q=-5;q[1]=-6;q[2]=-7;q[3]=-8; + q[4]=-1;q[5]=-2;q[6]=-3;q[7]=-4; + q[8]=-6;q[9]=-5;q[10]=-2;q[11]=-1; + q[12]=-8;q[13]=-7;q[14]=-4;q[15]=-3; + q[16]=-5;q[17]=-8;q[18]=-3;q[19]=-2; + q[20]=-7;q[21]=-6;q[22]=-1;q[23]=-4; + *ne=q;ne[1]=q+4;ne[2]=q+8;ne[3]=q+12;ne[4]=q+16;ne[5]=q+20; +} + +/** This initializes the class to be a tetrahedron. It calls the base class + * initialization routine to set up the edge and vertex information, and then + * sets up the neighbor information, with the initial faces being assigned ID + * numbers from -1 to -4. + * \param (x0,y0,z0) a position vector for the first vertex. + * \param (x1,y1,z1) a position vector for the second vertex. + * \param (x2,y2,z2) a position vector for the third vertex. + * \param (x3,y3,z3) a position vector for the fourth vertex. */ +void voronoicell_neighbor::init_tetrahedron(double x0,double y0,double z0,double x1,double y1,double z1,double x2,double y2,double z2,double x3,double y3,double z3) { + init_tetrahedron_base(x0,y0,z0,x1,y1,z1,x2,y2,z2,x3,y3,z3); + int *q=mne[3]; + *q=-4;q[1]=-3;q[2]=-2; + q[3]=-3;q[4]=-4;q[5]=-1; + q[6]=-4;q[7]=-2;q[8]=-1; + q[9]=-2;q[10]=-3;q[11]=-1; + *ne=q;ne[1]=q+3;ne[2]=q+6;ne[3]=q+9; +} + +/** This routine checks to make sure the neighbor information of each face is + * consistent. */ +void voronoicell_neighbor::check_facets() { + int i,j,k,l,m,q; + for(i=1;i=0) { + ed[i][j]=-1-k; + q=ne[i][j]; + l=cycle_up(ed[i][nu[i]+j],k); + do { + m=ed[k][l]; + ed[k][l]=-1-m; + if(ne[k][l]!=q) fprintf(stderr,"Facet error at (%d,%d)=%d, started from (%d,%d)=%d\n",k,l,ne[k][l],i,j,q); + l=cycle_up(ed[k][nu[k]+l],m); + k=m; + } while (k!=i); + } + } + reset_edges(); +} + +/** The class constructor allocates memory for storing neighbor information. */ +void voronoicell_neighbor::memory_setup() { + int i; + mne=new int*[current_vertex_order]; + ne=new int*[current_vertices]; + for(i=0;i<3;i++) mne[i]=new int[init_n_vertices*i]; + mne[3]=new int[init_3_vertices*3]; + for(i=4;i=0;i--) if(mem[i]>0) delete [] mne[i]; + delete [] mne; + delete [] ne; +} + +/** Computes a vector list of neighbors. */ +void voronoicell_neighbor::neighbors(std::vector &v) { + v.clear(); + int i,j,k,l,m; + for(i=1;i=0) { + v.push_back(ne[i][j]); + ed[i][j]=-1-k; + l=cycle_up(ed[i][nu[i]+j],k); + do { + m=ed[k][l]; + ed[k][l]=-1-m; + l=cycle_up(ed[k][nu[k]+l],m); + k=m; + } while (k!=i); + } + } + reset_edges(); +} + +/** Prints the vertices, their edges, the relation table, and also notifies if + * any memory errors are visible. */ +void voronoicell_base::print_edges() { + int j; + double *ptsp=pts; + for(int i=0;i=mep[nu[i]]+mec[nu[i]]*((nu[i]<<1)+1)) puts(" Memory error"); + else puts(""); + } +} + +/** This prints out the neighbor information for vertex i. */ +void voronoicell_neighbor::print_edges_neighbors(int i) { + if(nu[i]>0) { + int j=0; + printf(" ("); + while(j + +#include "config.hh" +#include "common.hh" + +namespace voro { + +/** \brief A class representing a single Voronoi cell. + * + * This class represents a single Voronoi cell, as a collection of vertices + * that are connected by edges. The class contains routines for initializing + * the Voronoi cell to be simple shapes such as a box, tetrahedron, or octahedron. + * It the contains routines for recomputing the cell based on cutting it + * by a plane, which forms the key routine for the Voronoi cell computation. + * It contains numerous routine for computing statistics about the Voronoi cell, + * and it can output the cell in several formats. + * + * This class is not intended for direct use, but forms the base of the + * voronoicell and voronoicell_neighbor classes, which extend it based on + * whether neighboring particle ID information needs to be tracked. */ +class voronoicell_base { + public: + /** This holds the current size of the arrays ed and nu, which + * hold the vertex information. If more vertices are created + * than can fit in this array, then it is dynamically extended + * using the add_memory_vertices routine. */ + int current_vertices; + /** This holds the current maximum allowed order of a vertex, + * which sets the size of the mem, mep, and mec arrays. If a + * vertex is created with more vertices than this, the arrays + * are dynamically extended using the add_memory_vorder routine. + */ + int current_vertex_order; + /** This sets the size of the main delete stack. */ + int current_delete_size; + /** This sets the size of the auxiliary delete stack. */ + int current_delete2_size; + /** This sets the size of the extra search stack. */ + int current_xsearch_size; + /** This sets the total number of vertices in the current cell. + */ + int p; + /** This is the index of particular point in the cell, which is + * used to start the tracing routines for plane intersection + * and cutting. These routines will work starting from any + * point, but it's often most efficient to start from the last + * point considered, since in many cases, the cell construction + * algorithm may consider many planes with similar vectors + * concurrently. */ + int up; + /** This is a two dimensional array that holds information + * about the edge connections of the vertices that make up the + * cell. The two dimensional array is not allocated in the + * usual method. To account for the fact the different vertices + * have different orders, and thus require different amounts of + * storage, the elements of ed[i] point to one-dimensional + * arrays in the mep[] array of different sizes. + * + * More specifically, if vertex i has order m, then ed[i] + * points to a one-dimensional array in mep[m] that has 2*m+1 + * entries. The first m elements hold the neighboring edges, so + * that the jth edge of vertex i is held in ed[i][j]. The next + * m elements hold a table of relations which is redundant but + * helps speed up the computation. It satisfies the relation + * ed[ed[i][j]][ed[i][m+j]]=i. The final entry holds a back + * pointer, so that ed[i+2*m]=i. The back pointers are used + * when rearranging the memory. */ + int **ed; + /** This array holds the order of the vertices in the Voronoi + * cell. This array is dynamically allocated, with its current + * size held by current_vertices. */ + int *nu; + unsigned int *mask; + /** This in an array with size 3*current_vertices for holding + * the positions of the vertices. */ + double *pts; + double tol; + double tol_cu; + double big_tol; + voronoicell_base(double max_len_sq); + ~voronoicell_base(); + void init_base(double xmin,double xmax,double ymin,double ymax,double zmin,double zmax); + void init_octahedron_base(double l); + void init_tetrahedron_base(double x0,double y0,double z0,double x1,double y1,double z1,double x2,double y2,double z2,double x3,double y3,double z3); + void translate(double x,double y,double z); + void draw_pov(double x,double y,double z,FILE *fp=stdout); + /** Outputs the cell in POV-Ray format, using cylinders for edges + * and spheres for vertices, to a given file. + * \param[in] (x,y,z) a displacement to add to the cell's + * position. + * \param[in] filename the name of the file to write to. */ + inline void draw_pov(double x,double y,double z,const char *filename) { + FILE *fp=safe_fopen(filename,"w"); + draw_pov(x,y,z,fp); + fclose(fp); + }; + void draw_pov_mesh(double x,double y,double z,FILE *fp=stdout); + /** Outputs the cell in POV-Ray format as a mesh2 object to a + * given file. + * \param[in] (x,y,z) a displacement to add to the cell's + * position. + * \param[in] filename the name of the file to write to. */ + inline void draw_pov_mesh(double x,double y,double z,const char *filename) { + FILE *fp=safe_fopen(filename,"w"); + draw_pov_mesh(x,y,z,fp); + fclose(fp); + } + void draw_gnuplot(double x,double y,double z,FILE *fp=stdout); + /** Outputs the cell in Gnuplot format a given file. + * \param[in] (x,y,z) a displacement to add to the cell's + * position. + * \param[in] filename the name of the file to write to. */ + inline void draw_gnuplot(double x,double y,double z,const char *filename) { + FILE *fp=safe_fopen(filename,"w"); + draw_gnuplot(x,y,z,fp); + fclose(fp); + } + double volume(); + double max_radius_squared(); + double total_edge_distance(); + double surface_area(); + void centroid(double &cx,double &cy,double &cz); + int number_of_faces(); + int number_of_edges(); + void vertex_orders(std::vector &v); + void output_vertex_orders(FILE *fp=stdout); + void vertices(std::vector &v); + void output_vertices(FILE *fp=stdout); + void vertices(double x,double y,double z,std::vector &v); + void output_vertices(double x,double y,double z,FILE *fp=stdout); + void solid_angles(std::vector &v); + void face_areas(std::vector &v); + void minkowski(double r,double &ar,double &vo); + /** Outputs the solid angles of the faces. + * \param[in] fp the file handle to write to. */ + inline void output_solid_angles(FILE *fp=stdout) { + std::vector v;solid_angles(v); + voro_print_vector(v,fp); + } + /** Outputs the areas of the faces. + * \param[in] fp the file handle to write to. */ + inline void output_face_areas(FILE *fp=stdout) { + std::vector v;face_areas(v); + voro_print_vector(v,fp); + } + void face_orders(std::vector &v); + /** Outputs a list of the number of sides of each face. + * \param[in] fp the file handle to write to. */ + inline void output_face_orders(FILE *fp=stdout) { + std::vector v;face_orders(v); + voro_print_vector(v,fp); + } + void face_freq_table(std::vector &v); + /** Outputs a */ + inline void output_face_freq_table(FILE *fp=stdout) { + std::vector v;face_freq_table(v); + voro_print_vector(v,fp); + } + void face_vertices(std::vector &v); + /** Outputs the */ + inline void output_face_vertices(FILE *fp=stdout) { + std::vector v;face_vertices(v); + voro_print_face_vertices(v,fp); + } + void face_perimeters(std::vector &v); + /** Outputs a list of the perimeters of each face. + * \param[in] fp the file handle to write to. */ + inline void output_face_perimeters(FILE *fp=stdout) { + std::vector v;face_perimeters(v); + voro_print_vector(v,fp); + } + void normals(std::vector &v); + /** Outputs a list of the perimeters of each face. + * \param[in] fp the file handle to write to. */ + inline void output_normals(FILE *fp=stdout) { + std::vector v;normals(v); + voro_print_positions(v,fp); + } + /** Outputs a custom string of information about the Voronoi + * cell to a file. It assumes the cell is at (0,0,0) and has a + * the default_radius associated with it. + * \param[in] format the custom format string to use. + * \param[in] fp the file handle to write to. */ + inline void output_custom(const char *format,FILE *fp=stdout) {output_custom(format,0,0,0,0,default_radius,fp);} + void output_custom(const char *format,int i,double x,double y,double z,double r,FILE *fp=stdout); + template + bool nplane(vc_class &vc,double x,double y,double z,double rsq,int p_id); + bool plane_intersects(double x,double y,double z,double rsq); + bool plane_intersects_guess(double x,double y,double z,double rsq); + void construct_relations(); + void check_relations(); + void check_duplicates(); + void print_edges(); + /** Returns a list of IDs of neighboring particles + * corresponding to each face. + * \param[out] v a reference to a vector in which to return the + * results. If no neighbor information is + * available, a blank vector is returned. */ + virtual void neighbors(std::vector &v) {v.clear();} + /** This is a virtual function that is overridden by a routine + * to print a list of IDs of neighboring particles + * corresponding to each face. By default, when no neighbor + * information is available, the routine does nothing. + * \param[in] fp the file handle to write to. */ + virtual void output_neighbors(FILE *fp=stdout) {} + /** This a virtual function that is overridden by a routine to + * print the neighboring particle IDs for a given vertex. By + * default, when no neighbor information is available, the + * routine does nothing. + * \param[in] i the vertex to consider. */ + virtual void print_edges_neighbors(int i) {}; + /** This is a simple inline function for picking out the index + * of the next edge counterclockwise at the current vertex. + * \param[in] a the index of an edge of the current vertex. + * \param[in] p the number of the vertex. + * \return 0 if a=nu[p]-1, or a+1 otherwise. */ + inline int cycle_up(int a,int p) {return a==nu[p]-1?0:a+1;} + /** This is a simple inline function for picking out the index + * of the next edge clockwise from the current vertex. + * \param[in] a the index of an edge of the current vertex. + * \param[in] p the number of the vertex. + * \return nu[p]-1 if a=0, or a-1 otherwise. */ + inline int cycle_down(int a,int p) {return a==0?nu[p]-1:a-1;} + protected: + /** This a one dimensional array that holds the current sizes + * of the memory allocations for them mep array.*/ + int *mem; + /** This is a one dimensional array that holds the current + * number of vertices of order p that are stored in the mep[p] + * array. */ + int *mec; + /** This is a two dimensional array for holding the information + * about the edges of the Voronoi cell. mep[p] is a + * one-dimensional array for holding the edge information about + * all vertices of order p, with each vertex holding 2*p+1 + * integers of information. The total number of vertices held + * on mep[p] is stored in mem[p]. If the space runs out, the + * code allocates more using the add_memory() routine. */ + int **mep; + inline void reset_edges(); + template + void check_memory_for_copy(vc_class &vc,voronoicell_base* vb); + void copy(voronoicell_base* vb); + private: + /** This is the delete stack, used to store the vertices which + * are going to be deleted during the plane cutting procedure. + */ + int *ds,*stackp,*stacke; + /** This is the auxiliary delete stack, which has size set by + * current_delete2_size. */ + int *ds2,*stackp2,*stacke2; + /** This is the extra search stack. */ + int *xse,*stackp3,*stacke3; + unsigned int maskc; + /** The x coordinate of the normal vector to the test plane. */ + double px; + /** The y coordinate of the normal vector to the test plane. */ + double py; + /** The z coordinate of the normal vector to the test plane. */ + double pz; + /** The magnitude of the normal vector to the test plane. */ + double prsq; + template + void add_memory(vc_class &vc,int i); + template + void add_memory_vertices(vc_class &vc); + template + void add_memory_vorder(vc_class &vc); + void add_memory_ds(); + void add_memory_ds2(); + void add_memory_xse(); + bool failsafe_find(int &lp,int &ls,int &us,double &l,double &u); + template + bool create_facet(vc_class &vc,int lp,int ls,double l,int us,double u,int p_id); + template + bool collapse_order1(vc_class &vc); + template + inline bool collapse_order2(vc_class &vc); + template + bool delete_connection(vc_class &vc,int j,int k,bool hand); + inline bool search_for_outside_edge(int &up); + inline void add_to_stack(int sc2,int lp); + inline void reset_mask() { + for(int i=0;i &v,int i,int j,int k); + inline bool search_edge(int l,int &m,int &k); + inline unsigned int m_test(int n,double &ans); + inline unsigned int m_testx(int n,double &ans); + unsigned int m_calc(int n,double &ans); + inline void flip(int tp) {ed[tp][nu[tp]<<1]=-1-ed[tp][nu[tp]<<1];} + int check_marginal(int n,double &ans); + friend class voronoicell; + friend class voronoicell_neighbor; +}; + +/** \brief Extension of the voronoicell_base class to represent a Voronoi + * cell without neighbor information. + * + * This class is an extension of the voronoicell_base class, in cases when + * is not necessary to track the IDs of neighboring particles associated + * with each face of the Voronoi cell. */ +class voronoicell : public voronoicell_base { + public: + using voronoicell_base::nplane; + voronoicell() : voronoicell_base(default_length*default_length) {} + voronoicell(double max_len_sq_) : voronoicell_base(max_len_sq_) {} + template + voronoicell(c_class &con) : voronoicell_base(con.max_len_sq) {} + /** Copies the information from another voronoicell class into + * this class, extending memory allocation if necessary. + * \param[in] c the class to copy. */ + inline void operator=(voronoicell &c) { + voronoicell_base* vb((voronoicell_base*) &c); + check_memory_for_copy(*this,vb);copy(vb); + } + /** Cuts a Voronoi cell using by the plane corresponding to the + * perpendicular bisector of a particle. + * \param[in] (x,y,z) the position of the particle. + * \param[in] rsq the modulus squared of the vector. + * \param[in] p_id the plane ID, ignored for this case where no + * neighbor tracking is enabled. + * \return False if the plane cut deleted the cell entirely, + * true otherwise. */ + inline bool nplane(double x,double y,double z,double rsq,int p_id) { + return nplane(*this,x,y,z,rsq,0); + } + /** Cuts a Voronoi cell using by the plane corresponding to the + * perpendicular bisector of a particle. + * \param[in] (x,y,z) the position of the particle. + * \param[in] p_id the plane ID, ignored for this case where no + * neighbor tracking is enabled. + * \return False if the plane cut deleted the cell entirely, + * true otherwise. */ + inline bool nplane(double x,double y,double z,int p_id) { + double rsq=x*x+y*y+z*z; + return nplane(*this,x,y,z,rsq,0); + } + /** Cuts a Voronoi cell using by the plane corresponding to the + * perpendicular bisector of a particle. + * \param[in] (x,y,z) the position of the particle. + * \param[in] rsq the modulus squared of the vector. + * \return False if the plane cut deleted the cell entirely, + * true otherwise. */ + inline bool plane(double x,double y,double z,double rsq) { + return nplane(*this,x,y,z,rsq,0); + } + /** Cuts a Voronoi cell using by the plane corresponding to the + * perpendicular bisector of a particle. + * \param[in] (x,y,z) the position of the particle. + * \return False if the plane cut deleted the cell entirely, + * true otherwise. */ + inline bool plane(double x,double y,double z) { + double rsq=x*x+y*y+z*z; + return nplane(*this,x,y,z,rsq,0); + } + /** Initializes the Voronoi cell to be rectangular box with the + * given dimensions. + * \param[in] (xmin,xmax) the minimum and maximum x coordinates. + * \param[in] (ymin,ymax) the minimum and maximum y coordinates. + * \param[in] (zmin,zmax) the minimum and maximum z coordinates. */ + inline void init(double xmin,double xmax,double ymin,double ymax,double zmin,double zmax) { + init_base(xmin,xmax,ymin,ymax,zmin,zmax); + } + /** Initializes the cell to be an octahedron with vertices at + * (l,0,0), (-l,0,0), (0,l,0), (0,-l,0), (0,0,l), and (0,0,-l). + * \param[in] l a parameter setting the size of the octahedron. + */ + inline void init_octahedron(double l) { + init_octahedron_base(l); + } + /** Initializes the cell to be a tetrahedron. + * \param[in] (x0,y0,z0) the coordinates of the first vertex. + * \param[in] (x1,y1,z1) the coordinates of the second vertex. + * \param[in] (x2,y2,z2) the coordinates of the third vertex. + * \param[in] (x3,y3,z3) the coordinates of the fourth vertex. + */ + inline void init_tetrahedron(double x0,double y0,double z0,double x1,double y1,double z1,double x2,double y2,double z2,double x3,double y3,double z3) { + init_tetrahedron_base(x0,y0,z0,x1,y1,z1,x2,y2,z2,x3,y3,z3); + } + void init_l_shape(); + private: + inline void n_allocate(int i,int m) {}; + inline void n_add_memory_vertices(int i) {}; + inline void n_add_memory_vorder(int i) {}; + inline void n_set_pointer(int p,int n) {}; + inline void n_copy(int a,int b,int c,int d) {}; + inline void n_set(int a,int b,int c) {}; + inline void n_set_aux1(int k) {}; + inline void n_copy_aux1(int a,int b) {}; + inline void n_copy_aux1_shift(int a,int b) {}; + inline void n_set_aux2_copy(int a,int b) {}; + inline void n_copy_pointer(int a,int b) {}; + inline void n_set_to_aux1(int j) {}; + inline void n_set_to_aux2(int j) {}; + inline void n_allocate_aux1(int i) {}; + inline void n_switch_to_aux1(int i) {}; + inline void n_copy_to_aux1(int i,int m) {}; + inline void n_set_to_aux1_offset(int k,int m) {}; + inline void n_neighbors(std::vector &v) {v.clear();}; + friend class voronoicell_base; +}; + +/** \brief Extension of the voronoicell_base class to represent a Voronoi cell + * with neighbor information. + * + * This class is an extension of the voronoicell_base class, in cases when the + * IDs of neighboring particles associated with each face of the Voronoi cell. + * It contains additional data structures mne and ne for storing this + * information. */ +class voronoicell_neighbor : public voronoicell_base { + public: + using voronoicell_base::nplane; + /** This two dimensional array holds the neighbor information + * associated with each vertex. mne[p] is a one dimensional + * array which holds all of the neighbor information for + * vertices of order p. */ + int **mne; + /** This is a two dimensional array that holds the neighbor + * information associated with each vertex. ne[i] points to a + * one-dimensional array in mne[nu[i]]. ne[i][j] holds the + * neighbor information associated with the jth edge of vertex + * i. It is set to the ID number of the plane that made the + * face that is clockwise from the jth edge. */ + int **ne; + voronoicell_neighbor() : voronoicell_base(default_length*default_length) { + memory_setup(); + } + voronoicell_neighbor(double max_len_sq_) : voronoicell_base(max_len_sq_) { + memory_setup(); + } + template + voronoicell_neighbor(c_class &con) : voronoicell_base(con.max_len_sq) { + memory_setup(); + } + ~voronoicell_neighbor(); + void operator=(voronoicell &c); + void operator=(voronoicell_neighbor &c); + /** Cuts the Voronoi cell by a particle whose center is at a + * separation of (x,y,z) from the cell center. The value of rsq + * should be initially set to \f$x^2+y^2+z^2\f$. + * \param[in] (x,y,z) the normal vector to the plane. + * \param[in] rsq the distance along this vector of the plane. + * \param[in] p_id the plane ID (for neighbor tracking only). + * \return False if the plane cut deleted the cell entirely, + * true otherwise. */ + inline bool nplane(double x,double y,double z,double rsq,int p_id) { + return nplane(*this,x,y,z,rsq,p_id); + } + /** This routine calculates the modulus squared of the vector + * before passing it to the main nplane() routine with full + * arguments. + * \param[in] (x,y,z) the vector to cut the cell by. + * \param[in] p_id the plane ID (for neighbor tracking only). + * \return False if the plane cut deleted the cell entirely, + * true otherwise. */ + inline bool nplane(double x,double y,double z,int p_id) { + double rsq=x*x+y*y+z*z; + return nplane(*this,x,y,z,rsq,p_id); + } + /** This version of the plane routine just makes up the plane + * ID to be zero. It will only be referenced if neighbor + * tracking is enabled. + * \param[in] (x,y,z) the vector to cut the cell by. + * \param[in] rsq the modulus squared of the vector. + * \return False if the plane cut deleted the cell entirely, + * true otherwise. */ + inline bool plane(double x,double y,double z,double rsq) { + return nplane(*this,x,y,z,rsq,0); + } + /** Cuts a Voronoi cell using the influence of a particle at + * (x,y,z), first calculating the modulus squared of this + * vector before passing it to the main nplane() routine. Zero + * is supplied as the plane ID, which will be ignored unless + * neighbor tracking is enabled. + * \param[in] (x,y,z) the vector to cut the cell by. + * \return False if the plane cut deleted the cell entirely, + * true otherwise. */ + inline bool plane(double x,double y,double z) { + double rsq=x*x+y*y+z*z; + return nplane(*this,x,y,z,rsq,0); + } + void init(double xmin,double xmax,double ymin,double ymax,double zmin,double zmax); + void init_octahedron(double l); + void init_tetrahedron(double x0,double y0,double z0,double x1,double y1,double z1,double x2,double y2,double z2,double x3,double y3,double z3); + void check_facets(); + virtual void neighbors(std::vector &v); + virtual void print_edges_neighbors(int i); + virtual void output_neighbors(FILE *fp=stdout) { + std::vector v;neighbors(v); + voro_print_vector(v,fp); + } + private: + int *paux1; + int *paux2; + void memory_setup(); + inline void n_allocate(int i,int m) {mne[i]=new int[m*i];} + inline void n_add_memory_vertices(int i) { + int **pp=new int*[i]; + for(int j=0;j &v,FILE *fp) { + if(v.size()>0) { + fprintf(fp,"(%g,%g,%g)",v[0],v[1],v[2]); + for(int k=3;(unsigned int) k &v,FILE *fp) { + int k=0,s=v.size(); + while(k+4 &v,FILE *fp) { + int k=0,s=v.size(); + while(k+4 &v,FILE *fp) { + int j,k=0,l; + if(v.size()>0) { + l=v[k++]; + if(l<=1) { + if(l==1) fprintf(fp,"(%d)",v[k++]); + else fputs("()",fp); + } else { + j=k+l; + fprintf(fp,"(%d",v[k++]); + while(k +#include +#include + +#include "config.hh" + +namespace voro { + +void check_duplicate(int n,double x,double y,double z,int id,double *qp); + +void voro_fatal_error(const char *p,int status); +void voro_print_positions(std::vector &v,FILE *fp=stdout); +FILE* safe_fopen(const char *filename,const char *mode); +void voro_print_vector(std::vector &v,FILE *fp=stdout); +void voro_print_vector(std::vector &v,FILE *fp=stdout); +void voro_print_face_vertices(std::vector &v,FILE *fp=stdout); + +} + +#endif diff --git a/src/third_party/voro/src/config.hh b/src/third_party/voro/src/config.hh new file mode 100644 index 000000000..61d54d365 --- /dev/null +++ b/src/third_party/voro/src/config.hh @@ -0,0 +1,123 @@ +// Voro++, a 3D cell-based Voronoi library +// +// Author : Chris H. Rycroft (Harvard University / LBL) +// Email : chr@alum.mit.edu +// Date : August 30th 2011 + +/** \file config.hh + * \brief Master configuration file for setting various compile-time options. */ + +#ifndef VOROPP_CONFIG_HH +#define VOROPP_CONFIG_HH + +#include + +namespace voro { + +// These constants set the initial memory allocation for the Voronoi cell +/** The initial memory allocation for the number of vertices. */ +const int init_vertices=256; +/** The initial memory allocation for the maximum vertex order. */ +const int init_vertex_order=64; +/** The initial memory allocation for the number of regular vertices of order + * 3. */ +const int init_3_vertices=256; +/** The initial memory allocation for the number of vertices of higher order. + */ +const int init_n_vertices=8; +/** The initial size for the delete stack. */ +const int init_delete_size=256; +/** The initial size for the auxiliary delete stack. */ +const int init_delete2_size=256; +/** The initial size for the extra search stack. */ +const int init_xsearch_size=32; +/** The initial size for the wall pointer array. */ +const int init_wall_size=32; +/** The default initial size for the ordering class. */ +const int init_ordering_size=4096; +/** The initial size of the pre_container chunk index. */ +const int init_chunk_size=256; + +// If the initial memory is too small, the program dynamically allocates more. +// However, if the limits below are reached, then the program bails out. +/** The maximum memory allocation for the number of vertices. */ +const int max_vertices=16777216; +/** The maximum memory allocation for the maximum vertex order. */ +const int max_vertex_order=2048; +/** The maximum memory allocation for the any particular order of vertex. */ +const int max_n_vertices=16777216; +/** The maximum size for the delete stack. */ +const int max_delete_size=16777216; +/** The maximum size for the auxiliary delete stack. */ +const int max_delete2_size=16777216; +/** The maximum size for the extra search stack. */ +const int max_xsearch_size=16777216; +/** The maximum amount of particle memory allocated for a single region. */ +const int max_particle_memory=16777216; +/** The maximum size for the wall pointer array. */ +const int max_wall_size=2048; +/** The maximum size for the ordering class. */ +const int max_ordering_size=67108864; +/** The maximum size for the pre_container chunk index. */ +const int max_chunk_size=65536; + +/** The chunk size in the pre_container classes. */ +const int pre_container_chunk_size=1024; + +#ifndef VOROPP_VERBOSE +/** Voro++ can print a number of different status and debugging messages to + * notify the user of special behavior, and this macro sets the amount which + * are displayed. At level 0, no messages are printed. At level 1, messages + * about unusual cases during cell construction are printed, such as when the + * plane routine bails out due to floating point problems. At level 2, general + * messages about memory expansion are printed. At level 3, technical details + * about memory management are printed. */ +#define VOROPP_VERBOSE 2 +#endif + +/** If a point is within this distance of a cutting plane, then the code + * assumes that point exactly lies on the plane. */ +const double tolerance=10.*std::numeric_limits::epsilon(); + +const double big_tolerance_fac=20.; + +const double default_length=1000.; + +/** A large number that is used in the computation. */ +const double large_number=std::numeric_limits::max(); + +/** A radius to use as a placeholder when no other information is available. */ +const double default_radius=0.5; + +/** The maximum number of shells of periodic images to test over. */ +const int max_unit_voro_shells=10; + +/** A guess for the optimal number of particles per block, used to set up the + * container grid. */ +const double optimal_particles=5.6; + +/** If this is set to 1, then the code reports any instances of particles being + * put outside of the container geometry. */ +#define VOROPP_REPORT_OUT_OF_BOUNDS 0 + +/** Voro++ returns this status code if there is a file-related error, such as + * not being able to open file. */ +#define VOROPP_FILE_ERROR 1 + +/** Voro++ returns this status code if there is a memory allocation error, if + * one of the safe memory limits is exceeded. */ +#define VOROPP_MEMORY_ERROR 2 + +/** Voro++ returns this status code if there is any type of internal error, if + * it detects that representation of the Voronoi cell is inconsistent. This + * status code will generally indicate a bug, and the developer should be + * contacted. */ +#define VOROPP_INTERNAL_ERROR 3 + +/** Voro++ returns this status code if it could not interpret the command line + * arguments passed to the command line utility. */ +#define VOROPP_CMD_LINE_ERROR 4 + +} + +#endif diff --git a/src/third_party/voro/src/container.cc b/src/third_party/voro/src/container.cc new file mode 100644 index 000000000..1d21dbc89 --- /dev/null +++ b/src/third_party/voro/src/container.cc @@ -0,0 +1,551 @@ +// Voro++, a 3D cell-based Voronoi library +// +// Author : Chris H. Rycroft (Harvard University / LBL) +// Email : chr@alum.mit.edu +// Date : August 30th 2011 + +/** \file container.cc + * \brief Function implementations for the container and related classes. */ + +#include "container.hh" + +namespace voro { + +/** The class constructor sets up the geometry of container, initializing the + * minimum and maximum coordinates in each direction, and setting whether each + * direction is periodic or not. It divides the container into a rectangular + * grid of blocks, and allocates memory for each of these for storing particle + * positions and IDs. + * \param[in] (ax_,bx_) the minimum and maximum x coordinates. + * \param[in] (ay_,by_) the minimum and maximum y coordinates. + * \param[in] (az_,bz_) the minimum and maximum z coordinates. + * \param[in] (nx_,ny_,nz_) the number of grid blocks in each of the three + * coordinate directions. + * \param[in] (xperiodic_,yperiodic_,zperiodic_) flags setting whether the + * container is periodic in each + * coordinate direction. + * \param[in] init_mem the initial memory allocation for each block. + * \param[in] ps_ the number of floating point entries to store for each + * particle. */ +container_base::container_base(double ax_,double bx_,double ay_,double by_,double az_,double bz_, + int nx_,int ny_,int nz_,bool xperiodic_,bool yperiodic_,bool zperiodic_,int init_mem,int ps_) + : voro_base(nx_,ny_,nz_,(bx_-ax_)/nx_,(by_-ay_)/ny_,(bz_-az_)/nz_), + ax(ax_), bx(bx_), ay(ay_), by(by_), az(az_), bz(bz_), + max_len_sq((bx-ax)*(bx-ax)*(xperiodic_?0.25:1)+(by-ay)*(by-ay)*(yperiodic_?0.25:1) + +(bz-az)*(bz-az)*(zperiodic_?0.25:1)), + xperiodic(xperiodic_), yperiodic(yperiodic_), zperiodic(zperiodic_), + id(new int*[nxyz]), p(new double*[nxyz]), co(new int[nxyz]), mem(new int[nxyz]), ps(ps_) { + + int l; + for(l=0;l=nx) return false; + + int j=step_int((y-ay)*ysp); + if(yperiodic) {l=step_mod(j,ny);y+=boxy*(l-j);j=l;} + else if(j<0||j>=ny) return false; + + int k=step_int((z-az)*zsp); + if(zperiodic) {l=step_mod(k,nz);z+=boxz*(l-k);k=l;} + else if(k<0||k>=nz) return false; + + ijk+=nx*j+nxy*k; + return true; +} + +/** Takes a position vector and attempts to remap it into the primary domain. + * \param[out] (ai,aj,ak) the periodic image displacement that the vector is in, + * with (0,0,0) corresponding to the primary domain. + * \param[out] (ci,cj,ck) the index of the block that the position vector is + * within, once it has been remapped. + * \param[in,out] (x,y,z) the position vector to consider, which is remapped + * into the primary domain during the routine. + * \param[out] ijk the block index that the vector is within. + * \return True if the particle is within the container or can be remapped into + * it, false if it lies outside of the container bounds. */ +inline bool container_base::remap(int &ai,int &aj,int &ak,int &ci,int &cj,int &ck,double &x,double &y,double &z,int &ijk) { + ci=step_int((x-ax)*xsp); + if(ci<0||ci>=nx) { + if(xperiodic) {ai=step_div(ci,nx);x-=ai*(bx-ax);ci-=ai*nx;} + else return false; + } else ai=0; + + cj=step_int((y-ay)*ysp); + if(cj<0||cj>=ny) { + if(yperiodic) {aj=step_div(cj,ny);y-=aj*(by-ay);cj-=aj*ny;} + else return false; + } else aj=0; + + ck=step_int((z-az)*zsp); + if(ck<0||ck>=nz) { + if(zperiodic) {ak=step_div(ck,nz);z-=ak*(bz-az);ck-=ak*nz;} + else return false; + } else ak=0; + + ijk=ci+nx*cj+nxy*ck; + return true; +} + +/** Takes a vector and finds the particle whose Voronoi cell contains that + * vector. This is equivalent to finding the particle which is nearest to the + * vector. Additional wall classes are not considered by this routine. + * \param[in] (x,y,z) the vector to test. + * \param[out] (rx,ry,rz) the position of the particle whose Voronoi cell + * contains the vector. If the container is periodic, + * this may point to a particle in a periodic image of + * the primary domain. + * \param[out] pid the ID of the particle. + * \return True if a particle was found. If the container has no particles, + * then the search will not find a Voronoi cell and false is returned. */ +bool container::find_voronoi_cell(double x,double y,double z,double &rx,double &ry,double &rz,int &pid) { + int ai,aj,ak,ci,cj,ck,ijk; + particle_record w; + double mrs; + + // If the given vector lies outside the domain, but the container + // is periodic, then remap it back into the domain + if(!remap(ai,aj,ak,ci,cj,ck,x,y,z,ijk)) return false; + vc.find_voronoi_cell(x,y,z,ci,cj,ck,ijk,w,mrs); + + if(w.ijk!=-1) { + + // Assemble the position vector of the particle to be returned, + // applying a periodic remapping if necessary + if(xperiodic) {ci+=w.di;if(ci<0||ci>=nx) ai+=step_div(ci,nx);} + if(yperiodic) {cj+=w.dj;if(cj<0||cj>=ny) aj+=step_div(cj,ny);} + if(zperiodic) {ck+=w.dk;if(ck<0||ck>=nz) ak+=step_div(ck,nz);} + rx=p[w.ijk][3*w.l]+ai*(bx-ax); + ry=p[w.ijk][3*w.l+1]+aj*(by-ay); + rz=p[w.ijk][3*w.l+2]+ak*(bz-az); + pid=id[w.ijk][w.l]; + return true; + } + + // If no particle if found then just return false + return false; +} + +/** Takes a vector and finds the particle whose Voronoi cell contains that + * vector. Additional wall classes are not considered by this routine. + * \param[in] (x,y,z) the vector to test. + * \param[out] (rx,ry,rz) the position of the particle whose Voronoi cell + * contains the vector. If the container is periodic, + * this may point to a particle in a periodic image of + * the primary domain. + * \param[out] pid the ID of the particle. + * \return True if a particle was found. If the container has no particles, + * then the search will not find a Voronoi cell and false is returned. */ +bool container_poly::find_voronoi_cell(double x,double y,double z,double &rx,double &ry,double &rz,int &pid) { + int ai,aj,ak,ci,cj,ck,ijk; + particle_record w; + double mrs; + + // If the given vector lies outside the domain, but the container + // is periodic, then remap it back into the domain + if(!remap(ai,aj,ak,ci,cj,ck,x,y,z,ijk)) return false; + vc.find_voronoi_cell(x,y,z,ci,cj,ck,ijk,w,mrs); + + if(w.ijk!=-1) { + + // Assemble the position vector of the particle to be returned, + // applying a periodic remapping if necessary + if(xperiodic) {ci+=w.di;if(ci<0||ci>=nx) ai+=step_div(ci,nx);} + if(yperiodic) {cj+=w.dj;if(cj<0||cj>=ny) aj+=step_div(cj,ny);} + if(zperiodic) {ck+=w.dk;if(ck<0||ck>=nz) ak+=step_div(ck,nz);} + rx=p[w.ijk][4*w.l]+ai*(bx-ax); + ry=p[w.ijk][4*w.l+1]+aj*(by-ay); + rz=p[w.ijk][4*w.l+2]+ak*(bz-az); + pid=id[w.ijk][w.l]; + return true; + } + + // If no particle if found then just return false + return false; +} + +/** Increase memory for a particular region. + * \param[in] i the index of the region to reallocate. */ +void container_base::add_particle_memory(int i) { + int l,nmem=mem[i]<<1; + + // Carry out a check on the memory allocation size, and + // print a status message if requested + if(nmem>max_particle_memory) + voro_fatal_error("Absolute maximum memory allocation exceeded",VOROPP_MEMORY_ERROR); +#if VOROPP_VERBOSE >=3 + fprintf(stderr,"Particle memory in region %d scaled up to %d\n",i,nmem); +#endif + + // Allocate new memory and copy in the contents of the old arrays + int *idp=new int[nmem]; + for(l=0;lbx||yby||zbz) return false; + return point_inside_walls(x,y,z); +} + +/** Draws an outline of the domain in gnuplot format. + * \param[in] fp the file handle to write to. */ +void container_base::draw_domain_gnuplot(FILE *fp) { + fprintf(fp,"%g %g %g\n%g %g %g\n%g %g %g\n%g %g %g\n",ax,ay,az,bx,ay,az,bx,by,az,ax,by,az); + fprintf(fp,"%g %g %g\n%g %g %g\n%g %g %g\n%g %g %g\n",ax,by,bz,bx,by,bz,bx,ay,bz,ax,ay,bz); + fprintf(fp,"%g %g %g\n\n%g %g %g\n%g %g %g\n\n",ax,by,bz,ax,ay,az,ax,ay,bz); + fprintf(fp,"%g %g %g\n%g %g %g\n\n%g %g %g\n%g %g %g\n\n",bx,ay,az,bx,ay,bz,bx,by,az,bx,by,bz); +} + +/** Draws an outline of the domain in POV-Ray format. + * \param[in] fp the file handle to write to. */ +void container_base::draw_domain_pov(FILE *fp) { + fprintf(fp,"cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n" + "cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n",ax,ay,az,bx,ay,az,ax,by,az,bx,by,az); + fprintf(fp,"cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n" + "cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n",ax,by,bz,bx,by,bz,ax,ay,bz,bx,ay,bz); + fprintf(fp,"cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n" + "cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n",ax,ay,az,ax,by,az,bx,ay,az,bx,by,az); + fprintf(fp,"cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n" + "cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n",bx,ay,bz,bx,by,bz,ax,ay,bz,ax,by,bz); + fprintf(fp,"cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n" + "cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n",ax,ay,az,ax,ay,bz,bx,ay,az,bx,ay,bz); + fprintf(fp,"cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n" + "cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n",bx,by,az,bx,by,bz,ax,by,az,ax,by,bz); + fprintf(fp,"sphere{<%g,%g,%g>,rr}\nsphere{<%g,%g,%g>,rr}\n" + "sphere{<%g,%g,%g>,rr}\nsphere{<%g,%g,%g>,rr}\n",ax,ay,az,bx,ay,az,ax,by,az,bx,by,az); + fprintf(fp,"sphere{<%g,%g,%g>,rr}\nsphere{<%g,%g,%g>,rr}\n" + "sphere{<%g,%g,%g>,rr}\nsphere{<%g,%g,%g>,rr}\n",ax,ay,bz,bx,ay,bz,ax,by,bz,bx,by,bz); +} + +/** The wall_list constructor sets up an array of pointers to wall classes. */ +wall_list::wall_list() : walls(new wall*[init_wall_size]), wep(walls), wel(walls+init_wall_size), + current_wall_size(init_wall_size) {} + +/** The wall_list destructor frees the array of pointers to the wall classes. + */ +wall_list::~wall_list() { + delete [] walls; +} + +/** Adds all of the walls on another wall_list to this class. + * \param[in] wl a reference to the wall class. */ +void wall_list::add_wall(wall_list &wl) { + for(wall **wp=wl.walls;wpmax_wall_size) + voro_fatal_error("Wall memory allocation exceeded absolute maximum",VOROPP_MEMORY_ERROR); + wall **nwalls=new wall*[current_wall_size],**nwp=nwalls,**wp=walls; + while(wp +#include + +#include "config.hh" +#include "common.hh" +#include "v_base.hh" +#include "cell.hh" +#include "c_loops.hh" +#include "v_compute.hh" +#include "rad_option.hh" + +namespace voro { + +/** \brief Pure virtual class from which wall objects are derived. + * + * This is a pure virtual class for a generic wall object. A wall object + * can be specified by deriving a new class from this and specifying the + * functions.*/ +class wall { + public: + virtual ~wall() {} + /** A pure virtual function for testing whether a point is + * inside the wall object. */ + virtual bool point_inside(double x,double y,double z) = 0; + /** A pure virtual function for cutting a cell without + * neighbor-tracking with a wall. */ + virtual bool cut_cell(voronoicell &c,double x,double y,double z) = 0; + /** A pure virtual function for cutting a cell with + * neighbor-tracking enabled with a wall. */ + virtual bool cut_cell(voronoicell_neighbor &c,double x,double y,double z) = 0; +}; + +/** \brief A class for storing a list of pointers to walls. + * + * This class stores a list of pointers to wall classes. It contains several + * simple routines that make use of the wall classes (such as telling whether a + * given position is inside all of the walls or not). It can be used by itself, + * but also forms part of container_base, for associating walls with this + * class. */ +class wall_list { + public: + /** An array holding pointers to wall objects. */ + wall **walls; + /** A pointer to the next free position to add a wall pointer. + */ + wall **wep; + wall_list(); + ~wall_list(); + /** Adds a wall to the list. + * \param[in] w the wall to add. */ + inline void add_wall(wall *w) { + if(wep==wel) increase_wall_memory(); + *(wep++)=w; + } + /** Adds a wall to the list. + * \param[in] w a reference to the wall to add. */ + inline void add_wall(wall &w) {add_wall(&w);} + void add_wall(wall_list &wl); + /** Determines whether a given position is inside all of the + * walls on the list. + * \param[in] (x,y,z) the position to test. + * \return True if it is inside, false if it is outside. */ + inline bool point_inside_walls(double x,double y,double z) { + for(wall **wp=walls;wppoint_inside(x,y,z))) return false; + return true; + } + /** Cuts a Voronoi cell by all of the walls currently on + * the list. + * \param[in] c a reference to the Voronoi cell class. + * \param[in] (x,y,z) the position of the cell. + * \return True if the cell still exists, false if the cell is + * deleted. */ + template + bool apply_walls(c_class &c,double x,double y,double z) { + for(wall **wp=walls;wpcut_cell(c,x,y,z))) return false; + return true; + } + void deallocate(); + protected: + void increase_wall_memory(); + /** A pointer to the limit of the walls array, used to + * determine when array is full. */ + wall **wel; + /** The current amount of memory allocated for walls. */ + int current_wall_size; +}; + +/** \brief Class for representing a particle system in a three-dimensional + * rectangular box. + * + * This class represents a system of particles in a three-dimensional + * rectangular box. Any combination of non-periodic and periodic coordinates + * can be used in the three coordinate directions. The class is not intended + * for direct use, but instead forms the base of the container and + * container_poly classes that add specialized routines for computing the + * regular and radical Voronoi tessellations respectively. It contains routines + * that are commonly between these two classes, such as those for drawing the + * domain, and placing particles within the internal data structure. + * + * The class is derived from the wall_list class, which encapsulates routines + * for associating walls with the container, and the voro_base class, which + * encapsulates routines about the underlying computational grid. */ +class container_base : public voro_base, public wall_list { + public: + /** The minimum x coordinate of the container. */ + const double ax; + /** The maximum x coordinate of the container. */ + const double bx; + /** The minimum y coordinate of the container. */ + const double ay; + /** The maximum y coordinate of the container. */ + const double by; + /** The minimum z coordinate of the container. */ + const double az; + /** The maximum z coordinate of the container. */ + const double bz; + /** The maximum length squared that could be encountered in the + * Voronoi cell calculation. */ + const double max_len_sq; + /** A boolean value that determines if the x coordinate in + * periodic or not. */ + const bool xperiodic; + /** A boolean value that determines if the y coordinate in + * periodic or not. */ + const bool yperiodic; + /** A boolean value that determines if the z coordinate in + * periodic or not. */ + const bool zperiodic; + /** This array holds the numerical IDs of each particle in each + * computational box. */ + int **id; + /** A two dimensional array holding particle positions. For the + * derived container_poly class, this also holds particle + * radii. */ + double **p; + /** This array holds the number of particles within each + * computational box of the container. */ + int *co; + /** This array holds the maximum amount of particle memory for + * each computational box of the container. If the number of + * particles in a particular box ever approaches this limit, + * more is allocated using the add_particle_memory() function. + */ + int *mem; + /** The amount of memory in the array structure for each + * particle. This is set to 3 when the basic class is + * initialized, so that the array holds (x,y,z) positions. If + * the container class is initialized as part of the derived + * class container_poly, then this is set to 4, to also hold + * the particle radii. */ + const int ps; + container_base(double ax_,double bx_,double ay_,double by_,double az_,double bz_, + int nx_,int ny_,int nz_,bool xperiodic_,bool yperiodic_,bool zperiodic_, + int init_mem,int ps_); + ~container_base(); + bool point_inside(double x,double y,double z); + void region_count(); + /** Initializes the Voronoi cell prior to a compute_cell + * operation for a specific particle being carried out by a + * voro_compute class. The cell is initialized to fill the + * entire container. For non-periodic coordinates, this is set + * by the position of the walls. For periodic coordinates, the + * space is equally divided in either direction from the + * particle's initial position. Plane cuts made by any walls + * that have been added are then applied to the cell. + * \param[in,out] c a reference to a voronoicell object. + * \param[in] ijk the block that the particle is within. + * \param[in] q the index of the particle within its block. + * \param[in] (ci,cj,ck) the coordinates of the block in the + * container coordinate system. + * \param[out] (i,j,k) the coordinates of the test block + * relative to the voro_compute + * coordinate system. + * \param[out] (x,y,z) the position of the particle. + * \param[out] disp a block displacement used internally by the + * compute_cell routine. + * \return False if the plane cuts applied by walls completely + * removed the cell, true otherwise. */ + template + inline bool initialize_voronoicell(v_cell &c,int ijk,int q,int ci,int cj,int ck, + int &i,int &j,int &k,double &x,double &y,double &z,int &disp) { + double x1,x2,y1,y2,z1,z2,*pp=p[ijk]+ps*q; + x=*(pp++);y=*(pp++);z=*pp; + if(xperiodic) {x1=-(x2=0.5*(bx-ax));i=nx;} else {x1=ax-x;x2=bx-x;i=ci;} + if(yperiodic) {y1=-(y2=0.5*(by-ay));j=ny;} else {y1=ay-y;y2=by-y;j=cj;} + if(zperiodic) {z1=-(z2=0.5*(bz-az));k=nz;} else {z1=az-z;z2=bz-z;k=ck;} + c.init(x1,x2,y1,y2,z1,z2); + if(!apply_walls(c,x,y,z)) return false; + disp=ijk-i-nx*(j+ny*k); + return true; + } + /** Initializes parameters for a find_voronoi_cell call within + * the voro_compute template. + * \param[in] (ci,cj,ck) the coordinates of the test block in + * the container coordinate system. + * \param[in] ijk the index of the test block + * \param[out] (i,j,k) the coordinates of the test block + * relative to the voro_compute + * coordinate system. + * \param[out] disp a block displacement used internally by the + * find_voronoi_cell routine. */ + inline void initialize_search(int ci,int cj,int ck,int ijk,int &i,int &j,int &k,int &disp) { + i=xperiodic?nx:ci; + j=yperiodic?ny:cj; + k=zperiodic?nz:ck; + disp=ijk-i-nx*(j+ny*k); + } + /** Returns the position of a particle currently being computed + * relative to the computational block that it is within. It is + * used to select the optimal worklist entry to use. + * \param[in] (x,y,z) the position of the particle. + * \param[in] (ci,cj,ck) the block that the particle is within. + * \param[out] (fx,fy,fz) the position relative to the block. + */ + inline void frac_pos(double x,double y,double z,double ci,double cj,double ck, + double &fx,double &fy,double &fz) { + fx=x-ax-boxx*ci; + fy=y-ay-boxy*cj; + fz=z-az-boxz*ck; + } + /** Calculates the index of block in the container structure + * corresponding to given coordinates. + * \param[in] (ci,cj,ck) the coordinates of the original block + * in the current computation, relative + * to the container coordinate system. + * \param[in] (ei,ej,ek) the displacement of the current block + * from the original block. + * \param[in,out] (qx,qy,qz) the periodic displacement that + * must be added to the particles + * within the computed block. + * \param[in] disp a block displacement used internally by the + * find_voronoi_cell and compute_cell routines. + * \return The block index. */ + inline int region_index(int ci,int cj,int ck,int ei,int ej,int ek,double &qx,double &qy,double &qz,int &disp) { + if(xperiodic) {if(ci+ei=(nx<<1)) {ei-=nx;qx=bx-ax;} else qx=0;} + if(yperiodic) {if(cj+ej=(ny<<1)) {ej-=ny;qy=by-ay;} else qy=0;} + if(zperiodic) {if(ck+ek=(nz<<1)) {ek-=nz;qz=bz-az;} else qz=0;} + return disp+ei+nx*(ej+ny*ek); + } + void draw_domain_gnuplot(FILE *fp=stdout); + /** Draws an outline of the domain in Gnuplot format. + * \param[in] filename the filename to write to. */ + inline void draw_domain_gnuplot(const char* filename) { + FILE *fp=safe_fopen(filename,"w"); + draw_domain_gnuplot(fp); + fclose(fp); + } + void draw_domain_pov(FILE *fp=stdout); + /** Draws an outline of the domain in Gnuplot format. + * \param[in] filename the filename to write to. */ + inline void draw_domain_pov(const char* filename) { + FILE *fp=safe_fopen(filename,"w"); + draw_domain_pov(fp); + fclose(fp); + } + /** Sums up the total number of stored particles. + * \return The number of particles. */ + inline int total_particles() { + int tp=*co; + for(int *cop=co+1;cop + void draw_particles(c_loop &vl,FILE *fp) { + double *pp; + if(vl.start()) do { + pp=p[vl.ijk]+3*vl.q; + fprintf(fp,"%d %g %g %g\n",id[vl.ijk][vl.q],*pp,pp[1],pp[2]); + } while(vl.inc()); + } + /** Dumps all of the particle IDs and positions to a file. + * \param[in] fp a file handle to write to. */ + inline void draw_particles(FILE *fp=stdout) { + c_loop_all vl(*this); + draw_particles(vl,fp); + } + /** Dumps all of the particle IDs and positions to a file. + * \param[in] filename the name of the file to write to. */ + inline void draw_particles(const char *filename) { + FILE *fp=safe_fopen(filename,"w"); + draw_particles(fp); + fclose(fp); + } + /** Dumps particle positions in POV-Ray format. + * \param[in] vl the loop class to use. + * \param[in] fp a file handle to write to. */ + template + void draw_particles_pov(c_loop &vl,FILE *fp) { + double *pp; + if(vl.start()) do { + pp=p[vl.ijk]+3*vl.q; + fprintf(fp,"// id %d\nsphere{<%g,%g,%g>,s}\n", + id[vl.ijk][vl.q],*pp,pp[1],pp[2]); + } while(vl.inc()); + } + /** Dumps all particle positions in POV-Ray format. + * \param[in] fp a file handle to write to. */ + inline void draw_particles_pov(FILE *fp=stdout) { + c_loop_all vl(*this); + draw_particles_pov(vl,fp); + } + /** Dumps all particle positions in POV-Ray format. + * \param[in] filename the name of the file to write to. */ + inline void draw_particles_pov(const char *filename) { + FILE *fp=safe_fopen(filename,"w"); + draw_particles_pov(fp); + fclose(fp); + } + /** Computes Voronoi cells and saves the output in gnuplot + * format. + * \param[in] vl the loop class to use. + * \param[in] fp a file handle to write to. */ + template + void draw_cells_gnuplot(c_loop &vl,FILE *fp) { + voronoicell c(*this);double *pp; + if(vl.start()) do if(compute_cell(c,vl)) { + pp=p[vl.ijk]+ps*vl.q; + c.draw_gnuplot(*pp,pp[1],pp[2],fp); + } while(vl.inc()); + } + /** Computes all Voronoi cells and saves the output in gnuplot + * format. + * \param[in] fp a file handle to write to. */ + inline void draw_cells_gnuplot(FILE *fp=stdout) { + c_loop_all vl(*this); + draw_cells_gnuplot(vl,fp); + } + /** Computes all Voronoi cells and saves the output in gnuplot + * format. + * \param[in] filename the name of the file to write to. */ + inline void draw_cells_gnuplot(const char *filename) { + FILE *fp=safe_fopen(filename,"w"); + draw_cells_gnuplot(fp); + fclose(fp); + } + /** Computes Voronoi cells and saves the output in POV-Ray + * format. + * \param[in] vl the loop class to use. + * \param[in] fp a file handle to write to. */ + template + void draw_cells_pov(c_loop &vl,FILE *fp) { + voronoicell c(*this);double *pp; + if(vl.start()) do if(compute_cell(c,vl)) { + fprintf(fp,"// cell %d\n",id[vl.ijk][vl.q]); + pp=p[vl.ijk]+ps*vl.q; + c.draw_pov(*pp,pp[1],pp[2],fp); + } while(vl.inc()); + } + /** Computes all Voronoi cells and saves the output in POV-Ray + * format. + * \param[in] fp a file handle to write to. */ + inline void draw_cells_pov(FILE *fp=stdout) { + c_loop_all vl(*this); + draw_cells_pov(vl,fp); + } + /** Computes all Voronoi cells and saves the output in POV-Ray + * format. + * \param[in] filename the name of the file to write to. */ + inline void draw_cells_pov(const char *filename) { + FILE *fp=safe_fopen(filename,"w"); + draw_cells_pov(fp); + fclose(fp); + } + /** Computes the Voronoi cells and saves customized information + * about them. + * \param[in] vl the loop class to use. + * \param[in] format the custom output string to use. + * \param[in] fp a file handle to write to. */ + template + void print_custom(c_loop &vl,const char *format,FILE *fp) { + int ijk,q;double *pp; + if(contains_neighbor(format)) { + voronoicell_neighbor c(*this); + if(vl.start()) do if(compute_cell(c,vl)) { + ijk=vl.ijk;q=vl.q;pp=p[ijk]+ps*q; + c.output_custom(format,id[ijk][q],*pp,pp[1],pp[2],default_radius,fp); + } while(vl.inc()); + } else { + voronoicell c(*this); + if(vl.start()) do if(compute_cell(c,vl)) { + ijk=vl.ijk;q=vl.q;pp=p[ijk]+ps*q; + c.output_custom(format,id[ijk][q],*pp,pp[1],pp[2],default_radius,fp); + } while(vl.inc()); + } + } + void print_custom(const char *format,FILE *fp=stdout); + void print_custom(const char *format,const char *filename); + bool find_voronoi_cell(double x,double y,double z,double &rx,double &ry,double &rz,int &pid); + /** Computes the Voronoi cell for a particle currently being + * referenced by a loop class. + * \param[out] c a Voronoi cell class in which to store the + * computed cell. + * \param[in] vl the loop class to use. + * \return True if the cell was computed. If the cell cannot be + * computed, if it is removed entirely by a wall or boundary + * condition, then the routine returns false. */ + template + inline bool compute_cell(v_cell &c,c_loop &vl) { + return vc.compute_cell(c,vl.ijk,vl.q,vl.i,vl.j,vl.k); + } + /** Computes the Voronoi cell for given particle. + * \param[out] c a Voronoi cell class in which to store the + * computed cell. + * \param[in] ijk the block that the particle is within. + * \param[in] q the index of the particle within the block. + * \return True if the cell was computed. If the cell cannot be + * computed, if it is removed entirely by a wall or boundary + * condition, then the routine returns false. */ + template + inline bool compute_cell(v_cell &c,int ijk,int q) { + int k=ijk/nxy,ijkt=ijk-nxy*k,j=ijkt/nx,i=ijkt-j*nx; + return vc.compute_cell(c,ijk,q,i,j,k); + } + /** Computes the Voronoi cell for a ghost particle at a given + * location. + * \param[out] c a Voronoi cell class in which to store the + * computed cell. + * \param[in] (x,y,z) the location of the ghost particle. + * \return True if the cell was computed. If the cell cannot be + * computed, if it is removed entirely by a wall or boundary + * condition, then the routine returns false. */ + template + inline bool compute_ghost_cell(v_cell &c,double x,double y,double z) { + int ijk; + if(put_locate_block(ijk,x,y,z)) { + double *pp=p[ijk]+3*co[ijk]++; + *(pp++)=x;*(pp++)=y;*pp=z; + bool q=compute_cell(c,ijk,co[ijk]-1); + co[ijk]--; + return q; + } + return false; + } + private: + voro_compute vc; + friend class voro_compute; +}; + +/** \brief Extension of the container_base class for computing radical Voronoi + * tessellations. + * + * This class is an extension of container_base class that has routines + * specifically for computing the radical Voronoi tessellation that depends on + * the particle radii. */ +class container_poly : public container_base, public radius_poly { + public: + container_poly(double ax_,double bx_,double ay_,double by_,double az_,double bz_, + int nx_,int ny_,int nz_,bool xperiodic_,bool yperiodic_,bool zperiodic_,int init_mem); + void clear(); + void put(int n,double x,double y,double z,double r); + void put(particle_order &vo,int n,double x,double y,double z,double r); + void import(FILE *fp=stdin); + void import(particle_order &vo,FILE *fp=stdin); + /** Imports a list of particles from an open file stream into + * the container_poly class. Entries of five numbers (Particle + * ID, x position, y position, z position, radius) are searched + * for. If the file cannot be successfully read, then the + * routine causes a fatal error. + * \param[in] filename the name of the file to open and read + * from. */ + inline void import(const char* filename) { + FILE *fp=safe_fopen(filename,"r"); + import(fp); + fclose(fp); + } + /** Imports a list of particles from an open file stream into + * the container_poly class. Entries of five numbers (Particle + * ID, x position, y position, z position, radius) are searched + * for. In addition, the order in which particles are read is + * saved into an ordering class. If the file cannot be + * successfully read, then the routine causes a fatal error. + * \param[in,out] vo the ordering class to use. + * \param[in] filename the name of the file to open and read + * from. */ + inline void import(particle_order &vo,const char* filename) { + FILE *fp=safe_fopen(filename,"r"); + import(vo,fp); + fclose(fp); + } + void compute_all_cells(); + double sum_cell_volumes(); + /** Dumps particle IDs, positions and radii to a file. + * \param[in] vl the loop class to use. + * \param[in] fp a file handle to write to. */ + template + void draw_particles(c_loop &vl,FILE *fp) { + double *pp; + if(vl.start()) do { + pp=p[vl.ijk]+4*vl.q; + fprintf(fp,"%d %g %g %g %g\n",id[vl.ijk][vl.q],*pp,pp[1],pp[2],pp[3]); + } while(vl.inc()); + } + /** Dumps all of the particle IDs, positions and radii to a + * file. + * \param[in] fp a file handle to write to. */ + inline void draw_particles(FILE *fp=stdout) { + c_loop_all vl(*this); + draw_particles(vl,fp); + } + /** Dumps all of the particle IDs, positions and radii to a + * file. + * \param[in] filename the name of the file to write to. */ + inline void draw_particles(const char *filename) { + FILE *fp=safe_fopen(filename,"w"); + draw_particles(fp); + fclose(fp); + } + /** Dumps particle positions in POV-Ray format. + * \param[in] vl the loop class to use. + * \param[in] fp a file handle to write to. */ + template + void draw_particles_pov(c_loop &vl,FILE *fp) { + double *pp; + if(vl.start()) do { + pp=p[vl.ijk]+4*vl.q; + fprintf(fp,"// id %d\nsphere{<%g,%g,%g>,%g}\n", + id[vl.ijk][vl.q],*pp,pp[1],pp[2],pp[3]); + } while(vl.inc()); + } + /** Dumps all the particle positions in POV-Ray format. + * \param[in] fp a file handle to write to. */ + inline void draw_particles_pov(FILE *fp=stdout) { + c_loop_all vl(*this); + draw_particles_pov(vl,fp); + } + /** Dumps all the particle positions in POV-Ray format. + * \param[in] filename the name of the file to write to. */ + inline void draw_particles_pov(const char *filename) { + FILE *fp=safe_fopen(filename,"w"); + draw_particles_pov(fp); + fclose(fp); + } + /** Computes Voronoi cells and saves the output in gnuplot + * format. + * \param[in] vl the loop class to use. + * \param[in] fp a file handle to write to. */ + template + void draw_cells_gnuplot(c_loop &vl,FILE *fp) { + voronoicell c;double *pp; + if(vl.start()) do if(compute_cell(c,vl)) { + pp=p[vl.ijk]+ps*vl.q; + c.draw_gnuplot(*pp,pp[1],pp[2],fp); + } while(vl.inc()); + } + /** Compute all Voronoi cells and saves the output in gnuplot + * format. + * \param[in] fp a file handle to write to. */ + inline void draw_cells_gnuplot(FILE *fp=stdout) { + c_loop_all vl(*this); + draw_cells_gnuplot(vl,fp); + } + /** Compute all Voronoi cells and saves the output in gnuplot + * format. + * \param[in] filename the name of the file to write to. */ + inline void draw_cells_gnuplot(const char *filename) { + FILE *fp=safe_fopen(filename,"w"); + draw_cells_gnuplot(fp); + fclose(fp); + } + /** Computes Voronoi cells and saves the output in POV-Ray + * format. + * \param[in] vl the loop class to use. + * \param[in] fp a file handle to write to. */ + template + void draw_cells_pov(c_loop &vl,FILE *fp) { + voronoicell c(*this);double *pp; + if(vl.start()) do if(compute_cell(c,vl)) { + fprintf(fp,"// cell %d\n",id[vl.ijk][vl.q]); + pp=p[vl.ijk]+ps*vl.q; + c.draw_pov(*pp,pp[1],pp[2],fp); + } while(vl.inc()); + } + /** Computes all Voronoi cells and saves the output in POV-Ray + * format. + * \param[in] fp a file handle to write to. */ + inline void draw_cells_pov(FILE *fp=stdout) { + c_loop_all vl(*this); + draw_cells_pov(vl,fp); + } + /** Computes all Voronoi cells and saves the output in POV-Ray + * format. + * \param[in] filename the name of the file to write to. */ + inline void draw_cells_pov(const char *filename) { + FILE *fp=safe_fopen(filename,"w"); + draw_cells_pov(fp); + fclose(fp); + } + /** Computes the Voronoi cells and saves customized information + * about them. + * \param[in] vl the loop class to use. + * \param[in] format the custom output string to use. + * \param[in] fp a file handle to write to. */ + template + void print_custom(c_loop &vl,const char *format,FILE *fp) { + int ijk,q;double *pp; + if(contains_neighbor(format)) { + voronoicell_neighbor c(*this); + if(vl.start()) do if(compute_cell(c,vl)) { + ijk=vl.ijk;q=vl.q;pp=p[ijk]+ps*q; + c.output_custom(format,id[ijk][q],*pp,pp[1],pp[2],pp[3],fp); + } while(vl.inc()); + } else { + voronoicell c(*this); + if(vl.start()) do if(compute_cell(c,vl)) { + ijk=vl.ijk;q=vl.q;pp=p[ijk]+ps*q; + c.output_custom(format,id[ijk][q],*pp,pp[1],pp[2],pp[3],fp); + } while(vl.inc()); + } + } + /** Computes the Voronoi cell for a particle currently being + * referenced by a loop class. + * \param[out] c a Voronoi cell class in which to store the + * computed cell. + * \param[in] vl the loop class to use. + * \return True if the cell was computed. If the cell cannot be + * computed, if it is removed entirely by a wall or boundary + * condition, then the routine returns false. */ + template + inline bool compute_cell(v_cell &c,c_loop &vl) { + return vc.compute_cell(c,vl.ijk,vl.q,vl.i,vl.j,vl.k); + } + /** Computes the Voronoi cell for given particle. + * \param[out] c a Voronoi cell class in which to store the + * computed cell. + * \param[in] ijk the block that the particle is within. + * \param[in] q the index of the particle within the block. + * \return True if the cell was computed. If the cell cannot be + * computed, if it is removed entirely by a wall or boundary + * condition, then the routine returns false. */ + template + inline bool compute_cell(v_cell &c,int ijk,int q) { + int k=ijk/nxy,ijkt=ijk-nxy*k,j=ijkt/nx,i=ijkt-j*nx; + return vc.compute_cell(c,ijk,q,i,j,k); + } + /** Computes the Voronoi cell for a ghost particle at a given + * location. + * \param[out] c a Voronoi cell class in which to store the + * computed cell. + * \param[in] (x,y,z) the location of the ghost particle. + * \param[in] r the radius of the ghost particle. + * \return True if the cell was computed. If the cell cannot be + * computed, if it is removed entirely by a wall or boundary + * condition, then the routine returns false. */ + template + inline bool compute_ghost_cell(v_cell &c,double x,double y,double z,double r) { + int ijk; + if(put_locate_block(ijk,x,y,z)) { + double *pp=p[ijk]+4*co[ijk]++,tm=max_radius; + *(pp++)=x;*(pp++)=y;*(pp++)=z;*pp=r; + if(r>max_radius) max_radius=r; + bool q=compute_cell(c,ijk,co[ijk]-1); + co[ijk]--;max_radius=tm; + return q; + } + return false; + } + void print_custom(const char *format,FILE *fp=stdout); + void print_custom(const char *format,const char *filename); + bool find_voronoi_cell(double x,double y,double z,double &rx,double &ry,double &rz,int &pid); + private: + voro_compute vc; + friend class voro_compute; +}; + +} + +#endif diff --git a/src/third_party/voro/src/container_prd.cc b/src/third_party/voro/src/container_prd.cc new file mode 100644 index 000000000..07625b379 --- /dev/null +++ b/src/third_party/voro/src/container_prd.cc @@ -0,0 +1,776 @@ +// Voro++, a 3D cell-based Voronoi library +// +// Author : Chris H. Rycroft (Harvard University / LBL) +// Email : chr@alum.mit.edu +// Date : August 30th 2011 + +/** \file container_prd.cc + * \brief Function implementations for the container_periodic_base and + * related classes. */ + +#include "container_prd.hh" + +namespace voro { + +/** The class constructor sets up the geometry of container, initializing the + * minimum and maximum coordinates in each direction, and setting whether each + * direction is periodic or not. It divides the container into a rectangular + * grid of blocks, and allocates memory for each of these for storing particle + * positions and IDs. + * \param[in] (bx_) The x coordinate of the first unit vector. + * \param[in] (bxy_,by_) The x and y coordinates of the second unit vector. + * \param[in] (bxz_,byz_,bz_) The x, y, and z coordinates of the third unit + * vector. + * \param[in] (nx_,ny_,nz_) the number of grid blocks in each of the three + * coordinate directions. + * \param[in] init_mem_ the initial memory allocation for each block. + * \param[in] ps_ the number of floating point entries to store for each + * particle. */ +container_periodic_base::container_periodic_base(double bx_,double bxy_,double by_, + double bxz_,double byz_,double bz_,int nx_,int ny_,int nz_,int init_mem_,int ps_) + : unitcell(bx_,bxy_,by_,bxz_,byz_,bz_), + voro_base(nx_,ny_,nz_,bx_/nx_,by_/ny_,bz_/nz_), max_len_sq(unit_voro.max_radius_squared()), + ey(int(max_uv_y*ysp+1)), ez(int(max_uv_z*zsp+1)), wy(ny+ey), wz(nz+ez), + oy(ny+2*ey), oz(nz+2*ez), oxyz(nx*oy*oz), id(new int*[oxyz]), p(new double*[oxyz]), + co(new int[oxyz]), mem(new int[oxyz]), img(new char[oxyz]), init_mem(init_mem_), ps(ps_) { + int i,j,k,l; + + // Clear the global arrays + int *pp=co;while(pp=0;l--) if(mem[l]>0) { + delete [] p[l]; + delete [] id[l]; + } + delete [] img; + delete [] mem; + delete [] co; + delete [] id; + delete [] p; +} + +/** The class constructor sets up the geometry of container. + * \param[in] (bx_) The x coordinate of the first unit vector. + * \param[in] (bxy_,by_) The x and y coordinates of the second unit vector. + * \param[in] (bxz_,byz_,bz_) The x, y, and z coordinates of the third unit + * vector. + * \param[in] (nx_,ny_,nz_) the number of grid blocks in each of the three + * coordinate directions. + * \param[in] init_mem_ the initial memory allocation for each block. */ +container_periodic::container_periodic(double bx_,double bxy_,double by_,double bxz_,double byz_,double bz_, + int nx_,int ny_,int nz_,int init_mem_) + : container_periodic_base(bx_,bxy_,by_,bxz_,byz_,bz_,nx_,ny_,nz_,init_mem_,3), + vc(*this,2*nx_+1,2*ey+1,2*ez+1) {} + +/** The class constructor sets up the geometry of container. + * \param[in] (bx_) The x coordinate of the first unit vector. + * \param[in] (bxy_,by_) The x and y coordinates of the second unit vector. + * \param[in] (bxz_,byz_,bz_) The x, y, and z coordinates of the third unit + * vector. + * \param[in] (nx_,ny_,nz_) the number of grid blocks in each of the three + * coordinate directions. + * \param[in] init_mem_ the initial memory allocation for each block. */ +container_periodic_poly::container_periodic_poly(double bx_,double bxy_,double by_,double bxz_,double byz_,double bz_, + int nx_,int ny_,int nz_,int init_mem_) + : container_periodic_base(bx_,bxy_,by_,bxz_,byz_,bz_,nx_,ny_,nz_,init_mem_,4), + vc(*this,2*nx_+1,2*ey+1,2*ez+1) {ppr=p;} + +/** Put a particle into the correct region of the container. + * \param[in] n the numerical ID of the inserted particle. + * \param[in] (x,y,z) the position vector of the inserted particle. */ +void container_periodic::put(int n,double x,double y,double z) { + int ijk; + put_locate_block(ijk,x,y,z); + for(int l=0;l=nz) { + int ak=step_div(k,nz); + z-=ak*bz;y-=ak*byz;x-=ak*bxz;k-=ak*nz; + } + + // Remap particle in the y direction if necessary + int j=step_int(y*ysp); + if(j<0||j>=ny) { + int aj=step_div(j,ny); + y-=aj*by;x-=aj*bxy;j-=aj*ny; + } + + // Remap particle in the x direction if necessary + ijk=step_int(x*xsp); + if(ijk<0||ijk>=nx) { + int ai=step_div(ijk,nx); + x-=ai*bx;ijk-=ai*nx; + } + + // Compute the block index and check memory allocation + j+=ey;k+=ez; + ijk+=nx*(j+oy*k); + if(co[ijk]==mem[ijk]) add_particle_memory(ijk); +} + +/** Takes a particle position vector and computes the region index into which + * it should be stored. If the container is periodic, then the routine also + * maps the particle position to ensure it is in the primary domain. If the + * container is not periodic, the routine bails out. + * \param[out] ijk the region index. + * \param[in,out] (x,y,z) the particle position, remapped into the primary + * domain if necessary. + * \param[out] (ai,aj,ak) the periodic image displacement that the particle is + * in, with (0,0,0) corresponding to the primary domain. + * \return True if the particle can be successfully placed into the container, + * false otherwise. */ +void container_periodic_base::put_locate_block(int &ijk,double &x,double &y,double &z,int &ai,int &aj,int &ak) { + + // Remap particle in the z direction if necessary + int k=step_int(z*zsp); + if(k<0||k>=nz) { + ak=step_div(k,nz); + z-=ak*bz;y-=ak*byz;x-=ak*bxz;k-=ak*nz; + } else ak=0; + + // Remap particle in the y direction if necessary + int j=step_int(y*ysp); + if(j<0||j>=ny) { + aj=step_div(j,ny); + y-=aj*by;x-=aj*bxy;j-=aj*ny; + } else aj=0; + + // Remap particle in the x direction if necessary + ijk=step_int(x*xsp); + if(ijk<0||ijk>=nx) { + ai=step_div(ijk,nx); + x-=ai*bx;ijk-=ai*nx; + } else ai=0; + + // Compute the block index and check memory allocation + j+=ey;k+=ez; + ijk+=nx*(j+oy*k); + if(co[ijk]==mem[ijk]) add_particle_memory(ijk); +} + +/** Takes a position vector and remaps it into the primary domain. + * \param[out] (ai,aj,ak) the periodic image displacement that the vector is in, + * with (0,0,0) corresponding to the primary domain. + * \param[out] (ci,cj,ck) the index of the block that the position vector is + * within, once it has been remapped. + * \param[in,out] (x,y,z) the position vector to consider, which is remapped + * into the primary domain during the routine. + * \param[out] ijk the block index that the vector is within. */ +inline void container_periodic_base::remap(int &ai,int &aj,int &ak,int &ci,int &cj,int &ck,double &x,double &y,double &z,int &ijk) { + + // Remap particle in the z direction if necessary + ck=step_int(z*zsp); + if(ck<0||ck>=nz) { + ak=step_div(ck,nz); + z-=ak*bz;y-=ak*byz;x-=ak*bxz;ck-=ak*nz; + } else ak=0; + + // Remap particle in the y direction if necessary + cj=step_int(y*ysp); + if(cj<0||cj>=ny) { + aj=step_div(cj,ny); + y-=aj*by;x-=aj*bxy;cj-=aj*ny; + } else aj=0; + + // Remap particle in the x direction if necessary + ci=step_int(x*xsp); + if(ci<0||ci>=nx) { + ai=step_div(ci,nx); + x-=ai*bx;ci-=ai*nx; + } else ai=0; + + cj+=ey;ck+=ez; + ijk=ci+nx*(cj+oy*ck); +} + +/** Takes a vector and finds the particle whose Voronoi cell contains that + * vector. This is equivalent to finding the particle which is nearest to the + * vector. + * \param[in] (x,y,z) the vector to test. + * \param[out] (rx,ry,rz) the position of the particle whose Voronoi cell + * contains the vector. This may point to a particle in + * a periodic image of the primary domain. + * \param[out] pid the ID of the particle. + * \return True if a particle was found. If the container has no particles, + * then the search will not find a Voronoi cell and false is returned. */ +bool container_periodic::find_voronoi_cell(double x,double y,double z,double &rx,double &ry,double &rz,int &pid) { + int ai,aj,ak,ci,cj,ck,ijk; + particle_record w; + double mrs; + + // Remap the vector into the primary domain and then search for the + // Voronoi cell that it is within + remap(ai,aj,ak,ci,cj,ck,x,y,z,ijk); + vc.find_voronoi_cell(x,y,z,ci,cj,ck,ijk,w,mrs); + + if(w.ijk!=-1) { + + // Assemble the position vector of the particle to be returned, + // applying a periodic remapping if necessary + ci+=w.di;if(ci<0||ci>=nx) ai+=step_div(ci,nx); + rx=p[w.ijk][3*w.l]+ak*bxz+aj*bxy+ai*bx; + ry=p[w.ijk][3*w.l+1]+ak*byz+aj*by; + rz=p[w.ijk][3*w.l+2]+ak*bz; + pid=id[w.ijk][w.l]; + return true; + } + return false; +} + +/** Takes a vector and finds the particle whose Voronoi cell contains that + * vector. Additional wall classes are not considered by this routine. + * \param[in] (x,y,z) the vector to test. + * \param[out] (rx,ry,rz) the position of the particle whose Voronoi cell + * contains the vector. If the container is periodic, + * this may point to a particle in a periodic image of + * the primary domain. + * \param[out] pid the ID of the particle. + * \return True if a particle was found. If the container has no particles, + * then the search will not find a Voronoi cell and false is returned. */ +bool container_periodic_poly::find_voronoi_cell(double x,double y,double z,double &rx,double &ry,double &rz,int &pid) { + int ai,aj,ak,ci,cj,ck,ijk; + particle_record w; + double mrs; + + // Remap the vector into the primary domain and then search for the + // Voronoi cell that it is within + remap(ai,aj,ak,ci,cj,ck,x,y,z,ijk); + vc.find_voronoi_cell(x,y,z,ci,cj,ck,ijk,w,mrs); + + if(w.ijk!=-1) { + + // Assemble the position vector of the particle to be returned, + // applying a periodic remapping if necessary + ci+=w.di;if(ci<0||ci>=nx) ai+=step_div(ci,nx); + rx=p[w.ijk][4*w.l]+ak*bxz+aj*bxy+ai*bx; + ry=p[w.ijk][4*w.l+1]+ak*byz+aj*by; + rz=p[w.ijk][4*w.l+2]+ak*bz; + pid=id[w.ijk][w.l]; + return true; + } + return false; +} + +/** Increase memory for a particular region. + * \param[in] i the index of the region to reallocate. */ +void container_periodic_base::add_particle_memory(int i) { + + // Handle the case when no memory has been allocated for this block + if(mem[i]==0) { + mem[i]=init_mem; + id[i]=new int[init_mem]; + p[i]=new double[ps*init_mem]; + return; + } + + // Otherwise, double the memory allocation for this block. Carry out a + // check on the memory allocation size, and print a status message if + // requested. + int l,nmem(mem[i]<<1); + if(nmem>max_particle_memory) + voro_fatal_error("Absolute maximum memory allocation exceeded",VOROPP_MEMORY_ERROR); +#if VOROPP_VERBOSE >=3 + fprintf(stderr,"Particle memory in region %d scaled up to %d\n",i,nmem); +#endif + + // Allocate new memory and copy in the contents of the old arrays + int *idp=new int[nmem]; + for(l=0;l0) { + + // Compute the block's bounds, adding in a small tolerance + mix=i*boxx-tolerance;max=mix+boxx+tolerance; + miy=(j-ey)*boxy-tolerance;may=miy+boxy+tolerance; + miz=(k-ez)*boxz-tolerance;maz=miz+boxz+tolerance; + + // Print entries for any particles that lie outside the block's + // bounds + for(pp=p[l],c=0;cmax||pp[1]may||pp[2]maz) + printf("%d %d %d %d %f %f %f %f %f %f %f %f %f\n", + id[l][c],i,j,k,*pp,pp[1],pp[2],mix,max,miy,may,miz,maz); + } +} + +/** Creates particles within an image block that is aligned with the primary + * domain in the z axis. In this case, the image block may be comprised of + * particles from two primary blocks. The routine considers these two primary + * blocks, and adds the needed particles to the image. The remaining particles + * from the primary blocks are also filled into the neighboring images. + * \param[in] (di,dj,dk) the index of the block to consider. The z index must + * satisfy ez<=dk0) { + odijk=dijk-1;adis=dis; + } else { + odijk=dijk+nx-1;adis=dis+bx; + } + img[odijk]|=2; + for(l=0;lswitchx) put_image(dijk,fijk,l,dis,by*ima,0); + else put_image(odijk,fijk,l,adis,by*ima,0); + } + } + + // Right image computation + if((img[dijk]&2)==0) { + if(fi==nx-1) { + fijk+=1-nx;switchx+=(1-nx)*boxx;dis+=bx; + } else { + fijk++;switchx+=boxx; + } + if(di==nx-1) { + odijk=dijk-nx+1;adis=dis-bx; + } else { + odijk=dijk+1;adis=dis; + } + img[odijk]|=1; + for(l=0;l=wz. */ +void container_periodic_base::create_vertical_image(int di,int dj,int dk) { + int l,dijk=di+nx*(dj+oy*dk),dijkl,dijkr,ima=step_div(dk-ez,nz); + int qj=dj+step_int(-ima*byz*ysp),qjdiv=step_div(qj-ey,ny); + int qi=di+step_int((-ima*bxz-qjdiv*bxy)*xsp),qidiv=step_div(qi,nx); + int fi=qi-qidiv*nx,fj=qj-qjdiv*ny,fijk=fi+nx*(fj+oy*(dk-ima*nz)),fijk2; + double disy=ima*byz+qjdiv*by,switchy=(dj-ey)*boxy-ima*byz-qjdiv*by; + double disx=ima*bxz+qjdiv*bxy+qidiv*bx,switchx=di*boxx-ima*bxz-qjdiv*bxy-qidiv*bx; + double switchx2,disxl,disxr,disx2,disxr2; + + if(di==0) {dijkl=dijk+nx-1;disxl=disx+bx;} + else {dijkl=dijk-1;disxl=disx;} + + if(di==nx-1) {dijkr=dijk-nx+1;disxr=disx-bx;} + else {dijkr=dijk+1;disxr=disx;} + + // Down-left image computation + bool y_exist=dj!=0; + if((img[dijk]&1)==0) { + img[dijkl]|=2; + if(y_exist) { + img[dijkl-nx]|=8; + img[dijk-nx]|=4; + } + for(l=0;lswitchy) { + if(p[fijk][ps*l]>switchx) put_image(dijk,fijk,l,disx,disy,bz*ima); + else put_image(dijkl,fijk,l,disxl,disy,bz*ima); + } else { + if(!y_exist) continue; + if(p[fijk][ps*l]>switchx) put_image(dijk-nx,fijk,l,disx,disy,bz*ima); + else put_image(dijkl-nx,fijk,l,disxl,disy,bz*ima); + } + } + } + + // Down-right image computation + if((img[dijk]&2)==0) { + if(fi==nx-1) { + fijk2=fijk+1-nx;switchx2=switchx+(1-nx)*boxx;disx2=disx+bx;disxr2=disxr+bx; + } else { + fijk2=fijk+1;switchx2=switchx+boxx;disx2=disx;disxr2=disxr; + } + img[dijkr]|=1; + if(y_exist) { + img[dijkr-nx]|=4; + img[dijk-nx]|=8; + } + for(l=0;lswitchy) { + if(p[fijk2][ps*l]>switchx2) put_image(dijkr,fijk2,l,disxr2,disy,bz*ima); + else put_image(dijk,fijk2,l,disx2,disy,bz*ima); + } else { + if(!y_exist) continue; + if(p[fijk2][ps*l]>switchx2) put_image(dijkr-nx,fijk2,l,disxr2,disy,bz*ima); + else put_image(dijk-nx,fijk2,l,disx2,disy,bz*ima); + } + } + } + + // Recomputation of some intermediate quantities for boundary cases + if(fj==wy-1) { + fijk+=nx*(1-ny)-fi; + switchy+=(1-ny)*boxy; + disy+=by; + qi=di+step_int(-(ima*bxz+(qjdiv+1)*bxy)*xsp); + int dqidiv=step_div(qi,nx)-qidiv;qidiv+=dqidiv; + fi=qi-qidiv*nx; + fijk+=fi; + disx+=bxy+bx*dqidiv; + disxl+=bxy+bx*dqidiv; + disxr+=bxy+bx*dqidiv; + switchx-=bxy+bx*dqidiv; + } else { + fijk+=nx;switchy+=boxy; + } + + // Up-left image computation + y_exist=dj!=oy-1; + if((img[dijk]&4)==0) { + img[dijkl]|=8; + if(y_exist) { + img[dijkl+nx]|=2; + img[dijk+nx]|=1; + } + for(l=0;lswitchy) { + if(!y_exist) continue; + if(p[fijk][ps*l]>switchx) put_image(dijk+nx,fijk,l,disx,disy,bz*ima); + else put_image(dijkl+nx,fijk,l,disxl,disy,bz*ima); + } else { + if(p[fijk][ps*l]>switchx) put_image(dijk,fijk,l,disx,disy,bz*ima); + else put_image(dijkl,fijk,l,disxl,disy,bz*ima); + } + } + } + + // Up-right image computation + if((img[dijk]&8)==0) { + if(fi==nx-1) { + fijk2=fijk+1-nx;switchx2=switchx+(1-nx)*boxx;disx2=disx+bx;disxr2=disxr+bx; + } else { + fijk2=fijk+1;switchx2=switchx+boxx;disx2=disx;disxr2=disxr; + } + img[dijkr]|=4; + if(y_exist) { + img[dijkr+nx]|=1; + img[dijk+nx]|=2; + } + for(l=0;lswitchy) { + if(!y_exist) continue; + if(p[fijk2][ps*l]>switchx2) put_image(dijkr+nx,fijk2,l,disxr2,disy,bz*ima); + else put_image(dijk+nx,fijk2,l,disx2,disy,bz*ima); + } else { + if(p[fijk2][ps*l]>switchx2) put_image(dijkr,fijk2,l,disxr2,disy,bz*ima); + else put_image(dijk,fijk2,l,disx2,disy,bz*ima); + } + } + } + + // All contributions to the block now added, so set all four bits of + // the image information + img[dijk]=15; +} + +/** Copies a particle position from the primary domain into an image block. + * \param[in] reg the block index within the primary domain that the particle + * is within. + * \param[in] fijk the index of the image block. + * \param[in] l the index of the particle entry within the primary block. + * \param[in] (dx,dy,dz) the displacement vector to add to the particle. */ +void container_periodic_base::put_image(int reg,int fijk,int l,double dx,double dy,double dz) { + if(co[reg]==mem[reg]) add_particle_memory(reg); + double *p1=p[reg]+ps*co[reg],*p2=p[fijk]+ps*l; + *(p1++)=*(p2++)+dx; + *(p1++)=*(p2++)+dy; + *p1=*p2+dz; + if(ps==4) *(++p1)=*(++p2); + id[reg][co[reg]++]=id[fijk][l]; +} + +} diff --git a/src/third_party/voro/src/container_prd.hh b/src/third_party/voro/src/container_prd.hh new file mode 100644 index 000000000..e350cda09 --- /dev/null +++ b/src/third_party/voro/src/container_prd.hh @@ -0,0 +1,655 @@ +// Voro++, a 3D cell-based Voronoi library +// +// Author : Chris H. Rycroft (Harvard University / LBL) +// Email : chr@alum.mit.edu +// Date : August 30th 2011 + +/** \file container_prd.hh + * \brief Header file for the container_periodic_base and related classes. */ + +#ifndef VOROPP_CONTAINER_PRD_HH +#define VOROPP_CONTAINER_PRD_HH + +#include +#include + +#include "config.hh" +#include "common.hh" +#include "v_base.hh" +#include "cell.hh" +#include "c_loops.hh" +#include "v_compute.hh" +#include "unitcell.hh" +#include "rad_option.hh" + +namespace voro { + +/** \brief Class for representing a particle system in a 3D periodic + * non-orthogonal periodic domain. + * + * This class represents a particle system in a three-dimensional + * non-orthogonal periodic domain. The domain is defined by three periodicity + * vectors (bx,0,0), (bxy,by,0), and (bxz,byz,bz) that represent a + * parallelepiped. Internally, the class stores particles in the box 0 + inline bool initialize_voronoicell(v_cell &c,int ijk,int q,int ci,int cj,int ck,int &i,int &j,int &k,double &x,double &y,double &z,int &disp) { + c=unit_voro; + double *pp=p[ijk]+ps*q; + x=*(pp++);y=*(pp++);z=*pp; + i=nx;j=ey;k=ez; + return true; + } + /** Initializes parameters for a find_voronoi_cell call within + * the voro_compute template. + * \param[in] (ci,cj,ck) the coordinates of the test block in + * the container coordinate system. + * \param[in] ijk the index of the test block + * \param[out] (i,j,k) the coordinates of the test block + * relative to the voro_compute + * coordinate system. + * \param[out] disp a block displacement used internally by the + * find_voronoi_cell routine (but not needed + * in this instance.) */ + inline void initialize_search(int ci,int cj,int ck,int ijk,int &i,int &j,int &k,int &disp) { + i=nx;j=ey;k=ez; + } + /** Returns the position of a particle currently being computed + * relative to the computational block that it is within. It is + * used to select the optimal worklist entry to use. + * \param[in] (x,y,z) the position of the particle. + * \param[in] (ci,cj,ck) the block that the particle is within. + * \param[out] (fx,fy,fz) the position relative to the block. + */ + inline void frac_pos(double x,double y,double z,double ci,double cj,double ck,double &fx,double &fy,double &fz) { + fx=x-boxx*ci; + fy=y-boxy*(cj-ey); + fz=z-boxz*(ck-ez); + } + /** Calculates the index of block in the container structure + * corresponding to given coordinates. + * \param[in] (ci,cj,ck) the coordinates of the original block + * in the current computation, relative + * to the container coordinate system. + * \param[in] (ei,ej,ek) the displacement of the current block + * from the original block. + * \param[in,out] (qx,qy,qz) the periodic displacement that + * must be added to the particles + * within the computed block. + * \param[in] disp a block displacement used internally by the + * find_voronoi_cell and compute_cell routines + * (but not needed in this instance.) + * \return The block index. */ + inline int region_index(int ci,int cj,int ck,int ei,int ej,int ek,double &qx,double &qy,double &qz,int &disp) { + int qi=ci+(ei-nx),qj=cj+(ej-ey),qk=ck+(ek-ez); + int iv(step_div(qi,nx));if(iv!=0) {qx=iv*bx;qi-=nx*iv;} else qx=0; + create_periodic_image(qi,qj,qk); + return qi+nx*(qj+oy*qk); + } + void create_all_images(); + void check_compartmentalized(); + protected: + void add_particle_memory(int i); + void put_locate_block(int &ijk,double &x,double &y,double &z); + void put_locate_block(int &ijk,double &x,double &y,double &z,int &ai,int &aj,int &ak); + /** Creates particles within an image block by copying them + * from the primary domain and shifting them. If the given + * block is aligned with the primary domain in the z-direction, + * the routine calls the simpler create_side_image routine + * where the image block may comprise of particles from up to + * two primary blocks. Otherwise is calls the more complex + * create_vertical_image where the image block may comprise of + * particles from up to four primary blocks. + * \param[in] (di,dj,dk) the coordinates of the image block to + * create. */ + inline void create_periodic_image(int di,int dj,int dk) { + if(di<0||di>=nx||dj<0||dj>=oy||dk<0||dk>=oz) + voro_fatal_error("Constructing periodic image for nonexistent point",VOROPP_INTERNAL_ERROR); + if(dk>=ez&&dk=wy) create_side_image(di,dj,dk); + } else create_vertical_image(di,dj,dk); + } + void create_side_image(int di,int dj,int dk); + void create_vertical_image(int di,int dj,int dk); + void put_image(int reg,int fijk,int l,double dx,double dy,double dz); + inline void remap(int &ai,int &aj,int &ak,int &ci,int &cj,int &ck,double &x,double &y,double &z,int &ijk); +}; + +/** \brief Extension of the container_periodic_base class for computing regular + * Voronoi tessellations. + * + * This class is an extension of the container_periodic_base that has routines + * specifically for computing the regular Voronoi tessellation with no + * dependence on particle radii. */ +class container_periodic : public container_periodic_base, public radius_mono { + public: + container_periodic(double bx_,double bxy_,double by_,double bxz_,double byz_,double bz_, + int nx_,int ny_,int nz_,int init_mem_); + void clear(); + void put(int n,double x,double y,double z); + void put(int n,double x,double y,double z,int &ai,int &aj,int &ak); + void put(particle_order &vo,int n,double x,double y,double z); + void import(FILE *fp=stdin); + void import(particle_order &vo,FILE *fp=stdin); + /** Imports a list of particles from an open file stream into + * the container. Entries of four numbers (Particle ID, x + * position, y position, z position) are searched for. If the + * file cannot be successfully read, then the routine causes a + * fatal error. + * \param[in] filename the name of the file to open and read + * from. */ + inline void import(const char* filename) { + FILE *fp=safe_fopen(filename,"r"); + import(fp); + fclose(fp); + } + /** Imports a list of particles from an open file stream into + * the container. Entries of four numbers (Particle ID, x + * position, y position, z position) are searched for. In + * addition, the order in which particles are read is saved + * into an ordering class. If the file cannot be successfully + * read, then the routine causes a fatal error. + * \param[in,out] vo the ordering class to use. + * \param[in] filename the name of the file to open and read + * from. */ + inline void import(particle_order &vo,const char* filename) { + FILE *fp=safe_fopen(filename,"r"); + import(vo,fp); + fclose(fp); + } + void compute_all_cells(); + double sum_cell_volumes(); + /** Dumps particle IDs and positions to a file. + * \param[in] vl the loop class to use. + * \param[in] fp a file handle to write to. */ + template + void draw_particles(c_loop &vl,FILE *fp) { + double *pp; + if(vl.start()) do { + pp=p[vl.ijk]+3*vl.q; + fprintf(fp,"%d %g %g %g\n",id[vl.ijk][vl.q],*pp,pp[1],pp[2]); + } while(vl.inc()); + } + /** Dumps all of the particle IDs and positions to a file. + * \param[in] fp a file handle to write to. */ + inline void draw_particles(FILE *fp=stdout) { + c_loop_all_periodic vl(*this); + draw_particles(vl,fp); + } + /** Dumps all of the particle IDs and positions to a file. + * \param[in] filename the name of the file to write to. */ + inline void draw_particles(const char *filename) { + FILE *fp=safe_fopen(filename,"w"); + draw_particles(fp); + fclose(fp); + } + /** Dumps particle positions in POV-Ray format. + * \param[in] vl the loop class to use. + * \param[in] fp a file handle to write to. */ + template + void draw_particles_pov(c_loop &vl,FILE *fp) { + double *pp; + if(vl.start()) do { + pp=p[vl.ijk]+3*vl.q; + fprintf(fp,"// id %d\nsphere{<%g,%g,%g>,s}\n", + id[vl.ijk][vl.q],*pp,pp[1],pp[2]); + } while(vl.inc()); + } + /** Dumps all particle positions in POV-Ray format. + * \param[in] fp a file handle to write to. */ + inline void draw_particles_pov(FILE *fp=stdout) { + c_loop_all_periodic vl(*this); + draw_particles_pov(vl,fp); + } + /** Dumps all particle positions in POV-Ray format. + * \param[in] filename the name of the file to write to. */ + inline void draw_particles_pov(const char *filename) { + FILE *fp=safe_fopen(filename,"w"); + draw_particles_pov(fp); + fclose(fp); + } + /** Computes Voronoi cells and saves the output in gnuplot + * format. + * \param[in] vl the loop class to use. + * \param[in] fp a file handle to write to. */ + template + void draw_cells_gnuplot(c_loop &vl,FILE *fp) { + voronoicell c(*this);double *pp; + if(vl.start()) do if(compute_cell(c,vl)) { + pp=p[vl.ijk]+ps*vl.q; + c.draw_gnuplot(*pp,pp[1],pp[2],fp); + } while(vl.inc()); + } + /** Computes all Voronoi cells and saves the output in gnuplot + * format. + * \param[in] fp a file handle to write to. */ + inline void draw_cells_gnuplot(FILE *fp=stdout) { + c_loop_all_periodic vl(*this); + draw_cells_gnuplot(vl,fp); + } + /** Compute all Voronoi cells and saves the output in gnuplot + * format. + * \param[in] filename the name of the file to write to. */ + inline void draw_cells_gnuplot(const char *filename) { + FILE *fp=safe_fopen(filename,"w"); + draw_cells_gnuplot(fp); + fclose(fp); + } + /** Computes Voronoi cells and saves the output in POV-Ray + * format. + * \param[in] vl the loop class to use. + * \param[in] fp a file handle to write to. */ + template + void draw_cells_pov(c_loop &vl,FILE *fp) { + voronoicell c(*this);double *pp; + if(vl.start()) do if(compute_cell(c,vl)) { + fprintf(fp,"// cell %d\n",id[vl.ijk][vl.q]); + pp=p[vl.ijk]+ps*vl.q; + c.draw_pov(*pp,pp[1],pp[2],fp); + } while(vl.inc()); + } + /** Computes all Voronoi cells and saves the output in POV-Ray + * format. + * \param[in] fp a file handle to write to. */ + inline void draw_cells_pov(FILE *fp=stdout) { + c_loop_all_periodic vl(*this); + draw_cells_pov(vl,fp); + } + /** Computes all Voronoi cells and saves the output in POV-Ray + * format. + * \param[in] filename the name of the file to write to. */ + inline void draw_cells_pov(const char *filename) { + FILE *fp=safe_fopen(filename,"w"); + draw_cells_pov(fp); + fclose(fp); + } + /** Computes the Voronoi cells and saves customized information + * about them. + * \param[in] vl the loop class to use. + * \param[in] format the custom output string to use. + * \param[in] fp a file handle to write to. */ + template + void print_custom(c_loop &vl,const char *format,FILE *fp) { + int ijk,q;double *pp; + if(contains_neighbor(format)) { + voronoicell_neighbor c(*this); + if(vl.start()) do if(compute_cell(c,vl)) { + ijk=vl.ijk;q=vl.q;pp=p[ijk]+ps*q; + c.output_custom(format,id[ijk][q],*pp,pp[1],pp[2],default_radius,fp); + } while(vl.inc()); + } else { + voronoicell c(*this); + if(vl.start()) do if(compute_cell(c,vl)) { + ijk=vl.ijk;q=vl.q;pp=p[ijk]+ps*q; + c.output_custom(format,id[ijk][q],*pp,pp[1],pp[2],default_radius,fp); + } while(vl.inc()); + } + } + void print_custom(const char *format,FILE *fp=stdout); + void print_custom(const char *format,const char *filename); + bool find_voronoi_cell(double x,double y,double z,double &rx,double &ry,double &rz,int &pid); + /** Computes the Voronoi cell for a particle currently being + * referenced by a loop class. + * \param[out] c a Voronoi cell class in which to store the + * computed cell. + * \param[in] vl the loop class to use. + * \return True if the cell was computed. If the cell cannot be + * computed because it was removed entirely for some reason, + * then the routine returns false. */ + template + inline bool compute_cell(v_cell &c,c_loop &vl) { + return vc.compute_cell(c,vl.ijk,vl.q,vl.i,vl.j,vl.k); + } + /** Computes the Voronoi cell for given particle. + * \param[out] c a Voronoi cell class in which to store the + * computed cell. + * \param[in] ijk the block that the particle is within. + * \param[in] q the index of the particle within the block. + * \return True if the cell was computed. If the cell cannot be + * computed because it was removed entirely for some reason, + * then the routine returns false. */ + template + inline bool compute_cell(v_cell &c,int ijk,int q) { + int k(ijk/(nx*oy)),ijkt(ijk-(nx*oy)*k),j(ijkt/nx),i(ijkt-j*nx); + return vc.compute_cell(c,ijk,q,i,j,k); + } + /** Computes the Voronoi cell for a ghost particle at a given + * location. + * \param[out] c a Voronoi cell class in which to store the + * computed cell. + * \param[in] (x,y,z) the location of the ghost particle. + * \return True if the cell was computed. If the cell cannot be + * computed, if it is removed entirely by a wall or boundary + * condition, then the routine returns false. */ + template + inline bool compute_ghost_cell(v_cell &c,double x,double y,double z) { + int ijk; + put_locate_block(ijk,x,y,z); + double *pp=p[ijk]+3*co[ijk]++; + *(pp++)=x;*(pp++)=y;*(pp++)=z; + bool q=compute_cell(c,ijk,co[ijk]-1); + co[ijk]--; + return q; + } + private: + voro_compute vc; + friend class voro_compute; +}; + +/** \brief Extension of the container_periodic_base class for computing radical + * Voronoi tessellations. + * + * This class is an extension of container_periodic_base that has routines + * specifically for computing the radical Voronoi tessellation that depends + * on the particle radii. */ +class container_periodic_poly : public container_periodic_base, public radius_poly { + public: + container_periodic_poly(double bx_,double bxy_,double by_,double bxz_,double byz_,double bz_, + int nx_,int ny_,int nz_,int init_mem_); + void clear(); + void put(int n,double x,double y,double z,double r); + void put(int n,double x,double y,double z,double r,int &ai,int &aj,int &ak); + void put(particle_order &vo,int n,double x,double y,double z,double r); + void import(FILE *fp=stdin); + void import(particle_order &vo,FILE *fp=stdin); + /** Imports a list of particles from an open file stream into + * the container_poly class. Entries of five numbers (Particle + * ID, x position, y position, z position, radius) are searched + * for. If the file cannot be successfully read, then the + * routine causes a fatal error. + * \param[in] filename the name of the file to open and read + * from. */ + inline void import(const char* filename) { + FILE *fp=safe_fopen(filename,"r"); + import(fp); + fclose(fp); + } + /** Imports a list of particles from an open file stream into + * the container_poly class. Entries of five numbers (Particle + * ID, x position, y position, z position, radius) are searched + * for. In addition, the order in which particles are read is + * saved into an ordering class. If the file cannot be + * successfully read, then the routine causes a fatal error. + * \param[in,out] vo the ordering class to use. + * \param[in] filename the name of the file to open and read + * from. */ + inline void import(particle_order &vo,const char* filename) { + FILE *fp=safe_fopen(filename,"r"); + import(vo,fp); + fclose(fp); + } + void compute_all_cells(); + double sum_cell_volumes(); + /** Dumps particle IDs, positions and radii to a file. + * \param[in] vl the loop class to use. + * \param[in] fp a file handle to write to. */ + template + void draw_particles(c_loop &vl,FILE *fp) { + double *pp; + if(vl.start()) do { + pp=p[vl.ijk]+4*vl.q; + fprintf(fp,"%d %g %g %g %g\n",id[vl.ijk][vl.q],*pp,pp[1],pp[2],pp[3]); + } while(vl.inc()); + } + /** Dumps all of the particle IDs, positions and radii to a + * file. + * \param[in] fp a file handle to write to. */ + inline void draw_particles(FILE *fp=stdout) { + c_loop_all_periodic vl(*this); + draw_particles(vl,fp); + } + /** Dumps all of the particle IDs, positions and radii to a + * file. + * \param[in] filename the name of the file to write to. */ + inline void draw_particles(const char *filename) { + FILE *fp=safe_fopen(filename,"w"); + draw_particles(fp); + fclose(fp); + } + /** Dumps particle positions in POV-Ray format. + * \param[in] vl the loop class to use. + * \param[in] fp a file handle to write to. */ + template + void draw_particles_pov(c_loop &vl,FILE *fp) { + double *pp; + if(vl.start()) do { + pp=p[vl.ijk]+4*vl.q; + fprintf(fp,"// id %d\nsphere{<%g,%g,%g>,%g}\n", + id[vl.ijk][vl.q],*pp,pp[1],pp[2],pp[3]); + } while(vl.inc()); + } + /** Dumps all the particle positions in POV-Ray format. + * \param[in] fp a file handle to write to. */ + inline void draw_particles_pov(FILE *fp=stdout) { + c_loop_all_periodic vl(*this); + draw_particles_pov(vl,fp); + } + /** Dumps all the particle positions in POV-Ray format. + * \param[in] filename the name of the file to write to. */ + inline void draw_particles_pov(const char *filename) { + FILE *fp(safe_fopen(filename,"w")); + draw_particles_pov(fp); + fclose(fp); + } + /** Computes Voronoi cells and saves the output in gnuplot + * format. + * \param[in] vl the loop class to use. + * \param[in] fp a file handle to write to. */ + template + void draw_cells_gnuplot(c_loop &vl,FILE *fp) { + voronoicell c(*this);double *pp; + if(vl.start()) do if(compute_cell(c,vl)) { + pp=p[vl.ijk]+ps*vl.q; + c.draw_gnuplot(*pp,pp[1],pp[2],fp); + } while(vl.inc()); + } + /** Compute all Voronoi cells and saves the output in gnuplot + * format. + * \param[in] fp a file handle to write to. */ + inline void draw_cells_gnuplot(FILE *fp=stdout) { + c_loop_all_periodic vl(*this); + draw_cells_gnuplot(vl,fp); + } + /** Compute all Voronoi cells and saves the output in gnuplot + * format. + * \param[in] filename the name of the file to write to. */ + inline void draw_cells_gnuplot(const char *filename) { + FILE *fp(safe_fopen(filename,"w")); + draw_cells_gnuplot(fp); + fclose(fp); + } + /** Computes Voronoi cells and saves the output in POV-Ray + * format. + * \param[in] vl the loop class to use. + * \param[in] fp a file handle to write to. */ + template + void draw_cells_pov(c_loop &vl,FILE *fp) { + voronoicell c(*this);double *pp; + if(vl.start()) do if(compute_cell(c,vl)) { + fprintf(fp,"// cell %d\n",id[vl.ijk][vl.q]); + pp=p[vl.ijk]+ps*vl.q; + c.draw_pov(*pp,pp[1],pp[2],fp); + } while(vl.inc()); + } + /** Computes all Voronoi cells and saves the output in POV-Ray + * format. + * \param[in] fp a file handle to write to. */ + inline void draw_cells_pov(FILE *fp=stdout) { + c_loop_all_periodic vl(*this); + draw_cells_pov(vl,fp); + } + /** Computes all Voronoi cells and saves the output in POV-Ray + * format. + * \param[in] filename the name of the file to write to. */ + inline void draw_cells_pov(const char *filename) { + FILE *fp(safe_fopen(filename,"w")); + draw_cells_pov(fp); + fclose(fp); + } + /** Computes the Voronoi cells and saves customized information + * about them. + * \param[in] vl the loop class to use. + * \param[in] format the custom output string to use. + * \param[in] fp a file handle to write to. */ + template + void print_custom(c_loop &vl,const char *format,FILE *fp) { + int ijk,q;double *pp; + if(contains_neighbor(format)) { + voronoicell_neighbor c(*this); + if(vl.start()) do if(compute_cell(c,vl)) { + ijk=vl.ijk;q=vl.q;pp=p[ijk]+ps*q; + c.output_custom(format,id[ijk][q],*pp,pp[1],pp[2],pp[3],fp); + } while(vl.inc()); + } else { + voronoicell c(*this); + if(vl.start()) do if(compute_cell(c,vl)) { + ijk=vl.ijk;q=vl.q;pp=p[ijk]+ps*q; + c.output_custom(format,id[ijk][q],*pp,pp[1],pp[2],pp[3],fp); + } while(vl.inc()); + } + } + /** Computes the Voronoi cell for a particle currently being + * referenced by a loop class. + * \param[out] c a Voronoi cell class in which to store the + * computed cell. + * \param[in] vl the loop class to use. + * \return True if the cell was computed. If the cell cannot be + * computed because it was removed entirely for some reason, + * then the routine returns false. */ + template + inline bool compute_cell(v_cell &c,c_loop &vl) { + return vc.compute_cell(c,vl.ijk,vl.q,vl.i,vl.j,vl.k); + } + /** Computes the Voronoi cell for given particle. + * \param[out] c a Voronoi cell class in which to store the + * computed cell. + * \param[in] ijk the block that the particle is within. + * \param[in] q the index of the particle within the block. + * \return True if the cell was computed. If the cell cannot be + * computed because it was removed entirely for some reason, + * then the routine returns false. */ + template + inline bool compute_cell(v_cell &c,int ijk,int q) { + int k(ijk/(nx*oy)),ijkt(ijk-(nx*oy)*k),j(ijkt/nx),i(ijkt-j*nx); + return vc.compute_cell(c,ijk,q,i,j,k); + } + /** Computes the Voronoi cell for a ghost particle at a given + * location. + * \param[out] c a Voronoi cell class in which to store the + * computed cell. + * \param[in] (x,y,z) the location of the ghost particle. + * \param[in] r the radius of the ghost particle. + * \return True if the cell was computed. If the cell cannot be + * computed, if it is removed entirely by a wall or boundary + * condition, then the routine returns false. */ + template + inline bool compute_ghost_cell(v_cell &c,double x,double y,double z,double r) { + int ijk; + put_locate_block(ijk,x,y,z); + double *pp=p[ijk]+4*co[ijk]++,tm=max_radius; + *(pp++)=x;*(pp++)=y;*(pp++)=z;*pp=r; + if(r>max_radius) max_radius=r; + bool q=compute_cell(c,ijk,co[ijk]-1); + co[ijk]--;max_radius=tm; + return q; + } + void print_custom(const char *format,FILE *fp=stdout); + void print_custom(const char *format,const char *filename); + bool find_voronoi_cell(double x,double y,double z,double &rx,double &ry,double &rz,int &pid); + private: + voro_compute vc; + friend class voro_compute; +}; + +} + +#endif diff --git a/src/third_party/voro/src/pre_container.cc b/src/third_party/voro/src/pre_container.cc new file mode 100644 index 000000000..9aa0eb335 --- /dev/null +++ b/src/third_party/voro/src/pre_container.cc @@ -0,0 +1,236 @@ +// Voro++, a 3D cell-based Voronoi library +// +// Author : Chris H. Rycroft (Harvard University / LBL) +// Email : chr@alum.mit.edu +// Date : August 30th 2011 + +/** \file pre_container.cc + * \brief Function implementations for the pre_container and related classes. + */ + +#include + +#include "config.hh" +#include "pre_container.hh" + +namespace voro { + +/** The class constructor sets up the geometry of container, initializing the + * minimum and maximum coordinates in each direction. It allocates an initial + * chunk into which to store particle information. + * \param[in] (ax_,bx_) the minimum and maximum x coordinates. + * \param[in] (ay_,by_) the minimum and maximum y coordinates. + * \param[in] (az_,bz_) the minimum and maximum z coordinates. + * \param[in] (xperiodic_,yperiodic_,zperiodic_ ) flags setting whether the + * container is periodic in each + * coordinate direction. + * \param[in] ps_ the number of floating point entries to store for each + * particle. */ +pre_container_base::pre_container_base(double ax_,double bx_,double ay_,double by_,double az_,double bz_, + bool xperiodic_,bool yperiodic_,bool zperiodic_,int ps_) : + ax(ax_), bx(bx_), ay(ay_), by(by_), az(az_), bz(bz_), + xperiodic(xperiodic_), yperiodic(yperiodic_), zperiodic(zperiodic_), ps(ps_), + index_sz(init_chunk_size), pre_id(new int*[index_sz]), end_id(pre_id), + pre_p(new double*[index_sz]), end_p(pre_p) { + ch_id=*end_id=new int[pre_container_chunk_size]; + l_id=end_id+index_sz;e_id=ch_id+pre_container_chunk_size; + ch_p=*end_p=new double[ps*pre_container_chunk_size]; +} + +/** The destructor frees the dynamically allocated memory. */ +pre_container_base::~pre_container_base() { + delete [] *end_p; + delete [] *end_id; + while (end_id!=pre_id) { + end_p--; + delete [] *end_p; + end_id--; + delete [] *end_id; + } + delete [] pre_p; + delete [] pre_id; +} + +/** Makes a guess at the optimal grid of blocks to use, computing in + * a way that + * \param[out] (nx,ny,nz) the number of blocks to use. */ +void pre_container_base::guess_optimal(int &nx,int &ny,int &nz) { + double dx=bx-ax,dy=by-ay,dz=bz-az; + double ilscale=pow(total_particles()/(optimal_particles*dx*dy*dz),1/3.0); + nx=int(dx*ilscale+1); + ny=int(dy*ilscale+1); + nz=int(dz*ilscale+1); +} + +/** Stores a particle ID and position, allocating a new memory chunk if + * necessary. For coordinate directions in which the container is not periodic, + * the routine checks to make sure that the particle is within the container + * bounds. If the particle is out of bounds, it is not stored. + * \param[in] n the numerical ID of the inserted particle. + * \param[in] (x,y,z) the position vector of the inserted particle. */ +void pre_container::put(int n,double x,double y,double z) { + if((xperiodic||(x>=ax&&x<=bx))&&(yperiodic||(y>=ay&&y<=by))&&(zperiodic||(z>=az&&z<=bz))) { + if(ch_id==e_id) new_chunk(); + *(ch_id++)=n; + *(ch_p++)=x;*(ch_p++)=y;*(ch_p++)=z; + } +#if VOROPP_REPORT_OUT_OF_BOUNDS ==1 + else fprintf(stderr,"Out of bounds: (x,y,z)=(%g,%g,%g)\n",x,y,z); +#endif +} + +/** Stores a particle ID and position, allocating a new memory chunk if necessary. + * \param[in] n the numerical ID of the inserted particle. + * \param[in] (x,y,z) the position vector of the inserted particle. + * \param[in] r the radius of the particle. */ +void pre_container_poly::put(int n,double x,double y,double z,double r) { + if((xperiodic||(x>=ax&&x<=bx))&&(yperiodic||(y>=ay&&y<=by))&&(zperiodic||(z>=az&&z<=bz))) { + if(ch_id==e_id) new_chunk(); + *(ch_id++)=n; + *(ch_p++)=x;*(ch_p++)=y;*(ch_p++)=z;*(ch_p++)=r; + } +#if VOROPP_REPORT_OUT_OF_BOUNDS ==1 + else fprintf(stderr,"Out of bounds: (x,y,z)=(%g,%g,%g)\n",x,y,z); +#endif +} + +/** Transfers the particles stored within the class to a container class. + * \param[in] con the container class to transfer to. */ +void pre_container::setup(container &con) { + int **c_id=pre_id,*idp,*ide,n; + double **c_p=pre_p,*pp,x,y,z; + while(c_idmax_chunk_size) + voro_fatal_error("Absolute memory limit on chunk index reached",VOROPP_MEMORY_ERROR); +#if VOROPP_VERBOSE >=2 + fprintf(stderr,"Pre-container chunk index scaled up to %d\n",index_sz); +#endif + int **n_id=new int*[index_sz],**p_id=n_id,**c_id=pre_id; + double **n_p=new double*[index_sz],**p_p=n_p,**c_p=pre_p; + while(c_id + +#include "c_loops.hh" +#include "container.hh" + +namespace voro { + +/** \brief A class for storing an arbitrary number of particles, prior to setting + * up a container geometry. + * + * The pre_container_base class can dynamically import and store an arbitrary + * number of particles. Once the particles have been read in, an appropriate + * container class can be set up with the optimal grid size, and the particles + * can be transferred. + * + * The pre_container_base class is not intended for direct use, but forms the + * base of the pre_container and pre_container_poly classes, that add routines + * depending on whether particle radii need to be tracked or not. */ +class pre_container_base { + public: + /** The minimum x coordinate of the container. */ + const double ax; + /** The maximum x coordinate of the container. */ + const double bx; + /** The minimum y coordinate of the container. */ + const double ay; + /** The maximum y coordinate of the container. */ + const double by; + /** The minimum z coordinate of the container. */ + const double az; + /** The maximum z coordinate of the container. */ + const double bz; + /** A boolean value that determines if the x coordinate in + * periodic or not. */ + const bool xperiodic; + /** A boolean value that determines if the y coordinate in + * periodic or not. */ + const bool yperiodic; + /** A boolean value that determines if the z coordinate in + * periodic or not. */ + const bool zperiodic; + void guess_optimal(int &nx,int &ny,int &nz); + pre_container_base(double ax_,double bx_,double ay_,double by_,double az_,double bz_,bool xperiodic_,bool yperiodic_,bool zperiodic_,int ps_); + ~pre_container_base(); + /** Calculates and returns the total number of particles stored + * within the class. + * \return The number of particles. */ + inline int total_particles() { + return (end_id-pre_id)*pre_container_chunk_size+(ch_id-*end_id); + } + protected: + /** The number of doubles associated with a single particle + * (three for the standard container, four when radius + * information is stored). */ + const int ps; + void new_chunk(); + void extend_chunk_index(); + /** The size of the chunk index. */ + int index_sz; + /** A pointer to the chunk index to store the integer particle + * IDs. */ + int **pre_id; + /** A pointer to the last allocated integer ID chunk. */ + int **end_id; + /** A pointer to the end of the integer ID chunk index, used to + * determine when the chunk index is full. */ + int **l_id; + /** A pointer to the next available slot on the current + * particle ID chunk. */ + int *ch_id; + /** A pointer to the end of the current integer chunk. */ + int *e_id; + /** A pointer to the chunk index to store the floating point + * information associated with particles. */ + double **pre_p; + /** A pointer to the last allocated chunk of floating point + * information. */ + double **end_p; + /** A pointer to the next available slot on the current + * floating point chunk. */ + double *ch_p; +}; + +/** \brief A class for storing an arbitrary number of particles without radius + * information, prior to setting up a container geometry. + * + * The pre_container class is an extension of the pre_container_base class for + * cases when no particle radius information is available. */ +class pre_container : public pre_container_base { + public: + /** The class constructor sets up the geometry of container, + * initializing the minimum and maximum coordinates in each + * direction. + * \param[in] (ax_,bx_) the minimum and maximum x coordinates. + * \param[in] (ay_,by_) the minimum and maximum y coordinates. + * \param[in] (az_,bz_) the minimum and maximum z coordinates. + * \param[in] (xperiodic_,yperiodic_,zperiodic_ ) flags setting whether the + * container is periodic in + * each coordinate direction. */ + pre_container(double ax_,double bx_,double ay_,double by_,double az_,double bz_, + bool xperiodic_,bool yperiodic_,bool zperiodic_) + : pre_container_base(ax_,bx_,ay_,by_,az_,bz_,xperiodic_,yperiodic_,zperiodic_,3) {}; + void put(int n,double x,double y,double z); + void import(FILE *fp=stdin); + /** Imports particles from a file. + * \param[in] filename the name of the file to read from. */ + inline void import(const char* filename) { + FILE *fp=safe_fopen(filename,"r"); + import(fp); + fclose(fp); + } + void setup(container &con); + void setup(particle_order &vo,container &con); +}; + +/** \brief A class for storing an arbitrary number of particles with radius + * information, prior to setting up a container geometry. + * + * The pre_container_poly class is an extension of the pre_container_base class + * for cases when particle radius information is available. */ +class pre_container_poly : public pre_container_base { + public: + /** The class constructor sets up the geometry of container, + * initializing the minimum and maximum coordinates in each + * direction. + * \param[in] (ax_,bx_) the minimum and maximum x coordinates. + * \param[in] (ay_,by_) the minimum and maximum y coordinates. + * \param[in] (az_,bz_) the minimum and maximum z coordinates. + * \param[in] (xperiodic_,yperiodic_,zperiodic_ ) flags setting whether the + * container is periodic in + * each coordinate direction. */ + pre_container_poly(double ax_,double bx_,double ay_,double by_,double az_,double bz_, + bool xperiodic_,bool yperiodic_,bool zperiodic_) + : pre_container_base(ax_,bx_,ay_,by_,az_,bz_,xperiodic_,yperiodic_,zperiodic_,4) {}; + void put(int n,double x,double y,double z,double r); + void import(FILE *fp=stdin); + /** Imports particles from a file. + * \param[in] filename the name of the file to read from. */ + inline void import(const char* filename) { + FILE *fp=safe_fopen(filename,"r"); + import(fp); + fclose(fp); + } + void setup(container_poly &con); + void setup(particle_order &vo,container_poly &con); +}; + +} + +#endif diff --git a/src/third_party/voro/src/rad_option.hh b/src/third_party/voro/src/rad_option.hh new file mode 100644 index 000000000..40dc490ed --- /dev/null +++ b/src/third_party/voro/src/rad_option.hh @@ -0,0 +1,158 @@ +// Voro++, a 3D cell-based Voronoi library +// +// Author : Chris H. Rycroft (Harvard University / LBL) +// Email : chr@alum.mit.edu +// Date : August 30th 2011 + +/** \file rad_option.hh + * \brief Header file for the classes encapsulating functionality for the + * regular and radical Voronoi tessellations. */ + +#ifndef VOROPP_RAD_OPTION_HH +#define VOROPP_RAD_OPTION_HH + +#include + +namespace voro { + +/** \brief Class containing all of the routines that are specific to computing + * the regular Voronoi tessellation. + * + * The container and container_periodic classes are derived from this class, + * and during the Voronoi cell computation, these routines are used to create + * the regular Voronoi tessellation. */ +class radius_mono { + protected: + /** This is called prior to computing a Voronoi cell for a + * given particle to initialize any required constants. + * \param[in] ijk the block that the particle is within. + * \param[in] s the index of the particle within the block. */ + inline void r_init(int ijk,int s) {} + /** Sets a required constant to be used when carrying out a + * plane bounds check. */ + inline void r_prime(double rv) {} + /** Carries out a radius bounds check. + * \param[in] crs the radius squared to be tested. + * \param[in] mrs the current maximum distance to a Voronoi + * vertex multiplied by two. + * \return True if particles at this radius could not possibly + * cut the cell, false otherwise. */ + inline bool r_ctest(double crs,double mrs) {return crs>mrs;} + /** Scales a plane displacement during a plane bounds check. + * \param[in] lrs the plane displacement. + * \return The scaled value. */ + inline double r_cutoff(double lrs) {return lrs;} + /** Adds the maximum radius squared to a given value. + * \param[in] rs the value to consider. + * \return The value with the radius squared added. */ + inline double r_max_add(double rs) {return rs;} + /** Subtracts the radius squared of a particle from a given + * value. + * \param[in] rs the value to consider. + * \param[in] ijk the block that the particle is within. + * \param[in] q the index of the particle within the block. + * \return The value with the radius squared subtracted. */ + inline double r_current_sub(double rs,int ijk,int q) {return rs;} + /** Scales a plane displacement prior to use in the plane cutting + * algorithm. + * \param[in] rs the initial plane displacement. + * \param[in] ijk the block that the particle is within. + * \param[in] q the index of the particle within the block. + * \return The scaled plane displacement. */ + inline double r_scale(double rs,int ijk,int q) {return rs;} + /** Scales a plane displacement prior to use in the plane + * cutting algorithm, and also checks if it could possibly cut + * the cell. + * \param[in,out] rs the plane displacement to be scaled. + * \param[in] mrs the current maximum distance to a Voronoi + * vertex multiplied by two. + * \param[in] ijk the block that the particle is within. + * \param[in] q the index of the particle within the block. + * \return True if the cell could possibly cut the cell, false + * otherwise. */ + inline bool r_scale_check(double &rs,double mrs,int ijk,int q) {return rssqrt(mrs*crs);} + /** Scales a plane displacement during a plane bounds check. + * \param[in] lrs the plane displacement. + * \return The scaled value. */ + inline double r_cutoff(double lrs) {return lrs*r_val;} + /** Adds the maximum radius squared to a given value. + * \param[in] rs the value to consider. + * \return The value with the radius squared added. */ + inline double r_max_add(double rs) {return rs+max_radius*max_radius;} + /** Subtracts the radius squared of a particle from a given + * value. + * \param[in] rs the value to consider. + * \param[in] ijk the block that the particle is within. + * \param[in] q the index of the particle within the block. + * \return The value with the radius squared subtracted. */ + inline double r_current_sub(double rs,int ijk,int q) { + return rs-ppr[ijk][4*q+3]*ppr[ijk][4*q+3]; + } + /** Scales a plane displacement prior to use in the plane cutting + * algorithm. + * \param[in] rs the initial plane displacement. + * \param[in] ijk the block that the particle is within. + * \param[in] q the index of the particle within the block. + * \return The scaled plane displacement. */ + inline double r_scale(double rs,int ijk,int q) { + return rs+r_rad-ppr[ijk][4*q+3]*ppr[ijk][4*q+3]; + } + /** Scales a plane displacement prior to use in the plane + * cutting algorithm, and also checks if it could possibly cut + * the cell. + * \param[in,out] rs the plane displacement to be scaled. + * \param[in] mrs the current maximum distance to a Voronoi + * vertex multiplied by two. + * \param[in] ijk the block that the particle is within. + * \param[in] q the index of the particle within the block. + * \return True if the cell could possibly cut the cell, false + * otherwise. */ + inline bool r_scale_check(double &rs,double mrs,int ijk,int q) { + double trs=rs; + rs+=r_rad-ppr[ijk][4*q+3]*ppr[ijk][4*q+3]; + return rs +#include + +#include "unitcell.hh" +#include "cell.hh" + +namespace voro { + +/** Initializes the unit cell class for a particular non-orthogonal periodic + * geometry, corresponding to a parallelepiped with sides given by three + * vectors. The class constructs the unit Voronoi cell corresponding to this + * geometry. + * \param[in] (bx_) The x coordinate of the first unit vector. + * \param[in] (bxy_,by_) The x and y coordinates of the second unit vector. + * \param[in] (bxz_,byz_,bz_) The x, y, and z coordinates of the third unit + * vector. */ +unitcell::unitcell(double bx_,double bxy_,double by_,double bxz_,double byz_,double bz_) + : bx(bx_), bxy(bxy_), by(by_), bxz(bxz_), byz(byz_), bz(bz_), + unit_voro(max_unit_voro_shells*max_unit_voro_shells*4*(bx*bx+by*by+bz*bz)) { + int i,j,l=1; + + // Initialize the Voronoi cell to be a very large rectangular box + const double ucx=max_unit_voro_shells*bx,ucy=max_unit_voro_shells*by,ucz=max_unit_voro_shells*bz; + unit_voro.init(-ucx,ucx,-ucy,ucy,-ucz,ucz); + + // Repeatedly cut the cell by shells of periodic image particles + while(l<2*max_unit_voro_shells) { + + // Check to see if any of the planes from the current shell + // will cut the cell + if(unit_voro_intersect(l)) { + + // If they do, apply the plane cuts from the current + // shell + unit_voro_apply(l,0,0); + for(i=1;il can't cut + // a cell lying within the paraboloid + // z<=(l*l-x*x-y*y)/(2*l). It is always a tighter bound + // than the one based on computing the maximum radius + // of a Voronoi cell vertex. + max_uv_y=max_uv_z=0; + double y,z,q,*pts=unit_voro.pts,*pp=pts; + while(ppmax_uv_y) max_uv_y=y+q; + if(z+q>max_uv_z) max_uv_z=z+q; + } + max_uv_z*=0.5; + max_uv_y*=0.5; + return; + } + l++; + } + + // If the routine makes it here, then the unit cell still hasn't been + // completely bounded by the plane cuts. Give the memory error code, + // because this is mainly a case of hitting a safe limit, than any + // inherent problem. + voro_fatal_error("Periodic cell computation failed",VOROPP_MEMORY_ERROR); +} + +/** Applies a pair of opposing plane cuts from a periodic image point + * to the unit Voronoi cell. + * \param[in] (i,j,k) the index of the periodic image to consider. */ +inline void unitcell::unit_voro_apply(int i,int j,int k) { + double x=i*bx+j*bxy+k*bxz,y=j*by+k*byz,z=k*bz; + unit_voro.plane(x,y,z); + unit_voro.plane(-x,-y,-z); +} + +/** Calculates whether the unit Voronoi cell intersects a given periodic image + * of the domain. + * \param[in] (dx,dy,dz) the displacement of the periodic image. + * \param[out] vol the proportion of the unit cell volume within this image, + * only computed in the case that the two intersect. + * \return True if they intersect, false otherwise. */ +bool unitcell::intersects_image(double dx,double dy,double dz,double &vol) { + const double bxinv=1/bx,byinv=1/by,bzinv=1/bz,ivol=bxinv*byinv*bzinv; + voronoicell c; + c=unit_voro; + dx*=2;dy*=2;dz*=2; + if(!c.plane(0,0,bzinv,dz+1)) return false; + if(!c.plane(0,0,-bzinv,-dz+1)) return false; + if(!c.plane(0,byinv,-byz*byinv*bzinv,dy+1)) return false; + if(!c.plane(0,-byinv,byz*byinv*bzinv,-dy+1)) return false; + if(!c.plane(bxinv,-bxy*bxinv*byinv,(bxy*byz-by*bxz)*ivol,dx+1)) return false; + if(!c.plane(-bxinv,bxy*bxinv*byinv,(-bxy*byz+by*bxz)*ivol,-dx+1)) return false; + vol=c.volume()*ivol; + return true; +} + +/** Computes a list of periodic domain images that intersect the unit Voronoi cell. + * \param[out] vi a vector containing triplets (i,j,k) corresponding to domain + * images that intersect the unit Voronoi cell, when it is + * centered in the middle of the primary domain. + * \param[out] vd a vector containing the fraction of the Voronoi cell volume + * within each corresponding image listed in vi. */ +void unitcell::images(std::vector &vi,std::vector &vd) { + const int ms2=max_unit_voro_shells*2+1,mss=ms2*ms2*ms2; + bool *a=new bool[mss],*ac=a+max_unit_voro_shells*(1+ms2*(1+ms2)),*ap=a; + int i,j,k; + double vol; + + // Initialize mask + while(ap q; + q.push(0);q.push(0);q.push(0); + + while(!q.empty()) { + + // Read the next entry on the queue + i=q.front();q.pop(); + j=q.front();q.pop(); + k=q.front();q.pop(); + + // Check intersection of this image + if(intersects_image(i,j,k,vol)) { + + // Add this entry to the output vectors + vi.push_back(i); + vi.push_back(j); + vi.push_back(k); + vd.push_back(vol); + + // Add neighbors to the queue if they have not been + // tested + ap=ac+i+ms2*(j+ms2*k); + if(k>-max_unit_voro_shells&&*(ap-ms2*ms2)) {q.push(i);q.push(j);q.push(k-1);*(ap-ms2*ms2)=false;} + if(j>-max_unit_voro_shells&&*(ap-ms2)) {q.push(i);q.push(j-1);q.push(k);*(ap-ms2)=false;} + if(i>-max_unit_voro_shells&&*(ap-1)) {q.push(i-1);q.push(j);q.push(k);*(ap-1)=false;} + if(i,<%g,0,0>,rr}\n" + "cylinder{<%g,%g,0>,<%g,%g,0>,rr}\n",bx,bxy,by,bx+bxy,by); + fprintf(fp,"cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n" + "cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n",bxz,byz,bz,bx+bxz,byz,bz,bxy+bxz,by+byz,bz,bx+bxy+bxz,by+byz,bz); + fprintf(fp,"cylinder{<0,0,0>,<%g,%g,0>,rr}\n" + "cylinder{<%g,0,0>,<%g,%g,0>,rr}\n",bxy,by,bx,bx+bxy,by); + fprintf(fp,"cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n" + "cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n",bxz,byz,bz,bxy+bxz,by+byz,bz,bx+bxz,byz,bz,bx+bxy+bxz,by+byz,bz); + fprintf(fp,"cylinder{<0,0,0>,<%g,%g,%g>,rr}\n" + "cylinder{<%g,0,0>,<%g,%g,%g>,rr}\n",bxz,byz,bz,bx,bx+bxz,byz,bz); + fprintf(fp,"cylinder{<%g,%g,0>,<%g,%g,%g>,rr}\n" + "cylinder{<%g,%g,0>,<%g,%g,%g>,rr}\n",bxy,by,bxy+bxz,by+byz,bz,bx+bxy,by,bx+bxy+bxz,by+byz,bz); + fprintf(fp,"sphere{<0,0,0>,rr}\nsphere{<%g,0,0>,rr}\n" + "sphere{<%g,%g,0>,rr}\nsphere{<%g,%g,0>,rr}\n",bx,bxy,by,bx+bxy,by); + fprintf(fp,"sphere{<%g,%g,%g>,rr}\nsphere{<%g,%g,%g>,rr}\n" + "sphere{<%g,%g,%g>,rr}\nsphere{<%g,%g,%g>,rr}\n",bxz,byz,bz,bx+bxz,byz,bz,bxy+bxz,by+byz,bz,bx+bxy+bxz,by+byz,bz); +} + +} diff --git a/src/third_party/voro/src/unitcell.hh b/src/third_party/voro/src/unitcell.hh new file mode 100644 index 000000000..0907b899d --- /dev/null +++ b/src/third_party/voro/src/unitcell.hh @@ -0,0 +1,79 @@ +// Voro++, a 3D cell-based Voronoi library +// +// Author : Chris H. Rycroft (Harvard University / LBL) +// Email : chr@alum.mit.edu +// Date : August 30th 2011 + +/** \file unitcell.hh + * \brief Header file for the unitcell class. */ + +#ifndef VOROPP_UNITCELL_HH +#define VOROPP_UNITCELL_HH + +#include + +#include "config.hh" +#include "cell.hh" + +namespace voro { + +/** \brief Class for computation of the unit Voronoi cell associated with + * a 3D non-rectangular periodic domain. */ +class unitcell { + public: + /** The x coordinate of the first vector defining the periodic + * domain. */ + const double bx; + /** The x coordinate of the second vector defining the periodic + * domain. */ + const double bxy; + /** The y coordinate of the second vector defining the periodic + * domain. */ + const double by; + /** The x coordinate of the third vector defining the periodic + * domain. */ + const double bxz; + /** The y coordinate of the third vector defining the periodic + * domain. */ + const double byz; + /** The z coordinate of the third vector defining the periodic + * domain. */ + const double bz; + /** The computed unit Voronoi cell corresponding the given + * 3D non-rectangular periodic domain geometry. */ + voronoicell unit_voro; + unitcell(double bx_,double bxy_,double by_,double bxz_,double byz_,double bz_); + /** Draws an outline of the domain in Gnuplot format. + * \param[in] filename the filename to write to. */ + inline void draw_domain_gnuplot(const char* filename) { + FILE *fp(safe_fopen(filename,"w")); + draw_domain_gnuplot(fp); + fclose(fp); + } + void draw_domain_gnuplot(FILE *fp=stdout); + /** Draws an outline of the domain in Gnuplot format. + * \param[in] filename the filename to write to. */ + inline void draw_domain_pov(const char* filename) { + FILE *fp(safe_fopen(filename,"w")); + draw_domain_pov(fp); + fclose(fp); + } + void draw_domain_pov(FILE *fp=stdout); + bool intersects_image(double dx,double dy,double dz,double &vol); + void images(std::vector &vi,std::vector &vd); + protected: + /** The maximum y-coordinate that could possibly cut the + * computed unit Voronoi cell. */ + double max_uv_y; + /** The maximum z-coordinate that could possibly cut the + * computed unit Voronoi cell. */ + double max_uv_z; + private: + inline void unit_voro_apply(int i,int j,int k); + bool unit_voro_intersect(int l); + inline bool unit_voro_test(int i,int j,int k); +}; + +} + +#endif diff --git a/src/third_party/voro/src/v_base.cc b/src/third_party/voro/src/v_base.cc new file mode 100644 index 000000000..143680065 --- /dev/null +++ b/src/third_party/voro/src/v_base.cc @@ -0,0 +1,118 @@ +// Voro++, a 3D cell-based Voronoi library +// +// Author : Chris H. Rycroft (Harvard University / LBL) +// Email : chr@alum.mit.edu +// Date : August 30th 2011 + +/** \file v_base.cc + * \brief Function implementations for the base Voronoi container class. */ + +#include "v_base.hh" +#include "config.hh" + +namespace voro { + +/** This function is called during container construction. The routine scans + * all of the worklists in the wl[] array. For a given worklist of blocks + * labeled \f$w_1\f$ to \f$w_n\f$, it computes a sequence \f$r_0\f$ to + * \f$r_n\f$ so that $r_i$ is the minimum distance to all the blocks + * \f$w_{j}\f$ where \f$j>i\f$ and all blocks outside the worklist. The values + * of \f$r_n\f$ is calculated first, as the minimum distance to any block in + * the shell surrounding the worklist. The \f$r_i\f$ are then computed in + * reverse order by considering the distance to \f$w_{i+1}\f$. */ +voro_base::voro_base(int nx_,int ny_,int nz_,double boxx_,double boxy_,double boxz_) : + nx(nx_), ny(ny_), nz(nz_), nxy(nx_*ny_), nxyz(nxy*nz_), boxx(boxx_), boxy(boxy_), boxz(boxz_), + xsp(1/boxx_), ysp(1/boxy_), zsp(1/boxz_), mrad(new double[wl_hgridcu*wl_seq_length]) { + const unsigned int b1=1<<21,b2=1<<22,b3=1<<24,b4=1<<25,b5=1<<27,b6=1<<28; + const double xstep=boxx/wl_fgrid,ystep=boxy/wl_fgrid,zstep=boxz/wl_fgrid; + int i,j,k,lx,ly,lz,q; + unsigned int f,*e=const_cast (wl); + double xlo,ylo,zlo,xhi,yhi,zhi,minr,*radp=mrad; + for(zlo=0,zhi=zstep,lz=0;lz>7&127)-64; + k=(f>>14&127)-64; + if((f&b2)==b2) { + compute_minimum(minr,xlo,xhi,ylo,yhi,zlo,zhi,i-1,j,k); + if((f&b1)==0) compute_minimum(minr,xlo,xhi,ylo,yhi,zlo,zhi,i+1,j,k); + } else if((f&b1)==b1) compute_minimum(minr,xlo,xhi,ylo,yhi,zlo,zhi,i+1,j,k); + if((f&b4)==b4) { + compute_minimum(minr,xlo,xhi,ylo,yhi,zlo,zhi,i,j-1,k); + if((f&b3)==0) compute_minimum(minr,xlo,xhi,ylo,yhi,zlo,zhi,i,j+1,k); + } else if((f&b3)==b3) compute_minimum(minr,xlo,xhi,ylo,yhi,zlo,zhi,i,j+1,k); + if((f&b6)==b6) { + compute_minimum(minr,xlo,xhi,ylo,yhi,zlo,zhi,i,j,k-1); + if((f&b5)==0) compute_minimum(minr,xlo,xhi,ylo,yhi,zlo,zhi,i,j,k+1); + } else if((f&b5)==b5) compute_minimum(minr,xlo,xhi,ylo,yhi,zlo,zhi,i,j,k+1); + } + q--; + while(q>0) { + radp[q]=minr; + f=e[q]; + i=(f&127)-64; + j=(f>>7&127)-64; + k=(f>>14&127)-64; + compute_minimum(minr,xlo,xhi,ylo,yhi,zlo,zhi,i,j,k); + q--; + } + *radp=minr; + e+=wl_seq_length; + radp+=wl_seq_length; + } + } + } +} + +/** Computes the minimum distance from a subregion to a given block. If this distance + * is smaller than the value of minr, then it passes + * \param[in,out] minr a pointer to the current minimum distance. If the distance + * computed in this function is smaller, then this distance is + * set to the new one. + * \param[out] (xlo,ylo,zlo) the lower coordinates of the subregion being + * considered. + * \param[out] (xhi,yhi,zhi) the upper coordinates of the subregion being + * considered. + * \param[in] (ti,tj,tk) the coordinates of the block. */ +void voro_base::compute_minimum(double &minr,double &xlo,double &xhi,double &ylo,double &yhi,double &zlo,double &zhi,int ti,int tj,int tk) { + double radsq,temp; + if(ti>0) {temp=boxx*ti-xhi;radsq=temp*temp;} + else if(ti<0) {temp=xlo-boxx*(1+ti);radsq=temp*temp;} + else radsq=0; + + if(tj>0) {temp=boxy*tj-yhi;radsq+=temp*temp;} + else if(tj<0) {temp=ylo-boxy*(1+tj);radsq+=temp*temp;} + + if(tk>0) {temp=boxz*tk-zhi;radsq+=temp*temp;} + else if(tk<0) {temp=zlo-boxz*(1+tk);radsq+=temp*temp;} + + if(radsq(format)); + + // Check to see if "%n" appears in the format sequence + while(*fmp!=0) { + if(*fmp=='%') { + fmp++; + if(*fmp=='n') return true; + else if(*fmp==0) return false; + } + fmp++; + } + + return false; +} + +#include "v_base_wl.cc" + +} diff --git a/src/third_party/voro/src/v_base.hh b/src/third_party/voro/src/v_base.hh new file mode 100644 index 000000000..3cd1296d0 --- /dev/null +++ b/src/third_party/voro/src/v_base.hh @@ -0,0 +1,88 @@ +// Voro++, a 3D cell-based Voronoi library +// +// Author : Chris H. Rycroft (Harvard University / LBL) +// Email : chr@alum.mit.edu +// Date : August 30th 2011 + +/** \file v_base.hh + * \brief Header file for the base Voronoi container class. */ + +#ifndef VOROPP_V_BASE_HH +#define VOROPP_V_BASE_HH + +#include "worklist.hh" + +namespace voro { + +/** \brief Class containing data structures common across all particle container classes. + * + * This class contains constants and data structures that are common across all + * particle container classes. It contains constants setting the size of the + * underlying subgrid of blocks that forms the basis of the Voronoi cell + * computations. It also constructs bound tables that are used in the Voronoi + * cell computation, and contains a number of routines that are common across + * all container classes. */ +class voro_base { + public: + /** The number of blocks in the x direction. */ + const int nx; + /** The number of blocks in the y direction. */ + const int ny; + /** The number of blocks in the z direction. */ + const int nz; + /** A constant, set to the value of nx multiplied by ny, which + * is used in the routines that step through blocks in + * sequence. */ + const int nxy; + /** A constant, set to the value of nx*ny*nz, which is used in + * the routines that step through blocks in sequence. */ + const int nxyz; + /** The size of a computational block in the x direction. */ + const double boxx; + /** The size of a computational block in the y direction. */ + const double boxy; + /** The size of a computational block in the z direction. */ + const double boxz; + /** The inverse box length in the x direction. */ + const double xsp; + /** The inverse box length in the y direction. */ + const double ysp; + /** The inverse box length in the z direction. */ + const double zsp; + /** An array to hold the minimum distances associated with the + * worklists. This array is initialized during container + * construction, by the initialize_radii() routine. */ + double *mrad; + /** The pre-computed block worklists. */ + static const unsigned int wl[wl_seq_length*wl_hgridcu]; + bool contains_neighbor(const char* format); + voro_base(int nx_,int ny_,int nz_,double boxx_,double boxy_,double boxz_); + ~voro_base() {delete [] mrad;} + protected: + /** A custom int function that returns consistent stepping + * for negative numbers, so that (-1.5, -0.5, 0.5, 1.5) maps + * to (-2,-1,0,1). + * \param[in] a the number to consider. + * \return The value of the custom int operation. */ + inline int step_int(double a) {return a<0?int(a)-1:int(a);} + /** A custom modulo function that returns consistent stepping + * for negative numbers. For example, (-2,-1,0,1,2) step_mod 2 + * is (0,1,0,1,0). + * \param[in] (a,b) the input integers. + * \return The value of a modulo b, consistent for negative + * numbers. */ + inline int step_mod(int a,int b) {return a>=0?a%b:b-1-(b-1-a)%b;} + /** A custom integer division function that returns consistent + * stepping for negative numbers. For example, (-2,-1,0,1,2) + * step_div 2 is (-1,-1,0,0,1). + * \param[in] (a,b) the input integers. + * \return The value of a div b, consistent for negative + * numbers. */ + inline int step_div(int a,int b) {return a>=0?a/b:-1+(a+1)/b;} + private: + void compute_minimum(double &minr,double &xlo,double &xhi,double &ylo,double &yhi,double &zlo,double &zhi,int ti,int tj,int tk); +}; + +} + +#endif diff --git a/src/third_party/voro/src/v_base_wl.cc b/src/third_party/voro/src/v_base_wl.cc new file mode 100644 index 000000000..ba37f5b25 --- /dev/null +++ b/src/third_party/voro/src/v_base_wl.cc @@ -0,0 +1,79 @@ +// Voro++, a 3D cell-based Voronoi library +// +// Author : Chris H. Rycroft (Harvard University / LBL) +// Email : chr@alum.mit.edu +// Date : August 30th 2011 + +/** \file v_base_wl.cc + * \brief The table of block worklists that are used during the cell + * computation, which is part of the voro_base class. + * + * This file is automatically generated by worklist_gen.pl and it is not + * intended to be edited by hand. */ + +const unsigned int voro_base::wl[wl_seq_length*wl_hgridcu]={ + 7,0x10203f,0x101fc0,0xfe040,0xfe03f,0x101fbf,0xfdfc0,0xfdfbf,0x10fe0bf,0x11020bf,0x11020c0,0x10fe0c0,0x2fe041,0x302041,0x301fc1,0x2fdfc1,0x8105fc0,0x8106040,0x810603f,0x8105fbf,0x701fbe,0x70203e,0x6fe03e,0x6fdfbe,0x30fdf3f,0x3101f3f,0x3101f40,0x30fdf40,0x180f9fc0,0x180fa040,0x180fa03f,0x180f9fbf,0x12fe0c1,0x13020c1,0x91060c0,0x91060bf,0x8306041,0x8305fc1,0x3301f41,0x32fdf41,0x182f9fc1,0x182fa041,0x190fa0c0,0x190fa0bf,0x16fe0be,0x17020be,0x870603e,0x8705fbe,0xb105f3f,0xb105f40,0x3701f3e,0x36fdf3e,0x186f9fbe,0x186fa03e,0x1b0f9f3f,0x1b0f9f40,0x93060c1,0x192fa0c1,0x97060be,0xb305f41,0x1b2f9f41,0x196fa0be,0xb705f3e,0x1b6f9f3e, + 11,0x101fc0,0xfe040,0xfdfc0,0x10203f,0x101fbf,0xfe03f,0xfdfbf,0xfdfc1,0x101fc1,0x102041,0xfe041,0x10fe0c0,0x11020c0,0x8106040,0x8105fc0,0x8105fbf,0x810603f,0x11020bf,0x10fe0bf,0x180fa040,0x180f9fc0,0x30fdf40,0x3101f40,0x3101f3f,0x30fdf3f,0x180f9fbf,0x180fa03f,0x6fe03e,0x70203e,0x701fbe,0x6fdfbe,0x8105fc1,0x8106041,0x11020c1,0x10fe0c1,0x180fa041,0x180f9fc1,0x30fdf41,0x3101f41,0x91060c0,0x91060bf,0x190fa0c0,0x190fa0bf,0xb105f40,0xb105f3f,0x8705fbe,0x870603e,0x97020be,0x16fe0be,0x1b0f9f40,0x1b0f9f3f,0x36fdf3e,0xb701f3e,0x1b6f9fbe,0x196fa03e,0x93060c1,0xb305f41,0x192fa0c1,0x1b2f9f41,0x1b2fdfc2,0xb301fc2,0x9302042,0x192fe042, + 11,0x101fc0,0xfe040,0xfdfc0,0xfdfbf,0x101fbf,0x10203f,0xfe03f,0xfe041,0x102041,0x101fc1,0xfdfc1,0x8105fc0,0x8106040,0x11020c0,0x10fe0c0,0x10fe0bf,0x11020bf,0x810603f,0x8105fbf,0x3101f40,0x30fdf40,0x180f9fc0,0x180fa040,0x180fa03f,0x180f9fbf,0x30fdf3f,0x3101f3f,0x8105fc1,0x8106041,0x11020c1,0x10fe0c1,0x180fa041,0x180f9fc1,0x30fdf41,0x3101f41,0x701fbe,0x70203e,0x6fe03e,0x6fdfbe,0x91060c0,0x91060bf,0xb105f40,0xb105f3f,0x190fa0c0,0x190fa0bf,0x93060c1,0x1b0f9f40,0x1b0f9f3f,0xb305f41,0x192fa0c1,0x16fe0be,0x17020be,0x970603e,0x8705fbe,0xb701f3e,0x36fdf3e,0x1b2f9f41,0x1b2fdfc2,0x192fe042,0x9302042,0xb301fc2,0x1b6f9fbe,0x196fa03e, + 11,0x101fc0,0xfe040,0xfdfc0,0xfdfbf,0x101fbf,0x10203f,0xfe03f,0xfe041,0x102041,0x101fc1,0xfdfc1,0x8105fc0,0x8106040,0x11020c0,0x10fe0c0,0x10fe0bf,0x11020bf,0x810603f,0x8105fbf,0x3101f40,0x30fdf40,0x180f9fc0,0x180fa040,0x10fe0c1,0x11020c1,0x8106041,0x8105fc1,0x3101f3f,0x30fdf3f,0x180f9fbf,0x180fa03f,0x180fa041,0x180f9fc1,0x30fdf41,0x3101f41,0x91060c0,0x91060bf,0x70203e,0x701fbe,0x6fdfbe,0x6fe03e,0x190fa0c0,0xb105f40,0xb105f3f,0x93060c1,0x190fa0bf,0x192fa0c1,0x1b0f9f40,0x1b0f9f3f,0xb305f41,0xb301fc2,0x9302042,0x192fe042,0x1b2fdfc2,0x1b2f9f41,0x16fe0be,0x17020be,0x970603e,0x8705fbe,0xb701f3e,0x36fdf3e,0x1b6f9fbe,0x196fa03e, + 11,0x10203f,0xfe040,0xfe03f,0x101fc0,0x101fbf,0xfdfc0,0xfdfbf,0xfe0bf,0x1020bf,0x1020c0,0xfe0c0,0x2fe041,0x302041,0x8106040,0x810603f,0x8105fbf,0x8105fc0,0x301fc1,0x2fdfc1,0x180fa040,0x180fa03f,0x6fe03e,0x70203e,0x701fbe,0x6fdfbe,0x180f9fbf,0x180f9fc0,0x30fdf40,0x3101f40,0x3101f3f,0x30fdf3f,0x81060bf,0x81060c0,0x3020c1,0x2fe0c1,0x180fa0c0,0x180fa0bf,0x6fe0be,0x7020be,0x8306041,0x8305fc1,0x182fa041,0x182f9fc1,0x870603e,0x8705fbe,0xb105f3f,0xb105f40,0xb301f41,0x32fdf41,0x186fa03e,0x186f9fbe,0x36fdf3e,0xb701f3e,0x1b6f9f3f,0x1b2f9f40,0x93060c1,0x97060be,0x192fa0c1,0x196fa0be,0x196fe13f,0x970213f,0x9302140,0x192fe140, + 9,0xfe040,0x10203f,0x101fc0,0xfdfc0,0xfe03f,0xfdfbf,0x101fbf,0x102041,0xfe041,0x10fe0c0,0x11020c0,0x11020bf,0x10fe0bf,0xfdfc1,0x101fc1,0x8106040,0x810603f,0x8105fc0,0x8105fbf,0x180fa040,0x180f9fc0,0x180fa03f,0x180f9fbf,0x10fe0c1,0x11020c1,0x70203e,0x6fe03e,0x6fdfbe,0x701fbe,0x3101f3f,0x3101f40,0x30fdf40,0x30fdf3f,0x8106041,0x91060c0,0x91060bf,0x8305fc1,0x180fa041,0x190fa0c0,0x190fa0bf,0x180f9fc1,0x30fdf41,0x3301f41,0x17020be,0x16fe0be,0x93060c1,0x870603e,0x8705fbe,0xb105f3f,0xb105f40,0x192fa0c1,0x1b0f9f40,0x1b0f9f3f,0x186f9fbe,0x196fa03e,0x1b6fdf3e,0xb701f3e,0x97060be,0xb305f41,0x1b2f9f41,0x1b2fdfc2,0x192fe042,0xa302042, + 11,0xfe040,0x101fc0,0xfdfc0,0xfe03f,0x10203f,0x101fbf,0xfdfbf,0xfe041,0x102041,0x101fc1,0xfdfc1,0x10fe0c0,0x11020c0,0x11020bf,0x10fe0bf,0x8106040,0x8105fc0,0x810603f,0x8105fbf,0x11020c1,0x10fe0c1,0x180fa040,0x180f9fc0,0x180fa03f,0x180f9fbf,0x30fdf40,0x3101f40,0x8106041,0x8105fc1,0x3101f3f,0x30fdf3f,0x91060c0,0x91060bf,0x180fa041,0x180f9fc1,0x6fe03e,0x70203e,0x701fbe,0x6fdfbe,0x190fa0c0,0x190fa0bf,0x30fdf41,0x3101f41,0x93060c1,0x192fa0c1,0xb105f40,0xb105f3f,0x17020be,0x16fe0be,0x970603e,0x8705fbe,0x1b0f9f40,0x1b0f9f3f,0x186f9fbe,0x196fa03e,0xb305f41,0xb301fc2,0x9302042,0x192fe042,0x1b2fdfc2,0x1b2f9f41,0x1b6fdf3e,0xb701f3e, + 11,0xfe040,0x101fc0,0xfdfc0,0xfe03f,0x10203f,0x101fbf,0xfdfbf,0xfe041,0x102041,0x101fc1,0xfdfc1,0x10fe0c0,0x11020c0,0x11020bf,0x10fe0bf,0x8106040,0x8105fc0,0x11020c1,0x10fe0c1,0x810603f,0x8105fbf,0x180fa040,0x180f9fc0,0x8106041,0x8105fc1,0x3101f40,0x180fa03f,0x180f9fbf,0x30fdf40,0x180fa041,0x180f9fc1,0x91060c0,0x3101f3f,0x30fdf3f,0x30fdf41,0x3101f41,0x91060bf,0x190fa0c0,0x91060c1,0x190fa0bf,0x186fe03e,0x70203e,0x3701fbe,0x1b6fdfbe,0x190fa0c1,0x182fe042,0xb105f40,0xb105f3f,0x302042,0x3301fc2,0x1b2fdfc2,0x1b0f9f40,0x1b6f9f3f,0xb105f41,0x17020be,0x196fe0be,0x970603e,0xb705fbe,0x1b2f9f41,0x192fe0c2,0x13020c2,0x9306042,0xb305fc2, + 11,0x10203f,0xfe040,0xfe03f,0xfdfbf,0x101fbf,0x101fc0,0xfdfc0,0xfe0c0,0x1020c0,0x1020bf,0xfe0bf,0x810603f,0x8106040,0x302041,0x2fe041,0x2fdfc1,0x301fc1,0x8105fc0,0x8105fbf,0x70203e,0x6fe03e,0x180fa03f,0x180fa040,0x180f9fc0,0x180f9fbf,0x6fdfbe,0x701fbe,0x81060bf,0x81060c0,0x3020c1,0x2fe0c1,0x180fa0c0,0x180fa0bf,0x6fe0be,0x7020be,0x3101f3f,0x3101f40,0x30fdf40,0x30fdf3f,0x8306041,0x8305fc1,0x870603e,0x8705fbe,0x182fa041,0x182f9fc1,0x93060c1,0x186fa03e,0x186f9fbe,0x97060be,0x192fa0c1,0x32fdf41,0x3301f41,0xb305f40,0xb105f3f,0xb701f3e,0x36fdf3e,0x196fa0be,0x196fe13f,0x192fe140,0x9302140,0x970213f,0x1b6f9f3f,0x1b2f9f40, + 11,0xfe040,0x10203f,0xfe03f,0xfdfc0,0x101fc0,0x101fbf,0xfdfbf,0xfe0c0,0x1020c0,0x1020bf,0xfe0bf,0x2fe041,0x302041,0x301fc1,0x2fdfc1,0x8106040,0x810603f,0x8105fc0,0x8105fbf,0x3020c1,0x2fe0c1,0x180fa040,0x180fa03f,0x180f9fc0,0x180f9fbf,0x6fe03e,0x70203e,0x81060c0,0x81060bf,0x701fbe,0x6fdfbe,0x8306041,0x8305fc1,0x180fa0c0,0x180fa0bf,0x30fdf40,0x3101f40,0x3101f3f,0x30fdf3f,0x182fa041,0x182f9fc1,0x6fe0be,0x7020be,0x93060c1,0x192fa0c1,0x870603e,0x8705fbe,0x3301f41,0x32fdf41,0xb305f40,0xb105f3f,0x186fa03e,0x186f9fbe,0x1b0f9f3f,0x1b2f9f40,0x97060be,0x970213f,0x9302140,0x192fe140,0x196fe13f,0x196fa0be,0x1b6fdf3e,0xb701f3e, + 15,0xfe040,0xfe03f,0x10203f,0x101fc0,0xfdfc0,0xfdfbf,0x101fbf,0x102041,0xfe041,0xfe0c0,0x1020c0,0x1020bf,0xfe0bf,0xfdfc1,0x101fc1,0x8106040,0x1020c1,0xfe0c1,0x810603f,0x8105fc0,0x8105fbf,0x180fa040,0x180fa03f,0x180f9fc0,0x180f9fbf,0x8106041,0x81060c0,0x81060bf,0x8105fc1,0x180fa041,0x180fa0c0,0x180fa0bf,0x6fe03e,0x70203e,0x3101f40,0x30fdf40,0x180f9fc1,0x30fdf3f,0x3101f3f,0x3701fbe,0x36fdfbe,0x93060c1,0x192fa0c1,0x6fe0be,0x7020be,0x3101f41,0x30fdf41,0xb305f40,0xb105f3f,0x970603e,0xb705fbe,0x196fa03e,0x186f9fbe,0x1b2f9f40,0x1b6f9f3f,0x192fe042,0x9302042,0xb301fc2,0x1b2fdfc2,0x192fe140,0x9302140,0x970213f,0x196fe13f, + 15,0xfe040,0xfdfc0,0x101fc0,0x10203f,0xfe03f,0xfdfbf,0x101fbf,0x102041,0xfe041,0xfdfc1,0x101fc1,0x1020c0,0xfe0c0,0xfe0bf,0x1020bf,0x1020c1,0xfe0c1,0x8106040,0x8105fc0,0x810603f,0x8105fbf,0x180fa040,0x180f9fc0,0x8106041,0x8105fc1,0x81060c0,0x180fa03f,0x180f9fbf,0x180fa041,0x180f9fc1,0x180fa0c0,0x81060bf,0x91060c1,0x3101f40,0x30fdf40,0x30fdf3f,0x180fa0bf,0x190fa0c1,0x3101f3f,0x3101f41,0x30fdf41,0x186fe03e,0x70203e,0x3701fbe,0x1b6fdfbe,0x186fe0be,0x7020be,0x8302042,0x182fe042,0x1b2fdfc2,0xb301fc2,0xb105f40,0xb705f3f,0xb305f41,0x1b2f9f40,0x1b6f9f3f,0x192fe140,0x9302140,0x93020c2,0x192fe0c2,0x196fe13f,0x970213f,0xa70603e, + 11,0x10203f,0xfe040,0xfe03f,0xfdfbf,0x101fbf,0x101fc0,0xfdfc0,0xfe0c0,0x1020c0,0x1020bf,0xfe0bf,0x810603f,0x8106040,0x302041,0x2fe041,0x2fdfc1,0x301fc1,0x8105fc0,0x8105fbf,0x70203e,0x6fe03e,0x180fa03f,0x180fa040,0x2fe0c1,0x3020c1,0x81060c0,0x81060bf,0x701fbe,0x6fdfbe,0x180f9fbf,0x180f9fc0,0x180fa0c0,0x180fa0bf,0x6fe0be,0x7020be,0x8306041,0x8305fc1,0x3101f40,0x3101f3f,0x30fdf3f,0x30fdf40,0x182fa041,0x870603e,0x8705fbe,0x93060c1,0x182f9fc1,0x192fa0c1,0x186fa03e,0x186f9fbe,0x97060be,0x970213f,0x9302140,0x192fe140,0x196fe13f,0x196fa0be,0x32fdf41,0x3301f41,0xb305f40,0xb105f3f,0xb701f3e,0x36fdf3e,0x1b6f9f3f,0x1b2f9f40, + 11,0xfe040,0x10203f,0xfe03f,0xfdfc0,0x101fc0,0x101fbf,0xfdfbf,0xfe0c0,0x1020c0,0x1020bf,0xfe0bf,0x2fe041,0x302041,0x301fc1,0x2fdfc1,0x8106040,0x810603f,0x3020c1,0x2fe0c1,0x8105fc0,0x8105fbf,0x180fa040,0x180fa03f,0x81060c0,0x81060bf,0x70203e,0x180f9fc0,0x180f9fbf,0x6fe03e,0x180fa0c0,0x180fa0bf,0x8306041,0x701fbe,0x6fdfbe,0x6fe0be,0x7020be,0x8305fc1,0x182fa041,0x83060c1,0x182f9fc1,0x1b0fdf40,0x3101f40,0x3701f3f,0x1b6fdf3f,0x182fa0c1,0x190fe140,0x870603e,0x8705fbe,0x1102140,0x170213f,0x196fe13f,0x186fa03e,0x1b6f9fbe,0x87060be,0x3301f41,0x1b2fdf41,0xb305f40,0xb705f3f,0x196fa0be,0x192fe141,0x1302141,0x9306140,0x970613f, + 15,0xfe040,0xfe03f,0x10203f,0x101fc0,0xfdfc0,0xfdfbf,0x101fbf,0x1020c0,0xfe0c0,0xfe0bf,0x1020bf,0x102041,0xfe041,0xfdfc1,0x101fc1,0x1020c1,0xfe0c1,0x8106040,0x810603f,0x8105fc0,0x8105fbf,0x180fa040,0x180fa03f,0x81060c0,0x81060bf,0x8106041,0x180f9fc0,0x180f9fbf,0x180fa0c0,0x180fa0bf,0x180fa041,0x8105fc1,0x83060c1,0x70203e,0x6fe03e,0x6fdfbe,0x180f9fc1,0x182fa0c1,0x701fbe,0x7020be,0x6fe0be,0x1b0fdf40,0x3101f40,0x3701f3f,0x1b6fdf3f,0x1b0fdf41,0x3101f41,0x9102140,0x190fe140,0x196fe13f,0x970213f,0x870603e,0xb705fbe,0x97060be,0x196fa03e,0x1b6f9fbe,0x192fe042,0x9302042,0x9302141,0x192fe141,0x1b2fdfc2,0xb301fc2,0xb505f40, + 17,0xfe040,0xfe03f,0x10203f,0x101fc0,0xfdfc0,0xfe041,0x102041,0x1020c0,0xfe0c0,0xfdfbf,0x101fbf,0x1020bf,0xfe0bf,0xfdfc1,0x101fc1,0x1020c1,0xfe0c1,0x8106040,0x810603f,0x8105fc0,0x8106041,0x81060c0,0x180fa040,0x180fa03f,0x180f9fc0,0x8105fbf,0x81060bf,0x8105fc1,0x180fa041,0x180fa0c0,0x180f9fbf,0x81060c1,0x180fa0bf,0x180f9fc1,0x180fa0c1,0x186fe03e,0x70203e,0x3101f40,0x1b0fdf40,0x1b0fdf3f,0x3101f3f,0x3701fbe,0x1b6fdfbe,0x186fe0be,0x7020be,0x9102140,0x190fe140,0x182fe042,0x8302042,0x3101f41,0x1b0fdf41,0x1b2fdfc2,0xb301fc2,0x83020c2,0x182fe0c2,0x196fe13f,0x970213f,0x9302141,0x192fe141,0x970603e,0xb305f40,0xb105f3f,0xb705fbe, + 11,0x10203f,0x101fc0,0x101fbf,0xfe040,0xfe03f,0xfdfc0,0xfdfbf,0x105fbf,0x10603f,0x106040,0x105fc0,0x301fc1,0x302041,0x11020c0,0x11020bf,0x10fe0bf,0x10fe0c0,0x2fe041,0x2fdfc1,0x3101f40,0x3101f3f,0x701fbe,0x70203e,0x6fe03e,0x6fdfbe,0x30fdf3f,0x30fdf40,0x180f9fc0,0x180fa040,0x180fa03f,0x180f9fbf,0x11060bf,0x11060c0,0x306041,0x305fc1,0x3105f40,0x3105f3f,0x705fbe,0x70603e,0x13020c1,0x12fe0c1,0x3301f41,0x32fdf41,0x17020be,0x16fe0be,0x190fa0bf,0x190fa0c0,0x192fa041,0x182f9fc1,0x3701f3e,0x36fdf3e,0x186f9fbe,0x196fa03e,0x1b6f9f3f,0x1b2f9f40,0x93060c1,0x97060be,0xb305f41,0xb705f3e,0xb709fbf,0x970a03f,0x930a040,0xb309fc0, + 9,0x101fc0,0x10203f,0xfe040,0xfdfc0,0x101fbf,0xfdfbf,0xfe03f,0x102041,0x101fc1,0x8105fc0,0x8106040,0x810603f,0x8105fbf,0xfdfc1,0xfe041,0x11020c0,0x11020bf,0x10fe0c0,0x10fe0bf,0x3101f40,0x30fdf40,0x3101f3f,0x30fdf3f,0x8105fc1,0x8106041,0x70203e,0x701fbe,0x6fdfbe,0x6fe03e,0x180fa03f,0x180fa040,0x180f9fc0,0x180f9fbf,0x11020c1,0x91060c0,0x91060bf,0x12fe0c1,0x3101f41,0xb105f40,0xb105f3f,0x30fdf41,0x180f9fc1,0x182fa041,0x870603e,0x8705fbe,0x93060c1,0x17020be,0x16fe0be,0x190fa0bf,0x190fa0c0,0xb305f41,0x1b0f9f40,0x1b0f9f3f,0x36fdf3e,0xb701f3e,0x1b6f9fbe,0x196fa03e,0x97060be,0x192fa0c1,0x1b2f9f41,0x1b2fdfc2,0xb301fc2,0x11302042, + 11,0x101fc0,0xfe040,0xfdfc0,0x101fbf,0x10203f,0xfe03f,0xfdfbf,0x101fc1,0x102041,0xfe041,0xfdfc1,0x8105fc0,0x8106040,0x810603f,0x8105fbf,0x11020c0,0x10fe0c0,0x11020bf,0x10fe0bf,0x8106041,0x8105fc1,0x3101f40,0x30fdf40,0x3101f3f,0x30fdf3f,0x180f9fc0,0x180fa040,0x11020c1,0x10fe0c1,0x180fa03f,0x180f9fbf,0x91060c0,0x91060bf,0x3101f41,0x30fdf41,0x701fbe,0x70203e,0x6fe03e,0x6fdfbe,0xb105f40,0xb105f3f,0x180f9fc1,0x180fa041,0x93060c1,0xb305f41,0x190fa0c0,0x190fa0bf,0x870603e,0x8705fbe,0x97020be,0x16fe0be,0x1b0f9f40,0x1b0f9f3f,0x36fdf3e,0xb701f3e,0x192fa0c1,0x192fe042,0x9302042,0xb301fc2,0x1b2fdfc2,0x1b2f9f41,0x1b6f9fbe,0x196fa03e, + 11,0x101fc0,0xfe040,0xfdfc0,0x101fbf,0x10203f,0xfe03f,0xfdfbf,0x101fc1,0x102041,0xfe041,0xfdfc1,0x8105fc0,0x8106040,0x810603f,0x8105fbf,0x11020c0,0x10fe0c0,0x8106041,0x8105fc1,0x11020bf,0x10fe0bf,0x3101f40,0x30fdf40,0x11020c1,0x10fe0c1,0x180fa040,0x3101f3f,0x30fdf3f,0x180f9fc0,0x3101f41,0x30fdf41,0x91060c0,0x180fa03f,0x180f9fbf,0x180f9fc1,0x180fa041,0x91060bf,0xb105f40,0x91060c1,0xb105f3f,0x3701fbe,0x70203e,0x186fe03e,0x1b6fdfbe,0xb105f41,0x3301fc2,0x190fa0c0,0x190fa0bf,0x302042,0x182fe042,0x1b2fdfc2,0x1b0f9f40,0x1b6f9f3f,0x190fa0c1,0x870603e,0xb705fbe,0x97020be,0x196fe0be,0x1b2f9f41,0xb305fc2,0x8306042,0x93020c2,0x192fe0c2, + 9,0x10203f,0x101fc0,0xfe040,0xfe03f,0x101fbf,0xfdfbf,0xfdfc0,0x1020c0,0x1020bf,0x810603f,0x8106040,0x8105fc0,0x8105fbf,0xfe0bf,0xfe0c0,0x302041,0x301fc1,0x2fe041,0x2fdfc1,0x70203e,0x6fe03e,0x701fbe,0x6fdfbe,0x81060bf,0x81060c0,0x3101f40,0x3101f3f,0x30fdf3f,0x30fdf40,0x180f9fc0,0x180fa040,0x180fa03f,0x180f9fbf,0x3020c1,0x8306041,0x8305fc1,0x12fe0c1,0x7020be,0x870603e,0x8705fbe,0x6fe0be,0x180fa0bf,0x190fa0c0,0xb105f40,0xb105f3f,0x93060c1,0x3301f41,0x32fdf41,0x182f9fc1,0x182fa041,0x97060be,0x186fa03e,0x186f9fbe,0x36fdf3e,0xb701f3e,0x1b6f9f3f,0x1b2f9f40,0xb305f41,0x192fa0c1,0x196fa0be,0x196fe13f,0x970213f,0x11302140, + 7,0x10203f,0x101fc0,0xfe040,0xfe03f,0x101fbf,0xfdfc0,0xfdfbf,0x302041,0x1020c0,0x8106040,0x810603f,0x1020bf,0xfe0c0,0x2fe041,0x301fc1,0x8105fc0,0x8105fbf,0xfe0bf,0x2fdfc1,0x3020c1,0x8306041,0x81060c0,0x81060bf,0x2fe0c1,0x8305fc1,0x3101f40,0x3101f3f,0x701fbe,0x70203e,0x6fe03e,0x180fa03f,0x180fa040,0x180f9fc0,0x30fdf40,0x30fdf3f,0x6fdfbe,0x180f9fbf,0x180fa0c0,0x182fa041,0x93060c1,0x870603e,0x7020be,0x6fe0be,0x180fa0bf,0x182f9fc1,0x32fdf41,0x3301f41,0xb105f40,0xb105f3f,0x8705fbe,0x97060be,0x192fa0c1,0xb305f41,0xb701f3e,0x36fdf3e,0x1b0f9f3f,0x1b2f9f40,0x1b6f9fbe,0x196fa03e,0x196fe13f,0x9302140,0x970213f,0x192fe140, + 11,0x101fc0,0xfe040,0xfdfc0,0x10203f,0x101fbf,0xfe03f,0xfdfbf,0x102041,0x101fc1,0xfe041,0xfdfc1,0x11020c0,0x8106040,0x8105fc0,0x10fe0c0,0x11020bf,0x810603f,0x8105fbf,0x10fe0bf,0x11020c1,0x8106041,0x8105fc1,0x10fe0c1,0x91060c0,0x91060bf,0x3101f40,0x30fdf40,0x180f9fc0,0x180fa040,0x180fa03f,0x180f9fbf,0x30fdf3f,0x3101f3f,0x701fbe,0x70203e,0x6fe03e,0x6fdfbe,0x93060c1,0x3101f41,0x30fdf41,0x180f9fc1,0x180fa041,0x190fa0c0,0x190fa0bf,0xb105f40,0xb105f3f,0x8705fbe,0x870603e,0x17020be,0x16fe0be,0x192fa0c1,0xb305f41,0xb301fc2,0x9302042,0x192fe042,0x1b2fdfc2,0x1b2f9f40,0x1b0f9f3f,0x186f9fbe,0x196fa03e,0x97060be,0xb701f3e,0x1b6fdf3e, + 11,0x101fc0,0xfe040,0xfdfc0,0x10203f,0x101fbf,0xfe03f,0xfdfbf,0x102041,0x101fc1,0xfe041,0xfdfc1,0x11020c0,0x8106040,0x8105fc0,0x10fe0c0,0x11020bf,0x810603f,0x8105fbf,0x10fe0bf,0x11020c1,0x8106041,0x8105fc1,0x10fe0c1,0x91060c0,0x91060bf,0x3101f40,0x30fdf40,0x180f9fc0,0x180fa040,0x180fa03f,0x180f9fbf,0x30fdf3f,0x3101f3f,0x3101f41,0x91060c1,0x180fa041,0x180f9fc1,0x30fdf41,0xb105f40,0x70203e,0x3701fbe,0x186fe03e,0x1b6fdfbe,0x190fa0c0,0x190fa0bf,0x190fa0c1,0xb105f3f,0xb105f41,0x3301fc2,0x302042,0x182fe042,0x1b2fdfc2,0x1b0f9f40,0x17020be,0x970603e,0xb705fbe,0x196fe0be,0x1b6f9f3f,0x1b2f9f41,0x13020c2,0x9306042,0xb305fc2,0x192fe0c2, + 11,0x10203f,0xfe040,0xfe03f,0x101fbf,0x101fc0,0xfdfc0,0xfdfbf,0x1020bf,0x1020c0,0xfe0c0,0xfe0bf,0x810603f,0x8106040,0x8105fc0,0x8105fbf,0x302041,0x2fe041,0x301fc1,0x2fdfc1,0x81060c0,0x81060bf,0x70203e,0x6fe03e,0x701fbe,0x6fdfbe,0x180fa03f,0x180fa040,0x3020c1,0x2fe0c1,0x180f9fc0,0x180f9fbf,0x8306041,0x8305fc1,0x7020be,0x6fe0be,0x3101f3f,0x3101f40,0x30fdf40,0x30fdf3f,0x870603e,0x8705fbe,0x180fa0bf,0x180fa0c0,0x93060c1,0x97060be,0x182fa041,0x182f9fc1,0xb105f40,0xb105f3f,0xb301f41,0x32fdf41,0x186fa03e,0x186f9fbe,0x36fdf3e,0xb701f3e,0x192fa0c1,0x192fe140,0x9302140,0x970213f,0x196fe13f,0x196fa0be,0x1b6f9f3f,0x1b2f9f40, + 11,0x10203f,0xfe040,0xfe03f,0x101fc0,0x101fbf,0xfdfc0,0xfdfbf,0x1020c0,0x1020bf,0xfe0c0,0xfe0bf,0x302041,0x8106040,0x810603f,0x2fe041,0x301fc1,0x8105fc0,0x8105fbf,0x2fdfc1,0x3020c1,0x81060c0,0x81060bf,0x2fe0c1,0x8306041,0x8305fc1,0x70203e,0x6fe03e,0x180fa03f,0x180fa040,0x180f9fc0,0x180f9fbf,0x6fdfbe,0x701fbe,0x3101f3f,0x3101f40,0x30fdf40,0x30fdf3f,0x93060c1,0x7020be,0x6fe0be,0x180fa0bf,0x180fa0c0,0x182fa041,0x182f9fc1,0x870603e,0x8705fbe,0xb105f3f,0xb105f40,0x3301f41,0x32fdf41,0x192fa0c1,0x97060be,0x970213f,0x9302140,0x192fe140,0x196fe13f,0x196fa03e,0x186f9fbe,0x1b0f9f3f,0x1b2f9f40,0xb305f41,0xb701f3e,0x1b6fdf3e, + 15,0xfe040,0x10203f,0x101fc0,0xfdfc0,0xfe03f,0x101fbf,0xfdfbf,0x102041,0x1020c0,0xfe0c0,0xfe041,0x101fc1,0xfdfc1,0x1020bf,0xfe0bf,0x8106040,0x810603f,0x8105fc0,0x8105fbf,0x1020c1,0xfe0c1,0x8106041,0x81060c0,0x81060bf,0x8105fc1,0x180fa040,0x180f9fc0,0x180fa03f,0x180f9fbf,0x93060c1,0x70203e,0x6fe03e,0x3101f40,0x1b0fdf40,0x3101f3f,0x3701fbe,0x6fdfbe,0x1b6fdf3f,0x180fa041,0x180fa0c0,0x180fa0bf,0x180f9fc1,0x1b0fdf41,0x3101f41,0x7020be,0x6fe0be,0x870603e,0x8705fbe,0xb105f40,0xb705f3f,0x192fa0c1,0x192fe140,0x9302140,0x970213f,0x97060be,0x9302042,0x192fe042,0xb301fc2,0xb305f41,0x1b2fdfc2,0x196fe13f,0x196fa03e,0x1b6f9fbe, + 14,0xfe040,0x101fc0,0xfdfc0,0x10203f,0xfe03f,0x101fbf,0xfdfbf,0x102041,0xfe041,0x101fc1,0xfdfc1,0x1020c0,0xfe0c0,0x1020bf,0x8106040,0xfe0bf,0x8105fc0,0x1020c1,0xfe0c1,0x810603f,0x8105fbf,0x8106041,0x8105fc1,0x81060c0,0x81060bf,0x91060c1,0x180fa040,0x180f9fc0,0x180fa03f,0x180f9fbf,0x180fa041,0x180f9fc1,0x1b0fdf40,0x3101f40,0x3101f3f,0x1b0fdf3f,0x180fa0c0,0x190fa0bf,0x186fe03e,0x70203e,0x3701fbe,0x3101f41,0x1b0fdf41,0x1b6fdfbe,0x190fa0c1,0x182fe042,0x302042,0xb105f40,0xb105f3f,0x3301fc2,0x1b2fdfc2,0x7020be,0x196fe0be,0x970603e,0xb705fbe,0xb105f41,0x9302140,0x192fe140,0x13020c2,0x192fe0c2,0x9306042,0xb305fc2,0x1170213f, + 11,0x10203f,0xfe040,0xfe03f,0x101fbf,0x101fc0,0xfdfc0,0xfdfbf,0x1020bf,0x1020c0,0xfe0c0,0xfe0bf,0x810603f,0x8106040,0x8105fc0,0x8105fbf,0x302041,0x2fe041,0x81060c0,0x81060bf,0x301fc1,0x2fdfc1,0x70203e,0x6fe03e,0x3020c1,0x2fe0c1,0x180fa040,0x701fbe,0x6fdfbe,0x180fa03f,0x7020be,0x6fe0be,0x8306041,0x180f9fc0,0x180f9fbf,0x180fa0bf,0x180fa0c0,0x8305fc1,0x870603e,0x83060c1,0x8705fbe,0x3701f3f,0x3101f40,0x1b0fdf40,0x1b6fdf3f,0x87060be,0x170213f,0x182fa041,0x182f9fc1,0x1102140,0x190fe140,0x196fe13f,0x186fa03e,0x1b6f9fbe,0x182fa0c1,0xb105f40,0xb705f3f,0xb301f41,0x1b2fdf41,0x196fa0be,0x970613f,0x9106140,0x9302141,0x192fe141, + 11,0x10203f,0xfe040,0xfe03f,0x101fc0,0x101fbf,0xfdfc0,0xfdfbf,0x1020c0,0x1020bf,0xfe0c0,0xfe0bf,0x302041,0x8106040,0x810603f,0x2fe041,0x301fc1,0x8105fc0,0x8105fbf,0x2fdfc1,0x3020c1,0x81060c0,0x81060bf,0x2fe0c1,0x8306041,0x8305fc1,0x70203e,0x6fe03e,0x180fa03f,0x180fa040,0x180f9fc0,0x180f9fbf,0x6fdfbe,0x701fbe,0x7020be,0x83060c1,0x180fa0c0,0x180fa0bf,0x6fe0be,0x870603e,0x3101f40,0x3701f3f,0x1b0fdf40,0x1b6fdf3f,0x182fa041,0x182f9fc1,0x182fa0c1,0x8705fbe,0x87060be,0x170213f,0x1102140,0x190fe140,0x196fe13f,0x186fa03e,0x3301f41,0xb305f40,0xb705f3f,0x1b2fdf41,0x1b6f9fbe,0x196fa0be,0x1302141,0x9306140,0x970613f,0x192fe141, + 14,0xfe040,0x10203f,0xfe03f,0x101fc0,0xfdfc0,0x101fbf,0xfdfbf,0x1020c0,0xfe0c0,0x1020bf,0xfe0bf,0x102041,0xfe041,0x101fc1,0x8106040,0xfdfc1,0x810603f,0x1020c1,0xfe0c1,0x8105fc0,0x8105fbf,0x81060c0,0x81060bf,0x8106041,0x8105fc1,0x83060c1,0x180fa040,0x180fa03f,0x180f9fc0,0x180f9fbf,0x180fa0c0,0x180fa0bf,0x186fe03e,0x70203e,0x701fbe,0x186fdfbe,0x180fa041,0x182f9fc1,0x1b0fdf40,0x3101f40,0x3701f3f,0x7020be,0x186fe0be,0x1b6fdf3f,0x182fa0c1,0x190fe140,0x1102140,0x870603e,0x8705fbe,0x170213f,0x196fe13f,0x3101f41,0x1b2fdf41,0xb305f40,0xb705f3f,0x87060be,0x9302042,0x192fe042,0x1302141,0x192fe141,0x9306140,0x970613f,0x13301fc2, + 17,0xfe040,0x10203f,0x101fc0,0xfdfc0,0xfe03f,0x1020c0,0x102041,0xfe041,0xfe0c0,0x101fbf,0xfdfbf,0x1020bf,0xfe0bf,0x101fc1,0xfdfc1,0x1020c1,0xfe0c1,0x8106040,0x810603f,0x8105fc0,0x8106041,0x81060c0,0x8105fbf,0x81060bf,0x8105fc1,0x81060c1,0x180fa040,0x180f9fc0,0x180fa03f,0x180fa0c0,0x180fa041,0x180f9fbf,0x180fa0bf,0x180f9fc1,0x180fa0c1,0x70203e,0x186fe03e,0x3101f40,0x1b0fdf40,0x3101f3f,0x3701fbe,0x186fdfbe,0x1b6fdf3f,0x3101f41,0x1b0fdf41,0x8302042,0x182fe042,0x9102140,0x7020be,0x186fe0be,0x190fe140,0x970213f,0x196fe13f,0x9102141,0xb301fc2,0x1b2fdfc2,0x93020c2,0x182fe0c2,0x192fe141,0x970603e,0xb305f40,0xb105f3f,0xb705fbe, + 11,0x10203f,0x101fc0,0x101fbf,0xfdfbf,0xfe03f,0xfe040,0xfdfc0,0x105fc0,0x106040,0x10603f,0x105fbf,0x11020bf,0x11020c0,0x302041,0x301fc1,0x2fdfc1,0x2fe041,0x10fe0c0,0x10fe0bf,0x70203e,0x701fbe,0x3101f3f,0x3101f40,0x30fdf40,0x30fdf3f,0x6fdfbe,0x6fe03e,0x11060bf,0x11060c0,0x306041,0x305fc1,0x3105f40,0x3105f3f,0x705fbe,0x70603e,0x180fa03f,0x180fa040,0x180f9fc0,0x180f9fbf,0x13020c1,0x12fe0c1,0x17020be,0x16fe0be,0x3301f41,0x32fdf41,0x93060c1,0x3701f3e,0x36fdf3e,0x97060be,0xb305f41,0x182f9fc1,0x182fa041,0x192fa0c0,0x190fa0bf,0x196fa03e,0x186f9fbe,0xb705f3e,0xb709fbf,0xb309fc0,0x930a040,0x970a03f,0x1b6f9f3f,0x1b2f9f40, + 11,0x101fc0,0x10203f,0x101fbf,0xfdfc0,0xfe040,0xfe03f,0xfdfbf,0x105fc0,0x106040,0x10603f,0x105fbf,0x301fc1,0x302041,0x2fe041,0x2fdfc1,0x11020c0,0x11020bf,0x10fe0c0,0x10fe0bf,0x306041,0x305fc1,0x3101f40,0x3101f3f,0x30fdf40,0x30fdf3f,0x701fbe,0x70203e,0x11060c0,0x11060bf,0x6fe03e,0x6fdfbe,0x13020c1,0x12fe0c1,0x3105f40,0x3105f3f,0x180f9fc0,0x180fa040,0x180fa03f,0x180f9fbf,0x3301f41,0x32fdf41,0x705fbe,0x70603e,0x93060c1,0xb305f41,0x17020be,0x16fe0be,0x182fa041,0x182f9fc1,0x192fa0c0,0x190fa0bf,0x3701f3e,0x36fdf3e,0x1b0f9f3f,0x1b2f9f40,0x97060be,0x970a03f,0x930a040,0xb309fc0,0xb709fbf,0xb705f3e,0x1b6f9fbe,0x196fa03e, + 15,0x101fc0,0x101fbf,0x10203f,0xfe040,0xfdfc0,0xfdfbf,0xfe03f,0x102041,0x101fc1,0x105fc0,0x106040,0x10603f,0x105fbf,0xfdfc1,0xfe041,0x11020c0,0x106041,0x105fc1,0x11020bf,0x10fe0c0,0x10fe0bf,0x3101f40,0x3101f3f,0x30fdf40,0x30fdf3f,0x11020c1,0x11060c0,0x11060bf,0x10fe0c1,0x3101f41,0x3105f40,0x3105f3f,0x701fbe,0x70203e,0x180fa040,0x180f9fc0,0x30fdf41,0x180f9fbf,0x180fa03f,0x186fe03e,0x186fdfbe,0x93060c1,0xb305f41,0x705fbe,0x70603e,0x180fa041,0x180f9fc1,0x192fa0c0,0x190fa0bf,0x97020be,0x196fe0be,0xb701f3e,0x36fdf3e,0x1b2f9f40,0x1b6f9f3f,0xb301fc2,0x9302042,0x192fe042,0x1b2fdfc2,0xb309fc0,0x930a040,0x970a03f,0xb709fbf, + 15,0x101fc0,0xfdfc0,0xfe040,0x10203f,0x101fbf,0xfdfbf,0xfe03f,0x102041,0x101fc1,0xfdfc1,0xfe041,0x106040,0x105fc0,0x105fbf,0x10603f,0x106041,0x105fc1,0x11020c0,0x10fe0c0,0x11020bf,0x10fe0bf,0x3101f40,0x30fdf40,0x11020c1,0x10fe0c1,0x11060c0,0x3101f3f,0x30fdf3f,0x3101f41,0x30fdf41,0x3105f40,0x11060bf,0x91060c1,0x180fa040,0x180f9fc0,0x180f9fbf,0x3105f3f,0xb105f41,0x180fa03f,0x180fa041,0x180f9fc1,0x3701fbe,0x70203e,0x186fe03e,0x1b6fdfbe,0x3705fbe,0x70603e,0x1302042,0x3301fc2,0x1b2fdfc2,0x192fe042,0x190fa0c0,0x196fa0bf,0x192fa0c1,0x1b2f9f40,0x1b6f9f3f,0xb309fc0,0x930a040,0x9306042,0xb305fc2,0xb709fbf,0x970a03f,0x117020be, + 11,0x10203f,0x101fc0,0x101fbf,0xfe03f,0xfe040,0xfdfc0,0xfdfbf,0x10603f,0x106040,0x105fc0,0x105fbf,0x11020bf,0x11020c0,0x10fe0c0,0x10fe0bf,0x302041,0x301fc1,0x2fe041,0x2fdfc1,0x11060c0,0x11060bf,0x70203e,0x701fbe,0x6fe03e,0x6fdfbe,0x3101f3f,0x3101f40,0x306041,0x305fc1,0x30fdf40,0x30fdf3f,0x13020c1,0x12fe0c1,0x70603e,0x705fbe,0x180fa03f,0x180fa040,0x180f9fc0,0x180f9fbf,0x17020be,0x16fe0be,0x3105f3f,0x3105f40,0x93060c1,0x97060be,0x3301f41,0x32fdf41,0x190fa0c0,0x190fa0bf,0x192fa041,0x182f9fc1,0x3701f3e,0x36fdf3e,0x186f9fbe,0x196fa03e,0xb305f41,0xb309fc0,0x930a040,0x970a03f,0xb709fbf,0xb705f3e,0x1b6f9f3f,0x1b2f9f40, + 11,0x10203f,0x101fc0,0x101fbf,0xfe040,0xfe03f,0xfdfc0,0xfdfbf,0x106040,0x10603f,0x105fc0,0x105fbf,0x302041,0x11020c0,0x11020bf,0x301fc1,0x2fe041,0x10fe0c0,0x10fe0bf,0x2fdfc1,0x306041,0x11060c0,0x11060bf,0x305fc1,0x13020c1,0x12fe0c1,0x70203e,0x701fbe,0x3101f3f,0x3101f40,0x30fdf40,0x30fdf3f,0x6fdfbe,0x6fe03e,0x180fa03f,0x180fa040,0x180f9fc0,0x180f9fbf,0x93060c1,0x70603e,0x705fbe,0x3105f3f,0x3105f40,0x3301f41,0x32fdf41,0x17020be,0x16fe0be,0x190fa0bf,0x190fa0c0,0x182fa041,0x182f9fc1,0xb305f41,0x97060be,0x970a03f,0x930a040,0xb309fc0,0xb709fbf,0xb701f3e,0x36fdf3e,0x1b0f9f3f,0x1b2f9f40,0x192fa0c1,0x196fa03e,0x1b6f9fbe, + 15,0x101fc0,0x10203f,0xfe040,0xfdfc0,0x101fbf,0xfe03f,0xfdfbf,0x102041,0x106040,0x105fc0,0x101fc1,0xfe041,0xfdfc1,0x10603f,0x105fbf,0x11020c0,0x11020bf,0x10fe0c0,0x10fe0bf,0x106041,0x105fc1,0x11020c1,0x11060c0,0x11060bf,0x10fe0c1,0x3101f40,0x30fdf40,0x3101f3f,0x30fdf3f,0x93060c1,0x70203e,0x701fbe,0x180fa040,0x1b0f9fc0,0x180fa03f,0x186fe03e,0x6fdfbe,0x1b6f9fbf,0x3101f41,0x3105f40,0x3105f3f,0x30fdf41,0x1b0f9fc1,0x180fa041,0x70603e,0x705fbe,0x17020be,0x16fe0be,0x190fa0c0,0x196fa0bf,0xb305f41,0xb309fc0,0x930a040,0x970a03f,0x97060be,0x9302042,0xb301fc2,0x192fe042,0x192fa0c1,0x1b2fdfc2,0xb709fbf,0xb701f3e,0x1b6fdf3e, + 14,0x101fc0,0xfe040,0xfdfc0,0x10203f,0x101fbf,0xfe03f,0xfdfbf,0x102041,0x101fc1,0xfe041,0xfdfc1,0x106040,0x105fc0,0x10603f,0x11020c0,0x105fbf,0x10fe0c0,0x106041,0x105fc1,0x11020bf,0x10fe0bf,0x11020c1,0x10fe0c1,0x11060c0,0x11060bf,0x91060c1,0x3101f40,0x30fdf40,0x3101f3f,0x30fdf3f,0x3101f41,0x30fdf41,0x1b0f9fc0,0x180fa040,0x180fa03f,0x1b0f9fbf,0x3105f40,0xb105f3f,0x3701fbe,0x70203e,0x186fe03e,0x180fa041,0x1b0f9fc1,0x1b6fdfbe,0xb105f41,0x3301fc2,0x302042,0x190fa0c0,0x190fa0bf,0x182fe042,0x1b2fdfc2,0x70603e,0xb705fbe,0x97020be,0x196fe0be,0x190fa0c1,0x930a040,0xb309fc0,0x8306042,0xb305fc2,0x93020c2,0x192fe0c2,0xa70a03f, + 15,0x10203f,0x101fbf,0x101fc0,0xfe040,0xfe03f,0xfdfbf,0xfdfc0,0x1020c0,0x1020bf,0x10603f,0x106040,0x105fc0,0x105fbf,0xfe0bf,0xfe0c0,0x302041,0x1060c0,0x1060bf,0x301fc1,0x2fe041,0x2fdfc1,0x70203e,0x701fbe,0x6fe03e,0x6fdfbe,0x3020c1,0x306041,0x305fc1,0x2fe0c1,0x7020be,0x70603e,0x705fbe,0x3101f3f,0x3101f40,0x180fa040,0x180fa03f,0x6fe0be,0x180f9fbf,0x180f9fc0,0x1b0fdf40,0x1b0fdf3f,0x93060c1,0x97060be,0x3105f3f,0x3105f40,0x180fa0c0,0x180fa0bf,0x192fa041,0x182f9fc1,0xb301f41,0x1b2fdf41,0xb701f3e,0x36fdf3e,0x196fa03e,0x1b6f9fbe,0x970213f,0x9302140,0x192fe140,0x196fe13f,0x970a03f,0x930a040,0xb309fc0,0xb709fbf, + 15,0x10203f,0x101fc0,0xfe040,0xfe03f,0x101fbf,0xfdfc0,0xfdfbf,0x1020c0,0x106040,0x10603f,0x1020bf,0xfe0c0,0xfe0bf,0x105fc0,0x105fbf,0x302041,0x301fc1,0x2fe041,0x2fdfc1,0x1060c0,0x1060bf,0x3020c1,0x306041,0x305fc1,0x2fe0c1,0x70203e,0x6fe03e,0x701fbe,0x6fdfbe,0x93060c1,0x3101f40,0x3101f3f,0x180fa040,0x186fa03f,0x180f9fc0,0x1b0fdf40,0x30fdf3f,0x1b6f9fbf,0x7020be,0x70603e,0x705fbe,0x6fe0be,0x186fa0bf,0x180fa0c0,0x3105f40,0x3105f3f,0x3301f41,0x32fdf41,0x182fa041,0x1b2f9fc1,0x97060be,0x970a03f,0x930a040,0xb309fc0,0xb305f41,0x9302140,0x970213f,0x192fe140,0x192fa0c1,0x196fe13f,0xb709fbf,0xb701f3e,0x1b6fdf3e, + 16,0x10203f,0x101fc0,0xfe040,0xfe03f,0x101fbf,0xfdfc0,0xfdfbf,0x102041,0x1020c0,0x106040,0x10603f,0x1020bf,0xfe0c0,0xfe041,0x101fc1,0x105fc0,0x105fbf,0xfe0bf,0xfdfc1,0x1020c1,0x106041,0x1060c0,0x1060bf,0xfe0c1,0x105fc1,0x93060c1,0x70203e,0x3101f40,0x180fa040,0x180fa03f,0x186fe03e,0x701fbe,0x3701f3f,0x30fdf40,0x1b0f9fc0,0x180f9fbf,0x186fdfbe,0x1b6fdf3f,0x3101f41,0x3105f40,0xb105f3f,0x70603e,0x7020be,0x6fe0be,0x180fa0c0,0x180fa041,0x182f9fc1,0x1b2fdf41,0xb705fbe,0x186fa0bf,0x192fa0c1,0x97060be,0xb305f41,0x9302042,0x192fe042,0x13301fc2,0x930a040,0x970a03f,0xb509fc0,0x9302140,0x970213f,0x192fe140,0x196fe13f, + 15,0x101fc0,0xfe040,0xfdfc0,0x10203f,0x101fbf,0xfe03f,0x102041,0x101fc1,0xfdfbf,0xfe041,0x1020c0,0x106040,0xfdfc1,0x105fc0,0xfe0c0,0x1020bf,0x10603f,0x105fbf,0xfe0bf,0x1020c1,0x106041,0x105fc1,0xfe0c1,0x1060c0,0x11060bf,0x91060c1,0x3101f40,0x180fa040,0x180f9fc0,0x1b0fdf40,0x3101f3f,0x30fdf3f,0x180fa03f,0x1b0f9fbf,0x180fa041,0x180f9fc1,0x3101f41,0x1b0fdf41,0x70203e,0x3701fbe,0x186fe03e,0x1b6fdfbe,0x3105f40,0xb105f3f,0x180fa0c0,0x190fa0bf,0x192fa0c1,0x302042,0x3301fc2,0xb305f41,0x182fe042,0x1b2fdfc2,0x17020be,0x170603e,0xb705fbe,0x196fe0be,0x9502140,0x194fe140,0x193020c2,0xa306042,0x930a040,0xb309fc0,0xa70a03f, + 15,0x10203f,0xfe03f,0xfe040,0x101fc0,0x101fbf,0xfdfbf,0xfdfc0,0x1020c0,0x1020bf,0xfe0bf,0xfe0c0,0x106040,0x10603f,0x105fbf,0x105fc0,0x1060c0,0x1060bf,0x302041,0x2fe041,0x301fc1,0x2fdfc1,0x70203e,0x6fe03e,0x3020c1,0x2fe0c1,0x306041,0x701fbe,0x6fdfbe,0x7020be,0x6fe0be,0x70603e,0x305fc1,0x83060c1,0x180fa040,0x180fa03f,0x180f9fbf,0x705fbe,0x87060be,0x180f9fc0,0x180fa0c0,0x180fa0bf,0x3701f3f,0x3101f40,0x1b0fdf40,0x1b6fdf3f,0x3705f3f,0x3105f40,0x1302140,0x170213f,0x196fe13f,0x192fe140,0x182fa041,0x1b2f9fc1,0x192fa0c1,0x196fa03e,0x1b6f9fbe,0x970a03f,0x930a040,0x9306140,0x970613f,0xb709fbf,0xb309fc0,0x13301f41, + 14,0x10203f,0xfe040,0xfe03f,0x101fc0,0x101fbf,0xfdfc0,0xfdfbf,0x1020c0,0x1020bf,0xfe0c0,0xfe0bf,0x106040,0x10603f,0x105fc0,0x302041,0x105fbf,0x2fe041,0x1060c0,0x1060bf,0x301fc1,0x2fdfc1,0x3020c1,0x2fe0c1,0x306041,0x305fc1,0x83060c1,0x70203e,0x6fe03e,0x701fbe,0x6fdfbe,0x7020be,0x6fe0be,0x186fa03f,0x180fa040,0x180f9fc0,0x186f9fbf,0x70603e,0x8705fbe,0x3701f3f,0x3101f40,0x1b0fdf40,0x180fa0c0,0x186fa0bf,0x1b6fdf3f,0x87060be,0x170213f,0x1102140,0x182fa041,0x182f9fc1,0x190fe140,0x196fe13f,0x3105f40,0xb705f3f,0xb301f41,0x1b2fdf41,0x182fa0c1,0x930a040,0x970a03f,0x9106140,0x970613f,0x9302141,0x192fe141,0xb509fc0, + 15,0x10203f,0xfe040,0xfe03f,0x101fc0,0x101fbf,0xfdfc0,0x1020c0,0x1020bf,0xfdfbf,0xfe0c0,0x102041,0x106040,0xfe0bf,0x10603f,0xfe041,0x101fc1,0x105fc0,0x105fbf,0xfdfc1,0x1020c1,0x1060c0,0x1060bf,0xfe0c1,0x106041,0x305fc1,0x83060c1,0x70203e,0x180fa040,0x180fa03f,0x186fe03e,0x701fbe,0x6fdfbe,0x180f9fc0,0x186f9fbf,0x180fa0c0,0x180fa0bf,0x7020be,0x186fe0be,0x3101f40,0x3701f3f,0x1b0fdf40,0x1b6fdf3f,0x70603e,0x8705fbe,0x180fa041,0x182f9fc1,0x192fa0c1,0x1102140,0x170213f,0x97060be,0x190fe140,0x196fe13f,0x3301f41,0x3305f40,0xb705f3f,0x1b2fdf41,0xa302042,0x1a2fe042,0x19302141,0x9506140,0x930a040,0x970a03f,0xb509fc0, + 17,0xfe040,0x10203f,0x101fc0,0xfdfc0,0xfe03f,0x1020c0,0x102041,0x101fbf,0xfe041,0xfe0c0,0x106040,0xfdfbf,0x1020bf,0x101fc1,0xfdfc1,0xfe0bf,0x1020c1,0x10603f,0x105fc0,0xfe0c1,0x1060c0,0x106041,0x8105fbf,0x81060bf,0x8105fc1,0x81060c1,0x180fa040,0x180f9fc0,0x180fa03f,0x180fa0c0,0x180fa041,0x180f9fbf,0x70203e,0x186fe03e,0x3101f40,0x1b0fdf40,0x180f9fc1,0x180fa0bf,0x701fbe,0x3701f3f,0x1b0fdf3f,0x1b6fdfbe,0x7020be,0x186fe0be,0x192fa0c1,0x9102140,0x190fe140,0x8302042,0x3101f41,0x1b0fdf41,0x182fe042,0xb301fc2,0xb305f40,0x870603e,0x970213f,0x196fe13f,0x11102141,0x113020c2,0x1b2fdfc2,0xb105f3f,0xb705fbe,0x97060be,0xa50a040, + 11,0x10203f,0x101fc0,0x101fbf,0xfdfbf,0xfe03f,0xfe040,0xfdfc0,0x105fc0,0x106040,0x10603f,0x105fbf,0x11020bf,0x11020c0,0x302041,0x301fc1,0x2fdfc1,0x2fe041,0x10fe0c0,0x10fe0bf,0x70203e,0x701fbe,0x3101f3f,0x3101f40,0x305fc1,0x306041,0x11060c0,0x11060bf,0x6fe03e,0x6fdfbe,0x30fdf3f,0x30fdf40,0x3105f40,0x3105f3f,0x705fbe,0x70603e,0x13020c1,0x12fe0c1,0x180fa040,0x180fa03f,0x180f9fbf,0x180f9fc0,0x3301f41,0x17020be,0x16fe0be,0x93060c1,0x32fdf41,0xb305f41,0x3701f3e,0x36fdf3e,0x97060be,0x970a03f,0x930a040,0xb309fc0,0xb709fbf,0xb705f3e,0x182f9fc1,0x182fa041,0x192fa0c0,0x190fa0bf,0x196fa03e,0x186f9fbe,0x1b6f9f3f,0x1b2f9f40, + 11,0x101fc0,0x10203f,0x101fbf,0xfdfc0,0xfe040,0xfe03f,0xfdfbf,0x105fc0,0x106040,0x10603f,0x105fbf,0x301fc1,0x302041,0x2fe041,0x2fdfc1,0x11020c0,0x11020bf,0x306041,0x305fc1,0x10fe0c0,0x10fe0bf,0x3101f40,0x3101f3f,0x11060c0,0x11060bf,0x70203e,0x30fdf40,0x30fdf3f,0x701fbe,0x3105f40,0x3105f3f,0x13020c1,0x6fe03e,0x6fdfbe,0x705fbe,0x70603e,0x12fe0c1,0x3301f41,0x13060c1,0x32fdf41,0x1b0f9fc0,0x180fa040,0x186fa03f,0x1b6f9fbf,0x3305f41,0xb109fc0,0x17020be,0x16fe0be,0x810a040,0x870a03f,0xb709fbf,0x3701f3e,0x1b6fdf3e,0x17060be,0x182fa041,0x1b2f9fc1,0x192fa0c0,0x196fa0bf,0xb705f3e,0xb309fc1,0x830a041,0x930a0c0,0x970a0bf, + 15,0x101fc0,0x101fbf,0x10203f,0xfe040,0xfdfc0,0xfdfbf,0xfe03f,0x106040,0x105fc0,0x105fbf,0x10603f,0x102041,0x101fc1,0xfdfc1,0xfe041,0x106041,0x105fc1,0x11020c0,0x11020bf,0x10fe0c0,0x10fe0bf,0x3101f40,0x3101f3f,0x11060c0,0x11060bf,0x11020c1,0x30fdf40,0x30fdf3f,0x3105f40,0x3105f3f,0x3101f41,0x10fe0c1,0x13060c1,0x70203e,0x701fbe,0x6fdfbe,0x30fdf41,0x3305f41,0x6fe03e,0x70603e,0x705fbe,0x1b0f9fc0,0x180fa040,0x186fa03f,0x1b6f9fbf,0x1b0f9fc1,0x180fa041,0x910a040,0xb109fc0,0xb709fbf,0x970a03f,0x17020be,0x196fe0be,0x97060be,0xb701f3e,0x1b6fdf3e,0xb301fc2,0x9302042,0x930a041,0xb309fc1,0x1b2fdfc2,0x192fe042,0x194fa0c0, + 17,0x101fc0,0x101fbf,0x10203f,0xfe040,0xfdfc0,0x101fc1,0x102041,0x106040,0x105fc0,0xfdfbf,0xfe03f,0x10603f,0x105fbf,0xfdfc1,0xfe041,0x106041,0x105fc1,0x11020c0,0x11020bf,0x10fe0c0,0x11020c1,0x11060c0,0x3101f40,0x3101f3f,0x30fdf40,0x10fe0bf,0x11060bf,0x10fe0c1,0x3101f41,0x3105f40,0x30fdf3f,0x11060c1,0x3105f3f,0x30fdf41,0x3105f41,0x3701fbe,0x70203e,0x180fa040,0x1b0f9fc0,0x1b0f9fbf,0x180fa03f,0x186fe03e,0x1b6fdfbe,0x3705fbe,0x70603e,0x910a040,0xb109fc0,0x3301fc2,0x1302042,0x180fa041,0x1b0f9fc1,0x1b2fdfc2,0x192fe042,0x1306042,0x3305fc2,0xb709fbf,0x970a03f,0x930a041,0xb309fc1,0x97020be,0x192fa0c0,0x190fa0bf,0x196fe0be, + 11,0x10203f,0x101fc0,0x101fbf,0xfe03f,0xfe040,0xfdfc0,0xfdfbf,0x10603f,0x106040,0x105fc0,0x105fbf,0x11020bf,0x11020c0,0x10fe0c0,0x10fe0bf,0x302041,0x301fc1,0x11060c0,0x11060bf,0x2fe041,0x2fdfc1,0x70203e,0x701fbe,0x306041,0x305fc1,0x3101f40,0x6fe03e,0x6fdfbe,0x3101f3f,0x70603e,0x705fbe,0x13020c1,0x30fdf40,0x30fdf3f,0x3105f3f,0x3105f40,0x12fe0c1,0x17020be,0x13060c1,0x16fe0be,0x186fa03f,0x180fa040,0x1b0f9fc0,0x1b6f9fbf,0x17060be,0x870a03f,0x3301f41,0x32fdf41,0x810a040,0xb109fc0,0xb709fbf,0x3701f3e,0x1b6fdf3e,0x3305f41,0x190fa0c0,0x196fa0bf,0x192fa041,0x1b2f9fc1,0xb705f3e,0x970a0bf,0x910a0c0,0x930a041,0xb309fc1, + 11,0x10203f,0x101fc0,0x101fbf,0xfe040,0xfe03f,0xfdfc0,0xfdfbf,0x106040,0x10603f,0x105fc0,0x105fbf,0x302041,0x11020c0,0x11020bf,0x301fc1,0x2fe041,0x10fe0c0,0x10fe0bf,0x2fdfc1,0x306041,0x11060c0,0x11060bf,0x305fc1,0x13020c1,0x12fe0c1,0x70203e,0x701fbe,0x3101f3f,0x3101f40,0x30fdf40,0x30fdf3f,0x6fdfbe,0x6fe03e,0x70603e,0x13060c1,0x3105f40,0x3105f3f,0x705fbe,0x17020be,0x180fa040,0x186fa03f,0x1b0f9fc0,0x1b6f9fbf,0x3301f41,0x32fdf41,0x3305f41,0x16fe0be,0x17060be,0x870a03f,0x810a040,0xb109fc0,0xb709fbf,0x3701f3e,0x182fa041,0x192fa0c0,0x196fa0bf,0x1b2f9fc1,0x1b6fdf3e,0xb705f3e,0x830a041,0x930a0c0,0x970a0bf,0xb309fc1, + 14,0x101fc0,0x10203f,0x101fbf,0xfe040,0xfdfc0,0xfe03f,0xfdfbf,0x106040,0x105fc0,0x10603f,0x105fbf,0x102041,0x101fc1,0xfe041,0x11020c0,0xfdfc1,0x11020bf,0x106041,0x105fc1,0x10fe0c0,0x10fe0bf,0x11060c0,0x11060bf,0x11020c1,0x10fe0c1,0x13060c1,0x3101f40,0x3101f3f,0x30fdf40,0x30fdf3f,0x3105f40,0x3105f3f,0x3701fbe,0x70203e,0x6fe03e,0x36fdfbe,0x3101f41,0x32fdf41,0x1b0f9fc0,0x180fa040,0x186fa03f,0x70603e,0x3705fbe,0x1b6f9fbf,0x3305f41,0xb109fc0,0x810a040,0x17020be,0x16fe0be,0x870a03f,0xb709fbf,0x180fa041,0x1b2f9fc1,0x192fa0c0,0x196fa0bf,0x17060be,0x9302042,0xb301fc2,0x830a041,0xb309fc1,0x930a0c0,0x970a0bf,0x1a2fe042, + 17,0x101fc0,0x10203f,0xfe040,0xfdfc0,0x101fbf,0x106040,0x102041,0x101fc1,0x105fc0,0xfe03f,0xfdfbf,0x10603f,0x105fbf,0xfe041,0xfdfc1,0x106041,0x105fc1,0x11020c0,0x11020bf,0x10fe0c0,0x11020c1,0x11060c0,0x10fe0bf,0x11060bf,0x10fe0c1,0x11060c1,0x3101f40,0x30fdf40,0x3101f3f,0x3105f40,0x3101f41,0x30fdf3f,0x3105f3f,0x30fdf41,0x3105f41,0x70203e,0x3701fbe,0x180fa040,0x1b0f9fc0,0x180fa03f,0x186fe03e,0x36fdfbe,0x1b6f9fbf,0x180fa041,0x1b0f9fc1,0x1302042,0x3301fc2,0x910a040,0x70603e,0x3705fbe,0xb109fc0,0x970a03f,0xb709fbf,0x910a041,0x192fe042,0x1b2fdfc2,0x9306042,0x3305fc2,0xb309fc1,0x97020be,0x192fa0c0,0x190fa0bf,0x196fe0be, + 15,0x10203f,0x101fbf,0x101fc0,0xfe040,0xfe03f,0xfdfbf,0xfdfc0,0x106040,0x10603f,0x105fbf,0x105fc0,0x1020c0,0x1020bf,0xfe0bf,0xfe0c0,0x1060c0,0x1060bf,0x302041,0x301fc1,0x2fe041,0x2fdfc1,0x70203e,0x701fbe,0x306041,0x305fc1,0x3020c1,0x6fe03e,0x6fdfbe,0x70603e,0x705fbe,0x7020be,0x2fe0c1,0x13060c1,0x3101f40,0x3101f3f,0x30fdf3f,0x6fe0be,0x17060be,0x30fdf40,0x3105f40,0x3105f3f,0x186fa03f,0x180fa040,0x1b0f9fc0,0x1b6f9fbf,0x186fa0bf,0x180fa0c0,0x830a040,0x870a03f,0xb709fbf,0xb309fc0,0x3301f41,0x1b2fdf41,0xb305f41,0xb701f3e,0x1b6fdf3e,0x970213f,0x9302140,0x930a0c0,0x970a0bf,0x196fe13f,0x192fe140,0x1a2fa041, + 14,0x10203f,0x101fc0,0x101fbf,0xfe040,0xfe03f,0xfdfc0,0xfdfbf,0x106040,0x10603f,0x105fc0,0x105fbf,0x1020c0,0x1020bf,0xfe0c0,0x302041,0xfe0bf,0x301fc1,0x1060c0,0x1060bf,0x2fe041,0x2fdfc1,0x306041,0x305fc1,0x3020c1,0x2fe0c1,0x13060c1,0x70203e,0x701fbe,0x6fe03e,0x6fdfbe,0x70603e,0x705fbe,0x3701f3f,0x3101f40,0x30fdf40,0x36fdf3f,0x7020be,0x16fe0be,0x186fa03f,0x180fa040,0x1b0f9fc0,0x3105f40,0x3705f3f,0x1b6f9fbf,0x17060be,0x870a03f,0x810a040,0x3301f41,0x32fdf41,0xb109fc0,0xb709fbf,0x180fa0c0,0x196fa0bf,0x192fa041,0x1b2f9fc1,0x3305f41,0x9302140,0x970213f,0x910a0c0,0x970a0bf,0x930a041,0xb309fc1,0x194fe140, + 15,0x10203f,0x101fc0,0x101fbf,0xfe040,0xfe03f,0xfdfc0,0x106040,0x10603f,0xfdfbf,0x105fc0,0x102041,0x1020c0,0x105fbf,0x1020bf,0x101fc1,0xfe041,0xfe0c0,0xfe0bf,0xfdfc1,0x106041,0x1060c0,0x1060bf,0x105fc1,0x1020c1,0x2fe0c1,0x13060c1,0x70203e,0x3101f40,0x3101f3f,0x3701fbe,0x6fe03e,0x6fdfbe,0x30fdf40,0x36fdf3f,0x3105f40,0x3105f3f,0x70603e,0x3705fbe,0x180fa040,0x186fa03f,0x1b0f9fc0,0x1b6f9fbf,0x7020be,0x16fe0be,0x3101f41,0x32fdf41,0xb305f41,0x810a040,0x870a03f,0x97060be,0xb109fc0,0xb709fbf,0x182fa041,0x182fa0c0,0x196fa0bf,0x1b2f9fc1,0x11302042,0x13301fc2,0xb30a041,0x950a0c0,0x9302140,0x970213f,0x194fe140, + 17,0x101fc0,0x10203f,0xfe040,0xfdfc0,0x101fbf,0x106040,0x102041,0xfe03f,0x101fc1,0x105fc0,0x1020c0,0xfdfbf,0x10603f,0xfe041,0xfdfc1,0x105fbf,0x106041,0x1020bf,0xfe0c0,0x105fc1,0x1060c0,0x1020c1,0x10fe0bf,0x11060bf,0x10fe0c1,0x11060c1,0x3101f40,0x30fdf40,0x3101f3f,0x3105f40,0x3101f41,0x30fdf3f,0x70203e,0x3701fbe,0x180fa040,0x1b0f9fc0,0x30fdf41,0x3105f3f,0x6fe03e,0x186fa03f,0x1b0f9fbf,0x1b6fdfbe,0x70603e,0x3705fbe,0xb305f41,0x910a040,0xb109fc0,0x1302042,0x180fa041,0x1b0f9fc1,0x3301fc2,0x192fe042,0x192fa0c0,0x17020be,0x970a03f,0xb709fbf,0xa10a041,0xa306042,0x1b2fdfc2,0x190fa0bf,0x196fe0be,0x97060be,0x11502140, + 17,0x10203f,0x101fbf,0x101fc0,0xfe040,0xfe03f,0x1020bf,0x1020c0,0x106040,0x10603f,0xfdfbf,0xfdfc0,0x105fc0,0x105fbf,0xfe0bf,0xfe0c0,0x1060c0,0x1060bf,0x302041,0x301fc1,0x2fe041,0x3020c1,0x306041,0x70203e,0x701fbe,0x6fe03e,0x2fdfc1,0x305fc1,0x2fe0c1,0x7020be,0x70603e,0x6fdfbe,0x3060c1,0x705fbe,0x6fe0be,0x7060be,0x3701f3f,0x3101f40,0x180fa040,0x186fa03f,0x186f9fbf,0x180f9fc0,0x1b0fdf40,0x1b6fdf3f,0x3705f3f,0x3105f40,0x830a040,0x870a03f,0x170213f,0x1302140,0x180fa0c0,0x186fa0bf,0x196fe13f,0x192fe140,0x1306140,0x170613f,0xb709fbf,0xb309fc0,0x930a0c0,0x970a0bf,0xb301f41,0x192fa041,0x182f9fc1,0x1b2fdf41, + 17,0x10203f,0x101fc0,0xfe040,0xfe03f,0x101fbf,0x106040,0x1020c0,0x1020bf,0x10603f,0xfdfc0,0xfdfbf,0x105fc0,0x105fbf,0xfe0c0,0xfe0bf,0x1060c0,0x1060bf,0x302041,0x301fc1,0x2fe041,0x3020c1,0x306041,0x2fdfc1,0x305fc1,0x2fe0c1,0x3060c1,0x70203e,0x6fe03e,0x701fbe,0x70603e,0x7020be,0x6fdfbe,0x705fbe,0x6fe0be,0x7060be,0x3101f40,0x3701f3f,0x180fa040,0x186fa03f,0x180f9fc0,0x1b0fdf40,0x36fdf3f,0x1b6f9fbf,0x180fa0c0,0x186fa0bf,0x1302140,0x170213f,0x830a040,0x3105f40,0x3705f3f,0x870a03f,0xb309fc0,0xb709fbf,0x830a0c0,0x192fe140,0x196fe13f,0x9306140,0x170613f,0x970a0bf,0xb301f41,0x192fa041,0x182f9fc1,0x1b2fdf41, + 17,0x10203f,0x101fc0,0xfe040,0xfe03f,0x101fbf,0x106040,0x1020c0,0xfdfc0,0x1020bf,0x10603f,0x102041,0xfdfbf,0x105fc0,0xfe0c0,0xfe0bf,0x105fbf,0x1060c0,0x101fc1,0xfe041,0x1060bf,0x106041,0x1020c1,0x2fdfc1,0x305fc1,0x2fe0c1,0x3060c1,0x70203e,0x6fe03e,0x701fbe,0x70603e,0x7020be,0x6fdfbe,0x3101f40,0x3701f3f,0x180fa040,0x186fa03f,0x6fe0be,0x705fbe,0x30fdf40,0x1b0f9fc0,0x186f9fbf,0x1b6fdf3f,0x3105f40,0x3705f3f,0x97060be,0x830a040,0x870a03f,0x1302140,0x180fa0c0,0x186fa0bf,0x170213f,0x192fe140,0x192fa041,0x3301f41,0xb309fc0,0xb709fbf,0x850a0c0,0x9506140,0x196fe13f,0x182f9fc1,0x1b2fdf41,0xb305f41,0x12302042, + 16,0x10203f,0x101fc0,0xfe040,0x102041,0x1020c0,0x106040,0x101fbf,0xfe03f,0xfdfc0,0x101fc1,0x105fc0,0x10603f,0x1020bf,0xfe0c0,0xfe041,0xfdfbf,0x1020c1,0x106041,0x1060c0,0x105fbf,0xfe0bf,0xfdfc1,0x105fc1,0x1060bf,0xfe0c1,0x83060c1,0x70203e,0x3101f40,0x180fa040,0x180fa03f,0x186fe03e,0x701fbe,0x3701f3f,0x30fdf40,0x1b0f9fc0,0x180fa041,0x180fa0c0,0x7020be,0x70603e,0x3105f40,0xb101f41,0x9302042,0x1102140,0x930a040,0x6fdfbe,0x36fdf3f,0x1b6f9fbf,0x190fa0bf,0x196fe0be,0x170213f,0x196fe140,0x182f9fc1,0x1b2fdf41,0xb301fc2,0x1a2fe042,0x192fa0c1,0x8705fbe,0xb705f3f,0xb309fc0,0xa70a03f,0x97060be,0x9706140,0x11302141 +}; diff --git a/src/third_party/voro/src/v_compute.cc b/src/third_party/voro/src/v_compute.cc new file mode 100644 index 000000000..847ef9ed0 --- /dev/null +++ b/src/third_party/voro/src/v_compute.cc @@ -0,0 +1,1006 @@ +// Voro++, a 3D cell-based Voronoi library +// +// Author : Chris H. Rycroft (Harvard University / LBL) +// Email : chr@alum.mit.edu +// Date : August 30th 2011 + +/** \file v_compute.cc + * \brief Function implementantions for the voro_compute template. */ + +#include "worklist.hh" +#include "v_compute.hh" +#include "rad_option.hh" +#include "container.hh" +#include "container_prd.hh" + +namespace voro { + +/** The class constructor initializes constants from the container class, and + * sets up the mask and queue used for Voronoi computations. + * \param[in] con_ a reference to the container class to use. + * \param[in] (hx_,hy_,hz_) the size of the mask to use. */ +template +voro_compute::voro_compute(c_class &con_,int hx_,int hy_,int hz_) : + con(con_), boxx(con_.boxx), boxy(con_.boxy), boxz(con_.boxz), + xsp(con_.xsp), ysp(con_.ysp), zsp(con_.zsp), + hx(hx_), hy(hy_), hz(hz_), hxy(hx_*hy_), hxyz(hxy*hz_), ps(con_.ps), + id(con_.id), p(con_.p), co(con_.co), bxsq(boxx*boxx+boxy*boxy+boxz*boxz), + mv(0), qu_size(3*(3+hxy+hz*(hx+hy))), wl(con_.wl), mrad(con_.mrad), + mask(new unsigned int[hxyz]), qu(new int[qu_size]), qu_l(qu+qu_size) { + reset_mask(); +} + +/** Scans all of the particles within a block to see if any of them have a + * smaller distance to the given test vector. If one is found, the routine + * updates the minimum distance and store information about this particle. + * \param[in] ijk the index of the block. + * \param[in] (x,y,z) the test vector to consider (which may have already had a + * periodic displacement applied to it). + * \param[in] (di,dj,dk) the coordinates of the current block, to store if the + * particle record is updated. + * \param[in,out] w a reference to a particle record in which to store + * information about the particle whose Voronoi cell the + * vector is within. + * \param[in,out] mrs the current minimum distance, that may be updated if a + * closer particle is found. */ +template +inline void voro_compute::scan_all(int ijk,double x,double y,double z,int di,int dj,int dk,particle_record &w,double &mrs) { + double x1,y1,z1,rs;bool in_block=false; + for(int l=0;l +void voro_compute::find_voronoi_cell(double x,double y,double z,int ci,int cj,int ck,int ijk,particle_record &w,double &mrs) { + double qx=0,qy=0,qz=0,rs; + int i,j,k,di,dj,dk,ei,ej,ek,f,g,disp; + double fx,fy,fz,mxs,mys,mzs,*radp; + unsigned int q,*e,*mijk; + + // Init setup for parameters to return + w.ijk=-1;mrs=large_number; + + con.initialize_search(ci,cj,ck,ijk,i,j,k,disp); + + // Test all particles in the particle's local region first + scan_all(ijk,x,y,z,0,0,0,w,mrs); + + // Now compute the fractional position of the particle within its + // region and store it in (fx,fy,fz). We use this to compute an index + // (di,dj,dk) of which subregion the particle is within. + unsigned int m1,m2; + con.frac_pos(x,y,z,ci,cj,ck,fx,fy,fz); + di=int(fx*xsp*wl_fgrid);dj=int(fy*ysp*wl_fgrid);dk=int(fz*zsp*wl_fgrid); + + // The indices (di,dj,dk) tell us which worklist to use, to test the + // blocks in the optimal order. But we only store worklists for the + // eighth of the region where di, dj, and dk are all less than half the + // full grid. The rest of the cases are handled by symmetry. In this + // section, we detect for these cases, by reflecting high values of di, + // dj, and dk. For these cases, a mask is constructed in m1 and m2 + // which is used to flip the worklist information when it is loaded. + if(di>=wl_hgrid) { + mxs=boxx-fx; + m1=127+(3<<21);m2=1+(1<<21);di=wl_fgrid-1-di;if(di<0) di=0; + } else {m1=m2=0;mxs=fx;} + if(dj>=wl_hgrid) { + mys=boxy-fy; + m1|=(127<<7)+(3<<24);m2|=(1<<7)+(1<<24);dj=wl_fgrid-1-dj;if(dj<0) dj=0; + } else mys=fy; + if(dk>=wl_hgrid) { + mzs=boxz-fz; + m1|=(127<<14)+(3<<27);m2|=(1<<14)+(1<<27);dk=wl_fgrid-1-dk;if(dk<0) dk=0; + } else mzs=fz; + + // Do a quick test to account for the case when the minimum radius is + // small enought that no other blocks need to be considered + rs=con.r_max_add(mrs); + if(mxs*mxs>rs&&mys*mys>rs&&mzs*mzs>rs) return; + + // Now compute which worklist we are going to use, and set radp and e to + // point at the right offsets + ijk=di+wl_hgrid*(dj+wl_hgrid*dk); + radp=mrad+ijk*wl_seq_length; + e=(const_cast (wl))+ijk*wl_seq_length; + + // Read in how many items in the worklist can be tested without having to + // worry about writing to the mask + f=e[0];g=0; + do { + + // If mrs is less than the minimum distance to any untested + // block, then we are done + if(con.r_max_add(mrs)>7)&127;dj-=64; + dk=(q>>14)&127;dk-=64; + + // Check that the worklist position is in range + ei=di+i;if(ei<0||ei>=hx) continue; + ej=dj+j;if(ej<0||ej>=hy) continue; + ek=dk+k;if(ek<0||ek>=hz) continue; + + // Call the compute_min_max_radius() function. This returns + // true if the minimum distance to the block is bigger than the + // current mrs, in which case we skip this block and move on. + // Otherwise, it computes the maximum distance to the block and + // returns it in crs. + if(compute_min_radius(di,dj,dk,fx,fy,fz,mrs)) continue; + + // Now compute which region we are going to loop over, adding a + // displacement for the periodic cases + ijk=con.region_index(ci,cj,ck,ei,ej,ek,qx,qy,qz,disp); + + // If mrs is bigger than the maximum distance to the block, + // then we have to test all particles in the block for + // intersections. Otherwise, we do additional checks and skip + // those particles which can't possibly intersect the block. + scan_all(ijk,x-qx,y-qy,z-qz,di,dj,dk,w,mrs); + } while(g>7)&127;dj-=64; + dk=(q>>14)&127;dk-=64; + + // Compute the position in the mask of the current block. If + // this lies outside the mask, then skip it. Otherwise, mark + // it. + ei=di+i;if(ei<0||ei>=hx) continue; + ej=dj+j;if(ej<0||ej>=hy) continue; + ek=dk+k;if(ek<0||ek>=hz) continue; + mijk=mask+ei+hx*(ej+hy*ek); + *mijk=mv; + + // Skip this block if it is further away than the current + // minimum radius + if(compute_min_radius(di,dj,dk,fx,fy,fz,mrs)) continue; + + // Now compute which region we are going to loop over, adding a + // displacement for the periodic cases + ijk=con.region_index(ci,cj,ck,ei,ej,ek,qx,qy,qz,disp); + scan_all(ijk,x-qx,y-qy,z-qz,di,dj,dk,w,mrs); + + if(qu_e>qu_l-18) add_list_memory(qu_s,qu_e); + scan_bits_mask_add(q,mijk,ei,ej,ek,qu_e); + } + + // Do a check to see if we've reached the radius cutoff + if(con.r_max_add(mrs) +inline void voro_compute::add_to_mask(int ei,int ej,int ek,int *&qu_e) { + unsigned int *mijk=mask+ei+hx*(ej+hy*ek); + if(ek>0) if(*(mijk-hxy)!=mv) {if(qu_e==qu_l) qu_e=qu;*(mijk-hxy)=mv;*(qu_e++)=ei;*(qu_e++)=ej;*(qu_e++)=ek-1;} + if(ej>0) if(*(mijk-hx)!=mv) {if(qu_e==qu_l) qu_e=qu;*(mijk-hx)=mv;*(qu_e++)=ei;*(qu_e++)=ej-1;*(qu_e++)=ek;} + if(ei>0) if(*(mijk-1)!=mv) {if(qu_e==qu_l) qu_e=qu;*(mijk-1)=mv;*(qu_e++)=ei-1;*(qu_e++)=ej;*(qu_e++)=ek;} + if(ei +inline void voro_compute::scan_bits_mask_add(unsigned int q,unsigned int *mijk,int ei,int ej,int ek,int *&qu_e) { + const unsigned int b1=1<<21,b2=1<<22,b3=1<<24,b4=1<<25,b5=1<<27,b6=1<<28; + if((q&b2)==b2) { + if(ei>0) {*(mijk-1)=mv;*(qu_e++)=ei-1;*(qu_e++)=ej;*(qu_e++)=ek;} + if((q&b1)==0&&ei0) {*(mijk-hx)=mv;*(qu_e++)=ei;*(qu_e++)=ej-1;*(qu_e++)=ek;} + if((q&b3)==0&&ej0) {*(mijk-hxy)=mv;*(qu_e++)=ei;*(qu_e++)=ej;*(qu_e++)=ek-1;} + if((q&b5)==0&&ek +template +bool voro_compute::compute_cell(v_cell &c,int ijk,int s,int ci,int cj,int ck) { + static const int count_list[8]={7,11,15,19,26,35,45,59},*count_e=count_list+8; + double x,y,z,x1,y1,z1,qx=0,qy=0,qz=0; + double xlo,ylo,zlo,xhi,yhi,zhi,x2,y2,z2,rs; + int i,j,k,di,dj,dk,ei,ej,ek,f,g,l,disp; + double fx,fy,fz,gxs,gys,gzs,*radp; + unsigned int q,*e,*mijk; + + if(!con.initialize_voronoicell(c,ijk,s,ci,cj,ck,i,j,k,x,y,z,disp)) return false; + con.r_init(ijk,s); + + // Initialize the Voronoi cell to fill the entire container + double crs,mrs; + + int next_count=3,*count_p=(const_cast (count_list)); + + // Test all particles in the particle's local region first + for(l=0;l=wl_hgrid) { + gxs=fx; + m1=127+(3<<21);m2=1+(1<<21);di=wl_fgrid-1-di;if(di<0) di=0; + } else {m1=m2=0;gxs=boxx-fx;} + if(dj>=wl_hgrid) { + gys=fy; + m1|=(127<<7)+(3<<24);m2|=(1<<7)+(1<<24);dj=wl_fgrid-1-dj;if(dj<0) dj=0; + } else gys=boxy-fy; + if(dk>=wl_hgrid) { + gzs=fz; + m1|=(127<<14)+(3<<27);m2|=(1<<14)+(1<<27);dk=wl_fgrid-1-dk;if(dk<0) dk=0; + } else gzs=boxz-fz; + gxs*=gxs;gys*=gys;gzs*=gzs; + + // Now compute which worklist we are going to use, and set radp and e to + // point at the right offsets + ijk=di+wl_hgrid*(dj+wl_hgrid*dk); + radp=mrad+ijk*wl_seq_length; + e=(const_cast (wl))+ijk*wl_seq_length; + + // Read in how many items in the worklist can be tested without having to + // worry about writing to the mask + f=e[0];g=0; + do { + + // At the intervals specified by count_list, we recompute the + // maximum radius squared + if(g==next_count) { + mrs=c.max_radius_squared(); + if(count_p!=count_e) next_count=*(count_p++); + } + + // If mrs is less than the minimum distance to any untested + // block, then we are done + if(con.r_ctest(radp[g],mrs)) return true; + g++; + + // Load in a block off the worklist, permute it with the + // symmetry mask, and decode its position. These are all + // integer bit operations so they should run very fast. + q=e[g];q^=m1;q+=m2; + di=q&127;di-=64; + dj=(q>>7)&127;dj-=64; + dk=(q>>14)&127;dk-=64; + + // Check that the worklist position is in range + ei=di+i;if(ei<0||ei>=hx) continue; + ej=dj+j;if(ej<0||ej>=hy) continue; + ek=dk+k;if(ek<0||ek>=hz) continue; + + // Call the compute_min_max_radius() function. This returns + // true if the minimum distance to the block is bigger than the + // current mrs, in which case we skip this block and move on. + // Otherwise, it computes the maximum distance to the block and + // returns it in crs. + if(compute_min_max_radius(di,dj,dk,fx,fy,fz,gxs,gys,gzs,crs,mrs)) continue; + + // Now compute which region we are going to loop over, adding a + // displacement for the periodic cases + ijk=con.region_index(ci,cj,ck,ei,ej,ek,qx,qy,qz,disp); + + // If mrs is bigger than the maximum distance to the block, + // then we have to test all particles in the block for + // intersections. Otherwise, we do additional checks and skip + // those particles which can't possibly intersect the block. + if(co[ijk]>0) { + l=0;x2=x-qx;y2=y-qy;z2=z-qz; + if(!con.r_ctest(crs,mrs)) { + do { + x1=p[ijk][ps*l]-x2; + y1=p[ijk][ps*l+1]-y2; + z1=p[ijk][ps*l+2]-z2; + rs=con.r_scale(x1*x1+y1*y1+z1*z1,ijk,l); + if(!c.nplane(x1,y1,z1,rs,id[ijk][l])) return false; + l++; + } while (l>7)&127;dj-=64; + dk=(q>>14)&127;dk-=64; + + // Compute the position in the mask of the current block. If + // this lies outside the mask, then skip it. Otherwise, mark + // it. + ei=di+i;if(ei<0||ei>=hx) continue; + ej=dj+j;if(ej<0||ej>=hy) continue; + ek=dk+k;if(ek<0||ek>=hz) continue; + mijk=mask+ei+hx*(ej+hy*ek); + *mijk=mv; + + // Call the compute_min_max_radius() function. This returns + // true if the minimum distance to the block is bigger than the + // current mrs, in which case we skip this block and move on. + // Otherwise, it computes the maximum distance to the block and + // returns it in crs. + if(compute_min_max_radius(di,dj,dk,fx,fy,fz,gxs,gys,gzs,crs,mrs)) continue; + + // Now compute which region we are going to loop over, adding a + // displacement for the periodic cases + ijk=con.region_index(ci,cj,ck,ei,ej,ek,qx,qy,qz,disp); + + // If mrs is bigger than the maximum distance to the block, + // then we have to test all particles in the block for + // intersections. Otherwise, we do additional checks and skip + // those particles which can't possibly intersect the block. + if(co[ijk]>0) { + l=0;x2=x-qx;y2=y-qy;z2=z-qz; + if(!con.r_ctest(crs,mrs)) { + do { + x1=p[ijk][ps*l]-x2; + y1=p[ijk][ps*l+1]-y2; + z1=p[ijk][ps*l+2]-z2; + rs=con.r_scale(x1*x1+y1*y1+z1*z1,ijk,l); + if(!c.nplane(x1,y1,z1,rs,id[ijk][l])) return false; + l++; + } while (lqu_l-18) add_list_memory(qu_s,qu_e); + + // Test the parts of the worklist element which tell us what + // neighbors of this block are not on the worklist. Store them + // on the block list, and mark the mask. + scan_bits_mask_add(q,mijk,ei,ej,ek,qu_e); + } + + // Do a check to see if we've reached the radius cutoff + if(con.r_ctest(radp[g],mrs)) return true; + + // We were unable to completely compute the cell based on the blocks in + // the worklist, so now we have to go block by block, reading in items + // off the list + while(qu_s!=qu_e) { + + // If we reached the end of the list memory loop back to the + // start + if(qu_s==qu_l) qu_s=qu; + + // Read in a block off the list, and compute the upper and lower + // coordinates in each of the three dimensions + ei=*(qu_s++);ej=*(qu_s++);ek=*(qu_s++); + xlo=(ei-i)*boxx-fx;xhi=xlo+boxx; + ylo=(ej-j)*boxy-fy;yhi=ylo+boxy; + zlo=(ek-k)*boxz-fz;zhi=zlo+boxz; + + // Carry out plane tests to see if any particle in this block + // could possibly intersect the cell + if(ei>i) { + if(ej>j) { + if(ek>k) {if(corner_test(c,xlo,ylo,zlo,xhi,yhi,zhi)) continue;} + else if(ekk) {if(corner_test(c,xlo,yhi,zlo,xhi,ylo,zhi)) continue;} + else if(ekk) {if(edge_y_test(c,xlo,ylo,zlo,xhi,yhi,zhi)) continue;} + else if(ekj) { + if(ek>k) {if(corner_test(c,xhi,ylo,zlo,xlo,yhi,zhi)) continue;} + else if(ekk) {if(corner_test(c,xhi,yhi,zlo,xlo,ylo,zhi)) continue;} + else if(ekk) {if(edge_y_test(c,xhi,ylo,zlo,xlo,yhi,zhi)) continue;} + else if(ekj) { + if(ek>k) {if(edge_x_test(c,xlo,ylo,zlo,xhi,yhi,zhi)) continue;} + else if(ekk) {if(edge_x_test(c,xlo,yhi,zlo,xhi,ylo,zhi)) continue;} + else if(ekk) {if(face_z_test(c,xlo,ylo,zlo,xhi,yhi)) continue;} + else if(ek0) { + l=0;x2=x-qx;y2=y-qy;z2=z-qz; + do { + x1=p[ijk][ps*l]-x2; + y1=p[ijk][ps*l+1]-y2; + z1=p[ijk][ps*l+2]-z2; + rs=con.r_scale(x1*x1+y1*y1+z1*z1,ijk,l); + if(!c.nplane(x1,y1,z1,rs,id[ijk][l])) return false; + l++; + } while (l +template +bool voro_compute::corner_test(v_cell &c,double xl,double yl,double zl,double xh,double yh,double zh) { + con.r_prime(xl*xl+yl*yl+zl*zl); + if(c.plane_intersects_guess(xh,yl,zl,con.r_cutoff(xl*xh+yl*yl+zl*zl))) return false; + if(c.plane_intersects(xh,yh,zl,con.r_cutoff(xl*xh+yl*yh+zl*zl))) return false; + if(c.plane_intersects(xl,yh,zl,con.r_cutoff(xl*xl+yl*yh+zl*zl))) return false; + if(c.plane_intersects(xl,yh,zh,con.r_cutoff(xl*xl+yl*yh+zl*zh))) return false; + if(c.plane_intersects(xl,yl,zh,con.r_cutoff(xl*xl+yl*yl+zl*zh))) return false; + if(c.plane_intersects(xh,yl,zh,con.r_cutoff(xl*xh+yl*yl+zl*zh))) return false; + return true; +} + +/** This function checks to see whether a particular block can possibly have + * any intersection with a Voronoi cell, for the case when the closest point + * from the cell center to the block is on an edge which points along the x + * direction. + * \param[in,out] c a reference to a Voronoi cell. + * \param[in] (x0,x1) the minimum and maximum relative x coordinates of the + * block. + * \param[in] (yl,zl) the relative y and z coordinates of the corner of the + * block closest to the cell center. + * \param[in] (yh,zh) the relative y and z coordinates of the corner of the + * block furthest away from the cell center. + * \return False if the block may intersect, true if does not. */ +template +template +inline bool voro_compute::edge_x_test(v_cell &c,double x0,double yl,double zl,double x1,double yh,double zh) { + con.r_prime(yl*yl+zl*zl); + if(c.plane_intersects_guess(x0,yl,zh,con.r_cutoff(yl*yl+zl*zh))) return false; + if(c.plane_intersects(x1,yl,zh,con.r_cutoff(yl*yl+zl*zh))) return false; + if(c.plane_intersects(x1,yl,zl,con.r_cutoff(yl*yl+zl*zl))) return false; + if(c.plane_intersects(x0,yl,zl,con.r_cutoff(yl*yl+zl*zl))) return false; + if(c.plane_intersects(x0,yh,zl,con.r_cutoff(yl*yh+zl*zl))) return false; + if(c.plane_intersects(x1,yh,zl,con.r_cutoff(yl*yh+zl*zl))) return false; + return true; +} + +/** This function checks to see whether a particular block can possibly have + * any intersection with a Voronoi cell, for the case when the closest point + * from the cell center to the block is on an edge which points along the y + * direction. + * \param[in,out] c a reference to a Voronoi cell. + * \param[in] (y0,y1) the minimum and maximum relative y coordinates of the + * block. + * \param[in] (xl,zl) the relative x and z coordinates of the corner of the + * block closest to the cell center. + * \param[in] (xh,zh) the relative x and z coordinates of the corner of the + * block furthest away from the cell center. + * \return False if the block may intersect, true if does not. */ +template +template +inline bool voro_compute::edge_y_test(v_cell &c,double xl,double y0,double zl,double xh,double y1,double zh) { + con.r_prime(xl*xl+zl*zl); + if(c.plane_intersects_guess(xl,y0,zh,con.r_cutoff(xl*xl+zl*zh))) return false; + if(c.plane_intersects(xl,y1,zh,con.r_cutoff(xl*xl+zl*zh))) return false; + if(c.plane_intersects(xl,y1,zl,con.r_cutoff(xl*xl+zl*zl))) return false; + if(c.plane_intersects(xl,y0,zl,con.r_cutoff(xl*xl+zl*zl))) return false; + if(c.plane_intersects(xh,y0,zl,con.r_cutoff(xl*xh+zl*zl))) return false; + if(c.plane_intersects(xh,y1,zl,con.r_cutoff(xl*xh+zl*zl))) return false; + return true; +} + +/** This function checks to see whether a particular block can possibly have + * any intersection with a Voronoi cell, for the case when the closest point + * from the cell center to the block is on an edge which points along the z + * direction. + * \param[in,out] c a reference to a Voronoi cell. + * \param[in] (z0,z1) the minimum and maximum relative z coordinates of the block. + * \param[in] (xl,yl) the relative x and y coordinates of the corner of the + * block closest to the cell center. + * \param[in] (xh,yh) the relative x and y coordinates of the corner of the + * block furthest away from the cell center. + * \return False if the block may intersect, true if does not. */ +template +template +inline bool voro_compute::edge_z_test(v_cell &c,double xl,double yl,double z0,double xh,double yh,double z1) { + con.r_prime(xl*xl+yl*yl); + if(c.plane_intersects_guess(xl,yh,z0,con.r_cutoff(xl*xl+yl*yh))) return false; + if(c.plane_intersects(xl,yh,z1,con.r_cutoff(xl*xl+yl*yh))) return false; + if(c.plane_intersects(xl,yl,z1,con.r_cutoff(xl*xl+yl*yl))) return false; + if(c.plane_intersects(xl,yl,z0,con.r_cutoff(xl*xl+yl*yl))) return false; + if(c.plane_intersects(xh,yl,z0,con.r_cutoff(xl*xh+yl*yl))) return false; + if(c.plane_intersects(xh,yl,z1,con.r_cutoff(xl*xh+yl*yl))) return false; + return true; +} + +/** This function checks to see whether a particular block can possibly have + * any intersection with a Voronoi cell, for the case when the closest point + * from the cell center to the block is on a face aligned with the x direction. + * \param[in,out] c a reference to a Voronoi cell. + * \param[in] xl the minimum distance from the cell center to the face. + * \param[in] (y0,y1) the minimum and maximum relative y coordinates of the + * block. + * \param[in] (z0,z1) the minimum and maximum relative z coordinates of the + * block. + * \return False if the block may intersect, true if does not. */ +template +template +inline bool voro_compute::face_x_test(v_cell &c,double xl,double y0,double z0,double y1,double z1) { + con.r_prime(xl*xl); + if(c.plane_intersects_guess(xl,y0,z0,con.r_cutoff(xl*xl))) return false; + if(c.plane_intersects(xl,y0,z1,con.r_cutoff(xl*xl))) return false; + if(c.plane_intersects(xl,y1,z1,con.r_cutoff(xl*xl))) return false; + if(c.plane_intersects(xl,y1,z0,con.r_cutoff(xl*xl))) return false; + return true; +} + +/** This function checks to see whether a particular block can possibly have + * any intersection with a Voronoi cell, for the case when the closest point + * from the cell center to the block is on a face aligned with the y direction. + * \param[in,out] c a reference to a Voronoi cell. + * \param[in] yl the minimum distance from the cell center to the face. + * \param[in] (x0,x1) the minimum and maximum relative x coordinates of the + * block. + * \param[in] (z0,z1) the minimum and maximum relative z coordinates of the + * block. + * \return False if the block may intersect, true if does not. */ +template +template +inline bool voro_compute::face_y_test(v_cell &c,double x0,double yl,double z0,double x1,double z1) { + con.r_prime(yl*yl); + if(c.plane_intersects_guess(x0,yl,z0,con.r_cutoff(yl*yl))) return false; + if(c.plane_intersects(x0,yl,z1,con.r_cutoff(yl*yl))) return false; + if(c.plane_intersects(x1,yl,z1,con.r_cutoff(yl*yl))) return false; + if(c.plane_intersects(x1,yl,z0,con.r_cutoff(yl*yl))) return false; + return true; +} + +/** This function checks to see whether a particular block can possibly have + * any intersection with a Voronoi cell, for the case when the closest point + * from the cell center to the block is on a face aligned with the z direction. + * \param[in,out] c a reference to a Voronoi cell. + * \param[in] zl the minimum distance from the cell center to the face. + * \param[in] (x0,x1) the minimum and maximum relative x coordinates of the + * block. + * \param[in] (y0,y1) the minimum and maximum relative y coordinates of the + * block. + * \return False if the block may intersect, true if does not. */ +template +template +inline bool voro_compute::face_z_test(v_cell &c,double x0,double y0,double zl,double x1,double y1) { + con.r_prime(zl*zl); + if(c.plane_intersects_guess(x0,y0,zl,con.r_cutoff(zl*zl))) return false; + if(c.plane_intersects(x0,y1,zl,con.r_cutoff(zl*zl))) return false; + if(c.plane_intersects(x1,y1,zl,con.r_cutoff(zl*zl))) return false; + if(c.plane_intersects(x1,y0,zl,con.r_cutoff(zl*zl))) return false; + return true; +} + +/** This routine checks to see whether a point is within a particular distance + * of a nearby region. If the point is within the distance of the region, then + * the routine returns true, and computes the maximum distance from the point + * to the region. Otherwise, the routine returns false. + * \param[in] (di,dj,dk) the position of the nearby region to be tested, + * relative to the region that the point is in. + * \param[in] (fx,fy,fz) the displacement of the point within its region. + * \param[in] (gxs,gys,gzs) the maximum squared distances from the point to the + * sides of its region. + * \param[out] crs a reference in which to return the maximum distance to the + * region (only computed if the routine returns false). + * \param[in] mrs the distance to be tested. + * \return True if the region is further away than mrs, false if the region in + * within mrs. */ +template +bool voro_compute::compute_min_max_radius(int di,int dj,int dk,double fx,double fy,double fz,double gxs,double gys,double gzs,double &crs,double mrs) { + double xlo,ylo,zlo; + if(di>0) { + xlo=di*boxx-fx; + crs=xlo*xlo; + if(dj>0) { + ylo=dj*boxy-fy; + crs+=ylo*ylo; + if(dk>0) { + zlo=dk*boxz-fz; + crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true; + crs+=bxsq+2*(boxx*xlo+boxy*ylo+boxz*zlo); + } else if(dk<0) { + zlo=(dk+1)*boxz-fz; + crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true; + crs+=bxsq+2*(boxx*xlo+boxy*ylo-boxz*zlo); + } else { + if(con.r_ctest(crs,mrs)) return true; + crs+=boxx*(2*xlo+boxx)+boxy*(2*ylo+boxy)+gzs; + } + } else if(dj<0) { + ylo=(dj+1)*boxy-fy; + crs+=ylo*ylo; + if(dk>0) { + zlo=dk*boxz-fz; + crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true; + crs+=bxsq+2*(boxx*xlo-boxy*ylo+boxz*zlo); + } else if(dk<0) { + zlo=(dk+1)*boxz-fz; + crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true; + crs+=bxsq+2*(boxx*xlo-boxy*ylo-boxz*zlo); + } else { + if(con.r_ctest(crs,mrs)) return true; + crs+=boxx*(2*xlo+boxx)+boxy*(-2*ylo+boxy)+gzs; + } + } else { + if(dk>0) { + zlo=dk*boxz-fz; + crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true; + crs+=boxz*(2*zlo+boxz); + } else if(dk<0) { + zlo=(dk+1)*boxz-fz; + crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true; + crs+=boxz*(-2*zlo+boxz); + } else { + if(con.r_ctest(crs,mrs)) return true; + crs+=gzs; + } + crs+=gys+boxx*(2*xlo+boxx); + } + } else if(di<0) { + xlo=(di+1)*boxx-fx; + crs=xlo*xlo; + if(dj>0) { + ylo=dj*boxy-fy; + crs+=ylo*ylo; + if(dk>0) { + zlo=dk*boxz-fz; + crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true; + crs+=bxsq+2*(-boxx*xlo+boxy*ylo+boxz*zlo); + } else if(dk<0) { + zlo=(dk+1)*boxz-fz; + crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true; + crs+=bxsq+2*(-boxx*xlo+boxy*ylo-boxz*zlo); + } else { + if(con.r_ctest(crs,mrs)) return true; + crs+=boxx*(-2*xlo+boxx)+boxy*(2*ylo+boxy)+gzs; + } + } else if(dj<0) { + ylo=(dj+1)*boxy-fy; + crs+=ylo*ylo; + if(dk>0) { + zlo=dk*boxz-fz; + crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true; + crs+=bxsq+2*(-boxx*xlo-boxy*ylo+boxz*zlo); + } else if(dk<0) { + zlo=(dk+1)*boxz-fz; + crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true; + crs+=bxsq+2*(-boxx*xlo-boxy*ylo-boxz*zlo); + } else { + if(con.r_ctest(crs,mrs)) return true; + crs+=boxx*(-2*xlo+boxx)+boxy*(-2*ylo+boxy)+gzs; + } + } else { + if(dk>0) { + zlo=dk*boxz-fz; + crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true; + crs+=boxz*(2*zlo+boxz); + } else if(dk<0) { + zlo=(dk+1)*boxz-fz; + crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true; + crs+=boxz*(-2*zlo+boxz); + } else { + if(con.r_ctest(crs,mrs)) return true; + crs+=gzs; + } + crs+=gys+boxx*(-2*xlo+boxx); + } + } else { + if(dj>0) { + ylo=dj*boxy-fy; + crs=ylo*ylo; + if(dk>0) { + zlo=dk*boxz-fz; + crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true; + crs+=boxz*(2*zlo+boxz); + } else if(dk<0) { + zlo=(dk+1)*boxz-fz; + crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true; + crs+=boxz*(-2*zlo+boxz); + } else { + if(con.r_ctest(crs,mrs)) return true; + crs+=gzs; + } + crs+=boxy*(2*ylo+boxy); + } else if(dj<0) { + ylo=(dj+1)*boxy-fy; + crs=ylo*ylo; + if(dk>0) { + zlo=dk*boxz-fz; + crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true; + crs+=boxz*(2*zlo+boxz); + } else if(dk<0) { + zlo=(dk+1)*boxz-fz; + crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true; + crs+=boxz*(-2*zlo+boxz); + } else { + if(con.r_ctest(crs,mrs)) return true; + crs+=gzs; + } + crs+=boxy*(-2*ylo+boxy); + } else { + if(dk>0) { + zlo=dk*boxz-fz;crs=zlo*zlo;if(con.r_ctest(crs,mrs)) return true; + crs+=boxz*(2*zlo+boxz); + } else if(dk<0) { + zlo=(dk+1)*boxz-fz;crs=zlo*zlo;if(con.r_ctest(crs,mrs)) return true; + crs+=boxz*(-2*zlo+boxz); + } else { + crs=0; + voro_fatal_error("Min/max radius function called for central block, which should never\nhappen.",VOROPP_INTERNAL_ERROR); + } + crs+=gys; + } + crs+=gxs; + } + return false; +} + +template +bool voro_compute::compute_min_radius(int di,int dj,int dk,double fx,double fy,double fz,double mrs) { + double t,crs; + + if(di>0) {t=di*boxx-fx;crs=t*t;} + else if(di<0) {t=(di+1)*boxx-fx;crs=t*t;} + else crs=0; + + if(dj>0) {t=dj*boxy-fy;crs+=t*t;} + else if(dj<0) {t=(dj+1)*boxy-fy;crs+=t*t;} + + if(dk>0) {t=dk*boxz-fz;crs+=t*t;} + else if(dk<0) {t=(dk+1)*boxz-fz;crs+=t*t;} + + return crs>con.r_max_add(mrs); +} + +/** Adds memory to the queue. + * \param[in,out] qu_s a reference to the queue start pointer. + * \param[in,out] qu_e a reference to the queue end pointer. */ +template +inline void voro_compute::add_list_memory(int*& qu_s,int*& qu_e) { + qu_size<<=1; + int *qu_n=new int[qu_size],*qu_c=qu_n; +#if VOROPP_VERBOSE >=2 + fprintf(stderr,"List memory scaled up to %d\n",qu_size); +#endif + if(qu_s<=qu_e) { + while(qu_s::voro_compute(container&,int,int,int); +template voro_compute::voro_compute(container_poly&,int,int,int); +template bool voro_compute::compute_cell(voronoicell&,int,int,int,int,int); +template bool voro_compute::compute_cell(voronoicell_neighbor&,int,int,int,int,int); +template void voro_compute::find_voronoi_cell(double,double,double,int,int,int,int,particle_record&,double&); +template bool voro_compute::compute_cell(voronoicell&,int,int,int,int,int); +template bool voro_compute::compute_cell(voronoicell_neighbor&,int,int,int,int,int); +template void voro_compute::find_voronoi_cell(double,double,double,int,int,int,int,particle_record&,double&); + +// Explicit template instantiation +template voro_compute::voro_compute(container_periodic&,int,int,int); +template voro_compute::voro_compute(container_periodic_poly&,int,int,int); +template bool voro_compute::compute_cell(voronoicell&,int,int,int,int,int); +template bool voro_compute::compute_cell(voronoicell_neighbor&,int,int,int,int,int); +template void voro_compute::find_voronoi_cell(double,double,double,int,int,int,int,particle_record&,double&); +template bool voro_compute::compute_cell(voronoicell&,int,int,int,int,int); +template bool voro_compute::compute_cell(voronoicell_neighbor&,int,int,int,int,int); +template void voro_compute::find_voronoi_cell(double,double,double,int,int,int,int,particle_record&,double&); + +} diff --git a/src/third_party/voro/src/v_compute.hh b/src/third_party/voro/src/v_compute.hh new file mode 100644 index 000000000..01cfb7d1c --- /dev/null +++ b/src/third_party/voro/src/v_compute.hh @@ -0,0 +1,149 @@ +// Voro++, a 3D cell-based Voronoi library +// +// Author : Chris H. Rycroft (Harvard University / LBL) +// Email : chr@alum.mit.edu +// Date : August 30th 2011 + +/** \file v_compute.hh + * \brief Header file for the voro_compute template and related classes. */ + +#ifndef VOROPP_V_COMPUTE_HH +#define VOROPP_V_COMPUTE_HH + +#include "config.hh" +#include "worklist.hh" +#include "cell.hh" + +namespace voro { + +/** \brief Structure for holding information about a particle. + * + * This small structure holds information about a single particle, and is used + * by several of the routines in the voro_compute template for passing + * information by reference between functions. */ +struct particle_record { + /** The index of the block that the particle is within. */ + int ijk; + /** The number of particle within its block. */ + int l; + /** The x-index of the block. */ + int di; + /** The y-index of the block. */ + int dj; + /** The z-index of the block. */ + int dk; +}; + +/** \brief Template for carrying out Voronoi cell computations. */ +template +class voro_compute { + public: + /** A reference to the container class on which to carry out*/ + c_class &con; + /** The size of an internal computational block in the x + * direction. */ + const double boxx; + /** The size of an internal computational block in the y + * direction. */ + const double boxy; + /** The size of an internal computational block in the z + * direction. */ + const double boxz; + /** The inverse box length in the x direction, set to + * nx/(bx-ax). */ + const double xsp; + /** The inverse box length in the y direction, set to + * ny/(by-ay). */ + const double ysp; + /** The inverse box length in the z direction, set to + * nz/(bz-az). */ + const double zsp; + /** The number of boxes in the x direction for the searching mask. */ + const int hx; + /** The number of boxes in the y direction for the searching mask. */ + const int hy; + /** The number of boxes in the z direction for the searching mask. */ + const int hz; + /** A constant, set to the value of hx multiplied by hy, which + * is used in the routines which step through mask boxes in + * sequence. */ + const int hxy; + /** A constant, set to the value of hx*hy*hz, which is used in + * the routines which step through mask boxes in sequence. */ + const int hxyz; + /** The number of floating point entries to store for each + * particle. */ + const int ps; + /** This array holds the numerical IDs of each particle in each + * computational box. */ + int **id; + /** A two dimensional array holding particle positions. For the + * derived container_poly class, this also holds particle + * radii. */ + double **p; + /** An array holding the number of particles within each + * computational box of the container. */ + int *co; + voro_compute(c_class &con_,int hx_,int hy_,int hz_); + /** The class destructor frees the dynamically allocated memory + * for the mask and queue. */ + ~voro_compute() { + delete [] qu; + delete [] mask; + } + template + bool compute_cell(v_cell &c,int ijk,int s,int ci,int cj,int ck); + void find_voronoi_cell(double x,double y,double z,int ci,int cj,int ck,int ijk,particle_record &w,double &mrs); + private: + /** A constant set to boxx*boxx+boxy*boxy+boxz*boxz, which is + * frequently used in the computation. */ + const double bxsq; + /** This sets the current value being used to mark tested blocks + * in the mask. */ + unsigned int mv; + /** The current size of the search list. */ + int qu_size; + /** A pointer to the array of worklists. */ + const unsigned int *wl; + /** An pointer to the array holding the minimum distances + * associated with the worklists. */ + double *mrad; + /** This array is used during the cell computation to determine + * which blocks have been considered. */ + unsigned int *mask; + /** An array is used to store the queue of blocks to test + * during the Voronoi cell computation. */ + int *qu; + /** A pointer to the end of the queue array, used to determine + * when the queue is full. */ + int *qu_l; + template + bool corner_test(v_cell &c,double xl,double yl,double zl,double xh,double yh,double zh); + template + inline bool edge_x_test(v_cell &c,double x0,double yl,double zl,double x1,double yh,double zh); + template + inline bool edge_y_test(v_cell &c,double xl,double y0,double zl,double xh,double y1,double zh); + template + inline bool edge_z_test(v_cell &c,double xl,double yl,double z0,double xh,double yh,double z1); + template + inline bool face_x_test(v_cell &c,double xl,double y0,double z0,double y1,double z1); + template + inline bool face_y_test(v_cell &c,double x0,double yl,double z0,double x1,double z1); + template + inline bool face_z_test(v_cell &c,double x0,double y0,double zl,double x1,double y1); + bool compute_min_max_radius(int di,int dj,int dk,double fx,double fy,double fz,double gx,double gy,double gz,double& crs,double mrs); + bool compute_min_radius(int di,int dj,int dk,double fx,double fy,double fz,double mrs); + inline void add_to_mask(int ei,int ej,int ek,int *&qu_e); + inline void scan_bits_mask_add(unsigned int q,unsigned int *mijk,int ei,int ej,int ek,int *&qu_e); + inline void scan_all(int ijk,double x,double y,double z,int di,int dj,int dk,particle_record &w,double &mrs); + void add_list_memory(int*& qu_s,int*& qu_e); + /** Resets the mask in cases where the mask counter wraps + * around. */ + inline void reset_mask() { + for(unsigned int *mp(mask);mp +bool wall_sphere::cut_cell_base(v_cell &c,double x,double y,double z) { + double xd=x-xc,yd=y-yc,zd=z-zc,dq=xd*xd+yd*yd+zd*zd; + if (dq>1e-5) { + dq=2*(sqrt(dq)*rc-dq); + return c.nplane(xd,yd,zd,dq,w_id); + } + return true; +} + +/** Tests to see whether a point is inside the plane wall object. + * \param[in] (x,y,z) the vector to test. + * \return True if the point is inside, false if the point is outside. */ +bool wall_plane::point_inside(double x,double y,double z) { + return x*xc+y*yc+z*zc +bool wall_plane::cut_cell_base(v_cell &c,double x,double y,double z) { + double dq=2*(ac-x*xc-y*yc-z*zc); + return c.nplane(xc,yc,zc,dq,w_id); +} + +/** Tests to see whether a point is inside the cylindrical wall object. + * \param[in] (x,y,z) the vector to test. + * \return True if the point is inside, false if the point is outside. */ +bool wall_cylinder::point_inside(double x,double y,double z) { + double xd=x-xc,yd=y-yc,zd=z-zc; + double pa=(xd*xa+yd*ya+zd*za)*asi; + xd-=xa*pa;yd-=ya*pa;zd-=za*pa; + return xd*xd+yd*yd+zd*zd +bool wall_cylinder::cut_cell_base(v_cell &c,double x,double y,double z) { + double xd=x-xc,yd=y-yc,zd=z-zc,pa=(xd*xa+yd*ya+zd*za)*asi; + xd-=xa*pa;yd-=ya*pa;zd-=za*pa; + pa=xd*xd+yd*yd+zd*zd; + if(pa>1e-5) { + pa=2*(sqrt(pa)*rc-pa); + return c.nplane(xd,yd,zd,pa,w_id); + } + return true; +} + +/** Tests to see whether a point is inside the cone wall object. + * \param[in] (x,y,z) the vector to test. + * \return True if the point is inside, false if the point is outside. */ +bool wall_cone::point_inside(double x,double y,double z) { + double xd=x-xc,yd=y-yc,zd=z-zc,pa=(xd*xa+yd*ya+zd*za)*asi; + xd-=xa*pa;yd-=ya*pa;zd-=za*pa; + pa*=gra; + if (pa<0) return false; + pa*=pa; + return xd*xd+yd*yd+zd*zd +bool wall_cone::cut_cell_base(v_cell &c,double x,double y,double z) { + double xd=x-xc,yd=y-yc,zd=z-zc,xf,yf,zf,q,pa=(xd*xa+yd*ya+zd*za)*asi; + xd-=xa*pa;yd-=ya*pa;zd-=za*pa; + pa=xd*xd+yd*yd+zd*zd; + if(pa>1e-5) { + pa=1/sqrt(pa); + q=sqrt(asi); + xf=-sang*q*xa+cang*pa*xd; + yf=-sang*q*ya+cang*pa*yd; + zf=-sang*q*za+cang*pa*zd; + pa=2*(xf*(xc-x)+yf*(yc-y)+zf*(zc-z)); + return c.nplane(xf,yf,zf,pa,w_id); + } + return true; +} + +// Explicit instantiation +template bool wall_sphere::cut_cell_base(voronoicell&,double,double,double); +template bool wall_sphere::cut_cell_base(voronoicell_neighbor&,double,double,double); +template bool wall_plane::cut_cell_base(voronoicell&,double,double,double); +template bool wall_plane::cut_cell_base(voronoicell_neighbor&,double,double,double); +template bool wall_cylinder::cut_cell_base(voronoicell&,double,double,double); +template bool wall_cylinder::cut_cell_base(voronoicell_neighbor&,double,double,double); +template bool wall_cone::cut_cell_base(voronoicell&,double,double,double); +template bool wall_cone::cut_cell_base(voronoicell_neighbor&,double,double,double); + +} diff --git a/src/third_party/voro/src/wall.hh b/src/third_party/voro/src/wall.hh new file mode 100644 index 000000000..d3f325bad --- /dev/null +++ b/src/third_party/voro/src/wall.hh @@ -0,0 +1,118 @@ +// Voro++, a 3D cell-based Voronoi library +// +// Author : Chris H. Rycroft (Harvard University / LBL) +// Email : chr@alum.mit.edu +// Date : August 30th 2011 + +/** \file wall.hh + * \brief Header file for the derived wall classes. */ + +#ifndef VOROPP_WALL_HH +#define VOROPP_WALL_HH + +#include "cell.hh" +#include "container.hh" + +namespace voro { + +/** \brief A class representing a spherical wall object. + * + * This class represents a spherical wall object. */ +struct wall_sphere : public wall { + public: + /** Constructs a spherical wall object. + * \param[in] w_id_ an ID number to associate with the wall for + * neighbor tracking. + * \param[in] (xc_,yc_,zc_) a position vector for the sphere's + * center. + * \param[in] rc_ the radius of the sphere. */ + wall_sphere(double xc_,double yc_,double zc_,double rc_,int w_id_=-99) + : w_id(w_id_), xc(xc_), yc(yc_), zc(zc_), rc(rc_) {} + bool point_inside(double x,double y,double z); + template + bool cut_cell_base(v_cell &c,double x,double y,double z); + bool cut_cell(voronoicell &c,double x,double y,double z) {return cut_cell_base(c,x,y,z);} + bool cut_cell(voronoicell_neighbor &c,double x,double y,double z) {return cut_cell_base(c,x,y,z);} + private: + const int w_id; + const double xc,yc,zc,rc; +}; + +/** \brief A class representing a plane wall object. + * + * This class represents a single plane wall object. */ +struct wall_plane : public wall { + public: + /** Constructs a plane wall object. + * \param[in] (xc_,yc_,zc_) a normal vector to the plane. + * \param[in] ac_ a displacement along the normal vector. + * \param[in] w_id_ an ID number to associate with the wall for + * neighbor tracking. */ + wall_plane(double xc_,double yc_,double zc_,double ac_,int w_id_=-99) + : w_id(w_id_), xc(xc_), yc(yc_), zc(zc_), ac(ac_) {} + bool point_inside(double x,double y,double z); + template + bool cut_cell_base(v_cell &c,double x,double y,double z); + bool cut_cell(voronoicell &c,double x,double y,double z) {return cut_cell_base(c,x,y,z);} + bool cut_cell(voronoicell_neighbor &c,double x,double y,double z) {return cut_cell_base(c,x,y,z);} + private: + const int w_id; + const double xc,yc,zc,ac; +}; + +/** \brief A class representing a cylindrical wall object. + * + * This class represents a open cylinder wall object. */ +struct wall_cylinder : public wall { + public: + /** Constructs a cylinder wall object. + * \param[in] (xc_,yc_,zc_) a point on the axis of the + * cylinder. + * \param[in] (xa_,ya_,za_) a vector pointing along the + * direction of the cylinder. + * \param[in] rc_ the radius of the cylinder + * \param[in] w_id_ an ID number to associate with the wall for + * neighbor tracking. */ + wall_cylinder(double xc_,double yc_,double zc_,double xa_,double ya_,double za_,double rc_,int w_id_=-99) + : w_id(w_id_), xc(xc_), yc(yc_), zc(zc_), xa(xa_), ya(ya_), za(za_), + asi(1/(xa_*xa_+ya_*ya_+za_*za_)), rc(rc_) {} + bool point_inside(double x,double y,double z); + template + bool cut_cell_base(v_cell &c,double x,double y,double z); + bool cut_cell(voronoicell &c,double x,double y,double z) {return cut_cell_base(c,x,y,z);} + bool cut_cell(voronoicell_neighbor &c,double x,double y,double z) {return cut_cell_base(c,x,y,z);} + private: + const int w_id; + const double xc,yc,zc,xa,ya,za,asi,rc; +}; + +/** \brief A class representing a conical wall object. + * + * This class represents a cone wall object. */ +struct wall_cone : public wall { + public: + /** Constructs a cone wall object. + * \param[in] (xc_,yc_,zc_) the apex of the cone. + * \param[in] (xa_,ya_,za_) a vector pointing along the axis of + * the cone. + * \param[in] ang the angle (in radians) of the cone, measured + * from the axis. + * \param[in] w_id_ an ID number to associate with the wall for + * neighbor tracking. */ + wall_cone(double xc_,double yc_,double zc_,double xa_,double ya_,double za_,double ang,int w_id_=-99) + : w_id(w_id_), xc(xc_), yc(yc_), zc(zc_), xa(xa_), ya(ya_), za(za_), + asi(1/(xa_*xa_+ya_*ya_+za_*za_)), + gra(tan(ang)), sang(sin(ang)), cang(cos(ang)) {} + bool point_inside(double x,double y,double z); + template + bool cut_cell_base(v_cell &c,double x,double y,double z); + bool cut_cell(voronoicell &c,double x,double y,double z) {return cut_cell_base(c,x,y,z);} + bool cut_cell(voronoicell_neighbor &c,double x,double y,double z) {return cut_cell_base(c,x,y,z);} + private: + const int w_id; + const double xc,yc,zc,xa,ya,za,asi,gra,sang,cang; +}; + +} + +#endif diff --git a/src/third_party/voro/src/worklist.hh b/src/third_party/voro/src/worklist.hh new file mode 100644 index 000000000..ab5f3f9b5 --- /dev/null +++ b/src/third_party/voro/src/worklist.hh @@ -0,0 +1,32 @@ +// Voro++, a 3D cell-based Voronoi library +// +// Author : Chris H. Rycroft (Harvard University / LBL) +// Email : chr@alum.mit.edu +// Date : August 30th 2011 + +/** \file worklist.hh + * \brief Header file for setting constants used in the block worklists that are + * used during cell computation. + * + * This file is automatically generated by worklist_gen.pl and it is not + * intended to be edited by hand. */ + +#ifndef VOROPP_WORKLIST_HH +#define VOROPP_WORKLIST_HH + +namespace voro { + +/** Each region is divided into a grid of subregions, and a worklist is +# constructed for each. This parameter sets is set to half the number of +# subregions that the block is divided into. */ +const int wl_hgrid=4; +/** The number of subregions that a block is subdivided into, which is twice +the value of hgrid. */ +const int wl_fgrid=8; +/** The total number of worklists, set to the cube of hgrid. */ +const int wl_hgridcu=64; +/** The number of elements in each worklist. */ +const int wl_seq_length=64; + +} +#endif From 56e026b41f618bf842895d42a830954a66ac820d Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Fri, 15 Dec 2023 20:55:28 -0800 Subject: [PATCH 02/36] Add Basic Python Binding --- bindings/python/manifold3d.cpp | 21 ++ src/manifold/include/manifold.h | 7 + src/manifold/src/manifold.cpp | 33 ++ src/third_party/CMakeLists.txt | 9 +- src/third_party/voro/CMakeLists.txt | 78 +++++ src/third_party/voro/README | 152 ++++++++ src/third_party/voro/src/README | 21 ++ src/third_party/voro/src/cmd_line.cc | 497 +++++++++++++++++++++++++++ src/third_party/voro/src/voro++.cc | 19 + src/third_party/voro/src/voro++.hh | 333 ++++++++++++++++++ 10 files changed, 1166 insertions(+), 4 deletions(-) create mode 100644 src/third_party/voro/CMakeLists.txt create mode 100644 src/third_party/voro/README create mode 100644 src/third_party/voro/src/README create mode 100644 src/third_party/voro/src/cmd_line.cc create mode 100644 src/third_party/voro/src/voro++.cc create mode 100644 src/third_party/voro/src/voro++.hh diff --git a/bindings/python/manifold3d.cpp b/bindings/python/manifold3d.cpp index 00f7d89ee..93a262db9 100644 --- a/bindings/python/manifold3d.cpp +++ b/bindings/python/manifold3d.cpp @@ -524,6 +524,27 @@ NB_MODULE(manifold3d, m) { .def("project", &Manifold::Project, "Returns a cross section representing the projected outline of this " "object onto the X-Y plane.") + .def( + "fracture", + [](Manifold & self, + const nb::ndarray> &pts, + const nb::ndarray> &weights) { + std::vector pts_vec; + std::vector weights_vec; + auto pointData = pts.data(); + auto pointShape = pts.shape(0); + auto weightData = weights.data(); + auto weightShape = weights.shape(0); + for (int i = 0; i < pointShape; i+=3) { + pts_vec.push_back({pointData[i], pointData[i+1], pointData[i+2]}); + } + for (int i = 0; i < weightShape; i++) { + weights_vec.push_back(weightData[i]); + } + return self.Fracture(pts_vec, weights_vec); + }, + "This operation Compute the fracturing of this Manifold into convex " + "chunks around the supplied points.") .def("status", &Manifold::Status, "Returns the reason for an input Mesh producing an empty Manifold. " "This Status only applies to Manifolds newly-created from an input " diff --git a/src/manifold/include/manifold.h b/src/manifold/include/manifold.h index 283b9346e..a3d8c6ef2 100644 --- a/src/manifold/include/manifold.h +++ b/src/manifold/include/manifold.h @@ -249,6 +249,13 @@ class Manifold { static Manifold Hull(const std::vector& pts); ///@} + /** @name Voronoi Fracture + */ + ///@{ + std::vector Fracture(const std::vector& pts, + const std::vector& weights) const; + ///@} + /** @name Testing hooks * These are just for internal testing. */ diff --git a/src/manifold/src/manifold.cpp b/src/manifold/src/manifold.cpp index 124a67f86..73493149d 100644 --- a/src/manifold/src/manifold.cpp +++ b/src/manifold/src/manifold.cpp @@ -17,6 +17,7 @@ #include #include "QuickHull.hpp" +#include "voro++.hh" #include "boolean3.h" #include "csg_tree.h" #include "impl.h" @@ -845,4 +846,36 @@ Manifold Manifold::Hull() const { return Hull(GetMesh().vertPos); } Manifold Manifold::Hull(const std::vector& manifolds) { return Compose(manifolds).Hull(); } + +/** + * Compute the fracturing of this Manifold into convex chunks. + * + * @param pts A vector of points over which to fracture the manifold. + * @param pts A vector of weights controlling the relative size of each chunk. + */ +std::vector Manifold::Fracture(const std::vector& pts, + const std::vector& weights) const { + voro::container_poly container(-0.5, 0.5, -0.5, 0.5, -0.5, 0.5, pts.size(), pts.size(), pts.size(), false, false, false, 0); + for (size_t i = 0; i < pts.size(); i++) { container.put(i, pts[i].x, pts[i].y, pts[i].z, weights[i]); } + container.compute_all_cells(); + + std::vector manifolds; + manifolds.reserve(pts.size()); + voro::voronoicell c(container); + voro::c_loop_all vl(container); + if (vl.start()) { + do { + std::vector verts; + verts.reserve(c.p); + for (size_t i = 0; i < c.p; i++) { + verts.push_back( + glm::vec3(c.pts[(i * 3) + 0], + c.pts[(i * 3) + 1], + c.pts[(i * 3) + 2])); + } + manifolds.push_back(Hull(verts) ^ *this); + } while (vl.inc()); + } + return manifolds; +} } // namespace manifold diff --git a/src/third_party/CMakeLists.txt b/src/third_party/CMakeLists.txt index 1103b7c8d..75d346b4f 100644 --- a/src/third_party/CMakeLists.txt +++ b/src/third_party/CMakeLists.txt @@ -8,7 +8,8 @@ add_library(quickhull quickhull/QuickHull.cpp) target_include_directories(quickhull PUBLIC quickhull) target_compile_features(quickhull PUBLIC cxx_std_17) -file(GLOB VORO_SOURCES voro/src/*.cc) -file(GLOB NOT_VORO_SOURCES voro/src/v_base_wl.cc src/voro++.cc) -list(REMOVE_ITEM VORO_SOURCES ${NOT_VORO_SOURCES}) -add_library(voro++ STATIC ${VORO_SOURCES}) +add_subdirectory(voro) +#file(GLOB VORO_SOURCES voro/src/*.cc) +#file(GLOB NOT_VORO_SOURCES voro/src/v_base_wl.cc src/voro++.cc) +#list(REMOVE_ITEM VORO_SOURCES ${NOT_VORO_SOURCES}) +#add_library(voro++ STATIC ${VORO_SOURCES}) diff --git a/src/third_party/voro/CMakeLists.txt b/src/third_party/voro/CMakeLists.txt new file mode 100644 index 000000000..c721f9b2c --- /dev/null +++ b/src/third_party/voro/CMakeLists.txt @@ -0,0 +1,78 @@ +cmake_minimum_required(VERSION 3.10) + +project(voro++ VERSION 0.4.6 LANGUAGES CXX) +set(SOVERSION "0") + +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CXX_FLAGS) + #release comes with -O3 by default + set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel." FORCE) +endif(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CXX_FLAGS) + +######################################################################## +# User input options # +######################################################################## +option(VORO_BUILD_SHARED_LIBS "Build shared libs" ON) +include(GNUInstallDirs) + +option(VORO_BUILD_EXAMPLES "Build examples" ON) +option(VORO_BUILD_CMD_LINE "Build command line project" ON) +option(VORO_ENABLE_DOXYGEN "Enable doxygen" ON) + +######################################################################## +#Find external packages +######################################################################## +if (${VORO_ENABLE_DOXYGEN}) + find_package(Doxygen) +endif() + +###################################### +# Include the following subdirectory # +###################################### + +file(GLOB VORO_SOURCES src/*.cc) +file(GLOB NOT_VORO_SOURCES src/v_base_wl.cc src/cmd_line.cc src/voro++.cc) +list(REMOVE_ITEM VORO_SOURCES ${NOT_VORO_SOURCES}) +add_library(voro++ ${VORO_SOURCES}) +set_target_properties(voro++ PROPERTIES + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/src" + SOVERSION ${SOVERSION}) +install(TARGETS voro++ EXPORT VORO_Targets LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) +#for voro++.hh +target_include_directories(voro++ PUBLIC $ $) + +if (${VORO_BUILD_CMD_LINE}) + add_executable(cmd_line src/cmd_line.cc) + target_link_libraries(cmd_line PRIVATE voro++) + #cannot have two targets with the same name, so renaming cmd_line + set_target_properties(cmd_line PROPERTIES OUTPUT_NAME voro++ + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/src") + install(TARGETS cmd_line RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) +endif() + +if (${VORO_BUILD_EXAMPLES}) + file(GLOB EXAMPLE_SOURCES examples/*/*.cc) + foreach(SOURCE ${EXAMPLE_SOURCES}) + string(REGEX REPLACE "^.*/([^/]*)\\.cc$" "\\1" PROGNAME "${SOURCE}") + if (NOT PROGNAME STREQUAL ellipsoid) #ellipsoid is broken + string(REGEX REPLACE "^.*/(examples/.*)/${PROGNAME}\\.cc$" "\\1" DIRNAME "${SOURCE}") + add_executable(${PROGNAME} ${SOURCE}) + target_link_libraries(${PROGNAME} PRIVATE voro++) + set_target_properties(${PROGNAME} PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${DIRNAME}" ) + endif() + endforeach(SOURCE) +endif() + +file(GLOB_RECURSE VORO_HEADERS src/voro++.hh) +install(FILES ${VORO_HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) +install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/man/voro++.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) +# no external deps for we can use target file as config file +install(EXPORT VORO_Targets FILE VOROConfig.cmake NAMESPACE VORO:: DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/VORO) +include(CMakePackageConfigHelpers) +write_basic_package_version_file("VOROConfigVersion.cmake" VERSION ${PROJECT_VERSION} COMPATIBILITY ExactVersion) +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/VOROConfigVersion.cmake" DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/VORO) + +if (${VORO_ENABLE_DOXYGEN} AND DOXYGEN_FOUND) + add_custom_target(doxygen COMMAND ${DOXYGEN_EXECUTABLE} src/Doxyfile + COMMENT "Build doxygen documentation") +endif() diff --git a/src/third_party/voro/README b/src/third_party/voro/README new file mode 100644 index 000000000..322316cc7 --- /dev/null +++ b/src/third_party/voro/README @@ -0,0 +1,152 @@ +Voro++, a 3D cell-based Voronoi library (http://math.lbl.gov/voro++/) +By Chris H. Rycroft (Harvard University / Lawrence Berkeley Laboratory) +================================================================ +Voro++ is a software library for carrying out three-dimensional computations +of the Voronoi tessellation. A distinguishing feature of the Voro++ library +is that it carries out cell-based calculations, computing the Voronoi cell +for each particle individually, rather than computing the Voronoi +tessellation as a global network of vertices and edges. It is particularly +well-suited for applications that rely on cell-based statistics, where +features of Voronoi cells (e.g. volume, centroid, number of faces) can be +used to analyze a system of particles. + +Voro++ comprises of several C++ classes that can be built as a static library +and linked to. A command-line utility is also provided that can analyze text +files of particle configurations and use most of the features of the code. +Numerous examples are provided to demonstrate the library's features and all of +these are discussed in detail on the library website. + + +Compilation - Linux / Mac OS / Windows with Cygwin +================================================== +The code is written in ANSI C++, and compiles on many system architectures. The +package contains the C++ source code, example files, miscellaneous utilities +and documentation. On Linux, Mac OS, and Windows (using Cygwin), the +compilation and installed can be carried out using GNU Make. + +To begin, the user should review the file "config.mk" in the top level +directory, to make sure that the compilation and installation settings are +appropriate for their system. Typing "make" will then compile the static +library, command-line utility, and examples. The command-line utility and +library will appear within the "src" directory. + +Following successful compilation, the library, command-line utility, and +documentation can be installed by typing "sudo make install". By default, the +program files are installed into /usr/local, and it may be necessary to modify +your environment variables in order to access the installed files: + +- to use the command-line utility, the variable PATH should contain + /usr/local/bin. +- to access the Voro++ man page, the variable MANPATH should contain + /usr/local/man. +- to access the Voro++ header files, code compilation should include + the flag '-I/usr/local/include/voro++'. +- to link to the static library, code compilation should include the + flags '-L/usr/local/lib' to tell the linker where to look, and then + '-lvoro++' to link to the library. + +The library website contains additional notes on setting environment variables, +and many guides are available on the Internet. + +The code can later be uninstalled with "sudo make uninstall". It is also +possible to use the library and command-line utility without installation by +calling the files directly once they have been compiled. On systems where the +user does not have root privileges to install into /usr/local, the "config.mk" +file can be modified to install into the user's home directory by setting +PREFIX=$(HOME). Voro++ supports parallel compilation by using the "make -j " +command where n is the number of threads. + + +Compilation - Windows without Cygwin +==================================== +On a Windows machine without a terminal environment like Cygwin, it is possible +to import and compile the library in many standard C++ development +environments. Users have reported success in building the library with +Microsoft Visual C++ Express and Code::Blocks. + + +Related programs +================ +No external dependencies are required to compile and run the code, but several +programs may be useful for analyzing the output: + +- The freeware plotting program Gnuplot (available at www.gnuplot.info) can be + used for rapid 2D and 3D visualization of the program output. + +- The freeware raytracer POV-Ray (available at www.povray.org) can be used for + high-quality renderings of the program output. + +- The reference manual is generated from comments in the source code using + Doxygen (available at www.doxygen.org). This package is only required if the + library files are being developed and the reference manuals need to be + regenerated. The complete reference manual to the current code is available + online at http://math.lbl.gov/voro++/doc/refman/ + + +Contents +======== +examples - many documented examples making use of the library +html - an HTML-based reference manual (generated by Doxygen) +man - contains the man page that is installed with the program +scripts - miscellaneous helper scripts +src - source code files + + +Usage +===== +Voro++ is released as free software through the Lawrence Berkeley National +Laboratory - a detailed copyright notice is provided below, and the complete +terms of the license can be found in the LICENSE file. + +I am very interested to hear from users of the software, so if you find this +useful, please email me at chr@alum.mit.edu. Also, if you plan to publish an +academic paper using this software, please consider citing one of the following +publications: + +- Chris H. Rycroft, "Voro++: A three-dimensional Voronoi cell library in C++", + Chaos 19, 041111 (2009). + +- Chris H. Rycroft, Gary S. Grest, James W. Landry, and Martin Z. Bazant, + "Analysis of Granular Flow in a Pebble-Bed Nuclear Reactor", + Phys. Rev. E 74, 021306 (2006). + +- Chris H. Rycroft, "Multiscale Modeling in Granular Flow", PhD thesis + submitted to the Massachusetts Institute of Technology, September 2007. + (http://seas.harvard.edu/~chr/publish/phd.html) + +The first reference contains a one-page overview of the library. The second +reference contains some of the initial images that were made using a very early +version of this code, to track small changes in packing fraction in a large +particle simulation. The third reference discusses the use of 3D Voronoi cells, +and describes the algorithms that were employed in the early version of this +code. Since the publication of the above references, the algorithms in Voro++ +have been significantly improved. + + +Copyright Notice +================ +Voro++ Copyright (c) 2008, The Regents of the University of California, through +Lawrence Berkeley National Laboratory (subject to receipt of any required +approvals from the U.S. Dept. of Energy). All rights reserved. + +If you have questions about your rights to use or distribute this software, +please contact Berkeley Lab's Technology Transfer Department at TTD@lbl.gov. + +NOTICE. This software was developed under partial funding from the U.S. +Department of Energy. As such, the U.S. Government has been granted for itself +and others acting on its behalf a paid-up, nonexclusive, irrevocable, worldwide +license in the Software to reproduce, prepare derivative works, and perform +publicly and display publicly. Beginning five (5) years after the date +permission to assert copyright is obtained from the U.S. Department of Energy, +and subject to any subsequent five (5) year renewals, the U.S. Government is +granted for itself and others acting on its behalf a paid-up, nonexclusive, +irrevocable, worldwide license in the Software to reproduce, prepare derivative +works, distribute copies to the public, perform publicly and display publicly, +and to permit others to do so. + + +Acknowledgments +=============== +This work was supported by the Director, Office of Science, Computational and +Technology Research, U.S. Department of Energy under Contract No. +DE-AC02-05CH11231. diff --git a/src/third_party/voro/src/README b/src/third_party/voro/src/README new file mode 100644 index 000000000..09326db58 --- /dev/null +++ b/src/third_party/voro/src/README @@ -0,0 +1,21 @@ +Voro++ library source files +=========================== +This directory contains the source code for the library. For full details of +each file, see the files section of the online reference manual at +http://math.lbl.gov/voro++/doc/refman/ + +Several other files are present: + +Makefile - the GNU Makefile controlling the compilation. + +Makefile.dep - a file containing all the dependencies of the source files, +automatically generated by the GNU compiler. + +cmd_line.cc - source file for creating the command-line utility that makes use +of the library. + +Doxyfile - configuration file for Doxygen, used to automatically generate +documentation based on the source code comments. + +worklist_gen.pl - perl script for automatically generating the worklist.hh and +v_base_wl.cc files. diff --git a/src/third_party/voro/src/cmd_line.cc b/src/third_party/voro/src/cmd_line.cc new file mode 100644 index 000000000..afd27ffb8 --- /dev/null +++ b/src/third_party/voro/src/cmd_line.cc @@ -0,0 +1,497 @@ +// Voro++, a 3D cell-based Voronoi library +// +// Author : Chris H. Rycroft (Harvard University / LBL) +// Email : chr@alum.mit.edu +// Date : August 30th 2011 + +/** \file cmd_line.cc + * \brief Source code for the command-line utility. */ + +#include + +#include "voro++.hh" +using namespace voro; + +enum blocks_mode { + none, + length_scale, + specified +}; + +// A maximum allowed number of regions, to prevent enormous amounts of memory +// being allocated +const int max_regions=16777216; + +// This message gets displayed if the user requests the help flag +void help_message() { + puts("Voro++ version 0.4.6, by Chris H. Rycroft (UC Berkeley/LBL)\n\n" + "Syntax: voro++ [options] \n" + " \n\n" + "By default, the utility reads in the input file of particle IDs and positions,\n" + "computes the Voronoi cell for each, and then creates with an\n" + "additional column containing the volume of each Voronoi cell.\n\n" + "Available options:\n" + " -c : Specify a custom output string\n" + " -g : Turn on the gnuplot output to \n" + " -h/--help : Print this information\n" + " -hc : Print information about custom output\n" + " -l : Manually specify a length scale to configure the internal\n" + " computational grid\n" + " -m : Manually choose the memory allocation per grid block\n" + " (default 8)\n" + " -n [3] : Manually specify the internal grid size\n" + " -o : Ensure that the output file has the same order as the input\n" + " file\n" + " -p : Make container periodic in all three directions\n" + " -px : Make container periodic in the x direction\n" + " -py : Make container periodic in the y direction\n" + " -pz : Make container periodic in the z direction\n" + " -r : Assume the input file has an extra coordinate for radii\n" + " -v : Verbose output\n" + " --version : Print version information\n" + " -wb [6] : Add six plane wall objects to make rectangular box containing\n" + " the space x1 and POV-Ray Voronoi\n" + " cells to \n" + " -yp : Save only POV-Ray particles to \n" + " -yv : Save only POV-Ray Voronoi cells to "); +} + +// This message gets displayed if the user requests information about doing +// custom output +void custom_output_message() { + puts("The \"-c\" option allows a string to be specified that will customize the output\n" + "file to contain a variety of statistics about each computed Voronoi cell. The\n" + "string is similar to the standard C printf() function, made up of text with\n" + "additional control sequences that begin with percentage signs that are expanded\n" + "to different statistics. See http://math.lbl.gov/voro++/doc/custom.html for more\n" + "information.\n" + "\nParticle-related:\n" + " %i The particle ID number\n" + " %x The x coordinate of the particle\n" + " %y The y coordinate of the particle\n" + " %z The z coordinate of the particle\n" + " %q The position vector of the particle, short for \"%x %y %z\"\n" + " %r The radius of the particle (only printed if -p enabled)\n" + "\nVertex-related:\n" + " %w The number of vertices in the Voronoi cell\n" + " %p A list of the vertices of the Voronoi cell in the format (x,y,z),\n" + " relative to the particle center\n" + " %P A list of the vertices of the Voronoi cell in the format (x,y,z),\n" + " relative to the global coordinate system\n" + " %o A list of the orders of each vertex\n" + " %m The maximum radius squared of a vertex position, relative to the\n" + " particle center\n" + "\nEdge-related:\n" + " %g The number of edges of the Voronoi cell\n" + " %E The total edge distance\n" + " %e A list of perimeters of each face\n" + "\nFace-related:\n" + " %s The number of faces of the Voronoi cell\n" + " %F The total surface area of the Voronoi cell\n" + " %A A frequency table of the number of edges for each face\n" + " %a A list of the number of edges for each face\n" + " %f A list of areas of each face\n" + " %t A list of bracketed sequences of vertices that make up each face\n" + " %l A list of normal vectors for each face\n" + " %n A list of neighboring particle or wall IDs corresponding to each face\n" + "\nVolume-related:\n" + " %v The volume of the Voronoi cell\n" + " %c The centroid of the Voronoi cell, relative to the particle center\n" + " %C The centroid of the Voronoi cell, in the global coordinate system"); +} + +// Ths message is displayed if the user requests version information +void version_message() { + puts("Voro++ version 0.4.6 (October 17th 2013)"); +} + +// Prints an error message. This is called when the program is unable to make +// sense of the command-line options. +void error_message() { + fputs("voro++: Unrecognized command-line options; type \"voro++ -h\" for more\ninformation.\n",stderr); +} + +// Carries out the Voronoi computation and outputs the results to the requested +// files +template +void cmd_line_output(c_loop &vl,c_class &con,const char* format,FILE* outfile,FILE* gnu_file,FILE* povp_file,FILE* povv_file,bool verbose,double &vol,int &vcc,int &tp) { + int pid,ps=con.ps;double x,y,z,r; + if(con.contains_neighbor(format)) { + voronoicell_neighbor c(con); + if(vl.start()) do if(con.compute_cell(c,vl)) { + vl.pos(pid,x,y,z,r); + if(outfile!=NULL) c.output_custom(format,pid,x,y,z,r,outfile); + if(gnu_file!=NULL) c.draw_gnuplot(x,y,z,gnu_file); + if(povp_file!=NULL) { + fprintf(povp_file,"// id %d\n",pid); + if(ps==4) fprintf(povp_file,"sphere{<%g,%g,%g>,%g}\n",x,y,z,r); + else fprintf(povp_file,"sphere{<%g,%g,%g>,s}\n",x,y,z); + } + if(povv_file!=NULL) { + fprintf(povv_file,"// cell %d\n",pid); + c.draw_pov(x,y,z,povv_file); + } + if(verbose) {vol+=c.volume();vcc++;} + } while(vl.inc()); + } else { + voronoicell c(con); + if(vl.start()) do if(con.compute_cell(c,vl)) { + vl.pos(pid,x,y,z,r); + if(outfile!=NULL) c.output_custom(format,pid,x,y,z,r,outfile); + if(gnu_file!=NULL) c.draw_gnuplot(x,y,z,gnu_file); + if(povp_file!=NULL) { + fprintf(povp_file,"// id %d\n",pid); + if(ps==4) fprintf(povp_file,"sphere{<%g,%g,%g>,%g}\n",x,y,z,r); + else fprintf(povp_file,"sphere{<%g,%g,%g>,s}\n",x,y,z); + } + if(povv_file!=NULL) { + fprintf(povv_file,"// cell %d\n",pid); + c.draw_pov(x,y,z,povv_file); + } + if(verbose) {vol+=c.volume();vcc++;} + } while(vl.inc()); + } + if(verbose) tp=con.total_particles(); +} + +int main(int argc,char **argv) { + int i=1,j=-7,custom_output=0,nx,ny,nz,init_mem(8); + double ls=0; + blocks_mode bm=none; + bool gnuplot_output=false,povp_output=false,povv_output=false,polydisperse=false; + bool xperiodic=false,yperiodic=false,zperiodic=false,ordered=false,verbose=false; + pre_container *pcon=NULL;pre_container_poly *pconp=NULL; + wall_list wl; + + // If there's one argument, check to see if it's requesting help. + // Otherwise, bail out with an error. + if(argc==2) { + if(strcmp(argv[1],"-h")==0||strcmp(argv[1],"--help")==0) { + help_message();return 0; + } else if(strcmp(argv[1],"-hc")==0) { + custom_output_message();return 0; + } else if(strcmp(argv[1],"--version")==0) { + version_message();return 0; + } else { + error_message(); + return VOROPP_CMD_LINE_ERROR; + } + } + + // If there aren't enough command-line arguments, then bail out + // with an error. + if(argc<7) { + error_message(); + return VOROPP_CMD_LINE_ERROR; + } + + // We have enough arguments. Now start searching for command-line + // options. + while(i=argc-8) {error_message();wl.deallocate();return VOROPP_CMD_LINE_ERROR;} + if(custom_output==0) { + custom_output=++i; + } else { + fputs("voro++: multiple custom output strings detected\n",stderr); + wl.deallocate(); + return VOROPP_CMD_LINE_ERROR; + } + } else if(strcmp(argv[i],"-g")==0) { + gnuplot_output=true; + } else if(strcmp(argv[i],"-h")==0||strcmp(argv[i],"--help")==0) { + help_message();wl.deallocate();return 0; + } else if(strcmp(argv[i],"-hc")==0) { + custom_output_message();wl.deallocate();return 0; + } else if(strcmp(argv[i],"-l")==0) { + if(i>=argc-8) {error_message();wl.deallocate();return VOROPP_CMD_LINE_ERROR;} + if(bm!=none) { + fputs("voro++: Conflicting options about grid setup (-l/-n)\n",stderr); + wl.deallocate(); + return VOROPP_CMD_LINE_ERROR; + } + bm=length_scale; + i++;ls=atof(argv[i]); + } else if(strcmp(argv[i],"-m")==0) { + i++;init_mem=atoi(argv[i]); + } else if(strcmp(argv[i],"-n")==0) { + if(i>=argc-10) {error_message();wl.deallocate();return VOROPP_CMD_LINE_ERROR;} + if(bm!=none) { + fputs("voro++: Conflicting options about grid setup (-l/-n)\n",stderr); + wl.deallocate(); + return VOROPP_CMD_LINE_ERROR; + } + bm=specified; + i++; + nx=atoi(argv[i++]); + ny=atoi(argv[i++]); + nz=atoi(argv[i]); + if(nx<=0||ny<=0||nz<=0) { + fputs("voro++: Computational grid specified with -n must be greater than one\n" + "in each direction\n",stderr); + wl.deallocate(); + return VOROPP_CMD_LINE_ERROR; + } + } else if(strcmp(argv[i],"-o")==0) { + ordered=true; + } else if(strcmp(argv[i],"-p")==0) { + xperiodic=yperiodic=zperiodic=true; + } else if(strcmp(argv[i],"-px")==0) { + xperiodic=true; + } else if(strcmp(argv[i],"-py")==0) { + yperiodic=true; + } else if(strcmp(argv[i],"-pz")==0) { + zperiodic=true; + } else if(strcmp(argv[i],"-r")==0) { + polydisperse=true; + } else if(strcmp(argv[i],"-v")==0) { + verbose=true; + } else if(strcmp(argv[i],"--version")==0) { + version_message(); + wl.deallocate(); + return 0; + } else if(strcmp(argv[i],"-wb")==0) { + if(i>=argc-13) {error_message();wl.deallocate();return VOROPP_CMD_LINE_ERROR;} + i++; + double w0=atof(argv[i++]),w1=atof(argv[i++]); + double w2=atof(argv[i++]),w3=atof(argv[i++]); + double w4=atof(argv[i++]),w5=atof(argv[i]); + wl.add_wall(new wall_plane(-1,0,0,-w0,j));j--; + wl.add_wall(new wall_plane(1,0,0,w1,j));j--; + wl.add_wall(new wall_plane(0,-1,0,-w2,j));j--; + wl.add_wall(new wall_plane(0,1,0,w3,j));j--; + wl.add_wall(new wall_plane(0,0,-1,-w4,j));j--; + wl.add_wall(new wall_plane(0,0,1,w5,j));j--; + } else if(strcmp(argv[i],"-ws")==0) { + if(i>=argc-11) {error_message();wl.deallocate();return VOROPP_CMD_LINE_ERROR;} + i++; + double w0=atof(argv[i++]),w1=atof(argv[i++]); + double w2=atof(argv[i++]),w3=atof(argv[i]); + wl.add_wall(new wall_sphere(w0,w1,w2,w3,j)); + j--; + } else if(strcmp(argv[i],"-wp")==0) { + if(i>=argc-11) {error_message();wl.deallocate();return VOROPP_CMD_LINE_ERROR;} + i++; + double w0=atof(argv[i++]),w1=atof(argv[i++]); + double w2=atof(argv[i++]),w3=atof(argv[i]); + wl.add_wall(new wall_plane(w0,w1,w2,w3,j)); + j--; + } else if(strcmp(argv[i],"-wc")==0) { + if(i>=argc-14) {error_message();wl.deallocate();return VOROPP_CMD_LINE_ERROR;} + i++; + double w0=atof(argv[i++]),w1=atof(argv[i++]); + double w2=atof(argv[i++]),w3=atof(argv[i++]); + double w4=atof(argv[i++]),w5=atof(argv[i++]); + double w6=atof(argv[i]); + wl.add_wall(new wall_cylinder(w0,w1,w2,w3,w4,w5,w6,j)); + j--; + } else if(strcmp(argv[i],"-wo")==0) { + if(i>=argc-14) {error_message();wl.deallocate();return VOROPP_CMD_LINE_ERROR;} + i++; + double w0=atof(argv[i++]),w1=atof(argv[i++]); + double w2=atof(argv[i++]),w3=atof(argv[i++]); + double w4=atof(argv[i++]),w5=atof(argv[i++]); + double w6=atof(argv[i]); + wl.add_wall(new wall_cone(w0,w1,w2,w3,w4,w5,w6,j)); + j--; + } else if(strcmp(argv[i],"-y")==0) { + povp_output=povv_output=true; + } else if(strcmp(argv[i],"-yp")==0) { + povp_output=true; + } else if(strcmp(argv[i],"-yv")==0) { + povv_output=true; + } else { + wl.deallocate(); + error_message(); + return VOROPP_CMD_LINE_ERROR; + } + i++; + } + + // Check the memory guess is positive + if(init_mem<=0) { + fputs("voro++: The memory allocation must be positive\n",stderr); + wl.deallocate(); + return VOROPP_CMD_LINE_ERROR; + } + + // Read in the dimensions of the test box, and estimate the number of + // boxes to divide the region up into + double ax=atof(argv[i]),bx=atof(argv[i+1]); + double ay=atof(argv[i+2]),by=atof(argv[i+3]); + double az=atof(argv[i+4]),bz=atof(argv[i+5]); + + // Check that for each coordinate, the minimum value is smaller + // than the maximum value + if(bximport(argv[i+6]); + pconp->guess_optimal(nx,ny,nz); + } else { + pcon=new pre_container(ax,bx,ay,by,az,bz,xperiodic,yperiodic,zperiodic); + pcon->import(argv[i+6]); + pcon->guess_optimal(nx,ny,nz); + } + } else { + double nxf,nyf,nzf; + if(bm==length_scale) { + + // Check that the length scale is positive and + // reasonably large + if(lsmax_regions) { + fprintf(stderr,"voro++: Number of computational blocks exceeds the maximum allowed of %d.\n" + "Either increase the particle length scale, or recompile with an increased\nmaximum.",max_regions); + wl.deallocate(); + return VOROPP_MEMORY_ERROR; + } + } + + // Check that the output filename is a sensible length + int flen=strlen(argv[i+6]); + if(flen>4096) { + fputs("voro++: Filename too long\n",stderr); + wl.deallocate(); + return VOROPP_CMD_LINE_ERROR; + } + + // Open files for output + char *buffer=new char[flen+7]; + sprintf(buffer,"%s.vol",argv[i+6]); + FILE *outfile=safe_fopen(buffer,"w"),*gnu_file,*povp_file,*povv_file; + if(gnuplot_output) { + sprintf(buffer,"%s.gnu",argv[i+6]); + gnu_file=safe_fopen(buffer,"w"); + } else gnu_file=NULL; + if(povp_output) { + sprintf(buffer,"%s_p.pov",argv[i+6]); + povp_file=safe_fopen(buffer,"w"); + } else povp_file=NULL; + if(povv_output) { + sprintf(buffer,"%s_v.pov",argv[i+6]); + povv_file=safe_fopen(buffer,"w"); + } else povv_file=NULL; + delete [] buffer; + + const char *c_str=(custom_output==0?(polydisperse?"%i %q %v %r":"%i %q %v"):argv[custom_output]); + + // Now switch depending on whether polydispersity was enabled, and + // whether output ordering is requested + double vol=0;int tp=0,vcc=0; + if(polydisperse) { + if(ordered) { + particle_order vo; + container_poly con(ax,bx,ay,by,az,bz,nx,ny,nz,xperiodic,yperiodic,zperiodic,init_mem); + con.add_wall(wl); + if(bm==none) { + pconp->setup(vo,con);delete pconp; + } else con.import(vo,argv[i+6]); + + c_loop_order vlo(con,vo); + cmd_line_output(vlo,con,c_str,outfile,gnu_file,povp_file,povv_file,verbose,vol,vcc,tp); + } else { + container_poly con(ax,bx,ay,by,az,bz,nx,ny,nz,xperiodic,yperiodic,zperiodic,init_mem); + con.add_wall(wl); + + if(bm==none) { + pconp->setup(con);delete pconp; + } else con.import(argv[i+6]); + + c_loop_all vla(con); + cmd_line_output(vla,con,c_str,outfile,gnu_file,povp_file,povv_file,verbose,vol,vcc,tp); + } + } else { + if(ordered) { + particle_order vo; + container con(ax,bx,ay,by,az,bz,nx,ny,nz,xperiodic,yperiodic,zperiodic,init_mem); + con.add_wall(wl); + if(bm==none) { + pcon->setup(vo,con);delete pcon; + } else con.import(vo,argv[i+6]); + + c_loop_order vlo(con,vo); + cmd_line_output(vlo,con,c_str,outfile,gnu_file,povp_file,povv_file,verbose,vol,vcc,tp); + } else { + container con(ax,bx,ay,by,az,bz,nx,ny,nz,xperiodic,yperiodic,zperiodic,init_mem); + con.add_wall(wl); + if(bm==none) { + pcon->setup(con);delete pcon; + } else con.import(argv[i+6]); + c_loop_all vla(con); + cmd_line_output(vla,con,c_str,outfile,gnu_file,povp_file,povv_file,verbose,vol,vcc,tp); + } + } + + // Print information if verbose output requested + if(verbose) { + printf("Container geometry : [%g:%g] [%g:%g] [%g:%g]\n" + "Computational grid size : %d by %d by %d (%s)\n" + "Filename : %s\n" + "Output string : %s%s\n",ax,bx,ay,by,az,bz,nx,ny,nz, + bm==none?"estimated from file":(bm==length_scale? + "estimated using length scale":"directly specified"), + argv[i+6],c_str,custom_output==0?" (default)":""); + printf("Total imported particles : %d (%.2g per grid block)\n" + "Total V. cells computed : %d\n" + "Total container volume : %g\n" + "Total V. cell volume : %g\n",tp,((double) tp)/(nx*ny*nz), + vcc,(bx-ax)*(by-ay)*(bz-az),vol); + } + + // Close output files + fclose(outfile); + if(gnu_file!=NULL) fclose(gnu_file); + if(povp_file!=NULL) fclose(povp_file); + if(povv_file!=NULL) fclose(povv_file); + return 0; +} diff --git a/src/third_party/voro/src/voro++.cc b/src/third_party/voro/src/voro++.cc new file mode 100644 index 000000000..8616f7f3d --- /dev/null +++ b/src/third_party/voro/src/voro++.cc @@ -0,0 +1,19 @@ +// Voro++, a 3D cell-based Voronoi library +// +// Author : Chris H. Rycroft (Harvard University / LBL) +// Email : chr@alum.mit.edu +// Date : August 30th 2011 + +/** \file voro++.cc + * \brief A file that loads all of the function implementation files. */ + +#include "cell.cc" +#include "common.cc" +#include "v_base.cc" +#include "container.cc" +#include "unitcell.cc" +#include "container_prd.cc" +#include "pre_container.cc" +#include "v_compute.cc" +#include "c_loops.cc" +#include "wall.cc" diff --git a/src/third_party/voro/src/voro++.hh b/src/third_party/voro/src/voro++.hh new file mode 100644 index 000000000..3c12d3d16 --- /dev/null +++ b/src/third_party/voro/src/voro++.hh @@ -0,0 +1,333 @@ +// Voro++, a 3D cell-based Voronoi library +// +// Author : Chris H. Rycroft (Harvard University / LBL) +// Email : chr@alum.mit.edu +// Date : August 30th 2011 + +/** \file voro++.hh + * \brief A file that loads all of the Voro++ header files. */ + +/** \mainpage Voro++ class reference manual + * \section intro Introduction + * Voro++ is a software library for carrying out three-dimensional computations + * of the Voronoi tessellation. A distinguishing feature of the Voro++ library + * is that it carries out cell-based calculations, computing the Voronoi cell + * for each particle individually, rather than computing the Voronoi + * tessellation as a global network of vertices and edges. It is particularly + * well-suited for applications that rely on cell-based statistics, where + * features of Voronoi cells (eg. volume, centroid, number of faces) can be + * used to analyze a system of particles. + * + * Voro++ is written in C++ and can be built as a static library that can be + * linked to. This manual provides a reference for every function in the class + * structure. For a general overview of the program, see the Voro++ website at + * http://math.lbl.gov/voro++/ and in particular the example programs at + * http://math.lbl.gov/voro++/examples/ that demonstrate many of the library's + * features. + * + * \section class C++ class structure + * The code is structured around several C++ classes. The voronoicell_base + * class contains all of the routines for constructing a single Voronoi cell. + * It represents the cell as a collection of vertices that are connected by + * edges, and there are routines for initializing, making, and outputting the + * cell. The voronoicell_base class form the base of the voronoicell and + * voronoicell_neighbor classes, which add specialized routines depending on + * whether neighboring particle ID information for each face must be tracked or + * not. Collectively, these classes are referred to as "voronoicell classes" + * within the documentation. + * + * There is a hierarchy of classes that represent three-dimensional particle + * systems. All of these are derived from the voro_base class, which contains + * constants that divide a three-dimensional system into a rectangular grid of + * equally-sized rectangular blocks; this grid is used for computational + * efficiency during the Voronoi calculations. + * + * The container_base, container, and container_poly are then derived from the + * voro_base class to represent a particle system in a specific + * three-dimensional rectangular box using both periodic and non-periodic + * boundary conditions. In addition, the container_periodic_base, + * container_periodic, and container_periodic_poly classes represent + * a particle system in a three-dimensional non-orthogonal periodic domain, + * defined by three periodicity vectors that represent a parallelepiped. + * Collectively, these classes are referred to as "container classes" within + * the documentation. + * + * The voro_compute template encapsulates all of the routines for computing + * Voronoi cells. Each container class has a voro_compute template within + * it, that accesses the container's particle system, and computes the Voronoi + * cells. + * + * There are several wall classes that can be used to apply certain boundary + * conditions using additional plane cuts during the Voronoi cell compution. + * The code also contains a number of small loop classes, c_loop_all, + * c_loop_subset, c_loop_all_periodic, and c_loop_order that can be used to + * iterate over a certain subset of particles in a container. The latter class + * makes use of a special particle_order class that stores a specific order of + * particles within the container. The library also contains the classes + * pre_container_base, pre_container, and pre_container_poly, that can be used + * as temporary storage when importing data of unknown size. + * + * \section voronoicell The voronoicell classes + * The voronoicell class represents a single Voronoi cell as a convex + * polyhedron, with a set of vertices that are connected by edges. The class + * contains a variety of functions that can be used to compute and output the + * Voronoi cell corresponding to a particular particle. The command init() + * can be used to initialize a cell as a large rectangular box. The Voronoi cell + * can then be computed by repeatedly cutting it with planes that correspond to + * the perpendicular bisectors between that particle and its neighbors. + * + * This is achieved by using the plane() routine, which will recompute the + * cell's vertices and edges after cutting it with a single plane. This is the + * key routine in voronoicell class. It begins by exploiting the convexity + * of the underlying cell, tracing between edges to work out if the cell + * intersects the cutting plane. If it does not intersect, then the routine + * immediately exits. Otherwise, it finds an edge or vertex that intersects + * the plane, and from there, traces out a new face on the cell, recomputing + * the edge and vertex structure accordingly. + * + * Once the cell is computed, there are many routines for computing features of + * the the Voronoi cell, such as its volume, surface area, or centroid. There + * are also many routines for outputting features of the Voronoi cell, or + * writing its shape in formats that can be read by Gnuplot or POV-Ray. + * + * \subsection internal Internal data representation + * The voronoicell class has a public member p representing the + * number of vertices. The polyhedral structure of the cell is stored + * in the following arrays: + * + * - pts: a one-dimensional array of floating point numbers, that represent the + * position vectors x_0, x_1, ..., x_{p-1} of the polyhedron vertices. + * - nu: the order of each vertex n_0, n_1, ..., n_{p-1}, corresponding to + * the number of other vertices to which each is connected. + * - ed: a two-dimensional table of edges and relations. For the ith vertex, + * ed[i] has 2n_i+1 elements. The first n_i elements are the edges e(j,i), + * where e(j,i) is the jth neighbor of vertex i. The edges are ordered + * according to a right-hand rule with respect to an outward-pointing normal. + * The next n_i elements are the relations l(j,i) which satisfy the property + * e(l(j,i),e(j,i)) = i. The final element of the ed[i] list is a back + * pointer used in memory allocation. + * + * In a very large number of cases, the values of n_i will be 3. This is because + * the only way that a higher-order vertex can be created in the plane() + * routine is if the cutting plane perfectly intersects an existing vertex. For + * random particle arrangements with position vectors specified to double + * precision this should happen very rarely. A preliminary version of this code + * was quite successful with only making use of vertices of order 3. However, + * when calculating millions of cells, it was found that this approach is not + * robust, since a single floating point error can invalidate the computation. + * This can also be a problem for cases featuring crystalline arrangements of + * particles where the corresponding Voronoi cells may have high-order vertices + * by construction. + * + * Because of this, Voro++ takes the approach that it if an existing vertex is + * within a small numerical tolerance of the cutting plane, it is treated as + * being exactly on the plane, and the polyhedral topology is recomputed + * accordingly. However, while this improves robustness, it also adds the + * complexity that n_i may no longer always be 3. This causes memory management + * to be significantly more complicated, as different vertices require a + * different number of elements in the ed[][] array. To accommodate this, the + * voronoicell class allocated edge memory in a different array called mep[][], + * in such a way that all vertices of order k are held in mep[k]. If vertex + * i has order k, then ed[i] points to memory within mep[k]. The array ed[][] + * is never directly initialized as a two-dimensional array itself, but points + * at allocations within mep[][]. To the user, it appears as though each row of + * ed[][] has a different number of elements. When vertices are added or + * deleted, care must be taken to reorder and reassign elements in these + * arrays. + * + * During the plane() routine, the code traces around the vertices of the cell, + * and adds new vertices along edges which intersect the cutting plane to + * create a new face. The values of l(j,i) are used in this computation, as + * when the code is traversing from one vertex on the cell to another, this + * information allows the code to immediately work out which edge of a vertex + * points back to the one it came from. As new vertices are created, the l(j,i) + * are also updated to ensure consistency. To ensure robustness, the plane + * cutting algorithm should work with any possible combination of vertices + * which are inside, outside, or exactly on the cutting plane. + * + * Vertices exactly on the cutting plane create some additional computational + * difficulties. If there are two marginal vertices connected by an existing + * edge, then it would be possible for duplicate edges to be created between + * those two vertices, if the plane routine traces along both sides of this + * edge while constructing the new face. The code recognizes these cases and + * prevents the double edge from being formed. Another possibility is the + * formation of vertices of order two or one. At the end of the plane cutting + * routine, the code checks to see if any of these are present, removing the + * order one vertices by just deleting them, and removing the order two + * vertices by connecting the two neighbors of each vertex together. It is + * possible that the removal of a single low-order vertex could result in the + * creation of additional low-order vertices, so the process is applied + * recursively until no more are left. + * + * \section container The container classes + * There are four container classes available for general usage: container, + * container_poly, container_periodic, and container_periodic_poly. Each of + * these represent a system of particles in a specific three-dimensional + * geometry. They contain routines for importing particles from a text file, + * and adding particles individually. They also contain a large number of + * analyzing and outputting the particle system. Internally, the routines that + * compute Voronoi cells do so by making use of the voro_compute template. + * Each container class contains routines that tell the voro_compute template + * about the specific geometry of this container. + * + * \section voro_compute The voro_compute template + * The voro_compute template encapsulates the routines for carrying out the + * Voronoi cell computations. It contains data structures suchs as a mask and a + * queue that are used in the computations. The voro_compute template is + * associated with a specific container class, and during the computation, it + * calls routines in the container class to access the particle positions that + * are stored there. + * + * The key routine in this class is compute_cell(), which makes use of a + * voronoicell class to construct a Voronoi cell for a specific particle in the + * container. The basic approach that this function takes is to repeatedly cut + * the Voronoi cell by planes corresponding neighboring particles, and stop + * when it recognizes that all the remaining particles in the container are too + * far away to possibly influence cell's shape. The code makes use of two + * possible methods for working out when a cell computation is complete: + * + * - Radius test: if the maximum distance of a Voronoi cell + * vertex from the cell center is R, then no particles more than a distance + * 2R away can possibly influence the cell. This a very fast computation to + * do, but it has no directionality: if the cell extends a long way in one + * direction then particles a long distance in other directions will still + * need to be tested. + * - Region test: it is possible to test whether a specific region can + * possibly influence the cell by applying a series of plane tests at the + * point on the region which is closest to the Voronoi cell center. This is a + * slower computation to do, but it has directionality. + * + * Another useful observation is that the regions that need to be tested are + * simply connected, meaning that if a particular region does not need to be + * tested, then neighboring regions which are further away do not need to be + * tested. + * + * For maximum efficiency, it was found that a hybrid approach making use of + * both of the above tests worked well in practice. Radius tests work well for + * the first few blocks, but switching to region tests after then prevent the + * code from becoming extremely slow, due to testing over very large spherical + * shells of particles. The compute_cell() routine therefore takes the + * following approach: + * + * - Initialize the voronoicell class to fill the entire computational domain. + * - Cut the cell by any wall objects that have been added to the container. + * - Apply plane cuts to the cell corresponding to the other particles which + * are within the current particle's region. + * - Test over a pre-computed worklist of neighboring regions, that have been + * ordered according to the minimum distance away from the particle's + * position. Apply radius tests after every few regions to see if the + * calculation can terminate. + * - If the code reaches the end of the worklist, add all the neighboring + * regions to a new list. + * - Carry out a region test on the first item of the list. If the region needs + * to be tested, apply the plane() routine for all of its particles, and then + * add any neighboring regions to the end of the list that need to be tested. + * Continue until the list has no elements left. + * + * The compute_cell() routine forms the basis of many other routines, such as + * store_cell_volumes() and draw_cells_gnuplot() that can be used to calculate + * and draw the cells in a container. + * + * \section walls Wall computation + * Wall computations are handled by making use of a pure virtual wall class. + * Specific wall types are derived from this class, and require the + * specification of two routines: point_inside() that tests to see if a point + * is inside a wall or not, and cut_cell() that cuts a cell according to the + * wall's position. The walls can be added to the container using the + * add_wall() command, and these are called each time a compute_cell() command + * is carried out. At present, wall types for planes, spheres, cylinders, and + * cones are provided, although custom walls can be added by creating new + * classes derived from the pure virtual class. Currently all wall types + * approximate the wall surface with a single plane, which produces some small + * errors, but generally gives good results for dense particle packings in + * direct contact with a wall surface. It would be possible to create more + * accurate walls by making cut_cell() routines that approximate the curved + * surface with multiple plane cuts. + * + * The wall objects can used for periodic calculations, although to obtain + * valid results, the walls should also be periodic as well. For example, in a + * domain that is periodic in the x direction, a cylinder aligned along the x + * axis could be added. At present, the interior of all wall objects are convex + * domains, and consequently any superposition of them will be a convex domain + * also. Carrying out computations in non-convex domains poses some problems, + * since this could theoretically lead to non-convex Voronoi cells, which the + * internal data representation of the voronoicell class does not support. For + * non-convex cases where the wall surfaces feature just a small amount of + * negative curvature (eg. a torus) approximating the curved surface with a + * single plane cut may give an acceptable level of accuracy. For non-convex + * cases that feature internal angles, the best strategy may be to decompose + * the domain into several convex subdomains, carry out a calculation in each, + * and then add the results together. The voronoicell class cannot be easily + * modified to handle non-convex cells as this would fundamentally alter the + * algorithms that it uses, and cases could arise where a single plane cut + * could create several new faces as opposed to just one. + * + * \section loops Loop classes + * The container classes have a number of simple routines for calculating + * Voronoi cells for all particles within them. However, in some situations it + * is desirable to iterate over a specific subset of particles. This can be + * achieved with the c_loop classes that are all derived from the c_loop_base + * class. Each class can iterate over a specific subset of particles in a + * container. There are three loop classes for use with the container and + * container_poly classes: + * + * - c_loop_all will loop over all of the particles in a container. + * - c_loop_subset will loop over a subset of particles in a container that lie + * within some geometrical region. It can loop over particles in a + * rectangular box, particles in a sphere, or particles that lie within + * specific internal computational blocks. + * - c_loop_order will loop over a specific list of particles that were + * previously stored in a particle_order class. + * + * Several of the key routines within the container classes (such as + * draw_cells_gnuplot and print_custom) have versions where they can be passed + * a loop class to use. Loop classes can also be used directly and there are + * some examples on the library website that demonstrate this. It is also + * possible to write custom loop classes. + * + * In addition to the loop classes mentioned above, there is also a + * c_loop_all_periodic class, that is specifically for use with the + * container_periodic and container_periodic_poly classes. Since the data + * structures of these containers differ considerably, it requires a different + * loop class that is not interoperable with the others. + * + * \section pre_container The pre_container classes + * Voro++ makes use of internal computational grid of blocks that are used to + * configure the code for maximum efficiency. As discussed on the library + * website, the best performance is achieved for around 5 particles per block, + * with anything in the range from 3 to 12 giving good performance. Usually + * the size of the grid can be chosen by ensuring that the number of blocks is + * equal to the number of particles divided by 5. + * + * However, this can be difficult to choose in cases when the number of + * particles is not known a priori, and in thes cases the pre_container classes + * can be used. They can import an arbitrary number of particle positions from + * a file, dynamically allocating memory in chunks as necessary. Once particles + * are imported, they can guess an optimal block arrangement to use for the + * container class, and then transfer the particles to the container. By + * default, this procedure is used by the command-line utility to enable it to + * work well with arbitrary sizes of input data. + * + * The pre_container class can be used when no particle radius information is + * available, and the pre_container_poly class can be used when radius + * information is available. At present, the pre_container classes can only be + * used with the container and container_poly classes. They do not support + * the container_periodic and container_periodic_poly classes. */ + +#ifndef VOROPP_HH +#define VOROPP_HH + +#include "config.hh" +#include "common.hh" +#include "cell.hh" +#include "v_base.hh" +#include "rad_option.hh" +#include "container.hh" +#include "unitcell.hh" +#include "container_prd.hh" +#include "pre_container.hh" +#include "v_compute.hh" +#include "c_loops.hh" +#include "wall.hh" + +#endif From 804d29a1512af846a18506ce165e221e7d61d360 Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Sat, 16 Dec 2023 00:32:04 -0800 Subject: [PATCH 03/36] Get Fracture Operation Working --- bindings/python/manifold3d.cpp | 9 ++--- src/manifold/include/manifold.h | 3 +- src/manifold/src/manifold.cpp | 61 ++++++++++++++++++++++----------- 3 files changed, 48 insertions(+), 25 deletions(-) diff --git a/bindings/python/manifold3d.cpp b/bindings/python/manifold3d.cpp index 93a262db9..0cabc0dfe 100644 --- a/bindings/python/manifold3d.cpp +++ b/bindings/python/manifold3d.cpp @@ -532,19 +532,20 @@ NB_MODULE(manifold3d, m) { std::vector pts_vec; std::vector weights_vec; auto pointData = pts.data(); - auto pointShape = pts.shape(0); auto weightData = weights.data(); - auto weightShape = weights.shape(0); - for (int i = 0; i < pointShape; i+=3) { + for (int i = 0; i < pts.shape(0) * pts.shape(1); i += pts.shape(1)) { pts_vec.push_back({pointData[i], pointData[i+1], pointData[i+2]}); } - for (int i = 0; i < weightShape; i++) { + for (int i = 0; i < weights.shape(0); i++) { weights_vec.push_back(weightData[i]); } return self.Fracture(pts_vec, weights_vec); }, "This operation Compute the fracturing of this Manifold into convex " "chunks around the supplied points.") + .def("convex_decomposition", &Manifold::ConvexDecomposition, + "This operation Compute the fracturing of this Manifold into convex " + "chunks around the supplied points.") .def("status", &Manifold::Status, "Returns the reason for an input Mesh producing an empty Manifold. " "This Status only applies to Manifolds newly-created from an input " diff --git a/src/manifold/include/manifold.h b/src/manifold/include/manifold.h index a3d8c6ef2..36bc8c7f1 100644 --- a/src/manifold/include/manifold.h +++ b/src/manifold/include/manifold.h @@ -249,11 +249,12 @@ class Manifold { static Manifold Hull(const std::vector& pts); ///@} - /** @name Voronoi Fracture + /** @name Voronoi Functions */ ///@{ std::vector Fracture(const std::vector& pts, const std::vector& weights) const; + std::vector ConvexDecomposition() const; ///@} /** @name Testing hooks diff --git a/src/manifold/src/manifold.cpp b/src/manifold/src/manifold.cpp index 73493149d..39f8b88f6 100644 --- a/src/manifold/src/manifold.cpp +++ b/src/manifold/src/manifold.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include "QuickHull.hpp" #include "voro++.hh" @@ -848,34 +849,54 @@ Manifold Manifold::Hull(const std::vector& manifolds) { } /** - * Compute the fracturing of this Manifold into convex chunks. + * Compute the voronoi fracturing of this Manifold into convex chunks. * * @param pts A vector of points over which to fracture the manifold. * @param pts A vector of weights controlling the relative size of each chunk. */ std::vector Manifold::Fracture(const std::vector& pts, const std::vector& weights) const { - voro::container_poly container(-0.5, 0.5, -0.5, 0.5, -0.5, 0.5, pts.size(), pts.size(), pts.size(), false, false, false, 0); - for (size_t i = 0; i < pts.size(); i++) { container.put(i, pts[i].x, pts[i].y, pts[i].z, weights[i]); } - container.compute_all_cells(); - - std::vector manifolds; - manifolds.reserve(pts.size()); + std::vector output; + output.reserve(pts.size()); + + Box bounds = BoundingBox(); + glm::vec3 min = bounds.min - 0.1f; + glm::vec3 max = bounds.max + 0.1f; + float V = (max.x - min.x) * (max.y - min.y) * (max.z - min.z); + float Nthird = powf((float)pts.size() / V, 1.0f / 3.0f); + voro::container_poly container( + min.x, max.x, min.y, max.y, min.z, max.z, std::roundf(Nthird * 4), + std::roundf(Nthird * 4), std::roundf(Nthird * 4), false, false, false, pts.size()); + + bool hasWeights = weights.size() == pts.size(); + for (size_t i = 0; i < pts.size(); i++) { + container.put(i, pts[i].x, pts[i].y, pts[i].z, hasWeights ? weights[i] : 1.0f); + } + size_t cell_coord = 0; voro::voronoicell c(container); voro::c_loop_all vl(container); - if (vl.start()) { - do { - std::vector verts; - verts.reserve(c.p); - for (size_t i = 0; i < c.p; i++) { - verts.push_back( - glm::vec3(c.pts[(i * 3) + 0], - c.pts[(i * 3) + 1], - c.pts[(i * 3) + 2])); + if (vl.start()) do { + if (container.compute_cell(c, vl)) { + std::vector verts; + verts.reserve(c.p); + int id; double x, y, z, r; vl.pos(id, x, y, z, r); + for (size_t i = 0; i < c.p; i++) { + verts.push_back(glm::vec3( + x + 0.5 * c.pts[(vl.ps*i) + 0], + y + 0.5 * c.pts[(vl.ps*i) + 1], + z + 0.5 * c.pts[(vl.ps*i) + 2])); + } + cell_coord++; + output.push_back(Hull(verts) ^ *this); + verts.clear(); } - manifolds.push_back(Hull(verts) ^ *this); - } while (vl.inc()); - } - return manifolds; + } while (vl.inc()); + return output; +} + +std::vector Manifold::ConvexDecomposition() const { + + return Fracture(GetMesh().vertPos, std::vector()); + } } // namespace manifold From 15579ef5911ed52803dd26ce2530029c7a452832 Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Sat, 16 Dec 2023 01:57:09 -0800 Subject: [PATCH 04/36] Add Broken VoACD Implementation --- src/manifold/src/face_op.cpp | 20 ++++++++++ src/manifold/src/impl.h | 1 + src/manifold/src/manifold.cpp | 73 ++++++++++++++++++++++++++++++++--- 3 files changed, 89 insertions(+), 5 deletions(-) diff --git a/src/manifold/src/face_op.cpp b/src/manifold/src/face_op.cpp index 331287655..5cf6e7d99 100644 --- a/src/manifold/src/face_op.cpp +++ b/src/manifold/src/face_op.cpp @@ -316,4 +316,24 @@ CrossSection Manifold::Impl::Project() const { return CrossSection(polys).Simplify(precision_); } + +glm::vec4 Manifold::Impl::Circumcircle(Vec verts, int face) const { + glm::vec3 va = verts[this->halfedge_[face + 0].startVert]; + glm::vec3 vb = verts[this->halfedge_[face + 1].startVert]; + glm::vec3 vc = verts[this->halfedge_[face + 2].startVert]; + + glm::vec3 a = va - vc; + glm::vec3 b = vb - vc; + float a_length = glm::length(a); + float b_length = glm::length(b); + glm::vec3 numerator = + glm::cross((((a_length * a_length) * b) - ((b_length * b_length) * a)), + glm::cross(a, b)); + float crs = glm::length(glm::cross(a, b)); + float denominator = 2.0 * (crs * crs); + glm::vec3 circumcenter = (numerator / denominator) + vc; + float circumradius = glm::length(circumcenter - vc); + return glm::vec4(circumcenter.x, circumcenter.y, circumcenter.z, + circumradius); +} } // namespace manifold diff --git a/src/manifold/src/impl.h b/src/manifold/src/impl.h index 4d12b637c..e1b7c6e5a 100644 --- a/src/manifold/src/impl.h +++ b/src/manifold/src/impl.h @@ -120,6 +120,7 @@ struct Manifold::Impl { glm::mat3x2 projection) const; CrossSection Slice(float height) const; CrossSection Project() const; + glm::vec4 Circumcircle(Vec verts, int face) const; // edge_op.cu void SimplifyTopology(); diff --git a/src/manifold/src/manifold.cpp b/src/manifold/src/manifold.cpp index 39f8b88f6..9323737bb 100644 --- a/src/manifold/src/manifold.cpp +++ b/src/manifold/src/manifold.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include "QuickHull.hpp" #include "voro++.hh" @@ -872,7 +873,6 @@ std::vector Manifold::Fracture(const std::vector& pts, for (size_t i = 0; i < pts.size(); i++) { container.put(i, pts[i].x, pts[i].y, pts[i].z, hasWeights ? weights[i] : 1.0f); } - size_t cell_coord = 0; voro::voronoicell c(container); voro::c_loop_all vl(container); if (vl.start()) do { @@ -886,8 +886,10 @@ std::vector Manifold::Fracture(const std::vector& pts, y + 0.5 * c.pts[(vl.ps*i) + 1], z + 0.5 * c.pts[(vl.ps*i) + 2])); } - cell_coord++; - output.push_back(Hull(verts) ^ *this); + Manifold outputManifold = Hull(verts) ^ *this; + if (outputManifold.GetProperties().volume > 0.0) { + output.push_back(outputManifold); + } verts.clear(); } } while (vl.inc()); @@ -895,8 +897,69 @@ std::vector Manifold::Fracture(const std::vector& pts, } std::vector Manifold::ConvexDecomposition() const { - - return Fracture(GetMesh().vertPos, std::vector()); + // Step 1. Get a list of all unique triangle faces with at least one reflex edge + std::cout << "About to get unique triangles" << std::endl; + std::unordered_set uniqueReflexFaceSet; + const Impl& impl = *GetCsgLeafNode().GetImpl(); + for(size_t i = 0; i < impl.halfedge_.size(); i++){ + //std::cout << "1" << std::endl; + Halfedge halfedge = impl.halfedge_[i]; + int faceA = halfedge.face; + //std::cout << "2" << std::endl; + int faceB = impl.halfedge_[halfedge.pairedHalfedge].face; + //std::cout << "3" << std::endl; + glm::vec3 tangent = glm::cross(impl.faceNormal_[faceA], + impl.vertPos_[halfedge.endVert] - impl.vertPos_[halfedge.startVert]); + //glm::vec3 tangent(impl.halfedgeTangent_[i].x, impl.halfedgeTangent_[i].y, + // impl.halfedgeTangent_[i].z); + //std::cout << "3.5" << std::endl; + float tangentProjection = glm::dot(impl.faceNormal_[faceB], tangent); + //std::cout << "4" << std::endl; + // If we've found a pair of reflex triangles, add them to the map + if (tangentProjection < 0.0f) { + uniqueReflexFaceSet.insert(faceA); + uniqueReflexFaceSet.insert(faceB); + } + } + std::vector uniqueFaces; // Copy to a vector for indexed access + uniqueFaces.insert(uniqueFaces.end(), uniqueReflexFaceSet.begin(), + uniqueReflexFaceSet.end()); + + // Step 2. Calculate the Circumcircles (centers + radii) of these triangles + std::cout << "About to Calculate Circumcircles" << std::endl; + std::vector circumcenters(uniqueFaces.size()); + std::vector circumradii (uniqueFaces.size()); + for(size_t i = 0; i < uniqueFaces.size(); i++){ + glm::vec4 circumcircle = impl.Circumcircle(impl.vertPos_, uniqueFaces[i]); + circumcenters[i] = glm::vec3(circumcircle.x, circumcircle.y, circumcircle.z); + circumradii[i] = circumcircle.w; + } + + // Step 3. If any two circumcenters are identical, joggle one of the triangle vertices, and store in a hashmap + std::cout << "About to get to Joggling" << std::endl; + Vec joggledVerts(impl.vertPos_); + for (size_t i = 0; i < circumcenters.size() - 1; i++) { + for (size_t j = i + 1; j < circumcenters.size(); j++) { + if (glm::distance(circumcenters[i], circumcenters[i]) < 0.00001) { + //std::cout << "I AM AN IDENTICAL CIRCUMCENTER" << std::endl; + joggledVerts[impl.halfedge_[uniqueFaces[i] + 0].startVert] += + glm::vec3(3.14159265359f, 3.14159265359f, 3.14159265359f) * 0.00001f; + } + } + } + + // Step 4. Recalculate the circumcenters + for (size_t i = 0; i < uniqueFaces.size(); i++) { + glm::vec4 circumcircle = impl.Circumcircle(joggledVerts, uniqueFaces[i]); + circumcenters[i] = glm::vec3(circumcircle.x, circumcircle.y, circumcircle.z); + circumradii[i] = circumcircle.w; + } + + // Step 5. Calculate the Voronoi Fracturing + return Fracture(circumcenters, circumradii); + // TODO: + // Step 6. Unjoggle the voronoi region vertices + // Step 7. Hull and Intersect with the original Manifold } } // namespace manifold From c039b8aad1cf2b9e71daca44387b60876d01beef Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Sat, 16 Dec 2023 02:11:14 -0800 Subject: [PATCH 05/36] Fix late night typos and formatting --- bindings/python/manifold3d.cpp | 6 +-- src/manifold/src/manifold.cpp | 91 +++++++++++++++++++--------------- 2 files changed, 54 insertions(+), 43 deletions(-) diff --git a/bindings/python/manifold3d.cpp b/bindings/python/manifold3d.cpp index 0cabc0dfe..0fdc97f0a 100644 --- a/bindings/python/manifold3d.cpp +++ b/bindings/python/manifold3d.cpp @@ -541,11 +541,11 @@ NB_MODULE(manifold3d, m) { } return self.Fracture(pts_vec, weights_vec); }, - "This operation Compute the fracturing of this Manifold into convex " + "This operation computes the fracturing of this Manifold into " "chunks around the supplied points.") .def("convex_decomposition", &Manifold::ConvexDecomposition, - "This operation Compute the fracturing of this Manifold into convex " - "chunks around the supplied points.") + "This operation computes the fracturing of this Manifold into the minimal " + "number of representative convex pieces.") .def("status", &Manifold::Status, "Returns the reason for an input Mesh producing an empty Manifold. " "This Status only applies to Manifolds newly-created from an input " diff --git a/src/manifold/src/manifold.cpp b/src/manifold/src/manifold.cpp index 9323737bb..e2b9c278b 100644 --- a/src/manifold/src/manifold.cpp +++ b/src/manifold/src/manifold.cpp @@ -855,8 +855,9 @@ Manifold Manifold::Hull(const std::vector& manifolds) { * @param pts A vector of points over which to fracture the manifold. * @param pts A vector of weights controlling the relative size of each chunk. */ -std::vector Manifold::Fracture(const std::vector& pts, - const std::vector& weights) const { +std::vector Manifold::Fracture( + const std::vector& pts, + const std::vector& weights) const { std::vector output; output.reserve(pts.size()); @@ -865,85 +866,94 @@ std::vector Manifold::Fracture(const std::vector& pts, glm::vec3 max = bounds.max + 0.1f; float V = (max.x - min.x) * (max.y - min.y) * (max.z - min.z); float Nthird = powf((float)pts.size() / V, 1.0f / 3.0f); - voro::container_poly container( - min.x, max.x, min.y, max.y, min.z, max.z, std::roundf(Nthird * 4), - std::roundf(Nthird * 4), std::roundf(Nthird * 4), false, false, false, pts.size()); + voro::container_poly container(min.x, max.x, min.y, max.y, min.z, max.z, + std::roundf(Nthird * (max.x - min.x)), + std::roundf(Nthird * (max.y - min.y)), + std::roundf(Nthird * (max.z - min.z)), false, + false, false, pts.size()); bool hasWeights = weights.size() == pts.size(); for (size_t i = 0; i < pts.size(); i++) { - container.put(i, pts[i].x, pts[i].y, pts[i].z, hasWeights ? weights[i] : 1.0f); + container.put(i, pts[i].x, pts[i].y, pts[i].z, + hasWeights ? weights[i] : 1.0f); } voro::voronoicell c(container); voro::c_loop_all vl(container); if (vl.start()) do { if (container.compute_cell(c, vl)) { - std::vector verts; - verts.reserve(c.p); - int id; double x, y, z, r; vl.pos(id, x, y, z, r); - for (size_t i = 0; i < c.p; i++) { - verts.push_back(glm::vec3( - x + 0.5 * c.pts[(vl.ps*i) + 0], - y + 0.5 * c.pts[(vl.ps*i) + 1], - z + 0.5 * c.pts[(vl.ps*i) + 2])); - } - Manifold outputManifold = Hull(verts) ^ *this; - if (outputManifold.GetProperties().volume > 0.0) { - output.push_back(outputManifold); - } - verts.clear(); + std::vector verts; + verts.reserve(c.p); + int id; + double x, y, z, r; + vl.pos(id, x, y, z, r); + for (size_t i = 0; i < c.p; i++) { + verts.push_back(glm::vec3(x + 0.5 * c.pts[(vl.ps * i) + 0], + y + 0.5 * c.pts[(vl.ps * i) + 1], + z + 0.5 * c.pts[(vl.ps * i) + 2])); + } + Manifold outputManifold = Hull(verts) ^ *this; + if (outputManifold.GetProperties().volume > 0.0) { + output.push_back(outputManifold); + } + verts.clear(); } - } while (vl.inc()); + } while (vl.inc()); return output; } std::vector Manifold::ConvexDecomposition() const { - // Step 1. Get a list of all unique triangle faces with at least one reflex edge + // Step 1. Get a list of all unique triangle faces with at least one reflex + // edge std::cout << "About to get unique triangles" << std::endl; std::unordered_set uniqueReflexFaceSet; const Impl& impl = *GetCsgLeafNode().GetImpl(); - for(size_t i = 0; i < impl.halfedge_.size(); i++){ - //std::cout << "1" << std::endl; + for (size_t i = 0; i < impl.halfedge_.size(); i++) { + // std::cout << "1" << std::endl; Halfedge halfedge = impl.halfedge_[i]; int faceA = halfedge.face; - //std::cout << "2" << std::endl; + // std::cout << "2" << std::endl; int faceB = impl.halfedge_[halfedge.pairedHalfedge].face; - //std::cout << "3" << std::endl; - glm::vec3 tangent = glm::cross(impl.faceNormal_[faceA], + // std::cout << "3" << std::endl; + glm::vec3 tangent = glm::cross( + impl.faceNormal_[faceA], impl.vertPos_[halfedge.endVert] - impl.vertPos_[halfedge.startVert]); - //glm::vec3 tangent(impl.halfedgeTangent_[i].x, impl.halfedgeTangent_[i].y, - // impl.halfedgeTangent_[i].z); - //std::cout << "3.5" << std::endl; + // glm::vec3 tangent(impl.halfedgeTangent_[i].x, impl.halfedgeTangent_[i].y, + // impl.halfedgeTangent_[i].z); + // std::cout << "3.5" << std::endl; float tangentProjection = glm::dot(impl.faceNormal_[faceB], tangent); - //std::cout << "4" << std::endl; - // If we've found a pair of reflex triangles, add them to the map + // std::cout << "4" << std::endl; + // If we've found a pair of reflex triangles, add them to the map if (tangentProjection < 0.0f) { uniqueReflexFaceSet.insert(faceA); uniqueReflexFaceSet.insert(faceB); } } - std::vector uniqueFaces; // Copy to a vector for indexed access + std::vector uniqueFaces; // Copy to a vector for indexed access uniqueFaces.insert(uniqueFaces.end(), uniqueReflexFaceSet.begin(), uniqueReflexFaceSet.end()); // Step 2. Calculate the Circumcircles (centers + radii) of these triangles std::cout << "About to Calculate Circumcircles" << std::endl; std::vector circumcenters(uniqueFaces.size()); - std::vector circumradii (uniqueFaces.size()); - for(size_t i = 0; i < uniqueFaces.size(); i++){ + std::vector circumradii(uniqueFaces.size()); + for (size_t i = 0; i < uniqueFaces.size(); i++) { glm::vec4 circumcircle = impl.Circumcircle(impl.vertPos_, uniqueFaces[i]); - circumcenters[i] = glm::vec3(circumcircle.x, circumcircle.y, circumcircle.z); + circumcenters[i] = + glm::vec3(circumcircle.x, circumcircle.y, circumcircle.z); circumradii[i] = circumcircle.w; } - // Step 3. If any two circumcenters are identical, joggle one of the triangle vertices, and store in a hashmap + // Step 3. If any two circumcenters are identical, joggle one of the triangle + // vertices, and store in a hashmap std::cout << "About to get to Joggling" << std::endl; Vec joggledVerts(impl.vertPos_); for (size_t i = 0; i < circumcenters.size() - 1; i++) { for (size_t j = i + 1; j < circumcenters.size(); j++) { if (glm::distance(circumcenters[i], circumcenters[i]) < 0.00001) { - //std::cout << "I AM AN IDENTICAL CIRCUMCENTER" << std::endl; + // std::cout << "I AM AN IDENTICAL CIRCUMCENTER" << std::endl; joggledVerts[impl.halfedge_[uniqueFaces[i] + 0].startVert] += - glm::vec3(3.14159265359f, 3.14159265359f, 3.14159265359f) * 0.00001f; + glm::vec3(3.14159265359f, 3.14159265359f, 3.14159265359f) * + 0.00001f; } } } @@ -951,7 +961,8 @@ std::vector Manifold::ConvexDecomposition() const { // Step 4. Recalculate the circumcenters for (size_t i = 0; i < uniqueFaces.size(); i++) { glm::vec4 circumcircle = impl.Circumcircle(joggledVerts, uniqueFaces[i]); - circumcenters[i] = glm::vec3(circumcircle.x, circumcircle.y, circumcircle.z); + circumcenters[i] = + glm::vec3(circumcircle.x, circumcircle.y, circumcircle.z); circumradii[i] = circumcircle.w; } From 3f6248da89531866ab897fb7a68be1027cb8ea17 Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Sun, 17 Dec 2023 10:34:28 -0800 Subject: [PATCH 06/36] Get Basic VoCD Working --- bindings/python/manifold3d.cpp | 8 ++-- src/manifold/include/manifold.h | 2 + src/manifold/src/face_op.cpp | 28 +++++------ src/manifold/src/impl.h | 2 +- src/manifold/src/manifold.cpp | 84 ++++++++++++++++++--------------- 5 files changed, 67 insertions(+), 57 deletions(-) diff --git a/bindings/python/manifold3d.cpp b/bindings/python/manifold3d.cpp index 0fdc97f0a..c96163b3c 100644 --- a/bindings/python/manifold3d.cpp +++ b/bindings/python/manifold3d.cpp @@ -527,10 +527,10 @@ NB_MODULE(manifold3d, m) { .def( "fracture", [](Manifold & self, - const nb::ndarray> &pts, - const nb::ndarray> &weights) { - std::vector pts_vec; - std::vector weights_vec; + const nb::ndarray> &pts, + const nb::ndarray> &weights) { + std::vector pts_vec; + std::vector weights_vec; auto pointData = pts.data(); auto weightData = weights.data(); for (int i = 0; i < pts.shape(0) * pts.shape(1); i += pts.shape(1)) { diff --git a/src/manifold/include/manifold.h b/src/manifold/include/manifold.h index 36bc8c7f1..2ef5612a7 100644 --- a/src/manifold/include/manifold.h +++ b/src/manifold/include/manifold.h @@ -252,6 +252,8 @@ class Manifold { /** @name Voronoi Functions */ ///@{ + std::vector Fracture(const std::vector& pts, + const std::vector& weights) const; std::vector Fracture(const std::vector& pts, const std::vector& weights) const; std::vector ConvexDecomposition() const; diff --git a/src/manifold/src/face_op.cpp b/src/manifold/src/face_op.cpp index 5cf6e7d99..01f5098da 100644 --- a/src/manifold/src/face_op.cpp +++ b/src/manifold/src/face_op.cpp @@ -317,23 +317,23 @@ CrossSection Manifold::Impl::Project() const { return CrossSection(polys).Simplify(precision_); } -glm::vec4 Manifold::Impl::Circumcircle(Vec verts, int face) const { - glm::vec3 va = verts[this->halfedge_[face + 0].startVert]; - glm::vec3 vb = verts[this->halfedge_[face + 1].startVert]; - glm::vec3 vc = verts[this->halfedge_[face + 2].startVert]; +glm::highp_f64vec4 Manifold::Impl::Circumcircle(Vec verts, int face) const { + glm::highp_f64vec3 va = verts[this->halfedge_[(face * 3) + 0].startVert]; + glm::highp_f64vec3 vb = verts[this->halfedge_[(face * 3) + 1].startVert]; + glm::highp_f64vec3 vc = verts[this->halfedge_[(face * 3) + 2].startVert]; - glm::vec3 a = va - vc; - glm::vec3 b = vb - vc; - float a_length = glm::length(a); - float b_length = glm::length(b); - glm::vec3 numerator = + glm::highp_f64vec3 a = va - vc; + glm::highp_f64vec3 b = vb - vc; + double a_length = glm::length(a); + double b_length = glm::length(b); + glm::highp_f64vec3 numerator = glm::cross((((a_length * a_length) * b) - ((b_length * b_length) * a)), glm::cross(a, b)); - float crs = glm::length(glm::cross(a, b)); - float denominator = 2.0 * (crs * crs); - glm::vec3 circumcenter = (numerator / denominator) + vc; - float circumradius = glm::length(circumcenter - vc); - return glm::vec4(circumcenter.x, circumcenter.y, circumcenter.z, + double crs = glm::length(glm::cross(a, b)); + double denominator = 2.0 * (crs * crs); + glm::highp_f64vec3 circumcenter = (numerator / denominator) + vc; + double circumradius = glm::length(circumcenter - vc); + return glm::highp_f64vec4(circumcenter.x, circumcenter.y, circumcenter.z, circumradius); } } // namespace manifold diff --git a/src/manifold/src/impl.h b/src/manifold/src/impl.h index e1b7c6e5a..7ce7e7cc8 100644 --- a/src/manifold/src/impl.h +++ b/src/manifold/src/impl.h @@ -120,7 +120,7 @@ struct Manifold::Impl { glm::mat3x2 projection) const; CrossSection Slice(float height) const; CrossSection Project() const; - glm::vec4 Circumcircle(Vec verts, int face) const; + glm::highp_f64vec4 Circumcircle(Vec verts, int face) const; // edge_op.cu void SimplifyTopology(); diff --git a/src/manifold/src/manifold.cpp b/src/manifold/src/manifold.cpp index e2b9c278b..d241c9ecd 100644 --- a/src/manifold/src/manifold.cpp +++ b/src/manifold/src/manifold.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include "QuickHull.hpp" #include "voro++.hh" @@ -849,6 +850,7 @@ Manifold Manifold::Hull(const std::vector& manifolds) { return Compose(manifolds).Hull(); } +// TODO: Handle Joggling in the Fracture Function Directly? /** * Compute the voronoi fracturing of this Manifold into convex chunks. * @@ -856,20 +858,20 @@ Manifold Manifold::Hull(const std::vector& manifolds) { * @param pts A vector of weights controlling the relative size of each chunk. */ std::vector Manifold::Fracture( - const std::vector& pts, - const std::vector& weights) const { + const std::vector& pts, + const std::vector& weights) const { std::vector output; output.reserve(pts.size()); Box bounds = BoundingBox(); glm::vec3 min = bounds.min - 0.1f; glm::vec3 max = bounds.max + 0.1f; - float V = (max.x - min.x) * (max.y - min.y) * (max.z - min.z); - float Nthird = powf((float)pts.size() / V, 1.0f / 3.0f); + double V = (max.x - min.x) * (max.y - min.y) * (max.z - min.z); + double Nthird = powf((double)pts.size() / V, 1.0f / 3.0f); voro::container_poly container(min.x, max.x, min.y, max.y, min.z, max.z, - std::roundf(Nthird * (max.x - min.x)), - std::roundf(Nthird * (max.y - min.y)), - std::roundf(Nthird * (max.z - min.z)), false, + std::round(Nthird * (max.x - min.x)), + std::round(Nthird * (max.y - min.y)), + std::round(Nthird * (max.z - min.z)), false, false, false, pts.size()); bool hasWeights = weights.size() == pts.size(); @@ -879,7 +881,7 @@ std::vector Manifold::Fracture( } voro::voronoicell c(container); voro::c_loop_all vl(container); - if (vl.start()) do { + if (vl.start()) do { // TODO: Parallelize this loop! if (container.compute_cell(c, vl)) { std::vector verts; verts.reserve(c.p); @@ -891,7 +893,7 @@ std::vector Manifold::Fracture( y + 0.5 * c.pts[(vl.ps * i) + 1], z + 0.5 * c.pts[(vl.ps * i) + 2])); } - Manifold outputManifold = Hull(verts) ^ *this; + Manifold outputManifold = Hull(verts) ^ *this; // TODO: Replace this intersection with a voro++ wall implementation or cutting the cell directly... if (outputManifold.GetProperties().volume > 0.0) { output.push_back(outputManifold); } @@ -900,30 +902,31 @@ std::vector Manifold::Fracture( } while (vl.inc()); return output; } +std::vector Manifold::Fracture( + const std::vector& pts, + const std::vector& weights) const { + std::vector highpVerts(pts.size()); + std::vector highpWeights(weights.size()); + for (size_t i = 0; i < pts.size(); i++) { + highpVerts[i] = pts[i]; highpWeights[i] = weights[i]; + } + return Fracture(highpVerts, highpWeights); +} std::vector Manifold::ConvexDecomposition() const { - // Step 1. Get a list of all unique triangle faces with at least one reflex - // edge - std::cout << "About to get unique triangles" << std::endl; + // Step 1. Get a list of all unique triangle faces with at least one reflex edge std::unordered_set uniqueReflexFaceSet; const Impl& impl = *GetCsgLeafNode().GetImpl(); for (size_t i = 0; i < impl.halfedge_.size(); i++) { - // std::cout << "1" << std::endl; Halfedge halfedge = impl.halfedge_[i]; int faceA = halfedge.face; - // std::cout << "2" << std::endl; int faceB = impl.halfedge_[halfedge.pairedHalfedge].face; - // std::cout << "3" << std::endl; glm::vec3 tangent = glm::cross( impl.faceNormal_[faceA], - impl.vertPos_[halfedge.endVert] - impl.vertPos_[halfedge.startVert]); - // glm::vec3 tangent(impl.halfedgeTangent_[i].x, impl.halfedgeTangent_[i].y, - // impl.halfedgeTangent_[i].z); - // std::cout << "3.5" << std::endl; + impl.vertPos_[impl.halfedge_[i].endVert] - impl.vertPos_[impl.halfedge_[i].startVert]); float tangentProjection = glm::dot(impl.faceNormal_[faceB], tangent); - // std::cout << "4" << std::endl; - // If we've found a pair of reflex triangles, add them to the map - if (tangentProjection < 0.0f) { + // If we've found a pair of reflex triangles, add them to the set + if (tangentProjection > 0.0f) { uniqueReflexFaceSet.insert(faceA); uniqueReflexFaceSet.insert(faceB); } @@ -933,44 +936,49 @@ std::vector Manifold::ConvexDecomposition() const { uniqueReflexFaceSet.end()); // Step 2. Calculate the Circumcircles (centers + radii) of these triangles - std::cout << "About to Calculate Circumcircles" << std::endl; - std::vector circumcenters(uniqueFaces.size()); - std::vector circumradii(uniqueFaces.size()); + std::vector circumcenters(uniqueFaces.size()); + std::vector circumradii(uniqueFaces.size()); + Vec joggledVerts(impl.vertPos_.size()); + for (size_t i = 0; i < impl.vertPos_.size(); i++) { + joggledVerts[i] = impl.vertPos_[i]; + } for (size_t i = 0; i < uniqueFaces.size(); i++) { - glm::vec4 circumcircle = impl.Circumcircle(impl.vertPos_, uniqueFaces[i]); + glm::highp_f64vec4 circumcircle = impl.Circumcircle(joggledVerts, uniqueFaces[i]); circumcenters[i] = - glm::vec3(circumcircle.x, circumcircle.y, circumcircle.z); + glm::highp_f64vec3(circumcircle.x, circumcircle.y, circumcircle.z); circumradii[i] = circumcircle.w; } // Step 3. If any two circumcenters are identical, joggle one of the triangle - // vertices, and store in a hashmap - std::cout << "About to get to Joggling" << std::endl; - Vec joggledVerts(impl.vertPos_); + // vertices, TODO: and store in a hashmap + //std::random_device rd; + std::mt19937 mt(1337); + std::uniform_real_distribution dist(0.0, 1.0); + double randOffset = 0.0f; for (size_t i = 0; i < circumcenters.size() - 1; i++) { for (size_t j = i + 1; j < circumcenters.size(); j++) { - if (glm::distance(circumcenters[i], circumcenters[i]) < 0.00001) { - // std::cout << "I AM AN IDENTICAL CIRCUMCENTER" << std::endl; - joggledVerts[impl.halfedge_[uniqueFaces[i] + 0].startVert] += - glm::vec3(3.14159265359f, 3.14159265359f, 3.14159265359f) * - 0.00001f; + if (glm::distance(circumcenters[i], circumcenters[j]) < 0.00001) { + joggledVerts[impl.halfedge_[(uniqueFaces[i] * 3) + 0].startVert] += + glm::highp_f64vec3(dist(mt) * 0.00000000001, dist(mt) * 0.00000000001, dist(mt) * 0.00000000001); } } } // Step 4. Recalculate the circumcenters for (size_t i = 0; i < uniqueFaces.size(); i++) { - glm::vec4 circumcircle = impl.Circumcircle(joggledVerts, uniqueFaces[i]); + glm::highp_f64vec4 circumcircle = impl.Circumcircle(joggledVerts, uniqueFaces[i]); circumcenters[i] = - glm::vec3(circumcircle.x, circumcircle.y, circumcircle.z); + glm::highp_f64vec3(circumcircle.x, circumcircle.y, circumcircle.z); circumradii[i] = circumcircle.w; } // Step 5. Calculate the Voronoi Fracturing - return Fracture(circumcenters, circumradii); + std::vector output = Fracture(circumcenters, circumradii); // TODO: // Step 6. Unjoggle the voronoi region vertices // Step 7. Hull and Intersect with the original Manifold + + return output; } } // namespace manifold From a32be233394c86d77ee148b5f03b65830a73266f Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Sun, 17 Dec 2023 15:03:51 -0800 Subject: [PATCH 07/36] Formatting --- bindings/python/manifold3d.cpp | 14 +++++++++----- src/manifold/src/face_op.cpp | 5 +++-- src/manifold/src/manifold.cpp | 35 +++++++++++++++++++++------------- 3 files changed, 34 insertions(+), 20 deletions(-) diff --git a/bindings/python/manifold3d.cpp b/bindings/python/manifold3d.cpp index c96163b3c..e6483d128 100644 --- a/bindings/python/manifold3d.cpp +++ b/bindings/python/manifold3d.cpp @@ -526,15 +526,19 @@ NB_MODULE(manifold3d, m) { "object onto the X-Y plane.") .def( "fracture", - [](Manifold & self, - const nb::ndarray> &pts, - const nb::ndarray> &weights) { + [](Manifold &self, + const nb::ndarray> &pts, + const nb::ndarray> &weights) { std::vector pts_vec; std::vector weights_vec; auto pointData = pts.data(); auto weightData = weights.data(); - for (int i = 0; i < pts.shape(0) * pts.shape(1); i += pts.shape(1)) { - pts_vec.push_back({pointData[i], pointData[i+1], pointData[i+2]}); + for (int i = 0; i < pts.shape(0) * pts.shape(1); + i += pts.shape(1)) { + pts_vec.push_back( + {pointData[i], pointData[i + 1], pointData[i + 2]}); } for (int i = 0; i < weights.shape(0); i++) { weights_vec.push_back(weightData[i]); diff --git a/src/manifold/src/face_op.cpp b/src/manifold/src/face_op.cpp index 01f5098da..a3c0e53d1 100644 --- a/src/manifold/src/face_op.cpp +++ b/src/manifold/src/face_op.cpp @@ -317,7 +317,8 @@ CrossSection Manifold::Impl::Project() const { return CrossSection(polys).Simplify(precision_); } -glm::highp_f64vec4 Manifold::Impl::Circumcircle(Vec verts, int face) const { +glm::highp_f64vec4 Manifold::Impl::Circumcircle(Vec verts, + int face) const { glm::highp_f64vec3 va = verts[this->halfedge_[(face * 3) + 0].startVert]; glm::highp_f64vec3 vb = verts[this->halfedge_[(face * 3) + 1].startVert]; glm::highp_f64vec3 vc = verts[this->halfedge_[(face * 3) + 2].startVert]; @@ -334,6 +335,6 @@ glm::highp_f64vec4 Manifold::Impl::Circumcircle(Vec verts, i glm::highp_f64vec3 circumcenter = (numerator / denominator) + vc; double circumradius = glm::length(circumcenter - vc); return glm::highp_f64vec4(circumcenter.x, circumcenter.y, circumcenter.z, - circumradius); + circumradius); } } // namespace manifold diff --git a/src/manifold/src/manifold.cpp b/src/manifold/src/manifold.cpp index d241c9ecd..3b7a1e5b4 100644 --- a/src/manifold/src/manifold.cpp +++ b/src/manifold/src/manifold.cpp @@ -15,7 +15,7 @@ #include #include #include -#include +//#include #include #include @@ -850,7 +850,7 @@ Manifold Manifold::Hull(const std::vector& manifolds) { return Compose(manifolds).Hull(); } -// TODO: Handle Joggling in the Fracture Function Directly? +// TODO: Handle Joggling in the Fracture Function Directly? /** * Compute the voronoi fracturing of this Manifold into convex chunks. * @@ -881,7 +881,7 @@ std::vector Manifold::Fracture( } voro::voronoicell c(container); voro::c_loop_all vl(container); - if (vl.start()) do { // TODO: Parallelize this loop! + if (vl.start()) do { // TODO: Parallelize this loop! if (container.compute_cell(c, vl)) { std::vector verts; verts.reserve(c.p); @@ -893,7 +893,10 @@ std::vector Manifold::Fracture( y + 0.5 * c.pts[(vl.ps * i) + 1], z + 0.5 * c.pts[(vl.ps * i) + 2])); } - Manifold outputManifold = Hull(verts) ^ *this; // TODO: Replace this intersection with a voro++ wall implementation or cutting the cell directly... + Manifold outputManifold = + Hull(verts) ^ + *this; // TODO: Replace this intersection with a voro++ wall + // implementation or cutting the cell directly... if (outputManifold.GetProperties().volume > 0.0) { output.push_back(outputManifold); } @@ -908,22 +911,25 @@ std::vector Manifold::Fracture( std::vector highpVerts(pts.size()); std::vector highpWeights(weights.size()); for (size_t i = 0; i < pts.size(); i++) { - highpVerts[i] = pts[i]; highpWeights[i] = weights[i]; + highpVerts[i] = pts[i]; + highpWeights[i] = weights[i]; } return Fracture(highpVerts, highpWeights); } std::vector Manifold::ConvexDecomposition() const { - // Step 1. Get a list of all unique triangle faces with at least one reflex edge + // Step 1. Get a list of all unique triangle faces with at least one reflex + // edge std::unordered_set uniqueReflexFaceSet; const Impl& impl = *GetCsgLeafNode().GetImpl(); for (size_t i = 0; i < impl.halfedge_.size(); i++) { Halfedge halfedge = impl.halfedge_[i]; int faceA = halfedge.face; int faceB = impl.halfedge_[halfedge.pairedHalfedge].face; - glm::vec3 tangent = glm::cross( - impl.faceNormal_[faceA], - impl.vertPos_[impl.halfedge_[i].endVert] - impl.vertPos_[impl.halfedge_[i].startVert]); + glm::vec3 tangent = + glm::cross(impl.faceNormal_[faceA], + impl.vertPos_[impl.halfedge_[i].endVert] - + impl.vertPos_[impl.halfedge_[i].startVert]); float tangentProjection = glm::dot(impl.faceNormal_[faceB], tangent); // If we've found a pair of reflex triangles, add them to the set if (tangentProjection > 0.0f) { @@ -943,7 +949,8 @@ std::vector Manifold::ConvexDecomposition() const { joggledVerts[i] = impl.vertPos_[i]; } for (size_t i = 0; i < uniqueFaces.size(); i++) { - glm::highp_f64vec4 circumcircle = impl.Circumcircle(joggledVerts, uniqueFaces[i]); + glm::highp_f64vec4 circumcircle = + impl.Circumcircle(joggledVerts, uniqueFaces[i]); circumcenters[i] = glm::highp_f64vec3(circumcircle.x, circumcircle.y, circumcircle.z); circumradii[i] = circumcircle.w; @@ -951,7 +958,6 @@ std::vector Manifold::ConvexDecomposition() const { // Step 3. If any two circumcenters are identical, joggle one of the triangle // vertices, TODO: and store in a hashmap - //std::random_device rd; std::mt19937 mt(1337); std::uniform_real_distribution dist(0.0, 1.0); double randOffset = 0.0f; @@ -959,14 +965,17 @@ std::vector Manifold::ConvexDecomposition() const { for (size_t j = i + 1; j < circumcenters.size(); j++) { if (glm::distance(circumcenters[i], circumcenters[j]) < 0.00001) { joggledVerts[impl.halfedge_[(uniqueFaces[i] * 3) + 0].startVert] += - glm::highp_f64vec3(dist(mt) * 0.00000000001, dist(mt) * 0.00000000001, dist(mt) * 0.00000000001); + glm::highp_f64vec3(dist(mt) * 0.00000000001, + dist(mt) * 0.00000000001, + dist(mt) * 0.00000000001); } } } // Step 4. Recalculate the circumcenters for (size_t i = 0; i < uniqueFaces.size(); i++) { - glm::highp_f64vec4 circumcircle = impl.Circumcircle(joggledVerts, uniqueFaces[i]); + glm::highp_f64vec4 circumcircle = + impl.Circumcircle(joggledVerts, uniqueFaces[i]); circumcenters[i] = glm::highp_f64vec3(circumcircle.x, circumcircle.y, circumcircle.z); circumradii[i] = circumcircle.w; From 2aef6ffac46967694d15666a2c7e06b3646e12a8 Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Sun, 17 Dec 2023 15:05:50 -0800 Subject: [PATCH 08/36] Formatting the Sequel --- bindings/python/manifold3d.cpp | 4 ++-- src/manifold/src/manifold.cpp | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/bindings/python/manifold3d.cpp b/bindings/python/manifold3d.cpp index e6483d128..499e4185f 100644 --- a/bindings/python/manifold3d.cpp +++ b/bindings/python/manifold3d.cpp @@ -545,8 +545,8 @@ NB_MODULE(manifold3d, m) { } return self.Fracture(pts_vec, weights_vec); }, - "This operation computes the fracturing of this Manifold into " - "chunks around the supplied points.") + "This operation computes the fracturing of this Manifold into the " + "minimal number of representative convex pieces.") .def("convex_decomposition", &Manifold::ConvexDecomposition, "This operation computes the fracturing of this Manifold into the minimal " "number of representative convex pieces.") diff --git a/src/manifold/src/manifold.cpp b/src/manifold/src/manifold.cpp index 3b7a1e5b4..beb48a8bb 100644 --- a/src/manifold/src/manifold.cpp +++ b/src/manifold/src/manifold.cpp @@ -15,16 +15,15 @@ #include #include #include -//#include -#include #include +#include #include "QuickHull.hpp" -#include "voro++.hh" #include "boolean3.h" #include "csg_tree.h" #include "impl.h" #include "par.h" +#include "voro++.hh" namespace { using namespace manifold; From 21f1daadbac740388bbcafaba5a6ff15fb13f963 Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Sun, 17 Dec 2023 15:06:51 -0800 Subject: [PATCH 09/36] Formatting Part 3 --- src/manifold/src/impl.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/manifold/src/impl.h b/src/manifold/src/impl.h index 7ce7e7cc8..e54b47293 100644 --- a/src/manifold/src/impl.h +++ b/src/manifold/src/impl.h @@ -120,7 +120,8 @@ struct Manifold::Impl { glm::mat3x2 projection) const; CrossSection Slice(float height) const; CrossSection Project() const; - glm::highp_f64vec4 Circumcircle(Vec verts, int face) const; + glm::highp_f64vec4 Circumcircle(Vec verts, + int face) const; // edge_op.cu void SimplifyTopology(); From f0b9faf3f87b6d8293594e2e62a7ef91ae2cc788 Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Sun, 17 Dec 2023 15:07:49 -0800 Subject: [PATCH 10/36] Final Formatting --- bindings/python/manifold3d.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bindings/python/manifold3d.cpp b/bindings/python/manifold3d.cpp index 499e4185f..b47a12265 100644 --- a/bindings/python/manifold3d.cpp +++ b/bindings/python/manifold3d.cpp @@ -546,7 +546,8 @@ NB_MODULE(manifold3d, m) { return self.Fracture(pts_vec, weights_vec); }, "This operation computes the fracturing of this Manifold into the " - "minimal number of representative convex pieces.") + "minimal " + "number of representative convex pieces.") .def("convex_decomposition", &Manifold::ConvexDecomposition, "This operation computes the fracturing of this Manifold into the minimal " "number of representative convex pieces.") From ae5114d69902333e191fe7fbfc27434fd62c81b8 Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Sun, 17 Dec 2023 15:35:35 -0800 Subject: [PATCH 11/36] After Final Formatting --- bindings/python/manifold3d.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/bindings/python/manifold3d.cpp b/bindings/python/manifold3d.cpp index b47a12265..65e38ccb7 100644 --- a/bindings/python/manifold3d.cpp +++ b/bindings/python/manifold3d.cpp @@ -546,11 +546,10 @@ NB_MODULE(manifold3d, m) { return self.Fracture(pts_vec, weights_vec); }, "This operation computes the fracturing of this Manifold into the " - "minimal " - "number of representative convex pieces.") + "chunks around the supplied points.") .def("convex_decomposition", &Manifold::ConvexDecomposition, - "This operation computes the fracturing of this Manifold into the minimal " - "number of representative convex pieces.") + "This operation computes the fracturing of this Manifold into the " + "minimal number of representative convex pieces.") .def("status", &Manifold::Status, "Returns the reason for an input Mesh producing an empty Manifold. " "This Status only applies to Manifolds newly-created from an input " From 595c9338398aae13592cee357054b593cf1c145f Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Sun, 17 Dec 2023 16:24:15 -0800 Subject: [PATCH 12/36] Use the common form of glm::dvec --- bindings/python/manifold3d.cpp | 2 +- src/manifold/include/manifold.h | 2 +- src/manifold/src/face_op.cpp | 16 ++++++++-------- src/manifold/src/impl.h | 2 +- src/manifold/src/manifold.cpp | 18 +++++++++--------- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/bindings/python/manifold3d.cpp b/bindings/python/manifold3d.cpp index 65e38ccb7..641456b5a 100644 --- a/bindings/python/manifold3d.cpp +++ b/bindings/python/manifold3d.cpp @@ -531,7 +531,7 @@ NB_MODULE(manifold3d, m) { nb::shape> &pts, const nb::ndarray> &weights) { - std::vector pts_vec; + std::vector pts_vec; std::vector weights_vec; auto pointData = pts.data(); auto weightData = weights.data(); diff --git a/src/manifold/include/manifold.h b/src/manifold/include/manifold.h index 2ef5612a7..fe9c419dd 100644 --- a/src/manifold/include/manifold.h +++ b/src/manifold/include/manifold.h @@ -252,7 +252,7 @@ class Manifold { /** @name Voronoi Functions */ ///@{ - std::vector Fracture(const std::vector& pts, + std::vector Fracture(const std::vector& pts, const std::vector& weights) const; std::vector Fracture(const std::vector& pts, const std::vector& weights) const; diff --git a/src/manifold/src/face_op.cpp b/src/manifold/src/face_op.cpp index a3c0e53d1..c89e90cbe 100644 --- a/src/manifold/src/face_op.cpp +++ b/src/manifold/src/face_op.cpp @@ -317,22 +317,22 @@ CrossSection Manifold::Impl::Project() const { return CrossSection(polys).Simplify(precision_); } -glm::highp_f64vec4 Manifold::Impl::Circumcircle(Vec verts, +glm::highp_f64vec4 Manifold::Impl::Circumcircle(Vec verts, int face) const { - glm::highp_f64vec3 va = verts[this->halfedge_[(face * 3) + 0].startVert]; - glm::highp_f64vec3 vb = verts[this->halfedge_[(face * 3) + 1].startVert]; - glm::highp_f64vec3 vc = verts[this->halfedge_[(face * 3) + 2].startVert]; + glm::dvec3 va = verts[this->halfedge_[(face * 3) + 0].startVert]; + glm::dvec3 vb = verts[this->halfedge_[(face * 3) + 1].startVert]; + glm::dvec3 vc = verts[this->halfedge_[(face * 3) + 2].startVert]; - glm::highp_f64vec3 a = va - vc; - glm::highp_f64vec3 b = vb - vc; + glm::dvec3 a = va - vc; + glm::dvec3 b = vb - vc; double a_length = glm::length(a); double b_length = glm::length(b); - glm::highp_f64vec3 numerator = + glm::dvec3 numerator = glm::cross((((a_length * a_length) * b) - ((b_length * b_length) * a)), glm::cross(a, b)); double crs = glm::length(glm::cross(a, b)); double denominator = 2.0 * (crs * crs); - glm::highp_f64vec3 circumcenter = (numerator / denominator) + vc; + glm::dvec3 circumcenter = (numerator / denominator) + vc; double circumradius = glm::length(circumcenter - vc); return glm::highp_f64vec4(circumcenter.x, circumcenter.y, circumcenter.z, circumradius); diff --git a/src/manifold/src/impl.h b/src/manifold/src/impl.h index e54b47293..2f8b92d77 100644 --- a/src/manifold/src/impl.h +++ b/src/manifold/src/impl.h @@ -120,7 +120,7 @@ struct Manifold::Impl { glm::mat3x2 projection) const; CrossSection Slice(float height) const; CrossSection Project() const; - glm::highp_f64vec4 Circumcircle(Vec verts, + glm::highp_f64vec4 Circumcircle(Vec verts, int face) const; // edge_op.cu diff --git a/src/manifold/src/manifold.cpp b/src/manifold/src/manifold.cpp index beb48a8bb..89faf244e 100644 --- a/src/manifold/src/manifold.cpp +++ b/src/manifold/src/manifold.cpp @@ -857,7 +857,7 @@ Manifold Manifold::Hull(const std::vector& manifolds) { * @param pts A vector of weights controlling the relative size of each chunk. */ std::vector Manifold::Fracture( - const std::vector& pts, + const std::vector& pts, const std::vector& weights) const { std::vector output; output.reserve(pts.size()); @@ -907,7 +907,7 @@ std::vector Manifold::Fracture( std::vector Manifold::Fracture( const std::vector& pts, const std::vector& weights) const { - std::vector highpVerts(pts.size()); + std::vector highpVerts(pts.size()); std::vector highpWeights(weights.size()); for (size_t i = 0; i < pts.size(); i++) { highpVerts[i] = pts[i]; @@ -941,9 +941,9 @@ std::vector Manifold::ConvexDecomposition() const { uniqueReflexFaceSet.end()); // Step 2. Calculate the Circumcircles (centers + radii) of these triangles - std::vector circumcenters(uniqueFaces.size()); + std::vector circumcenters(uniqueFaces.size()); std::vector circumradii(uniqueFaces.size()); - Vec joggledVerts(impl.vertPos_.size()); + Vec joggledVerts(impl.vertPos_.size()); for (size_t i = 0; i < impl.vertPos_.size(); i++) { joggledVerts[i] = impl.vertPos_[i]; } @@ -951,7 +951,7 @@ std::vector Manifold::ConvexDecomposition() const { glm::highp_f64vec4 circumcircle = impl.Circumcircle(joggledVerts, uniqueFaces[i]); circumcenters[i] = - glm::highp_f64vec3(circumcircle.x, circumcircle.y, circumcircle.z); + glm::dvec3(circumcircle.x, circumcircle.y, circumcircle.z); circumradii[i] = circumcircle.w; } @@ -964,9 +964,9 @@ std::vector Manifold::ConvexDecomposition() const { for (size_t j = i + 1; j < circumcenters.size(); j++) { if (glm::distance(circumcenters[i], circumcenters[j]) < 0.00001) { joggledVerts[impl.halfedge_[(uniqueFaces[i] * 3) + 0].startVert] += - glm::highp_f64vec3(dist(mt) * 0.00000000001, - dist(mt) * 0.00000000001, - dist(mt) * 0.00000000001); + glm::dvec3(dist(mt) * 0.00000000001, + dist(mt) * 0.00000000001, + dist(mt) * 0.00000000001); } } } @@ -976,7 +976,7 @@ std::vector Manifold::ConvexDecomposition() const { glm::highp_f64vec4 circumcircle = impl.Circumcircle(joggledVerts, uniqueFaces[i]); circumcenters[i] = - glm::highp_f64vec3(circumcircle.x, circumcircle.y, circumcircle.z); + glm::dvec3(circumcircle.x, circumcircle.y, circumcircle.z); circumradii[i] = circumcircle.w; } From 395e94b652ecc3e41bd616dfffd493434a7b430e Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Sun, 17 Dec 2023 16:26:53 -0800 Subject: [PATCH 13/36] Also dvec4 --- src/manifold/src/face_op.cpp | 4 ++-- src/manifold/src/impl.h | 3 +-- src/manifold/src/manifold.cpp | 7 +++---- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/manifold/src/face_op.cpp b/src/manifold/src/face_op.cpp index c89e90cbe..aa9d61ce9 100644 --- a/src/manifold/src/face_op.cpp +++ b/src/manifold/src/face_op.cpp @@ -317,7 +317,7 @@ CrossSection Manifold::Impl::Project() const { return CrossSection(polys).Simplify(precision_); } -glm::highp_f64vec4 Manifold::Impl::Circumcircle(Vec verts, +glm::dvec4 Manifold::Impl::Circumcircle(Vec verts, int face) const { glm::dvec3 va = verts[this->halfedge_[(face * 3) + 0].startVert]; glm::dvec3 vb = verts[this->halfedge_[(face * 3) + 1].startVert]; @@ -334,7 +334,7 @@ glm::highp_f64vec4 Manifold::Impl::Circumcircle(Vec verts, double denominator = 2.0 * (crs * crs); glm::dvec3 circumcenter = (numerator / denominator) + vc; double circumradius = glm::length(circumcenter - vc); - return glm::highp_f64vec4(circumcenter.x, circumcenter.y, circumcenter.z, + return glm::dvec4(circumcenter.x, circumcenter.y, circumcenter.z, circumradius); } } // namespace manifold diff --git a/src/manifold/src/impl.h b/src/manifold/src/impl.h index 2f8b92d77..e83add44b 100644 --- a/src/manifold/src/impl.h +++ b/src/manifold/src/impl.h @@ -120,8 +120,7 @@ struct Manifold::Impl { glm::mat3x2 projection) const; CrossSection Slice(float height) const; CrossSection Project() const; - glm::highp_f64vec4 Circumcircle(Vec verts, - int face) const; + glm::dvec4 Circumcircle(Vec verts, int face) const; // edge_op.cu void SimplifyTopology(); diff --git a/src/manifold/src/manifold.cpp b/src/manifold/src/manifold.cpp index 89faf244e..07564d360 100644 --- a/src/manifold/src/manifold.cpp +++ b/src/manifold/src/manifold.cpp @@ -948,7 +948,7 @@ std::vector Manifold::ConvexDecomposition() const { joggledVerts[i] = impl.vertPos_[i]; } for (size_t i = 0; i < uniqueFaces.size(); i++) { - glm::highp_f64vec4 circumcircle = + glm::dvec4 circumcircle = impl.Circumcircle(joggledVerts, uniqueFaces[i]); circumcenters[i] = glm::dvec3(circumcircle.x, circumcircle.y, circumcircle.z); @@ -964,8 +964,7 @@ std::vector Manifold::ConvexDecomposition() const { for (size_t j = i + 1; j < circumcenters.size(); j++) { if (glm::distance(circumcenters[i], circumcenters[j]) < 0.00001) { joggledVerts[impl.halfedge_[(uniqueFaces[i] * 3) + 0].startVert] += - glm::dvec3(dist(mt) * 0.00000000001, - dist(mt) * 0.00000000001, + glm::dvec3(dist(mt) * 0.00000000001, dist(mt) * 0.00000000001, dist(mt) * 0.00000000001); } } @@ -973,7 +972,7 @@ std::vector Manifold::ConvexDecomposition() const { // Step 4. Recalculate the circumcenters for (size_t i = 0; i < uniqueFaces.size(); i++) { - glm::highp_f64vec4 circumcircle = + glm::dvec4 circumcircle = impl.Circumcircle(joggledVerts, uniqueFaces[i]); circumcenters[i] = glm::dvec3(circumcircle.x, circumcircle.y, circumcircle.z); From 7e1fa10fcfde66a01084014b2b707a7c219b92ff Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Sun, 17 Dec 2023 16:29:01 -0800 Subject: [PATCH 14/36] Reformat again --- src/manifold/src/face_op.cpp | 5 ++--- src/manifold/src/manifold.cpp | 6 ++---- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/manifold/src/face_op.cpp b/src/manifold/src/face_op.cpp index aa9d61ce9..1207009e0 100644 --- a/src/manifold/src/face_op.cpp +++ b/src/manifold/src/face_op.cpp @@ -317,8 +317,7 @@ CrossSection Manifold::Impl::Project() const { return CrossSection(polys).Simplify(precision_); } -glm::dvec4 Manifold::Impl::Circumcircle(Vec verts, - int face) const { +glm::dvec4 Manifold::Impl::Circumcircle(Vec verts, int face) const { glm::dvec3 va = verts[this->halfedge_[(face * 3) + 0].startVert]; glm::dvec3 vb = verts[this->halfedge_[(face * 3) + 1].startVert]; glm::dvec3 vc = verts[this->halfedge_[(face * 3) + 2].startVert]; @@ -335,6 +334,6 @@ glm::dvec4 Manifold::Impl::Circumcircle(Vec verts, glm::dvec3 circumcenter = (numerator / denominator) + vc; double circumradius = glm::length(circumcenter - vc); return glm::dvec4(circumcenter.x, circumcenter.y, circumcenter.z, - circumradius); + circumradius); } } // namespace manifold diff --git a/src/manifold/src/manifold.cpp b/src/manifold/src/manifold.cpp index 07564d360..3b4cbf4e6 100644 --- a/src/manifold/src/manifold.cpp +++ b/src/manifold/src/manifold.cpp @@ -948,8 +948,7 @@ std::vector Manifold::ConvexDecomposition() const { joggledVerts[i] = impl.vertPos_[i]; } for (size_t i = 0; i < uniqueFaces.size(); i++) { - glm::dvec4 circumcircle = - impl.Circumcircle(joggledVerts, uniqueFaces[i]); + glm::dvec4 circumcircle = impl.Circumcircle(joggledVerts, uniqueFaces[i]); circumcenters[i] = glm::dvec3(circumcircle.x, circumcircle.y, circumcircle.z); circumradii[i] = circumcircle.w; @@ -972,8 +971,7 @@ std::vector Manifold::ConvexDecomposition() const { // Step 4. Recalculate the circumcenters for (size_t i = 0; i < uniqueFaces.size(); i++) { - glm::dvec4 circumcircle = - impl.Circumcircle(joggledVerts, uniqueFaces[i]); + glm::dvec4 circumcircle = impl.Circumcircle(joggledVerts, uniqueFaces[i]); circumcenters[i] = glm::dvec3(circumcircle.x, circumcircle.y, circumcircle.z); circumradii[i] = circumcircle.w; From dfe8e62db58cbfb0a5f16f610bf9f8bbe5ffa443 Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Mon, 18 Dec 2023 00:28:57 -0800 Subject: [PATCH 15/36] Remove the non-submodule folder --- src/third_party/voro/CMakeLists.txt | 78 - src/third_party/voro/LICENSE | 39 - src/third_party/voro/README | 152 -- src/third_party/voro/src/README | 21 - src/third_party/voro/src/c_loops.cc | 150 -- src/third_party/voro/src/c_loops.hh | 456 ---- src/third_party/voro/src/cell.cc | 2714 --------------------- src/third_party/voro/src/cell.hh | 553 ----- src/third_party/voro/src/cmd_line.cc | 497 ---- src/third_party/voro/src/common.cc | 138 -- src/third_party/voro/src/common.hh | 32 - src/third_party/voro/src/config.hh | 123 - src/third_party/voro/src/container.cc | 551 ----- src/third_party/voro/src/container.hh | 732 ------ src/third_party/voro/src/container_prd.cc | 776 ------ src/third_party/voro/src/container_prd.hh | 655 ----- src/third_party/voro/src/pre_container.cc | 236 -- src/third_party/voro/src/pre_container.hh | 162 -- src/third_party/voro/src/rad_option.hh | 158 -- src/third_party/voro/src/unitcell.cc | 232 -- src/third_party/voro/src/unitcell.hh | 79 - src/third_party/voro/src/v_base.cc | 118 - src/third_party/voro/src/v_base.hh | 88 - src/third_party/voro/src/v_base_wl.cc | 79 - src/third_party/voro/src/v_compute.cc | 1006 -------- src/third_party/voro/src/v_compute.hh | 149 -- src/third_party/voro/src/voro++.cc | 19 - src/third_party/voro/src/voro++.hh | 333 --- src/third_party/voro/src/wall.cc | 132 - src/third_party/voro/src/wall.hh | 118 - src/third_party/voro/src/worklist.hh | 32 - 31 files changed, 10608 deletions(-) delete mode 100644 src/third_party/voro/CMakeLists.txt delete mode 100644 src/third_party/voro/LICENSE delete mode 100644 src/third_party/voro/README delete mode 100644 src/third_party/voro/src/README delete mode 100644 src/third_party/voro/src/c_loops.cc delete mode 100644 src/third_party/voro/src/c_loops.hh delete mode 100644 src/third_party/voro/src/cell.cc delete mode 100644 src/third_party/voro/src/cell.hh delete mode 100644 src/third_party/voro/src/cmd_line.cc delete mode 100644 src/third_party/voro/src/common.cc delete mode 100644 src/third_party/voro/src/common.hh delete mode 100644 src/third_party/voro/src/config.hh delete mode 100644 src/third_party/voro/src/container.cc delete mode 100644 src/third_party/voro/src/container.hh delete mode 100644 src/third_party/voro/src/container_prd.cc delete mode 100644 src/third_party/voro/src/container_prd.hh delete mode 100644 src/third_party/voro/src/pre_container.cc delete mode 100644 src/third_party/voro/src/pre_container.hh delete mode 100644 src/third_party/voro/src/rad_option.hh delete mode 100644 src/third_party/voro/src/unitcell.cc delete mode 100644 src/third_party/voro/src/unitcell.hh delete mode 100644 src/third_party/voro/src/v_base.cc delete mode 100644 src/third_party/voro/src/v_base.hh delete mode 100644 src/third_party/voro/src/v_base_wl.cc delete mode 100644 src/third_party/voro/src/v_compute.cc delete mode 100644 src/third_party/voro/src/v_compute.hh delete mode 100644 src/third_party/voro/src/voro++.cc delete mode 100644 src/third_party/voro/src/voro++.hh delete mode 100644 src/third_party/voro/src/wall.cc delete mode 100644 src/third_party/voro/src/wall.hh delete mode 100644 src/third_party/voro/src/worklist.hh diff --git a/src/third_party/voro/CMakeLists.txt b/src/third_party/voro/CMakeLists.txt deleted file mode 100644 index c721f9b2c..000000000 --- a/src/third_party/voro/CMakeLists.txt +++ /dev/null @@ -1,78 +0,0 @@ -cmake_minimum_required(VERSION 3.10) - -project(voro++ VERSION 0.4.6 LANGUAGES CXX) -set(SOVERSION "0") - -if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CXX_FLAGS) - #release comes with -O3 by default - set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel." FORCE) -endif(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CXX_FLAGS) - -######################################################################## -# User input options # -######################################################################## -option(VORO_BUILD_SHARED_LIBS "Build shared libs" ON) -include(GNUInstallDirs) - -option(VORO_BUILD_EXAMPLES "Build examples" ON) -option(VORO_BUILD_CMD_LINE "Build command line project" ON) -option(VORO_ENABLE_DOXYGEN "Enable doxygen" ON) - -######################################################################## -#Find external packages -######################################################################## -if (${VORO_ENABLE_DOXYGEN}) - find_package(Doxygen) -endif() - -###################################### -# Include the following subdirectory # -###################################### - -file(GLOB VORO_SOURCES src/*.cc) -file(GLOB NOT_VORO_SOURCES src/v_base_wl.cc src/cmd_line.cc src/voro++.cc) -list(REMOVE_ITEM VORO_SOURCES ${NOT_VORO_SOURCES}) -add_library(voro++ ${VORO_SOURCES}) -set_target_properties(voro++ PROPERTIES - LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/src" - SOVERSION ${SOVERSION}) -install(TARGETS voro++ EXPORT VORO_Targets LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) -#for voro++.hh -target_include_directories(voro++ PUBLIC $ $) - -if (${VORO_BUILD_CMD_LINE}) - add_executable(cmd_line src/cmd_line.cc) - target_link_libraries(cmd_line PRIVATE voro++) - #cannot have two targets with the same name, so renaming cmd_line - set_target_properties(cmd_line PROPERTIES OUTPUT_NAME voro++ - RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/src") - install(TARGETS cmd_line RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) -endif() - -if (${VORO_BUILD_EXAMPLES}) - file(GLOB EXAMPLE_SOURCES examples/*/*.cc) - foreach(SOURCE ${EXAMPLE_SOURCES}) - string(REGEX REPLACE "^.*/([^/]*)\\.cc$" "\\1" PROGNAME "${SOURCE}") - if (NOT PROGNAME STREQUAL ellipsoid) #ellipsoid is broken - string(REGEX REPLACE "^.*/(examples/.*)/${PROGNAME}\\.cc$" "\\1" DIRNAME "${SOURCE}") - add_executable(${PROGNAME} ${SOURCE}) - target_link_libraries(${PROGNAME} PRIVATE voro++) - set_target_properties(${PROGNAME} PROPERTIES - RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${DIRNAME}" ) - endif() - endforeach(SOURCE) -endif() - -file(GLOB_RECURSE VORO_HEADERS src/voro++.hh) -install(FILES ${VORO_HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) -install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/man/voro++.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) -# no external deps for we can use target file as config file -install(EXPORT VORO_Targets FILE VOROConfig.cmake NAMESPACE VORO:: DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/VORO) -include(CMakePackageConfigHelpers) -write_basic_package_version_file("VOROConfigVersion.cmake" VERSION ${PROJECT_VERSION} COMPATIBILITY ExactVersion) -install(FILES "${CMAKE_CURRENT_BINARY_DIR}/VOROConfigVersion.cmake" DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/VORO) - -if (${VORO_ENABLE_DOXYGEN} AND DOXYGEN_FOUND) - add_custom_target(doxygen COMMAND ${DOXYGEN_EXECUTABLE} src/Doxyfile - COMMENT "Build doxygen documentation") -endif() diff --git a/src/third_party/voro/LICENSE b/src/third_party/voro/LICENSE deleted file mode 100644 index 7b05ace52..000000000 --- a/src/third_party/voro/LICENSE +++ /dev/null @@ -1,39 +0,0 @@ -Voro++ Copyright (c) 2008, The Regents of the University of California, through -Lawrence Berkeley National Laboratory (subject to receipt of any required -approvals from the U.S. Dept. of Energy). All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -(1) Redistributions of source code must retain the above copyright notice, this -list of conditions and the following disclaimer. - -(2) Redistributions in binary form must reproduce the above copyright notice, -this list of conditions and the following disclaimer in the documentation -and/or other materials provided with the distribution. - -(3) Neither the name of the University of California, Lawrence Berkeley -National Laboratory, U.S. Dept. of Energy nor the names of its contributors may -be used to endorse or promote products derived from this software without -specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -You are under no obligation whatsoever to provide any bug fixes, patches, or -upgrades to the features, functionality or performance of the source code -("Enhancements") to anyone; however, if you choose to make your Enhancements -available either publicly, or directly to Lawrence Berkeley National -Laboratory, without imposing a separate written license agreement for such -Enhancements, then you hereby grant the following license: a non-exclusive, -royalty-free perpetual license to install, use, modify, prepare derivative -works, incorporate into other computer software, distribute, and sublicense -such enhancements or derivative works thereof, in binary and source code form. diff --git a/src/third_party/voro/README b/src/third_party/voro/README deleted file mode 100644 index 322316cc7..000000000 --- a/src/third_party/voro/README +++ /dev/null @@ -1,152 +0,0 @@ -Voro++, a 3D cell-based Voronoi library (http://math.lbl.gov/voro++/) -By Chris H. Rycroft (Harvard University / Lawrence Berkeley Laboratory) -================================================================ -Voro++ is a software library for carrying out three-dimensional computations -of the Voronoi tessellation. A distinguishing feature of the Voro++ library -is that it carries out cell-based calculations, computing the Voronoi cell -for each particle individually, rather than computing the Voronoi -tessellation as a global network of vertices and edges. It is particularly -well-suited for applications that rely on cell-based statistics, where -features of Voronoi cells (e.g. volume, centroid, number of faces) can be -used to analyze a system of particles. - -Voro++ comprises of several C++ classes that can be built as a static library -and linked to. A command-line utility is also provided that can analyze text -files of particle configurations and use most of the features of the code. -Numerous examples are provided to demonstrate the library's features and all of -these are discussed in detail on the library website. - - -Compilation - Linux / Mac OS / Windows with Cygwin -================================================== -The code is written in ANSI C++, and compiles on many system architectures. The -package contains the C++ source code, example files, miscellaneous utilities -and documentation. On Linux, Mac OS, and Windows (using Cygwin), the -compilation and installed can be carried out using GNU Make. - -To begin, the user should review the file "config.mk" in the top level -directory, to make sure that the compilation and installation settings are -appropriate for their system. Typing "make" will then compile the static -library, command-line utility, and examples. The command-line utility and -library will appear within the "src" directory. - -Following successful compilation, the library, command-line utility, and -documentation can be installed by typing "sudo make install". By default, the -program files are installed into /usr/local, and it may be necessary to modify -your environment variables in order to access the installed files: - -- to use the command-line utility, the variable PATH should contain - /usr/local/bin. -- to access the Voro++ man page, the variable MANPATH should contain - /usr/local/man. -- to access the Voro++ header files, code compilation should include - the flag '-I/usr/local/include/voro++'. -- to link to the static library, code compilation should include the - flags '-L/usr/local/lib' to tell the linker where to look, and then - '-lvoro++' to link to the library. - -The library website contains additional notes on setting environment variables, -and many guides are available on the Internet. - -The code can later be uninstalled with "sudo make uninstall". It is also -possible to use the library and command-line utility without installation by -calling the files directly once they have been compiled. On systems where the -user does not have root privileges to install into /usr/local, the "config.mk" -file can be modified to install into the user's home directory by setting -PREFIX=$(HOME). Voro++ supports parallel compilation by using the "make -j " -command where n is the number of threads. - - -Compilation - Windows without Cygwin -==================================== -On a Windows machine without a terminal environment like Cygwin, it is possible -to import and compile the library in many standard C++ development -environments. Users have reported success in building the library with -Microsoft Visual C++ Express and Code::Blocks. - - -Related programs -================ -No external dependencies are required to compile and run the code, but several -programs may be useful for analyzing the output: - -- The freeware plotting program Gnuplot (available at www.gnuplot.info) can be - used for rapid 2D and 3D visualization of the program output. - -- The freeware raytracer POV-Ray (available at www.povray.org) can be used for - high-quality renderings of the program output. - -- The reference manual is generated from comments in the source code using - Doxygen (available at www.doxygen.org). This package is only required if the - library files are being developed and the reference manuals need to be - regenerated. The complete reference manual to the current code is available - online at http://math.lbl.gov/voro++/doc/refman/ - - -Contents -======== -examples - many documented examples making use of the library -html - an HTML-based reference manual (generated by Doxygen) -man - contains the man page that is installed with the program -scripts - miscellaneous helper scripts -src - source code files - - -Usage -===== -Voro++ is released as free software through the Lawrence Berkeley National -Laboratory - a detailed copyright notice is provided below, and the complete -terms of the license can be found in the LICENSE file. - -I am very interested to hear from users of the software, so if you find this -useful, please email me at chr@alum.mit.edu. Also, if you plan to publish an -academic paper using this software, please consider citing one of the following -publications: - -- Chris H. Rycroft, "Voro++: A three-dimensional Voronoi cell library in C++", - Chaos 19, 041111 (2009). - -- Chris H. Rycroft, Gary S. Grest, James W. Landry, and Martin Z. Bazant, - "Analysis of Granular Flow in a Pebble-Bed Nuclear Reactor", - Phys. Rev. E 74, 021306 (2006). - -- Chris H. Rycroft, "Multiscale Modeling in Granular Flow", PhD thesis - submitted to the Massachusetts Institute of Technology, September 2007. - (http://seas.harvard.edu/~chr/publish/phd.html) - -The first reference contains a one-page overview of the library. The second -reference contains some of the initial images that were made using a very early -version of this code, to track small changes in packing fraction in a large -particle simulation. The third reference discusses the use of 3D Voronoi cells, -and describes the algorithms that were employed in the early version of this -code. Since the publication of the above references, the algorithms in Voro++ -have been significantly improved. - - -Copyright Notice -================ -Voro++ Copyright (c) 2008, The Regents of the University of California, through -Lawrence Berkeley National Laboratory (subject to receipt of any required -approvals from the U.S. Dept. of Energy). All rights reserved. - -If you have questions about your rights to use or distribute this software, -please contact Berkeley Lab's Technology Transfer Department at TTD@lbl.gov. - -NOTICE. This software was developed under partial funding from the U.S. -Department of Energy. As such, the U.S. Government has been granted for itself -and others acting on its behalf a paid-up, nonexclusive, irrevocable, worldwide -license in the Software to reproduce, prepare derivative works, and perform -publicly and display publicly. Beginning five (5) years after the date -permission to assert copyright is obtained from the U.S. Department of Energy, -and subject to any subsequent five (5) year renewals, the U.S. Government is -granted for itself and others acting on its behalf a paid-up, nonexclusive, -irrevocable, worldwide license in the Software to reproduce, prepare derivative -works, distribute copies to the public, perform publicly and display publicly, -and to permit others to do so. - - -Acknowledgments -=============== -This work was supported by the Director, Office of Science, Computational and -Technology Research, U.S. Department of Energy under Contract No. -DE-AC02-05CH11231. diff --git a/src/third_party/voro/src/README b/src/third_party/voro/src/README deleted file mode 100644 index 09326db58..000000000 --- a/src/third_party/voro/src/README +++ /dev/null @@ -1,21 +0,0 @@ -Voro++ library source files -=========================== -This directory contains the source code for the library. For full details of -each file, see the files section of the online reference manual at -http://math.lbl.gov/voro++/doc/refman/ - -Several other files are present: - -Makefile - the GNU Makefile controlling the compilation. - -Makefile.dep - a file containing all the dependencies of the source files, -automatically generated by the GNU compiler. - -cmd_line.cc - source file for creating the command-line utility that makes use -of the library. - -Doxyfile - configuration file for Doxygen, used to automatically generate -documentation based on the source code comments. - -worklist_gen.pl - perl script for automatically generating the worklist.hh and -v_base_wl.cc files. diff --git a/src/third_party/voro/src/c_loops.cc b/src/third_party/voro/src/c_loops.cc deleted file mode 100644 index 86d3e4478..000000000 --- a/src/third_party/voro/src/c_loops.cc +++ /dev/null @@ -1,150 +0,0 @@ -// Voro++, a 3D cell-based Voronoi library -// -// Author : Chris H. Rycroft (Harvard University / LBL) -// Email : chr@alum.mit.edu -// Date : August 30th 2011 - -/** \file c_loops.cc - * \brief Function implementations for the loop classes. */ - -#include "c_loops.hh" - -namespace voro { - -/** Initializes a c_loop_subset object to scan over all particles within a - * given sphere. - * \param[in] (vx,vy,vz) the position vector of the center of the sphere. - * \param[in] r the radius of the sphere. - * \param[in] bounds_test whether to do detailed bounds checking. If this is - * false then the class will loop over all particles in - * blocks that overlap the given sphere. If it is true, - * the particle will only loop over the particles which - * actually lie within the sphere. - * \return True if there is any valid point to loop over, false otherwise. */ -void c_loop_subset::setup_sphere(double vx,double vy,double vz,double r,bool bounds_test) { - if(bounds_test) {mode=sphere;v0=vx;v1=vy;v2=vz;v3=r*r;} else mode=no_check; - ai=step_int((vx-ax-r)*xsp); - bi=step_int((vx-ax+r)*xsp); - aj=step_int((vy-ay-r)*ysp); - bj=step_int((vy-ay+r)*ysp); - ak=step_int((vz-az-r)*zsp); - bk=step_int((vz-az+r)*zsp); - setup_common(); -} - -/** Initializes the class to loop over all particles in a rectangular subgrid - * of blocks. - * \param[in] (ai_,bi_) the subgrid range in the x-direction, inclusive of both - * ends. - * \param[in] (aj_,bj_) the subgrid range in the y-direction, inclusive of both - * ends. - * \param[in] (ak_,bk_) the subgrid range in the z-direction, inclusive of both - * ends. - * \return True if there is any valid point to loop over, false otherwise. */ -void c_loop_subset::setup_intbox(int ai_,int bi_,int aj_,int bj_,int ak_,int bk_) { - ai=ai_;bi=bi_;aj=aj_;bj=bj_;ak=ak_;bk=bk_; - mode=no_check; - setup_common(); -} - -/** Sets up all of the common constants used for the loop. - * \return True if there is any valid point to loop over, false otherwise. */ -void c_loop_subset::setup_common() { - if(!xperiodic) { - if(ai<0) {ai=0;if(bi<0) bi=0;} - if(bi>=nx) {bi=nx-1;if(ai>=nx) ai=nx-1;} - } - if(!yperiodic) { - if(aj<0) {aj=0;if(bj<0) bj=0;} - if(bj>=ny) {bj=ny-1;if(aj>=ny) aj=ny-1;} - } - if(!zperiodic) { - if(ak<0) {ak=0;if(bk<0) bk=0;} - if(bk>=nz) {bk=nz-1;if(ak>=nz) ak=nz-1;} - } - ci=ai;cj=aj;ck=ak; - di=i=step_mod(ci,nx);apx=px=step_div(ci,nx)*sx; - dj=j=step_mod(cj,ny);apy=py=step_div(cj,ny)*sy; - dk=k=step_mod(ck,nz);apz=pz=step_div(ck,nz)*sz; - inc1=di-step_mod(bi,nx); - inc2=nx*(ny+dj-step_mod(bj,ny))+inc1; - inc1+=nx; - ijk=di+nx*(dj+ny*dk); - q=0; -} - -/** Starts the loop by finding the first particle within the container to - * consider. - * \return True if there is any particle to consider, false otherwise. */ -bool c_loop_subset::start() { - while(co[ijk]==0) {if(!next_block()) return false;} - while(mode!=no_check&&out_of_bounds()) { - q++; - while(q>=co[ijk]) {q=0;if(!next_block()) return false;} - } - return true; -} - -/** Initializes the class to loop over all particles in a rectangular box. - * \param[in] (xmin,xmax) the minimum and maximum x coordinates of the box. - * \param[in] (ymin,ymax) the minimum and maximum y coordinates of the box. - * \param[in] (zmin,zmax) the minimum and maximum z coordinates of the box. - * \param[in] bounds_test whether to do detailed bounds checking. If this is - * false then the class will loop over all particles in - * blocks that overlap the given box. If it is true, the - * particle will only loop over the particles which - * actually lie within the box. - * \return True if there is any valid point to loop over, false otherwise. */ -void c_loop_subset::setup_box(double xmin,double xmax,double ymin,double ymax,double zmin,double zmax,bool bounds_test) { - if(bounds_test) {mode=box;v0=xmin;v1=xmax;v2=ymin;v3=ymax;v4=zmin;v5=zmax;} else mode=no_check; - ai=step_int((xmin-ax)*xsp); - bi=step_int((xmax-ax)*xsp); - aj=step_int((ymin-ay)*ysp); - bj=step_int((ymax-ay)*ysp); - ak=step_int((zmin-az)*zsp); - bk=step_int((zmax-az)*zsp); - setup_common(); -} - -/** Computes whether the current point is out of bounds, relative to the - * current loop setup. - * \return True if the point is out of bounds, false otherwise. */ -bool c_loop_subset::out_of_bounds() { - double *pp=p[ijk]+ps*q; - if(mode==sphere) { - double fx(*pp+px-v0),fy(pp[1]+py-v1),fz(pp[2]+pz-v2); - return fx*fx+fy*fy+fz*fz>v3; - } else { - double f(*pp+px);if(fv1) return true; - f=pp[1]+py;if(fv3) return true; - f=pp[2]+pz;return fv5; - } -} - -/** Returns the next block to be tested in a loop, and updates the periodicity - * vector if necessary. */ -bool c_loop_subset::next_block() { - if(i - c_loop_base(c_class &con) : nx(con.nx), ny(con.ny), nz(con.nz), - nxy(con.nxy), nxyz(con.nxyz), ps(con.ps), - p(con.p), id(con.id), co(con.co) {} - /** Returns the position vector of the particle currently being - * considered by the loop. - * \param[out] (x,y,z) the position vector of the particle. */ - inline void pos(double &x,double &y,double &z) { - double *pp=p[ijk]+ps*q; - x=*(pp++);y=*(pp++);z=*pp; - } - /** Returns the ID, position vector, and radius of the particle - * currently being considered by the loop. - * \param[out] pid the particle ID. - * \param[out] (x,y,z) the position vector of the particle. - * \param[out] r the radius of the particle. If no radius - * information is available the default radius - * value is returned. */ - inline void pos(int &pid,double &x,double &y,double &z,double &r) { - pid=id[ijk][q]; - double *pp=p[ijk]+ps*q; - x=*(pp++);y=*(pp++);z=*pp; - r=ps==3?default_radius:*(++pp); - } - /** Returns the x position of the particle currently being - * considered by the loop. */ - inline double x() {return p[ijk][ps*q];} - /** Returns the y position of the particle currently being - * considered by the loop. */ - inline double y() {return p[ijk][ps*q+1];} - /** Returns the z position of the particle currently being - * considered by the loop. */ - inline double z() {return p[ijk][ps*q+2];} - /** Returns the ID of the particle currently being considered - * by the loop. */ - inline int pid() {return id[ijk][q];} -}; - -/** \brief Class for looping over all of the particles in a container. - * - * This is one of the simplest loop classes, that scans the computational - * blocks in order, and scans all the particles within each block in order. */ -class c_loop_all : public c_loop_base { - public: - /** The constructor copies several necessary constants from the - * base container class. - * \param[in] con the container class to use. */ - template - c_loop_all(c_class &con) : c_loop_base(con) {} - /** Sets the class to consider the first particle. - * \return True if there is any particle to consider, false - * otherwise. */ - inline bool start() { - i=j=k=ijk=q=0; - while(co[ijk]==0) if(!next_block()) return false; - return true; - } - /** Finds the next particle to test. - * \return True if there is another particle, false if no more - * particles are available. */ - inline bool inc() { - q++; - if(q>=co[ijk]) { - q=0; - do { - if(!next_block()) return false; - } while(co[ijk]==0); - } - return true; - } - private: - /** Updates the internal variables to find the next - * computational block with any particles. - * \return True if another block is found, false if there are - * no more blocks. */ - inline bool next_block() { - ijk++; - i++; - if(i==nx) { - i=0;j++; - if(j==ny) { - j=0;k++; - if(ijk==nxyz) return false; - } - } - return true; - } -}; - -/** \brief Class for looping over a subset of particles in a container. - * - * This class can loop over a subset of particles in a certain geometrical - * region within the container. The class can be set up to loop over a - * rectangular box or sphere. It can also rectangular group of internal - * computational blocks. */ -class c_loop_subset : public c_loop_base { - public: - /** The current mode of operation, determining whether tests - * should be applied to particles to ensure they are within a - * certain geometrical object. */ - c_loop_subset_mode mode; - /** The constructor copies several necessary constants from the - * base container class. - * \param[in] con the container class to use. */ - template - c_loop_subset(c_class &con) : c_loop_base(con), ax(con.ax), ay(con.ay), az(con.az), - sx(con.bx-ax), sy(con.by-ay), sz(con.bz-az), xsp(con.xsp), ysp(con.ysp), zsp(con.zsp), - xperiodic(con.xperiodic), yperiodic(con.yperiodic), zperiodic(con.zperiodic) {} - void setup_sphere(double vx,double vy,double vz,double r,bool bounds_test=true); - void setup_box(double xmin,double xmax,double ymin,double ymax,double zmin,double zmax,bool bounds_test=true); - void setup_intbox(int ai_,int bi_,int aj_,int bj_,int ak_,int bk_); - bool start(); - /** Finds the next particle to test. - * \return True if there is another particle, false if no more - * particles are available. */ - inline bool inc() { - do { - q++; - while(q>=co[ijk]) {q=0;if(!next_block()) return false;} - } while(mode!=no_check&&out_of_bounds()); - return true; - } - private: - const double ax,ay,az,sx,sy,sz,xsp,ysp,zsp; - const bool xperiodic,yperiodic,zperiodic; - double px,py,pz,apx,apy,apz; - double v0,v1,v2,v3,v4,v5; - int ai,bi,aj,bj,ak,bk; - int ci,cj,ck,di,dj,dk,inc1,inc2; - inline int step_mod(int a,int b) {return a>=0?a%b:b-1-(b-1-a)%b;} - inline int step_div(int a,int b) {return a>=0?a/b:-1+(a+1)/b;} - inline int step_int(double a) {return a<0?int(a)-1:int(a);} - void setup_common(); - bool next_block(); - bool out_of_bounds(); -}; - -/** \brief Class for looping over all of the particles specified in a - * pre-assembled particle_order class. - * - * The particle_order class can be used to create a specific order of particles - * within the container. This class can then loop over these particles in this - * order. The class is particularly useful in cases where the ordering of the - * output must match the ordering of particles as they were inserted into the - * container. */ -class c_loop_order : public c_loop_base { - public: - /** A reference to the ordering class to use. */ - particle_order &vo; - /** A pointer to the current position in the ordering class. */ - int *cp; - /** A pointer to the end position in the ordering class. */ - int *op; - /** The constructor copies several necessary constants from the - * base class, and sets up a reference to the ordering class to - * use. - * \param[in] con the container class to use. - * \param[in] vo_ the ordering class to use. */ - template - c_loop_order(c_class &con,particle_order &vo_) - : c_loop_base(con), vo(vo_), nx(con.nx), nxy(con.nxy) {} - /** Sets the class to consider the first particle. - * \return True if there is any particle to consider, false - * otherwise. */ - inline bool start() { - cp=vo.o;op=vo.op; - if(cp!=op) { - ijk=*(cp++);decode(); - q=*(cp++); - return true; - } else return false; - } - /** Finds the next particle to test. - * \return True if there is another particle, false if no more - * particles are available. */ - inline bool inc() { - if(cp==op) return false; - ijk=*(cp++);decode(); - q=*(cp++); - return true; - } - private: - /** The number of computational blocks in the x direction. */ - const int nx; - /** The number of computational blocks in a z-slice. */ - const int nxy; - /** Takes the current block index and computes indices in the - * x, y, and z directions. */ - inline void decode() { - k=ijk/nxy; - int ijkt=ijk-nxy*k; - j=ijkt/nx; - i=ijkt-j*nx; - } -}; - -/** \brief A class for looping over all particles in a container_periodic or - * container_periodic_poly class. - * - * Since the container_periodic and container_periodic_poly classes have a - * fundamentally different memory organization, the regular loop classes cannot - * be used with them. */ -class c_loop_all_periodic : public c_loop_base { - public: - /** The constructor copies several necessary constants from the - * base periodic container class. - * \param[in] con the periodic container class to use. */ - template - c_loop_all_periodic(c_class &con) : c_loop_base(con), ey(con.ey), ez(con.ez), wy(con.wy), wz(con.wz), - ijk0(nx*(ey+con.oy*ez)), inc2(2*nx*con.ey+1) {} - /** Sets the class to consider the first particle. - * \return True if there is any particle to consider, false - * otherwise. */ - inline bool start() { - i=0; - j=ey; - k=ez; - ijk=ijk0; - q=0; - while(co[ijk]==0) if(!next_block()) return false; - return true; - } - /** Finds the next particle to test. - * \return True if there is another particle, false if no more - * particles are available. */ - inline bool inc() { - q++; - if(q>=co[ijk]) { - q=0; - do { - if(!next_block()) return false; - } while(co[ijk]==0); - } - return true; - } - private: - /** The lower y index (inclusive) of the primary domain within - * the block structure. */ - int ey; - /** The lower y index (inclusive) of the primary domain within - * the block structure. */ - int ez; - /** The upper y index (exclusive) of the primary domain within - * the block structure. */ - int wy; - /** The upper z index (exclusive) of the primary domain within - * the block structure. */ - int wz; - /** The index of the (0,0,0) block within the block structure. - */ - int ijk0; - /** A value to increase ijk by when the z index is increased. - */ - int inc2; - /** Updates the internal variables to find the next - * computational block with any particles. - * \return True if another block is found, false if there are - * no more blocks. */ - inline bool next_block() { - i++; - if(i==nx) { - i=0;j++; - if(j==wy) { - j=ey;k++; - if(k==wz) return false; - ijk+=inc2; - } else ijk++; - } else ijk++; - return true; - } -}; - -/** \brief Class for looping over all of the particles specified in a - * pre-assembled particle_order class, for use with container_periodic classes. - * - * The particle_order class can be used to create a specific order of particles - * within the container. This class can then loop over these particles in this - * order. The class is particularly useful in cases where the ordering of the - * output must match the ordering of particles as they were inserted into the - * container. */ -class c_loop_order_periodic : public c_loop_base { - public: - /** A reference to the ordering class to use. */ - particle_order &vo; - /** A pointer to the current position in the ordering class. */ - int *cp; - /** A pointer to the end position in the ordering class. */ - int *op; - /** The constructor copies several necessary constants from the - * base class, and sets up a reference to the ordering class to - * use. - * \param[in] con the container class to use. - * \param[in] vo_ the ordering class to use. */ - template - c_loop_order_periodic(c_class &con,particle_order &vo_) - : c_loop_base(con), vo(vo_), nx(con.nx), oxy(con.nx*con.oy) {} - /** Sets the class to consider the first particle. - * \return True if there is any particle to consider, false - * otherwise. */ - inline bool start() { - cp=vo.o;op=vo.op; - if(cp!=op) { - ijk=*(cp++);decode(); - q=*(cp++); - return true; - } else return false; - } - /** Finds the next particle to test. - * \return True if there is another particle, false if no more - * particles are available. */ - inline bool inc() { - if(cp==op) return false; - ijk=*(cp++);decode(); - q=*(cp++); - return true; - } - private: - /** The number of computational blocks in the x direction. */ - const int nx; - /** The number of computational blocks in a z-slice. */ - const int oxy; - /** Takes the current block index and computes indices in the - * x, y, and z directions. */ - inline void decode() { - k=ijk/oxy; - int ijkt=ijk-oxy*k; - j=ijkt/nx; - i=ijkt-j*nx; - } -}; - -} - -#endif diff --git a/src/third_party/voro/src/cell.cc b/src/third_party/voro/src/cell.cc deleted file mode 100644 index 81e37dad8..000000000 --- a/src/third_party/voro/src/cell.cc +++ /dev/null @@ -1,2714 +0,0 @@ -// Voro++, a 3D cell-based Voronoi library -// -// Author : Chris H. Rycroft (Harvard University / LBL) -// Email : chr@alum.mit.edu -// Date : August 30th 2011 - -/** \file cell.cc - * \brief Function implementations for the voronoicell and related classes. */ - -#include -#include - -#include "config.hh" -#include "common.hh" -#include "cell.hh" - -namespace voro { - -/** Constructs a Voronoi cell and sets up the initial memory. */ -voronoicell_base::voronoicell_base(double max_len_sq) : - current_vertices(init_vertices), current_vertex_order(init_vertex_order), - current_delete_size(init_delete_size), current_delete2_size(init_delete2_size), - current_xsearch_size(init_xsearch_size), - ed(new int*[current_vertices]), nu(new int[current_vertices]), - mask(new unsigned int[current_vertices]), - pts(new double[current_vertices<<2]), tol(tolerance*max_len_sq), - tol_cu(tol*sqrt(tol)), big_tol(big_tolerance_fac*tol), mem(new int[current_vertex_order]), - mec(new int[current_vertex_order]), - mep(new int*[current_vertex_order]), ds(new int[current_delete_size]), - stacke(ds+current_delete_size), ds2(new int[current_delete2_size]), - stacke2(ds2+current_delete2_size), xse(new int[current_xsearch_size]), - stacke3(xse+current_xsearch_size), maskc(0) { - int i; - for(i=0;i=0;i--) if(mem[i]>0) delete [] mep[i]; - delete [] xse; - delete [] ds2;delete [] ds; - delete [] mep;delete [] mec; - delete [] mem;delete [] pts; - delete [] mask; - delete [] nu;delete [] ed; -} - -/** Ensures that enough memory is allocated prior to carrying out a copy. - * \param[in] vc a reference to the specialized version of the calling class. - * \param[in] vb a pointered to the class to be copied. */ -template -void voronoicell_base::check_memory_for_copy(vc_class &vc,voronoicell_base* vb) { - while(current_vertex_ordercurrent_vertex_order) add_memory_vorder(vc); - for(int i=0;imec[i]) add_memory(vc,i); - while(current_verticesp) add_memory_vertices(vc); -} - -/** Copies the vertex and edge information from another class. The routine - * assumes that enough memory is available for the copy. - * \param[in] vb a pointer to the class to copy. */ -void voronoicell_base::copy(voronoicell_base* vb) { - int i,j; - p=vb->p;up=0; - for(i=0;imec[i]; - for(j=0;jmep[i][j]; - for(j=0;jnu[i]; - for(i=0;i<(p<<2);i++) pts[i]=vb->pts[i]; -} - -/** Copies the information from another voronoicell class into this - * class, extending memory allocation if necessary. - * \param[in] c the class to copy. */ -void voronoicell_neighbor::operator=(voronoicell &c) { - voronoicell_base *vb=((voronoicell_base*) &c); - check_memory_for_copy(*this,vb);copy(vb); - int i,j; - for(i=0;i -void voronoicell_base::add_memory(vc_class &vc,int i) { - int s=(i<<1)+1; - if(mem[i]==0) { - vc.n_allocate(i,init_n_vertices); - mep[i]=new int[init_n_vertices*s]; - mem[i]=init_n_vertices; -#if VOROPP_VERBOSE >=2 - fprintf(stderr,"Order %d vertex memory created\n",i); -#endif - } else { - int j=0,k,*l; - mem[i]<<=1; - if(mem[i]>max_n_vertices) voro_fatal_error("Point memory allocation exceeded absolute maximum",VOROPP_MEMORY_ERROR); -#if VOROPP_VERBOSE >=2 - fprintf(stderr,"Order %d vertex memory scaled up to %d\n",i,mem[i]); -#endif - l=new int[s*mem[i]]; - int m=0; - vc.n_allocate_aux1(i); - while(j=0) { - ed[k]=l+j; - vc.n_set_to_aux1_offset(k,m); - } else { - int *dsp; - for(dsp=ds2;dsp=3 - fputs("Relocated dangling pointer",stderr); -#endif - } - for(k=0;k -void voronoicell_base::add_memory_vertices(vc_class &vc) { - int i=(current_vertices<<1),j,**pp,*pnu; - unsigned int* pmask; - if(i>max_vertices) voro_fatal_error("Vertex memory allocation exceeded absolute maximum",VOROPP_MEMORY_ERROR); -#if VOROPP_VERBOSE >=2 - fprintf(stderr,"Vertex memory scaled up to %d\n",i); -#endif - double *ppts; - pp=new int*[i]; - for(j=0;j -void voronoicell_base::add_memory_vorder(vc_class &vc) { - int i=(current_vertex_order<<1),j,*p1,**p2; - if(i>max_vertex_order) voro_fatal_error("Vertex order memory allocation exceeded absolute maximum",VOROPP_MEMORY_ERROR); -#if VOROPP_VERBOSE >=2 - fprintf(stderr,"Vertex order memory scaled up to %d\n",i); -#endif - p1=new int[i]; - for(j=0;jmax_delete_size) voro_fatal_error("Delete stack 1 memory allocation exceeded absolute maximum",VOROPP_MEMORY_ERROR); -#if VOROPP_VERBOSE >=2 - fprintf(stderr,"Delete stack 1 memory scaled up to %d\n",current_delete_size); -#endif - int *dsn=new int[current_delete_size],*dsnp=dsn,*dsp=ds; - while(dspmax_delete2_size) voro_fatal_error("Delete stack 2 memory allocation exceeded absolute maximum",VOROPP_MEMORY_ERROR); -#if VOROPP_VERBOSE >=2 - fprintf(stderr,"Delete stack 2 memory scaled up to %d\n",current_delete2_size); -#endif - int *dsn=new int[current_delete2_size],*dsnp=dsn,*dsp=ds2; - while(dspmax_xsearch_size) voro_fatal_error("Extra search stack memory allocation exceeded absolute maximum",VOROPP_MEMORY_ERROR); -#if VOROPP_VERBOSE >=2 - fprintf(stderr,"Extra search stack memory scaled up to %d\n",current_xsearch_size); -#endif - int *dsn=new int[current_xsearch_size],*dsnp=dsn,*dsp=xse; - while(dspl) break; - } - if(ls==nu[lp]) if(definite_max(lp,ls,l,u,uw)) { - up=lp; - return false; - } - - while(uw==0) { - //if(++count>=p) failsafe_find(lp,ls,us,l,u); - - // Test all the neighbors of the current point - // and find the one which is closest to the - // plane - vs=ed[lp][nu[lp]+ls];lp=up;l=u; - for(ls=0;lsl) break; - } - if(ls==nu[lp]&&definite_max(lp,ls,l,u,uw)) { - up=lp; - return false; - } - } - us=ed[lp][nu[lp]+ls]; - return true; -} - -/** Checks whether a particular point lp is a definite maximum, searching - * through any possible minor non-convexities, for a better maximum. - * \param[in] (x,y,z) the normal vector to the plane. */ -bool voronoicell_base::definite_max(int &lp,int &ls,double &l,double &u,unsigned int &uw) { - int tp=lp,ts,qp=0; - unsigned int qw; - double q; - - // Check to see whether point up is a well-defined maximum. Otherwise - // any neighboring vertices of up that are marginal need to be - // followed, to see if they lead to a better maximum. - for(ts=0;tsl-big_tol) break; - } - if(ts==nu[tp]) return true; - - // The point tp is marginal, so it will be necessary to do the - // flood-fill search. Mark the point tp and the point qp, and search - // any remaining neighbors of the point tp. - int *stackp=ds+1; - flip(lp); - flip(qp); - *ds=qp; - ts++; - while(tsl-big_tol) { - if(stackp==stacke) add_memory_ds(); - *(stackp++)=up; - flip(up); - } - ts++; - } - - // Consider additional marginal points, starting with the original - // point qp - int *spp=ds; - while(sppl) { - flip(lp); - lp=tp; - ls=ts; - m_test(lp,l); - up=qp; - uw=qw; - u=q; - while(stackp>ds) flip(*(--stackp)); - return false; - } - - // The point is marginal and therefore must also be - // considered - if(q>l-big_tol) { - if(stackp==stacke) { - int nn=stackp-spp; - add_memory_ds(); - spp=stackp-nn; - } - *(stackp++)=qp; - flip(qp); - } - } - } - - // Reset markers and return false - flip(lp); - while(stackp>ds) flip(*(--stackp)); - return true; -} - -inline bool voronoicell_base::search_downward(unsigned int &lw,int &lp,int &ls,int &us,double &l,double &u) { - int vs; - - // The test point is outside of the cutting space - for(us=0;usl) break; - } - if(us==nu[up]) if(definite_min(lp,us,l,u,lw)) return false; - - while(lw==2) { - //if(++count>=p) failsafe_find(lp,ls,us,l,u); - - // Test all the neighbors of the current point - // and find the one which is closest to the - // plane - vs=ed[up][nu[up]+us];up=lp;u=l; - for(us=0;usl) break; - } - if(us==nu[up]&&definite_min(lp,us,l,u,lw)) return false; - } - ls=ed[up][nu[up]+us]; - return true; -} - -bool voronoicell_base::definite_min(int &lp,int &us,double &l,double &u,unsigned int &lw) { - int tp=up,ts,qp=0; - unsigned int qw; - double q; - - // Check to see whether point up is a well-defined maximum. Otherwise - // any neighboring vertices of up that are marginal need to be - // followed, to see if they lead to a better maximum. - for(ts=0;tsds) flip(*(--stackp)); - return false; - } - - // The point is marginal and therefore must also be - // considered - if(qds) flip(*(--stackp)); - return true; -} - -/** Cuts the Voronoi cell by a particle whose center is at a separation of - * (x,y,z) from the cell center. The value of rsq should be initially set to - * \f$x^2+y^2+z^2\f$. - * \param[in] vc a reference to the specialized version of the calling class. - * \param[in] (x,y,z) the normal vector to the plane. - * \param[in] rsq the distance along this vector of the plane. - * \param[in] p_id the plane ID (for neighbor tracking only). - * \return False if the plane cut deleted the cell entirely, true otherwise. */ -template -bool voronoicell_base::nplane(vc_class &vc,double x,double y,double z,double rsq,int p_id) { - int i,j,lp=up,cp,qp,*dsp; - int us=0,ls=0; - unsigned int uw,lw; - int *edp,*edd;stackp=ds; - double u,l=0;up=0; - - // Initialize the safe testing routine - px=x;py=y;pz=z;prsq=rsq; - maskc+=4; - if(maskc<4) reset_mask(); - - uw=m_test(up,u); - if(uw==2) { - if(!search_downward(lw,lp,ls,us,l,u)) return false; - if(lw==1) {up=lp;lp=-1;} - } else if(uw==0) { - if(!search_upward(uw,lp,ls,us,l,u)) return true; - if(uw==1) lp=-1; - } else { - lp=-1; - } - - // Set stack pointers - stackp=ds;stackp2=ds2;stackp3=xse; - - // Store initial number of vertices - int op=p; - - if(create_facet(vc,lp,ls,l,us,u,p_id)) return false; - int k=0;int xtra=0; - while(xse+k=op) continue; - - if(uw==0) { - if(u>-big_tol&&ed[up][nu[up]<<1]!=-1) { - ed[up][nu[up]<<1]=-1; - if(stackp3==stacke3) add_memory_xse(); - *(stackp3++)=up; - } - } else if(uw==1) { - - // This is a possible facet starting - // from a vertex on the cutting plane - if(create_facet(vc,-1,0,0,0,u,p_id)) return false; - } else { - - // This is a new facet - us=ed[lp][nu[lp]+ls]; - m_test(lp,l); - if(create_facet(vc,lp,ls,l,us,u,p_id)) return false; - } - } - xtra++; - } - - // Reset back pointers on extra search stack - for(dsp=xse;dspds) { - --p; - while(ed[p][nu[p]]==-1) { - j=nu[p]; - edp=ed[p];edd=(mep[j]+((j<<1)+1)*--mec[j]); - while(edp0) voro_fatal_error("Zero order vertex formed",VOROPP_INTERNAL_ERROR); - - // Collapse any order 2 vertices and exit - return collapse_order2(vc); -} - -/** Creates a new facet. - * \return True if cell deleted, false otherwise. */ -template -bool voronoicell_base::create_facet(vc_class &vc,int lp,int ls,double l,int us,double u,int p_id) { - int i,j,k,qp,qs,iqs,cp,cs,rp,*edp,*edd; - unsigned int lw,qw; - bool new_double_edge=false,double_edge=false; - double q,r; - - // We're about to add the first point of the new facet. In either - // routine, we have to add a point, so first check there's space for - // it. - if(p==current_vertices) add_memory_vertices(vc); - - if(lp==-1) { - - // We want to be strict about reaching the conclusion that the - // cell is entirely within the cutting plane. It's not enough - // to find a vertex that has edges which are all inside or on - // the plane. If the vertex has neighbors that are also on the - // plane, we should check those too. - if(!search_for_outside_edge(up)) return true; - - // The search algorithm found a point which is on the cutting - // plane. We leave that point in place, and create a new one at - // the same location. - pts[(p<<2)]=pts[(up<<2)]; - pts[(p<<2)+1]=pts[(up<<2)+1]; - pts[(p<<2)+2]=pts[(up<<2)+2]; - - // Search for a collection of edges of the test vertex which - // are outside of the cutting space. Begin by testing the - // zeroth edge. - i=0; - lp=*ed[up]; - lw=m_testx(lp,l); - if(lw!=0) { - - // The first edge is either inside the cutting space, - // or lies within the cutting plane. Test the edges - // sequentially until we find one that is outside. - unsigned int rw=lw; - do { - i++; - - // If we reached the last edge with no luck - // then all of the vertices are inside - // or on the plane, so the cell is completely - // deleted - if(i==nu[up]) return true; - lp=ed[up][i]; - lw=m_testx(lp,l); - } while (lw!=0); - j=i+1; - - // We found an edge outside the cutting space. Keep - // moving through these edges until we find one that's - // inside or on the plane. - while(j=current_vertex_order) add_memory_vorder(vc); - if(mec[nu[p]]==mem[nu[p]]) add_memory(vc,nu[p]); - vc.n_set_pointer(p,nu[p]); - ed[p]=mep[nu[p]]+((nu[p]<<1)+1)*mec[nu[p]]++; - ed[p][nu[p]<<1]=p; - - // Copy the edges of the original vertex into the new - // one. Delete the edges of the original vertex, and - // update the relational table. - us=cycle_down(i,up); - while(i=current_vertex_order) add_memory_vorder(vc); - if(mec[nu[p]]==mem[nu[p]]) add_memory(vc,nu[p]); - - // Copy the edges of the original vertex into the new - // one. Delete the edges of the original vertex, and - // update the relational table. - vc.n_set_pointer(p,nu[p]); - ed[p]=mep[nu[p]]+((nu[p]<<1)+1)*mec[nu[p]]++; - ed[p][nu[p]<<1]=p; - us=i++; - while(i0) k+=nu[j]; - } else { - if(j>0) { - - // This vertex was visited before, so - // count those vertices to the ones we - // already have. - k+=nu[j]; - - // The only time when we might make a - // duplicate edge is if the point we're - // going to move to next is also a - // marginal point, so test for that - // first. - if(lw==1) { - - // Now see whether this marginal point - // has been visited before. - i=-ed[lp][nu[lp]<<1]; - if(i>0) { - - // Now see if the last edge of that other - // marginal point actually ends up here. - if(ed[i][nu[i]-1]==j) { - new_double_edge=true; - k-=1; - } else new_double_edge=false; - } else { - - // That marginal point hasn't been visited - // before, so we probably don't have to worry - // about duplicate edges, except in the - // case when that's the way into the end - // of the facet, because that way always creates - // an edge. - if(j==rp&&lp==up&&ed[qp][nu[qp]+qs]==us) { - new_double_edge=true; - k-=1; - } else new_double_edge=false; - } - } else new_double_edge=false; - } else { - - // The vertex hasn't been visited - // before, but let's see if it's - // marginal - if(lw==1) { - - // If it is, we need to check - // for the case that it's a - // small branch, and that we're - // heading right back to where - // we came from - i=-ed[lp][nu[lp]<<1]; - if(i==cp) { - new_double_edge=true; - k-=1; - } else new_double_edge=false; - } else new_double_edge=false; - } - } - - // k now holds the number of edges of the new vertex - // we are forming. Add memory for it if it doesn't exist - // already. - while(k>=current_vertex_order) add_memory_vorder(vc); - if(mec[k]==mem[k]) add_memory(vc,k); - - // Now create a new vertex with order k, or augment - // the existing one - if(j>0) { - - // If we're augmenting a vertex but we don't - // actually need any more edges, just skip this - // routine to avoid memory confusion - if(nu[j]!=k) { - - // Allocate memory and copy the edges - // of the previous instance into it - vc.n_set_aux1(k); - edp=mep[k]+((k<<1)+1)*mec[k]++; - i=0; - while(i -inline bool voronoicell_base::collapse_order2(vc_class &vc) { - if(!collapse_order1(vc)) return false; - int a,b,i,j,k,l; - while(mec[2]>0) { - - // Pick a order 2 vertex and read in its edges - i=--mec[2]; - j=mep[2][5*i];k=mep[2][5*i+1]; - if(j==k) { -#if VOROPP_VERBOSE >=1 - fputs("Order two vertex joins itself",stderr); -#endif - return false; - } - - // Scan the edges of j to see if joins k - for(l=0;l -bool voronoicell_base::collapse_order1(vc_class &vc) { - int i,j,k; - while(mec[1]>0) { - up=0; -#if VOROPP_VERBOSE >=1 - fputs("Order one collapse\n",stderr); -#endif - i=--mec[1]; - j=mep[1][3*i];k=mep[1][3*i+1]; - i=mep[1][3*i+2]; - if(!delete_connection(vc,j,k,false)) return false; - --p; - if(up==i) up=0; - if(p!=i) { - if(up==p) up=i; - pts[i<<2]=pts[p<<2]; - pts[(i<<2)+1]=pts[(p<<2)+1]; - pts[(i<<2)+2]=pts[(p<<2)+2]; - for(k=0;k -bool voronoicell_base::delete_connection(vc_class &vc,int j,int k,bool hand) { - int q=hand?k:cycle_up(k,j); - int i=nu[j]-1,l,*edp,*edd,m; -#if VOROPP_VERBOSE >=1 - if(i<1) { - fputs("Zero order vertex formed\n",stderr); - return false; - } -#endif - if(mec[i]==mem[i]) add_memory(vc,i); - vc.n_set_aux1(i); - for(l=0;l=0) { - ed[i][j]=-1-k; - l=cycle_up(ed[i][nu[i]+j],k); - vx=pts[k<<2]-*pts; - vy=pts[(k<<2)+1]-pts[1]; - vz=pts[(k<<2)+2]-pts[2]; - m=ed[k][l];ed[k][l]=-1-m; - while(m!=i) { - n=cycle_up(ed[k][nu[k]+l],m); - wx=pts[(m<<2)]-*pts; - wy=pts[(m<<2)+1]-pts[1]; - wz=pts[(m<<2)+2]-pts[2]; - vol+=ux*vy*wz+uy*vz*wx+uz*vx*wy-uz*vy*wx-uy*vx*wz-ux*vz*wy; - k=m;l=n;vx=wx;vy=wy;vz=wz; - m=ed[k][l];ed[k][l]=-1-m; - } - } - } - } - reset_edges(); - return vol*fe; -} - -/** Calculates the contributions to the Minkowski functionals for this Voronoi cell. - * \param[in] r the radius to consider. - * \param[out] ar the area functional. - * \param[out] vo the volume functional. */ -void voronoicell_base::minkowski(double r,double &ar,double &vo) { - int i,j,k,l,m,n; - ar=vo=0;r*=2; - for(i=1;i=0) { - ed[i][j]=-1-k; - l=cycle_up(ed[i][nu[i]+j],k); - m=ed[k][l];ed[k][l]=-1-m; - while(m!=i) { - n=cycle_up(ed[k][nu[k]+l],m); - minkowski_contrib(i,k,m,r,ar,vo); - k=m;l=n; - m=ed[k][l];ed[k][l]=-1-m; - } - } - } - vo*=0.125; - ar*=0.25; - reset_edges(); -} - -inline void voronoicell_base::minkowski_contrib(int i,int k,int m,double r,double &ar,double &vo) { - double ix=pts[4*i],iy=pts[4*i+1],iz=pts[4*i+2], - kx=pts[4*k],ky=pts[4*k+1],kz=pts[4*k+2], - mx=pts[4*m],my=pts[4*m+1],mz=pts[4*m+2], - ux=kx-ix,uy=ky-iy,uz=kz-iz,vx=mx-kx,vy=my-ky,vz=mz-kz, - e1x=uz*vy-uy*vz,e1y=ux*vz-uz*vx,e1z=uy*vx-ux*vy,e2x,e2y,e2z, - wmag=e1x*e1x+e1y*e1y+e1z*e1z; - if(wmag0.5) { - e2x=-e1y;e2y=e1x;e2z=0; - } else if(fabs(e1y)>0.5) { - e2x=0;e2y=-e1z;e2z=e1y; - } else { - e2x=e1z;e2y=0;e2z=-e1x; - } - wmag=1/sqrt(e2x*e2x+e2y*e2y+e2z*e2z); - e2x*=wmag;e2y*=wmag;e2z*=wmag; - - // Compute third orthonormal vector - double e3x=e1z*e2y-e1y*e2z, - e3y=e1x*e2z-e1z*e2x, - e3z=e1y*e2x-e1x*e2y, - x0=e1x*ix+e1y*iy+e1z*iz; - if(x0 &v) { - double solid_angle; - std::vector normalized(3*p); - v.clear(); - int i,j,k,l,m,n; - for (i=0;i=0) { - solid_angle=0; - ed[i][j]=-1-k; - l=cycle_up(ed[i][nu[i]+j],k); - m=ed[k][l];ed[k][l]=-1-m; - while(m!=i) { - n=cycle_up(ed[k][nu[k]+l],m); - solid_angle+=calculate_solid_angle(&normalized[3*i],&normalized[3*k],&normalized[3*m]); - k=m;l=n; - m=ed[k][l];ed[k][l]=-1-m; - } - v.push_back(solid_angle); - } - } - reset_edges(); -} - -/** Calculates the areas of each face of the Voronoi cell and prints the - * results to an output stream. - * \param[out] v the vector to store the results in. */ -void voronoicell_base::face_areas(std::vector &v) { - double area; - v.clear(); - int i,j,k,l,m,n; - double ux,uy,uz,vx,vy,vz,wx,wy,wz; - for(i=1;i=0) { - area=0; - ed[i][j]=-1-k; - l=cycle_up(ed[i][nu[i]+j],k); - m=ed[k][l];ed[k][l]=-1-m; - while(m!=i) { - n=cycle_up(ed[k][nu[k]+l],m); - ux=pts[4*k]-pts[4*i]; - uy=pts[4*k+1]-pts[4*i+1]; - uz=pts[4*k+2]-pts[4*i+2]; - vx=pts[4*m]-pts[4*i]; - vy=pts[4*m+1]-pts[4*i+1]; - vz=pts[4*m+2]-pts[4*i+2]; - wx=uy*vz-uz*vy; - wy=uz*vx-ux*vz; - wz=ux*vy-uy*vx; - area+=sqrt(wx*wx+wy*wy+wz*wz); - k=m;l=n; - m=ed[k][l];ed[k][l]=-1-m; - } - v.push_back(0.125*area); - } - } - reset_edges(); -} - -/** Calculates the total surface area of the Voronoi cell. - * \return The computed area. */ -double voronoicell_base::surface_area() { - double area=0; - int i,j,k,l,m,n; - double ux,uy,uz,vx,vy,vz,wx,wy,wz; - for(i=1;i=0) { - ed[i][j]=-1-k; - l=cycle_up(ed[i][nu[i]+j],k); - m=ed[k][l];ed[k][l]=-1-m; - while(m!=i) { - n=cycle_up(ed[k][nu[k]+l],m); - ux=pts[4*k]-pts[4*i]; - uy=pts[4*k+1]-pts[4*i+1]; - uz=pts[4*k+2]-pts[4*i+2]; - vx=pts[4*m]-pts[4*i]; - vy=pts[4*m+1]-pts[4*i+1]; - vz=pts[4*m+2]-pts[4*i+2]; - wx=uy*vz-uz*vy; - wy=uz*vx-ux*vz; - wz=ux*vy-uy*vx; - area+=sqrt(wx*wx+wy*wy+wz*wz); - k=m;l=n; - m=ed[k][l];ed[k][l]=-1-m; - } - } - } - reset_edges(); - return 0.125*area; -} - -/** Calculates the centroid of the Voronoi cell, by decomposing the cell into - * tetrahedra extending outward from the zeroth vertex. - * \param[out] (cx,cy,cz) references to floating point numbers in which to - * pass back the centroid vector. */ -void voronoicell_base::centroid(double &cx,double &cy,double &cz) { - double tvol,vol=0;cx=cy=cz=0; - int i,j,k,l,m,n; - double ux,uy,uz,vx,vy,vz,wx,wy,wz; - for(i=1;i=0) { - ed[i][j]=-1-k; - l=cycle_up(ed[i][nu[i]+j],k); - vx=pts[4*k]-*pts; - vy=pts[4*k+1]-pts[1]; - vz=pts[4*k+2]-pts[2]; - m=ed[k][l];ed[k][l]=-1-m; - while(m!=i) { - n=cycle_up(ed[k][nu[k]+l],m); - wx=pts[4*m]-*pts; - wy=pts[4*m+1]-pts[1]; - wz=pts[4*m+2]-pts[2]; - tvol=ux*vy*wz+uy*vz*wx+uz*vx*wy-uz*vy*wx-uy*vx*wz-ux*vz*wy; - vol+=tvol; - cx+=(wx+vx-ux)*tvol; - cy+=(wy+vy-uy)*tvol; - cz+=(wz+vz-uz)*tvol; - k=m;l=n;vx=wx;vy=wy;vz=wz; - m=ed[k][l];ed[k][l]=-1-m; - } - } - } - } - reset_edges(); - if(vol>tol_cu) { - vol=0.125/vol; - cx=cx*vol+0.5*(*pts); - cy=cy*vol+0.5*pts[1]; - cz=cz*vol+0.5*pts[2]; - } else cx=cy=cz=0; -} - -/** Computes the maximum radius squared of a vertex from the center of the - * cell. It can be used to determine when enough particles have been testing an - * all planes that could cut the cell have been considered. - * \return The maximum radius squared of a vertex.*/ -double voronoicell_base::max_radius_squared() { - double r,s,*ptsp=pts+4,*ptse=pts+(p<<2); - r=*pts*(*pts)+pts[1]*pts[1]+pts[2]*pts[2]; - while(ptspr) r=s; - } - return r; -} - -/** Calculates the total edge distance of the Voronoi cell. - * \return A floating point number holding the calculated distance. */ -double voronoicell_base::total_edge_distance() { - int i,j,k; - double dis=0,dx,dy,dz; - for(i=0;ii) { - dx=pts[k<<2]-pts[i<<2]; - dy=pts[(k<<2)+1]-pts[(i<<2)+1]; - dz=pts[(k<<2)+2]-pts[(i<<2)+2]; - dis+=sqrt(dx*dx+dy*dy+dz*dz); - } - } - return 0.5*dis; -} - -/** Outputs the edges of the Voronoi cell in POV-Ray format to an open file - * stream, displacing the cell by given vector. - * \param[in] (x,y,z) a displacement vector to be added to the cell's position. - * \param[in] fp a file handle to write to. */ -void voronoicell_base::draw_pov(double x,double y,double z,FILE* fp) { - int i,j,k;double *ptsp=pts,*pt2; - char posbuf1[128],posbuf2[128]; - for(i=0;i,r}\n",posbuf1); - for(j=0;j,<%s>,r}\n",posbuf1,posbuf2); - } - } - } -} - -/** Outputs the edges of the Voronoi cell in gnuplot format to an output stream. - * \param[in] (x,y,z) a displacement vector to be added to the cell's position. - * \param[in] fp a file handle to write to. */ -void voronoicell_base::draw_gnuplot(double x,double y,double z,FILE *fp) { - int i,j,k,l,m; - for(i=1;i=0) { - fprintf(fp,"%g %g %g\n",x+0.5*pts[i<<2],y+0.5*pts[(i<<2)+1],z+0.5*pts[(i<<2)+2]); - l=i;m=j; - do { - ed[k][ed[l][nu[l]+m]]=-1-l; - ed[l][m]=-1-k; - l=k; - fprintf(fp,"%g %g %g\n",x+0.5*pts[k<<2],y+0.5*pts[(k<<2)+1],z+0.5*pts[(k<<2)+2]); - } while (search_edge(l,m,k)); - fputs("\n\n",fp); - } - } - reset_edges(); -} - -inline bool voronoicell_base::search_edge(int l,int &m,int &k) { - for(m=0;m=0) return true; - } - return false; -} - -/** Outputs the Voronoi cell in the POV mesh2 format, described in section - * 1.3.2.2 of the POV-Ray documentation. The mesh2 output consists of a list of - * vertex vectors, followed by a list of triangular faces. The routine also - * makes use of the optional inside_vector specification, which makes the mesh - * object solid, so that the POV-Ray Constructive Solid Geometry (CSG) can be - * applied. - * \param[in] (x,y,z) a displacement vector to be added to the cell's position. - * \param[in] fp a file handle to write to. */ -void voronoicell_base::draw_pov_mesh(double x,double y,double z,FILE *fp) { - int i,j,k,l,m,n; - double *ptsp=pts; - fprintf(fp,"mesh2 {\nvertex_vectors {\n%d\n",p); - for(i=0;i\n",x+*ptsp*0.5,y+ptsp[1]*0.5,z+ptsp[2]*0.5); - fprintf(fp,"}\nface_indices {\n%d\n",(p-2)<<1); - for(i=1;i=0) { - ed[i][j]=-1-k; - l=cycle_up(ed[i][nu[i]+j],k); - m=ed[k][l];ed[k][l]=-1-m; - while(m!=i) { - n=cycle_up(ed[k][nu[k]+l],m); - fprintf(fp,",<%d,%d,%d>\n",i,k,m); - k=m;l=n; - m=ed[k][l];ed[k][l]=-1-m; - } - } - } - fputs("}\ninside_vector <0,0,1>\n}\n",fp); - reset_edges(); -} - -/** Several routines in the class that gather cell-based statistics internally - * track their progress by flipping edges to negative so that they know what - * parts of the cell have already been tested. This function resets them back - * to positive. When it is called, it assumes that every edge in the routine - * should have already been flipped to negative, and it bails out with an - * internal error if it encounters a positive edge. */ -inline void voronoicell_base::reset_edges() { - int i,j; - for(i=0;i=0) voro_fatal_error("Edge reset routine found a previously untested edge",VOROPP_INTERNAL_ERROR); - ed[i][j]=-1-ed[i][j]; - } -} - -/** Checks to see if a given vertex is inside, outside or within the test - * plane. If the point is far away from the test plane, the routine immediately - * returns whether it is inside or outside. If the routine is close the the - * plane and within the specified tolerance, then the special check_marginal() - * routine is called. - * \param[in] n the vertex to test. - * \param[out] ans the result of the scalar product used in evaluating the - * location of the point. - * \return -1 if the point is inside the plane, 1 if the point is outside the - * plane, or 0 if the point is within the plane. */ -inline unsigned int voronoicell_base::m_test(int n,double &ans) { - if(mask[n]>=maskc) { - ans=pts[4*n+3]; - return mask[n]&3; - } else return m_calc(n,ans); -} - -unsigned int voronoicell_base::m_calc(int n,double &ans) { - double *pp=pts+4*n; - ans=*(pp++)*px; - ans+=*(pp++)*py; - ans+=*(pp++)*pz-prsq; - *pp=ans; - unsigned int maskr=ans<-tol?0:(ans>tol?2:1); - mask[n]=maskc|maskr; - return maskr; -} - -/** Checks to see if a given vertex is inside, outside or within the test - * plane. If the point is far away from the test plane, the routine immediately - * returns whether it is inside or outside. If the routine is close the the - * plane and within the specified tolerance, then the special check_marginal() - * routine is called. - * \param[in] n the vertex to test. - * \param[out] ans the result of the scalar product used in evaluating the - * location of the point. - * \return -1 if the point is inside the plane, 1 if the point is outside the - * plane, or 0 if the point is within the plane. */ -inline unsigned int voronoicell_base::m_testx(int n,double &ans) { - unsigned int maskr; - if(mask[n]>=maskc) { - ans=pts[4*n+3]; - maskr=mask[n]&3; - } else maskr=m_calc(n,ans); - if(maskr==0&&ans>-big_tol&&ed[n][nu[n]<<1]!=-1) { - ed[n][nu[n]<<1]=-1; - if(stackp3==stacke3) add_memory_xse(); - *(stackp3++)=n; - } - return maskr; -} - -/** This routine calculates the unit normal vectors for every face. - * \param[out] v the vector to store the results in. */ -void voronoicell_base::normals(std::vector &v) { - int i,j,k; - v.clear(); - for(i=1;i=0) normals_search(v,i,j,k); - } - reset_edges(); -} - -/** This inline routine is called by normals(). It attempts to construct a - * single normal vector that is associated with a particular face. It first - * traces around the face, trying to find two vectors along the face edges - * whose vector product is above the numerical tolerance. It then constructs - * the normal vector using this product. If the face is too small, and none of - * the vector products are large enough, the routine may return (0,0,0) as the - * normal vector. - * \param[in] v the vector to store the results in. - * \param[in] i the initial vertex of the face to test. - * \param[in] j the index of an edge of the vertex. - * \param[in] k the neighboring vertex of i, set to ed[i][j]. */ -inline void voronoicell_base::normals_search(std::vector &v,int i,int j,int k) { - ed[i][j]=-1-k; - int l=cycle_up(ed[i][nu[i]+j],k),m; - double ux,uy,uz,vx,vy,vz,wx,wy,wz,wmag; - do { - m=ed[k][l];ed[k][l]=-1-m; - ux=pts[4*m]-pts[4*k]; - uy=pts[4*m+1]-pts[4*k+1]; - uz=pts[4*m+2]-pts[4*k+2]; - - // Test to see if the length of this edge is above the tolerance - if(ux*ux+uy*uy+uz*uz>tol) { - while(m!=i) { - l=cycle_up(ed[k][nu[k]+l],m); - k=m;m=ed[k][l];ed[k][l]=-1-m; - vx=pts[4*m]-pts[4*k]; - vy=pts[4*m+1]-pts[4*k+1]; - vz=pts[4*m+2]-pts[4*k+2]; - - // Construct the vector product of this edge with - // the previous one - wx=uz*vy-uy*vz; - wy=ux*vz-uz*vx; - wz=uy*vx-ux*vy; - wmag=wx*wx+wy*wy+wz*wz; - - // Test to see if this vector product of the - // two edges is above the tolerance - if(wmag>tol) { - - // Construct the normal vector and print it - wmag=1/sqrt(wmag); - v.push_back(wx*wmag); - v.push_back(wy*wmag); - v.push_back(wz*wmag); - - // Mark all of the remaining edges of this - // face and exit - while(m!=i) { - l=cycle_up(ed[k][nu[k]+l],m); - k=m;m=ed[k][l];ed[k][l]=-1-m; - } - return; - } - } - v.push_back(0); - v.push_back(0); - v.push_back(0); - return; - } - l=cycle_up(ed[k][nu[k]+l],m); - k=m; - } while (k!=i); - v.push_back(0); - v.push_back(0); - v.push_back(0); -} - -/** Returns the number of faces of a computed Voronoi cell. - * \return The number of faces. */ -int voronoicell_base::number_of_faces() { - int i,j,k,l,m,s=0; - for(i=1;i=0) { - s++; - ed[i][j]=-1-k; - l=cycle_up(ed[i][nu[i]+j],k); - do { - m=ed[k][l]; - ed[k][l]=-1-m; - l=cycle_up(ed[k][nu[k]+l],m); - k=m; - } while (k!=i); - - } - } - reset_edges(); - return s; -} - -/** Returns a vector of the vertex orders. - * \param[out] v the vector to store the results in. */ -void voronoicell_base::vertex_orders(std::vector &v) { - v.resize(p); - for(int i=0;i0) { - fprintf(fp,"%d",*nu); - for(int *nup=nu+1;nup &v) { - v.resize(3*p); - double *ptsp=pts; - for(int i=0;i<3*p;i+=3) { - v[i]=*(ptsp++)*0.5; - v[i+1]=*(ptsp++)*0.5; - v[i+2]=*ptsp*0.5;ptsp+=2; - } -} - -/** Outputs the vertex vectors using the local coordinate system. - * \param[out] fp the file handle to write to. */ -void voronoicell_base::output_vertices(FILE *fp) { - if(p>0) { - fprintf(fp,"(%g,%g,%g)",*pts*0.5,pts[1]*0.5,pts[2]*0.5); - for(double *ptsp=pts+4;ptsp &v) { - v.resize(3*p); - double *ptsp=pts; - for(int i=0;i<3*p;i+=3) { - v[i]=x+*(ptsp++)*0.5; - v[i+1]=y+*(ptsp++)*0.5; - v[i+2]=z+*ptsp*0.5;ptsp+=2; - } -} - -/** Outputs the vertex vectors using the global coordinate system. - * \param[out] fp the file handle to write to. - * \param[in] (x,y,z) the position vector of the particle in the global - * coordinate system. */ -void voronoicell_base::output_vertices(double x,double y,double z,FILE *fp) { - if(p>0) { - fprintf(fp,"(%g,%g,%g)",x+*pts*0.5,y+pts[1]*0.5,z+pts[2]*0.5); - for(double *ptsp=pts+4;ptsp &v) { - v.clear(); - int i,j,k,l,m; - double dx,dy,dz,perim; - for(i=1;i=0) { - dx=pts[k<<2]-pts[i<<2]; - dy=pts[(k<<2)+1]-pts[(i<<2)+1]; - dz=pts[(k<<2)+2]-pts[(i<<2)+2]; - perim=sqrt(dx*dx+dy*dy+dz*dz); - ed[i][j]=-1-k; - l=cycle_up(ed[i][nu[i]+j],k); - do { - m=ed[k][l]; - dx=pts[m<<2]-pts[k<<2]; - dy=pts[(m<<2)+1]-pts[(k<<2)+1]; - dz=pts[(m<<2)+2]-pts[(k<<2)+2]; - perim+=sqrt(dx*dx+dy*dy+dz*dz); - ed[k][l]=-1-m; - l=cycle_up(ed[k][nu[k]+l],m); - k=m; - } while (k!=i); - v.push_back(0.5*perim); - } - } - reset_edges(); -} - -/** For each face, this routine outputs a bracketed sequence of numbers - * containing a list of all the vertices that make up that face. - * \param[out] v the vector to store the results in. */ -void voronoicell_base::face_vertices(std::vector &v) { - int i,j,k,l,m,vp(0),vn; - v.clear(); - for(i=1;i=0) { - v.push_back(0); - v.push_back(i); - ed[i][j]=-1-k; - l=cycle_up(ed[i][nu[i]+j],k); - do { - v.push_back(k); - m=ed[k][l]; - ed[k][l]=-1-m; - l=cycle_up(ed[k][nu[k]+l],m); - k=m; - } while (k!=i); - vn=v.size(); - v[vp]=vn-vp-1; - vp=vn; - } - } - reset_edges(); -} - -/** Outputs a list of the number of edges in each face. - * \param[out] v the vector to store the results in. */ -void voronoicell_base::face_orders(std::vector &v) { - int i,j,k,l,m,q; - v.clear(); - for(i=1;i=0) { - q=1; - ed[i][j]=-1-k; - l=cycle_up(ed[i][nu[i]+j],k); - do { - q++; - m=ed[k][l]; - ed[k][l]=-1-m; - l=cycle_up(ed[k][nu[k]+l],m); - k=m; - } while (k!=i); - v.push_back(q);; - } - } - reset_edges(); -} - -/** Computes the number of edges that each face has and outputs a frequency - * table of the results. - * \param[out] v the vector to store the results in. */ -void voronoicell_base::face_freq_table(std::vector &v) { - int i,j,k,l,m,q; - v.clear(); - for(i=1;i=0) { - q=1; - ed[i][j]=-1-k; - l=cycle_up(ed[i][nu[i]+j],k); - do { - q++; - m=ed[k][l]; - ed[k][l]=-1-m; - l=cycle_up(ed[k][nu[k]+l],m); - k=m; - } while (k!=i); - if((unsigned int) q>=v.size()) v.resize(q+1,0); - v[q]++; - } - } - reset_edges(); -} - -/** This routine tests to see whether the cell intersects a plane by starting - * from the guess point up. If up intersects, then it immediately returns true. - * Otherwise, it calls the plane_intersects_track() routine. - * \param[in] (x,y,z) the normal vector to the plane. - * \param[in] rsq the distance along this vector of the plane. - * \return False if the plane does not intersect the plane, true if it does. */ -bool voronoicell_base::plane_intersects(double x,double y,double z,double rsq) { - double g=x*pts[up<<2]+y*pts[(up<<2)+1]+z*pts[(up<<2)+2]; - if(g>3,mp=1; - double m; - while(cag) { - if(m>rsq) return true; - g=m;up=mp; - } - ca+=mp++; - } - return plane_intersects_track(x,y,z,rsq,g); - } - return true; -} - -/* This routine tests to see if a cell intersects a plane, by tracing over the - * cell from vertex to vertex, starting at up. It is meant to be called either - * by plane_intersects() or plane_intersects_track(), when those routines - * cannot immediately resolve the case. - * \param[in] (x,y,z) the normal vector to the plane. - * \param[in] rsq the distance along this vector of the plane. - * \param[in] g the distance of up from the plane. - * \return False if the plane does not intersect the plane, true if it does. */ -inline bool voronoicell_base::plane_intersects_track(double x,double y,double z,double rsq,double g) { - - for(int tp=0;tprsq) return true; - return false; -/* - int ls,us,lp; - double l,u; - unsigned int uw; - - // Initialize the safe testing routine - px=x;py=y;pz=z;prsq=rsq; - maskc+=4; - if(maskc<4) reset_mask(); - - return search_upward(uw,lp,ls,us,l,u); -}*/ - /* - int count=0,ls,us,tp; - double t; - // The test point is outside of the cutting space - for(us=0;usg) { - ls=ed[up][nu[up]+us]; - up=tp; - while (t=p) { -#if VOROPP_VERBOSE >=1 - fputs("Bailed out of convex calculation",stderr); -#endif - for(tp=0;tprsq) return true; - return false; - } - - // Test all the neighbors of the current point - // and find the one which is closest to the - // plane - for(us=0;ust) break; - } - if(us==ls) { - us++; - while(ust) break; - us++; - } - if(us==nu[up]) return false; - } - ls=ed[up][nu[up]+us];up=tp;t=g; - } - return true; - } - } - return false;*/ -} - -/** Counts the number of edges of the Voronoi cell. - * \return the number of edges. */ -int voronoicell_base::number_of_edges() { - int edges=0,*nup=nu; - while(nup>1; -} - -/** Outputs a custom string of information about the Voronoi cell. The string - * of information follows a similar style as the C printf command, and detailed - * information about its format is available at - * http://math.lbl.gov/voro++/doc/custom.html. - * \param[in] format the custom string to print. - * \param[in] i the ID of the particle associated with this Voronoi cell. - * \param[in] (x,y,z) the position of the particle associated with this Voronoi - * cell. - * \param[in] r a radius associated with the particle. - * \param[in] fp the file handle to write to. */ -void voronoicell_base::output_custom(const char *format,int i,double x,double y,double z,double r,FILE *fp) { - char *fmp=(const_cast(format)); - std::vector vi; - std::vector vd; - while(*fmp!=0) { - if(*fmp=='%') { - fmp++; - switch(*fmp) { - - // Particle-related output - case 'i': fprintf(fp,"%d",i);break; - case 'x': fprintf(fp,"%g",x);break; - case 'y': fprintf(fp,"%g",y);break; - case 'z': fprintf(fp,"%g",z);break; - case 'q': fprintf(fp,"%g %g %g",x,y,z);break; - case 'r': fprintf(fp,"%g",r);break; - - // Vertex-related output - case 'w': fprintf(fp,"%d",p);break; - case 'p': output_vertices(fp);break; - case 'P': output_vertices(x,y,z,fp);break; - case 'o': output_vertex_orders(fp);break; - case 'm': fprintf(fp,"%g",0.25*max_radius_squared());break; - - // Edge-related output - case 'g': fprintf(fp,"%d",number_of_edges());break; - case 'E': fprintf(fp,"%g",total_edge_distance());break; - case 'e': face_perimeters(vd);voro_print_vector(vd,fp);break; - - // Face-related output - case 's': fprintf(fp,"%d",number_of_faces());break; - case 'F': fprintf(fp,"%g",surface_area());break; - case 'A': { - face_freq_table(vi); - voro_print_vector(vi,fp); - } break; - case 'a': face_orders(vi);voro_print_vector(vi,fp);break; - case 'f': face_areas(vd);voro_print_vector(vd,fp);break; - case 't': { - face_vertices(vi); - voro_print_face_vertices(vi,fp); - } break; - case 'l': normals(vd); - voro_print_positions(vd,fp); - break; - case 'n': neighbors(vi); - voro_print_vector(vi,fp); - break; - - // Volume-related output - case 'v': fprintf(fp,"%g",volume());break; - case 'c': { - double cx,cy,cz; - centroid(cx,cy,cz); - fprintf(fp,"%g %g %g",cx,cy,cz); - } break; - case 'C': { - double cx,cy,cz; - centroid(cx,cy,cz); - fprintf(fp,"%g %g %g",x+cx,y+cy,z+cz); - } break; - - // End-of-string reached - case 0: fmp--;break; - - // The percent sign is not part of a - // control sequence - default: putc('%',fp);putc(*fmp,fp); - } - } else putc(*fmp,fp); - fmp++; - } - fputs("\n",fp); -} - -/** This initializes the class to be a rectangular box. It calls the base class - * initialization routine to set up the edge and vertex information, and then - * sets up the neighbor information, with initial faces being assigned ID - * numbers from -1 to -6. - * \param[in] (xmin,xmax) the minimum and maximum x coordinates. - * \param[in] (ymin,ymax) the minimum and maximum y coordinates. - * \param[in] (zmin,zmax) the minimum and maximum z coordinates. */ -void voronoicell_neighbor::init(double xmin,double xmax,double ymin,double ymax,double zmin,double zmax) { - init_base(xmin,xmax,ymin,ymax,zmin,zmax); - int *q=mne[3]; - *q=-5;q[1]=-3;q[2]=-1; - q[3]=-5;q[4]=-2;q[5]=-3; - q[6]=-5;q[7]=-1;q[8]=-4; - q[9]=-5;q[10]=-4;q[11]=-2; - q[12]=-6;q[13]=-1;q[14]=-3; - q[15]=-6;q[16]=-3;q[17]=-2; - q[18]=-6;q[19]=-4;q[20]=-1; - q[21]=-6;q[22]=-2;q[23]=-4; - *ne=q;ne[1]=q+3;ne[2]=q+6;ne[3]=q+9; - ne[4]=q+12;ne[5]=q+15;ne[6]=q+18;ne[7]=q+21; -} - -/** This initializes the class to be an octahedron. It calls the base class - * initialization routine to set up the edge and vertex information, and then - * sets up the neighbor information, with the initial faces being assigned ID - * numbers from -1 to -8. - * \param[in] l The distance from the octahedron center to a vertex. Six - * vertices are initialized at (-l,0,0), (l,0,0), (0,-l,0), - * (0,l,0), (0,0,-l), and (0,0,l). */ -void voronoicell_neighbor::init_octahedron(double l) { - init_octahedron_base(l); - int *q=mne[4]; - *q=-5;q[1]=-6;q[2]=-7;q[3]=-8; - q[4]=-1;q[5]=-2;q[6]=-3;q[7]=-4; - q[8]=-6;q[9]=-5;q[10]=-2;q[11]=-1; - q[12]=-8;q[13]=-7;q[14]=-4;q[15]=-3; - q[16]=-5;q[17]=-8;q[18]=-3;q[19]=-2; - q[20]=-7;q[21]=-6;q[22]=-1;q[23]=-4; - *ne=q;ne[1]=q+4;ne[2]=q+8;ne[3]=q+12;ne[4]=q+16;ne[5]=q+20; -} - -/** This initializes the class to be a tetrahedron. It calls the base class - * initialization routine to set up the edge and vertex information, and then - * sets up the neighbor information, with the initial faces being assigned ID - * numbers from -1 to -4. - * \param (x0,y0,z0) a position vector for the first vertex. - * \param (x1,y1,z1) a position vector for the second vertex. - * \param (x2,y2,z2) a position vector for the third vertex. - * \param (x3,y3,z3) a position vector for the fourth vertex. */ -void voronoicell_neighbor::init_tetrahedron(double x0,double y0,double z0,double x1,double y1,double z1,double x2,double y2,double z2,double x3,double y3,double z3) { - init_tetrahedron_base(x0,y0,z0,x1,y1,z1,x2,y2,z2,x3,y3,z3); - int *q=mne[3]; - *q=-4;q[1]=-3;q[2]=-2; - q[3]=-3;q[4]=-4;q[5]=-1; - q[6]=-4;q[7]=-2;q[8]=-1; - q[9]=-2;q[10]=-3;q[11]=-1; - *ne=q;ne[1]=q+3;ne[2]=q+6;ne[3]=q+9; -} - -/** This routine checks to make sure the neighbor information of each face is - * consistent. */ -void voronoicell_neighbor::check_facets() { - int i,j,k,l,m,q; - for(i=1;i=0) { - ed[i][j]=-1-k; - q=ne[i][j]; - l=cycle_up(ed[i][nu[i]+j],k); - do { - m=ed[k][l]; - ed[k][l]=-1-m; - if(ne[k][l]!=q) fprintf(stderr,"Facet error at (%d,%d)=%d, started from (%d,%d)=%d\n",k,l,ne[k][l],i,j,q); - l=cycle_up(ed[k][nu[k]+l],m); - k=m; - } while (k!=i); - } - } - reset_edges(); -} - -/** The class constructor allocates memory for storing neighbor information. */ -void voronoicell_neighbor::memory_setup() { - int i; - mne=new int*[current_vertex_order]; - ne=new int*[current_vertices]; - for(i=0;i<3;i++) mne[i]=new int[init_n_vertices*i]; - mne[3]=new int[init_3_vertices*3]; - for(i=4;i=0;i--) if(mem[i]>0) delete [] mne[i]; - delete [] mne; - delete [] ne; -} - -/** Computes a vector list of neighbors. */ -void voronoicell_neighbor::neighbors(std::vector &v) { - v.clear(); - int i,j,k,l,m; - for(i=1;i=0) { - v.push_back(ne[i][j]); - ed[i][j]=-1-k; - l=cycle_up(ed[i][nu[i]+j],k); - do { - m=ed[k][l]; - ed[k][l]=-1-m; - l=cycle_up(ed[k][nu[k]+l],m); - k=m; - } while (k!=i); - } - } - reset_edges(); -} - -/** Prints the vertices, their edges, the relation table, and also notifies if - * any memory errors are visible. */ -void voronoicell_base::print_edges() { - int j; - double *ptsp=pts; - for(int i=0;i=mep[nu[i]]+mec[nu[i]]*((nu[i]<<1)+1)) puts(" Memory error"); - else puts(""); - } -} - -/** This prints out the neighbor information for vertex i. */ -void voronoicell_neighbor::print_edges_neighbors(int i) { - if(nu[i]>0) { - int j=0; - printf(" ("); - while(j - -#include "config.hh" -#include "common.hh" - -namespace voro { - -/** \brief A class representing a single Voronoi cell. - * - * This class represents a single Voronoi cell, as a collection of vertices - * that are connected by edges. The class contains routines for initializing - * the Voronoi cell to be simple shapes such as a box, tetrahedron, or octahedron. - * It the contains routines for recomputing the cell based on cutting it - * by a plane, which forms the key routine for the Voronoi cell computation. - * It contains numerous routine for computing statistics about the Voronoi cell, - * and it can output the cell in several formats. - * - * This class is not intended for direct use, but forms the base of the - * voronoicell and voronoicell_neighbor classes, which extend it based on - * whether neighboring particle ID information needs to be tracked. */ -class voronoicell_base { - public: - /** This holds the current size of the arrays ed and nu, which - * hold the vertex information. If more vertices are created - * than can fit in this array, then it is dynamically extended - * using the add_memory_vertices routine. */ - int current_vertices; - /** This holds the current maximum allowed order of a vertex, - * which sets the size of the mem, mep, and mec arrays. If a - * vertex is created with more vertices than this, the arrays - * are dynamically extended using the add_memory_vorder routine. - */ - int current_vertex_order; - /** This sets the size of the main delete stack. */ - int current_delete_size; - /** This sets the size of the auxiliary delete stack. */ - int current_delete2_size; - /** This sets the size of the extra search stack. */ - int current_xsearch_size; - /** This sets the total number of vertices in the current cell. - */ - int p; - /** This is the index of particular point in the cell, which is - * used to start the tracing routines for plane intersection - * and cutting. These routines will work starting from any - * point, but it's often most efficient to start from the last - * point considered, since in many cases, the cell construction - * algorithm may consider many planes with similar vectors - * concurrently. */ - int up; - /** This is a two dimensional array that holds information - * about the edge connections of the vertices that make up the - * cell. The two dimensional array is not allocated in the - * usual method. To account for the fact the different vertices - * have different orders, and thus require different amounts of - * storage, the elements of ed[i] point to one-dimensional - * arrays in the mep[] array of different sizes. - * - * More specifically, if vertex i has order m, then ed[i] - * points to a one-dimensional array in mep[m] that has 2*m+1 - * entries. The first m elements hold the neighboring edges, so - * that the jth edge of vertex i is held in ed[i][j]. The next - * m elements hold a table of relations which is redundant but - * helps speed up the computation. It satisfies the relation - * ed[ed[i][j]][ed[i][m+j]]=i. The final entry holds a back - * pointer, so that ed[i+2*m]=i. The back pointers are used - * when rearranging the memory. */ - int **ed; - /** This array holds the order of the vertices in the Voronoi - * cell. This array is dynamically allocated, with its current - * size held by current_vertices. */ - int *nu; - unsigned int *mask; - /** This in an array with size 3*current_vertices for holding - * the positions of the vertices. */ - double *pts; - double tol; - double tol_cu; - double big_tol; - voronoicell_base(double max_len_sq); - ~voronoicell_base(); - void init_base(double xmin,double xmax,double ymin,double ymax,double zmin,double zmax); - void init_octahedron_base(double l); - void init_tetrahedron_base(double x0,double y0,double z0,double x1,double y1,double z1,double x2,double y2,double z2,double x3,double y3,double z3); - void translate(double x,double y,double z); - void draw_pov(double x,double y,double z,FILE *fp=stdout); - /** Outputs the cell in POV-Ray format, using cylinders for edges - * and spheres for vertices, to a given file. - * \param[in] (x,y,z) a displacement to add to the cell's - * position. - * \param[in] filename the name of the file to write to. */ - inline void draw_pov(double x,double y,double z,const char *filename) { - FILE *fp=safe_fopen(filename,"w"); - draw_pov(x,y,z,fp); - fclose(fp); - }; - void draw_pov_mesh(double x,double y,double z,FILE *fp=stdout); - /** Outputs the cell in POV-Ray format as a mesh2 object to a - * given file. - * \param[in] (x,y,z) a displacement to add to the cell's - * position. - * \param[in] filename the name of the file to write to. */ - inline void draw_pov_mesh(double x,double y,double z,const char *filename) { - FILE *fp=safe_fopen(filename,"w"); - draw_pov_mesh(x,y,z,fp); - fclose(fp); - } - void draw_gnuplot(double x,double y,double z,FILE *fp=stdout); - /** Outputs the cell in Gnuplot format a given file. - * \param[in] (x,y,z) a displacement to add to the cell's - * position. - * \param[in] filename the name of the file to write to. */ - inline void draw_gnuplot(double x,double y,double z,const char *filename) { - FILE *fp=safe_fopen(filename,"w"); - draw_gnuplot(x,y,z,fp); - fclose(fp); - } - double volume(); - double max_radius_squared(); - double total_edge_distance(); - double surface_area(); - void centroid(double &cx,double &cy,double &cz); - int number_of_faces(); - int number_of_edges(); - void vertex_orders(std::vector &v); - void output_vertex_orders(FILE *fp=stdout); - void vertices(std::vector &v); - void output_vertices(FILE *fp=stdout); - void vertices(double x,double y,double z,std::vector &v); - void output_vertices(double x,double y,double z,FILE *fp=stdout); - void solid_angles(std::vector &v); - void face_areas(std::vector &v); - void minkowski(double r,double &ar,double &vo); - /** Outputs the solid angles of the faces. - * \param[in] fp the file handle to write to. */ - inline void output_solid_angles(FILE *fp=stdout) { - std::vector v;solid_angles(v); - voro_print_vector(v,fp); - } - /** Outputs the areas of the faces. - * \param[in] fp the file handle to write to. */ - inline void output_face_areas(FILE *fp=stdout) { - std::vector v;face_areas(v); - voro_print_vector(v,fp); - } - void face_orders(std::vector &v); - /** Outputs a list of the number of sides of each face. - * \param[in] fp the file handle to write to. */ - inline void output_face_orders(FILE *fp=stdout) { - std::vector v;face_orders(v); - voro_print_vector(v,fp); - } - void face_freq_table(std::vector &v); - /** Outputs a */ - inline void output_face_freq_table(FILE *fp=stdout) { - std::vector v;face_freq_table(v); - voro_print_vector(v,fp); - } - void face_vertices(std::vector &v); - /** Outputs the */ - inline void output_face_vertices(FILE *fp=stdout) { - std::vector v;face_vertices(v); - voro_print_face_vertices(v,fp); - } - void face_perimeters(std::vector &v); - /** Outputs a list of the perimeters of each face. - * \param[in] fp the file handle to write to. */ - inline void output_face_perimeters(FILE *fp=stdout) { - std::vector v;face_perimeters(v); - voro_print_vector(v,fp); - } - void normals(std::vector &v); - /** Outputs a list of the perimeters of each face. - * \param[in] fp the file handle to write to. */ - inline void output_normals(FILE *fp=stdout) { - std::vector v;normals(v); - voro_print_positions(v,fp); - } - /** Outputs a custom string of information about the Voronoi - * cell to a file. It assumes the cell is at (0,0,0) and has a - * the default_radius associated with it. - * \param[in] format the custom format string to use. - * \param[in] fp the file handle to write to. */ - inline void output_custom(const char *format,FILE *fp=stdout) {output_custom(format,0,0,0,0,default_radius,fp);} - void output_custom(const char *format,int i,double x,double y,double z,double r,FILE *fp=stdout); - template - bool nplane(vc_class &vc,double x,double y,double z,double rsq,int p_id); - bool plane_intersects(double x,double y,double z,double rsq); - bool plane_intersects_guess(double x,double y,double z,double rsq); - void construct_relations(); - void check_relations(); - void check_duplicates(); - void print_edges(); - /** Returns a list of IDs of neighboring particles - * corresponding to each face. - * \param[out] v a reference to a vector in which to return the - * results. If no neighbor information is - * available, a blank vector is returned. */ - virtual void neighbors(std::vector &v) {v.clear();} - /** This is a virtual function that is overridden by a routine - * to print a list of IDs of neighboring particles - * corresponding to each face. By default, when no neighbor - * information is available, the routine does nothing. - * \param[in] fp the file handle to write to. */ - virtual void output_neighbors(FILE *fp=stdout) {} - /** This a virtual function that is overridden by a routine to - * print the neighboring particle IDs for a given vertex. By - * default, when no neighbor information is available, the - * routine does nothing. - * \param[in] i the vertex to consider. */ - virtual void print_edges_neighbors(int i) {}; - /** This is a simple inline function for picking out the index - * of the next edge counterclockwise at the current vertex. - * \param[in] a the index of an edge of the current vertex. - * \param[in] p the number of the vertex. - * \return 0 if a=nu[p]-1, or a+1 otherwise. */ - inline int cycle_up(int a,int p) {return a==nu[p]-1?0:a+1;} - /** This is a simple inline function for picking out the index - * of the next edge clockwise from the current vertex. - * \param[in] a the index of an edge of the current vertex. - * \param[in] p the number of the vertex. - * \return nu[p]-1 if a=0, or a-1 otherwise. */ - inline int cycle_down(int a,int p) {return a==0?nu[p]-1:a-1;} - protected: - /** This a one dimensional array that holds the current sizes - * of the memory allocations for them mep array.*/ - int *mem; - /** This is a one dimensional array that holds the current - * number of vertices of order p that are stored in the mep[p] - * array. */ - int *mec; - /** This is a two dimensional array for holding the information - * about the edges of the Voronoi cell. mep[p] is a - * one-dimensional array for holding the edge information about - * all vertices of order p, with each vertex holding 2*p+1 - * integers of information. The total number of vertices held - * on mep[p] is stored in mem[p]. If the space runs out, the - * code allocates more using the add_memory() routine. */ - int **mep; - inline void reset_edges(); - template - void check_memory_for_copy(vc_class &vc,voronoicell_base* vb); - void copy(voronoicell_base* vb); - private: - /** This is the delete stack, used to store the vertices which - * are going to be deleted during the plane cutting procedure. - */ - int *ds,*stackp,*stacke; - /** This is the auxiliary delete stack, which has size set by - * current_delete2_size. */ - int *ds2,*stackp2,*stacke2; - /** This is the extra search stack. */ - int *xse,*stackp3,*stacke3; - unsigned int maskc; - /** The x coordinate of the normal vector to the test plane. */ - double px; - /** The y coordinate of the normal vector to the test plane. */ - double py; - /** The z coordinate of the normal vector to the test plane. */ - double pz; - /** The magnitude of the normal vector to the test plane. */ - double prsq; - template - void add_memory(vc_class &vc,int i); - template - void add_memory_vertices(vc_class &vc); - template - void add_memory_vorder(vc_class &vc); - void add_memory_ds(); - void add_memory_ds2(); - void add_memory_xse(); - bool failsafe_find(int &lp,int &ls,int &us,double &l,double &u); - template - bool create_facet(vc_class &vc,int lp,int ls,double l,int us,double u,int p_id); - template - bool collapse_order1(vc_class &vc); - template - inline bool collapse_order2(vc_class &vc); - template - bool delete_connection(vc_class &vc,int j,int k,bool hand); - inline bool search_for_outside_edge(int &up); - inline void add_to_stack(int sc2,int lp); - inline void reset_mask() { - for(int i=0;i &v,int i,int j,int k); - inline bool search_edge(int l,int &m,int &k); - inline unsigned int m_test(int n,double &ans); - inline unsigned int m_testx(int n,double &ans); - unsigned int m_calc(int n,double &ans); - inline void flip(int tp) {ed[tp][nu[tp]<<1]=-1-ed[tp][nu[tp]<<1];} - int check_marginal(int n,double &ans); - friend class voronoicell; - friend class voronoicell_neighbor; -}; - -/** \brief Extension of the voronoicell_base class to represent a Voronoi - * cell without neighbor information. - * - * This class is an extension of the voronoicell_base class, in cases when - * is not necessary to track the IDs of neighboring particles associated - * with each face of the Voronoi cell. */ -class voronoicell : public voronoicell_base { - public: - using voronoicell_base::nplane; - voronoicell() : voronoicell_base(default_length*default_length) {} - voronoicell(double max_len_sq_) : voronoicell_base(max_len_sq_) {} - template - voronoicell(c_class &con) : voronoicell_base(con.max_len_sq) {} - /** Copies the information from another voronoicell class into - * this class, extending memory allocation if necessary. - * \param[in] c the class to copy. */ - inline void operator=(voronoicell &c) { - voronoicell_base* vb((voronoicell_base*) &c); - check_memory_for_copy(*this,vb);copy(vb); - } - /** Cuts a Voronoi cell using by the plane corresponding to the - * perpendicular bisector of a particle. - * \param[in] (x,y,z) the position of the particle. - * \param[in] rsq the modulus squared of the vector. - * \param[in] p_id the plane ID, ignored for this case where no - * neighbor tracking is enabled. - * \return False if the plane cut deleted the cell entirely, - * true otherwise. */ - inline bool nplane(double x,double y,double z,double rsq,int p_id) { - return nplane(*this,x,y,z,rsq,0); - } - /** Cuts a Voronoi cell using by the plane corresponding to the - * perpendicular bisector of a particle. - * \param[in] (x,y,z) the position of the particle. - * \param[in] p_id the plane ID, ignored for this case where no - * neighbor tracking is enabled. - * \return False if the plane cut deleted the cell entirely, - * true otherwise. */ - inline bool nplane(double x,double y,double z,int p_id) { - double rsq=x*x+y*y+z*z; - return nplane(*this,x,y,z,rsq,0); - } - /** Cuts a Voronoi cell using by the plane corresponding to the - * perpendicular bisector of a particle. - * \param[in] (x,y,z) the position of the particle. - * \param[in] rsq the modulus squared of the vector. - * \return False if the plane cut deleted the cell entirely, - * true otherwise. */ - inline bool plane(double x,double y,double z,double rsq) { - return nplane(*this,x,y,z,rsq,0); - } - /** Cuts a Voronoi cell using by the plane corresponding to the - * perpendicular bisector of a particle. - * \param[in] (x,y,z) the position of the particle. - * \return False if the plane cut deleted the cell entirely, - * true otherwise. */ - inline bool plane(double x,double y,double z) { - double rsq=x*x+y*y+z*z; - return nplane(*this,x,y,z,rsq,0); - } - /** Initializes the Voronoi cell to be rectangular box with the - * given dimensions. - * \param[in] (xmin,xmax) the minimum and maximum x coordinates. - * \param[in] (ymin,ymax) the minimum and maximum y coordinates. - * \param[in] (zmin,zmax) the minimum and maximum z coordinates. */ - inline void init(double xmin,double xmax,double ymin,double ymax,double zmin,double zmax) { - init_base(xmin,xmax,ymin,ymax,zmin,zmax); - } - /** Initializes the cell to be an octahedron with vertices at - * (l,0,0), (-l,0,0), (0,l,0), (0,-l,0), (0,0,l), and (0,0,-l). - * \param[in] l a parameter setting the size of the octahedron. - */ - inline void init_octahedron(double l) { - init_octahedron_base(l); - } - /** Initializes the cell to be a tetrahedron. - * \param[in] (x0,y0,z0) the coordinates of the first vertex. - * \param[in] (x1,y1,z1) the coordinates of the second vertex. - * \param[in] (x2,y2,z2) the coordinates of the third vertex. - * \param[in] (x3,y3,z3) the coordinates of the fourth vertex. - */ - inline void init_tetrahedron(double x0,double y0,double z0,double x1,double y1,double z1,double x2,double y2,double z2,double x3,double y3,double z3) { - init_tetrahedron_base(x0,y0,z0,x1,y1,z1,x2,y2,z2,x3,y3,z3); - } - void init_l_shape(); - private: - inline void n_allocate(int i,int m) {}; - inline void n_add_memory_vertices(int i) {}; - inline void n_add_memory_vorder(int i) {}; - inline void n_set_pointer(int p,int n) {}; - inline void n_copy(int a,int b,int c,int d) {}; - inline void n_set(int a,int b,int c) {}; - inline void n_set_aux1(int k) {}; - inline void n_copy_aux1(int a,int b) {}; - inline void n_copy_aux1_shift(int a,int b) {}; - inline void n_set_aux2_copy(int a,int b) {}; - inline void n_copy_pointer(int a,int b) {}; - inline void n_set_to_aux1(int j) {}; - inline void n_set_to_aux2(int j) {}; - inline void n_allocate_aux1(int i) {}; - inline void n_switch_to_aux1(int i) {}; - inline void n_copy_to_aux1(int i,int m) {}; - inline void n_set_to_aux1_offset(int k,int m) {}; - inline void n_neighbors(std::vector &v) {v.clear();}; - friend class voronoicell_base; -}; - -/** \brief Extension of the voronoicell_base class to represent a Voronoi cell - * with neighbor information. - * - * This class is an extension of the voronoicell_base class, in cases when the - * IDs of neighboring particles associated with each face of the Voronoi cell. - * It contains additional data structures mne and ne for storing this - * information. */ -class voronoicell_neighbor : public voronoicell_base { - public: - using voronoicell_base::nplane; - /** This two dimensional array holds the neighbor information - * associated with each vertex. mne[p] is a one dimensional - * array which holds all of the neighbor information for - * vertices of order p. */ - int **mne; - /** This is a two dimensional array that holds the neighbor - * information associated with each vertex. ne[i] points to a - * one-dimensional array in mne[nu[i]]. ne[i][j] holds the - * neighbor information associated with the jth edge of vertex - * i. It is set to the ID number of the plane that made the - * face that is clockwise from the jth edge. */ - int **ne; - voronoicell_neighbor() : voronoicell_base(default_length*default_length) { - memory_setup(); - } - voronoicell_neighbor(double max_len_sq_) : voronoicell_base(max_len_sq_) { - memory_setup(); - } - template - voronoicell_neighbor(c_class &con) : voronoicell_base(con.max_len_sq) { - memory_setup(); - } - ~voronoicell_neighbor(); - void operator=(voronoicell &c); - void operator=(voronoicell_neighbor &c); - /** Cuts the Voronoi cell by a particle whose center is at a - * separation of (x,y,z) from the cell center. The value of rsq - * should be initially set to \f$x^2+y^2+z^2\f$. - * \param[in] (x,y,z) the normal vector to the plane. - * \param[in] rsq the distance along this vector of the plane. - * \param[in] p_id the plane ID (for neighbor tracking only). - * \return False if the plane cut deleted the cell entirely, - * true otherwise. */ - inline bool nplane(double x,double y,double z,double rsq,int p_id) { - return nplane(*this,x,y,z,rsq,p_id); - } - /** This routine calculates the modulus squared of the vector - * before passing it to the main nplane() routine with full - * arguments. - * \param[in] (x,y,z) the vector to cut the cell by. - * \param[in] p_id the plane ID (for neighbor tracking only). - * \return False if the plane cut deleted the cell entirely, - * true otherwise. */ - inline bool nplane(double x,double y,double z,int p_id) { - double rsq=x*x+y*y+z*z; - return nplane(*this,x,y,z,rsq,p_id); - } - /** This version of the plane routine just makes up the plane - * ID to be zero. It will only be referenced if neighbor - * tracking is enabled. - * \param[in] (x,y,z) the vector to cut the cell by. - * \param[in] rsq the modulus squared of the vector. - * \return False if the plane cut deleted the cell entirely, - * true otherwise. */ - inline bool plane(double x,double y,double z,double rsq) { - return nplane(*this,x,y,z,rsq,0); - } - /** Cuts a Voronoi cell using the influence of a particle at - * (x,y,z), first calculating the modulus squared of this - * vector before passing it to the main nplane() routine. Zero - * is supplied as the plane ID, which will be ignored unless - * neighbor tracking is enabled. - * \param[in] (x,y,z) the vector to cut the cell by. - * \return False if the plane cut deleted the cell entirely, - * true otherwise. */ - inline bool plane(double x,double y,double z) { - double rsq=x*x+y*y+z*z; - return nplane(*this,x,y,z,rsq,0); - } - void init(double xmin,double xmax,double ymin,double ymax,double zmin,double zmax); - void init_octahedron(double l); - void init_tetrahedron(double x0,double y0,double z0,double x1,double y1,double z1,double x2,double y2,double z2,double x3,double y3,double z3); - void check_facets(); - virtual void neighbors(std::vector &v); - virtual void print_edges_neighbors(int i); - virtual void output_neighbors(FILE *fp=stdout) { - std::vector v;neighbors(v); - voro_print_vector(v,fp); - } - private: - int *paux1; - int *paux2; - void memory_setup(); - inline void n_allocate(int i,int m) {mne[i]=new int[m*i];} - inline void n_add_memory_vertices(int i) { - int **pp=new int*[i]; - for(int j=0;j - -#include "voro++.hh" -using namespace voro; - -enum blocks_mode { - none, - length_scale, - specified -}; - -// A maximum allowed number of regions, to prevent enormous amounts of memory -// being allocated -const int max_regions=16777216; - -// This message gets displayed if the user requests the help flag -void help_message() { - puts("Voro++ version 0.4.6, by Chris H. Rycroft (UC Berkeley/LBL)\n\n" - "Syntax: voro++ [options] \n" - " \n\n" - "By default, the utility reads in the input file of particle IDs and positions,\n" - "computes the Voronoi cell for each, and then creates with an\n" - "additional column containing the volume of each Voronoi cell.\n\n" - "Available options:\n" - " -c : Specify a custom output string\n" - " -g : Turn on the gnuplot output to \n" - " -h/--help : Print this information\n" - " -hc : Print information about custom output\n" - " -l : Manually specify a length scale to configure the internal\n" - " computational grid\n" - " -m : Manually choose the memory allocation per grid block\n" - " (default 8)\n" - " -n [3] : Manually specify the internal grid size\n" - " -o : Ensure that the output file has the same order as the input\n" - " file\n" - " -p : Make container periodic in all three directions\n" - " -px : Make container periodic in the x direction\n" - " -py : Make container periodic in the y direction\n" - " -pz : Make container periodic in the z direction\n" - " -r : Assume the input file has an extra coordinate for radii\n" - " -v : Verbose output\n" - " --version : Print version information\n" - " -wb [6] : Add six plane wall objects to make rectangular box containing\n" - " the space x1 and POV-Ray Voronoi\n" - " cells to \n" - " -yp : Save only POV-Ray particles to \n" - " -yv : Save only POV-Ray Voronoi cells to "); -} - -// This message gets displayed if the user requests information about doing -// custom output -void custom_output_message() { - puts("The \"-c\" option allows a string to be specified that will customize the output\n" - "file to contain a variety of statistics about each computed Voronoi cell. The\n" - "string is similar to the standard C printf() function, made up of text with\n" - "additional control sequences that begin with percentage signs that are expanded\n" - "to different statistics. See http://math.lbl.gov/voro++/doc/custom.html for more\n" - "information.\n" - "\nParticle-related:\n" - " %i The particle ID number\n" - " %x The x coordinate of the particle\n" - " %y The y coordinate of the particle\n" - " %z The z coordinate of the particle\n" - " %q The position vector of the particle, short for \"%x %y %z\"\n" - " %r The radius of the particle (only printed if -p enabled)\n" - "\nVertex-related:\n" - " %w The number of vertices in the Voronoi cell\n" - " %p A list of the vertices of the Voronoi cell in the format (x,y,z),\n" - " relative to the particle center\n" - " %P A list of the vertices of the Voronoi cell in the format (x,y,z),\n" - " relative to the global coordinate system\n" - " %o A list of the orders of each vertex\n" - " %m The maximum radius squared of a vertex position, relative to the\n" - " particle center\n" - "\nEdge-related:\n" - " %g The number of edges of the Voronoi cell\n" - " %E The total edge distance\n" - " %e A list of perimeters of each face\n" - "\nFace-related:\n" - " %s The number of faces of the Voronoi cell\n" - " %F The total surface area of the Voronoi cell\n" - " %A A frequency table of the number of edges for each face\n" - " %a A list of the number of edges for each face\n" - " %f A list of areas of each face\n" - " %t A list of bracketed sequences of vertices that make up each face\n" - " %l A list of normal vectors for each face\n" - " %n A list of neighboring particle or wall IDs corresponding to each face\n" - "\nVolume-related:\n" - " %v The volume of the Voronoi cell\n" - " %c The centroid of the Voronoi cell, relative to the particle center\n" - " %C The centroid of the Voronoi cell, in the global coordinate system"); -} - -// Ths message is displayed if the user requests version information -void version_message() { - puts("Voro++ version 0.4.6 (October 17th 2013)"); -} - -// Prints an error message. This is called when the program is unable to make -// sense of the command-line options. -void error_message() { - fputs("voro++: Unrecognized command-line options; type \"voro++ -h\" for more\ninformation.\n",stderr); -} - -// Carries out the Voronoi computation and outputs the results to the requested -// files -template -void cmd_line_output(c_loop &vl,c_class &con,const char* format,FILE* outfile,FILE* gnu_file,FILE* povp_file,FILE* povv_file,bool verbose,double &vol,int &vcc,int &tp) { - int pid,ps=con.ps;double x,y,z,r; - if(con.contains_neighbor(format)) { - voronoicell_neighbor c(con); - if(vl.start()) do if(con.compute_cell(c,vl)) { - vl.pos(pid,x,y,z,r); - if(outfile!=NULL) c.output_custom(format,pid,x,y,z,r,outfile); - if(gnu_file!=NULL) c.draw_gnuplot(x,y,z,gnu_file); - if(povp_file!=NULL) { - fprintf(povp_file,"// id %d\n",pid); - if(ps==4) fprintf(povp_file,"sphere{<%g,%g,%g>,%g}\n",x,y,z,r); - else fprintf(povp_file,"sphere{<%g,%g,%g>,s}\n",x,y,z); - } - if(povv_file!=NULL) { - fprintf(povv_file,"// cell %d\n",pid); - c.draw_pov(x,y,z,povv_file); - } - if(verbose) {vol+=c.volume();vcc++;} - } while(vl.inc()); - } else { - voronoicell c(con); - if(vl.start()) do if(con.compute_cell(c,vl)) { - vl.pos(pid,x,y,z,r); - if(outfile!=NULL) c.output_custom(format,pid,x,y,z,r,outfile); - if(gnu_file!=NULL) c.draw_gnuplot(x,y,z,gnu_file); - if(povp_file!=NULL) { - fprintf(povp_file,"// id %d\n",pid); - if(ps==4) fprintf(povp_file,"sphere{<%g,%g,%g>,%g}\n",x,y,z,r); - else fprintf(povp_file,"sphere{<%g,%g,%g>,s}\n",x,y,z); - } - if(povv_file!=NULL) { - fprintf(povv_file,"// cell %d\n",pid); - c.draw_pov(x,y,z,povv_file); - } - if(verbose) {vol+=c.volume();vcc++;} - } while(vl.inc()); - } - if(verbose) tp=con.total_particles(); -} - -int main(int argc,char **argv) { - int i=1,j=-7,custom_output=0,nx,ny,nz,init_mem(8); - double ls=0; - blocks_mode bm=none; - bool gnuplot_output=false,povp_output=false,povv_output=false,polydisperse=false; - bool xperiodic=false,yperiodic=false,zperiodic=false,ordered=false,verbose=false; - pre_container *pcon=NULL;pre_container_poly *pconp=NULL; - wall_list wl; - - // If there's one argument, check to see if it's requesting help. - // Otherwise, bail out with an error. - if(argc==2) { - if(strcmp(argv[1],"-h")==0||strcmp(argv[1],"--help")==0) { - help_message();return 0; - } else if(strcmp(argv[1],"-hc")==0) { - custom_output_message();return 0; - } else if(strcmp(argv[1],"--version")==0) { - version_message();return 0; - } else { - error_message(); - return VOROPP_CMD_LINE_ERROR; - } - } - - // If there aren't enough command-line arguments, then bail out - // with an error. - if(argc<7) { - error_message(); - return VOROPP_CMD_LINE_ERROR; - } - - // We have enough arguments. Now start searching for command-line - // options. - while(i=argc-8) {error_message();wl.deallocate();return VOROPP_CMD_LINE_ERROR;} - if(custom_output==0) { - custom_output=++i; - } else { - fputs("voro++: multiple custom output strings detected\n",stderr); - wl.deallocate(); - return VOROPP_CMD_LINE_ERROR; - } - } else if(strcmp(argv[i],"-g")==0) { - gnuplot_output=true; - } else if(strcmp(argv[i],"-h")==0||strcmp(argv[i],"--help")==0) { - help_message();wl.deallocate();return 0; - } else if(strcmp(argv[i],"-hc")==0) { - custom_output_message();wl.deallocate();return 0; - } else if(strcmp(argv[i],"-l")==0) { - if(i>=argc-8) {error_message();wl.deallocate();return VOROPP_CMD_LINE_ERROR;} - if(bm!=none) { - fputs("voro++: Conflicting options about grid setup (-l/-n)\n",stderr); - wl.deallocate(); - return VOROPP_CMD_LINE_ERROR; - } - bm=length_scale; - i++;ls=atof(argv[i]); - } else if(strcmp(argv[i],"-m")==0) { - i++;init_mem=atoi(argv[i]); - } else if(strcmp(argv[i],"-n")==0) { - if(i>=argc-10) {error_message();wl.deallocate();return VOROPP_CMD_LINE_ERROR;} - if(bm!=none) { - fputs("voro++: Conflicting options about grid setup (-l/-n)\n",stderr); - wl.deallocate(); - return VOROPP_CMD_LINE_ERROR; - } - bm=specified; - i++; - nx=atoi(argv[i++]); - ny=atoi(argv[i++]); - nz=atoi(argv[i]); - if(nx<=0||ny<=0||nz<=0) { - fputs("voro++: Computational grid specified with -n must be greater than one\n" - "in each direction\n",stderr); - wl.deallocate(); - return VOROPP_CMD_LINE_ERROR; - } - } else if(strcmp(argv[i],"-o")==0) { - ordered=true; - } else if(strcmp(argv[i],"-p")==0) { - xperiodic=yperiodic=zperiodic=true; - } else if(strcmp(argv[i],"-px")==0) { - xperiodic=true; - } else if(strcmp(argv[i],"-py")==0) { - yperiodic=true; - } else if(strcmp(argv[i],"-pz")==0) { - zperiodic=true; - } else if(strcmp(argv[i],"-r")==0) { - polydisperse=true; - } else if(strcmp(argv[i],"-v")==0) { - verbose=true; - } else if(strcmp(argv[i],"--version")==0) { - version_message(); - wl.deallocate(); - return 0; - } else if(strcmp(argv[i],"-wb")==0) { - if(i>=argc-13) {error_message();wl.deallocate();return VOROPP_CMD_LINE_ERROR;} - i++; - double w0=atof(argv[i++]),w1=atof(argv[i++]); - double w2=atof(argv[i++]),w3=atof(argv[i++]); - double w4=atof(argv[i++]),w5=atof(argv[i]); - wl.add_wall(new wall_plane(-1,0,0,-w0,j));j--; - wl.add_wall(new wall_plane(1,0,0,w1,j));j--; - wl.add_wall(new wall_plane(0,-1,0,-w2,j));j--; - wl.add_wall(new wall_plane(0,1,0,w3,j));j--; - wl.add_wall(new wall_plane(0,0,-1,-w4,j));j--; - wl.add_wall(new wall_plane(0,0,1,w5,j));j--; - } else if(strcmp(argv[i],"-ws")==0) { - if(i>=argc-11) {error_message();wl.deallocate();return VOROPP_CMD_LINE_ERROR;} - i++; - double w0=atof(argv[i++]),w1=atof(argv[i++]); - double w2=atof(argv[i++]),w3=atof(argv[i]); - wl.add_wall(new wall_sphere(w0,w1,w2,w3,j)); - j--; - } else if(strcmp(argv[i],"-wp")==0) { - if(i>=argc-11) {error_message();wl.deallocate();return VOROPP_CMD_LINE_ERROR;} - i++; - double w0=atof(argv[i++]),w1=atof(argv[i++]); - double w2=atof(argv[i++]),w3=atof(argv[i]); - wl.add_wall(new wall_plane(w0,w1,w2,w3,j)); - j--; - } else if(strcmp(argv[i],"-wc")==0) { - if(i>=argc-14) {error_message();wl.deallocate();return VOROPP_CMD_LINE_ERROR;} - i++; - double w0=atof(argv[i++]),w1=atof(argv[i++]); - double w2=atof(argv[i++]),w3=atof(argv[i++]); - double w4=atof(argv[i++]),w5=atof(argv[i++]); - double w6=atof(argv[i]); - wl.add_wall(new wall_cylinder(w0,w1,w2,w3,w4,w5,w6,j)); - j--; - } else if(strcmp(argv[i],"-wo")==0) { - if(i>=argc-14) {error_message();wl.deallocate();return VOROPP_CMD_LINE_ERROR;} - i++; - double w0=atof(argv[i++]),w1=atof(argv[i++]); - double w2=atof(argv[i++]),w3=atof(argv[i++]); - double w4=atof(argv[i++]),w5=atof(argv[i++]); - double w6=atof(argv[i]); - wl.add_wall(new wall_cone(w0,w1,w2,w3,w4,w5,w6,j)); - j--; - } else if(strcmp(argv[i],"-y")==0) { - povp_output=povv_output=true; - } else if(strcmp(argv[i],"-yp")==0) { - povp_output=true; - } else if(strcmp(argv[i],"-yv")==0) { - povv_output=true; - } else { - wl.deallocate(); - error_message(); - return VOROPP_CMD_LINE_ERROR; - } - i++; - } - - // Check the memory guess is positive - if(init_mem<=0) { - fputs("voro++: The memory allocation must be positive\n",stderr); - wl.deallocate(); - return VOROPP_CMD_LINE_ERROR; - } - - // Read in the dimensions of the test box, and estimate the number of - // boxes to divide the region up into - double ax=atof(argv[i]),bx=atof(argv[i+1]); - double ay=atof(argv[i+2]),by=atof(argv[i+3]); - double az=atof(argv[i+4]),bz=atof(argv[i+5]); - - // Check that for each coordinate, the minimum value is smaller - // than the maximum value - if(bximport(argv[i+6]); - pconp->guess_optimal(nx,ny,nz); - } else { - pcon=new pre_container(ax,bx,ay,by,az,bz,xperiodic,yperiodic,zperiodic); - pcon->import(argv[i+6]); - pcon->guess_optimal(nx,ny,nz); - } - } else { - double nxf,nyf,nzf; - if(bm==length_scale) { - - // Check that the length scale is positive and - // reasonably large - if(lsmax_regions) { - fprintf(stderr,"voro++: Number of computational blocks exceeds the maximum allowed of %d.\n" - "Either increase the particle length scale, or recompile with an increased\nmaximum.",max_regions); - wl.deallocate(); - return VOROPP_MEMORY_ERROR; - } - } - - // Check that the output filename is a sensible length - int flen=strlen(argv[i+6]); - if(flen>4096) { - fputs("voro++: Filename too long\n",stderr); - wl.deallocate(); - return VOROPP_CMD_LINE_ERROR; - } - - // Open files for output - char *buffer=new char[flen+7]; - sprintf(buffer,"%s.vol",argv[i+6]); - FILE *outfile=safe_fopen(buffer,"w"),*gnu_file,*povp_file,*povv_file; - if(gnuplot_output) { - sprintf(buffer,"%s.gnu",argv[i+6]); - gnu_file=safe_fopen(buffer,"w"); - } else gnu_file=NULL; - if(povp_output) { - sprintf(buffer,"%s_p.pov",argv[i+6]); - povp_file=safe_fopen(buffer,"w"); - } else povp_file=NULL; - if(povv_output) { - sprintf(buffer,"%s_v.pov",argv[i+6]); - povv_file=safe_fopen(buffer,"w"); - } else povv_file=NULL; - delete [] buffer; - - const char *c_str=(custom_output==0?(polydisperse?"%i %q %v %r":"%i %q %v"):argv[custom_output]); - - // Now switch depending on whether polydispersity was enabled, and - // whether output ordering is requested - double vol=0;int tp=0,vcc=0; - if(polydisperse) { - if(ordered) { - particle_order vo; - container_poly con(ax,bx,ay,by,az,bz,nx,ny,nz,xperiodic,yperiodic,zperiodic,init_mem); - con.add_wall(wl); - if(bm==none) { - pconp->setup(vo,con);delete pconp; - } else con.import(vo,argv[i+6]); - - c_loop_order vlo(con,vo); - cmd_line_output(vlo,con,c_str,outfile,gnu_file,povp_file,povv_file,verbose,vol,vcc,tp); - } else { - container_poly con(ax,bx,ay,by,az,bz,nx,ny,nz,xperiodic,yperiodic,zperiodic,init_mem); - con.add_wall(wl); - - if(bm==none) { - pconp->setup(con);delete pconp; - } else con.import(argv[i+6]); - - c_loop_all vla(con); - cmd_line_output(vla,con,c_str,outfile,gnu_file,povp_file,povv_file,verbose,vol,vcc,tp); - } - } else { - if(ordered) { - particle_order vo; - container con(ax,bx,ay,by,az,bz,nx,ny,nz,xperiodic,yperiodic,zperiodic,init_mem); - con.add_wall(wl); - if(bm==none) { - pcon->setup(vo,con);delete pcon; - } else con.import(vo,argv[i+6]); - - c_loop_order vlo(con,vo); - cmd_line_output(vlo,con,c_str,outfile,gnu_file,povp_file,povv_file,verbose,vol,vcc,tp); - } else { - container con(ax,bx,ay,by,az,bz,nx,ny,nz,xperiodic,yperiodic,zperiodic,init_mem); - con.add_wall(wl); - if(bm==none) { - pcon->setup(con);delete pcon; - } else con.import(argv[i+6]); - c_loop_all vla(con); - cmd_line_output(vla,con,c_str,outfile,gnu_file,povp_file,povv_file,verbose,vol,vcc,tp); - } - } - - // Print information if verbose output requested - if(verbose) { - printf("Container geometry : [%g:%g] [%g:%g] [%g:%g]\n" - "Computational grid size : %d by %d by %d (%s)\n" - "Filename : %s\n" - "Output string : %s%s\n",ax,bx,ay,by,az,bz,nx,ny,nz, - bm==none?"estimated from file":(bm==length_scale? - "estimated using length scale":"directly specified"), - argv[i+6],c_str,custom_output==0?" (default)":""); - printf("Total imported particles : %d (%.2g per grid block)\n" - "Total V. cells computed : %d\n" - "Total container volume : %g\n" - "Total V. cell volume : %g\n",tp,((double) tp)/(nx*ny*nz), - vcc,(bx-ax)*(by-ay)*(bz-az),vol); - } - - // Close output files - fclose(outfile); - if(gnu_file!=NULL) fclose(gnu_file); - if(povp_file!=NULL) fclose(povp_file); - if(povv_file!=NULL) fclose(povv_file); - return 0; -} diff --git a/src/third_party/voro/src/common.cc b/src/third_party/voro/src/common.cc deleted file mode 100644 index bf8579a8a..000000000 --- a/src/third_party/voro/src/common.cc +++ /dev/null @@ -1,138 +0,0 @@ -// Voro++, a 3D cell-based Voronoi library -// -// Author : Chris H. Rycroft (Harvard University / LBL) -// Email : chr@alum.mit.edu -// Date : August 30th 2011 - -/** \file common.cc - * \brief Implementations of the small helper functions. */ - -#include "common.hh" - -namespace voro { - -void check_duplicate(int n,double x,double y,double z,int id,double *qp) { - double dx=*qp-x,dy=qp[1]-y,dz=qp[2]-z; - if(dx*dx+dy*dy+dz*dz<1e-10) { - printf("Duplicate: %d (%g,%g,%g) matches %d (%g,%g,%g)\n",n,x,y,z,id,*qp,qp[1],qp[2]); - exit(1); - } -} - -/** \brief Function for printing fatal error messages and exiting. - * - * Function for printing fatal error messages and exiting. - * \param[in] p a pointer to the message to print. - * \param[in] status the status code to return with. */ -void voro_fatal_error(const char *p,int status) { - fprintf(stderr,"voro++: %s\n",p); - exit(status); -} - -/** \brief Prints a vector of positions. - * - * Prints a vector of positions as bracketed triplets. - * \param[in] v the vector to print. - * \param[in] fp the file stream to print to. */ -void voro_print_positions(std::vector &v,FILE *fp) { - if(v.size()>0) { - fprintf(fp,"(%g,%g,%g)",v[0],v[1],v[2]); - for(int k=3;(unsigned int) k &v,FILE *fp) { - int k=0,s=v.size(); - while(k+4 &v,FILE *fp) { - int k=0,s=v.size(); - while(k+4 &v,FILE *fp) { - int j,k=0,l; - if(v.size()>0) { - l=v[k++]; - if(l<=1) { - if(l==1) fprintf(fp,"(%d)",v[k++]); - else fputs("()",fp); - } else { - j=k+l; - fprintf(fp,"(%d",v[k++]); - while(k -#include -#include - -#include "config.hh" - -namespace voro { - -void check_duplicate(int n,double x,double y,double z,int id,double *qp); - -void voro_fatal_error(const char *p,int status); -void voro_print_positions(std::vector &v,FILE *fp=stdout); -FILE* safe_fopen(const char *filename,const char *mode); -void voro_print_vector(std::vector &v,FILE *fp=stdout); -void voro_print_vector(std::vector &v,FILE *fp=stdout); -void voro_print_face_vertices(std::vector &v,FILE *fp=stdout); - -} - -#endif diff --git a/src/third_party/voro/src/config.hh b/src/third_party/voro/src/config.hh deleted file mode 100644 index 61d54d365..000000000 --- a/src/third_party/voro/src/config.hh +++ /dev/null @@ -1,123 +0,0 @@ -// Voro++, a 3D cell-based Voronoi library -// -// Author : Chris H. Rycroft (Harvard University / LBL) -// Email : chr@alum.mit.edu -// Date : August 30th 2011 - -/** \file config.hh - * \brief Master configuration file for setting various compile-time options. */ - -#ifndef VOROPP_CONFIG_HH -#define VOROPP_CONFIG_HH - -#include - -namespace voro { - -// These constants set the initial memory allocation for the Voronoi cell -/** The initial memory allocation for the number of vertices. */ -const int init_vertices=256; -/** The initial memory allocation for the maximum vertex order. */ -const int init_vertex_order=64; -/** The initial memory allocation for the number of regular vertices of order - * 3. */ -const int init_3_vertices=256; -/** The initial memory allocation for the number of vertices of higher order. - */ -const int init_n_vertices=8; -/** The initial size for the delete stack. */ -const int init_delete_size=256; -/** The initial size for the auxiliary delete stack. */ -const int init_delete2_size=256; -/** The initial size for the extra search stack. */ -const int init_xsearch_size=32; -/** The initial size for the wall pointer array. */ -const int init_wall_size=32; -/** The default initial size for the ordering class. */ -const int init_ordering_size=4096; -/** The initial size of the pre_container chunk index. */ -const int init_chunk_size=256; - -// If the initial memory is too small, the program dynamically allocates more. -// However, if the limits below are reached, then the program bails out. -/** The maximum memory allocation for the number of vertices. */ -const int max_vertices=16777216; -/** The maximum memory allocation for the maximum vertex order. */ -const int max_vertex_order=2048; -/** The maximum memory allocation for the any particular order of vertex. */ -const int max_n_vertices=16777216; -/** The maximum size for the delete stack. */ -const int max_delete_size=16777216; -/** The maximum size for the auxiliary delete stack. */ -const int max_delete2_size=16777216; -/** The maximum size for the extra search stack. */ -const int max_xsearch_size=16777216; -/** The maximum amount of particle memory allocated for a single region. */ -const int max_particle_memory=16777216; -/** The maximum size for the wall pointer array. */ -const int max_wall_size=2048; -/** The maximum size for the ordering class. */ -const int max_ordering_size=67108864; -/** The maximum size for the pre_container chunk index. */ -const int max_chunk_size=65536; - -/** The chunk size in the pre_container classes. */ -const int pre_container_chunk_size=1024; - -#ifndef VOROPP_VERBOSE -/** Voro++ can print a number of different status and debugging messages to - * notify the user of special behavior, and this macro sets the amount which - * are displayed. At level 0, no messages are printed. At level 1, messages - * about unusual cases during cell construction are printed, such as when the - * plane routine bails out due to floating point problems. At level 2, general - * messages about memory expansion are printed. At level 3, technical details - * about memory management are printed. */ -#define VOROPP_VERBOSE 2 -#endif - -/** If a point is within this distance of a cutting plane, then the code - * assumes that point exactly lies on the plane. */ -const double tolerance=10.*std::numeric_limits::epsilon(); - -const double big_tolerance_fac=20.; - -const double default_length=1000.; - -/** A large number that is used in the computation. */ -const double large_number=std::numeric_limits::max(); - -/** A radius to use as a placeholder when no other information is available. */ -const double default_radius=0.5; - -/** The maximum number of shells of periodic images to test over. */ -const int max_unit_voro_shells=10; - -/** A guess for the optimal number of particles per block, used to set up the - * container grid. */ -const double optimal_particles=5.6; - -/** If this is set to 1, then the code reports any instances of particles being - * put outside of the container geometry. */ -#define VOROPP_REPORT_OUT_OF_BOUNDS 0 - -/** Voro++ returns this status code if there is a file-related error, such as - * not being able to open file. */ -#define VOROPP_FILE_ERROR 1 - -/** Voro++ returns this status code if there is a memory allocation error, if - * one of the safe memory limits is exceeded. */ -#define VOROPP_MEMORY_ERROR 2 - -/** Voro++ returns this status code if there is any type of internal error, if - * it detects that representation of the Voronoi cell is inconsistent. This - * status code will generally indicate a bug, and the developer should be - * contacted. */ -#define VOROPP_INTERNAL_ERROR 3 - -/** Voro++ returns this status code if it could not interpret the command line - * arguments passed to the command line utility. */ -#define VOROPP_CMD_LINE_ERROR 4 - -} - -#endif diff --git a/src/third_party/voro/src/container.cc b/src/third_party/voro/src/container.cc deleted file mode 100644 index 1d21dbc89..000000000 --- a/src/third_party/voro/src/container.cc +++ /dev/null @@ -1,551 +0,0 @@ -// Voro++, a 3D cell-based Voronoi library -// -// Author : Chris H. Rycroft (Harvard University / LBL) -// Email : chr@alum.mit.edu -// Date : August 30th 2011 - -/** \file container.cc - * \brief Function implementations for the container and related classes. */ - -#include "container.hh" - -namespace voro { - -/** The class constructor sets up the geometry of container, initializing the - * minimum and maximum coordinates in each direction, and setting whether each - * direction is periodic or not. It divides the container into a rectangular - * grid of blocks, and allocates memory for each of these for storing particle - * positions and IDs. - * \param[in] (ax_,bx_) the minimum and maximum x coordinates. - * \param[in] (ay_,by_) the minimum and maximum y coordinates. - * \param[in] (az_,bz_) the minimum and maximum z coordinates. - * \param[in] (nx_,ny_,nz_) the number of grid blocks in each of the three - * coordinate directions. - * \param[in] (xperiodic_,yperiodic_,zperiodic_) flags setting whether the - * container is periodic in each - * coordinate direction. - * \param[in] init_mem the initial memory allocation for each block. - * \param[in] ps_ the number of floating point entries to store for each - * particle. */ -container_base::container_base(double ax_,double bx_,double ay_,double by_,double az_,double bz_, - int nx_,int ny_,int nz_,bool xperiodic_,bool yperiodic_,bool zperiodic_,int init_mem,int ps_) - : voro_base(nx_,ny_,nz_,(bx_-ax_)/nx_,(by_-ay_)/ny_,(bz_-az_)/nz_), - ax(ax_), bx(bx_), ay(ay_), by(by_), az(az_), bz(bz_), - max_len_sq((bx-ax)*(bx-ax)*(xperiodic_?0.25:1)+(by-ay)*(by-ay)*(yperiodic_?0.25:1) - +(bz-az)*(bz-az)*(zperiodic_?0.25:1)), - xperiodic(xperiodic_), yperiodic(yperiodic_), zperiodic(zperiodic_), - id(new int*[nxyz]), p(new double*[nxyz]), co(new int[nxyz]), mem(new int[nxyz]), ps(ps_) { - - int l; - for(l=0;l=nx) return false; - - int j=step_int((y-ay)*ysp); - if(yperiodic) {l=step_mod(j,ny);y+=boxy*(l-j);j=l;} - else if(j<0||j>=ny) return false; - - int k=step_int((z-az)*zsp); - if(zperiodic) {l=step_mod(k,nz);z+=boxz*(l-k);k=l;} - else if(k<0||k>=nz) return false; - - ijk+=nx*j+nxy*k; - return true; -} - -/** Takes a position vector and attempts to remap it into the primary domain. - * \param[out] (ai,aj,ak) the periodic image displacement that the vector is in, - * with (0,0,0) corresponding to the primary domain. - * \param[out] (ci,cj,ck) the index of the block that the position vector is - * within, once it has been remapped. - * \param[in,out] (x,y,z) the position vector to consider, which is remapped - * into the primary domain during the routine. - * \param[out] ijk the block index that the vector is within. - * \return True if the particle is within the container or can be remapped into - * it, false if it lies outside of the container bounds. */ -inline bool container_base::remap(int &ai,int &aj,int &ak,int &ci,int &cj,int &ck,double &x,double &y,double &z,int &ijk) { - ci=step_int((x-ax)*xsp); - if(ci<0||ci>=nx) { - if(xperiodic) {ai=step_div(ci,nx);x-=ai*(bx-ax);ci-=ai*nx;} - else return false; - } else ai=0; - - cj=step_int((y-ay)*ysp); - if(cj<0||cj>=ny) { - if(yperiodic) {aj=step_div(cj,ny);y-=aj*(by-ay);cj-=aj*ny;} - else return false; - } else aj=0; - - ck=step_int((z-az)*zsp); - if(ck<0||ck>=nz) { - if(zperiodic) {ak=step_div(ck,nz);z-=ak*(bz-az);ck-=ak*nz;} - else return false; - } else ak=0; - - ijk=ci+nx*cj+nxy*ck; - return true; -} - -/** Takes a vector and finds the particle whose Voronoi cell contains that - * vector. This is equivalent to finding the particle which is nearest to the - * vector. Additional wall classes are not considered by this routine. - * \param[in] (x,y,z) the vector to test. - * \param[out] (rx,ry,rz) the position of the particle whose Voronoi cell - * contains the vector. If the container is periodic, - * this may point to a particle in a periodic image of - * the primary domain. - * \param[out] pid the ID of the particle. - * \return True if a particle was found. If the container has no particles, - * then the search will not find a Voronoi cell and false is returned. */ -bool container::find_voronoi_cell(double x,double y,double z,double &rx,double &ry,double &rz,int &pid) { - int ai,aj,ak,ci,cj,ck,ijk; - particle_record w; - double mrs; - - // If the given vector lies outside the domain, but the container - // is periodic, then remap it back into the domain - if(!remap(ai,aj,ak,ci,cj,ck,x,y,z,ijk)) return false; - vc.find_voronoi_cell(x,y,z,ci,cj,ck,ijk,w,mrs); - - if(w.ijk!=-1) { - - // Assemble the position vector of the particle to be returned, - // applying a periodic remapping if necessary - if(xperiodic) {ci+=w.di;if(ci<0||ci>=nx) ai+=step_div(ci,nx);} - if(yperiodic) {cj+=w.dj;if(cj<0||cj>=ny) aj+=step_div(cj,ny);} - if(zperiodic) {ck+=w.dk;if(ck<0||ck>=nz) ak+=step_div(ck,nz);} - rx=p[w.ijk][3*w.l]+ai*(bx-ax); - ry=p[w.ijk][3*w.l+1]+aj*(by-ay); - rz=p[w.ijk][3*w.l+2]+ak*(bz-az); - pid=id[w.ijk][w.l]; - return true; - } - - // If no particle if found then just return false - return false; -} - -/** Takes a vector and finds the particle whose Voronoi cell contains that - * vector. Additional wall classes are not considered by this routine. - * \param[in] (x,y,z) the vector to test. - * \param[out] (rx,ry,rz) the position of the particle whose Voronoi cell - * contains the vector. If the container is periodic, - * this may point to a particle in a periodic image of - * the primary domain. - * \param[out] pid the ID of the particle. - * \return True if a particle was found. If the container has no particles, - * then the search will not find a Voronoi cell and false is returned. */ -bool container_poly::find_voronoi_cell(double x,double y,double z,double &rx,double &ry,double &rz,int &pid) { - int ai,aj,ak,ci,cj,ck,ijk; - particle_record w; - double mrs; - - // If the given vector lies outside the domain, but the container - // is periodic, then remap it back into the domain - if(!remap(ai,aj,ak,ci,cj,ck,x,y,z,ijk)) return false; - vc.find_voronoi_cell(x,y,z,ci,cj,ck,ijk,w,mrs); - - if(w.ijk!=-1) { - - // Assemble the position vector of the particle to be returned, - // applying a periodic remapping if necessary - if(xperiodic) {ci+=w.di;if(ci<0||ci>=nx) ai+=step_div(ci,nx);} - if(yperiodic) {cj+=w.dj;if(cj<0||cj>=ny) aj+=step_div(cj,ny);} - if(zperiodic) {ck+=w.dk;if(ck<0||ck>=nz) ak+=step_div(ck,nz);} - rx=p[w.ijk][4*w.l]+ai*(bx-ax); - ry=p[w.ijk][4*w.l+1]+aj*(by-ay); - rz=p[w.ijk][4*w.l+2]+ak*(bz-az); - pid=id[w.ijk][w.l]; - return true; - } - - // If no particle if found then just return false - return false; -} - -/** Increase memory for a particular region. - * \param[in] i the index of the region to reallocate. */ -void container_base::add_particle_memory(int i) { - int l,nmem=mem[i]<<1; - - // Carry out a check on the memory allocation size, and - // print a status message if requested - if(nmem>max_particle_memory) - voro_fatal_error("Absolute maximum memory allocation exceeded",VOROPP_MEMORY_ERROR); -#if VOROPP_VERBOSE >=3 - fprintf(stderr,"Particle memory in region %d scaled up to %d\n",i,nmem); -#endif - - // Allocate new memory and copy in the contents of the old arrays - int *idp=new int[nmem]; - for(l=0;lbx||yby||zbz) return false; - return point_inside_walls(x,y,z); -} - -/** Draws an outline of the domain in gnuplot format. - * \param[in] fp the file handle to write to. */ -void container_base::draw_domain_gnuplot(FILE *fp) { - fprintf(fp,"%g %g %g\n%g %g %g\n%g %g %g\n%g %g %g\n",ax,ay,az,bx,ay,az,bx,by,az,ax,by,az); - fprintf(fp,"%g %g %g\n%g %g %g\n%g %g %g\n%g %g %g\n",ax,by,bz,bx,by,bz,bx,ay,bz,ax,ay,bz); - fprintf(fp,"%g %g %g\n\n%g %g %g\n%g %g %g\n\n",ax,by,bz,ax,ay,az,ax,ay,bz); - fprintf(fp,"%g %g %g\n%g %g %g\n\n%g %g %g\n%g %g %g\n\n",bx,ay,az,bx,ay,bz,bx,by,az,bx,by,bz); -} - -/** Draws an outline of the domain in POV-Ray format. - * \param[in] fp the file handle to write to. */ -void container_base::draw_domain_pov(FILE *fp) { - fprintf(fp,"cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n" - "cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n",ax,ay,az,bx,ay,az,ax,by,az,bx,by,az); - fprintf(fp,"cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n" - "cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n",ax,by,bz,bx,by,bz,ax,ay,bz,bx,ay,bz); - fprintf(fp,"cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n" - "cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n",ax,ay,az,ax,by,az,bx,ay,az,bx,by,az); - fprintf(fp,"cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n" - "cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n",bx,ay,bz,bx,by,bz,ax,ay,bz,ax,by,bz); - fprintf(fp,"cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n" - "cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n",ax,ay,az,ax,ay,bz,bx,ay,az,bx,ay,bz); - fprintf(fp,"cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n" - "cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n",bx,by,az,bx,by,bz,ax,by,az,ax,by,bz); - fprintf(fp,"sphere{<%g,%g,%g>,rr}\nsphere{<%g,%g,%g>,rr}\n" - "sphere{<%g,%g,%g>,rr}\nsphere{<%g,%g,%g>,rr}\n",ax,ay,az,bx,ay,az,ax,by,az,bx,by,az); - fprintf(fp,"sphere{<%g,%g,%g>,rr}\nsphere{<%g,%g,%g>,rr}\n" - "sphere{<%g,%g,%g>,rr}\nsphere{<%g,%g,%g>,rr}\n",ax,ay,bz,bx,ay,bz,ax,by,bz,bx,by,bz); -} - -/** The wall_list constructor sets up an array of pointers to wall classes. */ -wall_list::wall_list() : walls(new wall*[init_wall_size]), wep(walls), wel(walls+init_wall_size), - current_wall_size(init_wall_size) {} - -/** The wall_list destructor frees the array of pointers to the wall classes. - */ -wall_list::~wall_list() { - delete [] walls; -} - -/** Adds all of the walls on another wall_list to this class. - * \param[in] wl a reference to the wall class. */ -void wall_list::add_wall(wall_list &wl) { - for(wall **wp=wl.walls;wpmax_wall_size) - voro_fatal_error("Wall memory allocation exceeded absolute maximum",VOROPP_MEMORY_ERROR); - wall **nwalls=new wall*[current_wall_size],**nwp=nwalls,**wp=walls; - while(wp -#include - -#include "config.hh" -#include "common.hh" -#include "v_base.hh" -#include "cell.hh" -#include "c_loops.hh" -#include "v_compute.hh" -#include "rad_option.hh" - -namespace voro { - -/** \brief Pure virtual class from which wall objects are derived. - * - * This is a pure virtual class for a generic wall object. A wall object - * can be specified by deriving a new class from this and specifying the - * functions.*/ -class wall { - public: - virtual ~wall() {} - /** A pure virtual function for testing whether a point is - * inside the wall object. */ - virtual bool point_inside(double x,double y,double z) = 0; - /** A pure virtual function for cutting a cell without - * neighbor-tracking with a wall. */ - virtual bool cut_cell(voronoicell &c,double x,double y,double z) = 0; - /** A pure virtual function for cutting a cell with - * neighbor-tracking enabled with a wall. */ - virtual bool cut_cell(voronoicell_neighbor &c,double x,double y,double z) = 0; -}; - -/** \brief A class for storing a list of pointers to walls. - * - * This class stores a list of pointers to wall classes. It contains several - * simple routines that make use of the wall classes (such as telling whether a - * given position is inside all of the walls or not). It can be used by itself, - * but also forms part of container_base, for associating walls with this - * class. */ -class wall_list { - public: - /** An array holding pointers to wall objects. */ - wall **walls; - /** A pointer to the next free position to add a wall pointer. - */ - wall **wep; - wall_list(); - ~wall_list(); - /** Adds a wall to the list. - * \param[in] w the wall to add. */ - inline void add_wall(wall *w) { - if(wep==wel) increase_wall_memory(); - *(wep++)=w; - } - /** Adds a wall to the list. - * \param[in] w a reference to the wall to add. */ - inline void add_wall(wall &w) {add_wall(&w);} - void add_wall(wall_list &wl); - /** Determines whether a given position is inside all of the - * walls on the list. - * \param[in] (x,y,z) the position to test. - * \return True if it is inside, false if it is outside. */ - inline bool point_inside_walls(double x,double y,double z) { - for(wall **wp=walls;wppoint_inside(x,y,z))) return false; - return true; - } - /** Cuts a Voronoi cell by all of the walls currently on - * the list. - * \param[in] c a reference to the Voronoi cell class. - * \param[in] (x,y,z) the position of the cell. - * \return True if the cell still exists, false if the cell is - * deleted. */ - template - bool apply_walls(c_class &c,double x,double y,double z) { - for(wall **wp=walls;wpcut_cell(c,x,y,z))) return false; - return true; - } - void deallocate(); - protected: - void increase_wall_memory(); - /** A pointer to the limit of the walls array, used to - * determine when array is full. */ - wall **wel; - /** The current amount of memory allocated for walls. */ - int current_wall_size; -}; - -/** \brief Class for representing a particle system in a three-dimensional - * rectangular box. - * - * This class represents a system of particles in a three-dimensional - * rectangular box. Any combination of non-periodic and periodic coordinates - * can be used in the three coordinate directions. The class is not intended - * for direct use, but instead forms the base of the container and - * container_poly classes that add specialized routines for computing the - * regular and radical Voronoi tessellations respectively. It contains routines - * that are commonly between these two classes, such as those for drawing the - * domain, and placing particles within the internal data structure. - * - * The class is derived from the wall_list class, which encapsulates routines - * for associating walls with the container, and the voro_base class, which - * encapsulates routines about the underlying computational grid. */ -class container_base : public voro_base, public wall_list { - public: - /** The minimum x coordinate of the container. */ - const double ax; - /** The maximum x coordinate of the container. */ - const double bx; - /** The minimum y coordinate of the container. */ - const double ay; - /** The maximum y coordinate of the container. */ - const double by; - /** The minimum z coordinate of the container. */ - const double az; - /** The maximum z coordinate of the container. */ - const double bz; - /** The maximum length squared that could be encountered in the - * Voronoi cell calculation. */ - const double max_len_sq; - /** A boolean value that determines if the x coordinate in - * periodic or not. */ - const bool xperiodic; - /** A boolean value that determines if the y coordinate in - * periodic or not. */ - const bool yperiodic; - /** A boolean value that determines if the z coordinate in - * periodic or not. */ - const bool zperiodic; - /** This array holds the numerical IDs of each particle in each - * computational box. */ - int **id; - /** A two dimensional array holding particle positions. For the - * derived container_poly class, this also holds particle - * radii. */ - double **p; - /** This array holds the number of particles within each - * computational box of the container. */ - int *co; - /** This array holds the maximum amount of particle memory for - * each computational box of the container. If the number of - * particles in a particular box ever approaches this limit, - * more is allocated using the add_particle_memory() function. - */ - int *mem; - /** The amount of memory in the array structure for each - * particle. This is set to 3 when the basic class is - * initialized, so that the array holds (x,y,z) positions. If - * the container class is initialized as part of the derived - * class container_poly, then this is set to 4, to also hold - * the particle radii. */ - const int ps; - container_base(double ax_,double bx_,double ay_,double by_,double az_,double bz_, - int nx_,int ny_,int nz_,bool xperiodic_,bool yperiodic_,bool zperiodic_, - int init_mem,int ps_); - ~container_base(); - bool point_inside(double x,double y,double z); - void region_count(); - /** Initializes the Voronoi cell prior to a compute_cell - * operation for a specific particle being carried out by a - * voro_compute class. The cell is initialized to fill the - * entire container. For non-periodic coordinates, this is set - * by the position of the walls. For periodic coordinates, the - * space is equally divided in either direction from the - * particle's initial position. Plane cuts made by any walls - * that have been added are then applied to the cell. - * \param[in,out] c a reference to a voronoicell object. - * \param[in] ijk the block that the particle is within. - * \param[in] q the index of the particle within its block. - * \param[in] (ci,cj,ck) the coordinates of the block in the - * container coordinate system. - * \param[out] (i,j,k) the coordinates of the test block - * relative to the voro_compute - * coordinate system. - * \param[out] (x,y,z) the position of the particle. - * \param[out] disp a block displacement used internally by the - * compute_cell routine. - * \return False if the plane cuts applied by walls completely - * removed the cell, true otherwise. */ - template - inline bool initialize_voronoicell(v_cell &c,int ijk,int q,int ci,int cj,int ck, - int &i,int &j,int &k,double &x,double &y,double &z,int &disp) { - double x1,x2,y1,y2,z1,z2,*pp=p[ijk]+ps*q; - x=*(pp++);y=*(pp++);z=*pp; - if(xperiodic) {x1=-(x2=0.5*(bx-ax));i=nx;} else {x1=ax-x;x2=bx-x;i=ci;} - if(yperiodic) {y1=-(y2=0.5*(by-ay));j=ny;} else {y1=ay-y;y2=by-y;j=cj;} - if(zperiodic) {z1=-(z2=0.5*(bz-az));k=nz;} else {z1=az-z;z2=bz-z;k=ck;} - c.init(x1,x2,y1,y2,z1,z2); - if(!apply_walls(c,x,y,z)) return false; - disp=ijk-i-nx*(j+ny*k); - return true; - } - /** Initializes parameters for a find_voronoi_cell call within - * the voro_compute template. - * \param[in] (ci,cj,ck) the coordinates of the test block in - * the container coordinate system. - * \param[in] ijk the index of the test block - * \param[out] (i,j,k) the coordinates of the test block - * relative to the voro_compute - * coordinate system. - * \param[out] disp a block displacement used internally by the - * find_voronoi_cell routine. */ - inline void initialize_search(int ci,int cj,int ck,int ijk,int &i,int &j,int &k,int &disp) { - i=xperiodic?nx:ci; - j=yperiodic?ny:cj; - k=zperiodic?nz:ck; - disp=ijk-i-nx*(j+ny*k); - } - /** Returns the position of a particle currently being computed - * relative to the computational block that it is within. It is - * used to select the optimal worklist entry to use. - * \param[in] (x,y,z) the position of the particle. - * \param[in] (ci,cj,ck) the block that the particle is within. - * \param[out] (fx,fy,fz) the position relative to the block. - */ - inline void frac_pos(double x,double y,double z,double ci,double cj,double ck, - double &fx,double &fy,double &fz) { - fx=x-ax-boxx*ci; - fy=y-ay-boxy*cj; - fz=z-az-boxz*ck; - } - /** Calculates the index of block in the container structure - * corresponding to given coordinates. - * \param[in] (ci,cj,ck) the coordinates of the original block - * in the current computation, relative - * to the container coordinate system. - * \param[in] (ei,ej,ek) the displacement of the current block - * from the original block. - * \param[in,out] (qx,qy,qz) the periodic displacement that - * must be added to the particles - * within the computed block. - * \param[in] disp a block displacement used internally by the - * find_voronoi_cell and compute_cell routines. - * \return The block index. */ - inline int region_index(int ci,int cj,int ck,int ei,int ej,int ek,double &qx,double &qy,double &qz,int &disp) { - if(xperiodic) {if(ci+ei=(nx<<1)) {ei-=nx;qx=bx-ax;} else qx=0;} - if(yperiodic) {if(cj+ej=(ny<<1)) {ej-=ny;qy=by-ay;} else qy=0;} - if(zperiodic) {if(ck+ek=(nz<<1)) {ek-=nz;qz=bz-az;} else qz=0;} - return disp+ei+nx*(ej+ny*ek); - } - void draw_domain_gnuplot(FILE *fp=stdout); - /** Draws an outline of the domain in Gnuplot format. - * \param[in] filename the filename to write to. */ - inline void draw_domain_gnuplot(const char* filename) { - FILE *fp=safe_fopen(filename,"w"); - draw_domain_gnuplot(fp); - fclose(fp); - } - void draw_domain_pov(FILE *fp=stdout); - /** Draws an outline of the domain in Gnuplot format. - * \param[in] filename the filename to write to. */ - inline void draw_domain_pov(const char* filename) { - FILE *fp=safe_fopen(filename,"w"); - draw_domain_pov(fp); - fclose(fp); - } - /** Sums up the total number of stored particles. - * \return The number of particles. */ - inline int total_particles() { - int tp=*co; - for(int *cop=co+1;cop - void draw_particles(c_loop &vl,FILE *fp) { - double *pp; - if(vl.start()) do { - pp=p[vl.ijk]+3*vl.q; - fprintf(fp,"%d %g %g %g\n",id[vl.ijk][vl.q],*pp,pp[1],pp[2]); - } while(vl.inc()); - } - /** Dumps all of the particle IDs and positions to a file. - * \param[in] fp a file handle to write to. */ - inline void draw_particles(FILE *fp=stdout) { - c_loop_all vl(*this); - draw_particles(vl,fp); - } - /** Dumps all of the particle IDs and positions to a file. - * \param[in] filename the name of the file to write to. */ - inline void draw_particles(const char *filename) { - FILE *fp=safe_fopen(filename,"w"); - draw_particles(fp); - fclose(fp); - } - /** Dumps particle positions in POV-Ray format. - * \param[in] vl the loop class to use. - * \param[in] fp a file handle to write to. */ - template - void draw_particles_pov(c_loop &vl,FILE *fp) { - double *pp; - if(vl.start()) do { - pp=p[vl.ijk]+3*vl.q; - fprintf(fp,"// id %d\nsphere{<%g,%g,%g>,s}\n", - id[vl.ijk][vl.q],*pp,pp[1],pp[2]); - } while(vl.inc()); - } - /** Dumps all particle positions in POV-Ray format. - * \param[in] fp a file handle to write to. */ - inline void draw_particles_pov(FILE *fp=stdout) { - c_loop_all vl(*this); - draw_particles_pov(vl,fp); - } - /** Dumps all particle positions in POV-Ray format. - * \param[in] filename the name of the file to write to. */ - inline void draw_particles_pov(const char *filename) { - FILE *fp=safe_fopen(filename,"w"); - draw_particles_pov(fp); - fclose(fp); - } - /** Computes Voronoi cells and saves the output in gnuplot - * format. - * \param[in] vl the loop class to use. - * \param[in] fp a file handle to write to. */ - template - void draw_cells_gnuplot(c_loop &vl,FILE *fp) { - voronoicell c(*this);double *pp; - if(vl.start()) do if(compute_cell(c,vl)) { - pp=p[vl.ijk]+ps*vl.q; - c.draw_gnuplot(*pp,pp[1],pp[2],fp); - } while(vl.inc()); - } - /** Computes all Voronoi cells and saves the output in gnuplot - * format. - * \param[in] fp a file handle to write to. */ - inline void draw_cells_gnuplot(FILE *fp=stdout) { - c_loop_all vl(*this); - draw_cells_gnuplot(vl,fp); - } - /** Computes all Voronoi cells and saves the output in gnuplot - * format. - * \param[in] filename the name of the file to write to. */ - inline void draw_cells_gnuplot(const char *filename) { - FILE *fp=safe_fopen(filename,"w"); - draw_cells_gnuplot(fp); - fclose(fp); - } - /** Computes Voronoi cells and saves the output in POV-Ray - * format. - * \param[in] vl the loop class to use. - * \param[in] fp a file handle to write to. */ - template - void draw_cells_pov(c_loop &vl,FILE *fp) { - voronoicell c(*this);double *pp; - if(vl.start()) do if(compute_cell(c,vl)) { - fprintf(fp,"// cell %d\n",id[vl.ijk][vl.q]); - pp=p[vl.ijk]+ps*vl.q; - c.draw_pov(*pp,pp[1],pp[2],fp); - } while(vl.inc()); - } - /** Computes all Voronoi cells and saves the output in POV-Ray - * format. - * \param[in] fp a file handle to write to. */ - inline void draw_cells_pov(FILE *fp=stdout) { - c_loop_all vl(*this); - draw_cells_pov(vl,fp); - } - /** Computes all Voronoi cells and saves the output in POV-Ray - * format. - * \param[in] filename the name of the file to write to. */ - inline void draw_cells_pov(const char *filename) { - FILE *fp=safe_fopen(filename,"w"); - draw_cells_pov(fp); - fclose(fp); - } - /** Computes the Voronoi cells and saves customized information - * about them. - * \param[in] vl the loop class to use. - * \param[in] format the custom output string to use. - * \param[in] fp a file handle to write to. */ - template - void print_custom(c_loop &vl,const char *format,FILE *fp) { - int ijk,q;double *pp; - if(contains_neighbor(format)) { - voronoicell_neighbor c(*this); - if(vl.start()) do if(compute_cell(c,vl)) { - ijk=vl.ijk;q=vl.q;pp=p[ijk]+ps*q; - c.output_custom(format,id[ijk][q],*pp,pp[1],pp[2],default_radius,fp); - } while(vl.inc()); - } else { - voronoicell c(*this); - if(vl.start()) do if(compute_cell(c,vl)) { - ijk=vl.ijk;q=vl.q;pp=p[ijk]+ps*q; - c.output_custom(format,id[ijk][q],*pp,pp[1],pp[2],default_radius,fp); - } while(vl.inc()); - } - } - void print_custom(const char *format,FILE *fp=stdout); - void print_custom(const char *format,const char *filename); - bool find_voronoi_cell(double x,double y,double z,double &rx,double &ry,double &rz,int &pid); - /** Computes the Voronoi cell for a particle currently being - * referenced by a loop class. - * \param[out] c a Voronoi cell class in which to store the - * computed cell. - * \param[in] vl the loop class to use. - * \return True if the cell was computed. If the cell cannot be - * computed, if it is removed entirely by a wall or boundary - * condition, then the routine returns false. */ - template - inline bool compute_cell(v_cell &c,c_loop &vl) { - return vc.compute_cell(c,vl.ijk,vl.q,vl.i,vl.j,vl.k); - } - /** Computes the Voronoi cell for given particle. - * \param[out] c a Voronoi cell class in which to store the - * computed cell. - * \param[in] ijk the block that the particle is within. - * \param[in] q the index of the particle within the block. - * \return True if the cell was computed. If the cell cannot be - * computed, if it is removed entirely by a wall or boundary - * condition, then the routine returns false. */ - template - inline bool compute_cell(v_cell &c,int ijk,int q) { - int k=ijk/nxy,ijkt=ijk-nxy*k,j=ijkt/nx,i=ijkt-j*nx; - return vc.compute_cell(c,ijk,q,i,j,k); - } - /** Computes the Voronoi cell for a ghost particle at a given - * location. - * \param[out] c a Voronoi cell class in which to store the - * computed cell. - * \param[in] (x,y,z) the location of the ghost particle. - * \return True if the cell was computed. If the cell cannot be - * computed, if it is removed entirely by a wall or boundary - * condition, then the routine returns false. */ - template - inline bool compute_ghost_cell(v_cell &c,double x,double y,double z) { - int ijk; - if(put_locate_block(ijk,x,y,z)) { - double *pp=p[ijk]+3*co[ijk]++; - *(pp++)=x;*(pp++)=y;*pp=z; - bool q=compute_cell(c,ijk,co[ijk]-1); - co[ijk]--; - return q; - } - return false; - } - private: - voro_compute vc; - friend class voro_compute; -}; - -/** \brief Extension of the container_base class for computing radical Voronoi - * tessellations. - * - * This class is an extension of container_base class that has routines - * specifically for computing the radical Voronoi tessellation that depends on - * the particle radii. */ -class container_poly : public container_base, public radius_poly { - public: - container_poly(double ax_,double bx_,double ay_,double by_,double az_,double bz_, - int nx_,int ny_,int nz_,bool xperiodic_,bool yperiodic_,bool zperiodic_,int init_mem); - void clear(); - void put(int n,double x,double y,double z,double r); - void put(particle_order &vo,int n,double x,double y,double z,double r); - void import(FILE *fp=stdin); - void import(particle_order &vo,FILE *fp=stdin); - /** Imports a list of particles from an open file stream into - * the container_poly class. Entries of five numbers (Particle - * ID, x position, y position, z position, radius) are searched - * for. If the file cannot be successfully read, then the - * routine causes a fatal error. - * \param[in] filename the name of the file to open and read - * from. */ - inline void import(const char* filename) { - FILE *fp=safe_fopen(filename,"r"); - import(fp); - fclose(fp); - } - /** Imports a list of particles from an open file stream into - * the container_poly class. Entries of five numbers (Particle - * ID, x position, y position, z position, radius) are searched - * for. In addition, the order in which particles are read is - * saved into an ordering class. If the file cannot be - * successfully read, then the routine causes a fatal error. - * \param[in,out] vo the ordering class to use. - * \param[in] filename the name of the file to open and read - * from. */ - inline void import(particle_order &vo,const char* filename) { - FILE *fp=safe_fopen(filename,"r"); - import(vo,fp); - fclose(fp); - } - void compute_all_cells(); - double sum_cell_volumes(); - /** Dumps particle IDs, positions and radii to a file. - * \param[in] vl the loop class to use. - * \param[in] fp a file handle to write to. */ - template - void draw_particles(c_loop &vl,FILE *fp) { - double *pp; - if(vl.start()) do { - pp=p[vl.ijk]+4*vl.q; - fprintf(fp,"%d %g %g %g %g\n",id[vl.ijk][vl.q],*pp,pp[1],pp[2],pp[3]); - } while(vl.inc()); - } - /** Dumps all of the particle IDs, positions and radii to a - * file. - * \param[in] fp a file handle to write to. */ - inline void draw_particles(FILE *fp=stdout) { - c_loop_all vl(*this); - draw_particles(vl,fp); - } - /** Dumps all of the particle IDs, positions and radii to a - * file. - * \param[in] filename the name of the file to write to. */ - inline void draw_particles(const char *filename) { - FILE *fp=safe_fopen(filename,"w"); - draw_particles(fp); - fclose(fp); - } - /** Dumps particle positions in POV-Ray format. - * \param[in] vl the loop class to use. - * \param[in] fp a file handle to write to. */ - template - void draw_particles_pov(c_loop &vl,FILE *fp) { - double *pp; - if(vl.start()) do { - pp=p[vl.ijk]+4*vl.q; - fprintf(fp,"// id %d\nsphere{<%g,%g,%g>,%g}\n", - id[vl.ijk][vl.q],*pp,pp[1],pp[2],pp[3]); - } while(vl.inc()); - } - /** Dumps all the particle positions in POV-Ray format. - * \param[in] fp a file handle to write to. */ - inline void draw_particles_pov(FILE *fp=stdout) { - c_loop_all vl(*this); - draw_particles_pov(vl,fp); - } - /** Dumps all the particle positions in POV-Ray format. - * \param[in] filename the name of the file to write to. */ - inline void draw_particles_pov(const char *filename) { - FILE *fp=safe_fopen(filename,"w"); - draw_particles_pov(fp); - fclose(fp); - } - /** Computes Voronoi cells and saves the output in gnuplot - * format. - * \param[in] vl the loop class to use. - * \param[in] fp a file handle to write to. */ - template - void draw_cells_gnuplot(c_loop &vl,FILE *fp) { - voronoicell c;double *pp; - if(vl.start()) do if(compute_cell(c,vl)) { - pp=p[vl.ijk]+ps*vl.q; - c.draw_gnuplot(*pp,pp[1],pp[2],fp); - } while(vl.inc()); - } - /** Compute all Voronoi cells and saves the output in gnuplot - * format. - * \param[in] fp a file handle to write to. */ - inline void draw_cells_gnuplot(FILE *fp=stdout) { - c_loop_all vl(*this); - draw_cells_gnuplot(vl,fp); - } - /** Compute all Voronoi cells and saves the output in gnuplot - * format. - * \param[in] filename the name of the file to write to. */ - inline void draw_cells_gnuplot(const char *filename) { - FILE *fp=safe_fopen(filename,"w"); - draw_cells_gnuplot(fp); - fclose(fp); - } - /** Computes Voronoi cells and saves the output in POV-Ray - * format. - * \param[in] vl the loop class to use. - * \param[in] fp a file handle to write to. */ - template - void draw_cells_pov(c_loop &vl,FILE *fp) { - voronoicell c(*this);double *pp; - if(vl.start()) do if(compute_cell(c,vl)) { - fprintf(fp,"// cell %d\n",id[vl.ijk][vl.q]); - pp=p[vl.ijk]+ps*vl.q; - c.draw_pov(*pp,pp[1],pp[2],fp); - } while(vl.inc()); - } - /** Computes all Voronoi cells and saves the output in POV-Ray - * format. - * \param[in] fp a file handle to write to. */ - inline void draw_cells_pov(FILE *fp=stdout) { - c_loop_all vl(*this); - draw_cells_pov(vl,fp); - } - /** Computes all Voronoi cells and saves the output in POV-Ray - * format. - * \param[in] filename the name of the file to write to. */ - inline void draw_cells_pov(const char *filename) { - FILE *fp=safe_fopen(filename,"w"); - draw_cells_pov(fp); - fclose(fp); - } - /** Computes the Voronoi cells and saves customized information - * about them. - * \param[in] vl the loop class to use. - * \param[in] format the custom output string to use. - * \param[in] fp a file handle to write to. */ - template - void print_custom(c_loop &vl,const char *format,FILE *fp) { - int ijk,q;double *pp; - if(contains_neighbor(format)) { - voronoicell_neighbor c(*this); - if(vl.start()) do if(compute_cell(c,vl)) { - ijk=vl.ijk;q=vl.q;pp=p[ijk]+ps*q; - c.output_custom(format,id[ijk][q],*pp,pp[1],pp[2],pp[3],fp); - } while(vl.inc()); - } else { - voronoicell c(*this); - if(vl.start()) do if(compute_cell(c,vl)) { - ijk=vl.ijk;q=vl.q;pp=p[ijk]+ps*q; - c.output_custom(format,id[ijk][q],*pp,pp[1],pp[2],pp[3],fp); - } while(vl.inc()); - } - } - /** Computes the Voronoi cell for a particle currently being - * referenced by a loop class. - * \param[out] c a Voronoi cell class in which to store the - * computed cell. - * \param[in] vl the loop class to use. - * \return True if the cell was computed. If the cell cannot be - * computed, if it is removed entirely by a wall or boundary - * condition, then the routine returns false. */ - template - inline bool compute_cell(v_cell &c,c_loop &vl) { - return vc.compute_cell(c,vl.ijk,vl.q,vl.i,vl.j,vl.k); - } - /** Computes the Voronoi cell for given particle. - * \param[out] c a Voronoi cell class in which to store the - * computed cell. - * \param[in] ijk the block that the particle is within. - * \param[in] q the index of the particle within the block. - * \return True if the cell was computed. If the cell cannot be - * computed, if it is removed entirely by a wall or boundary - * condition, then the routine returns false. */ - template - inline bool compute_cell(v_cell &c,int ijk,int q) { - int k=ijk/nxy,ijkt=ijk-nxy*k,j=ijkt/nx,i=ijkt-j*nx; - return vc.compute_cell(c,ijk,q,i,j,k); - } - /** Computes the Voronoi cell for a ghost particle at a given - * location. - * \param[out] c a Voronoi cell class in which to store the - * computed cell. - * \param[in] (x,y,z) the location of the ghost particle. - * \param[in] r the radius of the ghost particle. - * \return True if the cell was computed. If the cell cannot be - * computed, if it is removed entirely by a wall or boundary - * condition, then the routine returns false. */ - template - inline bool compute_ghost_cell(v_cell &c,double x,double y,double z,double r) { - int ijk; - if(put_locate_block(ijk,x,y,z)) { - double *pp=p[ijk]+4*co[ijk]++,tm=max_radius; - *(pp++)=x;*(pp++)=y;*(pp++)=z;*pp=r; - if(r>max_radius) max_radius=r; - bool q=compute_cell(c,ijk,co[ijk]-1); - co[ijk]--;max_radius=tm; - return q; - } - return false; - } - void print_custom(const char *format,FILE *fp=stdout); - void print_custom(const char *format,const char *filename); - bool find_voronoi_cell(double x,double y,double z,double &rx,double &ry,double &rz,int &pid); - private: - voro_compute vc; - friend class voro_compute; -}; - -} - -#endif diff --git a/src/third_party/voro/src/container_prd.cc b/src/third_party/voro/src/container_prd.cc deleted file mode 100644 index 07625b379..000000000 --- a/src/third_party/voro/src/container_prd.cc +++ /dev/null @@ -1,776 +0,0 @@ -// Voro++, a 3D cell-based Voronoi library -// -// Author : Chris H. Rycroft (Harvard University / LBL) -// Email : chr@alum.mit.edu -// Date : August 30th 2011 - -/** \file container_prd.cc - * \brief Function implementations for the container_periodic_base and - * related classes. */ - -#include "container_prd.hh" - -namespace voro { - -/** The class constructor sets up the geometry of container, initializing the - * minimum and maximum coordinates in each direction, and setting whether each - * direction is periodic or not. It divides the container into a rectangular - * grid of blocks, and allocates memory for each of these for storing particle - * positions and IDs. - * \param[in] (bx_) The x coordinate of the first unit vector. - * \param[in] (bxy_,by_) The x and y coordinates of the second unit vector. - * \param[in] (bxz_,byz_,bz_) The x, y, and z coordinates of the third unit - * vector. - * \param[in] (nx_,ny_,nz_) the number of grid blocks in each of the three - * coordinate directions. - * \param[in] init_mem_ the initial memory allocation for each block. - * \param[in] ps_ the number of floating point entries to store for each - * particle. */ -container_periodic_base::container_periodic_base(double bx_,double bxy_,double by_, - double bxz_,double byz_,double bz_,int nx_,int ny_,int nz_,int init_mem_,int ps_) - : unitcell(bx_,bxy_,by_,bxz_,byz_,bz_), - voro_base(nx_,ny_,nz_,bx_/nx_,by_/ny_,bz_/nz_), max_len_sq(unit_voro.max_radius_squared()), - ey(int(max_uv_y*ysp+1)), ez(int(max_uv_z*zsp+1)), wy(ny+ey), wz(nz+ez), - oy(ny+2*ey), oz(nz+2*ez), oxyz(nx*oy*oz), id(new int*[oxyz]), p(new double*[oxyz]), - co(new int[oxyz]), mem(new int[oxyz]), img(new char[oxyz]), init_mem(init_mem_), ps(ps_) { - int i,j,k,l; - - // Clear the global arrays - int *pp=co;while(pp=0;l--) if(mem[l]>0) { - delete [] p[l]; - delete [] id[l]; - } - delete [] img; - delete [] mem; - delete [] co; - delete [] id; - delete [] p; -} - -/** The class constructor sets up the geometry of container. - * \param[in] (bx_) The x coordinate of the first unit vector. - * \param[in] (bxy_,by_) The x and y coordinates of the second unit vector. - * \param[in] (bxz_,byz_,bz_) The x, y, and z coordinates of the third unit - * vector. - * \param[in] (nx_,ny_,nz_) the number of grid blocks in each of the three - * coordinate directions. - * \param[in] init_mem_ the initial memory allocation for each block. */ -container_periodic::container_periodic(double bx_,double bxy_,double by_,double bxz_,double byz_,double bz_, - int nx_,int ny_,int nz_,int init_mem_) - : container_periodic_base(bx_,bxy_,by_,bxz_,byz_,bz_,nx_,ny_,nz_,init_mem_,3), - vc(*this,2*nx_+1,2*ey+1,2*ez+1) {} - -/** The class constructor sets up the geometry of container. - * \param[in] (bx_) The x coordinate of the first unit vector. - * \param[in] (bxy_,by_) The x and y coordinates of the second unit vector. - * \param[in] (bxz_,byz_,bz_) The x, y, and z coordinates of the third unit - * vector. - * \param[in] (nx_,ny_,nz_) the number of grid blocks in each of the three - * coordinate directions. - * \param[in] init_mem_ the initial memory allocation for each block. */ -container_periodic_poly::container_periodic_poly(double bx_,double bxy_,double by_,double bxz_,double byz_,double bz_, - int nx_,int ny_,int nz_,int init_mem_) - : container_periodic_base(bx_,bxy_,by_,bxz_,byz_,bz_,nx_,ny_,nz_,init_mem_,4), - vc(*this,2*nx_+1,2*ey+1,2*ez+1) {ppr=p;} - -/** Put a particle into the correct region of the container. - * \param[in] n the numerical ID of the inserted particle. - * \param[in] (x,y,z) the position vector of the inserted particle. */ -void container_periodic::put(int n,double x,double y,double z) { - int ijk; - put_locate_block(ijk,x,y,z); - for(int l=0;l=nz) { - int ak=step_div(k,nz); - z-=ak*bz;y-=ak*byz;x-=ak*bxz;k-=ak*nz; - } - - // Remap particle in the y direction if necessary - int j=step_int(y*ysp); - if(j<0||j>=ny) { - int aj=step_div(j,ny); - y-=aj*by;x-=aj*bxy;j-=aj*ny; - } - - // Remap particle in the x direction if necessary - ijk=step_int(x*xsp); - if(ijk<0||ijk>=nx) { - int ai=step_div(ijk,nx); - x-=ai*bx;ijk-=ai*nx; - } - - // Compute the block index and check memory allocation - j+=ey;k+=ez; - ijk+=nx*(j+oy*k); - if(co[ijk]==mem[ijk]) add_particle_memory(ijk); -} - -/** Takes a particle position vector and computes the region index into which - * it should be stored. If the container is periodic, then the routine also - * maps the particle position to ensure it is in the primary domain. If the - * container is not periodic, the routine bails out. - * \param[out] ijk the region index. - * \param[in,out] (x,y,z) the particle position, remapped into the primary - * domain if necessary. - * \param[out] (ai,aj,ak) the periodic image displacement that the particle is - * in, with (0,0,0) corresponding to the primary domain. - * \return True if the particle can be successfully placed into the container, - * false otherwise. */ -void container_periodic_base::put_locate_block(int &ijk,double &x,double &y,double &z,int &ai,int &aj,int &ak) { - - // Remap particle in the z direction if necessary - int k=step_int(z*zsp); - if(k<0||k>=nz) { - ak=step_div(k,nz); - z-=ak*bz;y-=ak*byz;x-=ak*bxz;k-=ak*nz; - } else ak=0; - - // Remap particle in the y direction if necessary - int j=step_int(y*ysp); - if(j<0||j>=ny) { - aj=step_div(j,ny); - y-=aj*by;x-=aj*bxy;j-=aj*ny; - } else aj=0; - - // Remap particle in the x direction if necessary - ijk=step_int(x*xsp); - if(ijk<0||ijk>=nx) { - ai=step_div(ijk,nx); - x-=ai*bx;ijk-=ai*nx; - } else ai=0; - - // Compute the block index and check memory allocation - j+=ey;k+=ez; - ijk+=nx*(j+oy*k); - if(co[ijk]==mem[ijk]) add_particle_memory(ijk); -} - -/** Takes a position vector and remaps it into the primary domain. - * \param[out] (ai,aj,ak) the periodic image displacement that the vector is in, - * with (0,0,0) corresponding to the primary domain. - * \param[out] (ci,cj,ck) the index of the block that the position vector is - * within, once it has been remapped. - * \param[in,out] (x,y,z) the position vector to consider, which is remapped - * into the primary domain during the routine. - * \param[out] ijk the block index that the vector is within. */ -inline void container_periodic_base::remap(int &ai,int &aj,int &ak,int &ci,int &cj,int &ck,double &x,double &y,double &z,int &ijk) { - - // Remap particle in the z direction if necessary - ck=step_int(z*zsp); - if(ck<0||ck>=nz) { - ak=step_div(ck,nz); - z-=ak*bz;y-=ak*byz;x-=ak*bxz;ck-=ak*nz; - } else ak=0; - - // Remap particle in the y direction if necessary - cj=step_int(y*ysp); - if(cj<0||cj>=ny) { - aj=step_div(cj,ny); - y-=aj*by;x-=aj*bxy;cj-=aj*ny; - } else aj=0; - - // Remap particle in the x direction if necessary - ci=step_int(x*xsp); - if(ci<0||ci>=nx) { - ai=step_div(ci,nx); - x-=ai*bx;ci-=ai*nx; - } else ai=0; - - cj+=ey;ck+=ez; - ijk=ci+nx*(cj+oy*ck); -} - -/** Takes a vector and finds the particle whose Voronoi cell contains that - * vector. This is equivalent to finding the particle which is nearest to the - * vector. - * \param[in] (x,y,z) the vector to test. - * \param[out] (rx,ry,rz) the position of the particle whose Voronoi cell - * contains the vector. This may point to a particle in - * a periodic image of the primary domain. - * \param[out] pid the ID of the particle. - * \return True if a particle was found. If the container has no particles, - * then the search will not find a Voronoi cell and false is returned. */ -bool container_periodic::find_voronoi_cell(double x,double y,double z,double &rx,double &ry,double &rz,int &pid) { - int ai,aj,ak,ci,cj,ck,ijk; - particle_record w; - double mrs; - - // Remap the vector into the primary domain and then search for the - // Voronoi cell that it is within - remap(ai,aj,ak,ci,cj,ck,x,y,z,ijk); - vc.find_voronoi_cell(x,y,z,ci,cj,ck,ijk,w,mrs); - - if(w.ijk!=-1) { - - // Assemble the position vector of the particle to be returned, - // applying a periodic remapping if necessary - ci+=w.di;if(ci<0||ci>=nx) ai+=step_div(ci,nx); - rx=p[w.ijk][3*w.l]+ak*bxz+aj*bxy+ai*bx; - ry=p[w.ijk][3*w.l+1]+ak*byz+aj*by; - rz=p[w.ijk][3*w.l+2]+ak*bz; - pid=id[w.ijk][w.l]; - return true; - } - return false; -} - -/** Takes a vector and finds the particle whose Voronoi cell contains that - * vector. Additional wall classes are not considered by this routine. - * \param[in] (x,y,z) the vector to test. - * \param[out] (rx,ry,rz) the position of the particle whose Voronoi cell - * contains the vector. If the container is periodic, - * this may point to a particle in a periodic image of - * the primary domain. - * \param[out] pid the ID of the particle. - * \return True if a particle was found. If the container has no particles, - * then the search will not find a Voronoi cell and false is returned. */ -bool container_periodic_poly::find_voronoi_cell(double x,double y,double z,double &rx,double &ry,double &rz,int &pid) { - int ai,aj,ak,ci,cj,ck,ijk; - particle_record w; - double mrs; - - // Remap the vector into the primary domain and then search for the - // Voronoi cell that it is within - remap(ai,aj,ak,ci,cj,ck,x,y,z,ijk); - vc.find_voronoi_cell(x,y,z,ci,cj,ck,ijk,w,mrs); - - if(w.ijk!=-1) { - - // Assemble the position vector of the particle to be returned, - // applying a periodic remapping if necessary - ci+=w.di;if(ci<0||ci>=nx) ai+=step_div(ci,nx); - rx=p[w.ijk][4*w.l]+ak*bxz+aj*bxy+ai*bx; - ry=p[w.ijk][4*w.l+1]+ak*byz+aj*by; - rz=p[w.ijk][4*w.l+2]+ak*bz; - pid=id[w.ijk][w.l]; - return true; - } - return false; -} - -/** Increase memory for a particular region. - * \param[in] i the index of the region to reallocate. */ -void container_periodic_base::add_particle_memory(int i) { - - // Handle the case when no memory has been allocated for this block - if(mem[i]==0) { - mem[i]=init_mem; - id[i]=new int[init_mem]; - p[i]=new double[ps*init_mem]; - return; - } - - // Otherwise, double the memory allocation for this block. Carry out a - // check on the memory allocation size, and print a status message if - // requested. - int l,nmem(mem[i]<<1); - if(nmem>max_particle_memory) - voro_fatal_error("Absolute maximum memory allocation exceeded",VOROPP_MEMORY_ERROR); -#if VOROPP_VERBOSE >=3 - fprintf(stderr,"Particle memory in region %d scaled up to %d\n",i,nmem); -#endif - - // Allocate new memory and copy in the contents of the old arrays - int *idp=new int[nmem]; - for(l=0;l0) { - - // Compute the block's bounds, adding in a small tolerance - mix=i*boxx-tolerance;max=mix+boxx+tolerance; - miy=(j-ey)*boxy-tolerance;may=miy+boxy+tolerance; - miz=(k-ez)*boxz-tolerance;maz=miz+boxz+tolerance; - - // Print entries for any particles that lie outside the block's - // bounds - for(pp=p[l],c=0;cmax||pp[1]may||pp[2]maz) - printf("%d %d %d %d %f %f %f %f %f %f %f %f %f\n", - id[l][c],i,j,k,*pp,pp[1],pp[2],mix,max,miy,may,miz,maz); - } -} - -/** Creates particles within an image block that is aligned with the primary - * domain in the z axis. In this case, the image block may be comprised of - * particles from two primary blocks. The routine considers these two primary - * blocks, and adds the needed particles to the image. The remaining particles - * from the primary blocks are also filled into the neighboring images. - * \param[in] (di,dj,dk) the index of the block to consider. The z index must - * satisfy ez<=dk0) { - odijk=dijk-1;adis=dis; - } else { - odijk=dijk+nx-1;adis=dis+bx; - } - img[odijk]|=2; - for(l=0;lswitchx) put_image(dijk,fijk,l,dis,by*ima,0); - else put_image(odijk,fijk,l,adis,by*ima,0); - } - } - - // Right image computation - if((img[dijk]&2)==0) { - if(fi==nx-1) { - fijk+=1-nx;switchx+=(1-nx)*boxx;dis+=bx; - } else { - fijk++;switchx+=boxx; - } - if(di==nx-1) { - odijk=dijk-nx+1;adis=dis-bx; - } else { - odijk=dijk+1;adis=dis; - } - img[odijk]|=1; - for(l=0;l=wz. */ -void container_periodic_base::create_vertical_image(int di,int dj,int dk) { - int l,dijk=di+nx*(dj+oy*dk),dijkl,dijkr,ima=step_div(dk-ez,nz); - int qj=dj+step_int(-ima*byz*ysp),qjdiv=step_div(qj-ey,ny); - int qi=di+step_int((-ima*bxz-qjdiv*bxy)*xsp),qidiv=step_div(qi,nx); - int fi=qi-qidiv*nx,fj=qj-qjdiv*ny,fijk=fi+nx*(fj+oy*(dk-ima*nz)),fijk2; - double disy=ima*byz+qjdiv*by,switchy=(dj-ey)*boxy-ima*byz-qjdiv*by; - double disx=ima*bxz+qjdiv*bxy+qidiv*bx,switchx=di*boxx-ima*bxz-qjdiv*bxy-qidiv*bx; - double switchx2,disxl,disxr,disx2,disxr2; - - if(di==0) {dijkl=dijk+nx-1;disxl=disx+bx;} - else {dijkl=dijk-1;disxl=disx;} - - if(di==nx-1) {dijkr=dijk-nx+1;disxr=disx-bx;} - else {dijkr=dijk+1;disxr=disx;} - - // Down-left image computation - bool y_exist=dj!=0; - if((img[dijk]&1)==0) { - img[dijkl]|=2; - if(y_exist) { - img[dijkl-nx]|=8; - img[dijk-nx]|=4; - } - for(l=0;lswitchy) { - if(p[fijk][ps*l]>switchx) put_image(dijk,fijk,l,disx,disy,bz*ima); - else put_image(dijkl,fijk,l,disxl,disy,bz*ima); - } else { - if(!y_exist) continue; - if(p[fijk][ps*l]>switchx) put_image(dijk-nx,fijk,l,disx,disy,bz*ima); - else put_image(dijkl-nx,fijk,l,disxl,disy,bz*ima); - } - } - } - - // Down-right image computation - if((img[dijk]&2)==0) { - if(fi==nx-1) { - fijk2=fijk+1-nx;switchx2=switchx+(1-nx)*boxx;disx2=disx+bx;disxr2=disxr+bx; - } else { - fijk2=fijk+1;switchx2=switchx+boxx;disx2=disx;disxr2=disxr; - } - img[dijkr]|=1; - if(y_exist) { - img[dijkr-nx]|=4; - img[dijk-nx]|=8; - } - for(l=0;lswitchy) { - if(p[fijk2][ps*l]>switchx2) put_image(dijkr,fijk2,l,disxr2,disy,bz*ima); - else put_image(dijk,fijk2,l,disx2,disy,bz*ima); - } else { - if(!y_exist) continue; - if(p[fijk2][ps*l]>switchx2) put_image(dijkr-nx,fijk2,l,disxr2,disy,bz*ima); - else put_image(dijk-nx,fijk2,l,disx2,disy,bz*ima); - } - } - } - - // Recomputation of some intermediate quantities for boundary cases - if(fj==wy-1) { - fijk+=nx*(1-ny)-fi; - switchy+=(1-ny)*boxy; - disy+=by; - qi=di+step_int(-(ima*bxz+(qjdiv+1)*bxy)*xsp); - int dqidiv=step_div(qi,nx)-qidiv;qidiv+=dqidiv; - fi=qi-qidiv*nx; - fijk+=fi; - disx+=bxy+bx*dqidiv; - disxl+=bxy+bx*dqidiv; - disxr+=bxy+bx*dqidiv; - switchx-=bxy+bx*dqidiv; - } else { - fijk+=nx;switchy+=boxy; - } - - // Up-left image computation - y_exist=dj!=oy-1; - if((img[dijk]&4)==0) { - img[dijkl]|=8; - if(y_exist) { - img[dijkl+nx]|=2; - img[dijk+nx]|=1; - } - for(l=0;lswitchy) { - if(!y_exist) continue; - if(p[fijk][ps*l]>switchx) put_image(dijk+nx,fijk,l,disx,disy,bz*ima); - else put_image(dijkl+nx,fijk,l,disxl,disy,bz*ima); - } else { - if(p[fijk][ps*l]>switchx) put_image(dijk,fijk,l,disx,disy,bz*ima); - else put_image(dijkl,fijk,l,disxl,disy,bz*ima); - } - } - } - - // Up-right image computation - if((img[dijk]&8)==0) { - if(fi==nx-1) { - fijk2=fijk+1-nx;switchx2=switchx+(1-nx)*boxx;disx2=disx+bx;disxr2=disxr+bx; - } else { - fijk2=fijk+1;switchx2=switchx+boxx;disx2=disx;disxr2=disxr; - } - img[dijkr]|=4; - if(y_exist) { - img[dijkr+nx]|=1; - img[dijk+nx]|=2; - } - for(l=0;lswitchy) { - if(!y_exist) continue; - if(p[fijk2][ps*l]>switchx2) put_image(dijkr+nx,fijk2,l,disxr2,disy,bz*ima); - else put_image(dijk+nx,fijk2,l,disx2,disy,bz*ima); - } else { - if(p[fijk2][ps*l]>switchx2) put_image(dijkr,fijk2,l,disxr2,disy,bz*ima); - else put_image(dijk,fijk2,l,disx2,disy,bz*ima); - } - } - } - - // All contributions to the block now added, so set all four bits of - // the image information - img[dijk]=15; -} - -/** Copies a particle position from the primary domain into an image block. - * \param[in] reg the block index within the primary domain that the particle - * is within. - * \param[in] fijk the index of the image block. - * \param[in] l the index of the particle entry within the primary block. - * \param[in] (dx,dy,dz) the displacement vector to add to the particle. */ -void container_periodic_base::put_image(int reg,int fijk,int l,double dx,double dy,double dz) { - if(co[reg]==mem[reg]) add_particle_memory(reg); - double *p1=p[reg]+ps*co[reg],*p2=p[fijk]+ps*l; - *(p1++)=*(p2++)+dx; - *(p1++)=*(p2++)+dy; - *p1=*p2+dz; - if(ps==4) *(++p1)=*(++p2); - id[reg][co[reg]++]=id[fijk][l]; -} - -} diff --git a/src/third_party/voro/src/container_prd.hh b/src/third_party/voro/src/container_prd.hh deleted file mode 100644 index e350cda09..000000000 --- a/src/third_party/voro/src/container_prd.hh +++ /dev/null @@ -1,655 +0,0 @@ -// Voro++, a 3D cell-based Voronoi library -// -// Author : Chris H. Rycroft (Harvard University / LBL) -// Email : chr@alum.mit.edu -// Date : August 30th 2011 - -/** \file container_prd.hh - * \brief Header file for the container_periodic_base and related classes. */ - -#ifndef VOROPP_CONTAINER_PRD_HH -#define VOROPP_CONTAINER_PRD_HH - -#include -#include - -#include "config.hh" -#include "common.hh" -#include "v_base.hh" -#include "cell.hh" -#include "c_loops.hh" -#include "v_compute.hh" -#include "unitcell.hh" -#include "rad_option.hh" - -namespace voro { - -/** \brief Class for representing a particle system in a 3D periodic - * non-orthogonal periodic domain. - * - * This class represents a particle system in a three-dimensional - * non-orthogonal periodic domain. The domain is defined by three periodicity - * vectors (bx,0,0), (bxy,by,0), and (bxz,byz,bz) that represent a - * parallelepiped. Internally, the class stores particles in the box 0 - inline bool initialize_voronoicell(v_cell &c,int ijk,int q,int ci,int cj,int ck,int &i,int &j,int &k,double &x,double &y,double &z,int &disp) { - c=unit_voro; - double *pp=p[ijk]+ps*q; - x=*(pp++);y=*(pp++);z=*pp; - i=nx;j=ey;k=ez; - return true; - } - /** Initializes parameters for a find_voronoi_cell call within - * the voro_compute template. - * \param[in] (ci,cj,ck) the coordinates of the test block in - * the container coordinate system. - * \param[in] ijk the index of the test block - * \param[out] (i,j,k) the coordinates of the test block - * relative to the voro_compute - * coordinate system. - * \param[out] disp a block displacement used internally by the - * find_voronoi_cell routine (but not needed - * in this instance.) */ - inline void initialize_search(int ci,int cj,int ck,int ijk,int &i,int &j,int &k,int &disp) { - i=nx;j=ey;k=ez; - } - /** Returns the position of a particle currently being computed - * relative to the computational block that it is within. It is - * used to select the optimal worklist entry to use. - * \param[in] (x,y,z) the position of the particle. - * \param[in] (ci,cj,ck) the block that the particle is within. - * \param[out] (fx,fy,fz) the position relative to the block. - */ - inline void frac_pos(double x,double y,double z,double ci,double cj,double ck,double &fx,double &fy,double &fz) { - fx=x-boxx*ci; - fy=y-boxy*(cj-ey); - fz=z-boxz*(ck-ez); - } - /** Calculates the index of block in the container structure - * corresponding to given coordinates. - * \param[in] (ci,cj,ck) the coordinates of the original block - * in the current computation, relative - * to the container coordinate system. - * \param[in] (ei,ej,ek) the displacement of the current block - * from the original block. - * \param[in,out] (qx,qy,qz) the periodic displacement that - * must be added to the particles - * within the computed block. - * \param[in] disp a block displacement used internally by the - * find_voronoi_cell and compute_cell routines - * (but not needed in this instance.) - * \return The block index. */ - inline int region_index(int ci,int cj,int ck,int ei,int ej,int ek,double &qx,double &qy,double &qz,int &disp) { - int qi=ci+(ei-nx),qj=cj+(ej-ey),qk=ck+(ek-ez); - int iv(step_div(qi,nx));if(iv!=0) {qx=iv*bx;qi-=nx*iv;} else qx=0; - create_periodic_image(qi,qj,qk); - return qi+nx*(qj+oy*qk); - } - void create_all_images(); - void check_compartmentalized(); - protected: - void add_particle_memory(int i); - void put_locate_block(int &ijk,double &x,double &y,double &z); - void put_locate_block(int &ijk,double &x,double &y,double &z,int &ai,int &aj,int &ak); - /** Creates particles within an image block by copying them - * from the primary domain and shifting them. If the given - * block is aligned with the primary domain in the z-direction, - * the routine calls the simpler create_side_image routine - * where the image block may comprise of particles from up to - * two primary blocks. Otherwise is calls the more complex - * create_vertical_image where the image block may comprise of - * particles from up to four primary blocks. - * \param[in] (di,dj,dk) the coordinates of the image block to - * create. */ - inline void create_periodic_image(int di,int dj,int dk) { - if(di<0||di>=nx||dj<0||dj>=oy||dk<0||dk>=oz) - voro_fatal_error("Constructing periodic image for nonexistent point",VOROPP_INTERNAL_ERROR); - if(dk>=ez&&dk=wy) create_side_image(di,dj,dk); - } else create_vertical_image(di,dj,dk); - } - void create_side_image(int di,int dj,int dk); - void create_vertical_image(int di,int dj,int dk); - void put_image(int reg,int fijk,int l,double dx,double dy,double dz); - inline void remap(int &ai,int &aj,int &ak,int &ci,int &cj,int &ck,double &x,double &y,double &z,int &ijk); -}; - -/** \brief Extension of the container_periodic_base class for computing regular - * Voronoi tessellations. - * - * This class is an extension of the container_periodic_base that has routines - * specifically for computing the regular Voronoi tessellation with no - * dependence on particle radii. */ -class container_periodic : public container_periodic_base, public radius_mono { - public: - container_periodic(double bx_,double bxy_,double by_,double bxz_,double byz_,double bz_, - int nx_,int ny_,int nz_,int init_mem_); - void clear(); - void put(int n,double x,double y,double z); - void put(int n,double x,double y,double z,int &ai,int &aj,int &ak); - void put(particle_order &vo,int n,double x,double y,double z); - void import(FILE *fp=stdin); - void import(particle_order &vo,FILE *fp=stdin); - /** Imports a list of particles from an open file stream into - * the container. Entries of four numbers (Particle ID, x - * position, y position, z position) are searched for. If the - * file cannot be successfully read, then the routine causes a - * fatal error. - * \param[in] filename the name of the file to open and read - * from. */ - inline void import(const char* filename) { - FILE *fp=safe_fopen(filename,"r"); - import(fp); - fclose(fp); - } - /** Imports a list of particles from an open file stream into - * the container. Entries of four numbers (Particle ID, x - * position, y position, z position) are searched for. In - * addition, the order in which particles are read is saved - * into an ordering class. If the file cannot be successfully - * read, then the routine causes a fatal error. - * \param[in,out] vo the ordering class to use. - * \param[in] filename the name of the file to open and read - * from. */ - inline void import(particle_order &vo,const char* filename) { - FILE *fp=safe_fopen(filename,"r"); - import(vo,fp); - fclose(fp); - } - void compute_all_cells(); - double sum_cell_volumes(); - /** Dumps particle IDs and positions to a file. - * \param[in] vl the loop class to use. - * \param[in] fp a file handle to write to. */ - template - void draw_particles(c_loop &vl,FILE *fp) { - double *pp; - if(vl.start()) do { - pp=p[vl.ijk]+3*vl.q; - fprintf(fp,"%d %g %g %g\n",id[vl.ijk][vl.q],*pp,pp[1],pp[2]); - } while(vl.inc()); - } - /** Dumps all of the particle IDs and positions to a file. - * \param[in] fp a file handle to write to. */ - inline void draw_particles(FILE *fp=stdout) { - c_loop_all_periodic vl(*this); - draw_particles(vl,fp); - } - /** Dumps all of the particle IDs and positions to a file. - * \param[in] filename the name of the file to write to. */ - inline void draw_particles(const char *filename) { - FILE *fp=safe_fopen(filename,"w"); - draw_particles(fp); - fclose(fp); - } - /** Dumps particle positions in POV-Ray format. - * \param[in] vl the loop class to use. - * \param[in] fp a file handle to write to. */ - template - void draw_particles_pov(c_loop &vl,FILE *fp) { - double *pp; - if(vl.start()) do { - pp=p[vl.ijk]+3*vl.q; - fprintf(fp,"// id %d\nsphere{<%g,%g,%g>,s}\n", - id[vl.ijk][vl.q],*pp,pp[1],pp[2]); - } while(vl.inc()); - } - /** Dumps all particle positions in POV-Ray format. - * \param[in] fp a file handle to write to. */ - inline void draw_particles_pov(FILE *fp=stdout) { - c_loop_all_periodic vl(*this); - draw_particles_pov(vl,fp); - } - /** Dumps all particle positions in POV-Ray format. - * \param[in] filename the name of the file to write to. */ - inline void draw_particles_pov(const char *filename) { - FILE *fp=safe_fopen(filename,"w"); - draw_particles_pov(fp); - fclose(fp); - } - /** Computes Voronoi cells and saves the output in gnuplot - * format. - * \param[in] vl the loop class to use. - * \param[in] fp a file handle to write to. */ - template - void draw_cells_gnuplot(c_loop &vl,FILE *fp) { - voronoicell c(*this);double *pp; - if(vl.start()) do if(compute_cell(c,vl)) { - pp=p[vl.ijk]+ps*vl.q; - c.draw_gnuplot(*pp,pp[1],pp[2],fp); - } while(vl.inc()); - } - /** Computes all Voronoi cells and saves the output in gnuplot - * format. - * \param[in] fp a file handle to write to. */ - inline void draw_cells_gnuplot(FILE *fp=stdout) { - c_loop_all_periodic vl(*this); - draw_cells_gnuplot(vl,fp); - } - /** Compute all Voronoi cells and saves the output in gnuplot - * format. - * \param[in] filename the name of the file to write to. */ - inline void draw_cells_gnuplot(const char *filename) { - FILE *fp=safe_fopen(filename,"w"); - draw_cells_gnuplot(fp); - fclose(fp); - } - /** Computes Voronoi cells and saves the output in POV-Ray - * format. - * \param[in] vl the loop class to use. - * \param[in] fp a file handle to write to. */ - template - void draw_cells_pov(c_loop &vl,FILE *fp) { - voronoicell c(*this);double *pp; - if(vl.start()) do if(compute_cell(c,vl)) { - fprintf(fp,"// cell %d\n",id[vl.ijk][vl.q]); - pp=p[vl.ijk]+ps*vl.q; - c.draw_pov(*pp,pp[1],pp[2],fp); - } while(vl.inc()); - } - /** Computes all Voronoi cells and saves the output in POV-Ray - * format. - * \param[in] fp a file handle to write to. */ - inline void draw_cells_pov(FILE *fp=stdout) { - c_loop_all_periodic vl(*this); - draw_cells_pov(vl,fp); - } - /** Computes all Voronoi cells and saves the output in POV-Ray - * format. - * \param[in] filename the name of the file to write to. */ - inline void draw_cells_pov(const char *filename) { - FILE *fp=safe_fopen(filename,"w"); - draw_cells_pov(fp); - fclose(fp); - } - /** Computes the Voronoi cells and saves customized information - * about them. - * \param[in] vl the loop class to use. - * \param[in] format the custom output string to use. - * \param[in] fp a file handle to write to. */ - template - void print_custom(c_loop &vl,const char *format,FILE *fp) { - int ijk,q;double *pp; - if(contains_neighbor(format)) { - voronoicell_neighbor c(*this); - if(vl.start()) do if(compute_cell(c,vl)) { - ijk=vl.ijk;q=vl.q;pp=p[ijk]+ps*q; - c.output_custom(format,id[ijk][q],*pp,pp[1],pp[2],default_radius,fp); - } while(vl.inc()); - } else { - voronoicell c(*this); - if(vl.start()) do if(compute_cell(c,vl)) { - ijk=vl.ijk;q=vl.q;pp=p[ijk]+ps*q; - c.output_custom(format,id[ijk][q],*pp,pp[1],pp[2],default_radius,fp); - } while(vl.inc()); - } - } - void print_custom(const char *format,FILE *fp=stdout); - void print_custom(const char *format,const char *filename); - bool find_voronoi_cell(double x,double y,double z,double &rx,double &ry,double &rz,int &pid); - /** Computes the Voronoi cell for a particle currently being - * referenced by a loop class. - * \param[out] c a Voronoi cell class in which to store the - * computed cell. - * \param[in] vl the loop class to use. - * \return True if the cell was computed. If the cell cannot be - * computed because it was removed entirely for some reason, - * then the routine returns false. */ - template - inline bool compute_cell(v_cell &c,c_loop &vl) { - return vc.compute_cell(c,vl.ijk,vl.q,vl.i,vl.j,vl.k); - } - /** Computes the Voronoi cell for given particle. - * \param[out] c a Voronoi cell class in which to store the - * computed cell. - * \param[in] ijk the block that the particle is within. - * \param[in] q the index of the particle within the block. - * \return True if the cell was computed. If the cell cannot be - * computed because it was removed entirely for some reason, - * then the routine returns false. */ - template - inline bool compute_cell(v_cell &c,int ijk,int q) { - int k(ijk/(nx*oy)),ijkt(ijk-(nx*oy)*k),j(ijkt/nx),i(ijkt-j*nx); - return vc.compute_cell(c,ijk,q,i,j,k); - } - /** Computes the Voronoi cell for a ghost particle at a given - * location. - * \param[out] c a Voronoi cell class in which to store the - * computed cell. - * \param[in] (x,y,z) the location of the ghost particle. - * \return True if the cell was computed. If the cell cannot be - * computed, if it is removed entirely by a wall or boundary - * condition, then the routine returns false. */ - template - inline bool compute_ghost_cell(v_cell &c,double x,double y,double z) { - int ijk; - put_locate_block(ijk,x,y,z); - double *pp=p[ijk]+3*co[ijk]++; - *(pp++)=x;*(pp++)=y;*(pp++)=z; - bool q=compute_cell(c,ijk,co[ijk]-1); - co[ijk]--; - return q; - } - private: - voro_compute vc; - friend class voro_compute; -}; - -/** \brief Extension of the container_periodic_base class for computing radical - * Voronoi tessellations. - * - * This class is an extension of container_periodic_base that has routines - * specifically for computing the radical Voronoi tessellation that depends - * on the particle radii. */ -class container_periodic_poly : public container_periodic_base, public radius_poly { - public: - container_periodic_poly(double bx_,double bxy_,double by_,double bxz_,double byz_,double bz_, - int nx_,int ny_,int nz_,int init_mem_); - void clear(); - void put(int n,double x,double y,double z,double r); - void put(int n,double x,double y,double z,double r,int &ai,int &aj,int &ak); - void put(particle_order &vo,int n,double x,double y,double z,double r); - void import(FILE *fp=stdin); - void import(particle_order &vo,FILE *fp=stdin); - /** Imports a list of particles from an open file stream into - * the container_poly class. Entries of five numbers (Particle - * ID, x position, y position, z position, radius) are searched - * for. If the file cannot be successfully read, then the - * routine causes a fatal error. - * \param[in] filename the name of the file to open and read - * from. */ - inline void import(const char* filename) { - FILE *fp=safe_fopen(filename,"r"); - import(fp); - fclose(fp); - } - /** Imports a list of particles from an open file stream into - * the container_poly class. Entries of five numbers (Particle - * ID, x position, y position, z position, radius) are searched - * for. In addition, the order in which particles are read is - * saved into an ordering class. If the file cannot be - * successfully read, then the routine causes a fatal error. - * \param[in,out] vo the ordering class to use. - * \param[in] filename the name of the file to open and read - * from. */ - inline void import(particle_order &vo,const char* filename) { - FILE *fp=safe_fopen(filename,"r"); - import(vo,fp); - fclose(fp); - } - void compute_all_cells(); - double sum_cell_volumes(); - /** Dumps particle IDs, positions and radii to a file. - * \param[in] vl the loop class to use. - * \param[in] fp a file handle to write to. */ - template - void draw_particles(c_loop &vl,FILE *fp) { - double *pp; - if(vl.start()) do { - pp=p[vl.ijk]+4*vl.q; - fprintf(fp,"%d %g %g %g %g\n",id[vl.ijk][vl.q],*pp,pp[1],pp[2],pp[3]); - } while(vl.inc()); - } - /** Dumps all of the particle IDs, positions and radii to a - * file. - * \param[in] fp a file handle to write to. */ - inline void draw_particles(FILE *fp=stdout) { - c_loop_all_periodic vl(*this); - draw_particles(vl,fp); - } - /** Dumps all of the particle IDs, positions and radii to a - * file. - * \param[in] filename the name of the file to write to. */ - inline void draw_particles(const char *filename) { - FILE *fp=safe_fopen(filename,"w"); - draw_particles(fp); - fclose(fp); - } - /** Dumps particle positions in POV-Ray format. - * \param[in] vl the loop class to use. - * \param[in] fp a file handle to write to. */ - template - void draw_particles_pov(c_loop &vl,FILE *fp) { - double *pp; - if(vl.start()) do { - pp=p[vl.ijk]+4*vl.q; - fprintf(fp,"// id %d\nsphere{<%g,%g,%g>,%g}\n", - id[vl.ijk][vl.q],*pp,pp[1],pp[2],pp[3]); - } while(vl.inc()); - } - /** Dumps all the particle positions in POV-Ray format. - * \param[in] fp a file handle to write to. */ - inline void draw_particles_pov(FILE *fp=stdout) { - c_loop_all_periodic vl(*this); - draw_particles_pov(vl,fp); - } - /** Dumps all the particle positions in POV-Ray format. - * \param[in] filename the name of the file to write to. */ - inline void draw_particles_pov(const char *filename) { - FILE *fp(safe_fopen(filename,"w")); - draw_particles_pov(fp); - fclose(fp); - } - /** Computes Voronoi cells and saves the output in gnuplot - * format. - * \param[in] vl the loop class to use. - * \param[in] fp a file handle to write to. */ - template - void draw_cells_gnuplot(c_loop &vl,FILE *fp) { - voronoicell c(*this);double *pp; - if(vl.start()) do if(compute_cell(c,vl)) { - pp=p[vl.ijk]+ps*vl.q; - c.draw_gnuplot(*pp,pp[1],pp[2],fp); - } while(vl.inc()); - } - /** Compute all Voronoi cells and saves the output in gnuplot - * format. - * \param[in] fp a file handle to write to. */ - inline void draw_cells_gnuplot(FILE *fp=stdout) { - c_loop_all_periodic vl(*this); - draw_cells_gnuplot(vl,fp); - } - /** Compute all Voronoi cells and saves the output in gnuplot - * format. - * \param[in] filename the name of the file to write to. */ - inline void draw_cells_gnuplot(const char *filename) { - FILE *fp(safe_fopen(filename,"w")); - draw_cells_gnuplot(fp); - fclose(fp); - } - /** Computes Voronoi cells and saves the output in POV-Ray - * format. - * \param[in] vl the loop class to use. - * \param[in] fp a file handle to write to. */ - template - void draw_cells_pov(c_loop &vl,FILE *fp) { - voronoicell c(*this);double *pp; - if(vl.start()) do if(compute_cell(c,vl)) { - fprintf(fp,"// cell %d\n",id[vl.ijk][vl.q]); - pp=p[vl.ijk]+ps*vl.q; - c.draw_pov(*pp,pp[1],pp[2],fp); - } while(vl.inc()); - } - /** Computes all Voronoi cells and saves the output in POV-Ray - * format. - * \param[in] fp a file handle to write to. */ - inline void draw_cells_pov(FILE *fp=stdout) { - c_loop_all_periodic vl(*this); - draw_cells_pov(vl,fp); - } - /** Computes all Voronoi cells and saves the output in POV-Ray - * format. - * \param[in] filename the name of the file to write to. */ - inline void draw_cells_pov(const char *filename) { - FILE *fp(safe_fopen(filename,"w")); - draw_cells_pov(fp); - fclose(fp); - } - /** Computes the Voronoi cells and saves customized information - * about them. - * \param[in] vl the loop class to use. - * \param[in] format the custom output string to use. - * \param[in] fp a file handle to write to. */ - template - void print_custom(c_loop &vl,const char *format,FILE *fp) { - int ijk,q;double *pp; - if(contains_neighbor(format)) { - voronoicell_neighbor c(*this); - if(vl.start()) do if(compute_cell(c,vl)) { - ijk=vl.ijk;q=vl.q;pp=p[ijk]+ps*q; - c.output_custom(format,id[ijk][q],*pp,pp[1],pp[2],pp[3],fp); - } while(vl.inc()); - } else { - voronoicell c(*this); - if(vl.start()) do if(compute_cell(c,vl)) { - ijk=vl.ijk;q=vl.q;pp=p[ijk]+ps*q; - c.output_custom(format,id[ijk][q],*pp,pp[1],pp[2],pp[3],fp); - } while(vl.inc()); - } - } - /** Computes the Voronoi cell for a particle currently being - * referenced by a loop class. - * \param[out] c a Voronoi cell class in which to store the - * computed cell. - * \param[in] vl the loop class to use. - * \return True if the cell was computed. If the cell cannot be - * computed because it was removed entirely for some reason, - * then the routine returns false. */ - template - inline bool compute_cell(v_cell &c,c_loop &vl) { - return vc.compute_cell(c,vl.ijk,vl.q,vl.i,vl.j,vl.k); - } - /** Computes the Voronoi cell for given particle. - * \param[out] c a Voronoi cell class in which to store the - * computed cell. - * \param[in] ijk the block that the particle is within. - * \param[in] q the index of the particle within the block. - * \return True if the cell was computed. If the cell cannot be - * computed because it was removed entirely for some reason, - * then the routine returns false. */ - template - inline bool compute_cell(v_cell &c,int ijk,int q) { - int k(ijk/(nx*oy)),ijkt(ijk-(nx*oy)*k),j(ijkt/nx),i(ijkt-j*nx); - return vc.compute_cell(c,ijk,q,i,j,k); - } - /** Computes the Voronoi cell for a ghost particle at a given - * location. - * \param[out] c a Voronoi cell class in which to store the - * computed cell. - * \param[in] (x,y,z) the location of the ghost particle. - * \param[in] r the radius of the ghost particle. - * \return True if the cell was computed. If the cell cannot be - * computed, if it is removed entirely by a wall or boundary - * condition, then the routine returns false. */ - template - inline bool compute_ghost_cell(v_cell &c,double x,double y,double z,double r) { - int ijk; - put_locate_block(ijk,x,y,z); - double *pp=p[ijk]+4*co[ijk]++,tm=max_radius; - *(pp++)=x;*(pp++)=y;*(pp++)=z;*pp=r; - if(r>max_radius) max_radius=r; - bool q=compute_cell(c,ijk,co[ijk]-1); - co[ijk]--;max_radius=tm; - return q; - } - void print_custom(const char *format,FILE *fp=stdout); - void print_custom(const char *format,const char *filename); - bool find_voronoi_cell(double x,double y,double z,double &rx,double &ry,double &rz,int &pid); - private: - voro_compute vc; - friend class voro_compute; -}; - -} - -#endif diff --git a/src/third_party/voro/src/pre_container.cc b/src/third_party/voro/src/pre_container.cc deleted file mode 100644 index 9aa0eb335..000000000 --- a/src/third_party/voro/src/pre_container.cc +++ /dev/null @@ -1,236 +0,0 @@ -// Voro++, a 3D cell-based Voronoi library -// -// Author : Chris H. Rycroft (Harvard University / LBL) -// Email : chr@alum.mit.edu -// Date : August 30th 2011 - -/** \file pre_container.cc - * \brief Function implementations for the pre_container and related classes. - */ - -#include - -#include "config.hh" -#include "pre_container.hh" - -namespace voro { - -/** The class constructor sets up the geometry of container, initializing the - * minimum and maximum coordinates in each direction. It allocates an initial - * chunk into which to store particle information. - * \param[in] (ax_,bx_) the minimum and maximum x coordinates. - * \param[in] (ay_,by_) the minimum and maximum y coordinates. - * \param[in] (az_,bz_) the minimum and maximum z coordinates. - * \param[in] (xperiodic_,yperiodic_,zperiodic_ ) flags setting whether the - * container is periodic in each - * coordinate direction. - * \param[in] ps_ the number of floating point entries to store for each - * particle. */ -pre_container_base::pre_container_base(double ax_,double bx_,double ay_,double by_,double az_,double bz_, - bool xperiodic_,bool yperiodic_,bool zperiodic_,int ps_) : - ax(ax_), bx(bx_), ay(ay_), by(by_), az(az_), bz(bz_), - xperiodic(xperiodic_), yperiodic(yperiodic_), zperiodic(zperiodic_), ps(ps_), - index_sz(init_chunk_size), pre_id(new int*[index_sz]), end_id(pre_id), - pre_p(new double*[index_sz]), end_p(pre_p) { - ch_id=*end_id=new int[pre_container_chunk_size]; - l_id=end_id+index_sz;e_id=ch_id+pre_container_chunk_size; - ch_p=*end_p=new double[ps*pre_container_chunk_size]; -} - -/** The destructor frees the dynamically allocated memory. */ -pre_container_base::~pre_container_base() { - delete [] *end_p; - delete [] *end_id; - while (end_id!=pre_id) { - end_p--; - delete [] *end_p; - end_id--; - delete [] *end_id; - } - delete [] pre_p; - delete [] pre_id; -} - -/** Makes a guess at the optimal grid of blocks to use, computing in - * a way that - * \param[out] (nx,ny,nz) the number of blocks to use. */ -void pre_container_base::guess_optimal(int &nx,int &ny,int &nz) { - double dx=bx-ax,dy=by-ay,dz=bz-az; - double ilscale=pow(total_particles()/(optimal_particles*dx*dy*dz),1/3.0); - nx=int(dx*ilscale+1); - ny=int(dy*ilscale+1); - nz=int(dz*ilscale+1); -} - -/** Stores a particle ID and position, allocating a new memory chunk if - * necessary. For coordinate directions in which the container is not periodic, - * the routine checks to make sure that the particle is within the container - * bounds. If the particle is out of bounds, it is not stored. - * \param[in] n the numerical ID of the inserted particle. - * \param[in] (x,y,z) the position vector of the inserted particle. */ -void pre_container::put(int n,double x,double y,double z) { - if((xperiodic||(x>=ax&&x<=bx))&&(yperiodic||(y>=ay&&y<=by))&&(zperiodic||(z>=az&&z<=bz))) { - if(ch_id==e_id) new_chunk(); - *(ch_id++)=n; - *(ch_p++)=x;*(ch_p++)=y;*(ch_p++)=z; - } -#if VOROPP_REPORT_OUT_OF_BOUNDS ==1 - else fprintf(stderr,"Out of bounds: (x,y,z)=(%g,%g,%g)\n",x,y,z); -#endif -} - -/** Stores a particle ID and position, allocating a new memory chunk if necessary. - * \param[in] n the numerical ID of the inserted particle. - * \param[in] (x,y,z) the position vector of the inserted particle. - * \param[in] r the radius of the particle. */ -void pre_container_poly::put(int n,double x,double y,double z,double r) { - if((xperiodic||(x>=ax&&x<=bx))&&(yperiodic||(y>=ay&&y<=by))&&(zperiodic||(z>=az&&z<=bz))) { - if(ch_id==e_id) new_chunk(); - *(ch_id++)=n; - *(ch_p++)=x;*(ch_p++)=y;*(ch_p++)=z;*(ch_p++)=r; - } -#if VOROPP_REPORT_OUT_OF_BOUNDS ==1 - else fprintf(stderr,"Out of bounds: (x,y,z)=(%g,%g,%g)\n",x,y,z); -#endif -} - -/** Transfers the particles stored within the class to a container class. - * \param[in] con the container class to transfer to. */ -void pre_container::setup(container &con) { - int **c_id=pre_id,*idp,*ide,n; - double **c_p=pre_p,*pp,x,y,z; - while(c_idmax_chunk_size) - voro_fatal_error("Absolute memory limit on chunk index reached",VOROPP_MEMORY_ERROR); -#if VOROPP_VERBOSE >=2 - fprintf(stderr,"Pre-container chunk index scaled up to %d\n",index_sz); -#endif - int **n_id=new int*[index_sz],**p_id=n_id,**c_id=pre_id; - double **n_p=new double*[index_sz],**p_p=n_p,**c_p=pre_p; - while(c_id - -#include "c_loops.hh" -#include "container.hh" - -namespace voro { - -/** \brief A class for storing an arbitrary number of particles, prior to setting - * up a container geometry. - * - * The pre_container_base class can dynamically import and store an arbitrary - * number of particles. Once the particles have been read in, an appropriate - * container class can be set up with the optimal grid size, and the particles - * can be transferred. - * - * The pre_container_base class is not intended for direct use, but forms the - * base of the pre_container and pre_container_poly classes, that add routines - * depending on whether particle radii need to be tracked or not. */ -class pre_container_base { - public: - /** The minimum x coordinate of the container. */ - const double ax; - /** The maximum x coordinate of the container. */ - const double bx; - /** The minimum y coordinate of the container. */ - const double ay; - /** The maximum y coordinate of the container. */ - const double by; - /** The minimum z coordinate of the container. */ - const double az; - /** The maximum z coordinate of the container. */ - const double bz; - /** A boolean value that determines if the x coordinate in - * periodic or not. */ - const bool xperiodic; - /** A boolean value that determines if the y coordinate in - * periodic or not. */ - const bool yperiodic; - /** A boolean value that determines if the z coordinate in - * periodic or not. */ - const bool zperiodic; - void guess_optimal(int &nx,int &ny,int &nz); - pre_container_base(double ax_,double bx_,double ay_,double by_,double az_,double bz_,bool xperiodic_,bool yperiodic_,bool zperiodic_,int ps_); - ~pre_container_base(); - /** Calculates and returns the total number of particles stored - * within the class. - * \return The number of particles. */ - inline int total_particles() { - return (end_id-pre_id)*pre_container_chunk_size+(ch_id-*end_id); - } - protected: - /** The number of doubles associated with a single particle - * (three for the standard container, four when radius - * information is stored). */ - const int ps; - void new_chunk(); - void extend_chunk_index(); - /** The size of the chunk index. */ - int index_sz; - /** A pointer to the chunk index to store the integer particle - * IDs. */ - int **pre_id; - /** A pointer to the last allocated integer ID chunk. */ - int **end_id; - /** A pointer to the end of the integer ID chunk index, used to - * determine when the chunk index is full. */ - int **l_id; - /** A pointer to the next available slot on the current - * particle ID chunk. */ - int *ch_id; - /** A pointer to the end of the current integer chunk. */ - int *e_id; - /** A pointer to the chunk index to store the floating point - * information associated with particles. */ - double **pre_p; - /** A pointer to the last allocated chunk of floating point - * information. */ - double **end_p; - /** A pointer to the next available slot on the current - * floating point chunk. */ - double *ch_p; -}; - -/** \brief A class for storing an arbitrary number of particles without radius - * information, prior to setting up a container geometry. - * - * The pre_container class is an extension of the pre_container_base class for - * cases when no particle radius information is available. */ -class pre_container : public pre_container_base { - public: - /** The class constructor sets up the geometry of container, - * initializing the minimum and maximum coordinates in each - * direction. - * \param[in] (ax_,bx_) the minimum and maximum x coordinates. - * \param[in] (ay_,by_) the minimum and maximum y coordinates. - * \param[in] (az_,bz_) the minimum and maximum z coordinates. - * \param[in] (xperiodic_,yperiodic_,zperiodic_ ) flags setting whether the - * container is periodic in - * each coordinate direction. */ - pre_container(double ax_,double bx_,double ay_,double by_,double az_,double bz_, - bool xperiodic_,bool yperiodic_,bool zperiodic_) - : pre_container_base(ax_,bx_,ay_,by_,az_,bz_,xperiodic_,yperiodic_,zperiodic_,3) {}; - void put(int n,double x,double y,double z); - void import(FILE *fp=stdin); - /** Imports particles from a file. - * \param[in] filename the name of the file to read from. */ - inline void import(const char* filename) { - FILE *fp=safe_fopen(filename,"r"); - import(fp); - fclose(fp); - } - void setup(container &con); - void setup(particle_order &vo,container &con); -}; - -/** \brief A class for storing an arbitrary number of particles with radius - * information, prior to setting up a container geometry. - * - * The pre_container_poly class is an extension of the pre_container_base class - * for cases when particle radius information is available. */ -class pre_container_poly : public pre_container_base { - public: - /** The class constructor sets up the geometry of container, - * initializing the minimum and maximum coordinates in each - * direction. - * \param[in] (ax_,bx_) the minimum and maximum x coordinates. - * \param[in] (ay_,by_) the minimum and maximum y coordinates. - * \param[in] (az_,bz_) the minimum and maximum z coordinates. - * \param[in] (xperiodic_,yperiodic_,zperiodic_ ) flags setting whether the - * container is periodic in - * each coordinate direction. */ - pre_container_poly(double ax_,double bx_,double ay_,double by_,double az_,double bz_, - bool xperiodic_,bool yperiodic_,bool zperiodic_) - : pre_container_base(ax_,bx_,ay_,by_,az_,bz_,xperiodic_,yperiodic_,zperiodic_,4) {}; - void put(int n,double x,double y,double z,double r); - void import(FILE *fp=stdin); - /** Imports particles from a file. - * \param[in] filename the name of the file to read from. */ - inline void import(const char* filename) { - FILE *fp=safe_fopen(filename,"r"); - import(fp); - fclose(fp); - } - void setup(container_poly &con); - void setup(particle_order &vo,container_poly &con); -}; - -} - -#endif diff --git a/src/third_party/voro/src/rad_option.hh b/src/third_party/voro/src/rad_option.hh deleted file mode 100644 index 40dc490ed..000000000 --- a/src/third_party/voro/src/rad_option.hh +++ /dev/null @@ -1,158 +0,0 @@ -// Voro++, a 3D cell-based Voronoi library -// -// Author : Chris H. Rycroft (Harvard University / LBL) -// Email : chr@alum.mit.edu -// Date : August 30th 2011 - -/** \file rad_option.hh - * \brief Header file for the classes encapsulating functionality for the - * regular and radical Voronoi tessellations. */ - -#ifndef VOROPP_RAD_OPTION_HH -#define VOROPP_RAD_OPTION_HH - -#include - -namespace voro { - -/** \brief Class containing all of the routines that are specific to computing - * the regular Voronoi tessellation. - * - * The container and container_periodic classes are derived from this class, - * and during the Voronoi cell computation, these routines are used to create - * the regular Voronoi tessellation. */ -class radius_mono { - protected: - /** This is called prior to computing a Voronoi cell for a - * given particle to initialize any required constants. - * \param[in] ijk the block that the particle is within. - * \param[in] s the index of the particle within the block. */ - inline void r_init(int ijk,int s) {} - /** Sets a required constant to be used when carrying out a - * plane bounds check. */ - inline void r_prime(double rv) {} - /** Carries out a radius bounds check. - * \param[in] crs the radius squared to be tested. - * \param[in] mrs the current maximum distance to a Voronoi - * vertex multiplied by two. - * \return True if particles at this radius could not possibly - * cut the cell, false otherwise. */ - inline bool r_ctest(double crs,double mrs) {return crs>mrs;} - /** Scales a plane displacement during a plane bounds check. - * \param[in] lrs the plane displacement. - * \return The scaled value. */ - inline double r_cutoff(double lrs) {return lrs;} - /** Adds the maximum radius squared to a given value. - * \param[in] rs the value to consider. - * \return The value with the radius squared added. */ - inline double r_max_add(double rs) {return rs;} - /** Subtracts the radius squared of a particle from a given - * value. - * \param[in] rs the value to consider. - * \param[in] ijk the block that the particle is within. - * \param[in] q the index of the particle within the block. - * \return The value with the radius squared subtracted. */ - inline double r_current_sub(double rs,int ijk,int q) {return rs;} - /** Scales a plane displacement prior to use in the plane cutting - * algorithm. - * \param[in] rs the initial plane displacement. - * \param[in] ijk the block that the particle is within. - * \param[in] q the index of the particle within the block. - * \return The scaled plane displacement. */ - inline double r_scale(double rs,int ijk,int q) {return rs;} - /** Scales a plane displacement prior to use in the plane - * cutting algorithm, and also checks if it could possibly cut - * the cell. - * \param[in,out] rs the plane displacement to be scaled. - * \param[in] mrs the current maximum distance to a Voronoi - * vertex multiplied by two. - * \param[in] ijk the block that the particle is within. - * \param[in] q the index of the particle within the block. - * \return True if the cell could possibly cut the cell, false - * otherwise. */ - inline bool r_scale_check(double &rs,double mrs,int ijk,int q) {return rssqrt(mrs*crs);} - /** Scales a plane displacement during a plane bounds check. - * \param[in] lrs the plane displacement. - * \return The scaled value. */ - inline double r_cutoff(double lrs) {return lrs*r_val;} - /** Adds the maximum radius squared to a given value. - * \param[in] rs the value to consider. - * \return The value with the radius squared added. */ - inline double r_max_add(double rs) {return rs+max_radius*max_radius;} - /** Subtracts the radius squared of a particle from a given - * value. - * \param[in] rs the value to consider. - * \param[in] ijk the block that the particle is within. - * \param[in] q the index of the particle within the block. - * \return The value with the radius squared subtracted. */ - inline double r_current_sub(double rs,int ijk,int q) { - return rs-ppr[ijk][4*q+3]*ppr[ijk][4*q+3]; - } - /** Scales a plane displacement prior to use in the plane cutting - * algorithm. - * \param[in] rs the initial plane displacement. - * \param[in] ijk the block that the particle is within. - * \param[in] q the index of the particle within the block. - * \return The scaled plane displacement. */ - inline double r_scale(double rs,int ijk,int q) { - return rs+r_rad-ppr[ijk][4*q+3]*ppr[ijk][4*q+3]; - } - /** Scales a plane displacement prior to use in the plane - * cutting algorithm, and also checks if it could possibly cut - * the cell. - * \param[in,out] rs the plane displacement to be scaled. - * \param[in] mrs the current maximum distance to a Voronoi - * vertex multiplied by two. - * \param[in] ijk the block that the particle is within. - * \param[in] q the index of the particle within the block. - * \return True if the cell could possibly cut the cell, false - * otherwise. */ - inline bool r_scale_check(double &rs,double mrs,int ijk,int q) { - double trs=rs; - rs+=r_rad-ppr[ijk][4*q+3]*ppr[ijk][4*q+3]; - return rs -#include - -#include "unitcell.hh" -#include "cell.hh" - -namespace voro { - -/** Initializes the unit cell class for a particular non-orthogonal periodic - * geometry, corresponding to a parallelepiped with sides given by three - * vectors. The class constructs the unit Voronoi cell corresponding to this - * geometry. - * \param[in] (bx_) The x coordinate of the first unit vector. - * \param[in] (bxy_,by_) The x and y coordinates of the second unit vector. - * \param[in] (bxz_,byz_,bz_) The x, y, and z coordinates of the third unit - * vector. */ -unitcell::unitcell(double bx_,double bxy_,double by_,double bxz_,double byz_,double bz_) - : bx(bx_), bxy(bxy_), by(by_), bxz(bxz_), byz(byz_), bz(bz_), - unit_voro(max_unit_voro_shells*max_unit_voro_shells*4*(bx*bx+by*by+bz*bz)) { - int i,j,l=1; - - // Initialize the Voronoi cell to be a very large rectangular box - const double ucx=max_unit_voro_shells*bx,ucy=max_unit_voro_shells*by,ucz=max_unit_voro_shells*bz; - unit_voro.init(-ucx,ucx,-ucy,ucy,-ucz,ucz); - - // Repeatedly cut the cell by shells of periodic image particles - while(l<2*max_unit_voro_shells) { - - // Check to see if any of the planes from the current shell - // will cut the cell - if(unit_voro_intersect(l)) { - - // If they do, apply the plane cuts from the current - // shell - unit_voro_apply(l,0,0); - for(i=1;il can't cut - // a cell lying within the paraboloid - // z<=(l*l-x*x-y*y)/(2*l). It is always a tighter bound - // than the one based on computing the maximum radius - // of a Voronoi cell vertex. - max_uv_y=max_uv_z=0; - double y,z,q,*pts=unit_voro.pts,*pp=pts; - while(ppmax_uv_y) max_uv_y=y+q; - if(z+q>max_uv_z) max_uv_z=z+q; - } - max_uv_z*=0.5; - max_uv_y*=0.5; - return; - } - l++; - } - - // If the routine makes it here, then the unit cell still hasn't been - // completely bounded by the plane cuts. Give the memory error code, - // because this is mainly a case of hitting a safe limit, than any - // inherent problem. - voro_fatal_error("Periodic cell computation failed",VOROPP_MEMORY_ERROR); -} - -/** Applies a pair of opposing plane cuts from a periodic image point - * to the unit Voronoi cell. - * \param[in] (i,j,k) the index of the periodic image to consider. */ -inline void unitcell::unit_voro_apply(int i,int j,int k) { - double x=i*bx+j*bxy+k*bxz,y=j*by+k*byz,z=k*bz; - unit_voro.plane(x,y,z); - unit_voro.plane(-x,-y,-z); -} - -/** Calculates whether the unit Voronoi cell intersects a given periodic image - * of the domain. - * \param[in] (dx,dy,dz) the displacement of the periodic image. - * \param[out] vol the proportion of the unit cell volume within this image, - * only computed in the case that the two intersect. - * \return True if they intersect, false otherwise. */ -bool unitcell::intersects_image(double dx,double dy,double dz,double &vol) { - const double bxinv=1/bx,byinv=1/by,bzinv=1/bz,ivol=bxinv*byinv*bzinv; - voronoicell c; - c=unit_voro; - dx*=2;dy*=2;dz*=2; - if(!c.plane(0,0,bzinv,dz+1)) return false; - if(!c.plane(0,0,-bzinv,-dz+1)) return false; - if(!c.plane(0,byinv,-byz*byinv*bzinv,dy+1)) return false; - if(!c.plane(0,-byinv,byz*byinv*bzinv,-dy+1)) return false; - if(!c.plane(bxinv,-bxy*bxinv*byinv,(bxy*byz-by*bxz)*ivol,dx+1)) return false; - if(!c.plane(-bxinv,bxy*bxinv*byinv,(-bxy*byz+by*bxz)*ivol,-dx+1)) return false; - vol=c.volume()*ivol; - return true; -} - -/** Computes a list of periodic domain images that intersect the unit Voronoi cell. - * \param[out] vi a vector containing triplets (i,j,k) corresponding to domain - * images that intersect the unit Voronoi cell, when it is - * centered in the middle of the primary domain. - * \param[out] vd a vector containing the fraction of the Voronoi cell volume - * within each corresponding image listed in vi. */ -void unitcell::images(std::vector &vi,std::vector &vd) { - const int ms2=max_unit_voro_shells*2+1,mss=ms2*ms2*ms2; - bool *a=new bool[mss],*ac=a+max_unit_voro_shells*(1+ms2*(1+ms2)),*ap=a; - int i,j,k; - double vol; - - // Initialize mask - while(ap q; - q.push(0);q.push(0);q.push(0); - - while(!q.empty()) { - - // Read the next entry on the queue - i=q.front();q.pop(); - j=q.front();q.pop(); - k=q.front();q.pop(); - - // Check intersection of this image - if(intersects_image(i,j,k,vol)) { - - // Add this entry to the output vectors - vi.push_back(i); - vi.push_back(j); - vi.push_back(k); - vd.push_back(vol); - - // Add neighbors to the queue if they have not been - // tested - ap=ac+i+ms2*(j+ms2*k); - if(k>-max_unit_voro_shells&&*(ap-ms2*ms2)) {q.push(i);q.push(j);q.push(k-1);*(ap-ms2*ms2)=false;} - if(j>-max_unit_voro_shells&&*(ap-ms2)) {q.push(i);q.push(j-1);q.push(k);*(ap-ms2)=false;} - if(i>-max_unit_voro_shells&&*(ap-1)) {q.push(i-1);q.push(j);q.push(k);*(ap-1)=false;} - if(i,<%g,0,0>,rr}\n" - "cylinder{<%g,%g,0>,<%g,%g,0>,rr}\n",bx,bxy,by,bx+bxy,by); - fprintf(fp,"cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n" - "cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n",bxz,byz,bz,bx+bxz,byz,bz,bxy+bxz,by+byz,bz,bx+bxy+bxz,by+byz,bz); - fprintf(fp,"cylinder{<0,0,0>,<%g,%g,0>,rr}\n" - "cylinder{<%g,0,0>,<%g,%g,0>,rr}\n",bxy,by,bx,bx+bxy,by); - fprintf(fp,"cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n" - "cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n",bxz,byz,bz,bxy+bxz,by+byz,bz,bx+bxz,byz,bz,bx+bxy+bxz,by+byz,bz); - fprintf(fp,"cylinder{<0,0,0>,<%g,%g,%g>,rr}\n" - "cylinder{<%g,0,0>,<%g,%g,%g>,rr}\n",bxz,byz,bz,bx,bx+bxz,byz,bz); - fprintf(fp,"cylinder{<%g,%g,0>,<%g,%g,%g>,rr}\n" - "cylinder{<%g,%g,0>,<%g,%g,%g>,rr}\n",bxy,by,bxy+bxz,by+byz,bz,bx+bxy,by,bx+bxy+bxz,by+byz,bz); - fprintf(fp,"sphere{<0,0,0>,rr}\nsphere{<%g,0,0>,rr}\n" - "sphere{<%g,%g,0>,rr}\nsphere{<%g,%g,0>,rr}\n",bx,bxy,by,bx+bxy,by); - fprintf(fp,"sphere{<%g,%g,%g>,rr}\nsphere{<%g,%g,%g>,rr}\n" - "sphere{<%g,%g,%g>,rr}\nsphere{<%g,%g,%g>,rr}\n",bxz,byz,bz,bx+bxz,byz,bz,bxy+bxz,by+byz,bz,bx+bxy+bxz,by+byz,bz); -} - -} diff --git a/src/third_party/voro/src/unitcell.hh b/src/third_party/voro/src/unitcell.hh deleted file mode 100644 index 0907b899d..000000000 --- a/src/third_party/voro/src/unitcell.hh +++ /dev/null @@ -1,79 +0,0 @@ -// Voro++, a 3D cell-based Voronoi library -// -// Author : Chris H. Rycroft (Harvard University / LBL) -// Email : chr@alum.mit.edu -// Date : August 30th 2011 - -/** \file unitcell.hh - * \brief Header file for the unitcell class. */ - -#ifndef VOROPP_UNITCELL_HH -#define VOROPP_UNITCELL_HH - -#include - -#include "config.hh" -#include "cell.hh" - -namespace voro { - -/** \brief Class for computation of the unit Voronoi cell associated with - * a 3D non-rectangular periodic domain. */ -class unitcell { - public: - /** The x coordinate of the first vector defining the periodic - * domain. */ - const double bx; - /** The x coordinate of the second vector defining the periodic - * domain. */ - const double bxy; - /** The y coordinate of the second vector defining the periodic - * domain. */ - const double by; - /** The x coordinate of the third vector defining the periodic - * domain. */ - const double bxz; - /** The y coordinate of the third vector defining the periodic - * domain. */ - const double byz; - /** The z coordinate of the third vector defining the periodic - * domain. */ - const double bz; - /** The computed unit Voronoi cell corresponding the given - * 3D non-rectangular periodic domain geometry. */ - voronoicell unit_voro; - unitcell(double bx_,double bxy_,double by_,double bxz_,double byz_,double bz_); - /** Draws an outline of the domain in Gnuplot format. - * \param[in] filename the filename to write to. */ - inline void draw_domain_gnuplot(const char* filename) { - FILE *fp(safe_fopen(filename,"w")); - draw_domain_gnuplot(fp); - fclose(fp); - } - void draw_domain_gnuplot(FILE *fp=stdout); - /** Draws an outline of the domain in Gnuplot format. - * \param[in] filename the filename to write to. */ - inline void draw_domain_pov(const char* filename) { - FILE *fp(safe_fopen(filename,"w")); - draw_domain_pov(fp); - fclose(fp); - } - void draw_domain_pov(FILE *fp=stdout); - bool intersects_image(double dx,double dy,double dz,double &vol); - void images(std::vector &vi,std::vector &vd); - protected: - /** The maximum y-coordinate that could possibly cut the - * computed unit Voronoi cell. */ - double max_uv_y; - /** The maximum z-coordinate that could possibly cut the - * computed unit Voronoi cell. */ - double max_uv_z; - private: - inline void unit_voro_apply(int i,int j,int k); - bool unit_voro_intersect(int l); - inline bool unit_voro_test(int i,int j,int k); -}; - -} - -#endif diff --git a/src/third_party/voro/src/v_base.cc b/src/third_party/voro/src/v_base.cc deleted file mode 100644 index 143680065..000000000 --- a/src/third_party/voro/src/v_base.cc +++ /dev/null @@ -1,118 +0,0 @@ -// Voro++, a 3D cell-based Voronoi library -// -// Author : Chris H. Rycroft (Harvard University / LBL) -// Email : chr@alum.mit.edu -// Date : August 30th 2011 - -/** \file v_base.cc - * \brief Function implementations for the base Voronoi container class. */ - -#include "v_base.hh" -#include "config.hh" - -namespace voro { - -/** This function is called during container construction. The routine scans - * all of the worklists in the wl[] array. For a given worklist of blocks - * labeled \f$w_1\f$ to \f$w_n\f$, it computes a sequence \f$r_0\f$ to - * \f$r_n\f$ so that $r_i$ is the minimum distance to all the blocks - * \f$w_{j}\f$ where \f$j>i\f$ and all blocks outside the worklist. The values - * of \f$r_n\f$ is calculated first, as the minimum distance to any block in - * the shell surrounding the worklist. The \f$r_i\f$ are then computed in - * reverse order by considering the distance to \f$w_{i+1}\f$. */ -voro_base::voro_base(int nx_,int ny_,int nz_,double boxx_,double boxy_,double boxz_) : - nx(nx_), ny(ny_), nz(nz_), nxy(nx_*ny_), nxyz(nxy*nz_), boxx(boxx_), boxy(boxy_), boxz(boxz_), - xsp(1/boxx_), ysp(1/boxy_), zsp(1/boxz_), mrad(new double[wl_hgridcu*wl_seq_length]) { - const unsigned int b1=1<<21,b2=1<<22,b3=1<<24,b4=1<<25,b5=1<<27,b6=1<<28; - const double xstep=boxx/wl_fgrid,ystep=boxy/wl_fgrid,zstep=boxz/wl_fgrid; - int i,j,k,lx,ly,lz,q; - unsigned int f,*e=const_cast (wl); - double xlo,ylo,zlo,xhi,yhi,zhi,minr,*radp=mrad; - for(zlo=0,zhi=zstep,lz=0;lz>7&127)-64; - k=(f>>14&127)-64; - if((f&b2)==b2) { - compute_minimum(minr,xlo,xhi,ylo,yhi,zlo,zhi,i-1,j,k); - if((f&b1)==0) compute_minimum(minr,xlo,xhi,ylo,yhi,zlo,zhi,i+1,j,k); - } else if((f&b1)==b1) compute_minimum(minr,xlo,xhi,ylo,yhi,zlo,zhi,i+1,j,k); - if((f&b4)==b4) { - compute_minimum(minr,xlo,xhi,ylo,yhi,zlo,zhi,i,j-1,k); - if((f&b3)==0) compute_minimum(minr,xlo,xhi,ylo,yhi,zlo,zhi,i,j+1,k); - } else if((f&b3)==b3) compute_minimum(minr,xlo,xhi,ylo,yhi,zlo,zhi,i,j+1,k); - if((f&b6)==b6) { - compute_minimum(minr,xlo,xhi,ylo,yhi,zlo,zhi,i,j,k-1); - if((f&b5)==0) compute_minimum(minr,xlo,xhi,ylo,yhi,zlo,zhi,i,j,k+1); - } else if((f&b5)==b5) compute_minimum(minr,xlo,xhi,ylo,yhi,zlo,zhi,i,j,k+1); - } - q--; - while(q>0) { - radp[q]=minr; - f=e[q]; - i=(f&127)-64; - j=(f>>7&127)-64; - k=(f>>14&127)-64; - compute_minimum(minr,xlo,xhi,ylo,yhi,zlo,zhi,i,j,k); - q--; - } - *radp=minr; - e+=wl_seq_length; - radp+=wl_seq_length; - } - } - } -} - -/** Computes the minimum distance from a subregion to a given block. If this distance - * is smaller than the value of minr, then it passes - * \param[in,out] minr a pointer to the current minimum distance. If the distance - * computed in this function is smaller, then this distance is - * set to the new one. - * \param[out] (xlo,ylo,zlo) the lower coordinates of the subregion being - * considered. - * \param[out] (xhi,yhi,zhi) the upper coordinates of the subregion being - * considered. - * \param[in] (ti,tj,tk) the coordinates of the block. */ -void voro_base::compute_minimum(double &minr,double &xlo,double &xhi,double &ylo,double &yhi,double &zlo,double &zhi,int ti,int tj,int tk) { - double radsq,temp; - if(ti>0) {temp=boxx*ti-xhi;radsq=temp*temp;} - else if(ti<0) {temp=xlo-boxx*(1+ti);radsq=temp*temp;} - else radsq=0; - - if(tj>0) {temp=boxy*tj-yhi;radsq+=temp*temp;} - else if(tj<0) {temp=ylo-boxy*(1+tj);radsq+=temp*temp;} - - if(tk>0) {temp=boxz*tk-zhi;radsq+=temp*temp;} - else if(tk<0) {temp=zlo-boxz*(1+tk);radsq+=temp*temp;} - - if(radsq(format)); - - // Check to see if "%n" appears in the format sequence - while(*fmp!=0) { - if(*fmp=='%') { - fmp++; - if(*fmp=='n') return true; - else if(*fmp==0) return false; - } - fmp++; - } - - return false; -} - -#include "v_base_wl.cc" - -} diff --git a/src/third_party/voro/src/v_base.hh b/src/third_party/voro/src/v_base.hh deleted file mode 100644 index 3cd1296d0..000000000 --- a/src/third_party/voro/src/v_base.hh +++ /dev/null @@ -1,88 +0,0 @@ -// Voro++, a 3D cell-based Voronoi library -// -// Author : Chris H. Rycroft (Harvard University / LBL) -// Email : chr@alum.mit.edu -// Date : August 30th 2011 - -/** \file v_base.hh - * \brief Header file for the base Voronoi container class. */ - -#ifndef VOROPP_V_BASE_HH -#define VOROPP_V_BASE_HH - -#include "worklist.hh" - -namespace voro { - -/** \brief Class containing data structures common across all particle container classes. - * - * This class contains constants and data structures that are common across all - * particle container classes. It contains constants setting the size of the - * underlying subgrid of blocks that forms the basis of the Voronoi cell - * computations. It also constructs bound tables that are used in the Voronoi - * cell computation, and contains a number of routines that are common across - * all container classes. */ -class voro_base { - public: - /** The number of blocks in the x direction. */ - const int nx; - /** The number of blocks in the y direction. */ - const int ny; - /** The number of blocks in the z direction. */ - const int nz; - /** A constant, set to the value of nx multiplied by ny, which - * is used in the routines that step through blocks in - * sequence. */ - const int nxy; - /** A constant, set to the value of nx*ny*nz, which is used in - * the routines that step through blocks in sequence. */ - const int nxyz; - /** The size of a computational block in the x direction. */ - const double boxx; - /** The size of a computational block in the y direction. */ - const double boxy; - /** The size of a computational block in the z direction. */ - const double boxz; - /** The inverse box length in the x direction. */ - const double xsp; - /** The inverse box length in the y direction. */ - const double ysp; - /** The inverse box length in the z direction. */ - const double zsp; - /** An array to hold the minimum distances associated with the - * worklists. This array is initialized during container - * construction, by the initialize_radii() routine. */ - double *mrad; - /** The pre-computed block worklists. */ - static const unsigned int wl[wl_seq_length*wl_hgridcu]; - bool contains_neighbor(const char* format); - voro_base(int nx_,int ny_,int nz_,double boxx_,double boxy_,double boxz_); - ~voro_base() {delete [] mrad;} - protected: - /** A custom int function that returns consistent stepping - * for negative numbers, so that (-1.5, -0.5, 0.5, 1.5) maps - * to (-2,-1,0,1). - * \param[in] a the number to consider. - * \return The value of the custom int operation. */ - inline int step_int(double a) {return a<0?int(a)-1:int(a);} - /** A custom modulo function that returns consistent stepping - * for negative numbers. For example, (-2,-1,0,1,2) step_mod 2 - * is (0,1,0,1,0). - * \param[in] (a,b) the input integers. - * \return The value of a modulo b, consistent for negative - * numbers. */ - inline int step_mod(int a,int b) {return a>=0?a%b:b-1-(b-1-a)%b;} - /** A custom integer division function that returns consistent - * stepping for negative numbers. For example, (-2,-1,0,1,2) - * step_div 2 is (-1,-1,0,0,1). - * \param[in] (a,b) the input integers. - * \return The value of a div b, consistent for negative - * numbers. */ - inline int step_div(int a,int b) {return a>=0?a/b:-1+(a+1)/b;} - private: - void compute_minimum(double &minr,double &xlo,double &xhi,double &ylo,double &yhi,double &zlo,double &zhi,int ti,int tj,int tk); -}; - -} - -#endif diff --git a/src/third_party/voro/src/v_base_wl.cc b/src/third_party/voro/src/v_base_wl.cc deleted file mode 100644 index ba37f5b25..000000000 --- a/src/third_party/voro/src/v_base_wl.cc +++ /dev/null @@ -1,79 +0,0 @@ -// Voro++, a 3D cell-based Voronoi library -// -// Author : Chris H. Rycroft (Harvard University / LBL) -// Email : chr@alum.mit.edu -// Date : August 30th 2011 - -/** \file v_base_wl.cc - * \brief The table of block worklists that are used during the cell - * computation, which is part of the voro_base class. - * - * This file is automatically generated by worklist_gen.pl and it is not - * intended to be edited by hand. */ - -const unsigned int voro_base::wl[wl_seq_length*wl_hgridcu]={ - 7,0x10203f,0x101fc0,0xfe040,0xfe03f,0x101fbf,0xfdfc0,0xfdfbf,0x10fe0bf,0x11020bf,0x11020c0,0x10fe0c0,0x2fe041,0x302041,0x301fc1,0x2fdfc1,0x8105fc0,0x8106040,0x810603f,0x8105fbf,0x701fbe,0x70203e,0x6fe03e,0x6fdfbe,0x30fdf3f,0x3101f3f,0x3101f40,0x30fdf40,0x180f9fc0,0x180fa040,0x180fa03f,0x180f9fbf,0x12fe0c1,0x13020c1,0x91060c0,0x91060bf,0x8306041,0x8305fc1,0x3301f41,0x32fdf41,0x182f9fc1,0x182fa041,0x190fa0c0,0x190fa0bf,0x16fe0be,0x17020be,0x870603e,0x8705fbe,0xb105f3f,0xb105f40,0x3701f3e,0x36fdf3e,0x186f9fbe,0x186fa03e,0x1b0f9f3f,0x1b0f9f40,0x93060c1,0x192fa0c1,0x97060be,0xb305f41,0x1b2f9f41,0x196fa0be,0xb705f3e,0x1b6f9f3e, - 11,0x101fc0,0xfe040,0xfdfc0,0x10203f,0x101fbf,0xfe03f,0xfdfbf,0xfdfc1,0x101fc1,0x102041,0xfe041,0x10fe0c0,0x11020c0,0x8106040,0x8105fc0,0x8105fbf,0x810603f,0x11020bf,0x10fe0bf,0x180fa040,0x180f9fc0,0x30fdf40,0x3101f40,0x3101f3f,0x30fdf3f,0x180f9fbf,0x180fa03f,0x6fe03e,0x70203e,0x701fbe,0x6fdfbe,0x8105fc1,0x8106041,0x11020c1,0x10fe0c1,0x180fa041,0x180f9fc1,0x30fdf41,0x3101f41,0x91060c0,0x91060bf,0x190fa0c0,0x190fa0bf,0xb105f40,0xb105f3f,0x8705fbe,0x870603e,0x97020be,0x16fe0be,0x1b0f9f40,0x1b0f9f3f,0x36fdf3e,0xb701f3e,0x1b6f9fbe,0x196fa03e,0x93060c1,0xb305f41,0x192fa0c1,0x1b2f9f41,0x1b2fdfc2,0xb301fc2,0x9302042,0x192fe042, - 11,0x101fc0,0xfe040,0xfdfc0,0xfdfbf,0x101fbf,0x10203f,0xfe03f,0xfe041,0x102041,0x101fc1,0xfdfc1,0x8105fc0,0x8106040,0x11020c0,0x10fe0c0,0x10fe0bf,0x11020bf,0x810603f,0x8105fbf,0x3101f40,0x30fdf40,0x180f9fc0,0x180fa040,0x180fa03f,0x180f9fbf,0x30fdf3f,0x3101f3f,0x8105fc1,0x8106041,0x11020c1,0x10fe0c1,0x180fa041,0x180f9fc1,0x30fdf41,0x3101f41,0x701fbe,0x70203e,0x6fe03e,0x6fdfbe,0x91060c0,0x91060bf,0xb105f40,0xb105f3f,0x190fa0c0,0x190fa0bf,0x93060c1,0x1b0f9f40,0x1b0f9f3f,0xb305f41,0x192fa0c1,0x16fe0be,0x17020be,0x970603e,0x8705fbe,0xb701f3e,0x36fdf3e,0x1b2f9f41,0x1b2fdfc2,0x192fe042,0x9302042,0xb301fc2,0x1b6f9fbe,0x196fa03e, - 11,0x101fc0,0xfe040,0xfdfc0,0xfdfbf,0x101fbf,0x10203f,0xfe03f,0xfe041,0x102041,0x101fc1,0xfdfc1,0x8105fc0,0x8106040,0x11020c0,0x10fe0c0,0x10fe0bf,0x11020bf,0x810603f,0x8105fbf,0x3101f40,0x30fdf40,0x180f9fc0,0x180fa040,0x10fe0c1,0x11020c1,0x8106041,0x8105fc1,0x3101f3f,0x30fdf3f,0x180f9fbf,0x180fa03f,0x180fa041,0x180f9fc1,0x30fdf41,0x3101f41,0x91060c0,0x91060bf,0x70203e,0x701fbe,0x6fdfbe,0x6fe03e,0x190fa0c0,0xb105f40,0xb105f3f,0x93060c1,0x190fa0bf,0x192fa0c1,0x1b0f9f40,0x1b0f9f3f,0xb305f41,0xb301fc2,0x9302042,0x192fe042,0x1b2fdfc2,0x1b2f9f41,0x16fe0be,0x17020be,0x970603e,0x8705fbe,0xb701f3e,0x36fdf3e,0x1b6f9fbe,0x196fa03e, - 11,0x10203f,0xfe040,0xfe03f,0x101fc0,0x101fbf,0xfdfc0,0xfdfbf,0xfe0bf,0x1020bf,0x1020c0,0xfe0c0,0x2fe041,0x302041,0x8106040,0x810603f,0x8105fbf,0x8105fc0,0x301fc1,0x2fdfc1,0x180fa040,0x180fa03f,0x6fe03e,0x70203e,0x701fbe,0x6fdfbe,0x180f9fbf,0x180f9fc0,0x30fdf40,0x3101f40,0x3101f3f,0x30fdf3f,0x81060bf,0x81060c0,0x3020c1,0x2fe0c1,0x180fa0c0,0x180fa0bf,0x6fe0be,0x7020be,0x8306041,0x8305fc1,0x182fa041,0x182f9fc1,0x870603e,0x8705fbe,0xb105f3f,0xb105f40,0xb301f41,0x32fdf41,0x186fa03e,0x186f9fbe,0x36fdf3e,0xb701f3e,0x1b6f9f3f,0x1b2f9f40,0x93060c1,0x97060be,0x192fa0c1,0x196fa0be,0x196fe13f,0x970213f,0x9302140,0x192fe140, - 9,0xfe040,0x10203f,0x101fc0,0xfdfc0,0xfe03f,0xfdfbf,0x101fbf,0x102041,0xfe041,0x10fe0c0,0x11020c0,0x11020bf,0x10fe0bf,0xfdfc1,0x101fc1,0x8106040,0x810603f,0x8105fc0,0x8105fbf,0x180fa040,0x180f9fc0,0x180fa03f,0x180f9fbf,0x10fe0c1,0x11020c1,0x70203e,0x6fe03e,0x6fdfbe,0x701fbe,0x3101f3f,0x3101f40,0x30fdf40,0x30fdf3f,0x8106041,0x91060c0,0x91060bf,0x8305fc1,0x180fa041,0x190fa0c0,0x190fa0bf,0x180f9fc1,0x30fdf41,0x3301f41,0x17020be,0x16fe0be,0x93060c1,0x870603e,0x8705fbe,0xb105f3f,0xb105f40,0x192fa0c1,0x1b0f9f40,0x1b0f9f3f,0x186f9fbe,0x196fa03e,0x1b6fdf3e,0xb701f3e,0x97060be,0xb305f41,0x1b2f9f41,0x1b2fdfc2,0x192fe042,0xa302042, - 11,0xfe040,0x101fc0,0xfdfc0,0xfe03f,0x10203f,0x101fbf,0xfdfbf,0xfe041,0x102041,0x101fc1,0xfdfc1,0x10fe0c0,0x11020c0,0x11020bf,0x10fe0bf,0x8106040,0x8105fc0,0x810603f,0x8105fbf,0x11020c1,0x10fe0c1,0x180fa040,0x180f9fc0,0x180fa03f,0x180f9fbf,0x30fdf40,0x3101f40,0x8106041,0x8105fc1,0x3101f3f,0x30fdf3f,0x91060c0,0x91060bf,0x180fa041,0x180f9fc1,0x6fe03e,0x70203e,0x701fbe,0x6fdfbe,0x190fa0c0,0x190fa0bf,0x30fdf41,0x3101f41,0x93060c1,0x192fa0c1,0xb105f40,0xb105f3f,0x17020be,0x16fe0be,0x970603e,0x8705fbe,0x1b0f9f40,0x1b0f9f3f,0x186f9fbe,0x196fa03e,0xb305f41,0xb301fc2,0x9302042,0x192fe042,0x1b2fdfc2,0x1b2f9f41,0x1b6fdf3e,0xb701f3e, - 11,0xfe040,0x101fc0,0xfdfc0,0xfe03f,0x10203f,0x101fbf,0xfdfbf,0xfe041,0x102041,0x101fc1,0xfdfc1,0x10fe0c0,0x11020c0,0x11020bf,0x10fe0bf,0x8106040,0x8105fc0,0x11020c1,0x10fe0c1,0x810603f,0x8105fbf,0x180fa040,0x180f9fc0,0x8106041,0x8105fc1,0x3101f40,0x180fa03f,0x180f9fbf,0x30fdf40,0x180fa041,0x180f9fc1,0x91060c0,0x3101f3f,0x30fdf3f,0x30fdf41,0x3101f41,0x91060bf,0x190fa0c0,0x91060c1,0x190fa0bf,0x186fe03e,0x70203e,0x3701fbe,0x1b6fdfbe,0x190fa0c1,0x182fe042,0xb105f40,0xb105f3f,0x302042,0x3301fc2,0x1b2fdfc2,0x1b0f9f40,0x1b6f9f3f,0xb105f41,0x17020be,0x196fe0be,0x970603e,0xb705fbe,0x1b2f9f41,0x192fe0c2,0x13020c2,0x9306042,0xb305fc2, - 11,0x10203f,0xfe040,0xfe03f,0xfdfbf,0x101fbf,0x101fc0,0xfdfc0,0xfe0c0,0x1020c0,0x1020bf,0xfe0bf,0x810603f,0x8106040,0x302041,0x2fe041,0x2fdfc1,0x301fc1,0x8105fc0,0x8105fbf,0x70203e,0x6fe03e,0x180fa03f,0x180fa040,0x180f9fc0,0x180f9fbf,0x6fdfbe,0x701fbe,0x81060bf,0x81060c0,0x3020c1,0x2fe0c1,0x180fa0c0,0x180fa0bf,0x6fe0be,0x7020be,0x3101f3f,0x3101f40,0x30fdf40,0x30fdf3f,0x8306041,0x8305fc1,0x870603e,0x8705fbe,0x182fa041,0x182f9fc1,0x93060c1,0x186fa03e,0x186f9fbe,0x97060be,0x192fa0c1,0x32fdf41,0x3301f41,0xb305f40,0xb105f3f,0xb701f3e,0x36fdf3e,0x196fa0be,0x196fe13f,0x192fe140,0x9302140,0x970213f,0x1b6f9f3f,0x1b2f9f40, - 11,0xfe040,0x10203f,0xfe03f,0xfdfc0,0x101fc0,0x101fbf,0xfdfbf,0xfe0c0,0x1020c0,0x1020bf,0xfe0bf,0x2fe041,0x302041,0x301fc1,0x2fdfc1,0x8106040,0x810603f,0x8105fc0,0x8105fbf,0x3020c1,0x2fe0c1,0x180fa040,0x180fa03f,0x180f9fc0,0x180f9fbf,0x6fe03e,0x70203e,0x81060c0,0x81060bf,0x701fbe,0x6fdfbe,0x8306041,0x8305fc1,0x180fa0c0,0x180fa0bf,0x30fdf40,0x3101f40,0x3101f3f,0x30fdf3f,0x182fa041,0x182f9fc1,0x6fe0be,0x7020be,0x93060c1,0x192fa0c1,0x870603e,0x8705fbe,0x3301f41,0x32fdf41,0xb305f40,0xb105f3f,0x186fa03e,0x186f9fbe,0x1b0f9f3f,0x1b2f9f40,0x97060be,0x970213f,0x9302140,0x192fe140,0x196fe13f,0x196fa0be,0x1b6fdf3e,0xb701f3e, - 15,0xfe040,0xfe03f,0x10203f,0x101fc0,0xfdfc0,0xfdfbf,0x101fbf,0x102041,0xfe041,0xfe0c0,0x1020c0,0x1020bf,0xfe0bf,0xfdfc1,0x101fc1,0x8106040,0x1020c1,0xfe0c1,0x810603f,0x8105fc0,0x8105fbf,0x180fa040,0x180fa03f,0x180f9fc0,0x180f9fbf,0x8106041,0x81060c0,0x81060bf,0x8105fc1,0x180fa041,0x180fa0c0,0x180fa0bf,0x6fe03e,0x70203e,0x3101f40,0x30fdf40,0x180f9fc1,0x30fdf3f,0x3101f3f,0x3701fbe,0x36fdfbe,0x93060c1,0x192fa0c1,0x6fe0be,0x7020be,0x3101f41,0x30fdf41,0xb305f40,0xb105f3f,0x970603e,0xb705fbe,0x196fa03e,0x186f9fbe,0x1b2f9f40,0x1b6f9f3f,0x192fe042,0x9302042,0xb301fc2,0x1b2fdfc2,0x192fe140,0x9302140,0x970213f,0x196fe13f, - 15,0xfe040,0xfdfc0,0x101fc0,0x10203f,0xfe03f,0xfdfbf,0x101fbf,0x102041,0xfe041,0xfdfc1,0x101fc1,0x1020c0,0xfe0c0,0xfe0bf,0x1020bf,0x1020c1,0xfe0c1,0x8106040,0x8105fc0,0x810603f,0x8105fbf,0x180fa040,0x180f9fc0,0x8106041,0x8105fc1,0x81060c0,0x180fa03f,0x180f9fbf,0x180fa041,0x180f9fc1,0x180fa0c0,0x81060bf,0x91060c1,0x3101f40,0x30fdf40,0x30fdf3f,0x180fa0bf,0x190fa0c1,0x3101f3f,0x3101f41,0x30fdf41,0x186fe03e,0x70203e,0x3701fbe,0x1b6fdfbe,0x186fe0be,0x7020be,0x8302042,0x182fe042,0x1b2fdfc2,0xb301fc2,0xb105f40,0xb705f3f,0xb305f41,0x1b2f9f40,0x1b6f9f3f,0x192fe140,0x9302140,0x93020c2,0x192fe0c2,0x196fe13f,0x970213f,0xa70603e, - 11,0x10203f,0xfe040,0xfe03f,0xfdfbf,0x101fbf,0x101fc0,0xfdfc0,0xfe0c0,0x1020c0,0x1020bf,0xfe0bf,0x810603f,0x8106040,0x302041,0x2fe041,0x2fdfc1,0x301fc1,0x8105fc0,0x8105fbf,0x70203e,0x6fe03e,0x180fa03f,0x180fa040,0x2fe0c1,0x3020c1,0x81060c0,0x81060bf,0x701fbe,0x6fdfbe,0x180f9fbf,0x180f9fc0,0x180fa0c0,0x180fa0bf,0x6fe0be,0x7020be,0x8306041,0x8305fc1,0x3101f40,0x3101f3f,0x30fdf3f,0x30fdf40,0x182fa041,0x870603e,0x8705fbe,0x93060c1,0x182f9fc1,0x192fa0c1,0x186fa03e,0x186f9fbe,0x97060be,0x970213f,0x9302140,0x192fe140,0x196fe13f,0x196fa0be,0x32fdf41,0x3301f41,0xb305f40,0xb105f3f,0xb701f3e,0x36fdf3e,0x1b6f9f3f,0x1b2f9f40, - 11,0xfe040,0x10203f,0xfe03f,0xfdfc0,0x101fc0,0x101fbf,0xfdfbf,0xfe0c0,0x1020c0,0x1020bf,0xfe0bf,0x2fe041,0x302041,0x301fc1,0x2fdfc1,0x8106040,0x810603f,0x3020c1,0x2fe0c1,0x8105fc0,0x8105fbf,0x180fa040,0x180fa03f,0x81060c0,0x81060bf,0x70203e,0x180f9fc0,0x180f9fbf,0x6fe03e,0x180fa0c0,0x180fa0bf,0x8306041,0x701fbe,0x6fdfbe,0x6fe0be,0x7020be,0x8305fc1,0x182fa041,0x83060c1,0x182f9fc1,0x1b0fdf40,0x3101f40,0x3701f3f,0x1b6fdf3f,0x182fa0c1,0x190fe140,0x870603e,0x8705fbe,0x1102140,0x170213f,0x196fe13f,0x186fa03e,0x1b6f9fbe,0x87060be,0x3301f41,0x1b2fdf41,0xb305f40,0xb705f3f,0x196fa0be,0x192fe141,0x1302141,0x9306140,0x970613f, - 15,0xfe040,0xfe03f,0x10203f,0x101fc0,0xfdfc0,0xfdfbf,0x101fbf,0x1020c0,0xfe0c0,0xfe0bf,0x1020bf,0x102041,0xfe041,0xfdfc1,0x101fc1,0x1020c1,0xfe0c1,0x8106040,0x810603f,0x8105fc0,0x8105fbf,0x180fa040,0x180fa03f,0x81060c0,0x81060bf,0x8106041,0x180f9fc0,0x180f9fbf,0x180fa0c0,0x180fa0bf,0x180fa041,0x8105fc1,0x83060c1,0x70203e,0x6fe03e,0x6fdfbe,0x180f9fc1,0x182fa0c1,0x701fbe,0x7020be,0x6fe0be,0x1b0fdf40,0x3101f40,0x3701f3f,0x1b6fdf3f,0x1b0fdf41,0x3101f41,0x9102140,0x190fe140,0x196fe13f,0x970213f,0x870603e,0xb705fbe,0x97060be,0x196fa03e,0x1b6f9fbe,0x192fe042,0x9302042,0x9302141,0x192fe141,0x1b2fdfc2,0xb301fc2,0xb505f40, - 17,0xfe040,0xfe03f,0x10203f,0x101fc0,0xfdfc0,0xfe041,0x102041,0x1020c0,0xfe0c0,0xfdfbf,0x101fbf,0x1020bf,0xfe0bf,0xfdfc1,0x101fc1,0x1020c1,0xfe0c1,0x8106040,0x810603f,0x8105fc0,0x8106041,0x81060c0,0x180fa040,0x180fa03f,0x180f9fc0,0x8105fbf,0x81060bf,0x8105fc1,0x180fa041,0x180fa0c0,0x180f9fbf,0x81060c1,0x180fa0bf,0x180f9fc1,0x180fa0c1,0x186fe03e,0x70203e,0x3101f40,0x1b0fdf40,0x1b0fdf3f,0x3101f3f,0x3701fbe,0x1b6fdfbe,0x186fe0be,0x7020be,0x9102140,0x190fe140,0x182fe042,0x8302042,0x3101f41,0x1b0fdf41,0x1b2fdfc2,0xb301fc2,0x83020c2,0x182fe0c2,0x196fe13f,0x970213f,0x9302141,0x192fe141,0x970603e,0xb305f40,0xb105f3f,0xb705fbe, - 11,0x10203f,0x101fc0,0x101fbf,0xfe040,0xfe03f,0xfdfc0,0xfdfbf,0x105fbf,0x10603f,0x106040,0x105fc0,0x301fc1,0x302041,0x11020c0,0x11020bf,0x10fe0bf,0x10fe0c0,0x2fe041,0x2fdfc1,0x3101f40,0x3101f3f,0x701fbe,0x70203e,0x6fe03e,0x6fdfbe,0x30fdf3f,0x30fdf40,0x180f9fc0,0x180fa040,0x180fa03f,0x180f9fbf,0x11060bf,0x11060c0,0x306041,0x305fc1,0x3105f40,0x3105f3f,0x705fbe,0x70603e,0x13020c1,0x12fe0c1,0x3301f41,0x32fdf41,0x17020be,0x16fe0be,0x190fa0bf,0x190fa0c0,0x192fa041,0x182f9fc1,0x3701f3e,0x36fdf3e,0x186f9fbe,0x196fa03e,0x1b6f9f3f,0x1b2f9f40,0x93060c1,0x97060be,0xb305f41,0xb705f3e,0xb709fbf,0x970a03f,0x930a040,0xb309fc0, - 9,0x101fc0,0x10203f,0xfe040,0xfdfc0,0x101fbf,0xfdfbf,0xfe03f,0x102041,0x101fc1,0x8105fc0,0x8106040,0x810603f,0x8105fbf,0xfdfc1,0xfe041,0x11020c0,0x11020bf,0x10fe0c0,0x10fe0bf,0x3101f40,0x30fdf40,0x3101f3f,0x30fdf3f,0x8105fc1,0x8106041,0x70203e,0x701fbe,0x6fdfbe,0x6fe03e,0x180fa03f,0x180fa040,0x180f9fc0,0x180f9fbf,0x11020c1,0x91060c0,0x91060bf,0x12fe0c1,0x3101f41,0xb105f40,0xb105f3f,0x30fdf41,0x180f9fc1,0x182fa041,0x870603e,0x8705fbe,0x93060c1,0x17020be,0x16fe0be,0x190fa0bf,0x190fa0c0,0xb305f41,0x1b0f9f40,0x1b0f9f3f,0x36fdf3e,0xb701f3e,0x1b6f9fbe,0x196fa03e,0x97060be,0x192fa0c1,0x1b2f9f41,0x1b2fdfc2,0xb301fc2,0x11302042, - 11,0x101fc0,0xfe040,0xfdfc0,0x101fbf,0x10203f,0xfe03f,0xfdfbf,0x101fc1,0x102041,0xfe041,0xfdfc1,0x8105fc0,0x8106040,0x810603f,0x8105fbf,0x11020c0,0x10fe0c0,0x11020bf,0x10fe0bf,0x8106041,0x8105fc1,0x3101f40,0x30fdf40,0x3101f3f,0x30fdf3f,0x180f9fc0,0x180fa040,0x11020c1,0x10fe0c1,0x180fa03f,0x180f9fbf,0x91060c0,0x91060bf,0x3101f41,0x30fdf41,0x701fbe,0x70203e,0x6fe03e,0x6fdfbe,0xb105f40,0xb105f3f,0x180f9fc1,0x180fa041,0x93060c1,0xb305f41,0x190fa0c0,0x190fa0bf,0x870603e,0x8705fbe,0x97020be,0x16fe0be,0x1b0f9f40,0x1b0f9f3f,0x36fdf3e,0xb701f3e,0x192fa0c1,0x192fe042,0x9302042,0xb301fc2,0x1b2fdfc2,0x1b2f9f41,0x1b6f9fbe,0x196fa03e, - 11,0x101fc0,0xfe040,0xfdfc0,0x101fbf,0x10203f,0xfe03f,0xfdfbf,0x101fc1,0x102041,0xfe041,0xfdfc1,0x8105fc0,0x8106040,0x810603f,0x8105fbf,0x11020c0,0x10fe0c0,0x8106041,0x8105fc1,0x11020bf,0x10fe0bf,0x3101f40,0x30fdf40,0x11020c1,0x10fe0c1,0x180fa040,0x3101f3f,0x30fdf3f,0x180f9fc0,0x3101f41,0x30fdf41,0x91060c0,0x180fa03f,0x180f9fbf,0x180f9fc1,0x180fa041,0x91060bf,0xb105f40,0x91060c1,0xb105f3f,0x3701fbe,0x70203e,0x186fe03e,0x1b6fdfbe,0xb105f41,0x3301fc2,0x190fa0c0,0x190fa0bf,0x302042,0x182fe042,0x1b2fdfc2,0x1b0f9f40,0x1b6f9f3f,0x190fa0c1,0x870603e,0xb705fbe,0x97020be,0x196fe0be,0x1b2f9f41,0xb305fc2,0x8306042,0x93020c2,0x192fe0c2, - 9,0x10203f,0x101fc0,0xfe040,0xfe03f,0x101fbf,0xfdfbf,0xfdfc0,0x1020c0,0x1020bf,0x810603f,0x8106040,0x8105fc0,0x8105fbf,0xfe0bf,0xfe0c0,0x302041,0x301fc1,0x2fe041,0x2fdfc1,0x70203e,0x6fe03e,0x701fbe,0x6fdfbe,0x81060bf,0x81060c0,0x3101f40,0x3101f3f,0x30fdf3f,0x30fdf40,0x180f9fc0,0x180fa040,0x180fa03f,0x180f9fbf,0x3020c1,0x8306041,0x8305fc1,0x12fe0c1,0x7020be,0x870603e,0x8705fbe,0x6fe0be,0x180fa0bf,0x190fa0c0,0xb105f40,0xb105f3f,0x93060c1,0x3301f41,0x32fdf41,0x182f9fc1,0x182fa041,0x97060be,0x186fa03e,0x186f9fbe,0x36fdf3e,0xb701f3e,0x1b6f9f3f,0x1b2f9f40,0xb305f41,0x192fa0c1,0x196fa0be,0x196fe13f,0x970213f,0x11302140, - 7,0x10203f,0x101fc0,0xfe040,0xfe03f,0x101fbf,0xfdfc0,0xfdfbf,0x302041,0x1020c0,0x8106040,0x810603f,0x1020bf,0xfe0c0,0x2fe041,0x301fc1,0x8105fc0,0x8105fbf,0xfe0bf,0x2fdfc1,0x3020c1,0x8306041,0x81060c0,0x81060bf,0x2fe0c1,0x8305fc1,0x3101f40,0x3101f3f,0x701fbe,0x70203e,0x6fe03e,0x180fa03f,0x180fa040,0x180f9fc0,0x30fdf40,0x30fdf3f,0x6fdfbe,0x180f9fbf,0x180fa0c0,0x182fa041,0x93060c1,0x870603e,0x7020be,0x6fe0be,0x180fa0bf,0x182f9fc1,0x32fdf41,0x3301f41,0xb105f40,0xb105f3f,0x8705fbe,0x97060be,0x192fa0c1,0xb305f41,0xb701f3e,0x36fdf3e,0x1b0f9f3f,0x1b2f9f40,0x1b6f9fbe,0x196fa03e,0x196fe13f,0x9302140,0x970213f,0x192fe140, - 11,0x101fc0,0xfe040,0xfdfc0,0x10203f,0x101fbf,0xfe03f,0xfdfbf,0x102041,0x101fc1,0xfe041,0xfdfc1,0x11020c0,0x8106040,0x8105fc0,0x10fe0c0,0x11020bf,0x810603f,0x8105fbf,0x10fe0bf,0x11020c1,0x8106041,0x8105fc1,0x10fe0c1,0x91060c0,0x91060bf,0x3101f40,0x30fdf40,0x180f9fc0,0x180fa040,0x180fa03f,0x180f9fbf,0x30fdf3f,0x3101f3f,0x701fbe,0x70203e,0x6fe03e,0x6fdfbe,0x93060c1,0x3101f41,0x30fdf41,0x180f9fc1,0x180fa041,0x190fa0c0,0x190fa0bf,0xb105f40,0xb105f3f,0x8705fbe,0x870603e,0x17020be,0x16fe0be,0x192fa0c1,0xb305f41,0xb301fc2,0x9302042,0x192fe042,0x1b2fdfc2,0x1b2f9f40,0x1b0f9f3f,0x186f9fbe,0x196fa03e,0x97060be,0xb701f3e,0x1b6fdf3e, - 11,0x101fc0,0xfe040,0xfdfc0,0x10203f,0x101fbf,0xfe03f,0xfdfbf,0x102041,0x101fc1,0xfe041,0xfdfc1,0x11020c0,0x8106040,0x8105fc0,0x10fe0c0,0x11020bf,0x810603f,0x8105fbf,0x10fe0bf,0x11020c1,0x8106041,0x8105fc1,0x10fe0c1,0x91060c0,0x91060bf,0x3101f40,0x30fdf40,0x180f9fc0,0x180fa040,0x180fa03f,0x180f9fbf,0x30fdf3f,0x3101f3f,0x3101f41,0x91060c1,0x180fa041,0x180f9fc1,0x30fdf41,0xb105f40,0x70203e,0x3701fbe,0x186fe03e,0x1b6fdfbe,0x190fa0c0,0x190fa0bf,0x190fa0c1,0xb105f3f,0xb105f41,0x3301fc2,0x302042,0x182fe042,0x1b2fdfc2,0x1b0f9f40,0x17020be,0x970603e,0xb705fbe,0x196fe0be,0x1b6f9f3f,0x1b2f9f41,0x13020c2,0x9306042,0xb305fc2,0x192fe0c2, - 11,0x10203f,0xfe040,0xfe03f,0x101fbf,0x101fc0,0xfdfc0,0xfdfbf,0x1020bf,0x1020c0,0xfe0c0,0xfe0bf,0x810603f,0x8106040,0x8105fc0,0x8105fbf,0x302041,0x2fe041,0x301fc1,0x2fdfc1,0x81060c0,0x81060bf,0x70203e,0x6fe03e,0x701fbe,0x6fdfbe,0x180fa03f,0x180fa040,0x3020c1,0x2fe0c1,0x180f9fc0,0x180f9fbf,0x8306041,0x8305fc1,0x7020be,0x6fe0be,0x3101f3f,0x3101f40,0x30fdf40,0x30fdf3f,0x870603e,0x8705fbe,0x180fa0bf,0x180fa0c0,0x93060c1,0x97060be,0x182fa041,0x182f9fc1,0xb105f40,0xb105f3f,0xb301f41,0x32fdf41,0x186fa03e,0x186f9fbe,0x36fdf3e,0xb701f3e,0x192fa0c1,0x192fe140,0x9302140,0x970213f,0x196fe13f,0x196fa0be,0x1b6f9f3f,0x1b2f9f40, - 11,0x10203f,0xfe040,0xfe03f,0x101fc0,0x101fbf,0xfdfc0,0xfdfbf,0x1020c0,0x1020bf,0xfe0c0,0xfe0bf,0x302041,0x8106040,0x810603f,0x2fe041,0x301fc1,0x8105fc0,0x8105fbf,0x2fdfc1,0x3020c1,0x81060c0,0x81060bf,0x2fe0c1,0x8306041,0x8305fc1,0x70203e,0x6fe03e,0x180fa03f,0x180fa040,0x180f9fc0,0x180f9fbf,0x6fdfbe,0x701fbe,0x3101f3f,0x3101f40,0x30fdf40,0x30fdf3f,0x93060c1,0x7020be,0x6fe0be,0x180fa0bf,0x180fa0c0,0x182fa041,0x182f9fc1,0x870603e,0x8705fbe,0xb105f3f,0xb105f40,0x3301f41,0x32fdf41,0x192fa0c1,0x97060be,0x970213f,0x9302140,0x192fe140,0x196fe13f,0x196fa03e,0x186f9fbe,0x1b0f9f3f,0x1b2f9f40,0xb305f41,0xb701f3e,0x1b6fdf3e, - 15,0xfe040,0x10203f,0x101fc0,0xfdfc0,0xfe03f,0x101fbf,0xfdfbf,0x102041,0x1020c0,0xfe0c0,0xfe041,0x101fc1,0xfdfc1,0x1020bf,0xfe0bf,0x8106040,0x810603f,0x8105fc0,0x8105fbf,0x1020c1,0xfe0c1,0x8106041,0x81060c0,0x81060bf,0x8105fc1,0x180fa040,0x180f9fc0,0x180fa03f,0x180f9fbf,0x93060c1,0x70203e,0x6fe03e,0x3101f40,0x1b0fdf40,0x3101f3f,0x3701fbe,0x6fdfbe,0x1b6fdf3f,0x180fa041,0x180fa0c0,0x180fa0bf,0x180f9fc1,0x1b0fdf41,0x3101f41,0x7020be,0x6fe0be,0x870603e,0x8705fbe,0xb105f40,0xb705f3f,0x192fa0c1,0x192fe140,0x9302140,0x970213f,0x97060be,0x9302042,0x192fe042,0xb301fc2,0xb305f41,0x1b2fdfc2,0x196fe13f,0x196fa03e,0x1b6f9fbe, - 14,0xfe040,0x101fc0,0xfdfc0,0x10203f,0xfe03f,0x101fbf,0xfdfbf,0x102041,0xfe041,0x101fc1,0xfdfc1,0x1020c0,0xfe0c0,0x1020bf,0x8106040,0xfe0bf,0x8105fc0,0x1020c1,0xfe0c1,0x810603f,0x8105fbf,0x8106041,0x8105fc1,0x81060c0,0x81060bf,0x91060c1,0x180fa040,0x180f9fc0,0x180fa03f,0x180f9fbf,0x180fa041,0x180f9fc1,0x1b0fdf40,0x3101f40,0x3101f3f,0x1b0fdf3f,0x180fa0c0,0x190fa0bf,0x186fe03e,0x70203e,0x3701fbe,0x3101f41,0x1b0fdf41,0x1b6fdfbe,0x190fa0c1,0x182fe042,0x302042,0xb105f40,0xb105f3f,0x3301fc2,0x1b2fdfc2,0x7020be,0x196fe0be,0x970603e,0xb705fbe,0xb105f41,0x9302140,0x192fe140,0x13020c2,0x192fe0c2,0x9306042,0xb305fc2,0x1170213f, - 11,0x10203f,0xfe040,0xfe03f,0x101fbf,0x101fc0,0xfdfc0,0xfdfbf,0x1020bf,0x1020c0,0xfe0c0,0xfe0bf,0x810603f,0x8106040,0x8105fc0,0x8105fbf,0x302041,0x2fe041,0x81060c0,0x81060bf,0x301fc1,0x2fdfc1,0x70203e,0x6fe03e,0x3020c1,0x2fe0c1,0x180fa040,0x701fbe,0x6fdfbe,0x180fa03f,0x7020be,0x6fe0be,0x8306041,0x180f9fc0,0x180f9fbf,0x180fa0bf,0x180fa0c0,0x8305fc1,0x870603e,0x83060c1,0x8705fbe,0x3701f3f,0x3101f40,0x1b0fdf40,0x1b6fdf3f,0x87060be,0x170213f,0x182fa041,0x182f9fc1,0x1102140,0x190fe140,0x196fe13f,0x186fa03e,0x1b6f9fbe,0x182fa0c1,0xb105f40,0xb705f3f,0xb301f41,0x1b2fdf41,0x196fa0be,0x970613f,0x9106140,0x9302141,0x192fe141, - 11,0x10203f,0xfe040,0xfe03f,0x101fc0,0x101fbf,0xfdfc0,0xfdfbf,0x1020c0,0x1020bf,0xfe0c0,0xfe0bf,0x302041,0x8106040,0x810603f,0x2fe041,0x301fc1,0x8105fc0,0x8105fbf,0x2fdfc1,0x3020c1,0x81060c0,0x81060bf,0x2fe0c1,0x8306041,0x8305fc1,0x70203e,0x6fe03e,0x180fa03f,0x180fa040,0x180f9fc0,0x180f9fbf,0x6fdfbe,0x701fbe,0x7020be,0x83060c1,0x180fa0c0,0x180fa0bf,0x6fe0be,0x870603e,0x3101f40,0x3701f3f,0x1b0fdf40,0x1b6fdf3f,0x182fa041,0x182f9fc1,0x182fa0c1,0x8705fbe,0x87060be,0x170213f,0x1102140,0x190fe140,0x196fe13f,0x186fa03e,0x3301f41,0xb305f40,0xb705f3f,0x1b2fdf41,0x1b6f9fbe,0x196fa0be,0x1302141,0x9306140,0x970613f,0x192fe141, - 14,0xfe040,0x10203f,0xfe03f,0x101fc0,0xfdfc0,0x101fbf,0xfdfbf,0x1020c0,0xfe0c0,0x1020bf,0xfe0bf,0x102041,0xfe041,0x101fc1,0x8106040,0xfdfc1,0x810603f,0x1020c1,0xfe0c1,0x8105fc0,0x8105fbf,0x81060c0,0x81060bf,0x8106041,0x8105fc1,0x83060c1,0x180fa040,0x180fa03f,0x180f9fc0,0x180f9fbf,0x180fa0c0,0x180fa0bf,0x186fe03e,0x70203e,0x701fbe,0x186fdfbe,0x180fa041,0x182f9fc1,0x1b0fdf40,0x3101f40,0x3701f3f,0x7020be,0x186fe0be,0x1b6fdf3f,0x182fa0c1,0x190fe140,0x1102140,0x870603e,0x8705fbe,0x170213f,0x196fe13f,0x3101f41,0x1b2fdf41,0xb305f40,0xb705f3f,0x87060be,0x9302042,0x192fe042,0x1302141,0x192fe141,0x9306140,0x970613f,0x13301fc2, - 17,0xfe040,0x10203f,0x101fc0,0xfdfc0,0xfe03f,0x1020c0,0x102041,0xfe041,0xfe0c0,0x101fbf,0xfdfbf,0x1020bf,0xfe0bf,0x101fc1,0xfdfc1,0x1020c1,0xfe0c1,0x8106040,0x810603f,0x8105fc0,0x8106041,0x81060c0,0x8105fbf,0x81060bf,0x8105fc1,0x81060c1,0x180fa040,0x180f9fc0,0x180fa03f,0x180fa0c0,0x180fa041,0x180f9fbf,0x180fa0bf,0x180f9fc1,0x180fa0c1,0x70203e,0x186fe03e,0x3101f40,0x1b0fdf40,0x3101f3f,0x3701fbe,0x186fdfbe,0x1b6fdf3f,0x3101f41,0x1b0fdf41,0x8302042,0x182fe042,0x9102140,0x7020be,0x186fe0be,0x190fe140,0x970213f,0x196fe13f,0x9102141,0xb301fc2,0x1b2fdfc2,0x93020c2,0x182fe0c2,0x192fe141,0x970603e,0xb305f40,0xb105f3f,0xb705fbe, - 11,0x10203f,0x101fc0,0x101fbf,0xfdfbf,0xfe03f,0xfe040,0xfdfc0,0x105fc0,0x106040,0x10603f,0x105fbf,0x11020bf,0x11020c0,0x302041,0x301fc1,0x2fdfc1,0x2fe041,0x10fe0c0,0x10fe0bf,0x70203e,0x701fbe,0x3101f3f,0x3101f40,0x30fdf40,0x30fdf3f,0x6fdfbe,0x6fe03e,0x11060bf,0x11060c0,0x306041,0x305fc1,0x3105f40,0x3105f3f,0x705fbe,0x70603e,0x180fa03f,0x180fa040,0x180f9fc0,0x180f9fbf,0x13020c1,0x12fe0c1,0x17020be,0x16fe0be,0x3301f41,0x32fdf41,0x93060c1,0x3701f3e,0x36fdf3e,0x97060be,0xb305f41,0x182f9fc1,0x182fa041,0x192fa0c0,0x190fa0bf,0x196fa03e,0x186f9fbe,0xb705f3e,0xb709fbf,0xb309fc0,0x930a040,0x970a03f,0x1b6f9f3f,0x1b2f9f40, - 11,0x101fc0,0x10203f,0x101fbf,0xfdfc0,0xfe040,0xfe03f,0xfdfbf,0x105fc0,0x106040,0x10603f,0x105fbf,0x301fc1,0x302041,0x2fe041,0x2fdfc1,0x11020c0,0x11020bf,0x10fe0c0,0x10fe0bf,0x306041,0x305fc1,0x3101f40,0x3101f3f,0x30fdf40,0x30fdf3f,0x701fbe,0x70203e,0x11060c0,0x11060bf,0x6fe03e,0x6fdfbe,0x13020c1,0x12fe0c1,0x3105f40,0x3105f3f,0x180f9fc0,0x180fa040,0x180fa03f,0x180f9fbf,0x3301f41,0x32fdf41,0x705fbe,0x70603e,0x93060c1,0xb305f41,0x17020be,0x16fe0be,0x182fa041,0x182f9fc1,0x192fa0c0,0x190fa0bf,0x3701f3e,0x36fdf3e,0x1b0f9f3f,0x1b2f9f40,0x97060be,0x970a03f,0x930a040,0xb309fc0,0xb709fbf,0xb705f3e,0x1b6f9fbe,0x196fa03e, - 15,0x101fc0,0x101fbf,0x10203f,0xfe040,0xfdfc0,0xfdfbf,0xfe03f,0x102041,0x101fc1,0x105fc0,0x106040,0x10603f,0x105fbf,0xfdfc1,0xfe041,0x11020c0,0x106041,0x105fc1,0x11020bf,0x10fe0c0,0x10fe0bf,0x3101f40,0x3101f3f,0x30fdf40,0x30fdf3f,0x11020c1,0x11060c0,0x11060bf,0x10fe0c1,0x3101f41,0x3105f40,0x3105f3f,0x701fbe,0x70203e,0x180fa040,0x180f9fc0,0x30fdf41,0x180f9fbf,0x180fa03f,0x186fe03e,0x186fdfbe,0x93060c1,0xb305f41,0x705fbe,0x70603e,0x180fa041,0x180f9fc1,0x192fa0c0,0x190fa0bf,0x97020be,0x196fe0be,0xb701f3e,0x36fdf3e,0x1b2f9f40,0x1b6f9f3f,0xb301fc2,0x9302042,0x192fe042,0x1b2fdfc2,0xb309fc0,0x930a040,0x970a03f,0xb709fbf, - 15,0x101fc0,0xfdfc0,0xfe040,0x10203f,0x101fbf,0xfdfbf,0xfe03f,0x102041,0x101fc1,0xfdfc1,0xfe041,0x106040,0x105fc0,0x105fbf,0x10603f,0x106041,0x105fc1,0x11020c0,0x10fe0c0,0x11020bf,0x10fe0bf,0x3101f40,0x30fdf40,0x11020c1,0x10fe0c1,0x11060c0,0x3101f3f,0x30fdf3f,0x3101f41,0x30fdf41,0x3105f40,0x11060bf,0x91060c1,0x180fa040,0x180f9fc0,0x180f9fbf,0x3105f3f,0xb105f41,0x180fa03f,0x180fa041,0x180f9fc1,0x3701fbe,0x70203e,0x186fe03e,0x1b6fdfbe,0x3705fbe,0x70603e,0x1302042,0x3301fc2,0x1b2fdfc2,0x192fe042,0x190fa0c0,0x196fa0bf,0x192fa0c1,0x1b2f9f40,0x1b6f9f3f,0xb309fc0,0x930a040,0x9306042,0xb305fc2,0xb709fbf,0x970a03f,0x117020be, - 11,0x10203f,0x101fc0,0x101fbf,0xfe03f,0xfe040,0xfdfc0,0xfdfbf,0x10603f,0x106040,0x105fc0,0x105fbf,0x11020bf,0x11020c0,0x10fe0c0,0x10fe0bf,0x302041,0x301fc1,0x2fe041,0x2fdfc1,0x11060c0,0x11060bf,0x70203e,0x701fbe,0x6fe03e,0x6fdfbe,0x3101f3f,0x3101f40,0x306041,0x305fc1,0x30fdf40,0x30fdf3f,0x13020c1,0x12fe0c1,0x70603e,0x705fbe,0x180fa03f,0x180fa040,0x180f9fc0,0x180f9fbf,0x17020be,0x16fe0be,0x3105f3f,0x3105f40,0x93060c1,0x97060be,0x3301f41,0x32fdf41,0x190fa0c0,0x190fa0bf,0x192fa041,0x182f9fc1,0x3701f3e,0x36fdf3e,0x186f9fbe,0x196fa03e,0xb305f41,0xb309fc0,0x930a040,0x970a03f,0xb709fbf,0xb705f3e,0x1b6f9f3f,0x1b2f9f40, - 11,0x10203f,0x101fc0,0x101fbf,0xfe040,0xfe03f,0xfdfc0,0xfdfbf,0x106040,0x10603f,0x105fc0,0x105fbf,0x302041,0x11020c0,0x11020bf,0x301fc1,0x2fe041,0x10fe0c0,0x10fe0bf,0x2fdfc1,0x306041,0x11060c0,0x11060bf,0x305fc1,0x13020c1,0x12fe0c1,0x70203e,0x701fbe,0x3101f3f,0x3101f40,0x30fdf40,0x30fdf3f,0x6fdfbe,0x6fe03e,0x180fa03f,0x180fa040,0x180f9fc0,0x180f9fbf,0x93060c1,0x70603e,0x705fbe,0x3105f3f,0x3105f40,0x3301f41,0x32fdf41,0x17020be,0x16fe0be,0x190fa0bf,0x190fa0c0,0x182fa041,0x182f9fc1,0xb305f41,0x97060be,0x970a03f,0x930a040,0xb309fc0,0xb709fbf,0xb701f3e,0x36fdf3e,0x1b0f9f3f,0x1b2f9f40,0x192fa0c1,0x196fa03e,0x1b6f9fbe, - 15,0x101fc0,0x10203f,0xfe040,0xfdfc0,0x101fbf,0xfe03f,0xfdfbf,0x102041,0x106040,0x105fc0,0x101fc1,0xfe041,0xfdfc1,0x10603f,0x105fbf,0x11020c0,0x11020bf,0x10fe0c0,0x10fe0bf,0x106041,0x105fc1,0x11020c1,0x11060c0,0x11060bf,0x10fe0c1,0x3101f40,0x30fdf40,0x3101f3f,0x30fdf3f,0x93060c1,0x70203e,0x701fbe,0x180fa040,0x1b0f9fc0,0x180fa03f,0x186fe03e,0x6fdfbe,0x1b6f9fbf,0x3101f41,0x3105f40,0x3105f3f,0x30fdf41,0x1b0f9fc1,0x180fa041,0x70603e,0x705fbe,0x17020be,0x16fe0be,0x190fa0c0,0x196fa0bf,0xb305f41,0xb309fc0,0x930a040,0x970a03f,0x97060be,0x9302042,0xb301fc2,0x192fe042,0x192fa0c1,0x1b2fdfc2,0xb709fbf,0xb701f3e,0x1b6fdf3e, - 14,0x101fc0,0xfe040,0xfdfc0,0x10203f,0x101fbf,0xfe03f,0xfdfbf,0x102041,0x101fc1,0xfe041,0xfdfc1,0x106040,0x105fc0,0x10603f,0x11020c0,0x105fbf,0x10fe0c0,0x106041,0x105fc1,0x11020bf,0x10fe0bf,0x11020c1,0x10fe0c1,0x11060c0,0x11060bf,0x91060c1,0x3101f40,0x30fdf40,0x3101f3f,0x30fdf3f,0x3101f41,0x30fdf41,0x1b0f9fc0,0x180fa040,0x180fa03f,0x1b0f9fbf,0x3105f40,0xb105f3f,0x3701fbe,0x70203e,0x186fe03e,0x180fa041,0x1b0f9fc1,0x1b6fdfbe,0xb105f41,0x3301fc2,0x302042,0x190fa0c0,0x190fa0bf,0x182fe042,0x1b2fdfc2,0x70603e,0xb705fbe,0x97020be,0x196fe0be,0x190fa0c1,0x930a040,0xb309fc0,0x8306042,0xb305fc2,0x93020c2,0x192fe0c2,0xa70a03f, - 15,0x10203f,0x101fbf,0x101fc0,0xfe040,0xfe03f,0xfdfbf,0xfdfc0,0x1020c0,0x1020bf,0x10603f,0x106040,0x105fc0,0x105fbf,0xfe0bf,0xfe0c0,0x302041,0x1060c0,0x1060bf,0x301fc1,0x2fe041,0x2fdfc1,0x70203e,0x701fbe,0x6fe03e,0x6fdfbe,0x3020c1,0x306041,0x305fc1,0x2fe0c1,0x7020be,0x70603e,0x705fbe,0x3101f3f,0x3101f40,0x180fa040,0x180fa03f,0x6fe0be,0x180f9fbf,0x180f9fc0,0x1b0fdf40,0x1b0fdf3f,0x93060c1,0x97060be,0x3105f3f,0x3105f40,0x180fa0c0,0x180fa0bf,0x192fa041,0x182f9fc1,0xb301f41,0x1b2fdf41,0xb701f3e,0x36fdf3e,0x196fa03e,0x1b6f9fbe,0x970213f,0x9302140,0x192fe140,0x196fe13f,0x970a03f,0x930a040,0xb309fc0,0xb709fbf, - 15,0x10203f,0x101fc0,0xfe040,0xfe03f,0x101fbf,0xfdfc0,0xfdfbf,0x1020c0,0x106040,0x10603f,0x1020bf,0xfe0c0,0xfe0bf,0x105fc0,0x105fbf,0x302041,0x301fc1,0x2fe041,0x2fdfc1,0x1060c0,0x1060bf,0x3020c1,0x306041,0x305fc1,0x2fe0c1,0x70203e,0x6fe03e,0x701fbe,0x6fdfbe,0x93060c1,0x3101f40,0x3101f3f,0x180fa040,0x186fa03f,0x180f9fc0,0x1b0fdf40,0x30fdf3f,0x1b6f9fbf,0x7020be,0x70603e,0x705fbe,0x6fe0be,0x186fa0bf,0x180fa0c0,0x3105f40,0x3105f3f,0x3301f41,0x32fdf41,0x182fa041,0x1b2f9fc1,0x97060be,0x970a03f,0x930a040,0xb309fc0,0xb305f41,0x9302140,0x970213f,0x192fe140,0x192fa0c1,0x196fe13f,0xb709fbf,0xb701f3e,0x1b6fdf3e, - 16,0x10203f,0x101fc0,0xfe040,0xfe03f,0x101fbf,0xfdfc0,0xfdfbf,0x102041,0x1020c0,0x106040,0x10603f,0x1020bf,0xfe0c0,0xfe041,0x101fc1,0x105fc0,0x105fbf,0xfe0bf,0xfdfc1,0x1020c1,0x106041,0x1060c0,0x1060bf,0xfe0c1,0x105fc1,0x93060c1,0x70203e,0x3101f40,0x180fa040,0x180fa03f,0x186fe03e,0x701fbe,0x3701f3f,0x30fdf40,0x1b0f9fc0,0x180f9fbf,0x186fdfbe,0x1b6fdf3f,0x3101f41,0x3105f40,0xb105f3f,0x70603e,0x7020be,0x6fe0be,0x180fa0c0,0x180fa041,0x182f9fc1,0x1b2fdf41,0xb705fbe,0x186fa0bf,0x192fa0c1,0x97060be,0xb305f41,0x9302042,0x192fe042,0x13301fc2,0x930a040,0x970a03f,0xb509fc0,0x9302140,0x970213f,0x192fe140,0x196fe13f, - 15,0x101fc0,0xfe040,0xfdfc0,0x10203f,0x101fbf,0xfe03f,0x102041,0x101fc1,0xfdfbf,0xfe041,0x1020c0,0x106040,0xfdfc1,0x105fc0,0xfe0c0,0x1020bf,0x10603f,0x105fbf,0xfe0bf,0x1020c1,0x106041,0x105fc1,0xfe0c1,0x1060c0,0x11060bf,0x91060c1,0x3101f40,0x180fa040,0x180f9fc0,0x1b0fdf40,0x3101f3f,0x30fdf3f,0x180fa03f,0x1b0f9fbf,0x180fa041,0x180f9fc1,0x3101f41,0x1b0fdf41,0x70203e,0x3701fbe,0x186fe03e,0x1b6fdfbe,0x3105f40,0xb105f3f,0x180fa0c0,0x190fa0bf,0x192fa0c1,0x302042,0x3301fc2,0xb305f41,0x182fe042,0x1b2fdfc2,0x17020be,0x170603e,0xb705fbe,0x196fe0be,0x9502140,0x194fe140,0x193020c2,0xa306042,0x930a040,0xb309fc0,0xa70a03f, - 15,0x10203f,0xfe03f,0xfe040,0x101fc0,0x101fbf,0xfdfbf,0xfdfc0,0x1020c0,0x1020bf,0xfe0bf,0xfe0c0,0x106040,0x10603f,0x105fbf,0x105fc0,0x1060c0,0x1060bf,0x302041,0x2fe041,0x301fc1,0x2fdfc1,0x70203e,0x6fe03e,0x3020c1,0x2fe0c1,0x306041,0x701fbe,0x6fdfbe,0x7020be,0x6fe0be,0x70603e,0x305fc1,0x83060c1,0x180fa040,0x180fa03f,0x180f9fbf,0x705fbe,0x87060be,0x180f9fc0,0x180fa0c0,0x180fa0bf,0x3701f3f,0x3101f40,0x1b0fdf40,0x1b6fdf3f,0x3705f3f,0x3105f40,0x1302140,0x170213f,0x196fe13f,0x192fe140,0x182fa041,0x1b2f9fc1,0x192fa0c1,0x196fa03e,0x1b6f9fbe,0x970a03f,0x930a040,0x9306140,0x970613f,0xb709fbf,0xb309fc0,0x13301f41, - 14,0x10203f,0xfe040,0xfe03f,0x101fc0,0x101fbf,0xfdfc0,0xfdfbf,0x1020c0,0x1020bf,0xfe0c0,0xfe0bf,0x106040,0x10603f,0x105fc0,0x302041,0x105fbf,0x2fe041,0x1060c0,0x1060bf,0x301fc1,0x2fdfc1,0x3020c1,0x2fe0c1,0x306041,0x305fc1,0x83060c1,0x70203e,0x6fe03e,0x701fbe,0x6fdfbe,0x7020be,0x6fe0be,0x186fa03f,0x180fa040,0x180f9fc0,0x186f9fbf,0x70603e,0x8705fbe,0x3701f3f,0x3101f40,0x1b0fdf40,0x180fa0c0,0x186fa0bf,0x1b6fdf3f,0x87060be,0x170213f,0x1102140,0x182fa041,0x182f9fc1,0x190fe140,0x196fe13f,0x3105f40,0xb705f3f,0xb301f41,0x1b2fdf41,0x182fa0c1,0x930a040,0x970a03f,0x9106140,0x970613f,0x9302141,0x192fe141,0xb509fc0, - 15,0x10203f,0xfe040,0xfe03f,0x101fc0,0x101fbf,0xfdfc0,0x1020c0,0x1020bf,0xfdfbf,0xfe0c0,0x102041,0x106040,0xfe0bf,0x10603f,0xfe041,0x101fc1,0x105fc0,0x105fbf,0xfdfc1,0x1020c1,0x1060c0,0x1060bf,0xfe0c1,0x106041,0x305fc1,0x83060c1,0x70203e,0x180fa040,0x180fa03f,0x186fe03e,0x701fbe,0x6fdfbe,0x180f9fc0,0x186f9fbf,0x180fa0c0,0x180fa0bf,0x7020be,0x186fe0be,0x3101f40,0x3701f3f,0x1b0fdf40,0x1b6fdf3f,0x70603e,0x8705fbe,0x180fa041,0x182f9fc1,0x192fa0c1,0x1102140,0x170213f,0x97060be,0x190fe140,0x196fe13f,0x3301f41,0x3305f40,0xb705f3f,0x1b2fdf41,0xa302042,0x1a2fe042,0x19302141,0x9506140,0x930a040,0x970a03f,0xb509fc0, - 17,0xfe040,0x10203f,0x101fc0,0xfdfc0,0xfe03f,0x1020c0,0x102041,0x101fbf,0xfe041,0xfe0c0,0x106040,0xfdfbf,0x1020bf,0x101fc1,0xfdfc1,0xfe0bf,0x1020c1,0x10603f,0x105fc0,0xfe0c1,0x1060c0,0x106041,0x8105fbf,0x81060bf,0x8105fc1,0x81060c1,0x180fa040,0x180f9fc0,0x180fa03f,0x180fa0c0,0x180fa041,0x180f9fbf,0x70203e,0x186fe03e,0x3101f40,0x1b0fdf40,0x180f9fc1,0x180fa0bf,0x701fbe,0x3701f3f,0x1b0fdf3f,0x1b6fdfbe,0x7020be,0x186fe0be,0x192fa0c1,0x9102140,0x190fe140,0x8302042,0x3101f41,0x1b0fdf41,0x182fe042,0xb301fc2,0xb305f40,0x870603e,0x970213f,0x196fe13f,0x11102141,0x113020c2,0x1b2fdfc2,0xb105f3f,0xb705fbe,0x97060be,0xa50a040, - 11,0x10203f,0x101fc0,0x101fbf,0xfdfbf,0xfe03f,0xfe040,0xfdfc0,0x105fc0,0x106040,0x10603f,0x105fbf,0x11020bf,0x11020c0,0x302041,0x301fc1,0x2fdfc1,0x2fe041,0x10fe0c0,0x10fe0bf,0x70203e,0x701fbe,0x3101f3f,0x3101f40,0x305fc1,0x306041,0x11060c0,0x11060bf,0x6fe03e,0x6fdfbe,0x30fdf3f,0x30fdf40,0x3105f40,0x3105f3f,0x705fbe,0x70603e,0x13020c1,0x12fe0c1,0x180fa040,0x180fa03f,0x180f9fbf,0x180f9fc0,0x3301f41,0x17020be,0x16fe0be,0x93060c1,0x32fdf41,0xb305f41,0x3701f3e,0x36fdf3e,0x97060be,0x970a03f,0x930a040,0xb309fc0,0xb709fbf,0xb705f3e,0x182f9fc1,0x182fa041,0x192fa0c0,0x190fa0bf,0x196fa03e,0x186f9fbe,0x1b6f9f3f,0x1b2f9f40, - 11,0x101fc0,0x10203f,0x101fbf,0xfdfc0,0xfe040,0xfe03f,0xfdfbf,0x105fc0,0x106040,0x10603f,0x105fbf,0x301fc1,0x302041,0x2fe041,0x2fdfc1,0x11020c0,0x11020bf,0x306041,0x305fc1,0x10fe0c0,0x10fe0bf,0x3101f40,0x3101f3f,0x11060c0,0x11060bf,0x70203e,0x30fdf40,0x30fdf3f,0x701fbe,0x3105f40,0x3105f3f,0x13020c1,0x6fe03e,0x6fdfbe,0x705fbe,0x70603e,0x12fe0c1,0x3301f41,0x13060c1,0x32fdf41,0x1b0f9fc0,0x180fa040,0x186fa03f,0x1b6f9fbf,0x3305f41,0xb109fc0,0x17020be,0x16fe0be,0x810a040,0x870a03f,0xb709fbf,0x3701f3e,0x1b6fdf3e,0x17060be,0x182fa041,0x1b2f9fc1,0x192fa0c0,0x196fa0bf,0xb705f3e,0xb309fc1,0x830a041,0x930a0c0,0x970a0bf, - 15,0x101fc0,0x101fbf,0x10203f,0xfe040,0xfdfc0,0xfdfbf,0xfe03f,0x106040,0x105fc0,0x105fbf,0x10603f,0x102041,0x101fc1,0xfdfc1,0xfe041,0x106041,0x105fc1,0x11020c0,0x11020bf,0x10fe0c0,0x10fe0bf,0x3101f40,0x3101f3f,0x11060c0,0x11060bf,0x11020c1,0x30fdf40,0x30fdf3f,0x3105f40,0x3105f3f,0x3101f41,0x10fe0c1,0x13060c1,0x70203e,0x701fbe,0x6fdfbe,0x30fdf41,0x3305f41,0x6fe03e,0x70603e,0x705fbe,0x1b0f9fc0,0x180fa040,0x186fa03f,0x1b6f9fbf,0x1b0f9fc1,0x180fa041,0x910a040,0xb109fc0,0xb709fbf,0x970a03f,0x17020be,0x196fe0be,0x97060be,0xb701f3e,0x1b6fdf3e,0xb301fc2,0x9302042,0x930a041,0xb309fc1,0x1b2fdfc2,0x192fe042,0x194fa0c0, - 17,0x101fc0,0x101fbf,0x10203f,0xfe040,0xfdfc0,0x101fc1,0x102041,0x106040,0x105fc0,0xfdfbf,0xfe03f,0x10603f,0x105fbf,0xfdfc1,0xfe041,0x106041,0x105fc1,0x11020c0,0x11020bf,0x10fe0c0,0x11020c1,0x11060c0,0x3101f40,0x3101f3f,0x30fdf40,0x10fe0bf,0x11060bf,0x10fe0c1,0x3101f41,0x3105f40,0x30fdf3f,0x11060c1,0x3105f3f,0x30fdf41,0x3105f41,0x3701fbe,0x70203e,0x180fa040,0x1b0f9fc0,0x1b0f9fbf,0x180fa03f,0x186fe03e,0x1b6fdfbe,0x3705fbe,0x70603e,0x910a040,0xb109fc0,0x3301fc2,0x1302042,0x180fa041,0x1b0f9fc1,0x1b2fdfc2,0x192fe042,0x1306042,0x3305fc2,0xb709fbf,0x970a03f,0x930a041,0xb309fc1,0x97020be,0x192fa0c0,0x190fa0bf,0x196fe0be, - 11,0x10203f,0x101fc0,0x101fbf,0xfe03f,0xfe040,0xfdfc0,0xfdfbf,0x10603f,0x106040,0x105fc0,0x105fbf,0x11020bf,0x11020c0,0x10fe0c0,0x10fe0bf,0x302041,0x301fc1,0x11060c0,0x11060bf,0x2fe041,0x2fdfc1,0x70203e,0x701fbe,0x306041,0x305fc1,0x3101f40,0x6fe03e,0x6fdfbe,0x3101f3f,0x70603e,0x705fbe,0x13020c1,0x30fdf40,0x30fdf3f,0x3105f3f,0x3105f40,0x12fe0c1,0x17020be,0x13060c1,0x16fe0be,0x186fa03f,0x180fa040,0x1b0f9fc0,0x1b6f9fbf,0x17060be,0x870a03f,0x3301f41,0x32fdf41,0x810a040,0xb109fc0,0xb709fbf,0x3701f3e,0x1b6fdf3e,0x3305f41,0x190fa0c0,0x196fa0bf,0x192fa041,0x1b2f9fc1,0xb705f3e,0x970a0bf,0x910a0c0,0x930a041,0xb309fc1, - 11,0x10203f,0x101fc0,0x101fbf,0xfe040,0xfe03f,0xfdfc0,0xfdfbf,0x106040,0x10603f,0x105fc0,0x105fbf,0x302041,0x11020c0,0x11020bf,0x301fc1,0x2fe041,0x10fe0c0,0x10fe0bf,0x2fdfc1,0x306041,0x11060c0,0x11060bf,0x305fc1,0x13020c1,0x12fe0c1,0x70203e,0x701fbe,0x3101f3f,0x3101f40,0x30fdf40,0x30fdf3f,0x6fdfbe,0x6fe03e,0x70603e,0x13060c1,0x3105f40,0x3105f3f,0x705fbe,0x17020be,0x180fa040,0x186fa03f,0x1b0f9fc0,0x1b6f9fbf,0x3301f41,0x32fdf41,0x3305f41,0x16fe0be,0x17060be,0x870a03f,0x810a040,0xb109fc0,0xb709fbf,0x3701f3e,0x182fa041,0x192fa0c0,0x196fa0bf,0x1b2f9fc1,0x1b6fdf3e,0xb705f3e,0x830a041,0x930a0c0,0x970a0bf,0xb309fc1, - 14,0x101fc0,0x10203f,0x101fbf,0xfe040,0xfdfc0,0xfe03f,0xfdfbf,0x106040,0x105fc0,0x10603f,0x105fbf,0x102041,0x101fc1,0xfe041,0x11020c0,0xfdfc1,0x11020bf,0x106041,0x105fc1,0x10fe0c0,0x10fe0bf,0x11060c0,0x11060bf,0x11020c1,0x10fe0c1,0x13060c1,0x3101f40,0x3101f3f,0x30fdf40,0x30fdf3f,0x3105f40,0x3105f3f,0x3701fbe,0x70203e,0x6fe03e,0x36fdfbe,0x3101f41,0x32fdf41,0x1b0f9fc0,0x180fa040,0x186fa03f,0x70603e,0x3705fbe,0x1b6f9fbf,0x3305f41,0xb109fc0,0x810a040,0x17020be,0x16fe0be,0x870a03f,0xb709fbf,0x180fa041,0x1b2f9fc1,0x192fa0c0,0x196fa0bf,0x17060be,0x9302042,0xb301fc2,0x830a041,0xb309fc1,0x930a0c0,0x970a0bf,0x1a2fe042, - 17,0x101fc0,0x10203f,0xfe040,0xfdfc0,0x101fbf,0x106040,0x102041,0x101fc1,0x105fc0,0xfe03f,0xfdfbf,0x10603f,0x105fbf,0xfe041,0xfdfc1,0x106041,0x105fc1,0x11020c0,0x11020bf,0x10fe0c0,0x11020c1,0x11060c0,0x10fe0bf,0x11060bf,0x10fe0c1,0x11060c1,0x3101f40,0x30fdf40,0x3101f3f,0x3105f40,0x3101f41,0x30fdf3f,0x3105f3f,0x30fdf41,0x3105f41,0x70203e,0x3701fbe,0x180fa040,0x1b0f9fc0,0x180fa03f,0x186fe03e,0x36fdfbe,0x1b6f9fbf,0x180fa041,0x1b0f9fc1,0x1302042,0x3301fc2,0x910a040,0x70603e,0x3705fbe,0xb109fc0,0x970a03f,0xb709fbf,0x910a041,0x192fe042,0x1b2fdfc2,0x9306042,0x3305fc2,0xb309fc1,0x97020be,0x192fa0c0,0x190fa0bf,0x196fe0be, - 15,0x10203f,0x101fbf,0x101fc0,0xfe040,0xfe03f,0xfdfbf,0xfdfc0,0x106040,0x10603f,0x105fbf,0x105fc0,0x1020c0,0x1020bf,0xfe0bf,0xfe0c0,0x1060c0,0x1060bf,0x302041,0x301fc1,0x2fe041,0x2fdfc1,0x70203e,0x701fbe,0x306041,0x305fc1,0x3020c1,0x6fe03e,0x6fdfbe,0x70603e,0x705fbe,0x7020be,0x2fe0c1,0x13060c1,0x3101f40,0x3101f3f,0x30fdf3f,0x6fe0be,0x17060be,0x30fdf40,0x3105f40,0x3105f3f,0x186fa03f,0x180fa040,0x1b0f9fc0,0x1b6f9fbf,0x186fa0bf,0x180fa0c0,0x830a040,0x870a03f,0xb709fbf,0xb309fc0,0x3301f41,0x1b2fdf41,0xb305f41,0xb701f3e,0x1b6fdf3e,0x970213f,0x9302140,0x930a0c0,0x970a0bf,0x196fe13f,0x192fe140,0x1a2fa041, - 14,0x10203f,0x101fc0,0x101fbf,0xfe040,0xfe03f,0xfdfc0,0xfdfbf,0x106040,0x10603f,0x105fc0,0x105fbf,0x1020c0,0x1020bf,0xfe0c0,0x302041,0xfe0bf,0x301fc1,0x1060c0,0x1060bf,0x2fe041,0x2fdfc1,0x306041,0x305fc1,0x3020c1,0x2fe0c1,0x13060c1,0x70203e,0x701fbe,0x6fe03e,0x6fdfbe,0x70603e,0x705fbe,0x3701f3f,0x3101f40,0x30fdf40,0x36fdf3f,0x7020be,0x16fe0be,0x186fa03f,0x180fa040,0x1b0f9fc0,0x3105f40,0x3705f3f,0x1b6f9fbf,0x17060be,0x870a03f,0x810a040,0x3301f41,0x32fdf41,0xb109fc0,0xb709fbf,0x180fa0c0,0x196fa0bf,0x192fa041,0x1b2f9fc1,0x3305f41,0x9302140,0x970213f,0x910a0c0,0x970a0bf,0x930a041,0xb309fc1,0x194fe140, - 15,0x10203f,0x101fc0,0x101fbf,0xfe040,0xfe03f,0xfdfc0,0x106040,0x10603f,0xfdfbf,0x105fc0,0x102041,0x1020c0,0x105fbf,0x1020bf,0x101fc1,0xfe041,0xfe0c0,0xfe0bf,0xfdfc1,0x106041,0x1060c0,0x1060bf,0x105fc1,0x1020c1,0x2fe0c1,0x13060c1,0x70203e,0x3101f40,0x3101f3f,0x3701fbe,0x6fe03e,0x6fdfbe,0x30fdf40,0x36fdf3f,0x3105f40,0x3105f3f,0x70603e,0x3705fbe,0x180fa040,0x186fa03f,0x1b0f9fc0,0x1b6f9fbf,0x7020be,0x16fe0be,0x3101f41,0x32fdf41,0xb305f41,0x810a040,0x870a03f,0x97060be,0xb109fc0,0xb709fbf,0x182fa041,0x182fa0c0,0x196fa0bf,0x1b2f9fc1,0x11302042,0x13301fc2,0xb30a041,0x950a0c0,0x9302140,0x970213f,0x194fe140, - 17,0x101fc0,0x10203f,0xfe040,0xfdfc0,0x101fbf,0x106040,0x102041,0xfe03f,0x101fc1,0x105fc0,0x1020c0,0xfdfbf,0x10603f,0xfe041,0xfdfc1,0x105fbf,0x106041,0x1020bf,0xfe0c0,0x105fc1,0x1060c0,0x1020c1,0x10fe0bf,0x11060bf,0x10fe0c1,0x11060c1,0x3101f40,0x30fdf40,0x3101f3f,0x3105f40,0x3101f41,0x30fdf3f,0x70203e,0x3701fbe,0x180fa040,0x1b0f9fc0,0x30fdf41,0x3105f3f,0x6fe03e,0x186fa03f,0x1b0f9fbf,0x1b6fdfbe,0x70603e,0x3705fbe,0xb305f41,0x910a040,0xb109fc0,0x1302042,0x180fa041,0x1b0f9fc1,0x3301fc2,0x192fe042,0x192fa0c0,0x17020be,0x970a03f,0xb709fbf,0xa10a041,0xa306042,0x1b2fdfc2,0x190fa0bf,0x196fe0be,0x97060be,0x11502140, - 17,0x10203f,0x101fbf,0x101fc0,0xfe040,0xfe03f,0x1020bf,0x1020c0,0x106040,0x10603f,0xfdfbf,0xfdfc0,0x105fc0,0x105fbf,0xfe0bf,0xfe0c0,0x1060c0,0x1060bf,0x302041,0x301fc1,0x2fe041,0x3020c1,0x306041,0x70203e,0x701fbe,0x6fe03e,0x2fdfc1,0x305fc1,0x2fe0c1,0x7020be,0x70603e,0x6fdfbe,0x3060c1,0x705fbe,0x6fe0be,0x7060be,0x3701f3f,0x3101f40,0x180fa040,0x186fa03f,0x186f9fbf,0x180f9fc0,0x1b0fdf40,0x1b6fdf3f,0x3705f3f,0x3105f40,0x830a040,0x870a03f,0x170213f,0x1302140,0x180fa0c0,0x186fa0bf,0x196fe13f,0x192fe140,0x1306140,0x170613f,0xb709fbf,0xb309fc0,0x930a0c0,0x970a0bf,0xb301f41,0x192fa041,0x182f9fc1,0x1b2fdf41, - 17,0x10203f,0x101fc0,0xfe040,0xfe03f,0x101fbf,0x106040,0x1020c0,0x1020bf,0x10603f,0xfdfc0,0xfdfbf,0x105fc0,0x105fbf,0xfe0c0,0xfe0bf,0x1060c0,0x1060bf,0x302041,0x301fc1,0x2fe041,0x3020c1,0x306041,0x2fdfc1,0x305fc1,0x2fe0c1,0x3060c1,0x70203e,0x6fe03e,0x701fbe,0x70603e,0x7020be,0x6fdfbe,0x705fbe,0x6fe0be,0x7060be,0x3101f40,0x3701f3f,0x180fa040,0x186fa03f,0x180f9fc0,0x1b0fdf40,0x36fdf3f,0x1b6f9fbf,0x180fa0c0,0x186fa0bf,0x1302140,0x170213f,0x830a040,0x3105f40,0x3705f3f,0x870a03f,0xb309fc0,0xb709fbf,0x830a0c0,0x192fe140,0x196fe13f,0x9306140,0x170613f,0x970a0bf,0xb301f41,0x192fa041,0x182f9fc1,0x1b2fdf41, - 17,0x10203f,0x101fc0,0xfe040,0xfe03f,0x101fbf,0x106040,0x1020c0,0xfdfc0,0x1020bf,0x10603f,0x102041,0xfdfbf,0x105fc0,0xfe0c0,0xfe0bf,0x105fbf,0x1060c0,0x101fc1,0xfe041,0x1060bf,0x106041,0x1020c1,0x2fdfc1,0x305fc1,0x2fe0c1,0x3060c1,0x70203e,0x6fe03e,0x701fbe,0x70603e,0x7020be,0x6fdfbe,0x3101f40,0x3701f3f,0x180fa040,0x186fa03f,0x6fe0be,0x705fbe,0x30fdf40,0x1b0f9fc0,0x186f9fbf,0x1b6fdf3f,0x3105f40,0x3705f3f,0x97060be,0x830a040,0x870a03f,0x1302140,0x180fa0c0,0x186fa0bf,0x170213f,0x192fe140,0x192fa041,0x3301f41,0xb309fc0,0xb709fbf,0x850a0c0,0x9506140,0x196fe13f,0x182f9fc1,0x1b2fdf41,0xb305f41,0x12302042, - 16,0x10203f,0x101fc0,0xfe040,0x102041,0x1020c0,0x106040,0x101fbf,0xfe03f,0xfdfc0,0x101fc1,0x105fc0,0x10603f,0x1020bf,0xfe0c0,0xfe041,0xfdfbf,0x1020c1,0x106041,0x1060c0,0x105fbf,0xfe0bf,0xfdfc1,0x105fc1,0x1060bf,0xfe0c1,0x83060c1,0x70203e,0x3101f40,0x180fa040,0x180fa03f,0x186fe03e,0x701fbe,0x3701f3f,0x30fdf40,0x1b0f9fc0,0x180fa041,0x180fa0c0,0x7020be,0x70603e,0x3105f40,0xb101f41,0x9302042,0x1102140,0x930a040,0x6fdfbe,0x36fdf3f,0x1b6f9fbf,0x190fa0bf,0x196fe0be,0x170213f,0x196fe140,0x182f9fc1,0x1b2fdf41,0xb301fc2,0x1a2fe042,0x192fa0c1,0x8705fbe,0xb705f3f,0xb309fc0,0xa70a03f,0x97060be,0x9706140,0x11302141 -}; diff --git a/src/third_party/voro/src/v_compute.cc b/src/third_party/voro/src/v_compute.cc deleted file mode 100644 index 847ef9ed0..000000000 --- a/src/third_party/voro/src/v_compute.cc +++ /dev/null @@ -1,1006 +0,0 @@ -// Voro++, a 3D cell-based Voronoi library -// -// Author : Chris H. Rycroft (Harvard University / LBL) -// Email : chr@alum.mit.edu -// Date : August 30th 2011 - -/** \file v_compute.cc - * \brief Function implementantions for the voro_compute template. */ - -#include "worklist.hh" -#include "v_compute.hh" -#include "rad_option.hh" -#include "container.hh" -#include "container_prd.hh" - -namespace voro { - -/** The class constructor initializes constants from the container class, and - * sets up the mask and queue used for Voronoi computations. - * \param[in] con_ a reference to the container class to use. - * \param[in] (hx_,hy_,hz_) the size of the mask to use. */ -template -voro_compute::voro_compute(c_class &con_,int hx_,int hy_,int hz_) : - con(con_), boxx(con_.boxx), boxy(con_.boxy), boxz(con_.boxz), - xsp(con_.xsp), ysp(con_.ysp), zsp(con_.zsp), - hx(hx_), hy(hy_), hz(hz_), hxy(hx_*hy_), hxyz(hxy*hz_), ps(con_.ps), - id(con_.id), p(con_.p), co(con_.co), bxsq(boxx*boxx+boxy*boxy+boxz*boxz), - mv(0), qu_size(3*(3+hxy+hz*(hx+hy))), wl(con_.wl), mrad(con_.mrad), - mask(new unsigned int[hxyz]), qu(new int[qu_size]), qu_l(qu+qu_size) { - reset_mask(); -} - -/** Scans all of the particles within a block to see if any of them have a - * smaller distance to the given test vector. If one is found, the routine - * updates the minimum distance and store information about this particle. - * \param[in] ijk the index of the block. - * \param[in] (x,y,z) the test vector to consider (which may have already had a - * periodic displacement applied to it). - * \param[in] (di,dj,dk) the coordinates of the current block, to store if the - * particle record is updated. - * \param[in,out] w a reference to a particle record in which to store - * information about the particle whose Voronoi cell the - * vector is within. - * \param[in,out] mrs the current minimum distance, that may be updated if a - * closer particle is found. */ -template -inline void voro_compute::scan_all(int ijk,double x,double y,double z,int di,int dj,int dk,particle_record &w,double &mrs) { - double x1,y1,z1,rs;bool in_block=false; - for(int l=0;l -void voro_compute::find_voronoi_cell(double x,double y,double z,int ci,int cj,int ck,int ijk,particle_record &w,double &mrs) { - double qx=0,qy=0,qz=0,rs; - int i,j,k,di,dj,dk,ei,ej,ek,f,g,disp; - double fx,fy,fz,mxs,mys,mzs,*radp; - unsigned int q,*e,*mijk; - - // Init setup for parameters to return - w.ijk=-1;mrs=large_number; - - con.initialize_search(ci,cj,ck,ijk,i,j,k,disp); - - // Test all particles in the particle's local region first - scan_all(ijk,x,y,z,0,0,0,w,mrs); - - // Now compute the fractional position of the particle within its - // region and store it in (fx,fy,fz). We use this to compute an index - // (di,dj,dk) of which subregion the particle is within. - unsigned int m1,m2; - con.frac_pos(x,y,z,ci,cj,ck,fx,fy,fz); - di=int(fx*xsp*wl_fgrid);dj=int(fy*ysp*wl_fgrid);dk=int(fz*zsp*wl_fgrid); - - // The indices (di,dj,dk) tell us which worklist to use, to test the - // blocks in the optimal order. But we only store worklists for the - // eighth of the region where di, dj, and dk are all less than half the - // full grid. The rest of the cases are handled by symmetry. In this - // section, we detect for these cases, by reflecting high values of di, - // dj, and dk. For these cases, a mask is constructed in m1 and m2 - // which is used to flip the worklist information when it is loaded. - if(di>=wl_hgrid) { - mxs=boxx-fx; - m1=127+(3<<21);m2=1+(1<<21);di=wl_fgrid-1-di;if(di<0) di=0; - } else {m1=m2=0;mxs=fx;} - if(dj>=wl_hgrid) { - mys=boxy-fy; - m1|=(127<<7)+(3<<24);m2|=(1<<7)+(1<<24);dj=wl_fgrid-1-dj;if(dj<0) dj=0; - } else mys=fy; - if(dk>=wl_hgrid) { - mzs=boxz-fz; - m1|=(127<<14)+(3<<27);m2|=(1<<14)+(1<<27);dk=wl_fgrid-1-dk;if(dk<0) dk=0; - } else mzs=fz; - - // Do a quick test to account for the case when the minimum radius is - // small enought that no other blocks need to be considered - rs=con.r_max_add(mrs); - if(mxs*mxs>rs&&mys*mys>rs&&mzs*mzs>rs) return; - - // Now compute which worklist we are going to use, and set radp and e to - // point at the right offsets - ijk=di+wl_hgrid*(dj+wl_hgrid*dk); - radp=mrad+ijk*wl_seq_length; - e=(const_cast (wl))+ijk*wl_seq_length; - - // Read in how many items in the worklist can be tested without having to - // worry about writing to the mask - f=e[0];g=0; - do { - - // If mrs is less than the minimum distance to any untested - // block, then we are done - if(con.r_max_add(mrs)>7)&127;dj-=64; - dk=(q>>14)&127;dk-=64; - - // Check that the worklist position is in range - ei=di+i;if(ei<0||ei>=hx) continue; - ej=dj+j;if(ej<0||ej>=hy) continue; - ek=dk+k;if(ek<0||ek>=hz) continue; - - // Call the compute_min_max_radius() function. This returns - // true if the minimum distance to the block is bigger than the - // current mrs, in which case we skip this block and move on. - // Otherwise, it computes the maximum distance to the block and - // returns it in crs. - if(compute_min_radius(di,dj,dk,fx,fy,fz,mrs)) continue; - - // Now compute which region we are going to loop over, adding a - // displacement for the periodic cases - ijk=con.region_index(ci,cj,ck,ei,ej,ek,qx,qy,qz,disp); - - // If mrs is bigger than the maximum distance to the block, - // then we have to test all particles in the block for - // intersections. Otherwise, we do additional checks and skip - // those particles which can't possibly intersect the block. - scan_all(ijk,x-qx,y-qy,z-qz,di,dj,dk,w,mrs); - } while(g>7)&127;dj-=64; - dk=(q>>14)&127;dk-=64; - - // Compute the position in the mask of the current block. If - // this lies outside the mask, then skip it. Otherwise, mark - // it. - ei=di+i;if(ei<0||ei>=hx) continue; - ej=dj+j;if(ej<0||ej>=hy) continue; - ek=dk+k;if(ek<0||ek>=hz) continue; - mijk=mask+ei+hx*(ej+hy*ek); - *mijk=mv; - - // Skip this block if it is further away than the current - // minimum radius - if(compute_min_radius(di,dj,dk,fx,fy,fz,mrs)) continue; - - // Now compute which region we are going to loop over, adding a - // displacement for the periodic cases - ijk=con.region_index(ci,cj,ck,ei,ej,ek,qx,qy,qz,disp); - scan_all(ijk,x-qx,y-qy,z-qz,di,dj,dk,w,mrs); - - if(qu_e>qu_l-18) add_list_memory(qu_s,qu_e); - scan_bits_mask_add(q,mijk,ei,ej,ek,qu_e); - } - - // Do a check to see if we've reached the radius cutoff - if(con.r_max_add(mrs) -inline void voro_compute::add_to_mask(int ei,int ej,int ek,int *&qu_e) { - unsigned int *mijk=mask+ei+hx*(ej+hy*ek); - if(ek>0) if(*(mijk-hxy)!=mv) {if(qu_e==qu_l) qu_e=qu;*(mijk-hxy)=mv;*(qu_e++)=ei;*(qu_e++)=ej;*(qu_e++)=ek-1;} - if(ej>0) if(*(mijk-hx)!=mv) {if(qu_e==qu_l) qu_e=qu;*(mijk-hx)=mv;*(qu_e++)=ei;*(qu_e++)=ej-1;*(qu_e++)=ek;} - if(ei>0) if(*(mijk-1)!=mv) {if(qu_e==qu_l) qu_e=qu;*(mijk-1)=mv;*(qu_e++)=ei-1;*(qu_e++)=ej;*(qu_e++)=ek;} - if(ei -inline void voro_compute::scan_bits_mask_add(unsigned int q,unsigned int *mijk,int ei,int ej,int ek,int *&qu_e) { - const unsigned int b1=1<<21,b2=1<<22,b3=1<<24,b4=1<<25,b5=1<<27,b6=1<<28; - if((q&b2)==b2) { - if(ei>0) {*(mijk-1)=mv;*(qu_e++)=ei-1;*(qu_e++)=ej;*(qu_e++)=ek;} - if((q&b1)==0&&ei0) {*(mijk-hx)=mv;*(qu_e++)=ei;*(qu_e++)=ej-1;*(qu_e++)=ek;} - if((q&b3)==0&&ej0) {*(mijk-hxy)=mv;*(qu_e++)=ei;*(qu_e++)=ej;*(qu_e++)=ek-1;} - if((q&b5)==0&&ek -template -bool voro_compute::compute_cell(v_cell &c,int ijk,int s,int ci,int cj,int ck) { - static const int count_list[8]={7,11,15,19,26,35,45,59},*count_e=count_list+8; - double x,y,z,x1,y1,z1,qx=0,qy=0,qz=0; - double xlo,ylo,zlo,xhi,yhi,zhi,x2,y2,z2,rs; - int i,j,k,di,dj,dk,ei,ej,ek,f,g,l,disp; - double fx,fy,fz,gxs,gys,gzs,*radp; - unsigned int q,*e,*mijk; - - if(!con.initialize_voronoicell(c,ijk,s,ci,cj,ck,i,j,k,x,y,z,disp)) return false; - con.r_init(ijk,s); - - // Initialize the Voronoi cell to fill the entire container - double crs,mrs; - - int next_count=3,*count_p=(const_cast (count_list)); - - // Test all particles in the particle's local region first - for(l=0;l=wl_hgrid) { - gxs=fx; - m1=127+(3<<21);m2=1+(1<<21);di=wl_fgrid-1-di;if(di<0) di=0; - } else {m1=m2=0;gxs=boxx-fx;} - if(dj>=wl_hgrid) { - gys=fy; - m1|=(127<<7)+(3<<24);m2|=(1<<7)+(1<<24);dj=wl_fgrid-1-dj;if(dj<0) dj=0; - } else gys=boxy-fy; - if(dk>=wl_hgrid) { - gzs=fz; - m1|=(127<<14)+(3<<27);m2|=(1<<14)+(1<<27);dk=wl_fgrid-1-dk;if(dk<0) dk=0; - } else gzs=boxz-fz; - gxs*=gxs;gys*=gys;gzs*=gzs; - - // Now compute which worklist we are going to use, and set radp and e to - // point at the right offsets - ijk=di+wl_hgrid*(dj+wl_hgrid*dk); - radp=mrad+ijk*wl_seq_length; - e=(const_cast (wl))+ijk*wl_seq_length; - - // Read in how many items in the worklist can be tested without having to - // worry about writing to the mask - f=e[0];g=0; - do { - - // At the intervals specified by count_list, we recompute the - // maximum radius squared - if(g==next_count) { - mrs=c.max_radius_squared(); - if(count_p!=count_e) next_count=*(count_p++); - } - - // If mrs is less than the minimum distance to any untested - // block, then we are done - if(con.r_ctest(radp[g],mrs)) return true; - g++; - - // Load in a block off the worklist, permute it with the - // symmetry mask, and decode its position. These are all - // integer bit operations so they should run very fast. - q=e[g];q^=m1;q+=m2; - di=q&127;di-=64; - dj=(q>>7)&127;dj-=64; - dk=(q>>14)&127;dk-=64; - - // Check that the worklist position is in range - ei=di+i;if(ei<0||ei>=hx) continue; - ej=dj+j;if(ej<0||ej>=hy) continue; - ek=dk+k;if(ek<0||ek>=hz) continue; - - // Call the compute_min_max_radius() function. This returns - // true if the minimum distance to the block is bigger than the - // current mrs, in which case we skip this block and move on. - // Otherwise, it computes the maximum distance to the block and - // returns it in crs. - if(compute_min_max_radius(di,dj,dk,fx,fy,fz,gxs,gys,gzs,crs,mrs)) continue; - - // Now compute which region we are going to loop over, adding a - // displacement for the periodic cases - ijk=con.region_index(ci,cj,ck,ei,ej,ek,qx,qy,qz,disp); - - // If mrs is bigger than the maximum distance to the block, - // then we have to test all particles in the block for - // intersections. Otherwise, we do additional checks and skip - // those particles which can't possibly intersect the block. - if(co[ijk]>0) { - l=0;x2=x-qx;y2=y-qy;z2=z-qz; - if(!con.r_ctest(crs,mrs)) { - do { - x1=p[ijk][ps*l]-x2; - y1=p[ijk][ps*l+1]-y2; - z1=p[ijk][ps*l+2]-z2; - rs=con.r_scale(x1*x1+y1*y1+z1*z1,ijk,l); - if(!c.nplane(x1,y1,z1,rs,id[ijk][l])) return false; - l++; - } while (l>7)&127;dj-=64; - dk=(q>>14)&127;dk-=64; - - // Compute the position in the mask of the current block. If - // this lies outside the mask, then skip it. Otherwise, mark - // it. - ei=di+i;if(ei<0||ei>=hx) continue; - ej=dj+j;if(ej<0||ej>=hy) continue; - ek=dk+k;if(ek<0||ek>=hz) continue; - mijk=mask+ei+hx*(ej+hy*ek); - *mijk=mv; - - // Call the compute_min_max_radius() function. This returns - // true if the minimum distance to the block is bigger than the - // current mrs, in which case we skip this block and move on. - // Otherwise, it computes the maximum distance to the block and - // returns it in crs. - if(compute_min_max_radius(di,dj,dk,fx,fy,fz,gxs,gys,gzs,crs,mrs)) continue; - - // Now compute which region we are going to loop over, adding a - // displacement for the periodic cases - ijk=con.region_index(ci,cj,ck,ei,ej,ek,qx,qy,qz,disp); - - // If mrs is bigger than the maximum distance to the block, - // then we have to test all particles in the block for - // intersections. Otherwise, we do additional checks and skip - // those particles which can't possibly intersect the block. - if(co[ijk]>0) { - l=0;x2=x-qx;y2=y-qy;z2=z-qz; - if(!con.r_ctest(crs,mrs)) { - do { - x1=p[ijk][ps*l]-x2; - y1=p[ijk][ps*l+1]-y2; - z1=p[ijk][ps*l+2]-z2; - rs=con.r_scale(x1*x1+y1*y1+z1*z1,ijk,l); - if(!c.nplane(x1,y1,z1,rs,id[ijk][l])) return false; - l++; - } while (lqu_l-18) add_list_memory(qu_s,qu_e); - - // Test the parts of the worklist element which tell us what - // neighbors of this block are not on the worklist. Store them - // on the block list, and mark the mask. - scan_bits_mask_add(q,mijk,ei,ej,ek,qu_e); - } - - // Do a check to see if we've reached the radius cutoff - if(con.r_ctest(radp[g],mrs)) return true; - - // We were unable to completely compute the cell based on the blocks in - // the worklist, so now we have to go block by block, reading in items - // off the list - while(qu_s!=qu_e) { - - // If we reached the end of the list memory loop back to the - // start - if(qu_s==qu_l) qu_s=qu; - - // Read in a block off the list, and compute the upper and lower - // coordinates in each of the three dimensions - ei=*(qu_s++);ej=*(qu_s++);ek=*(qu_s++); - xlo=(ei-i)*boxx-fx;xhi=xlo+boxx; - ylo=(ej-j)*boxy-fy;yhi=ylo+boxy; - zlo=(ek-k)*boxz-fz;zhi=zlo+boxz; - - // Carry out plane tests to see if any particle in this block - // could possibly intersect the cell - if(ei>i) { - if(ej>j) { - if(ek>k) {if(corner_test(c,xlo,ylo,zlo,xhi,yhi,zhi)) continue;} - else if(ekk) {if(corner_test(c,xlo,yhi,zlo,xhi,ylo,zhi)) continue;} - else if(ekk) {if(edge_y_test(c,xlo,ylo,zlo,xhi,yhi,zhi)) continue;} - else if(ekj) { - if(ek>k) {if(corner_test(c,xhi,ylo,zlo,xlo,yhi,zhi)) continue;} - else if(ekk) {if(corner_test(c,xhi,yhi,zlo,xlo,ylo,zhi)) continue;} - else if(ekk) {if(edge_y_test(c,xhi,ylo,zlo,xlo,yhi,zhi)) continue;} - else if(ekj) { - if(ek>k) {if(edge_x_test(c,xlo,ylo,zlo,xhi,yhi,zhi)) continue;} - else if(ekk) {if(edge_x_test(c,xlo,yhi,zlo,xhi,ylo,zhi)) continue;} - else if(ekk) {if(face_z_test(c,xlo,ylo,zlo,xhi,yhi)) continue;} - else if(ek0) { - l=0;x2=x-qx;y2=y-qy;z2=z-qz; - do { - x1=p[ijk][ps*l]-x2; - y1=p[ijk][ps*l+1]-y2; - z1=p[ijk][ps*l+2]-z2; - rs=con.r_scale(x1*x1+y1*y1+z1*z1,ijk,l); - if(!c.nplane(x1,y1,z1,rs,id[ijk][l])) return false; - l++; - } while (l -template -bool voro_compute::corner_test(v_cell &c,double xl,double yl,double zl,double xh,double yh,double zh) { - con.r_prime(xl*xl+yl*yl+zl*zl); - if(c.plane_intersects_guess(xh,yl,zl,con.r_cutoff(xl*xh+yl*yl+zl*zl))) return false; - if(c.plane_intersects(xh,yh,zl,con.r_cutoff(xl*xh+yl*yh+zl*zl))) return false; - if(c.plane_intersects(xl,yh,zl,con.r_cutoff(xl*xl+yl*yh+zl*zl))) return false; - if(c.plane_intersects(xl,yh,zh,con.r_cutoff(xl*xl+yl*yh+zl*zh))) return false; - if(c.plane_intersects(xl,yl,zh,con.r_cutoff(xl*xl+yl*yl+zl*zh))) return false; - if(c.plane_intersects(xh,yl,zh,con.r_cutoff(xl*xh+yl*yl+zl*zh))) return false; - return true; -} - -/** This function checks to see whether a particular block can possibly have - * any intersection with a Voronoi cell, for the case when the closest point - * from the cell center to the block is on an edge which points along the x - * direction. - * \param[in,out] c a reference to a Voronoi cell. - * \param[in] (x0,x1) the minimum and maximum relative x coordinates of the - * block. - * \param[in] (yl,zl) the relative y and z coordinates of the corner of the - * block closest to the cell center. - * \param[in] (yh,zh) the relative y and z coordinates of the corner of the - * block furthest away from the cell center. - * \return False if the block may intersect, true if does not. */ -template -template -inline bool voro_compute::edge_x_test(v_cell &c,double x0,double yl,double zl,double x1,double yh,double zh) { - con.r_prime(yl*yl+zl*zl); - if(c.plane_intersects_guess(x0,yl,zh,con.r_cutoff(yl*yl+zl*zh))) return false; - if(c.plane_intersects(x1,yl,zh,con.r_cutoff(yl*yl+zl*zh))) return false; - if(c.plane_intersects(x1,yl,zl,con.r_cutoff(yl*yl+zl*zl))) return false; - if(c.plane_intersects(x0,yl,zl,con.r_cutoff(yl*yl+zl*zl))) return false; - if(c.plane_intersects(x0,yh,zl,con.r_cutoff(yl*yh+zl*zl))) return false; - if(c.plane_intersects(x1,yh,zl,con.r_cutoff(yl*yh+zl*zl))) return false; - return true; -} - -/** This function checks to see whether a particular block can possibly have - * any intersection with a Voronoi cell, for the case when the closest point - * from the cell center to the block is on an edge which points along the y - * direction. - * \param[in,out] c a reference to a Voronoi cell. - * \param[in] (y0,y1) the minimum and maximum relative y coordinates of the - * block. - * \param[in] (xl,zl) the relative x and z coordinates of the corner of the - * block closest to the cell center. - * \param[in] (xh,zh) the relative x and z coordinates of the corner of the - * block furthest away from the cell center. - * \return False if the block may intersect, true if does not. */ -template -template -inline bool voro_compute::edge_y_test(v_cell &c,double xl,double y0,double zl,double xh,double y1,double zh) { - con.r_prime(xl*xl+zl*zl); - if(c.plane_intersects_guess(xl,y0,zh,con.r_cutoff(xl*xl+zl*zh))) return false; - if(c.plane_intersects(xl,y1,zh,con.r_cutoff(xl*xl+zl*zh))) return false; - if(c.plane_intersects(xl,y1,zl,con.r_cutoff(xl*xl+zl*zl))) return false; - if(c.plane_intersects(xl,y0,zl,con.r_cutoff(xl*xl+zl*zl))) return false; - if(c.plane_intersects(xh,y0,zl,con.r_cutoff(xl*xh+zl*zl))) return false; - if(c.plane_intersects(xh,y1,zl,con.r_cutoff(xl*xh+zl*zl))) return false; - return true; -} - -/** This function checks to see whether a particular block can possibly have - * any intersection with a Voronoi cell, for the case when the closest point - * from the cell center to the block is on an edge which points along the z - * direction. - * \param[in,out] c a reference to a Voronoi cell. - * \param[in] (z0,z1) the minimum and maximum relative z coordinates of the block. - * \param[in] (xl,yl) the relative x and y coordinates of the corner of the - * block closest to the cell center. - * \param[in] (xh,yh) the relative x and y coordinates of the corner of the - * block furthest away from the cell center. - * \return False if the block may intersect, true if does not. */ -template -template -inline bool voro_compute::edge_z_test(v_cell &c,double xl,double yl,double z0,double xh,double yh,double z1) { - con.r_prime(xl*xl+yl*yl); - if(c.plane_intersects_guess(xl,yh,z0,con.r_cutoff(xl*xl+yl*yh))) return false; - if(c.plane_intersects(xl,yh,z1,con.r_cutoff(xl*xl+yl*yh))) return false; - if(c.plane_intersects(xl,yl,z1,con.r_cutoff(xl*xl+yl*yl))) return false; - if(c.plane_intersects(xl,yl,z0,con.r_cutoff(xl*xl+yl*yl))) return false; - if(c.plane_intersects(xh,yl,z0,con.r_cutoff(xl*xh+yl*yl))) return false; - if(c.plane_intersects(xh,yl,z1,con.r_cutoff(xl*xh+yl*yl))) return false; - return true; -} - -/** This function checks to see whether a particular block can possibly have - * any intersection with a Voronoi cell, for the case when the closest point - * from the cell center to the block is on a face aligned with the x direction. - * \param[in,out] c a reference to a Voronoi cell. - * \param[in] xl the minimum distance from the cell center to the face. - * \param[in] (y0,y1) the minimum and maximum relative y coordinates of the - * block. - * \param[in] (z0,z1) the minimum and maximum relative z coordinates of the - * block. - * \return False if the block may intersect, true if does not. */ -template -template -inline bool voro_compute::face_x_test(v_cell &c,double xl,double y0,double z0,double y1,double z1) { - con.r_prime(xl*xl); - if(c.plane_intersects_guess(xl,y0,z0,con.r_cutoff(xl*xl))) return false; - if(c.plane_intersects(xl,y0,z1,con.r_cutoff(xl*xl))) return false; - if(c.plane_intersects(xl,y1,z1,con.r_cutoff(xl*xl))) return false; - if(c.plane_intersects(xl,y1,z0,con.r_cutoff(xl*xl))) return false; - return true; -} - -/** This function checks to see whether a particular block can possibly have - * any intersection with a Voronoi cell, for the case when the closest point - * from the cell center to the block is on a face aligned with the y direction. - * \param[in,out] c a reference to a Voronoi cell. - * \param[in] yl the minimum distance from the cell center to the face. - * \param[in] (x0,x1) the minimum and maximum relative x coordinates of the - * block. - * \param[in] (z0,z1) the minimum and maximum relative z coordinates of the - * block. - * \return False if the block may intersect, true if does not. */ -template -template -inline bool voro_compute::face_y_test(v_cell &c,double x0,double yl,double z0,double x1,double z1) { - con.r_prime(yl*yl); - if(c.plane_intersects_guess(x0,yl,z0,con.r_cutoff(yl*yl))) return false; - if(c.plane_intersects(x0,yl,z1,con.r_cutoff(yl*yl))) return false; - if(c.plane_intersects(x1,yl,z1,con.r_cutoff(yl*yl))) return false; - if(c.plane_intersects(x1,yl,z0,con.r_cutoff(yl*yl))) return false; - return true; -} - -/** This function checks to see whether a particular block can possibly have - * any intersection with a Voronoi cell, for the case when the closest point - * from the cell center to the block is on a face aligned with the z direction. - * \param[in,out] c a reference to a Voronoi cell. - * \param[in] zl the minimum distance from the cell center to the face. - * \param[in] (x0,x1) the minimum and maximum relative x coordinates of the - * block. - * \param[in] (y0,y1) the minimum and maximum relative y coordinates of the - * block. - * \return False if the block may intersect, true if does not. */ -template -template -inline bool voro_compute::face_z_test(v_cell &c,double x0,double y0,double zl,double x1,double y1) { - con.r_prime(zl*zl); - if(c.plane_intersects_guess(x0,y0,zl,con.r_cutoff(zl*zl))) return false; - if(c.plane_intersects(x0,y1,zl,con.r_cutoff(zl*zl))) return false; - if(c.plane_intersects(x1,y1,zl,con.r_cutoff(zl*zl))) return false; - if(c.plane_intersects(x1,y0,zl,con.r_cutoff(zl*zl))) return false; - return true; -} - -/** This routine checks to see whether a point is within a particular distance - * of a nearby region. If the point is within the distance of the region, then - * the routine returns true, and computes the maximum distance from the point - * to the region. Otherwise, the routine returns false. - * \param[in] (di,dj,dk) the position of the nearby region to be tested, - * relative to the region that the point is in. - * \param[in] (fx,fy,fz) the displacement of the point within its region. - * \param[in] (gxs,gys,gzs) the maximum squared distances from the point to the - * sides of its region. - * \param[out] crs a reference in which to return the maximum distance to the - * region (only computed if the routine returns false). - * \param[in] mrs the distance to be tested. - * \return True if the region is further away than mrs, false if the region in - * within mrs. */ -template -bool voro_compute::compute_min_max_radius(int di,int dj,int dk,double fx,double fy,double fz,double gxs,double gys,double gzs,double &crs,double mrs) { - double xlo,ylo,zlo; - if(di>0) { - xlo=di*boxx-fx; - crs=xlo*xlo; - if(dj>0) { - ylo=dj*boxy-fy; - crs+=ylo*ylo; - if(dk>0) { - zlo=dk*boxz-fz; - crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true; - crs+=bxsq+2*(boxx*xlo+boxy*ylo+boxz*zlo); - } else if(dk<0) { - zlo=(dk+1)*boxz-fz; - crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true; - crs+=bxsq+2*(boxx*xlo+boxy*ylo-boxz*zlo); - } else { - if(con.r_ctest(crs,mrs)) return true; - crs+=boxx*(2*xlo+boxx)+boxy*(2*ylo+boxy)+gzs; - } - } else if(dj<0) { - ylo=(dj+1)*boxy-fy; - crs+=ylo*ylo; - if(dk>0) { - zlo=dk*boxz-fz; - crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true; - crs+=bxsq+2*(boxx*xlo-boxy*ylo+boxz*zlo); - } else if(dk<0) { - zlo=(dk+1)*boxz-fz; - crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true; - crs+=bxsq+2*(boxx*xlo-boxy*ylo-boxz*zlo); - } else { - if(con.r_ctest(crs,mrs)) return true; - crs+=boxx*(2*xlo+boxx)+boxy*(-2*ylo+boxy)+gzs; - } - } else { - if(dk>0) { - zlo=dk*boxz-fz; - crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true; - crs+=boxz*(2*zlo+boxz); - } else if(dk<0) { - zlo=(dk+1)*boxz-fz; - crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true; - crs+=boxz*(-2*zlo+boxz); - } else { - if(con.r_ctest(crs,mrs)) return true; - crs+=gzs; - } - crs+=gys+boxx*(2*xlo+boxx); - } - } else if(di<0) { - xlo=(di+1)*boxx-fx; - crs=xlo*xlo; - if(dj>0) { - ylo=dj*boxy-fy; - crs+=ylo*ylo; - if(dk>0) { - zlo=dk*boxz-fz; - crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true; - crs+=bxsq+2*(-boxx*xlo+boxy*ylo+boxz*zlo); - } else if(dk<0) { - zlo=(dk+1)*boxz-fz; - crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true; - crs+=bxsq+2*(-boxx*xlo+boxy*ylo-boxz*zlo); - } else { - if(con.r_ctest(crs,mrs)) return true; - crs+=boxx*(-2*xlo+boxx)+boxy*(2*ylo+boxy)+gzs; - } - } else if(dj<0) { - ylo=(dj+1)*boxy-fy; - crs+=ylo*ylo; - if(dk>0) { - zlo=dk*boxz-fz; - crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true; - crs+=bxsq+2*(-boxx*xlo-boxy*ylo+boxz*zlo); - } else if(dk<0) { - zlo=(dk+1)*boxz-fz; - crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true; - crs+=bxsq+2*(-boxx*xlo-boxy*ylo-boxz*zlo); - } else { - if(con.r_ctest(crs,mrs)) return true; - crs+=boxx*(-2*xlo+boxx)+boxy*(-2*ylo+boxy)+gzs; - } - } else { - if(dk>0) { - zlo=dk*boxz-fz; - crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true; - crs+=boxz*(2*zlo+boxz); - } else if(dk<0) { - zlo=(dk+1)*boxz-fz; - crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true; - crs+=boxz*(-2*zlo+boxz); - } else { - if(con.r_ctest(crs,mrs)) return true; - crs+=gzs; - } - crs+=gys+boxx*(-2*xlo+boxx); - } - } else { - if(dj>0) { - ylo=dj*boxy-fy; - crs=ylo*ylo; - if(dk>0) { - zlo=dk*boxz-fz; - crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true; - crs+=boxz*(2*zlo+boxz); - } else if(dk<0) { - zlo=(dk+1)*boxz-fz; - crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true; - crs+=boxz*(-2*zlo+boxz); - } else { - if(con.r_ctest(crs,mrs)) return true; - crs+=gzs; - } - crs+=boxy*(2*ylo+boxy); - } else if(dj<0) { - ylo=(dj+1)*boxy-fy; - crs=ylo*ylo; - if(dk>0) { - zlo=dk*boxz-fz; - crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true; - crs+=boxz*(2*zlo+boxz); - } else if(dk<0) { - zlo=(dk+1)*boxz-fz; - crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true; - crs+=boxz*(-2*zlo+boxz); - } else { - if(con.r_ctest(crs,mrs)) return true; - crs+=gzs; - } - crs+=boxy*(-2*ylo+boxy); - } else { - if(dk>0) { - zlo=dk*boxz-fz;crs=zlo*zlo;if(con.r_ctest(crs,mrs)) return true; - crs+=boxz*(2*zlo+boxz); - } else if(dk<0) { - zlo=(dk+1)*boxz-fz;crs=zlo*zlo;if(con.r_ctest(crs,mrs)) return true; - crs+=boxz*(-2*zlo+boxz); - } else { - crs=0; - voro_fatal_error("Min/max radius function called for central block, which should never\nhappen.",VOROPP_INTERNAL_ERROR); - } - crs+=gys; - } - crs+=gxs; - } - return false; -} - -template -bool voro_compute::compute_min_radius(int di,int dj,int dk,double fx,double fy,double fz,double mrs) { - double t,crs; - - if(di>0) {t=di*boxx-fx;crs=t*t;} - else if(di<0) {t=(di+1)*boxx-fx;crs=t*t;} - else crs=0; - - if(dj>0) {t=dj*boxy-fy;crs+=t*t;} - else if(dj<0) {t=(dj+1)*boxy-fy;crs+=t*t;} - - if(dk>0) {t=dk*boxz-fz;crs+=t*t;} - else if(dk<0) {t=(dk+1)*boxz-fz;crs+=t*t;} - - return crs>con.r_max_add(mrs); -} - -/** Adds memory to the queue. - * \param[in,out] qu_s a reference to the queue start pointer. - * \param[in,out] qu_e a reference to the queue end pointer. */ -template -inline void voro_compute::add_list_memory(int*& qu_s,int*& qu_e) { - qu_size<<=1; - int *qu_n=new int[qu_size],*qu_c=qu_n; -#if VOROPP_VERBOSE >=2 - fprintf(stderr,"List memory scaled up to %d\n",qu_size); -#endif - if(qu_s<=qu_e) { - while(qu_s::voro_compute(container&,int,int,int); -template voro_compute::voro_compute(container_poly&,int,int,int); -template bool voro_compute::compute_cell(voronoicell&,int,int,int,int,int); -template bool voro_compute::compute_cell(voronoicell_neighbor&,int,int,int,int,int); -template void voro_compute::find_voronoi_cell(double,double,double,int,int,int,int,particle_record&,double&); -template bool voro_compute::compute_cell(voronoicell&,int,int,int,int,int); -template bool voro_compute::compute_cell(voronoicell_neighbor&,int,int,int,int,int); -template void voro_compute::find_voronoi_cell(double,double,double,int,int,int,int,particle_record&,double&); - -// Explicit template instantiation -template voro_compute::voro_compute(container_periodic&,int,int,int); -template voro_compute::voro_compute(container_periodic_poly&,int,int,int); -template bool voro_compute::compute_cell(voronoicell&,int,int,int,int,int); -template bool voro_compute::compute_cell(voronoicell_neighbor&,int,int,int,int,int); -template void voro_compute::find_voronoi_cell(double,double,double,int,int,int,int,particle_record&,double&); -template bool voro_compute::compute_cell(voronoicell&,int,int,int,int,int); -template bool voro_compute::compute_cell(voronoicell_neighbor&,int,int,int,int,int); -template void voro_compute::find_voronoi_cell(double,double,double,int,int,int,int,particle_record&,double&); - -} diff --git a/src/third_party/voro/src/v_compute.hh b/src/third_party/voro/src/v_compute.hh deleted file mode 100644 index 01cfb7d1c..000000000 --- a/src/third_party/voro/src/v_compute.hh +++ /dev/null @@ -1,149 +0,0 @@ -// Voro++, a 3D cell-based Voronoi library -// -// Author : Chris H. Rycroft (Harvard University / LBL) -// Email : chr@alum.mit.edu -// Date : August 30th 2011 - -/** \file v_compute.hh - * \brief Header file for the voro_compute template and related classes. */ - -#ifndef VOROPP_V_COMPUTE_HH -#define VOROPP_V_COMPUTE_HH - -#include "config.hh" -#include "worklist.hh" -#include "cell.hh" - -namespace voro { - -/** \brief Structure for holding information about a particle. - * - * This small structure holds information about a single particle, and is used - * by several of the routines in the voro_compute template for passing - * information by reference between functions. */ -struct particle_record { - /** The index of the block that the particle is within. */ - int ijk; - /** The number of particle within its block. */ - int l; - /** The x-index of the block. */ - int di; - /** The y-index of the block. */ - int dj; - /** The z-index of the block. */ - int dk; -}; - -/** \brief Template for carrying out Voronoi cell computations. */ -template -class voro_compute { - public: - /** A reference to the container class on which to carry out*/ - c_class &con; - /** The size of an internal computational block in the x - * direction. */ - const double boxx; - /** The size of an internal computational block in the y - * direction. */ - const double boxy; - /** The size of an internal computational block in the z - * direction. */ - const double boxz; - /** The inverse box length in the x direction, set to - * nx/(bx-ax). */ - const double xsp; - /** The inverse box length in the y direction, set to - * ny/(by-ay). */ - const double ysp; - /** The inverse box length in the z direction, set to - * nz/(bz-az). */ - const double zsp; - /** The number of boxes in the x direction for the searching mask. */ - const int hx; - /** The number of boxes in the y direction for the searching mask. */ - const int hy; - /** The number of boxes in the z direction for the searching mask. */ - const int hz; - /** A constant, set to the value of hx multiplied by hy, which - * is used in the routines which step through mask boxes in - * sequence. */ - const int hxy; - /** A constant, set to the value of hx*hy*hz, which is used in - * the routines which step through mask boxes in sequence. */ - const int hxyz; - /** The number of floating point entries to store for each - * particle. */ - const int ps; - /** This array holds the numerical IDs of each particle in each - * computational box. */ - int **id; - /** A two dimensional array holding particle positions. For the - * derived container_poly class, this also holds particle - * radii. */ - double **p; - /** An array holding the number of particles within each - * computational box of the container. */ - int *co; - voro_compute(c_class &con_,int hx_,int hy_,int hz_); - /** The class destructor frees the dynamically allocated memory - * for the mask and queue. */ - ~voro_compute() { - delete [] qu; - delete [] mask; - } - template - bool compute_cell(v_cell &c,int ijk,int s,int ci,int cj,int ck); - void find_voronoi_cell(double x,double y,double z,int ci,int cj,int ck,int ijk,particle_record &w,double &mrs); - private: - /** A constant set to boxx*boxx+boxy*boxy+boxz*boxz, which is - * frequently used in the computation. */ - const double bxsq; - /** This sets the current value being used to mark tested blocks - * in the mask. */ - unsigned int mv; - /** The current size of the search list. */ - int qu_size; - /** A pointer to the array of worklists. */ - const unsigned int *wl; - /** An pointer to the array holding the minimum distances - * associated with the worklists. */ - double *mrad; - /** This array is used during the cell computation to determine - * which blocks have been considered. */ - unsigned int *mask; - /** An array is used to store the queue of blocks to test - * during the Voronoi cell computation. */ - int *qu; - /** A pointer to the end of the queue array, used to determine - * when the queue is full. */ - int *qu_l; - template - bool corner_test(v_cell &c,double xl,double yl,double zl,double xh,double yh,double zh); - template - inline bool edge_x_test(v_cell &c,double x0,double yl,double zl,double x1,double yh,double zh); - template - inline bool edge_y_test(v_cell &c,double xl,double y0,double zl,double xh,double y1,double zh); - template - inline bool edge_z_test(v_cell &c,double xl,double yl,double z0,double xh,double yh,double z1); - template - inline bool face_x_test(v_cell &c,double xl,double y0,double z0,double y1,double z1); - template - inline bool face_y_test(v_cell &c,double x0,double yl,double z0,double x1,double z1); - template - inline bool face_z_test(v_cell &c,double x0,double y0,double zl,double x1,double y1); - bool compute_min_max_radius(int di,int dj,int dk,double fx,double fy,double fz,double gx,double gy,double gz,double& crs,double mrs); - bool compute_min_radius(int di,int dj,int dk,double fx,double fy,double fz,double mrs); - inline void add_to_mask(int ei,int ej,int ek,int *&qu_e); - inline void scan_bits_mask_add(unsigned int q,unsigned int *mijk,int ei,int ej,int ek,int *&qu_e); - inline void scan_all(int ijk,double x,double y,double z,int di,int dj,int dk,particle_record &w,double &mrs); - void add_list_memory(int*& qu_s,int*& qu_e); - /** Resets the mask in cases where the mask counter wraps - * around. */ - inline void reset_mask() { - for(unsigned int *mp(mask);mp -bool wall_sphere::cut_cell_base(v_cell &c,double x,double y,double z) { - double xd=x-xc,yd=y-yc,zd=z-zc,dq=xd*xd+yd*yd+zd*zd; - if (dq>1e-5) { - dq=2*(sqrt(dq)*rc-dq); - return c.nplane(xd,yd,zd,dq,w_id); - } - return true; -} - -/** Tests to see whether a point is inside the plane wall object. - * \param[in] (x,y,z) the vector to test. - * \return True if the point is inside, false if the point is outside. */ -bool wall_plane::point_inside(double x,double y,double z) { - return x*xc+y*yc+z*zc -bool wall_plane::cut_cell_base(v_cell &c,double x,double y,double z) { - double dq=2*(ac-x*xc-y*yc-z*zc); - return c.nplane(xc,yc,zc,dq,w_id); -} - -/** Tests to see whether a point is inside the cylindrical wall object. - * \param[in] (x,y,z) the vector to test. - * \return True if the point is inside, false if the point is outside. */ -bool wall_cylinder::point_inside(double x,double y,double z) { - double xd=x-xc,yd=y-yc,zd=z-zc; - double pa=(xd*xa+yd*ya+zd*za)*asi; - xd-=xa*pa;yd-=ya*pa;zd-=za*pa; - return xd*xd+yd*yd+zd*zd -bool wall_cylinder::cut_cell_base(v_cell &c,double x,double y,double z) { - double xd=x-xc,yd=y-yc,zd=z-zc,pa=(xd*xa+yd*ya+zd*za)*asi; - xd-=xa*pa;yd-=ya*pa;zd-=za*pa; - pa=xd*xd+yd*yd+zd*zd; - if(pa>1e-5) { - pa=2*(sqrt(pa)*rc-pa); - return c.nplane(xd,yd,zd,pa,w_id); - } - return true; -} - -/** Tests to see whether a point is inside the cone wall object. - * \param[in] (x,y,z) the vector to test. - * \return True if the point is inside, false if the point is outside. */ -bool wall_cone::point_inside(double x,double y,double z) { - double xd=x-xc,yd=y-yc,zd=z-zc,pa=(xd*xa+yd*ya+zd*za)*asi; - xd-=xa*pa;yd-=ya*pa;zd-=za*pa; - pa*=gra; - if (pa<0) return false; - pa*=pa; - return xd*xd+yd*yd+zd*zd -bool wall_cone::cut_cell_base(v_cell &c,double x,double y,double z) { - double xd=x-xc,yd=y-yc,zd=z-zc,xf,yf,zf,q,pa=(xd*xa+yd*ya+zd*za)*asi; - xd-=xa*pa;yd-=ya*pa;zd-=za*pa; - pa=xd*xd+yd*yd+zd*zd; - if(pa>1e-5) { - pa=1/sqrt(pa); - q=sqrt(asi); - xf=-sang*q*xa+cang*pa*xd; - yf=-sang*q*ya+cang*pa*yd; - zf=-sang*q*za+cang*pa*zd; - pa=2*(xf*(xc-x)+yf*(yc-y)+zf*(zc-z)); - return c.nplane(xf,yf,zf,pa,w_id); - } - return true; -} - -// Explicit instantiation -template bool wall_sphere::cut_cell_base(voronoicell&,double,double,double); -template bool wall_sphere::cut_cell_base(voronoicell_neighbor&,double,double,double); -template bool wall_plane::cut_cell_base(voronoicell&,double,double,double); -template bool wall_plane::cut_cell_base(voronoicell_neighbor&,double,double,double); -template bool wall_cylinder::cut_cell_base(voronoicell&,double,double,double); -template bool wall_cylinder::cut_cell_base(voronoicell_neighbor&,double,double,double); -template bool wall_cone::cut_cell_base(voronoicell&,double,double,double); -template bool wall_cone::cut_cell_base(voronoicell_neighbor&,double,double,double); - -} diff --git a/src/third_party/voro/src/wall.hh b/src/third_party/voro/src/wall.hh deleted file mode 100644 index d3f325bad..000000000 --- a/src/third_party/voro/src/wall.hh +++ /dev/null @@ -1,118 +0,0 @@ -// Voro++, a 3D cell-based Voronoi library -// -// Author : Chris H. Rycroft (Harvard University / LBL) -// Email : chr@alum.mit.edu -// Date : August 30th 2011 - -/** \file wall.hh - * \brief Header file for the derived wall classes. */ - -#ifndef VOROPP_WALL_HH -#define VOROPP_WALL_HH - -#include "cell.hh" -#include "container.hh" - -namespace voro { - -/** \brief A class representing a spherical wall object. - * - * This class represents a spherical wall object. */ -struct wall_sphere : public wall { - public: - /** Constructs a spherical wall object. - * \param[in] w_id_ an ID number to associate with the wall for - * neighbor tracking. - * \param[in] (xc_,yc_,zc_) a position vector for the sphere's - * center. - * \param[in] rc_ the radius of the sphere. */ - wall_sphere(double xc_,double yc_,double zc_,double rc_,int w_id_=-99) - : w_id(w_id_), xc(xc_), yc(yc_), zc(zc_), rc(rc_) {} - bool point_inside(double x,double y,double z); - template - bool cut_cell_base(v_cell &c,double x,double y,double z); - bool cut_cell(voronoicell &c,double x,double y,double z) {return cut_cell_base(c,x,y,z);} - bool cut_cell(voronoicell_neighbor &c,double x,double y,double z) {return cut_cell_base(c,x,y,z);} - private: - const int w_id; - const double xc,yc,zc,rc; -}; - -/** \brief A class representing a plane wall object. - * - * This class represents a single plane wall object. */ -struct wall_plane : public wall { - public: - /** Constructs a plane wall object. - * \param[in] (xc_,yc_,zc_) a normal vector to the plane. - * \param[in] ac_ a displacement along the normal vector. - * \param[in] w_id_ an ID number to associate with the wall for - * neighbor tracking. */ - wall_plane(double xc_,double yc_,double zc_,double ac_,int w_id_=-99) - : w_id(w_id_), xc(xc_), yc(yc_), zc(zc_), ac(ac_) {} - bool point_inside(double x,double y,double z); - template - bool cut_cell_base(v_cell &c,double x,double y,double z); - bool cut_cell(voronoicell &c,double x,double y,double z) {return cut_cell_base(c,x,y,z);} - bool cut_cell(voronoicell_neighbor &c,double x,double y,double z) {return cut_cell_base(c,x,y,z);} - private: - const int w_id; - const double xc,yc,zc,ac; -}; - -/** \brief A class representing a cylindrical wall object. - * - * This class represents a open cylinder wall object. */ -struct wall_cylinder : public wall { - public: - /** Constructs a cylinder wall object. - * \param[in] (xc_,yc_,zc_) a point on the axis of the - * cylinder. - * \param[in] (xa_,ya_,za_) a vector pointing along the - * direction of the cylinder. - * \param[in] rc_ the radius of the cylinder - * \param[in] w_id_ an ID number to associate with the wall for - * neighbor tracking. */ - wall_cylinder(double xc_,double yc_,double zc_,double xa_,double ya_,double za_,double rc_,int w_id_=-99) - : w_id(w_id_), xc(xc_), yc(yc_), zc(zc_), xa(xa_), ya(ya_), za(za_), - asi(1/(xa_*xa_+ya_*ya_+za_*za_)), rc(rc_) {} - bool point_inside(double x,double y,double z); - template - bool cut_cell_base(v_cell &c,double x,double y,double z); - bool cut_cell(voronoicell &c,double x,double y,double z) {return cut_cell_base(c,x,y,z);} - bool cut_cell(voronoicell_neighbor &c,double x,double y,double z) {return cut_cell_base(c,x,y,z);} - private: - const int w_id; - const double xc,yc,zc,xa,ya,za,asi,rc; -}; - -/** \brief A class representing a conical wall object. - * - * This class represents a cone wall object. */ -struct wall_cone : public wall { - public: - /** Constructs a cone wall object. - * \param[in] (xc_,yc_,zc_) the apex of the cone. - * \param[in] (xa_,ya_,za_) a vector pointing along the axis of - * the cone. - * \param[in] ang the angle (in radians) of the cone, measured - * from the axis. - * \param[in] w_id_ an ID number to associate with the wall for - * neighbor tracking. */ - wall_cone(double xc_,double yc_,double zc_,double xa_,double ya_,double za_,double ang,int w_id_=-99) - : w_id(w_id_), xc(xc_), yc(yc_), zc(zc_), xa(xa_), ya(ya_), za(za_), - asi(1/(xa_*xa_+ya_*ya_+za_*za_)), - gra(tan(ang)), sang(sin(ang)), cang(cos(ang)) {} - bool point_inside(double x,double y,double z); - template - bool cut_cell_base(v_cell &c,double x,double y,double z); - bool cut_cell(voronoicell &c,double x,double y,double z) {return cut_cell_base(c,x,y,z);} - bool cut_cell(voronoicell_neighbor &c,double x,double y,double z) {return cut_cell_base(c,x,y,z);} - private: - const int w_id; - const double xc,yc,zc,xa,ya,za,asi,gra,sang,cang; -}; - -} - -#endif diff --git a/src/third_party/voro/src/worklist.hh b/src/third_party/voro/src/worklist.hh deleted file mode 100644 index ab5f3f9b5..000000000 --- a/src/third_party/voro/src/worklist.hh +++ /dev/null @@ -1,32 +0,0 @@ -// Voro++, a 3D cell-based Voronoi library -// -// Author : Chris H. Rycroft (Harvard University / LBL) -// Email : chr@alum.mit.edu -// Date : August 30th 2011 - -/** \file worklist.hh - * \brief Header file for setting constants used in the block worklists that are - * used during cell computation. - * - * This file is automatically generated by worklist_gen.pl and it is not - * intended to be edited by hand. */ - -#ifndef VOROPP_WORKLIST_HH -#define VOROPP_WORKLIST_HH - -namespace voro { - -/** Each region is divided into a grid of subregions, and a worklist is -# constructed for each. This parameter sets is set to half the number of -# subregions that the block is divided into. */ -const int wl_hgrid=4; -/** The number of subregions that a block is subdivided into, which is twice -the value of hgrid. */ -const int wl_fgrid=8; -/** The total number of worklists, set to the cube of hgrid. */ -const int wl_hgridcu=64; -/** The number of elements in each worklist. */ -const int wl_seq_length=64; - -} -#endif From 103497038f737d1310d1856686d9e34fbf532e91 Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Mon, 18 Dec 2023 00:38:52 -0800 Subject: [PATCH 16/36] Switch Voro to a Git Submodule --- .gitmodules | 3 +++ src/third_party/CMakeLists.txt | 8 ++++---- src/third_party/voro | 1 + 3 files changed, 8 insertions(+), 4 deletions(-) create mode 160000 src/third_party/voro diff --git a/.gitmodules b/.gitmodules index 338f77fd8..b58e714f5 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,6 @@ [submodule "bindings/python/third_party/nanobind"] path = bindings/python/third_party/nanobind url = https://github.com/wjakob/nanobind +[submodule "src/third_party/voro"] + path = src/third_party/voro + url = https://github.com/chr1shr/voro diff --git a/src/third_party/CMakeLists.txt b/src/third_party/CMakeLists.txt index 75d346b4f..91f17c4f0 100644 --- a/src/third_party/CMakeLists.txt +++ b/src/third_party/CMakeLists.txt @@ -8,8 +8,8 @@ add_library(quickhull quickhull/QuickHull.cpp) target_include_directories(quickhull PUBLIC quickhull) target_compile_features(quickhull PUBLIC cxx_std_17) +set(VORO_BUILD_SHARED_LIBS OFF CACHE BOOL "") +set(VORO_BUILD_EXAMPLES OFF CACHE BOOL "") +set(VORO_BUILD_CMD_LINE OFF CACHE BOOL "") +set(VORO_ENABLE_DOXYGEN OFF CACHE BOOL "") add_subdirectory(voro) -#file(GLOB VORO_SOURCES voro/src/*.cc) -#file(GLOB NOT_VORO_SOURCES voro/src/v_base_wl.cc src/voro++.cc) -#list(REMOVE_ITEM VORO_SOURCES ${NOT_VORO_SOURCES}) -#add_library(voro++ STATIC ${VORO_SOURCES}) diff --git a/src/third_party/voro b/src/third_party/voro new file mode 160000 index 000000000..56d619faf --- /dev/null +++ b/src/third_party/voro @@ -0,0 +1 @@ +Subproject commit 56d619faf3479313399516ad71c32773c29be859 From 76ca0d9dfcaacafce85ea0f55ffdeca2c5bd5ea9 Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Mon, 18 Dec 2023 01:01:15 -0800 Subject: [PATCH 17/36] Add a test --- src/manifold/src/manifold.cpp | 9 ++++----- test/manifold_test.cpp | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/manifold/src/manifold.cpp b/src/manifold/src/manifold.cpp index 3b4cbf4e6..c7511bbfc 100644 --- a/src/manifold/src/manifold.cpp +++ b/src/manifold/src/manifold.cpp @@ -931,7 +931,7 @@ std::vector Manifold::ConvexDecomposition() const { impl.vertPos_[impl.halfedge_[i].startVert]); float tangentProjection = glm::dot(impl.faceNormal_[faceB], tangent); // If we've found a pair of reflex triangles, add them to the set - if (tangentProjection > 0.0f) { + if (tangentProjection > 1e-6) { uniqueReflexFaceSet.insert(faceA); uniqueReflexFaceSet.insert(faceB); } @@ -958,13 +958,12 @@ std::vector Manifold::ConvexDecomposition() const { // vertices, TODO: and store in a hashmap std::mt19937 mt(1337); std::uniform_real_distribution dist(0.0, 1.0); - double randOffset = 0.0f; + double randOffset = 0.0; for (size_t i = 0; i < circumcenters.size() - 1; i++) { for (size_t j = i + 1; j < circumcenters.size(); j++) { - if (glm::distance(circumcenters[i], circumcenters[j]) < 0.00001) { + if (glm::distance(circumcenters[i], circumcenters[j]) < 1e-6) { joggledVerts[impl.halfedge_[(uniqueFaces[i] * 3) + 0].startVert] += - glm::dvec3(dist(mt) * 0.00000000001, dist(mt) * 0.00000000001, - dist(mt) * 0.00000000001); + glm::dvec3(dist(mt), dist(mt), dist(mt)) * 1e-11; } } } diff --git a/test/manifold_test.cpp b/test/manifold_test.cpp index 036f5182d..5da378ebd 100644 --- a/test/manifold_test.cpp +++ b/test/manifold_test.cpp @@ -767,3 +767,21 @@ TEST(Manifold, EmptyHull) { {0, 0, 0}, {1, 0, 0}, {0, 1, 0}, {1, 1, 0}}; EXPECT_TRUE(Manifold::Hull(coplanar).IsEmpty()); } + +TEST(Manifold, ConvexDecomposition) { + Manifold sphere = Manifold::Sphere(0.6, 20); + Manifold cube = Manifold::Cube({1.0, 1.0, 1.0}, true); + Manifold nonConvex = cube - sphere; + std::vector convexParts = nonConvex.ConvexDecomposition(); + + EXPECT_EQ(convexParts.size(), 224); + + float originalVolume = nonConvex.GetProperties().volume; + float convex_volume = 0.0; + Manifold manifold_union = Manifold::Manifold(); + for (Manifold cur_manifold : convexParts) { + manifold_union += cur_manifold.Hull(); + } + float union_volume = manifold_union.GetProperties().volume; + EXPECT_NEAR(originalVolume, union_volume, 1e-6); +} From 492135437f9a64400f32751ad38fa6e1f3bec17b Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Mon, 18 Dec 2023 01:33:12 -0800 Subject: [PATCH 18/36] Add untested bindings --- bindings/c/include/manifoldc.h | 5 ++++ bindings/c/manifoldc.cpp | 19 ++++++++++++++ bindings/wasm/bindings.cpp | 2 ++ bindings/wasm/bindings.js | 46 ++++++++++++++++++++++++++++++++++ 4 files changed, 72 insertions(+) diff --git a/bindings/c/include/manifoldc.h b/bindings/c/include/manifoldc.h index 8203405ab..5e57c03c3 100644 --- a/bindings/c/include/manifoldc.h +++ b/bindings/c/include/manifoldc.h @@ -166,6 +166,11 @@ ManifoldManifold *manifold_revolve(void *mem, ManifoldCrossSection *cs, ManifoldManifold *manifold_compose(void *mem, ManifoldManifoldVec *ms); ManifoldManifoldVec *manifold_decompose(void *mem, ManifoldManifold *m); ManifoldManifold *manifold_as_original(void *mem, ManifoldManifold *m); +ManifoldManifoldVec *manifold_fracture(void *mem, ManifoldManifold *m, + ManifoldVec3 *points, float *weights, + size_t length); +ManifoldManifoldVec *manifold_convex_decomposition(void *mem, + ManifoldManifold *m); // Manifold Info diff --git a/bindings/c/manifoldc.cpp b/bindings/c/manifoldc.cpp index 5e1ea9652..d6420ad94 100644 --- a/bindings/c/manifoldc.cpp +++ b/bindings/c/manifoldc.cpp @@ -405,6 +405,25 @@ ManifoldManifoldVec *manifold_decompose(void *mem, ManifoldManifold *m) { return to_c(new (mem) std::vector(comps)); } +ManifoldManifold *manifold_fracture(void *mem, ManifoldManifold *m, + ManifoldVec3 *points, float *weights, + size_t length) { + std::vector pts(length); + std::vector wts(length); + for (int i = 0; i < length; ++i) { + pts[i] = {points[i].x, points[i].y, points[i].z}; + wts[i] = weights[i]; + } + auto comps = from_c(m)->Fracture(pts, wts); + return to_c(new (mem) std::vector(comps)); +} + +ManifoldManifold *manifold_convex_decomposition(void *mem, + ManifoldManifold *m) { + auto comps = from_c(m)->ConvexDecomposition(); + return to_c(new (mem) std::vector(comps)); +} + ManifoldMeshGL *manifold_get_meshgl(void *mem, ManifoldManifold *m) { auto mesh = from_c(m)->GetMeshGL(); return to_c(new (mem) MeshGL(mesh)); diff --git a/bindings/wasm/bindings.cpp b/bindings/wasm/bindings.cpp index 778b94938..b7ada08aa 100644 --- a/bindings/wasm/bindings.cpp +++ b/bindings/wasm/bindings.cpp @@ -152,6 +152,8 @@ EMSCRIPTEN_BINDINGS(whatever) { .function("_Mirror", &Manifold::Mirror) .function("_Decompose", select_overload() const>( &Manifold::Decompose)) + .function("_Fracture", &Manifold::Fracture) + .function("_ConvexDecomposition", &Manifold::ConvexDecomposition) .function("isEmpty", &Manifold::IsEmpty) .function("status", &Manifold::Status) .function("numVert", &Manifold::NumVert) diff --git a/bindings/wasm/bindings.js b/bindings/wasm/bindings.js index ac49132c5..8010e2304 100644 --- a/bindings/wasm/bindings.js +++ b/bindings/wasm/bindings.js @@ -289,6 +289,52 @@ Module.setup = function() { return result; }; + Module.Manifold.prototype.fracture = function(points, weights) { + if (points instanceof Array){ + let pts = new Module.Vector_vec3(); + let wts = new Module.Vector_f32(); + for (const m of points) { + if (m instanceof Array && typeof m[0] == 'number' && m.length >= 3) { + pts.push_back({x: m[0], y: m[1], z: m[2]}); + if(m.length >= 4){ + wts.push_back(m[3]); + } + } else if (m.x) { + pts.push_back(m); + } else { + pushVec3(pts, m); + } + } + if (weights instanceof Array && weights.length == points.length) { + for (const m of weights) { + if (typeof m == 'number') { + wts.push_back(m); + } + } + } else { + if (wts.size() == 0){ + for (const m of points) { + wts.push_back(1.0); + } + } + } + + const vec = this._Fracture(pts, wts); + pts.delete(); + wts.delete(); + const result = fromVec(vec); + vec.delete(); + return result; + } + }; + + Module.Manifold.prototype.convexDecomposition = function() { + const vec = this._ConvexDecomposition(); + const result = fromVec(vec); + vec.delete(); + return result; + }; + Module.Manifold.prototype.boundingBox = function() { const result = this._boundingBox(); return { From d8fd65db9edd13ba5642c23c25d361a07e4cfd08 Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Mon, 18 Dec 2023 01:40:15 -0800 Subject: [PATCH 19/36] Minor Formatting --- bindings/c/include/manifoldc.h | 4 ++-- bindings/c/manifoldc.cpp | 6 +++--- bindings/wasm/bindings.js | 6 +++--- test/manifold_test.cpp | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/bindings/c/include/manifoldc.h b/bindings/c/include/manifoldc.h index 5e57c03c3..221a09b25 100644 --- a/bindings/c/include/manifoldc.h +++ b/bindings/c/include/manifoldc.h @@ -166,8 +166,8 @@ ManifoldManifold *manifold_revolve(void *mem, ManifoldCrossSection *cs, ManifoldManifold *manifold_compose(void *mem, ManifoldManifoldVec *ms); ManifoldManifoldVec *manifold_decompose(void *mem, ManifoldManifold *m); ManifoldManifold *manifold_as_original(void *mem, ManifoldManifold *m); -ManifoldManifoldVec *manifold_fracture(void *mem, ManifoldManifold *m, - ManifoldVec3 *points, float *weights, +ManifoldManifoldVec *manifold_fracture(void *mem, ManifoldManifold *m, + ManifoldVec3 *points, float *weights, size_t length); ManifoldManifoldVec *manifold_convex_decomposition(void *mem, ManifoldManifold *m); diff --git a/bindings/c/manifoldc.cpp b/bindings/c/manifoldc.cpp index d6420ad94..67519432f 100644 --- a/bindings/c/manifoldc.cpp +++ b/bindings/c/manifoldc.cpp @@ -405,8 +405,8 @@ ManifoldManifoldVec *manifold_decompose(void *mem, ManifoldManifold *m) { return to_c(new (mem) std::vector(comps)); } -ManifoldManifold *manifold_fracture(void *mem, ManifoldManifold *m, - ManifoldVec3 *points, float *weights, +ManifoldManifold *manifold_fracture(void *mem, ManifoldManifold *m, + ManifoldVec3 *points, float *weights, size_t length) { std::vector pts(length); std::vector wts(length); @@ -418,7 +418,7 @@ ManifoldManifold *manifold_fracture(void *mem, ManifoldManifold *m, return to_c(new (mem) std::vector(comps)); } -ManifoldManifold *manifold_convex_decomposition(void *mem, +ManifoldManifold *manifold_convex_decomposition(void *mem, ManifoldManifold *m) { auto comps = from_c(m)->ConvexDecomposition(); return to_c(new (mem) std::vector(comps)); diff --git a/bindings/wasm/bindings.js b/bindings/wasm/bindings.js index 8010e2304..70565de2f 100644 --- a/bindings/wasm/bindings.js +++ b/bindings/wasm/bindings.js @@ -290,13 +290,13 @@ Module.setup = function() { }; Module.Manifold.prototype.fracture = function(points, weights) { - if (points instanceof Array){ + if (points instanceof Array) { let pts = new Module.Vector_vec3(); let wts = new Module.Vector_f32(); for (const m of points) { if (m instanceof Array && typeof m[0] == 'number' && m.length >= 3) { pts.push_back({x: m[0], y: m[1], z: m[2]}); - if(m.length >= 4){ + if (m.length >= 4){ wts.push_back(m[3]); } } else if (m.x) { @@ -312,7 +312,7 @@ Module.setup = function() { } } } else { - if (wts.size() == 0){ + if (wts.size() == 0) { for (const m of points) { wts.push_back(1.0); } diff --git a/test/manifold_test.cpp b/test/manifold_test.cpp index 5da378ebd..c3b37774e 100644 --- a/test/manifold_test.cpp +++ b/test/manifold_test.cpp @@ -778,7 +778,7 @@ TEST(Manifold, ConvexDecomposition) { float originalVolume = nonConvex.GetProperties().volume; float convex_volume = 0.0; - Manifold manifold_union = Manifold::Manifold(); + Manifold manifold_union = convexParts[0].AsOriginal(); for (Manifold cur_manifold : convexParts) { manifold_union += cur_manifold.Hull(); } From 04ae42ec6f3f91f9ed53daafba88640954d1e228 Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Mon, 18 Dec 2023 01:41:07 -0800 Subject: [PATCH 20/36] More formatting --- bindings/wasm/bindings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/wasm/bindings.js b/bindings/wasm/bindings.js index 70565de2f..b481128af 100644 --- a/bindings/wasm/bindings.js +++ b/bindings/wasm/bindings.js @@ -296,7 +296,7 @@ Module.setup = function() { for (const m of points) { if (m instanceof Array && typeof m[0] == 'number' && m.length >= 3) { pts.push_back({x: m[0], y: m[1], z: m[2]}); - if (m.length >= 4){ + if (m.length >= 4) { wts.push_back(m[3]); } } else if (m.x) { From 7909f598fff04e51b671481bea244844bf07d621 Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Mon, 18 Dec 2023 11:34:06 -0800 Subject: [PATCH 21/36] Dummy Commit --- src/manifold/src/manifold.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/manifold/src/manifold.cpp b/src/manifold/src/manifold.cpp index c7511bbfc..d7a55bb66 100644 --- a/src/manifold/src/manifold.cpp +++ b/src/manifold/src/manifold.cpp @@ -892,10 +892,7 @@ std::vector Manifold::Fracture( y + 0.5 * c.pts[(vl.ps * i) + 1], z + 0.5 * c.pts[(vl.ps * i) + 2])); } - Manifold outputManifold = - Hull(verts) ^ - *this; // TODO: Replace this intersection with a voro++ wall - // implementation or cutting the cell directly... + Manifold outputManifold = Hull(verts) ^ *this; if (outputManifold.GetProperties().volume > 0.0) { output.push_back(outputManifold); } From 2505b1585c1f5107a2d50d09df89cb8843a11fd8 Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Mon, 18 Dec 2023 12:45:15 -0800 Subject: [PATCH 22/36] Parallelize Voronoi Computation --- src/manifold/src/manifold.cpp | 68 ++++++++++++++++++++++++----------- 1 file changed, 47 insertions(+), 21 deletions(-) diff --git a/src/manifold/src/manifold.cpp b/src/manifold/src/manifold.cpp index d7a55bb66..cc8b69361 100644 --- a/src/manifold/src/manifold.cpp +++ b/src/manifold/src/manifold.cpp @@ -64,6 +64,30 @@ struct UpdateProperties { } }; +struct ComputeVoronoiCell { + voro::container_poly* container; + const Manifold* original; + std::vector* output; + void operator()(thrust::tuple inOut) { + const int idx = thrust::get<0>(inOut); + glm::ivec3& cell_idx = thrust::get<1>(inOut); + glm::dvec4& cell_pos = thrust::get<2>(inOut); + voro::voronoicell c(*container); + + if (container->compute_cell(c, cell_idx.y, cell_idx.z)) { + std::vector verts; + verts.reserve(c.p); + for (size_t i = 0; i < c.p; i++) { + verts.push_back(glm::vec3(cell_pos.x + 0.5 * c.pts[(4 * i) + 0], + cell_pos.y + 0.5 * c.pts[(4 * i) + 1], + cell_pos.z + 0.5 * c.pts[(4 * i) + 2])); + } + (*output)[idx] = Manifold::Hull(verts) ^ *original; + verts.clear(); + } + } +}; + Manifold Halfspace(Box bBox, glm::vec3 normal, float originOffset) { normal = glm::normalize(normal); Manifold cutter = @@ -859,8 +883,10 @@ Manifold Manifold::Hull(const std::vector& manifolds) { std::vector Manifold::Fracture( const std::vector& pts, const std::vector& weights) const { + ZoneScoped; std::vector output; output.reserve(pts.size()); + output.resize(pts.size()); Box bounds = BoundingBox(); glm::vec3 min = bounds.min - 0.1f; @@ -870,35 +896,34 @@ std::vector Manifold::Fracture( voro::container_poly container(min.x, max.x, min.y, max.y, min.z, max.z, std::round(Nthird * (max.x - min.x)), std::round(Nthird * (max.y - min.y)), - std::round(Nthird * (max.z - min.z)), false, - false, false, pts.size()); + std::round(Nthird * (max.z - min.z)), // + false, false, false, pts.size()); bool hasWeights = weights.size() == pts.size(); for (size_t i = 0; i < pts.size(); i++) { container.put(i, pts[i].x, pts[i].y, pts[i].z, hasWeights ? weights[i] : 1.0f); } - voro::voronoicell c(container); + + // Prepare Parallel Voronoi Computation + std::vector cellIndices; + std::vector cellPosWeight; voro::c_loop_all vl(container); - if (vl.start()) do { // TODO: Parallelize this loop! - if (container.compute_cell(c, vl)) { - std::vector verts; - verts.reserve(c.p); - int id; - double x, y, z, r; - vl.pos(id, x, y, z, r); - for (size_t i = 0; i < c.p; i++) { - verts.push_back(glm::vec3(x + 0.5 * c.pts[(vl.ps * i) + 0], - y + 0.5 * c.pts[(vl.ps * i) + 1], - z + 0.5 * c.pts[(vl.ps * i) + 2])); - } - Manifold outputManifold = Hull(verts) ^ *this; - if (outputManifold.GetProperties().volume > 0.0) { - output.push_back(outputManifold); - } - verts.clear(); - } + if (vl.start()) do { + int id; + double x, y, z, r; + vl.pos(id, x, y, z, r); + cellIndices.push_back({id, vl.ijk, vl.q}); + cellPosWeight.push_back({x, y, z, r}); } while (vl.inc()); + + ComputeVoronoiCell computeVoronoiCell; + computeVoronoiCell.container = &container; + computeVoronoiCell.original = this; + computeVoronoiCell.output = &output; + thrust::for_each_n( + thrust::host, zip(countAt(0), cellIndices.begin(), cellPosWeight.begin()), + cellIndices.size(), computeVoronoiCell); return output; } std::vector Manifold::Fracture( @@ -914,6 +939,7 @@ std::vector Manifold::Fracture( } std::vector Manifold::ConvexDecomposition() const { + ZoneScoped; // Step 1. Get a list of all unique triangle faces with at least one reflex // edge std::unordered_set uniqueReflexFaceSet; From f86adc530d8902ef0aba1c9bbdd2295a26bd5333 Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Mon, 18 Dec 2023 12:53:12 -0800 Subject: [PATCH 23/36] Add Early Exit --- src/manifold/src/manifold.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/manifold/src/manifold.cpp b/src/manifold/src/manifold.cpp index cc8b69361..b2bfdae5a 100644 --- a/src/manifold/src/manifold.cpp +++ b/src/manifold/src/manifold.cpp @@ -959,6 +959,12 @@ std::vector Manifold::ConvexDecomposition() const { uniqueReflexFaceSet.insert(faceB); } } + + // Early-Exit if the part is already convex + if (uniqueReflexFaceSet.size() == 0) { + return std::vector(1, *this); + } + std::vector uniqueFaces; // Copy to a vector for indexed access uniqueFaces.insert(uniqueFaces.end(), uniqueReflexFaceSet.begin(), uniqueReflexFaceSet.end()); From c574580b55331615b3b1ae54c5e4e8d7238704cf Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Mon, 18 Dec 2023 14:03:59 -0800 Subject: [PATCH 24/36] Add a function for computing many convex hulls in parallel --- bindings/python/manifold3d.cpp | 6 ++++++ src/manifold/include/manifold.h | 2 ++ src/manifold/src/manifold.cpp | 28 +++++++++++++++++++++++----- 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/bindings/python/manifold3d.cpp b/bindings/python/manifold3d.cpp index 641456b5a..ace6120e2 100644 --- a/bindings/python/manifold3d.cpp +++ b/bindings/python/manifold3d.cpp @@ -200,6 +200,12 @@ NB_MODULE(manifold3d, m) { return Manifold::Hull(vec); }, "Compute the convex hull enveloping a set of 3d points.") + .def_static( + "batch_batch_hull", //Manifold::BatchHull, + [](std::vector > & ms) { + return Manifold::BatchHull(ms); + }, + "Compute the convex hulls enveloping multiple sets of manifolds.") .def( "transform", [](Manifold &self, nb::ndarray> &mat) { diff --git a/src/manifold/include/manifold.h b/src/manifold/include/manifold.h index fe9c419dd..2f028bd7a 100644 --- a/src/manifold/include/manifold.h +++ b/src/manifold/include/manifold.h @@ -247,6 +247,8 @@ class Manifold { Manifold Hull() const; static Manifold Hull(const std::vector& manifolds); static Manifold Hull(const std::vector& pts); + static std::vector BatchHull( + const std::vector>& manifolds); ///@} /** @name Voronoi Functions diff --git a/src/manifold/src/manifold.cpp b/src/manifold/src/manifold.cpp index b2bfdae5a..670f67328 100644 --- a/src/manifold/src/manifold.cpp +++ b/src/manifold/src/manifold.cpp @@ -873,16 +873,35 @@ Manifold Manifold::Hull(const std::vector& manifolds) { return Compose(manifolds).Hull(); } +/** + * Compute the convex hulls enveloping many sets of manifolds. + * + * @param manifolds A vector of vectors of manifolds over which compute hulls. + */ +std::vector Manifold::BatchHull( + const std::vector>& manifolds) { + ZoneScoped; + std::vector output; + output.reserve(manifolds.size()); + output.resize(manifolds.size()); + thrust::for_each_n( + thrust::host, zip(manifolds.begin(), output.begin()), manifolds.size(), + [](thrust::tuple, Manifold&> inOut) { + thrust::get<1>(inOut) = Manifold::Hull(thrust::get<0>(inOut)); + }); + return output; +} + // TODO: Handle Joggling in the Fracture Function Directly? /** * Compute the voronoi fracturing of this Manifold into convex chunks. * * @param pts A vector of points over which to fracture the manifold. - * @param pts A vector of weights controlling the relative size of each chunk. + * @param wts A vector of weights controlling the relative size of each chunk. */ std::vector Manifold::Fracture( const std::vector& pts, - const std::vector& weights) const { + const std::vector& wts) const { ZoneScoped; std::vector output; output.reserve(pts.size()); @@ -899,10 +918,9 @@ std::vector Manifold::Fracture( std::round(Nthird * (max.z - min.z)), // false, false, false, pts.size()); - bool hasWeights = weights.size() == pts.size(); + bool hasWeights = wts.size() == pts.size(); for (size_t i = 0; i < pts.size(); i++) { - container.put(i, pts[i].x, pts[i].y, pts[i].z, - hasWeights ? weights[i] : 1.0f); + container.put(i, pts[i].x, pts[i].y, pts[i].z, hasWeights ? wts[i] : 1.0f); } // Prepare Parallel Voronoi Computation From 1cb3a83ab6bd072fcc53161f90d4e320fd47d422 Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Mon, 18 Dec 2023 15:01:45 -0800 Subject: [PATCH 25/36] Add Minkowski Function --- bindings/python/manifold3d.cpp | 14 ++++++------ src/manifold/include/manifold.h | 7 ++++++ src/manifold/src/manifold.cpp | 39 +++++++++++++++++++++++++++++++++ 3 files changed, 53 insertions(+), 7 deletions(-) diff --git a/bindings/python/manifold3d.cpp b/bindings/python/manifold3d.cpp index ace6120e2..8fdb3d5b3 100644 --- a/bindings/python/manifold3d.cpp +++ b/bindings/python/manifold3d.cpp @@ -201,11 +201,11 @@ NB_MODULE(manifold3d, m) { }, "Compute the convex hull enveloping a set of 3d points.") .def_static( - "batch_batch_hull", //Manifold::BatchHull, - [](std::vector > & ms) { - return Manifold::BatchHull(ms); - }, + "batch_batch_hull", Manifold::BatchHull, "Compute the convex hulls enveloping multiple sets of manifolds.") + .def_static("minkowski", Manifold::Minkowski, nb::arg("a"), nb::arg("b"), + nb::arg("useNaive"), + "Compute the minkowski sum of two manifolds.") .def( "transform", [](Manifold &self, nb::ndarray> &mat) { @@ -687,9 +687,9 @@ NB_MODULE(manifold3d, m) { "Default is origin at the bottom.") .def_static( "sphere", - [](float radius, int circularSegments = 0) { - return Manifold::Sphere(radius, circularSegments); - }, + [](float radius, + int circularSegments = + 0) { return Manifold::Sphere(radius, circularSegments); }, nb::arg("radius"), nb::arg("circular_segments") = 0, "Constructs a geodesic sphere of a given radius.\n" "\n" diff --git a/src/manifold/include/manifold.h b/src/manifold/include/manifold.h index 2f028bd7a..5da03e689 100644 --- a/src/manifold/include/manifold.h +++ b/src/manifold/include/manifold.h @@ -261,6 +261,13 @@ class Manifold { std::vector ConvexDecomposition() const; ///@} + /** @name Minkowski Functions + */ + ///@{ + static Manifold Manifold::Minkowski(const Manifold& a, const Manifold& b, + bool useNaive = false); + ///@} + /** @name Testing hooks * These are just for internal testing. */ diff --git a/src/manifold/src/manifold.cpp b/src/manifold/src/manifold.cpp index 670f67328..2c65b2932 100644 --- a/src/manifold/src/manifold.cpp +++ b/src/manifold/src/manifold.cpp @@ -1032,4 +1032,43 @@ std::vector Manifold::ConvexDecomposition() const { return output; } + +/** + * Compute the minkowski sum of two manifolds. + * + * @param a The first manifold in the sum. + * @param b The second manifold in the sum. + */ +Manifold Manifold::Minkowski(const Manifold& a, const Manifold& b, + bool useNaive) { + std::vector aDecomposition = a.ConvexDecomposition(); + std::vector bDecomposition = b.ConvexDecomposition(); + + std::vector> composedParts; + std::vector composedHulls({a}); + if (!useNaive) { // Use the general method + for (Manifold aPart : aDecomposition) { + manifold::Mesh aMesh = aPart.GetMesh(); + for (Manifold bPart : bDecomposition) { + std::vector abComposition; + for (glm::vec3 vertexPosition : aMesh.vertPos) { + abComposition.push_back(bPart.Translate(vertexPosition)); + } + composedParts.push_back(abComposition); + } + } + auto newHulls = Manifold::BatchHull(composedParts); + composedHulls.insert(composedHulls.end(), newHulls.begin(), newHulls.end()); + } else { // Use the naive method, which is only valid when b is convex + manifold::Mesh aMesh = a.GetMesh(); + for (glm::ivec3 vertexIndices : aMesh.triVerts) { + composedParts.push_back({b.Translate(aMesh.vertPos[vertexIndices.x]), + b.Translate(aMesh.vertPos[vertexIndices.y]), + b.Translate(aMesh.vertPos[vertexIndices.z])}); + } + auto newHulls = Manifold::BatchHull(composedParts); + composedHulls.insert(composedHulls.end(), newHulls.begin(), newHulls.end()); + } + return Manifold::BatchBoolean(composedHulls, manifold::OpType::Add); +} } // namespace manifold From e84e05cdbf0af3ebcc1ce9c525aa8b76baa5bc71 Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Mon, 18 Dec 2023 15:05:30 -0800 Subject: [PATCH 26/36] Fix Formatting --- bindings/python/manifold3d.cpp | 2 +- src/manifold/src/manifold.cpp | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/bindings/python/manifold3d.cpp b/bindings/python/manifold3d.cpp index 8fdb3d5b3..74c2223e4 100644 --- a/bindings/python/manifold3d.cpp +++ b/bindings/python/manifold3d.cpp @@ -205,7 +205,7 @@ NB_MODULE(manifold3d, m) { "Compute the convex hulls enveloping multiple sets of manifolds.") .def_static("minkowski", Manifold::Minkowski, nb::arg("a"), nb::arg("b"), nb::arg("useNaive"), - "Compute the minkowski sum of two manifolds.") + "Compute the minkowski sum of two manifolds.") .def( "transform", [](Manifold &self, nb::ndarray> &mat) { diff --git a/src/manifold/src/manifold.cpp b/src/manifold/src/manifold.cpp index 2c65b2932..aeeb9fe73 100644 --- a/src/manifold/src/manifold.cpp +++ b/src/manifold/src/manifold.cpp @@ -899,9 +899,8 @@ std::vector Manifold::BatchHull( * @param pts A vector of points over which to fracture the manifold. * @param wts A vector of weights controlling the relative size of each chunk. */ -std::vector Manifold::Fracture( - const std::vector& pts, - const std::vector& wts) const { +std::vector Manifold::Fracture(const std::vector& pts, + const std::vector& wts) const { ZoneScoped; std::vector output; output.reserve(pts.size()); From a4d20c0154ea698b83b867c170c87be4047c7672 Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Mon, 18 Dec 2023 15:15:46 -0800 Subject: [PATCH 27/36] Remove Extra Qualification --- src/manifold/include/manifold.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/manifold/include/manifold.h b/src/manifold/include/manifold.h index 5da03e689..ce481e5fd 100644 --- a/src/manifold/include/manifold.h +++ b/src/manifold/include/manifold.h @@ -264,8 +264,8 @@ class Manifold { /** @name Minkowski Functions */ ///@{ - static Manifold Manifold::Minkowski(const Manifold& a, const Manifold& b, - bool useNaive = false); + static Manifold Minkowski(const Manifold& a, const Manifold& b, + bool useNaive = false); ///@} /** @name Testing hooks From 9c04c6d91d1d6608f4ed99b5c7404251430d6b3f Mon Sep 17 00:00:00 2001 From: pca006132 Date: Tue, 19 Dec 2023 08:19:24 +0800 Subject: [PATCH 28/36] added cgal convex decomposition test --- extras/CMakeLists.txt | 9 +++- extras/perf_test_cgal_cd.cpp | 89 ++++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 extras/perf_test_cgal_cd.cpp diff --git a/extras/CMakeLists.txt b/extras/CMakeLists.txt index 27824f270..d23ca7e6a 100644 --- a/extras/CMakeLists.txt +++ b/extras/CMakeLists.txt @@ -39,11 +39,18 @@ if(MANIFOLD_EXPORT) endif() if(BUILD_TEST_CGAL) - add_executable(perfTestCGAL perf_test_cgal.cpp) find_package(CGAL REQUIRED COMPONENTS Core) find_package(Boost REQUIRED COMPONENTS thread) + + add_executable(perfTestCGAL perf_test_cgal.cpp) target_compile_definitions(perfTestCGAL PRIVATE CGAL_USE_GMPXX) target_link_libraries(perfTestCGAL manifold CGAL::CGAL CGAL::CGAL_Core Boost::thread) target_compile_options(perfTestCGAL PRIVATE ${MANIFOLD_FLAGS}) target_compile_features(perfTestCGAL PUBLIC cxx_std_17) + + add_executable(perfTestCGALCD perf_test_cgal_cd.cpp) + target_compile_definitions(perfTestCGALCD PRIVATE CGAL_USE_GMPXX) + target_link_libraries(perfTestCGALCD manifold CGAL::CGAL CGAL::CGAL_Core Boost::thread) + target_compile_options(perfTestCGALCD PRIVATE ${MANIFOLD_FLAGS}) + target_compile_features(perfTestCGALCD PUBLIC cxx_std_17) endif() diff --git a/extras/perf_test_cgal_cd.cpp b/extras/perf_test_cgal_cd.cpp new file mode 100644 index 000000000..d55654bac --- /dev/null +++ b/extras/perf_test_cgal_cd.cpp @@ -0,0 +1,89 @@ +// Copyright 2023 The Manifold Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include + +#include +#include + +#include "manifold.h" + +using namespace manifold; + +using Kernel = CGAL::Epeck; +using Polyhedron = CGAL::Polyhedron_3; +using HalfedgeDS = Polyhedron::HalfedgeDS; +using NefPolyhedron = CGAL::Nef_polyhedron_3; + +template +class BuildFromManifold : public CGAL::Modifier_base { + public: + using Vertex = typename HDS::Vertex; + using Point = typename Vertex::Point; + BuildFromManifold(const Manifold manifold) : manifold(manifold) {} + void operator()(HDS &hds) { + // Postcondition: hds is a valid polyhedral surface. + CGAL::Polyhedron_incremental_builder_3 B(hds, true); + auto mesh = manifold.GetMeshGL(); + B.begin_surface(mesh.NumVert(), mesh.NumTri(), mesh.NumTri() * 3); + + for (size_t i = 0; i < mesh.vertProperties.size(); i += mesh.numProp) { + B.add_vertex(Point(mesh.vertProperties[i], mesh.vertProperties[i + 1], + mesh.vertProperties[i + 2])); + } + for (size_t i = 0; i < mesh.triVerts.size(); i += 3) { + B.begin_facet(); + for (const int j : {0, 1, 2}) B.add_vertex_to_facet(mesh.triVerts[i + j]); + B.end_facet(); + } + B.end_surface(); + } + + private: + const Manifold manifold; +}; + +int main(int argc, char **argv) { + Manifold spherecube = Manifold::Cube(glm::vec3(1), true) - Manifold::Sphere(0.6, 100); + BuildFromManifold build(spherecube); + std::cout << "nTri = " << spherecube.NumTri() << std::endl; + + auto start = std::chrono::high_resolution_clock::now(); + Polyhedron poly; + poly.delegate(build); + auto end = std::chrono::high_resolution_clock::now(); + std::chrono::duration elapsed = end - start; + std::cout << "to Polyhedron took " << elapsed.count() << " sec" + << std::endl; + + start = std::chrono::high_resolution_clock::now(); + NefPolyhedron np(poly); + end = std::chrono::high_resolution_clock::now(); + elapsed = end - start; + std::cout << "conversion to Nef Polyhedron took " << elapsed.count() + << " sec" << std::endl; + + start = std::chrono::high_resolution_clock::now(); + CGAL::convex_decomposition_3(np); + std::cout << "decomposed into " << np.number_of_volumes() << " parts" + << std::endl; + end = std::chrono::high_resolution_clock::now(); + elapsed = end - start; + std::cout << "decomposition took " << elapsed.count() << " sec" << std::endl + << std::endl; + return 0; +} From b6978bfaec1670ba9ce67fe0ca1a3964f5e5a694 Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Mon, 18 Dec 2023 17:10:21 -0800 Subject: [PATCH 29/36] Update CGAL Test with Direct Comparison... --- extras/perf_test_cgal_cd.cpp | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/extras/perf_test_cgal_cd.cpp b/extras/perf_test_cgal_cd.cpp index d55654bac..da2e9ec79 100644 --- a/extras/perf_test_cgal_cd.cpp +++ b/extras/perf_test_cgal_cd.cpp @@ -59,31 +59,36 @@ class BuildFromManifold : public CGAL::Modifier_base { int main(int argc, char **argv) { Manifold spherecube = Manifold::Cube(glm::vec3(1), true) - Manifold::Sphere(0.6, 100); + BuildFromManifold build(spherecube); std::cout << "nTri = " << spherecube.NumTri() << std::endl; auto start = std::chrono::high_resolution_clock::now(); Polyhedron poly; poly.delegate(build); - auto end = std::chrono::high_resolution_clock::now(); - std::chrono::duration elapsed = end - start; - std::cout << "to Polyhedron took " << elapsed.count() << " sec" + std::cout << "to Polyhedron took " + << (std::chrono::high_resolution_clock::now() - start).count() / 1e9 + << " sec" << std::endl; start = std::chrono::high_resolution_clock::now(); NefPolyhedron np(poly); - end = std::chrono::high_resolution_clock::now(); - elapsed = end - start; - std::cout << "conversion to Nef Polyhedron took " << elapsed.count() + std::cout << "conversion to Nef Polyhedron took " + << (std::chrono::high_resolution_clock::now() - start).count() / 1e9 << " sec" << std::endl; start = std::chrono::high_resolution_clock::now(); - CGAL::convex_decomposition_3(np); - std::cout << "decomposed into " << np.number_of_volumes() << " parts" + auto convexDecomposition = spherecube.ConvexDecomposition(); + std::cout << "[MANIFOLD] decomposed into " << convexDecomposition.size() << " parts in " + << (std::chrono::high_resolution_clock::now() - start).count() / 1e9 + << " sec" << std::endl; - end = std::chrono::high_resolution_clock::now(); - elapsed = end - start; - std::cout << "decomposition took " << elapsed.count() << " sec" << std::endl + + start = std::chrono::high_resolution_clock::now(); + CGAL::convex_decomposition_3(np); + std::cout << "[CGAL] decomposed into " << np.number_of_volumes() << " parts in " + << (std::chrono::high_resolution_clock::now() - start).count() / 1e9 + << " sec" << std::endl; return 0; } From 7ab872b1f9340a9bb78793cb53014290c417714d Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Mon, 18 Dec 2023 17:20:39 -0800 Subject: [PATCH 30/36] Add Minkowski Sum Test Too --- extras/perf_test_cgal_cd.cpp | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/extras/perf_test_cgal_cd.cpp b/extras/perf_test_cgal_cd.cpp index da2e9ec79..29abf37f5 100644 --- a/extras/perf_test_cgal_cd.cpp +++ b/extras/perf_test_cgal_cd.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -59,6 +60,7 @@ class BuildFromManifold : public CGAL::Modifier_base { int main(int argc, char **argv) { Manifold spherecube = Manifold::Cube(glm::vec3(1), true) - Manifold::Sphere(0.6, 100); + Manifold smallsphere = Manifold::Sphere(0.1, 20); BuildFromManifold build(spherecube); std::cout << "nTri = " << spherecube.NumTri() << std::endl; @@ -84,11 +86,35 @@ int main(int argc, char **argv) { << " sec" << std::endl; + start = std::chrono::high_resolution_clock::now(); + auto generalMinkowskiSum = Manifold::Minkowski(spherecube, smallsphere); + std::cout << "[MANIFOLD] general minkowski summed in " + << (std::chrono::high_resolution_clock::now() - start).count() / 1e9 + << " sec" << std::endl; + + start = std::chrono::high_resolution_clock::now(); + auto naiveMinkowskiSum = Manifold::Minkowski(spherecube, smallsphere, true); + std::cout << "[MANIFOLD] naive minkowski summed in " + << (std::chrono::high_resolution_clock::now() - start).count() / 1e9 + << " sec" << std::endl; + start = std::chrono::high_resolution_clock::now(); CGAL::convex_decomposition_3(np); std::cout << "[CGAL] decomposed into " << np.number_of_volumes() << " parts in " << (std::chrono::high_resolution_clock::now() - start).count() / 1e9 << " sec" << std::endl; + + // Create the Small Sphere NEF Polyhedron for Minkowski Summing + Polyhedron poly2; + poly.delegate(BuildFromManifold(smallsphere)); + NefPolyhedron np2(poly); + + start = std::chrono::high_resolution_clock::now(); + CGAL::minkowski_sum_3(np, np2); + std::cout << "[CGAL] minkowski summed in " + << (std::chrono::high_resolution_clock::now() - start).count() / 1e9 + << " sec" << std::endl; + return 0; } From 3446bb0914d4ac45ebe445e8e1415ceaae40c647 Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Tue, 19 Dec 2023 12:49:18 -0800 Subject: [PATCH 31/36] Remove the need for Joggling --- src/manifold/include/manifold.h | 1 + src/manifold/src/manifold.cpp | 91 ++++++++++++++------------------- 2 files changed, 40 insertions(+), 52 deletions(-) diff --git a/src/manifold/include/manifold.h b/src/manifold/include/manifold.h index bc6eb5fb1..b7798f5c6 100644 --- a/src/manifold/include/manifold.h +++ b/src/manifold/include/manifold.h @@ -260,6 +260,7 @@ class Manifold { const std::vector& weights) const; std::vector Fracture(const std::vector& pts, const std::vector& weights) const; + std::vector Manifold::ReflexFaces(double tolerance = 1e-8) const; std::vector ConvexDecomposition() const; ///@} diff --git a/src/manifold/src/manifold.cpp b/src/manifold/src/manifold.cpp index f2fe9844d..bf6d3a144 100644 --- a/src/manifold/src/manifold.cpp +++ b/src/manifold/src/manifold.cpp @@ -72,7 +72,7 @@ struct ComputeVoronoiCell { const int idx = thrust::get<0>(inOut); glm::ivec3& cell_idx = thrust::get<1>(inOut); glm::dvec4& cell_pos = thrust::get<2>(inOut); - voro::voronoicell c(*container); + voro::voronoicell_neighbor c(*container); if (container->compute_cell(c, cell_idx.y, cell_idx.z)) { std::vector verts; @@ -82,8 +82,11 @@ struct ComputeVoronoiCell { cell_pos.y + 0.5 * c.pts[(4 * i) + 1], cell_pos.z + 0.5 * c.pts[(4 * i) + 2])); } - (*output)[idx] = Manifold::Hull(verts) ^ *original; + (*output)[cell_idx.x] = Manifold::Hull(verts) ^ *original; verts.clear(); + } else { + std::cout << "[ERROR] Degenerate Voronoi Cell at Index: " << cell_idx.x + << std::endl; } } }; @@ -906,7 +909,6 @@ std::vector Manifold::BatchHull( return output; } -// TODO: Handle Joggling in the Fracture Function Directly? /** * Compute the voronoi fracturing of this Manifold into convex chunks. * @@ -969,81 +971,68 @@ std::vector Manifold::Fracture( return Fracture(highpVerts, highpWeights); } -std::vector Manifold::ConvexDecomposition() const { - ZoneScoped; - // Step 1. Get a list of all unique triangle faces with at least one reflex - // edge +std::vector Manifold::ReflexFaces(double tolerance) const { std::unordered_set uniqueReflexFaceSet; const Impl& impl = *GetCsgLeafNode().GetImpl(); for (size_t i = 0; i < impl.halfedge_.size(); i++) { Halfedge halfedge = impl.halfedge_[i]; int faceA = halfedge.face; int faceB = impl.halfedge_[halfedge.pairedHalfedge].face; - glm::vec3 tangent = - glm::cross(impl.faceNormal_[faceA], - impl.vertPos_[impl.halfedge_[i].endVert] - - impl.vertPos_[impl.halfedge_[i].startVert]); - float tangentProjection = glm::dot(impl.faceNormal_[faceB], tangent); + glm::dvec3 tangent = + glm::cross((glm::dvec3)impl.faceNormal_[faceA], + (glm::dvec3)impl.vertPos_[impl.halfedge_[i].endVert] - + (glm::dvec3)impl.vertPos_[impl.halfedge_[i].startVert]); + double tangentProjection = + glm::dot((glm::dvec3)impl.faceNormal_[faceB], tangent); // If we've found a pair of reflex triangles, add them to the set - if (tangentProjection > 1e-6) { + if (tangentProjection > tolerance) { uniqueReflexFaceSet.insert(faceA); uniqueReflexFaceSet.insert(faceB); } } - - // Early-Exit if the part is already convex - if (uniqueReflexFaceSet.size() == 0) { - return std::vector(1, *this); - } - std::vector uniqueFaces; // Copy to a vector for indexed access uniqueFaces.insert(uniqueFaces.end(), uniqueReflexFaceSet.begin(), uniqueReflexFaceSet.end()); + return uniqueFaces; +} + +std::vector Manifold::ConvexDecomposition() const { + ZoneScoped; + std::vector uniqueFaces = ReflexFaces(); + + // Early-Exit if the manifold is already convex + if (uniqueFaces.size() == 0) { + return std::vector(1, *this); + } - // Step 2. Calculate the Circumcircles (centers + radii) of these triangles + const Impl& impl = *GetCsgLeafNode().GetImpl(); std::vector circumcenters(uniqueFaces.size()); std::vector circumradii(uniqueFaces.size()); - Vec joggledVerts(impl.vertPos_.size()); + Vec dVerts(impl.vertPos_.size()); for (size_t i = 0; i < impl.vertPos_.size(); i++) { - joggledVerts[i] = impl.vertPos_[i]; + dVerts[i] = impl.vertPos_[i]; } for (size_t i = 0; i < uniqueFaces.size(); i++) { - glm::dvec4 circumcircle = impl.Circumcircle(joggledVerts, uniqueFaces[i]); + glm::dvec4 circumcircle = impl.Circumcircle(dVerts, uniqueFaces[i]); circumcenters[i] = glm::dvec3(circumcircle.x, circumcircle.y, circumcircle.z); circumradii[i] = circumcircle.w; } - // Step 3. If any two circumcenters are identical, joggle one of the triangle - // vertices, TODO: and store in a hashmap - std::mt19937 mt(1337); - std::uniform_real_distribution dist(0.0, 1.0); - double randOffset = 0.0; for (size_t i = 0; i < circumcenters.size() - 1; i++) { - for (size_t j = i + 1; j < circumcenters.size(); j++) { - if (glm::distance(circumcenters[i], circumcenters[j]) < 1e-6) { - joggledVerts[impl.halfedge_[(uniqueFaces[i] * 3) + 0].startVert] += - glm::dvec3(dist(mt), dist(mt), dist(mt)) * 1e-11; + for (size_t j = circumcenters.size() - 1; j > i; j--) { + if (glm::distance(circumcenters[i], circumcenters[j]) < 1e-9) { + std::vector::iterator it = circumcenters.begin(); + std::advance(it, j); + circumcenters.erase(it); + std::vector::iterator it2 = circumradii.begin(); + std::advance(it2, j); + circumradii.erase(it2); } } } - // Step 4. Recalculate the circumcenters - for (size_t i = 0; i < uniqueFaces.size(); i++) { - glm::dvec4 circumcircle = impl.Circumcircle(joggledVerts, uniqueFaces[i]); - circumcenters[i] = - glm::dvec3(circumcircle.x, circumcircle.y, circumcircle.z); - circumradii[i] = circumcircle.w; - } - - // Step 5. Calculate the Voronoi Fracturing - std::vector output = Fracture(circumcenters, circumradii); - - // TODO: - // Step 6. Unjoggle the voronoi region vertices - // Step 7. Hull and Intersect with the original Manifold - - return output; + return Fracture(circumcenters, circumradii); } /** @@ -1070,8 +1059,6 @@ Manifold Manifold::Minkowski(const Manifold& a, const Manifold& b, composedParts.push_back(abComposition); } } - auto newHulls = Manifold::BatchHull(composedParts); - composedHulls.insert(composedHulls.end(), newHulls.begin(), newHulls.end()); } else { // Use the naive method, which is only valid when b is convex manifold::Mesh aMesh = a.GetMesh(); for (glm::ivec3 vertexIndices : aMesh.triVerts) { @@ -1079,9 +1066,9 @@ Manifold Manifold::Minkowski(const Manifold& a, const Manifold& b, b.Translate(aMesh.vertPos[vertexIndices.y]), b.Translate(aMesh.vertPos[vertexIndices.z])}); } - auto newHulls = Manifold::BatchHull(composedParts); - composedHulls.insert(composedHulls.end(), newHulls.begin(), newHulls.end()); } + auto newHulls = Manifold::BatchHull(composedParts); + composedHulls.insert(composedHulls.end(), newHulls.begin(), newHulls.end()); return Manifold::BatchBoolean(composedHulls, manifold::OpType::Add); } } // namespace manifold From 258612cff3568d386c1a147a570ea500b84cecff Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Tue, 19 Dec 2023 13:05:12 -0800 Subject: [PATCH 32/36] Add Non-Convex - Non-Convex Implementation --- src/manifold/src/manifold.cpp | 45 ++++++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 8 deletions(-) diff --git a/src/manifold/src/manifold.cpp b/src/manifold/src/manifold.cpp index bf6d3a144..3b3b2b08a 100644 --- a/src/manifold/src/manifold.cpp +++ b/src/manifold/src/manifold.cpp @@ -1043,12 +1043,11 @@ std::vector Manifold::ConvexDecomposition() const { */ Manifold Manifold::Minkowski(const Manifold& a, const Manifold& b, bool useNaive) { - std::vector aDecomposition = a.ConvexDecomposition(); - std::vector bDecomposition = b.ConvexDecomposition(); - std::vector> composedParts; std::vector composedHulls({a}); if (!useNaive) { // Use the general method + std::vector aDecomposition = a.ConvexDecomposition(); + std::vector bDecomposition = b.ConvexDecomposition(); for (Manifold aPart : aDecomposition) { manifold::Mesh aMesh = aPart.GetMesh(); for (Manifold bPart : bDecomposition) { @@ -1059,12 +1058,42 @@ Manifold Manifold::Minkowski(const Manifold& a, const Manifold& b, composedParts.push_back(abComposition); } } - } else { // Use the naive method, which is only valid when b is convex + } else { // Use the naive method TODO: Add Deeper Multithreading + bool aConvex = a.ReflexFaces().size() == 0; + bool bConvex = b.ReflexFaces().size() == 0; manifold::Mesh aMesh = a.GetMesh(); - for (glm::ivec3 vertexIndices : aMesh.triVerts) { - composedParts.push_back({b.Translate(aMesh.vertPos[vertexIndices.x]), - b.Translate(aMesh.vertPos[vertexIndices.y]), - b.Translate(aMesh.vertPos[vertexIndices.z])}); + + // Convex-Convex Minkowski: Very Fast + if (aConvex && bConvex) { + std::vector simpleHull; + for (glm::vec3 vertex : aMesh.vertPos) { + simpleHull.push_back({b.Translate(vertex)}); + } + composedHulls.push_back(Manifold::Hull(simpleHull)); + // Convex - Non-Convex Minkowski: Slower + } else if (!aConvex && bConvex) { + for (glm::ivec3 vertexIndices : aMesh.triVerts) { + composedParts.push_back({b.Translate(aMesh.vertPos[vertexIndices.x]), + b.Translate(aMesh.vertPos[vertexIndices.y]), + b.Translate(aMesh.vertPos[vertexIndices.z])}); + } + // Non-Convex - Non-Convex Minkowski: Very Slow + } else if (!aConvex && !bConvex) { + manifold::Mesh bMesh = b.GetMesh(); + for (glm::ivec3 aVertexIndices : aMesh.triVerts) { + for (glm::ivec3 bVertexIndices : bMesh.triVerts) { + composedHulls.push_back(Manifold::Hull( + {aMesh.vertPos[aVertexIndices.x] + bMesh.vertPos[bVertexIndices.x], + aMesh.vertPos[aVertexIndices.x] + bMesh.vertPos[bVertexIndices.y], + aMesh.vertPos[aVertexIndices.x] + bMesh.vertPos[bVertexIndices.z], + aMesh.vertPos[aVertexIndices.y] + bMesh.vertPos[bVertexIndices.x], + aMesh.vertPos[aVertexIndices.y] + bMesh.vertPos[bVertexIndices.y], + aMesh.vertPos[aVertexIndices.y] + bMesh.vertPos[bVertexIndices.z], + aMesh.vertPos[aVertexIndices.z] + bMesh.vertPos[bVertexIndices.x], + aMesh.vertPos[aVertexIndices.z] + bMesh.vertPos[bVertexIndices.y], + aMesh.vertPos[aVertexIndices.z] + bMesh.vertPos[bVertexIndices.z]})); + } + } } } auto newHulls = Manifold::BatchHull(composedParts); From 3dcd3a5e3ae7a04b33d308a34cbaf33d8d18a258 Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Tue, 19 Dec 2023 13:39:37 -0800 Subject: [PATCH 33/36] Handle alternate order submission --- src/manifold/src/manifold.cpp | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/manifold/src/manifold.cpp b/src/manifold/src/manifold.cpp index 3b3b2b08a..3d21015e8 100644 --- a/src/manifold/src/manifold.cpp +++ b/src/manifold/src/manifold.cpp @@ -1061,25 +1061,34 @@ Manifold Manifold::Minkowski(const Manifold& a, const Manifold& b, } else { // Use the naive method TODO: Add Deeper Multithreading bool aConvex = a.ReflexFaces().size() == 0; bool bConvex = b.ReflexFaces().size() == 0; - manifold::Mesh aMesh = a.GetMesh(); + + // If the convex manifold was supplied first, swap them! + Manifold aM = a, bM = b; + if (aConvex && !bConvex) { + aM = b; bM = a; + aConvex = !aConvex; + bConvex = !bConvex; + } + + manifold::Mesh aMesh = aM.GetMesh(); // Convex-Convex Minkowski: Very Fast if (aConvex && bConvex) { std::vector simpleHull; for (glm::vec3 vertex : aMesh.vertPos) { - simpleHull.push_back({b.Translate(vertex)}); + simpleHull.push_back({bM.Translate(vertex)}); } composedHulls.push_back(Manifold::Hull(simpleHull)); // Convex - Non-Convex Minkowski: Slower } else if (!aConvex && bConvex) { for (glm::ivec3 vertexIndices : aMesh.triVerts) { - composedParts.push_back({b.Translate(aMesh.vertPos[vertexIndices.x]), - b.Translate(aMesh.vertPos[vertexIndices.y]), - b.Translate(aMesh.vertPos[vertexIndices.z])}); + composedParts.push_back({bM.Translate(aMesh.vertPos[vertexIndices.x]), + bM.Translate(aMesh.vertPos[vertexIndices.y]), + bM.Translate(aMesh.vertPos[vertexIndices.z])}); } // Non-Convex - Non-Convex Minkowski: Very Slow } else if (!aConvex && !bConvex) { - manifold::Mesh bMesh = b.GetMesh(); + manifold::Mesh bMesh = bM.GetMesh(); for (glm::ivec3 aVertexIndices : aMesh.triVerts) { for (glm::ivec3 bVertexIndices : bMesh.triVerts) { composedHulls.push_back(Manifold::Hull( From 6a8a407e28f019dfb787616416826141556aae0f Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Tue, 19 Dec 2023 14:27:40 -0800 Subject: [PATCH 34/36] Finish Multithreading Naive Function --- src/manifold/src/manifold.cpp | 95 +++++++++++++++++++++++++++++------ 1 file changed, 80 insertions(+), 15 deletions(-) diff --git a/src/manifold/src/manifold.cpp b/src/manifold/src/manifold.cpp index 3d21015e8..01373d2b8 100644 --- a/src/manifold/src/manifold.cpp +++ b/src/manifold/src/manifold.cpp @@ -91,6 +91,41 @@ struct ComputeVoronoiCell { } }; +struct ComputeTriangleHull { + const Manifold* bM; + std::vector* vertPos; + std::vector* output; + void operator()(thrust::tuple inOut) { + const int idx = thrust::get<0>(inOut); + glm::ivec3& tri = thrust::get<1>(inOut); + (*output)[idx] = Manifold::Hull({(*bM).Translate((*vertPos)[tri.x]), + (*bM).Translate((*vertPos)[tri.y]), + (*bM).Translate((*vertPos)[tri.z])}); + } +}; + +struct ComputeTriangleTriangleHull { + std::vector* vertPosAPtr; + std::vector* vertPosBPtr; + std::vector* output; + void operator()(thrust::tuple> inOut) { + const int idx = thrust::get<0>(inOut); + std::pair tris = thrust::get<1>(inOut); + auto vertPosA = *vertPosAPtr; + auto vertPosB = *vertPosBPtr; + (*output)[idx] = Manifold::Hull( + {vertPosA[tris.first.x] + vertPosB[tris.second.x], + vertPosA[tris.first.x] + vertPosB[tris.second.y], + vertPosA[tris.first.x] + vertPosB[tris.second.z], + vertPosA[tris.first.y] + vertPosB[tris.second.x], + vertPosA[tris.first.y] + vertPosB[tris.second.y], + vertPosA[tris.first.y] + vertPosB[tris.second.z], + vertPosA[tris.first.z] + vertPosB[tris.second.x], + vertPosA[tris.first.z] + vertPosB[tris.second.y], + vertPosA[tris.first.z] + vertPosB[tris.second.z]}); + } +}; + Manifold Halfspace(Box bBox, glm::vec3 normal, float originOffset) { normal = glm::normalize(normal); Manifold cutter = @@ -1081,28 +1116,58 @@ Manifold Manifold::Minkowski(const Manifold& a, const Manifold& b, composedHulls.push_back(Manifold::Hull(simpleHull)); // Convex - Non-Convex Minkowski: Slower } else if (!aConvex && bConvex) { - for (glm::ivec3 vertexIndices : aMesh.triVerts) { - composedParts.push_back({bM.Translate(aMesh.vertPos[vertexIndices.x]), - bM.Translate(aMesh.vertPos[vertexIndices.y]), - bM.Translate(aMesh.vertPos[vertexIndices.z])}); - } + // Speed Equivalent? + //for (glm::ivec3 vertexIndices : aMesh.triVerts) { + // composedParts.push_back({bM.Translate(aMesh.vertPos[vertexIndices.x]), + // bM.Translate(aMesh.vertPos[vertexIndices.y]), + // bM.Translate(aMesh.vertPos[vertexIndices.z])}); + //} + composedHulls.resize(aMesh.triVerts.size() + 1); + thrust::for_each_n( + thrust::host, zip(countAt(1), aMesh.triVerts.begin()), + aMesh.triVerts.size(), + ComputeTriangleHull({&bM, &aMesh.vertPos, &composedHulls})); // Non-Convex - Non-Convex Minkowski: Very Slow } else if (!aConvex && !bConvex) { manifold::Mesh bMesh = bM.GetMesh(); + + // Speed Equivalent? + // for (glm::ivec3 aVertexIndices : aMesh.triVerts) { + // for (glm::ivec3 bVertexIndices : bMesh.triVerts) { + // composedHulls.push_back(Manifold::Hull( + // {aMesh.vertPos[aVertexIndices.x] + + // bMesh.vertPos[bVertexIndices.x], + // aMesh.vertPos[aVertexIndices.x] + + // bMesh.vertPos[bVertexIndices.y], + // aMesh.vertPos[aVertexIndices.x] + + // bMesh.vertPos[bVertexIndices.z], + // aMesh.vertPos[aVertexIndices.y] + + // bMesh.vertPos[bVertexIndices.x], + // aMesh.vertPos[aVertexIndices.y] + + // bMesh.vertPos[bVertexIndices.y], + // aMesh.vertPos[aVertexIndices.y] + + // bMesh.vertPos[bVertexIndices.z], + // aMesh.vertPos[aVertexIndices.z] + + // bMesh.vertPos[bVertexIndices.x], + // aMesh.vertPos[aVertexIndices.z] + + // bMesh.vertPos[bVertexIndices.y], + // aMesh.vertPos[aVertexIndices.z] + + // bMesh.vertPos[bVertexIndices.z]})); + // } + //} + + std::vector> trianglePairs; for (glm::ivec3 aVertexIndices : aMesh.triVerts) { for (glm::ivec3 bVertexIndices : bMesh.triVerts) { - composedHulls.push_back(Manifold::Hull( - {aMesh.vertPos[aVertexIndices.x] + bMesh.vertPos[bVertexIndices.x], - aMesh.vertPos[aVertexIndices.x] + bMesh.vertPos[bVertexIndices.y], - aMesh.vertPos[aVertexIndices.x] + bMesh.vertPos[bVertexIndices.z], - aMesh.vertPos[aVertexIndices.y] + bMesh.vertPos[bVertexIndices.x], - aMesh.vertPos[aVertexIndices.y] + bMesh.vertPos[bVertexIndices.y], - aMesh.vertPos[aVertexIndices.y] + bMesh.vertPos[bVertexIndices.z], - aMesh.vertPos[aVertexIndices.z] + bMesh.vertPos[bVertexIndices.x], - aMesh.vertPos[aVertexIndices.z] + bMesh.vertPos[bVertexIndices.y], - aMesh.vertPos[aVertexIndices.z] + bMesh.vertPos[bVertexIndices.z]})); + trianglePairs.push_back({aVertexIndices, bVertexIndices}); } } + composedHulls.resize(trianglePairs.size() + 1); + thrust::for_each_n( + thrust::host, zip(countAt(1), trianglePairs.begin()), + trianglePairs.size(), + ComputeTriangleTriangleHull( + {&aMesh.vertPos, &bMesh.vertPos, &composedHulls})); } } auto newHulls = Manifold::BatchHull(composedParts); From c4537e75a31b861a4551ef70f3dc06e5e752f8aa Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Tue, 19 Dec 2023 14:29:33 -0800 Subject: [PATCH 35/36] Fix Formatting --- extras/perf_test_cgal_cd.cpp | 18 +++++++++--------- src/manifold/src/manifold.cpp | 32 ++++++++++++++++---------------- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/extras/perf_test_cgal_cd.cpp b/extras/perf_test_cgal_cd.cpp index 29abf37f5..3f7eb32d3 100644 --- a/extras/perf_test_cgal_cd.cpp +++ b/extras/perf_test_cgal_cd.cpp @@ -59,7 +59,8 @@ class BuildFromManifold : public CGAL::Modifier_base { }; int main(int argc, char **argv) { - Manifold spherecube = Manifold::Cube(glm::vec3(1), true) - Manifold::Sphere(0.6, 100); + Manifold spherecube = + Manifold::Cube(glm::vec3(1), true) - Manifold::Sphere(0.6, 100); Manifold smallsphere = Manifold::Sphere(0.1, 20); BuildFromManifold build(spherecube); @@ -70,8 +71,7 @@ int main(int argc, char **argv) { poly.delegate(build); std::cout << "to Polyhedron took " << (std::chrono::high_resolution_clock::now() - start).count() / 1e9 - << " sec" - << std::endl; + << " sec" << std::endl; start = std::chrono::high_resolution_clock::now(); NefPolyhedron np(poly); @@ -81,10 +81,10 @@ int main(int argc, char **argv) { start = std::chrono::high_resolution_clock::now(); auto convexDecomposition = spherecube.ConvexDecomposition(); - std::cout << "[MANIFOLD] decomposed into " << convexDecomposition.size() << " parts in " + std::cout << "[MANIFOLD] decomposed into " << convexDecomposition.size() + << " parts in " << (std::chrono::high_resolution_clock::now() - start).count() / 1e9 - << " sec" - << std::endl; + << " sec" << std::endl; start = std::chrono::high_resolution_clock::now(); auto generalMinkowskiSum = Manifold::Minkowski(spherecube, smallsphere); @@ -100,10 +100,10 @@ int main(int argc, char **argv) { start = std::chrono::high_resolution_clock::now(); CGAL::convex_decomposition_3(np); - std::cout << "[CGAL] decomposed into " << np.number_of_volumes() << " parts in " + std::cout << "[CGAL] decomposed into " << np.number_of_volumes() + << " parts in " << (std::chrono::high_resolution_clock::now() - start).count() / 1e9 - << " sec" - << std::endl; + << " sec" << std::endl; // Create the Small Sphere NEF Polyhedron for Minkowski Summing Polyhedron poly2; diff --git a/src/manifold/src/manifold.cpp b/src/manifold/src/manifold.cpp index 01373d2b8..4382ff410 100644 --- a/src/manifold/src/manifold.cpp +++ b/src/manifold/src/manifold.cpp @@ -113,16 +113,16 @@ struct ComputeTriangleTriangleHull { std::pair tris = thrust::get<1>(inOut); auto vertPosA = *vertPosAPtr; auto vertPosB = *vertPosBPtr; - (*output)[idx] = Manifold::Hull( - {vertPosA[tris.first.x] + vertPosB[tris.second.x], - vertPosA[tris.first.x] + vertPosB[tris.second.y], - vertPosA[tris.first.x] + vertPosB[tris.second.z], - vertPosA[tris.first.y] + vertPosB[tris.second.x], - vertPosA[tris.first.y] + vertPosB[tris.second.y], - vertPosA[tris.first.y] + vertPosB[tris.second.z], - vertPosA[tris.first.z] + vertPosB[tris.second.x], - vertPosA[tris.first.z] + vertPosB[tris.second.y], - vertPosA[tris.first.z] + vertPosB[tris.second.z]}); + (*output)[idx] = + Manifold::Hull({vertPosA[tris.first.x] + vertPosB[tris.second.x], + vertPosA[tris.first.x] + vertPosB[tris.second.y], + vertPosA[tris.first.x] + vertPosB[tris.second.z], + vertPosA[tris.first.y] + vertPosB[tris.second.x], + vertPosA[tris.first.y] + vertPosB[tris.second.y], + vertPosA[tris.first.y] + vertPosB[tris.second.z], + vertPosA[tris.first.z] + vertPosB[tris.second.x], + vertPosA[tris.first.z] + vertPosB[tris.second.y], + vertPosA[tris.first.z] + vertPosB[tris.second.z]}); } }; @@ -1099,8 +1099,9 @@ Manifold Manifold::Minkowski(const Manifold& a, const Manifold& b, // If the convex manifold was supplied first, swap them! Manifold aM = a, bM = b; - if (aConvex && !bConvex) { - aM = b; bM = a; + if (aConvex && !bConvex) { + aM = b; + bM = a; aConvex = !aConvex; bConvex = !bConvex; } @@ -1117,7 +1118,7 @@ Manifold Manifold::Minkowski(const Manifold& a, const Manifold& b, // Convex - Non-Convex Minkowski: Slower } else if (!aConvex && bConvex) { // Speed Equivalent? - //for (glm::ivec3 vertexIndices : aMesh.triVerts) { + // for (glm::ivec3 vertexIndices : aMesh.triVerts) { // composedParts.push_back({bM.Translate(aMesh.vertPos[vertexIndices.x]), // bM.Translate(aMesh.vertPos[vertexIndices.y]), // bM.Translate(aMesh.vertPos[vertexIndices.z])}); @@ -1163,9 +1164,8 @@ Manifold Manifold::Minkowski(const Manifold& a, const Manifold& b, } } composedHulls.resize(trianglePairs.size() + 1); - thrust::for_each_n( - thrust::host, zip(countAt(1), trianglePairs.begin()), - trianglePairs.size(), + thrust::for_each_n(thrust::host, zip(countAt(1), trianglePairs.begin()), + trianglePairs.size(), ComputeTriangleTriangleHull( {&aMesh.vertPos, &bMesh.vertPos, &composedHulls})); } From 19c58ca73e853f74e9bf6e3041d77ca8aaba0742 Mon Sep 17 00:00:00 2001 From: Johnathon Selstad Date: Tue, 19 Dec 2023 18:16:26 -0800 Subject: [PATCH 36/36] Detect Degenerate Triangles --- src/manifold/src/face_op.cpp | 9 +++++++++ src/manifold/src/manifold.cpp | 34 ++++++++++++++++++++++++++++++---- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/src/manifold/src/face_op.cpp b/src/manifold/src/face_op.cpp index 1207009e0..3ba371868 100644 --- a/src/manifold/src/face_op.cpp +++ b/src/manifold/src/face_op.cpp @@ -324,8 +324,10 @@ glm::dvec4 Manifold::Impl::Circumcircle(Vec verts, int face) const { glm::dvec3 a = va - vc; glm::dvec3 b = vb - vc; + glm::dvec3 c = va - vb; double a_length = glm::length(a); double b_length = glm::length(b); + double c_length = glm::length(c); glm::dvec3 numerator = glm::cross((((a_length * a_length) * b) - ((b_length * b_length) * a)), glm::cross(a, b)); @@ -333,6 +335,13 @@ glm::dvec4 Manifold::Impl::Circumcircle(Vec verts, int face) const { double denominator = 2.0 * (crs * crs); glm::dvec3 circumcenter = (numerator / denominator) + vc; double circumradius = glm::length(circumcenter - vc); + + double max_length = std::fmax(a_length, std::fmax(b_length, c_length)); + double min_length = std::fmin(a_length, std::fmin(b_length, c_length)); + if (max_length / min_length > 15.0) { + circumradius *= -1.0; // Mark this triangle as degenerate + } + return glm::dvec4(circumcenter.x, circumcenter.y, circumcenter.z, circumradius); } diff --git a/src/manifold/src/manifold.cpp b/src/manifold/src/manifold.cpp index 4382ff410..1d612ed04 100644 --- a/src/manifold/src/manifold.cpp +++ b/src/manifold/src/manifold.cpp @@ -1033,9 +1033,9 @@ std::vector Manifold::ReflexFaces(double tolerance) const { std::vector Manifold::ConvexDecomposition() const { ZoneScoped; - std::vector uniqueFaces = ReflexFaces(); // Early-Exit if the manifold is already convex + std::vector uniqueFaces = ReflexFaces(); if (uniqueFaces.size() == 0) { return std::vector(1, *this); } @@ -1047,16 +1047,40 @@ std::vector Manifold::ConvexDecomposition() const { for (size_t i = 0; i < impl.vertPos_.size(); i++) { dVerts[i] = impl.vertPos_[i]; } + std::vector debugShapes; for (size_t i = 0; i < uniqueFaces.size(); i++) { glm::dvec4 circumcircle = impl.Circumcircle(dVerts, uniqueFaces[i]); circumcenters[i] = glm::dvec3(circumcircle.x, circumcircle.y, circumcircle.z); circumradii[i] = circumcircle.w; + + // Debug Draw the Circumcenter and Triangle of Degenerate Triangles + // if (circumcircle.w < 0.0) { + // std::cout << "Circumradius: " << circumcircle.w << std::endl; + // debugShapes.push_back(Hull( + // {Manifold::Sphere(circumcircle.w * 0.1, 10) + // .Translate(glm::vec3( + // dVerts[impl.halfedge_[(uniqueFaces[i] * 3) + + // 0].startVert])), + // Manifold::Sphere(circumcircle.w * 0.1, 10) + // .Translate(glm::vec3( + // dVerts[impl.halfedge_[(uniqueFaces[i] * 3) + + // 1].startVert])), + // Manifold::Sphere(circumcircle.w * 0.1, 10) + // .Translate(glm::vec3( + // dVerts[impl.halfedge_[(uniqueFaces[i] * 3) + + // 2].startVert])) + // })); + // + // debugShapes.push_back(Manifold::Sphere(circumcircle.w * 0.2, 10) + // .Translate(glm::vec3(circumcenters[i]))); + // } } for (size_t i = 0; i < circumcenters.size() - 1; i++) { for (size_t j = circumcenters.size() - 1; j > i; j--) { - if (glm::distance(circumcenters[i], circumcenters[j]) < 1e-9) { + if (glm::distance(circumcenters[i], circumcenters[j]) < 1e-9 || + circumradii[j] < 0.0) { std::vector::iterator it = circumcenters.begin(); std::advance(it, j); circumcenters.erase(it); @@ -1067,7 +1091,9 @@ std::vector Manifold::ConvexDecomposition() const { } } - return Fracture(circumcenters, circumradii); + std::vector output = Fracture(circumcenters, circumradii); + output.insert(output.end(), debugShapes.begin(), debugShapes.end()); + return output; } /** @@ -1093,7 +1119,7 @@ Manifold Manifold::Minkowski(const Manifold& a, const Manifold& b, composedParts.push_back(abComposition); } } - } else { // Use the naive method TODO: Add Deeper Multithreading + } else { // Use the naive method bool aConvex = a.ReflexFaces().size() == 0; bool bConvex = b.ReflexFaces().size() == 0;