Bugfix in search_for_outside_edge routine.
[voro++.git] / trunk / zeo / v_network.cc
blob547846b3f26288ba31684394114d0dad8c31c4b4
1 #include "v_network.hh"
3 /** Initializes the Voronoi network object. The geometry is set up to match a
4 * corresponding container class, and memory is allocated for the network.
5 * \param[in] c a reference to a container or container_poly class. */
6 template<class c_class>
7 voronoi_network::voronoi_network(c_class &c,double net_tol_) :
8 bx(c.bx), bxy(c.bxy), by(c.by), bxz(c.bxz), byz(c.byz), bz(c.bz),
9 nx(c.nx), ny(c.ny), nz(c.nz), nxyz(nx*ny*nz),
10 xsp(nx/bx), ysp(ny/by), zsp(nz/bz), net_tol(net_tol_) {
11 int l;
13 // Allocate memory for vertex structure
14 pts=new double*[nxyz];
15 idmem=new int*[nxyz];
16 ptsc=new int[nxyz];
17 ptsmem=new int[nxyz];
18 for(l=0;l<nxyz;l++) {
19 pts[l]=new double[4*init_network_vertex_memory];
20 idmem[l]=new int[init_network_vertex_memory];
21 ptsc[l]=0;ptsmem[l]=init_network_vertex_memory;
24 // Allocate memory for network edges and related statistics
25 edc=0;edmem=init_network_vertex_memory*nxyz;
26 ed=new int*[edmem];
27 ne=new int*[edmem];
28 pered=new unsigned int*[edmem];
29 raded=new block*[edmem];
30 nu=new int[edmem];
31 nec=new int[edmem];
32 numem=new int[edmem];
34 // Allocate memory for back pointers
35 reg=new int[edmem];
36 regp=new int[edmem];
38 // Allocate edge memory
39 for(l=0;l<edmem;l++) {
40 ed[l]=new int[2*init_network_edge_memory];
41 ne[l]=ed[l]+init_network_edge_memory;
43 for(l=0;l<edmem;l++) raded[l]=new block[init_network_edge_memory];
44 for(l=0;l<edmem;l++) pered[l]=new unsigned int[init_network_edge_memory];
45 for(l=0;l<edmem;l++) {nu[l]=nec[l]=0;numem[l]=init_network_edge_memory;}
47 // vertices
48 vmap=new int[4*init_vertices];
49 map_mem=init_vertices;
52 /** The voronoi_network destructor removes the dynamically allocated memory. */
53 voronoi_network::~voronoi_network() {
54 int l;
56 // Remove Voronoi mapping array
57 delete [] vmap;
59 // Remove individual edge arrays
60 for(l=0;l<edmem;l++) delete [] pered[l];
61 for(l=0;l<edmem;l++) delete [] raded[l];
62 for(l=0;l<edmem;l++) delete [] ed[l];
64 // Remove back pointers
65 delete [] regp;delete [] reg;
67 // Remove memory for edges and related statistics
68 delete [] numem;delete [] nec;delete [] nu;
69 delete [] raded;delete [] pered;
70 delete [] ne;delete [] ed;
72 // Remove vertex structure arrays
73 for(l=0;l<nxyz;l++) {
74 delete [] idmem[l];
75 delete [] pts[l];
77 delete [] ptsmem;delete [] ptsc;
78 delete [] idmem;delete [] pts;
81 /** Increase network memory for a particular region. */
82 void voronoi_network::add_network_memory(int l) {
83 ptsmem[l]<<=1;
85 // Check to see that an absolute maximum in memory allocation
86 // has not been reached, to prevent runaway allocation
87 if(ptsmem[l]>max_network_vertex_memory)
88 voro_fatal_error("Container vertex maximum memory allocation exceeded",VOROPP_MEMORY_ERROR);
90 // Allocate new arrays
91 double *npts(new double[4*ptsmem[l]]);
92 int *nidmem(new int[ptsmem[l]]);
94 // Copy the contents of the old arrays into the new ones
95 for(int i=0;i<4*ptsc[l];i++) npts[i]=pts[l][i];
96 for(int i=0;i<ptsc[l];i++) nidmem[i]=idmem[l][i];
98 // Delete old arrays and update pointers to the new ones
99 delete [] pts[l];delete [] idmem[l];
100 pts[l]=npts;idmem[l]=nidmem;
103 /** Increase edge network memory. */
104 void voronoi_network::add_edge_network_memory() {
105 int i;
106 edmem<<=1;
108 // Allocate new arrays
109 int **ned(new int*[edmem]);
110 int **nne(new int*[edmem]);
111 block **nraded(new block*[edmem]);
112 unsigned int **npered(new unsigned int*[edmem]);
113 int *nnu(new int[edmem]);
114 int *nnec(new int[edmem]);
115 int *nnumem(new int[edmem]);
116 int *nreg(new int[edmem]);
117 int *nregp(new int[edmem]);
119 // Copy the contents of the old arrays into the new ones
120 for(i=0;i<edc;i++) {
121 ned[i]=ed[i];
122 nne[i]=ne[i];
123 nraded[i]=raded[i];
124 npered[i]=pered[i];
125 nnu[i]=nu[i];
126 nnec[i]=nec[i];
127 nnumem[i]=numem[i];
128 nreg[i]=reg[i];
129 nregp[i]=regp[i];
132 // Carry out new allocation
133 while(i<edmem) {
134 ned[i]=new int[2*init_network_edge_memory];
135 nne[i]=ned[i]+init_network_edge_memory;
136 nnu[i]=nnec[i]=0;nnumem[i]=init_network_edge_memory;
137 nraded[i]=new block[init_network_edge_memory];
138 npered[i++]=new unsigned int[init_network_edge_memory];
141 // Delete old arrays and update pointers to the new ones
142 delete [] ed;ed=ned;
143 delete [] ne;ne=nne;
144 delete [] raded;raded=nraded;
145 delete [] pered;pered=npered;
146 delete [] nu;nu=nnu;
147 delete [] nec;nec=nnec;
148 delete [] numem;numem=nnumem;
149 delete [] reg;reg=nreg;
150 delete [] regp;regp=nregp;
153 /** Increase a particular vertex memory. */
154 void voronoi_network::add_particular_vertex_memory(int l) {
155 numem[l]<<=1;
157 // Check that the vertex allocation does not exceed a maximum safe
158 // limit
159 if(numem[l]>max_vertex_order)
160 voro_fatal_error("Particular vertex maximum memory allocation exceeded",VOROPP_MEMORY_ERROR);
162 // Allocate new arrays
163 int *ned(new int[2*numem[l]]);
164 int *nne(ned+numem[l]);
165 block *nraded(new block[numem[l]]);
166 unsigned int *npered(new unsigned int[numem[l]]);
168 // Copy the contents of the old arrays into the new ones
169 for(int i=0;i<nu[l];i++) {
170 ned[i]=ed[l][i];
171 nraded[i]=raded[l][i];
172 npered[i]=pered[l][i];
174 for(int i=0;i<nec[l];i++) nne[i]=ne[l][i];
176 // Delete old arrays and update pointers to the new ones
177 delete [] ed[l];ed[l]=ned;ne[l]=nne;
178 delete [] raded[l];raded[l]=nraded;
179 delete [] pered[l];pered[l]=npered;
183 /** Increases the memory for the vertex mapping.
184 * \param[in] pmem the amount of memory needed. */
185 void voronoi_network::add_mapping_memory(int pmem) {
186 do {map_mem<<=1;} while(map_mem<pmem);
187 delete [] vmap;
188 vmap=new int[4*map_mem];
191 /** Clears the class of all vertices and edges. */
192 void voronoi_network::clear_network() {
193 int l;
194 edc=0;
195 for(l=0;l<nxyz;l++) ptsc[l]=0;
196 for(l=0;l<edmem;l++) nu[l]=0;
199 /** Outputs the network in a format that can be read by gnuplot.
200 * \param[in] fp a file handle to write to. */
201 void voronoi_network::draw_network(FILE *fp) {
202 int l,q,ai,aj,ak;
203 double x,y,z,*ptsp;
204 for(l=0;l<edc;l++) {
205 ptsp=pts[reg[l]]+4*regp[l];
206 x=*(ptsp++);y=*(ptsp++);z=*ptsp;
207 for(q=0;q<nu[l];q++) {
208 unpack_periodicity(pered[l][q],ai,aj,ak);
209 if(ed[l][q]<l&&ai==0&&aj==0&&ak==0) continue;
210 ptsp=pts[reg[ed[l][q]]]+4*regp[ed[l][q]];
211 fprintf(fp,"%g %g %g\n%g %g %g\n\n\n",x,y,z,
212 *ptsp+bx*ai+bxy*aj+bxz*ak,
213 ptsp[1]+by*aj+byz*ak,ptsp[2]+bz*ak);
218 /** Prints out the network.
219 * \param[in] fp a file handle to write to.
220 * \param[in] reverse_remove a boolean value, setting whether or not to remove
221 * reverse edges. */
222 void voronoi_network::print_network(FILE *fp,bool reverse_remove) {
223 int ai,aj,ak,j,l,ll,q;
224 double x,y,z,x2,y2,z2,*ptsp;
226 // Print the vertex table
227 fprintf(fp,"Vertex table:\n%d\n",edc);
228 //os << edc << "\n";
229 for(l=0;l<edc;l++) {
230 ptsp=pts[reg[l]];j=4*regp[l];
231 fprintf(fp,"%d %g %g %g %g",l,ptsp[j],ptsp[j+1],ptsp[j+2],ptsp[j+3]);
232 for(ll=0;ll<nec[l];ll++) fprintf(fp," %d",ne[l][ll]);
233 fputs("\n",fp);
236 // Print out the edge table, loop over vertices
237 fputs("\nEdge table:\n",fp);
238 for(l=0;l<edc;l++) {
240 // Store the position of this vertex
241 ptsp=pts[reg[l]];j=4*regp[l];
242 x=ptsp[j];y=ptsp[j+1];z=ptsp[j+2];
244 // Loop over edges of this vertex
245 for(q=0;q<nu[l];q++) {
247 unpack_periodicity(pered[l][q],ai,aj,ak);
249 // If this option is enabled, then the code will not
250 // print edges from i to j for j<i.
251 if(reverse_remove) if(ed[l][q]<l&&ai==0&&aj==0&&ak==0) continue;
253 fprintf(fp,"%d -> %d",l,ed[l][q]);
254 raded[l][q].print(fp);
256 // Compute and print the length of the edge
257 ptsp=pts[reg[ed[l][q]]];j=4*regp[ed[l][q]];
258 x2=ptsp[j]+ai*bx+aj*bxy+ak*bxz-x;
259 y2=ptsp[j+1]+aj*by+ak*byz-y;
260 z2=ptsp[j+2]+ak*bz-z;
261 fprintf(fp," %d %d %d %g\n",ai,aj,ak,sqrt(x2*x2+y2*y2+z2*z2));
266 // Converts three periodic image displacements into a single unsigned integer.
267 // \param[in] i the periodic image in the x direction.
268 // \param[in] j the periodic image in the y direction.
269 // \param[in] k the periodic image in the z direction.
270 // \return The packed integer. */
271 inline unsigned int voronoi_network::pack_periodicity(int i,int j,int k) {
272 unsigned int pa=((unsigned int) (127+i));
273 pa<<=8;pa+=((unsigned int) (127+j));
274 pa<<=8;pa+=((unsigned int) (127+k));
275 return pa;
278 /** Unpacks an unsigned integer into three periodic image displacements.
279 * \param[in] pa the packed integer.
280 // \param[out] i the periodic image in the x direction.
281 // \param[out] j the periodic image in the y direction.
282 // \param[out] k the periodic image in the z direction. */
283 inline void voronoi_network::unpack_periodicity(unsigned int pa,int &i,int &j,int &k) {
284 i=((signed int) (pa>>16))-127;
285 j=((signed int) ((pa>>8)&255))-127;
286 k=((signed int) (pa&255))-127;
289 /** Adds a Voronoi cell to the network structure. The routine first checks all
290 * of the Voronoi cell vertices and merges them with existing ones where
291 * possible. Edges are then added to the structure.
292 * \param[in] c a reference to a Voronoi cell.
293 * \param[in] (x,y,z) the position of the Voronoi cell.
294 * \param[in] idn the ID number of the particle associated with the cell. */
295 template<class v_cell>
296 void voronoi_network::add_to_network_internal(v_cell &c,int idn,double x,double y,double z,double rad,int *cmap) {
297 int i,j,k,ijk,l,q,ai,aj,ak,*vmp(cmap);
298 double gx,gy,vx,vy,vz,crad,*cp(c.pts);
300 // Loop over the vertices of the Voronoi cell
301 for(l=0;l<c.p;l++,vmp+=4) {
303 // Compute the real position of this vertex, and evaluate its
304 // position along the non-rectangular axes
305 vx=x+cp[4*l]*0.5;vy=y+cp[4*l+1]*0.5;vz=z+cp[4*l+2]*0.5;
306 gx=vx-vy*(bxy/by)+vz*(bxy*byz-by*bxz)/(by*bz);
307 gy=vy-vz*(byz/bz);
309 // Compute the adjusted radius, which will be needed either way
310 crad=0.5*sqrt(cp[4*l]*cp[4*l]+cp[4*l+1]*cp[4*l+1]+cp[4*l+2]*cp[4*l+2])-rad;
312 // Check to see if a vertex very close to this one already
313 // exists in the network
314 if(search_previous(gx,gy,vx,vy,vz,ijk,q,vmp[1],vmp[2],vmp[3])) {
316 // If it does, then just map the Voronoi cell
317 // vertex to it
318 *vmp=idmem[ijk][q];
320 // Store this radius if it smaller than the current
321 // value
322 if(pts[ijk][4*q+3]>crad) pts[ijk][4*q+3]=crad;
323 } else {
324 k=step_int(vz*zsp);if(k<0||k>=nz) {ak=step_div(k,nz);vx-=bxz*ak;vy-=byz*ak;vz-=bz*ak;k-=ak*nz;} else ak=0;
325 j=step_int(gy*ysp);if(j<0||j>=ny) {aj=step_div(j,ny);vx-=bxy*aj;vy-=by*aj;j-=aj*ny;} else aj=0;
326 i=step_int(gx*xsp);if(i<0||i>=nx) {ai=step_div(i,nx);vx-=bx*ai;i-=ai*nx;} else ai=0;
328 vmp[1]=ai;vmp[2]=aj;vmp[3]=ak;
329 ijk=i+nx*(j+ny*k);
331 if(edc==edmem) add_edge_network_memory();
332 if(ptsc[ijk]==ptsmem[ijk]) add_network_memory(ijk);
334 reg[edc]=ijk;regp[edc]=ptsc[ijk];
335 pts[ijk][4*ptsc[ijk]]=vx;
336 pts[ijk][4*ptsc[ijk]+1]=vy;
337 pts[ijk][4*ptsc[ijk]+2]=vz;
338 pts[ijk][4*ptsc[ijk]+3]=crad;
339 idmem[ijk][ptsc[ijk]++]=edc;
340 *vmp=edc++;
343 // Add the neighbor information to this vertex
344 add_neighbor(*vmp,idn);
347 add_edges_to_network(c,x,y,z,rad,cmap);
350 /** Adds a neighboring particle ID to a vertex in the Voronoi network, first
351 * checking that the ID is not already recorded.
352 * \param[in] k the Voronoi vertex.
353 * \param[in] idn the particle ID number. */
354 inline void voronoi_network::add_neighbor(int k,int idn) {
355 for(int i=0;i<nec[k];i++) if(ne[k][i]==idn) return;
356 if(nec[k]==numem[k]) add_particular_vertex_memory(k);
357 ne[k][nec[k]++]=idn;
360 /** Adds edges to the network structure, after the vertices have been
361 * considered. This routine assumes that the cmap array has a mapping between
362 * Voronoi cell vertices and Voronoi network vertices. */
363 template<class v_cell>
364 void voronoi_network::add_edges_to_network(v_cell &c,double x,double y,double z,double rad,int *cmap) {
365 int i,j,ai,aj,ak,bi,bj,bk,k,l,q,*vmp;unsigned int cper;
366 double vx,vy,vz,wx,wy,wz,dx,dy,dz,dis;double *pp;
367 for(l=0;l<c.p;l++) {
368 vmp=cmap+4*l;k=*(vmp++);ai=*(vmp++);aj=*(vmp++);ak=*vmp;
369 pp=pts[reg[k]]+4*regp[k];
370 vx=pp[0]+ai*bx+aj*bxy+ak*bxz;
371 vy=pp[1]+aj*by+ak*byz;
372 vz=pp[2]+ak*bz;
373 for(q=0;q<c.nu[l];q++) {
374 i=c.ed[l][q];
375 vmp=cmap+4*i;
376 j=*(vmp++);bi=*(vmp++);bj=*(vmp++);bk=*vmp;
378 // Skip if this is a self-connecting edge
379 if(j==k&&bi==ai&&bj==aj&&bk==ak) continue;
380 cper=pack_periodicity(bi-ai,bj-aj,bk-ak);
381 pp=pts[reg[j]]+(4*regp[j]);
382 wx=pp[0]+bi*bx+bj*bxy+bk*bxz;
383 wy=pp[1]+bj*by+bk*byz;
384 wz=pp[2]+bk*bz;
385 dx=wx-vx;dy=wy-vy;dz=wz-vz;
386 dis=(x-vx)*dx+(y-vy)*dy+(z-vz)*dz;
387 dis/=dx*dx+dy*dy+dz*dz;
388 if(dis<0) dis=0;if(dis>1) dis=1;
389 wx=vx-x+dis*dx;wy=vy-y+dis*dy;wz=vz-z+dis*dz;
390 int nat=not_already_there(k,j,cper);
391 if(nat==nu[k]) {
392 if(nu[k]==numem[k]) add_particular_vertex_memory(k);
393 ed[k][nu[k]]=j;
394 raded[k][nu[k]].first(sqrt(wx*wx+wy*wy+wz*wz)-rad,dis);
395 pered[k][nu[k]++]=cper;
396 } else {
397 raded[k][nat].add(sqrt(wx*wx+wy*wy+wz*wz)-rad,dis);
403 template<class v_cell>
404 void voronoi_network::add_to_network_rectangular_internal(v_cell &c,int idn,double x,double y,double z,double rad,int *cmap) {
405 int i,j,k,ijk,l,q,ai,aj,ak,*vmp(cmap);
406 double vx,vy,vz,crad,*cp(c.pts);
408 for(l=0;l<c.p;l++,vmp+=4) {
409 vx=x+cp[4*l]*0.5;vy=y+cp[4*l+1]*0.5;vz=z+cp[4*l+2]*0.5;
410 crad=0.5*sqrt(cp[4*l]*cp[4*l]+cp[4*l+1]*cp[4*l+1]+cp[4*l+2]*cp[4*l+2])-rad;
411 if(safe_search_previous_rect(vx,vy,vz,ijk,q,vmp[1],vmp[2],vmp[3])) {
412 *vmp=idmem[ijk][q];
414 // Store this radius if it smaller than the current
415 // value
416 if(pts[ijk][4*q+3]>crad) pts[ijk][4*q+3]=crad;
417 } else {
418 k=step_int(vz*zsp);
419 if(k<0||k>=nz) {
420 ak=step_div(k,nz);
421 vz-=ak*bz;vy-=ak*byz;vx-=ak*bxz;k-=ak*nz;
422 } else ak=0;
423 j=step_int(vy*ysp);
424 if(j<0||j>=ny) {
425 aj=step_div(j,ny);
426 vy-=aj*by;vx-=aj*bxy;j-=aj*ny;
427 } else aj=0;
428 i=step_int(vx*xsp);
429 if(i<0||i>=nx) {
430 ai=step_div(i,nx);
431 vx-=ai*bx;i-=ai*nx;
432 } else ai=0;
433 vmp[1]=ai;
434 vmp[2]=aj;
435 vmp[3]=ak;
436 ijk=i+nx*(j+ny*k);
437 if(edc==edmem) add_edge_network_memory();
438 if(ptsc[ijk]==ptsmem[ijk]) add_network_memory(ijk);
439 reg[edc]=ijk;regp[edc]=ptsc[ijk];
440 pts[ijk][4*ptsc[ijk]]=vx;
441 pts[ijk][4*ptsc[ijk]+1]=vy;
442 pts[ijk][4*ptsc[ijk]+2]=vz;
443 pts[ijk][4*ptsc[ijk]+3]=crad;
444 idmem[ijk][ptsc[ijk]++]=edc;
445 *vmp=edc++;
448 add_neighbor(*vmp,idn);
451 add_edges_to_network(c,x,y,z,rad,cmap);
454 int voronoi_network::not_already_there(int k,int j,unsigned int cper) {
455 for(int i=0;i<nu[k];i++) if(ed[k][i]==j&&pered[k][i]==cper) return i;
456 return nu[k];
459 bool voronoi_network::search_previous(double gx,double gy,double x,double y,double z,int &ijk,int &q,int &pi,int &pj,int &pk) {
460 int ai=step_int((gx-net_tol)*xsp),bi=step_int((gx+net_tol)*xsp);
461 int aj=step_int((gy-net_tol)*ysp),bj=step_int((gy+net_tol)*ysp);
462 int ak=step_int((z-net_tol)*zsp),bk=step_int((z+net_tol)*zsp);
463 int i,j,k,mi,mj,mk;
464 double px,py,pz,px2,py2,px3,*pp;
466 for(k=ak;k<=bk;k++) {
467 pk=step_div(k,nz);px=pk*bxz;py=pk*byz;pz=pk*bz;mk=k-nz*pk;
468 for(j=aj;j<=bj;j++) {
469 pj=step_div(j,ny);px2=px+pj*bxy;py2=py+pj*by;mj=j-ny*pj;
470 for(i=ai;i<=bi;i++) {
471 pi=step_div(i,nx);px3=px2+pi*bx;mi=i-nx*pi;
472 ijk=mi+nx*(mj+ny*mk);
473 pp=pts[ijk];
474 for(q=0;q<ptsc[ijk];q++,pp+=4) if(fabs(*pp+px3-x)<net_tol&&fabs(pp[1]+py2-y)<net_tol&&fabs(pp[2]+pz-z)<net_tol) return true;
478 return false;
481 bool voronoi_network::safe_search_previous_rect(double x,double y,double z,int &ijk,int &q,int &ci,int &cj,int &ck) {
482 const double tol(0.5*net_tol);
483 if(search_previous_rect(x+tol,y+tol,z+tol,ijk,q,ci,cj,ck)) return true;
484 if(search_previous_rect(x-tol,y+tol,z+tol,ijk,q,ci,cj,ck)) return true;
485 if(search_previous_rect(x+tol,y-tol,z+tol,ijk,q,ci,cj,ck)) return true;
486 if(search_previous_rect(x-tol,y-tol,z+tol,ijk,q,ci,cj,ck)) return true;
487 if(search_previous_rect(x+tol,y+tol,z-tol,ijk,q,ci,cj,ck)) return true;
488 if(search_previous_rect(x-tol,y+tol,z-tol,ijk,q,ci,cj,ck)) return true;
489 if(search_previous_rect(x+tol,y-tol,z-tol,ijk,q,ci,cj,ck)) return true;
490 return search_previous_rect(x-tol,y-tol,z-tol,ijk,q,ci,cj,ck);
493 bool voronoi_network::search_previous_rect(double x,double y,double z,int &ijk,int &q,int &ci,int &cj,int &ck) {
494 int k=step_int(z*zsp);
495 if(k<0||k>=nz) {
496 ck=step_div(k,nz);
497 z-=ck*bz;y-=ck*byz;x-=ck*bxz;k-=ck*nz;
498 } else ck=0;
500 int j=step_int(y*ysp);
501 if(j<0||j>=ny) {
502 cj=step_div(j,ny);
503 y-=cj*by;x-=cj*bxy;j-=cj*ny;
504 } else cj=0;
506 ijk=step_int(x*xsp);
507 if(ijk<0||ijk>=nx) {
508 ci=step_div(ijk,nx);
509 x-=ci*bx;ijk-=ci*nx;
510 } else ci=0;
512 ijk+=nx*(j+ny*k);double *pp(pts[ijk]);
513 for(q=0;q<ptsc[ijk];q++,pp+=4)
514 if(fabs(*pp-x)<net_tol&&fabs(pp[1]-y)<net_tol&&fabs(pp[2]-z)<net_tol) return true;
515 return false;
518 /** Custom int function, that gives consistent stepping for negative numbers.
519 * With normal int, we have (-1.5,-0.5,0.5,1.5) -> (-1,0,0,1).
520 * With this routine, we have (-1.5,-0.5,0.5,1.5) -> (-2,-1,0,1). */
521 inline int voronoi_network::step_int(double a) {
522 return a<0?int(a)-1:int(a);
525 /** Custom integer division function, that gives consistent stepping for
526 * negative numbers. */
527 inline int voronoi_network::step_div(int a,int b) {
528 return a>=0?a/b:-1+(a+1)/b;
531 // Explicit instantiation
532 template voronoi_network::voronoi_network(container_periodic&, double);
533 template voronoi_network::voronoi_network(container_periodic_poly&, double);
534 template void voronoi_network::add_to_network<voronoicell>(voronoicell&, int, double, double, double, double);
535 template void voronoi_network::add_to_network<voronoicell_neighbor>(voronoicell_neighbor&, int, double, double, double, double);
536 template void voronoi_network::add_to_network_rectangular<voronoicell>(voronoicell&, int, double, double, double, double);
537 template void voronoi_network::add_to_network_rectangular<voronoicell_neighbor>(voronoicell_neighbor&, int, double, double, double, double);