// clang-format off /* 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. */ // Voro++, a 3D cell-based Voronoi library // // Author : Chris H. Rycroft (LBL / UC Berkeley) // Email : chr@alum.mit.edu // Date : August 30th 2011 // // Modified by PM Larsen for use in Polyhedral Template Matching /** \file cell.cc * \brief Function implementations for the voronoicell and related classes. */ #include #include #include #include "ptm_voronoi_config.h" #include "ptm_voronoi_cell.h" namespace ptm_voro { inline void voro_fatal_error(const char *p,int status) { fprintf(stderr,"voro++: %s\n",p); exit(status); //return -1;//status; } /** Constructs a Voronoi cell and sets up the initial memory. */ voronoicell_base::voronoicell_base() : current_vertices(init_vertices), current_vertex_order(init_vertex_order), current_delete_size(init_delete_size), current_delete2_size(init_delete2_size), ed(new int*[current_vertices]), nu(new int[current_vertices]), pts(new double[3*current_vertices]), 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_delete_size), current_marginal(init_marginal), marg(new int[current_marginal]) { int i; for (i=0;i<3;i++) { mem[i]=init_n_vertices;mec[i]=0; mep[i]=new int[init_n_vertices*((i<<1)+1)]; } mem[3]=init_3_vertices;mec[3]=0; mep[3]=new int[init_3_vertices*7]; for (i=4;i=0;i--) if (mem[i]>0) delete [] mep[i]; delete [] marg; delete [] ds2;delete [] ds; delete [] mep;delete [] mec; delete [] mem;delete [] pts; 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,ds2); while (current_verticesp) add_memory_vertices(vc); } /** Increases the memory storage for a particular vertex order, by increasing * the size of the of the corresponding mep array. If the arrays already exist, * their size is doubled; if they don't exist, then new ones of size * init_n_vertices are allocated. The routine also ensures that the pointers in * the ed array are updated, by making use of the back pointers. For the cases * where the back pointer has been temporarily overwritten in the marginal * vertex code, the auxiliary delete stack is scanned to find out how to update * the ed value. If the template has been instantiated with the neighbor * tracking turned on, then the routine also reallocates the corresponding mne * array. * \param[in] i the order of the vertex memory to be increased. */ template void voronoicell_base::add_memory(vc_class &vc,int i,int *stackp2) { 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) { printf("nope: %d\n", current_vertices); exit(3); int i=(current_vertices<<1),j,**pp,*pnu; 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 (dsp inline bool voronoicell_base::search_for_outside_edge(vc_class &vc,int &up) { int i,lp,lw,*j(ds2),*stackp2(ds2); double l; *(stackp2++)=up; while (j inline void voronoicell_base::add_to_stack(vc_class &vc,int lp,int *&stackp2) { (void)vc; for (int *k(ds2);k bool voronoicell_base::nplane(vc_class &vc,double x,double y,double z,double rsq,int p_id) { int count=0,i,j,k,lp=up,cp,qp,rp,*stackp(ds),*stackp2(ds2),*dsp; int us=0,ls=0,qs,iqs,cs,uw,qw,lw; int *edp,*edd; double u,l,r,q;bool complicated_setup=false,new_double_edge=false,double_edge=false; // Initialize the safe testing routine n_marg=0;px=x;py=y;pz=z;prsq=rsq; // Test approximately sqrt(n)/4 points for their proximity to the plane // and keep the one which is closest uw=m_test(up,u); // Starting from an initial guess, we now move from vertex to vertex, // to try and find an edge which intersects the cutting plane, // or a vertex which is on the plane try { if (uw==1) { // The test point is inside the cutting plane. us=0; do { lp=ed[up][us]; lw=m_test(lp,l); if (l=p) throw true; u=l;up=lp; for (us=0;us=p) throw true; u=q;up=qp; for (us=0;us=1 fputs("Bailed out of convex calculation\n",stderr); #endif qw=1;lw=0; for (qp=0;qp=current_vertex_order) add_memory_vorder(vc); if (mec[nu[p]]==mem[nu[p]]) add_memory(vc,nu[p],stackp2); 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],stackp2); // 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==0) { // 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==0) { // 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,stackp2); // 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 (ids) { --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); } /** During the creation of a new facet in the plane routine, it is possible * that some order two vertices may arise. This routine removes them. * Suppose an order two vertex joins c and d. If there's a edge between * c and d already, then the order two vertex is just removed; otherwise, * the order two vertex is removed and c and d are joined together directly. * It is possible this process will create order two or order one vertices, * and the routine is continually run until all of them are removed. * \return False if the vertex removal was unsuccessful, indicative of the cell * reducing to zero volume and disappearing; true if the vertex removal * was successful. */ template 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 inline 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[3*i]=pts[3*p]; pts[3*i+1]=pts[3*p+1]; pts[3*i+2]=pts[3*p+2]; for (k=0;k inline 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,ds2); vc.n_set_aux1(i); for (l=0;l &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[3*k]-pts[3*i]; uy=pts[3*k+1]-pts[3*i+1]; uz=pts[3*k+2]-pts[3*i+2]; vx=pts[3*m]-pts[3*i]; vy=pts[3*m+1]-pts[3*i+1]; vz=pts[3*m+2]-pts[3*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(); } /** 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 int voronoicell_base::m_test(int n,double &ans) { double *pp=pts+n+(n<<1); ans=*(pp++)*px; ans+=*(pp++)*py; ans+=*pp*pz-prsq; if (ans<-tolerance2) { return -1; } else if (ans>tolerance2) { return 1; } return check_marginal(n,ans); } /** Checks to see if a given vertex is inside, outside or within the test * plane, for the case when the point has been detected to be very close to the * plane. The routine ensures that the returned results are always consistent * with previous tests, by keeping a table of any marginal results. The routine * first sees if the vertex is in the table, and if it finds a previously * computed result it uses that. Otherwise, it computes a result for this * vertex and adds it the table. * \param[in] n the vertex to test. * \param[in] 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. */ int voronoicell_base::check_marginal(int n,double &ans) { int i; for (i=0;imax_marginal) voro_fatal_error("Marginal case buffer allocation exceeded absolute maximum",VOROPP_MEMORY_ERROR); #if VOROPP_VERBOSE >=2 fprintf(stderr,"Marginal cases buffer scaled up to %d\n",i); #endif int *pmarg=new int[current_marginal]; for (int j=0;jtolerance?1:(ans<-tolerance?-1:0); return marg[n_marg-1]; } /** 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 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. */ voronoicell_neighbor::voronoicell_neighbor() { 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(); } /** 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 vectors in the global coordinate system. * \param[out] v the vector to store the results in. * \param[in] (x,y,z) the position vector of the particle in the global * coordinate system. */ void voronoicell_base::vertices(double x,double y,double z,std::vector &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; } } /** 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(); } // Explicit instantiation template bool voronoicell_base::nplane(voronoicell_neighbor&,double,double,double,double,int); template void voronoicell_base::check_memory_for_copy(voronoicell_neighbor&,voronoicell_base*); }