1 |
francois |
979 |
// Voro++, a 3D cell-based Voronoi library |
2 |
|
|
// |
3 |
|
|
// Author : Chris H. Rycroft (LBL / UC Berkeley) |
4 |
|
|
// Email : chr@alum.mit.edu |
5 |
|
|
// Date : August 30th 2011 |
6 |
|
|
|
7 |
|
|
/** \file cell.cc |
8 |
|
|
* \brief Function implementations for the voronoicell and related classes. */ |
9 |
|
|
|
10 |
|
|
#include <cmath> |
11 |
|
|
#include <cstring> |
12 |
|
|
|
13 |
|
|
#include "config.hh" |
14 |
|
|
#include "common.hh" |
15 |
|
|
#include "cell.hh" |
16 |
|
|
|
17 |
|
|
namespace voro { |
18 |
|
|
|
19 |
|
|
/** Constructs a Voronoi cell and sets up the initial memory. */ |
20 |
|
|
voronoicell_base::voronoicell_base() : |
21 |
|
|
current_vertices(init_vertices), current_vertex_order(init_vertex_order), |
22 |
|
|
current_delete_size(init_delete_size), current_delete2_size(init_delete2_size), |
23 |
|
|
ed(new int*[current_vertices]), nu(new int[current_vertices]), |
24 |
|
|
pts(new double[3*current_vertices]), mem(new int[current_vertex_order]), |
25 |
|
|
mec(new int[current_vertex_order]), mep(new int*[current_vertex_order]), |
26 |
|
|
ds(new int[current_delete_size]), stacke(ds+current_delete_size), |
27 |
|
|
ds2(new int[current_delete2_size]), stacke2(ds2+current_delete_size), |
28 |
|
|
current_marginal(init_marginal), marg(new int[current_marginal]) { |
29 |
|
|
int i; |
30 |
|
|
for(i=0;i<3;i++) { |
31 |
|
|
mem[i]=init_n_vertices;mec[i]=0; |
32 |
|
|
mep[i]=new int[init_n_vertices*((i<<1)+1)]; |
33 |
|
|
} |
34 |
|
|
mem[3]=init_3_vertices;mec[3]=0; |
35 |
|
|
mep[3]=new int[init_3_vertices*7]; |
36 |
|
|
for(i=4;i<current_vertex_order;i++) { |
37 |
|
|
mem[i]=init_n_vertices;mec[i]=0; |
38 |
|
|
mep[i]=new int[init_n_vertices*((i<<1)+1)]; |
39 |
|
|
} |
40 |
|
|
} |
41 |
|
|
|
42 |
|
|
/** The voronoicell destructor deallocates all the dynamic memory. */ |
43 |
|
|
voronoicell_base::~voronoicell_base() { |
44 |
|
|
for(int i=current_vertex_order-1;i>=0;i--) if(mem[i]>0) delete [] mep[i]; |
45 |
|
|
delete [] marg; |
46 |
|
|
delete [] ds2;delete [] ds; |
47 |
|
|
delete [] mep;delete [] mec; |
48 |
|
|
delete [] mem;delete [] pts; |
49 |
|
|
delete [] nu;delete [] ed; |
50 |
|
|
} |
51 |
|
|
|
52 |
|
|
/** Ensures that enough memory is allocated prior to carrying out a copy. |
53 |
|
|
* \param[in] vc a reference to the specialized version of the calling class. |
54 |
|
|
* \param[in] vb a pointered to the class to be copied. */ |
55 |
|
|
template<class vc_class> |
56 |
|
|
void voronoicell_base::check_memory_for_copy(vc_class &vc,voronoicell_base* vb) { |
57 |
|
|
while(current_vertex_order<vb->current_vertex_order) add_memory_vorder(vc); |
58 |
|
|
for(int i=0;i<current_vertex_order;i++) while(mem[i]<vb->mec[i]) add_memory(vc,i,ds2); |
59 |
|
|
while(current_vertices<vb->p) add_memory_vertices(vc); |
60 |
|
|
} |
61 |
|
|
|
62 |
|
|
/** Copies the vertex and edge information from another class. The routine |
63 |
|
|
* assumes that enough memory is available for the copy. |
64 |
|
|
* \param[in] vb a pointer to the class to copy. */ |
65 |
|
|
void voronoicell_base::copy(voronoicell_base* vb) { |
66 |
|
|
int i,j; |
67 |
|
|
p=vb->p;up=0; |
68 |
|
|
for(i=0;i<current_vertex_order;i++) { |
69 |
|
|
mec[i]=vb->mec[i]; |
70 |
|
|
for(j=0;j<mec[i]*(2*i+1);j++) mep[i][j]=vb->mep[i][j]; |
71 |
|
|
for(j=0;j<mec[i]*(2*i+1);j+=2*i+1) ed[mep[i][j+2*i]]=mep[i]+j; |
72 |
|
|
} |
73 |
|
|
for(i=0;i<p;i++) nu[i]=vb->nu[i]; |
74 |
|
|
for(i=0;i<3*p;i++) pts[i]=vb->pts[i]; |
75 |
|
|
} |
76 |
|
|
|
77 |
|
|
/** Copies the information from another voronoicell class into this |
78 |
|
|
* class, extending memory allocation if necessary. |
79 |
|
|
* \param[in] c the class to copy. */ |
80 |
|
|
void voronoicell_neighbor::operator=(voronoicell &c) { |
81 |
|
|
voronoicell_base *vb=((voronoicell_base*) &c); |
82 |
|
|
check_memory_for_copy(*this,vb);copy(vb); |
83 |
|
|
int i,j; |
84 |
|
|
for(i=0;i<c.current_vertex_order;i++) { |
85 |
|
|
for(j=0;j<c.mec[i]*i;j++) mne[i][j]=0; |
86 |
|
|
for(j=0;j<c.mec[i];j++) ne[c.mep[i][(2*i+1)*j+2*i]]=mne[i]+(j*i); |
87 |
|
|
} |
88 |
|
|
} |
89 |
|
|
|
90 |
|
|
/** Copies the information from another voronoicell_neighbor class into this |
91 |
|
|
* class, extending memory allocation if necessary. |
92 |
|
|
* \param[in] c the class to copy. */ |
93 |
|
|
void voronoicell_neighbor::operator=(voronoicell_neighbor &c) { |
94 |
|
|
voronoicell_base *vb=((voronoicell_base*) &c); |
95 |
|
|
check_memory_for_copy(*this,vb);copy(vb); |
96 |
|
|
int i,j; |
97 |
|
|
for(i=0;i<c.current_vertex_order;i++) { |
98 |
|
|
for(j=0;j<c.mec[i]*i;j++) mne[i][j]=c.mne[i][j]; |
99 |
|
|
for(j=0;j<c.mec[i];j++) ne[c.mep[i][(2*i+1)*j+2*i]]=mne[i]+(j*i); |
100 |
|
|
} |
101 |
|
|
} |
102 |
|
|
|
103 |
|
|
/** Translates the vertices of the Voronoi cell by a given vector. |
104 |
|
|
* \param[in] (x,y,z) the coordinates of the vector. */ |
105 |
|
|
void voronoicell_base::translate(double x,double y,double z) { |
106 |
|
|
x*=2;y*=2;z*=2; |
107 |
|
|
double *ptsp=pts; |
108 |
|
|
while(ptsp<pts+3*p) { |
109 |
|
|
*(ptsp++)=x;*(ptsp++)=y;*(ptsp++)=z; |
110 |
|
|
} |
111 |
|
|
} |
112 |
|
|
|
113 |
|
|
/** Increases the memory storage for a particular vertex order, by increasing |
114 |
|
|
* the size of the of the corresponding mep array. If the arrays already exist, |
115 |
|
|
* their size is doubled; if they don't exist, then new ones of size |
116 |
|
|
* init_n_vertices are allocated. The routine also ensures that the pointers in |
117 |
|
|
* the ed array are updated, by making use of the back pointers. For the cases |
118 |
|
|
* where the back pointer has been temporarily overwritten in the marginal |
119 |
|
|
* vertex code, the auxiliary delete stack is scanned to find out how to update |
120 |
|
|
* the ed value. If the template has been instantiated with the neighbor |
121 |
|
|
* tracking turned on, then the routine also reallocates the corresponding mne |
122 |
|
|
* array. |
123 |
|
|
* \param[in] i the order of the vertex memory to be increased. */ |
124 |
|
|
template<class vc_class> |
125 |
|
|
void voronoicell_base::add_memory(vc_class &vc,int i,int *stackp2) { |
126 |
|
|
int s=(i<<1)+1; |
127 |
|
|
if(mem[i]==0) { |
128 |
|
|
vc.n_allocate(i,init_n_vertices); |
129 |
|
|
mep[i]=new int[init_n_vertices*s]; |
130 |
|
|
mem[i]=init_n_vertices; |
131 |
|
|
#if VOROPP_VERBOSE >=2 |
132 |
|
|
fprintf(stderr,"Order %d vertex memory created\n",i); |
133 |
|
|
#endif |
134 |
|
|
} else { |
135 |
|
|
int j=0,k,*l; |
136 |
|
|
mem[i]<<=1; |
137 |
|
|
if(mem[i]>max_n_vertices) voro_fatal_error("Point memory allocation exceeded absolute maximum",VOROPP_MEMORY_ERROR); |
138 |
|
|
#if VOROPP_VERBOSE >=2 |
139 |
|
|
fprintf(stderr,"Order %d vertex memory scaled up to %d\n",i,mem[i]); |
140 |
|
|
#endif |
141 |
|
|
l=new int[s*mem[i]]; |
142 |
|
|
int m=0; |
143 |
|
|
vc.n_allocate_aux1(i); |
144 |
|
|
while(j<s*mec[i]) { |
145 |
|
|
k=mep[i][j+(i<<1)]; |
146 |
|
|
if(k>=0) { |
147 |
|
|
ed[k]=l+j; |
148 |
|
|
vc.n_set_to_aux1_offset(k,m); |
149 |
|
|
} else { |
150 |
|
|
int *dsp; |
151 |
|
|
for(dsp=ds2;dsp<stackp2;dsp++) { |
152 |
|
|
if(ed[*dsp]==mep[i]+j) { |
153 |
|
|
ed[*dsp]=l+j; |
154 |
|
|
vc.n_set_to_aux1_offset(*dsp,m); |
155 |
|
|
break; |
156 |
|
|
} |
157 |
|
|
} |
158 |
|
|
if(dsp==stackp2) voro_fatal_error("Couldn't relocate dangling pointer",VOROPP_INTERNAL_ERROR); |
159 |
|
|
#if VOROPP_VERBOSE >=3 |
160 |
|
|
fputs("Relocated dangling pointer",stderr); |
161 |
|
|
#endif |
162 |
|
|
} |
163 |
|
|
for(k=0;k<s;k++,j++) l[j]=mep[i][j]; |
164 |
|
|
for(k=0;k<i;k++,m++) vc.n_copy_to_aux1(i,m); |
165 |
|
|
} |
166 |
|
|
delete [] mep[i]; |
167 |
|
|
mep[i]=l; |
168 |
|
|
vc.n_switch_to_aux1(i); |
169 |
|
|
} |
170 |
|
|
} |
171 |
|
|
|
172 |
|
|
/** Doubles the maximum number of vertices allowed, by reallocating the ed, nu, |
173 |
|
|
* and pts arrays. If the allocation exceeds the absolute maximum set in |
174 |
|
|
* max_vertices, then the routine exits with a fatal error. If the template has |
175 |
|
|
* been instantiated with the neighbor tracking turned on, then the routine |
176 |
|
|
* also reallocates the ne array. */ |
177 |
|
|
template<class vc_class> |
178 |
|
|
void voronoicell_base::add_memory_vertices(vc_class &vc) { |
179 |
|
|
int i=(current_vertices<<1),j,**pp,*pnu; |
180 |
|
|
if(i>max_vertices) voro_fatal_error("Vertex memory allocation exceeded absolute maximum",VOROPP_MEMORY_ERROR); |
181 |
|
|
#if VOROPP_VERBOSE >=2 |
182 |
|
|
fprintf(stderr,"Vertex memory scaled up to %d\n",i); |
183 |
|
|
#endif |
184 |
|
|
double *ppts; |
185 |
|
|
pp=new int*[i]; |
186 |
|
|
for(j=0;j<current_vertices;j++) pp[j]=ed[j]; |
187 |
|
|
delete [] ed;ed=pp; |
188 |
|
|
vc.n_add_memory_vertices(i); |
189 |
|
|
pnu=new int[i]; |
190 |
|
|
for(j=0;j<current_vertices;j++) pnu[j]=nu[j]; |
191 |
|
|
delete [] nu;nu=pnu; |
192 |
|
|
ppts=new double[3*i]; |
193 |
|
|
for(j=0;j<3*current_vertices;j++) ppts[j]=pts[j]; |
194 |
|
|
delete [] pts;pts=ppts; |
195 |
|
|
current_vertices=i; |
196 |
|
|
} |
197 |
|
|
|
198 |
|
|
/** Doubles the maximum allowed vertex order, by reallocating mem, mep, and mec |
199 |
|
|
* arrays. If the allocation exceeds the absolute maximum set in |
200 |
|
|
* max_vertex_order, then the routine causes a fatal error. If the template has |
201 |
|
|
* been instantiated with the neighbor tracking turned on, then the routine |
202 |
|
|
* also reallocates the mne array. */ |
203 |
|
|
template<class vc_class> |
204 |
|
|
void voronoicell_base::add_memory_vorder(vc_class &vc) { |
205 |
|
|
int i=(current_vertex_order<<1),j,*p1,**p2; |
206 |
|
|
if(i>max_vertex_order) voro_fatal_error("Vertex order memory allocation exceeded absolute maximum",VOROPP_MEMORY_ERROR); |
207 |
|
|
#if VOROPP_VERBOSE >=2 |
208 |
|
|
fprintf(stderr,"Vertex order memory scaled up to %d\n",i); |
209 |
|
|
#endif |
210 |
|
|
p1=new int[i]; |
211 |
|
|
for(j=0;j<current_vertex_order;j++) p1[j]=mem[j];while(j<i) p1[j++]=0; |
212 |
|
|
delete [] mem;mem=p1; |
213 |
|
|
p2=new int*[i]; |
214 |
|
|
for(j=0;j<current_vertex_order;j++) p2[j]=mep[j]; |
215 |
|
|
delete [] mep;mep=p2; |
216 |
|
|
p1=new int[i]; |
217 |
|
|
for(j=0;j<current_vertex_order;j++) p1[j]=mec[j];while(j<i) p1[j++]=0; |
218 |
|
|
delete [] mec;mec=p1; |
219 |
|
|
vc.n_add_memory_vorder(i); |
220 |
|
|
current_vertex_order=i; |
221 |
|
|
} |
222 |
|
|
|
223 |
|
|
/** Doubles the size allocation of the main delete stack. If the allocation |
224 |
|
|
* exceeds the absolute maximum set in max_delete_size, then routine causes a |
225 |
|
|
* fatal error. */ |
226 |
|
|
void voronoicell_base::add_memory_ds(int *&stackp) { |
227 |
|
|
current_delete_size<<=1; |
228 |
|
|
if(current_delete_size>max_delete_size) voro_fatal_error("Delete stack 1 memory allocation exceeded absolute maximum",VOROPP_MEMORY_ERROR); |
229 |
|
|
#if VOROPP_VERBOSE >=2 |
230 |
|
|
fprintf(stderr,"Delete stack 1 memory scaled up to %d\n",current_delete_size); |
231 |
|
|
#endif |
232 |
|
|
int *dsn=new int[current_delete_size],*dsnp=dsn,*dsp=ds; |
233 |
|
|
while(dsp<stackp) *(dsnp++)=*(dsp++); |
234 |
|
|
delete [] ds;ds=dsn;stackp=dsnp; |
235 |
|
|
stacke=ds+current_delete_size; |
236 |
|
|
} |
237 |
|
|
|
238 |
|
|
/** Doubles the size allocation of the auxiliary delete stack. If the |
239 |
|
|
* allocation exceeds the absolute maximum set in max_delete2_size, then the |
240 |
|
|
* routine causes a fatal error. */ |
241 |
|
|
void voronoicell_base::add_memory_ds2(int *&stackp2) { |
242 |
|
|
current_delete2_size<<=1; |
243 |
|
|
if(current_delete2_size>max_delete2_size) voro_fatal_error("Delete stack 2 memory allocation exceeded absolute maximum",VOROPP_MEMORY_ERROR); |
244 |
|
|
#if VOROPP_VERBOSE >=2 |
245 |
|
|
fprintf(stderr,"Delete stack 2 memory scaled up to %d\n",current_delete2_size); |
246 |
|
|
#endif |
247 |
|
|
int *dsn=new int[current_delete2_size],*dsnp=dsn,*dsp=ds2; |
248 |
|
|
while(dsp<stackp2) *(dsnp++)=*(dsp++); |
249 |
|
|
delete [] ds2;ds2=dsn;stackp2=dsnp; |
250 |
|
|
stacke2=ds2+current_delete2_size; |
251 |
|
|
} |
252 |
|
|
|
253 |
|
|
/** Initializes a Voronoi cell as a rectangular box with the given dimensions. |
254 |
|
|
* \param[in] (xmin,xmax) the minimum and maximum x coordinates. |
255 |
|
|
* \param[in] (ymin,ymax) the minimum and maximum y coordinates. |
256 |
|
|
* \param[in] (zmin,zmax) the minimum and maximum z coordinates. */ |
257 |
|
|
void voronoicell_base::init_base(double xmin,double xmax,double ymin,double ymax,double zmin,double zmax) { |
258 |
|
|
for(int i=0;i<current_vertex_order;i++) mec[i]=0;up=0; |
259 |
|
|
mec[3]=p=8;xmin*=2;xmax*=2;ymin*=2;ymax*=2;zmin*=2;zmax*=2; |
260 |
|
|
*pts=xmin;pts[1]=ymin;pts[2]=zmin; |
261 |
|
|
pts[3]=xmax;pts[4]=ymin;pts[5]=zmin; |
262 |
|
|
pts[6]=xmin;pts[7]=ymax;pts[8]=zmin; |
263 |
|
|
pts[9]=xmax;pts[10]=ymax;pts[11]=zmin; |
264 |
|
|
pts[12]=xmin;pts[13]=ymin;pts[14]=zmax; |
265 |
|
|
pts[15]=xmax;pts[16]=ymin;pts[17]=zmax; |
266 |
|
|
pts[18]=xmin;pts[19]=ymax;pts[20]=zmax; |
267 |
|
|
pts[21]=xmax;pts[22]=ymax;pts[23]=zmax; |
268 |
|
|
int *q=mep[3]; |
269 |
|
|
*q=1;q[1]=4;q[2]=2;q[3]=2;q[4]=1;q[5]=0;q[6]=0; |
270 |
|
|
q[7]=3;q[8]=5;q[9]=0;q[10]=2;q[11]=1;q[12]=0;q[13]=1; |
271 |
|
|
q[14]=0;q[15]=6;q[16]=3;q[17]=2;q[18]=1;q[19]=0;q[20]=2; |
272 |
|
|
q[21]=2;q[22]=7;q[23]=1;q[24]=2;q[25]=1;q[26]=0;q[27]=3; |
273 |
|
|
q[28]=6;q[29]=0;q[30]=5;q[31]=2;q[32]=1;q[33]=0;q[34]=4; |
274 |
|
|
q[35]=4;q[36]=1;q[37]=7;q[38]=2;q[39]=1;q[40]=0;q[41]=5; |
275 |
|
|
q[42]=7;q[43]=2;q[44]=4;q[45]=2;q[46]=1;q[47]=0;q[48]=6; |
276 |
|
|
q[49]=5;q[50]=3;q[51]=6;q[52]=2;q[53]=1;q[54]=0;q[55]=7; |
277 |
|
|
*ed=q;ed[1]=q+7;ed[2]=q+14;ed[3]=q+21; |
278 |
|
|
ed[4]=q+28;ed[5]=q+35;ed[6]=q+42;ed[7]=q+49; |
279 |
|
|
*nu=nu[1]=nu[2]=nu[3]=nu[4]=nu[5]=nu[6]=nu[7]=3; |
280 |
|
|
} |
281 |
|
|
|
282 |
|
|
/** Initializes a Voronoi cell as a regular octahedron. |
283 |
|
|
* \param[in] l The distance from the octahedron center to a vertex. Six |
284 |
|
|
* vertices are initialized at (-l,0,0), (l,0,0), (0,-l,0), |
285 |
|
|
* (0,l,0), (0,0,-l), and (0,0,l). */ |
286 |
|
|
void voronoicell_base::init_octahedron_base(double l) { |
287 |
|
|
for(int i=0;i<current_vertex_order;i++) mec[i]=0;up=0; |
288 |
|
|
mec[4]=p=6;l*=2; |
289 |
|
|
*pts=-l;pts[1]=0;pts[2]=0; |
290 |
|
|
pts[3]=l;pts[4]=0;pts[5]=0; |
291 |
|
|
pts[6]=0;pts[7]=-l;pts[8]=0; |
292 |
|
|
pts[9]=0;pts[10]=l;pts[11]=0; |
293 |
|
|
pts[12]=0;pts[13]=0;pts[14]=-l; |
294 |
|
|
pts[15]=0;pts[16]=0;pts[17]=l; |
295 |
|
|
int *q=mep[4]; |
296 |
|
|
*q=2;q[1]=5;q[2]=3;q[3]=4;q[4]=0;q[5]=0;q[6]=0;q[7]=0;q[8]=0; |
297 |
|
|
q[9]=2;q[10]=4;q[11]=3;q[12]=5;q[13]=2;q[14]=2;q[15]=2;q[16]=2;q[17]=1; |
298 |
|
|
q[18]=0;q[19]=4;q[20]=1;q[21]=5;q[22]=0;q[23]=3;q[24]=0;q[25]=1;q[26]=2; |
299 |
|
|
q[27]=0;q[28]=5;q[29]=1;q[30]=4;q[31]=2;q[32]=3;q[33]=2;q[34]=1;q[35]=3; |
300 |
|
|
q[36]=0;q[37]=3;q[38]=1;q[39]=2;q[40]=3;q[41]=3;q[42]=1;q[43]=1;q[44]=4; |
301 |
|
|
q[45]=0;q[46]=2;q[47]=1;q[48]=3;q[49]=1;q[50]=3;q[51]=3;q[52]=1;q[53]=5; |
302 |
|
|
*ed=q;ed[1]=q+9;ed[2]=q+18;ed[3]=q+27;ed[4]=q+36;ed[5]=q+45; |
303 |
|
|
*nu=nu[1]=nu[2]=nu[3]=nu[4]=nu[5]=4; |
304 |
|
|
} |
305 |
|
|
|
306 |
|
|
/** Initializes a Voronoi cell as a tetrahedron. It assumes that the normal to |
307 |
|
|
* the face for the first three vertices points inside. |
308 |
|
|
* \param (x0,y0,z0) a position vector for the first vertex. |
309 |
|
|
* \param (x1,y1,z1) a position vector for the second vertex. |
310 |
|
|
* \param (x2,y2,z2) a position vector for the third vertex. |
311 |
|
|
* \param (x3,y3,z3) a position vector for the fourth vertex. */ |
312 |
|
|
void voronoicell_base::init_tetrahedron_base(double x0,double y0,double z0,double x1,double y1,double z1,double x2,double y2,double z2,double x3,double y3,double z3) { |
313 |
|
|
for(int i=0;i<current_vertex_order;i++) mec[i]=0;up=0; |
314 |
|
|
mec[3]=p=4; |
315 |
|
|
*pts=x0*2;pts[1]=y0*2;pts[2]=z0*2; |
316 |
|
|
pts[3]=x1*2;pts[4]=y1*2;pts[5]=z1*2; |
317 |
|
|
pts[6]=x2*2;pts[7]=y2*2;pts[8]=z2*2; |
318 |
|
|
pts[9]=x3*2;pts[10]=y3*2;pts[11]=z3*2; |
319 |
|
|
int *q=mep[3]; |
320 |
|
|
*q=1;q[1]=3;q[2]=2;q[3]=0;q[4]=0;q[5]=0;q[6]=0; |
321 |
|
|
q[7]=0;q[8]=2;q[9]=3;q[10]=0;q[11]=2;q[12]=1;q[13]=1; |
322 |
|
|
q[14]=0;q[15]=3;q[16]=1;q[17]=2;q[18]=2;q[19]=1;q[20]=2; |
323 |
|
|
q[21]=0;q[22]=1;q[23]=2;q[24]=1;q[25]=2;q[26]=1;q[27]=3; |
324 |
|
|
*ed=q;ed[1]=q+7;ed[2]=q+14;ed[3]=q+21; |
325 |
|
|
*nu=nu[1]=nu[2]=nu[3]=3; |
326 |
|
|
} |
327 |
|
|
|
328 |
|
|
/** Checks that the relational table of the Voronoi cell is accurate, and |
329 |
|
|
* prints out any errors. This algorithm is O(p), so running it every time the |
330 |
|
|
* plane routine is called will result in a significant slowdown. */ |
331 |
|
|
void voronoicell_base::check_relations() { |
332 |
|
|
int i,j; |
333 |
|
|
for(i=0;i<p;i++) for(j=0;j<nu[i];j++) if(ed[ed[i][j]][ed[i][nu[i]+j]]!=i) |
334 |
|
|
printf("Relational error at point %d, edge %d.\n",i,j); |
335 |
|
|
} |
336 |
|
|
|
337 |
|
|
/** This routine checks for any two vertices that are connected by more than |
338 |
|
|
* one edge. The plane algorithm is designed so that this should not happen, so |
339 |
|
|
* any occurrences are most likely errors. Note that the routine is O(p), so |
340 |
|
|
* running it every time the plane routine is called will result in a |
341 |
|
|
* significant slowdown. */ |
342 |
|
|
void voronoicell_base::check_duplicates() { |
343 |
|
|
int i,j,k; |
344 |
|
|
for(i=0;i<p;i++) for(j=1;j<nu[i];j++) for(k=0;k<j;k++) if(ed[i][j]==ed[i][k]) |
345 |
|
|
printf("Duplicate edges: (%d,%d) and (%d,%d) [%d]\n",i,j,i,k,ed[i][j]); |
346 |
|
|
} |
347 |
|
|
|
348 |
|
|
/** Constructs the relational table if the edges have been specified. */ |
349 |
|
|
void voronoicell_base::construct_relations() { |
350 |
|
|
int i,j,k,l; |
351 |
|
|
for(i=0;i<p;i++) for(j=0;j<nu[i];j++) { |
352 |
|
|
k=ed[i][j]; |
353 |
|
|
l=0; |
354 |
|
|
while(ed[k][l]!=i) { |
355 |
|
|
l++; |
356 |
|
|
if(l==nu[k]) voro_fatal_error("Relation table construction failed",VOROPP_INTERNAL_ERROR); |
357 |
|
|
} |
358 |
|
|
ed[i][nu[i]+j]=l; |
359 |
|
|
} |
360 |
|
|
} |
361 |
|
|
|
362 |
|
|
/** Starting from a point within the current cutting plane, this routine attempts |
363 |
|
|
* to find an edge to a point outside the cutting plane. This prevents the plane |
364 |
|
|
* routine from . |
365 |
|
|
* \param[in] vc a reference to the specialized version of the calling class. |
366 |
|
|
* \param[in,out] up */ |
367 |
|
|
template<class vc_class> |
368 |
|
|
inline bool voronoicell_base::search_for_outside_edge(vc_class &vc,int &up) { |
369 |
|
|
int i,lp,lw,*j(ds2),*stackp2(ds2); |
370 |
|
|
double l; |
371 |
|
|
*(stackp2++)=up; |
372 |
|
|
while(j<stackp2) { |
373 |
|
|
up=*(j++); |
374 |
|
|
for(i=0;i<nu[up];i++) { |
375 |
|
|
lp=ed[up][i]; |
376 |
|
|
lw=m_test(lp,l); |
377 |
|
|
if(lw==-1) return true; |
378 |
|
|
else if(lw==0) add_to_stack(vc,lp,stackp2); |
379 |
|
|
} |
380 |
|
|
} |
381 |
|
|
return false; |
382 |
|
|
} |
383 |
|
|
|
384 |
|
|
/** Adds a point to the auxiliary delete stack if it is not already there. |
385 |
|
|
* \param[in] vc a reference to the specialized version of the calling class. |
386 |
|
|
* \param[in] lp the index of the point to add. |
387 |
|
|
* \param[in,out] stackp2 a pointer to the end of the stack entries. */ |
388 |
|
|
template<class vc_class> |
389 |
|
|
inline void voronoicell_base::add_to_stack(vc_class &vc,int lp,int *&stackp2) { |
390 |
|
|
for(int *k(ds2);k<stackp2;k++) if(*k==lp) return; |
391 |
|
|
if(stackp2==stacke2) add_memory_ds2(stackp2); |
392 |
|
|
*(stackp2++)=lp; |
393 |
|
|
} |
394 |
|
|
|
395 |
|
|
/** Cuts the Voronoi cell by a particle whose center is at a separation of |
396 |
|
|
* (x,y,z) from the cell center. The value of rsq should be initially set to |
397 |
|
|
* \f$x^2+y^2+z^2\f$. |
398 |
|
|
* \param[in] vc a reference to the specialized version of the calling class. |
399 |
|
|
* \param[in] (x,y,z) the normal vector to the plane. |
400 |
|
|
* \param[in] rsq the distance along this vector of the plane. |
401 |
|
|
* \param[in] p_id the plane ID (for neighbor tracking only). |
402 |
|
|
* \return False if the plane cut deleted the cell entirely, true otherwise. */ |
403 |
|
|
template<class vc_class> |
404 |
|
|
bool voronoicell_base::nplane(vc_class &vc,double x,double y,double z,double rsq,int p_id) { |
405 |
|
|
int count=0,i,j,k,lp=up,cp,qp,rp,*stackp(ds),*stackp2(ds2),*dsp; |
406 |
|
|
int us=0,ls=0,qs,iqs,cs,uw,qw,lw; |
407 |
|
|
int *edp,*edd; |
408 |
|
|
double u,l,r,q;bool complicated_setup=false,new_double_edge=false,double_edge=false; |
409 |
|
|
|
410 |
|
|
// Initialize the safe testing routine |
411 |
|
|
n_marg=0;px=x;py=y;pz=z;prsq=rsq; |
412 |
|
|
|
413 |
|
|
// Test approximately sqrt(n)/4 points for their proximity to the plane |
414 |
|
|
// and keep the one which is closest |
415 |
|
|
uw=m_test(up,u); |
416 |
|
|
|
417 |
|
|
// Starting from an initial guess, we now move from vertex to vertex, |
418 |
|
|
// to try and find an edge which intersects the cutting plane, |
419 |
|
|
// or a vertex which is on the plane |
420 |
|
|
try { |
421 |
|
|
if(uw==1) { |
422 |
|
|
|
423 |
|
|
// The test point is inside the cutting plane. |
424 |
|
|
us=0; |
425 |
|
|
do { |
426 |
|
|
lp=ed[up][us]; |
427 |
|
|
lw=m_test(lp,l); |
428 |
|
|
if(l<u) break; |
429 |
|
|
us++; |
430 |
|
|
} while (us<nu[up]); |
431 |
|
|
|
432 |
|
|
if(us==nu[up]) { |
433 |
|
|
return false; |
434 |
|
|
} |
435 |
|
|
|
436 |
|
|
ls=ed[up][nu[up]+us]; |
437 |
|
|
while(lw==1) { |
438 |
|
|
if(++count>=p) throw true; |
439 |
|
|
u=l;up=lp; |
440 |
|
|
for(us=0;us<ls;us++) { |
441 |
|
|
lp=ed[up][us]; |
442 |
|
|
lw=m_test(lp,l); |
443 |
|
|
if(l<u) break; |
444 |
|
|
} |
445 |
|
|
if(us==ls) { |
446 |
|
|
us++; |
447 |
|
|
while(us<nu[up]) { |
448 |
|
|
lp=ed[up][us]; |
449 |
|
|
lw=m_test(lp,l); |
450 |
|
|
if(l<u) break; |
451 |
|
|
us++; |
452 |
|
|
} |
453 |
|
|
if(us==nu[up]) { |
454 |
|
|
return false; |
455 |
|
|
} |
456 |
|
|
} |
457 |
|
|
ls=ed[up][nu[up]+us]; |
458 |
|
|
} |
459 |
|
|
|
460 |
|
|
// If the last point in the iteration is within the |
461 |
|
|
// plane, we need to do the complicated setup |
462 |
|
|
// routine. Otherwise, we use the regular iteration. |
463 |
|
|
if(lw==0) { |
464 |
|
|
up=lp; |
465 |
|
|
complicated_setup=true; |
466 |
|
|
} else complicated_setup=false; |
467 |
|
|
} else if(uw==-1) { |
468 |
|
|
us=0; |
469 |
|
|
do { |
470 |
|
|
qp=ed[up][us]; |
471 |
|
|
qw=m_test(qp,q); |
472 |
|
|
if(u<q) break; |
473 |
|
|
us++; |
474 |
|
|
} while (us<nu[up]); |
475 |
|
|
if(us==nu[up]) return true; |
476 |
|
|
|
477 |
|
|
while(qw==-1) { |
478 |
|
|
qs=ed[up][nu[up]+us]; |
479 |
|
|
if(++count>=p) throw true; |
480 |
|
|
u=q;up=qp; |
481 |
|
|
for(us=0;us<qs;us++) { |
482 |
|
|
qp=ed[up][us]; |
483 |
|
|
qw=m_test(qp,q); |
484 |
|
|
if(u<q) break; |
485 |
|
|
} |
486 |
|
|
if(us==qs) { |
487 |
|
|
us++; |
488 |
|
|
while(us<nu[up]) { |
489 |
|
|
qp=ed[up][us]; |
490 |
|
|
qw=m_test(qp,q); |
491 |
|
|
if(u<q) break; |
492 |
|
|
us++; |
493 |
|
|
} |
494 |
|
|
if(us==nu[up]) return true; |
495 |
|
|
} |
496 |
|
|
} |
497 |
|
|
if(qw==1) { |
498 |
|
|
lp=up;ls=us;l=u; |
499 |
|
|
up=qp;us=ed[lp][nu[lp]+ls];u=q; |
500 |
|
|
complicated_setup=false; |
501 |
|
|
} else { |
502 |
|
|
up=qp; |
503 |
|
|
complicated_setup=true; |
504 |
|
|
} |
505 |
|
|
} else { |
506 |
|
|
|
507 |
|
|
// Our original test point was on the plane, so we |
508 |
|
|
// automatically head for the complicated setup |
509 |
|
|
// routine |
510 |
|
|
complicated_setup=true; |
511 |
|
|
} |
512 |
|
|
} |
513 |
|
|
catch(bool except) { |
514 |
|
|
// This routine is a fall-back, in case floating point errors |
515 |
|
|
// cause the usual search routine to fail. In the fall-back |
516 |
|
|
// routine, we just test every edge to find one straddling |
517 |
|
|
// the plane. |
518 |
|
|
#if VOROPP_VERBOSE >=1 |
519 |
|
|
fputs("Bailed out of convex calculation\n",stderr); |
520 |
|
|
#endif |
521 |
|
|
qw=1;lw=0; |
522 |
|
|
for(qp=0;qp<p;qp++) { |
523 |
|
|
qw=m_test(qp,q); |
524 |
|
|
if(qw==1) { |
525 |
|
|
|
526 |
|
|
// The point is inside the cutting space. Now |
527 |
|
|
// see if we can find a neighbor which isn't. |
528 |
|
|
for(us=0;us<nu[qp];us++) { |
529 |
|
|
lp=ed[qp][us]; |
530 |
|
|
if(lp<qp) { |
531 |
|
|
lw=m_test(lp,l); |
532 |
|
|
if(lw!=1) break; |
533 |
|
|
} |
534 |
|
|
} |
535 |
|
|
if(us<nu[qp]) { |
536 |
|
|
up=qp; |
537 |
|
|
if(lw==0) { |
538 |
|
|
complicated_setup=true; |
539 |
|
|
} else { |
540 |
|
|
complicated_setup=false; |
541 |
|
|
u=q; |
542 |
|
|
ls=ed[up][nu[up]+us]; |
543 |
|
|
} |
544 |
|
|
break; |
545 |
|
|
} |
546 |
|
|
} else if(qw==-1) { |
547 |
|
|
|
548 |
|
|
// The point is outside the cutting space. See |
549 |
|
|
// if we can find a neighbor which isn't. |
550 |
|
|
for(ls=0;ls<nu[qp];ls++) { |
551 |
|
|
up=ed[qp][ls]; |
552 |
|
|
if(up<qp) { |
553 |
|
|
uw=m_test(up,u); |
554 |
|
|
if(uw!=-1) break; |
555 |
|
|
} |
556 |
|
|
} |
557 |
|
|
if(ls<nu[qp]) { |
558 |
|
|
if(uw==0) { |
559 |
|
|
up=qp; |
560 |
|
|
complicated_setup=true; |
561 |
|
|
} else { |
562 |
|
|
complicated_setup=false; |
563 |
|
|
lp=qp;l=q; |
564 |
|
|
us=ed[lp][nu[lp]+ls]; |
565 |
|
|
} |
566 |
|
|
break; |
567 |
|
|
} |
568 |
|
|
} else { |
569 |
|
|
|
570 |
|
|
// The point is in the plane, so we just |
571 |
|
|
// proceed with the complicated setup routine |
572 |
|
|
up=qp; |
573 |
|
|
complicated_setup=true; |
574 |
|
|
break; |
575 |
|
|
} |
576 |
|
|
} |
577 |
|
|
if(qp==p) return qw==-1?true:false; |
578 |
|
|
} |
579 |
|
|
|
580 |
|
|
// We're about to add the first point of the new facet. In either |
581 |
|
|
// routine, we have to add a point, so first check there's space for |
582 |
|
|
// it. |
583 |
|
|
if(p==current_vertices) add_memory_vertices(vc); |
584 |
|
|
|
585 |
|
|
if(complicated_setup) { |
586 |
|
|
|
587 |
|
|
// We want to be strict about reaching the conclusion that the |
588 |
|
|
// cell is entirely within the cutting plane. It's not enough |
589 |
|
|
// to find a vertex that has edges which are all inside or on |
590 |
|
|
// the plane. If the vertex has neighbors that are also on the |
591 |
|
|
// plane, we should check those too. |
592 |
|
|
if(!search_for_outside_edge(vc,up)) return false; |
593 |
|
|
|
594 |
|
|
// The search algorithm found a point which is on the cutting |
595 |
|
|
// plane. We leave that point in place, and create a new one at |
596 |
|
|
// the same location. |
597 |
|
|
pts[3*p]=pts[3*up]; |
598 |
|
|
pts[3*p+1]=pts[3*up+1]; |
599 |
|
|
pts[3*p+2]=pts[3*up+2]; |
600 |
|
|
|
601 |
|
|
// Search for a collection of edges of the test vertex which |
602 |
|
|
// are outside of the cutting space. Begin by testing the |
603 |
|
|
// zeroth edge. |
604 |
|
|
i=0; |
605 |
|
|
lp=*ed[up]; |
606 |
|
|
lw=m_test(lp,l); |
607 |
|
|
if(lw!=-1) { |
608 |
|
|
|
609 |
|
|
// The first edge is either inside the cutting space, |
610 |
|
|
// or lies within the cutting plane. Test the edges |
611 |
|
|
// sequentially until we find one that is outside. |
612 |
|
|
rp=lw; |
613 |
|
|
do { |
614 |
|
|
i++; |
615 |
|
|
|
616 |
|
|
// If we reached the last edge with no luck |
617 |
|
|
// then all of the vertices are inside |
618 |
|
|
// or on the plane, so the cell is completely |
619 |
|
|
// deleted |
620 |
|
|
if(i==nu[up]) return false; |
621 |
|
|
lp=ed[up][i]; |
622 |
|
|
lw=m_test(lp,l); |
623 |
|
|
} while (lw!=-1); |
624 |
|
|
j=i+1; |
625 |
|
|
|
626 |
|
|
// We found an edge outside the cutting space. Keep |
627 |
|
|
// moving through these edges until we find one that's |
628 |
|
|
// inside or on the plane. |
629 |
|
|
while(j<nu[up]) { |
630 |
|
|
lp=ed[up][j]; |
631 |
|
|
lw=m_test(lp,l); |
632 |
|
|
if(lw!=-1) break; |
633 |
|
|
j++; |
634 |
|
|
} |
635 |
|
|
|
636 |
|
|
// Compute the number of edges for the new vertex. In |
637 |
|
|
// general it will be the number of outside edges |
638 |
|
|
// found, plus two. But we need to recognize the |
639 |
|
|
// special case when all but one edge is outside, and |
640 |
|
|
// the remaining one is on the plane. For that case we |
641 |
|
|
// have to reduce the edge count by one to prevent |
642 |
|
|
// doubling up. |
643 |
|
|
if(j==nu[up]&&i==1&&rp==0) { |
644 |
|
|
nu[p]=nu[up]; |
645 |
|
|
double_edge=true; |
646 |
|
|
} else nu[p]=j-i+2; |
647 |
|
|
k=1; |
648 |
|
|
|
649 |
|
|
// Add memory for the new vertex if needed, and |
650 |
|
|
// initialize |
651 |
|
|
while (nu[p]>=current_vertex_order) add_memory_vorder(vc); |
652 |
|
|
if(mec[nu[p]]==mem[nu[p]]) add_memory(vc,nu[p],stackp2); |
653 |
|
|
vc.n_set_pointer(p,nu[p]); |
654 |
|
|
ed[p]=mep[nu[p]]+((nu[p]<<1)+1)*mec[nu[p]]++; |
655 |
|
|
ed[p][nu[p]<<1]=p; |
656 |
|
|
|
657 |
|
|
// Copy the edges of the original vertex into the new |
658 |
|
|
// one. Delete the edges of the original vertex, and |
659 |
|
|
// update the relational table. |
660 |
|
|
us=cycle_down(i,up); |
661 |
|
|
while(i<j) { |
662 |
|
|
qp=ed[up][i]; |
663 |
|
|
qs=ed[up][nu[up]+i]; |
664 |
|
|
vc.n_copy(p,k,up,i); |
665 |
|
|
ed[p][k]=qp; |
666 |
|
|
ed[p][nu[p]+k]=qs; |
667 |
|
|
ed[qp][qs]=p; |
668 |
|
|
ed[qp][nu[qp]+qs]=k; |
669 |
|
|
ed[up][i]=-1; |
670 |
|
|
i++;k++; |
671 |
|
|
} |
672 |
|
|
qs=i==nu[up]?0:i; |
673 |
|
|
} else { |
674 |
|
|
|
675 |
|
|
// In this case, the zeroth edge is outside the cutting |
676 |
|
|
// plane. Begin by searching backwards from the last |
677 |
|
|
// edge until we find an edge which isn't outside. |
678 |
|
|
i=nu[up]-1; |
679 |
|
|
lp=ed[up][i]; |
680 |
|
|
lw=m_test(lp,l); |
681 |
|
|
while(lw==-1) { |
682 |
|
|
i--; |
683 |
|
|
|
684 |
|
|
// If i reaches zero, then we have a point in |
685 |
|
|
// the plane all of whose edges are outside |
686 |
|
|
// the cutting space, so we just exit |
687 |
|
|
if(i==0) return true; |
688 |
|
|
lp=ed[up][i]; |
689 |
|
|
lw=m_test(lp,l); |
690 |
|
|
} |
691 |
|
|
|
692 |
|
|
// Now search forwards from zero |
693 |
|
|
j=1; |
694 |
|
|
qp=ed[up][j]; |
695 |
|
|
qw=m_test(qp,q); |
696 |
|
|
while(qw==-1) { |
697 |
|
|
j++; |
698 |
|
|
qp=ed[up][j]; |
699 |
|
|
qw=m_test(qp,l); |
700 |
|
|
} |
701 |
|
|
|
702 |
|
|
// Compute the number of edges for the new vertex. In |
703 |
|
|
// general it will be the number of outside edges |
704 |
|
|
// found, plus two. But we need to recognize the |
705 |
|
|
// special case when all but one edge is outside, and |
706 |
|
|
// the remaining one is on the plane. For that case we |
707 |
|
|
// have to reduce the edge count by one to prevent |
708 |
|
|
// doubling up. |
709 |
|
|
if(i==j&&qw==0) { |
710 |
|
|
double_edge=true; |
711 |
|
|
nu[p]=nu[up]; |
712 |
|
|
} else { |
713 |
|
|
nu[p]=nu[up]-i+j+1; |
714 |
|
|
} |
715 |
|
|
|
716 |
|
|
// Add memory to store the vertex if it doesn't exist |
717 |
|
|
// already |
718 |
|
|
k=1; |
719 |
|
|
while(nu[p]>=current_vertex_order) add_memory_vorder(vc); |
720 |
|
|
if(mec[nu[p]]==mem[nu[p]]) add_memory(vc,nu[p],stackp2); |
721 |
|
|
|
722 |
|
|
// Copy the edges of the original vertex into the new |
723 |
|
|
// one. Delete the edges of the original vertex, and |
724 |
|
|
// update the relational table. |
725 |
|
|
vc.n_set_pointer(p,nu[p]); |
726 |
|
|
ed[p]=mep[nu[p]]+((nu[p]<<1)+1)*mec[nu[p]]++; |
727 |
|
|
ed[p][nu[p]<<1]=p; |
728 |
|
|
us=i++; |
729 |
|
|
while(i<nu[up]) { |
730 |
|
|
qp=ed[up][i]; |
731 |
|
|
qs=ed[up][nu[up]+i]; |
732 |
|
|
vc.n_copy(p,k,up,i); |
733 |
|
|
ed[p][k]=qp; |
734 |
|
|
ed[p][nu[p]+k]=qs; |
735 |
|
|
ed[qp][qs]=p; |
736 |
|
|
ed[qp][nu[qp]+qs]=k; |
737 |
|
|
ed[up][i]=-1; |
738 |
|
|
i++;k++; |
739 |
|
|
} |
740 |
|
|
i=0; |
741 |
|
|
while(i<j) { |
742 |
|
|
qp=ed[up][i]; |
743 |
|
|
qs=ed[up][nu[up]+i]; |
744 |
|
|
vc.n_copy(p,k,up,i); |
745 |
|
|
ed[p][k]=qp; |
746 |
|
|
ed[p][nu[p]+k]=qs; |
747 |
|
|
ed[qp][qs]=p; |
748 |
|
|
ed[qp][nu[qp]+qs]=k; |
749 |
|
|
ed[up][i]=-1; |
750 |
|
|
i++;k++; |
751 |
|
|
} |
752 |
|
|
qs=j; |
753 |
|
|
} |
754 |
|
|
if(!double_edge) { |
755 |
|
|
vc.n_copy(p,k,up,qs); |
756 |
|
|
vc.n_set(p,0,p_id); |
757 |
|
|
} else vc.n_copy(p,0,up,qs); |
758 |
|
|
|
759 |
|
|
// Add this point to the auxiliary delete stack |
760 |
|
|
if(stackp2==stacke2) add_memory_ds2(stackp2); |
761 |
|
|
*(stackp2++)=up; |
762 |
|
|
|
763 |
|
|
// Look at the edges on either side of the group that was |
764 |
|
|
// detected. We're going to commence facet computation by |
765 |
|
|
// moving along one of them. We are going to end up coming back |
766 |
|
|
// along the other one. |
767 |
|
|
cs=k; |
768 |
|
|
qp=up;q=u; |
769 |
|
|
i=ed[up][us]; |
770 |
|
|
us=ed[up][nu[up]+us]; |
771 |
|
|
up=i; |
772 |
|
|
ed[qp][nu[qp]<<1]=-p; |
773 |
|
|
|
774 |
|
|
} else { |
775 |
|
|
|
776 |
|
|
// The search algorithm found an intersected edge between the |
777 |
|
|
// points lp and up. Create a new vertex between them which |
778 |
|
|
// lies on the cutting plane. Since u and l differ by at least |
779 |
|
|
// the tolerance, this division should never screw up. |
780 |
|
|
if(stackp==stacke) add_memory_ds(stackp); |
781 |
|
|
*(stackp++)=up; |
782 |
|
|
r=u/(u-l);l=1-r; |
783 |
|
|
pts[3*p]=pts[3*lp]*r+pts[3*up]*l; |
784 |
|
|
pts[3*p+1]=pts[3*lp+1]*r+pts[3*up+1]*l; |
785 |
|
|
pts[3*p+2]=pts[3*lp+2]*r+pts[3*up+2]*l; |
786 |
|
|
|
787 |
|
|
// This point will always have three edges. Connect one of them |
788 |
|
|
// to lp. |
789 |
|
|
nu[p]=3; |
790 |
|
|
if(mec[3]==mem[3]) add_memory(vc,3,stackp2); |
791 |
|
|
vc.n_set_pointer(p,3); |
792 |
|
|
vc.n_set(p,0,p_id); |
793 |
|
|
vc.n_copy(p,1,up,us); |
794 |
|
|
vc.n_copy(p,2,lp,ls); |
795 |
|
|
ed[p]=mep[3]+7*mec[3]++; |
796 |
|
|
ed[p][6]=p; |
797 |
|
|
ed[up][us]=-1; |
798 |
|
|
ed[lp][ls]=p; |
799 |
|
|
ed[lp][nu[lp]+ls]=1; |
800 |
|
|
ed[p][1]=lp; |
801 |
|
|
ed[p][nu[p]+1]=ls; |
802 |
|
|
cs=2; |
803 |
|
|
|
804 |
|
|
// Set the direction to move in |
805 |
|
|
qs=cycle_up(us,up); |
806 |
|
|
qp=up;q=u; |
807 |
|
|
} |
808 |
|
|
|
809 |
|
|
// When the code reaches here, we have initialized the first point, and |
810 |
|
|
// we have a direction for moving it to construct the rest of the facet |
811 |
|
|
cp=p;rp=p;p++; |
812 |
|
|
while(qp!=up||qs!=us) { |
813 |
|
|
|
814 |
|
|
// We're currently tracing round an intersected facet. Keep |
815 |
|
|
// moving around it until we find a point or edge which |
816 |
|
|
// intersects the plane. |
817 |
|
|
lp=ed[qp][qs]; |
818 |
|
|
lw=m_test(lp,l); |
819 |
|
|
|
820 |
|
|
if(lw==1) { |
821 |
|
|
|
822 |
|
|
// The point is still in the cutting space. Just add it |
823 |
|
|
// to the delete stack and keep moving. |
824 |
|
|
qs=cycle_up(ed[qp][nu[qp]+qs],lp); |
825 |
|
|
qp=lp; |
826 |
|
|
q=l; |
827 |
|
|
if(stackp==stacke) add_memory_ds(stackp); |
828 |
|
|
*(stackp++)=qp; |
829 |
|
|
|
830 |
|
|
} else if(lw==-1) { |
831 |
|
|
|
832 |
|
|
// The point is outside of the cutting space, so we've |
833 |
|
|
// found an intersected edge. Introduce a regular point |
834 |
|
|
// at the point of intersection. Connect it to the |
835 |
|
|
// point we just tested. Also connect it to the previous |
836 |
|
|
// new point in the facet we're constructing. |
837 |
|
|
if(p==current_vertices) add_memory_vertices(vc); |
838 |
|
|
r=q/(q-l);l=1-r; |
839 |
|
|
pts[3*p]=pts[3*lp]*r+pts[3*qp]*l; |
840 |
|
|
pts[3*p+1]=pts[3*lp+1]*r+pts[3*qp+1]*l; |
841 |
|
|
pts[3*p+2]=pts[3*lp+2]*r+pts[3*qp+2]*l; |
842 |
|
|
nu[p]=3; |
843 |
|
|
if(mec[3]==mem[3]) add_memory(vc,3,stackp2); |
844 |
|
|
ls=ed[qp][qs+nu[qp]]; |
845 |
|
|
vc.n_set_pointer(p,3); |
846 |
|
|
vc.n_set(p,0,p_id); |
847 |
|
|
vc.n_copy(p,1,qp,qs); |
848 |
|
|
vc.n_copy(p,2,lp,ls); |
849 |
|
|
ed[p]=mep[3]+7*mec[3]++; |
850 |
|
|
*ed[p]=cp; |
851 |
|
|
ed[p][1]=lp; |
852 |
|
|
ed[p][3]=cs; |
853 |
|
|
ed[p][4]=ls; |
854 |
|
|
ed[p][6]=p; |
855 |
|
|
ed[lp][ls]=p; |
856 |
|
|
ed[lp][nu[lp]+ls]=1; |
857 |
|
|
ed[cp][cs]=p; |
858 |
|
|
ed[cp][nu[cp]+cs]=0; |
859 |
|
|
ed[qp][qs]=-1; |
860 |
|
|
qs=cycle_up(qs,qp); |
861 |
|
|
cp=p++; |
862 |
|
|
cs=2; |
863 |
|
|
} else { |
864 |
|
|
|
865 |
|
|
// We've found a point which is on the cutting plane. |
866 |
|
|
// We're going to introduce a new point right here, but |
867 |
|
|
// first we need to figure out the number of edges it |
868 |
|
|
// has. |
869 |
|
|
if(p==current_vertices) add_memory_vertices(vc); |
870 |
|
|
|
871 |
|
|
// If the previous vertex detected a double edge, our |
872 |
|
|
// new vertex will have one less edge. |
873 |
|
|
k=double_edge?0:1; |
874 |
|
|
qs=ed[qp][nu[qp]+qs]; |
875 |
|
|
qp=lp; |
876 |
|
|
iqs=qs; |
877 |
|
|
|
878 |
|
|
// Start testing the edges of the current point until |
879 |
|
|
// we find one which isn't outside the cutting space |
880 |
|
|
do { |
881 |
|
|
k++; |
882 |
|
|
qs=cycle_up(qs,qp); |
883 |
|
|
lp=ed[qp][qs]; |
884 |
|
|
lw=m_test(lp,l); |
885 |
|
|
} while (lw==-1); |
886 |
|
|
|
887 |
|
|
// Now we need to find out whether this marginal vertex |
888 |
|
|
// we are on has been visited before, because if that's |
889 |
|
|
// the case, we need to add vertices to the existing |
890 |
|
|
// new vertex, rather than creating a fresh one. We also |
891 |
|
|
// need to figure out whether we're in a case where we |
892 |
|
|
// might be creating a duplicate edge. |
893 |
|
|
j=-ed[qp][nu[qp]<<1]; |
894 |
|
|
if(qp==up&&qs==us) { |
895 |
|
|
|
896 |
|
|
// If we're heading into the final part of the |
897 |
|
|
// new facet, then we never worry about the |
898 |
|
|
// duplicate edge calculation. |
899 |
|
|
new_double_edge=false; |
900 |
|
|
if(j>0) k+=nu[j]; |
901 |
|
|
} else { |
902 |
|
|
if(j>0) { |
903 |
|
|
|
904 |
|
|
// This vertex was visited before, so |
905 |
|
|
// count those vertices to the ones we |
906 |
|
|
// already have. |
907 |
|
|
k+=nu[j]; |
908 |
|
|
|
909 |
|
|
// The only time when we might make a |
910 |
|
|
// duplicate edge is if the point we're |
911 |
|
|
// going to move to next is also a |
912 |
|
|
// marginal point, so test for that |
913 |
|
|
// first. |
914 |
|
|
if(lw==0) { |
915 |
|
|
|
916 |
|
|
// Now see whether this marginal point |
917 |
|
|
// has been visited before. |
918 |
|
|
i=-ed[lp][nu[lp]<<1]; |
919 |
|
|
if(i>0) { |
920 |
|
|
|
921 |
|
|
// Now see if the last edge of that other |
922 |
|
|
// marginal point actually ends up here. |
923 |
|
|
if(ed[i][nu[i]-1]==j) { |
924 |
|
|
new_double_edge=true; |
925 |
|
|
k-=1; |
926 |
|
|
} else new_double_edge=false; |
927 |
|
|
} else { |
928 |
|
|
|
929 |
|
|
// That marginal point hasn't been visited |
930 |
|
|
// before, so we probably don't have to worry |
931 |
|
|
// about duplicate edges, except in the |
932 |
|
|
// case when that's the way into the end |
933 |
|
|
// of the facet, because that way always creates |
934 |
|
|
// an edge. |
935 |
|
|
if(j==rp&&lp==up&&ed[qp][nu[qp]+qs]==us) { |
936 |
|
|
new_double_edge=true; |
937 |
|
|
k-=1; |
938 |
|
|
} else new_double_edge=false; |
939 |
|
|
} |
940 |
|
|
} else new_double_edge=false; |
941 |
|
|
} else { |
942 |
|
|
|
943 |
|
|
// The vertex hasn't been visited |
944 |
|
|
// before, but let's see if it's |
945 |
|
|
// marginal |
946 |
|
|
if(lw==0) { |
947 |
|
|
|
948 |
|
|
// If it is, we need to check |
949 |
|
|
// for the case that it's a |
950 |
|
|
// small branch, and that we're |
951 |
|
|
// heading right back to where |
952 |
|
|
// we came from |
953 |
|
|
i=-ed[lp][nu[lp]<<1]; |
954 |
|
|
if(i==cp) { |
955 |
|
|
new_double_edge=true; |
956 |
|
|
k-=1; |
957 |
|
|
} else new_double_edge=false; |
958 |
|
|
} else new_double_edge=false; |
959 |
|
|
} |
960 |
|
|
} |
961 |
|
|
|
962 |
|
|
// k now holds the number of edges of the new vertex |
963 |
|
|
// we are forming. Add memory for it if it doesn't exist |
964 |
|
|
// already. |
965 |
|
|
while(k>=current_vertex_order) add_memory_vorder(vc); |
966 |
|
|
if(mec[k]==mem[k]) add_memory(vc,k,stackp2); |
967 |
|
|
|
968 |
|
|
// Now create a new vertex with order k, or augment |
969 |
|
|
// the existing one |
970 |
|
|
if(j>0) { |
971 |
|
|
|
972 |
|
|
// If we're augmenting a vertex but we don't |
973 |
|
|
// actually need any more edges, just skip this |
974 |
|
|
// routine to avoid memory confusion |
975 |
|
|
if(nu[j]!=k) { |
976 |
|
|
// Allocate memory and copy the edges |
977 |
|
|
// of the previous instance into it |
978 |
|
|
vc.n_set_aux1(k); |
979 |
|
|
edp=mep[k]+((k<<1)+1)*mec[k]++; |
980 |
|
|
i=0; |
981 |
|
|
while(i<nu[j]) { |
982 |
|
|
vc.n_copy_aux1(j,i); |
983 |
|
|
edp[i]=ed[j][i]; |
984 |
|
|
edp[k+i]=ed[j][nu[j]+i]; |
985 |
|
|
i++; |
986 |
|
|
} |
987 |
|
|
edp[k<<1]=j; |
988 |
|
|
|
989 |
|
|
// Remove the previous instance with |
990 |
|
|
// fewer vertices from the memory |
991 |
|
|
// structure |
992 |
|
|
edd=mep[nu[j]]+((nu[j]<<1)+1)*--mec[nu[j]]; |
993 |
|
|
if(edd!=ed[j]) { |
994 |
|
|
for(lw=0;lw<=(nu[j]<<1);lw++) ed[j][lw]=edd[lw]; |
995 |
|
|
vc.n_set_aux2_copy(j,nu[j]); |
996 |
|
|
vc.n_copy_pointer(edd[nu[j]<<1],j); |
997 |
|
|
ed[edd[nu[j]<<1]]=ed[j]; |
998 |
|
|
} |
999 |
|
|
vc.n_set_to_aux1(j); |
1000 |
|
|
ed[j]=edp; |
1001 |
|
|
} else i=nu[j]; |
1002 |
|
|
} else { |
1003 |
|
|
|
1004 |
|
|
// Allocate a new vertex of order k |
1005 |
|
|
vc.n_set_pointer(p,k); |
1006 |
|
|
ed[p]=mep[k]+((k<<1)+1)*mec[k]++; |
1007 |
|
|
ed[p][k<<1]=p; |
1008 |
|
|
if(stackp2==stacke2) add_memory_ds2(stackp2); |
1009 |
|
|
*(stackp2++)=qp; |
1010 |
|
|
pts[3*p]=pts[3*qp]; |
1011 |
|
|
pts[3*p+1]=pts[3*qp+1]; |
1012 |
|
|
pts[3*p+2]=pts[3*qp+2]; |
1013 |
|
|
ed[qp][nu[qp]<<1]=-p; |
1014 |
|
|
j=p++; |
1015 |
|
|
i=0; |
1016 |
|
|
} |
1017 |
|
|
nu[j]=k; |
1018 |
|
|
|
1019 |
|
|
// Unless the previous case was a double edge, connect |
1020 |
|
|
// the first available edge of the new vertex to the |
1021 |
|
|
// last one in the facet |
1022 |
|
|
if(!double_edge) { |
1023 |
|
|
ed[j][i]=cp; |
1024 |
|
|
ed[j][nu[j]+i]=cs; |
1025 |
|
|
vc.n_set(j,i,p_id); |
1026 |
|
|
ed[cp][cs]=j; |
1027 |
|
|
ed[cp][nu[cp]+cs]=i; |
1028 |
|
|
i++; |
1029 |
|
|
} |
1030 |
|
|
|
1031 |
|
|
// Copy in the edges of the underlying vertex, |
1032 |
|
|
// and do one less if this was a double edge |
1033 |
|
|
qs=iqs; |
1034 |
|
|
while(i<(new_double_edge?k:k-1)) { |
1035 |
|
|
qs=cycle_up(qs,qp); |
1036 |
|
|
lp=ed[qp][qs];ls=ed[qp][nu[qp]+qs]; |
1037 |
|
|
vc.n_copy(j,i,qp,qs); |
1038 |
|
|
ed[j][i]=lp; |
1039 |
|
|
ed[j][nu[j]+i]=ls; |
1040 |
|
|
ed[lp][ls]=j; |
1041 |
|
|
ed[lp][nu[lp]+ls]=i; |
1042 |
|
|
ed[qp][qs]=-1; |
1043 |
|
|
i++; |
1044 |
|
|
} |
1045 |
|
|
qs=cycle_up(qs,qp); |
1046 |
|
|
cs=i; |
1047 |
|
|
cp=j; |
1048 |
|
|
vc.n_copy(j,new_double_edge?0:cs,qp,qs); |
1049 |
|
|
|
1050 |
|
|
// Update the double_edge flag, to pass it |
1051 |
|
|
// to the next instance of this routine |
1052 |
|
|
double_edge=new_double_edge; |
1053 |
|
|
} |
1054 |
|
|
} |
1055 |
|
|
|
1056 |
|
|
// Connect the final created vertex to the initial one |
1057 |
|
|
ed[cp][cs]=rp; |
1058 |
|
|
*ed[rp]=cp; |
1059 |
|
|
ed[cp][nu[cp]+cs]=0; |
1060 |
|
|
ed[rp][nu[rp]]=cs; |
1061 |
|
|
|
1062 |
|
|
// Delete points: first, remove any duplicates |
1063 |
|
|
dsp=ds; |
1064 |
|
|
while(dsp<stackp) { |
1065 |
|
|
j=*dsp; |
1066 |
|
|
if(ed[j][nu[j]]!=-1) { |
1067 |
|
|
ed[j][nu[j]]=-1; |
1068 |
|
|
dsp++; |
1069 |
|
|
} else *dsp=*(--stackp); |
1070 |
|
|
} |
1071 |
|
|
|
1072 |
|
|
// Add the points in the auxiliary delete stack, |
1073 |
|
|
// and reset their back pointers |
1074 |
|
|
for(dsp=ds2;dsp<stackp2;dsp++) { |
1075 |
|
|
j=*dsp; |
1076 |
|
|
ed[j][nu[j]<<1]=j; |
1077 |
|
|
if(ed[j][nu[j]]!=-1) { |
1078 |
|
|
ed[j][nu[j]]=-1; |
1079 |
|
|
if(stackp==stacke) add_memory_ds(stackp); |
1080 |
|
|
*(stackp++)=j; |
1081 |
|
|
} |
1082 |
|
|
} |
1083 |
|
|
|
1084 |
|
|
// Scan connections and add in extras |
1085 |
|
|
for(dsp=ds;dsp<stackp;dsp++) { |
1086 |
|
|
cp=*dsp; |
1087 |
|
|
for(edp=ed[cp];edp<ed[cp]+nu[cp];edp++) { |
1088 |
|
|
qp=*edp; |
1089 |
|
|
if(qp!=-1&&ed[qp][nu[qp]]!=-1) { |
1090 |
|
|
if(stackp==stacke) { |
1091 |
|
|
int dis=stackp-dsp; |
1092 |
|
|
add_memory_ds(stackp); |
1093 |
|
|
dsp=ds+dis; |
1094 |
|
|
} |
1095 |
|
|
*(stackp++)=qp; |
1096 |
|
|
ed[qp][nu[qp]]=-1; |
1097 |
|
|
} |
1098 |
|
|
} |
1099 |
|
|
} |
1100 |
|
|
up=0; |
1101 |
|
|
|
1102 |
|
|
// Delete them from the array structure |
1103 |
|
|
while(stackp>ds) { |
1104 |
|
|
--p; |
1105 |
|
|
while(ed[p][nu[p]]==-1) { |
1106 |
|
|
j=nu[p]; |
1107 |
|
|
edp=ed[p];edd=(mep[j]+((j<<1)+1)*--mec[j]); |
1108 |
|
|
while(edp<ed[p]+(j<<1)+1) *(edp++)=*(edd++); |
1109 |
|
|
vc.n_set_aux2_copy(p,j); |
1110 |
|
|
vc.n_copy_pointer(ed[p][(j<<1)],p); |
1111 |
|
|
ed[ed[p][(j<<1)]]=ed[p]; |
1112 |
|
|
--p; |
1113 |
|
|
} |
1114 |
|
|
up=*(--stackp); |
1115 |
|
|
if(up<p) { |
1116 |
|
|
|
1117 |
|
|
// Vertex management |
1118 |
|
|
pts[3*up]=pts[3*p]; |
1119 |
|
|
pts[3*up+1]=pts[3*p+1]; |
1120 |
|
|
pts[3*up+2]=pts[3*p+2]; |
1121 |
|
|
|
1122 |
|
|
// Memory management |
1123 |
|
|
j=nu[up]; |
1124 |
|
|
edp=ed[up];edd=(mep[j]+((j<<1)+1)*--mec[j]); |
1125 |
|
|
while(edp<ed[up]+(j<<1)+1) *(edp++)=*(edd++); |
1126 |
|
|
vc.n_set_aux2_copy(up,j); |
1127 |
|
|
vc.n_copy_pointer(ed[up][j<<1],up); |
1128 |
|
|
vc.n_copy_pointer(up,p); |
1129 |
|
|
ed[ed[up][j<<1]]=ed[up]; |
1130 |
|
|
|
1131 |
|
|
// Edge management |
1132 |
|
|
ed[up]=ed[p]; |
1133 |
|
|
nu[up]=nu[p]; |
1134 |
|
|
for(i=0;i<nu[up];i++) ed[ed[up][i]][ed[up][nu[up]+i]]=up; |
1135 |
|
|
ed[up][nu[up]<<1]=up; |
1136 |
|
|
} else up=p++; |
1137 |
|
|
} |
1138 |
|
|
|
1139 |
|
|
// Check for any vertices of zero order |
1140 |
|
|
if(*mec>0) voro_fatal_error("Zero order vertex formed",VOROPP_INTERNAL_ERROR); |
1141 |
|
|
|
1142 |
|
|
// Collapse any order 2 vertices and exit |
1143 |
|
|
return collapse_order2(vc); |
1144 |
|
|
} |
1145 |
|
|
|
1146 |
|
|
/** During the creation of a new facet in the plane routine, it is possible |
1147 |
|
|
* that some order two vertices may arise. This routine removes them. |
1148 |
|
|
* Suppose an order two vertex joins c and d. If there's a edge between |
1149 |
|
|
* c and d already, then the order two vertex is just removed; otherwise, |
1150 |
|
|
* the order two vertex is removed and c and d are joined together directly. |
1151 |
|
|
* It is possible this process will create order two or order one vertices, |
1152 |
|
|
* and the routine is continually run until all of them are removed. |
1153 |
|
|
* \return False if the vertex removal was unsuccessful, indicative of the cell |
1154 |
|
|
* reducing to zero volume and disappearing; true if the vertex removal |
1155 |
|
|
* was successful. */ |
1156 |
|
|
template<class vc_class> |
1157 |
|
|
inline bool voronoicell_base::collapse_order2(vc_class &vc) { |
1158 |
|
|
if(!collapse_order1(vc)) return false; |
1159 |
|
|
int a,b,i,j,k,l; |
1160 |
|
|
while(mec[2]>0) { |
1161 |
|
|
|
1162 |
|
|
// Pick a order 2 vertex and read in its edges |
1163 |
|
|
i=--mec[2]; |
1164 |
|
|
j=mep[2][5*i];k=mep[2][5*i+1]; |
1165 |
|
|
if(j==k) { |
1166 |
|
|
#if VOROPP_VERBOSE >=1 |
1167 |
|
|
fputs("Order two vertex joins itself",stderr); |
1168 |
|
|
#endif |
1169 |
|
|
return false; |
1170 |
|
|
} |
1171 |
|
|
|
1172 |
|
|
// Scan the edges of j to see if joins k |
1173 |
|
|
for(l=0;l<nu[j];l++) { |
1174 |
|
|
if(ed[j][l]==k) break; |
1175 |
|
|
} |
1176 |
|
|
|
1177 |
|
|
// If j doesn't already join k, join them together. |
1178 |
|
|
// Otherwise delete the connection to the current |
1179 |
|
|
// vertex from j and k. |
1180 |
|
|
a=mep[2][5*i+2];b=mep[2][5*i+3];i=mep[2][5*i+4]; |
1181 |
|
|
if(l==nu[j]) { |
1182 |
|
|
ed[j][a]=k; |
1183 |
|
|
ed[k][b]=j; |
1184 |
|
|
ed[j][nu[j]+a]=b; |
1185 |
|
|
ed[k][nu[k]+b]=a; |
1186 |
|
|
} else { |
1187 |
|
|
if(!delete_connection(vc,j,a,false)) return false; |
1188 |
|
|
if(!delete_connection(vc,k,b,true)) return false; |
1189 |
|
|
} |
1190 |
|
|
|
1191 |
|
|
// Compact the memory |
1192 |
|
|
--p; |
1193 |
|
|
if(up==i) up=0; |
1194 |
|
|
if(p!=i) { |
1195 |
|
|
if(up==p) up=i; |
1196 |
|
|
pts[3*i]=pts[3*p]; |
1197 |
|
|
pts[3*i+1]=pts[3*p+1]; |
1198 |
|
|
pts[3*i+2]=pts[3*p+2]; |
1199 |
|
|
for(k=0;k<nu[p];k++) ed[ed[p][k]][ed[p][nu[p]+k]]=i; |
1200 |
|
|
vc.n_copy_pointer(i,p); |
1201 |
|
|
ed[i]=ed[p]; |
1202 |
|
|
nu[i]=nu[p]; |
1203 |
|
|
ed[i][nu[i]<<1]=i; |
1204 |
|
|
} |
1205 |
|
|
|
1206 |
|
|
// Collapse any order 1 vertices if they were created |
1207 |
|
|
if(!collapse_order1(vc)) return false; |
1208 |
|
|
} |
1209 |
|
|
return true; |
1210 |
|
|
} |
1211 |
|
|
|
1212 |
|
|
/** Order one vertices can potentially be created during the order two collapse |
1213 |
|
|
* routine. This routine keeps removing them until there are none left. |
1214 |
|
|
* \return False if the vertex removal was unsuccessful, indicative of the cell |
1215 |
|
|
* having zero volume and disappearing; true if the vertex removal was |
1216 |
|
|
* successful. */ |
1217 |
|
|
template<class vc_class> |
1218 |
|
|
inline bool voronoicell_base::collapse_order1(vc_class &vc) { |
1219 |
|
|
int i,j,k; |
1220 |
|
|
while(mec[1]>0) { |
1221 |
|
|
up=0; |
1222 |
|
|
#if VOROPP_VERBOSE >=1 |
1223 |
|
|
fputs("Order one collapse\n",stderr); |
1224 |
|
|
#endif |
1225 |
|
|
i=--mec[1]; |
1226 |
|
|
j=mep[1][3*i];k=mep[1][3*i+1]; |
1227 |
|
|
i=mep[1][3*i+2]; |
1228 |
|
|
if(!delete_connection(vc,j,k,false)) return false; |
1229 |
|
|
--p; |
1230 |
|
|
if(up==i) up=0; |
1231 |
|
|
if(p!=i) { |
1232 |
|
|
if(up==p) up=i; |
1233 |
|
|
pts[3*i]=pts[3*p]; |
1234 |
|
|
pts[3*i+1]=pts[3*p+1]; |
1235 |
|
|
pts[3*i+2]=pts[3*p+2]; |
1236 |
|
|
for(k=0;k<nu[p];k++) ed[ed[p][k]][ed[p][nu[p]+k]]=i; |
1237 |
|
|
vc.n_copy_pointer(i,p); |
1238 |
|
|
ed[i]=ed[p]; |
1239 |
|
|
nu[i]=nu[p]; |
1240 |
|
|
ed[i][nu[i]<<1]=i; |
1241 |
|
|
} |
1242 |
|
|
} |
1243 |
|
|
return true; |
1244 |
|
|
} |
1245 |
|
|
|
1246 |
|
|
/** This routine deletes the kth edge of vertex j and reorganizes the memory. |
1247 |
|
|
* If the neighbor computation is enabled, we also have to supply an handedness |
1248 |
|
|
* flag to decide whether to preserve the plane on the left or right of the |
1249 |
|
|
* connection. |
1250 |
|
|
* \return False if a zero order vertex was formed, indicative of the cell |
1251 |
|
|
* disappearing; true if the vertex removal was successful. */ |
1252 |
|
|
template<class vc_class> |
1253 |
|
|
inline bool voronoicell_base::delete_connection(vc_class &vc,int j,int k,bool hand) { |
1254 |
|
|
int q=hand?k:cycle_up(k,j); |
1255 |
|
|
int i=nu[j]-1,l,*edp,*edd,m; |
1256 |
|
|
#if VOROPP_VERBOSE >=1 |
1257 |
|
|
if(i<1) { |
1258 |
|
|
fputs("Zero order vertex formed\n",stderr); |
1259 |
|
|
return false; |
1260 |
|
|
} |
1261 |
|
|
#endif |
1262 |
|
|
if(mec[i]==mem[i]) add_memory(vc,i,ds2); |
1263 |
|
|
vc.n_set_aux1(i); |
1264 |
|
|
for(l=0;l<q;l++) vc.n_copy_aux1(j,l); |
1265 |
|
|
while(l<i) { |
1266 |
|
|
vc.n_copy_aux1_shift(j,l); |
1267 |
|
|
l++; |
1268 |
|
|
} |
1269 |
|
|
edp=mep[i]+((i<<1)+1)*mec[i]++; |
1270 |
|
|
edp[i<<1]=j; |
1271 |
|
|
for(l=0;l<k;l++) { |
1272 |
|
|
edp[l]=ed[j][l]; |
1273 |
|
|
edp[l+i]=ed[j][l+nu[j]]; |
1274 |
|
|
} |
1275 |
|
|
while(l<i) { |
1276 |
|
|
m=ed[j][l+1]; |
1277 |
|
|
edp[l]=m; |
1278 |
|
|
k=ed[j][l+nu[j]+1]; |
1279 |
|
|
edp[l+i]=k; |
1280 |
|
|
ed[m][nu[m]+k]--; |
1281 |
|
|
l++; |
1282 |
|
|
} |
1283 |
|
|
|
1284 |
|
|
edd=mep[nu[j]]+((nu[j]<<1)+1)*--mec[nu[j]]; |
1285 |
|
|
for(l=0;l<=(nu[j]<<1);l++) ed[j][l]=edd[l]; |
1286 |
|
|
vc.n_set_aux2_copy(j,nu[j]); |
1287 |
|
|
vc.n_set_to_aux2(edd[nu[j]<<1]); |
1288 |
|
|
vc.n_set_to_aux1(j); |
1289 |
|
|
ed[edd[nu[j]<<1]]=edd; |
1290 |
|
|
ed[j]=edp; |
1291 |
|
|
nu[j]=i; |
1292 |
|
|
return true; |
1293 |
|
|
} |
1294 |
|
|
|
1295 |
|
|
/** Calculates the volume of the Voronoi cell, by decomposing the cell into |
1296 |
|
|
* tetrahedra extending outward from the zeroth vertex, whose volumes are |
1297 |
|
|
* evaluated using a scalar triple product. |
1298 |
|
|
* \return A floating point number holding the calculated volume. */ |
1299 |
|
|
double voronoicell_base::volume() { |
1300 |
|
|
const double fe=1/48.0; |
1301 |
|
|
double vol=0; |
1302 |
|
|
int i,j,k,l,m,n; |
1303 |
|
|
double ux,uy,uz,vx,vy,vz,wx,wy,wz; |
1304 |
|
|
for(i=1;i<p;i++) { |
1305 |
|
|
ux=*pts-pts[3*i]; |
1306 |
|
|
uy=pts[1]-pts[3*i+1]; |
1307 |
|
|
uz=pts[2]-pts[3*i+2]; |
1308 |
|
|
for(j=0;j<nu[i];j++) { |
1309 |
|
|
k=ed[i][j]; |
1310 |
|
|
if(k>=0) { |
1311 |
|
|
ed[i][j]=-1-k; |
1312 |
|
|
l=cycle_up(ed[i][nu[i]+j],k); |
1313 |
|
|
vx=pts[3*k]-*pts; |
1314 |
|
|
vy=pts[3*k+1]-pts[1]; |
1315 |
|
|
vz=pts[3*k+2]-pts[2]; |
1316 |
|
|
m=ed[k][l];ed[k][l]=-1-m; |
1317 |
|
|
while(m!=i) { |
1318 |
|
|
n=cycle_up(ed[k][nu[k]+l],m); |
1319 |
|
|
wx=pts[3*m]-*pts; |
1320 |
|
|
wy=pts[3*m+1]-pts[1]; |
1321 |
|
|
wz=pts[3*m+2]-pts[2]; |
1322 |
|
|
vol+=ux*vy*wz+uy*vz*wx+uz*vx*wy-uz*vy*wx-uy*vx*wz-ux*vz*wy; |
1323 |
|
|
k=m;l=n;vx=wx;vy=wy;vz=wz; |
1324 |
|
|
m=ed[k][l];ed[k][l]=-1-m; |
1325 |
|
|
} |
1326 |
|
|
} |
1327 |
|
|
} |
1328 |
|
|
} |
1329 |
|
|
reset_edges(); |
1330 |
|
|
return vol*fe; |
1331 |
|
|
} |
1332 |
|
|
|
1333 |
|
|
/** Calculates the areas of each face of the Voronoi cell and prints the |
1334 |
|
|
* results to an output stream. |
1335 |
|
|
* \param[out] v the vector to store the results in. */ |
1336 |
|
|
void voronoicell_base::face_areas(std::vector<double> &v) { |
1337 |
|
|
double area; |
1338 |
|
|
v.clear(); |
1339 |
|
|
int i,j,k,l,m,n; |
1340 |
|
|
double ux,uy,uz,vx,vy,vz,wx,wy,wz; |
1341 |
|
|
for(i=1;i<p;i++) for(j=0;j<nu[i];j++) { |
1342 |
|
|
k=ed[i][j]; |
1343 |
|
|
if(k>=0) { |
1344 |
|
|
area=0; |
1345 |
|
|
ed[i][j]=-1-k; |
1346 |
|
|
l=cycle_up(ed[i][nu[i]+j],k); |
1347 |
|
|
m=ed[k][l];ed[k][l]=-1-m; |
1348 |
|
|
while(m!=i) { |
1349 |
|
|
n=cycle_up(ed[k][nu[k]+l],m); |
1350 |
|
|
ux=pts[3*k]-pts[3*i]; |
1351 |
|
|
uy=pts[3*k+1]-pts[3*i+1]; |
1352 |
|
|
uz=pts[3*k+2]-pts[3*i+2]; |
1353 |
|
|
vx=pts[3*m]-pts[3*i]; |
1354 |
|
|
vy=pts[3*m+1]-pts[3*i+1]; |
1355 |
|
|
vz=pts[3*m+2]-pts[3*i+2]; |
1356 |
|
|
wx=uy*vz-uz*vy; |
1357 |
|
|
wy=uz*vx-ux*vz; |
1358 |
|
|
wz=ux*vy-uy*vx; |
1359 |
|
|
area+=sqrt(wx*wx+wy*wy+wz*wz); |
1360 |
|
|
k=m;l=n; |
1361 |
|
|
m=ed[k][l];ed[k][l]=-1-m; |
1362 |
|
|
} |
1363 |
|
|
v.push_back(0.125*area); |
1364 |
|
|
} |
1365 |
|
|
} |
1366 |
|
|
reset_edges(); |
1367 |
|
|
} |
1368 |
|
|
|
1369 |
|
|
|
1370 |
|
|
/** Calculates the total surface area of the Voronoi cell. |
1371 |
|
|
* \return The computed area. */ |
1372 |
|
|
double voronoicell_base::surface_area() { |
1373 |
|
|
double area=0; |
1374 |
|
|
int i,j,k,l,m,n; |
1375 |
|
|
double ux,uy,uz,vx,vy,vz,wx,wy,wz; |
1376 |
|
|
for(i=1;i<p;i++) for(j=0;j<nu[i];j++) { |
1377 |
|
|
k=ed[i][j]; |
1378 |
|
|
if(k>=0) { |
1379 |
|
|
ed[i][j]=-1-k; |
1380 |
|
|
l=cycle_up(ed[i][nu[i]+j],k); |
1381 |
|
|
m=ed[k][l];ed[k][l]=-1-m; |
1382 |
|
|
while(m!=i) { |
1383 |
|
|
n=cycle_up(ed[k][nu[k]+l],m); |
1384 |
|
|
ux=pts[3*k]-pts[3*i]; |
1385 |
|
|
uy=pts[3*k+1]-pts[3*i+1]; |
1386 |
|
|
uz=pts[3*k+2]-pts[3*i+2]; |
1387 |
|
|
vx=pts[3*m]-pts[3*i]; |
1388 |
|
|
vy=pts[3*m+1]-pts[3*i+1]; |
1389 |
|
|
vz=pts[3*m+2]-pts[3*i+2]; |
1390 |
|
|
wx=uy*vz-uz*vy; |
1391 |
|
|
wy=uz*vx-ux*vz; |
1392 |
|
|
wz=ux*vy-uy*vx; |
1393 |
|
|
area+=sqrt(wx*wx+wy*wy+wz*wz); |
1394 |
|
|
k=m;l=n; |
1395 |
|
|
m=ed[k][l];ed[k][l]=-1-m; |
1396 |
|
|
} |
1397 |
|
|
} |
1398 |
|
|
} |
1399 |
|
|
reset_edges(); |
1400 |
|
|
return 0.125*area; |
1401 |
|
|
} |
1402 |
|
|
|
1403 |
|
|
|
1404 |
|
|
/** Calculates the centroid of the Voronoi cell, by decomposing the cell into |
1405 |
|
|
* tetrahedra extending outward from the zeroth vertex. |
1406 |
|
|
* \param[out] (cx,cy,cz) references to floating point numbers in which to |
1407 |
|
|
* pass back the centroid vector. */ |
1408 |
|
|
void voronoicell_base::centroid(double &cx,double &cy,double &cz) { |
1409 |
|
|
double tvol,vol=0;cx=cy=cz=0; |
1410 |
|
|
int i,j,k,l,m,n; |
1411 |
|
|
double ux,uy,uz,vx,vy,vz,wx,wy,wz; |
1412 |
|
|
for(i=1;i<p;i++) { |
1413 |
|
|
ux=*pts-pts[3*i]; |
1414 |
|
|
uy=pts[1]-pts[3*i+1]; |
1415 |
|
|
uz=pts[2]-pts[3*i+2]; |
1416 |
|
|
for(j=0;j<nu[i];j++) { |
1417 |
|
|
k=ed[i][j]; |
1418 |
|
|
if(k>=0) { |
1419 |
|
|
ed[i][j]=-1-k; |
1420 |
|
|
l=cycle_up(ed[i][nu[i]+j],k); |
1421 |
|
|
vx=pts[3*k]-*pts; |
1422 |
|
|
vy=pts[3*k+1]-pts[1]; |
1423 |
|
|
vz=pts[3*k+2]-pts[2]; |
1424 |
|
|
m=ed[k][l];ed[k][l]=-1-m; |
1425 |
|
|
while(m!=i) { |
1426 |
|
|
n=cycle_up(ed[k][nu[k]+l],m); |
1427 |
|
|
wx=pts[3*m]-*pts; |
1428 |
|
|
wy=pts[3*m+1]-pts[1]; |
1429 |
|
|
wz=pts[3*m+2]-pts[2]; |
1430 |
|
|
tvol=ux*vy*wz+uy*vz*wx+uz*vx*wy-uz*vy*wx-uy*vx*wz-ux*vz*wy; |
1431 |
|
|
vol+=tvol; |
1432 |
|
|
cx+=(wx+vx-ux)*tvol; |
1433 |
|
|
cy+=(wy+vy-uy)*tvol; |
1434 |
|
|
cz+=(wz+vz-uz)*tvol; |
1435 |
|
|
k=m;l=n;vx=wx;vy=wy;vz=wz; |
1436 |
|
|
m=ed[k][l];ed[k][l]=-1-m; |
1437 |
|
|
} |
1438 |
|
|
} |
1439 |
|
|
} |
1440 |
|
|
} |
1441 |
|
|
reset_edges(); |
1442 |
|
|
if(vol>tolerance_sq) { |
1443 |
|
|
vol=0.125/vol; |
1444 |
|
|
cx=cx*vol+0.5*(*pts); |
1445 |
|
|
cy=cy*vol+0.5*pts[1]; |
1446 |
|
|
cz=cz*vol+0.5*pts[2]; |
1447 |
|
|
} else cx=cy=cz=0; |
1448 |
|
|
} |
1449 |
|
|
|
1450 |
|
|
/** Computes the maximum radius squared of a vertex from the center of the |
1451 |
|
|
* cell. It can be used to determine when enough particles have been testing an |
1452 |
|
|
* all planes that could cut the cell have been considered. |
1453 |
|
|
* \return The maximum radius squared of a vertex.*/ |
1454 |
|
|
double voronoicell_base::max_radius_squared() { |
1455 |
|
|
double r,s,*ptsp=pts+3,*ptse=pts+3*p; |
1456 |
|
|
r=*pts*(*pts)+pts[1]*pts[1]+pts[2]*pts[2]; |
1457 |
|
|
while(ptsp<ptse) { |
1458 |
|
|
s=*ptsp*(*ptsp);ptsp++; |
1459 |
|
|
s+=*ptsp*(*ptsp);ptsp++; |
1460 |
|
|
s+=*ptsp*(*ptsp);ptsp++; |
1461 |
|
|
if(s>r) r=s; |
1462 |
|
|
} |
1463 |
|
|
return r; |
1464 |
|
|
} |
1465 |
|
|
|
1466 |
|
|
/** Calculates the total edge distance of the Voronoi cell. |
1467 |
|
|
* \return A floating point number holding the calculated distance. */ |
1468 |
|
|
double voronoicell_base::total_edge_distance() { |
1469 |
|
|
int i,j,k; |
1470 |
|
|
double dis=0,dx,dy,dz; |
1471 |
|
|
for(i=0;i<p-1;i++) for(j=0;j<nu[i];j++) { |
1472 |
|
|
k=ed[i][j]; |
1473 |
|
|
if(k>i) { |
1474 |
|
|
dx=pts[3*k]-pts[3*i]; |
1475 |
|
|
dy=pts[3*k+1]-pts[3*i+1]; |
1476 |
|
|
dz=pts[3*k+2]-pts[3*i+2]; |
1477 |
|
|
dis+=sqrt(dx*dx+dy*dy+dz*dz); |
1478 |
|
|
} |
1479 |
|
|
} |
1480 |
|
|
return 0.5*dis; |
1481 |
|
|
} |
1482 |
|
|
|
1483 |
|
|
/** Outputs the edges of the Voronoi cell in POV-Ray format to an open file |
1484 |
|
|
* stream, displacing the cell by given vector. |
1485 |
|
|
* \param[in] (x,y,z) a displacement vector to be added to the cell's position. |
1486 |
|
|
* \param[in] fp a file handle to write to. */ |
1487 |
|
|
void voronoicell_base::draw_pov(double x,double y,double z,FILE* fp) { |
1488 |
|
|
int i,j,k;double *ptsp=pts,*pt2; |
1489 |
|
|
char posbuf1[128],posbuf2[128]; |
1490 |
|
|
for(i=0;i<p;i++,ptsp+=3) { |
1491 |
|
|
sprintf(posbuf1,"%g,%g,%g",x+*ptsp*0.5,y+ptsp[1]*0.5,z+ptsp[2]*0.5); |
1492 |
|
|
fprintf(fp,"sphere{<%s>,r}\n",posbuf1); |
1493 |
|
|
for(j=0;j<nu[i];j++) { |
1494 |
|
|
k=ed[i][j]; |
1495 |
|
|
if(k<i) { |
1496 |
|
|
pt2=pts+3*k; |
1497 |
|
|
sprintf(posbuf2,"%g,%g,%g",x+*pt2*0.5,y+0.5*pt2[1],z+0.5*pt2[2]); |
1498 |
|
|
if(strcmp(posbuf1,posbuf2)!=0) fprintf(fp,"cylinder{<%s>,<%s>,r}\n",posbuf1,posbuf2); |
1499 |
|
|
} |
1500 |
|
|
} |
1501 |
|
|
} |
1502 |
|
|
} |
1503 |
|
|
|
1504 |
|
|
/** Outputs the edges of the Voronoi cell in gnuplot format to an output stream. |
1505 |
|
|
* \param[in] (x,y,z) a displacement vector to be added to the cell's position. |
1506 |
|
|
* \param[in] fp a file handle to write to. */ |
1507 |
|
|
void voronoicell_base::draw_gnuplot(double x,double y,double z,FILE *fp) { |
1508 |
|
|
int i,j,k,l,m; |
1509 |
|
|
for(i=1;i<p;i++) for(j=0;j<nu[i];j++) { |
1510 |
|
|
k=ed[i][j]; |
1511 |
|
|
if(k>=0) { |
1512 |
|
|
fprintf(fp,"%g %g %g\n",x+0.5*pts[3*i],y+0.5*pts[3*i+1],z+0.5*pts[3*i+2]); |
1513 |
|
|
l=i;m=j; |
1514 |
|
|
do { |
1515 |
|
|
ed[k][ed[l][nu[l]+m]]=-1-l; |
1516 |
|
|
ed[l][m]=-1-k; |
1517 |
|
|
l=k; |
1518 |
|
|
fprintf(fp,"%g %g %g\n",x+0.5*pts[3*k],y+0.5*pts[3*k+1],z+0.5*pts[3*k+2]); |
1519 |
|
|
} while (search_edge(l,m,k)); |
1520 |
|
|
fputs("\n\n",fp); |
1521 |
|
|
} |
1522 |
|
|
} |
1523 |
|
|
reset_edges(); |
1524 |
|
|
} |
1525 |
|
|
|
1526 |
|
|
inline bool voronoicell_base::search_edge(int l,int &m,int &k) { |
1527 |
|
|
for(m=0;m<nu[l];m++) { |
1528 |
|
|
k=ed[l][m]; |
1529 |
|
|
if(k>=0) return true; |
1530 |
|
|
} |
1531 |
|
|
return false; |
1532 |
|
|
} |
1533 |
|
|
|
1534 |
|
|
/** Outputs the Voronoi cell in the POV mesh2 format, described in section |
1535 |
|
|
* 1.3.2.2 of the POV-Ray documentation. The mesh2 output consists of a list of |
1536 |
|
|
* vertex vectors, followed by a list of triangular faces. The routine also |
1537 |
|
|
* makes use of the optional inside_vector specification, which makes the mesh |
1538 |
|
|
* object solid, so the the POV-Ray Constructive Solid Geometry (CSG) can be |
1539 |
|
|
* applied. |
1540 |
|
|
* \param[in] (x,y,z) a displacement vector to be added to the cell's position. |
1541 |
|
|
* \param[in] fp a file handle to write to. */ |
1542 |
|
|
void voronoicell_base::draw_pov_mesh(double x,double y,double z,FILE *fp) { |
1543 |
|
|
int i,j,k,l,m,n; |
1544 |
|
|
double *ptsp=pts; |
1545 |
|
|
fprintf(fp,"mesh2 {\nvertex_vectors {\n%d\n",p); |
1546 |
|
|
for(i=0;i<p;i++,ptsp+=3) fprintf(fp,",<%g,%g,%g>\n",x+*ptsp*0.5,y+ptsp[1]*0.5,z+ptsp[2]*0.5); |
1547 |
|
|
fprintf(fp,"}\nface_indices {\n%d\n",(p-2)<<1); |
1548 |
|
|
for(i=1;i<p;i++) for(j=0;j<nu[i];j++) { |
1549 |
|
|
k=ed[i][j]; |
1550 |
|
|
if(k>=0) { |
1551 |
|
|
ed[i][j]=-1-k; |
1552 |
|
|
l=cycle_up(ed[i][nu[i]+j],k); |
1553 |
|
|
m=ed[k][l];ed[k][l]=-1-m; |
1554 |
|
|
while(m!=i) { |
1555 |
|
|
n=cycle_up(ed[k][nu[k]+l],m); |
1556 |
|
|
fprintf(fp,",<%d,%d,%d>\n",i,k,m); |
1557 |
|
|
k=m;l=n; |
1558 |
|
|
m=ed[k][l];ed[k][l]=-1-m; |
1559 |
|
|
} |
1560 |
|
|
} |
1561 |
|
|
} |
1562 |
|
|
fputs("}\ninside_vector <0,0,1>\n}\n",fp); |
1563 |
|
|
reset_edges(); |
1564 |
|
|
} |
1565 |
|
|
|
1566 |
|
|
/** Several routines in the class that gather cell-based statistics internally |
1567 |
|
|
* track their progress by flipping edges to negative so that they know what |
1568 |
|
|
* parts of the cell have already been tested. This function resets them back |
1569 |
|
|
* to positive. When it is called, it assumes that every edge in the routine |
1570 |
|
|
* should have already been flipped to negative, and it bails out with an |
1571 |
|
|
* internal error if it encounters a positive edge. */ |
1572 |
|
|
inline void voronoicell_base::reset_edges() { |
1573 |
|
|
int i,j; |
1574 |
|
|
for(i=0;i<p;i++) for(j=0;j<nu[i];j++) { |
1575 |
|
|
if(ed[i][j]>=0) voro_fatal_error("Edge reset routine found a previously untested edge",VOROPP_INTERNAL_ERROR); |
1576 |
|
|
ed[i][j]=-1-ed[i][j]; |
1577 |
|
|
} |
1578 |
|
|
} |
1579 |
|
|
|
1580 |
|
|
/** Checks to see if a given vertex is inside, outside or within the test |
1581 |
|
|
* plane. If the point is far away from the test plane, the routine immediately |
1582 |
|
|
* returns whether it is inside or outside. If the routine is close the the |
1583 |
|
|
* plane and within the specified tolerance, then the special check_marginal() |
1584 |
|
|
* routine is called. |
1585 |
|
|
* \param[in] n the vertex to test. |
1586 |
|
|
* \param[out] ans the result of the scalar product used in evaluating the |
1587 |
|
|
* location of the point. |
1588 |
|
|
* \return -1 if the point is inside the plane, 1 if the point is outside the |
1589 |
|
|
* plane, or 0 if the point is within the plane. */ |
1590 |
|
|
inline int voronoicell_base::m_test(int n,double &ans) { |
1591 |
|
|
double *pp=pts+n+(n<<1); |
1592 |
|
|
ans=*(pp++)*px; |
1593 |
|
|
ans+=*(pp++)*py; |
1594 |
|
|
ans+=*pp*pz-prsq; |
1595 |
|
|
if(ans<-tolerance2) { |
1596 |
|
|
return -1; |
1597 |
|
|
} else if(ans>tolerance2) { |
1598 |
|
|
return 1; |
1599 |
|
|
} |
1600 |
|
|
return check_marginal(n,ans); |
1601 |
|
|
} |
1602 |
|
|
|
1603 |
|
|
/** Checks to see if a given vertex is inside, outside or within the test |
1604 |
|
|
* plane, for the case when the point has been detected to be very close to the |
1605 |
|
|
* plane. The routine ensures that the returned results are always consistent |
1606 |
|
|
* with previous tests, by keeping a table of any marginal results. The routine |
1607 |
|
|
* first sees if the vertex is in the table, and if it finds a previously |
1608 |
|
|
* computed result it uses that. Otherwise, it computes a result for this |
1609 |
|
|
* vertex and adds it the table. |
1610 |
|
|
* \param[in] n the vertex to test. |
1611 |
|
|
* \param[in] ans the result of the scalar product used in evaluating |
1612 |
|
|
* the location of the point. |
1613 |
|
|
* \return -1 if the point is inside the plane, 1 if the point is outside the |
1614 |
|
|
* plane, or 0 if the point is within the plane. */ |
1615 |
|
|
int voronoicell_base::check_marginal(int n,double &ans) { |
1616 |
|
|
int i; |
1617 |
|
|
for(i=0;i<n_marg;i+=2) if(marg[i]==n) return marg[i+1]; |
1618 |
|
|
if(n_marg==current_marginal) { |
1619 |
|
|
current_marginal<<=1; |
1620 |
|
|
if(current_marginal>max_marginal) |
1621 |
|
|
voro_fatal_error("Marginal case buffer allocation exceeded absolute maximum",VOROPP_MEMORY_ERROR); |
1622 |
|
|
#if VOROPP_VERBOSE >=2 |
1623 |
|
|
fprintf(stderr,"Marginal cases buffer scaled up to %d\n",i); |
1624 |
|
|
#endif |
1625 |
|
|
int *pmarg=new int[current_marginal]; |
1626 |
|
|
for(int j=0;j<n_marg;j++) pmarg[j]=marg[j]; |
1627 |
|
|
delete [] marg; |
1628 |
|
|
marg=pmarg; |
1629 |
|
|
} |
1630 |
|
|
marg[n_marg++]=n; |
1631 |
|
|
marg[n_marg++]=ans>tolerance?1:(ans<-tolerance?-1:0); |
1632 |
|
|
return marg[n_marg-1]; |
1633 |
|
|
} |
1634 |
|
|
|
1635 |
|
|
/** For each face of the Voronoi cell, this routine prints the out the normal |
1636 |
|
|
* vector of the face, and scales it to the distance from the cell center to |
1637 |
|
|
* that plane. |
1638 |
|
|
* \param[out] v the vector to store the results in. */ |
1639 |
|
|
void voronoicell_base::normals(std::vector<double> &v) { |
1640 |
|
|
int i,j,k; |
1641 |
|
|
v.clear(); |
1642 |
|
|
for(i=1;i<p;i++) for(j=0;j<nu[i];j++) { |
1643 |
|
|
k=ed[i][j]; |
1644 |
|
|
if(k>=0) normals_search(v,i,j,k); |
1645 |
|
|
} |
1646 |
|
|
reset_edges(); |
1647 |
|
|
} |
1648 |
|
|
|
1649 |
|
|
/** This inline routine is called by normals(). It attempts to construct a |
1650 |
|
|
* single normal vector that is associated with a particular face. It first |
1651 |
|
|
* traces around the face, trying to find two vectors along the face edges |
1652 |
|
|
* whose vector product is above the numerical tolerance. It then constructs |
1653 |
|
|
* the normal vector using this product. If the face is too small, and none of |
1654 |
|
|
* the vector products are large enough, the routine may return (0,0,0) as the |
1655 |
|
|
* normal vector. |
1656 |
|
|
* \param[in] v the vector to store the results in. |
1657 |
|
|
* \param[in] i the initial vertex of the face to test. |
1658 |
|
|
* \param[in] j the index of an edge of the vertex. |
1659 |
|
|
* \param[in] k the neighboring vertex of i, set to ed[i][j]. */ |
1660 |
|
|
inline void voronoicell_base::normals_search(std::vector<double> &v,int i,int j,int k) { |
1661 |
|
|
ed[i][j]=-1-k; |
1662 |
|
|
int l=cycle_up(ed[i][nu[i]+j],k),m; |
1663 |
|
|
double ux,uy,uz,vx,vy,vz,wx,wy,wz,wmag; |
1664 |
|
|
do { |
1665 |
|
|
m=ed[k][l];ed[k][l]=-1-m; |
1666 |
|
|
ux=pts[3*m]-pts[3*k]; |
1667 |
|
|
uy=pts[3*m+1]-pts[3*k+1]; |
1668 |
|
|
uz=pts[3*m+2]-pts[3*k+2]; |
1669 |
|
|
|
1670 |
|
|
// Test to see if the length of this edge is above the tolerance |
1671 |
|
|
if(ux*ux+uy*uy+uz*uz>tolerance_sq) { |
1672 |
|
|
while(m!=i) { |
1673 |
|
|
l=cycle_up(ed[k][nu[k]+l],m); |
1674 |
|
|
k=m;m=ed[k][l];ed[k][l]=-1-m; |
1675 |
|
|
vx=pts[3*m]-pts[3*k]; |
1676 |
|
|
vy=pts[3*m+1]-pts[3*k+1]; |
1677 |
|
|
vz=pts[3*m+2]-pts[3*k+2]; |
1678 |
|
|
|
1679 |
|
|
// Construct the vector product of this edge with |
1680 |
|
|
// the previous one |
1681 |
|
|
wx=uz*vy-uy*vz; |
1682 |
|
|
wy=ux*vz-uz*vx; |
1683 |
|
|
wz=uy*vx-ux*vy; |
1684 |
|
|
wmag=wx*wx+wy*wy+wz*wz; |
1685 |
|
|
|
1686 |
|
|
// Test to see if this vector product of the |
1687 |
|
|
// two edges is above the tolerance |
1688 |
|
|
if(wmag>tolerance_sq) { |
1689 |
|
|
|
1690 |
|
|
// Construct the normal vector and print it |
1691 |
|
|
wmag=1/sqrt(wmag); |
1692 |
|
|
v.push_back(wx*wmag); |
1693 |
|
|
v.push_back(wy*wmag); |
1694 |
|
|
v.push_back(wz*wmag); |
1695 |
|
|
|
1696 |
|
|
// Mark all of the remaining edges of this |
1697 |
|
|
// face and exit |
1698 |
|
|
while(m!=i) { |
1699 |
|
|
l=cycle_up(ed[k][nu[k]+l],m); |
1700 |
|
|
k=m;m=ed[k][l];ed[k][l]=-1-m; |
1701 |
|
|
} |
1702 |
|
|
return; |
1703 |
|
|
} |
1704 |
|
|
} |
1705 |
|
|
v.push_back(0); |
1706 |
|
|
v.push_back(0); |
1707 |
|
|
v.push_back(0); |
1708 |
|
|
return; |
1709 |
|
|
} |
1710 |
|
|
l=cycle_up(ed[k][nu[k]+l],m); |
1711 |
|
|
k=m; |
1712 |
|
|
} while (k!=i); |
1713 |
|
|
v.push_back(0); |
1714 |
|
|
v.push_back(0); |
1715 |
|
|
v.push_back(0); |
1716 |
|
|
} |
1717 |
|
|
|
1718 |
|
|
|
1719 |
|
|
/** Returns the number of faces of a computed Voronoi cell. |
1720 |
|
|
* \return The number of faces. */ |
1721 |
|
|
int voronoicell_base::number_of_faces() { |
1722 |
|
|
int i,j,k,l,m,s=0; |
1723 |
|
|
for(i=1;i<p;i++) for(j=0;j<nu[i];j++) { |
1724 |
|
|
k=ed[i][j]; |
1725 |
|
|
if(k>=0) { |
1726 |
|
|
s++; |
1727 |
|
|
ed[i][j]=-1-k; |
1728 |
|
|
l=cycle_up(ed[i][nu[i]+j],k); |
1729 |
|
|
do { |
1730 |
|
|
m=ed[k][l]; |
1731 |
|
|
ed[k][l]=-1-m; |
1732 |
|
|
l=cycle_up(ed[k][nu[k]+l],m); |
1733 |
|
|
k=m; |
1734 |
|
|
} while (k!=i); |
1735 |
|
|
|
1736 |
|
|
} |
1737 |
|
|
} |
1738 |
|
|
reset_edges(); |
1739 |
|
|
return s; |
1740 |
|
|
} |
1741 |
|
|
|
1742 |
|
|
/** Returns a vector of the vertex orders. |
1743 |
|
|
* \param[out] v the vector to store the results in. */ |
1744 |
|
|
void voronoicell_base::vertex_orders(std::vector<int> &v) { |
1745 |
|
|
v.resize(p); |
1746 |
|
|
for(int i=0;i<p;i++) v[i]=nu[i]; |
1747 |
|
|
} |
1748 |
|
|
|
1749 |
|
|
/** Outputs the vertex orders. |
1750 |
|
|
* \param[out] fp the file handle to write to. */ |
1751 |
|
|
void voronoicell_base::output_vertex_orders(FILE *fp) { |
1752 |
|
|
if(p>0) { |
1753 |
|
|
fprintf(fp,"%d",*nu); |
1754 |
|
|
for(int *nup=nu+1;nup<nu+p;nup++) fprintf(fp," %d",*nup); |
1755 |
|
|
} |
1756 |
|
|
} |
1757 |
|
|
|
1758 |
|
|
/** Returns a vector of the vertex vectors using the local coordinate system. |
1759 |
|
|
* \param[out] v the vector to store the results in. */ |
1760 |
|
|
void voronoicell_base::vertices(std::vector<double> &v) { |
1761 |
|
|
v.resize(3*p); |
1762 |
|
|
double *ptsp=pts; |
1763 |
|
|
for(int i=0;i<3*p;i+=3) { |
1764 |
|
|
v[i]=*(ptsp++)*0.5; |
1765 |
|
|
v[i+1]=*(ptsp++)*0.5; |
1766 |
|
|
v[i+2]=*(ptsp++)*0.5; |
1767 |
|
|
} |
1768 |
|
|
} |
1769 |
|
|
|
1770 |
|
|
/** Outputs the vertex vectors using the local coordinate system. |
1771 |
|
|
* \param[out] fp the file handle to write to. */ |
1772 |
|
|
void voronoicell_base::output_vertices(FILE *fp) { |
1773 |
|
|
if(p>0) { |
1774 |
|
|
fprintf(fp,"(%g,%g,%g)",*pts*0.5,pts[1]*0.5,pts[2]*0.5); |
1775 |
|
|
for(double *ptsp=pts+3;ptsp<pts+3*p;ptsp+=3) fprintf(fp," (%g,%g,%g)",*ptsp*0.5,ptsp[1]*0.5,ptsp[2]*0.5); |
1776 |
|
|
} |
1777 |
|
|
} |
1778 |
|
|
|
1779 |
|
|
|
1780 |
|
|
/** Returns a vector of the vertex vectors in the global coordinate system. |
1781 |
|
|
* \param[out] v the vector to store the results in. |
1782 |
|
|
* \param[in] (x,y,z) the position vector of the particle in the global |
1783 |
|
|
* coordinate system. */ |
1784 |
|
|
void voronoicell_base::vertices(double x,double y,double z,std::vector<double> &v) { |
1785 |
|
|
v.resize(3*p); |
1786 |
|
|
double *ptsp=pts; |
1787 |
|
|
for(int i=0;i<3*p;i+=3) { |
1788 |
|
|
v[i]=x+*(ptsp++)*0.5; |
1789 |
|
|
v[i+1]=y+*(ptsp++)*0.5; |
1790 |
|
|
v[i+2]=z+*(ptsp++)*0.5; |
1791 |
|
|
} |
1792 |
|
|
} |
1793 |
|
|
|
1794 |
|
|
/** Outputs the vertex vectors using the global coordinate system. |
1795 |
|
|
* \param[out] fp the file handle to write to. |
1796 |
|
|
* \param[in] (x,y,z) the position vector of the particle in the global |
1797 |
|
|
* coordinate system. */ |
1798 |
|
|
void voronoicell_base::output_vertices(double x,double y,double z,FILE *fp) { |
1799 |
|
|
if(p>0) { |
1800 |
|
|
fprintf(fp,"(%g,%g,%g)",x+*pts*0.5,y+pts[1]*0.5,z+pts[2]*0.5); |
1801 |
|
|
for(double *ptsp=pts+3;ptsp<pts+3*p;ptsp+=3) fprintf(fp," (%g,%g,%g)",x+*ptsp*0.5,y+ptsp[1]*0.5,z+ptsp[2]*0.5); |
1802 |
|
|
} |
1803 |
|
|
} |
1804 |
|
|
|
1805 |
|
|
/** This routine returns the perimeters of each face. |
1806 |
|
|
* \param[out] v the vector to store the results in. */ |
1807 |
|
|
void voronoicell_base::face_perimeters(std::vector<double> &v) { |
1808 |
|
|
v.clear(); |
1809 |
|
|
int i,j,k,l,m; |
1810 |
|
|
double dx,dy,dz,perim; |
1811 |
|
|
for(i=1;i<p;i++) for(j=0;j<nu[i];j++) { |
1812 |
|
|
k=ed[i][j]; |
1813 |
|
|
if(k>=0) { |
1814 |
|
|
dx=pts[3*k]-pts[3*i]; |
1815 |
|
|
dy=pts[3*k+1]-pts[3*i+1]; |
1816 |
|
|
dz=pts[3*k+2]-pts[3*i+2]; |
1817 |
|
|
perim=sqrt(dx*dx+dy*dy+dz*dz); |
1818 |
|
|
ed[i][j]=-1-k; |
1819 |
|
|
l=cycle_up(ed[i][nu[i]+j],k); |
1820 |
|
|
do { |
1821 |
|
|
m=ed[k][l]; |
1822 |
|
|
dx=pts[3*m]-pts[3*k]; |
1823 |
|
|
dy=pts[3*m+1]-pts[3*k+1]; |
1824 |
|
|
dz=pts[3*m+2]-pts[3*k+2]; |
1825 |
|
|
perim+=sqrt(dx*dx+dy*dy+dz*dz); |
1826 |
|
|
ed[k][l]=-1-m; |
1827 |
|
|
l=cycle_up(ed[k][nu[k]+l],m); |
1828 |
|
|
k=m; |
1829 |
|
|
} while (k!=i); |
1830 |
|
|
v.push_back(0.5*perim); |
1831 |
|
|
} |
1832 |
|
|
} |
1833 |
|
|
reset_edges(); |
1834 |
|
|
} |
1835 |
|
|
|
1836 |
|
|
/** For each face, this routine outputs a bracketed sequence of numbers |
1837 |
|
|
* containing a list of all the vertices that make up that face. |
1838 |
|
|
* \param[out] v the vector to store the results in. */ |
1839 |
|
|
void voronoicell_base::face_vertices(std::vector<int> &v) { |
1840 |
|
|
int i,j,k,l,m,vp(0),vn; |
1841 |
|
|
v.clear(); |
1842 |
|
|
for(i=1;i<p;i++) for(j=0;j<nu[i];j++) { |
1843 |
|
|
k=ed[i][j]; |
1844 |
|
|
if(k>=0) { |
1845 |
|
|
v.push_back(0); |
1846 |
|
|
v.push_back(i); |
1847 |
|
|
ed[i][j]=-1-k; |
1848 |
|
|
l=cycle_up(ed[i][nu[i]+j],k); |
1849 |
|
|
do { |
1850 |
|
|
v.push_back(k); |
1851 |
|
|
m=ed[k][l]; |
1852 |
|
|
ed[k][l]=-1-m; |
1853 |
|
|
l=cycle_up(ed[k][nu[k]+l],m); |
1854 |
|
|
k=m; |
1855 |
|
|
} while (k!=i); |
1856 |
|
|
vn=v.size(); |
1857 |
|
|
v[vp]=vn-vp-1; |
1858 |
|
|
vp=vn; |
1859 |
|
|
} |
1860 |
|
|
} |
1861 |
|
|
reset_edges(); |
1862 |
|
|
} |
1863 |
|
|
|
1864 |
|
|
/** Outputs a list of the number of edges in each face. |
1865 |
|
|
* \param[out] v the vector to store the results in. */ |
1866 |
|
|
void voronoicell_base::face_orders(std::vector<int> &v) { |
1867 |
|
|
int i,j,k,l,m,q; |
1868 |
|
|
v.clear(); |
1869 |
|
|
for(i=1;i<p;i++) for(j=0;j<nu[i];j++) { |
1870 |
|
|
k=ed[i][j]; |
1871 |
|
|
if(k>=0) { |
1872 |
|
|
q=1; |
1873 |
|
|
ed[i][j]=-1-k; |
1874 |
|
|
l=cycle_up(ed[i][nu[i]+j],k); |
1875 |
|
|
do { |
1876 |
|
|
q++; |
1877 |
|
|
m=ed[k][l]; |
1878 |
|
|
ed[k][l]=-1-m; |
1879 |
|
|
l=cycle_up(ed[k][nu[k]+l],m); |
1880 |
|
|
k=m; |
1881 |
|
|
} while (k!=i); |
1882 |
|
|
v.push_back(q);; |
1883 |
|
|
} |
1884 |
|
|
} |
1885 |
|
|
reset_edges(); |
1886 |
|
|
} |
1887 |
|
|
|
1888 |
|
|
/** Computes the number of edges that each face has and outputs a frequency |
1889 |
|
|
* table of the results. |
1890 |
|
|
* \param[out] v the vector to store the results in. */ |
1891 |
|
|
void voronoicell_base::face_freq_table(std::vector<int> &v) { |
1892 |
|
|
int i,j,k,l,m,q; |
1893 |
|
|
v.clear(); |
1894 |
|
|
for(i=1;i<p;i++) for(j=0;j<nu[i];j++) { |
1895 |
|
|
k=ed[i][j]; |
1896 |
|
|
if(k>=0) { |
1897 |
|
|
q=1; |
1898 |
|
|
ed[i][j]=-1-k; |
1899 |
|
|
l=cycle_up(ed[i][nu[i]+j],k); |
1900 |
|
|
do { |
1901 |
|
|
q++; |
1902 |
|
|
m=ed[k][l]; |
1903 |
|
|
ed[k][l]=-1-m; |
1904 |
|
|
l=cycle_up(ed[k][nu[k]+l],m); |
1905 |
|
|
k=m; |
1906 |
|
|
} while (k!=i); |
1907 |
|
|
if((unsigned int) q>=v.size()) v.resize(q+1,0); |
1908 |
|
|
v[q]++; |
1909 |
|
|
} |
1910 |
|
|
} |
1911 |
|
|
reset_edges(); |
1912 |
|
|
} |
1913 |
|
|
|
1914 |
|
|
/** This routine tests to see whether the cell intersects a plane by starting |
1915 |
|
|
* from the guess point up. If up intersects, then it immediately returns true. |
1916 |
|
|
* Otherwise, it calls the plane_intersects_track() routine. |
1917 |
|
|
* \param[in] (x,y,z) the normal vector to the plane. |
1918 |
|
|
* \param[in] rsq the distance along this vector of the plane. |
1919 |
|
|
* \return False if the plane does not intersect the plane, true if it does. */ |
1920 |
|
|
bool voronoicell_base::plane_intersects(double x,double y,double z,double rsq) { |
1921 |
|
|
double g=x*pts[3*up]+y*pts[3*up+1]+z*pts[3*up+2]; |
1922 |
|
|
if(g<rsq) return plane_intersects_track(x,y,z,rsq,g); |
1923 |
|
|
return true; |
1924 |
|
|
} |
1925 |
|
|
|
1926 |
|
|
/** This routine tests to see if a cell intersects a plane. It first tests a |
1927 |
|
|
* random sample of approximately sqrt(p)/4 points. If any of those are |
1928 |
|
|
* intersect, then it immediately returns true. Otherwise, it takes the closest |
1929 |
|
|
* point and passes that to plane_intersect_track() routine. |
1930 |
|
|
* \param[in] (x,y,z) the normal vector to the plane. |
1931 |
|
|
* \param[in] rsq the distance along this vector of the plane. |
1932 |
|
|
* \return False if the plane does not intersect the plane, true if it does. */ |
1933 |
|
|
bool voronoicell_base::plane_intersects_guess(double x,double y,double z,double rsq) { |
1934 |
|
|
up=0; |
1935 |
|
|
double g=x*pts[3*up]+y*pts[3*up+1]+z*pts[3*up+2]; |
1936 |
|
|
if(g<rsq) { |
1937 |
|
|
int ca=1,cc=p>>3,mp=1; |
1938 |
|
|
double m; |
1939 |
|
|
while(ca<cc) { |
1940 |
|
|
m=x*pts[3*mp]+y*pts[3*mp+1]+z*pts[3*mp+2]; |
1941 |
|
|
if(m>g) { |
1942 |
|
|
if(m>rsq) return true; |
1943 |
|
|
g=m;up=mp; |
1944 |
|
|
} |
1945 |
|
|
ca+=mp++; |
1946 |
|
|
} |
1947 |
|
|
return plane_intersects_track(x,y,z,rsq,g); |
1948 |
|
|
} |
1949 |
|
|
return true; |
1950 |
|
|
} |
1951 |
|
|
|
1952 |
|
|
/* This routine tests to see if a cell intersects a plane, by tracing over the cell from |
1953 |
|
|
* vertex to vertex, starting at up. It is meant to be called either by plane_intersects() |
1954 |
|
|
* or plane_intersects_track(), when those routines cannot immediately resolve the case. |
1955 |
|
|
* \param[in] (x,y,z) the normal vector to the plane. |
1956 |
|
|
* \param[in] rsq the distance along this vector of the plane. |
1957 |
|
|
* \param[in] g the distance of up from the plane. |
1958 |
|
|
* \return False if the plane does not intersect the plane, true if it does. */ |
1959 |
|
|
inline bool voronoicell_base::plane_intersects_track(double x,double y,double z,double rsq,double g) { |
1960 |
|
|
int count=0,ls,us,tp; |
1961 |
|
|
double t; |
1962 |
|
|
|
1963 |
|
|
// The test point is outside of the cutting space |
1964 |
|
|
for(us=0;us<nu[up];us++) { |
1965 |
|
|
tp=ed[up][us]; |
1966 |
|
|
t=x*pts[3*tp]+y*pts[3*tp+1]+z*pts[3*tp+2]; |
1967 |
|
|
if(t>g) { |
1968 |
|
|
ls=ed[up][nu[up]+us]; |
1969 |
|
|
up=tp; |
1970 |
|
|
while (t<rsq) { |
1971 |
|
|
if(++count>=p) { |
1972 |
|
|
#if VOROPP_VERBOSE >=1 |
1973 |
|
|
fputs("Bailed out of convex calculation",stderr); |
1974 |
|
|
#endif |
1975 |
|
|
for(tp=0;tp<p;tp++) if(x*pts[3*tp]+y*pts[3*tp+1]+z*pts[3*tp+2]>rsq) return true; |
1976 |
|
|
return false; |
1977 |
|
|
} |
1978 |
|
|
|
1979 |
|
|
// Test all the neighbors of the current point |
1980 |
|
|
// and find the one which is closest to the |
1981 |
|
|
// plane |
1982 |
|
|
for(us=0;us<ls;us++) { |
1983 |
|
|
tp=ed[up][us]; |
1984 |
|
|
g=x*pts[3*tp]+y*pts[3*tp+1]+z*pts[3*tp+2]; |
1985 |
|
|
if(g>t) break; |
1986 |
|
|
} |
1987 |
|
|
if(us==ls) { |
1988 |
|
|
us++; |
1989 |
|
|
while(us<nu[up]) { |
1990 |
|
|
tp=ed[up][us]; |
1991 |
|
|
g=x*pts[3*tp]+y*pts[3*tp+1]+z*pts[3*tp+2]; |
1992 |
|
|
if(g>t) break; |
1993 |
|
|
us++; |
1994 |
|
|
} |
1995 |
|
|
if(us==nu[up]) return false; |
1996 |
|
|
} |
1997 |
|
|
ls=ed[up][nu[up]+us];up=tp;t=g; |
1998 |
|
|
} |
1999 |
|
|
return true; |
2000 |
|
|
} |
2001 |
|
|
} |
2002 |
|
|
return false; |
2003 |
|
|
} |
2004 |
|
|
|
2005 |
|
|
/** Counts the number of edges of the Voronoi cell. |
2006 |
|
|
* \return the number of edges. */ |
2007 |
|
|
int voronoicell_base::number_of_edges() { |
2008 |
|
|
int edges=0,*nup=nu; |
2009 |
|
|
while(nup<nu+p) edges+=*(nup++); |
2010 |
|
|
return edges>>1; |
2011 |
|
|
} |
2012 |
|
|
|
2013 |
|
|
/** Outputs a custom string of information about the Voronoi cell. The string |
2014 |
|
|
* of information follows a similar style as the C printf command, and detailed |
2015 |
|
|
* information about its format is available at |
2016 |
|
|
* http://math.lbl.gov/voro++/doc/custom.html. |
2017 |
|
|
* \param[in] format the custom string to print. |
2018 |
|
|
* \param[in] i the ID of the particle associated with this Voronoi cell. |
2019 |
|
|
* \param[in] (x,y,z) the position of the particle associated with this Voronoi |
2020 |
|
|
* cell. |
2021 |
|
|
* \param[in] r a radius associated with the particle. |
2022 |
|
|
* \param[in] fp the file handle to write to. */ |
2023 |
|
|
void voronoicell_base::output_custom(const char *format,int i,double x,double y,double z,double r,FILE *fp) { |
2024 |
|
|
char *fmp=(const_cast<char*>(format)); |
2025 |
|
|
std::vector<int> vi; |
2026 |
|
|
std::vector<double> vd; |
2027 |
|
|
while(*fmp!=0) { |
2028 |
|
|
if(*fmp=='%') { |
2029 |
|
|
fmp++; |
2030 |
|
|
switch(*fmp) { |
2031 |
|
|
|
2032 |
|
|
// Particle-related output |
2033 |
|
|
case 'i': fprintf(fp,"%d",i);break; |
2034 |
|
|
case 'x': fprintf(fp,"%g",x);break; |
2035 |
|
|
case 'y': fprintf(fp,"%g",y);break; |
2036 |
|
|
case 'z': fprintf(fp,"%g",z);break; |
2037 |
|
|
case 'q': fprintf(fp,"%g %g %g",x,y,z);break; |
2038 |
|
|
case 'r': fprintf(fp,"%g",r);break; |
2039 |
|
|
|
2040 |
|
|
// Vertex-related output |
2041 |
|
|
case 'w': fprintf(fp,"%d",p);break; |
2042 |
|
|
case 'p': output_vertices(fp);break; |
2043 |
|
|
case 'P': output_vertices(x,y,z,fp);break; |
2044 |
|
|
case 'o': output_vertex_orders(fp);break; |
2045 |
|
|
case 'm': fprintf(fp,"%g",0.25*max_radius_squared());break; |
2046 |
|
|
|
2047 |
|
|
// Edge-related output |
2048 |
|
|
case 'g': fprintf(fp,"%d",number_of_edges());break; |
2049 |
|
|
case 'E': fprintf(fp,"%g",total_edge_distance());break; |
2050 |
|
|
case 'e': face_perimeters(vd);voro_print_vector(vd,fp);break; |
2051 |
|
|
|
2052 |
|
|
// Face-related output |
2053 |
|
|
case 's': fprintf(fp,"%d",number_of_faces());break; |
2054 |
|
|
case 'F': fprintf(fp,"%g",surface_area());break; |
2055 |
|
|
case 'A': { |
2056 |
|
|
face_freq_table(vi); |
2057 |
|
|
voro_print_vector(vi,fp); |
2058 |
|
|
} break; |
2059 |
|
|
case 'a': face_orders(vi);voro_print_vector(vi,fp);break; |
2060 |
|
|
case 'f': face_areas(vd);voro_print_vector(vd,fp);break; |
2061 |
|
|
case 't': { |
2062 |
|
|
face_vertices(vi); |
2063 |
|
|
voro_print_face_vertices(vi,fp); |
2064 |
|
|
} break; |
2065 |
|
|
case 'l': normals(vd); |
2066 |
|
|
voro_print_positions(vd,fp); |
2067 |
|
|
break; |
2068 |
|
|
case 'n': neighbors(vi); |
2069 |
|
|
voro_print_vector(vi,fp); |
2070 |
|
|
break; |
2071 |
|
|
|
2072 |
|
|
// Volume-related output |
2073 |
|
|
case 'v': fprintf(fp,"%g",volume());break; |
2074 |
|
|
case 'c': { |
2075 |
|
|
double cx,cy,cz; |
2076 |
|
|
centroid(cx,cy,cz); |
2077 |
|
|
fprintf(fp,"%g %g %g",cx,cy,cz); |
2078 |
|
|
} break; |
2079 |
|
|
case 'C': { |
2080 |
|
|
double cx,cy,cz; |
2081 |
|
|
centroid(cx,cy,cz); |
2082 |
|
|
fprintf(fp,"%g %g %g",x+cx,y+cy,z+cz); |
2083 |
|
|
} break; |
2084 |
|
|
|
2085 |
|
|
// End-of-string reached |
2086 |
|
|
case 0: fmp--;break; |
2087 |
|
|
|
2088 |
|
|
// The percent sign is not part of a |
2089 |
|
|
// control sequence |
2090 |
|
|
default: putc('%',fp);putc(*fmp,fp); |
2091 |
|
|
} |
2092 |
|
|
} else putc(*fmp,fp); |
2093 |
|
|
fmp++; |
2094 |
|
|
} |
2095 |
|
|
fputs("\n",fp); |
2096 |
|
|
} |
2097 |
|
|
|
2098 |
|
|
/** This initializes the class to be a rectangular box. It calls the base class |
2099 |
|
|
* initialization routine to set up the edge and vertex information, and then |
2100 |
|
|
* sets up the neighbor information, with initial faces being assigned ID |
2101 |
|
|
* numbers from -1 to -6. |
2102 |
|
|
* \param[in] (xmin,xmax) the minimum and maximum x coordinates. |
2103 |
|
|
* \param[in] (ymin,ymax) the minimum and maximum y coordinates. |
2104 |
|
|
* \param[in] (zmin,zmax) the minimum and maximum z coordinates. */ |
2105 |
|
|
void voronoicell_neighbor::init(double xmin,double xmax,double ymin,double ymax,double zmin,double zmax) { |
2106 |
|
|
init_base(xmin,xmax,ymin,ymax,zmin,zmax); |
2107 |
|
|
int *q=mne[3]; |
2108 |
|
|
*q=-5;q[1]=-3;q[2]=-1; |
2109 |
|
|
q[3]=-5;q[4]=-2;q[5]=-3; |
2110 |
|
|
q[6]=-5;q[7]=-1;q[8]=-4; |
2111 |
|
|
q[9]=-5;q[10]=-4;q[11]=-2; |
2112 |
|
|
q[12]=-6;q[13]=-1;q[14]=-3; |
2113 |
|
|
q[15]=-6;q[16]=-3;q[17]=-2; |
2114 |
|
|
q[18]=-6;q[19]=-4;q[20]=-1; |
2115 |
|
|
q[21]=-6;q[22]=-2;q[23]=-4; |
2116 |
|
|
*ne=q;ne[1]=q+3;ne[2]=q+6;ne[3]=q+9; |
2117 |
|
|
ne[4]=q+12;ne[5]=q+15;ne[6]=q+18;ne[7]=q+21; |
2118 |
|
|
} |
2119 |
|
|
|
2120 |
|
|
/** This initializes the class to be an octahedron. It calls the base class |
2121 |
|
|
* initialization routine to set up the edge and vertex information, and then |
2122 |
|
|
* sets up the neighbor information, with the initial faces being assigned ID |
2123 |
|
|
* numbers from -1 to -8. |
2124 |
|
|
* \param[in] l The distance from the octahedron center to a vertex. Six |
2125 |
|
|
* vertices are initialized at (-l,0,0), (l,0,0), (0,-l,0), |
2126 |
|
|
* (0,l,0), (0,0,-l), and (0,0,l). */ |
2127 |
|
|
void voronoicell_neighbor::init_octahedron(double l) { |
2128 |
|
|
init_octahedron_base(l); |
2129 |
|
|
int *q=mne[4]; |
2130 |
|
|
*q=-5;q[1]=-6;q[2]=-7;q[3]=-8; |
2131 |
|
|
q[4]=-1;q[5]=-2;q[6]=-3;q[7]=-4; |
2132 |
|
|
q[8]=-6;q[9]=-5;q[10]=-2;q[11]=-1; |
2133 |
|
|
q[12]=-8;q[13]=-7;q[14]=-4;q[15]=-3; |
2134 |
|
|
q[16]=-5;q[17]=-8;q[18]=-3;q[19]=-2; |
2135 |
|
|
q[20]=-7;q[21]=-6;q[22]=-1;q[23]=-4; |
2136 |
|
|
*ne=q;ne[1]=q+4;ne[2]=q+8;ne[3]=q+12;ne[4]=q+16;ne[5]=q+20; |
2137 |
|
|
} |
2138 |
|
|
|
2139 |
|
|
/** This initializes the class to be a tetrahedron. It calls the base class |
2140 |
|
|
* initialization routine to set up the edge and vertex information, and then |
2141 |
|
|
* sets up the neighbor information, with the initial faces being assigned ID |
2142 |
|
|
* numbers from -1 to -4. |
2143 |
|
|
* \param (x0,y0,z0) a position vector for the first vertex. |
2144 |
|
|
* \param (x1,y1,z1) a position vector for the second vertex. |
2145 |
|
|
* \param (x2,y2,z2) a position vector for the third vertex. |
2146 |
|
|
* \param (x3,y3,z3) a position vector for the fourth vertex. */ |
2147 |
|
|
void voronoicell_neighbor::init_tetrahedron(double x0,double y0,double z0,double x1,double y1,double z1,double x2,double y2,double z2,double x3,double y3,double z3) { |
2148 |
|
|
init_tetrahedron_base(x0,y0,z0,x1,y1,z1,x2,y2,z2,x3,y3,z3); |
2149 |
|
|
int *q=mne[3]; |
2150 |
|
|
*q=-4;q[1]=-3;q[2]=-2; |
2151 |
|
|
q[3]=-3;q[4]=-4;q[5]=-1; |
2152 |
|
|
q[6]=-4;q[7]=-2;q[8]=-1; |
2153 |
|
|
q[9]=-2;q[10]=-3;q[11]=-1; |
2154 |
|
|
*ne=q;ne[1]=q+3;ne[2]=q+6;ne[3]=q+9; |
2155 |
|
|
} |
2156 |
|
|
|
2157 |
|
|
/** This routine checks to make sure the neighbor information of each face is |
2158 |
|
|
* consistent. */ |
2159 |
|
|
void voronoicell_neighbor::check_facets() { |
2160 |
|
|
int i,j,k,l,m,q; |
2161 |
|
|
for(i=1;i<p;i++) for(j=0;j<nu[i];j++) { |
2162 |
|
|
k=ed[i][j]; |
2163 |
|
|
if(k>=0) { |
2164 |
|
|
ed[i][j]=-1-k; |
2165 |
|
|
q=ne[i][j]; |
2166 |
|
|
l=cycle_up(ed[i][nu[i]+j],k); |
2167 |
|
|
do { |
2168 |
|
|
m=ed[k][l]; |
2169 |
|
|
ed[k][l]=-1-m; |
2170 |
|
|
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); |
2171 |
|
|
l=cycle_up(ed[k][nu[k]+l],m); |
2172 |
|
|
k=m; |
2173 |
|
|
} while (k!=i); |
2174 |
|
|
} |
2175 |
|
|
} |
2176 |
|
|
reset_edges(); |
2177 |
|
|
} |
2178 |
|
|
|
2179 |
|
|
/** The class constructor allocates memory for storing neighbor information. */ |
2180 |
|
|
voronoicell_neighbor::voronoicell_neighbor() { |
2181 |
|
|
int i; |
2182 |
|
|
mne=new int*[current_vertex_order]; |
2183 |
|
|
ne=new int*[current_vertices]; |
2184 |
|
|
for(i=0;i<3;i++) mne[i]=new int[init_n_vertices*i]; |
2185 |
|
|
mne[3]=new int[init_3_vertices*3]; |
2186 |
|
|
for(i=4;i<current_vertex_order;i++) mne[i]=new int[init_n_vertices*i]; |
2187 |
|
|
} |
2188 |
|
|
|
2189 |
|
|
/** The class destructor frees the dynamically allocated memory for storing |
2190 |
|
|
* neighbor information. */ |
2191 |
|
|
voronoicell_neighbor::~voronoicell_neighbor() { |
2192 |
|
|
for(int i=current_vertex_order-1;i>=0;i--) if(mem[i]>0) delete [] mne[i]; |
2193 |
|
|
delete [] mne; |
2194 |
|
|
delete [] ne; |
2195 |
|
|
} |
2196 |
|
|
|
2197 |
|
|
/** Computes a vector list of neighbors. */ |
2198 |
|
|
void voronoicell_neighbor::neighbors(std::vector<int> &v) { |
2199 |
|
|
v.clear(); |
2200 |
|
|
int i,j,k,l,m; |
2201 |
|
|
for(i=1;i<p;i++) for(j=0;j<nu[i];j++) { |
2202 |
|
|
k=ed[i][j]; |
2203 |
|
|
if(k>=0) { |
2204 |
|
|
v.push_back(ne[i][j]); |
2205 |
|
|
ed[i][j]=-1-k; |
2206 |
|
|
l=cycle_up(ed[i][nu[i]+j],k); |
2207 |
|
|
do { |
2208 |
|
|
m=ed[k][l]; |
2209 |
|
|
ed[k][l]=-1-m; |
2210 |
|
|
l=cycle_up(ed[k][nu[k]+l],m); |
2211 |
|
|
k=m; |
2212 |
|
|
} while (k!=i); |
2213 |
|
|
} |
2214 |
|
|
} |
2215 |
|
|
reset_edges(); |
2216 |
|
|
} |
2217 |
|
|
|
2218 |
|
|
/** Prints the vertices, their edges, the relation table, and also notifies if |
2219 |
|
|
* any memory errors are visible. */ |
2220 |
|
|
void voronoicell_base::print_edges() { |
2221 |
|
|
int j; |
2222 |
|
|
double *ptsp=pts; |
2223 |
|
|
for(int i=0;i<p;i++,ptsp+=3) { |
2224 |
|
|
printf("%d %d ",i,nu[i]); |
2225 |
|
|
for(j=0;j<nu[i];j++) printf(" %d",ed[i][j]); |
2226 |
|
|
printf(" "); |
2227 |
|
|
while(j<(nu[i]<<1)) printf(" %d",ed[i][j]); |
2228 |
|
|
printf(" %d",ed[i][j]); |
2229 |
|
|
print_edges_neighbors(i); |
2230 |
|
|
printf(" %g %g %g %p",*ptsp,ptsp[1],ptsp[2],(void*) ed[i]); |
2231 |
|
|
if(ed[i]>=mep[nu[i]]+mec[nu[i]]*((nu[i]<<1)+1)) puts(" Memory error"); |
2232 |
|
|
else puts(""); |
2233 |
|
|
} |
2234 |
|
|
} |
2235 |
|
|
|
2236 |
|
|
/** This prints out the neighbor information for vertex i. */ |
2237 |
|
|
void voronoicell_neighbor::print_edges_neighbors(int i) { |
2238 |
|
|
if(nu[i]>0) { |
2239 |
|
|
int j=0; |
2240 |
|
|
printf(" ("); |
2241 |
|
|
while(j<nu[i]-1) printf("%d,",ne[i][j++]); |
2242 |
|
|
printf("%d)",ne[i][j]); |
2243 |
|
|
} else printf(" ()"); |
2244 |
|
|
} |
2245 |
|
|
|
2246 |
|
|
// Explicit instantiation |
2247 |
|
|
template bool voronoicell_base::nplane(voronoicell&,double,double,double,double,int); |
2248 |
|
|
template bool voronoicell_base::nplane(voronoicell_neighbor&,double,double,double,double,int); |
2249 |
|
|
template void voronoicell_base::check_memory_for_copy(voronoicell&,voronoicell_base*); |
2250 |
|
|
template void voronoicell_base::check_memory_for_copy(voronoicell_neighbor&,voronoicell_base*); |
2251 |
|
|
|
2252 |
|
|
} |