2 * \brief Function implementations for the voronoicell_2d class. */
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() {
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
;
31 *q
=1;q
[1]=3;q
[2]=2;q
[3]=0;q
[4]=3;q
[5]=1;q
[6]=0;q
[7]=2;
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
;
48 for(int i
=0; i
<(2*noofbnds
); i
+=2){
50 pts
[i
+1]=2*bnds_loc
[i
+1];
55 }if((i
+2)==(noofbnds
*2)){
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;
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
));
85 /** Outputs the edges of the Voronoi cell in gnuplot format to an output
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
) {
93 fprintf(fp
,"%g %g\n",x
+0.5*pts
[2*k
],y
+0.5*pts
[2*k
+1]);
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
) {
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
);
111 fprintf(fp
,"%g,%g,%g>,r}\n",x
+0.5*pts
[2*k
],y
+0.5*pts
[2*k
+1],z
);
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];
123 s
=*ptsp
*(*ptsp
);ptsp
++;
124 s
+=*ptsp
*(*ptsp
);ptsp
++;
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
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.
145 up2
=ed
[2*up
];u2
=pos(x
,y
,rsq
,up2
);
146 up3
=ed
[2*up
+1];u3
=pos(x
,y
,rsq
,up3
);
148 while(u2
<tolerance
) {
157 while(u3
<tolerance
) {
168 // Add this point to the delete stack, and search clockwise
169 // to find additional points that need to be deleted.
173 while(u2
>tolerance
) {
174 if(stackp
==stacke
) add_memory_ds(stackp
);
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.
188 if(p
==current_vertices
) add_memory_vertices();
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
;
198 // Search counter-clockwise for additional points that need to be
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
);
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
217 if(p
==current_vertices
) add_memory_vertices();
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
;
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
233 while(ed
[2*--p
]==-1);
239 pts
[2*up
+1]=pts
[2*p
+1];
241 ed
[2*up
+1]=ed
[2*p
+1];
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]);
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
){
280 cid
=pos(x1
, y1
, rsq
, 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.
302 nid
=pos(x1
,y1
,rsq
,ni
);
303 if(nid
<tolerance
|| (((pts
[2*ni
]*x2
)+(pts
[2*ni
+1]*y2
))<tolerance
)) {
321 if(stackp
==stacke
) add_memory_ds(stackp
);
325 //see if ci lies on the cutting plane, if it doesnt introduce a new vertex
329 if(p
==current_vertices
) add_memory_vertices();
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
;
344 //continue around the cell clockwise deleting points until ci is being cut and ni isn't.
348 nid
=pos(x1
,y1
,rsq
,ni
);
349 while(nid
>tolerance
){
350 if(stackp
==stacke
) add_memory_ds(stackp
);
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
368 ed
[2*patch2
+1]=patch1
;
370 if(p
==current_vertices
) add_memory_vertices();
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
;
377 ed
[patch2
*2+1]=patch1
;
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
385 while(ed
[2*--p
]==-1);
391 pts
[2*up
+1]=pts
[2*p
+1];
393 ed
[2*up
+1]=ed
[2*p
+1];
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
;
407 wpl
=pow((pow(wpx
,2.0)+pow(wpy
,2.0)),0.5);
422 if(((pcx
<wx1
&& pcx
<wx2
) || (pcx
>wx1
&& pcx
>wx2
)) ||
423 ((pcy
<wy1
&& pcy
<wy2
) || (pcy
>wy1
&& pcy
>wy2
))){
428 this->plane_nonconvex(pcx
,pcy
,rs
);
437 /** Calculates the perimeter of the Voronoi cell.
438 * \return A floating point number holding the calculated distance. */
439 double voronoicell_2d::perimeter() {
441 int k
=0,l
;double perim
=0,dx
,dy
;
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
);
452 /** Calculates the area of the Voronoi cell.
453 * \return A floating point number holding the calculated distance. */
454 double voronoicell_2d::area() {
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
;
460 dx2
=pts
[2*k
]-x
;dy2
=pts
[2*k
+1]-y
;
461 area
+=dx1
*dy2
-dx2
*dy1
;
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
) {
472 static const double third
=1/3.0;
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
;
479 dx2
=pts
[2*k
]-x
;dy2
=pts
[2*k
+1]-y
;
480 area
=dx1
*dy2
-dx2
*dy1
;
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
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
));
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;
529 fprintf(fp
,"%g %g",cx
,cy
);
534 fprintf(fp
,"%g %g",x
+cx
,y
+cy
);
537 // End-of-string reached
540 // The percent sign is not part of a
542 default: putc('%',fp
);putc(*fmp
,fp
);
544 } else putc(*fmp
,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
);
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
++);
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
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
);
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
;