#include <cmath>
#include <queue>
#include "unitcell.hh"
#include "cell.hh"
namespace voro {
unitcell::unitcell(double bx_,double bxy_,double by_,double bxz_,double byz_,double bz_)
: bx(bx_), bxy(bxy_), by(by_), bxz(bxz_), byz(byz_), bz(bz_),
unit_voro(max_unit_voro_shells*max_unit_voro_shells*4*(bx*bx+by*by+bz*bz)) {
int i,j,l=1;
const double ucx=max_unit_voro_shells*bx,ucy=max_unit_voro_shells*by,ucz=max_unit_voro_shells*bz;
unit_voro.init(-ucx,ucx,-ucy,ucy,-ucz,ucz);
while(l<2*max_unit_voro_shells) {
if(unit_voro_intersect(l)) {
unit_voro_apply(l,0,0);
for(i=1;i<l;i++) {
unit_voro_apply(l,i,0);
unit_voro_apply(-l,i,0);
}
for(i=-l;i<=l;i++) unit_voro_apply(i,l,0);
for(i=1;i<l;i++) for(j=-l+1;j<=l;j++) {
unit_voro_apply(l,j,i);
unit_voro_apply(-j,l,i);
unit_voro_apply(-l,-j,i);
unit_voro_apply(j,-l,i);
}
for(i=-l;i<=l;i++) for(j=-l;j<=l;j++) unit_voro_apply(i,j,l);
} else {
max_uv_y=max_uv_z=0;
double y,z,q,*pts=unit_voro.pts,*pp=pts;
while(pp<pts+4*unit_voro.p) {
q=*(pp++);y=*(pp++);z=*pp;pp+=2;q=sqrt(q*q+y*y+z*z);
if(y+q>max_uv_y) max_uv_y=y+q;
if(z+q>max_uv_z) max_uv_z=z+q;
}
max_uv_z*=0.5;
max_uv_y*=0.5;
return;
}
l++;
}
voro_fatal_error("Periodic cell computation failed",VOROPP_MEMORY_ERROR);
}
inline void unitcell::unit_voro_apply(int i,int j,int k) {
double x=i*bx+j*bxy+k*bxz,y=j*by+k*byz,z=k*bz;
unit_voro.plane(x,y,z);
unit_voro.plane(-x,-y,-z);
}
bool unitcell::intersects_image(double dx,double dy,double dz,double &vol) {
const double bxinv=1/bx,byinv=1/by,bzinv=1/bz,ivol=bxinv*byinv*bzinv;
voronoicell c;
c=unit_voro;
dx*=2;dy*=2;dz*=2;
if(!c.plane(0,0,bzinv,dz+1)) return false;
if(!c.plane(0,0,-bzinv,-dz+1)) return false;
if(!c.plane(0,byinv,-byz*byinv*bzinv,dy+1)) return false;
if(!c.plane(0,-byinv,byz*byinv*bzinv,-dy+1)) return false;
if(!c.plane(bxinv,-bxy*bxinv*byinv,(bxy*byz-by*bxz)*ivol,dx+1)) return false;
if(!c.plane(-bxinv,bxy*bxinv*byinv,(-bxy*byz+by*bxz)*ivol,-dx+1)) return false;
vol=c.volume()*ivol;
return true;
}
void unitcell::images(std::vector<int> &vi,std::vector<double> &vd) {
const int ms2=max_unit_voro_shells*2+1,mss=ms2*ms2*ms2;
bool *a=new bool[mss],*ac=a+max_unit_voro_shells*(1+ms2*(1+ms2)),*ap=a;
int i,j,k;
double vol;
while(ap<ac) *(ap++)=true;
*(ap++)=false;
while(ap<a+mss) *(ap++)=true;
std::queue<int> q;
q.push(0);q.push(0);q.push(0);
while(!q.empty()) {
i=q.front();q.pop();
j=q.front();q.pop();
k=q.front();q.pop();
if(intersects_image(i,j,k,vol)) {
vi.push_back(i);
vi.push_back(j);
vi.push_back(k);
vd.push_back(vol);
ap=ac+i+ms2*(j+ms2*k);
if(k>-max_unit_voro_shells&&*(ap-ms2*ms2)) {q.push(i);q.push(j);q.push(k-1);*(ap-ms2*ms2)=false;}
if(j>-max_unit_voro_shells&&*(ap-ms2)) {q.push(i);q.push(j-1);q.push(k);*(ap-ms2)=false;}
if(i>-max_unit_voro_shells&&*(ap-1)) {q.push(i-1);q.push(j);q.push(k);*(ap-1)=false;}
if(i<max_unit_voro_shells&&*(ap+1)) {q.push(i+1);q.push(j);q.push(k);*(ap+1)=false;}
if(j<max_unit_voro_shells&&*(ap+ms2)) {q.push(i);q.push(j+1);q.push(k);*(ap+ms2)=false;}
if(k<max_unit_voro_shells&&*(ap+ms2*ms2)) {q.push(i);q.push(j);q.push(k+1);*(ap+ms2*ms2)=false;}
}
}
delete [] a;
}
bool unitcell::unit_voro_intersect(int l) {
int i,j;
if(unit_voro_test(l,0,0)) return true;
for(i=1;i<l;i++) {
if(unit_voro_test(l,i,0)) return true;
if(unit_voro_test(-l,i,0)) return true;
}
for(i=-l;i<=l;i++) if(unit_voro_test(i,l,0)) return true;
for(i=1;i<l;i++) for(j=-l+1;j<=l;j++) {
if(unit_voro_test(l,j,i)) return true;
if(unit_voro_test(-j,l,i)) return true;
if(unit_voro_test(-l,-j,i)) return true;
if(unit_voro_test(j,-l,i)) return true;
}
for(i=-l;i<=l;i++) for(j=-l;j<=l;j++) if(unit_voro_test(i,j,l)) return true;
return false;
}
inline bool unitcell::unit_voro_test(int i,int j,int k) {
double x=i*bx+j*bxy+k*bxz,y=j*by+k*byz,z=k*bz;
double rsq=x*x+y*y+z*z;
return unit_voro.plane_intersects(x,y,z,rsq);
}
void unitcell::draw_domain_gnuplot(FILE *fp) {
fprintf(fp,"0 0 0\n%g 0 0\n%g %g 0\n%g %g 0\n",bx,bx+bxy,by,bxy,by);
fprintf(fp,"%g %g %g\n%g %g %g\n%g %g %g\n%g %g %g\n",bxy+bxz,by+byz,bz,bx+bxy+bxz,by+byz,bz,bx+bxz,byz,bz,bxz,byz,bz);
fprintf(fp,"0 0 0\n%g %g 0\n\n%g %g %g\n%g %g %g\n\n",bxy,by,bxz,byz,bz,bxy+bxz,by+byz,bz);
fprintf(fp,"%g 0 0\n%g %g %g\n\n%g %g 0\n%g %g %g\n\n",bx,bx+bxz,byz,bz,bx+bxy,by,bx+bxy+bxz,by+byz,bz);
}
void unitcell::draw_domain_pov(FILE *fp) {
fprintf(fp,"cylinder{0,0,0>,<%g,0,0>,rr}\n"
"cylinder{<%g,%g,0>,<%g,%g,0>,rr}\n",bx,bxy,by,bx+bxy,by);
fprintf(fp,"cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n"
"cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n",bxz,byz,bz,bx+bxz,byz,bz,bxy+bxz,by+byz,bz,bx+bxy+bxz,by+byz,bz);
fprintf(fp,"cylinder{<0,0,0>,<%g,%g,0>,rr}\n"
"cylinder{<%g,0,0>,<%g,%g,0>,rr}\n",bxy,by,bx,bx+bxy,by);
fprintf(fp,"cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n"
"cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n",bxz,byz,bz,bxy+bxz,by+byz,bz,bx+bxz,byz,bz,bx+bxy+bxz,by+byz,bz);
fprintf(fp,"cylinder{<0,0,0>,<%g,%g,%g>,rr}\n"
"cylinder{<%g,0,0>,<%g,%g,%g>,rr}\n",bxz,byz,bz,bx,bx+bxz,byz,bz);
fprintf(fp,"cylinder{<%g,%g,0>,<%g,%g,%g>,rr}\n"
"cylinder{<%g,%g,0>,<%g,%g,%g>,rr}\n",bxy,by,bxy+bxz,by+byz,bz,bx+bxy,by,bx+bxy+bxz,by+byz,bz);
fprintf(fp,"sphere{<0,0,0>,rr}\nsphere{<%g,0,0>,rr}\n"
"sphere{<%g,%g,0>,rr}\nsphere{<%g,%g,0>,rr}\n",bx,bxy,by,bx+bxy,by);
fprintf(fp,"sphere{<%g,%g,%g>,rr}\nsphere{<%g,%g,%g>,rr}\n"
"sphere{<%g,%g,%g>,rr}\nsphere{<%g,%g,%g>,rr}\n",bxz,byz,bz,bx+bxz,byz,bz,bxy+bxz,by+byz,bz,bx+bxy+bxz,by+byz,bz);
}
}