Strip extra spaces from code.
[voro++.git] / branches / 2d / src / cell_2d.cc
blob09dea020b5cee41248d10a3dbaa10c511536f3e0
1 /** \file cell_2d.cc
2 * \brief Function implementations for the voronoicell_2d class. */
4 #include "cell_2d.hh"
5 #include "cell_nc_2d.hh"
7 namespace voro {
9 /** Constructs a 2D Voronoi cell and sets up the initial memory. */
10 voronoicell_base_2d::voronoicell_base_2d() :
11 current_vertices(init_vertices), current_delete_size(init_delete_size),
12 ed(new int[2*current_vertices]), pts(new double[2*current_vertices]),
13 ds(new int[current_delete_size]), stacke(ds+current_delete_size) {
16 /** The voronoicell_2d destructor deallocates all of the dynamic memory. */
17 voronoicell_base_2d::~voronoicell_base_2d() {
18 delete [] ds;
19 delete [] pts;
20 delete [] ed;
23 /** Initializes a Voronoi cell as a rectangle with the given dimensions.
24 * \param[in] (xmin,xmax) the minimum and maximum x coordinates.
25 * \param[in] (ymin,ymax) the minimum and maximum y coordinates. */
26 void voronoicell_base_2d::init_base(double xmin,double xmax,double ymin,double ymax) {
27 p=4;xmin*=2;xmax*=2;ymin*=2;ymax*=2;
28 *pts=xmin;pts[1]=ymin;
29 pts[2]=xmax;pts[3]=ymin;
30 pts[4]=xmax;pts[5]=ymax;
31 pts[6]=xmin;pts[7]=ymax;
32 int *q=ed;
33 *q=1;q[1]=3;q[2]=2;q[3]=0;q[4]=3;q[5]=1;q[6]=0;q[7]=2;
36 /** Outputs the edges of the Voronoi cell in gnuplot format to an output
37 * stream.
38 * \param[in] (x,y) a displacement vector to be added to the cell's position.
39 * \param[in] fp the file handle to write to. */
40 void voronoicell_base_2d::draw_gnuplot(double x,double y,FILE *fp) {
41 if(p==0) return;
42 int k=0;
43 do {
44 fprintf(fp,"%g %g\n",x+0.5*pts[2*k],y+0.5*pts[2*k+1]);
45 k=ed[2*k];
46 } while (k!=0);
47 fprintf(fp,"%g %g\n\n",x+0.5*pts[0],y+0.5*pts[1]);
50 /** Outputs the edges of the Voronoi cell in POV-Ray format to an open file
51 * stream, displacing the cell by given vector.
52 * \param[in] (x,y) a displacement vector to be added to the cell's position.
53 * \param[in] fp the file handle to write to. */
54 void voronoicell_base_2d::draw_pov(double x,double y,FILE *fp) {
55 if(p==0) return;
56 int k=0;
57 do {
58 fprintf(fp,"sphere{<%g,%g,0>,r}\ncylinder{<%g,%g,0>,<"
59 ,x+0.5*pts[2*k],y+0.5*pts[2*k+1]
60 ,x+0.5*pts[2*k],y+0.5*pts[2*k+1]);
61 k=ed[2*k];
62 fprintf(fp,"%g,%g,0>,r}\n",x+0.5*pts[2*k],y+0.5*pts[2*k+1]);
63 } while (k!=0);
66 /** Computes the maximum radius squared of a vertex from the center of the
67 * cell. It can be used to determine when enough particles have been testing an
68 * all planes that could cut the cell have been considered.
69 * \return The maximum radius squared of a vertex.*/
70 double voronoicell_base_2d::max_radius_squared() {
71 double r,s,*ptsp(pts+2),*ptse(pts+2*p);
72 r=*pts*(*pts)+pts[1]*pts[1];
73 while(ptsp<ptse) {
74 s=*ptsp*(*ptsp);ptsp++;
75 s+=*ptsp*(*ptsp);ptsp++;
76 if(s>r) r=s;
78 return r;
81 /** Cuts the Voronoi cell by a particle whose center is at a separation of
82 * (x,y) from the cell center. The value of rsq should be initially set to
83 * \f$x^2+y^2\f$.
84 * \param[in] (x,y) the normal vector to the plane.
85 * \param[in] rsq the distance along this vector of the plane.
86 * \return False if the plane cut deleted the cell entirely, true otherwise. */
87 bool voronoicell_base_2d::plane_intersects(double x,double y,double rsq) {
88 int up=0,up2,up3;
89 double u,u2,u3;
91 // First try and find a vertex that is within the cutting plane, if
92 // there is one. If one can't be found, then the cell is not cut by
93 // this plane and the routine immediately returns true.
94 u=pos(x,y,rsq,up);
95 if(u<tolerance) {
96 up2=ed[2*up];u2=pos(x,y,rsq,up2);
97 up3=ed[2*up+1];u3=pos(x,y,rsq,up3);
98 if(u2>u3) {
99 while(u2<tolerance) {
100 up2=ed[2*up2];
101 u2=pos(x,y,rsq,up2);
102 if(up2==up3) return false;
104 up=up2;u=u2;
105 } else {
106 while(u3<tolerance) {
107 up3=ed[2*up3+1];
108 u3=pos(x,y,rsq,up3);
109 if(up2==up3) return false;
111 up=up3;u=u3;
114 return true;
118 /** Cuts the Voronoi cell by a particle whose center is at a separation of
119 * (x,y) from the cell center. The value of rsq should be initially set to
120 * \f$x^2+y^2\f$.
121 * \param[in] (x,y) the normal vector to the plane.
122 * \param[in] rsq the distance along this vector of the plane.
123 * \return False if the plane cut deleted the cell entirely, true otherwise. */
124 template<class vc_class>
125 bool voronoicell_base_2d::nplane(vc_class &vc,double x,double y,double rsq,int p_id) {
126 int up=0,up2,up3;
127 double u,u2,u3;
129 // First try and find a vertex that is within the cutting plane, if
130 // there is one. If one can't be found, then the cell is not cut by
131 // this plane and the routine immediately returns true.
132 u=pos(x,y,rsq,up);
133 if(u<tolerance) {
134 up2=ed[2*up];u2=pos(x,y,rsq,up2);
135 up3=ed[2*up+1];u3=pos(x,y,rsq,up3);
136 if(u2>u3) {
137 while(u2<tolerance) {
138 up2=ed[2*up2];
139 u2=pos(x,y,rsq,up2);
140 if(up2==up3) return true;
142 up=up2;u=u2;
143 } else {
144 while(u3<tolerance) {
145 up3=ed[2*up3+1];
146 u3=pos(x,y,rsq,up3);
147 if(up2==up3) return true;
149 up=up3;u=u3;
153 return nplane_cut(vc,x,y,rsq,p_id,u,up);
157 template<class vc_class>
158 bool voronoicell_base_2d::nplane_cut(vc_class &vc,double x,double y,double rsq,int p_id,double u,int up) {
159 int cp,lp,up2,up3,*stackp=ds;
160 double fac,l,u2,u3;
162 // Add this point to the delete stack, and search counter-clockwise to
163 // find additional points that need to be deleted.
164 *(stackp++)=up;
165 l=u;up2=ed[2*up];
166 u2=pos(x,y,rsq,up2);
167 while(u2>tolerance) {
168 if(stackp==stacke) add_memory_ds(stackp);
169 *(stackp++)=up2;
170 up2=ed[2*up2];
171 l=u2;
172 u2=pos(x,y,rsq,up2);
173 if(up2==up) return false;
176 // Consider the first point that was found in the counter-clockwise
177 // direction that was not inside the cutting plane. If it lies on the
178 // cutting plane then do nothing. Otherwise, introduce a new vertex.
179 if(u2>-tolerance) cp=up2;
180 else {
181 if(p==current_vertices) add_memory_vertices(vc);
182 lp=ed[2*up2+1];
183 fac=1/(u2-l);
184 pts[2*p]=(pts[2*lp]*u2-pts[2*up2]*l)*fac;
185 pts[2*p+1]=(pts[2*lp+1]*u2-pts[2*up2+1]*l)*fac;
186 vc.n_copy(p,lp);
187 ed[2*p]=up2;
188 ed[2*up2+1]=p;
189 cp=p++;
192 // Search clockwise for additional points that need to be deleted
193 l=u;up3=ed[2*up+1];u3=pos(x,y,rsq,up3);
194 while(u3>tolerance) {
195 if(stackp==stacke) add_memory_ds(stackp);
196 *(stackp++)=up3;
197 up3=ed[2*up3+1];
198 l=u3;
199 u3=pos(x,y,rsq,up3);
200 if(up3==up2) break;
203 // Either adjust the existing vertex or create new one, and connect it
204 // with the vertex found on the previous search in the counter-clockwise
205 // direction
206 if(u3>tolerance) {
207 ed[2*cp+1]=up3;
208 ed[2*up3]=cp;
209 vc.n_set(up3,p_id);
210 } else {
211 if(p==current_vertices) add_memory_vertices(vc);
212 lp=ed[2*up3];
213 fac=1/(u3-l);
214 pts[2*p]=(pts[2*lp]*u3-pts[2*up3]*l)*fac;
215 pts[2*p+1]=(pts[2*lp+1]*u3-pts[2*up3+1]*l)*fac;
216 ed[2*p]=cp;
217 ed[2*cp+1]=p;
218 vc.n_set(p,p_id);
219 ed[2*p+1]=up3;
220 ed[2*up3]=p++;
223 // Mark points on the delete stack
224 for(int *sp=ds;sp<stackp;sp++) ed[*sp*2]=-1;
226 // Remove them from the memory structure
227 while(stackp>ds) {
228 while(ed[2*--p]==-1);
229 up=*(--stackp);
230 if(up<p) {
231 ed[2*ed[2*p]+1]=up;
232 ed[2*ed[2*p+1]]=up;
233 pts[2*up]=pts[2*p];
234 pts[2*up+1]=pts[2*p+1];
235 vc.n_copy(up,p);
236 ed[2*up]=ed[2*p];
237 ed[2*up+1]=ed[2*p+1];
238 } else p++;
240 return true;
243 /** Returns a vector of the vertex vectors using the local coordinate system.
244 * \param[out] v the vector to store the results in. */
245 void voronoicell_base_2d::vertices(vector<double> &v) {
246 v.resize(2*p);
247 double *ptsp=pts;
248 for(int i=0;i<2*p;i+=2) {
249 v[i]=*(ptsp++)*0.5;
250 v[i+1]=*(ptsp++)*0.5;
254 /** Outputs the vertex vectors using the local coordinate system.
255 * \param[out] fp the file handle to write to. */
256 void voronoicell_base_2d::output_vertices(FILE *fp) {
257 if(p>0) {
258 fprintf(fp,"(%g,%g)",*pts*0.5,pts[1]*0.5);
259 for(double *ptsp=pts+2;ptsp<pts+2*p;ptsp+=2) fprintf(fp," (%g,%g)",*ptsp*0.5,ptsp[1]*0.5);
263 /** Returns a vector of the vertex vectors in the global coordinate system.
264 * \param[out] v the vector to store the results in.
265 * \param[in] (x,y,z) the position vector of the particle in the global
266 * coordinate system. */
267 void voronoicell_base_2d::vertices(double x,double y,vector<double> &v) {
268 v.resize(2*p);
269 double *ptsp=pts;
270 for(int i=0;i<2*p;i+=2) {
271 v[i]=x+*(ptsp++)*0.5;
272 v[i+1]=y+*(ptsp++)*0.5;
276 /** Outputs the vertex vectors using the global coordinate system.
277 * \param[out] fp the file handle to write to.
278 * \param[in] (x,y,z) the position vector of the particle in the global
279 * coordinate system. */
280 void voronoicell_base_2d::output_vertices(double x,double y,FILE *fp) {
281 if(p>0) {
282 fprintf(fp,"(%g,%g)",x+*pts*0.5,y+pts[1]*0.5);
283 for(double *ptsp=pts+2;ptsp<pts+2*p;ptsp+=2) fprintf(fp," (%g,%g)",x+*ptsp*0.5,y+ptsp[1]*0.5);
287 /** Calculates the perimeter of the Voronoi cell.
288 * \return A floating point number holding the calculated distance. */
289 double voronoicell_base_2d::perimeter() {
290 if(p==0) return 0;
291 int k=0,l;double perim=0,dx,dy;
292 do {
293 l=ed[2*k];
294 dx=pts[2*k]-pts[2*l];
295 dy=pts[2*k+1]-pts[2*l+1];
296 perim+=sqrt(dx*dx+dy*dy);
297 k=l;
298 } while (k!=0);
299 return 0.5*perim;
302 /** Calculates the perimeter of the Voronoi cell.
303 * \return A floating point number holding the calculated distance. */
304 void voronoicell_base_2d::edge_lengths(vector<double> &vd) {
305 if(p==0) {vd.clear();return;}
306 vd.resize(p);
307 int i=0,k=0,l;double dx,dy;
308 do {
309 l=ed[2*k];
310 dx=pts[2*k]-pts[2*l];
311 dy=pts[2*k+1]-pts[2*l+1];
312 vd[i++]=sqrt(dx*dx+dy*dy);
313 k=l;
314 } while (k!=0);
317 /** Calculates the perimeter of the Voronoi cell.
318 * \return A floating point number holding the calculated distance. */
319 void voronoicell_base_2d::normals(vector<double> &vd) {
320 if(p==0) {vd.clear();return;}
321 vd.resize(2*p);
322 int i=0,k=0,l;double dx,dy,nor;
323 do {
324 l=ed[2*k];
325 dx=pts[2*k]-pts[2*l];
326 dy=pts[2*k+1]-pts[2*l+1];
327 nor=dx*dx+dy*dy;
328 if(nor>tolerance_sq) {
329 nor=1/sqrt(nor);
330 vd[i++]=dy*nor;
331 vd[i++]=-dx*nor;
332 } else {
333 vd[i++]=0;
334 vd[i++]=0;
336 k=l;
337 } while (k!=0);
341 /** Calculates the area of the Voronoi cell.
342 * \return A floating point number holding the calculated distance. */
343 double voronoicell_base_2d::area() {
344 if(p==0) return 0;
345 int k(*ed);double area=0,x=*pts,y=pts[1],dx1,dy1,dx2,dy2;
346 dx1=pts[2*k]-x;dy1=pts[2*k+1]-y;
347 k=ed[2*k];
348 while(k!=0) {
349 dx2=pts[2*k]-x;dy2=pts[2*k+1]-y;
350 area+=dx1*dy2-dx2*dy1;
351 dx1=dx2;dy1=dy2;
352 k=ed[2*k];
354 return 0.125*area;
357 /** Calculates the centroid of the Voronoi cell.
358 * \param[out] (cx,cy) The coordinates of the centroid. */
359 void voronoicell_base_2d::centroid(double &cx,double &cy) {
360 cx=cy=0;
361 static const double third=1/3.0;
362 if(p==0) return;
363 int k(*ed);
364 double area,tarea=0,x=*pts,y=pts[1],dx1,dy1,dx2,dy2;
365 dx1=pts[2*k]-x;dy1=pts[2*k+1]-y;
366 k=ed[2*k];
367 while(k!=0) {
368 dx2=pts[2*k]-x;dy2=pts[2*k+1]-y;
369 area=dx1*dy2-dx2*dy1;
370 tarea+=area;
371 cx+=area*(dx1+dx2);
372 cy+=area*(dy1+dy2);
373 dx1=dx2;dy1=dy2;
374 k=ed[2*k];
376 tarea=third/tarea;
377 cx=0.5*(x+cx*tarea);
378 cy=0.5*(y+cy*tarea);
386 /** Computes the Voronoi cells for all particles in the container, and for each
387 * cell, outputs a line containing custom information about the cell structure.
388 * The output format is specified using an input string with control sequences
389 * similar to the standard C printf() routine.
390 * \param[in] format the format of the output lines, using control sequences to
391 * denote the different cell statistics.
392 * \param[in] i the ID of the particle associated with this Voronoi cell.
393 * \param[in] (x,y) the position of the particle associated with this Voronoi
394 * cell.
395 * \param[in] r a radius associated with the particle.
396 * \param[in] fp the file handle to write to. */
397 void voronoicell_base_2d::output_custom(const char *format,int i,double x,double y,double r,FILE *fp) {
398 char *fmp(const_cast<char*>(format));
399 vector<int> vi;
400 vector<double> vd;
401 while(*fmp!=0) {
402 if(*fmp=='%') {
403 fmp++;
404 switch(*fmp) {
406 // Particle-related output
407 case 'i': fprintf(fp,"%d",i);break;
408 case 'x': fprintf(fp,"%g",x);break;
409 case 'y': fprintf(fp,"%g",y);break;
410 case 'q': fprintf(fp,"%g %g",x,y);break;
411 case 'r': fprintf(fp,"%g",r);break;
413 // Vertex-related output
414 case 'w': fprintf(fp,"%d",p);break;
415 case 'p': output_vertices(fp);break;
416 case 'P': output_vertices(x,y,fp);break;
417 case 'm': fprintf(fp,"%g",0.25*max_radius_squared());break;
419 // Edge-related output
420 case 'g': fprintf(fp,"%d",p);break;
421 case 'E': fprintf(fp,"%g",perimeter());break;
422 case 'e': edge_lengths(vd);voro_print_vector(vd,fp);break;
423 case 'l': normals(vd);
424 voro_print_positions_2d(vd,fp);
425 break;
426 case 'n': neighbors(vi);
427 voro_print_vector(vi,fp);
428 break;
430 // Area-related output
431 case 'a': fprintf(fp,"%g",area());break;
432 case 'c': {
433 double cx,cy;
434 centroid(cx,cy);
435 fprintf(fp,"%g %g",cx,cy);
436 } break;
437 case 'C': {
438 double cx,cy;
439 centroid(cx,cy);
440 fprintf(fp,"%g %g",x+cx,y+cy);
441 } break;
443 // End-of-string reached
444 case 0: fmp--;break;
446 // The percent sign is not part of a
447 // control sequence
448 default: putc('%',fp);putc(*fmp,fp);
450 } else putc(*fmp,fp);
451 fmp++;
453 fputs("\n",fp);
456 /** Doubles the storage for the vertices, by reallocating the pts and ed
457 * arrays. If the allocation exceeds the absolute maximum set in max_vertices,
458 * then the routine exits with a fatal error. */
459 template<class vc_class>
460 void voronoicell_base_2d::add_memory_vertices(vc_class &vc) {
461 double *ppe(pts+2*current_vertices);
462 int *ede(ed+2*current_vertices);
464 // Double the memory allocation and check it is within range
465 current_vertices<<=1;
466 if(current_vertices>max_vertices) voro_fatal_error("Vertex memory allocation exceeded absolute maximum",VOROPP_MEMORY_ERROR);
467 #if VOROPP_VERBOSE >=2
468 fprintf(stderr,"Vertex memory scaled up to %d\n",current_vertices);
469 #endif
471 // Copy the vertex positions
472 double *npts(new double[2*current_vertices]),*npp(npts),*pp(pts);
473 while(pp<ppe) *(npp++)=*(pp++);
474 delete [] pts;pts=npts;
476 // Copy the edge table
477 int *ned(new int[2*current_vertices]),*nep(ned),*edp(ed);
478 while(edp<ede) *(nep++)=*(edp++);
479 delete [] ed;ed=ned;
481 // Double the neighbor information if necessary
482 vc.n_add_memory_vertices();
485 inline void voronoicell_neighbor_2d::n_add_memory_vertices() {
486 int *nne=new int[current_vertices],*nee=ne+(current_vertices>>1),*nep=ne,*nnep=nne;
487 while(nep<nee) *(nnep++)=*(nep++);
488 delete [] ne;ne=nne;
491 void voronoicell_neighbor_2d::neighbors(vector<int> &v) {
492 v.resize(p);
493 for(int i=0;i<p;i++) v[i]=ne[i];
496 void voronoicell_neighbor_2d::init(double xmin,double xmax,double ymin,double ymax) {
497 init_base(xmin,xmax,ymin,ymax);
498 *ne=-3;ne[1]=-2;ne[2]=-4;ne[3]=-1;
501 /** Doubles the size allocation of the delete stack. If the allocation exceeds
502 * the absolute maximum set in max_delete_size, then routine causes a fatal
503 * error.
504 * \param[in] stackp a reference to the current stack pointer. */
505 void voronoicell_base_2d::add_memory_ds(int *&stackp) {
506 current_delete_size<<=1;
507 if(current_delete_size>max_delete_size) voro_fatal_error("Delete stack 1 memory allocation exceeded absolute maximum",VOROPP_MEMORY_ERROR);
508 #if VOROPP_VERBOSE >=2
509 fprintf(stderr,"Delete stack 1 memory scaled up to %d\n",current_delete_size);
510 #endif
511 int *dsn(new int[current_delete_size]),*dsnp(dsn),*dsp(ds);
512 while(dsp<stackp) *(dsnp++)=*(dsp++);
513 delete [] ds;ds=dsn;stackp=dsnp;
514 stacke=ds+current_delete_size;
517 // Explicit instantiation
518 template bool voronoicell_base_2d::nplane(voronoicell_2d&,double,double,double,int);
519 template bool voronoicell_base_2d::nplane(voronoicell_neighbor_2d&,double,double,double,int);
520 template bool voronoicell_base_2d::nplane_cut(voronoicell_2d&,double,double,double,int,double,int);
521 template bool voronoicell_base_2d::nplane_cut(voronoicell_neighbor_2d&,double,double,double,int,double,int);
522 template void voronoicell_base_2d::add_memory_vertices(voronoicell_2d&);
523 template void voronoicell_base_2d::add_memory_vertices(voronoicell_neighbor_2d&);
524 template bool voronoicell_base_2d::nplane(voronoicell_nonconvex_2d&,double,double,double,int);
525 template bool voronoicell_base_2d::nplane(voronoicell_nonconvex_neighbor_2d&,double,double,double,int);
526 template bool voronoicell_base_2d::nplane_cut(voronoicell_nonconvex_2d&,double,double,double,int,double,int);
527 template bool voronoicell_base_2d::nplane_cut(voronoicell_nonconvex_neighbor_2d&,double,double,double,int,double,int);
528 template void voronoicell_base_2d::add_memory_vertices(voronoicell_nonconvex_2d&);
529 template void voronoicell_base_2d::add_memory_vertices(voronoicell_nonconvex_neighbor_2d&);