Reset platonic code.
[voro++.git] / branches / 2d_boundary / src / cell_2d.cc
blob9ef043ab4b5b033fc205f9b792e151e8341508cb
1 /**d \file cell_2d.cc
2 * \brief Function implementations for the voronoicell_2d class. */
4 #include "cell_2d.hh"
5 #include <iostream>
6 bool debugging=false;
7 /** Constructs a 2D Voronoi cell and sets up the initial memory. */
8 voronoicell_2d::voronoicell_2d() :
9 current_vertices(init_vertices), current_delete_size(init_delete_size),
10 ed(new int[2*current_vertices]), pts(new double[2*current_vertices]),
11 ds(new int[current_delete_size]), stacke(ds+current_delete_size) {
14 /** The voronoicell_2d destructor deallocates all of the dynamic memory. */
15 voronoicell_2d::~voronoicell_2d() {
16 delete [] ds;
17 delete [] pts;
18 delete [] ed;
21 /** Initializes a Voronoi cell as a rectangle with the given dimensions.
22 * \param[in] (xmin,xmax) the minimum and maximum x coordinates.
23 * \param[in] (ymin,ymax) the minimum and maximum y coordinates. */
24 void voronoicell_2d::init(double xmin,double xmax,double ymin,double ymax) {
25 p=4;xmin*=2;xmax*=2;ymin*=2;ymax*=2;
26 *pts=xmin;pts[1]=ymin;
27 pts[2]=xmax;pts[3]=ymin;
28 pts[4]=xmax;pts[5]=ymax;
29 pts[6]=xmin;pts[7]=ymax;
30 int *q=ed;
31 *q=1;q[1]=3;q[2]=2;q[3]=0;q[4]=3;q[5]=1;q[6]=0;q[7]=2;
32 for(int i=0;i<4;i++){
33 reg1[i]=0;
34 reg2[i]=0;
37 /** Initialize a Voronoi cell as exactly fitting the non-convex domain specified in bnds
38 * \param[in] bnds, a pointer to the beginning of the array specifing the intial vertices.
39 The first point int bnds MUST be the origin, the second point in bnds MUST be the first edge of
40 the nonconvexity in the the counterclockwise direction. All subsequent points MUST define the cel
41 l in a counterclockwise fashion.
42 * \param[in] #bnds an int specifying the size of bnds. */
44 void voronoicell_2d::init_nonconvex(double bnds_loc[], int noofbnds){
46 double x1, y1, x2, y2;
47 p=noofbnds;
48 for(int i=0; i<(2*noofbnds); i+=2){
49 pts[i]=2*bnds_loc[i];
50 pts[i+1]=2*bnds_loc[i+1];
51 if(i==0){
52 ed[0]=noofbnds-1;
53 }else{
54 ed[i]=(i/2)-1;
55 }if((i+2)==(noofbnds*2)){
56 ed[i+1]=0;
57 }else{
58 ed[i+1]=(i/2)+1;
64 x1=bnds_loc[2]; y1=bnds_loc[3]; x2=bnds_loc[(2*noofbnds)-2]; y2=bnds_loc[(2*noofbnds)-1];
65 reg1[0]=x1; reg1[1]=y1; reg2[0]=x2; reg2[1]=y2;
66 reg1[2]=y1; reg1[3]=-x1; reg2[2]=-y2; reg2[3]=x2;
69 /** Outputs the cell-area
71 double voronoicell_2d::cell_area(){
72 double area, cx, cy, nx, ny;
73 int cid=0, nid, fid=0;
74 nid=ed[0];
75 do{
76 cx=pts[2*cid]; cy=pts[2*cid+1]; nx=pts[2*nid]; ny=pts[2*nid+1];
77 area+=((cx*ny)-(nx*cy));
78 cid=nid;
79 nid=ed[2*cid];
80 }while(cid!=fid);
81 area=area/2;
82 if(area<0) area*=-1;
83 return area;
85 /** Outputs the edges of the Voronoi cell in gnuplot format to an output
86 * stream.
87 * \param[in] (x,y) a displacement vector to be added to the cell's position.
88 * \param[in] fp the file handle to write to. */
89 void voronoicell_2d::draw_gnuplot(double x,double y,FILE *fp) {
90 if(p==0) return;
91 int k=0;
92 do {
93 fprintf(fp,"%g %g\n",x+0.5*pts[2*k],y+0.5*pts[2*k+1]);
94 k=ed[2*k];
95 } while (k!=0);
96 fprintf(fp,"%g %g\n\n",x+0.5*pts[0],y+0.5*pts[1]);
99 /** Outputs the edges of the Voronoi cell in POV-Ray format to an open file
100 * stream, displacing the cell by given vector.
101 * \param[in] (x,y,z) a displacement vector to be added to the cell's position.
102 * \param[in] fp the file handle to write to. */
103 void voronoicell_2d::draw_pov(double x,double y,double z,FILE *fp) {
104 if(p==0) return;
105 int k=0;
106 do {
107 fprintf(fp,"sphere{<%g,%g,%g>,r}\ncylinder{<%g,%g,%g>,<"
108 ,x+0.5*pts[2*k],y+0.5*pts[2*k+1],z
109 ,x+0.5*pts[2*k],y+0.5*pts[2*k+1],z);
110 k=ed[2*k];
111 fprintf(fp,"%g,%g,%g>,r}\n",x+0.5*pts[2*k],y+0.5*pts[2*k+1],z);
112 } while (k!=0);
115 /** Computes the maximum radius squared of a vertex from the center of the
116 * cell. It can be used to determine when enough particles have been testing an
117 * all planes that could cut the cell have been considered.
118 * \return The maximum radius squared of a vertex.*/
119 double voronoicell_2d::max_radius_squared() {
120 double r,s,*ptsp(pts+2),*ptse(pts+2*p);
121 r=*pts*(*pts)+pts[1]*pts[1];
122 while(ptsp<ptse) {
123 s=*ptsp*(*ptsp);ptsp++;
124 s+=*ptsp*(*ptsp);ptsp++;
125 if(s>r) r=s;
127 return r;
130 /** Cuts the Voronoi cell by a particle whose center is at a separation of
131 * (x,y) from the cell center. The value of rsq should be initially set to
132 * \f$x^2+y^2\f$.
133 * \param[in] (x,y) the normal vector to the plane.
134 * \param[in] rsq the distance along this vector of the plane.
135 * \return False if the plane cut deleted the cell entirely, true otherwise. */
136 bool voronoicell_2d::plane(double x,double y,double rsq) {
137 int cp,lp,up=0,up2,up3,*stackp(ds);
138 double fac,l,u,u2,u3;
140 // First try and find a vertex that is within the cutting plane, if
141 // there is one. If one can't be found, then the cell is not cut by
142 // this plane and the routine immediately returns true.
143 u=pos(x,y,rsq,up);
144 if(u<tolerance) {
145 up2=ed[2*up];u2=pos(x,y,rsq,up2);
146 up3=ed[2*up+1];u3=pos(x,y,rsq,up3);
147 if(u2>u3) {
148 while(u2<tolerance) {
149 up2=ed[2*up2];
150 u2=pos(x,y,rsq,up2);
151 if(up2==up3){
152 return true;
155 up=up2;u=u2;
156 } else {
157 while(u3<tolerance) {
158 up3=ed[2*up3+1];
159 u3=pos(x,y,rsq,up3);
160 if(up2==up3){
161 return true;
164 up=up3;u=u3;
168 // Add this point to the delete stack, and search clockwise
169 // to find additional points that need to be deleted.
170 *(stackp++)=up;
171 l=u;up2=ed[2*up];
172 u2=pos(x,y,rsq,up2);
173 while(u2>tolerance) {
174 if(stackp==stacke) add_memory_ds(stackp);
175 *(stackp++)=up2;
176 up2=ed[2*up2];
177 l=u2;
178 u2=pos(x,y,rsq,up2);
179 if(up2==up) return false;
182 // Consider the first point that was found in the clockwise direction
183 // that was not inside the cutting plane. If it lies on the cutting
184 // plane then do nothing. Otherwise, introduce a new vertex.
185 if(u2>-tolerance) {
186 cp=up2;
187 } else {
188 if(p==current_vertices) add_memory_vertices();
189 lp=ed[2*up2+1];
190 fac=1/(u2-l);
191 pts[2*p]=(pts[2*lp]*u2-pts[2*up2]*l)*fac;
192 pts[2*p+1]=(pts[2*lp+1]*u2-pts[2*up2+1]*l)*fac;
193 ed[2*p]=up2;
194 ed[2*up2+1]=p;
195 cp=p++;
198 // Search counter-clockwise for additional points that need to be
199 // deleted
200 l=u;up3=ed[2*up+1];u3=pos(x,y,rsq,up3);
201 while(u3>tolerance) {
202 if(stackp==stacke) add_memory_ds(stackp);
203 *(stackp++)=up3;
204 up3=ed[2*up3+1];
205 l=u3;
206 u3=pos(x,y,rsq,up3);
207 if(up3==up2) break;
210 // Either adjust the existing vertex or create new one, and connect it
211 // with the vertex found on the previous search in the clockwise
212 // direction
213 if(u3>tolerance) {
214 ed[2*cp+1]=up3;
215 ed[2*up3]=cp;
216 } else {
217 if(p==current_vertices) add_memory_vertices();
218 lp=ed[2*up3];
219 fac=1/(u3-l);
220 pts[2*p]=(pts[2*lp]*u3-pts[2*up3]*l)*fac;
221 pts[2*p+1]=(pts[2*lp+1]*u3-pts[2*up3+1]*l)*fac;
222 ed[2*p]=cp;
223 ed[2*cp+1]=p;
224 ed[2*p+1]=up3;
225 ed[2*up3]=p++;
228 // Mark points on the delete stack
229 for(int *sp=ds;sp<stackp;sp++) ed[*sp*2]=-1;
231 // Remove them from the memory structure
232 while(stackp>ds) {
233 while(ed[2*--p]==-1);
234 up=*(--stackp);
235 if(up<p) {
236 ed[2*ed[2*p]+1]=up;
237 ed[2*ed[2*p+1]]=up;
238 pts[2*up]=pts[2*p];
239 pts[2*up+1]=pts[2*p+1];
240 ed[2*up]=ed[2*p];
241 ed[2*up+1]=ed[2*p+1];
242 } else p++;
244 return true;
246 void voronoicell_2d::initialize_regions(double x1, double y1, double x2, double y2, bool ext){
247 reg1[0]=x1; reg1[1]=y1; reg2[0]=x2; reg2[1]=y2;
248 reg1[2]=y1; reg1[3]=-x1;
249 reg2[2]=-y2; reg2[3]=x2;
251 /** The same as Plane excepts it handles nonconvex cells, for which the handling of the vertices is more complex. */
253 bool voronoicell_2d::plane_nonconvex(double x,double y,double rsq) {
255 //first determine which region we are in.
256 if((!(reg1[0]==x && reg1[1]==y) && ((x*reg1[0]+y*reg1[1])>0) &&((x*reg1[2]+y*reg1[3])>0)) ||
257 (reg2[0]==x && reg2[1]==y) ){
258 return halfplane(x, y, rsq, reg1[2], reg1[3]);
259 }if((!(reg2[0]==x && reg2[1]==y) && ((x*reg2[0]+y*reg2[1])>0)&&((x*reg2[2]+y*reg2[3])>0)) ||
260 (reg1[0]==x && reg1[1]==y) ){
261 return halfplane(x, y, rsq, reg2[2], reg2[3]);
263 }else{
264 return plane(x,y,rsq);
267 /** cuts the given cell by a half plane given
268 * \param[in] (x1 ,y1) the normal vector to the plane.
269 * \param[in] rsq the distance along this vector of the plane.
270 * \param[in] (x2, y2) the normal vector to the plane from which we want the plane cut to start.
271 * \return False if the plane cut deleted the cell entirely, true otherwise. */
273 bool voronoicell_2d::halfplane(double x1, double y1, double rsq, double x2, double y2) {
274 int si=0, ci=0, ni, patch1, patch2, *stackp(ds);
275 double cid=pos(x1,y1,rsq,ci), nid, fac;
276 bool rightchunk=false;
277 //first find a vertex that is not being cut by the plane
278 while(cid>tolerance){
279 ci=ed[2*ci+1];
280 cid=pos(x1, y1, rsq, ci);
281 if(ci==si){
282 return false;
285 si=ci;
294 //now circle around the vertices until we find the right chunk to cut.
295 //When we exit this loop, ci will not be cut, and ni will be the first index to be cut by the plane.
296 //if nothing is cut, return true.
297 while(!rightchunk){
298 ni=ed[ci*2];
299 if(ni==si){
300 return true;
302 nid=pos(x1,y1,rsq,ni);
303 if(nid<tolerance || (((pts[2*ni]*x2)+(pts[2*ni+1]*y2))<tolerance)) {
304 ci=ni;
305 cid=nid;
306 continue;
309 else{
310 rightchunk=true;
321 if(stackp==stacke) add_memory_ds(stackp);
322 *(stackp++)=ni;
325 //see if ci lies on the cutting plane, if it doesnt introduce a new vertex
326 if(cid>-tolerance){
327 patch1=ci;
328 }else{
329 if(p==current_vertices) add_memory_vertices();
330 fac=1/(cid-nid);
331 pts[2*p]=(pts[2*ni]*cid-pts[2*ci]*nid)*fac;
332 pts[2*p+1]=(pts[2*ni+1]*cid-pts[2*ci+1]*nid)*fac;
333 patch1=p++;
334 ed[2*ci]=patch1;
335 ed[2*patch1+1]=ci;
344 //continue around the cell clockwise deleting points until ci is being cut and ni isn't.
345 ci=ni;
346 cid=nid;
347 ni=ed[2*ci];
348 nid=pos(x1,y1,rsq,ni);
349 while(nid>tolerance){
350 if(stackp==stacke) add_memory_ds(stackp);
351 *(stackp++)=ni;
352 ci=ni;
353 cid=nid;
354 ni=ed[2*ci];
355 nid=pos(x1,y1,rsq,ni);
363 //now ci is being cut and ni is not. If ni lies on the cutting plane, then do nothing, if it does not, introduce
364 //a new vertex.
365 if(nid>-tolerance){
366 patch2=ni;
367 ed[2*patch1]=patch2;
368 ed[2*patch2+1]=patch1;
369 }else{
370 if(p==current_vertices) add_memory_vertices();
371 fac=1/(nid-cid);
372 pts[2*p]=(pts[2*ci]*nid-pts[2*ni]*cid)*fac;
373 pts[2*p+1]=(pts[2*ci+1]*nid-pts[2*ni+1]*cid)*fac;
374 ed[2*p]=ni;
375 ed[2*ni+1]=p;
376 patch2=p++;
377 ed[patch2*2+1]=patch1;
378 ed[2*patch1]=patch2;
380 // Mark points on the delete stack
381 for(int *sp=ds;sp<stackp;sp++) ed[*sp*2]=-1;
383 // Remove them from the memory structure
384 while(stackp>ds) {
385 while(ed[2*--p]==-1);
386 int up=*(--stackp);
387 if(up<p) {
388 ed[2*ed[2*p]+1]=up;
389 ed[2*ed[2*p+1]]=up;
390 pts[2*up]=pts[2*p];
391 pts[2*up+1]=pts[2*p+1];
392 ed[2*up]=ed[2*p];
393 ed[2*up+1]=ed[2*p+1];
394 } else p++;
398 return true;
402 bool voronoicell_2d::wallcut(double wx1,double wy1,double wx2,double wy2){
403 double wox, woy, wpx, wpy, wpl, nl, pcx, pcy, rs;
404 if((wx1==0 && wy1==0) || (wx2==0 && wy2==0)) return true;
405 wox=wx2-wx1; woy=wy2-wy1;
406 wpx=-woy; wpy=wox;
407 wpl=pow((pow(wpx,2.0)+pow(wpy,2.0)),0.5);
409 wpx=wpx/wpl;
410 wpy=wpy/wpl;
414 nl=wx1*wpx+wy1*wpy;
417 pcx=wpx*nl;
418 pcy=wpy*nl;
422 if(((pcx<wx1 && pcx<wx2) || (pcx>wx1 && pcx>wx2)) ||
423 ((pcy<wy1 && pcy<wy2) || (pcy>wy1 && pcy>wy2))){
424 return true;
425 }else{
426 pcx*=2; pcy*=2;
427 rs=pcx*pcx+pcy*pcy;
428 this->plane_nonconvex(pcx,pcy,rs);
433 return true;
437 /** Calculates the perimeter of the Voronoi cell.
438 * \return A floating point number holding the calculated distance. */
439 double voronoicell_2d::perimeter() {
440 if(p==0) return 0;
441 int k=0,l;double perim=0,dx,dy;
442 do {
443 l=ed[2*k];
444 dx=pts[2*k]-pts[2*l];
445 dy=pts[2*k+1]-pts[2*l+1];
446 perim+=sqrt(dx*dx+dy*dy);
447 k=l;
448 } while (k!=0);
449 return 0.5*perim;
452 /** Calculates the area of the Voronoi cell.
453 * \return A floating point number holding the calculated distance. */
454 double voronoicell_2d::area() {
455 if(p==0) return 0;
456 int k(*ed);double area=0,x=*pts,y=pts[1],dx1,dy1,dx2,dy2;
457 dx1=pts[2*k]-x;dy1=pts[2*k+1]-y;
458 k=ed[2*k];
459 while(k!=0) {
460 dx2=pts[2*k]-x;dy2=pts[2*k+1]-y;
461 area+=dx1*dy2-dx2*dy1;
462 dx1=dx2;dy1=dy2;
463 k=ed[2*k];
465 return 0.125*area;
468 /** Calculates the centroid of the Voronoi cell.
469 * \param[out] (cx,cy) The coordinates of the centroid. */
470 void voronoicell_2d::centroid(double &cx,double &cy) {
471 cx=cy=0;
472 static const double third=1/3.0;
473 if(p==0) return;
474 int k(*ed);
475 double area,tarea=0,x=*pts,y=pts[1],dx1,dy1,dx2,dy2;
476 dx1=pts[2*k]-x;dy1=pts[2*k+1]-y;
477 k=ed[2*k];
478 while(k!=0) {
479 dx2=pts[2*k]-x;dy2=pts[2*k+1]-y;
480 area=dx1*dy2-dx2*dy1;
481 tarea+=area;
482 cx+=area*(dx1+dx2);
483 cy+=area*(dy1+dy2);
484 dx1=dx2;dy1=dy2;
485 k=ed[2*k];
487 tarea=third/tarea;
488 cx=0.5*(x+cx*tarea);
489 cy=0.5*(y+cy*tarea);
492 /** Computes the Voronoi cells for all particles in the container, and for each
493 * cell, outputs a line containing custom information about the cell structure.
494 * The output format is specified using an input string with control sequences
495 * similar to the standard C printf() routine.
496 * \param[in] format the format of the output lines, using control sequences to
497 * denote the different cell statistics.
498 * \param[in] i the ID of the particle associated with this Voronoi cell.
499 * \param[in] (x,y) the position of the particle associated with this Voronoi
500 * cell.
501 * \param[in] r a radius associated with the particle.
502 * \param[in] fp the file handle to write to. */
503 void voronoicell_2d::output_custom(const char *format,int i,double x,double y,double r,FILE *fp) {
504 char *fmp(const_cast<char*>(format));
505 while(*fmp!=0) {
506 if(*fmp=='%') {
507 fmp++;
508 switch(*fmp) {
510 // Particle-related output
511 case 'i': fprintf(fp,"%d",i);break;
512 case 'x': fprintf(fp,"%g",x);break;
513 case 'y': fprintf(fp,"%g",y);break;
514 case 'q': fprintf(fp,"%g %g",x,y);break;
515 case 'r': fprintf(fp,"%g",r);break;
517 // Vertex-related output
518 case 'w': fprintf(fp,"%d",p);break;
519 case 'm': fprintf(fp,"%g",0.25*max_radius_squared());break;
521 // Edge-related output
522 case 'p': fprintf(fp,"%g",perimeter());break;
524 // Area-related output
525 case 'a': fprintf(fp,"%g",area());break;
526 case 'c': {
527 double cx,cy;
528 centroid(cx,cy);
529 fprintf(fp,"%g %g",cx,cy);
530 } break;
531 case 'C': {
532 double cx,cy;
533 centroid(cx,cy);
534 fprintf(fp,"%g %g",x+cx,y+cy);
535 } break;
537 // End-of-string reached
538 case 0: fmp--;break;
540 // The percent sign is not part of a
541 // control sequence
542 default: putc('%',fp);putc(*fmp,fp);
544 } else putc(*fmp,fp);
545 fmp++;
547 fputs("\n",fp);
550 /** Doubles the storage for the vertices, by reallocating the pts and ed
551 * arrays. If the allocation exceeds the absolute maximum set in max_vertices,
552 * then the routine exits with a fatal error. */
553 void voronoicell_2d::add_memory_vertices() {
554 double *ppe(pts+2*current_vertices);
555 int *ede(ed+2*current_vertices);
557 // Double the memory allocation and check it is within range
558 current_vertices<<=1;
559 if(current_vertices>max_vertices) voropp_fatal_error("Vertex memory allocation exceeded absolute maximum",VOROPP_MEMORY_ERROR);
560 #if VOROPP_VERBOSE >=2
561 fprintf(stderr,"Vertex memory scaled up to %d\n",current_vertices);
562 #endif
564 // Copy the vertex positions
565 double *npts(new double[2*current_vertices]),*npp(npts),*pp(pts);
566 while(pp<ppe) *(npp++)=*(pp++);
567 delete [] pts;pts=npts;
569 // Copy the edge table
570 int *ned(new int[2*current_vertices]),*nep(ned),*edp(ed);
571 while(edp<ede) *(nep++)=*(edp++);
572 delete [] ed;ed=ned;
575 /** Doubles the size allocation of the delete stack. If the allocation exceeds
576 * the absolute maximum set in max_delete_size, then routine causes a fatal
577 * error.
578 * \param[in] stackp a reference to the current stack pointer. */
579 void voronoicell_2d::add_memory_ds(int *&stackp) {
580 current_delete_size<<=1;
581 if(current_delete_size>max_delete_size) voropp_fatal_error("Delete stack 1 memory allocation exceeded absolute maximum",VOROPP_MEMORY_ERROR);
582 #if VOROPP_VERBOSE >=2
583 fprintf(stderr,"Delete stack 1 memory scaled up to %d\n",current_delete_size);
584 #endif
585 int *dsn(new int[current_delete_size]),*dsnp(dsn),*dsp(ds);
586 while(dsp<stackp) *(dsnp++)=*(dsp++);
587 delete [] ds;ds=dsn;stackp=dsnp;
588 stacke=ds+current_delete_size;