Page MenuHome

voro_explo_mod15.patch

voro_explo_mod15.patch

This file is larger than 256 KB, so syntax highlighting was skipped.
Index: release/scripts/startup/bl_ui/properties_data_modifier.py
===================================================================
--- release/scripts/startup/bl_ui/properties_data_modifier.py (revision 53656)
+++ release/scripts/startup/bl_ui/properties_data_modifier.py (working copy)
@@ -277,26 +277,42 @@
split.prop(md, "use_edge_sharp", text="Sharp Edges")
def EXPLODE(self, layout, ob, md):
+ layout.prop(md, "mode")
split = layout.split()
- col = split.column()
- col.label(text="Vertex group:")
- col.prop_search(md, "vertex_group", ob, "vertex_groups", text="")
- sub = col.column()
- sub.active = bool(md.vertex_group)
- sub.prop(md, "protect")
- col.label(text="Particle UV")
- col.prop_search(md, "particle_uv", ob.data, "uv_textures", text="")
+ if (md.mode == 'CELLS'):
+ col = split.column()
+ col.label("Point Source:")
+ col.prop(md, "point_source")
+ col.prop(md, "use_boolean")
+ if (md.use_boolean == True):
+ col.prop(md, "flip_normal")
+ col.prop(md, "inner_material")
+ if (md.refracture == False):
+ col.prop(md, "use_cache")
+ if (md.use_cache == False):
+ col.prop(md, "refracture")
+ col.prop(md, "emit_continuously")
+ if (md.emit_continuously == False):
+ col.prop(md, "map_delay")
+ elif (md.mode == 'FACES'):
+ col = split.column()
+ col.label(text="Vertex group:")
+ col.prop_search(md, "vertex_group", ob, "vertex_groups", text="")
+ sub = col.column()
+ sub.active = bool(md.vertex_group)
+ sub.prop(md, "protect")
+ col.label(text="Particle UV")
+ col.prop_search(md, "particle_uv", ob.data, "uv_textures", text="")
- col = split.column()
- col.prop(md, "use_edge_cut")
- col.prop(md, "show_unborn")
- col.prop(md, "show_alive")
- col.prop(md, "show_dead")
- col.prop(md, "use_size")
+ col = split.column()
+ col.prop(md, "use_edge_cut")
+ col.prop(md, "show_unborn")
+ col.prop(md, "show_alive")
+ col.prop(md, "show_dead")
+ col.prop(md, "use_size")
+ layout.operator("object.explode_refresh", text="Refresh")
- layout.operator("object.explode_refresh", text="Refresh")
-
def FLUID_SIMULATION(self, layout, ob, md):
layout.label(text="Settings can be found inside the Physics context")
@@ -782,6 +798,7 @@
sub = col.column(align=True)
sub.prop(md, "scale_x", text="Scale X")
sub.prop(md, "scale_y", text="Scale Y")
+
def WARP(self, layout, ob, md):
use_falloff = (md.falloff_type != 'NONE')
Index: source/creator/CMakeLists.txt
===================================================================
--- source/creator/CMakeLists.txt (revision 53656)
+++ source/creator/CMakeLists.txt (working copy)
@@ -969,6 +969,10 @@
if(WITH_INTERNATIONAL)
list(APPEND BLENDER_SORTED_LIBS bf_intern_locale)
endif()
+
+ if(WITH_MOD_VORONOI)
+ list(APPEND BLENDER_SORTED_LIBS extern_voro++)
+ endif()
if(WITH_BULLET AND NOT WITH_BULLET_SYSTEM)
list_insert_after(BLENDER_SORTED_LIBS "ge_logic_ngnetwork" "extern_bullet")
Index: source/blenderplayer/CMakeLists.txt
===================================================================
--- source/blenderplayer/CMakeLists.txt (revision 53656)
+++ source/blenderplayer/CMakeLists.txt (working copy)
@@ -195,6 +195,10 @@
if(WITH_INTERNATIONAL)
list(APPEND BLENDER_SORTED_LIBS bf_intern_locale)
endif()
+
+ if(WITH_MOD_VORONOI)
+ list(APPEND BLENDER_SORTED_LIBS extern_voro++)
+ endif()
foreach(SORTLIB ${BLENDER_SORTED_LIBS})
set(REMLIB ${SORTLIB})
Index: source/blender/modifiers/intern/MOD_explode.c
===================================================================
--- source/blender/modifiers/intern/MOD_explode.c (revision 53656)
+++ source/blender/modifiers/intern/MOD_explode.c (working copy)
@@ -22,7 +22,8 @@
* Ton Roosendaal,
* Ben Batt,
* Brecht Van Lommel,
- * Campbell Barton
+ * Campbell Barton,
+ * Martin Felke
*
* ***** END GPL LICENSE BLOCK *****
*
@@ -34,6 +35,7 @@
#include "DNA_meshdata_types.h"
+#include "DNA_mesh_types.h"
#include "DNA_scene_types.h"
#include "DNA_object_types.h"
@@ -57,19 +59,131 @@
#include "MOD_util.h"
+#include "MOD_boolean_util.h"
+#include "../../../../extern/voro++/src/c_interface.hh"
+#include "bmesh.h"
+#include "BLI_path_util.h"
+#include <ctype.h>
+#include "DNA_material_types.h"
+#include "BKE_material.h"
+#include "DNA_gpencil_types.h"
+#include "../../editors/include/ED_object.h"
+#include "BKE_global.h"
+#include "../../editors/include/ED_physics.h"
+#include "BKE_main.h"
+#include "BKE_library.h"
+
+void updateMesh(VoronoiCell* cell, Object* ob);
+
static void initData(ModifierData *md)
{
ExplodeModifierData *emd = (ExplodeModifierData *) md;
+ emd->mode = eFractureMode_Faces;
+ emd->use_boolean = FALSE;
+ emd->use_cache = MOD_VORONOI_USECACHE;
+ emd->refracture = FALSE;
+ emd->fracMesh = NULL;
+ emd->tempOb = NULL;
+ emd->cells = NULL;
+ emd->flip_normal = FALSE;
+
+ emd->last_part = 0;
+ emd->last_bool = FALSE;
+ emd->last_flip = FALSE;
+
emd->facepa = NULL;
+ emd->emit_continuously = FALSE;
emd->flag |= eExplodeFlag_Unborn + eExplodeFlag_Alive + eExplodeFlag_Dead;
+ emd->patree = NULL;
+ emd->map_delay = 1;
+ emd->last_map_delay = 1;
+ emd->inner_material = NULL;
+ emd->point_source = eOwnParticles;
+ emd->last_point_source = eOwnParticles;
}
+
+static void freeCells(ExplodeModifierData* emd)
+{
+ int c = 0;
+
+ if ((emd->cells) && (emd->mode == eFractureMode_Cells))
+ {
+ if (emd->cells->data)
+ {
+ for (c = 0; c < emd->cells->count; c++)
+ {
+ MEM_freeN(emd->cells->data[c].vertco);
+ emd->cells->data[c].vertco = NULL;
+ MEM_freeN(emd->cells->data[c].vertices);
+ emd->cells->data[c].vertices = NULL;
+ DM_release(emd->cells->data[c].cell_mesh);
+ MEM_freeN(emd->cells->data[c].cell_mesh);
+ emd->cells->data[c].cell_mesh = NULL;
+ }
+
+ MEM_freeN(emd->cells->data);
+ emd->cells->data = NULL;
+ }
+
+ MEM_freeN(emd->cells);
+ emd->cells = NULL;
+ }
+}
+
+#ifdef WITH_MOD_VORONOI
+
static void freeData(ModifierData *md)
{
ExplodeModifierData *emd = (ExplodeModifierData *) md;
+
+ freeCells(emd);
+
+ if ((emd->fracMesh) && (emd->mode == eFractureMode_Cells))
+ {
+ BM_mesh_free(emd->fracMesh);
+ emd->fracMesh = NULL;
+ }
+
+ if ((emd->tempOb) && (emd->mode == eFractureMode_Cells))
+ {
+ BKE_libblock_free_us(&(G.main->object), emd->tempOb);
+ BKE_object_unlink(emd->tempOb);
+ BKE_object_free(emd->tempOb);
+ emd->tempOb = NULL;
+ }
+
+ //if (emd->mode == eFractureMode_Faces)
+ {
+ if (emd->facepa) MEM_freeN(emd->facepa);
+ }
- if (emd->facepa) MEM_freeN(emd->facepa);
+ if (emd->patree)
+ {
+ BLI_kdtree_free(emd->patree);
+ emd->patree = NULL;
+ }
+
+ if (emd->inner_material)
+ { //will be freed by walk/foreachIDLink ?
+ emd->inner_material = NULL;
+ }
+
}
+
+#else
+
+static void freeData(ModifierData *md)
+{
+ ExplodeModifierData *emd = (ExplodeModifierData *) md;
+ if (emd->mode == eFractureMode_Faces)
+ {
+ if (emd->facepa) MEM_freeN(emd->facepa);
+ }
+}
+
+#endif
+
static void copyData(ModifierData *md, ModifierData *target)
{
ExplodeModifierData *emd = (ExplodeModifierData *) md;
@@ -79,7 +193,26 @@
temd->flag = emd->flag;
temd->protect = emd->protect;
temd->vgroup = emd->vgroup;
+
+ temd->mode = emd->mode;
+ temd->use_boolean = emd->use_boolean;
+ temd->fracMesh = emd->fracMesh;
+ temd->use_cache = emd->use_cache;
+ temd->refracture = emd->refracture;
+ temd->tempOb = emd->tempOb;
+ temd->cells = emd->cells;
+ temd->flip_normal = emd->flip_normal;
+ temd->last_part = emd->last_part;
+ temd->last_bool = emd->last_bool;
+ temd->last_flip = emd->last_flip;
+ temd->emit_continuously = emd->emit_continuously;
+ temd->map_delay = emd->map_delay;
+ temd->last_map_delay = emd->last_map_delay;
+ temd->inner_material = emd->inner_material;
+ temd->point_source = emd->point_source;
+ temd->last_point_source = emd->last_point_source;
}
+
static int dependsOnTime(ModifierData *UNUSED(md))
{
return 1;
@@ -989,6 +1122,888 @@
}
return psmd;
}
+
+static int dm_minmax(DerivedMesh* dm, float min[3], float max[3])
+{
+
+ int verts = dm->getNumVerts(dm);
+ MVert *mverts = dm->getVertArray(dm);
+ MVert *mvert;
+ int i = 0;
+
+ INIT_MINMAX(min, max);
+ for (i = 0; i < verts; i++) {
+ mvert = &mverts[i];
+ minmax_v3v3_v3(min, max, mvert->co);
+ }
+
+ return (verts != 0);
+}
+
+static int points_from_verts(Object* ob, int totobj, float** points, int p_exist)
+{
+ int v, o, pt = p_exist;
+ float co[3];
+
+ for (o = 0; o < totobj; o++)
+ {
+ if (ob[o].type == OB_MESH)
+ {
+ Mesh* me = (Mesh*)ob[o].data;
+ for (v = 0; v < me->totvert; v++)
+ {
+ *points = MEM_reallocN(*points, ((pt+1)*3)*sizeof(float));
+
+ co[0] = me->mvert[v].co[0];
+ co[1] = me->mvert[v].co[1];
+ co[2] = me->mvert[v].co[2];
+
+ mul_m4_v3(ob->obmat, co);
+
+ (*points)[pt*3] = co[0];
+ (*points)[pt*3+1] = co[1];
+ (*points)[pt*3+2] = co[2];
+ pt++;
+ }
+ }
+ }
+
+ return pt;
+}
+
+static int points_from_particles(Object* ob, int totobj, Scene* scene, float** points, int p_exist)
+{
+ int o, p, pt = p_exist;
+ ParticleSystemModifierData* psmd;
+ ParticleData* pa;
+ ParticleSimulationData sim = {NULL};
+ ParticleKey birth;
+ ModifierData* mod;
+
+ for (o = 0; o < totobj; o++)
+ {
+ for (mod = ob[o].modifiers.first; mod; mod = mod->next)
+ {
+ if (mod->type == eModifierType_ParticleSystem)
+ {
+ psmd = (ParticleSystemModifierData*)mod;
+ sim.scene = scene;
+ sim.ob = ob;
+ sim.psys = psmd->psys;
+ sim.psmd = psmd;
+
+ for (p = 0, pa = psmd->psys->particles; p < psmd->psys->totpart; p++, pa++)
+ {
+ psys_get_birth_coordinates(&sim, pa, &birth, 0, 0);
+ *points = MEM_reallocN(*points, ((pt+1)*3)* sizeof(float));
+ (*points)[pt*3] = birth.co[0];
+ (*points)[pt*3+1] = birth.co[1];
+ (*points)[pt*3+2] = birth.co[2];
+ pt++;
+ }
+ }
+ }
+ }
+
+ return pt;
+}
+
+static int points_from_greasepencil(Object* ob, int totobj, float** points, int p_exist)
+{
+ bGPDlayer* gpl;
+ bGPDframe* gpf;
+ bGPDstroke* gps;
+ int pt = p_exist, p, o;
+
+ for (o = 0; o < totobj; o++)
+ {
+ if ((ob[o].gpd) && (ob[o].gpd->layers.first))
+ {
+ for (gpl = ob[o].gpd->layers.first; gpl; gpl = gpl->next)
+ {
+ gpf = gpl->actframe;
+ for (gps = gpf->strokes.first; gps; gps = gps->next)
+ {
+ for (p = 0; p < gps->totpoints; p++)
+ {
+ *points = MEM_reallocN(*points, ((pt+1)*3)*sizeof(float));
+ (*points)[pt*3] = gps->points[p].x;
+ (*points)[pt*3+1] = gps->points[p].y;
+ (*points)[pt*3+2] = gps->points[p].z;
+ pt++;
+ }
+ }
+ }
+ }
+ }
+
+ return pt;
+}
+
+static int isChild(Object* ob, Object* child)
+{
+ Object *par;
+ if (child->parent && child->parent == ob)
+ {
+ return TRUE;
+ }
+
+ for (par = child->parent; par; par = par->parent) {
+ if (par == ob) {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+static int getChildren(Scene* scene, Object* ob, Object* children)
+{
+ Base* base;
+ int ctr = 0;
+
+ for (base = scene->base.first; base; base = base->next)
+ {
+ if (isChild(ob, base->object))
+ {
+ children = MEM_reallocN(children, sizeof(Object) * (ctr+1));
+ children[ctr] = *(base->object);
+ ctr++;
+ }
+ }
+
+ return ctr;
+}
+
+static int get_points(ExplodeModifierData *emd, Scene *scene, Object *ob, float **points)
+{
+ int totpoint = 0, totchildren = 0;
+ //int fallback = FALSE;
+ Object* children = NULL;
+
+ if (emd->point_source & (eChildParticles | eChildVerts ))
+ {
+ children = MEM_mallocN(sizeof(Object), "get_points->children");
+ totchildren += getChildren(scene, ob, children);
+ }
+
+ if (emd->point_source & eOwnParticles)
+ {
+ totpoint += points_from_particles(ob, 1, scene, points, totpoint);
+ }
+
+ if (emd->point_source & eChildParticles)
+ {
+ totpoint += points_from_particles(children, totchildren, scene, points , totpoint);
+ /*if ((totpoint == 0) && (!(emd->point_source & eOwnVerts)) && (!fallback))
+ { // if no child particles available, return original geometry
+ totpoint += points_from_verts(ob, 1, points, totpoint);
+ fallback = TRUE;
+ }*/
+ }
+
+ if (emd->point_source & eChildVerts)
+ {
+ totpoint += points_from_verts(children, totchildren, points, totpoint);
+ /*if ((totpoint == 0) && (!(emd->point_source & eOwnVerts)) && (!fallback))
+ { // if no childverts available, return original geometry
+ totpoint += points_from_verts(ob, 1, points, totpoint);
+ fallback = TRUE;
+ }*/
+ }
+
+ if (emd->point_source & eGreasePencil)
+ {
+ totpoint += points_from_greasepencil(ob, 1, points, totpoint);
+
+ /*if ((totpoint == 0) && (!(emd->point_source & eOwnVerts)) && (!fallback))
+ { // if no greasepencil available, return original geometry
+ totpoint += points_from_verts(ob, 1, points, totpoint);
+ fallback = TRUE;
+ }*/
+ }
+
+ if (emd->point_source & eOwnVerts)
+ {
+ totpoint += points_from_verts(ob, 1, points, totpoint);
+ }
+
+
+ if (children)
+ {
+ MEM_freeN(children);
+ children = NULL;
+ }
+
+ return totpoint;
+}
+
+
+
+// create the voronoi cell faces inside the existing mesh
+static BMesh* fractureToCells(Object *ob, DerivedMesh* derivedData, ParticleSystemModifierData* psmd, ExplodeModifierData* emd)
+{
+ void* container = NULL;
+ void* particle_order = NULL;
+ float min[3], max[3];
+ int p = 0;
+ ParticleData *pa = NULL;
+ //ParticleSimulationData sim = {NULL};
+ //ParticleKey birth;
+ float co[3], vco[3];//, cfra;
+ BMesh *bm = NULL, *bmtemp = NULL;
+
+ FILE *fp = NULL;
+ int vert_index = 0;
+ int read = 0;
+ BMVert **faceverts = NULL, **tempvert = NULL, *vert = NULL, **localverts = NULL;
+ BMEdge **faceedges = NULL, *edge = NULL;
+ int *facevert_indexes = NULL;
+ int face_index = 0;
+ int edge_index = 0;
+ char c;
+ int facevert_index = 0;
+ BMFace *face = NULL;
+
+ DerivedMesh *dm = NULL, *boolresult = NULL;
+ MEdge* ed = NULL;
+ MFace* fa = NULL;
+
+ int totvert, totedge, totface;
+ int v, e, f;
+ int tempvert_index;
+ const char *file;
+ char *path, *fullpath;
+ float imat[4][4];
+ float theta = 0.0f;
+ int n_size = 8;
+
+ float* points = NULL;
+ int totpoint = 0;
+
+ if (emd->use_boolean)
+ {
+ //theta = -0.01f;
+ //make container bigger for boolean case,so cube and container dont have equal size which can lead to boolean errors
+ if (emd->flip_normal)
+ { //cubes usually need flip_normal, so enable theta only here, otherwise it will be subtracted
+ theta = 0.01f;
+ }
+
+ INIT_MINMAX(min, max);
+ BKE_mesh_minmax(ob->data, min, max);
+ }
+ else
+ {
+ //con = voronoi.domain(xmin-theta,xmax+theta,ymin-theta,ymax+theta,zmin-theta,zmax+theta,nx,ny,nz,False, False, False, particles)
+ dm_minmax(derivedData, min, max);
+ }
+
+ //use global coordinates for container
+ mul_v3_m4v3(min, ob->obmat, min);
+ mul_v3_m4v3(max, ob->obmat, max);
+
+
+ // printf("Container: %f;%f;%f;%f;%f;%f \n", min[0], max[0], min[1], max[1], min[2], max[2]);
+ //TODO: maybe support simple shapes without boolean, but eh...
+ container = container_new(min[0]-theta, max[0]+theta, min[1]-theta, max[1]+theta, min[2]-theta, max[2]+theta,
+ n_size, n_size, n_size, FALSE, FALSE, FALSE, psmd->psys->totpart); //add number of parts here!
+ // particle_order = particle_order_new();
+
+
+ /* sim.scene = emd->modifier.scene;
+ sim.ob = ob;
+ sim.psys = psmd->psys;
+ sim.psmd = psmd;*/
+
+
+ //choose from point sources here
+ if (!emd->refracture)
+ {
+ points = MEM_mallocN(sizeof(float), "points");
+ totpoint = get_points(emd, emd->modifier.scene, ob, &points);
+
+ //no points, cant do anything
+ if (totpoint == 0) return DM_to_bmesh(derivedData);
+
+ if (emd->point_source == eOwnVerts)
+ {
+ //make container a little bigger ?
+ theta = 0.01f;
+ }
+ container = container_new(min[0]-theta, max[0]+theta, min[1]-theta, max[1]+theta, min[2]-theta, max[2]+theta,
+ n_size, n_size, n_size, FALSE, FALSE, FALSE, totpoint);
+
+ for (p = 0; p < totpoint; p++)
+ {
+ co[0] = points[p*3];
+ co[1] = points[p*3+1];
+ co[2] = points[p*3+2];
+ container_put(container, particle_order, p, co[0], co[1], co[2]);
+ }
+ }
+ else
+ {
+ container = container_new(min[0]-theta, max[0]+theta, min[1]-theta, max[1]+theta, min[2]-theta, max[2]+theta,
+ n_size, n_size, n_size, FALSE, FALSE, FALSE, psmd->psys->totpart);
+ for (p = 0, pa = psmd->psys->particles; p < psmd->psys->totpart; p++, pa++)
+ {
+ co[0] = pa->state.co[0];
+ co[1] = pa->state.co[1];
+ co[2] = pa->state.co[2];
+
+ /*else
+ {
+ psys_get_birth_coordinates(&sim, pa, &birth, 0, 0);
+ co[0] = birth.co[0];
+ co[1] = birth.co[1];
+ co[2] = birth.co[2];
+ }*/
+
+ // printf("Particle: %f, %f, %f \n", co[0], co[1], co[2]);
+
+ //use particle positions to fracture the object, tell those to the voronoi container
+ container_put(container, particle_order, p, co[0], co[1], co[2]);
+ }
+ }
+
+ //TODO: write results to temp file, ensure using the systems temp dir...
+ // this prints out vertex positions and face indexes
+ file = "test.out";
+ path = MEM_mallocN(((strlen(BLI_temporary_dir()) + strlen(file) + 2) * sizeof(char)), "path");
+ path = strcpy(path, BLI_temporary_dir());
+ fullpath = strcat(path, file);
+ fp = fopen(fullpath, "w+");
+
+
+ //this triggers computation of the voronoi cells and produces the desired data:
+ //see voro++ homepage for detailed description
+ //TODO: insert link here
+
+ // %P global vertex coordinates of voronoi vertices
+ // v the vertex -> face delimiter
+ // %t the indexes to the cell vertices, describes which vertices build each face
+ // f the face -> centroid section delimiter
+ // %C the centroid of the voronoi cell, used to find nearest particle in createCellpa
+
+ //%i the particle index
+ container_print_custom(container, "%P v %t f %C", fp);
+ fflush(fp);
+ rewind(fp);
+
+ if (points)
+ {
+ MEM_freeN(points);
+ points = NULL;
+ }
+
+
+ bm = DM_to_bmesh(derivedData);
+
+ //empty the mesh
+ BM_mesh_clear(bm);
+
+ //TODO: maybe put variable declarations at top of function ? C90 ?
+ //TODO: check for setting in psys to volume
+ //vert_index = 0;
+
+ if (emd->cells)
+ {
+ freeCells(emd);
+ }
+
+ emd->cells = MEM_mallocN(sizeof(VoronoiCells), "emd->cells");
+ emd->cells->data = MEM_mallocN(sizeof(VoronoiCell), "emd->cells->data");
+ emd->cells->count = 0;
+
+
+ while(feof(fp) == 0)
+ {
+ //printf("Reading line...\n");
+
+ //store cell data: centroid and associated vertex coords
+ emd->cells->data = MEM_reallocN(emd->cells->data, sizeof(VoronoiCell) * (emd->cells->count + 1));
+ emd->cells->data[emd->cells->count].vertices = MEM_mallocN(sizeof(BMVert*), "vertices");// (float*)malloc(3 * sizeof(float));
+ emd->cells->data[emd->cells->count].vertco = MEM_mallocN(sizeof(float), "vertco");
+ emd->cells->data[emd->cells->count].vertex_count = 0;
+ emd->cells->data[emd->cells->count].particle_index = -1;
+
+
+ bmtemp = BM_mesh_create(&bm_mesh_chunksize_default);
+ tempvert = MEM_mallocN(sizeof(BMVert*), "tempvert");
+ tempvert_index = 0;
+
+ // Read in the cell data, each line in the output file represents a voronoi cell
+ while (1)
+ {
+ // Read in the vertex coordinates of the cell vertices
+ read = fscanf(fp, "(%f,%f,%f) ", &vco[0], &vco[1], &vco[2]);
+ if (read < 3) break;
+
+ //back to object space
+
+ invert_m4_m4(imat, ob->obmat);
+ mul_v3_m4v3(vco, imat, vco);
+
+
+ tempvert = MEM_reallocN(tempvert, sizeof(BMVert*) * (tempvert_index + 1));
+ vert = BM_vert_create(bmtemp, vco, NULL, 0);
+ tempvert[tempvert_index] = vert;
+
+ tempvert_index++;
+ }
+
+ faceverts = MEM_mallocN(sizeof(BMVert*), "faceverts");
+ faceedges = MEM_mallocN(sizeof(BMEdge*), "faceedges");
+ facevert_indexes = MEM_mallocN(sizeof(int), "facevert_indexes");
+
+ face_index = 0;
+ edge_index = 0;
+
+ while(1)
+ {
+ // printf ("Reading faces...\n");
+ c = fgetc(fp);
+ if (isdigit(c)) //maybe atoi !! (ascii value to int ?)
+ {
+ //put/seek back and better do fscanf !
+ fseek(fp, -sizeof(char), SEEK_CUR);
+ facevert_index = 0;
+ fscanf(fp, "%d", &facevert_index);
+ faceverts = MEM_reallocN(faceverts, (face_index + 1) * sizeof(BMVert*));
+ // find vertices for each face, store indexes here
+ faceverts[face_index] = tempvert[facevert_index];//emd->cells->data[emd->cells->count].vertices[facevert_index];
+
+ facevert_indexes = MEM_reallocN(facevert_indexes, sizeof(int) * (face_index+1));
+ facevert_indexes[face_index] = facevert_index;
+
+ if (face_index > 0)
+ {
+ //argh, need to determine edges manually...
+ faceedges = MEM_reallocN(faceedges, (edge_index + 1) * sizeof(BMEdge*));
+ edge = BM_edge_create(bmtemp, faceverts[face_index - 1], faceverts[face_index], NULL, 0);
+ faceedges[edge_index] = edge;
+ edge_index++;
+
+ }
+
+ face_index++;
+
+ }
+ else if ( c == ')')
+ {
+ //end of face tuple, can create face now, but before create last edge to close the circle
+ faceedges = MEM_reallocN(faceedges, (edge_index + 1) * sizeof(BMEdge*));
+ edge = BM_edge_create(bmtemp, faceverts[face_index-1], faceverts[0], NULL, 0);
+ faceedges[edge_index] = edge;
+
+ face = BM_face_create(bmtemp, faceverts, faceedges, face_index, 0);
+ if (emd->flip_normal)
+ {
+ BM_face_normal_flip(bmtemp, face);
+ }
+
+ // MEM_freeN(faceedges);
+ // MEM_freeN(faceverts);
+ // MEM_freeN(facevert_indexes);
+ edge_index = 0;
+ face_index = 0;
+ //faceverts = MEM_mallocN(sizeof(BMVert*), "faceverts");
+ //faceedges = MEM_mallocN(sizeof(BMEdge*), "faceedges");
+ //facevert_indexes = MEM_mallocN(sizeof(int), "facevert_indexes");
+ }
+ else if ((c == 'f') || (feof(fp) != 0))
+ {
+ if (c == 'f')
+ {
+ //Intersection, use elements from temporary per-cell bmeshes and write to global bmesh, which
+ //is passed around and whose vertices are manipulated directly.
+ int mat_index = 0;
+ dm = CDDM_from_bmesh(bmtemp, TRUE);
+ BM_mesh_free(bmtemp);
+ bmtemp = NULL;
+
+ DM_ensure_tessface(derivedData);
+ CDDM_calc_edges_tessface(derivedData);
+ CDDM_tessfaces_to_faces(derivedData);
+ CDDM_calc_normals(derivedData);
+
+ DM_ensure_tessface(dm);
+ CDDM_calc_edges_tessface(dm);
+ CDDM_tessfaces_to_faces(dm);
+ CDDM_calc_normals(dm);
+
+ if (emd->use_boolean)
+ {
+ //put temp bmesh to temp object ? Necessary ? Seems so.
+ //TODO: maybe get along without temp object ?
+ if (!emd->tempOb)
+ {
+ emd->tempOb = BKE_object_add_only_object(OB_MESH, "Intersect");
+ //emd->tempOb = BKE_object_add(emd->modifier.scene, OB_MESH);
+ }
+
+ if (!emd->tempOb->data)
+ {
+ emd->tempOb->data = BKE_object_obdata_add_from_type(OB_MESH);
+ //object_add_material_slot(emd->tempOb);
+ }
+
+
+ //assign inner material to temp Object
+ if (emd->inner_material)
+ {
+ //assign inner material as secondary mat to ob if not there already
+ mat_index = find_material_index(ob, emd->inner_material);
+ if (mat_index == 0)
+ {
+ object_add_material_slot(ob);
+ assign_material(ob, emd->inner_material, ob->totcol, BKE_MAT_ASSIGN_OBDATA);
+ }
+
+ //shard gets inner material, maybe assign to all faces as well (in case this does not happen automatically
+ assign_material(emd->tempOb, emd->inner_material, 1, BKE_MAT_ASSIGN_OBDATA);
+ }
+
+ DM_to_mesh(dm, emd->tempOb->data, emd->tempOb);
+ copy_m4_m4(emd->tempOb->obmat, ob->obmat);
+
+ //CustomData_merge(&derivedData->faceData, &dm->faceData, CD_MASK_DERIVEDMESH & ~(CD_MASK_NORMAL | CD_MASK_ORIGINDEX), CD_DUPLICATE, derivedData->numTessFaceData);
+
+ boolresult = NewBooleanDerivedMesh(dm, emd->tempOb, derivedData, ob, eBooleanModifierOp_Intersect);
+
+ //if boolean fails, return original mesh, emit a warning
+ if (!boolresult)
+ {
+ boolresult = dm;
+ printf("Boolean Operation failed, using original mesh !\n");
+ }
+ else
+ {
+ DM_release(dm);
+ MEM_freeN(dm);
+ }
+ }
+ else
+ {
+ boolresult = dm;
+ }
+
+
+ //DM_ensure_tessface(boolresult);
+ CDDM_calc_edges_tessface(boolresult);
+ CDDM_tessfaces_to_faces(boolresult);
+ CDDM_calc_normals(boolresult);
+ DM_ensure_tessface(boolresult);
+
+ emd->cells->data[emd->cells->count].cell_mesh = boolresult;// CDDM_copy(boolresult);
+ //CustomData_merge(&boolresult->faceData, &emd->cells->data[emd->cells->count].cell_mesh->faceData,
+ // CD_MASK_DERIVEDMESH & ~(CD_MASK_NORMAL | CD_MASK_ORIGINDEX), CD_DUPLICATE, boolresult->numTessFaceData);
+
+ totvert = boolresult->getNumVerts(boolresult);
+ totedge = boolresult->getNumEdges(boolresult);
+ totface = boolresult->getNumTessFaces(boolresult);
+
+ localverts = MEM_mallocN(sizeof(BMVert*) * totvert, "localverts");
+ ed = boolresult->getEdgeArray(boolresult);
+ fa = boolresult->getTessFaceArray(boolresult);
+
+ for (v = 0; v < totvert; v++)
+ {
+ boolresult->getVertCo(boolresult, v, co);
+
+ emd->cells->data[emd->cells->count].vertices =
+ MEM_reallocN(emd->cells->data[emd->cells->count].vertices, (vert_index + 1)* sizeof(BMVert));
+
+ emd->cells->data[emd->cells->count].vertex_count++;
+
+
+ vert = BM_vert_create(bm, co, NULL, 0);
+ localverts[v] = vert;
+
+ emd->cells->data[emd->cells->count].vertices[vert_index] = vert;
+
+ //store original coordinates for later re-use
+
+ emd->cells->data[emd->cells->count].vertco =
+ MEM_reallocN(emd->cells->data[emd->cells->count].vertco, (vert_index + 1)* (3*sizeof(float)));
+
+ emd->cells->data[emd->cells->count].vertco[3*vert_index] = vert->co[0];
+ emd->cells->data[emd->cells->count].vertco[3*vert_index+1] = vert->co[1];
+ emd->cells->data[emd->cells->count].vertco[3*vert_index+2] = vert->co[2];
+
+ vert_index++;
+ }
+
+ for (e = 0; e < totedge; e++)
+ {
+ BM_edge_create(bm, localverts[ed[e].v1], localverts[ed[e].v2], NULL, 0);
+ }
+
+ for (f = 0; f < totface; f++)
+ {
+ if ((fa[f].v4 > 0) && (fa[f].v4 < totvert))
+ { //create quad
+ face = BM_face_create_quad_tri(bm, localverts[fa[f].v1], localverts[fa[f].v2], localverts[fa[f].v3], localverts[fa[f].v4], NULL, 0);
+ face->mat_nr = fa[f].mat_nr;
+
+ }
+ else
+ { //triangle only
+ face = BM_face_create_quad_tri(bm, localverts[fa[f].v1], localverts[fa[f].v2], localverts[fa[f].v3], NULL, NULL, 0);
+ face->mat_nr = fa[f].mat_nr;
+ }
+ }
+
+
+ MEM_freeN(localverts);
+ }
+
+ edge_index = 0;
+ face_index = 0;
+ // MEM_freeN(faceedges);
+ // MEM_freeN(faceverts);
+ // faceverts = MEM_mallocN(sizeof(BMVert*), "faceverts");
+ // faceedges = MEM_mallocN(sizeof(BMEdge*), "faceverts");
+ break;
+ }
+ }
+
+ //read centroid
+ fscanf(fp, " %f %f %f", &emd->cells->data[emd->cells->count].centroid[0],
+ &emd->cells->data[emd->cells->count].centroid[1],
+ &emd->cells->data[emd->cells->count].centroid[2]);
+
+ invert_m4_m4(imat, ob->obmat);
+ mul_m4_v3(imat, emd->cells->data[emd->cells->count].centroid);
+
+ //skip newline
+ if (feof(fp) == 0)
+ {
+
+#ifdef _WIN32
+ //skip \r\n
+ fseek(fp, 2*sizeof(char), SEEK_CUR);
+#else
+ //skip \n
+ fseek(fp, sizeof(char), SEEK_CUR);
+#endif
+ emd->cells->count++;
+ }
+ else
+ {
+ //EOF reached, free last vertco/vertices
+ MEM_freeN(emd->cells->data[emd->cells->count].vertco);
+ MEM_freeN(emd->cells->data[emd->cells->count].vertices);
+ }
+
+ vert_index = 0;
+ MEM_freeN(tempvert);
+ if (bmtemp) BM_mesh_free(bmtemp);
+ MEM_freeN(faceverts);
+ MEM_freeN(facevert_indexes);
+ MEM_freeN(faceedges);
+ }
+
+ fclose(fp);
+ MEM_freeN(path);
+
+ printf("%d cells missing\n", totpoint - emd->cells->count); //use totpoint here
+
+ return bm;
+}
+
+static void createParticleTree(ExplodeModifierData *emd, ParticleSystemModifierData *psmd, Scene* scene, Object* ob)
+{
+ ParticleSimulationData sim = {NULL};
+ ParticleSystem *psys = psmd->psys;
+ ParticleData *pa;
+ ParticleKey birth;
+ int p = 0, totpart = 0;
+
+ totpart = psys->totpart;
+ sim.scene = scene;
+ sim.ob = ob;
+ sim.psys = psmd->psys;
+ sim.psmd = psmd;
+
+ /* make tree of emitter locations */
+ if (emd->patree)
+ {
+ BLI_kdtree_free(emd->patree);
+ emd->patree = NULL;
+ }
+
+ emd->patree = BLI_kdtree_new(totpart);
+ for (p = 0, pa = psys->particles; p < totpart; p++, pa++)
+ {
+ if (emd->emit_continuously)
+ {
+ psys_get_birth_coordinates(&sim, pa, &birth, 0, 0);
+ BLI_kdtree_insert(emd->patree, p, birth.co, NULL);
+ }
+ else if (ELEM3(pa->alive, PARS_ALIVE, PARS_DYING, PARS_DEAD))
+ {
+ //psys_particle_on_emitter(psmd, psys->part->from, pa->num, pa->num_dmcache, pa->fuv, pa->foffset, co, NULL, NULL, NULL, NULL, NULL);
+ psys_get_birth_coordinates(&sim, pa, &birth, 0, 0);
+ BLI_kdtree_insert(emd->patree, p, birth.co, NULL);
+ }
+ }
+
+ BLI_kdtree_balance(emd->patree);
+}
+
+
+static void mapCellsToParticles(ExplodeModifierData *emd, ParticleSystemModifierData *psmd, Scene* scene, Object* ob)
+{
+ ParticleSystem *psys = psmd->psys;
+ float center[3];
+ int p = 0, c;
+ // if voronoi: need to set centroids of cells to nearest particle, apply same(?) matrix to a group of verts
+ float cfra;
+ cfra = BKE_scene_frame_get(scene);
+
+ for(c = 0; c < emd->cells->count; c++)
+ {
+ center[0] = emd->cells->data[c].centroid[0];
+ center[1] = emd->cells->data[c].centroid[1];
+ center[2] = emd->cells->data[c].centroid[2];
+
+ //centroids were stored in object space, go to global space (particles are in global space)
+ mul_m4_v3(ob->obmat, center);
+ p = BLI_kdtree_find_nearest(emd->patree, center, NULL, NULL);
+
+ if (emd->emit_continuously)
+ {
+ if (ELEM3(psys->particles[p].alive, PARS_ALIVE, PARS_DYING, PARS_DEAD))
+ {
+ emd->cells->data[c].particle_index = p;
+ }
+ else
+ {
+ emd->cells->data[c].particle_index = -1;
+ }
+ }
+ else
+ {
+ if ((emd->cells->data[c].particle_index == -1) && (cfra > (psys->part->sta + emd->map_delay)))
+ {
+ //map once, with delay, the larger the delay, the more smaller chunks !
+ emd->cells->data[c].particle_index = p;
+ }
+ }
+ }
+}
+
+static void explodeCells(ExplodeModifierData *emd,
+ ParticleSystemModifierData *psmd, Scene *scene, Object *ob)
+{
+ ParticleSimulationData sim = {NULL};
+ ParticleData *pa = NULL, *pars = psmd->psys->particles;
+ ParticleKey state, birth;
+ float imat[4][4];
+ float rot[4];
+ int totpart = 0;
+ int i, j, p;
+
+ totpart = psmd->psys->totpart;
+
+ sim.scene = scene;
+ sim.ob = ob;
+ sim.psys = psmd->psys;
+ sim.psmd = psmd;
+
+ if (emd->cells == NULL)
+ {
+ return;
+ }
+
+ /* getting back to object space */ // do i need this ?
+ invert_m4_m4(imat, ob->obmat);
+ psmd->psys->lattice = psys_get_lattice(&sim);
+
+ //create a new mesh aka "explode" from new vertex positions. use bmesh for that now.
+
+ //since we dont have real access to tessfaces we can deal with from our cells, need to recreate the edges
+ //and faces now the bmesh way. Or ... simply move the verts ? and recreate the DerivedMesh.
+
+ //but... need the Verts objects... hopefully global indexes are the same as in DerivedMesh, if not, need to deal with BMesh there too.
+
+ for (i = 0; i < emd->cells->count; i++)
+ {
+ p = emd->cells->data[i].particle_index;
+ if ((p >= 0) && (p < totpart))
+ {
+ pa = pars + p;
+ }
+
+ for (j = 0; j < emd->cells->data[i].vertex_count; j++)
+ {
+ // BMVert *vert = BM_vert_at_index(bm, ind);
+ BMVert* vert = emd->cells->data[i].vertices[j];
+
+ //reset to original coords // stored at fracture time
+ vert->co[0] = emd->cells->data[i].vertco[j*3];
+ vert->co[1] = emd->cells->data[i].vertco[j*3+1];
+ vert->co[2] = emd->cells->data[i].vertco[j*3+2];
+
+ if ((p < 0) || (p > totpart-1) || ((!emd->emit_continuously) && (pa->alive == PARS_UNBORN)))
+ {
+ continue;
+ }
+
+ //do recalc here ! what about uv maps and such.... ? let boolean (initially) handle this, but
+ //i HOPE vertex movement will update the uvs as well... or bust
+
+ //particle CACHE causes lots of problems with this kind of calculation.
+ psys_get_birth_coordinates(&sim, pa, &birth, 0, 0);
+ // psys_particle_on_emitter(psmd, psmd->psys->part->from, pa->num, pa->num_dmcache, pa->fuv, pa->foffset, co, NULL, NULL, NULL, NULL, NULL);
+
+ //birth.time = cfra;
+ //psys_get_particle_state(&sim, p, &birth, 1);
+ //birth = pa->prev_state;
+ state = pa->state;
+
+ //vertco = vert->co;
+ mul_m4_v3(ob->obmat, vert->co);
+ sub_v3_v3(vert->co, birth.co);
+
+ /* apply rotation, size & location */
+ //only if defined, means if checked in psys
+ if (psmd->psys->part->flag & PART_ROTATIONS)
+ {
+ sub_qt_qtqt(rot, state.rot, birth.rot);
+ mul_qt_v3(rot, vert->co);
+ }
+
+ //TODO: maybe apply size flag, alive / unborn / dead flags
+ // if (emd->flag & eExplodeFlag_PaSize)
+ // mul_v3_fl(vert->co, pa->size);
+
+ add_v3_v3(vert->co, state.co);
+
+ mul_m4_v3(imat, vert->co);
+
+ }
+ }
+
+ if (psmd->psys->lattice) {
+ end_latt_deform(psmd->psys->lattice);
+ psmd->psys->lattice = NULL;
+ }
+
+ //return bm;
+}
+
+static void resetCells(ExplodeModifierData *emd)
+{
+ int c;
+
+ for (c = 0; c < emd->cells->count; c++)
+ {
+ emd->cells->data[c].particle_index = -1;
+ }
+}
+
static DerivedMesh *applyModifier(ModifierData *md, Object *ob,
DerivedMesh *derivedData,
ModifierApplyFlag UNUSED(flag))
@@ -996,54 +2011,261 @@
DerivedMesh *dm = derivedData;
ExplodeModifierData *emd = (ExplodeModifierData *) md;
ParticleSystemModifierData *psmd = findPrecedingParticlesystem(ob, md);
+
+ DerivedMesh *result = NULL, *d = NULL;
+ int i = 0, j = 0, f, f_index = 0, ml_index = 0;
+ MTFace* mtface = NULL;
+ MTFace* mtf = NULL;
+ MTFace tf;
+ //MTFaces not used in BMesh/further modifier processing , so need to split to MTexPoly/MLoopUV
+ MTexPoly* mtps, mtp;
+ MLoopUV* mluvs, mluv;
+ MLoop* mla = NULL, *ml;
+ MPoly* mpa = NULL, *mp;
+ float imat[4][4], oldobmat[4][4];
+
+ if (psmd)
+ {
+ if (emd->mode == eFractureMode_Cells)
+ {
+#ifdef WITH_MOD_VORONOI
+
+ if ((emd->cells == NULL) ||
+ (emd->last_part != psmd->psys->totpart) ||
+ (emd->last_bool != emd->use_boolean) ||
+ (emd->last_flip != emd->flip_normal) ||
+ (emd->last_point_source != emd->point_source) ||
+ (emd->use_cache == FALSE))
+ {
+ invert_m4_m4(imat, ob->obmat);
+ copy_m4_m4(oldobmat, ob->obmat);
+ mult_m4_m4m4(ob->obmat, imat, ob->obmat); //neutralize obmat
+
+ if (emd->cells) BM_mesh_free(emd->fracMesh);
+ emd->fracMesh = fractureToCells(ob, derivedData, psmd, emd);
+
+ copy_m4_m4(ob->obmat, oldobmat); // restore obmat
+
+ emd->last_part = psmd->psys->totpart;
+ emd->last_bool = emd->use_boolean;
+ emd->last_flip = emd->flip_normal;
+ emd->last_point_source = emd->point_source;
+
+ }
+
+ if (emd->refracture)
+ {
+ if ((emd->cells == NULL) ||
+ (emd->last_part != psmd->psys->totpart) ||
+ (emd->last_bool != emd->use_boolean) ||
+ (emd->last_flip != emd->flip_normal) ||
+ (emd->last_point_source != emd->point_source) ||
+ (emd->use_cache == FALSE))
+ {
+ if (emd->fracMesh) BM_mesh_free(emd->fracMesh);
+ emd->fracMesh = fractureToCells(ob, derivedData, psmd, emd);
+ }
+
+ emd->last_part = psmd->psys->totpart;
+ emd->last_bool = emd->use_boolean;
+ emd->last_flip = emd->flip_normal;
+
+ result = CDDM_from_bmesh(emd->fracMesh, TRUE);
+ BM_mesh_free(emd->fracMesh);
+ emd->fracMesh = NULL;
+ return result;
+
+ }
+ else
+ {
+ //BM_mesh_copy(emd->fracMesh); loses some faces too, hrm.
+ if (emd->map_delay != emd->last_map_delay) resetCells(emd);
+ emd->last_map_delay = emd->map_delay;
+ createParticleTree(emd, psmd, md->scene, ob);
+ mapCellsToParticles(emd, psmd, md->scene, ob);
+ explodeCells(emd, psmd, md->scene, ob);
+ result = CDDM_from_bmesh(emd->fracMesh, TRUE);
+
+ DM_ensure_tessface(result);
+ CDDM_calc_edges_tessface(result);
+ CDDM_tessfaces_to_faces(result);
+ CDDM_calc_normals(result);
+
+ if (emd->use_boolean)
+ {
+ mtface = MEM_mallocN(sizeof(MTFace), "mtface");
+ mtps = MEM_mallocN(sizeof(MTexPoly), "mtps");
+ mluvs = MEM_mallocN(sizeof(MLoopUV), "mluvs");
+
+ f_index = 0;
+ ml_index = 0;
+ //Image* img;
+
+ for (i = 0; i < emd->cells->count; i++)
+ {
+ d = emd->cells->data[i].cell_mesh;
+ //f = d->numTessFaceData;
+
+ //if (mtf) MEM_freeN(mtf);
+ //hope this merges the MTFace data... successfully...
+ mtf = DM_get_tessface_data_layer(d, CD_MTFACE);
+ if (!mtf)
+ {
+ //argh, something went wrong, data will be missing...
+ MEM_freeN(mtface);
+ mtface = NULL;
+ break;
+ }
+
+
+ /*CDDM_calc_edges_tessface(d);
+ CDDM_tessfaces_to_faces(d);
+ CDDM_calc_normals(d);
+ DM_ensure_tessface(d);*/
+ //if (mpa) MEM_freeN(mpa);
+ //if (mla) MEM_freeN(mla);
+ mpa = d->getPolyArray(d);
+ mla = d->getLoopArray(d);
+
+ for (f = 0; f < d->numTessFaceData; f++)
+ {
+ mtface = MEM_reallocN(mtface, sizeof(MTFace) * (f_index+1));
+ tf = mtf[f];
+
+ //setting image with the crowbar, hrm...
+ /*if (tf.tpage)
+ {
+ img = tf.tpage;
+ }
+ if (!tf.tpage)
+ {
+ //free(mtface);
+ //mtface = NULL;
+ //break;
+ tf.tpage = img;
+ }*/
+ mtps = MEM_reallocN(mtps, sizeof(MTexPoly) * (f_index+1));
+ mtp.tpage = tf.tpage;
+ mtp.flag = tf.flag;
+ mtp.mode = tf.mode;
+ mtp.tile = tf.tile;
+ mtp.transp = tf.transp;
+ mtps[f_index] = mtp;
+
+ //assume facecount = polycount and faces = polys since only tris and quads available
+ //because of boolean
+
+ mp = mpa + f; // get Polygon according to face index
+ mluvs = MEM_reallocN(mluvs, sizeof(MLoopUV)* (mp->totloop + ml_index));
+
+ for (j = mp->loopstart; j < mp->loopstart + mp->totloop; j++)
+ {
+ //assume vertindex = uvindex, sigh, is that so ?, and no more than 4 loops !
+ ml = mla + j;
+ //mluv.flag = MLOOPUV_PINNED;//; MLOOPUV_EDGESEL;// MLOOPUV_VERTSEL;
+ mluv.uv[0] = tf.uv[j-mp->loopstart][0];
+ mluv.uv[1] = tf.uv[j-mp->loopstart][1];
+ mluvs[ml_index] = mluv;
+ ml_index++;
+ }
+
+ mtface[f_index] = tf;
+ f_index++;
+ }
+ //MEM_freeN(mtf);
+ //MEM_freeN(mla);
+ //MEM_freeN(mpa);
+
+ //if(!mtface)
+ // break;
+ }
+
+ if (mtface)
+ {
+ CustomData *fdata, *pdata, *ldata;
+ fdata = &result->faceData;
+ ldata = &result->loopData;
+ pdata = &result->polyData;
+
+ CustomData_add_layer(fdata, CD_MTFACE , CD_DUPLICATE, mtface, f_index);
+ CustomData_add_layer(pdata, CD_MTEXPOLY, CD_DUPLICATE, mtps, f_index);
+ CustomData_add_layer(ldata, CD_MLOOPUV, CD_DUPLICATE, mluvs, ml_index);
+
+ MEM_freeN(mtface);
+ MEM_freeN(mtps);
+ MEM_freeN(mluvs);
+ //MEM_freeN(mtf);
+ //MEM_freeN(mla);
+ //MEM_freeN(mpa);
+ }
+ }
+
+ return result;
+ }
+#else
+ emd->mode = eFractureMode_Faces;
+ return derivedData;
+#endif
+ }
- DM_ensure_tessface(dm); /* BMESH - UNTIL MODIFIER IS UPDATED FOR MPoly */
+
+ else if (emd->mode == eFractureMode_Faces)
+ {
+ ParticleSystem *psys = psmd->psys;
+ DM_ensure_tessface(dm); /* BMESH - UNTIL MODIFIER IS UPDATED FOR MPoly */
- if (psmd) {
- ParticleSystem *psys = psmd->psys;
+ if (psys == NULL || psys->totpart == 0) return derivedData;
+ if (psys->part == NULL || psys->particles == NULL) return derivedData;
+ if (psmd->dm == NULL) return derivedData;
- if (psys == NULL || psys->totpart == 0) return derivedData;
- if (psys->part == NULL || psys->particles == NULL) return derivedData;
- if (psmd->dm == NULL) return derivedData;
+ /* 1. find faces to be exploded if needed */
+ if (emd->facepa == NULL ||
+ psmd->flag & eParticleSystemFlag_Pars ||
+ emd->flag & eExplodeFlag_CalcFaces ||
+ MEM_allocN_len(emd->facepa) / sizeof(int) != dm->getNumTessFaces(dm))
+ {
+ if (psmd->flag & eParticleSystemFlag_Pars)
+ psmd->flag &= ~eParticleSystemFlag_Pars;
+
+ if (emd->flag & eExplodeFlag_CalcFaces)
+ emd->flag &= ~eExplodeFlag_CalcFaces;
- /* 1. find faces to be exploded if needed */
- if (emd->facepa == NULL ||
- psmd->flag & eParticleSystemFlag_Pars ||
- emd->flag & eExplodeFlag_CalcFaces ||
- MEM_allocN_len(emd->facepa) / sizeof(int) != dm->getNumTessFaces(dm))
- {
- if (psmd->flag & eParticleSystemFlag_Pars)
- psmd->flag &= ~eParticleSystemFlag_Pars;
-
- if (emd->flag & eExplodeFlag_CalcFaces)
- emd->flag &= ~eExplodeFlag_CalcFaces;
+ createFacepa(emd, psmd, derivedData);
+ }
+ /* 2. create new mesh */
+ if (emd->flag & eExplodeFlag_EdgeCut) {
+ int *facepa = emd->facepa;
+ DerivedMesh *splitdm = cutEdges(emd, dm);
+ DerivedMesh *explode = explodeMesh(emd, psmd, md->scene, ob, splitdm);
- createFacepa(emd, psmd, derivedData);
- }
- /* 2. create new mesh */
- if (emd->flag & eExplodeFlag_EdgeCut) {
- int *facepa = emd->facepa;
- DerivedMesh *splitdm = cutEdges(emd, dm);
- DerivedMesh *explode = explodeMesh(emd, psmd, md->scene, ob, splitdm);
-
- MEM_freeN(emd->facepa);
- emd->facepa = facepa;
- splitdm->release(splitdm);
- return explode;
- }
- else
- return explodeMesh(emd, psmd, md->scene, ob, derivedData);
+ MEM_freeN(emd->facepa);
+ emd->facepa = facepa;
+ splitdm->release(splitdm);
+ return explode;
+ }
+ else
+ return explodeMesh(emd, psmd, md->scene, ob, derivedData);
+ }
}
return derivedData;
}
+static void foreachIDLink(ModifierData *md, Object *ob,
+ IDWalkFunc walk, void *userData)
+{
+ ExplodeModifierData *emd = (ExplodeModifierData *) md;
+
+ walk(userData, ob, (ID **)&emd->inner_material);
+}
+
ModifierTypeInfo modifierType_Explode = {
/* name */ "Explode",
/* structName */ "ExplodeModifierData",
/* structSize */ sizeof(ExplodeModifierData),
/* type */ eModifierTypeType_Constructive,
- /* flags */ eModifierTypeFlag_AcceptsMesh,
+ /* flags */ eModifierTypeFlag_AcceptsMesh |
+ eModifierTypeFlag_Single, //more modifiers dont really make sense
/* copyData */ copyData,
/* deformVerts */ NULL,
/* deformMatrices */ NULL,
@@ -1059,6 +2281,6 @@
/* dependsOnTime */ dependsOnTime,
/* dependsOnNormals */ NULL,
/* foreachObjectLink */ NULL,
- /* foreachIDLink */ NULL,
+ /* foreachIDLink */ foreachIDLink,
/* foreachTexLink */ NULL,
};
Index: source/blender/modifiers/SConscript
===================================================================
--- source/blender/modifiers/SConscript (revision 53656)
+++ source/blender/modifiers/SConscript (working copy)
@@ -52,6 +52,10 @@
if env['WITH_BF_OCEANSIM']:
defs.append('WITH_OCEANSIM')
+if env['WITH_BF_VORONOI']:
+ incs += ' #/extern/voro++'
+ defs.append('WITH_MOD_VORONOI')
+
if env['WITH_BF_GAMEENGINE']:
incs += ' #/extern/recastnavigation'
defs.append('WITH_GAMEENGINE')
Index: source/blender/modifiers/CMakeLists.txt
===================================================================
--- source/blender/modifiers/CMakeLists.txt (revision 53656)
+++ source/blender/modifiers/CMakeLists.txt (working copy)
@@ -129,6 +129,13 @@
add_definitions(-DWITH_OCEANSIM)
endif()
+if(WITH_MOD_VORONOI)
+ add_definitions(-DWITH_MOD_VORONOI)
+ list(APPEND INC
+ ../../../extern/voro++
+ )
+endif()
+
if(WITH_GAMEENGINE)
# for MOD_navmesh.c
add_definitions(-DWITH_GAMEENGINE)
Index: source/blender/makesdna/DNA_modifier_types.h
===================================================================
--- source/blender/makesdna/DNA_modifier_types.h (revision 53656)
+++ source/blender/makesdna/DNA_modifier_types.h (working copy)
@@ -631,12 +631,67 @@
eExplodeFlag_Dead = (1<<5),
} ExplodeModifierFlag;
+typedef enum {
+ eFractureMode_Cells = 0,
+ eFractureMode_Faces = 1,
+} eFractureMode;
+
+enum {
+ MOD_VORONOI_USEBOOLEAN = (1 << 1),
+ MOD_VORONOI_REFRACTURE = (1 << 2),
+ MOD_VORONOI_USECACHE = (1 << 3),
+ MOD_VORONOI_FLIPNORMAL = (1 << 4),
+ MOD_VORONOI_EMITCONTINUOUSLY = (1 << 5)
+};
+
+typedef struct VoronoiCell {
+ struct BMVert **vertices;
+ float *vertco;
+ struct DerivedMesh *cell_mesh;
+ int vertex_count;
+ int particle_index;
+ float centroid[3];
+ char pad[4];
+} VoronoiCell;
+
+typedef struct VoronoiCells {
+ VoronoiCell *data;
+ int count;
+ char pad[4];
+} VoronoiCells;
+
+typedef enum {
+ eOwnVerts = (1 << 0),
+ eOwnParticles = (1 << 1),
+ eChildVerts = (1 << 2),
+ eChildParticles = (1 << 3),
+ eGreasePencil = (1 << 4),
+
+} eVoronoiPointSource;
+
typedef struct ExplodeModifierData {
ModifierData modifier;
- int *facepa;
+
+ //for voronoi cell mode
+ VoronoiCells *cells;
+ struct BMesh *fracMesh;
+ struct Object *tempOb;
+ struct KDTree *patree;
+ struct Material *inner_material;
+
+ //for face mode
+ int *facepa;
short flag, vgroup;
float protect;
- char uvname[64]; /* MAX_CUSTOMDATA_LAYER_NAME */
+ char uvname[64]; /* MAX_CUSTOMDATA_LAYER_NAME */
+
+ //for voronoi cell mode
+ int use_boolean, refracture, use_cache, flip_normal;
+ int last_part, last_bool, last_flip, emit_continuously;
+ int mode, map_delay, last_map_delay, point_source;
+ int last_point_source;
+ char pad[4];
+
} ExplodeModifierData;
typedef struct MultiresModifierData {
Index: source/blender/makesrna/intern/rna_modifier.c
===================================================================
--- source/blender/makesrna/intern/rna_modifier.c (revision 53656)
+++ source/blender/makesrna/intern/rna_modifier.c (working copy)
@@ -2120,7 +2120,23 @@
{
StructRNA *srna;
PropertyRNA *prop;
+
+ static EnumPropertyItem prop_mode_items[] = {
+ {eFractureMode_Cells, "CELLS", 0, "Voronoi Cells", "Fracture to voronoi cells and move them with particles"},
+ {eFractureMode_Faces, "FACES", 0, "Mesh Faces", "Move mesh faces with particles"},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ static EnumPropertyItem prop_point_source_items[] = {
+ {eOwnParticles, "OWN_PARTICLES", 0, "Own Particles", "Use own particles as point cloud"},
+ {eOwnVerts, "OWN_VERTS", 0, "Own Vertices", "Use own vertices as point cloud"},
+ {eChildParticles, "CHILD_PARTICLES", 0, "Child Particles", "Use particles of child objects as point cloud"},
+ {eChildVerts, "CHILD_VERTS", 0, "Child Vertices", "Use child vertices as point cloud"},
+ {eGreasePencil, "GREASE_PENCIL", 0, "Grease Pencil", "Use grease pencil points as point cloud"},
+ {0, NULL, 0, NULL, NULL}
+ };
+
srna = RNA_def_struct(brna, "ExplodeModifier", "Modifier");
RNA_def_struct_ui_text(srna, "Explode Modifier", "Explosion effect modifier based on a particle system");
RNA_def_struct_sdna(srna, "ExplodeModifierData");
@@ -2166,6 +2182,55 @@
RNA_def_property_string_maxlength(prop, MAX_CUSTOMDATA_LAYER_NAME);
RNA_def_property_ui_text(prop, "Particle UV", "UV map to change with particle age");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
+
+ prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, prop_mode_items);
+ RNA_def_property_ui_text(prop, "Mode", "Mode of fracture");
+ RNA_def_property_update(prop, 0, "rna_Modifier_update");
+
+ prop = RNA_def_property(srna, "point_source", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, prop_point_source_items);
+ RNA_def_property_flag(prop, PROP_ENUM_FLAG);
+ RNA_def_property_enum_default(prop, eOwnParticles);
+ RNA_def_property_ui_text(prop, "Point Source", "Source of point cloud");
+ RNA_def_property_update(prop, 0, "rna_Modifier_update");
+
+ prop = RNA_def_property(srna, "use_boolean", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "use_boolean", MOD_VORONOI_USEBOOLEAN);
+ RNA_def_property_ui_text(prop, "Use Boolean Intersection", "Intersect shards with original object shape");
+ RNA_def_property_update(prop, 0, "rna_Modifier_update");
+
+ prop = RNA_def_property(srna, "refracture", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "refracture", MOD_VORONOI_REFRACTURE);
+ RNA_def_property_ui_text(prop, "Keep Refracturing", "Refracture the object when particles move");
+ RNA_def_property_update(prop, 0, "rna_Modifier_update");
+
+ prop = RNA_def_property(srna, "use_cache", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "use_cache", MOD_VORONOI_USECACHE);
+ RNA_def_property_ui_text(prop, "Use Fracture Cache", "Store the fractured mesh in a cache for faster re-use");
+ RNA_def_property_update(prop, 0, "rna_Modifier_update");
+
+ prop = RNA_def_property(srna, "flip_normal", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flip_normal", MOD_VORONOI_FLIPNORMAL);
+ RNA_def_property_ui_text(prop, "Flip Normals", "Flip the normals when using boolean intersection, to possibly fix odd looking shapes");
+ RNA_def_property_update(prop, 0, "rna_Modifier_update");
+
+ prop = RNA_def_property(srna, "emit_continuously", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "emit_continuously", MOD_VORONOI_EMITCONTINUOUSLY);
+ RNA_def_property_ui_text(prop, "Emit Continuously", "Keep re-emitting the voronoi cells until all particles are dead");
+ RNA_def_property_update(prop, 0, "rna_Modifier_update");
+
+ prop = RNA_def_property(srna, "map_delay", PROP_INT, PROP_NONE);
+ RNA_def_property_range(prop, 0, 1000); //TODO: get correct psys end value here ?
+ RNA_def_property_ui_text(prop, "Map Delay", "Delay in frames after which the object is broken up intially ");
+ RNA_def_property_update(prop, 0, "rna_Modifier_update");
+
+ prop = RNA_def_property(srna, "inner_material", PROP_POINTER, PROP_NONE);
+ RNA_def_property_ui_text(prop, "Inner Material", "");
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_update(prop, 0, "rna_Modifier_update");
+
+
}
static void rna_def_modifier_cloth(BlenderRNA *brna)
Index: source/blender/blenloader/intern/readfile.c
===================================================================
--- source/blender/blenloader/intern/readfile.c (revision 53656)
+++ source/blender/blenloader/intern/readfile.c (working copy)
@@ -4632,6 +4632,13 @@
ExplodeModifierData *psmd = (ExplodeModifierData *)md;
psmd->facepa = NULL;
+
+ //should all be regenerated
+ psmd->fracMesh = NULL;
+ psmd->cells = NULL;
+ psmd->tempOb = NULL;
+ psmd->patree = NULL;
+ psmd->inner_material = NULL;
}
else if (md->type == eModifierType_MeshDeform) {
MeshDeformModifierData *mmd = (MeshDeformModifierData *)md;
Index: build_files/scons/tools/btools.py
===================================================================
--- build_files/scons/tools/btools.py (revision 53656)
+++ build_files/scons/tools/btools.py (working copy)
@@ -156,6 +156,7 @@
'WITH_BF_BOOLEAN',
'WITH_BF_REMESH',
'WITH_BF_OCEANSIM',
+ 'WITH_BF_VORONOI',
'WITH_BF_SMOKE',
'WITH_BF_CXX_GUARDEDALLOC',
'WITH_BF_JEMALLOC', 'WITH_BF_STATICJEMALLOC', 'BF_JEMALLOC', 'BF_JEMALLOC_INC', 'BF_JEMALLOC_LIBPATH', 'BF_JEMALLOC_LIB', 'BF_JEMALLOC_LIB_STATIC',
@@ -271,6 +272,7 @@
(BoolVariable('WITH_BF_REMESH', 'Build with remesh modifier', True)),
(BoolVariable('WITH_BF_OCEANSIM', 'Build with ocean simulation', False)),
(BoolVariable('WITH_BF_SMOKE', 'Build with smoke simulation', True)),
+ (BoolVariable('WITH_BF_VORONOI', 'Build with voronoi cells in explo modifier', True)),
('BF_PROFILE_FLAGS', 'Profiling compiler flags', ''),
(BoolVariable('WITH_BF_OPENAL', 'Use OpenAL if true', False)),
('BF_OPENAL', 'Base path for OpenAL', ''),
Index: extern/SConscript
===================================================================
--- extern/SConscript (revision 53656)
+++ extern/SConscript (working copy)
@@ -43,3 +43,6 @@
# FreeBSD doesn't seems to support XDND protocol
if env['OURPLATFORM'] in ('linux', 'openbsd3', 'sunos5', 'aix4', 'aix5'):
SConscript(['xdnd/SConscript'])
+
+if env ['WITH_BF_VORONOI']:
+ SConscript(['voro++/SConscript'])
Index: extern/voro++/LICENSE
===================================================================
--- extern/voro++/LICENSE (revision 0)
+++ extern/voro++/LICENSE (revision 0)
@@ -0,0 +1,39 @@
+Voro++ Copyright (c) 2008, The Regents of the University of California, through
+Lawrence Berkeley National Laboratory (subject to receipt of any required
+approvals from the U.S. Dept. of Energy). All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+(1) Redistributions of source code must retain the above copyright notice, this
+list of conditions and the following disclaimer.
+
+(2) Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimer in the documentation
+and/or other materials provided with the distribution.
+
+(3) Neither the name of the University of California, Lawrence Berkeley
+National Laboratory, U.S. Dept. of Energy nor the names of its contributors may
+be used to endorse or promote products derived from this software without
+specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+You are under no obligation whatsoever to provide any bug fixes, patches, or
+upgrades to the features, functionality or performance of the source code
+("Enhancements") to anyone; however, if you choose to make your Enhancements
+available either publicly, or directly to Lawrence Berkeley National
+Laboratory, without imposing a separate written license agreement for such
+Enhancements, then you hereby grant the following license: a non-exclusive,
+royalty-free perpetual license to install, use, modify, prepare derivative
+works, incorporate into other computer software, distribute, and sublicense
+such enhancements or derivative works thereof, in binary and source code form.
Index: extern/voro++/SConscript
===================================================================
--- extern/voro++/SConscript (revision 0)
+++ extern/voro++/SConscript (revision 0)
@@ -0,0 +1,12 @@
+#!/usr/bin/python
+
+import os
+Import ('env')
+
+sources = env.Glob('src/*.cc')
+sources.remove('src'+os.sep+'v_base_wl.cc')
+defs = []
+incs = ['src']
+
+
+env.BlenderLib ('extern_voro++', Split(sources), incs, defs, libtype=['extern'], priority=[40] )
Index: extern/voro++/src/container.hh
===================================================================
--- extern/voro++/src/container.hh (revision 0)
+++ extern/voro++/src/container.hh (revision 0)
@@ -0,0 +1,687 @@
+// Voro++, a 3D cell-based Voronoi library
+//
+// Author : Chris H. Rycroft (LBL / UC Berkeley)
+// Email : chr@alum.mit.edu
+// Date : August 30th 2011
+
+/** \file container.hh
+ * \brief Header file for the container_base and related classes. */
+
+#ifndef VOROPP_CONTAINER_HH
+#define VOROPP_CONTAINER_HH
+
+#include <cstdio>
+#include <vector>
+
+#include "config.hh"
+#include "common.hh"
+#include "v_base.hh"
+#include "cell.hh"
+#include "c_loops.hh"
+#include "v_compute.hh"
+#include "rad_option.hh"
+
+namespace voro {
+
+/** \brief Pure virtual class from which wall objects are derived.
+ *
+ * This is a pure virtual class for a generic wall object. A wall object
+ * can be specified by deriving a new class from this and specifying the
+ * functions.*/
+class wall {
+ public:
+ virtual ~wall() {}
+ /** A pure virtual function for testing whether a point is
+ * inside the wall object. */
+ virtual bool point_inside(double x,double y,double z) = 0;
+ /** A pure virtual function for cutting a cell without
+ * neighbor-tracking with a wall. */
+ virtual bool cut_cell(voronoicell &c,double x,double y,double z) = 0;
+ /** A pure virtual function for cutting a cell with
+ * neighbor-tracking enabled with a wall. */
+ virtual bool cut_cell(voronoicell_neighbor &c,double x,double y,double z) = 0;
+};
+
+/** \brief A class for storing a list of pointers to walls.
+ *
+ * This class stores a list of pointers to wall classes. It contains several
+ * simple routines that make use of the wall classes (such as telling whether a
+ * given position is inside all of the walls or not). It can be used by itself,
+ * but also forms part of container_base, for associating walls with this
+ * class. */
+class wall_list {
+ public:
+ /** An array holding pointers to wall objects. */
+ wall **walls;
+ /** A pointer to the next free position to add a wall pointer.
+ */
+ wall **wep;
+ wall_list();
+ ~wall_list();
+ /** Adds a wall to the list.
+ * \param[in] w the wall to add. */
+ inline void add_wall(wall *w) {
+ if(wep==wel) increase_wall_memory();
+ *(wep++)=w;
+ }
+ /** Adds a wall to the list.
+ * \param[in] w a reference to the wall to add. */
+ inline void add_wall(wall &w) {add_wall(&w);}
+ void add_wall(wall_list &wl);
+ /** Determines whether a given position is inside all of the
+ * walls on the list.
+ * \param[in] (x,y,z) the position to test.
+ * \return True if it is inside, false if it is outside. */
+ inline bool point_inside_walls(double x,double y,double z) {
+ for(wall **wp=walls;wp<wep;wp++) if(!((*wp)->point_inside(x,y,z))) return false;
+ return true;
+ }
+ /** Cuts a Voronoi cell by all of the walls currently on
+ * the list.
+ * \param[in] c a reference to the Voronoi cell class.
+ * \param[in] (x,y,z) the position of the cell.
+ * \return True if the cell still exists, false if the cell is
+ * deleted. */
+ template<class c_class>
+ bool apply_walls(c_class &c,double x,double y,double z) {
+ for(wall **wp=walls;wp<wep;wp++) if(!((*wp)->cut_cell(c,x,y,z))) return false;
+ return true;
+ }
+ void deallocate();
+ protected:
+ void increase_wall_memory();
+ /** A pointer to the limit of the walls array, used to
+ * determine when array is full. */
+ wall **wel;
+ /** The current amount of memory allocated for walls. */
+ int current_wall_size;
+};
+
+/** \brief Class for representing a particle system in a three-dimensional
+ * rectangular box.
+ *
+ * This class represents a system of particles in a three-dimensional
+ * rectangular box. Any combination of non-periodic and periodic coordinates
+ * can be used in the three coordinate directions. The class is not intended
+ * for direct use, but instead forms the base of the container and
+ * container_poly classes that add specialized routines for computing the
+ * regular and radical Voronoi tessellations respectively. It contains routines
+ * that are commonly between these two classes, such as those for drawing the
+ * domain, and placing particles within the internal data structure.
+ *
+ * The class is derived from the wall_list class, which encapsulates routines
+ * for associating walls with the container, and the voro_base class, which
+ * encapsulates routines about the underlying computational grid. */
+class container_base : public voro_base, public wall_list {
+ public:
+ /** The minimum x coordinate of the container. */
+ const double ax;
+ /** The maximum x coordinate of the container. */
+ const double bx;
+ /** The minimum y coordinate of the container. */
+ const double ay;
+ /** The maximum y coordinate of the container. */
+ const double by;
+ /** The minimum z coordinate of the container. */
+ const double az;
+ /** The maximum z coordinate of the container. */
+ const double bz;
+ /** A boolean value that determines if the x coordinate in
+ * periodic or not. */
+ const bool xperiodic;
+ /** A boolean value that determines if the y coordinate in
+ * periodic or not. */
+ const bool yperiodic;
+ /** A boolean value that determines if the z coordinate in
+ * periodic or not. */
+ const bool zperiodic;
+ /** This array holds the numerical IDs of each particle in each
+ * computational box. */
+ int **id;
+ /** A two dimensional array holding particle positions. For the
+ * derived container_poly class, this also holds particle
+ * radii. */
+ double **p;
+ /** This array holds the number of particles within each
+ * computational box of the container. */
+ int *co;
+ /** This array holds the maximum amount of particle memory for
+ * each computational box of the container. If the number of
+ * particles in a particular box ever approaches this limit,
+ * more is allocated using the add_particle_memory() function.
+ */
+ int *mem;
+ /** The amount of memory in the array structure for each
+ * particle. This is set to 3 when the basic class is
+ * initialized, so that the array holds (x,y,z) positions. If
+ * the container class is initialized as part of the derived
+ * class container_poly, then this is set to 4, to also hold
+ * the particle radii. */
+ const int ps;
+ container_base(double ax_,double bx_,double ay_,double by_,double az_,double bz_,
+ int nx_,int ny_,int nz_,bool xperiodic_,bool yperiodic_,bool zperiodic_,
+ int init_mem,int ps_);
+ ~container_base();
+ bool point_inside(double x,double y,double z);
+ void region_count();
+ /** Initializes the Voronoi cell prior to a compute_cell
+ * operation for a specific particle being carried out by a
+ * voro_compute class. The cell is initialized to fill the
+ * entire container. For non-periodic coordinates, this is set
+ * by the position of the walls. For periodic coordinates, the
+ * space is equally divided in either direction from the
+ * particle's initial position. Plane cuts made by any walls
+ * that have been added are then applied to the cell.
+ * \param[in,out] c a reference to a voronoicell object.
+ * \param[in] ijk the block that the particle is within.
+ * \param[in] q the index of the particle within its block.
+ * \param[in] (ci,cj,ck) the coordinates of the block in the
+ * container coordinate system.
+ * \param[out] (i,j,k) the coordinates of the test block
+ * relative to the voro_compute
+ * coordinate system.
+ * \param[out] (x,y,z) the position of the particle.
+ * \param[out] disp a block displacement used internally by the
+ * compute_cell routine.
+ * \return False if the plane cuts applied by walls completely
+ * removed the cell, true otherwise. */
+ template<class v_cell>
+ inline bool initialize_voronoicell(v_cell &c,int ijk,int q,int ci,int cj,int ck,
+ int &i,int &j,int &k,double &x,double &y,double &z,int &disp) {
+ double x1,x2,y1,y2,z1,z2,*pp=p[ijk]+ps*q;
+ x=*(pp++);y=*(pp++);z=*pp;
+ if(xperiodic) {x1=-(x2=0.5*(bx-ax));i=nx;} else {x1=ax-x;x2=bx-x;i=ci;}
+ if(yperiodic) {y1=-(y2=0.5*(by-ay));j=ny;} else {y1=ay-y;y2=by-y;j=cj;}
+ if(zperiodic) {z1=-(z2=0.5*(bz-az));k=nz;} else {z1=az-z;z2=bz-z;k=ck;}
+ c.init(x1,x2,y1,y2,z1,z2);
+ if(!apply_walls(c,x,y,z)) return false;
+ disp=ijk-i-nx*(j+ny*k);
+ return true;
+ }
+ /** Initializes parameters for a find_voronoi_cell call within
+ * the voro_compute template.
+ * \param[in] (ci,cj,ck) the coordinates of the test block in
+ * the container coordinate system.
+ * \param[in] ijk the index of the test block
+ * \param[out] (i,j,k) the coordinates of the test block
+ * relative to the voro_compute
+ * coordinate system.
+ * \param[out] disp a block displacement used internally by the
+ * find_voronoi_cell routine. */
+ inline void initialize_search(int ci,int cj,int ck,int ijk,int &i,int &j,int &k,int &disp) {
+ i=xperiodic?nx:ci;
+ j=yperiodic?ny:cj;
+ k=zperiodic?nz:ck;
+ disp=ijk-i-nx*(j+ny*k);
+ }
+ /** Returns the position of a particle currently being computed
+ * relative to the computational block that it is within. It is
+ * used to select the optimal worklist entry to use.
+ * \param[in] (x,y,z) the position of the particle.
+ * \param[in] (ci,cj,ck) the block that the particle is within.
+ * \param[out] (fx,fy,fz) the position relative to the block.
+ */
+ inline void frac_pos(double x,double y,double z,double ci,double cj,double ck,
+ double &fx,double &fy,double &fz) {
+ fx=x-ax-boxx*ci;
+ fy=y-ay-boxy*cj;
+ fz=z-az-boxz*ck;
+ }
+ /** Calculates the index of block in the container structure
+ * corresponding to given coordinates.
+ * \param[in] (ci,cj,ck) the coordinates of the original block
+ * in the current computation, relative
+ * to the container coordinate system.
+ * \param[in] (ei,ej,ek) the displacement of the current block
+ * from the original block.
+ * \param[in,out] (qx,qy,qz) the periodic displacement that
+ * must be added to the particles
+ * within the computed block.
+ * \param[in] disp a block displacement used internally by the
+ * find_voronoi_cell and compute_cell routines.
+ * \return The block index. */
+ inline int region_index(int ci,int cj,int ck,int ei,int ej,int ek,double &qx,double &qy,double &qz,int &disp) {
+ if(xperiodic) {if(ci+ei<nx) {ei+=nx;qx=-(bx-ax);} else if(ci+ei>=(nx<<1)) {ei-=nx;qx=bx-ax;} else qx=0;}
+ if(yperiodic) {if(cj+ej<ny) {ej+=ny;qy=-(by-ay);} else if(cj+ej>=(ny<<1)) {ej-=ny;qy=by-ay;} else qy=0;}
+ if(zperiodic) {if(ck+ek<nz) {ek+=nz;qz=-(bz-az);} else if(ck+ek>=(nz<<1)) {ek-=nz;qz=bz-az;} else qz=0;}
+ return disp+ei+nx*(ej+ny*ek);
+ }
+ void draw_domain_gnuplot(FILE *fp=stdout);
+ /** Draws an outline of the domain in Gnuplot format.
+ * \param[in] filename the filename to write to. */
+ inline void draw_domain_gnuplot(const char* filename) {
+ FILE *fp=safe_fopen(filename,"w");
+ draw_domain_gnuplot(fp);
+ fclose(fp);
+ }
+ void draw_domain_pov(FILE *fp=stdout);
+ /** Draws an outline of the domain in Gnuplot format.
+ * \param[in] filename the filename to write to. */
+ inline void draw_domain_pov(const char* filename) {
+ FILE *fp=safe_fopen(filename,"w");
+ draw_domain_pov(fp);
+ fclose(fp);
+ }
+ /** Sums up the total number of stored particles.
+ * \return The number of particles. */
+ inline int total_particles() {
+ int tp=*co;
+ for(int *cop=co+1;cop<co+nxyz;cop++) tp+=*cop;
+ return tp;
+ }
+ protected:
+ void add_particle_memory(int i);
+ inline bool put_locate_block(int &ijk,double &x,double &y,double &z);
+ inline bool put_remap(int &ijk,double &x,double &y,double &z);
+ inline bool remap(int &ai,int &aj,int &ak,int &ci,int &cj,int &ck,double &x,double &y,double &z,int &ijk);
+};
+
+/** \brief Extension of the container_base class for computing regular Voronoi
+ * tessellations.
+ *
+ * This class is an extension of the container_base class that has routines
+ * specifically for computing the regular Voronoi tessellation with no
+ * dependence on particle radii. */
+class container : public container_base, public radius_mono {
+ public:
+ container(double ax_,double bx_,double ay_,double by_,double az_,double bz_,
+ int nx_,int ny_,int nz_,bool xperiodic_,bool yperiodic_,bool zperiodic_,int init_mem);
+ void clear();
+ void put(int n,double x,double y,double z);
+ void put(particle_order &vo,int n,double x,double y,double z);
+ void import(FILE *fp=stdin);
+ void import(particle_order &vo,FILE *fp=stdin);
+ /** Imports a list of particles from an open file stream into
+ * the container. Entries of four numbers (Particle ID, x
+ * position, y position, z position) are searched for. If the
+ * file cannot be successfully read, then the routine causes a
+ * fatal error.
+ * \param[in] filename the name of the file to open and read
+ * from. */
+ inline void import(const char* filename) {
+ FILE *fp=safe_fopen(filename,"r");
+ import(fp);
+ fclose(fp);
+ }
+ /** Imports a list of particles from an open file stream into
+ * the container. Entries of four numbers (Particle ID, x
+ * position, y position, z position) are searched for. In
+ * addition, the order in which particles are read is saved
+ * into an ordering class. If the file cannot be successfully
+ * read, then the routine causes a fatal error.
+ * \param[in,out] vo the ordering class to use.
+ * \param[in] filename the name of the file to open and read
+ * from. */
+ inline void import(particle_order &vo,const char* filename) {
+ FILE *fp=safe_fopen(filename,"r");
+ import(vo,fp);
+ fclose(fp);
+ }
+ void compute_all_cells();
+ double sum_cell_volumes();
+ /** Dumps particle IDs and positions to a file.
+ * \param[in] vl the loop class to use.
+ * \param[in] fp a file handle to write to. */
+ template<class c_loop>
+ void draw_particles(c_loop &vl,FILE *fp) {
+ double *pp;
+ if(vl.start()) do {
+ pp=p[vl.ijk]+3*vl.q;
+ fprintf(fp,"%d %g %g %g\n",id[vl.ijk][vl.q],*pp,pp[1],pp[2]);
+ } while(vl.inc());
+ }
+ /** Dumps all of the particle IDs and positions to a file.
+ * \param[in] fp a file handle to write to. */
+ inline void draw_particles(FILE *fp=stdout) {
+ c_loop_all vl(*this);
+ draw_particles(vl,fp);
+ }
+ /** Dumps all of the particle IDs and positions to a file.
+ * \param[in] filename the name of the file to write to. */
+ inline void draw_particles(const char *filename) {
+ FILE *fp=safe_fopen(filename,"w");
+ draw_particles(fp);
+ fclose(fp);
+ }
+ /** Dumps particle positions in POV-Ray format.
+ * \param[in] vl the loop class to use.
+ * \param[in] fp a file handle to write to. */
+ template<class c_loop>
+ void draw_particles_pov(c_loop &vl,FILE *fp) {
+ double *pp;
+ if(vl.start()) do {
+ pp=p[vl.ijk]+3*vl.q;
+ fprintf(fp,"// id %d\nsphere{<%g,%g,%g>,s}\n",
+ id[vl.ijk][vl.q],*pp,pp[1],pp[2]);
+ } while(vl.inc());
+ }
+ /** Dumps all particle positions in POV-Ray format.
+ * \param[in] fp a file handle to write to. */
+ inline void draw_particles_pov(FILE *fp=stdout) {
+ c_loop_all vl(*this);
+ draw_particles_pov(vl,fp);
+ }
+ /** Dumps all particle positions in POV-Ray format.
+ * \param[in] filename the name of the file to write to. */
+ inline void draw_particles_pov(const char *filename) {
+ FILE *fp=safe_fopen(filename,"w");
+ draw_particles_pov(fp);
+ fclose(fp);
+ }
+ /** Computes Voronoi cells and saves the output in gnuplot
+ * format.
+ * \param[in] vl the loop class to use.
+ * \param[in] fp a file handle to write to. */
+ template<class c_loop>
+ void draw_cells_gnuplot(c_loop &vl,FILE *fp) {
+ voronoicell c;double *pp;
+ if(vl.start()) do if(compute_cell(c,vl)) {
+ pp=p[vl.ijk]+ps*vl.q;
+ c.draw_gnuplot(*pp,pp[1],pp[2],fp);
+ } while(vl.inc());
+ }
+ /** Computes all Voronoi cells and saves the output in gnuplot
+ * format.
+ * \param[in] fp a file handle to write to. */
+ inline void draw_cells_gnuplot(FILE *fp=stdout) {
+ c_loop_all vl(*this);
+ draw_cells_gnuplot(vl,fp);
+ }
+ /** Computes all Voronoi cells and saves the output in gnuplot
+ * format.
+ * \param[in] filename the name of the file to write to. */
+ inline void draw_cells_gnuplot(const char *filename) {
+ FILE *fp=safe_fopen(filename,"w");
+ draw_cells_gnuplot(fp);
+ fclose(fp);
+ }
+ /** Computes Voronoi cells and saves the output in POV-Ray
+ * format.
+ * \param[in] vl the loop class to use.
+ * \param[in] fp a file handle to write to. */
+ template<class c_loop>
+ void draw_cells_pov(c_loop &vl,FILE *fp) {
+ voronoicell c;double *pp;
+ if(vl.start()) do if(compute_cell(c,vl)) {
+ fprintf(fp,"// cell %d\n",id[vl.ijk][vl.q]);
+ pp=p[vl.ijk]+ps*vl.q;
+ c.draw_pov(*pp,pp[1],pp[2],fp);
+ } while(vl.inc());
+ }
+ /** Computes all Voronoi cells and saves the output in POV-Ray
+ * format.
+ * \param[in] fp a file handle to write to. */
+ inline void draw_cells_pov(FILE *fp=stdout) {
+ c_loop_all vl(*this);
+ draw_cells_pov(vl,fp);
+ }
+ /** Computes all Voronoi cells and saves the output in POV-Ray
+ * format.
+ * \param[in] filename the name of the file to write to. */
+ inline void draw_cells_pov(const char *filename) {
+ FILE *fp=safe_fopen(filename,"w");
+ draw_cells_pov(fp);
+ fclose(fp);
+ }
+ /** Computes the Voronoi cells and saves customized information
+ * about them.
+ * \param[in] vl the loop class to use.
+ * \param[in] format the custom output string to use.
+ * \param[in] fp a file handle to write to. */
+ template<class c_loop>
+ void print_custom(c_loop &vl,const char *format,FILE *fp) {
+ int ijk,q;double *pp;
+ if(contains_neighbor(format)) {
+ voronoicell_neighbor c;
+ if(vl.start()) do if(compute_cell(c,vl)) {
+ ijk=vl.ijk;q=vl.q;pp=p[ijk]+ps*q;
+ c.output_custom(format,id[ijk][q],*pp,pp[1],pp[2],default_radius,fp);
+ } while(vl.inc());
+ } else {
+ voronoicell c;
+ if(vl.start()) do if(compute_cell(c,vl)) {
+ ijk=vl.ijk;q=vl.q;pp=p[ijk]+ps*q;
+ c.output_custom(format,id[ijk][q],*pp,pp[1],pp[2],default_radius,fp);
+ } while(vl.inc());
+ }
+ }
+ void print_custom(const char *format,FILE *fp=stdout);
+ void print_custom(const char *format,const char *filename);
+ bool find_voronoi_cell(double x,double y,double z,double &rx,double &ry,double &rz,int &pid);
+ /** Computes the Voronoi cell for a particle currently being
+ * referenced by a loop class.
+ * \param[out] c a Voronoi cell class in which to store the
+ * computed cell.
+ * \param[in] vl the loop class to use.
+ * \return True if the cell was computed. If the cell cannot be
+ * computed, if it is removed entirely by a wall or boundary
+ * condition, then the routine returns false. */
+ template<class v_cell,class c_loop>
+ inline bool compute_cell(v_cell &c,c_loop &vl) {
+ return vc.compute_cell(c,vl.ijk,vl.q,vl.i,vl.j,vl.k);
+ }
+ /** Computes the Voronoi cell for given particle.
+ * \param[out] c a Voronoi cell class in which to store the
+ * computed cell.
+ * \param[in] ijk the block that the particle is within.
+ * \param[in] q the index of the particle within the block.
+ * \return True if the cell was computed. If the cell cannot be
+ * computed, if it is removed entirely by a wall or boundary
+ * condition, then the routine returns false. */
+ template<class v_cell>
+ inline bool compute_cell(v_cell &c,int ijk,int q) {
+ int k=ijk/nxy,ijkt=ijk-nxy*k,j=ijkt/nx,i=ijkt-j*nx;
+ return vc.compute_cell(c,ijk,q,i,j,k);
+ }
+ private:
+ voro_compute<container> vc;
+ friend class voro_compute<container>;
+};
+
+/** \brief Extension of the container_base class for computing radical Voronoi
+ * tessellations.
+ *
+ * This class is an extension of container_base class that has routines
+ * specifically for computing the radical Voronoi tessellation that depends on
+ * the particle radii. */
+class container_poly : public container_base, public radius_poly {
+ public:
+ container_poly(double ax_,double bx_,double ay_,double by_,double az_,double bz_,
+ int nx_,int ny_,int nz_,bool xperiodic_,bool yperiodic_,bool zperiodic_,int init_mem);
+ void clear();
+ void put(int n,double x,double y,double z,double r);
+ void put(particle_order &vo,int n,double x,double y,double z,double r);
+ void import(FILE *fp=stdin);
+ void import(particle_order &vo,FILE *fp=stdin);
+ /** Imports a list of particles from an open file stream into
+ * the container_poly class. Entries of five numbers (Particle
+ * ID, x position, y position, z position, radius) are searched
+ * for. If the file cannot be successfully read, then the
+ * routine causes a fatal error.
+ * \param[in] filename the name of the file to open and read
+ * from. */
+ inline void import(const char* filename) {
+ FILE *fp=safe_fopen(filename,"r");
+ import(fp);
+ fclose(fp);
+ }
+ /** Imports a list of particles from an open file stream into
+ * the container_poly class. Entries of five numbers (Particle
+ * ID, x position, y position, z position, radius) are searched
+ * for. In addition, the order in which particles are read is
+ * saved into an ordering class. If the file cannot be
+ * successfully read, then the routine causes a fatal error.
+ * \param[in,out] vo the ordering class to use.
+ * \param[in] filename the name of the file to open and read
+ * from. */
+ inline void import(particle_order &vo,const char* filename) {
+ FILE *fp=safe_fopen(filename,"r");
+ import(vo,fp);
+ fclose(fp);
+ }
+ void compute_all_cells();
+ double sum_cell_volumes();
+ /** Dumps particle IDs, positions and radii to a file.
+ * \param[in] vl the loop class to use.
+ * \param[in] fp a file handle to write to. */
+ template<class c_loop>
+ void draw_particles(c_loop &vl,FILE *fp) {
+ double *pp;
+ if(vl.start()) do {
+ pp=p[vl.ijk]+4*vl.q;
+ fprintf(fp,"%d %g %g %g %g\n",id[vl.ijk][vl.q],*pp,pp[1],pp[2],pp[3]);
+ } while(vl.inc());
+ }
+ /** Dumps all of the particle IDs, positions and radii to a
+ * file.
+ * \param[in] fp a file handle to write to. */
+ inline void draw_particles(FILE *fp=stdout) {
+ c_loop_all vl(*this);
+ draw_particles(vl,fp);
+ }
+ /** Dumps all of the particle IDs, positions and radii to a
+ * file.
+ * \param[in] filename the name of the file to write to. */
+ inline void draw_particles(const char *filename) {
+ FILE *fp=safe_fopen(filename,"w");
+ draw_particles(fp);
+ fclose(fp);
+ }
+ /** Dumps particle positions in POV-Ray format.
+ * \param[in] vl the loop class to use.
+ * \param[in] fp a file handle to write to. */
+ template<class c_loop>
+ void draw_particles_pov(c_loop &vl,FILE *fp) {
+ double *pp;
+ if(vl.start()) do {
+ pp=p[vl.ijk]+4*vl.q;
+ fprintf(fp,"// id %d\nsphere{<%g,%g,%g>,%g}\n",
+ id[vl.ijk][vl.q],*pp,pp[1],pp[2],pp[3]);
+ } while(vl.inc());
+ }
+ /** Dumps all the particle positions in POV-Ray format.
+ * \param[in] fp a file handle to write to. */
+ inline void draw_particles_pov(FILE *fp=stdout) {
+ c_loop_all vl(*this);
+ draw_particles_pov(vl,fp);
+ }
+ /** Dumps all the particle positions in POV-Ray format.
+ * \param[in] filename the name of the file to write to. */
+ inline void draw_particles_pov(const char *filename) {
+ FILE *fp=safe_fopen(filename,"w");
+ draw_particles_pov(fp);
+ fclose(fp);
+ }
+ /** Computes Voronoi cells and saves the output in gnuplot
+ * format.
+ * \param[in] vl the loop class to use.
+ * \param[in] fp a file handle to write to. */
+ template<class c_loop>
+ void draw_cells_gnuplot(c_loop &vl,FILE *fp) {
+ voronoicell c;double *pp;
+ if(vl.start()) do if(compute_cell(c,vl)) {
+ pp=p[vl.ijk]+ps*vl.q;
+ c.draw_gnuplot(*pp,pp[1],pp[2],fp);
+ } while(vl.inc());
+ }
+ /** Compute all Voronoi cells and saves the output in gnuplot
+ * format.
+ * \param[in] fp a file handle to write to. */
+ inline void draw_cells_gnuplot(FILE *fp=stdout) {
+ c_loop_all vl(*this);
+ draw_cells_gnuplot(vl,fp);
+ }
+ /** Compute all Voronoi cells and saves the output in gnuplot
+ * format.
+ * \param[in] filename the name of the file to write to. */
+ inline void draw_cells_gnuplot(const char *filename) {
+ FILE *fp=safe_fopen(filename,"w");
+ draw_cells_gnuplot(fp);
+ fclose(fp);
+ }
+ /** Computes Voronoi cells and saves the output in POV-Ray
+ * format.
+ * \param[in] vl the loop class to use.
+ * \param[in] fp a file handle to write to. */
+ template<class c_loop>
+ void draw_cells_pov(c_loop &vl,FILE *fp) {
+ voronoicell c;double *pp;
+ if(vl.start()) do if(compute_cell(c,vl)) {
+ fprintf(fp,"// cell %d\n",id[vl.ijk][vl.q]);
+ pp=p[vl.ijk]+ps*vl.q;
+ c.draw_pov(*pp,pp[1],pp[2],fp);
+ } while(vl.inc());
+ }
+ /** Computes all Voronoi cells and saves the output in POV-Ray
+ * format.
+ * \param[in] fp a file handle to write to. */
+ inline void draw_cells_pov(FILE *fp=stdout) {
+ c_loop_all vl(*this);
+ draw_cells_pov(vl,fp);
+ }
+ /** Computes all Voronoi cells and saves the output in POV-Ray
+ * format.
+ * \param[in] filename the name of the file to write to. */
+ inline void draw_cells_pov(const char *filename) {
+ FILE *fp=safe_fopen(filename,"w");
+ draw_cells_pov(fp);
+ fclose(fp);
+ }
+ /** Computes the Voronoi cells and saves customized information
+ * about them.
+ * \param[in] vl the loop class to use.
+ * \param[in] format the custom output string to use.
+ * \param[in] fp a file handle to write to. */
+ template<class c_loop>
+ void print_custom(c_loop &vl,const char *format,FILE *fp) {
+ int ijk,q;double *pp;
+ if(contains_neighbor(format)) {
+ voronoicell_neighbor c;
+ if(vl.start()) do if(compute_cell(c,vl)) {
+ ijk=vl.ijk;q=vl.q;pp=p[ijk]+ps*q;
+ c.output_custom(format,id[ijk][q],*pp,pp[1],pp[2],pp[3],fp);
+ } while(vl.inc());
+ } else {
+ voronoicell c;
+ if(vl.start()) do if(compute_cell(c,vl)) {
+ ijk=vl.ijk;q=vl.q;pp=p[ijk]+ps*q;
+ c.output_custom(format,id[ijk][q],*pp,pp[1],pp[2],pp[3],fp);
+ } while(vl.inc());
+ }
+ }
+ /** Computes the Voronoi cell for a particle currently being
+ * referenced by a loop class.
+ * \param[out] c a Voronoi cell class in which to store the
+ * computed cell.
+ * \param[in] vl the loop class to use.
+ * \return True if the cell was computed. If the cell cannot be
+ * computed, if it is removed entirely by a wall or boundary
+ * condition, then the routine returns false. */
+ template<class v_cell,class c_loop>
+ inline bool compute_cell(v_cell &c,c_loop &vl) {
+ return vc.compute_cell(c,vl.ijk,vl.q,vl.i,vl.j,vl.k);
+ }
+ /** Computes the Voronoi cell for given particle.
+ * \param[out] c a Voronoi cell class in which to store the
+ * computed cell.
+ * \param[in] ijk the block that the particle is within.
+ * \param[in] q the index of the particle within the block.
+ * \return True if the cell was computed. If the cell cannot be
+ * computed, if it is removed entirely by a wall or boundary
+ * condition, then the routine returns false. */
+ template<class v_cell>
+ inline bool compute_cell(v_cell &c,int ijk,int q) {
+ int k=ijk/nxy,ijkt=ijk-nxy*k,j=ijkt/nx,i=ijkt-j*nx;
+ return vc.compute_cell(c,ijk,q,i,j,k);
+ }
+ void print_custom(const char *format,FILE *fp=stdout);
+ void print_custom(const char *format,const char *filename);
+ bool find_voronoi_cell(double x,double y,double z,double &rx,double &ry,double &rz,int &pid);
+ private:
+ voro_compute<container_poly> vc;
+ friend class voro_compute<container_poly>;
+};
+
+}
+
+#endif
Index: extern/voro++/src/Doxyfile
===================================================================
--- extern/voro++/src/Doxyfile (revision 0)
+++ extern/voro++/src/Doxyfile (revision 0)
@@ -0,0 +1,1793 @@
+# Doxyfile 1.8.1.1
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or sequence of words) that should
+# identify the project. Note that if you do not use Doxywizard you need
+# to put quotes around the project name if it contains spaces.
+
+PROJECT_NAME = Voro++
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER =
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer
+# a quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF =
+
+# With the PROJECT_LOGO tag one can specify an logo or icon that is
+# included in the documentation. The maximum height of the logo should not
+# exceed 55 pixels and the maximum width should not exceed 200 pixels.
+# Doxygen will copy the logo to the output directory.
+
+PROJECT_LOGO =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = ..
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
+# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak,
+# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = NO
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful if your file system
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 8
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES =
+
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding
+# "class=itcl::class" will allow you to use the command class in the
+# itcl::class meaning.
+
+TCL_SUBST =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given extension.
+# Doxygen has a built-in mapping, but you can override or extend it using this
+# tag. The format is ext=language, where ext is a file extension, and language
+# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C,
+# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make
+# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
+# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions
+# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all
+# comments according to the Markdown format, which allows for more readable
+# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you
+# can mix doxygen, HTML, and XML commands with Markdown formatting.
+# Disable only in case of backward compatibilities issues.
+
+MARKDOWN_SUPPORT = YES
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also makes the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT = YES
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter
+# and setter methods for a property. Setting this option to YES (the default)
+# will make doxygen replace the get and set methods by a property in the
+# documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and
+# unions are shown inside the group in which they are included (e.g. using
+# @ingroup) instead of on a separate page (for HTML and Man pages) or
+# section (for LaTeX and RTF).
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and
+# unions with only public data fields will be shown inline in the documentation
+# of the scope in which they are defined (i.e. file, namespace, or group
+# documentation), provided this scope is documented. If set to NO (the default),
+# structs, classes, and unions are shown on a separate page (for HTML and Man
+# pages) or section (for LaTeX and RTF).
+
+INLINE_SIMPLE_STRUCTS = NO
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penalty.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will roughly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols.
+
+SYMBOL_CACHE_SIZE = 0
+
+# Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be
+# set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given
+# their name and scope. Since this can be an expensive process and often the
+# same symbol appear multiple times in the code, doxygen keeps a cache of
+# pre-resolved symbols. If the cache is too small doxygen will become slower.
+# If the cache is too large, memory is wasted. The cache size is given by this
+# formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols.
+
+LOOKUP_CACHE_SIZE = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal scope will be included in the documentation.
+
+EXTRACT_PACKAGE = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespaces are hidden.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES = NO
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
+# will list include files with double quotes in the documentation
+# rather than with sharp brackets.
+
+FORCE_LOCAL_INCLUDES = NO
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
+# will sort the (brief and detailed) documentation of class members so that
+# constructors and destructors are listed first. If set to NO (the default)
+# the constructors will appear in the respective orders defined by
+# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
+# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
+# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to
+# do proper type resolution of all parameters of a function it will reject a
+# match between the prototype and the implementation of a member function even
+# if there is only one candidate or it is obvious which candidate to choose
+# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen
+# will still accept a match between prototype and implementation in such cases.
+
+STRICT_PROTO_MATCHING = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or macro consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and macros in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page.
+# This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option.
+# You can optionally specify a file name after the option, if omitted
+# DoxygenLayout.xml will be used as the name of the layout file.
+
+LAYOUT_FILE =
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files
+# containing the references data. This must be a list of .bib files. The
+# .bib extension is automatically appended if omitted. Using this command
+# requires the bibtex tool to be installed. See also
+# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style
+# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this
+# feature you need bibtex and perl available in the search path.
+
+CITE_BIB_FILES =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = YES
+
+# The WARN_NO_PARAMDOC option can be enabled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT = .
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh
+# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py
+# *.f90 *.f *.for *.vhd *.vhdl
+
+FILE_PATTERNS =
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE = cmd_line.cc
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+# If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis.
+# Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match.
+# The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty or if
+# non of the patterns match the file name, INPUT_FILTER is applied.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any)
+# and it is also possible to disable source filtering for a specific pattern
+# using *.ext= (so without naming a filter). This option only has effect when
+# FILTER_SOURCE_FILES is enabled.
+
+FILTER_SOURCE_PATTERNS =
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER = YES
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C, C++ and Fortran comments will always remain visible.
+
+STRIP_CODE_COMMENTS = NO
+
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.
+# Otherwise they will link to the documentation.
+
+REFERENCES_LINK_SOURCE = NO
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header. Note that when using a custom header you are responsible
+# for the proper inclusion of any scripts and style sheets that doxygen
+# needs, which is dependent on the configuration options used.
+# It is advised to generate a default header using "doxygen -w html
+# header.html footer.html stylesheet.css YourConfigFile" and then modify
+# that header. Note that the header is subject to change so you typically
+# have to redo this when upgrading to a newer version of doxygen or when
+# changing the value of configuration settings such as GENERATE_TREEVIEW!
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# style sheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that
+# the files will be copied as-is; there are no commands or markers available.
+
+HTML_EXTRA_FILES =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
+# Doxygen will adjust the colors in the style sheet and background images
+# according to this color. Hue is specified as an angle on a colorwheel,
+# see http://en.wikipedia.org/wiki/Hue for more information.
+# For instance the value 0 represents red, 60 is yellow, 120 is green,
+# 180 is cyan, 240 is blue, 300 purple, and 360 is red again.
+# The allowed range is 0 to 359.
+
+HTML_COLORSTYLE_HUE = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of
+# the colors in the HTML output. For a value of 0 the output will use
+# grayscales only. A value of 255 will produce the most vivid colors.
+
+HTML_COLORSTYLE_SAT = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to
+# the luminance component of the colors in the HTML output. Values below
+# 100 gradually make the output lighter, whereas values above 100 make
+# the output darker. The value divided by 100 is the actual gamma applied,
+# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2,
+# and 100 does not change the gamma.
+
+HTML_COLORSTYLE_GAMMA = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting
+# this to NO can help when comparing the output of multiple runs.
+
+HTML_TIMESTAMP = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded.
+
+HTML_DYNAMIC_SECTIONS = YES
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of
+# entries shown in the various tree structured indices initially; the user
+# can expand and collapse entries dynamically later on. Doxygen will expand
+# the tree to such a level that at most the specified number of entries are
+# visible (unless a fully collapsed tree already exceeds this amount).
+# So setting the number of entries 1 will produce a full collapsed tree by
+# default. 0 is a special value representing an infinite number of entries
+# and will result in a full expanded tree by default.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+
+GENERATE_DOCSET = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+
+DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+
+# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher.
+
+DOCSET_PUBLISHER_NAME = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated
+# that can be used as input for Qt's qhelpgenerator to generate a
+# Qt Compressed Help (.qch) of the generated HTML documentation.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to
+# add. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
+# Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
+# Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS =
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
+# .qhp file.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
+# will be generated, which together with the HTML files, form an Eclipse help
+# plugin. To install this plugin and make it available under the help contents
+# menu in Eclipse, the contents of the directory containing the HTML and XML
+# files needs to be copied into the plugins directory of eclipse. The name of
+# the directory within the plugins directory should be the same as
+# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
+# the help appears.
+
+GENERATE_ECLIPSEHELP = NO
+
+# A unique identifier for the eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have
+# this name.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs)
+# at top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it. Since the tabs have the same information as the
+# navigation tree you can set this option to NO if you already set
+# GENERATE_TREEVIEW to YES.
+
+DISABLE_INDEX = NO
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to YES, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
+# Windows users are probably better off using the HTML help feature.
+# Since the tree basically has the same information as the tab index you
+# could consider to set DISABLE_INDEX to NO when enabling this option.
+
+GENERATE_TREEVIEW = NO
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values
+# (range [0,1..20]) that doxygen will group on one line in the generated HTML
+# documentation. Note that a value of 0 will completely suppress the enum
+# values from appearing in the overview section.
+
+ENUM_VALUES_PER_LINE = 4
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open
+# links to external symbols imported via tag files in a separate window.
+
+EXT_LINKS_IN_WINDOW = NO
+
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are
+# not supported properly for IE 6.0, but are supported on all modern browsers.
+# Note that when changing this option you need to delete any form_*.png files
+# in the HTML output before the changes have effect.
+
+FORMULA_TRANSPARENT = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax
+# (see http://www.mathjax.org) which uses client side Javascript for the
+# rendering instead of using prerendered bitmaps. Use this if you do not
+# have LaTeX installed or if you want to formulas look prettier in the HTML
+# output. When enabled you may also need to install MathJax separately and
+# configure the path to it using the MATHJAX_RELPATH option.
+
+USE_MATHJAX = NO
+
+# When MathJax is enabled you need to specify the location relative to the
+# HTML output directory using the MATHJAX_RELPATH option. The destination
+# directory should contain the MathJax.js script. For instance, if the mathjax
+# directory is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to
+# the MathJax Content Delivery Network so you can quickly see the result without
+# installing MathJax.
+# However, it is strongly recommended to install a local
+# copy of MathJax from http://www.mathjax.org before deployment.
+
+MATHJAX_RELPATH = http://www.mathjax.org/mathjax
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension
+# names that should be enabled during MathJax rendering.
+
+MATHJAX_EXTENSIONS =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box
+# for the HTML output. The underlying search engine uses javascript
+# and DHTML and should work on any modern browser. Note that when using
+# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets
+# (GENERATE_DOCSET) there is already a search function so this one should
+# typically be disabled. For large projects the javascript based search engine
+# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
+
+SEARCHENGINE = NO
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a PHP enabled web server instead of at the web client
+# using Javascript. Doxygen will generate the search PHP script and index
+# file to put on the web server. The advantage of the server
+# based approach is that it scales better to large projects and allows
+# full text search. The disadvantages are that it is more difficult to setup
+# and does not have live searching capabilities.
+
+SERVER_BASED_SEARCH = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+# Note that when enabling USE_PDFLATEX this option is only used for
+# generating bitmaps for formulas in the HTML output, but not in the
+# Makefile that is written to the output directory.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = YES
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = letter
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES = fullpage, \
+ palatino
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for
+# the generated latex document. The footer should contain everything after
+# the last chapter. If it is left blank doxygen will generate a
+# standard footer. Notice: only use this tag if you know what you are doing!
+
+LATEX_FOOTER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = YES
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = YES
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES = YES
+
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include
+# source code with syntax highlighting in the LaTeX output.
+# Note that which sources are shown also depends on other settings
+# such as SOURCE_BROWSER.
+
+LATEX_SOURCE_CODE = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See
+# http://en.wikipedia.org/wiki/BibTeX for more info.
+
+LATEX_BIB_STYLE = plain
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load style sheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader.
+# This is useful
+# if you want to understand what is going on.
+# On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# pointed to by INCLUDE_PATH will be searched when a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition that
+# overrules the definition found in the source code.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all references to function-like macros
+# that are alone on a line, have an all uppercase name, and do not end with a
+# semicolon, because these will confuse the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles. For each
+# tag file the location of the external documentation should be added. The
+# format of a tag file without this location is as follows:
+#
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+#
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths
+# or URLs. Note that each tag file must have a unique name (where the name does
+# NOT include the path). If a tag file is not located in the directory in which
+# doxygen is run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option also works with HAVE_DOT disabled, but it is recommended to
+# install and use dot, since it yields more powerful graphs.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = NO
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is
+# allowed to run in parallel. When set to 0 (the default) doxygen will
+# base this on the number of processors available in the system. You can set it
+# explicitly to a value larger than 0 to get control over the balance
+# between CPU load and processing speed.
+
+DOT_NUM_THREADS = 0
+
+# By default doxygen will use the Helvetica font for all dot files that
+# doxygen generates. When you want a differently looking font you can specify
+# the font name using DOT_FONTNAME. You need to make sure dot is able to find
+# the font, which can be done by putting it in a standard location or by setting
+# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the
+# directory containing the font.
+
+DOT_FONTNAME = FreeSans
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
+# The default size is 10pt.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the Helvetica font.
+# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to
+# set the path where dot can find it.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK = NO
+
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside
+# the class node. If there are many fields or methods and many nodes the
+# graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS
+# threshold limits the number of items for each type to make the size more
+# managable. Set this to 0 for no limit. Note that the threshold may be
+# exceeded by 50% before the limit is enforced.
+
+UML_LIMIT_NUM_FIELDS = 10
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will generate a graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are svg, png, jpg, or gif.
+# If left blank png will be used. If you choose svg you need to set
+# HTML_FILE_EXTENSION to xhtml in order to make the SVG files
+# visible in IE 9+ (other browsers do not have this requirement).
+
+DOT_IMAGE_FORMAT = png
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+# Note that this requires a modern browser other than Internet Explorer.
+# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you
+# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files
+# visible. Older versions of IE do not have SVG support.
+
+INTERACTIVE_SVG = NO
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the
+# \mscfile command).
+
+MSCFILE_DIRS =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT = YES
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
Index: extern/voro++/src/v_base_wl.cc
===================================================================
--- extern/voro++/src/v_base_wl.cc (revision 0)
+++ extern/voro++/src/v_base_wl.cc (revision 0)
@@ -0,0 +1,79 @@
+// Voro++, a 3D cell-based Voronoi library
+//
+// Author : Chris H. Rycroft (LBL / UC Berkeley)
+// Email : chr@alum.mit.edu
+// Date : August 30th 2011
+
+/** \file v_base_wl.cc
+ * \brief The table of block worklists that are used during the cell
+ * computation, which is part of the voro_base class.
+ *
+ * This file is automatically generated by worklist_gen.pl and it is not
+ * intended to be edited by hand. */
+
+const unsigned int voro_base::wl[wl_seq_length*wl_hgridcu]={
+ 7,0x10203f,0x101fc0,0xfe040,0xfe03f,0x101fbf,0xfdfc0,0xfdfbf,0x10fe0bf,0x11020bf,0x11020c0,0x10fe0c0,0x2fe041,0x302041,0x301fc1,0x2fdfc1,0x8105fc0,0x8106040,0x810603f,0x8105fbf,0x701fbe,0x70203e,0x6fe03e,0x6fdfbe,0x30fdf3f,0x3101f3f,0x3101f40,0x30fdf40,0x180f9fc0,0x180fa040,0x180fa03f,0x180f9fbf,0x12fe0c1,0x13020c1,0x91060c0,0x91060bf,0x8306041,0x8305fc1,0x3301f41,0x32fdf41,0x182f9fc1,0x182fa041,0x190fa0c0,0x190fa0bf,0x16fe0be,0x17020be,0x870603e,0x8705fbe,0xb105f3f,0xb105f40,0x3701f3e,0x36fdf3e,0x186f9fbe,0x186fa03e,0x1b0f9f3f,0x1b0f9f40,0x93060c1,0x192fa0c1,0x97060be,0xb305f41,0x1b2f9f41,0x196fa0be,0xb705f3e,0x1b6f9f3e,
+ 11,0x101fc0,0xfe040,0xfdfc0,0x10203f,0x101fbf,0xfe03f,0xfdfbf,0xfdfc1,0x101fc1,0x102041,0xfe041,0x10fe0c0,0x11020c0,0x8106040,0x8105fc0,0x8105fbf,0x810603f,0x11020bf,0x10fe0bf,0x180fa040,0x180f9fc0,0x30fdf40,0x3101f40,0x3101f3f,0x30fdf3f,0x180f9fbf,0x180fa03f,0x6fe03e,0x70203e,0x701fbe,0x6fdfbe,0x8105fc1,0x8106041,0x11020c1,0x10fe0c1,0x180fa041,0x180f9fc1,0x30fdf41,0x3101f41,0x91060c0,0x91060bf,0x190fa0c0,0x190fa0bf,0xb105f40,0xb105f3f,0x8705fbe,0x870603e,0x97020be,0x16fe0be,0x1b0f9f40,0x1b0f9f3f,0x36fdf3e,0xb701f3e,0x1b6f9fbe,0x196fa03e,0x93060c1,0xb305f41,0x192fa0c1,0x1b2f9f41,0x1b2fdfc2,0xb301fc2,0x9302042,0x192fe042,
+ 11,0x101fc0,0xfe040,0xfdfc0,0xfdfbf,0x101fbf,0x10203f,0xfe03f,0xfe041,0x102041,0x101fc1,0xfdfc1,0x8105fc0,0x8106040,0x11020c0,0x10fe0c0,0x10fe0bf,0x11020bf,0x810603f,0x8105fbf,0x3101f40,0x30fdf40,0x180f9fc0,0x180fa040,0x180fa03f,0x180f9fbf,0x30fdf3f,0x3101f3f,0x8105fc1,0x8106041,0x11020c1,0x10fe0c1,0x180fa041,0x180f9fc1,0x30fdf41,0x3101f41,0x701fbe,0x70203e,0x6fe03e,0x6fdfbe,0x91060c0,0x91060bf,0xb105f40,0xb105f3f,0x190fa0c0,0x190fa0bf,0x93060c1,0x1b0f9f40,0x1b0f9f3f,0xb305f41,0x192fa0c1,0x16fe0be,0x17020be,0x970603e,0x8705fbe,0xb701f3e,0x36fdf3e,0x1b2f9f41,0x1b2fdfc2,0x192fe042,0x9302042,0xb301fc2,0x1b6f9fbe,0x196fa03e,
+ 11,0x101fc0,0xfe040,0xfdfc0,0xfdfbf,0x101fbf,0x10203f,0xfe03f,0xfe041,0x102041,0x101fc1,0xfdfc1,0x8105fc0,0x8106040,0x11020c0,0x10fe0c0,0x10fe0bf,0x11020bf,0x810603f,0x8105fbf,0x3101f40,0x30fdf40,0x180f9fc0,0x180fa040,0x10fe0c1,0x11020c1,0x8106041,0x8105fc1,0x3101f3f,0x30fdf3f,0x180f9fbf,0x180fa03f,0x180fa041,0x180f9fc1,0x30fdf41,0x3101f41,0x91060c0,0x91060bf,0x70203e,0x701fbe,0x6fdfbe,0x6fe03e,0x190fa0c0,0xb105f40,0xb105f3f,0x93060c1,0x190fa0bf,0x192fa0c1,0x1b0f9f40,0x1b0f9f3f,0xb305f41,0xb301fc2,0x9302042,0x192fe042,0x1b2fdfc2,0x1b2f9f41,0x16fe0be,0x17020be,0x970603e,0x8705fbe,0xb701f3e,0x36fdf3e,0x1b6f9fbe,0x196fa03e,
+ 11,0x10203f,0xfe040,0xfe03f,0x101fc0,0x101fbf,0xfdfc0,0xfdfbf,0xfe0bf,0x1020bf,0x1020c0,0xfe0c0,0x2fe041,0x302041,0x8106040,0x810603f,0x8105fbf,0x8105fc0,0x301fc1,0x2fdfc1,0x180fa040,0x180fa03f,0x6fe03e,0x70203e,0x701fbe,0x6fdfbe,0x180f9fbf,0x180f9fc0,0x30fdf40,0x3101f40,0x3101f3f,0x30fdf3f,0x81060bf,0x81060c0,0x3020c1,0x2fe0c1,0x180fa0c0,0x180fa0bf,0x6fe0be,0x7020be,0x8306041,0x8305fc1,0x182fa041,0x182f9fc1,0x870603e,0x8705fbe,0xb105f3f,0xb105f40,0xb301f41,0x32fdf41,0x186fa03e,0x186f9fbe,0x36fdf3e,0xb701f3e,0x1b6f9f3f,0x1b2f9f40,0x93060c1,0x97060be,0x192fa0c1,0x196fa0be,0x196fe13f,0x970213f,0x9302140,0x192fe140,
+ 9,0xfe040,0x10203f,0x101fc0,0xfdfc0,0xfe03f,0xfdfbf,0x101fbf,0x102041,0xfe041,0x10fe0c0,0x11020c0,0x11020bf,0x10fe0bf,0xfdfc1,0x101fc1,0x8106040,0x810603f,0x8105fc0,0x8105fbf,0x180fa040,0x180f9fc0,0x180fa03f,0x180f9fbf,0x10fe0c1,0x11020c1,0x70203e,0x6fe03e,0x6fdfbe,0x701fbe,0x3101f3f,0x3101f40,0x30fdf40,0x30fdf3f,0x8106041,0x91060c0,0x91060bf,0x8305fc1,0x180fa041,0x190fa0c0,0x190fa0bf,0x180f9fc1,0x30fdf41,0x3301f41,0x17020be,0x16fe0be,0x93060c1,0x870603e,0x8705fbe,0xb105f3f,0xb105f40,0x192fa0c1,0x1b0f9f40,0x1b0f9f3f,0x186f9fbe,0x196fa03e,0x1b6fdf3e,0xb701f3e,0x97060be,0xb305f41,0x1b2f9f41,0x1b2fdfc2,0x192fe042,0xa302042,
+ 11,0xfe040,0x101fc0,0xfdfc0,0xfe03f,0x10203f,0x101fbf,0xfdfbf,0xfe041,0x102041,0x101fc1,0xfdfc1,0x10fe0c0,0x11020c0,0x11020bf,0x10fe0bf,0x8106040,0x8105fc0,0x810603f,0x8105fbf,0x11020c1,0x10fe0c1,0x180fa040,0x180f9fc0,0x180fa03f,0x180f9fbf,0x30fdf40,0x3101f40,0x8106041,0x8105fc1,0x3101f3f,0x30fdf3f,0x91060c0,0x91060bf,0x180fa041,0x180f9fc1,0x6fe03e,0x70203e,0x701fbe,0x6fdfbe,0x190fa0c0,0x190fa0bf,0x30fdf41,0x3101f41,0x93060c1,0x192fa0c1,0xb105f40,0xb105f3f,0x17020be,0x16fe0be,0x970603e,0x8705fbe,0x1b0f9f40,0x1b0f9f3f,0x186f9fbe,0x196fa03e,0xb305f41,0xb301fc2,0x9302042,0x192fe042,0x1b2fdfc2,0x1b2f9f41,0x1b6fdf3e,0xb701f3e,
+ 11,0xfe040,0x101fc0,0xfdfc0,0xfe03f,0x10203f,0x101fbf,0xfdfbf,0xfe041,0x102041,0x101fc1,0xfdfc1,0x10fe0c0,0x11020c0,0x11020bf,0x10fe0bf,0x8106040,0x8105fc0,0x11020c1,0x10fe0c1,0x810603f,0x8105fbf,0x180fa040,0x180f9fc0,0x8106041,0x8105fc1,0x3101f40,0x180fa03f,0x180f9fbf,0x30fdf40,0x180fa041,0x180f9fc1,0x91060c0,0x3101f3f,0x30fdf3f,0x30fdf41,0x3101f41,0x91060bf,0x190fa0c0,0x91060c1,0x190fa0bf,0x186fe03e,0x70203e,0x3701fbe,0x1b6fdfbe,0x190fa0c1,0x182fe042,0xb105f40,0xb105f3f,0x302042,0x3301fc2,0x1b2fdfc2,0x1b0f9f40,0x1b6f9f3f,0xb105f41,0x17020be,0x196fe0be,0x970603e,0xb705fbe,0x1b2f9f41,0x192fe0c2,0x13020c2,0x9306042,0xb305fc2,
+ 11,0x10203f,0xfe040,0xfe03f,0xfdfbf,0x101fbf,0x101fc0,0xfdfc0,0xfe0c0,0x1020c0,0x1020bf,0xfe0bf,0x810603f,0x8106040,0x302041,0x2fe041,0x2fdfc1,0x301fc1,0x8105fc0,0x8105fbf,0x70203e,0x6fe03e,0x180fa03f,0x180fa040,0x180f9fc0,0x180f9fbf,0x6fdfbe,0x701fbe,0x81060bf,0x81060c0,0x3020c1,0x2fe0c1,0x180fa0c0,0x180fa0bf,0x6fe0be,0x7020be,0x3101f3f,0x3101f40,0x30fdf40,0x30fdf3f,0x8306041,0x8305fc1,0x870603e,0x8705fbe,0x182fa041,0x182f9fc1,0x93060c1,0x186fa03e,0x186f9fbe,0x97060be,0x192fa0c1,0x32fdf41,0x3301f41,0xb305f40,0xb105f3f,0xb701f3e,0x36fdf3e,0x196fa0be,0x196fe13f,0x192fe140,0x9302140,0x970213f,0x1b6f9f3f,0x1b2f9f40,
+ 11,0xfe040,0x10203f,0xfe03f,0xfdfc0,0x101fc0,0x101fbf,0xfdfbf,0xfe0c0,0x1020c0,0x1020bf,0xfe0bf,0x2fe041,0x302041,0x301fc1,0x2fdfc1,0x8106040,0x810603f,0x8105fc0,0x8105fbf,0x3020c1,0x2fe0c1,0x180fa040,0x180fa03f,0x180f9fc0,0x180f9fbf,0x6fe03e,0x70203e,0x81060c0,0x81060bf,0x701fbe,0x6fdfbe,0x8306041,0x8305fc1,0x180fa0c0,0x180fa0bf,0x30fdf40,0x3101f40,0x3101f3f,0x30fdf3f,0x182fa041,0x182f9fc1,0x6fe0be,0x7020be,0x93060c1,0x192fa0c1,0x870603e,0x8705fbe,0x3301f41,0x32fdf41,0xb305f40,0xb105f3f,0x186fa03e,0x186f9fbe,0x1b0f9f3f,0x1b2f9f40,0x97060be,0x970213f,0x9302140,0x192fe140,0x196fe13f,0x196fa0be,0x1b6fdf3e,0xb701f3e,
+ 15,0xfe040,0xfe03f,0x10203f,0x101fc0,0xfdfc0,0xfdfbf,0x101fbf,0x102041,0xfe041,0xfe0c0,0x1020c0,0x1020bf,0xfe0bf,0xfdfc1,0x101fc1,0x8106040,0x1020c1,0xfe0c1,0x810603f,0x8105fc0,0x8105fbf,0x180fa040,0x180fa03f,0x180f9fc0,0x180f9fbf,0x8106041,0x81060c0,0x81060bf,0x8105fc1,0x180fa041,0x180fa0c0,0x180fa0bf,0x6fe03e,0x70203e,0x3101f40,0x30fdf40,0x180f9fc1,0x30fdf3f,0x3101f3f,0x3701fbe,0x36fdfbe,0x93060c1,0x192fa0c1,0x6fe0be,0x7020be,0x3101f41,0x30fdf41,0xb305f40,0xb105f3f,0x970603e,0xb705fbe,0x196fa03e,0x186f9fbe,0x1b2f9f40,0x1b6f9f3f,0x192fe042,0x9302042,0xb301fc2,0x1b2fdfc2,0x192fe140,0x9302140,0x970213f,0x196fe13f,
+ 15,0xfe040,0xfdfc0,0x101fc0,0x10203f,0xfe03f,0xfdfbf,0x101fbf,0x102041,0xfe041,0xfdfc1,0x101fc1,0x1020c0,0xfe0c0,0xfe0bf,0x1020bf,0x1020c1,0xfe0c1,0x8106040,0x8105fc0,0x810603f,0x8105fbf,0x180fa040,0x180f9fc0,0x8106041,0x8105fc1,0x81060c0,0x180fa03f,0x180f9fbf,0x180fa041,0x180f9fc1,0x180fa0c0,0x81060bf,0x91060c1,0x3101f40,0x30fdf40,0x30fdf3f,0x180fa0bf,0x190fa0c1,0x3101f3f,0x3101f41,0x30fdf41,0x186fe03e,0x70203e,0x3701fbe,0x1b6fdfbe,0x186fe0be,0x7020be,0x8302042,0x182fe042,0x1b2fdfc2,0xb301fc2,0xb105f40,0xb705f3f,0xb305f41,0x1b2f9f40,0x1b6f9f3f,0x192fe140,0x9302140,0x93020c2,0x192fe0c2,0x196fe13f,0x970213f,0xa70603e,
+ 11,0x10203f,0xfe040,0xfe03f,0xfdfbf,0x101fbf,0x101fc0,0xfdfc0,0xfe0c0,0x1020c0,0x1020bf,0xfe0bf,0x810603f,0x8106040,0x302041,0x2fe041,0x2fdfc1,0x301fc1,0x8105fc0,0x8105fbf,0x70203e,0x6fe03e,0x180fa03f,0x180fa040,0x2fe0c1,0x3020c1,0x81060c0,0x81060bf,0x701fbe,0x6fdfbe,0x180f9fbf,0x180f9fc0,0x180fa0c0,0x180fa0bf,0x6fe0be,0x7020be,0x8306041,0x8305fc1,0x3101f40,0x3101f3f,0x30fdf3f,0x30fdf40,0x182fa041,0x870603e,0x8705fbe,0x93060c1,0x182f9fc1,0x192fa0c1,0x186fa03e,0x186f9fbe,0x97060be,0x970213f,0x9302140,0x192fe140,0x196fe13f,0x196fa0be,0x32fdf41,0x3301f41,0xb305f40,0xb105f3f,0xb701f3e,0x36fdf3e,0x1b6f9f3f,0x1b2f9f40,
+ 11,0xfe040,0x10203f,0xfe03f,0xfdfc0,0x101fc0,0x101fbf,0xfdfbf,0xfe0c0,0x1020c0,0x1020bf,0xfe0bf,0x2fe041,0x302041,0x301fc1,0x2fdfc1,0x8106040,0x810603f,0x3020c1,0x2fe0c1,0x8105fc0,0x8105fbf,0x180fa040,0x180fa03f,0x81060c0,0x81060bf,0x70203e,0x180f9fc0,0x180f9fbf,0x6fe03e,0x180fa0c0,0x180fa0bf,0x8306041,0x701fbe,0x6fdfbe,0x6fe0be,0x7020be,0x8305fc1,0x182fa041,0x83060c1,0x182f9fc1,0x1b0fdf40,0x3101f40,0x3701f3f,0x1b6fdf3f,0x182fa0c1,0x190fe140,0x870603e,0x8705fbe,0x1102140,0x170213f,0x196fe13f,0x186fa03e,0x1b6f9fbe,0x87060be,0x3301f41,0x1b2fdf41,0xb305f40,0xb705f3f,0x196fa0be,0x192fe141,0x1302141,0x9306140,0x970613f,
+ 15,0xfe040,0xfe03f,0x10203f,0x101fc0,0xfdfc0,0xfdfbf,0x101fbf,0x1020c0,0xfe0c0,0xfe0bf,0x1020bf,0x102041,0xfe041,0xfdfc1,0x101fc1,0x1020c1,0xfe0c1,0x8106040,0x810603f,0x8105fc0,0x8105fbf,0x180fa040,0x180fa03f,0x81060c0,0x81060bf,0x8106041,0x180f9fc0,0x180f9fbf,0x180fa0c0,0x180fa0bf,0x180fa041,0x8105fc1,0x83060c1,0x70203e,0x6fe03e,0x6fdfbe,0x180f9fc1,0x182fa0c1,0x701fbe,0x7020be,0x6fe0be,0x1b0fdf40,0x3101f40,0x3701f3f,0x1b6fdf3f,0x1b0fdf41,0x3101f41,0x9102140,0x190fe140,0x196fe13f,0x970213f,0x870603e,0xb705fbe,0x97060be,0x196fa03e,0x1b6f9fbe,0x192fe042,0x9302042,0x9302141,0x192fe141,0x1b2fdfc2,0xb301fc2,0xb505f40,
+ 17,0xfe040,0xfe03f,0x10203f,0x101fc0,0xfdfc0,0xfe041,0x102041,0x1020c0,0xfe0c0,0xfdfbf,0x101fbf,0x1020bf,0xfe0bf,0xfdfc1,0x101fc1,0x1020c1,0xfe0c1,0x8106040,0x810603f,0x8105fc0,0x8106041,0x81060c0,0x180fa040,0x180fa03f,0x180f9fc0,0x8105fbf,0x81060bf,0x8105fc1,0x180fa041,0x180fa0c0,0x180f9fbf,0x81060c1,0x180fa0bf,0x180f9fc1,0x180fa0c1,0x186fe03e,0x70203e,0x3101f40,0x1b0fdf40,0x1b0fdf3f,0x3101f3f,0x3701fbe,0x1b6fdfbe,0x186fe0be,0x7020be,0x9102140,0x190fe140,0x182fe042,0x8302042,0x3101f41,0x1b0fdf41,0x1b2fdfc2,0xb301fc2,0x83020c2,0x182fe0c2,0x196fe13f,0x970213f,0x9302141,0x192fe141,0x970603e,0xb305f40,0xb105f3f,0xb705fbe,
+ 11,0x10203f,0x101fc0,0x101fbf,0xfe040,0xfe03f,0xfdfc0,0xfdfbf,0x105fbf,0x10603f,0x106040,0x105fc0,0x301fc1,0x302041,0x11020c0,0x11020bf,0x10fe0bf,0x10fe0c0,0x2fe041,0x2fdfc1,0x3101f40,0x3101f3f,0x701fbe,0x70203e,0x6fe03e,0x6fdfbe,0x30fdf3f,0x30fdf40,0x180f9fc0,0x180fa040,0x180fa03f,0x180f9fbf,0x11060bf,0x11060c0,0x306041,0x305fc1,0x3105f40,0x3105f3f,0x705fbe,0x70603e,0x13020c1,0x12fe0c1,0x3301f41,0x32fdf41,0x17020be,0x16fe0be,0x190fa0bf,0x190fa0c0,0x192fa041,0x182f9fc1,0x3701f3e,0x36fdf3e,0x186f9fbe,0x196fa03e,0x1b6f9f3f,0x1b2f9f40,0x93060c1,0x97060be,0xb305f41,0xb705f3e,0xb709fbf,0x970a03f,0x930a040,0xb309fc0,
+ 9,0x101fc0,0x10203f,0xfe040,0xfdfc0,0x101fbf,0xfdfbf,0xfe03f,0x102041,0x101fc1,0x8105fc0,0x8106040,0x810603f,0x8105fbf,0xfdfc1,0xfe041,0x11020c0,0x11020bf,0x10fe0c0,0x10fe0bf,0x3101f40,0x30fdf40,0x3101f3f,0x30fdf3f,0x8105fc1,0x8106041,0x70203e,0x701fbe,0x6fdfbe,0x6fe03e,0x180fa03f,0x180fa040,0x180f9fc0,0x180f9fbf,0x11020c1,0x91060c0,0x91060bf,0x12fe0c1,0x3101f41,0xb105f40,0xb105f3f,0x30fdf41,0x180f9fc1,0x182fa041,0x870603e,0x8705fbe,0x93060c1,0x17020be,0x16fe0be,0x190fa0bf,0x190fa0c0,0xb305f41,0x1b0f9f40,0x1b0f9f3f,0x36fdf3e,0xb701f3e,0x1b6f9fbe,0x196fa03e,0x97060be,0x192fa0c1,0x1b2f9f41,0x1b2fdfc2,0xb301fc2,0x11302042,
+ 11,0x101fc0,0xfe040,0xfdfc0,0x101fbf,0x10203f,0xfe03f,0xfdfbf,0x101fc1,0x102041,0xfe041,0xfdfc1,0x8105fc0,0x8106040,0x810603f,0x8105fbf,0x11020c0,0x10fe0c0,0x11020bf,0x10fe0bf,0x8106041,0x8105fc1,0x3101f40,0x30fdf40,0x3101f3f,0x30fdf3f,0x180f9fc0,0x180fa040,0x11020c1,0x10fe0c1,0x180fa03f,0x180f9fbf,0x91060c0,0x91060bf,0x3101f41,0x30fdf41,0x701fbe,0x70203e,0x6fe03e,0x6fdfbe,0xb105f40,0xb105f3f,0x180f9fc1,0x180fa041,0x93060c1,0xb305f41,0x190fa0c0,0x190fa0bf,0x870603e,0x8705fbe,0x97020be,0x16fe0be,0x1b0f9f40,0x1b0f9f3f,0x36fdf3e,0xb701f3e,0x192fa0c1,0x192fe042,0x9302042,0xb301fc2,0x1b2fdfc2,0x1b2f9f41,0x1b6f9fbe,0x196fa03e,
+ 11,0x101fc0,0xfe040,0xfdfc0,0x101fbf,0x10203f,0xfe03f,0xfdfbf,0x101fc1,0x102041,0xfe041,0xfdfc1,0x8105fc0,0x8106040,0x810603f,0x8105fbf,0x11020c0,0x10fe0c0,0x8106041,0x8105fc1,0x11020bf,0x10fe0bf,0x3101f40,0x30fdf40,0x11020c1,0x10fe0c1,0x180fa040,0x3101f3f,0x30fdf3f,0x180f9fc0,0x3101f41,0x30fdf41,0x91060c0,0x180fa03f,0x180f9fbf,0x180f9fc1,0x180fa041,0x91060bf,0xb105f40,0x91060c1,0xb105f3f,0x3701fbe,0x70203e,0x186fe03e,0x1b6fdfbe,0xb105f41,0x3301fc2,0x190fa0c0,0x190fa0bf,0x302042,0x182fe042,0x1b2fdfc2,0x1b0f9f40,0x1b6f9f3f,0x190fa0c1,0x870603e,0xb705fbe,0x97020be,0x196fe0be,0x1b2f9f41,0xb305fc2,0x8306042,0x93020c2,0x192fe0c2,
+ 9,0x10203f,0x101fc0,0xfe040,0xfe03f,0x101fbf,0xfdfbf,0xfdfc0,0x1020c0,0x1020bf,0x810603f,0x8106040,0x8105fc0,0x8105fbf,0xfe0bf,0xfe0c0,0x302041,0x301fc1,0x2fe041,0x2fdfc1,0x70203e,0x6fe03e,0x701fbe,0x6fdfbe,0x81060bf,0x81060c0,0x3101f40,0x3101f3f,0x30fdf3f,0x30fdf40,0x180f9fc0,0x180fa040,0x180fa03f,0x180f9fbf,0x3020c1,0x8306041,0x8305fc1,0x12fe0c1,0x7020be,0x870603e,0x8705fbe,0x6fe0be,0x180fa0bf,0x190fa0c0,0xb105f40,0xb105f3f,0x93060c1,0x3301f41,0x32fdf41,0x182f9fc1,0x182fa041,0x97060be,0x186fa03e,0x186f9fbe,0x36fdf3e,0xb701f3e,0x1b6f9f3f,0x1b2f9f40,0xb305f41,0x192fa0c1,0x196fa0be,0x196fe13f,0x970213f,0x11302140,
+ 7,0x10203f,0x101fc0,0xfe040,0xfe03f,0x101fbf,0xfdfc0,0xfdfbf,0x302041,0x1020c0,0x8106040,0x810603f,0x1020bf,0xfe0c0,0x2fe041,0x301fc1,0x8105fc0,0x8105fbf,0xfe0bf,0x2fdfc1,0x3020c1,0x8306041,0x81060c0,0x81060bf,0x2fe0c1,0x8305fc1,0x3101f40,0x3101f3f,0x701fbe,0x70203e,0x6fe03e,0x180fa03f,0x180fa040,0x180f9fc0,0x30fdf40,0x30fdf3f,0x6fdfbe,0x180f9fbf,0x180fa0c0,0x182fa041,0x93060c1,0x870603e,0x7020be,0x6fe0be,0x180fa0bf,0x182f9fc1,0x32fdf41,0x3301f41,0xb105f40,0xb105f3f,0x8705fbe,0x97060be,0x192fa0c1,0xb305f41,0xb701f3e,0x36fdf3e,0x1b0f9f3f,0x1b2f9f40,0x1b6f9fbe,0x196fa03e,0x196fe13f,0x9302140,0x970213f,0x192fe140,
+ 11,0x101fc0,0xfe040,0xfdfc0,0x10203f,0x101fbf,0xfe03f,0xfdfbf,0x102041,0x101fc1,0xfe041,0xfdfc1,0x11020c0,0x8106040,0x8105fc0,0x10fe0c0,0x11020bf,0x810603f,0x8105fbf,0x10fe0bf,0x11020c1,0x8106041,0x8105fc1,0x10fe0c1,0x91060c0,0x91060bf,0x3101f40,0x30fdf40,0x180f9fc0,0x180fa040,0x180fa03f,0x180f9fbf,0x30fdf3f,0x3101f3f,0x701fbe,0x70203e,0x6fe03e,0x6fdfbe,0x93060c1,0x3101f41,0x30fdf41,0x180f9fc1,0x180fa041,0x190fa0c0,0x190fa0bf,0xb105f40,0xb105f3f,0x8705fbe,0x870603e,0x17020be,0x16fe0be,0x192fa0c1,0xb305f41,0xb301fc2,0x9302042,0x192fe042,0x1b2fdfc2,0x1b2f9f40,0x1b0f9f3f,0x186f9fbe,0x196fa03e,0x97060be,0xb701f3e,0x1b6fdf3e,
+ 11,0x101fc0,0xfe040,0xfdfc0,0x10203f,0x101fbf,0xfe03f,0xfdfbf,0x102041,0x101fc1,0xfe041,0xfdfc1,0x11020c0,0x8106040,0x8105fc0,0x10fe0c0,0x11020bf,0x810603f,0x8105fbf,0x10fe0bf,0x11020c1,0x8106041,0x8105fc1,0x10fe0c1,0x91060c0,0x91060bf,0x3101f40,0x30fdf40,0x180f9fc0,0x180fa040,0x180fa03f,0x180f9fbf,0x30fdf3f,0x3101f3f,0x3101f41,0x91060c1,0x180fa041,0x180f9fc1,0x30fdf41,0xb105f40,0x70203e,0x3701fbe,0x186fe03e,0x1b6fdfbe,0x190fa0c0,0x190fa0bf,0x190fa0c1,0xb105f3f,0xb105f41,0x3301fc2,0x302042,0x182fe042,0x1b2fdfc2,0x1b0f9f40,0x17020be,0x970603e,0xb705fbe,0x196fe0be,0x1b6f9f3f,0x1b2f9f41,0x13020c2,0x9306042,0xb305fc2,0x192fe0c2,
+ 11,0x10203f,0xfe040,0xfe03f,0x101fbf,0x101fc0,0xfdfc0,0xfdfbf,0x1020bf,0x1020c0,0xfe0c0,0xfe0bf,0x810603f,0x8106040,0x8105fc0,0x8105fbf,0x302041,0x2fe041,0x301fc1,0x2fdfc1,0x81060c0,0x81060bf,0x70203e,0x6fe03e,0x701fbe,0x6fdfbe,0x180fa03f,0x180fa040,0x3020c1,0x2fe0c1,0x180f9fc0,0x180f9fbf,0x8306041,0x8305fc1,0x7020be,0x6fe0be,0x3101f3f,0x3101f40,0x30fdf40,0x30fdf3f,0x870603e,0x8705fbe,0x180fa0bf,0x180fa0c0,0x93060c1,0x97060be,0x182fa041,0x182f9fc1,0xb105f40,0xb105f3f,0xb301f41,0x32fdf41,0x186fa03e,0x186f9fbe,0x36fdf3e,0xb701f3e,0x192fa0c1,0x192fe140,0x9302140,0x970213f,0x196fe13f,0x196fa0be,0x1b6f9f3f,0x1b2f9f40,
+ 11,0x10203f,0xfe040,0xfe03f,0x101fc0,0x101fbf,0xfdfc0,0xfdfbf,0x1020c0,0x1020bf,0xfe0c0,0xfe0bf,0x302041,0x8106040,0x810603f,0x2fe041,0x301fc1,0x8105fc0,0x8105fbf,0x2fdfc1,0x3020c1,0x81060c0,0x81060bf,0x2fe0c1,0x8306041,0x8305fc1,0x70203e,0x6fe03e,0x180fa03f,0x180fa040,0x180f9fc0,0x180f9fbf,0x6fdfbe,0x701fbe,0x3101f3f,0x3101f40,0x30fdf40,0x30fdf3f,0x93060c1,0x7020be,0x6fe0be,0x180fa0bf,0x180fa0c0,0x182fa041,0x182f9fc1,0x870603e,0x8705fbe,0xb105f3f,0xb105f40,0x3301f41,0x32fdf41,0x192fa0c1,0x97060be,0x970213f,0x9302140,0x192fe140,0x196fe13f,0x196fa03e,0x186f9fbe,0x1b0f9f3f,0x1b2f9f40,0xb305f41,0xb701f3e,0x1b6fdf3e,
+ 15,0xfe040,0x10203f,0x101fc0,0xfdfc0,0xfe03f,0x101fbf,0xfdfbf,0x102041,0x1020c0,0xfe0c0,0xfe041,0x101fc1,0xfdfc1,0x1020bf,0xfe0bf,0x8106040,0x810603f,0x8105fc0,0x8105fbf,0x1020c1,0xfe0c1,0x8106041,0x81060c0,0x81060bf,0x8105fc1,0x180fa040,0x180f9fc0,0x180fa03f,0x180f9fbf,0x93060c1,0x70203e,0x6fe03e,0x3101f40,0x1b0fdf40,0x3101f3f,0x3701fbe,0x6fdfbe,0x1b6fdf3f,0x180fa041,0x180fa0c0,0x180fa0bf,0x180f9fc1,0x1b0fdf41,0x3101f41,0x7020be,0x6fe0be,0x870603e,0x8705fbe,0xb105f40,0xb705f3f,0x192fa0c1,0x192fe140,0x9302140,0x970213f,0x97060be,0x9302042,0x192fe042,0xb301fc2,0xb305f41,0x1b2fdfc2,0x196fe13f,0x196fa03e,0x1b6f9fbe,
+ 14,0xfe040,0x101fc0,0xfdfc0,0x10203f,0xfe03f,0x101fbf,0xfdfbf,0x102041,0xfe041,0x101fc1,0xfdfc1,0x1020c0,0xfe0c0,0x1020bf,0x8106040,0xfe0bf,0x8105fc0,0x1020c1,0xfe0c1,0x810603f,0x8105fbf,0x8106041,0x8105fc1,0x81060c0,0x81060bf,0x91060c1,0x180fa040,0x180f9fc0,0x180fa03f,0x180f9fbf,0x180fa041,0x180f9fc1,0x1b0fdf40,0x3101f40,0x3101f3f,0x1b0fdf3f,0x180fa0c0,0x190fa0bf,0x186fe03e,0x70203e,0x3701fbe,0x3101f41,0x1b0fdf41,0x1b6fdfbe,0x190fa0c1,0x182fe042,0x302042,0xb105f40,0xb105f3f,0x3301fc2,0x1b2fdfc2,0x7020be,0x196fe0be,0x970603e,0xb705fbe,0xb105f41,0x9302140,0x192fe140,0x13020c2,0x192fe0c2,0x9306042,0xb305fc2,0x1170213f,
+ 11,0x10203f,0xfe040,0xfe03f,0x101fbf,0x101fc0,0xfdfc0,0xfdfbf,0x1020bf,0x1020c0,0xfe0c0,0xfe0bf,0x810603f,0x8106040,0x8105fc0,0x8105fbf,0x302041,0x2fe041,0x81060c0,0x81060bf,0x301fc1,0x2fdfc1,0x70203e,0x6fe03e,0x3020c1,0x2fe0c1,0x180fa040,0x701fbe,0x6fdfbe,0x180fa03f,0x7020be,0x6fe0be,0x8306041,0x180f9fc0,0x180f9fbf,0x180fa0bf,0x180fa0c0,0x8305fc1,0x870603e,0x83060c1,0x8705fbe,0x3701f3f,0x3101f40,0x1b0fdf40,0x1b6fdf3f,0x87060be,0x170213f,0x182fa041,0x182f9fc1,0x1102140,0x190fe140,0x196fe13f,0x186fa03e,0x1b6f9fbe,0x182fa0c1,0xb105f40,0xb705f3f,0xb301f41,0x1b2fdf41,0x196fa0be,0x970613f,0x9106140,0x9302141,0x192fe141,
+ 11,0x10203f,0xfe040,0xfe03f,0x101fc0,0x101fbf,0xfdfc0,0xfdfbf,0x1020c0,0x1020bf,0xfe0c0,0xfe0bf,0x302041,0x8106040,0x810603f,0x2fe041,0x301fc1,0x8105fc0,0x8105fbf,0x2fdfc1,0x3020c1,0x81060c0,0x81060bf,0x2fe0c1,0x8306041,0x8305fc1,0x70203e,0x6fe03e,0x180fa03f,0x180fa040,0x180f9fc0,0x180f9fbf,0x6fdfbe,0x701fbe,0x7020be,0x83060c1,0x180fa0c0,0x180fa0bf,0x6fe0be,0x870603e,0x3101f40,0x3701f3f,0x1b0fdf40,0x1b6fdf3f,0x182fa041,0x182f9fc1,0x182fa0c1,0x8705fbe,0x87060be,0x170213f,0x1102140,0x190fe140,0x196fe13f,0x186fa03e,0x3301f41,0xb305f40,0xb705f3f,0x1b2fdf41,0x1b6f9fbe,0x196fa0be,0x1302141,0x9306140,0x970613f,0x192fe141,
+ 14,0xfe040,0x10203f,0xfe03f,0x101fc0,0xfdfc0,0x101fbf,0xfdfbf,0x1020c0,0xfe0c0,0x1020bf,0xfe0bf,0x102041,0xfe041,0x101fc1,0x8106040,0xfdfc1,0x810603f,0x1020c1,0xfe0c1,0x8105fc0,0x8105fbf,0x81060c0,0x81060bf,0x8106041,0x8105fc1,0x83060c1,0x180fa040,0x180fa03f,0x180f9fc0,0x180f9fbf,0x180fa0c0,0x180fa0bf,0x186fe03e,0x70203e,0x701fbe,0x186fdfbe,0x180fa041,0x182f9fc1,0x1b0fdf40,0x3101f40,0x3701f3f,0x7020be,0x186fe0be,0x1b6fdf3f,0x182fa0c1,0x190fe140,0x1102140,0x870603e,0x8705fbe,0x170213f,0x196fe13f,0x3101f41,0x1b2fdf41,0xb305f40,0xb705f3f,0x87060be,0x9302042,0x192fe042,0x1302141,0x192fe141,0x9306140,0x970613f,0x13301fc2,
+ 17,0xfe040,0x10203f,0x101fc0,0xfdfc0,0xfe03f,0x1020c0,0x102041,0xfe041,0xfe0c0,0x101fbf,0xfdfbf,0x1020bf,0xfe0bf,0x101fc1,0xfdfc1,0x1020c1,0xfe0c1,0x8106040,0x810603f,0x8105fc0,0x8106041,0x81060c0,0x8105fbf,0x81060bf,0x8105fc1,0x81060c1,0x180fa040,0x180f9fc0,0x180fa03f,0x180fa0c0,0x180fa041,0x180f9fbf,0x180fa0bf,0x180f9fc1,0x180fa0c1,0x70203e,0x186fe03e,0x3101f40,0x1b0fdf40,0x3101f3f,0x3701fbe,0x186fdfbe,0x1b6fdf3f,0x3101f41,0x1b0fdf41,0x8302042,0x182fe042,0x9102140,0x7020be,0x186fe0be,0x190fe140,0x970213f,0x196fe13f,0x9102141,0xb301fc2,0x1b2fdfc2,0x93020c2,0x182fe0c2,0x192fe141,0x970603e,0xb305f40,0xb105f3f,0xb705fbe,
+ 11,0x10203f,0x101fc0,0x101fbf,0xfdfbf,0xfe03f,0xfe040,0xfdfc0,0x105fc0,0x106040,0x10603f,0x105fbf,0x11020bf,0x11020c0,0x302041,0x301fc1,0x2fdfc1,0x2fe041,0x10fe0c0,0x10fe0bf,0x70203e,0x701fbe,0x3101f3f,0x3101f40,0x30fdf40,0x30fdf3f,0x6fdfbe,0x6fe03e,0x11060bf,0x11060c0,0x306041,0x305fc1,0x3105f40,0x3105f3f,0x705fbe,0x70603e,0x180fa03f,0x180fa040,0x180f9fc0,0x180f9fbf,0x13020c1,0x12fe0c1,0x17020be,0x16fe0be,0x3301f41,0x32fdf41,0x93060c1,0x3701f3e,0x36fdf3e,0x97060be,0xb305f41,0x182f9fc1,0x182fa041,0x192fa0c0,0x190fa0bf,0x196fa03e,0x186f9fbe,0xb705f3e,0xb709fbf,0xb309fc0,0x930a040,0x970a03f,0x1b6f9f3f,0x1b2f9f40,
+ 11,0x101fc0,0x10203f,0x101fbf,0xfdfc0,0xfe040,0xfe03f,0xfdfbf,0x105fc0,0x106040,0x10603f,0x105fbf,0x301fc1,0x302041,0x2fe041,0x2fdfc1,0x11020c0,0x11020bf,0x10fe0c0,0x10fe0bf,0x306041,0x305fc1,0x3101f40,0x3101f3f,0x30fdf40,0x30fdf3f,0x701fbe,0x70203e,0x11060c0,0x11060bf,0x6fe03e,0x6fdfbe,0x13020c1,0x12fe0c1,0x3105f40,0x3105f3f,0x180f9fc0,0x180fa040,0x180fa03f,0x180f9fbf,0x3301f41,0x32fdf41,0x705fbe,0x70603e,0x93060c1,0xb305f41,0x17020be,0x16fe0be,0x182fa041,0x182f9fc1,0x192fa0c0,0x190fa0bf,0x3701f3e,0x36fdf3e,0x1b0f9f3f,0x1b2f9f40,0x97060be,0x970a03f,0x930a040,0xb309fc0,0xb709fbf,0xb705f3e,0x1b6f9fbe,0x196fa03e,
+ 15,0x101fc0,0x101fbf,0x10203f,0xfe040,0xfdfc0,0xfdfbf,0xfe03f,0x102041,0x101fc1,0x105fc0,0x106040,0x10603f,0x105fbf,0xfdfc1,0xfe041,0x11020c0,0x106041,0x105fc1,0x11020bf,0x10fe0c0,0x10fe0bf,0x3101f40,0x3101f3f,0x30fdf40,0x30fdf3f,0x11020c1,0x11060c0,0x11060bf,0x10fe0c1,0x3101f41,0x3105f40,0x3105f3f,0x701fbe,0x70203e,0x180fa040,0x180f9fc0,0x30fdf41,0x180f9fbf,0x180fa03f,0x186fe03e,0x186fdfbe,0x93060c1,0xb305f41,0x705fbe,0x70603e,0x180fa041,0x180f9fc1,0x192fa0c0,0x190fa0bf,0x97020be,0x196fe0be,0xb701f3e,0x36fdf3e,0x1b2f9f40,0x1b6f9f3f,0xb301fc2,0x9302042,0x192fe042,0x1b2fdfc2,0xb309fc0,0x930a040,0x970a03f,0xb709fbf,
+ 15,0x101fc0,0xfdfc0,0xfe040,0x10203f,0x101fbf,0xfdfbf,0xfe03f,0x102041,0x101fc1,0xfdfc1,0xfe041,0x106040,0x105fc0,0x105fbf,0x10603f,0x106041,0x105fc1,0x11020c0,0x10fe0c0,0x11020bf,0x10fe0bf,0x3101f40,0x30fdf40,0x11020c1,0x10fe0c1,0x11060c0,0x3101f3f,0x30fdf3f,0x3101f41,0x30fdf41,0x3105f40,0x11060bf,0x91060c1,0x180fa040,0x180f9fc0,0x180f9fbf,0x3105f3f,0xb105f41,0x180fa03f,0x180fa041,0x180f9fc1,0x3701fbe,0x70203e,0x186fe03e,0x1b6fdfbe,0x3705fbe,0x70603e,0x1302042,0x3301fc2,0x1b2fdfc2,0x192fe042,0x190fa0c0,0x196fa0bf,0x192fa0c1,0x1b2f9f40,0x1b6f9f3f,0xb309fc0,0x930a040,0x9306042,0xb305fc2,0xb709fbf,0x970a03f,0x117020be,
+ 11,0x10203f,0x101fc0,0x101fbf,0xfe03f,0xfe040,0xfdfc0,0xfdfbf,0x10603f,0x106040,0x105fc0,0x105fbf,0x11020bf,0x11020c0,0x10fe0c0,0x10fe0bf,0x302041,0x301fc1,0x2fe041,0x2fdfc1,0x11060c0,0x11060bf,0x70203e,0x701fbe,0x6fe03e,0x6fdfbe,0x3101f3f,0x3101f40,0x306041,0x305fc1,0x30fdf40,0x30fdf3f,0x13020c1,0x12fe0c1,0x70603e,0x705fbe,0x180fa03f,0x180fa040,0x180f9fc0,0x180f9fbf,0x17020be,0x16fe0be,0x3105f3f,0x3105f40,0x93060c1,0x97060be,0x3301f41,0x32fdf41,0x190fa0c0,0x190fa0bf,0x192fa041,0x182f9fc1,0x3701f3e,0x36fdf3e,0x186f9fbe,0x196fa03e,0xb305f41,0xb309fc0,0x930a040,0x970a03f,0xb709fbf,0xb705f3e,0x1b6f9f3f,0x1b2f9f40,
+ 11,0x10203f,0x101fc0,0x101fbf,0xfe040,0xfe03f,0xfdfc0,0xfdfbf,0x106040,0x10603f,0x105fc0,0x105fbf,0x302041,0x11020c0,0x11020bf,0x301fc1,0x2fe041,0x10fe0c0,0x10fe0bf,0x2fdfc1,0x306041,0x11060c0,0x11060bf,0x305fc1,0x13020c1,0x12fe0c1,0x70203e,0x701fbe,0x3101f3f,0x3101f40,0x30fdf40,0x30fdf3f,0x6fdfbe,0x6fe03e,0x180fa03f,0x180fa040,0x180f9fc0,0x180f9fbf,0x93060c1,0x70603e,0x705fbe,0x3105f3f,0x3105f40,0x3301f41,0x32fdf41,0x17020be,0x16fe0be,0x190fa0bf,0x190fa0c0,0x182fa041,0x182f9fc1,0xb305f41,0x97060be,0x970a03f,0x930a040,0xb309fc0,0xb709fbf,0xb701f3e,0x36fdf3e,0x1b0f9f3f,0x1b2f9f40,0x192fa0c1,0x196fa03e,0x1b6f9fbe,
+ 15,0x101fc0,0x10203f,0xfe040,0xfdfc0,0x101fbf,0xfe03f,0xfdfbf,0x102041,0x106040,0x105fc0,0x101fc1,0xfe041,0xfdfc1,0x10603f,0x105fbf,0x11020c0,0x11020bf,0x10fe0c0,0x10fe0bf,0x106041,0x105fc1,0x11020c1,0x11060c0,0x11060bf,0x10fe0c1,0x3101f40,0x30fdf40,0x3101f3f,0x30fdf3f,0x93060c1,0x70203e,0x701fbe,0x180fa040,0x1b0f9fc0,0x180fa03f,0x186fe03e,0x6fdfbe,0x1b6f9fbf,0x3101f41,0x3105f40,0x3105f3f,0x30fdf41,0x1b0f9fc1,0x180fa041,0x70603e,0x705fbe,0x17020be,0x16fe0be,0x190fa0c0,0x196fa0bf,0xb305f41,0xb309fc0,0x930a040,0x970a03f,0x97060be,0x9302042,0xb301fc2,0x192fe042,0x192fa0c1,0x1b2fdfc2,0xb709fbf,0xb701f3e,0x1b6fdf3e,
+ 14,0x101fc0,0xfe040,0xfdfc0,0x10203f,0x101fbf,0xfe03f,0xfdfbf,0x102041,0x101fc1,0xfe041,0xfdfc1,0x106040,0x105fc0,0x10603f,0x11020c0,0x105fbf,0x10fe0c0,0x106041,0x105fc1,0x11020bf,0x10fe0bf,0x11020c1,0x10fe0c1,0x11060c0,0x11060bf,0x91060c1,0x3101f40,0x30fdf40,0x3101f3f,0x30fdf3f,0x3101f41,0x30fdf41,0x1b0f9fc0,0x180fa040,0x180fa03f,0x1b0f9fbf,0x3105f40,0xb105f3f,0x3701fbe,0x70203e,0x186fe03e,0x180fa041,0x1b0f9fc1,0x1b6fdfbe,0xb105f41,0x3301fc2,0x302042,0x190fa0c0,0x190fa0bf,0x182fe042,0x1b2fdfc2,0x70603e,0xb705fbe,0x97020be,0x196fe0be,0x190fa0c1,0x930a040,0xb309fc0,0x8306042,0xb305fc2,0x93020c2,0x192fe0c2,0xa70a03f,
+ 15,0x10203f,0x101fbf,0x101fc0,0xfe040,0xfe03f,0xfdfbf,0xfdfc0,0x1020c0,0x1020bf,0x10603f,0x106040,0x105fc0,0x105fbf,0xfe0bf,0xfe0c0,0x302041,0x1060c0,0x1060bf,0x301fc1,0x2fe041,0x2fdfc1,0x70203e,0x701fbe,0x6fe03e,0x6fdfbe,0x3020c1,0x306041,0x305fc1,0x2fe0c1,0x7020be,0x70603e,0x705fbe,0x3101f3f,0x3101f40,0x180fa040,0x180fa03f,0x6fe0be,0x180f9fbf,0x180f9fc0,0x1b0fdf40,0x1b0fdf3f,0x93060c1,0x97060be,0x3105f3f,0x3105f40,0x180fa0c0,0x180fa0bf,0x192fa041,0x182f9fc1,0xb301f41,0x1b2fdf41,0xb701f3e,0x36fdf3e,0x196fa03e,0x1b6f9fbe,0x970213f,0x9302140,0x192fe140,0x196fe13f,0x970a03f,0x930a040,0xb309fc0,0xb709fbf,
+ 15,0x10203f,0x101fc0,0xfe040,0xfe03f,0x101fbf,0xfdfc0,0xfdfbf,0x1020c0,0x106040,0x10603f,0x1020bf,0xfe0c0,0xfe0bf,0x105fc0,0x105fbf,0x302041,0x301fc1,0x2fe041,0x2fdfc1,0x1060c0,0x1060bf,0x3020c1,0x306041,0x305fc1,0x2fe0c1,0x70203e,0x6fe03e,0x701fbe,0x6fdfbe,0x93060c1,0x3101f40,0x3101f3f,0x180fa040,0x186fa03f,0x180f9fc0,0x1b0fdf40,0x30fdf3f,0x1b6f9fbf,0x7020be,0x70603e,0x705fbe,0x6fe0be,0x186fa0bf,0x180fa0c0,0x3105f40,0x3105f3f,0x3301f41,0x32fdf41,0x182fa041,0x1b2f9fc1,0x97060be,0x970a03f,0x930a040,0xb309fc0,0xb305f41,0x9302140,0x970213f,0x192fe140,0x192fa0c1,0x196fe13f,0xb709fbf,0xb701f3e,0x1b6fdf3e,
+ 16,0x10203f,0x101fc0,0xfe040,0xfe03f,0x101fbf,0xfdfc0,0xfdfbf,0x102041,0x1020c0,0x106040,0x10603f,0x1020bf,0xfe0c0,0xfe041,0x101fc1,0x105fc0,0x105fbf,0xfe0bf,0xfdfc1,0x1020c1,0x106041,0x1060c0,0x1060bf,0xfe0c1,0x105fc1,0x93060c1,0x70203e,0x3101f40,0x180fa040,0x180fa03f,0x186fe03e,0x701fbe,0x3701f3f,0x30fdf40,0x1b0f9fc0,0x180f9fbf,0x186fdfbe,0x1b6fdf3f,0x3101f41,0x3105f40,0xb105f3f,0x70603e,0x7020be,0x6fe0be,0x180fa0c0,0x180fa041,0x182f9fc1,0x1b2fdf41,0xb705fbe,0x186fa0bf,0x192fa0c1,0x97060be,0xb305f41,0x9302042,0x192fe042,0x13301fc2,0x930a040,0x970a03f,0xb509fc0,0x9302140,0x970213f,0x192fe140,0x196fe13f,
+ 15,0x101fc0,0xfe040,0xfdfc0,0x10203f,0x101fbf,0xfe03f,0x102041,0x101fc1,0xfdfbf,0xfe041,0x1020c0,0x106040,0xfdfc1,0x105fc0,0xfe0c0,0x1020bf,0x10603f,0x105fbf,0xfe0bf,0x1020c1,0x106041,0x105fc1,0xfe0c1,0x1060c0,0x11060bf,0x91060c1,0x3101f40,0x180fa040,0x180f9fc0,0x1b0fdf40,0x3101f3f,0x30fdf3f,0x180fa03f,0x1b0f9fbf,0x180fa041,0x180f9fc1,0x3101f41,0x1b0fdf41,0x70203e,0x3701fbe,0x186fe03e,0x1b6fdfbe,0x3105f40,0xb105f3f,0x180fa0c0,0x190fa0bf,0x192fa0c1,0x302042,0x3301fc2,0xb305f41,0x182fe042,0x1b2fdfc2,0x17020be,0x170603e,0xb705fbe,0x196fe0be,0x9502140,0x194fe140,0x193020c2,0xa306042,0x930a040,0xb309fc0,0xa70a03f,
+ 15,0x10203f,0xfe03f,0xfe040,0x101fc0,0x101fbf,0xfdfbf,0xfdfc0,0x1020c0,0x1020bf,0xfe0bf,0xfe0c0,0x106040,0x10603f,0x105fbf,0x105fc0,0x1060c0,0x1060bf,0x302041,0x2fe041,0x301fc1,0x2fdfc1,0x70203e,0x6fe03e,0x3020c1,0x2fe0c1,0x306041,0x701fbe,0x6fdfbe,0x7020be,0x6fe0be,0x70603e,0x305fc1,0x83060c1,0x180fa040,0x180fa03f,0x180f9fbf,0x705fbe,0x87060be,0x180f9fc0,0x180fa0c0,0x180fa0bf,0x3701f3f,0x3101f40,0x1b0fdf40,0x1b6fdf3f,0x3705f3f,0x3105f40,0x1302140,0x170213f,0x196fe13f,0x192fe140,0x182fa041,0x1b2f9fc1,0x192fa0c1,0x196fa03e,0x1b6f9fbe,0x970a03f,0x930a040,0x9306140,0x970613f,0xb709fbf,0xb309fc0,0x13301f41,
+ 14,0x10203f,0xfe040,0xfe03f,0x101fc0,0x101fbf,0xfdfc0,0xfdfbf,0x1020c0,0x1020bf,0xfe0c0,0xfe0bf,0x106040,0x10603f,0x105fc0,0x302041,0x105fbf,0x2fe041,0x1060c0,0x1060bf,0x301fc1,0x2fdfc1,0x3020c1,0x2fe0c1,0x306041,0x305fc1,0x83060c1,0x70203e,0x6fe03e,0x701fbe,0x6fdfbe,0x7020be,0x6fe0be,0x186fa03f,0x180fa040,0x180f9fc0,0x186f9fbf,0x70603e,0x8705fbe,0x3701f3f,0x3101f40,0x1b0fdf40,0x180fa0c0,0x186fa0bf,0x1b6fdf3f,0x87060be,0x170213f,0x1102140,0x182fa041,0x182f9fc1,0x190fe140,0x196fe13f,0x3105f40,0xb705f3f,0xb301f41,0x1b2fdf41,0x182fa0c1,0x930a040,0x970a03f,0x9106140,0x970613f,0x9302141,0x192fe141,0xb509fc0,
+ 15,0x10203f,0xfe040,0xfe03f,0x101fc0,0x101fbf,0xfdfc0,0x1020c0,0x1020bf,0xfdfbf,0xfe0c0,0x102041,0x106040,0xfe0bf,0x10603f,0xfe041,0x101fc1,0x105fc0,0x105fbf,0xfdfc1,0x1020c1,0x1060c0,0x1060bf,0xfe0c1,0x106041,0x305fc1,0x83060c1,0x70203e,0x180fa040,0x180fa03f,0x186fe03e,0x701fbe,0x6fdfbe,0x180f9fc0,0x186f9fbf,0x180fa0c0,0x180fa0bf,0x7020be,0x186fe0be,0x3101f40,0x3701f3f,0x1b0fdf40,0x1b6fdf3f,0x70603e,0x8705fbe,0x180fa041,0x182f9fc1,0x192fa0c1,0x1102140,0x170213f,0x97060be,0x190fe140,0x196fe13f,0x3301f41,0x3305f40,0xb705f3f,0x1b2fdf41,0xa302042,0x1a2fe042,0x19302141,0x9506140,0x930a040,0x970a03f,0xb509fc0,
+ 17,0xfe040,0x10203f,0x101fc0,0xfdfc0,0xfe03f,0x1020c0,0x102041,0x101fbf,0xfe041,0xfe0c0,0x106040,0xfdfbf,0x1020bf,0x101fc1,0xfdfc1,0xfe0bf,0x1020c1,0x10603f,0x105fc0,0xfe0c1,0x1060c0,0x106041,0x8105fbf,0x81060bf,0x8105fc1,0x81060c1,0x180fa040,0x180f9fc0,0x180fa03f,0x180fa0c0,0x180fa041,0x180f9fbf,0x70203e,0x186fe03e,0x3101f40,0x1b0fdf40,0x180f9fc1,0x180fa0bf,0x701fbe,0x3701f3f,0x1b0fdf3f,0x1b6fdfbe,0x7020be,0x186fe0be,0x192fa0c1,0x9102140,0x190fe140,0x8302042,0x3101f41,0x1b0fdf41,0x182fe042,0xb301fc2,0xb305f40,0x870603e,0x970213f,0x196fe13f,0x11102141,0x113020c2,0x1b2fdfc2,0xb105f3f,0xb705fbe,0x97060be,0xa50a040,
+ 11,0x10203f,0x101fc0,0x101fbf,0xfdfbf,0xfe03f,0xfe040,0xfdfc0,0x105fc0,0x106040,0x10603f,0x105fbf,0x11020bf,0x11020c0,0x302041,0x301fc1,0x2fdfc1,0x2fe041,0x10fe0c0,0x10fe0bf,0x70203e,0x701fbe,0x3101f3f,0x3101f40,0x305fc1,0x306041,0x11060c0,0x11060bf,0x6fe03e,0x6fdfbe,0x30fdf3f,0x30fdf40,0x3105f40,0x3105f3f,0x705fbe,0x70603e,0x13020c1,0x12fe0c1,0x180fa040,0x180fa03f,0x180f9fbf,0x180f9fc0,0x3301f41,0x17020be,0x16fe0be,0x93060c1,0x32fdf41,0xb305f41,0x3701f3e,0x36fdf3e,0x97060be,0x970a03f,0x930a040,0xb309fc0,0xb709fbf,0xb705f3e,0x182f9fc1,0x182fa041,0x192fa0c0,0x190fa0bf,0x196fa03e,0x186f9fbe,0x1b6f9f3f,0x1b2f9f40,
+ 11,0x101fc0,0x10203f,0x101fbf,0xfdfc0,0xfe040,0xfe03f,0xfdfbf,0x105fc0,0x106040,0x10603f,0x105fbf,0x301fc1,0x302041,0x2fe041,0x2fdfc1,0x11020c0,0x11020bf,0x306041,0x305fc1,0x10fe0c0,0x10fe0bf,0x3101f40,0x3101f3f,0x11060c0,0x11060bf,0x70203e,0x30fdf40,0x30fdf3f,0x701fbe,0x3105f40,0x3105f3f,0x13020c1,0x6fe03e,0x6fdfbe,0x705fbe,0x70603e,0x12fe0c1,0x3301f41,0x13060c1,0x32fdf41,0x1b0f9fc0,0x180fa040,0x186fa03f,0x1b6f9fbf,0x3305f41,0xb109fc0,0x17020be,0x16fe0be,0x810a040,0x870a03f,0xb709fbf,0x3701f3e,0x1b6fdf3e,0x17060be,0x182fa041,0x1b2f9fc1,0x192fa0c0,0x196fa0bf,0xb705f3e,0xb309fc1,0x830a041,0x930a0c0,0x970a0bf,
+ 15,0x101fc0,0x101fbf,0x10203f,0xfe040,0xfdfc0,0xfdfbf,0xfe03f,0x106040,0x105fc0,0x105fbf,0x10603f,0x102041,0x101fc1,0xfdfc1,0xfe041,0x106041,0x105fc1,0x11020c0,0x11020bf,0x10fe0c0,0x10fe0bf,0x3101f40,0x3101f3f,0x11060c0,0x11060bf,0x11020c1,0x30fdf40,0x30fdf3f,0x3105f40,0x3105f3f,0x3101f41,0x10fe0c1,0x13060c1,0x70203e,0x701fbe,0x6fdfbe,0x30fdf41,0x3305f41,0x6fe03e,0x70603e,0x705fbe,0x1b0f9fc0,0x180fa040,0x186fa03f,0x1b6f9fbf,0x1b0f9fc1,0x180fa041,0x910a040,0xb109fc0,0xb709fbf,0x970a03f,0x17020be,0x196fe0be,0x97060be,0xb701f3e,0x1b6fdf3e,0xb301fc2,0x9302042,0x930a041,0xb309fc1,0x1b2fdfc2,0x192fe042,0x194fa0c0,
+ 17,0x101fc0,0x101fbf,0x10203f,0xfe040,0xfdfc0,0x101fc1,0x102041,0x106040,0x105fc0,0xfdfbf,0xfe03f,0x10603f,0x105fbf,0xfdfc1,0xfe041,0x106041,0x105fc1,0x11020c0,0x11020bf,0x10fe0c0,0x11020c1,0x11060c0,0x3101f40,0x3101f3f,0x30fdf40,0x10fe0bf,0x11060bf,0x10fe0c1,0x3101f41,0x3105f40,0x30fdf3f,0x11060c1,0x3105f3f,0x30fdf41,0x3105f41,0x3701fbe,0x70203e,0x180fa040,0x1b0f9fc0,0x1b0f9fbf,0x180fa03f,0x186fe03e,0x1b6fdfbe,0x3705fbe,0x70603e,0x910a040,0xb109fc0,0x3301fc2,0x1302042,0x180fa041,0x1b0f9fc1,0x1b2fdfc2,0x192fe042,0x1306042,0x3305fc2,0xb709fbf,0x970a03f,0x930a041,0xb309fc1,0x97020be,0x192fa0c0,0x190fa0bf,0x196fe0be,
+ 11,0x10203f,0x101fc0,0x101fbf,0xfe03f,0xfe040,0xfdfc0,0xfdfbf,0x10603f,0x106040,0x105fc0,0x105fbf,0x11020bf,0x11020c0,0x10fe0c0,0x10fe0bf,0x302041,0x301fc1,0x11060c0,0x11060bf,0x2fe041,0x2fdfc1,0x70203e,0x701fbe,0x306041,0x305fc1,0x3101f40,0x6fe03e,0x6fdfbe,0x3101f3f,0x70603e,0x705fbe,0x13020c1,0x30fdf40,0x30fdf3f,0x3105f3f,0x3105f40,0x12fe0c1,0x17020be,0x13060c1,0x16fe0be,0x186fa03f,0x180fa040,0x1b0f9fc0,0x1b6f9fbf,0x17060be,0x870a03f,0x3301f41,0x32fdf41,0x810a040,0xb109fc0,0xb709fbf,0x3701f3e,0x1b6fdf3e,0x3305f41,0x190fa0c0,0x196fa0bf,0x192fa041,0x1b2f9fc1,0xb705f3e,0x970a0bf,0x910a0c0,0x930a041,0xb309fc1,
+ 11,0x10203f,0x101fc0,0x101fbf,0xfe040,0xfe03f,0xfdfc0,0xfdfbf,0x106040,0x10603f,0x105fc0,0x105fbf,0x302041,0x11020c0,0x11020bf,0x301fc1,0x2fe041,0x10fe0c0,0x10fe0bf,0x2fdfc1,0x306041,0x11060c0,0x11060bf,0x305fc1,0x13020c1,0x12fe0c1,0x70203e,0x701fbe,0x3101f3f,0x3101f40,0x30fdf40,0x30fdf3f,0x6fdfbe,0x6fe03e,0x70603e,0x13060c1,0x3105f40,0x3105f3f,0x705fbe,0x17020be,0x180fa040,0x186fa03f,0x1b0f9fc0,0x1b6f9fbf,0x3301f41,0x32fdf41,0x3305f41,0x16fe0be,0x17060be,0x870a03f,0x810a040,0xb109fc0,0xb709fbf,0x3701f3e,0x182fa041,0x192fa0c0,0x196fa0bf,0x1b2f9fc1,0x1b6fdf3e,0xb705f3e,0x830a041,0x930a0c0,0x970a0bf,0xb309fc1,
+ 14,0x101fc0,0x10203f,0x101fbf,0xfe040,0xfdfc0,0xfe03f,0xfdfbf,0x106040,0x105fc0,0x10603f,0x105fbf,0x102041,0x101fc1,0xfe041,0x11020c0,0xfdfc1,0x11020bf,0x106041,0x105fc1,0x10fe0c0,0x10fe0bf,0x11060c0,0x11060bf,0x11020c1,0x10fe0c1,0x13060c1,0x3101f40,0x3101f3f,0x30fdf40,0x30fdf3f,0x3105f40,0x3105f3f,0x3701fbe,0x70203e,0x6fe03e,0x36fdfbe,0x3101f41,0x32fdf41,0x1b0f9fc0,0x180fa040,0x186fa03f,0x70603e,0x3705fbe,0x1b6f9fbf,0x3305f41,0xb109fc0,0x810a040,0x17020be,0x16fe0be,0x870a03f,0xb709fbf,0x180fa041,0x1b2f9fc1,0x192fa0c0,0x196fa0bf,0x17060be,0x9302042,0xb301fc2,0x830a041,0xb309fc1,0x930a0c0,0x970a0bf,0x1a2fe042,
+ 17,0x101fc0,0x10203f,0xfe040,0xfdfc0,0x101fbf,0x106040,0x102041,0x101fc1,0x105fc0,0xfe03f,0xfdfbf,0x10603f,0x105fbf,0xfe041,0xfdfc1,0x106041,0x105fc1,0x11020c0,0x11020bf,0x10fe0c0,0x11020c1,0x11060c0,0x10fe0bf,0x11060bf,0x10fe0c1,0x11060c1,0x3101f40,0x30fdf40,0x3101f3f,0x3105f40,0x3101f41,0x30fdf3f,0x3105f3f,0x30fdf41,0x3105f41,0x70203e,0x3701fbe,0x180fa040,0x1b0f9fc0,0x180fa03f,0x186fe03e,0x36fdfbe,0x1b6f9fbf,0x180fa041,0x1b0f9fc1,0x1302042,0x3301fc2,0x910a040,0x70603e,0x3705fbe,0xb109fc0,0x970a03f,0xb709fbf,0x910a041,0x192fe042,0x1b2fdfc2,0x9306042,0x3305fc2,0xb309fc1,0x97020be,0x192fa0c0,0x190fa0bf,0x196fe0be,
+ 15,0x10203f,0x101fbf,0x101fc0,0xfe040,0xfe03f,0xfdfbf,0xfdfc0,0x106040,0x10603f,0x105fbf,0x105fc0,0x1020c0,0x1020bf,0xfe0bf,0xfe0c0,0x1060c0,0x1060bf,0x302041,0x301fc1,0x2fe041,0x2fdfc1,0x70203e,0x701fbe,0x306041,0x305fc1,0x3020c1,0x6fe03e,0x6fdfbe,0x70603e,0x705fbe,0x7020be,0x2fe0c1,0x13060c1,0x3101f40,0x3101f3f,0x30fdf3f,0x6fe0be,0x17060be,0x30fdf40,0x3105f40,0x3105f3f,0x186fa03f,0x180fa040,0x1b0f9fc0,0x1b6f9fbf,0x186fa0bf,0x180fa0c0,0x830a040,0x870a03f,0xb709fbf,0xb309fc0,0x3301f41,0x1b2fdf41,0xb305f41,0xb701f3e,0x1b6fdf3e,0x970213f,0x9302140,0x930a0c0,0x970a0bf,0x196fe13f,0x192fe140,0x1a2fa041,
+ 14,0x10203f,0x101fc0,0x101fbf,0xfe040,0xfe03f,0xfdfc0,0xfdfbf,0x106040,0x10603f,0x105fc0,0x105fbf,0x1020c0,0x1020bf,0xfe0c0,0x302041,0xfe0bf,0x301fc1,0x1060c0,0x1060bf,0x2fe041,0x2fdfc1,0x306041,0x305fc1,0x3020c1,0x2fe0c1,0x13060c1,0x70203e,0x701fbe,0x6fe03e,0x6fdfbe,0x70603e,0x705fbe,0x3701f3f,0x3101f40,0x30fdf40,0x36fdf3f,0x7020be,0x16fe0be,0x186fa03f,0x180fa040,0x1b0f9fc0,0x3105f40,0x3705f3f,0x1b6f9fbf,0x17060be,0x870a03f,0x810a040,0x3301f41,0x32fdf41,0xb109fc0,0xb709fbf,0x180fa0c0,0x196fa0bf,0x192fa041,0x1b2f9fc1,0x3305f41,0x9302140,0x970213f,0x910a0c0,0x970a0bf,0x930a041,0xb309fc1,0x194fe140,
+ 15,0x10203f,0x101fc0,0x101fbf,0xfe040,0xfe03f,0xfdfc0,0x106040,0x10603f,0xfdfbf,0x105fc0,0x102041,0x1020c0,0x105fbf,0x1020bf,0x101fc1,0xfe041,0xfe0c0,0xfe0bf,0xfdfc1,0x106041,0x1060c0,0x1060bf,0x105fc1,0x1020c1,0x2fe0c1,0x13060c1,0x70203e,0x3101f40,0x3101f3f,0x3701fbe,0x6fe03e,0x6fdfbe,0x30fdf40,0x36fdf3f,0x3105f40,0x3105f3f,0x70603e,0x3705fbe,0x180fa040,0x186fa03f,0x1b0f9fc0,0x1b6f9fbf,0x7020be,0x16fe0be,0x3101f41,0x32fdf41,0xb305f41,0x810a040,0x870a03f,0x97060be,0xb109fc0,0xb709fbf,0x182fa041,0x182fa0c0,0x196fa0bf,0x1b2f9fc1,0x11302042,0x13301fc2,0xb30a041,0x950a0c0,0x9302140,0x970213f,0x194fe140,
+ 17,0x101fc0,0x10203f,0xfe040,0xfdfc0,0x101fbf,0x106040,0x102041,0xfe03f,0x101fc1,0x105fc0,0x1020c0,0xfdfbf,0x10603f,0xfe041,0xfdfc1,0x105fbf,0x106041,0x1020bf,0xfe0c0,0x105fc1,0x1060c0,0x1020c1,0x10fe0bf,0x11060bf,0x10fe0c1,0x11060c1,0x3101f40,0x30fdf40,0x3101f3f,0x3105f40,0x3101f41,0x30fdf3f,0x70203e,0x3701fbe,0x180fa040,0x1b0f9fc0,0x30fdf41,0x3105f3f,0x6fe03e,0x186fa03f,0x1b0f9fbf,0x1b6fdfbe,0x70603e,0x3705fbe,0xb305f41,0x910a040,0xb109fc0,0x1302042,0x180fa041,0x1b0f9fc1,0x3301fc2,0x192fe042,0x192fa0c0,0x17020be,0x970a03f,0xb709fbf,0xa10a041,0xa306042,0x1b2fdfc2,0x190fa0bf,0x196fe0be,0x97060be,0x11502140,
+ 17,0x10203f,0x101fbf,0x101fc0,0xfe040,0xfe03f,0x1020bf,0x1020c0,0x106040,0x10603f,0xfdfbf,0xfdfc0,0x105fc0,0x105fbf,0xfe0bf,0xfe0c0,0x1060c0,0x1060bf,0x302041,0x301fc1,0x2fe041,0x3020c1,0x306041,0x70203e,0x701fbe,0x6fe03e,0x2fdfc1,0x305fc1,0x2fe0c1,0x7020be,0x70603e,0x6fdfbe,0x3060c1,0x705fbe,0x6fe0be,0x7060be,0x3701f3f,0x3101f40,0x180fa040,0x186fa03f,0x186f9fbf,0x180f9fc0,0x1b0fdf40,0x1b6fdf3f,0x3705f3f,0x3105f40,0x830a040,0x870a03f,0x170213f,0x1302140,0x180fa0c0,0x186fa0bf,0x196fe13f,0x192fe140,0x1306140,0x170613f,0xb709fbf,0xb309fc0,0x930a0c0,0x970a0bf,0xb301f41,0x192fa041,0x182f9fc1,0x1b2fdf41,
+ 17,0x10203f,0x101fc0,0xfe040,0xfe03f,0x101fbf,0x106040,0x1020c0,0x1020bf,0x10603f,0xfdfc0,0xfdfbf,0x105fc0,0x105fbf,0xfe0c0,0xfe0bf,0x1060c0,0x1060bf,0x302041,0x301fc1,0x2fe041,0x3020c1,0x306041,0x2fdfc1,0x305fc1,0x2fe0c1,0x3060c1,0x70203e,0x6fe03e,0x701fbe,0x70603e,0x7020be,0x6fdfbe,0x705fbe,0x6fe0be,0x7060be,0x3101f40,0x3701f3f,0x180fa040,0x186fa03f,0x180f9fc0,0x1b0fdf40,0x36fdf3f,0x1b6f9fbf,0x180fa0c0,0x186fa0bf,0x1302140,0x170213f,0x830a040,0x3105f40,0x3705f3f,0x870a03f,0xb309fc0,0xb709fbf,0x830a0c0,0x192fe140,0x196fe13f,0x9306140,0x170613f,0x970a0bf,0xb301f41,0x192fa041,0x182f9fc1,0x1b2fdf41,
+ 17,0x10203f,0x101fc0,0xfe040,0xfe03f,0x101fbf,0x106040,0x1020c0,0xfdfc0,0x1020bf,0x10603f,0x102041,0xfdfbf,0x105fc0,0xfe0c0,0xfe0bf,0x105fbf,0x1060c0,0x101fc1,0xfe041,0x1060bf,0x106041,0x1020c1,0x2fdfc1,0x305fc1,0x2fe0c1,0x3060c1,0x70203e,0x6fe03e,0x701fbe,0x70603e,0x7020be,0x6fdfbe,0x3101f40,0x3701f3f,0x180fa040,0x186fa03f,0x6fe0be,0x705fbe,0x30fdf40,0x1b0f9fc0,0x186f9fbf,0x1b6fdf3f,0x3105f40,0x3705f3f,0x97060be,0x830a040,0x870a03f,0x1302140,0x180fa0c0,0x186fa0bf,0x170213f,0x192fe140,0x192fa041,0x3301f41,0xb309fc0,0xb709fbf,0x850a0c0,0x9506140,0x196fe13f,0x182f9fc1,0x1b2fdf41,0xb305f41,0x12302042,
+ 16,0x10203f,0x101fc0,0xfe040,0x102041,0x1020c0,0x106040,0x101fbf,0xfe03f,0xfdfc0,0x101fc1,0x105fc0,0x10603f,0x1020bf,0xfe0c0,0xfe041,0xfdfbf,0x1020c1,0x106041,0x1060c0,0x105fbf,0xfe0bf,0xfdfc1,0x105fc1,0x1060bf,0xfe0c1,0x83060c1,0x70203e,0x3101f40,0x180fa040,0x180fa03f,0x186fe03e,0x701fbe,0x3701f3f,0x30fdf40,0x1b0f9fc0,0x180fa041,0x180fa0c0,0x7020be,0x70603e,0x3105f40,0xb101f41,0x9302042,0x1102140,0x930a040,0x6fdfbe,0x36fdf3f,0x1b6f9fbf,0x190fa0bf,0x196fe0be,0x170213f,0x196fe140,0x182f9fc1,0x1b2fdf41,0xb301fc2,0x1a2fe042,0x192fa0c1,0x8705fbe,0xb705f3f,0xb309fc0,0xa70a03f,0x97060be,0x9706140,0x11302141
+};
Index: extern/voro++/src/pre_container.hh
===================================================================
--- extern/voro++/src/pre_container.hh (revision 0)
+++ extern/voro++/src/pre_container.hh (revision 0)
@@ -0,0 +1,162 @@
+// Voro++, a 3D cell-based Voronoi library
+//
+// Author : Chris H. Rycroft (LBL / UC Berkeley)
+// Email : chr@alum.mit.edu
+// Date : August 30th 2011
+
+/** \file pre_container.hh
+ * \brief Header file for the pre_container and related classes. */
+
+#ifndef VOROPP_PRE_CONTAINER_HH
+#define VOROPP_PRE_CONTAINER_HH
+
+#include <cstdio>
+
+#include "c_loops.hh"
+#include "container.hh"
+
+namespace voro {
+
+/** \brief A class for storing an arbitrary number of particles, prior to setting
+ * up a container geometry.
+ *
+ * The pre_container_base class can dynamically import and store an arbitrary
+ * number of particles. Once the particles have been read in, an appropriate
+ * container class can be set up with the optimal grid size, and the particles
+ * can be transferred.
+ *
+ * The pre_container_base class is not intended for direct use, but forms the
+ * base of the pre_container and pre_container_poly classes, that add routines
+ * depending on whether particle radii need to be tracked or not. */
+class pre_container_base {
+ public:
+ /** The minimum x coordinate of the container. */
+ const double ax;
+ /** The maximum x coordinate of the container. */
+ const double bx;
+ /** The minimum y coordinate of the container. */
+ const double ay;
+ /** The maximum y coordinate of the container. */
+ const double by;
+ /** The minimum z coordinate of the container. */
+ const double az;
+ /** The maximum z coordinate of the container. */
+ const double bz;
+ /** A boolean value that determines if the x coordinate in
+ * periodic or not. */
+ const bool xperiodic;
+ /** A boolean value that determines if the y coordinate in
+ * periodic or not. */
+ const bool yperiodic;
+ /** A boolean value that determines if the z coordinate in
+ * periodic or not. */
+ const bool zperiodic;
+ void guess_optimal(int &nx,int &ny,int &nz);
+ pre_container_base(double ax_,double bx_,double ay_,double by_,double az_,double bz_,bool xperiodic_,bool yperiodic_,bool zperiodic_,int ps_);
+ ~pre_container_base();
+ /** Calculates and returns the total number of particles stored
+ * within the class.
+ * \return The number of particles. */
+ inline int total_particles() {
+ return (end_id-pre_id)*pre_container_chunk_size+(ch_id-*end_id);
+ }
+ protected:
+ /** The number of doubles associated with a single particle
+ * (three for the standard container, four when radius
+ * information is stored). */
+ const int ps;
+ void new_chunk();
+ void extend_chunk_index();
+ /** The size of the chunk index. */
+ int index_sz;
+ /** A pointer to the chunk index to store the integer particle
+ * IDs. */
+ int **pre_id;
+ /** A pointer to the last allocated integer ID chunk. */
+ int **end_id;
+ /** A pointer to the end of the integer ID chunk index, used to
+ * determine when the chunk index is full. */
+ int **l_id;
+ /** A pointer to the next available slot on the current
+ * particle ID chunk. */
+ int *ch_id;
+ /** A pointer to the end of the current integer chunk. */
+ int *e_id;
+ /** A pointer to the chunk index to store the floating point
+ * information associated with particles. */
+ double **pre_p;
+ /** A pointer to the last allocated chunk of floating point
+ * information. */
+ double **end_p;
+ /** A pointer to the next available slot on the current
+ * floating point chunk. */
+ double *ch_p;
+};
+
+/** \brief A class for storing an arbitrary number of particles without radius
+ * information, prior to setting up a container geometry.
+ *
+ * The pre_container class is an extension of the pre_container_base class for
+ * cases when no particle radius information is available. */
+class pre_container : public pre_container_base {
+ public:
+ /** The class constructor sets up the geometry of container,
+ * initializing the minimum and maximum coordinates in each
+ * direction.
+ * \param[in] (ax_,bx_) the minimum and maximum x coordinates.
+ * \param[in] (ay_,by_) the minimum and maximum y coordinates.
+ * \param[in] (az_,bz_) the minimum and maximum z coordinates.
+ * \param[in] (xperiodic_,yperiodic_,zperiodic_ ) flags setting whether the
+ * container is periodic in
+ * each coordinate direction. */
+ pre_container(double ax_,double bx_,double ay_,double by_,double az_,double bz_,
+ bool xperiodic_,bool yperiodic_,bool zperiodic_)
+ : pre_container_base(ax_,bx_,ay_,by_,az_,bz_,xperiodic_,yperiodic_,zperiodic_,3) {};
+ void put(int n,double x,double y,double z);
+ void import(FILE *fp=stdin);
+ /** Imports particles from a file.
+ * \param[in] filename the name of the file to read from. */
+ inline void import(const char* filename) {
+ FILE *fp=safe_fopen(filename,"r");
+ import(fp);
+ fclose(fp);
+ }
+ void setup(container &con);
+ void setup(particle_order &vo,container &con);
+};
+
+/** \brief A class for storing an arbitrary number of particles with radius
+ * information, prior to setting up a container geometry.
+ *
+ * The pre_container_poly class is an extension of the pre_container_base class
+ * for cases when particle radius information is available. */
+class pre_container_poly : public pre_container_base {
+ public:
+ /** The class constructor sets up the geometry of container,
+ * initializing the minimum and maximum coordinates in each
+ * direction.
+ * \param[in] (ax_,bx_) the minimum and maximum x coordinates.
+ * \param[in] (ay_,by_) the minimum and maximum y coordinates.
+ * \param[in] (az_,bz_) the minimum and maximum z coordinates.
+ * \param[in] (xperiodic_,yperiodic_,zperiodic_ ) flags setting whether the
+ * container is periodic in
+ * each coordinate direction. */
+ pre_container_poly(double ax_,double bx_,double ay_,double by_,double az_,double bz_,
+ bool xperiodic_,bool yperiodic_,bool zperiodic_)
+ : pre_container_base(ax_,bx_,ay_,by_,az_,bz_,xperiodic_,yperiodic_,zperiodic_,4) {};
+ void put(int n,double x,double y,double z,double r);
+ void import(FILE *fp=stdin);
+ /** Imports particles from a file.
+ * \param[in] filename the name of the file to read from. */
+ inline void import(const char* filename) {
+ FILE *fp=safe_fopen(filename,"r");
+ import(fp);
+ fclose(fp);
+ }
+ void setup(container_poly &con);
+ void setup(particle_order &vo,container_poly &con);
+};
+
+}
+
+#endif
Index: extern/voro++/src/c_interface.cc
===================================================================
--- extern/voro++/src/c_interface.cc (revision 0)
+++ extern/voro++/src/c_interface.cc (revision 0)
@@ -0,0 +1,42 @@
+#include "c_interface.hh"
+#include "voro++.hh"
+
+extern "C" {
+
+ container* container_new(double ax_,double bx_,double ay_,double by_,double az_,double bz_,
+ int nx_,int ny_,int nz_,int xperiodic_,int yperiodic_,int zperiodic_,int init_mem)
+ {
+
+ return new voro::container(ax_, bx_, ay_, by_, az_, bz_, nx_, ny_, nz_,
+ xperiodic_, yperiodic_, zperiodic_, init_mem);
+ }
+
+ particle_order* particle_order_new(void)
+ {
+ return new voro::particle_order();
+ }
+
+ void container_put(container* container, particle_order* p_order, int n,double x,double y,double z)
+ {
+ voro::container* c = (voro::container*)container;
+ voro::particle_order* po = (voro::particle_order*)p_order;
+
+ if (po)
+ {
+ c->put(*po, n, x, y, z);
+ }
+ else
+ {
+ c->put(n, x, y, z);
+ }
+
+ }
+
+ void container_print_custom(container* container, const char* format, FILE* fp)
+ {
+ voro::container* c = (voro::container*)container;
+ c->print_custom(format, fp);
+ }
+
+}
+
Index: extern/voro++/src/common.hh
===================================================================
--- extern/voro++/src/common.hh (revision 0)
+++ extern/voro++/src/common.hh (revision 0)
@@ -0,0 +1,67 @@
+// Voro++, a 3D cell-based Voronoi library
+//
+// Author : Chris H. Rycroft (LBL / UC Berkeley)
+// Email : chr@alum.mit.edu
+// Date : August 30th 2011
+
+/** \file common.hh
+ * \brief Header file for the small helper functions. */
+
+#ifndef VOROPP_COMMON_HH
+#define VOROPP_COMMON_HH
+
+#include <cstdio>
+#include <cstdlib>
+#include <vector>
+
+#include "config.hh"
+
+namespace voro {
+
+/** \brief Function for printing fatal error messages and exiting.
+ *
+ * Function for printing fatal error messages and exiting.
+ * \param[in] p a pointer to the message to print.
+ * \param[in] status the status code to return with. */
+inline void voro_fatal_error(const char *p,int status) {
+ fprintf(stderr,"voro++: %s\n",p);
+ exit(status);
+}
+
+/** \brief Prints a vector of positions.
+ *
+ * Prints a vector of positions as bracketed triplets.
+ * \param[in] v the vector to print.
+ * \param[in] fp the file stream to print to. */
+inline void voro_print_positions(std::vector<double> &v,FILE *fp=stdout) {
+ if(v.size()>0) {
+ fprintf(fp,"(%g,%g,%g)",v[0],v[1],v[2]);
+ for(int k=3;(unsigned int) k<v.size();k+=3) {
+ fprintf(fp," (%g,%g,%g)",v[k],v[k+1],v[k+2]);
+ }
+ }
+}
+
+/** \brief Opens a file and checks the operation was successful.
+ *
+ * Opens a file, and checks the return value to ensure that the operation
+ * was successful.
+ * \param[in] filename the file to open.
+ * \param[in] mode the cstdio fopen mode to use.
+ * \return The file handle. */
+inline FILE* safe_fopen(const char *filename,const char *mode) {
+ FILE *fp=fopen(filename,mode);
+ if(fp==NULL) {
+ fprintf(stderr,"voro++: Unable to open file '%s'\n",filename);
+ exit(VOROPP_FILE_ERROR);
+ }
+ return fp;
+}
+
+void voro_print_vector(std::vector<int> &v,FILE *fp=stdout);
+void voro_print_vector(std::vector<double> &v,FILE *fp=stdout);
+void voro_print_face_vertices(std::vector<int> &v,FILE *fp=stdout);
+
+}
+
+#endif
Index: extern/voro++/src/rad_option.hh
===================================================================
--- extern/voro++/src/rad_option.hh (revision 0)
+++ extern/voro++/src/rad_option.hh (revision 0)
@@ -0,0 +1,158 @@
+// Voro++, a 3D cell-based Voronoi library
+//
+// Author : Chris H. Rycroft (LBL / UC Berkeley)
+// Email : chr@alum.mit.edu
+// Date : August 30th 2011
+
+/** \file rad_option.hh
+ * \brief Header file for the classes encapsulating functionality for the
+ * regular and radical Voronoi tessellations. */
+
+#ifndef VOROPP_RAD_OPTION_HH
+#define VOROPP_RAD_OPTION_HH
+
+#include <cmath>
+
+namespace voro {
+
+/** \brief Class containing all of the routines that are specific to computing
+ * the regular Voronoi tessellation.
+ *
+ * The container and container_periodic classes are derived from this class,
+ * and during the Voronoi cell computation, these routines are used to create
+ * the regular Voronoi tessellation. */
+class radius_mono {
+ protected:
+ /** This is called prior to computing a Voronoi cell for a
+ * given particle to initialize any required constants.
+ * \param[in] ijk the block that the particle is within.
+ * \param[in] s the index of the particle within the block. */
+ inline void r_init(int ijk,int s) {}
+ /** Sets a required constant to be used when carrying out a
+ * plane bounds check. */
+ inline void r_prime(double rv) {}
+ /** Carries out a radius bounds check.
+ * \param[in] crs the radius squared to be tested.
+ * \param[in] mrs the current maximum distance to a Voronoi
+ * vertex multiplied by two.
+ * \return True if particles at this radius could not possibly
+ * cut the cell, false otherwise. */
+ inline bool r_ctest(double crs,double mrs) {return crs>mrs;}
+ /** Scales a plane displacement during a plane bounds check.
+ * \param[in] lrs the plane displacement.
+ * \return The scaled value. */
+ inline double r_cutoff(double lrs) {return lrs;}
+ /** Adds the maximum radius squared to a given value.
+ * \param[in] rs the value to consider.
+ * \return The value with the radius squared added. */
+ inline double r_max_add(double rs) {return rs;}
+ /** Subtracts the radius squared of a particle from a given
+ * value.
+ * \param[in] rs the value to consider.
+ * \param[in] ijk the block that the particle is within.
+ * \param[in] q the index of the particle within the block.
+ * \return The value with the radius squared subtracted. */
+ inline double r_current_sub(double rs,int ijk,int q) {return rs;}
+ /** Scales a plane displacement prior to use in the plane cutting
+ * algorithm.
+ * \param[in] rs the initial plane displacement.
+ * \param[in] ijk the block that the particle is within.
+ * \param[in] q the index of the particle within the block.
+ * \return The scaled plane displacement. */
+ inline double r_scale(double rs,int ijk,int q) {return rs;}
+ /** Scales a plane displacement prior to use in the plane
+ * cutting algorithm, and also checks if it could possibly cut
+ * the cell.
+ * \param[in,out] rs the plane displacement to be scaled.
+ * \param[in] mrs the current maximum distance to a Voronoi
+ * vertex multiplied by two.
+ * \param[in] ijk the block that the particle is within.
+ * \param[in] q the index of the particle within the block.
+ * \return True if the cell could possibly cut the cell, false
+ * otherwise. */
+ inline bool r_scale_check(double &rs,double mrs,int ijk,int q) {return rs<mrs;}
+};
+
+/** \brief Class containing all of the routines that are specific to computing
+ * the radical Voronoi tessellation.
+ *
+ * The container_poly and container_periodic_poly classes are derived from this
+ * class, and during the Voronoi cell computation, these routines are used to
+ * create the radical Voronoi tessellation. */
+class radius_poly {
+ public:
+ /** A two-dimensional array holding particle positions and radii. */
+ double **ppr;
+ /** The current maximum radius of any particle, used to
+ * determine when to cut off the radical Voronoi computation.
+ * */
+ double max_radius;
+ /** The class constructor sets the maximum particle radius to
+ * be zero. */
+ radius_poly() : max_radius(0) {}
+ protected:
+ /** This is called prior to computing a Voronoi cell for a
+ * given particle to initialize any required constants.
+ * \param[in] ijk the block that the particle is within.
+ * \param[in] s the index of the particle within the block. */
+ inline void r_init(int ijk,int s) {
+ r_rad=ppr[ijk][4*s+3]*ppr[ijk][4*s+3];
+ r_mul=r_rad-max_radius*max_radius;
+ }
+ /** Sets a required constant to be used when carrying out a
+ * plane bounds check. */
+ inline void r_prime(double rv) {r_val=1+r_mul/rv;}
+ /** Carries out a radius bounds check.
+ * \param[in] crs the radius squared to be tested.
+ * \param[in] mrs the current maximum distance to a Voronoi
+ * vertex multiplied by two.
+ * \return True if particles at this radius could not possibly
+ * cut the cell, false otherwise. */
+ inline bool r_ctest(double crs,double mrs) {return crs+r_mul>sqrt(mrs*crs);}
+ /** Scales a plane displacement during a plane bounds check.
+ * \param[in] lrs the plane displacement.
+ * \return The scaled value. */
+ inline double r_cutoff(double lrs) {return lrs*r_val;}
+ /** Adds the maximum radius squared to a given value.
+ * \param[in] rs the value to consider.
+ * \return The value with the radius squared added. */
+ inline double r_max_add(double rs) {return rs+max_radius*max_radius;}
+ /** Subtracts the radius squared of a particle from a given
+ * value.
+ * \param[in] rs the value to consider.
+ * \param[in] ijk the block that the particle is within.
+ * \param[in] q the index of the particle within the block.
+ * \return The value with the radius squared subtracted. */
+ inline double r_current_sub(double rs,int ijk,int q) {
+ return rs-ppr[ijk][4*q+3]*ppr[ijk][4*q+3];
+ }
+ /** Scales a plane displacement prior to use in the plane cutting
+ * algorithm.
+ * \param[in] rs the initial plane displacement.
+ * \param[in] ijk the block that the particle is within.
+ * \param[in] q the index of the particle within the block.
+ * \return The scaled plane displacement. */
+ inline double r_scale(double rs,int ijk,int q) {
+ return rs+r_rad-ppr[ijk][4*q+3]*ppr[ijk][4*q+3];
+ }
+ /** Scales a plane displacement prior to use in the plane
+ * cutting algorithm, and also checks if it could possibly cut
+ * the cell.
+ * \param[in,out] rs the plane displacement to be scaled.
+ * \param[in] mrs the current maximum distance to a Voronoi
+ * vertex multiplied by two.
+ * \param[in] ijk the block that the particle is within.
+ * \param[in] q the index of the particle within the block.
+ * \return True if the cell could possibly cut the cell, false
+ * otherwise. */
+ inline bool r_scale_check(double &rs,double mrs,int ijk,int q) {
+ double trs=rs;
+ rs+=r_rad-ppr[ijk][4*q+3]*ppr[ijk][4*q+3];
+ return rs<sqrt(mrs*trs);
+ }
+ private:
+ double r_rad,r_mul,r_val;
+};
+
+}
+#endif
Index: extern/voro++/src/c_loops.hh
===================================================================
--- extern/voro++/src/c_loops.hh (revision 0)
+++ extern/voro++/src/c_loops.hh (revision 0)
@@ -0,0 +1,456 @@
+// Voro++, a 3D cell-based Voronoi library
+//
+// Author : Chris H. Rycroft (LBL / UC Berkeley)
+// Email : chr@alum.mit.edu
+// Date : August 30th 2011
+
+/** \file c_loops.hh
+ * \brief Header file for the loop classes. */
+
+#ifndef VOROPP_C_LOOPS_HH
+#define VOROPP_C_LOOPS_HH
+
+#include "config.hh"
+
+namespace voro {
+
+/** A type associated with a c_loop_subset class, determining what type of
+ * geometrical region to loop over. */
+enum c_loop_subset_mode {
+ sphere,
+ box,
+ no_check
+};
+
+/** \brief A class for storing ordering information when particles are added to
+ * a container.
+ *
+ * When particles are added to a container class, they are sorted into an
+ * internal computational grid of blocks. The particle_order class provides a
+ * mechanism for remembering which block particles were sorted into. The import
+ * and put routines in the container class have variants that also take a
+ * particle_order class. Each time they are called, they will store the block
+ * that the particle was sorted into, plus the position of the particle within
+ * the block. The particle_order class can used by the c_loop_order class to
+ * specifically loop over the particles that have their information stored
+ * within it. */
+class particle_order {
+ public:
+ /** A pointer to the array holding the ordering. */
+ int *o;
+ /** A pointer to the next position in the ordering array in
+ * which to store an entry. */
+ int *op;
+ /** The current memory allocation for the class, set to the
+ * number of entries which can be stored. */
+ int size;
+ /** The particle_order constructor allocates memory to store the
+ * ordering information.
+ * \param[in] init_size the initial amount of memory to
+ * allocate. */
+ particle_order(int init_size=init_ordering_size)
+ : o(new int[init_size<<1]),op(o),size(init_size) {}
+ /** The particle_order destructor frees the dynamically allocated
+ * memory used to store the ordering information. */
+ ~particle_order() {
+ delete [] o;
+ }
+ /** Adds a record to the order, corresponding to the memory
+ * address of where a particle was placed into the container.
+ * \param[in] ijk the block into which the particle was placed.
+ * \param[in] q the position within the block where the
+ * particle was placed. */
+ inline void add(int ijk,int q) {
+ if(op==o+size) add_ordering_memory();
+ *(op++)=ijk;*(op++)=q;
+ }
+ private:
+ void add_ordering_memory();
+};
+
+/** \brief Base class for looping over particles in a container.
+ *
+ * This class forms the base of all classes that can loop over a subset of
+ * particles in a contaner in some order. When initialized, it stores constants
+ * about the corresponding container geometry. It also contains a number of
+ * routines for interrogating which particle currently being considered by the
+ * loop, which are common between all of the derived classes. */
+class c_loop_base {
+ public:
+ /** The number of blocks in the x direction. */
+ const int nx;
+ /** The number of blocks in the y direction. */
+ const int ny;
+ /** The number of blocks in the z direction. */
+ const int nz;
+ /** A constant, set to the value of nx multiplied by ny, which
+ * is used in the routines that step through blocks in
+ * sequence. */
+ const int nxy;
+ /** A constant, set to the value of nx*ny*nz, which is used in
+ * the routines that step through blocks in sequence. */
+ const int nxyz;
+ /** The number of floating point numbers per particle in the
+ * associated container data structure. */
+ const int ps;
+ /** A pointer to the particle position information in the
+ * associated container data structure. */
+ double **p;
+ /** A pointer to the particle ID information in the associated
+ * container data structure. */
+ int **id;
+ /** A pointer to the particle counts in the associated
+ * container data structure. */
+ int *co;
+ /** The current x-index of the block under consideration by the
+ * loop. */
+ int i;
+ /** The current y-index of the block under consideration by the
+ * loop. */
+ int j;
+ /** The current z-index of the block under consideration by the
+ * loop. */
+ int k;
+ /** The current index of the block under consideration by the
+ * loop. */
+ int ijk;
+ /** The index of the particle under consideration within the current
+ * block. */
+ int q;
+ /** The constructor copies several necessary constants from the
+ * base container class.
+ * \param[in] con the container class to use. */
+ template<class c_class>
+ c_loop_base(c_class &con) : nx(con.nx), ny(con.ny), nz(con.nz),
+ nxy(con.nxy), nxyz(con.nxyz), ps(con.ps),
+ p(con.p), id(con.id), co(con.co) {}
+ /** Returns the position vector of the particle currently being
+ * considered by the loop.
+ * \param[out] (x,y,z) the position vector of the particle. */
+ inline void pos(double &x,double &y,double &z) {
+ double *pp=p[ijk]+ps*q;
+ x=*(pp++);y=*(pp++);z=*pp;
+ }
+ /** Returns the ID, position vector, and radius of the particle
+ * currently being considered by the loop.
+ * \param[out] pid the particle ID.
+ * \param[out] (x,y,z) the position vector of the particle.
+ * \param[out] r the radius of the particle. If no radius
+ * information is available the default radius
+ * value is returned. */
+ inline void pos(int &pid,double &x,double &y,double &z,double &r) {
+ pid=id[ijk][q];
+ double *pp=p[ijk]+ps*q;
+ x=*(pp++);y=*(pp++);z=*pp;
+ r=ps==3?default_radius:*(++pp);
+ }
+ /** Returns the x position of the particle currently being
+ * considered by the loop. */
+ inline double x() {return p[ijk][ps*q];}
+ /** Returns the y position of the particle currently being
+ * considered by the loop. */
+ inline double y() {return p[ijk][ps*q+1];}
+ /** Returns the z position of the particle currently being
+ * considered by the loop. */
+ inline double z() {return p[ijk][ps*q+2];}
+ /** Returns the ID of the particle currently being considered
+ * by the loop. */
+ inline int pid() {return id[ijk][q];}
+};
+
+/** \brief Class for looping over all of the particles in a container.
+ *
+ * This is one of the simplest loop classes, that scans the computational
+ * blocks in order, and scans all the particles within each block in order. */
+class c_loop_all : public c_loop_base {
+ public:
+ /** The constructor copies several necessary constants from the
+ * base container class.
+ * \param[in] con the container class to use. */
+ template<class c_class>
+ c_loop_all(c_class &con) : c_loop_base(con) {}
+ /** Sets the class to consider the first particle.
+ * \return True if there is any particle to consider, false
+ * otherwise. */
+ inline bool start() {
+ i=j=k=ijk=q=0;
+ while(co[ijk]==0) if(!next_block()) return false;
+ return true;
+ }
+ /** Finds the next particle to test.
+ * \return True if there is another particle, false if no more
+ * particles are available. */
+ inline bool inc() {
+ q++;
+ if(q>=co[ijk]) {
+ q=0;
+ do {
+ if(!next_block()) return false;
+ } while(co[ijk]==0);
+ }
+ return true;
+ }
+ private:
+ /** Updates the internal variables to find the next
+ * computational block with any particles.
+ * \return True if another block is found, false if there are
+ * no more blocks. */
+ inline bool next_block() {
+ ijk++;
+ i++;
+ if(i==nx) {
+ i=0;j++;
+ if(j==ny) {
+ j=0;k++;
+ if(ijk==nxyz) return false;
+ }
+ }
+ return true;
+ }
+};
+
+/** \brief Class for looping over a subset of particles in a container.
+ *
+ * This class can loop over a subset of particles in a certain geometrical
+ * region within the container. The class can be set up to loop over a
+ * rectangular box or sphere. It can also rectangular group of internal
+ * computational blocks. */
+class c_loop_subset : public c_loop_base {
+ public:
+ /** The current mode of operation, determining whether tests
+ * should be applied to particles to ensure they are within a
+ * certain geometrical object. */
+ c_loop_subset_mode mode;
+ /** The constructor copies several necessary constants from the
+ * base container class.
+ * \param[in] con the container class to use. */
+ template<class c_class>
+ c_loop_subset(c_class &con) : c_loop_base(con), ax(con.ax), ay(con.ay), az(con.az),
+ sx(con.bx-ax), sy(con.by-ay), sz(con.bz-az), xsp(con.xsp), ysp(con.ysp), zsp(con.zsp),
+ xperiodic(con.xperiodic), yperiodic(con.yperiodic), zperiodic(con.zperiodic) {}
+ void setup_sphere(double vx,double vy,double vz,double r,bool bounds_test=true);
+ void setup_box(double xmin,double xmax,double ymin,double ymax,double zmin,double zmax,bool bounds_test=true);
+ void setup_intbox(int ai_,int bi_,int aj_,int bj_,int ak_,int bk_);
+ bool start();
+ /** Finds the next particle to test.
+ * \return True if there is another particle, false if no more
+ * particles are available. */
+ inline bool inc() {
+ do {
+ q++;
+ while(q>=co[ijk]) {q=0;if(!next_block()) return false;}
+ } while(mode!=no_check&&out_of_bounds());
+ return true;
+ }
+ private:
+ const double ax,ay,az,sx,sy,sz,xsp,ysp,zsp;
+ const bool xperiodic,yperiodic,zperiodic;
+ double px,py,pz,apx,apy,apz;
+ double v0,v1,v2,v3,v4,v5;
+ int ai,bi,aj,bj,ak,bk,s;
+ int ci,cj,ck,di,dj,dk,inc1,inc2;
+ inline int step_mod(int a,int b) {return a>=0?a%b:b-1-(b-1-a)%b;}
+ inline int step_div(int a,int b) {return a>=0?a/b:-1+(a+1)/b;}
+ inline int step_int(double a) {return a<0?int(a)-1:int(a);}
+ void setup_common();
+ bool next_block();
+ bool out_of_bounds();
+};
+
+/** \brief Class for looping over all of the particles specified in a
+ * pre-assembled particle_order class.
+ *
+ * The particle_order class can be used to create a specific order of particles
+ * within the container. This class can then loop over these particles in this
+ * order. The class is particularly useful in cases where the ordering of the
+ * output must match the ordering of particles as they were inserted into the
+ * container. */
+class c_loop_order : public c_loop_base {
+ public:
+ /** A reference to the ordering class to use. */
+ particle_order &vo;
+ /** A pointer to the current position in the ordering class. */
+ int *cp;
+ /** A pointer to the end position in the ordering class. */
+ int *op;
+ /** The constructor copies several necessary constants from the
+ * base class, and sets up a reference to the ordering class to
+ * use.
+ * \param[in] con the container class to use.
+ * \param[in] vo_ the ordering class to use. */
+ template<class c_class>
+ c_loop_order(c_class &con,particle_order &vo_)
+ : c_loop_base(con), vo(vo_), nx(con.nx), nxy(con.nxy) {}
+ /** Sets the class to consider the first particle.
+ * \return True if there is any particle to consider, false
+ * otherwise. */
+ inline bool start() {
+ cp=vo.o;op=vo.op;
+ if(cp!=op) {
+ ijk=*(cp++);decode();
+ q=*(cp++);
+ return true;
+ } else return false;
+ }
+ /** Finds the next particle to test.
+ * \return True if there is another particle, false if no more
+ * particles are available. */
+ inline bool inc() {
+ if(cp==op) return false;
+ ijk=*(cp++);decode();
+ q=*(cp++);
+ return true;
+ }
+ private:
+ /** The number of computational blocks in the x direction. */
+ const int nx;
+ /** The number of computational blocks in a z-slice. */
+ const int nxy;
+ /** Takes the current block index and computes indices in the
+ * x, y, and z directions. */
+ inline void decode() {
+ k=ijk/nxy;
+ int ijkt=ijk-nxy*k;
+ j=ijkt/nx;
+ i=ijkt-j*nx;
+ }
+};
+
+/** \brief A class for looping over all particles in a container_periodic or
+ * container_periodic_poly class.
+ *
+ * Since the container_periodic and container_periodic_poly classes have a
+ * fundamentally different memory organization, the regular loop classes cannot
+ * be used with them. */
+class c_loop_all_periodic : public c_loop_base {
+ public:
+ /** The constructor copies several necessary constants from the
+ * base periodic container class.
+ * \param[in] con the periodic container class to use. */
+ template<class c_class>
+ c_loop_all_periodic(c_class &con) : c_loop_base(con), ey(con.ey), ez(con.ez), wy(con.wy), wz(con.wz),
+ ijk0(nx*(ey+con.oy*ez)), inc2(2*nx*con.ey+1) {}
+ /** Sets the class to consider the first particle.
+ * \return True if there is any particle to consider, false
+ * otherwise. */
+ inline bool start() {
+ i=0;
+ j=ey;
+ k=ez;
+ ijk=ijk0;
+ q=0;
+ while(co[ijk]==0) if(!next_block()) return false;
+ return true;
+ }
+ /** Finds the next particle to test.
+ * \return True if there is another particle, false if no more
+ * particles are available. */
+ inline bool inc() {
+ q++;
+ if(q>=co[ijk]) {
+ q=0;
+ do {
+ if(!next_block()) return false;
+ } while(co[ijk]==0);
+ }
+ return true;
+ }
+ private:
+ /** The lower y index (inclusive) of the primary domain within
+ * the block structure. */
+ int ey;
+ /** The lower y index (inclusive) of the primary domain within
+ * the block structure. */
+ int ez;
+ /** The upper y index (exclusive) of the primary domain within
+ * the block structure. */
+ int wy;
+ /** The upper z index (exclusive) of the primary domain within
+ * the block structure. */
+ int wz;
+ /** The index of the (0,0,0) block within the block structure.
+ */
+ int ijk0;
+ /** A value to increase ijk by when the z index is increased.
+ */
+ int inc2;
+ /** Updates the internal variables to find the next
+ * computational block with any particles.
+ * \return True if another block is found, false if there are
+ * no more blocks. */
+ inline bool next_block() {
+ i++;
+ if(i==nx) {
+ i=0;j++;
+ if(j==wy) {
+ j=ey;k++;
+ if(k==wz) return false;
+ ijk+=inc2;
+ } else ijk++;
+ } else ijk++;
+ return true;
+ }
+};
+
+/** \brief Class for looping over all of the particles specified in a
+ * pre-assembled particle_order class, for use with container_periodic classes.
+ *
+ * The particle_order class can be used to create a specific order of particles
+ * within the container. This class can then loop over these particles in this
+ * order. The class is particularly useful in cases where the ordering of the
+ * output must match the ordering of particles as they were inserted into the
+ * container. */
+class c_loop_order_periodic : public c_loop_base {
+ public:
+ /** A reference to the ordering class to use. */
+ particle_order &vo;
+ /** A pointer to the current position in the ordering class. */
+ int *cp;
+ /** A pointer to the end position in the ordering class. */
+ int *op;
+ /** The constructor copies several necessary constants from the
+ * base class, and sets up a reference to the ordering class to
+ * use.
+ * \param[in] con the container class to use.
+ * \param[in] vo_ the ordering class to use. */
+ template<class c_class>
+ c_loop_order_periodic(c_class &con,particle_order &vo_)
+ : c_loop_base(con), vo(vo_), nx(con.nx), oxy(con.nx*con.oy) {}
+ /** Sets the class to consider the first particle.
+ * \return True if there is any particle to consider, false
+ * otherwise. */
+ inline bool start() {
+ cp=vo.o;op=vo.op;
+ if(cp!=op) {
+ ijk=*(cp++);decode();
+ q=*(cp++);
+ return true;
+ } else return false;
+ }
+ /** Finds the next particle to test.
+ * \return True if there is another particle, false if no more
+ * particles are available. */
+ inline bool inc() {
+ if(cp==op) return false;
+ ijk=*(cp++);decode();
+ q=*(cp++);
+ return true;
+ }
+ private:
+ /** The number of computational blocks in the x direction. */
+ const int nx;
+ /** The number of computational blocks in a z-slice. */
+ const int oxy;
+ /** Takes the current block index and computes indices in the
+ * x, y, and z directions. */
+ inline void decode() {
+ k=ijk/oxy;
+ int ijkt=ijk-oxy*k;
+ j=ijkt/nx;
+ i=ijkt-j*nx;
+ }
+};
+
+}
+
+#endif
Index: extern/voro++/src/v_base.hh
===================================================================
--- extern/voro++/src/v_base.hh (revision 0)
+++ extern/voro++/src/v_base.hh (revision 0)
@@ -0,0 +1,88 @@
+// Voro++, a 3D cell-based Voronoi library
+//
+// Author : Chris H. Rycroft (LBL / UC Berkeley)
+// Email : chr@alum.mit.edu
+// Date : August 30th 2011
+
+/** \file v_base.hh
+ * \brief Header file for the base Voronoi container class. */
+
+#ifndef VOROPP_V_BASE_HH
+#define VOROPP_V_BASE_HH
+
+#include "worklist.hh"
+
+namespace voro {
+
+/** \brief Class containing data structures common across all particle container classes.
+ *
+ * This class contains constants and data structures that are common across all
+ * particle container classes. It contains constants setting the size of the
+ * underlying subgrid of blocks that forms the basis of the Voronoi cell
+ * computations. It also constructs bound tables that are used in the Voronoi
+ * cell computation, and contains a number of routines that are common across
+ * all container classes. */
+class voro_base {
+ public:
+ /** The number of blocks in the x direction. */
+ const int nx;
+ /** The number of blocks in the y direction. */
+ const int ny;
+ /** The number of blocks in the z direction. */
+ const int nz;
+ /** A constant, set to the value of nx multiplied by ny, which
+ * is used in the routines that step through blocks in
+ * sequence. */
+ const int nxy;
+ /** A constant, set to the value of nx*ny*nz, which is used in
+ * the routines that step through blocks in sequence. */
+ const int nxyz;
+ /** The size of a computational block in the x direction. */
+ const double boxx;
+ /** The size of a computational block in the y direction. */
+ const double boxy;
+ /** The size of a computational block in the z direction. */
+ const double boxz;
+ /** The inverse box length in the x direction. */
+ const double xsp;
+ /** The inverse box length in the y direction. */
+ const double ysp;
+ /** The inverse box length in the z direction. */
+ const double zsp;
+ /** An array to hold the minimum distances associated with the
+ * worklists. This array is initialized during container
+ * construction, by the initialize_radii() routine. */
+ double *mrad;
+ /** The pre-computed block worklists. */
+ static const unsigned int wl[wl_seq_length*wl_hgridcu];
+ bool contains_neighbor(const char* format);
+ voro_base(int nx_,int ny_,int nz_,double boxx_,double boxy_,double boxz_);
+ ~voro_base() {delete [] mrad;}
+ protected:
+ /** A custom int function that returns consistent stepping
+ * for negative numbers, so that (-1.5, -0.5, 0.5, 1.5) maps
+ * to (-2,-1,0,1).
+ * \param[in] a the number to consider.
+ * \return The value of the custom int operation. */
+ inline int step_int(double a) {return a<0?int(a)-1:int(a);}
+ /** A custom modulo function that returns consistent stepping
+ * for negative numbers. For example, (-2,-1,0,1,2) step_mod 2
+ * is (0,1,0,1,0).
+ * \param[in] (a,b) the input integers.
+ * \return The value of a modulo b, consistent for negative
+ * numbers. */
+ inline int step_mod(int a,int b) {return a>=0?a%b:b-1-(b-1-a)%b;}
+ /** A custom integer division function that returns consistent
+ * stepping for negative numbers. For example, (-2,-1,0,1,2)
+ * step_div 2 is (-1,-1,0,0,1).
+ * \param[in] (a,b) the input integers.
+ * \return The value of a div b, consistent for negative
+ * numbers. */
+ inline int step_div(int a,int b) {return a>=0?a/b:-1+(a+1)/b;}
+ private:
+ void compute_minimum(double &minr,double &xlo,double &xhi,double &ylo,double &yhi,double &zlo,double &zhi,int ti,int tj,int tk);
+};
+
+}
+
+#endif
Index: extern/voro++/src/wall.hh
===================================================================
--- extern/voro++/src/wall.hh (revision 0)
+++ extern/voro++/src/wall.hh (revision 0)
@@ -0,0 +1,119 @@
+// Voro++, a 3D cell-based Voronoi library
+//
+// Author : Chris H. Rycroft (LBL / UC Berkeley)
+// Email : chr@alum.mit.edu
+// Date : August 30th 2011
+
+/** \file wall.hh
+ * \brief Header file for the derived wall classes. */
+
+#ifndef VOROPP_WALL_HH
+#define VOROPP_WALL_HH
+
+#include "cell.hh"
+#include "container.hh"
+
+namespace voro {
+
+/** \brief A class representing a spherical wall object.
+ *
+ * This class represents a spherical wall object. */
+struct wall_sphere : public wall {
+ public:
+ /** Constructs a spherical wall object.
+ * \param[in] w_id_ an ID number to associate with the wall for
+ * neighbor tracking.
+ * \param[in] (xc_,yc_,zc_) a position vector for the sphere's
+ * center.
+ * \param[in] rc_ the radius of the sphere. */
+ wall_sphere(double xc_,double yc_,double zc_,double rc_,int w_id_=-99)
+ : w_id(w_id_), xc(xc_), yc(yc_), zc(zc_), rc(rc_) {}
+ bool point_inside(double x,double y,double z);
+ template<class v_cell>
+ bool cut_cell_base(v_cell &c,double x,double y,double z);
+ bool cut_cell(voronoicell &c,double x,double y,double z) {return cut_cell_base(c,x,y,z);}
+ bool cut_cell(voronoicell_neighbor &c,double x,double y,double z) {return cut_cell_base(c,x,y,z);}
+ private:
+ const int w_id;
+ const double xc,yc,zc,rc;
+};
+
+/** \brief A class representing a plane wall object.
+ *
+ * This class represents a single plane wall object. */
+struct wall_plane : public wall {
+ public:
+ /** Constructs a plane wall object.
+ * \param[in] (xc_,yc_,zc_) a normal vector to the plane.
+ * \param[in] ac_ a displacement along the normal vector.
+ * \param[in] w_id_ an ID number to associate with the wall for
+ * neighbor tracking. */
+ wall_plane(double xc_,double yc_,double zc_,double ac_,int w_id_=-99)
+ : w_id(w_id_), xc(xc_), yc(yc_), zc(zc_), ac(ac_) {}
+ bool point_inside(double x,double y,double z);
+ template<class v_cell>
+ bool cut_cell_base(v_cell &c,double x,double y,double z);
+ bool cut_cell(voronoicell &c,double x,double y,double z) {return cut_cell_base(c,x,y,z);}
+ bool cut_cell(voronoicell_neighbor &c,double x,double y,double z) {return cut_cell_base(c,x,y,z);}
+ private:
+ const int w_id;
+ const double xc,yc,zc,ac;
+};
+
+/** \brief A class representing a cylindrical wall object.
+ *
+ * This class represents a open cylinder wall object. */
+struct wall_cylinder : public wall {
+ public:
+ /** Constructs a cylinder wall object.
+ * \param[in] (xc_,yc_,zc_) a point on the axis of the
+ * cylinder.
+ * \param[in] (xa_,ya_,za_) a vector pointing along the
+ * direction of the cylinder.
+ * \param[in] rc_ the radius of the cylinder
+ * \param[in] w_id_ an ID number to associate with the wall for
+ * neighbor tracking. */
+ wall_cylinder(double xc_,double yc_,double zc_,double xa_,double ya_,double za_,double rc_,int w_id_=-99)
+ : w_id(w_id_), xc(xc_), yc(yc_), zc(zc_), xa(xa_), ya(ya_), za(za_),
+ asi(1/(xa_*xa_+ya_*ya_+za_*za_)), rc(rc_) {}
+ bool point_inside(double x,double y,double z);
+ template<class v_cell>
+ bool cut_cell_base(v_cell &c,double x,double y,double z);
+ bool cut_cell(voronoicell &c,double x,double y,double z) {return cut_cell_base(c,x,y,z);}
+ bool cut_cell(voronoicell_neighbor &c,double x,double y,double z) {return cut_cell_base(c,x,y,z);}
+ private:
+ const int w_id;
+ const double xc,yc,zc,xa,ya,za,asi,rc;
+};
+
+
+/** \brief A class representing a conical wall object.
+ *
+ * This class represents a cone wall object. */
+struct wall_cone : public wall {
+ public:
+ /** Constructs a cone wall object.
+ * \param[in] (xc_,yc_,zc_) the apex of the cone.
+ * \param[in] (xa_,ya_,za_) a vector pointing along the axis of
+ * the cone.
+ * \param[in] ang the angle (in radians) of the cone, measured
+ * from the axis.
+ * \param[in] w_id_ an ID number to associate with the wall for
+ * neighbor tracking. */
+ wall_cone(double xc_,double yc_,double zc_,double xa_,double ya_,double za_,double ang,int w_id_=-99)
+ : w_id(w_id_), xc(xc_), yc(yc_), zc(zc_), xa(xa_), ya(ya_), za(za_),
+ asi(1/(xa_*xa_+ya_*ya_+za_*za_)),
+ gra(tan(ang)), sang(sin(ang)), cang(cos(ang)) {}
+ bool point_inside(double x,double y,double z);
+ template<class v_cell>
+ bool cut_cell_base(v_cell &c,double x,double y,double z);
+ bool cut_cell(voronoicell &c,double x,double y,double z) {return cut_cell_base(c,x,y,z);}
+ bool cut_cell(voronoicell_neighbor &c,double x,double y,double z) {return cut_cell_base(c,x,y,z);}
+ private:
+ const int w_id;
+ const double xc,yc,zc,xa,ya,za,asi,gra,sang,cang;
+};
+
+}
+
+#endif
Index: extern/voro++/src/cmd_line.cc
===================================================================
--- extern/voro++/src/cmd_line.cc (revision 0)
+++ extern/voro++/src/cmd_line.cc (revision 0)
@@ -0,0 +1,498 @@
+// Voro++, a 3D cell-based Voronoi library
+//
+// Author : Chris H. Rycroft (LBL / UC Berkeley)
+// Email : chr@alum.mit.edu
+// Date : August 30th 2011
+
+/** \file cmd_line.cc
+ * \brief Source code for the command-line utility. */
+
+#include <cstring>
+
+#include "voro++.hh"
+using namespace voro;
+
+enum blocks_mode {
+ none,
+ length_scale,
+ specified
+};
+
+// A maximum allowed number of regions, to prevent enormous amounts of memory
+// being allocated
+const int max_regions=16777216;
+
+// This message gets displayed if the user requests the help flag
+void help_message() {
+ puts("Voro++ version 0.4.5, by Chris H. Rycroft (UC Berkeley/LBL)\n\n"
+ "Syntax: voro++ [options] <x_min> <x_max> <y_min>\n"
+ " <y_max> <z_min> <z_max> <filename>\n\n"
+ "By default, the utility reads in the input file of particle IDs and positions,\n"
+ "computes the Voronoi cell for each, and then creates <filename.vol> with an\n"
+ "additional column containing the volume of each Voronoi cell.\n\n"
+ "Available options:\n"
+ " -c <str> : Specify a custom output string\n"
+ " -g : Turn on the gnuplot output to <filename.gnu>\n"
+ " -h/--help : Print this information\n"
+ " -hc : Print information about custom output\n"
+ " -l <len> : Manually specify a length scale to configure the internal\n"
+ " computational grid\n"
+ " -m <mem> : Manually choose the memory allocation per grid block\n"
+ " (default 8)\n"
+ " -n [3] : Manually specify the internal grid size\n"
+ " -o : Ensure that the output file has the same order as the input\n"
+ " file\n"
+ " -p : Make container periodic in all three directions\n"
+ " -px : Make container periodic in the x direction\n"
+ " -py : Make container periodic in the y direction\n"
+ " -pz : Make container periodic in the z direction\n"
+ " -r : Assume the input file has an extra coordinate for radii\n"
+ " -v : Verbose output\n"
+ " --version : Print version information\n"
+ " -wb [6] : Add six plane wall objects to make rectangular box containing\n"
+ " the space x1<x<x2, x3<y<x4, x5<z<x6\n"
+ " -wc [7] : Add a cylinder wall object, centered on (x1,x2,x3),\n"
+ " pointing in (x4,x5,x6), radius x7\n"
+ " -wo [7] : Add a conical wall object, apex at (x1,x2,x3), axis\n"
+ " along (x4,x5,x6), angle x7 in radians\n"
+ " -ws [4] : Add a sphere wall object, centered on (x1,x2,x3),\n"
+ " with radius x4\n"
+ " -wp [4] : Add a plane wall object, with normal (x1,x2,x3),\n"
+ " and displacement x4\n"
+ " -y : Save POV-Ray particles to <filename_p.pov> and POV-Ray Voronoi\n"
+ " cells to <filename_v.pov>\n"
+ " -yp : Save only POV-Ray particles to <filename_p.pov>\n"
+ " -yv : Save only POV-Ray Voronoi cells to <filename_v.pov>");
+}
+
+// This message gets displayed if the user requests information about doing
+// custom output
+void custom_output_message() {
+ puts("The \"-c\" option allows a string to be specified that will customize the output\n"
+ "file to contain a variety of statistics about each computed Voronoi cell. The\n"
+ "string is similar to the standard C printf() function, made up of text with\n"
+ "additional control sequences that begin with percentage signs that are expanded\n"
+ "to different statistics. See http://math.lbl.gov/voro++/doc/custom.html for more\n"
+ "information.\n"
+ "\nParticle-related:\n"
+ " %i The particle ID number\n"
+ " %x The x coordinate of the particle\n"
+ " %y The y coordinate of the particle\n"
+ " %z The z coordinate of the particle\n"
+ " %q The position vector of the particle, short for \"%x %y %z\"\n"
+ " %r The radius of the particle (only printed if -p enabled)\n"
+ "\nVertex-related:\n"
+ " %w The number of vertices in the Voronoi cell\n"
+ " %p A list of the vertices of the Voronoi cell in the format (x,y,z),\n"
+ " relative to the particle center\n"
+ " %P A list of the vertices of the Voronoi cell in the format (x,y,z),\n"
+ " relative to the global coordinate system\n"
+ " %o A list of the orders of each vertex\n"
+ " %m The maximum radius squared of a vertex position, relative to the\n"
+ " particle center\n"
+ "\nEdge-related:\n"
+ " %g The number of edges of the Voronoi cell\n"
+ " %E The total edge distance\n"
+ " %e A list of perimeters of each face\n"
+ "\nFace-related:\n"
+ " %s The number of faces of the Voronoi cell\n"
+ " %F The total surface area of the Voronoi cell\n"
+ " %A A frequency table of the number of edges for each face\n"
+ " %a A list of the number of edges for each face\n"
+ " %f A list of areas of each face\n"
+ " %t A list of bracketed sequences of vertices that make up each face\n"
+ " %l A list of normal vectors for each face\n"
+ " %n A list of neighboring particle or wall IDs corresponding to each face\n"
+ "\nVolume-related:\n"
+ " %v The volume of the Voronoi cell\n"
+ " %c The centroid of the Voronoi cell, relative to the particle center\n"
+ " %C The centroid of the Voronoi cell, in the global coordinate system");
+}
+
+// Ths message is displayed if the user requests version information
+void version_message() {
+ puts("Voro++ version 0.4.5 (July 27th 2012)");
+}
+
+// Prints an error message. This is called when the program is unable to make
+// sense of the command-line options.
+void error_message() {
+ fputs("voro++: Unrecognized command-line options; type \"voro++ -h\" for more\ninformation.\n",stderr);
+}
+
+// Carries out the Voronoi computation and outputs the results to the requested
+// files
+template<class c_loop,class c_class>
+void cmd_line_output(c_loop &vl,c_class &con,const char* format,FILE* outfile,FILE* gnu_file,FILE* povp_file,FILE* povv_file,bool verbose,double &vol,int &vcc,int &tp) {
+ int pid,ps=con.ps;double x,y,z,r;
+ if(con.contains_neighbor(format)) {
+ voronoicell_neighbor c;
+ if(vl.start()) do if(con.compute_cell(c,vl)) {
+ vl.pos(pid,x,y,z,r);
+ if(outfile!=NULL) c.output_custom(format,pid,x,y,z,r,outfile);
+ if(gnu_file!=NULL) c.draw_gnuplot(x,y,z,gnu_file);
+ if(povp_file!=NULL) {
+ fprintf(povp_file,"// id %d\n",pid);
+ if(ps==4) fprintf(povp_file,"sphere{<%g,%g,%g>,%g}\n",x,y,z,r);
+ else fprintf(povp_file,"sphere{<%g,%g,%g>,s}\n",x,y,z);
+ }
+ if(povv_file!=NULL) {
+ fprintf(povv_file,"// cell %d\n",pid);
+ c.draw_pov(x,y,z,povv_file);
+ }
+ if(verbose) {vol+=c.volume();vcc++;}
+ } while(vl.inc());
+ } else {
+ voronoicell c;
+ if(vl.start()) do if(con.compute_cell(c,vl)) {
+ vl.pos(pid,x,y,z,r);
+ if(outfile!=NULL) c.output_custom(format,pid,x,y,z,r,outfile);
+ if(gnu_file!=NULL) c.draw_gnuplot(x,y,z,gnu_file);
+ if(povp_file!=NULL) {
+ fprintf(povp_file,"// id %d\n",pid);
+ if(ps==4) fprintf(povp_file,"sphere{<%g,%g,%g>,%g}\n",x,y,z,r);
+ else fprintf(povp_file,"sphere{<%g,%g,%g>,s}\n",x,y,z);
+ }
+ if(povv_file!=NULL) {
+ fprintf(povv_file,"// cell %d\n",pid);
+ c.draw_pov(x,y,z,povv_file);
+ }
+ if(verbose) {vol+=c.volume();vcc++;}
+ } while(vl.inc());
+ }
+ if(verbose) tp=con.total_particles();
+}
+
+int main(int argc,char **argv) {
+ int i=1,j=-7,custom_output=0,nx,ny,nz,init_mem(8);
+ double ls=0;
+ blocks_mode bm=none;
+ bool gnuplot_output=false,povp_output=false,povv_output=false,polydisperse=false;
+ bool xperiodic=false,yperiodic=false,zperiodic=false,ordered=false,verbose=false;
+ pre_container *pcon=NULL;pre_container_poly *pconp=NULL;
+ wall_list wl;
+
+ // If there's one argument, check to see if it's requesting help.
+ // Otherwise, bail out with an error.
+ if(argc==2) {
+ if(strcmp(argv[1],"-h")==0||strcmp(argv[1],"--help")==0) {
+ help_message();return 0;
+ } else if(strcmp(argv[1],"-hc")==0) {
+ custom_output_message();return 0;
+ } else if(strcmp(argv[1],"--version")==0) {
+ version_message();return 0;
+ } else {
+ error_message();
+ return VOROPP_CMD_LINE_ERROR;
+ }
+ }
+
+ // If there aren't enough command-line arguments, then bail out
+ // with an error.
+ if(argc<7) {
+ error_message();
+ return VOROPP_CMD_LINE_ERROR;
+ }
+
+ // We have enough arguments. Now start searching for command-line
+ // options.
+ while(i<argc-7) {
+ if(strcmp(argv[i],"-c")==0) {
+ if(i>=argc-8) {error_message();wl.deallocate();return VOROPP_CMD_LINE_ERROR;}
+ if(custom_output==0) {
+ custom_output=++i;
+ } else {
+ fputs("voro++: multiple custom output strings detected\n",stderr);
+ wl.deallocate();
+ return VOROPP_CMD_LINE_ERROR;
+ }
+ } else if(strcmp(argv[i],"-g")==0) {
+ gnuplot_output=true;
+ } else if(strcmp(argv[i],"-h")==0||strcmp(argv[i],"--help")==0) {
+ help_message();wl.deallocate();return 0;
+ } else if(strcmp(argv[i],"-hc")==0) {
+ custom_output_message();wl.deallocate();return 0;
+ } else if(strcmp(argv[i],"-l")==0) {
+ if(i>=argc-8) {error_message();wl.deallocate();return VOROPP_CMD_LINE_ERROR;}
+ if(bm!=none) {
+ fputs("voro++: Conflicting options about grid setup (-l/-n)\n",stderr);
+ wl.deallocate();
+ return VOROPP_CMD_LINE_ERROR;
+ }
+ bm=length_scale;
+ i++;ls=atof(argv[i]);
+ } else if(strcmp(argv[i],"-m")==0) {
+ i++;init_mem=atoi(argv[i]);
+ } else if(strcmp(argv[i],"-n")==0) {
+ if(i>=argc-10) {error_message();wl.deallocate();return VOROPP_CMD_LINE_ERROR;}
+ if(bm!=none) {
+ fputs("voro++: Conflicting options about grid setup (-l/-n)\n",stderr);
+ wl.deallocate();
+ return VOROPP_CMD_LINE_ERROR;
+ }
+ bm=specified;
+ i++;
+ nx=atoi(argv[i++]);
+ ny=atoi(argv[i++]);
+ nz=atoi(argv[i]);
+ if(nx<=0||ny<=0||nz<=0) {
+ fputs("voro++: Computational grid specified with -n must be greater than one\n"
+ "in each direction\n",stderr);
+ wl.deallocate();
+ return VOROPP_CMD_LINE_ERROR;
+ }
+ } else if(strcmp(argv[i],"-o")==0) {
+ ordered=true;
+ } else if(strcmp(argv[i],"-p")==0) {
+ xperiodic=yperiodic=zperiodic=true;
+ } else if(strcmp(argv[i],"-px")==0) {
+ xperiodic=true;
+ } else if(strcmp(argv[i],"-py")==0) {
+ yperiodic=true;
+ } else if(strcmp(argv[i],"-pz")==0) {
+ zperiodic=true;
+ } else if(strcmp(argv[i],"-r")==0) {
+ polydisperse=true;
+ } else if(strcmp(argv[i],"-v")==0) {
+ verbose=true;
+ } else if(strcmp(argv[i],"--version")==0) {
+ version_message();
+ wl.deallocate();
+ return 0;
+ } else if(strcmp(argv[i],"-wb")==0) {
+ if(i>=argc-13) {error_message();wl.deallocate();return VOROPP_CMD_LINE_ERROR;}
+ i++;
+ double w0=atof(argv[i++]),w1=atof(argv[i++]);
+ double w2=atof(argv[i++]),w3=atof(argv[i++]);
+ double w4=atof(argv[i++]),w5=atof(argv[i]);
+ wl.add_wall(new wall_plane(-1,0,0,-w0,j));j--;
+ wl.add_wall(new wall_plane(1,0,0,w1,j));j--;
+ wl.add_wall(new wall_plane(0,-1,0,-w2,j));j--;
+ wl.add_wall(new wall_plane(0,1,0,w3,j));j--;
+ wl.add_wall(new wall_plane(0,0,-1,-w4,j));j--;
+ wl.add_wall(new wall_plane(0,0,1,w5,j));j--;
+ } else if(strcmp(argv[i],"-ws")==0) {
+ if(i>=argc-11) {error_message();wl.deallocate();return VOROPP_CMD_LINE_ERROR;}
+ i++;
+ double w0=atof(argv[i++]),w1=atof(argv[i++]);
+ double w2=atof(argv[i++]),w3=atof(argv[i]);
+ wl.add_wall(new wall_sphere(w0,w1,w2,w3,j));
+ j--;
+ } else if(strcmp(argv[i],"-wp")==0) {
+ if(i>=argc-11) {error_message();wl.deallocate();return VOROPP_CMD_LINE_ERROR;}
+ i++;
+ double w0=atof(argv[i++]),w1=atof(argv[i++]);
+ double w2=atof(argv[i++]),w3=atof(argv[i]);
+ wl.add_wall(new wall_plane(w0,w1,w2,w3,j));
+ j--;
+ } else if(strcmp(argv[i],"-wc")==0) {
+ if(i>=argc-14) {error_message();wl.deallocate();return VOROPP_CMD_LINE_ERROR;}
+ i++;
+ double w0=atof(argv[i++]),w1=atof(argv[i++]);
+ double w2=atof(argv[i++]),w3=atof(argv[i++]);
+ double w4=atof(argv[i++]),w5=atof(argv[i++]);
+ double w6=atof(argv[i]);
+ wl.add_wall(new wall_cylinder(w0,w1,w2,w3,w4,w5,w6,j));
+ j--;
+ } else if(strcmp(argv[i],"-wo")==0) {
+ if(i>=argc-14) {error_message();wl.deallocate();return VOROPP_CMD_LINE_ERROR;}
+ i++;
+ double w0=atof(argv[i++]),w1=atof(argv[i++]);
+ double w2=atof(argv[i++]),w3=atof(argv[i++]);
+ double w4=atof(argv[i++]),w5=atof(argv[i++]);
+ double w6=atof(argv[i]);
+ wl.add_wall(new wall_cone(w0,w1,w2,w3,w4,w5,w6,j));
+ j--;
+ } else if(strcmp(argv[i],"-y")==0) {
+ povp_output=povv_output=true;
+ } else if(strcmp(argv[i],"-yp")==0) {
+ povp_output=true;
+ } else if(strcmp(argv[i],"-yv")==0) {
+ povv_output=true;
+ } else {
+ wl.deallocate();
+ error_message();
+ return VOROPP_CMD_LINE_ERROR;
+ }
+ i++;
+ }
+
+ // Check the memory guess is positive
+ if(init_mem<=0) {
+ fputs("voro++: The memory allocation must be positive\n",stderr);
+ wl.deallocate();
+ return VOROPP_CMD_LINE_ERROR;
+ }
+
+ // Read in the dimensions of the test box, and estimate the number of
+ // boxes to divide the region up into
+ double ax=atof(argv[i]),bx=atof(argv[i+1]);
+ double ay=atof(argv[i+2]),by=atof(argv[i+3]);
+ double az=atof(argv[i+4]),bz=atof(argv[i+5]);
+
+ // Check that for each coordinate, the minimum value is smaller
+ // than the maximum value
+ if(bx<ax) {
+ fputs("voro++: Minimum x coordinate exceeds maximum x coordinate\n",stderr);
+ wl.deallocate();
+ return VOROPP_CMD_LINE_ERROR;
+ }
+ if(by<ay) {
+ fputs("voro++: Minimum y coordinate exceeds maximum y coordinate\n",stderr);
+ wl.deallocate();
+ return VOROPP_CMD_LINE_ERROR;
+ }
+ if(bz<az) {
+ fputs("voro++: Minimum z coordinate exceeds maximum z coordinate\n",stderr);
+ wl.deallocate();
+ return VOROPP_CMD_LINE_ERROR;
+ }
+
+ if(bm==none) {
+ if(polydisperse) {
+ pconp=new pre_container_poly(ax,bx,ay,by,az,bz,xperiodic,yperiodic,zperiodic);
+ pconp->import(argv[i+6]);
+ pconp->guess_optimal(nx,ny,nz);
+ } else {
+ pcon=new pre_container(ax,bx,ay,by,az,bz,xperiodic,yperiodic,zperiodic);
+ pcon->import(argv[i+6]);
+ pcon->guess_optimal(nx,ny,nz);
+ }
+ } else {
+ double nxf,nyf,nzf;
+ if(bm==length_scale) {
+
+ // Check that the length scale is positive and
+ // reasonably large
+ if(ls<tolerance) {
+ fputs("voro++: ",stderr);
+ if(ls<0) {
+ fputs("The length scale must be positive\n",stderr);
+ } else {
+ fprintf(stderr,"The length scale is smaller than the safe limit of %g. Either\nincrease the particle length scale, or recompile with a different limit.\n",tolerance);
+ }
+ wl.deallocate();
+ return VOROPP_CMD_LINE_ERROR;
+ }
+ ls=0.6/ls;
+ nxf=(bx-ax)*ls+1;
+ nyf=(by-ay)*ls+1;
+ nzf=(bz-az)*ls+1;
+
+ nx=int(nxf);ny=int(nyf);nz=int(nzf);
+ } else {
+ nxf=nx;nyf=ny;nzf=nz;
+ }
+
+ // Compute the number regions based on the length scale
+ // provided. If the total number exceeds a cutoff then bail
+ // out, to prevent making a massive memory allocation. Do this
+ // test using floating point numbers, since huge integers could
+ // potentially wrap around to negative values.
+ if(nxf*nyf*nzf>max_regions) {
+ fprintf(stderr,"voro++: Number of computational blocks exceeds the maximum allowed of %d.\n"
+ "Either increase the particle length scale, or recompile with an increased\nmaximum.",max_regions);
+ wl.deallocate();
+ return VOROPP_MEMORY_ERROR;
+ }
+ }
+
+ // Check that the output filename is a sensible length
+ int flen=strlen(argv[i+6]);
+ if(flen>4096) {
+ fputs("voro++: Filename too long\n",stderr);
+ wl.deallocate();
+ return VOROPP_CMD_LINE_ERROR;
+ }
+
+ // Open files for output
+ char *buffer=new char[flen+7];
+ sprintf(buffer,"%s.vol",argv[i+6]);
+ FILE *outfile=safe_fopen(buffer,"w"),*gnu_file,*povp_file,*povv_file;
+ if(gnuplot_output) {
+ sprintf(buffer,"%s.gnu",argv[i+6]);
+ gnu_file=safe_fopen(buffer,"w");
+ } else gnu_file=NULL;
+ if(povp_output) {
+ sprintf(buffer,"%s_p.pov",argv[i+6]);
+ povp_file=safe_fopen(buffer,"w");
+ } else povp_file=NULL;
+ if(povv_output) {
+ sprintf(buffer,"%s_v.pov",argv[i+6]);
+ povv_file=safe_fopen(buffer,"w");
+ } else povv_file=NULL;
+ delete [] buffer;
+
+ const char *c_str=(custom_output==0?(polydisperse?"%i %q %v %r":"%i %q %v"):argv[custom_output]);
+
+ // Now switch depending on whether polydispersity was enabled, and
+ // whether output ordering is requested
+ double vol=0;int tp=0,vcc=0;
+ if(polydisperse) {
+ if(ordered) {
+ particle_order vo;
+ container_poly con(ax,bx,ay,by,az,bz,nx,ny,nz,xperiodic,yperiodic,zperiodic,init_mem);
+ con.add_wall(wl);
+ if(bm==none) {
+ pconp->setup(vo,con);delete pconp;
+ } else con.import(vo,argv[i+6]);
+
+ c_loop_order vlo(con,vo);
+ cmd_line_output(vlo,con,c_str,outfile,gnu_file,povp_file,povv_file,verbose,vol,vcc,tp);
+ } else {
+ container_poly con(ax,bx,ay,by,az,bz,nx,ny,nz,xperiodic,yperiodic,zperiodic,init_mem);
+ con.add_wall(wl);
+
+ if(bm==none) {
+ pconp->setup(con);delete pconp;
+ } else con.import(argv[i+6]);
+
+ c_loop_all vla(con);
+ cmd_line_output(vla,con,c_str,outfile,gnu_file,povp_file,povv_file,verbose,vol,vcc,tp);
+ }
+ } else {
+ if(ordered) {
+ particle_order vo;
+ container con(ax,bx,ay,by,az,bz,nx,ny,nz,xperiodic,yperiodic,zperiodic,init_mem);
+ con.add_wall(wl);
+ if(bm==none) {
+ pcon->setup(vo,con);delete pcon;
+ } else con.import(vo,argv[i+6]);
+
+ c_loop_order vlo(con,vo);
+ cmd_line_output(vlo,con,c_str,outfile,gnu_file,povp_file,povv_file,verbose,vol,vcc,tp);
+ } else {
+ container con(ax,bx,ay,by,az,bz,nx,ny,nz,xperiodic,yperiodic,zperiodic,init_mem);
+ con.add_wall(wl);
+ if(bm==none) {
+ pcon->setup(con);delete pcon;
+ } else con.import(argv[i+6]);
+ c_loop_all vla(con);
+ cmd_line_output(vla,con,c_str,outfile,gnu_file,povp_file,povv_file,verbose,vol,vcc,tp);
+ }
+ }
+
+ // Print information if verbose output requested
+ if(verbose) {
+ printf("Container geometry : [%g:%g] [%g:%g] [%g:%g]\n"
+ "Computational grid size : %d by %d by %d (%s)\n"
+ "Filename : %s\n"
+ "Output string : %s%s\n",ax,bx,ay,by,az,bz,nx,ny,nz,
+ bm==none?"estimated from file":(bm==length_scale?
+ "estimated using length scale":"directly specified"),
+ argv[i+6],c_str,custom_output==0?" (default)":"");
+ printf("Total imported particles : %d (%.2g per grid block)\n"
+ "Total V. cells computed : %d\n"
+ "Total container volume : %g\n"
+ "Total V. cell volume : %g\n",tp,((double) tp)/(nx*ny*nz),
+ vcc,(bx-ax)*(by-ay)*(bz-az),vol);
+ }
+
+ // Close output files
+ fclose(outfile);
+ if(gnu_file!=NULL) fclose(gnu_file);
+ if(povp_file!=NULL) fclose(povp_file);
+ if(povv_file!=NULL) fclose(povv_file);
+ return 0;
+}
+
Index: extern/voro++/src/voro++.cc
===================================================================
--- extern/voro++/src/voro++.cc (revision 0)
+++ extern/voro++/src/voro++.cc (revision 0)
@@ -0,0 +1,19 @@
+// Voro++, a 3D cell-based Voronoi library
+//
+// Author : Chris H. Rycroft (LBL / UC Berkeley)
+// Email : chr@alum.mit.edu
+// Date : August 30th 2011
+
+/** \file voro++.cc
+ * \brief A file that loads all of the function implementation files. */
+
+#include "cell.cc"
+#include "common.cc"
+#include "v_base.cc"
+#include "container.cc"
+#include "unitcell.cc"
+#include "container_prd.cc"
+#include "pre_container.cc"
+#include "v_compute.cc"
+#include "c_loops.cc"
+#include "wall.cc"
Index: extern/voro++/src/v_compute.hh
===================================================================
--- extern/voro++/src/v_compute.hh (revision 0)
+++ extern/voro++/src/v_compute.hh (revision 0)
@@ -0,0 +1,149 @@
+// Voro++, a 3D cell-based Voronoi library
+//
+// Author : Chris H. Rycroft (LBL / UC Berkeley)
+// Email : chr@alum.mit.edu
+// Date : August 30th 2011
+
+/** \file v_compute.hh
+ * \brief Header file for the voro_compute template and related classes. */
+
+#ifndef VOROPP_V_COMPUTE_HH
+#define VOROPP_V_COMPUTE_HH
+
+#include "config.hh"
+#include "worklist.hh"
+#include "cell.hh"
+
+namespace voro {
+
+/** \brief Structure for holding information about a particle.
+ *
+ * This small structure holds information about a single particle, and is used
+ * by several of the routines in the voro_compute template for passing
+ * information by reference between functions. */
+struct particle_record {
+ /** The index of the block that the particle is within. */
+ int ijk;
+ /** The number of particle within its block. */
+ int l;
+ /** The x-index of the block. */
+ int di;
+ /** The y-index of the block. */
+ int dj;
+ /** The z-index of the block. */
+ int dk;
+};
+
+/** \brief Template for carrying out Voronoi cell computations. */
+template <class c_class>
+class voro_compute {
+ public:
+ /** A reference to the container class on which to carry out*/
+ c_class &con;
+ /** The size of an internal computational block in the x
+ * direction. */
+ const double boxx;
+ /** The size of an internal computational block in the y
+ * direction. */
+ const double boxy;
+ /** The size of an internal computational block in the z
+ * direction. */
+ const double boxz;
+ /** The inverse box length in the x direction, set to
+ * nx/(bx-ax). */
+ const double xsp;
+ /** The inverse box length in the y direction, set to
+ * ny/(by-ay). */
+ const double ysp;
+ /** The inverse box length in the z direction, set to
+ * nz/(bz-az). */
+ const double zsp;
+ /** The number of boxes in the x direction for the searching mask. */
+ const int hx;
+ /** The number of boxes in the y direction for the searching mask. */
+ const int hy;
+ /** The number of boxes in the z direction for the searching mask. */
+ const int hz;
+ /** A constant, set to the value of hx multiplied by hy, which
+ * is used in the routines which step through mask boxes in
+ * sequence. */
+ const int hxy;
+ /** A constant, set to the value of hx*hy*hz, which is used in
+ * the routines which step through mask boxes in sequence. */
+ const int hxyz;
+ /** The number of floating point entries to store for each
+ * particle. */
+ const int ps;
+ /** This array holds the numerical IDs of each particle in each
+ * computational box. */
+ int **id;
+ /** A two dimensional array holding particle positions. For the
+ * derived container_poly class, this also holds particle
+ * radii. */
+ double **p;
+ /** An array holding the number of particles within each
+ * computational box of the container. */
+ int *co;
+ voro_compute(c_class &con_,int hx_,int hy_,int hz_);
+ /** The class destructor frees the dynamically allocated memory
+ * for the mask and queue. */
+ ~voro_compute() {
+ delete [] qu;
+ delete [] mask;
+ }
+ template<class v_cell>
+ bool compute_cell(v_cell &c,int ijk,int s,int ci,int cj,int ck);
+ void find_voronoi_cell(double x,double y,double z,int ci,int cj,int ck,int ijk,particle_record &w,double &mrs);
+ private:
+ /** A constant set to boxx*boxx+boxy*boxy+boxz*boxz, which is
+ * frequently used in the computation. */
+ const double bxsq;
+ /** This sets the current value being used to mark tested blocks
+ * in the mask. */
+ unsigned int mv;
+ /** The current size of the search list. */
+ int qu_size;
+ /** A pointer to the array of worklists. */
+ const unsigned int *wl;
+ /** An pointer to the array holding the minimum distances
+ * associated with the worklists. */
+ double *mrad;
+ /** This array is used during the cell computation to determine
+ * which blocks have been considered. */
+ unsigned int *mask;
+ /** An array is used to store the queue of blocks to test
+ * during the Voronoi cell computation. */
+ int *qu;
+ /** A pointer to the end of the queue array, used to determine
+ * when the queue is full. */
+ int *qu_l;
+ template<class v_cell>
+ bool corner_test(v_cell &c,double xl,double yl,double zl,double xh,double yh,double zh);
+ template<class v_cell>
+ inline bool edge_x_test(v_cell &c,double x0,double yl,double zl,double x1,double yh,double zh);
+ template<class v_cell>
+ inline bool edge_y_test(v_cell &c,double xl,double y0,double zl,double xh,double y1,double zh);
+ template<class v_cell>
+ inline bool edge_z_test(v_cell &c,double xl,double yl,double z0,double xh,double yh,double z1);
+ template<class v_cell>
+ inline bool face_x_test(v_cell &c,double xl,double y0,double z0,double y1,double z1);
+ template<class v_cell>
+ inline bool face_y_test(v_cell &c,double x0,double yl,double z0,double x1,double z1);
+ template<class v_cell>
+ inline bool face_z_test(v_cell &c,double x0,double y0,double zl,double x1,double y1);
+ bool compute_min_max_radius(int di,int dj,int dk,double fx,double fy,double fz,double gx,double gy,double gz,double& crs,double mrs);
+ bool compute_min_radius(int di,int dj,int dk,double fx,double fy,double fz,double mrs);
+ inline void add_to_mask(int ei,int ej,int ek,int *&qu_e);
+ inline void scan_bits_mask_add(unsigned int q,unsigned int *mijk,int ei,int ej,int ek,int *&qu_e);
+ inline void scan_all(int ijk,double x,double y,double z,int di,int dj,int dk,particle_record &w,double &mrs);
+ void add_list_memory(int*& qu_s,int*& qu_e);
+ /** Resets the mask in cases where the mask counter wraps
+ * around. */
+ inline void reset_mask() {
+ for(unsigned int *mp(mask);mp<mask+hxyz;mp++) *mp=0;
+ }
+};
+
+}
+
+#endif
Index: extern/voro++/src/unitcell.cc
===================================================================
--- extern/voro++/src/unitcell.cc (revision 0)
+++ extern/voro++/src/unitcell.cc (revision 0)
@@ -0,0 +1,231 @@
+// Voro++, a 3D cell-based Voronoi library
+//
+// Author : Chris H. Rycroft (LBL / UC Berkeley)
+// Email : chr@alum.mit.edu
+// Date : August 30th 2011
+
+/** \file unitcell.cc
+ * \brief Function implementations for the unitcell class. */
+
+#include <cmath>
+#include <queue>
+
+#include "unitcell.hh"
+#include "cell.hh"
+
+namespace voro {
+
+/** Initializes the unit cell class for a particular non-orthogonal periodic
+ * geometry, corresponding to a parallelepiped with sides given by three
+ * vectors. The class constructs the unit Voronoi cell corresponding to this
+ * geometry.
+ * \param[in] (bx_) The x coordinate of the first unit vector.
+ * \param[in] (bxy_,by_) The x and y coordinates of the second unit vector.
+ * \param[in] (bxz_,byz_,bz_) The x, y, and z coordinates of the third unit
+ * vector. */
+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_) {
+ int i,j,l=1;
+
+ // Initialize the Voronoi cell to be a very large rectangular box
+ 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);
+
+ // Repeatedly cut the cell by shells of periodic image particles
+ while(l<2*max_unit_voro_shells) {
+
+ // Check to see if any of the planes from the current shell
+ // will cut the cell
+ if(unit_voro_intersect(l)) {
+
+ // If they do, apply the plane cuts from the current
+ // shell
+ 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 {
+
+ // Calculate a bound on the maximum y and z coordinates
+ // that could possibly cut the cell. This is based upon
+ // a geometric result that particles with z>l can't cut
+ // a cell lying within the paraboloid
+ // z<=(l*l-x*x-y*y)/(2*l). It is always a tighter bound
+ // than the one based on computing the maximum radius
+ // of a Voronoi cell vertex.
+ max_uv_y=max_uv_z=0;
+ double y,z,q,*pts=unit_voro.pts,*pp=pts;
+ while(pp<pts+3*unit_voro.p) {
+ q=*(pp++);y=*(pp++);z=*(pp++);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++;
+ }
+
+ // If the routine makes it here, then the unit cell still hasn't been
+ // completely bounded by the plane cuts. Give the memory error code,
+ // because this is mainly a case of hitting a safe limit, than any
+ // inherent problem.
+ voro_fatal_error("Periodic cell computation failed",VOROPP_MEMORY_ERROR);
+}
+
+/** Applies a pair of opposing plane cuts from a periodic image point
+ * to the unit Voronoi cell.
+ * \param[in] (i,j,k) the index of the periodic image to consider. */
+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);
+}
+
+/** Calculates whether the unit Voronoi cell intersects a given periodic image
+ * of the domain.
+ * \param[in] (dx,dy,dz) the displacement of the periodic image.
+ * \param[out] vol the proportion of the unit cell volume within this image,
+ * only computed in the case that the two intersect.
+ * \return True if they intersect, false otherwise. */
+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;
+}
+
+/** Computes a list of periodic domain images that intersect the unit Voronoi cell.
+ * \param[out] vi a vector containing triplets (i,j,k) corresponding to domain
+ * images that intersect the unit Voronoi cell, when it is
+ * centered in the middle of the primary domain.
+ * \param[out] vd a vector containing the fraction of the Voronoi cell volume
+ * within each corresponding image listed in vi. */
+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;
+
+ // Initialize mask
+ while(ap<ac) *(ap++)=true;
+ *(ap++)=false;
+ while(ap<a+mss) *(ap++)=true;
+
+ // Set up the queue and add (0,0,0) image to it
+ std::queue<int> q;
+ q.push(0);q.push(0);q.push(0);
+
+ while(!q.empty()) {
+
+ // Read the next entry on the queue
+ i=q.front();q.pop();
+ j=q.front();q.pop();
+ k=q.front();q.pop();
+
+ // Check intersection of this image
+ if(intersects_image(i,j,k,vol)) {
+
+ // Add this entry to the output vectors
+ vi.push_back(i);
+ vi.push_back(j);
+ vi.push_back(k);
+ vd.push_back(vol);
+
+ // Add neighbors to the queue if they have not been
+ // tested
+ 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;}
+ }
+ }
+
+ // Remove mask memory
+ delete [] a;
+}
+
+/** Tests to see if a shell of periodic images could possibly cut the periodic
+ * unit cell.
+ * \param[in] l the index of the shell to consider.
+ * \return True if a point in the shell cuts the cell, false otherwise. */
+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;
+}
+
+/** Tests to see if a plane cut from a particular periodic image will cut th
+ * unit Voronoi cell.
+ * \param[in] (i,j,k) the index of the periodic image to consider.
+ * \return True if the image cuts the cell, false otherwise. */
+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);
+}
+
+/** Draws the periodic domain in gnuplot format.
+ * \param[in] fp the file handle to write to. */
+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);
+}
+
+/** Draws the periodic domain in POV-Ray format.
+ * \param[in] fp the file handle to write to. */
+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);
+}
+
+}
Index: extern/voro++/src/cell.cc
===================================================================
--- extern/voro++/src/cell.cc (revision 0)
+++ extern/voro++/src/cell.cc (revision 0)
@@ -0,0 +1,2252 @@
+// Voro++, a 3D cell-based Voronoi library
+//
+// Author : Chris H. Rycroft (LBL / UC Berkeley)
+// Email : chr@alum.mit.edu
+// Date : August 30th 2011
+
+/** \file cell.cc
+ * \brief Function implementations for the voronoicell and related classes. */
+
+#include <cmath>
+#include <cstring>
+
+#include "config.hh"
+#include "common.hh"
+#include "cell.hh"
+
+namespace voro {
+
+/** Constructs a Voronoi cell and sets up the initial memory. */
+voronoicell_base::voronoicell_base() :
+ current_vertices(init_vertices), current_vertex_order(init_vertex_order),
+ current_delete_size(init_delete_size), current_delete2_size(init_delete2_size),
+ ed(new int*[current_vertices]), nu(new int[current_vertices]),
+ pts(new double[3*current_vertices]), mem(new int[current_vertex_order]),
+ mec(new int[current_vertex_order]), mep(new int*[current_vertex_order]),
+ ds(new int[current_delete_size]), stacke(ds+current_delete_size),
+ ds2(new int[current_delete2_size]), stacke2(ds2+current_delete_size),
+ current_marginal(init_marginal), marg(new int[current_marginal]) {
+ int i;
+ for(i=0;i<3;i++) {
+ mem[i]=init_n_vertices;mec[i]=0;
+ mep[i]=new int[init_n_vertices*((i<<1)+1)];
+ }
+ mem[3]=init_3_vertices;mec[3]=0;
+ mep[3]=new int[init_3_vertices*7];
+ for(i=4;i<current_vertex_order;i++) {
+ mem[i]=init_n_vertices;mec[i]=0;
+ mep[i]=new int[init_n_vertices*((i<<1)+1)];
+ }
+}
+
+/** The voronoicell destructor deallocates all the dynamic memory. */
+voronoicell_base::~voronoicell_base() {
+ for(int i=current_vertex_order-1;i>=0;i--) if(mem[i]>0) delete [] mep[i];
+ delete [] marg;
+ delete [] ds2;delete [] ds;
+ delete [] mep;delete [] mec;
+ delete [] mem;delete [] pts;
+ delete [] nu;delete [] ed;
+}
+
+/** Ensures that enough memory is allocated prior to carrying out a copy.
+ * \param[in] vc a reference to the specialized version of the calling class.
+ * \param[in] vb a pointered to the class to be copied. */
+template<class vc_class>
+void voronoicell_base::check_memory_for_copy(vc_class &vc,voronoicell_base* vb) {
+ while(current_vertex_order<vb->current_vertex_order) add_memory_vorder(vc);
+ for(int i=0;i<current_vertex_order;i++) while(mem[i]<vb->mec[i]) add_memory(vc,i,ds2);
+ while(current_vertices<vb->p) add_memory_vertices(vc);
+}
+
+/** Copies the vertex and edge information from another class. The routine
+ * assumes that enough memory is available for the copy.
+ * \param[in] vb a pointer to the class to copy. */
+void voronoicell_base::copy(voronoicell_base* vb) {
+ int i,j;
+ p=vb->p;up=0;
+ for(i=0;i<current_vertex_order;i++) {
+ mec[i]=vb->mec[i];
+ for(j=0;j<mec[i]*(2*i+1);j++) mep[i][j]=vb->mep[i][j];
+ for(j=0;j<mec[i]*(2*i+1);j+=2*i+1) ed[mep[i][j+2*i]]=mep[i]+j;
+ }
+ for(i=0;i<p;i++) nu[i]=vb->nu[i];
+ for(i=0;i<3*p;i++) pts[i]=vb->pts[i];
+}
+
+/** Copies the information from another voronoicell class into this
+ * class, extending memory allocation if necessary.
+ * \param[in] c the class to copy. */
+void voronoicell_neighbor::operator=(voronoicell &c) {
+ voronoicell_base *vb=((voronoicell_base*) &c);
+ check_memory_for_copy(*this,vb);copy(vb);
+ int i,j;
+ for(i=0;i<c.current_vertex_order;i++) {
+ for(j=0;j<c.mec[i]*i;j++) mne[i][j]=0;
+ for(j=0;j<c.mec[i];j++) ne[c.mep[i][(2*i+1)*j+2*i]]=mne[i]+(j*i);
+ }
+}
+
+/** Copies the information from another voronoicell_neighbor class into this
+ * class, extending memory allocation if necessary.
+ * \param[in] c the class to copy. */
+void voronoicell_neighbor::operator=(voronoicell_neighbor &c) {
+ voronoicell_base *vb=((voronoicell_base*) &c);
+ check_memory_for_copy(*this,vb);copy(vb);
+ int i,j;
+ for(i=0;i<c.current_vertex_order;i++) {
+ for(j=0;j<c.mec[i]*i;j++) mne[i][j]=c.mne[i][j];
+ for(j=0;j<c.mec[i];j++) ne[c.mep[i][(2*i+1)*j+2*i]]=mne[i]+(j*i);
+ }
+}
+
+/** Translates the vertices of the Voronoi cell by a given vector.
+ * \param[in] (x,y,z) the coordinates of the vector. */
+void voronoicell_base::translate(double x,double y,double z) {
+ x*=2;y*=2;z*=2;
+ double *ptsp=pts;
+ while(ptsp<pts+3*p) {
+ *(ptsp++)=x;*(ptsp++)=y;*(ptsp++)=z;
+ }
+}
+
+/** Increases the memory storage for a particular vertex order, by increasing
+ * the size of the of the corresponding mep array. If the arrays already exist,
+ * their size is doubled; if they don't exist, then new ones of size
+ * init_n_vertices are allocated. The routine also ensures that the pointers in
+ * the ed array are updated, by making use of the back pointers. For the cases
+ * where the back pointer has been temporarily overwritten in the marginal
+ * vertex code, the auxiliary delete stack is scanned to find out how to update
+ * the ed value. If the template has been instantiated with the neighbor
+ * tracking turned on, then the routine also reallocates the corresponding mne
+ * array.
+ * \param[in] i the order of the vertex memory to be increased. */
+template<class vc_class>
+void voronoicell_base::add_memory(vc_class &vc,int i,int *stackp2) {
+ int s=(i<<1)+1;
+ if(mem[i]==0) {
+ vc.n_allocate(i,init_n_vertices);
+ mep[i]=new int[init_n_vertices*s];
+ mem[i]=init_n_vertices;
+#if VOROPP_VERBOSE >=2
+ fprintf(stderr,"Order %d vertex memory created\n",i);
+#endif
+ } else {
+ int j=0,k,*l;
+ mem[i]<<=1;
+ if(mem[i]>max_n_vertices) voro_fatal_error("Point memory allocation exceeded absolute maximum",VOROPP_MEMORY_ERROR);
+#if VOROPP_VERBOSE >=2
+ fprintf(stderr,"Order %d vertex memory scaled up to %d\n",i,mem[i]);
+#endif
+ l=new int[s*mem[i]];
+ int m=0;
+ vc.n_allocate_aux1(i);
+ while(j<s*mec[i]) {
+ k=mep[i][j+(i<<1)];
+ if(k>=0) {
+ ed[k]=l+j;
+ vc.n_set_to_aux1_offset(k,m);
+ } else {
+ int *dsp;
+ for(dsp=ds2;dsp<stackp2;dsp++) {
+ if(ed[*dsp]==mep[i]+j) {
+ ed[*dsp]=l+j;
+ vc.n_set_to_aux1_offset(*dsp,m);
+ break;
+ }
+ }
+ if(dsp==stackp2) voro_fatal_error("Couldn't relocate dangling pointer",VOROPP_INTERNAL_ERROR);
+#if VOROPP_VERBOSE >=3
+ fputs("Relocated dangling pointer",stderr);
+#endif
+ }
+ for(k=0;k<s;k++,j++) l[j]=mep[i][j];
+ for(k=0;k<i;k++,m++) vc.n_copy_to_aux1(i,m);
+ }
+ delete [] mep[i];
+ mep[i]=l;
+ vc.n_switch_to_aux1(i);
+ }
+}
+
+/** Doubles the maximum number of vertices allowed, by reallocating the ed, nu,
+ * and pts arrays. If the allocation exceeds the absolute maximum set in
+ * max_vertices, then the routine exits with a fatal error. If the template has
+ * been instantiated with the neighbor tracking turned on, then the routine
+ * also reallocates the ne array. */
+template<class vc_class>
+void voronoicell_base::add_memory_vertices(vc_class &vc) {
+ int i=(current_vertices<<1),j,**pp,*pnu;
+ if(i>max_vertices) voro_fatal_error("Vertex memory allocation exceeded absolute maximum",VOROPP_MEMORY_ERROR);
+#if VOROPP_VERBOSE >=2
+ fprintf(stderr,"Vertex memory scaled up to %d\n",i);
+#endif
+ double *ppts;
+ pp=new int*[i];
+ for(j=0;j<current_vertices;j++) pp[j]=ed[j];
+ delete [] ed;ed=pp;
+ vc.n_add_memory_vertices(i);
+ pnu=new int[i];
+ for(j=0;j<current_vertices;j++) pnu[j]=nu[j];
+ delete [] nu;nu=pnu;
+ ppts=new double[3*i];
+ for(j=0;j<3*current_vertices;j++) ppts[j]=pts[j];
+ delete [] pts;pts=ppts;
+ current_vertices=i;
+}
+
+/** Doubles the maximum allowed vertex order, by reallocating mem, mep, and mec
+ * arrays. If the allocation exceeds the absolute maximum set in
+ * max_vertex_order, then the routine causes a fatal error. If the template has
+ * been instantiated with the neighbor tracking turned on, then the routine
+ * also reallocates the mne array. */
+template<class vc_class>
+void voronoicell_base::add_memory_vorder(vc_class &vc) {
+ int i=(current_vertex_order<<1),j,*p1,**p2;
+ if(i>max_vertex_order) voro_fatal_error("Vertex order memory allocation exceeded absolute maximum",VOROPP_MEMORY_ERROR);
+#if VOROPP_VERBOSE >=2
+ fprintf(stderr,"Vertex order memory scaled up to %d\n",i);
+#endif
+ p1=new int[i];
+ for(j=0;j<current_vertex_order;j++) p1[j]=mem[j];while(j<i) p1[j++]=0;
+ delete [] mem;mem=p1;
+ p2=new int*[i];
+ for(j=0;j<current_vertex_order;j++) p2[j]=mep[j];
+ delete [] mep;mep=p2;
+ p1=new int[i];
+ for(j=0;j<current_vertex_order;j++) p1[j]=mec[j];while(j<i) p1[j++]=0;
+ delete [] mec;mec=p1;
+ vc.n_add_memory_vorder(i);
+ current_vertex_order=i;
+}
+
+/** Doubles the size allocation of the main delete stack. If the allocation
+ * exceeds the absolute maximum set in max_delete_size, then routine causes a
+ * fatal error. */
+void voronoicell_base::add_memory_ds(int *&stackp) {
+ current_delete_size<<=1;
+ if(current_delete_size>max_delete_size) voro_fatal_error("Delete stack 1 memory allocation exceeded absolute maximum",VOROPP_MEMORY_ERROR);
+#if VOROPP_VERBOSE >=2
+ fprintf(stderr,"Delete stack 1 memory scaled up to %d\n",current_delete_size);
+#endif
+ int *dsn=new int[current_delete_size],*dsnp=dsn,*dsp=ds;
+ while(dsp<stackp) *(dsnp++)=*(dsp++);
+ delete [] ds;ds=dsn;stackp=dsnp;
+ stacke=ds+current_delete_size;
+}
+
+/** Doubles the size allocation of the auxiliary delete stack. If the
+ * allocation exceeds the absolute maximum set in max_delete2_size, then the
+ * routine causes a fatal error. */
+void voronoicell_base::add_memory_ds2(int *&stackp2) {
+ current_delete2_size<<=1;
+ if(current_delete2_size>max_delete2_size) voro_fatal_error("Delete stack 2 memory allocation exceeded absolute maximum",VOROPP_MEMORY_ERROR);
+#if VOROPP_VERBOSE >=2
+ fprintf(stderr,"Delete stack 2 memory scaled up to %d\n",current_delete2_size);
+#endif
+ int *dsn=new int[current_delete2_size],*dsnp=dsn,*dsp=ds2;
+ while(dsp<stackp2) *(dsnp++)=*(dsp++);
+ delete [] ds2;ds2=dsn;stackp2=dsnp;
+ stacke2=ds2+current_delete2_size;
+}
+
+/** Initializes a Voronoi cell as a rectangular box with the given dimensions.
+ * \param[in] (xmin,xmax) the minimum and maximum x coordinates.
+ * \param[in] (ymin,ymax) the minimum and maximum y coordinates.
+ * \param[in] (zmin,zmax) the minimum and maximum z coordinates. */
+void voronoicell_base::init_base(double xmin,double xmax,double ymin,double ymax,double zmin,double zmax) {
+ for(int i=0;i<current_vertex_order;i++) mec[i]=0;up=0;
+ mec[3]=p=8;xmin*=2;xmax*=2;ymin*=2;ymax*=2;zmin*=2;zmax*=2;
+ *pts=xmin;pts[1]=ymin;pts[2]=zmin;
+ pts[3]=xmax;pts[4]=ymin;pts[5]=zmin;
+ pts[6]=xmin;pts[7]=ymax;pts[8]=zmin;
+ pts[9]=xmax;pts[10]=ymax;pts[11]=zmin;
+ pts[12]=xmin;pts[13]=ymin;pts[14]=zmax;
+ pts[15]=xmax;pts[16]=ymin;pts[17]=zmax;
+ pts[18]=xmin;pts[19]=ymax;pts[20]=zmax;
+ pts[21]=xmax;pts[22]=ymax;pts[23]=zmax;
+ int *q=mep[3];
+ *q=1;q[1]=4;q[2]=2;q[3]=2;q[4]=1;q[5]=0;q[6]=0;
+ q[7]=3;q[8]=5;q[9]=0;q[10]=2;q[11]=1;q[12]=0;q[13]=1;
+ q[14]=0;q[15]=6;q[16]=3;q[17]=2;q[18]=1;q[19]=0;q[20]=2;
+ q[21]=2;q[22]=7;q[23]=1;q[24]=2;q[25]=1;q[26]=0;q[27]=3;
+ q[28]=6;q[29]=0;q[30]=5;q[31]=2;q[32]=1;q[33]=0;q[34]=4;
+ q[35]=4;q[36]=1;q[37]=7;q[38]=2;q[39]=1;q[40]=0;q[41]=5;
+ q[42]=7;q[43]=2;q[44]=4;q[45]=2;q[46]=1;q[47]=0;q[48]=6;
+ q[49]=5;q[50]=3;q[51]=6;q[52]=2;q[53]=1;q[54]=0;q[55]=7;
+ *ed=q;ed[1]=q+7;ed[2]=q+14;ed[3]=q+21;
+ ed[4]=q+28;ed[5]=q+35;ed[6]=q+42;ed[7]=q+49;
+ *nu=nu[1]=nu[2]=nu[3]=nu[4]=nu[5]=nu[6]=nu[7]=3;
+}
+
+/** Initializes a Voronoi cell as a regular octahedron.
+ * \param[in] l The distance from the octahedron center to a vertex. Six
+ * vertices are initialized at (-l,0,0), (l,0,0), (0,-l,0),
+ * (0,l,0), (0,0,-l), and (0,0,l). */
+void voronoicell_base::init_octahedron_base(double l) {
+ for(int i=0;i<current_vertex_order;i++) mec[i]=0;up=0;
+ mec[4]=p=6;l*=2;
+ *pts=-l;pts[1]=0;pts[2]=0;
+ pts[3]=l;pts[4]=0;pts[5]=0;
+ pts[6]=0;pts[7]=-l;pts[8]=0;
+ pts[9]=0;pts[10]=l;pts[11]=0;
+ pts[12]=0;pts[13]=0;pts[14]=-l;
+ pts[15]=0;pts[16]=0;pts[17]=l;
+ int *q=mep[4];
+ *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;
+ 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;
+ 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;
+ 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;
+ 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;
+ 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;
+ *ed=q;ed[1]=q+9;ed[2]=q+18;ed[3]=q+27;ed[4]=q+36;ed[5]=q+45;
+ *nu=nu[1]=nu[2]=nu[3]=nu[4]=nu[5]=4;
+}
+
+/** Initializes a Voronoi cell as a tetrahedron. It assumes that the normal to
+ * the face for the first three vertices points inside.
+ * \param (x0,y0,z0) a position vector for the first vertex.
+ * \param (x1,y1,z1) a position vector for the second vertex.
+ * \param (x2,y2,z2) a position vector for the third vertex.
+ * \param (x3,y3,z3) a position vector for the fourth vertex. */
+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) {
+ for(int i=0;i<current_vertex_order;i++) mec[i]=0;up=0;
+ mec[3]=p=4;
+ *pts=x0*2;pts[1]=y0*2;pts[2]=z0*2;
+ pts[3]=x1*2;pts[4]=y1*2;pts[5]=z1*2;
+ pts[6]=x2*2;pts[7]=y2*2;pts[8]=z2*2;
+ pts[9]=x3*2;pts[10]=y3*2;pts[11]=z3*2;
+ int *q=mep[3];
+ *q=1;q[1]=3;q[2]=2;q[3]=0;q[4]=0;q[5]=0;q[6]=0;
+ q[7]=0;q[8]=2;q[9]=3;q[10]=0;q[11]=2;q[12]=1;q[13]=1;
+ q[14]=0;q[15]=3;q[16]=1;q[17]=2;q[18]=2;q[19]=1;q[20]=2;
+ q[21]=0;q[22]=1;q[23]=2;q[24]=1;q[25]=2;q[26]=1;q[27]=3;
+ *ed=q;ed[1]=q+7;ed[2]=q+14;ed[3]=q+21;
+ *nu=nu[1]=nu[2]=nu[3]=3;
+}
+
+/** Checks that the relational table of the Voronoi cell is accurate, and
+ * prints out any errors. This algorithm is O(p), so running it every time the
+ * plane routine is called will result in a significant slowdown. */
+void voronoicell_base::check_relations() {
+ int i,j;
+ for(i=0;i<p;i++) for(j=0;j<nu[i];j++) if(ed[ed[i][j]][ed[i][nu[i]+j]]!=i)
+ printf("Relational error at point %d, edge %d.\n",i,j);
+}
+
+/** This routine checks for any two vertices that are connected by more than
+ * one edge. The plane algorithm is designed so that this should not happen, so
+ * any occurrences are most likely errors. Note that the routine is O(p), so
+ * running it every time the plane routine is called will result in a
+ * significant slowdown. */
+void voronoicell_base::check_duplicates() {
+ int i,j,k;
+ 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])
+ printf("Duplicate edges: (%d,%d) and (%d,%d) [%d]\n",i,j,i,k,ed[i][j]);
+}
+
+/** Constructs the relational table if the edges have been specified. */
+void voronoicell_base::construct_relations() {
+ int i,j,k,l;
+ for(i=0;i<p;i++) for(j=0;j<nu[i];j++) {
+ k=ed[i][j];
+ l=0;
+ while(ed[k][l]!=i) {
+ l++;
+ if(l==nu[k]) voro_fatal_error("Relation table construction failed",VOROPP_INTERNAL_ERROR);
+ }
+ ed[i][nu[i]+j]=l;
+ }
+}
+
+/** Starting from a point within the current cutting plane, this routine attempts
+ * to find an edge to a point outside the cutting plane. This prevents the plane
+ * routine from .
+ * \param[in] vc a reference to the specialized version of the calling class.
+ * \param[in,out] up */
+template<class vc_class>
+inline bool voronoicell_base::search_for_outside_edge(vc_class &vc,int &up) {
+ int i,lp,lw,*j(ds2),*stackp2(ds2);
+ double l;
+ *(stackp2++)=up;
+ while(j<stackp2) {
+ up=*(j++);
+ for(i=0;i<nu[up];i++) {
+ lp=ed[up][i];
+ lw=m_test(lp,l);
+ if(lw==-1) return true;
+ else if(lw==0) add_to_stack(vc,lp,stackp2);
+ }
+ }
+ return false;
+}
+
+/** Adds a point to the auxiliary delete stack if it is not already there.
+ * \param[in] vc a reference to the specialized version of the calling class.
+ * \param[in] lp the index of the point to add.
+ * \param[in,out] stackp2 a pointer to the end of the stack entries. */
+template<class vc_class>
+inline void voronoicell_base::add_to_stack(vc_class &vc,int lp,int *&stackp2) {
+ for(int *k(ds2);k<stackp2;k++) if(*k==lp) return;
+ if(stackp2==stacke2) add_memory_ds2(stackp2);
+ *(stackp2++)=lp;
+}
+
+/** Cuts the Voronoi cell by a particle whose center is at a separation of
+ * (x,y,z) from the cell center. The value of rsq should be initially set to
+ * \f$x^2+y^2+z^2\f$.
+ * \param[in] vc a reference to the specialized version of the calling class.
+ * \param[in] (x,y,z) the normal vector to the plane.
+ * \param[in] rsq the distance along this vector of the plane.
+ * \param[in] p_id the plane ID (for neighbor tracking only).
+ * \return False if the plane cut deleted the cell entirely, true otherwise. */
+template<class vc_class>
+bool voronoicell_base::nplane(vc_class &vc,double x,double y,double z,double rsq,int p_id) {
+ int count=0,i,j,k,lp=up,cp,qp,rp,*stackp(ds),*stackp2(ds2),*dsp;
+ int us=0,ls=0,qs,iqs,cs,uw,qw,lw;
+ int *edp,*edd;
+ double u,l,r,q;bool complicated_setup=false,new_double_edge=false,double_edge=false;
+
+ // Initialize the safe testing routine
+ n_marg=0;px=x;py=y;pz=z;prsq=rsq;
+
+ // Test approximately sqrt(n)/4 points for their proximity to the plane
+ // and keep the one which is closest
+ uw=m_test(up,u);
+
+ // Starting from an initial guess, we now move from vertex to vertex,
+ // to try and find an edge which intersects the cutting plane,
+ // or a vertex which is on the plane
+ try {
+ if(uw==1) {
+
+ // The test point is inside the cutting plane.
+ us=0;
+ do {
+ lp=ed[up][us];
+ lw=m_test(lp,l);
+ if(l<u) break;
+ us++;
+ } while (us<nu[up]);
+
+ if(us==nu[up]) {
+ return false;
+ }
+
+ ls=ed[up][nu[up]+us];
+ while(lw==1) {
+ if(++count>=p) throw true;
+ u=l;up=lp;
+ for(us=0;us<ls;us++) {
+ lp=ed[up][us];
+ lw=m_test(lp,l);
+ if(l<u) break;
+ }
+ if(us==ls) {
+ us++;
+ while(us<nu[up]) {
+ lp=ed[up][us];
+ lw=m_test(lp,l);
+ if(l<u) break;
+ us++;
+ }
+ if(us==nu[up]) {
+ return false;
+ }
+ }
+ ls=ed[up][nu[up]+us];
+ }
+
+ // If the last point in the iteration is within the
+ // plane, we need to do the complicated setup
+ // routine. Otherwise, we use the regular iteration.
+ if(lw==0) {
+ up=lp;
+ complicated_setup=true;
+ } else complicated_setup=false;
+ } else if(uw==-1) {
+ us=0;
+ do {
+ qp=ed[up][us];
+ qw=m_test(qp,q);
+ if(u<q) break;
+ us++;
+ } while (us<nu[up]);
+ if(us==nu[up]) return true;
+
+ while(qw==-1) {
+ qs=ed[up][nu[up]+us];
+ if(++count>=p) throw true;
+ u=q;up=qp;
+ for(us=0;us<qs;us++) {
+ qp=ed[up][us];
+ qw=m_test(qp,q);
+ if(u<q) break;
+ }
+ if(us==qs) {
+ us++;
+ while(us<nu[up]) {
+ qp=ed[up][us];
+ qw=m_test(qp,q);
+ if(u<q) break;
+ us++;
+ }
+ if(us==nu[up]) return true;
+ }
+ }
+ if(qw==1) {
+ lp=up;ls=us;l=u;
+ up=qp;us=ed[lp][nu[lp]+ls];u=q;
+ complicated_setup=false;
+ } else {
+ up=qp;
+ complicated_setup=true;
+ }
+ } else {
+
+ // Our original test point was on the plane, so we
+ // automatically head for the complicated setup
+ // routine
+ complicated_setup=true;
+ }
+ }
+ catch(bool except) {
+ // This routine is a fall-back, in case floating point errors
+ // cause the usual search routine to fail. In the fall-back
+ // routine, we just test every edge to find one straddling
+ // the plane.
+#if VOROPP_VERBOSE >=1
+ fputs("Bailed out of convex calculation\n",stderr);
+#endif
+ qw=1;lw=0;
+ for(qp=0;qp<p;qp++) {
+ qw=m_test(qp,q);
+ if(qw==1) {
+
+ // The point is inside the cutting space. Now
+ // see if we can find a neighbor which isn't.
+ for(us=0;us<nu[qp];us++) {
+ lp=ed[qp][us];
+ if(lp<qp) {
+ lw=m_test(lp,l);
+ if(lw!=1) break;
+ }
+ }
+ if(us<nu[qp]) {
+ up=qp;
+ if(lw==0) {
+ complicated_setup=true;
+ } else {
+ complicated_setup=false;
+ u=q;
+ ls=ed[up][nu[up]+us];
+ }
+ break;
+ }
+ } else if(qw==-1) {
+
+ // The point is outside the cutting space. See
+ // if we can find a neighbor which isn't.
+ for(ls=0;ls<nu[qp];ls++) {
+ up=ed[qp][ls];
+ if(up<qp) {
+ uw=m_test(up,u);
+ if(uw!=-1) break;
+ }
+ }
+ if(ls<nu[qp]) {
+ if(uw==0) {
+ up=qp;
+ complicated_setup=true;
+ } else {
+ complicated_setup=false;
+ lp=qp;l=q;
+ us=ed[lp][nu[lp]+ls];
+ }
+ break;
+ }
+ } else {
+
+ // The point is in the plane, so we just
+ // proceed with the complicated setup routine
+ up=qp;
+ complicated_setup=true;
+ break;
+ }
+ }
+ if(qp==p) return qw==-1?true:false;
+ }
+
+ // We're about to add the first point of the new facet. In either
+ // routine, we have to add a point, so first check there's space for
+ // it.
+ if(p==current_vertices) add_memory_vertices(vc);
+
+ if(complicated_setup) {
+
+ // We want to be strict about reaching the conclusion that the
+ // cell is entirely within the cutting plane. It's not enough
+ // to find a vertex that has edges which are all inside or on
+ // the plane. If the vertex has neighbors that are also on the
+ // plane, we should check those too.
+ if(!search_for_outside_edge(vc,up)) return false;
+
+ // The search algorithm found a point which is on the cutting
+ // plane. We leave that point in place, and create a new one at
+ // the same location.
+ pts[3*p]=pts[3*up];
+ pts[3*p+1]=pts[3*up+1];
+ pts[3*p+2]=pts[3*up+2];
+
+ // Search for a collection of edges of the test vertex which
+ // are outside of the cutting space. Begin by testing the
+ // zeroth edge.
+ i=0;
+ lp=*ed[up];
+ lw=m_test(lp,l);
+ if(lw!=-1) {
+
+ // The first edge is either inside the cutting space,
+ // or lies within the cutting plane. Test the edges
+ // sequentially until we find one that is outside.
+ rp=lw;
+ do {
+ i++;
+
+ // If we reached the last edge with no luck
+ // then all of the vertices are inside
+ // or on the plane, so the cell is completely
+ // deleted
+ if(i==nu[up]) return false;
+ lp=ed[up][i];
+ lw=m_test(lp,l);
+ } while (lw!=-1);
+ j=i+1;
+
+ // We found an edge outside the cutting space. Keep
+ // moving through these edges until we find one that's
+ // inside or on the plane.
+ while(j<nu[up]) {
+ lp=ed[up][j];
+ lw=m_test(lp,l);
+ if(lw!=-1) break;
+ j++;
+ }
+
+ // Compute the number of edges for the new vertex. In
+ // general it will be the number of outside edges
+ // found, plus two. But we need to recognize the
+ // special case when all but one edge is outside, and
+ // the remaining one is on the plane. For that case we
+ // have to reduce the edge count by one to prevent
+ // doubling up.
+ if(j==nu[up]&&i==1&&rp==0) {
+ nu[p]=nu[up];
+ double_edge=true;
+ } else nu[p]=j-i+2;
+ k=1;
+
+ // Add memory for the new vertex if needed, and
+ // initialize
+ while (nu[p]>=current_vertex_order) add_memory_vorder(vc);
+ if(mec[nu[p]]==mem[nu[p]]) add_memory(vc,nu[p],stackp2);
+ vc.n_set_pointer(p,nu[p]);
+ ed[p]=mep[nu[p]]+((nu[p]<<1)+1)*mec[nu[p]]++;
+ ed[p][nu[p]<<1]=p;
+
+ // Copy the edges of the original vertex into the new
+ // one. Delete the edges of the original vertex, and
+ // update the relational table.
+ us=cycle_down(i,up);
+ while(i<j) {
+ qp=ed[up][i];
+ qs=ed[up][nu[up]+i];
+ vc.n_copy(p,k,up,i);
+ ed[p][k]=qp;
+ ed[p][nu[p]+k]=qs;
+ ed[qp][qs]=p;
+ ed[qp][nu[qp]+qs]=k;
+ ed[up][i]=-1;
+ i++;k++;
+ }
+ qs=i==nu[up]?0:i;
+ } else {
+
+ // In this case, the zeroth edge is outside the cutting
+ // plane. Begin by searching backwards from the last
+ // edge until we find an edge which isn't outside.
+ i=nu[up]-1;
+ lp=ed[up][i];
+ lw=m_test(lp,l);
+ while(lw==-1) {
+ i--;
+
+ // If i reaches zero, then we have a point in
+ // the plane all of whose edges are outside
+ // the cutting space, so we just exit
+ if(i==0) return true;
+ lp=ed[up][i];
+ lw=m_test(lp,l);
+ }
+
+ // Now search forwards from zero
+ j=1;
+ qp=ed[up][j];
+ qw=m_test(qp,q);
+ while(qw==-1) {
+ j++;
+ qp=ed[up][j];
+ qw=m_test(qp,l);
+ }
+
+ // Compute the number of edges for the new vertex. In
+ // general it will be the number of outside edges
+ // found, plus two. But we need to recognize the
+ // special case when all but one edge is outside, and
+ // the remaining one is on the plane. For that case we
+ // have to reduce the edge count by one to prevent
+ // doubling up.
+ if(i==j&&qw==0) {
+ double_edge=true;
+ nu[p]=nu[up];
+ } else {
+ nu[p]=nu[up]-i+j+1;
+ }
+
+ // Add memory to store the vertex if it doesn't exist
+ // already
+ k=1;
+ while(nu[p]>=current_vertex_order) add_memory_vorder(vc);
+ if(mec[nu[p]]==mem[nu[p]]) add_memory(vc,nu[p],stackp2);
+
+ // Copy the edges of the original vertex into the new
+ // one. Delete the edges of the original vertex, and
+ // update the relational table.
+ vc.n_set_pointer(p,nu[p]);
+ ed[p]=mep[nu[p]]+((nu[p]<<1)+1)*mec[nu[p]]++;
+ ed[p][nu[p]<<1]=p;
+ us=i++;
+ while(i<nu[up]) {
+ qp=ed[up][i];
+ qs=ed[up][nu[up]+i];
+ vc.n_copy(p,k,up,i);
+ ed[p][k]=qp;
+ ed[p][nu[p]+k]=qs;
+ ed[qp][qs]=p;
+ ed[qp][nu[qp]+qs]=k;
+ ed[up][i]=-1;
+ i++;k++;
+ }
+ i=0;
+ while(i<j) {
+ qp=ed[up][i];
+ qs=ed[up][nu[up]+i];
+ vc.n_copy(p,k,up,i);
+ ed[p][k]=qp;
+ ed[p][nu[p]+k]=qs;
+ ed[qp][qs]=p;
+ ed[qp][nu[qp]+qs]=k;
+ ed[up][i]=-1;
+ i++;k++;
+ }
+ qs=j;
+ }
+ if(!double_edge) {
+ vc.n_copy(p,k,up,qs);
+ vc.n_set(p,0,p_id);
+ } else vc.n_copy(p,0,up,qs);
+
+ // Add this point to the auxiliary delete stack
+ if(stackp2==stacke2) add_memory_ds2(stackp2);
+ *(stackp2++)=up;
+
+ // Look at the edges on either side of the group that was
+ // detected. We're going to commence facet computation by
+ // moving along one of them. We are going to end up coming back
+ // along the other one.
+ cs=k;
+ qp=up;q=u;
+ i=ed[up][us];
+ us=ed[up][nu[up]+us];
+ up=i;
+ ed[qp][nu[qp]<<1]=-p;
+
+ } else {
+
+ // The search algorithm found an intersected edge between the
+ // points lp and up. Create a new vertex between them which
+ // lies on the cutting plane. Since u and l differ by at least
+ // the tolerance, this division should never screw up.
+ if(stackp==stacke) add_memory_ds(stackp);
+ *(stackp++)=up;
+ r=u/(u-l);l=1-r;
+ pts[3*p]=pts[3*lp]*r+pts[3*up]*l;
+ pts[3*p+1]=pts[3*lp+1]*r+pts[3*up+1]*l;
+ pts[3*p+2]=pts[3*lp+2]*r+pts[3*up+2]*l;
+
+ // This point will always have three edges. Connect one of them
+ // to lp.
+ nu[p]=3;
+ if(mec[3]==mem[3]) add_memory(vc,3,stackp2);
+ vc.n_set_pointer(p,3);
+ vc.n_set(p,0,p_id);
+ vc.n_copy(p,1,up,us);
+ vc.n_copy(p,2,lp,ls);
+ ed[p]=mep[3]+7*mec[3]++;
+ ed[p][6]=p;
+ ed[up][us]=-1;
+ ed[lp][ls]=p;
+ ed[lp][nu[lp]+ls]=1;
+ ed[p][1]=lp;
+ ed[p][nu[p]+1]=ls;
+ cs=2;
+
+ // Set the direction to move in
+ qs=cycle_up(us,up);
+ qp=up;q=u;
+ }
+
+ // When the code reaches here, we have initialized the first point, and
+ // we have a direction for moving it to construct the rest of the facet
+ cp=p;rp=p;p++;
+ while(qp!=up||qs!=us) {
+
+ // We're currently tracing round an intersected facet. Keep
+ // moving around it until we find a point or edge which
+ // intersects the plane.
+ lp=ed[qp][qs];
+ lw=m_test(lp,l);
+
+ if(lw==1) {
+
+ // The point is still in the cutting space. Just add it
+ // to the delete stack and keep moving.
+ qs=cycle_up(ed[qp][nu[qp]+qs],lp);
+ qp=lp;
+ q=l;
+ if(stackp==stacke) add_memory_ds(stackp);
+ *(stackp++)=qp;
+
+ } else if(lw==-1) {
+
+ // The point is outside of the cutting space, so we've
+ // found an intersected edge. Introduce a regular point
+ // at the point of intersection. Connect it to the
+ // point we just tested. Also connect it to the previous
+ // new point in the facet we're constructing.
+ if(p==current_vertices) add_memory_vertices(vc);
+ r=q/(q-l);l=1-r;
+ pts[3*p]=pts[3*lp]*r+pts[3*qp]*l;
+ pts[3*p+1]=pts[3*lp+1]*r+pts[3*qp+1]*l;
+ pts[3*p+2]=pts[3*lp+2]*r+pts[3*qp+2]*l;
+ nu[p]=3;
+ if(mec[3]==mem[3]) add_memory(vc,3,stackp2);
+ ls=ed[qp][qs+nu[qp]];
+ vc.n_set_pointer(p,3);
+ vc.n_set(p,0,p_id);
+ vc.n_copy(p,1,qp,qs);
+ vc.n_copy(p,2,lp,ls);
+ ed[p]=mep[3]+7*mec[3]++;
+ *ed[p]=cp;
+ ed[p][1]=lp;
+ ed[p][3]=cs;
+ ed[p][4]=ls;
+ ed[p][6]=p;
+ ed[lp][ls]=p;
+ ed[lp][nu[lp]+ls]=1;
+ ed[cp][cs]=p;
+ ed[cp][nu[cp]+cs]=0;
+ ed[qp][qs]=-1;
+ qs=cycle_up(qs,qp);
+ cp=p++;
+ cs=2;
+ } else {
+
+ // We've found a point which is on the cutting plane.
+ // We're going to introduce a new point right here, but
+ // first we need to figure out the number of edges it
+ // has.
+ if(p==current_vertices) add_memory_vertices(vc);
+
+ // If the previous vertex detected a double edge, our
+ // new vertex will have one less edge.
+ k=double_edge?0:1;
+ qs=ed[qp][nu[qp]+qs];
+ qp=lp;
+ iqs=qs;
+
+ // Start testing the edges of the current point until
+ // we find one which isn't outside the cutting space
+ do {
+ k++;
+ qs=cycle_up(qs,qp);
+ lp=ed[qp][qs];
+ lw=m_test(lp,l);
+ } while (lw==-1);
+
+ // Now we need to find out whether this marginal vertex
+ // we are on has been visited before, because if that's
+ // the case, we need to add vertices to the existing
+ // new vertex, rather than creating a fresh one. We also
+ // need to figure out whether we're in a case where we
+ // might be creating a duplicate edge.
+ j=-ed[qp][nu[qp]<<1];
+ if(qp==up&&qs==us) {
+
+ // If we're heading into the final part of the
+ // new facet, then we never worry about the
+ // duplicate edge calculation.
+ new_double_edge=false;
+ if(j>0) k+=nu[j];
+ } else {
+ if(j>0) {
+
+ // This vertex was visited before, so
+ // count those vertices to the ones we
+ // already have.
+ k+=nu[j];
+
+ // The only time when we might make a
+ // duplicate edge is if the point we're
+ // going to move to next is also a
+ // marginal point, so test for that
+ // first.
+ if(lw==0) {
+
+ // Now see whether this marginal point
+ // has been visited before.
+ i=-ed[lp][nu[lp]<<1];
+ if(i>0) {
+
+ // Now see if the last edge of that other
+ // marginal point actually ends up here.
+ if(ed[i][nu[i]-1]==j) {
+ new_double_edge=true;
+ k-=1;
+ } else new_double_edge=false;
+ } else {
+
+ // That marginal point hasn't been visited
+ // before, so we probably don't have to worry
+ // about duplicate edges, except in the
+ // case when that's the way into the end
+ // of the facet, because that way always creates
+ // an edge.
+ if(j==rp&&lp==up&&ed[qp][nu[qp]+qs]==us) {
+ new_double_edge=true;
+ k-=1;
+ } else new_double_edge=false;
+ }
+ } else new_double_edge=false;
+ } else {
+
+ // The vertex hasn't been visited
+ // before, but let's see if it's
+ // marginal
+ if(lw==0) {
+
+ // If it is, we need to check
+ // for the case that it's a
+ // small branch, and that we're
+ // heading right back to where
+ // we came from
+ i=-ed[lp][nu[lp]<<1];
+ if(i==cp) {
+ new_double_edge=true;
+ k-=1;
+ } else new_double_edge=false;
+ } else new_double_edge=false;
+ }
+ }
+
+ // k now holds the number of edges of the new vertex
+ // we are forming. Add memory for it if it doesn't exist
+ // already.
+ while(k>=current_vertex_order) add_memory_vorder(vc);
+ if(mec[k]==mem[k]) add_memory(vc,k,stackp2);
+
+ // Now create a new vertex with order k, or augment
+ // the existing one
+ if(j>0) {
+
+ // If we're augmenting a vertex but we don't
+ // actually need any more edges, just skip this
+ // routine to avoid memory confusion
+ if(nu[j]!=k) {
+ // Allocate memory and copy the edges
+ // of the previous instance into it
+ vc.n_set_aux1(k);
+ edp=mep[k]+((k<<1)+1)*mec[k]++;
+ i=0;
+ while(i<nu[j]) {
+ vc.n_copy_aux1(j,i);
+ edp[i]=ed[j][i];
+ edp[k+i]=ed[j][nu[j]+i];
+ i++;
+ }
+ edp[k<<1]=j;
+
+ // Remove the previous instance with
+ // fewer vertices from the memory
+ // structure
+ edd=mep[nu[j]]+((nu[j]<<1)+1)*--mec[nu[j]];
+ if(edd!=ed[j]) {
+ for(lw=0;lw<=(nu[j]<<1);lw++) ed[j][lw]=edd[lw];
+ vc.n_set_aux2_copy(j,nu[j]);
+ vc.n_copy_pointer(edd[nu[j]<<1],j);
+ ed[edd[nu[j]<<1]]=ed[j];
+ }
+ vc.n_set_to_aux1(j);
+ ed[j]=edp;
+ } else i=nu[j];
+ } else {
+
+ // Allocate a new vertex of order k
+ vc.n_set_pointer(p,k);
+ ed[p]=mep[k]+((k<<1)+1)*mec[k]++;
+ ed[p][k<<1]=p;
+ if(stackp2==stacke2) add_memory_ds2(stackp2);
+ *(stackp2++)=qp;
+ pts[3*p]=pts[3*qp];
+ pts[3*p+1]=pts[3*qp+1];
+ pts[3*p+2]=pts[3*qp+2];
+ ed[qp][nu[qp]<<1]=-p;
+ j=p++;
+ i=0;
+ }
+ nu[j]=k;
+
+ // Unless the previous case was a double edge, connect
+ // the first available edge of the new vertex to the
+ // last one in the facet
+ if(!double_edge) {
+ ed[j][i]=cp;
+ ed[j][nu[j]+i]=cs;
+ vc.n_set(j,i,p_id);
+ ed[cp][cs]=j;
+ ed[cp][nu[cp]+cs]=i;
+ i++;
+ }
+
+ // Copy in the edges of the underlying vertex,
+ // and do one less if this was a double edge
+ qs=iqs;
+ while(i<(new_double_edge?k:k-1)) {
+ qs=cycle_up(qs,qp);
+ lp=ed[qp][qs];ls=ed[qp][nu[qp]+qs];
+ vc.n_copy(j,i,qp,qs);
+ ed[j][i]=lp;
+ ed[j][nu[j]+i]=ls;
+ ed[lp][ls]=j;
+ ed[lp][nu[lp]+ls]=i;
+ ed[qp][qs]=-1;
+ i++;
+ }
+ qs=cycle_up(qs,qp);
+ cs=i;
+ cp=j;
+ vc.n_copy(j,new_double_edge?0:cs,qp,qs);
+
+ // Update the double_edge flag, to pass it
+ // to the next instance of this routine
+ double_edge=new_double_edge;
+ }
+ }
+
+ // Connect the final created vertex to the initial one
+ ed[cp][cs]=rp;
+ *ed[rp]=cp;
+ ed[cp][nu[cp]+cs]=0;
+ ed[rp][nu[rp]]=cs;
+
+ // Delete points: first, remove any duplicates
+ dsp=ds;
+ while(dsp<stackp) {
+ j=*dsp;
+ if(ed[j][nu[j]]!=-1) {
+ ed[j][nu[j]]=-1;
+ dsp++;
+ } else *dsp=*(--stackp);
+ }
+
+ // Add the points in the auxiliary delete stack,
+ // and reset their back pointers
+ for(dsp=ds2;dsp<stackp2;dsp++) {
+ j=*dsp;
+ ed[j][nu[j]<<1]=j;
+ if(ed[j][nu[j]]!=-1) {
+ ed[j][nu[j]]=-1;
+ if(stackp==stacke) add_memory_ds(stackp);
+ *(stackp++)=j;
+ }
+ }
+
+ // Scan connections and add in extras
+ for(dsp=ds;dsp<stackp;dsp++) {
+ cp=*dsp;
+ for(edp=ed[cp];edp<ed[cp]+nu[cp];edp++) {
+ qp=*edp;
+ if(qp!=-1&&ed[qp][nu[qp]]!=-1) {
+ if(stackp==stacke) {
+ int dis=stackp-dsp;
+ add_memory_ds(stackp);
+ dsp=ds+dis;
+ }
+ *(stackp++)=qp;
+ ed[qp][nu[qp]]=-1;
+ }
+ }
+ }
+ up=0;
+
+ // Delete them from the array structure
+ while(stackp>ds) {
+ --p;
+ while(ed[p][nu[p]]==-1) {
+ j=nu[p];
+ edp=ed[p];edd=(mep[j]+((j<<1)+1)*--mec[j]);
+ while(edp<ed[p]+(j<<1)+1) *(edp++)=*(edd++);
+ vc.n_set_aux2_copy(p,j);
+ vc.n_copy_pointer(ed[p][(j<<1)],p);
+ ed[ed[p][(j<<1)]]=ed[p];
+ --p;
+ }
+ up=*(--stackp);
+ if(up<p) {
+
+ // Vertex management
+ pts[3*up]=pts[3*p];
+ pts[3*up+1]=pts[3*p+1];
+ pts[3*up+2]=pts[3*p+2];
+
+ // Memory management
+ j=nu[up];
+ edp=ed[up];edd=(mep[j]+((j<<1)+1)*--mec[j]);
+ while(edp<ed[up]+(j<<1)+1) *(edp++)=*(edd++);
+ vc.n_set_aux2_copy(up,j);
+ vc.n_copy_pointer(ed[up][j<<1],up);
+ vc.n_copy_pointer(up,p);
+ ed[ed[up][j<<1]]=ed[up];
+
+ // Edge management
+ ed[up]=ed[p];
+ nu[up]=nu[p];
+ for(i=0;i<nu[up];i++) ed[ed[up][i]][ed[up][nu[up]+i]]=up;
+ ed[up][nu[up]<<1]=up;
+ } else up=p++;
+ }
+
+ // Check for any vertices of zero order
+ if(*mec>0) voro_fatal_error("Zero order vertex formed",VOROPP_INTERNAL_ERROR);
+
+ // Collapse any order 2 vertices and exit
+ return collapse_order2(vc);
+}
+
+/** During the creation of a new facet in the plane routine, it is possible
+ * that some order two vertices may arise. This routine removes them.
+ * Suppose an order two vertex joins c and d. If there's a edge between
+ * c and d already, then the order two vertex is just removed; otherwise,
+ * the order two vertex is removed and c and d are joined together directly.
+ * It is possible this process will create order two or order one vertices,
+ * and the routine is continually run until all of them are removed.
+ * \return False if the vertex removal was unsuccessful, indicative of the cell
+ * reducing to zero volume and disappearing; true if the vertex removal
+ * was successful. */
+template<class vc_class>
+inline bool voronoicell_base::collapse_order2(vc_class &vc) {
+ if(!collapse_order1(vc)) return false;
+ int a,b,i,j,k,l;
+ while(mec[2]>0) {
+
+ // Pick a order 2 vertex and read in its edges
+ i=--mec[2];
+ j=mep[2][5*i];k=mep[2][5*i+1];
+ if(j==k) {
+#if VOROPP_VERBOSE >=1
+ fputs("Order two vertex joins itself",stderr);
+#endif
+ return false;
+ }
+
+ // Scan the edges of j to see if joins k
+ for(l=0;l<nu[j];l++) {
+ if(ed[j][l]==k) break;
+ }
+
+ // If j doesn't already join k, join them together.
+ // Otherwise delete the connection to the current
+ // vertex from j and k.
+ a=mep[2][5*i+2];b=mep[2][5*i+3];i=mep[2][5*i+4];
+ if(l==nu[j]) {
+ ed[j][a]=k;
+ ed[k][b]=j;
+ ed[j][nu[j]+a]=b;
+ ed[k][nu[k]+b]=a;
+ } else {
+ if(!delete_connection(vc,j,a,false)) return false;
+ if(!delete_connection(vc,k,b,true)) return false;
+ }
+
+ // Compact the memory
+ --p;
+ if(up==i) up=0;
+ if(p!=i) {
+ if(up==p) up=i;
+ pts[3*i]=pts[3*p];
+ pts[3*i+1]=pts[3*p+1];
+ pts[3*i+2]=pts[3*p+2];
+ for(k=0;k<nu[p];k++) ed[ed[p][k]][ed[p][nu[p]+k]]=i;
+ vc.n_copy_pointer(i,p);
+ ed[i]=ed[p];
+ nu[i]=nu[p];
+ ed[i][nu[i]<<1]=i;
+ }
+
+ // Collapse any order 1 vertices if they were created
+ if(!collapse_order1(vc)) return false;
+ }
+ return true;
+}
+
+/** Order one vertices can potentially be created during the order two collapse
+ * routine. This routine keeps removing them until there are none left.
+ * \return False if the vertex removal was unsuccessful, indicative of the cell
+ * having zero volume and disappearing; true if the vertex removal was
+ * successful. */
+template<class vc_class>
+inline bool voronoicell_base::collapse_order1(vc_class &vc) {
+ int i,j,k;
+ while(mec[1]>0) {
+ up=0;
+#if VOROPP_VERBOSE >=1
+ fputs("Order one collapse\n",stderr);
+#endif
+ i=--mec[1];
+ j=mep[1][3*i];k=mep[1][3*i+1];
+ i=mep[1][3*i+2];
+ if(!delete_connection(vc,j,k,false)) return false;
+ --p;
+ if(up==i) up=0;
+ if(p!=i) {
+ if(up==p) up=i;
+ pts[3*i]=pts[3*p];
+ pts[3*i+1]=pts[3*p+1];
+ pts[3*i+2]=pts[3*p+2];
+ for(k=0;k<nu[p];k++) ed[ed[p][k]][ed[p][nu[p]+k]]=i;
+ vc.n_copy_pointer(i,p);
+ ed[i]=ed[p];
+ nu[i]=nu[p];
+ ed[i][nu[i]<<1]=i;
+ }
+ }
+ return true;
+}
+
+/** This routine deletes the kth edge of vertex j and reorganizes the memory.
+ * If the neighbor computation is enabled, we also have to supply an handedness
+ * flag to decide whether to preserve the plane on the left or right of the
+ * connection.
+ * \return False if a zero order vertex was formed, indicative of the cell
+ * disappearing; true if the vertex removal was successful. */
+template<class vc_class>
+inline bool voronoicell_base::delete_connection(vc_class &vc,int j,int k,bool hand) {
+ int q=hand?k:cycle_up(k,j);
+ int i=nu[j]-1,l,*edp,*edd,m;
+#if VOROPP_VERBOSE >=1
+ if(i<1) {
+ fputs("Zero order vertex formed\n",stderr);
+ return false;
+ }
+#endif
+ if(mec[i]==mem[i]) add_memory(vc,i,ds2);
+ vc.n_set_aux1(i);
+ for(l=0;l<q;l++) vc.n_copy_aux1(j,l);
+ while(l<i) {
+ vc.n_copy_aux1_shift(j,l);
+ l++;
+ }
+ edp=mep[i]+((i<<1)+1)*mec[i]++;
+ edp[i<<1]=j;
+ for(l=0;l<k;l++) {
+ edp[l]=ed[j][l];
+ edp[l+i]=ed[j][l+nu[j]];
+ }
+ while(l<i) {
+ m=ed[j][l+1];
+ edp[l]=m;
+ k=ed[j][l+nu[j]+1];
+ edp[l+i]=k;
+ ed[m][nu[m]+k]--;
+ l++;
+ }
+
+ edd=mep[nu[j]]+((nu[j]<<1)+1)*--mec[nu[j]];
+ for(l=0;l<=(nu[j]<<1);l++) ed[j][l]=edd[l];
+ vc.n_set_aux2_copy(j,nu[j]);
+ vc.n_set_to_aux2(edd[nu[j]<<1]);
+ vc.n_set_to_aux1(j);
+ ed[edd[nu[j]<<1]]=edd;
+ ed[j]=edp;
+ nu[j]=i;
+ return true;
+}
+
+/** Calculates the volume of the Voronoi cell, by decomposing the cell into
+ * tetrahedra extending outward from the zeroth vertex, whose volumes are
+ * evaluated using a scalar triple product.
+ * \return A floating point number holding the calculated volume. */
+double voronoicell_base::volume() {
+ const double fe=1/48.0;
+ double vol=0;
+ int i,j,k,l,m,n;
+ double ux,uy,uz,vx,vy,vz,wx,wy,wz;
+ for(i=1;i<p;i++) {
+ ux=*pts-pts[3*i];
+ uy=pts[1]-pts[3*i+1];
+ uz=pts[2]-pts[3*i+2];
+ for(j=0;j<nu[i];j++) {
+ k=ed[i][j];
+ if(k>=0) {
+ ed[i][j]=-1-k;
+ l=cycle_up(ed[i][nu[i]+j],k);
+ vx=pts[3*k]-*pts;
+ vy=pts[3*k+1]-pts[1];
+ vz=pts[3*k+2]-pts[2];
+ m=ed[k][l];ed[k][l]=-1-m;
+ while(m!=i) {
+ n=cycle_up(ed[k][nu[k]+l],m);
+ wx=pts[3*m]-*pts;
+ wy=pts[3*m+1]-pts[1];
+ wz=pts[3*m+2]-pts[2];
+ vol+=ux*vy*wz+uy*vz*wx+uz*vx*wy-uz*vy*wx-uy*vx*wz-ux*vz*wy;
+ k=m;l=n;vx=wx;vy=wy;vz=wz;
+ m=ed[k][l];ed[k][l]=-1-m;
+ }
+ }
+ }
+ }
+ reset_edges();
+ return vol*fe;
+}
+
+/** Calculates the areas of each face of the Voronoi cell and prints the
+ * results to an output stream.
+ * \param[out] v the vector to store the results in. */
+void voronoicell_base::face_areas(std::vector<double> &v) {
+ double area;
+ v.clear();
+ int i,j,k,l,m,n;
+ double ux,uy,uz,vx,vy,vz,wx,wy,wz;
+ for(i=1;i<p;i++) for(j=0;j<nu[i];j++) {
+ k=ed[i][j];
+ if(k>=0) {
+ area=0;
+ ed[i][j]=-1-k;
+ l=cycle_up(ed[i][nu[i]+j],k);
+ m=ed[k][l];ed[k][l]=-1-m;
+ while(m!=i) {
+ n=cycle_up(ed[k][nu[k]+l],m);
+ ux=pts[3*k]-pts[3*i];
+ uy=pts[3*k+1]-pts[3*i+1];
+ uz=pts[3*k+2]-pts[3*i+2];
+ vx=pts[3*m]-pts[3*i];
+ vy=pts[3*m+1]-pts[3*i+1];
+ vz=pts[3*m+2]-pts[3*i+2];
+ wx=uy*vz-uz*vy;
+ wy=uz*vx-ux*vz;
+ wz=ux*vy-uy*vx;
+ area+=sqrt(wx*wx+wy*wy+wz*wz);
+ k=m;l=n;
+ m=ed[k][l];ed[k][l]=-1-m;
+ }
+ v.push_back(0.125*area);
+ }
+ }
+ reset_edges();
+}
+
+
+/** Calculates the total surface area of the Voronoi cell.
+ * \return The computed area. */
+double voronoicell_base::surface_area() {
+ double area=0;
+ int i,j,k,l,m,n;
+ double ux,uy,uz,vx,vy,vz,wx,wy,wz;
+ for(i=1;i<p;i++) for(j=0;j<nu[i];j++) {
+ k=ed[i][j];
+ if(k>=0) {
+ ed[i][j]=-1-k;
+ l=cycle_up(ed[i][nu[i]+j],k);
+ m=ed[k][l];ed[k][l]=-1-m;
+ while(m!=i) {
+ n=cycle_up(ed[k][nu[k]+l],m);
+ ux=pts[3*k]-pts[3*i];
+ uy=pts[3*k+1]-pts[3*i+1];
+ uz=pts[3*k+2]-pts[3*i+2];
+ vx=pts[3*m]-pts[3*i];
+ vy=pts[3*m+1]-pts[3*i+1];
+ vz=pts[3*m+2]-pts[3*i+2];
+ wx=uy*vz-uz*vy;
+ wy=uz*vx-ux*vz;
+ wz=ux*vy-uy*vx;
+ area+=sqrt(wx*wx+wy*wy+wz*wz);
+ k=m;l=n;
+ m=ed[k][l];ed[k][l]=-1-m;
+ }
+ }
+ }
+ reset_edges();
+ return 0.125*area;
+}
+
+
+/** Calculates the centroid of the Voronoi cell, by decomposing the cell into
+ * tetrahedra extending outward from the zeroth vertex.
+ * \param[out] (cx,cy,cz) references to floating point numbers in which to
+ * pass back the centroid vector. */
+void voronoicell_base::centroid(double &cx,double &cy,double &cz) {
+ double tvol,vol=0;cx=cy=cz=0;
+ int i,j,k,l,m,n;
+ double ux,uy,uz,vx,vy,vz,wx,wy,wz;
+ for(i=1;i<p;i++) {
+ ux=*pts-pts[3*i];
+ uy=pts[1]-pts[3*i+1];
+ uz=pts[2]-pts[3*i+2];
+ for(j=0;j<nu[i];j++) {
+ k=ed[i][j];
+ if(k>=0) {
+ ed[i][j]=-1-k;
+ l=cycle_up(ed[i][nu[i]+j],k);
+ vx=pts[3*k]-*pts;
+ vy=pts[3*k+1]-pts[1];
+ vz=pts[3*k+2]-pts[2];
+ m=ed[k][l];ed[k][l]=-1-m;
+ while(m!=i) {
+ n=cycle_up(ed[k][nu[k]+l],m);
+ wx=pts[3*m]-*pts;
+ wy=pts[3*m+1]-pts[1];
+ wz=pts[3*m+2]-pts[2];
+ tvol=ux*vy*wz+uy*vz*wx+uz*vx*wy-uz*vy*wx-uy*vx*wz-ux*vz*wy;
+ vol+=tvol;
+ cx+=(wx+vx-ux)*tvol;
+ cy+=(wy+vy-uy)*tvol;
+ cz+=(wz+vz-uz)*tvol;
+ k=m;l=n;vx=wx;vy=wy;vz=wz;
+ m=ed[k][l];ed[k][l]=-1-m;
+ }
+ }
+ }
+ }
+ reset_edges();
+ if(vol>tolerance_sq) {
+ vol=0.125/vol;
+ cx=cx*vol+0.5*(*pts);
+ cy=cy*vol+0.5*pts[1];
+ cz=cz*vol+0.5*pts[2];
+ } else cx=cy=cz=0;
+}
+
+/** Computes the maximum radius squared of a vertex from the center of the
+ * cell. It can be used to determine when enough particles have been testing an
+ * all planes that could cut the cell have been considered.
+ * \return The maximum radius squared of a vertex.*/
+double voronoicell_base::max_radius_squared() {
+ double r,s,*ptsp=pts+3,*ptse=pts+3*p;
+ r=*pts*(*pts)+pts[1]*pts[1]+pts[2]*pts[2];
+ while(ptsp<ptse) {
+ s=*ptsp*(*ptsp);ptsp++;
+ s+=*ptsp*(*ptsp);ptsp++;
+ s+=*ptsp*(*ptsp);ptsp++;
+ if(s>r) r=s;
+ }
+ return r;
+}
+
+/** Calculates the total edge distance of the Voronoi cell.
+ * \return A floating point number holding the calculated distance. */
+double voronoicell_base::total_edge_distance() {
+ int i,j,k;
+ double dis=0,dx,dy,dz;
+ for(i=0;i<p-1;i++) for(j=0;j<nu[i];j++) {
+ k=ed[i][j];
+ if(k>i) {
+ dx=pts[3*k]-pts[3*i];
+ dy=pts[3*k+1]-pts[3*i+1];
+ dz=pts[3*k+2]-pts[3*i+2];
+ dis+=sqrt(dx*dx+dy*dy+dz*dz);
+ }
+ }
+ return 0.5*dis;
+}
+
+/** Outputs the edges of the Voronoi cell in POV-Ray format to an open file
+ * stream, displacing the cell by given vector.
+ * \param[in] (x,y,z) a displacement vector to be added to the cell's position.
+ * \param[in] fp a file handle to write to. */
+void voronoicell_base::draw_pov(double x,double y,double z,FILE* fp) {
+ int i,j,k;double *ptsp=pts,*pt2;
+ char posbuf1[128],posbuf2[128];
+ for(i=0;i<p;i++,ptsp+=3) {
+ sprintf(posbuf1,"%g,%g,%g",x+*ptsp*0.5,y+ptsp[1]*0.5,z+ptsp[2]*0.5);
+ fprintf(fp,"sphere{<%s>,r}\n",posbuf1);
+ for(j=0;j<nu[i];j++) {
+ k=ed[i][j];
+ if(k<i) {
+ pt2=pts+3*k;
+ sprintf(posbuf2,"%g,%g,%g",x+*pt2*0.5,y+0.5*pt2[1],z+0.5*pt2[2]);
+ if(strcmp(posbuf1,posbuf2)!=0) fprintf(fp,"cylinder{<%s>,<%s>,r}\n",posbuf1,posbuf2);
+ }
+ }
+ }
+}
+
+/** Outputs the edges of the Voronoi cell in gnuplot format to an output stream.
+ * \param[in] (x,y,z) a displacement vector to be added to the cell's position.
+ * \param[in] fp a file handle to write to. */
+void voronoicell_base::draw_gnuplot(double x,double y,double z,FILE *fp) {
+ int i,j,k,l,m;
+ for(i=1;i<p;i++) for(j=0;j<nu[i];j++) {
+ k=ed[i][j];
+ if(k>=0) {
+ 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]);
+ l=i;m=j;
+ do {
+ ed[k][ed[l][nu[l]+m]]=-1-l;
+ ed[l][m]=-1-k;
+ l=k;
+ 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]);
+ } while (search_edge(l,m,k));
+ fputs("\n\n",fp);
+ }
+ }
+ reset_edges();
+}
+
+inline bool voronoicell_base::search_edge(int l,int &m,int &k) {
+ for(m=0;m<nu[l];m++) {
+ k=ed[l][m];
+ if(k>=0) return true;
+ }
+ return false;
+}
+
+/** Outputs the Voronoi cell in the POV mesh2 format, described in section
+ * 1.3.2.2 of the POV-Ray documentation. The mesh2 output consists of a list of
+ * vertex vectors, followed by a list of triangular faces. The routine also
+ * makes use of the optional inside_vector specification, which makes the mesh
+ * object solid, so the the POV-Ray Constructive Solid Geometry (CSG) can be
+ * applied.
+ * \param[in] (x,y,z) a displacement vector to be added to the cell's position.
+ * \param[in] fp a file handle to write to. */
+void voronoicell_base::draw_pov_mesh(double x,double y,double z,FILE *fp) {
+ int i,j,k,l,m,n;
+ double *ptsp=pts;
+ fprintf(fp,"mesh2 {\nvertex_vectors {\n%d\n",p);
+ 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);
+ fprintf(fp,"}\nface_indices {\n%d\n",(p-2)<<1);
+ for(i=1;i<p;i++) for(j=0;j<nu[i];j++) {
+ k=ed[i][j];
+ if(k>=0) {
+ ed[i][j]=-1-k;
+ l=cycle_up(ed[i][nu[i]+j],k);
+ m=ed[k][l];ed[k][l]=-1-m;
+ while(m!=i) {
+ n=cycle_up(ed[k][nu[k]+l],m);
+ fprintf(fp,",<%d,%d,%d>\n",i,k,m);
+ k=m;l=n;
+ m=ed[k][l];ed[k][l]=-1-m;
+ }
+ }
+ }
+ fputs("}\ninside_vector <0,0,1>\n}\n",fp);
+ reset_edges();
+}
+
+/** Several routines in the class that gather cell-based statistics internally
+ * track their progress by flipping edges to negative so that they know what
+ * parts of the cell have already been tested. This function resets them back
+ * to positive. When it is called, it assumes that every edge in the routine
+ * should have already been flipped to negative, and it bails out with an
+ * internal error if it encounters a positive edge. */
+inline void voronoicell_base::reset_edges() {
+ int i,j;
+ for(i=0;i<p;i++) for(j=0;j<nu[i];j++) {
+ if(ed[i][j]>=0) voro_fatal_error("Edge reset routine found a previously untested edge",VOROPP_INTERNAL_ERROR);
+ ed[i][j]=-1-ed[i][j];
+ }
+}
+
+/** Checks to see if a given vertex is inside, outside or within the test
+ * plane. If the point is far away from the test plane, the routine immediately
+ * returns whether it is inside or outside. If the routine is close the the
+ * plane and within the specified tolerance, then the special check_marginal()
+ * routine is called.
+ * \param[in] n the vertex to test.
+ * \param[out] ans the result of the scalar product used in evaluating the
+ * location of the point.
+ * \return -1 if the point is inside the plane, 1 if the point is outside the
+ * plane, or 0 if the point is within the plane. */
+inline int voronoicell_base::m_test(int n,double &ans) {
+ double *pp=pts+n+(n<<1);
+ ans=*(pp++)*px;
+ ans+=*(pp++)*py;
+ ans+=*pp*pz-prsq;
+ if(ans<-tolerance2) {
+ return -1;
+ } else if(ans>tolerance2) {
+ return 1;
+ }
+ return check_marginal(n,ans);
+}
+
+/** Checks to see if a given vertex is inside, outside or within the test
+ * plane, for the case when the point has been detected to be very close to the
+ * plane. The routine ensures that the returned results are always consistent
+ * with previous tests, by keeping a table of any marginal results. The routine
+ * first sees if the vertex is in the table, and if it finds a previously
+ * computed result it uses that. Otherwise, it computes a result for this
+ * vertex and adds it the table.
+ * \param[in] n the vertex to test.
+ * \param[in] ans the result of the scalar product used in evaluating
+ * the location of the point.
+ * \return -1 if the point is inside the plane, 1 if the point is outside the
+ * plane, or 0 if the point is within the plane. */
+int voronoicell_base::check_marginal(int n,double &ans) {
+ int i;
+ for(i=0;i<n_marg;i+=2) if(marg[i]==n) return marg[i+1];
+ if(n_marg==current_marginal) {
+ current_marginal<<=1;
+ if(current_marginal>max_marginal)
+ voro_fatal_error("Marginal case buffer allocation exceeded absolute maximum",VOROPP_MEMORY_ERROR);
+#if VOROPP_VERBOSE >=2
+ fprintf(stderr,"Marginal cases buffer scaled up to %d\n",i);
+#endif
+ int *pmarg=new int[current_marginal];
+ for(int j=0;j<n_marg;j++) pmarg[j]=marg[j];
+ delete [] marg;
+ marg=pmarg;
+ }
+ marg[n_marg++]=n;
+ marg[n_marg++]=ans>tolerance?1:(ans<-tolerance?-1:0);
+ return marg[n_marg-1];
+}
+
+/** For each face of the Voronoi cell, this routine prints the out the normal
+ * vector of the face, and scales it to the distance from the cell center to
+ * that plane.
+ * \param[out] v the vector to store the results in. */
+void voronoicell_base::normals(std::vector<double> &v) {
+ int i,j,k;
+ v.clear();
+ for(i=1;i<p;i++) for(j=0;j<nu[i];j++) {
+ k=ed[i][j];
+ if(k>=0) normals_search(v,i,j,k);
+ }
+ reset_edges();
+}
+
+/** This inline routine is called by normals(). It attempts to construct a
+ * single normal vector that is associated with a particular face. It first
+ * traces around the face, trying to find two vectors along the face edges
+ * whose vector product is above the numerical tolerance. It then constructs
+ * the normal vector using this product. If the face is too small, and none of
+ * the vector products are large enough, the routine may return (0,0,0) as the
+ * normal vector.
+ * \param[in] v the vector to store the results in.
+ * \param[in] i the initial vertex of the face to test.
+ * \param[in] j the index of an edge of the vertex.
+ * \param[in] k the neighboring vertex of i, set to ed[i][j]. */
+inline void voronoicell_base::normals_search(std::vector<double> &v,int i,int j,int k) {
+ ed[i][j]=-1-k;
+ int l=cycle_up(ed[i][nu[i]+j],k),m;
+ double ux,uy,uz,vx,vy,vz,wx,wy,wz,wmag;
+ do {
+ m=ed[k][l];ed[k][l]=-1-m;
+ ux=pts[3*m]-pts[3*k];
+ uy=pts[3*m+1]-pts[3*k+1];
+ uz=pts[3*m+2]-pts[3*k+2];
+
+ // Test to see if the length of this edge is above the tolerance
+ if(ux*ux+uy*uy+uz*uz>tolerance_sq) {
+ while(m!=i) {
+ l=cycle_up(ed[k][nu[k]+l],m);
+ k=m;m=ed[k][l];ed[k][l]=-1-m;
+ vx=pts[3*m]-pts[3*k];
+ vy=pts[3*m+1]-pts[3*k+1];
+ vz=pts[3*m+2]-pts[3*k+2];
+
+ // Construct the vector product of this edge with
+ // the previous one
+ wx=uz*vy-uy*vz;
+ wy=ux*vz-uz*vx;
+ wz=uy*vx-ux*vy;
+ wmag=wx*wx+wy*wy+wz*wz;
+
+ // Test to see if this vector product of the
+ // two edges is above the tolerance
+ if(wmag>tolerance_sq) {
+
+ // Construct the normal vector and print it
+ wmag=1/sqrt(wmag);
+ v.push_back(wx*wmag);
+ v.push_back(wy*wmag);
+ v.push_back(wz*wmag);
+
+ // Mark all of the remaining edges of this
+ // face and exit
+ while(m!=i) {
+ l=cycle_up(ed[k][nu[k]+l],m);
+ k=m;m=ed[k][l];ed[k][l]=-1-m;
+ }
+ return;
+ }
+ }
+ v.push_back(0);
+ v.push_back(0);
+ v.push_back(0);
+ return;
+ }
+ l=cycle_up(ed[k][nu[k]+l],m);
+ k=m;
+ } while (k!=i);
+ v.push_back(0);
+ v.push_back(0);
+ v.push_back(0);
+}
+
+
+/** Returns the number of faces of a computed Voronoi cell.
+ * \return The number of faces. */
+int voronoicell_base::number_of_faces() {
+ int i,j,k,l,m,s=0;
+ for(i=1;i<p;i++) for(j=0;j<nu[i];j++) {
+ k=ed[i][j];
+ if(k>=0) {
+ s++;
+ ed[i][j]=-1-k;
+ l=cycle_up(ed[i][nu[i]+j],k);
+ do {
+ m=ed[k][l];
+ ed[k][l]=-1-m;
+ l=cycle_up(ed[k][nu[k]+l],m);
+ k=m;
+ } while (k!=i);
+
+ }
+ }
+ reset_edges();
+ return s;
+}
+
+/** Returns a vector of the vertex orders.
+ * \param[out] v the vector to store the results in. */
+void voronoicell_base::vertex_orders(std::vector<int> &v) {
+ v.resize(p);
+ for(int i=0;i<p;i++) v[i]=nu[i];
+}
+
+/** Outputs the vertex orders.
+ * \param[out] fp the file handle to write to. */
+void voronoicell_base::output_vertex_orders(FILE *fp) {
+ if(p>0) {
+ fprintf(fp,"%d",*nu);
+ for(int *nup=nu+1;nup<nu+p;nup++) fprintf(fp," %d",*nup);
+ }
+}
+
+/** Returns a vector of the vertex vectors using the local coordinate system.
+ * \param[out] v the vector to store the results in. */
+void voronoicell_base::vertices(std::vector<double> &v) {
+ v.resize(3*p);
+ double *ptsp=pts;
+ for(int i=0;i<3*p;i+=3) {
+ v[i]=*(ptsp++)*0.5;
+ v[i+1]=*(ptsp++)*0.5;
+ v[i+2]=*(ptsp++)*0.5;
+ }
+}
+
+/** Outputs the vertex vectors using the local coordinate system.
+ * \param[out] fp the file handle to write to. */
+void voronoicell_base::output_vertices(FILE *fp) {
+ if(p>0) {
+ fprintf(fp,"(%g,%g,%g)",*pts*0.5,pts[1]*0.5,pts[2]*0.5);
+ 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);
+ }
+}
+
+
+/** Returns a vector of the vertex vectors in the global coordinate system.
+ * \param[out] v the vector to store the results in.
+ * \param[in] (x,y,z) the position vector of the particle in the global
+ * coordinate system. */
+void voronoicell_base::vertices(double x,double y,double z,std::vector<double> &v) {
+ v.resize(3*p);
+ double *ptsp=pts;
+ for(int i=0;i<3*p;i+=3) {
+ v[i]=x+*(ptsp++)*0.5;
+ v[i+1]=y+*(ptsp++)*0.5;
+ v[i+2]=z+*(ptsp++)*0.5;
+ }
+}
+
+/** Outputs the vertex vectors using the global coordinate system.
+ * \param[out] fp the file handle to write to.
+ * \param[in] (x,y,z) the position vector of the particle in the global
+ * coordinate system. */
+void voronoicell_base::output_vertices(double x,double y,double z,FILE *fp) {
+ if(p>0) {
+ fprintf(fp,"(%g,%g,%g)",x+*pts*0.5,y+pts[1]*0.5,z+pts[2]*0.5);
+ 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);
+ }
+}
+
+/** This routine returns the perimeters of each face.
+ * \param[out] v the vector to store the results in. */
+void voronoicell_base::face_perimeters(std::vector<double> &v) {
+ v.clear();
+ int i,j,k,l,m;
+ double dx,dy,dz,perim;
+ for(i=1;i<p;i++) for(j=0;j<nu[i];j++) {
+ k=ed[i][j];
+ if(k>=0) {
+ dx=pts[3*k]-pts[3*i];
+ dy=pts[3*k+1]-pts[3*i+1];
+ dz=pts[3*k+2]-pts[3*i+2];
+ perim=sqrt(dx*dx+dy*dy+dz*dz);
+ ed[i][j]=-1-k;
+ l=cycle_up(ed[i][nu[i]+j],k);
+ do {
+ m=ed[k][l];
+ dx=pts[3*m]-pts[3*k];
+ dy=pts[3*m+1]-pts[3*k+1];
+ dz=pts[3*m+2]-pts[3*k+2];
+ perim+=sqrt(dx*dx+dy*dy+dz*dz);
+ ed[k][l]=-1-m;
+ l=cycle_up(ed[k][nu[k]+l],m);
+ k=m;
+ } while (k!=i);
+ v.push_back(0.5*perim);
+ }
+ }
+ reset_edges();
+}
+
+/** For each face, this routine outputs a bracketed sequence of numbers
+ * containing a list of all the vertices that make up that face.
+ * \param[out] v the vector to store the results in. */
+void voronoicell_base::face_vertices(std::vector<int> &v) {
+ int i,j,k,l,m,vp(0),vn;
+ v.clear();
+ for(i=1;i<p;i++) for(j=0;j<nu[i];j++) {
+ k=ed[i][j];
+ if(k>=0) {
+ v.push_back(0);
+ v.push_back(i);
+ ed[i][j]=-1-k;
+ l=cycle_up(ed[i][nu[i]+j],k);
+ do {
+ v.push_back(k);
+ m=ed[k][l];
+ ed[k][l]=-1-m;
+ l=cycle_up(ed[k][nu[k]+l],m);
+ k=m;
+ } while (k!=i);
+ vn=v.size();
+ v[vp]=vn-vp-1;
+ vp=vn;
+ }
+ }
+ reset_edges();
+}
+
+/** Outputs a list of the number of edges in each face.
+ * \param[out] v the vector to store the results in. */
+void voronoicell_base::face_orders(std::vector<int> &v) {
+ int i,j,k,l,m,q;
+ v.clear();
+ for(i=1;i<p;i++) for(j=0;j<nu[i];j++) {
+ k=ed[i][j];
+ if(k>=0) {
+ q=1;
+ ed[i][j]=-1-k;
+ l=cycle_up(ed[i][nu[i]+j],k);
+ do {
+ q++;
+ m=ed[k][l];
+ ed[k][l]=-1-m;
+ l=cycle_up(ed[k][nu[k]+l],m);
+ k=m;
+ } while (k!=i);
+ v.push_back(q);;
+ }
+ }
+ reset_edges();
+}
+
+/** Computes the number of edges that each face has and outputs a frequency
+ * table of the results.
+ * \param[out] v the vector to store the results in. */
+void voronoicell_base::face_freq_table(std::vector<int> &v) {
+ int i,j,k,l,m,q;
+ v.clear();
+ for(i=1;i<p;i++) for(j=0;j<nu[i];j++) {
+ k=ed[i][j];
+ if(k>=0) {
+ q=1;
+ ed[i][j]=-1-k;
+ l=cycle_up(ed[i][nu[i]+j],k);
+ do {
+ q++;
+ m=ed[k][l];
+ ed[k][l]=-1-m;
+ l=cycle_up(ed[k][nu[k]+l],m);
+ k=m;
+ } while (k!=i);
+ if((unsigned int) q>=v.size()) v.resize(q+1,0);
+ v[q]++;
+ }
+ }
+ reset_edges();
+}
+
+/** This routine tests to see whether the cell intersects a plane by starting
+ * from the guess point up. If up intersects, then it immediately returns true.
+ * Otherwise, it calls the plane_intersects_track() routine.
+ * \param[in] (x,y,z) the normal vector to the plane.
+ * \param[in] rsq the distance along this vector of the plane.
+ * \return False if the plane does not intersect the plane, true if it does. */
+bool voronoicell_base::plane_intersects(double x,double y,double z,double rsq) {
+ double g=x*pts[3*up]+y*pts[3*up+1]+z*pts[3*up+2];
+ if(g<rsq) return plane_intersects_track(x,y,z,rsq,g);
+ return true;
+}
+
+/** This routine tests to see if a cell intersects a plane. It first tests a
+ * random sample of approximately sqrt(p)/4 points. If any of those are
+ * intersect, then it immediately returns true. Otherwise, it takes the closest
+ * point and passes that to plane_intersect_track() routine.
+ * \param[in] (x,y,z) the normal vector to the plane.
+ * \param[in] rsq the distance along this vector of the plane.
+ * \return False if the plane does not intersect the plane, true if it does. */
+bool voronoicell_base::plane_intersects_guess(double x,double y,double z,double rsq) {
+ up=0;
+ double g=x*pts[3*up]+y*pts[3*up+1]+z*pts[3*up+2];
+ if(g<rsq) {
+ int ca=1,cc=p>>3,mp=1;
+ double m;
+ while(ca<cc) {
+ m=x*pts[3*mp]+y*pts[3*mp+1]+z*pts[3*mp+2];
+ if(m>g) {
+ if(m>rsq) return true;
+ g=m;up=mp;
+ }
+ ca+=mp++;
+ }
+ return plane_intersects_track(x,y,z,rsq,g);
+ }
+ return true;
+}
+
+/* This routine tests to see if a cell intersects a plane, by tracing over the cell from
+ * vertex to vertex, starting at up. It is meant to be called either by plane_intersects()
+ * or plane_intersects_track(), when those routines cannot immediately resolve the case.
+ * \param[in] (x,y,z) the normal vector to the plane.
+ * \param[in] rsq the distance along this vector of the plane.
+ * \param[in] g the distance of up from the plane.
+ * \return False if the plane does not intersect the plane, true if it does. */
+inline bool voronoicell_base::plane_intersects_track(double x,double y,double z,double rsq,double g) {
+ int count=0,ls,us,tp;
+ double t;
+
+ // The test point is outside of the cutting space
+ for(us=0;us<nu[up];us++) {
+ tp=ed[up][us];
+ t=x*pts[3*tp]+y*pts[3*tp+1]+z*pts[3*tp+2];
+ if(t>g) {
+ ls=ed[up][nu[up]+us];
+ up=tp;
+ while (t<rsq) {
+ if(++count>=p) {
+#if VOROPP_VERBOSE >=1
+ fputs("Bailed out of convex calculation",stderr);
+#endif
+ for(tp=0;tp<p;tp++) if(x*pts[3*tp]+y*pts[3*tp+1]+z*pts[3*tp+2]>rsq) return true;
+ return false;
+ }
+
+ // Test all the neighbors of the current point
+ // and find the one which is closest to the
+ // plane
+ for(us=0;us<ls;us++) {
+ tp=ed[up][us];
+ g=x*pts[3*tp]+y*pts[3*tp+1]+z*pts[3*tp+2];
+ if(g>t) break;
+ }
+ if(us==ls) {
+ us++;
+ while(us<nu[up]) {
+ tp=ed[up][us];
+ g=x*pts[3*tp]+y*pts[3*tp+1]+z*pts[3*tp+2];
+ if(g>t) break;
+ us++;
+ }
+ if(us==nu[up]) return false;
+ }
+ ls=ed[up][nu[up]+us];up=tp;t=g;
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+/** Counts the number of edges of the Voronoi cell.
+ * \return the number of edges. */
+int voronoicell_base::number_of_edges() {
+ int edges=0,*nup=nu;
+ while(nup<nu+p) edges+=*(nup++);
+ return edges>>1;
+}
+
+/** Outputs a custom string of information about the Voronoi cell. The string
+ * of information follows a similar style as the C printf command, and detailed
+ * information about its format is available at
+ * http://math.lbl.gov/voro++/doc/custom.html.
+ * \param[in] format the custom string to print.
+ * \param[in] i the ID of the particle associated with this Voronoi cell.
+ * \param[in] (x,y,z) the position of the particle associated with this Voronoi
+ * cell.
+ * \param[in] r a radius associated with the particle.
+ * \param[in] fp the file handle to write to. */
+void voronoicell_base::output_custom(const char *format,int i,double x,double y,double z,double r,FILE *fp) {
+ char *fmp=(const_cast<char*>(format));
+ std::vector<int> vi;
+ std::vector<double> vd;
+ while(*fmp!=0) {
+ if(*fmp=='%') {
+ fmp++;
+ switch(*fmp) {
+
+ // Particle-related output
+ case 'i': fprintf(fp,"%d",i);break;
+ case 'x': fprintf(fp,"%g",x);break;
+ case 'y': fprintf(fp,"%g",y);break;
+ case 'z': fprintf(fp,"%g",z);break;
+ case 'q': fprintf(fp,"%g %g %g",x,y,z);break;
+ case 'r': fprintf(fp,"%g",r);break;
+
+ // Vertex-related output
+ case 'w': fprintf(fp,"%d",p);break;
+ case 'p': output_vertices(fp);break;
+ case 'P': output_vertices(x,y,z,fp);break;
+ case 'o': output_vertex_orders(fp);break;
+ case 'm': fprintf(fp,"%g",0.25*max_radius_squared());break;
+
+ // Edge-related output
+ case 'g': fprintf(fp,"%d",number_of_edges());break;
+ case 'E': fprintf(fp,"%g",total_edge_distance());break;
+ case 'e': face_perimeters(vd);voro_print_vector(vd,fp);break;
+
+ // Face-related output
+ case 's': fprintf(fp,"%d",number_of_faces());break;
+ case 'F': fprintf(fp,"%g",surface_area());break;
+ case 'A': {
+ face_freq_table(vi);
+ voro_print_vector(vi,fp);
+ } break;
+ case 'a': face_orders(vi);voro_print_vector(vi,fp);break;
+ case 'f': face_areas(vd);voro_print_vector(vd,fp);break;
+ case 't': {
+ face_vertices(vi);
+ voro_print_face_vertices(vi,fp);
+ } break;
+ case 'l': normals(vd);
+ voro_print_positions(vd,fp);
+ break;
+ case 'n': neighbors(vi);
+ voro_print_vector(vi,fp);
+ break;
+
+ // Volume-related output
+ case 'v': fprintf(fp,"%g",volume());break;
+ case 'c': {
+ double cx,cy,cz;
+ centroid(cx,cy,cz);
+ fprintf(fp,"%g %g %g",cx,cy,cz);
+ } break;
+ case 'C': {
+ double cx,cy,cz;
+ centroid(cx,cy,cz);
+ fprintf(fp,"%g %g %g",x+cx,y+cy,z+cz);
+ } break;
+
+ // End-of-string reached
+ case 0: fmp--;break;
+
+ // The percent sign is not part of a
+ // control sequence
+ default: putc('%',fp);putc(*fmp,fp);
+ }
+ } else putc(*fmp,fp);
+ fmp++;
+ }
+ fputs("\n",fp);
+}
+
+/** This initializes the class to be a rectangular box. It calls the base class
+ * initialization routine to set up the edge and vertex information, and then
+ * sets up the neighbor information, with initial faces being assigned ID
+ * numbers from -1 to -6.
+ * \param[in] (xmin,xmax) the minimum and maximum x coordinates.
+ * \param[in] (ymin,ymax) the minimum and maximum y coordinates.
+ * \param[in] (zmin,zmax) the minimum and maximum z coordinates. */
+void voronoicell_neighbor::init(double xmin,double xmax,double ymin,double ymax,double zmin,double zmax) {
+ init_base(xmin,xmax,ymin,ymax,zmin,zmax);
+ int *q=mne[3];
+ *q=-5;q[1]=-3;q[2]=-1;
+ q[3]=-5;q[4]=-2;q[5]=-3;
+ q[6]=-5;q[7]=-1;q[8]=-4;
+ q[9]=-5;q[10]=-4;q[11]=-2;
+ q[12]=-6;q[13]=-1;q[14]=-3;
+ q[15]=-6;q[16]=-3;q[17]=-2;
+ q[18]=-6;q[19]=-4;q[20]=-1;
+ q[21]=-6;q[22]=-2;q[23]=-4;
+ *ne=q;ne[1]=q+3;ne[2]=q+6;ne[3]=q+9;
+ ne[4]=q+12;ne[5]=q+15;ne[6]=q+18;ne[7]=q+21;
+}
+
+/** This initializes the class to be an octahedron. It calls the base class
+ * initialization routine to set up the edge and vertex information, and then
+ * sets up the neighbor information, with the initial faces being assigned ID
+ * numbers from -1 to -8.
+ * \param[in] l The distance from the octahedron center to a vertex. Six
+ * vertices are initialized at (-l,0,0), (l,0,0), (0,-l,0),
+ * (0,l,0), (0,0,-l), and (0,0,l). */
+void voronoicell_neighbor::init_octahedron(double l) {
+ init_octahedron_base(l);
+ int *q=mne[4];
+ *q=-5;q[1]=-6;q[2]=-7;q[3]=-8;
+ q[4]=-1;q[5]=-2;q[6]=-3;q[7]=-4;
+ q[8]=-6;q[9]=-5;q[10]=-2;q[11]=-1;
+ q[12]=-8;q[13]=-7;q[14]=-4;q[15]=-3;
+ q[16]=-5;q[17]=-8;q[18]=-3;q[19]=-2;
+ q[20]=-7;q[21]=-6;q[22]=-1;q[23]=-4;
+ *ne=q;ne[1]=q+4;ne[2]=q+8;ne[3]=q+12;ne[4]=q+16;ne[5]=q+20;
+}
+
+/** This initializes the class to be a tetrahedron. It calls the base class
+ * initialization routine to set up the edge and vertex information, and then
+ * sets up the neighbor information, with the initial faces being assigned ID
+ * numbers from -1 to -4.
+ * \param (x0,y0,z0) a position vector for the first vertex.
+ * \param (x1,y1,z1) a position vector for the second vertex.
+ * \param (x2,y2,z2) a position vector for the third vertex.
+ * \param (x3,y3,z3) a position vector for the fourth vertex. */
+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) {
+ init_tetrahedron_base(x0,y0,z0,x1,y1,z1,x2,y2,z2,x3,y3,z3);
+ int *q=mne[3];
+ *q=-4;q[1]=-3;q[2]=-2;
+ q[3]=-3;q[4]=-4;q[5]=-1;
+ q[6]=-4;q[7]=-2;q[8]=-1;
+ q[9]=-2;q[10]=-3;q[11]=-1;
+ *ne=q;ne[1]=q+3;ne[2]=q+6;ne[3]=q+9;
+}
+
+/** This routine checks to make sure the neighbor information of each face is
+ * consistent. */
+void voronoicell_neighbor::check_facets() {
+ int i,j,k,l,m,q;
+ for(i=1;i<p;i++) for(j=0;j<nu[i];j++) {
+ k=ed[i][j];
+ if(k>=0) {
+ ed[i][j]=-1-k;
+ q=ne[i][j];
+ l=cycle_up(ed[i][nu[i]+j],k);
+ do {
+ m=ed[k][l];
+ ed[k][l]=-1-m;
+ 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);
+ l=cycle_up(ed[k][nu[k]+l],m);
+ k=m;
+ } while (k!=i);
+ }
+ }
+ reset_edges();
+}
+
+/** The class constructor allocates memory for storing neighbor information. */
+voronoicell_neighbor::voronoicell_neighbor() {
+ int i;
+ mne=new int*[current_vertex_order];
+ ne=new int*[current_vertices];
+ for(i=0;i<3;i++) mne[i]=new int[init_n_vertices*i];
+ mne[3]=new int[init_3_vertices*3];
+ for(i=4;i<current_vertex_order;i++) mne[i]=new int[init_n_vertices*i];
+}
+
+/** The class destructor frees the dynamically allocated memory for storing
+ * neighbor information. */
+voronoicell_neighbor::~voronoicell_neighbor() {
+ for(int i=current_vertex_order-1;i>=0;i--) if(mem[i]>0) delete [] mne[i];
+ delete [] mne;
+ delete [] ne;
+}
+
+/** Computes a vector list of neighbors. */
+void voronoicell_neighbor::neighbors(std::vector<int> &v) {
+ v.clear();
+ int i,j,k,l,m;
+ for(i=1;i<p;i++) for(j=0;j<nu[i];j++) {
+ k=ed[i][j];
+ if(k>=0) {
+ v.push_back(ne[i][j]);
+ ed[i][j]=-1-k;
+ l=cycle_up(ed[i][nu[i]+j],k);
+ do {
+ m=ed[k][l];
+ ed[k][l]=-1-m;
+ l=cycle_up(ed[k][nu[k]+l],m);
+ k=m;
+ } while (k!=i);
+ }
+ }
+ reset_edges();
+}
+
+/** Prints the vertices, their edges, the relation table, and also notifies if
+ * any memory errors are visible. */
+void voronoicell_base::print_edges() {
+ int j;
+ double *ptsp=pts;
+ for(int i=0;i<p;i++,ptsp+=3) {
+ printf("%d %d ",i,nu[i]);
+ for(j=0;j<nu[i];j++) printf(" %d",ed[i][j]);
+ printf(" ");
+ while(j<(nu[i]<<1)) printf(" %d",ed[i][j]);
+ printf(" %d",ed[i][j]);
+ print_edges_neighbors(i);
+ printf(" %g %g %g %p",*ptsp,ptsp[1],ptsp[2],(void*) ed[i]);
+ if(ed[i]>=mep[nu[i]]+mec[nu[i]]*((nu[i]<<1)+1)) puts(" Memory error");
+ else puts("");
+ }
+}
+
+/** This prints out the neighbor information for vertex i. */
+void voronoicell_neighbor::print_edges_neighbors(int i) {
+ if(nu[i]>0) {
+ int j=0;
+ printf(" (");
+ while(j<nu[i]-1) printf("%d,",ne[i][j++]);
+ printf("%d)",ne[i][j]);
+ } else printf(" ()");
+}
+
+// Explicit instantiation
+template bool voronoicell_base::nplane(voronoicell&,double,double,double,double,int);
+template bool voronoicell_base::nplane(voronoicell_neighbor&,double,double,double,double,int);
+template void voronoicell_base::check_memory_for_copy(voronoicell&,voronoicell_base*);
+template void voronoicell_base::check_memory_for_copy(voronoicell_neighbor&,voronoicell_base*);
+
+}
Index: extern/voro++/src/container.cc
===================================================================
--- extern/voro++/src/container.cc (revision 0)
+++ extern/voro++/src/container.cc (revision 0)
@@ -0,0 +1,549 @@
+// Voro++, a 3D cell-based Voronoi library
+//
+// Author : Chris H. Rycroft (LBL / UC Berkeley)
+// Email : chr@alum.mit.edu
+// Date : August 30th 2011
+
+/** \file container.cc
+ * \brief Function implementations for the container and related classes. */
+
+#include "container.hh"
+
+namespace voro {
+
+/** The class constructor sets up the geometry of container, initializing the
+ * minimum and maximum coordinates in each direction, and setting whether each
+ * direction is periodic or not. It divides the container into a rectangular
+ * grid of blocks, and allocates memory for each of these for storing particle
+ * positions and IDs.
+ * \param[in] (ax_,bx_) the minimum and maximum x coordinates.
+ * \param[in] (ay_,by_) the minimum and maximum y coordinates.
+ * \param[in] (az_,bz_) the minimum and maximum z coordinates.
+ * \param[in] (nx_,ny_,nz_) the number of grid blocks in each of the three
+ * coordinate directions.
+ * \param[in] (xperiodic_,yperiodic_,zperiodic_) flags setting whether the
+ * container is periodic in each
+ * coordinate direction.
+ * \param[in] init_mem the initial memory allocation for each block.
+ * \param[in] ps_ the number of floating point entries to store for each
+ * particle. */
+container_base::container_base(double ax_,double bx_,double ay_,double by_,double az_,double bz_,
+ int nx_,int ny_,int nz_,bool xperiodic_,bool yperiodic_,bool zperiodic_,int init_mem,int ps_)
+ : voro_base(nx_,ny_,nz_,(bx_-ax_)/nx_,(by_-ay_)/ny_,(bz_-az_)/nz_),
+ ax(ax_), bx(bx_), ay(ay_), by(by_), az(az_), bz(bz_),
+ xperiodic(xperiodic_), yperiodic(yperiodic_), zperiodic(zperiodic_),
+ id(new int*[nxyz]), p(new double*[nxyz]), co(new int[nxyz]), mem(new int[nxyz]), ps(ps_) {
+ int l;
+ for(l=0;l<nxyz;l++) co[l]=0;
+ for(l=0;l<nxyz;l++) mem[l]=init_mem;
+ for(l=0;l<nxyz;l++) id[l]=new int[init_mem];
+ for(l=0;l<nxyz;l++) p[l]=new double[ps*init_mem];
+}
+
+/** The container destructor frees the dynamically allocated memory. */
+container_base::~container_base() {
+ int l;
+ for(l=0;l<nxyz;l++) delete [] p[l];
+ for(l=0;l<nxyz;l++) delete [] id[l];
+ delete [] id;
+ delete [] p;
+ delete [] co;
+ delete [] mem;
+}
+
+/** The class constructor sets up the geometry of container.
+ * \param[in] (ax_,bx_) the minimum and maximum x coordinates.
+ * \param[in] (ay_,by_) the minimum and maximum y coordinates.
+ * \param[in] (az_,bz_) the minimum and maximum z coordinates.
+ * \param[in] (nx_,ny_,nz_) the number of grid blocks in each of the three
+ * coordinate directions.
+ * \param[in] (xperiodic_,yperiodic_,zperiodic_) flags setting whether the
+ * container is periodic in each
+ * coordinate direction.
+ * \param[in] init_mem the initial memory allocation for each block. */
+container::container(double ax_,double bx_,double ay_,double by_,double az_,double bz_,
+ int nx_,int ny_,int nz_,bool xperiodic_,bool yperiodic_,bool zperiodic_,int init_mem)
+ : container_base(ax_,bx_,ay_,by_,az_,bz_,nx_,ny_,nz_,xperiodic_,yperiodic_,zperiodic_,init_mem,3),
+ vc(*this,xperiodic_?2*nx_+1:nx_,yperiodic_?2*ny_+1:ny_,zperiodic_?2*nz_+1:nz_) {}
+
+/** The class constructor sets up the geometry of container.
+ * \param[in] (ax_,bx_) the minimum and maximum x coordinates.
+ * \param[in] (ay_,by_) the minimum and maximum y coordinates.
+ * \param[in] (az_,bz_) the minimum and maximum z coordinates.
+ * \param[in] (nx_,ny_,nz_) the number of grid blocks in each of the three
+ * coordinate directions.
+ * \param[in] (xperiodic_,yperiodic_,zperiodic_) flags setting whether the
+ * container is periodic in each
+ * coordinate direction.
+ * \param[in] init_mem the initial memory allocation for each block. */
+container_poly::container_poly(double ax_,double bx_,double ay_,double by_,double az_,double bz_,
+ int nx_,int ny_,int nz_,bool xperiodic_,bool yperiodic_,bool zperiodic_,int init_mem)
+ : container_base(ax_,bx_,ay_,by_,az_,bz_,nx_,ny_,nz_,xperiodic_,yperiodic_,zperiodic_,init_mem,4),
+ vc(*this,xperiodic_?2*nx_+1:nx_,yperiodic_?2*ny_+1:ny_,zperiodic_?2*nz_+1:nz_) {ppr=p;}
+
+/** Put a particle into the correct region of the container.
+ * \param[in] n the numerical ID of the inserted particle.
+ * \param[in] (x,y,z) the position vector of the inserted particle. */
+void container::put(int n,double x,double y,double z) {
+ int ijk;
+ if(put_locate_block(ijk,x,y,z)) {
+ id[ijk][co[ijk]]=n;
+ double *pp=p[ijk]+3*co[ijk]++;
+ *(pp++)=x;*(pp++)=y;*pp=z;
+ }
+}
+
+/** Put a particle into the correct region of the container.
+ * \param[in] n the numerical ID of the inserted particle.
+ * \param[in] (x,y,z) the position vector of the inserted particle.
+ * \param[in] r the radius of the particle. */
+void container_poly::put(int n,double x,double y,double z,double r) {
+ int ijk;
+ if(put_locate_block(ijk,x,y,z)) {
+ id[ijk][co[ijk]]=n;
+ double *pp=p[ijk]+4*co[ijk]++;
+ *(pp++)=x;*(pp++)=y;*(pp++)=z;*pp=r;
+ if(max_radius<r) max_radius=r;
+ }
+}
+
+/** Put a particle into the correct region of the container, also recording
+ * into which region it was stored.
+ * \param[in] vo the ordering class in which to record the region.
+ * \param[in] n the numerical ID of the inserted particle.
+ * \param[in] (x,y,z) the position vector of the inserted particle. */
+void container::put(particle_order &vo,int n,double x,double y,double z) {
+ int ijk;
+ if(put_locate_block(ijk,x,y,z)) {
+ id[ijk][co[ijk]]=n;
+ vo.add(ijk,co[ijk]);
+ double *pp=p[ijk]+3*co[ijk]++;
+ *(pp++)=x;*(pp++)=y;*pp=z;
+ }
+}
+
+/** Put a particle into the correct region of the container, also recording
+ * into which region it was stored.
+ * \param[in] vo the ordering class in which to record the region.
+ * \param[in] n the numerical ID of the inserted particle.
+ * \param[in] (x,y,z) the position vector of the inserted particle.
+ * \param[in] r the radius of the particle. */
+void container_poly::put(particle_order &vo,int n,double x,double y,double z,double r) {
+ int ijk;
+ if(put_locate_block(ijk,x,y,z)) {
+ id[ijk][co[ijk]]=n;
+ vo.add(ijk,co[ijk]);
+ double *pp=p[ijk]+4*co[ijk]++;
+ *(pp++)=x;*(pp++)=y;*(pp++)=z;*pp=r;
+ if(max_radius<r) max_radius=r;
+ }
+}
+
+/** This routine takes a particle position vector, tries to remap it into the
+ * primary domain. If successful, it computes the region into which it can be
+ * stored and checks that there is enough memory within this region to store
+ * it.
+ * \param[out] ijk the region index.
+ * \param[in,out] (x,y,z) the particle position, remapped into the primary
+ * domain if necessary.
+ * \return True if the particle can be successfully placed into the container,
+ * false otherwise. */
+inline bool container_base::put_locate_block(int &ijk,double &x,double &y,double &z) {
+ if(put_remap(ijk,x,y,z)) {
+ if(co[ijk]==mem[ijk]) add_particle_memory(ijk);
+ return true;
+ }
+#if VOROPP_REPORT_OUT_OF_BOUNDS ==1
+ fprintf(stderr,"Out of bounds: (x,y,z)=(%g,%g,%g)\n",x,y,z);
+#endif
+ return false;
+}
+
+/** Takes a particle position vector and computes the region index into which
+ * it should be stored. If the container is periodic, then the routine also
+ * maps the particle position to ensure it is in the primary domain. If the
+ * container is not periodic, the routine bails out.
+ * \param[out] ijk the region index.
+ * \param[in,out] (x,y,z) the particle position, remapped into the primary
+ * domain if necessary.
+ * \return True if the particle can be successfully placed into the container,
+ * false otherwise. */
+inline bool container_base::put_remap(int &ijk,double &x,double &y,double &z) {
+ int l;
+
+ ijk=step_int((x-ax)*xsp);
+ if(xperiodic) {l=step_mod(ijk,nx);x+=boxx*(l-ijk);ijk=l;}
+ else if(ijk<0||ijk>=nx) return false;
+
+ int j=step_int((y-ay)*ysp);
+ if(yperiodic) {l=step_mod(j,ny);y+=boxy*(l-j);j=l;}
+ else if(j<0||j>=ny) return false;
+
+ int k=step_int((z-az)*zsp);
+ if(zperiodic) {l=step_mod(k,nz);z+=boxz*(l-k);k=l;}
+ else if(k<0||k>=nz) return false;
+
+ ijk+=nx*j+nxy*k;
+ return true;
+}
+
+/** Takes a position vector and attempts to remap it into the primary domain.
+ * \param[out] (ai,aj,ak) the periodic image displacement that the vector is in,
+ * with (0,0,0) corresponding to the primary domain.
+ * \param[out] (ci,cj,ck) the index of the block that the position vector is
+ * within, once it has been remapped.
+ * \param[in,out] (x,y,z) the position vector to consider, which is remapped
+ * into the primary domain during the routine.
+ * \param[out] ijk the block index that the vector is within.
+ * \return True if the particle is within the container or can be remapped into
+ * it, false if it lies outside of the container bounds. */
+inline bool container_base::remap(int &ai,int &aj,int &ak,int &ci,int &cj,int &ck,double &x,double &y,double &z,int &ijk) {
+ ci=step_int((x-ax)*xsp);
+ if(ci<0||ci>=nx) {
+ if(xperiodic) {ai=step_div(ci,nx);x-=ai*(bx-ax);ci-=ai*nx;}
+ else return false;
+ } else ai=0;
+
+ cj=step_int((y-ay)*ysp);
+ if(cj<0||cj>=ny) {
+ if(yperiodic) {aj=step_div(cj,ny);y-=aj*(by-ay);cj-=aj*ny;}
+ else return false;
+ } else aj=0;
+
+ ck=step_int((z-az)*zsp);
+ if(ck<0||ck>=nz) {
+ if(zperiodic) {ak=step_div(ck,nz);z-=ak*(bz-az);ck-=ak*nz;}
+ else return false;
+ } else ak=0;
+
+ ijk=ci+nx*cj+nxy*ck;
+ return true;
+}
+
+/** Takes a vector and finds the particle whose Voronoi cell contains that
+ * vector. This is equivalent to finding the particle which is nearest to the
+ * vector. Additional wall classes are not considered by this routine.
+ * \param[in] (x,y,z) the vector to test.
+ * \param[out] (rx,ry,rz) the position of the particle whose Voronoi cell
+ * contains the vector. If the container is periodic,
+ * this may point to a particle in a periodic image of
+ * the primary domain.
+ * \param[out] pid the ID of the particle.
+ * \return True if a particle was found. If the container has no particles,
+ * then the search will not find a Voronoi cell and false is returned. */
+bool container::find_voronoi_cell(double x,double y,double z,double &rx,double &ry,double &rz,int &pid) {
+ int ai,aj,ak,ci,cj,ck,ijk;
+ particle_record w;
+ double mrs;
+
+ // If the given vector lies outside the domain, but the container
+ // is periodic, then remap it back into the domain
+ if(!remap(ai,aj,ak,ci,cj,ck,x,y,z,ijk)) return false;
+ vc.find_voronoi_cell(x,y,z,ci,cj,ck,ijk,w,mrs);
+
+ if(w.ijk!=-1) {
+
+ // Assemble the position vector of the particle to be returned,
+ // applying a periodic remapping if necessary
+ if(xperiodic) {ci+=w.di;if(ci<0||ci>=nx) ai+=step_div(ci,nx);}
+ if(yperiodic) {cj+=w.dj;if(cj<0||cj>=ny) aj+=step_div(cj,ny);}
+ if(zperiodic) {ck+=w.dk;if(ck<0||ck>=nz) ak+=step_div(ck,nz);}
+ rx=p[w.ijk][3*w.l]+ai*(bx-ax);
+ ry=p[w.ijk][3*w.l+1]+aj*(by-ay);
+ rz=p[w.ijk][3*w.l+2]+ak*(bz-az);
+ pid=id[w.ijk][w.l];
+ return true;
+ }
+
+ // If no particle if found then just return false
+ return false;
+}
+
+/** Takes a vector and finds the particle whose Voronoi cell contains that
+ * vector. Additional wall classes are not considered by this routine.
+ * \param[in] (x,y,z) the vector to test.
+ * \param[out] (rx,ry,rz) the position of the particle whose Voronoi cell
+ * contains the vector. If the container is periodic,
+ * this may point to a particle in a periodic image of
+ * the primary domain.
+ * \param[out] pid the ID of the particle.
+ * \return True if a particle was found. If the container has no particles,
+ * then the search will not find a Voronoi cell and false is returned. */
+bool container_poly::find_voronoi_cell(double x,double y,double z,double &rx,double &ry,double &rz,int &pid) {
+ int ai,aj,ak,ci,cj,ck,ijk;
+ particle_record w;
+ double mrs;
+
+ // If the given vector lies outside the domain, but the container
+ // is periodic, then remap it back into the domain
+ if(!remap(ai,aj,ak,ci,cj,ck,x,y,z,ijk)) return false;
+ vc.find_voronoi_cell(x,y,z,ci,cj,ck,ijk,w,mrs);
+
+ if(w.ijk!=-1) {
+
+ // Assemble the position vector of the particle to be returned,
+ // applying a periodic remapping if necessary
+ if(xperiodic) {ci+=w.di;if(ci<0||ci>=nx) ai+=step_div(ci,nx);}
+ if(yperiodic) {cj+=w.dj;if(cj<0||cj>=ny) aj+=step_div(cj,ny);}
+ if(zperiodic) {ck+=w.dk;if(ck<0||ck>=nz) ak+=step_div(ck,nz);}
+ rx=p[w.ijk][4*w.l]+ai*(bx-ax);
+ ry=p[w.ijk][4*w.l+1]+aj*(by-ay);
+ rz=p[w.ijk][4*w.l+2]+ak*(bz-az);
+ pid=id[w.ijk][w.l];
+ return true;
+ }
+
+ // If no particle if found then just return false
+ return false;
+}
+
+/** Increase memory for a particular region.
+ * \param[in] i the index of the region to reallocate. */
+void container_base::add_particle_memory(int i) {
+ int l,nmem=mem[i]<<1;
+
+ // Carry out a check on the memory allocation size, and
+ // print a status message if requested
+ if(nmem>max_particle_memory)
+ voro_fatal_error("Absolute maximum memory allocation exceeded",VOROPP_MEMORY_ERROR);
+#if VOROPP_VERBOSE >=3
+ fprintf(stderr,"Particle memory in region %d scaled up to %d\n",i,nmem);
+#endif
+
+ // Allocate new memory and copy in the contents of the old arrays
+ int *idp=new int[nmem];
+ for(l=0;l<co[i];l++) idp[l]=id[i][l];
+ double *pp=new double[ps*nmem];
+ for(l=0;l<ps*co[i];l++) pp[l]=p[i][l];
+
+ // Update pointers and delete old arrays
+ mem[i]=nmem;
+ delete [] id[i];id[i]=idp;
+ delete [] p[i];p[i]=pp;
+}
+
+/** Import a list of particles from an open file stream into the container.
+ * Entries of four numbers (Particle ID, x position, y position, z position)
+ * are searched for. If the file cannot be successfully read, then the routine
+ * causes a fatal error.
+ * \param[in] fp the file handle to read from. */
+void container::import(FILE *fp) {
+ int i,j;
+ double x,y,z;
+ while((j=fscanf(fp,"%d %lg %lg %lg",&i,&x,&y,&z))==4) put(i,x,y,z);
+ if(j!=EOF) voro_fatal_error("File import error",VOROPP_FILE_ERROR);
+}
+
+/** Import a list of particles from an open file stream, also storing the order
+ * of that the particles are read. Entries of four numbers (Particle ID, x
+ * position, y position, z position) are searched for. If the file cannot be
+ * successfully read, then the routine causes a fatal error.
+ * \param[in,out] vo a reference to an ordering class to use.
+ * \param[in] fp the file handle to read from. */
+void container::import(particle_order &vo,FILE *fp) {
+ int i,j;
+ double x,y,z;
+ while((j=fscanf(fp,"%d %lg %lg %lg",&i,&x,&y,&z))==4) put(vo,i,x,y,z);
+ if(j!=EOF) voro_fatal_error("File import error",VOROPP_FILE_ERROR);
+}
+
+/** Import a list of particles from an open file stream into the container.
+ * Entries of five numbers (Particle ID, x position, y position, z position,
+ * radius) are searched for. If the file cannot be successfully read, then the
+ * routine causes a fatal error.
+ * \param[in] fp the file handle to read from. */
+void container_poly::import(FILE *fp) {
+ int i,j;
+ double x,y,z,r;
+ while((j=fscanf(fp,"%d %lg %lg %lg %lg",&i,&x,&y,&z,&r))==5) put(i,x,y,z,r);
+ if(j!=EOF) voro_fatal_error("File import error",VOROPP_FILE_ERROR);
+}
+
+/** Import a list of particles from an open file stream, also storing the order
+ * of that the particles are read. Entries of four numbers (Particle ID, x
+ * position, y position, z position, radius) are searched for. If the file
+ * cannot be successfully read, then the routine causes a fatal error.
+ * \param[in,out] vo a reference to an ordering class to use.
+ * \param[in] fp the file handle to read from. */
+void container_poly::import(particle_order &vo,FILE *fp) {
+ int i,j;
+ double x,y,z,r;
+ while((j=fscanf(fp,"%d %lg %lg %lg %lg",&i,&x,&y,&z,&r))==5) put(vo,i,x,y,z,r);
+ if(j!=EOF) voro_fatal_error("File import error",VOROPP_FILE_ERROR);
+}
+
+/** Outputs the a list of all the container regions along with the number of
+ * particles stored within each. */
+void container_base::region_count() {
+ int i,j,k,*cop=co;
+ for(k=0;k<nz;k++) for(j=0;j<ny;j++) for(i=0;i<nx;i++)
+ printf("Region (%d,%d,%d): %d particles\n",i,j,k,*(cop++));
+}
+
+/** Clears a container of particles. */
+void container::clear() {
+ for(int *cop=co;cop<co+nxyz;cop++) *cop=0;
+}
+
+/** Clears a container of particles, also clearing resetting the maximum radius
+ * to zero. */
+void container_poly::clear() {
+ for(int *cop=co;cop<co+nxyz;cop++) *cop=0;
+ max_radius=0;
+}
+
+/** Computes all the Voronoi cells and saves customized information about them.
+ * \param[in] format the custom output string to use.
+ * \param[in] fp a file handle to write to. */
+void container::print_custom(const char *format,FILE *fp) {
+ c_loop_all vl(*this);
+ print_custom(vl,format,fp);
+}
+
+/** Computes all the Voronoi cells and saves customized
+ * information about them.
+ * \param[in] format the custom output string to use.
+ * \param[in] fp a file handle to write to. */
+void container_poly::print_custom(const char *format,FILE *fp) {
+ c_loop_all vl(*this);
+ print_custom(vl,format,fp);
+}
+
+/** Computes all the Voronoi cells and saves customized information about them.
+ * \param[in] format the custom output string to use.
+ * \param[in] filename the name of the file to write to. */
+void container::print_custom(const char *format,const char *filename) {
+ FILE *fp=safe_fopen(filename,"w");
+ print_custom(format,fp);
+ fclose(fp);
+}
+
+/** Computes all the Voronoi cells and saves customized
+ * information about them
+ * \param[in] format the custom output string to use.
+ * \param[in] filename the name of the file to write to. */
+void container_poly::print_custom(const char *format,const char *filename) {
+ FILE *fp=safe_fopen(filename,"w");
+ print_custom(format,fp);
+ fclose(fp);
+}
+
+/** Computes all of the Voronoi cells in the container, but does nothing
+ * with the output. It is useful for measuring the pure computation time
+ * of the Voronoi algorithm, without any additional calculations such as
+ * volume evaluation or cell output. */
+void container::compute_all_cells() {
+ voronoicell c;
+ c_loop_all vl(*this);
+ if(vl.start()) do compute_cell(c,vl);
+ while(vl.inc());
+}
+
+/** Computes all of the Voronoi cells in the container, but does nothing
+ * with the output. It is useful for measuring the pure computation time
+ * of the Voronoi algorithm, without any additional calculations such as
+ * volume evaluation or cell output. */
+void container_poly::compute_all_cells() {
+ voronoicell c;
+ c_loop_all vl(*this);
+ if(vl.start()) do compute_cell(c,vl);while(vl.inc());
+}
+
+/** Calculates all of the Voronoi cells and sums their volumes. In most cases
+ * without walls, the sum of the Voronoi cell volumes should equal the volume
+ * of the container to numerical precision.
+ * \return The sum of all of the computed Voronoi volumes. */
+double container::sum_cell_volumes() {
+ voronoicell c;
+ double vol=0;
+ c_loop_all vl(*this);
+ if(vl.start()) do if(compute_cell(c,vl)) vol+=c.volume();while(vl.inc());
+ return vol;
+}
+
+/** Calculates all of the Voronoi cells and sums their volumes. In most cases
+ * without walls, the sum of the Voronoi cell volumes should equal the volume
+ * of the container to numerical precision.
+ * \return The sum of all of the computed Voronoi volumes. */
+double container_poly::sum_cell_volumes() {
+ voronoicell c;
+ double vol=0;
+ c_loop_all vl(*this);
+ if(vl.start()) do if(compute_cell(c,vl)) vol+=c.volume();while(vl.inc());
+ return vol;
+}
+
+/** This function tests to see if a given vector lies within the container
+ * bounds and any walls.
+ * \param[in] (x,y,z) the position vector to be tested.
+ * \return True if the point is inside the container, false if the point is
+ * outside. */
+bool container_base::point_inside(double x,double y,double z) {
+ if(x<ax||x>bx||y<ay||y>by||z<az||z>bz) return false;
+ return point_inside_walls(x,y,z);
+}
+
+/** Draws an outline of the domain in gnuplot format.
+ * \param[in] fp the file handle to write to. */
+void container_base::draw_domain_gnuplot(FILE *fp) {
+ fprintf(fp,"%g %g %g\n%g %g %g\n%g %g %g\n%g %g %g\n",ax,ay,az,bx,ay,az,bx,by,az,ax,by,az);
+ fprintf(fp,"%g %g %g\n%g %g %g\n%g %g %g\n%g %g %g\n",ax,by,bz,bx,by,bz,bx,ay,bz,ax,ay,bz);
+ fprintf(fp,"%g %g %g\n\n%g %g %g\n%g %g %g\n\n",ax,by,bz,ax,ay,az,ax,ay,bz);
+ fprintf(fp,"%g %g %g\n%g %g %g\n\n%g %g %g\n%g %g %g\n\n",bx,ay,az,bx,ay,bz,bx,by,az,bx,by,bz);
+}
+
+/** Draws an outline of the domain in POV-Ray format.
+ * \param[in] fp the file handle to write to. */
+void container_base::draw_domain_pov(FILE *fp) {
+ fprintf(fp,"cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n"
+ "cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n",ax,ay,az,bx,ay,az,ax,by,az,bx,by,az);
+ fprintf(fp,"cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n"
+ "cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n",ax,by,bz,bx,by,bz,ax,ay,bz,bx,ay,bz);
+ fprintf(fp,"cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n"
+ "cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n",ax,ay,az,ax,by,az,bx,ay,az,bx,by,az);
+ fprintf(fp,"cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n"
+ "cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n",bx,ay,bz,bx,by,bz,ax,ay,bz,ax,by,bz);
+ fprintf(fp,"cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n"
+ "cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n",ax,ay,az,ax,ay,bz,bx,ay,az,bx,ay,bz);
+ fprintf(fp,"cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n"
+ "cylinder{<%g,%g,%g>,<%g,%g,%g>,rr}\n",bx,by,az,bx,by,bz,ax,by,az,ax,by,bz);
+ fprintf(fp,"sphere{<%g,%g,%g>,rr}\nsphere{<%g,%g,%g>,rr}\n"
+ "sphere{<%g,%g,%g>,rr}\nsphere{<%g,%g,%g>,rr}\n",ax,ay,az,bx,ay,az,ax,by,az,bx,by,az);
+ fprintf(fp,"sphere{<%g,%g,%g>,rr}\nsphere{<%g,%g,%g>,rr}\n"
+ "sphere{<%g,%g,%g>,rr}\nsphere{<%g,%g,%g>,rr}\n",ax,ay,bz,bx,ay,bz,ax,by,bz,bx,by,bz);
+}
+
+
+/** The wall_list constructor sets up an array of pointers to wall classes. */
+wall_list::wall_list() : walls(new wall*[init_wall_size]), wep(walls), wel(walls+init_wall_size),
+ current_wall_size(init_wall_size) {}
+
+/** The wall_list destructor frees the array of pointers to the wall classes.
+ */
+wall_list::~wall_list() {
+ delete [] walls;
+}
+
+/** Adds all of the walls on another wall_list to this class.
+ * \param[in] wl a reference to the wall class. */
+void wall_list::add_wall(wall_list &wl) {
+ for(wall **wp=wl.walls;wp<wl.wep;wp++) add_wall(*wp);
+}
+
+/** Deallocates all of the wall classes pointed to by the wall_list. */
+void wall_list::deallocate() {
+ for(wall **wp=walls;wp<wep;wp++) delete *wp;
+}
+
+/** Increases the memory allocation for the walls array. */
+void wall_list::increase_wall_memory() {
+ current_wall_size<<=1;
+ if(current_wall_size>max_wall_size)
+ voro_fatal_error("Wall memory allocation exceeded absolute maximum",VOROPP_MEMORY_ERROR);
+ wall **nwalls=new wall*[current_wall_size],**nwp=nwalls,**wp=walls;
+ while(wp<wep) *(nwp++)=*(wp++);
+ delete [] walls;
+ walls=nwalls;wel=walls+current_wall_size;wep=nwp;
+}
+
+}
Index: extern/voro++/src/worklist.hh
===================================================================
--- extern/voro++/src/worklist.hh (revision 0)
+++ extern/voro++/src/worklist.hh (revision 0)
@@ -0,0 +1,32 @@
+// Voro++, a 3D cell-based Voronoi library
+//
+// Author : Chris H. Rycroft (LBL / UC Berkeley)
+// Email : chr@alum.mit.edu
+// Date : August 30th 2011
+
+/** \file worklist.hh
+ * \brief Header file for setting constants used in the block worklists that are
+ * used during cell computation.
+ *
+ * This file is automatically generated by worklist_gen.pl and it is not
+ * intended to be edited by hand. */
+
+#ifndef VOROPP_WORKLIST_HH
+#define VOROPP_WORKLIST_HH
+
+namespace voro {
+
+/** Each region is divided into a grid of subregions, and a worklist is
+# constructed for each. This parameter sets is set to half the number of
+# subregions that the block is divided into. */
+const int wl_hgrid=4;
+/** The number of subregions that a block is subdivided into, which is twice
+the value of hgrid. */
+const int wl_fgrid=8;
+/** The total number of worklists, set to the cube of hgrid. */
+const int wl_hgridcu=64;
+/** The number of elements in each worklist. */
+const int wl_seq_length=64;
+
+}
+#endif
Index: extern/voro++/src/common.cc
===================================================================
--- extern/voro++/src/common.cc (revision 0)
+++ extern/voro++/src/common.cc (revision 0)
@@ -0,0 +1,90 @@
+// Voro++, a 3D cell-based Voronoi library
+//
+// Author : Chris H. Rycroft (LBL / UC Berkeley)
+// Email : chr@alum.mit.edu
+// Date : August 30th 2011
+
+/** \file common.cc
+ * \brief Implementations of the small helper functions. */
+
+#include "common.hh"
+
+namespace voro {
+
+/** \brief Prints a vector of integers.
+ *
+ * Prints a vector of integers.
+ * \param[in] v the vector to print.
+ * \param[in] fp the file stream to print to. */
+void voro_print_vector(std::vector<int> &v,FILE *fp) {
+ int k=0,s=v.size();
+ while(k+4<s) {
+ fprintf(fp,"%d %d %d %d ",v[k],v[k+1],v[k+2],v[k+3]);
+ k+=4;
+ }
+ if(k+3<=s) {
+ if(k+4==s) fprintf(fp,"%d %d %d %d",v[k],v[k+1],v[k+2],v[k+3]);
+ else fprintf(fp,"%d %d %d",v[k],v[k+1],v[k+2]);
+ } else {
+ if(k+2==s) fprintf(fp,"%d %d",v[k],v[k+1]);
+ else fprintf(fp,"%d",v[k]);
+ }
+}
+
+/** \brief Prints a vector of doubles.
+ *
+ * Prints a vector of doubles.
+ * \param[in] v the vector to print.
+ * \param[in] fp the file stream to print to. */
+void voro_print_vector(std::vector<double> &v,FILE *fp) {
+ int k=0,s=v.size();
+ while(k+4<s) {
+ fprintf(fp,"%g %g %g %g ",v[k],v[k+1],v[k+2],v[k+3]);
+ k+=4;
+ }
+ if(k+3<=s) {
+ if(k+4==s) fprintf(fp,"%g %g %g %g",v[k],v[k+1],v[k+2],v[k+3]);
+ else fprintf(fp,"%g %g %g",v[k],v[k+1],v[k+2]);
+ } else {
+ if(k+2==s) fprintf(fp,"%g %g",v[k],v[k+1]);
+ else fprintf(fp,"%g",v[k]);
+ }
+}
+
+/** \brief Prints a vector a face vertex information.
+ *
+ * Prints a vector of face vertex information. A value is read, which
+ * corresponds to the number of vertices in the next face. The routine reads
+ * this number of values and prints them as a bracked list. This is repeated
+ * until the end of the vector is reached.
+ * \param[in] v the vector to interpret and print.
+ * \param[in] fp the file stream to print to. */
+void voro_print_face_vertices(std::vector<int> &v,FILE *fp) {
+ int j,k=0,l;
+ if(v.size()>0) {
+ l=v[k++];
+ if(l<=1) {
+ if(l==1) fprintf(fp,"(%d)",v[k++]);
+ else fputs("()",fp);
+ } else {
+ j=k+l;
+ fprintf(fp,"(%d",v[k++]);
+ while(k<j) fprintf(fp,",%d",v[k++]);
+ fputs(")",fp);
+ }
+ while((unsigned int) k<v.size()) {
+ l=v[k++];
+ if(l<=1) {
+ if(l==1) fprintf(fp," (%d)",v[k++]);
+ else fputs(" ()",fp);
+ } else {
+ j=k+l;
+ fprintf(fp," (%d",v[k++]);
+ while(k<j) fprintf(fp,",%d",v[k++]);
+ fputs(")",fp);
+ }
+ }
+ }
+}
+
+}
Index: extern/voro++/src/pre_container.cc
===================================================================
--- extern/voro++/src/pre_container.cc (revision 0)
+++ extern/voro++/src/pre_container.cc (revision 0)
@@ -0,0 +1,236 @@
+// Voro++, a 3D cell-based Voronoi library
+//
+// Author : Chris H. Rycroft (LBL / UC Berkeley)
+// Email : chr@alum.mit.edu
+// Date : August 30th 2011
+
+/** \file pre_container.cc
+ * \brief Function implementations for the pre_container and related classes.
+ */
+
+#include <cmath>
+
+#include "config.hh"
+#include "pre_container.hh"
+
+namespace voro {
+
+/** The class constructor sets up the geometry of container, initializing the
+ * minimum and maximum coordinates in each direction. It allocates an initial
+ * chunk into which to store particle information.
+ * \param[in] (ax_,bx_) the minimum and maximum x coordinates.
+ * \param[in] (ay_,by_) the minimum and maximum y coordinates.
+ * \param[in] (az_,bz_) the minimum and maximum z coordinates.
+ * \param[in] (xperiodic_,yperiodic_,zperiodic_ ) flags setting whether the
+ * container is periodic in each
+ * coordinate direction.
+ * \param[in] ps_ the number of floating point entries to store for each
+ * particle. */
+pre_container_base::pre_container_base(double ax_,double bx_,double ay_,double by_,double az_,double bz_,
+ bool xperiodic_,bool yperiodic_,bool zperiodic_,int ps_) :
+ ax(ax_), bx(bx_), ay(ay_), by(by_), az(az_), bz(bz_),
+ xperiodic(xperiodic_), yperiodic(yperiodic_), zperiodic(zperiodic_), ps(ps_),
+ index_sz(init_chunk_size), pre_id(new int*[index_sz]), end_id(pre_id),
+ pre_p(new double*[index_sz]), end_p(pre_p) {
+ ch_id=*end_id=new int[pre_container_chunk_size];
+ l_id=end_id+index_sz;e_id=ch_id+pre_container_chunk_size;
+ ch_p=*end_p=new double[ps*pre_container_chunk_size];
+}
+
+/** The destructor frees the dynamically allocated memory. */
+pre_container_base::~pre_container_base() {
+ delete [] *end_p;
+ delete [] *end_id;
+ while (end_id!=pre_id) {
+ end_p--;
+ delete [] *end_p;
+ end_id--;
+ delete [] *end_id;
+ }
+ delete [] pre_p;
+ delete [] pre_id;
+}
+
+/** Makes a guess at the optimal grid of blocks to use, computing in
+ * a way that
+ * \param[out] (nx,ny,nz) the number of blocks to use. */
+void pre_container_base::guess_optimal(int &nx,int &ny,int &nz) {
+ double dx=bx-ax,dy=by-ay,dz=bz-az;
+ double ilscale=pow(total_particles()/(optimal_particles*dx*dy*dz),1/3.0);
+ nx=int(dx*ilscale+1);
+ ny=int(dy*ilscale+1);
+ nz=int(dz*ilscale+1);
+}
+
+/** Stores a particle ID and position, allocating a new memory chunk if
+ * necessary. For coordinate directions in which the container is not periodic,
+ * the routine checks to make sure that the particle is within the container
+ * bounds. If the particle is out of bounds, it is not stored.
+ * \param[in] n the numerical ID of the inserted particle.
+ * \param[in] (x,y,z) the position vector of the inserted particle. */
+void pre_container::put(int n,double x,double y,double z) {
+ if((xperiodic||(x>=ax&&x<=bx))&&(yperiodic||(y>=ay&&y<=by))&&(zperiodic||(z>=az&&z<=bz))) {
+ if(ch_id==e_id) new_chunk();
+ *(ch_id++)=n;
+ *(ch_p++)=x;*(ch_p++)=y;*(ch_p++)=z;
+ }
+#if VOROPP_REPORT_OUT_OF_BOUNDS ==1
+ else fprintf(stderr,"Out of bounds: (x,y,z)=(%g,%g,%g)\n",x,y,z);
+#endif
+}
+
+/** Stores a particle ID and position, allocating a new memory chunk if necessary.
+ * \param[in] n the numerical ID of the inserted particle.
+ * \param[in] (x,y,z) the position vector of the inserted particle.
+ * \param[in] r the radius of the particle. */
+void pre_container_poly::put(int n,double x,double y,double z,double r) {
+ if((xperiodic||(x>=ax&&x<=bx))&&(yperiodic||(y>=ay&&y<=by))&&(zperiodic||(z>=az&&z<=bz))) {
+ if(ch_id==e_id) new_chunk();
+ *(ch_id++)=n;
+ *(ch_p++)=x;*(ch_p++)=y;*(ch_p++)=z;*(ch_p++)=r;
+ }
+#if VOROPP_REPORT_OUT_OF_BOUNDS ==1
+ else fprintf(stderr,"Out of bounds: (x,y,z)=(%g,%g,%g)\n",x,y,z);
+#endif
+}
+
+/** Transfers the particles stored within the class to a container class.
+ * \param[in] con the container class to transfer to. */
+void pre_container::setup(container &con) {
+ int **c_id=pre_id,*idp,*ide,n;
+ double **c_p=pre_p,*pp,x,y,z;
+ while(c_id<end_id) {
+ idp=*(c_id++);ide=idp+pre_container_chunk_size;
+ pp=*(c_p++);
+ while(idp<ide) {
+ n=*(idp++);x=*(pp++);y=*(pp++);z=*(pp++);
+ con.put(n,x,y,z);
+ }
+ }
+ idp=*c_id;
+ pp=*c_p;
+ while(idp<ch_id) {
+ n=*(idp++);x=*(pp++);y=*(pp++);z=*(pp++);
+ con.put(n,x,y,z);
+ }
+}
+
+/** Transfers the particles stored within the class to a container_poly class.
+ * \param[in] con the container_poly class to transfer to. */
+void pre_container_poly::setup(container_poly &con) {
+ int **c_id=pre_id,*idp,*ide,n;
+ double **c_p=pre_p,*pp,x,y,z,r;
+ while(c_id<end_id) {
+ idp=*(c_id++);ide=idp+pre_container_chunk_size;
+ pp=*(c_p++);
+ while(idp<ide) {
+ n=*(idp++);x=*(pp++);y=*(pp++);z=*(pp++);r=*(pp++);
+ con.put(n,x,y,z,r);
+ }
+ }
+ idp=*c_id;
+ pp=*c_p;
+ while(idp<ch_id) {
+ n=*(idp++);x=*(pp++);y=*(pp++);z=*(pp++);r=*(pp++);
+ con.put(n,x,y,z,r);
+ }
+}
+
+/** Transfers the particles stored within the class to a container class, also
+ * recording the order in which particles were stored.
+ * \param[in] vo the ordering class to use.
+ * \param[in] con the container class to transfer to. */
+void pre_container::setup(particle_order &vo,container &con) {
+ int **c_id=pre_id,*idp,*ide,n;
+ double **c_p=pre_p,*pp,x,y,z;
+ while(c_id<end_id) {
+ idp=*(c_id++);ide=idp+pre_container_chunk_size;
+ pp=*(c_p++);
+ while(idp<ide) {
+ n=*(idp++);x=*(pp++);y=*(pp++);z=*(pp++);
+ con.put(vo,n,x,y,z);
+ }
+ }
+ idp=*c_id;
+ pp=*c_p;
+ while(idp<ch_id) {
+ n=*(idp++);x=*(pp++);y=*(pp++);z=*(pp++);
+ con.put(vo,n,x,y,z);
+ }
+}
+
+/** Transfers the particles stored within the class to a container_poly class,
+ * also recording the order in which particles were stored.
+ * \param[in] vo the ordering class to use.
+ * \param[in] con the container_poly class to transfer to. */
+void pre_container_poly::setup(particle_order &vo,container_poly &con) {
+ int **c_id=pre_id,*idp,*ide,n;
+ double **c_p=pre_p,*pp,x,y,z,r;
+ while(c_id<end_id) {
+ idp=*(c_id++);ide=idp+pre_container_chunk_size;
+ pp=*(c_p++);
+ while(idp<ide) {
+ n=*(idp++);x=*(pp++);y=*(pp++);z=*(pp++);r=*(pp++);
+ con.put(vo,n,x,y,z,r);
+ }
+ }
+ idp=*c_id;
+ pp=*c_p;
+ while(idp<ch_id) {
+ n=*(idp++);x=*(pp++);y=*(pp++);z=*(pp++);r=*(pp++);
+ con.put(vo,n,x,y,z,r);
+ }
+}
+
+/** Import a list of particles from an open file stream into the container.
+ * Entries of four numbers (Particle ID, x position, y position, z position)
+ * are searched for. If the file cannot be successfully read, then the routine
+ * causes a fatal error.
+ * \param[in] fp the file handle to read from. */
+void pre_container::import(FILE *fp) {
+ int i,j;
+ double x,y,z;
+ while((j=fscanf(fp,"%d %lg %lg %lg",&i,&x,&y,&z))==4) put(i,x,y,z);
+ if(j!=EOF) voro_fatal_error("File import error",VOROPP_FILE_ERROR);
+}
+
+/** Import a list of particles from an open file stream, also storing the order
+ * of that the particles are read. Entries of four numbers (Particle ID, x
+ * position, y position, z position) are searched for. If the file cannot be
+ * successfully read, then the routine causes a fatal error.
+ * \param[in] fp the file handle to read from. */
+void pre_container_poly::import(FILE *fp) {
+ int i,j;
+ double x,y,z,r;
+ while((j=fscanf(fp,"%d %lg %lg %lg %lg",&i,&x,&y,&z,&r))==5) put(i,x,y,z,r);
+ if(j!=EOF) voro_fatal_error("File import error",VOROPP_FILE_ERROR);
+}
+
+/** Allocates a new chunk of memory for storing particles. */
+void pre_container_base::new_chunk() {
+ end_id++;end_p++;
+ if(end_id==l_id) extend_chunk_index();
+ ch_id=*end_id=new int[pre_container_chunk_size];
+ e_id=ch_id+pre_container_chunk_size;
+ ch_p=*end_p=new double[ps*pre_container_chunk_size];
+}
+
+/** Extends the index of chunks. */
+void pre_container_base::extend_chunk_index() {
+ index_sz<<=1;
+ if(index_sz>max_chunk_size)
+ voro_fatal_error("Absolute memory limit on chunk index reached",VOROPP_MEMORY_ERROR);
+#if VOROPP_VERBOSE >=2
+ fprintf(stderr,"Pre-container chunk index scaled up to %d\n",index_sz);
+#endif
+ int **n_id=new int*[index_sz],**p_id=n_id,**c_id=pre_id;
+ double **n_p=new double*[index_sz],**p_p=n_p,**c_p=pre_p;
+ while(c_id<end_id) {
+ *(p_id++)=*(c_id++);
+ *(p_p++)=*(c_p++);
+ }
+ delete [] pre_id;pre_id=n_id;end_id=p_id;l_id=pre_id+index_sz;
+ delete [] pre_p;pre_p=n_p;end_p=p_p;
+}
+
+}
Index: extern/voro++/src/Makefile
===================================================================
--- extern/voro++/src/Makefile (revision 0)
+++ extern/voro++/src/Makefile (revision 0)
@@ -0,0 +1,39 @@
+# Voro++ makefile
+#
+# Author : Chris H. Rycroft (LBL / UC Berkeley)
+# Email : chr@alum.mit.edu
+# Date : August 30th 2011
+
+# Load the common configuration file
+include ../config.mk
+
+# List of the common source files
+objs=cell.o common.o container.o unitcell.o v_compute.o c_loops.o \
+ v_base.o wall.o pre_container.o container_prd.o
+src=$(patsubst %.o,%.cc,$(objs))
+
+# Makefile rules
+all: libvoro++.a voro++
+
+depend:
+ $(CXX) -MM $(src) >Makefile.dep
+
+include Makefile.dep
+
+libvoro++.a: $(objs)
+ rm -f libvoro++.a
+ ar rs libvoro++.a $^
+
+voro++: libvoro++.a cmd_line.cc
+ $(CXX) $(CFLAGS) -L. -o voro++ cmd_line.cc -lvoro++
+
+%.o: %.cc
+ $(CXX) $(CFLAGS) -c $<
+
+help: Doxyfile $(SOURCE)
+ doxygen Doxyfile
+
+clean:
+ rm -f $(objs) voro++ libvoro++.a
+
+.PHONY: all help execs depend
Index: extern/voro++/src/worklist_gen.pl
===================================================================
--- extern/voro++/src/worklist_gen.pl (revision 0)
+++ extern/voro++/src/worklist_gen.pl (revision 0)
@@ -0,0 +1,236 @@
+#!/usr/bin/perl
+# Voro++, a 3D cell-based Voronoi library
+#
+# Author : Chris H. Rycroft (LBL / UC Berkeley)
+# Email : chr@alum.mit.edu
+# Date : August 30th 2011
+#
+# worklist_gen.pl - this perl script is used to automatically generate the
+# worklists of blocks that are stored in worklist.cc, that are used by the
+# compute_cell() routine to ensure maximum efficiency
+
+# Each region is divided into a grid of subregions, and a worklist is
+# constructed for each. This parameter sets is set to half the number of
+# subregions that the block is divided into.
+$hr=4;
+
+# This parameter is automatically set to the the number of subregions that the
+# block is divided into
+$r=$hr*2;
+
+# This parameter sets the total number of block entries in each worklist
+$ls=63;
+
+# When the worklists are being constructed, a mask array is made use of. To
+# prevent the creation of array elements with negative indices, this parameter
+# sets an arbitrary displacement.
+$dis=8;
+
+# Constants used mask indexing
+$d=2*$dis+1;$dd=$d*$d;$d0=(1+$d+$dd)*$dis;
+
+use Math::Trig;
+
+# Construct the worklist header file, based on the parameters above
+open W,">worklist.hh";
+print W <<EOF;
+// Voro++, a 3D cell-based Voronoi library
+//
+// Author : Chris H. Rycroft (LBL / UC Berkeley)
+// Email : chr\@alum.mit.edu
+// Date : August 30th 2011
+
+/** \\file worklist.hh
+ * \\brief Header file for setting constants used in the block worklists that are
+ * used during cell computation.
+ *
+ * This file is automatically generated by worklist_gen.pl and it is not
+ * intended to be edited by hand. */
+
+#ifndef VOROPP_WORKLIST_HH
+#define VOROPP_WORKLIST_HH
+
+namespace voro {
+
+/** Each region is divided into a grid of subregions, and a worklist is
+# constructed for each. This parameter sets is set to half the number of
+# subregions that the block is divided into. */
+EOF
+print W "const int wl_hgrid=$hr;\n";
+print W <<EOF;
+/** The number of subregions that a block is subdivided into, which is twice
+the value of hgrid. */
+EOF
+print W "const int wl_fgrid=$r;\n";
+print W <<EOF;
+/** The total number of worklists, set to the cube of hgrid. */
+EOF
+printf W "const int wl_hgridcu=%d;\n",$hr*$hr*$hr;
+print W <<EOF;
+/** The number of elements in each worklist. */
+EOF
+printf W "const int wl_seq_length=%d;\n",$ls+1;
+print W <<EOF;
+
+}
+#endif
+EOF
+close W;
+
+# Construct the preamble to the worklist.cc file
+open W,">v_base_wl.cc";
+print W <<EOF;
+// Voro++, a 3D cell-based Voronoi library
+//
+// Author : Chris H. Rycroft (LBL / UC Berkeley)
+// Email : chr\@alum.mit.edu
+// Date : August 30th 2011
+
+/** \\file v_base_wl.cc
+ * \\brief The table of block worklists that are used during the cell
+ * computation, which is part of the voro_base class.
+ *
+ * This file is automatically generated by worklist_gen.pl and it is not
+ * intended to be edited by hand. */
+
+EOF
+printf W "const unsigned int voro_base::wl[wl_seq_length*wl_hgridcu]={\n";
+
+# Now create a worklist for each subregion
+for($kk=0;$kk<$hr;$kk++) {
+ for($jj=0;$jj<$hr;$jj++) {
+ for($ii=0;$ii<$hr;$ii++) {
+ worklist($ii,$jj,$kk);
+ }
+ }
+}
+
+# Finish the file and close it
+print W "};\n";
+close W;
+
+# A subroutine
+sub worklist {
+# print "@_[0] @_[1] @_[2]\n";
+ $ind=@_[0]+$hr*(@_[1]+$hr*@_[2]);
+ $ac=0;$v+=2;
+ $xp=$yp=$zp=0;
+ $x=(@_[0]+0.5)/$r;
+ $y=(@_[1]+0.5)/$r;
+ $z=(@_[2]+0.5)/$r;
+ $m[$d0]=$v;
+ add(1,0,0);add(0,1,0);add(0,0,1);
+ add(-1,0,0);add(0,-1,0);add(0,0,-1);
+ foreach $l (1..$ls) {
+ $minwei=1e9;
+ foreach (0..$ac-1) {
+ $xt=@a[3*$_];$yt=@a[3*$_+1];$zt=@a[3*$_+2];
+# $wei=dis($x,$y,$z,$xt,$yt,$zt)+1*acos(($xt*$xp+$yt*$yp+$zt*$zp)/($xt*$xt+$yt*$yt+$zt*$zt)*($xp*$xp+$yp*$yp+$zp*$zp));
+ $wei=adis($x,$y,$z,$xt,$yt,$zt)+0.02*sqrt(($xt-$xp)**2+($yt-$yp)**2+($zt-$zp)**2);
+ $nx=$_,$minwei=$wei if $wei<$minwei;
+ }
+ $xp=@a[3*$nx];$yp=@a[3*$nx+1];$zp=@a[3*$nx+2];
+ add($xp+1,$yp,$zp);add($xp,$yp+1,$zp);add($xp,$yp,$zp+1);
+ add($xp-1,$yp,$zp);add($xp,$yp-1,$zp);add($xp,$yp,$zp-1);
+# print "=> $l $xp $yp $zp\n" if $l<4;
+ push @b,(splice @a,3*$nx,3);$ac--;
+ }
+
+ # Mark all blocks that are on this worklist entry
+ $m[$d0]=++$v;
+ for($i=0;$i<$#b;$i+=3) {
+ $xt=$b[$i];$yt=$b[$i+1];$zt=$b[$i+2];
+ $m[$d0+$xt+$d*$yt+$dd*$zt]=$v;
+ }
+
+ # Find which neighboring outside blocks need to be marked when
+ # considering this block, being as conservative as possible by
+ # overwriting the marks, so that the last possible entry that can reach
+ # a block is used
+ for($i=$j=0;$i<$#b;$i+=3,$j++) {
+ $xt=$b[$i];$yt=$b[$i+1];$zt=$b[$i+2];
+ $k=$d0+$xt+$yt*$d+$zt*$dd;
+ $la[$k+1]=$j, $m[$k+1]=$v+1 if $xt>=0 && $m[$k+1]!=$v;
+ $la[$k+$d]=$j, $m[$k+$d]=$v+1 if $yt>=0 && $m[$k+$d]!=$v;
+ $la[$k+$dd]=$j, $m[$k+$dd]=$v+1 if $zt>=0 && $m[$k+$dd]!=$v;
+ $la[$k-1]=$j, $m[$k-1]=$v+1 if $xt<=0 && $m[$k-1]!=$v;
+ $la[$k-$d]=$j, $m[$k-$d]=$v+1 if $yt<=0 && $m[$k-$d]!=$v;
+ $la[$k-$dd]=$j, $m[$k-$dd]=$v+1 if $zt<=0 && $m[$k-$dd]!=$v;
+ }
+
+ # Scan to ensure that no neighboring blocks have been missed by the
+ # outwards-looking logic in the above section
+ for($i=0;$i<$#b;$i+=3) {
+ wl_check($d0+$b[$i]+$b[$i+1]*$d+$b[$i+2]*$dd);
+ }
+
+ # Compute the number of entries where outside blocks do not need to be
+ # consider
+ for($i=$j=0;$i<$#b;$i+=3,$j++) {
+ $k=$d0+$b[$i]+$b[$i+1]*$d+$b[$i+2]*$dd;
+ last if $m[$k+1]!=$v;
+ last if $m[$k+$d]!=$v;
+ last if $m[$k+$dd]!=$v;
+ last if $m[$k-1]!=$v;
+ last if $m[$k-$d]!=$v;
+ last if $m[$k-$dd]!=$v;
+ }
+ print W "\t$j";
+
+ # Create worklist entry and save to file
+ $j=0;
+ while ($#b>0) {
+ $xt=shift @b;$yt=shift @b;$zt=shift @b;
+ $k=$d0+$xt+$yt*$d+$zt*$dd;
+ $o=0;
+ $o|=1 if $m[$k+1]!=$v && $la[$k+1]==$j;
+ $o^=3 if $m[$k-1]!=$v && $la[$k-1]==$j;
+ $o|=8 if $m[$k+$d]!=$v && $la[$k+$d]==$j;
+ $o^=24 if $m[$k-$d]!=$v && $la[$k-$d]==$j;
+ $o|=64 if $m[$k+$dd]!=$v && $la[$k+$dd]==$j;
+ $o^=192 if $m[$k-$dd]!=$v && $la[$k-$dd]==$j;
+ printf W ",%#x",(($xt+64)|($yt+64)<<7|($zt+64)<<14|$o<<21);
+ $j++;
+ }
+ print W "," unless $ind==$hr*$hr*$hr-1;
+ print W "\n";
+
+ # Remove list memory
+ undef @a;
+ undef @b;
+}
+
+sub add {
+ if ($m[$d0+@_[0]+$d*@_[1]+$dd*@_[2]]!=$v) {
+ $ac++;
+ push @a,@_[0],@_[1],@_[2];
+ $m[$d0+@_[0]+$d*@_[1]+$dd*@_[2]]=$v;
+ }
+}
+
+sub dis {
+ $xl=@_[3]+0.3-@_[0];$xh=@_[3]+0.7-@_[0];
+ $yl=@_[4]+0.3-@_[1];$yh=@_[4]+0.7-@_[1];
+ $zl=@_[5]+0.3-@_[2];$zh=@_[5]+0.7-@_[2];
+ $dis=(abs($xl)<abs($xh)?$xl:$xh)**2
+ +(abs($yl)<abs($yh)?$yl:$yh)**2
+ +(abs($zl)<abs($zh)?$zl:$zh)**2;
+ return sqrt $dis;
+}
+
+sub adis {
+ $xco=$yco=$zco=0;
+ $xco=@_[0]-@_[3] if @_[3]>0;
+ $xco=@_[0]-@_[3]-1 if @_[3]<0;
+ $yco=@_[1]-@_[4] if @_[4]>0;
+ $yco=@_[1]-@_[4]-1 if @_[4]<0;
+ $zco=@_[2]-@_[5] if @_[5]>0;
+ $zco=@_[2]-@_[5]-1 if @_[5]<0;
+ return sqrt $xco*$xco+$yco*$yco+$zco*$zco;
+}
+
+sub wl_check {
+ die "Failure in worklist construction\n" if $m[@_[0]+1]<$v||$m[@_[0]-1]<$v
+ ||$m[@_[0]+$d]<$v||$m[@_[0]-$d]<$v
+ ||$m[@_[0]+$dd]<$v||$m[@_[0]-$dd]<$v;
+}
Index: extern/voro++/src/c_loops.cc
===================================================================
--- extern/voro++/src/c_loops.cc (revision 0)
+++ extern/voro++/src/c_loops.cc (revision 0)
@@ -0,0 +1,150 @@
+// Voro++, a 3D cell-based Voronoi library
+//
+// Author : Chris H. Rycroft (LBL / UC Berkeley)
+// Email : chr@alum.mit.edu
+// Date : August 30th 2011
+
+/** \file c_loops.cc
+ * \brief Function implementations for the loop classes. */
+
+#include "c_loops.hh"
+
+namespace voro {
+
+/** Initializes a c_loop_subset object to scan over all particles within a
+ * given sphere.
+ * \param[in] (vx,vy,vz) the position vector of the center of the sphere.
+ * \param[in] r the radius of the sphere.
+ * \param[in] bounds_test whether to do detailed bounds checking. If this is
+ * false then the class will loop over all particles in
+ * blocks that overlap the given sphere. If it is true,
+ * the particle will only loop over the particles which
+ * actually lie within the sphere.
+ * \return True if there is any valid point to loop over, false otherwise. */
+void c_loop_subset::setup_sphere(double vx,double vy,double vz,double r,bool bounds_test) {
+ if(bounds_test) {mode=sphere;v0=vx;v1=vy;v2=vz;v3=r*r;} else mode=no_check;
+ ai=step_int((vx-ax-r)*xsp);
+ bi=step_int((vx-ax+r)*xsp);
+ aj=step_int((vy-ay-r)*ysp);
+ bj=step_int((vy-ay+r)*ysp);
+ ak=step_int((vz-az-r)*zsp);
+ bk=step_int((vz-az+r)*zsp);
+ setup_common();
+}
+
+/** Initializes the class to loop over all particles in a rectangular subgrid
+ * of blocks.
+ * \param[in] (ai_,bi_) the subgrid range in the x-direction, inclusive of both
+ * ends.
+ * \param[in] (aj_,bj_) the subgrid range in the y-direction, inclusive of both
+ * ends.
+ * \param[in] (ak_,bk_) the subgrid range in the z-direction, inclusive of both
+ * ends.
+ * \return True if there is any valid point to loop over, false otherwise. */
+void c_loop_subset::setup_intbox(int ai_,int bi_,int aj_,int bj_,int ak_,int bk_) {
+ ai=ai_;bi=bi_;aj=aj_;bj=bj_;ak=ak_;bk=bk_;
+ mode=no_check;
+ setup_common();
+}
+
+/** Sets up all of the common constants used for the loop.
+ * \return True if there is any valid point to loop over, false otherwise. */
+void c_loop_subset::setup_common() {
+ if(!xperiodic) {
+ if(ai<0) {ai=0;if(bi<0) bi=0;}
+ if(bi>=nx) {bi=nx-1;if(ai>=nx) ai=nx-1;}
+ }
+ if(!yperiodic) {
+ if(aj<0) {aj=0;if(bj<0) bj=0;}
+ if(bj>=ny) {bj=ny-1;if(aj>=ny) aj=ny-1;}
+ }
+ if(!zperiodic) {
+ if(ak<0) {ak=0;if(bk<0) bk=0;}
+ if(bk>=nz) {bk=nz-1;if(ak>=nz) ak=nz-1;}
+ }
+ ci=ai;cj=aj;ck=ak;
+ di=i=step_mod(ci,nx);apx=px=step_div(ci,nx)*sx;
+ dj=j=step_mod(cj,ny);apy=py=step_div(cj,ny)*sy;
+ dk=k=step_mod(ck,nz);apz=pz=step_div(ck,nz)*sz;
+ inc1=di-step_mod(bi,nx);
+ inc2=nx*(ny+dj-step_mod(bj,ny))+inc1;
+ inc1+=nx;
+ ijk=di+nx*(dj+ny*dk);
+ q=0;
+}
+
+/** Starts the loop by finding the first particle within the container to
+ * consider.
+ * \return True if there is any particle to consider, false otherwise. */
+bool c_loop_subset::start() {
+ while(co[ijk]==0) {if(!next_block()) return false;}
+ while(mode!=no_check&&out_of_bounds()) {
+ q++;
+ while(q>=co[ijk]) {q=0;if(!next_block()) return false;}
+ }
+ return true;
+}
+
+/** Initializes the class to loop over all particles in a rectangular box.
+ * \param[in] (xmin,xmax) the minimum and maximum x coordinates of the box.
+ * \param[in] (ymin,ymax) the minimum and maximum y coordinates of the box.
+ * \param[in] (zmin,zmax) the minimum and maximum z coordinates of the box.
+ * \param[in] bounds_test whether to do detailed bounds checking. If this is
+ * false then the class will loop over all particles in
+ * blocks that overlap the given box. If it is true, the
+ * particle will only loop over the particles which
+ * actually lie within the box.
+ * \return True if there is any valid point to loop over, false otherwise. */
+void c_loop_subset::setup_box(double xmin,double xmax,double ymin,double ymax,double zmin,double zmax,bool bounds_test) {
+ if(bounds_test) {mode=box;v0=xmin;v1=xmax;v2=ymin;v3=ymax;v4=zmin;v5=zmax;} else mode=no_check;
+ ai=step_int((xmin-ax)*xsp);
+ bi=step_int((xmax-ax)*xsp);
+ aj=step_int((ymin-ay)*ysp);
+ bj=step_int((ymax-ay)*ysp);
+ ak=step_int((zmin-az)*zsp);
+ bk=step_int((zmax-az)*zsp);
+ setup_common();
+}
+
+/** Computes whether the current point is out of bounds, relative to the
+ * current loop setup.
+ * \return True if the point is out of bounds, false otherwise. */
+bool c_loop_subset::out_of_bounds() {
+ double *pp=p[ijk]+ps*q;
+ if(mode==sphere) {
+ double fx(*pp+px-v0),fy(pp[1]+py-v1),fz(pp[2]+pz-v2);
+ return fx*fx+fy*fy+fz*fz>v3;
+ } else {
+ double f(*pp+px);if(f<v0||f>v1) return true;
+ f=pp[1]+py;if(f<v2||f>v3) return true;
+ f=pp[2]+pz;return f<v4||f>v5;
+ }
+}
+
+/** Returns the next block to be tested in a loop, and updates the periodicity
+ * vector if necessary. */
+bool c_loop_subset::next_block() {
+ if(i<bi) {
+ i++;
+ if(ci<nx-1) {ci++;ijk++;} else {ci=0;ijk+=1-nx;px+=sx;}
+ return true;
+ } else if(j<bj) {
+ i=ai;ci=di;px=apx;j++;
+ if(cj<ny-1) {cj++;ijk+=inc1;} else {cj=0;ijk+=inc1-nxy;py+=sy;}
+ return true;
+ } else if(k<bk) {
+ i=ai;ci=di;j=aj;cj=dj;px=apx;py=apy;k++;
+ if(ck<nz-1) {ck++;ijk+=inc2;} else {ck=0;ijk+=inc2-nxyz;pz+=sz;}
+ return true;
+ } else return false;
+}
+
+/** Extends the memory available for storing the ordering. */
+void particle_order::add_ordering_memory() {
+ int *no=new int[size<<2],*nop=no,*opp=o;
+ while(opp<op) *(nop++)=*(opp++);
+ delete [] o;
+ size<<=1;o=no;op=nop;
+}
+
+}
Index: extern/voro++/src/v_base.cc
===================================================================
--- extern/voro++/src/v_base.cc (revision 0)
+++ extern/voro++/src/v_base.cc (revision 0)
@@ -0,0 +1,118 @@
+// Voro++, a 3D cell-based Voronoi library
+//
+// Author : Chris H. Rycroft (LBL / UC Berkeley)
+// Email : chr@alum.mit.edu
+// Date : August 30th 2011
+
+/** \file v_base.cc
+ * \brief Function implementations for the base Voronoi container class. */
+
+#include "v_base.hh"
+#include "config.hh"
+
+namespace voro {
+
+/** This function is called during container construction. The routine scans
+ * all of the worklists in the wl[] array. For a given worklist of blocks
+ * labeled \f$w_1\f$ to \f$w_n\f$, it computes a sequence \f$r_0\f$ to
+ * \f$r_n\f$ so that $r_i$ is the minimum distance to all the blocks
+ * \f$w_{j}\f$ where \f$j>i\f$ and all blocks outside the worklist. The values
+ * of \f$r_n\f$ is calculated first, as the minimum distance to any block in
+ * the shell surrounding the worklist. The \f$r_i\f$ are then computed in
+ * reverse order by considering the distance to \f$w_{i+1}\f$. */
+voro_base::voro_base(int nx_,int ny_,int nz_,double boxx_,double boxy_,double boxz_) :
+ nx(nx_), ny(ny_), nz(nz_), nxy(nx_*ny_), nxyz(nxy*nz_), boxx(boxx_), boxy(boxy_), boxz(boxz_),
+ xsp(1/boxx_), ysp(1/boxy_), zsp(1/boxz_), mrad(new double[wl_hgridcu*wl_seq_length]) {
+ const unsigned int b1=1<<21,b2=1<<22,b3=1<<24,b4=1<<25,b5=1<<27,b6=1<<28;
+ const double xstep=boxx/wl_fgrid,ystep=boxy/wl_fgrid,zstep=boxz/wl_fgrid;
+ int i,j,k,lx,ly,lz,q;
+ unsigned int f,*e=const_cast<unsigned int*> (wl);
+ double xlo,ylo,zlo,xhi,yhi,zhi,minr,*radp=mrad;
+ for(zlo=0,zhi=zstep,lz=0;lz<wl_hgrid;zlo=zhi,zhi+=zstep,lz++) {
+ for(ylo=0,yhi=ystep,ly=0;ly<wl_hgrid;ylo=yhi,yhi+=ystep,ly++) {
+ for(xlo=0,xhi=xstep,lx=0;lx<wl_hgrid;xlo=xhi,xhi+=xstep,lx++) {
+ minr=large_number;
+ for(q=e[0]+1;q<wl_seq_length;q++) {
+ f=e[q];
+ i=(f&127)-64;
+ j=(f>>7&127)-64;
+ k=(f>>14&127)-64;
+ if((f&b2)==b2) {
+ compute_minimum(minr,xlo,xhi,ylo,yhi,zlo,zhi,i-1,j,k);
+ if((f&b1)==0) compute_minimum(minr,xlo,xhi,ylo,yhi,zlo,zhi,i+1,j,k);
+ } else if((f&b1)==b1) compute_minimum(minr,xlo,xhi,ylo,yhi,zlo,zhi,i+1,j,k);
+ if((f&b4)==b4) {
+ compute_minimum(minr,xlo,xhi,ylo,yhi,zlo,zhi,i,j-1,k);
+ if((f&b3)==0) compute_minimum(minr,xlo,xhi,ylo,yhi,zlo,zhi,i,j+1,k);
+ } else if((f&b3)==b3) compute_minimum(minr,xlo,xhi,ylo,yhi,zlo,zhi,i,j+1,k);
+ if((f&b6)==b6) {
+ compute_minimum(minr,xlo,xhi,ylo,yhi,zlo,zhi,i,j,k-1);
+ if((f&b5)==0) compute_minimum(minr,xlo,xhi,ylo,yhi,zlo,zhi,i,j,k+1);
+ } else if((f&b5)==b5) compute_minimum(minr,xlo,xhi,ylo,yhi,zlo,zhi,i,j,k+1);
+ }
+ q--;
+ while(q>0) {
+ radp[q]=minr;
+ f=e[q];
+ i=(f&127)-64;
+ j=(f>>7&127)-64;
+ k=(f>>14&127)-64;
+ compute_minimum(minr,xlo,xhi,ylo,yhi,zlo,zhi,i,j,k);
+ q--;
+ }
+ *radp=minr;
+ e+=wl_seq_length;
+ radp+=wl_seq_length;
+ }
+ }
+ }
+}
+
+/** Computes the minimum distance from a subregion to a given block. If this distance
+ * is smaller than the value of minr, then it passes
+ * \param[in,out] minr a pointer to the current minimum distance. If the distance
+ * computed in this function is smaller, then this distance is
+ * set to the new one.
+ * \param[out] (xlo,ylo,zlo) the lower coordinates of the subregion being
+ * considered.
+ * \param[out] (xhi,yhi,zhi) the upper coordinates of the subregion being
+ * considered.
+ * \param[in] (ti,tj,tk) the coordinates of the block. */
+void voro_base::compute_minimum(double &minr,double &xlo,double &xhi,double &ylo,double &yhi,double &zlo,double &zhi,int ti,int tj,int tk) {
+ double radsq,temp;
+ if(ti>0) {temp=boxx*ti-xhi;radsq=temp*temp;}
+ else if(ti<0) {temp=xlo-boxx*(1+ti);radsq=temp*temp;}
+ else radsq=0;
+
+ if(tj>0) {temp=boxy*tj-yhi;radsq+=temp*temp;}
+ else if(tj<0) {temp=ylo-boxy*(1+tj);radsq+=temp*temp;}
+
+ if(tk>0) {temp=boxz*tk-zhi;radsq+=temp*temp;}
+ else if(tk<0) {temp=zlo-boxz*(1+tk);radsq+=temp*temp;}
+
+ if(radsq<minr) minr=radsq;
+}
+
+/** Checks to see whether "%n" appears in a format sequence to determine
+ * whether neighbor information is required or not.
+ * \param[in] format the format string to check.
+ * \return True if a "%n" is found, false otherwise. */
+bool voro_base::contains_neighbor(const char *format) {
+ char *fmp=(const_cast<char*>(format));
+
+ // Check to see if "%n" appears in the format sequence
+ while(*fmp!=0) {
+ if(*fmp=='%') {
+ fmp++;
+ if(*fmp=='n') return true;
+ else if(*fmp==0) return false;
+ }
+ fmp++;
+ }
+
+ return false;
+}
+
+#include "v_base_wl.cc"
+
+}
Index: extern/voro++/src/wall.cc
===================================================================
--- extern/voro++/src/wall.cc (revision 0)
+++ extern/voro++/src/wall.cc (revision 0)
@@ -0,0 +1,122 @@
+// Voro++, a 3D cell-based Voronoi library
+//
+// Author : Chris H. Rycroft (LBL / UC Berkeley)
+// Email : chr@alum.mit.edu
+// Date : August 30th 2011
+
+/** \file wall.cc
+ * \brief Function implementations for the derived wall classes. */
+
+#include "wall.hh"
+
+namespace voro {
+
+/** Tests to see whether a point is inside the sphere wall object.
+ * \param[in,out] (x,y,z) the vector to test.
+ * \return True if the point is inside, false if the point is outside. */
+bool wall_sphere::point_inside(double x,double y,double z) {
+ return (x-xc)*(x-xc)+(y-yc)*(y-yc)+(z-zc)*(z-zc)<rc*rc;
+}
+
+/** Cuts a cell by the sphere wall object. The spherical wall is approximated by
+ * a single plane applied at the point on the sphere which is closest to the center
+ * of the cell. This works well for particle arrangements that are packed against
+ * the wall, but loses accuracy for sparse particle distributions.
+ * \param[in,out] c the Voronoi cell to be cut.
+ * \param[in] (x,y,z) the location of the Voronoi cell.
+ * \return True if the cell still exists, false if the cell is deleted. */
+template<class v_cell>
+bool wall_sphere::cut_cell_base(v_cell &c,double x,double y,double z) {
+ double xd=x-xc,yd=y-yc,zd=z-zc,dq=xd*xd+yd*yd+zd*zd;
+ if (dq>1e-5) {
+ dq=2*(sqrt(dq)*rc-dq);
+ return c.nplane(xd,yd,zd,dq,w_id);
+ }
+ return true;
+}
+
+/** Tests to see whether a point is inside the plane wall object.
+ * \param[in] (x,y,z) the vector to test.
+ * \return True if the point is inside, false if the point is outside. */
+bool wall_plane::point_inside(double x,double y,double z) {
+ return x*xc+y*yc+z*zc<ac;
+}
+
+/** Cuts a cell by the plane wall object.
+ * \param[in,out] c the Voronoi cell to be cut.
+ * \param[in] (x,y,z) the location of the Voronoi cell.
+ * \return True if the cell still exists, false if the cell is deleted. */
+template<class v_cell>
+bool wall_plane::cut_cell_base(v_cell &c,double x,double y,double z) {
+ double dq=2*(ac-x*xc-y*yc-z*zc);
+ return c.nplane(xc,yc,zc,dq,w_id);
+}
+
+/** Tests to see whether a point is inside the cylindrical wall object.
+ * \param[in] (x,y,z) the vector to test.
+ * \return True if the point is inside, false if the point is outside. */
+bool wall_cylinder::point_inside(double x,double y,double z) {
+ double xd=x-xc,yd=y-yc,zd=z-zc;
+ double pa=(xd*xa+yd*ya+zd*za)*asi;
+ xd-=xa*pa;yd-=ya*pa;zd-=za*pa;
+ return xd*xd+yd*yd+zd*zd<rc*rc;
+}
+
+/** Cuts a cell by the cylindrical wall object. The cylindrical wall is
+ * approximated by a single plane applied at the point on the cylinder which is
+ * closest to the center of the cell. This works well for particle arrangements
+ * that are packed against the wall, but loses accuracy for sparse particle
+ * distributions.
+ * \param[in,out] c the Voronoi cell to be cut.
+ * \param[in] (x,y,z) the location of the Voronoi cell.
+ * \return True if the cell still exists, false if the cell is deleted. */
+template<class v_cell>
+bool wall_cylinder::cut_cell_base(v_cell &c,double x,double y,double z) {
+ double xd=x-xc,yd=y-yc,zd=z-zc,pa=(xd*xa+yd*ya+zd*za)*asi;
+ xd-=xa*pa;yd-=ya*pa;zd-=za*pa;
+ pa=xd*xd+yd*yd+zd*zd;
+ if(pa>1e-5) {
+ pa=2*(sqrt(pa)*rc-pa);
+ return c.nplane(xd,yd,zd,pa,w_id);
+ }
+ return true;
+}
+
+/** Tests to see whether a point is inside the cone wall object.
+ * \param[in] (x,y,z) the vector to test.
+ * \return True if the point is inside, false if the point is outside. */
+bool wall_cone::point_inside(double x,double y,double z) {
+ double xd=x-xc,yd=y-yc,zd=z-zc,pa=(xd*xa+yd*ya+zd*za)*asi;
+ xd-=xa*pa;yd-=ya*pa;zd-=za*pa;
+ pa*=gra;
+ if (pa<0) return false;
+ pa*=pa;
+ return xd*xd+yd*yd+zd*zd<pa;
+}
+
+/** Cuts a cell by the cone wall object. The conical wall is
+ * approximated by a single plane applied at the point on the cone which is
+ * closest to the center of the cell. This works well for particle arrangements
+ * that are packed against the wall, but loses accuracy for sparse particle
+ * distributions.
+ * \param[in,out] c the Voronoi cell to be cut.
+ * \param[in] (x,y,z) the location of the Voronoi cell.
+ * \return True if the cell still exists, false if the cell is deleted. */
+template<class v_cell>
+bool wall_cone::cut_cell_base(v_cell &c,double x,double y,double z) {
+ double xd=x-xc,yd=y-yc,zd=z-zc,xf,yf,zf,q,pa=(xd*xa+yd*ya+zd*za)*asi;
+ xd-=xa*pa;yd-=ya*pa;zd-=za*pa;
+ pa=xd*xd+yd*yd+zd*zd;
+ if(pa>1e-5) {
+ pa=1/sqrt(pa);
+ q=sqrt(asi);
+ xf=-sang*q*xa+cang*pa*xd;
+ yf=-sang*q*ya+cang*pa*yd;
+ zf=-sang*q*za+cang*pa*zd;
+ pa=2*(xf*(xc-x)+yf*(yc-y)+zf*(zc-z));
+ return c.nplane(xf,yf,zf,pa,w_id);
+ }
+ return true;
+}
+
+}
Index: extern/voro++/src/Makefile.dep
===================================================================
--- extern/voro++/src/Makefile.dep (revision 0)
+++ extern/voro++/src/Makefile.dep (revision 0)
@@ -0,0 +1,18 @@
+cell.o: cell.cc config.hh common.hh cell.hh
+common.o: common.cc common.hh config.hh
+container.o: container.cc container.hh config.hh common.hh v_base.hh \
+ worklist.hh cell.hh c_loops.hh v_compute.hh rad_option.hh
+unitcell.o: unitcell.cc unitcell.hh config.hh cell.hh common.hh
+v_compute.o: v_compute.cc worklist.hh v_compute.hh config.hh cell.hh \
+ common.hh rad_option.hh container.hh v_base.hh c_loops.hh \
+ container_prd.hh unitcell.hh
+c_loops.o: c_loops.cc c_loops.hh config.hh
+v_base.o: v_base.cc v_base.hh worklist.hh config.hh v_base_wl.cc
+wall.o: wall.cc wall.hh cell.hh config.hh common.hh container.hh \
+ v_base.hh worklist.hh c_loops.hh v_compute.hh rad_option.hh
+pre_container.o: pre_container.cc config.hh pre_container.hh c_loops.hh \
+ container.hh common.hh v_base.hh worklist.hh cell.hh v_compute.hh \
+ rad_option.hh
+container_prd.o: container_prd.cc container_prd.hh config.hh common.hh \
+ v_base.hh worklist.hh cell.hh c_loops.hh v_compute.hh unitcell.hh \
+ rad_option.hh
Index: extern/voro++/src/container_prd.hh
===================================================================
--- extern/voro++/src/container_prd.hh (revision 0)
+++ extern/voro++/src/container_prd.hh (revision 0)
@@ -0,0 +1,616 @@
+// Voro++, a 3D cell-based Voronoi library
+//
+// Author : Chris H. Rycroft (LBL / UC Berkeley)
+// Email : chr@alum.mit.edu
+// Date : August 30th 2011
+
+/** \file container_prd.hh
+ * \brief Header file for the container_periodic_base and related classes. */
+
+#ifndef VOROPP_CONTAINER_PRD_HH
+#define VOROPP_CONTAINER_PRD_HH
+
+#include <cstdio>
+#include <vector>
+
+#include "config.hh"
+#include "common.hh"
+#include "v_base.hh"
+#include "cell.hh"
+#include "c_loops.hh"
+#include "v_compute.hh"
+#include "unitcell.hh"
+#include "rad_option.hh"
+
+namespace voro {
+
+/** \brief Class for representing a particle system in a 3D periodic
+ * non-orthogonal periodic domain.
+ *
+ * This class represents a particle system in a three-dimensional
+ * non-orthogonal periodic domain. The domain is defined by three periodicity
+ * vectors (bx,0,0), (bxy,by,0), and (bxz,byz,bz) that represent a
+ * parallelepiped. Internally, the class stores particles in the box 0<x<bx,
+ * 0<y<by, 0<z<bz, and constructs periodic images of particles that displaced
+ * by the three periodicity vectors when they are necessary for the
+ * computation. The internal memory structure for this class is significantly
+ * different from the container_base class in order to handle the dynamic
+ * construction of these periodic images.
+ *
+ * The class is derived from the unitcell class, which encapsulates information
+ * about the domain geometry, and the voro_base class, which encapsulates
+ * information about the underlying computational grid. */
+class container_periodic_base : public unitcell, public voro_base {
+ public:
+ /** The lower y index (inclusive) of the primary domain within
+ * the block structure. */
+ int ey;
+ /** The lower z index (inclusive) of the primary domain within
+ * the block structure. */
+ int ez;
+ /** The upper y index (exclusive) of the primary domain within
+ * the block structure. */
+ int wy;
+ /** The upper z index (exclusive) of the primary domain within
+ * the block structure. */
+ int wz;
+ /** The total size of the block structure (including images) in
+ * the y direction. */
+ int oy;
+ /** The total size of the block structure (including images) in
+ * the z direction. */
+ int oz;
+ /** The total number of blocks. */
+ int oxyz;
+ /** This array holds the numerical IDs of each particle in each
+ * computational box. */
+ int **id;
+ /** A two dimensional array holding particle positions. For the
+ * derived container_poly class, this also holds particle
+ * radii. */
+ double **p;
+ /** This array holds the number of particles within each
+ * computational box of the container. */
+ int *co;
+ /** This array holds the maximum amount of particle memory for
+ * each computational box of the container. If the number of
+ * particles in a particular box ever approaches this limit,
+ * more is allocated using the add_particle_memory() function.
+ */
+ int *mem;
+ /** An array holding information about periodic image
+ * construction at a given location. */
+ char *img;
+ /** The initial amount of memory to allocate for particles
+ * for each block. */
+ const int init_mem;
+ /** The amount of memory in the array structure for each
+ * particle. This is set to 3 when the basic class is
+ * initialized, so that the array holds (x,y,z) positions. If
+ * the container class is initialized as part of the derived
+ * class container_poly, then this is set to 4, to also hold
+ * the particle radii. */
+ const int ps;
+ container_periodic_base(double bx_,double bxy_,double by_,double bxz_,double byz_,double bz_,
+ int nx_,int ny_,int nz_,int init_mem_,int ps);
+ ~container_periodic_base();
+ /** Prints all particles in the container, including those that
+ * have been constructed in image blocks. */
+ inline void print_all_particles() {
+ int ijk,q;
+ for(ijk=0;ijk<oxyz;ijk++) for(q=0;q<co[ijk];q++)
+ printf("%d %g %g %g\n",id[ijk][q],p[ijk][ps*q],p[ijk][ps*q+1],p[ijk][ps*q+2]);
+ }
+ void region_count();
+ /** Initializes the Voronoi cell prior to a compute_cell
+ * operation for a specific particle being carried out by a
+ * voro_compute class. The cell is initialized to be the
+ * pre-computed unit Voronoi cell based on planes formed by
+ * periodic images of the particle.
+ * \param[in,out] c a reference to a voronoicell object.
+ * \param[in] ijk the block that the particle is within.
+ * \param[in] q the index of the particle within its block.
+ * \param[in] (ci,cj,ck) the coordinates of the block in the
+ * container coordinate system.
+ * \param[out] (i,j,k) the coordinates of the test block
+ * relative to the voro_compute
+ * coordinate system.
+ * \param[out] (x,y,z) the position of the particle.
+ * \param[out] disp a block displacement used internally by the
+ * compute_cell routine.
+ * \return False if the plane cuts applied by walls completely
+ * removed the cell, true otherwise. */
+ template<class v_cell>
+ inline bool initialize_voronoicell(v_cell &c,int ijk,int q,int ci,int cj,int ck,int &i,int &j,int &k,double &x,double &y,double &z,int &disp) {
+ c=unit_voro;
+ double *pp=p[ijk]+ps*q;
+ x=*(pp++);y=*(pp++);z=*pp;
+ i=nx;j=ey;k=ez;
+ return true;
+ }
+ /** Initializes parameters for a find_voronoi_cell call within
+ * the voro_compute template.
+ * \param[in] (ci,cj,ck) the coordinates of the test block in
+ * the container coordinate system.
+ * \param[in] ijk the index of the test block
+ * \param[out] (i,j,k) the coordinates of the test block
+ * relative to the voro_compute
+ * coordinate system.
+ * \param[out] disp a block displacement used internally by the
+ * find_voronoi_cell routine (but not needed
+ * in this instance.) */
+ inline void initialize_search(int ci,int cj,int ck,int ijk,int &i,int &j,int &k,int &disp) {
+ i=nx;j=ey;k=ez;
+ }
+ /** Returns the position of a particle currently being computed
+ * relative to the computational block that it is within. It is
+ * used to select the optimal worklist entry to use.
+ * \param[in] (x,y,z) the position of the particle.
+ * \param[in] (ci,cj,ck) the block that the particle is within.
+ * \param[out] (fx,fy,fz) the position relative to the block.
+ */
+ inline void frac_pos(double x,double y,double z,double ci,double cj,double ck,double &fx,double &fy,double &fz) {
+ fx=x-boxx*ci;
+ fy=y-boxy*(cj-ey);
+ fz=z-boxz*(ck-ez);
+ }
+ /** Calculates the index of block in the container structure
+ * corresponding to given coordinates.
+ * \param[in] (ci,cj,ck) the coordinates of the original block
+ * in the current computation, relative
+ * to the container coordinate system.
+ * \param[in] (ei,ej,ek) the displacement of the current block
+ * from the original block.
+ * \param[in,out] (qx,qy,qz) the periodic displacement that
+ * must be added to the particles
+ * within the computed block.
+ * \param[in] disp a block displacement used internally by the
+ * find_voronoi_cell and compute_cell routines
+ * (but not needed in this instance.)
+ * \return The block index. */
+ inline int region_index(int ci,int cj,int ck,int ei,int ej,int ek,double &qx,double &qy,double &qz,int &disp) {
+ int qi=ci+(ei-nx),qj=cj+(ej-ey),qk=ck+(ek-ez);
+ int iv(step_div(qi,nx));if(iv!=0) {qx=iv*bx;qi-=nx*iv;} else qx=0;
+ create_periodic_image(qi,qj,qk);
+ return qi+nx*(qj+oy*qk);
+ }
+ void create_all_images();
+ void check_compartmentalized();
+ protected:
+ void add_particle_memory(int i);
+ void put_locate_block(int &ijk,double &x,double &y,double &z);
+ void put_locate_block(int &ijk,double &x,double &y,double &z,int &ai,int &aj,int &ak);
+ /** Creates particles within an image block by copying them
+ * from the primary domain and shifting them. If the given
+ * block is aligned with the primary domain in the z-direction,
+ * the routine calls the simpler create_side_image routine
+ * where the image block may comprise of particles from up to
+ * two primary blocks. Otherwise is calls the more complex
+ * create_vertical_image where the image block may comprise of
+ * particles from up to four primary blocks.
+ * \param[in] (di,dj,dk) the coordinates of the image block to
+ * create. */
+ inline void create_periodic_image(int di,int dj,int dk) {
+ if(di<0||di>=nx||dj<0||dj>=oy||dk<0||dk>=oz)
+ voro_fatal_error("Constructing periodic image for nonexistent point",VOROPP_INTERNAL_ERROR);
+ if(dk>=ez&&dk<wz) {
+ if(dj<ey||dj>=wy) create_side_image(di,dj,dk);
+ } else create_vertical_image(di,dj,dk);
+ }
+ void create_side_image(int di,int dj,int dk);
+ void create_vertical_image(int di,int dj,int dk);
+ void put_image(int reg,int fijk,int l,double dx,double dy,double dz);
+ inline void remap(int &ai,int &aj,int &ak,int &ci,int &cj,int &ck,double &x,double &y,double &z,int &ijk);
+};
+
+/** \brief Extension of the container_periodic_base class for computing regular
+ * Voronoi tessellations.
+ *
+ * This class is an extension of the container_periodic_base that has routines
+ * specifically for computing the regular Voronoi tessellation with no
+ * dependence on particle radii. */
+class container_periodic : public container_periodic_base, public radius_mono {
+ public:
+ container_periodic(double bx_,double bxy_,double by_,double bxz_,double byz_,double bz_,
+ int nx_,int ny_,int nz_,int init_mem_);
+ void clear();
+ void put(int n,double x,double y,double z);
+ void put(int n,double x,double y,double z,int &ai,int &aj,int &ak);
+ void put(particle_order &vo,int n,double x,double y,double z);
+ void import(FILE *fp=stdin);
+ void import(particle_order &vo,FILE *fp=stdin);
+ /** Imports a list of particles from an open file stream into
+ * the container. Entries of four numbers (Particle ID, x
+ * position, y position, z position) are searched for. If the
+ * file cannot be successfully read, then the routine causes a
+ * fatal error.
+ * \param[in] filename the name of the file to open and read
+ * from. */
+ inline void import(const char* filename) {
+ FILE *fp=safe_fopen(filename,"r");
+ import(fp);
+ fclose(fp);
+ }
+ /** Imports a list of particles from an open file stream into
+ * the container. Entries of four numbers (Particle ID, x
+ * position, y position, z position) are searched for. In
+ * addition, the order in which particles are read is saved
+ * into an ordering class. If the file cannot be successfully
+ * read, then the routine causes a fatal error.
+ * \param[in,out] vo the ordering class to use.
+ * \param[in] filename the name of the file to open and read
+ * from. */
+ inline void import(particle_order &vo,const char* filename) {
+ FILE *fp=safe_fopen(filename,"r");
+ import(vo,fp);
+ fclose(fp);
+ }
+ void compute_all_cells();
+ double sum_cell_volumes();
+ /** Dumps particle IDs and positions to a file.
+ * \param[in] vl the loop class to use.
+ * \param[in] fp a file handle to write to. */
+ template<class c_loop>
+ void draw_particles(c_loop &vl,FILE *fp) {
+ double *pp;
+ if(vl.start()) do {
+ pp=p[vl.ijk]+3*vl.q;
+ fprintf(fp,"%d %g %g %g\n",id[vl.ijk][vl.q],*pp,pp[1],pp[2]);
+ } while(vl.inc());
+ }
+ /** Dumps all of the particle IDs and positions to a file.
+ * \param[in] fp a file handle to write to. */
+ inline void draw_particles(FILE *fp=stdout) {
+ c_loop_all_periodic vl(*this);
+ draw_particles(vl,fp);
+ }
+ /** Dumps all of the particle IDs and positions to a file.
+ * \param[in] filename the name of the file to write to. */
+ inline void draw_particles(const char *filename) {
+ FILE *fp=safe_fopen(filename,"w");
+ draw_particles(fp);
+ fclose(fp);
+ }
+ /** Dumps particle positions in POV-Ray format.
+ * \param[in] vl the loop class to use.
+ * \param[in] fp a file handle to write to. */
+ template<class c_loop>
+ void draw_particles_pov(c_loop &vl,FILE *fp) {
+ double *pp;
+ if(vl.start()) do {
+ pp=p[vl.ijk]+3*vl.q;
+ fprintf(fp,"// id %d\nsphere{<%g,%g,%g>,s}\n",
+ id[vl.ijk][vl.q],*pp,pp[1],pp[2]);
+ } while(vl.inc());
+ }
+ /** Dumps all particle positions in POV-Ray format.
+ * \param[in] fp a file handle to write to. */
+ inline void draw_particles_pov(FILE *fp=stdout) {
+ c_loop_all_periodic vl(*this);
+ draw_particles_pov(vl,fp);
+ }
+ /** Dumps all particle positions in POV-Ray format.
+ * \param[in] filename the name of the file to write to. */
+ inline void draw_particles_pov(const char *filename) {
+ FILE *fp=safe_fopen(filename,"w");
+ draw_particles_pov(fp);
+ fclose(fp);
+ }
+ /** Computes Voronoi cells and saves the output in gnuplot
+ * format.
+ * \param[in] vl the loop class to use.
+ * \param[in] fp a file handle to write to. */
+ template<class c_loop>
+ void draw_cells_gnuplot(c_loop &vl,FILE *fp) {
+ voronoicell c;double *pp;
+ if(vl.start()) do if(compute_cell(c,vl)) {
+ pp=p[vl.ijk]+ps*vl.q;
+ c.draw_gnuplot(*pp,pp[1],pp[2],fp);
+ } while(vl.inc());
+ }
+ /** Computes all Voronoi cells and saves the output in gnuplot
+ * format.
+ * \param[in] fp a file handle to write to. */
+ inline void draw_cells_gnuplot(FILE *fp=stdout) {
+ c_loop_all_periodic vl(*this);
+ draw_cells_gnuplot(vl,fp);
+ }
+ /** Compute all Voronoi cells and saves the output in gnuplot
+ * format.
+ * \param[in] filename the name of the file to write to. */
+ inline void draw_cells_gnuplot(const char *filename) {
+ FILE *fp=safe_fopen(filename,"w");
+ draw_cells_gnuplot(fp);
+ fclose(fp);
+ }
+ /** Computes Voronoi cells and saves the output in POV-Ray
+ * format.
+ * \param[in] vl the loop class to use.
+ * \param[in] fp a file handle to write to. */
+ template<class c_loop>
+ void draw_cells_pov(c_loop &vl,FILE *fp) {
+ voronoicell c;double *pp;
+ if(vl.start()) do if(compute_cell(c,vl)) {
+ fprintf(fp,"// cell %d\n",id[vl.ijk][vl.q]);
+ pp=p[vl.ijk]+ps*vl.q;
+ c.draw_pov(*pp,pp[1],pp[2],fp);
+ } while(vl.inc());
+ }
+ /** Computes all Voronoi cells and saves the output in POV-Ray
+ * format.
+ * \param[in] fp a file handle to write to. */
+ inline void draw_cells_pov(FILE *fp=stdout) {
+ c_loop_all_periodic vl(*this);
+ draw_cells_pov(vl,fp);
+ }
+ /** Computes all Voronoi cells and saves the output in POV-Ray
+ * format.
+ * \param[in] filename the name of the file to write to. */
+ inline void draw_cells_pov(const char *filename) {
+ FILE *fp=safe_fopen(filename,"w");
+ draw_cells_pov(fp);
+ fclose(fp);
+ }
+ /** Computes the Voronoi cells and saves customized information
+ * about them.
+ * \param[in] vl the loop class to use.
+ * \param[in] format the custom output string to use.
+ * \param[in] fp a file handle to write to. */
+ template<class c_loop>
+ void print_custom(c_loop &vl,const char *format,FILE *fp) {
+ int ijk,q;double *pp;
+ if(contains_neighbor(format)) {
+ voronoicell_neighbor c;
+ if(vl.start()) do if(compute_cell(c,vl)) {
+ ijk=vl.ijk;q=vl.q;pp=p[ijk]+ps*q;
+ c.output_custom(format,id[ijk][q],*pp,pp[1],pp[2],default_radius,fp);
+ } while(vl.inc());
+ } else {
+ voronoicell c;
+ if(vl.start()) do if(compute_cell(c,vl)) {
+ ijk=vl.ijk;q=vl.q;pp=p[ijk]+ps*q;
+ c.output_custom(format,id[ijk][q],*pp,pp[1],pp[2],default_radius,fp);
+ } while(vl.inc());
+ }
+ }
+ void print_custom(const char *format,FILE *fp=stdout);
+ void print_custom(const char *format,const char *filename);
+ bool find_voronoi_cell(double x,double y,double z,double &rx,double &ry,double &rz,int &pid);
+ /** Computes the Voronoi cell for a particle currently being
+ * referenced by a loop class.
+ * \param[out] c a Voronoi cell class in which to store the
+ * computed cell.
+ * \param[in] vl the loop class to use.
+ * \return True if the cell was computed. If the cell cannot be
+ * computed because it was removed entirely for some reason,
+ * then the routine returns false. */
+ template<class v_cell,class c_loop>
+ inline bool compute_cell(v_cell &c,c_loop &vl) {
+ return vc.compute_cell(c,vl.ijk,vl.q,vl.i,vl.j,vl.k);
+ }
+ /** Computes the Voronoi cell for given particle.
+ * \param[out] c a Voronoi cell class in which to store the
+ * computed cell.
+ * \param[in] ijk the block that the particle is within.
+ * \param[in] q the index of the particle within the block.
+ * \return True if the cell was computed. If the cell cannot be
+ * computed because it was removed entirely for some reason,
+ * then the routine returns false. */
+ template<class v_cell>
+ inline bool compute_cell(v_cell &c,int ijk,int q) {
+ int k(ijk/(nx*oy)),ijkt(ijk-(nx*oy)*k),j(ijkt/nx),i(ijkt-j*nx);
+ return vc.compute_cell(c,ijk,q,i,j,k);
+ }
+ private:
+ voro_compute<container_periodic> vc;
+ friend class voro_compute<container_periodic>;
+};
+
+/** \brief Extension of the container_periodic_base class for computing radical
+ * Voronoi tessellations.
+ *
+ * This class is an extension of container_periodic_base that has routines
+ * specifically for computing the radical Voronoi tessellation that depends
+ * on the particle radii. */
+class container_periodic_poly : public container_periodic_base, public radius_poly {
+ public:
+ container_periodic_poly(double bx_,double bxy_,double by_,double bxz_,double byz_,double bz_,
+ int nx_,int ny_,int nz_,int init_mem_);
+ void clear();
+ void put(int n,double x,double y,double z,double r);
+ void put(int n,double x,double y,double z,double r,int &ai,int &aj,int &ak);
+ void put(particle_order &vo,int n,double x,double y,double z,double r);
+ void import(FILE *fp=stdin);
+ void import(particle_order &vo,FILE *fp=stdin);
+ /** Imports a list of particles from an open file stream into
+ * the container_poly class. Entries of five numbers (Particle
+ * ID, x position, y position, z position, radius) are searched
+ * for. If the file cannot be successfully read, then the
+ * routine causes a fatal error.
+ * \param[in] filename the name of the file to open and read
+ * from. */
+ inline void import(const char* filename) {
+ FILE *fp=safe_fopen(filename,"r");
+ import(fp);
+ fclose(fp);
+ }
+ /** Imports a list of particles from an open file stream into
+ * the container_poly class. Entries of five numbers (Particle
+ * ID, x position, y position, z position, radius) are searched
+ * for. In addition, the order in which particles are read is
+ * saved into an ordering class. If the file cannot be
+ * successfully read, then the routine causes a fatal error.
+ * \param[in,out] vo the ordering class to use.
+ * \param[in] filename the name of the file to open and read
+ * from. */
+ inline void import(particle_order &vo,const char* filename) {
+ FILE *fp=safe_fopen(filename,"r");
+ import(vo,fp);
+ fclose(fp);
+ }
+ void compute_all_cells();
+ double sum_cell_volumes();
+ /** Dumps particle IDs, positions and radii to a file.
+ * \param[in] vl the loop class to use.
+ * \param[in] fp a file handle to write to. */
+ template<class c_loop>
+ void draw_particles(c_loop &vl,FILE *fp) {
+ double *pp;
+ if(vl.start()) do {
+ pp=p[vl.ijk]+4*vl.q;
+ fprintf(fp,"%d %g %g %g %g\n",id[vl.ijk][vl.q],*pp,pp[1],pp[2],pp[3]);
+ } while(vl.inc());
+ }
+ /** Dumps all of the particle IDs, positions and radii to a
+ * file.
+ * \param[in] fp a file handle to write to. */
+ inline void draw_particles(FILE *fp=stdout) {
+ c_loop_all_periodic vl(*this);
+ draw_particles(vl,fp);
+ }
+ /** Dumps all of the particle IDs, positions and radii to a
+ * file.
+ * \param[in] filename the name of the file to write to. */
+ inline void draw_particles(const char *filename) {
+ FILE *fp=safe_fopen(filename,"w");
+ draw_particles(fp);
+ fclose(fp);
+ }
+ /** Dumps particle positions in POV-Ray format.
+ * \param[in] vl the loop class to use.
+ * \param[in] fp a file handle to write to. */
+ template<class c_loop>
+ void draw_particles_pov(c_loop &vl,FILE *fp) {
+ double *pp;
+ if(vl.start()) do {
+ pp=p[vl.ijk]+4*vl.q;
+ fprintf(fp,"// id %d\nsphere{<%g,%g,%g>,%g}\n",
+ id[vl.ijk][vl.q],*pp,pp[1],pp[2],pp[3]);
+ } while(vl.inc());
+ }
+ /** Dumps all the particle positions in POV-Ray format.
+ * \param[in] fp a file handle to write to. */
+ inline void draw_particles_pov(FILE *fp=stdout) {
+ c_loop_all_periodic vl(*this);
+ draw_particles_pov(vl,fp);
+ }
+ /** Dumps all the particle positions in POV-Ray format.
+ * \param[in] filename the name of the file to write to. */
+ inline void draw_particles_pov(const char *filename) {
+ FILE *fp(safe_fopen(filename,"w"));
+ draw_particles_pov(fp);
+ fclose(fp);
+ }
+ /** Computes Voronoi cells and saves the output in gnuplot
+ * format.
+ * \param[in] vl the loop class to use.
+ * \param[in] fp a file handle to write to. */
+ template<class c_loop>
+ void draw_cells_gnuplot(c_loop &vl,FILE *fp) {
+ voronoicell c;double *pp;
+ if(vl.start()) do if(compute_cell(c,vl)) {
+ pp=p[vl.ijk]+ps*vl.q;
+ c.draw_gnuplot(*pp,pp[1],pp[2],fp);
+ } while(vl.inc());
+ }
+ /** Compute all Voronoi cells and saves the output in gnuplot
+ * format.
+ * \param[in] fp a file handle to write to. */
+ inline void draw_cells_gnuplot(FILE *fp=stdout) {
+ c_loop_all_periodic vl(*this);
+ draw_cells_gnuplot(vl,fp);
+ }
+ /** Compute all Voronoi cells and saves the output in gnuplot
+ * format.
+ * \param[in] filename the name of the file to write to. */
+ inline void draw_cells_gnuplot(const char *filename) {
+ FILE *fp(safe_fopen(filename,"w"));
+ draw_cells_gnuplot(fp);
+ fclose(fp);
+ }
+ /** Computes Voronoi cells and saves the output in POV-Ray
+ * format.
+ * \param[in] vl the loop class to use.
+ * \param[in] fp a file handle to write to. */
+ template<class c_loop>
+ void draw_cells_pov(c_loop &vl,FILE *fp) {
+ voronoicell c;double *pp;
+ if(vl.start()) do if(compute_cell(c,vl)) {
+ fprintf(fp,"// cell %d\n",id[vl.ijk][vl.q]);
+ pp=p[vl.ijk]+ps*vl.q;
+ c.draw_pov(*pp,pp[1],pp[2],fp);
+ } while(vl.inc());
+ }
+ /** Computes all Voronoi cells and saves the output in POV-Ray
+ * format.
+ * \param[in] fp a file handle to write to. */
+ inline void draw_cells_pov(FILE *fp=stdout) {
+ c_loop_all_periodic vl(*this);
+ draw_cells_pov(vl,fp);
+ }
+ /** Computes all Voronoi cells and saves the output in POV-Ray
+ * format.
+ * \param[in] filename the name of the file to write to. */
+ inline void draw_cells_pov(const char *filename) {
+ FILE *fp(safe_fopen(filename,"w"));
+ draw_cells_pov(fp);
+ fclose(fp);
+ }
+ /** Computes the Voronoi cells and saves customized information
+ * about them.
+ * \param[in] vl the loop class to use.
+ * \param[in] format the custom output string to use.
+ * \param[in] fp a file handle to write to. */
+ template<class c_loop>
+ void print_custom(c_loop &vl,const char *format,FILE *fp) {
+ int ijk,q;double *pp;
+ if(contains_neighbor(format)) {
+ voronoicell_neighbor c;
+ if(vl.start()) do if(compute_cell(c,vl)) {
+ ijk=vl.ijk;q=vl.q;pp=p[ijk]+ps*q;
+ c.output_custom(format,id[ijk][q],*pp,pp[1],pp[2],pp[3],fp);
+ } while(vl.inc());
+ } else {
+ voronoicell c;
+ if(vl.start()) do if(compute_cell(c,vl)) {
+ ijk=vl.ijk;q=vl.q;pp=p[ijk]+ps*q;
+ c.output_custom(format,id[ijk][q],*pp,pp[1],pp[2],pp[3],fp);
+ } while(vl.inc());
+ }
+ }
+ /** Computes the Voronoi cell for a particle currently being
+ * referenced by a loop class.
+ * \param[out] c a Voronoi cell class in which to store the
+ * computed cell.
+ * \param[in] vl the loop class to use.
+ * \return True if the cell was computed. If the cell cannot be
+ * computed because it was removed entirely for some reason,
+ * then the routine returns false. */
+ template<class v_cell,class c_loop>
+ inline bool compute_cell(v_cell &c,c_loop &vl) {
+ return vc.compute_cell(c,vl.ijk,vl.q,vl.i,vl.j,vl.k);
+ }
+ /** Computes the Voronoi cell for given particle.
+ * \param[out] c a Voronoi cell class in which to store the
+ * computed cell.
+ * \param[in] ijk the block that the particle is within.
+ * \param[in] q the index of the particle within the block.
+ * \return True if the cell was computed. If the cell cannot be
+ * computed because it was removed entirely for some reason,
+ * then the routine returns false. */
+ template<class v_cell>
+ inline bool compute_cell(v_cell &c,int ijk,int q) {
+ int k(ijk/(nx*oy)),ijkt(ijk-(nx*oy)*k),j(ijkt/nx),i(ijkt-j*nx);
+ return vc.compute_cell(c,ijk,q,i,j,k);
+ }
+ void print_custom(const char *format,FILE *fp=stdout);
+ void print_custom(const char *format,const char *filename);
+ bool find_voronoi_cell(double x,double y,double z,double &rx,double &ry,double &rz,int &pid);
+ private:
+ voro_compute<container_periodic_poly> vc;
+ friend class voro_compute<container_periodic_poly>;
+};
+
+}
+
+#endif
Index: extern/voro++/src/v_compute.cc
===================================================================
--- extern/voro++/src/v_compute.cc (revision 0)
+++ extern/voro++/src/v_compute.cc (revision 0)
@@ -0,0 +1,1006 @@
+// Voro++, a 3D cell-based Voronoi library
+//
+// Author : Chris H. Rycroft (LBL / UC Berkeley)
+// Email : chr@alum.mit.edu
+// Date : August 30th 2011
+
+/** \file v_compute.cc
+ * \brief Function implementantions for the voro_compute template. */
+
+#include "worklist.hh"
+#include "v_compute.hh"
+#include "rad_option.hh"
+#include "container.hh"
+#include "container_prd.hh"
+
+namespace voro {
+
+/** The class constructor initializes constants from the container class, and
+ * sets up the mask and queue used for Voronoi computations.
+ * \param[in] con_ a reference to the container class to use.
+ * \param[in] (hx_,hy_,hz_) the size of the mask to use. */
+template<class c_class>
+voro_compute<c_class>::voro_compute(c_class &con_,int hx_,int hy_,int hz_) :
+ con(con_), boxx(con_.boxx), boxy(con_.boxy), boxz(con_.boxz),
+ xsp(con_.xsp), ysp(con_.ysp), zsp(con_.zsp),
+ hx(hx_), hy(hy_), hz(hz_), hxy(hx_*hy_), hxyz(hxy*hz_), ps(con_.ps),
+ id(con_.id), p(con_.p), co(con_.co), bxsq(boxx*boxx+boxy*boxy+boxz*boxz),
+ mv(0), qu_size(3*(3+hxy+hz*(hx+hy))), wl(con_.wl), mrad(con_.mrad),
+ mask(new unsigned int[hxyz]), qu(new int[qu_size]), qu_l(qu+qu_size) {
+ reset_mask();
+}
+
+/** Scans all of the particles within a block to see if any of them have a
+ * smaller distance to the given test vector. If one is found, the routine
+ * updates the minimum distance and store information about this particle.
+ * \param[in] ijk the index of the block.
+ * \param[in] (x,y,z) the test vector to consider (which may have already had a
+ * periodic displacement applied to it).
+ * \param[in] (di,dj,dk) the coordinates of the current block, to store if the
+ * particle record is updated.
+ * \param[in,out] w a reference to a particle record in which to store
+ * information about the particle whose Voronoi cell the
+ * vector is within.
+ * \param[in,out] mrs the current minimum distance, that may be updated if a
+ * closer particle is found. */
+template<class c_class>
+inline void voro_compute<c_class>::scan_all(int ijk,double x,double y,double z,int di,int dj,int dk,particle_record &w,double &mrs) {
+ double x1,y1,z1,rs;bool in_block=false;
+ for(int l=0;l<co[ijk];l++) {
+ x1=p[ijk][ps*l]-x;
+ y1=p[ijk][ps*l+1]-y;
+ z1=p[ijk][ps*l+2]-z;
+ rs=con.r_current_sub(x1*x1+y1*y1+z1*z1,ijk,l);
+ if(rs<mrs) {mrs=rs;w.l=l;in_block=true;}
+ }
+ if(in_block) {w.ijk=ijk;w.di=di;w.dj=dj,w.dk=dk;}
+}
+
+/** Finds the Voronoi cell that given vector is within. For containers that are
+ * not radially dependent, this corresponds to findig the particle that is
+ * closest to the vector; for the radical tessellation containers, this
+ * corresponds to a finding the minimum weighted distance.
+ * \param[in] (x,y,z) the vector to consider.
+ * \param[in] (ci,cj,ck) the coordinates of the block that the test particle is
+ * in relative to the container data structure.
+ * \param[in] ijk the index of the block that the test particle is in.
+ * \param[out] w a reference to a particle record in which to store information
+ * about the particle whose Voronoi cell the vector is within.
+ * \param[out] mrs the minimum computed distance. */
+template<class c_class>
+void voro_compute<c_class>::find_voronoi_cell(double x,double y,double z,int ci,int cj,int ck,int ijk,particle_record &w,double &mrs) {
+ double qx=0,qy=0,qz=0,rs;
+ int i,j,k,di,dj,dk,ei,ej,ek,f,g,disp;
+ double fx,fy,fz,mxs,mys,mzs,*radp;
+ unsigned int q,*e,*mijk;
+
+ // Init setup for parameters to return
+ w.ijk=-1;mrs=large_number;
+
+ con.initialize_search(ci,cj,ck,ijk,i,j,k,disp);
+
+ // Test all particles in the particle's local region first
+ scan_all(ijk,x,y,z,0,0,0,w,mrs);
+
+ // Now compute the fractional position of the particle within its
+ // region and store it in (fx,fy,fz). We use this to compute an index
+ // (di,dj,dk) of which subregion the particle is within.
+ unsigned int m1,m2;
+ con.frac_pos(x,y,z,ci,cj,ck,fx,fy,fz);
+ di=int(fx*xsp*wl_fgrid);dj=int(fy*ysp*wl_fgrid);dk=int(fz*zsp*wl_fgrid);
+
+ // The indices (di,dj,dk) tell us which worklist to use, to test the
+ // blocks in the optimal order. But we only store worklists for the
+ // eighth of the region where di, dj, and dk are all less than half the
+ // full grid. The rest of the cases are handled by symmetry. In this
+ // section, we detect for these cases, by reflecting high values of di,
+ // dj, and dk. For these cases, a mask is constructed in m1 and m2
+ // which is used to flip the worklist information when it is loaded.
+ if(di>=wl_hgrid) {
+ mxs=boxx-fx;
+ m1=127+(3<<21);m2=1+(1<<21);di=wl_fgrid-1-di;if(di<0) di=0;
+ } else {m1=m2=0;mxs=fx;}
+ if(dj>=wl_hgrid) {
+ mys=boxy-fy;
+ m1|=(127<<7)+(3<<24);m2|=(1<<7)+(1<<24);dj=wl_fgrid-1-dj;if(dj<0) dj=0;
+ } else mys=fy;
+ if(dk>=wl_hgrid) {
+ mzs=boxz-fz;
+ m1|=(127<<14)+(3<<27);m2|=(1<<14)+(1<<27);dk=wl_fgrid-1-dk;if(dk<0) dk=0;
+ } else mzs=fz;
+
+ // Do a quick test to account for the case when the minimum radius is
+ // small enought that no other blocks need to be considered
+ rs=con.r_max_add(mrs);
+ if(mxs*mxs>rs&&mys*mys>rs&&mzs*mzs>rs) return;
+
+ // Now compute which worklist we are going to use, and set radp and e to
+ // point at the right offsets
+ ijk=di+wl_hgrid*(dj+wl_hgrid*dk);
+ radp=mrad+ijk*wl_seq_length;
+ e=(const_cast<unsigned int*> (wl))+ijk*wl_seq_length;
+
+ // Read in how many items in the worklist can be tested without having to
+ // worry about writing to the mask
+ f=e[0];g=0;
+ do {
+
+ // If mrs is less than the minimum distance to any untested
+ // block, then we are done
+ if(con.r_max_add(mrs)<radp[g]) return;
+ g++;
+
+ // Load in a block off the worklist, permute it with the
+ // symmetry mask, and decode its position. These are all
+ // integer bit operations so they should run very fast.
+ q=e[g];q^=m1;q+=m2;
+ di=q&127;di-=64;
+ dj=(q>>7)&127;dj-=64;
+ dk=(q>>14)&127;dk-=64;
+
+ // Check that the worklist position is in range
+ ei=di+i;if(ei<0||ei>=hx) continue;
+ ej=dj+j;if(ej<0||ej>=hy) continue;
+ ek=dk+k;if(ek<0||ek>=hz) continue;
+
+ // Call the compute_min_max_radius() function. This returns
+ // true if the minimum distance to the block is bigger than the
+ // current mrs, in which case we skip this block and move on.
+ // Otherwise, it computes the maximum distance to the block and
+ // returns it in crs.
+ if(compute_min_radius(di,dj,dk,fx,fy,fz,mrs)) continue;
+
+ // Now compute which region we are going to loop over, adding a
+ // displacement for the periodic cases
+ ijk=con.region_index(ci,cj,ck,ei,ej,ek,qx,qy,qz,disp);
+
+ // If mrs is bigger than the maximum distance to the block,
+ // then we have to test all particles in the block for
+ // intersections. Otherwise, we do additional checks and skip
+ // those particles which can't possibly intersect the block.
+ scan_all(ijk,x-qx,y-qy,z-qz,di,dj,dk,w,mrs);
+ } while(g<f);
+
+ // Update mask value and initialize queue
+ mv++;
+ if(mv==0) {reset_mask();mv=1;}
+ int *qu_s=qu,*qu_e=qu;
+
+ while(g<wl_seq_length-1) {
+
+ // If mrs is less than the minimum distance to any untested
+ // block, then we are done
+ if(con.r_max_add(mrs)<radp[g]) return;
+ g++;
+
+ // Load in a block off the worklist, permute it with the
+ // symmetry mask, and decode its position. These are all
+ // integer bit operations so they should run very fast.
+ q=e[g];q^=m1;q+=m2;
+ di=q&127;di-=64;
+ dj=(q>>7)&127;dj-=64;
+ dk=(q>>14)&127;dk-=64;
+
+ // Compute the position in the mask of the current block. If
+ // this lies outside the mask, then skip it. Otherwise, mark
+ // it.
+ ei=di+i;if(ei<0||ei>=hx) continue;
+ ej=dj+j;if(ej<0||ej>=hy) continue;
+ ek=dk+k;if(ek<0||ek>=hz) continue;
+ mijk=mask+ei+hx*(ej+hy*ek);
+ *mijk=mv;
+
+ // Skip this block if it is further away than the current
+ // minimum radius
+ if(compute_min_radius(di,dj,dk,fx,fy,fz,mrs)) continue;
+
+ // Now compute which region we are going to loop over, adding a
+ // displacement for the periodic cases
+ ijk=con.region_index(ci,cj,ck,ei,ej,ek,qx,qy,qz,disp);
+ scan_all(ijk,x-qx,y-qy,z-qz,di,dj,dk,w,mrs);
+
+ if(qu_e>qu_l-18) add_list_memory(qu_s,qu_e);
+ scan_bits_mask_add(q,mijk,ei,ej,ek,qu_e);
+ }
+
+ // Do a check to see if we've reached the radius cutoff
+ if(con.r_max_add(mrs)<radp[g]) return;
+
+ // We were unable to completely compute the cell based on the blocks in
+ // the worklist, so now we have to go block by block, reading in items
+ // off the list
+ while(qu_s!=qu_e) {
+
+ // Read the next entry of the queue
+ if(qu_s==qu_l) qu_s=qu;
+ ei=*(qu_s++);ej=*(qu_s++);ek=*(qu_s++);
+ di=ei-i;dj=ej-j;dk=ek-k;
+ if(compute_min_radius(di,dj,dk,fx,fy,fz,mrs)) continue;
+
+ ijk=con.region_index(ci,cj,ck,ei,ej,ek,qx,qy,qz,disp);
+ scan_all(ijk,x-qx,y-qy,z-qz,di,dj,dk,w,mrs);
+
+ // Test the neighbors of the current block, and add them to the
+ // block list if they haven't already been tested
+ if((qu_s<=qu_e?(qu_l-qu_e)+(qu_s-qu):qu_s-qu_e)<18) add_list_memory(qu_s,qu_e);
+ add_to_mask(ei,ej,ek,qu_e);
+ }
+}
+
+/** Scans the six orthogonal neighbors of a given block and adds them to the
+ * queue if they haven't been considered already. It assumes that the queue
+ * will definitely have enough memory to add six entries at the end.
+ * \param[in] (ei,ej,ek) the block to consider.
+ * \param[in,out] qu_e a pointer to the end of the queue. */
+template<class c_class>
+inline void voro_compute<c_class>::add_to_mask(int ei,int ej,int ek,int *&qu_e) {
+ unsigned int *mijk=mask+ei+hx*(ej+hy*ek);
+ if(ek>0) if(*(mijk-hxy)!=mv) {if(qu_e==qu_l) qu_e=qu;*(mijk-hxy)=mv;*(qu_e++)=ei;*(qu_e++)=ej;*(qu_e++)=ek-1;}
+ if(ej>0) if(*(mijk-hx)!=mv) {if(qu_e==qu_l) qu_e=qu;*(mijk-hx)=mv;*(qu_e++)=ei;*(qu_e++)=ej-1;*(qu_e++)=ek;}
+ if(ei>0) if(*(mijk-1)!=mv) {if(qu_e==qu_l) qu_e=qu;*(mijk-1)=mv;*(qu_e++)=ei-1;*(qu_e++)=ej;*(qu_e++)=ek;}
+ if(ei<hx-1) if(*(mijk+1)!=mv) {if(qu_e==qu_l) qu_e=qu;*(mijk+1)=mv;*(qu_e++)=ei+1;*(qu_e++)=ej;*(qu_e++)=ek;}
+ if(ej<hy-1) if(*(mijk+hx)!=mv) {if(qu_e==qu_l) qu_e=qu;*(mijk+hx)=mv;*(qu_e++)=ei;*(qu_e++)=ej+1;*(qu_e++)=ek;}
+ if(ek<hz-1) if(*(mijk+hxy)!=mv) {if(qu_e==qu_l) qu_e=qu;*(mijk+hxy)=mv;*(qu_e++)=ei;*(qu_e++)=ej;*(qu_e++)=ek+1;}
+}
+
+/** Scans a worklist entry and adds any blocks to the queue
+ * \param[in] (ei,ej,ek) the block to consider.
+ * \param[in,out] qu_e a pointer to the end of the queue. */
+template<class c_class>
+inline void voro_compute<c_class>::scan_bits_mask_add(unsigned int q,unsigned int *mijk,int ei,int ej,int ek,int *&qu_e) {
+ const unsigned int b1=1<<21,b2=1<<22,b3=1<<24,b4=1<<25,b5=1<<27,b6=1<<28;
+ if((q&b2)==b2) {
+ if(ei>0) {*(mijk-1)=mv;*(qu_e++)=ei-1;*(qu_e++)=ej;*(qu_e++)=ek;}
+ if((q&b1)==0&&ei<hx-1) {*(mijk+1)=mv;*(qu_e++)=ei+1;*(qu_e++)=ej;*(qu_e++)=ek;}
+ } else if((q&b1)==b1&&ei<hx-1) {*(mijk+1)=mv;*(qu_e++)=ei+1;*(qu_e++)=ej;*(qu_e++)=ek;}
+ if((q&b4)==b4) {
+ if(ej>0) {*(mijk-hx)=mv;*(qu_e++)=ei;*(qu_e++)=ej-1;*(qu_e++)=ek;}
+ if((q&b3)==0&&ej<hy-1) {*(mijk+hx)=mv;*(qu_e++)=ei;*(qu_e++)=ej+1;*(qu_e++)=ek;}
+ } else if((q&b3)==b3&&ej<hy-1) {*(mijk+hx)=mv;*(qu_e++)=ei;*(qu_e++)=ej+1;*(qu_e++)=ek;}
+ if((q&b6)==b6) {
+ if(ek>0) {*(mijk-hxy)=mv;*(qu_e++)=ei;*(qu_e++)=ej;*(qu_e++)=ek-1;}
+ if((q&b5)==0&&ek<hz-1) {*(mijk+hxy)=mv;*(qu_e++)=ei;*(qu_e++)=ej;*(qu_e++)=ek+1;}
+ } else if((q&b5)==b5&&ek<hz-1) {*(mijk+hxy)=mv;*(qu_e++)=ei;*(qu_e++)=ej;*(qu_e++)=ek+1;}
+}
+
+/** This routine computes a Voronoi cell for a single particle in the
+ * container. It can be called by the user, but is also forms the core part of
+ * several of the main functions, such as store_cell_volumes(), print_all(),
+ * and the drawing routines. The algorithm constructs the cell by testing over
+ * the neighbors of the particle, working outwards until it reaches those
+ * particles which could not possibly intersect the cell. For maximum
+ * efficiency, this algorithm is divided into three parts. In the first
+ * section, the algorithm tests over the blocks which are in the immediate
+ * vicinity of the particle, by making use of one of the precomputed worklists.
+ * The code then continues to test blocks on the worklist, but also begins to
+ * construct a list of neighboring blocks outside the worklist which may need
+ * to be test. In the third section, the routine starts testing these
+ * neighboring blocks, evaluating whether or not a particle in them could
+ * possibly intersect the cell. For blocks that intersect the cell, it tests
+ * the particles in that block, and then adds the block neighbors to the list
+ * of potential places to consider.
+ * \param[in,out] c a reference to a voronoicell object.
+ * \param[in] ijk the index of the block that the test particle is in.
+ * \param[in] s the index of the particle within the test block.
+ * \param[in] (ci,cj,ck) the coordinates of the block that the test particle is
+ * in relative to the container data structure.
+ * \return False if the Voronoi cell was completely removed during the
+ * computation and has zero volume, true otherwise. */
+template<class c_class>
+template<class v_cell>
+bool voro_compute<c_class>::compute_cell(v_cell &c,int ijk,int s,int ci,int cj,int ck) {
+ static const int count_list[8]={7,11,15,19,26,35,45,59},*count_e=count_list+8;
+ double x,y,z,x1,y1,z1,qx=0,qy=0,qz=0;
+ double xlo,ylo,zlo,xhi,yhi,zhi,x2,y2,z2,rs;
+ int i,j,k,di,dj,dk,ei,ej,ek,f,g,l,disp;
+ double fx,fy,fz,gxs,gys,gzs,*radp;
+ unsigned int q,*e,*mijk;
+
+ if(!con.initialize_voronoicell(c,ijk,s,ci,cj,ck,i,j,k,x,y,z,disp)) return false;
+ con.r_init(ijk,s);
+
+ // Initialize the Voronoi cell to fill the entire container
+ double crs,mrs;
+
+ int next_count=3,*count_p=(const_cast<int*> (count_list));
+
+ // Test all particles in the particle's local region first
+ for(l=0;l<s;l++) {
+ x1=p[ijk][ps*l]-x;
+ y1=p[ijk][ps*l+1]-y;
+ z1=p[ijk][ps*l+2]-z;
+ rs=con.r_scale(x1*x1+y1*y1+z1*z1,ijk,l);
+ if(!c.nplane(x1,y1,z1,rs,id[ijk][l])) return false;
+ }
+ l++;
+ while(l<co[ijk]) {
+ x1=p[ijk][ps*l]-x;
+ y1=p[ijk][ps*l+1]-y;
+ z1=p[ijk][ps*l+2]-z;
+ rs=con.r_scale(x1*x1+y1*y1+z1*z1,ijk,l);
+ if(!c.nplane(x1,y1,z1,rs,id[ijk][l])) return false;
+ l++;
+ }
+
+ // Now compute the maximum distance squared from the cell center to a
+ // vertex. This is used to cut off the calculation since we only need
+ // to test out to twice this range.
+ mrs=c.max_radius_squared();
+
+ // Now compute the fractional position of the particle within its
+ // region and store it in (fx,fy,fz). We use this to compute an index
+ // (di,dj,dk) of which subregion the particle is within.
+ unsigned int m1,m2;
+ con.frac_pos(x,y,z,ci,cj,ck,fx,fy,fz);
+ di=int(fx*xsp*wl_fgrid);dj=int(fy*ysp*wl_fgrid);dk=int(fz*zsp*wl_fgrid);
+
+ // The indices (di,dj,dk) tell us which worklist to use, to test the
+ // blocks in the optimal order. But we only store worklists for the
+ // eighth of the region where di, dj, and dk are all less than half the
+ // full grid. The rest of the cases are handled by symmetry. In this
+ // section, we detect for these cases, by reflecting high values of di,
+ // dj, and dk. For these cases, a mask is constructed in m1 and m2
+ // which is used to flip the worklist information when it is loaded.
+ if(di>=wl_hgrid) {
+ gxs=fx;
+ m1=127+(3<<21);m2=1+(1<<21);di=wl_fgrid-1-di;if(di<0) di=0;
+ } else {m1=m2=0;gxs=boxx-fx;}
+ if(dj>=wl_hgrid) {
+ gys=fy;
+ m1|=(127<<7)+(3<<24);m2|=(1<<7)+(1<<24);dj=wl_fgrid-1-dj;if(dj<0) dj=0;
+ } else gys=boxy-fy;
+ if(dk>=wl_hgrid) {
+ gzs=fz;
+ m1|=(127<<14)+(3<<27);m2|=(1<<14)+(1<<27);dk=wl_fgrid-1-dk;if(dk<0) dk=0;
+ } else gzs=boxz-fz;
+ gxs*=gxs;gys*=gys;gzs*=gzs;
+
+ // Now compute which worklist we are going to use, and set radp and e to
+ // point at the right offsets
+ ijk=di+wl_hgrid*(dj+wl_hgrid*dk);
+ radp=mrad+ijk*wl_seq_length;
+ e=(const_cast<unsigned int*> (wl))+ijk*wl_seq_length;
+
+ // Read in how many items in the worklist can be tested without having to
+ // worry about writing to the mask
+ f=e[0];g=0;
+ do {
+
+ // At the intervals specified by count_list, we recompute the
+ // maximum radius squared
+ if(g==next_count) {
+ mrs=c.max_radius_squared();
+ if(count_p!=count_e) next_count=*(count_p++);
+ }
+
+ // If mrs is less than the minimum distance to any untested
+ // block, then we are done
+ if(con.r_ctest(radp[g],mrs)) return true;
+ g++;
+
+ // Load in a block off the worklist, permute it with the
+ // symmetry mask, and decode its position. These are all
+ // integer bit operations so they should run very fast.
+ q=e[g];q^=m1;q+=m2;
+ di=q&127;di-=64;
+ dj=(q>>7)&127;dj-=64;
+ dk=(q>>14)&127;dk-=64;
+
+ // Check that the worklist position is in range
+ ei=di+i;if(ei<0||ei>=hx) continue;
+ ej=dj+j;if(ej<0||ej>=hy) continue;
+ ek=dk+k;if(ek<0||ek>=hz) continue;
+
+ // Call the compute_min_max_radius() function. This returns
+ // true if the minimum distance to the block is bigger than the
+ // current mrs, in which case we skip this block and move on.
+ // Otherwise, it computes the maximum distance to the block and
+ // returns it in crs.
+ if(compute_min_max_radius(di,dj,dk,fx,fy,fz,gxs,gys,gzs,crs,mrs)) continue;
+
+ // Now compute which region we are going to loop over, adding a
+ // displacement for the periodic cases
+ ijk=con.region_index(ci,cj,ck,ei,ej,ek,qx,qy,qz,disp);
+
+ // If mrs is bigger than the maximum distance to the block,
+ // then we have to test all particles in the block for
+ // intersections. Otherwise, we do additional checks and skip
+ // those particles which can't possibly intersect the block.
+ if(co[ijk]>0) {
+ l=0;x2=x-qx;y2=y-qy;z2=z-qz;
+ if(!con.r_ctest(crs,mrs)) {
+ do {
+ x1=p[ijk][ps*l]-x2;
+ y1=p[ijk][ps*l+1]-y2;
+ z1=p[ijk][ps*l+2]-z2;
+ rs=con.r_scale(x1*x1+y1*y1+z1*z1,ijk,l);
+ if(!c.nplane(x1,y1,z1,rs,id[ijk][l])) return false;
+ l++;
+ } while (l<co[ijk]);
+ } else {
+ do {
+ x1=p[ijk][ps*l]-x2;
+ y1=p[ijk][ps*l+1]-y2;
+ z1=p[ijk][ps*l+2]-z2;
+ rs=x1*x1+y1*y1+z1*z1;
+ if(con.r_scale_check(rs,mrs,ijk,l)&&!c.nplane(x1,y1,z1,rs,id[ijk][l])) return false;
+ l++;
+ } while (l<co[ijk]);
+ }
+ }
+ } while(g<f);
+
+ // If we reach here, we were unable to compute the entire cell using
+ // the first part of the worklist. This section of the algorithm
+ // continues the worklist, but it now starts preparing the mask that we
+ // need if we end up going block by block. We do the same as before,
+ // but we put a mark down on the mask for every block that's tested.
+ // The worklist also contains information about which neighbors of each
+ // block are not also on the worklist, and we start storing those
+ // points in a list in case we have to go block by block. Update the
+ // mask counter, and if it wraps around then reset the whole mask; that
+ // will only happen once every 2^32 tries.
+ mv++;
+ if(mv==0) {reset_mask();mv=1;}
+
+ // Set the queue pointers
+ int *qu_s=qu,*qu_e=qu;
+
+ while(g<wl_seq_length-1) {
+
+ // At the intervals specified by count_list, we recompute the
+ // maximum radius squared
+ if(g==next_count) {
+ mrs=c.max_radius_squared();
+ if(count_p!=count_e) next_count=*(count_p++);
+ }
+
+ // If mrs is less than the minimum distance to any untested
+ // block, then we are done
+ if(con.r_ctest(radp[g],mrs)) return true;
+ g++;
+
+ // Load in a block off the worklist, permute it with the
+ // symmetry mask, and decode its position. These are all
+ // integer bit operations so they should run very fast.
+ q=e[g];q^=m1;q+=m2;
+ di=q&127;di-=64;
+ dj=(q>>7)&127;dj-=64;
+ dk=(q>>14)&127;dk-=64;
+
+ // Compute the position in the mask of the current block. If
+ // this lies outside the mask, then skip it. Otherwise, mark
+ // it.
+ ei=di+i;if(ei<0||ei>=hx) continue;
+ ej=dj+j;if(ej<0||ej>=hy) continue;
+ ek=dk+k;if(ek<0||ek>=hz) continue;
+ mijk=mask+ei+hx*(ej+hy*ek);
+ *mijk=mv;
+
+ // Call the compute_min_max_radius() function. This returns
+ // true if the minimum distance to the block is bigger than the
+ // current mrs, in which case we skip this block and move on.
+ // Otherwise, it computes the maximum distance to the block and
+ // returns it in crs.
+ if(compute_min_max_radius(di,dj,dk,fx,fy,fz,gxs,gys,gzs,crs,mrs)) continue;
+
+ // Now compute which region we are going to loop over, adding a
+ // displacement for the periodic cases
+ ijk=con.region_index(ci,cj,ck,ei,ej,ek,qx,qy,qz,disp);
+
+ // If mrs is bigger than the maximum distance to the block,
+ // then we have to test all particles in the block for
+ // intersections. Otherwise, we do additional checks and skip
+ // those particles which can't possibly intersect the block.
+ if(co[ijk]>0) {
+ l=0;x2=x-qx;y2=y-qy;z2=z-qz;
+ if(!con.r_ctest(crs,mrs)) {
+ do {
+ x1=p[ijk][ps*l]-x2;
+ y1=p[ijk][ps*l+1]-y2;
+ z1=p[ijk][ps*l+2]-z2;
+ rs=con.r_scale(x1*x1+y1*y1+z1*z1,ijk,l);
+ if(!c.nplane(x1,y1,z1,rs,id[ijk][l])) return false;
+ l++;
+ } while (l<co[ijk]);
+ } else {
+ do {
+ x1=p[ijk][ps*l]-x2;
+ y1=p[ijk][ps*l+1]-y2;
+ z1=p[ijk][ps*l+2]-z2;
+ rs=x1*x1+y1*y1+z1*z1;
+ if(con.r_scale_check(rs,mrs,ijk,l)&&!c.nplane(x1,y1,z1,rs,id[ijk][l])) return false;
+ l++;
+ } while (l<co[ijk]);
+ }
+ }
+
+ // If there might not be enough memory on the list for these
+ // additions, then add more
+ if(qu_e>qu_l-18) add_list_memory(qu_s,qu_e);
+
+ // Test the parts of the worklist element which tell us what
+ // neighbors of this block are not on the worklist. Store them
+ // on the block list, and mark the mask.
+ scan_bits_mask_add(q,mijk,ei,ej,ek,qu_e);
+ }
+
+ // Do a check to see if we've reached the radius cutoff
+ if(con.r_ctest(radp[g],mrs)) return true;
+
+ // We were unable to completely compute the cell based on the blocks in
+ // the worklist, so now we have to go block by block, reading in items
+ // off the list
+ while(qu_s!=qu_e) {
+
+ // If we reached the end of the list memory loop back to the
+ // start
+ if(qu_s==qu_l) qu_s=qu;
+
+ // Read in a block off the list, and compute the upper and lower
+ // coordinates in each of the three dimensions
+ ei=*(qu_s++);ej=*(qu_s++);ek=*(qu_s++);
+ xlo=(ei-i)*boxx-fx;xhi=xlo+boxx;
+ ylo=(ej-j)*boxy-fy;yhi=ylo+boxy;
+ zlo=(ek-k)*boxz-fz;zhi=zlo+boxz;
+
+ // Carry out plane tests to see if any particle in this block
+ // could possibly intersect the cell
+ if(ei>i) {
+ if(ej>j) {
+ if(ek>k) {if(corner_test(c,xlo,ylo,zlo,xhi,yhi,zhi)) continue;}
+ else if(ek<k) {if(corner_test(c,xlo,ylo,zhi,xhi,yhi,zlo)) continue;}
+ else {if(edge_z_test(c,xlo,ylo,zlo,xhi,yhi,zhi)) continue;}
+ } else if(ej<j) {
+ if(ek>k) {if(corner_test(c,xlo,yhi,zlo,xhi,ylo,zhi)) continue;}
+ else if(ek<k) {if(corner_test(c,xlo,yhi,zhi,xhi,ylo,zlo)) continue;}
+ else {if(edge_z_test(c,xlo,yhi,zlo,xhi,ylo,zhi)) continue;}
+ } else {
+ if(ek>k) {if(edge_y_test(c,xlo,ylo,zlo,xhi,yhi,zhi)) continue;}
+ else if(ek<k) {if(edge_y_test(c,xlo,ylo,zhi,xhi,yhi,zlo)) continue;}
+ else {if(face_x_test(c,xlo,ylo,zlo,yhi,zhi)) continue;}
+ }
+ } else if(ei<i) {
+ if(ej>j) {
+ if(ek>k) {if(corner_test(c,xhi,ylo,zlo,xlo,yhi,zhi)) continue;}
+ else if(ek<k) {if(corner_test(c,xhi,ylo,zhi,xlo,yhi,zlo)) continue;}
+ else {if(edge_z_test(c,xhi,ylo,zlo,xlo,yhi,zhi)) continue;}
+ } else if(ej<j) {
+ if(ek>k) {if(corner_test(c,xhi,yhi,zlo,xlo,ylo,zhi)) continue;}
+ else if(ek<k) {if(corner_test(c,xhi,yhi,zhi,xlo,ylo,zlo)) continue;}
+ else {if(edge_z_test(c,xhi,yhi,zlo,xlo,ylo,zhi)) continue;}
+ } else {
+ if(ek>k) {if(edge_y_test(c,xhi,ylo,zlo,xlo,yhi,zhi)) continue;}
+ else if(ek<k) {if(edge_y_test(c,xhi,ylo,zhi,xlo,yhi,zlo)) continue;}
+ else {if(face_x_test(c,xhi,ylo,zlo,yhi,zhi)) continue;}
+ }
+ } else {
+ if(ej>j) {
+ if(ek>k) {if(edge_x_test(c,xlo,ylo,zlo,xhi,yhi,zhi)) continue;}
+ else if(ek<k) {if(edge_x_test(c,xlo,ylo,zhi,xhi,yhi,zlo)) continue;}
+ else {if(face_y_test(c,xlo,ylo,zlo,xhi,zhi)) continue;}
+ } else if(ej<j) {
+ if(ek>k) {if(edge_x_test(c,xlo,yhi,zlo,xhi,ylo,zhi)) continue;}
+ else if(ek<k) {if(edge_x_test(c,xlo,yhi,zhi,xhi,ylo,zlo)) continue;}
+ else {if(face_y_test(c,xlo,yhi,zlo,xhi,zhi)) continue;}
+ } else {
+ if(ek>k) {if(face_z_test(c,xlo,ylo,zlo,xhi,yhi)) continue;}
+ else if(ek<k) {if(face_z_test(c,xlo,ylo,zhi,xhi,yhi)) continue;}
+ else voro_fatal_error("Compute cell routine revisiting central block, which should never\nhappen.",VOROPP_INTERNAL_ERROR);
+ }
+ }
+
+ // Now compute the region that we are going to test over, and
+ // set a displacement vector for the periodic cases
+ ijk=con.region_index(ci,cj,ck,ei,ej,ek,qx,qy,qz,disp);
+
+ // Loop over all the elements in the block to test for cuts. It
+ // would be possible to exclude some of these cases by testing
+ // against mrs, but this will probably not save time.
+ if(co[ijk]>0) {
+ l=0;x2=x-qx;y2=y-qy;z2=z-qz;
+ do {
+ x1=p[ijk][ps*l]-x2;
+ y1=p[ijk][ps*l+1]-y2;
+ z1=p[ijk][ps*l+2]-z2;
+ rs=con.r_scale(x1*x1+y1*y1+z1*z1,ijk,l);
+ if(!c.nplane(x1,y1,z1,rs,id[ijk][l])) return false;
+ l++;
+ } while (l<co[ijk]);
+ }
+
+ // If there's not much memory on the block list then add more
+ if((qu_s<=qu_e?(qu_l-qu_e)+(qu_s-qu):qu_s-qu_e)<18) add_list_memory(qu_s,qu_e);
+
+ // Test the neighbors of the current block, and add them to the
+ // block list if they haven't already been tested
+ add_to_mask(ei,ej,ek,qu_e);
+ }
+
+ return true;
+}
+
+/** This function checks to see whether a particular block can possibly have
+ * any intersection with a Voronoi cell, for the case when the closest point
+ * from the cell center to the block is at a corner.
+ * \param[in,out] c a reference to a Voronoi cell.
+ * \param[in] (xl,yl,zl) the relative coordinates of the corner of the block
+ * closest to the cell center.
+ * \param[in] (xh,yh,zh) the relative coordinates of the corner of the block
+ * furthest away from the cell center.
+ * \return False if the block may intersect, true if does not. */
+template<class c_class>
+template<class v_cell>
+bool voro_compute<c_class>::corner_test(v_cell &c,double xl,double yl,double zl,double xh,double yh,double zh) {
+ con.r_prime(xl*xl+yl*yl+zl*zl);
+ if(c.plane_intersects_guess(xh,yl,zl,con.r_cutoff(xl*xh+yl*yl+zl*zl))) return false;
+ if(c.plane_intersects(xh,yh,zl,con.r_cutoff(xl*xh+yl*yh+zl*zl))) return false;
+ if(c.plane_intersects(xl,yh,zl,con.r_cutoff(xl*xl+yl*yh+zl*zl))) return false;
+ if(c.plane_intersects(xl,yh,zh,con.r_cutoff(xl*xl+yl*yh+zl*zh))) return false;
+ if(c.plane_intersects(xl,yl,zh,con.r_cutoff(xl*xl+yl*yl+zl*zh))) return false;
+ if(c.plane_intersects(xh,yl,zh,con.r_cutoff(xl*xh+yl*yl+zl*zh))) return false;
+ return true;
+}
+
+/** This function checks to see whether a particular block can possibly have
+ * any intersection with a Voronoi cell, for the case when the closest point
+ * from the cell center to the block is on an edge which points along the x
+ * direction.
+ * \param[in,out] c a reference to a Voronoi cell.
+ * \param[in] (x0,x1) the minimum and maximum relative x coordinates of the
+ * block.
+ * \param[in] (yl,zl) the relative y and z coordinates of the corner of the
+ * block closest to the cell center.
+ * \param[in] (yh,zh) the relative y and z coordinates of the corner of the
+ * block furthest away from the cell center.
+ * \return False if the block may intersect, true if does not. */
+template<class c_class>
+template<class v_cell>
+inline bool voro_compute<c_class>::edge_x_test(v_cell &c,double x0,double yl,double zl,double x1,double yh,double zh) {
+ con.r_prime(yl*yl+zl*zl);
+ if(c.plane_intersects_guess(x0,yl,zh,con.r_cutoff(yl*yl+zl*zh))) return false;
+ if(c.plane_intersects(x1,yl,zh,con.r_cutoff(yl*yl+zl*zh))) return false;
+ if(c.plane_intersects(x1,yl,zl,con.r_cutoff(yl*yl+zl*zl))) return false;
+ if(c.plane_intersects(x0,yl,zl,con.r_cutoff(yl*yl+zl*zl))) return false;
+ if(c.plane_intersects(x0,yh,zl,con.r_cutoff(yl*yh+zl*zl))) return false;
+ if(c.plane_intersects(x1,yh,zl,con.r_cutoff(yl*yh+zl*zl))) return false;
+ return true;
+}
+
+/** This function checks to see whether a particular block can possibly have
+ * any intersection with a Voronoi cell, for the case when the closest point
+ * from the cell center to the block is on an edge which points along the y
+ * direction.
+ * \param[in,out] c a reference to a Voronoi cell.
+ * \param[in] (y0,y1) the minimum and maximum relative y coordinates of the
+ * block.
+ * \param[in] (xl,zl) the relative x and z coordinates of the corner of the
+ * block closest to the cell center.
+ * \param[in] (xh,zh) the relative x and z coordinates of the corner of the
+ * block furthest away from the cell center.
+ * \return False if the block may intersect, true if does not. */
+template<class c_class>
+template<class v_cell>
+inline bool voro_compute<c_class>::edge_y_test(v_cell &c,double xl,double y0,double zl,double xh,double y1,double zh) {
+ con.r_prime(xl*xl+zl*zl);
+ if(c.plane_intersects_guess(xl,y0,zh,con.r_cutoff(xl*xl+zl*zh))) return false;
+ if(c.plane_intersects(xl,y1,zh,con.r_cutoff(xl*xl+zl*zh))) return false;
+ if(c.plane_intersects(xl,y1,zl,con.r_cutoff(xl*xl+zl*zl))) return false;
+ if(c.plane_intersects(xl,y0,zl,con.r_cutoff(xl*xl+zl*zl))) return false;
+ if(c.plane_intersects(xh,y0,zl,con.r_cutoff(xl*xh+zl*zl))) return false;
+ if(c.plane_intersects(xh,y1,zl,con.r_cutoff(xl*xh+zl*zl))) return false;
+ return true;
+}
+
+/** This function checks to see whether a particular block can possibly have
+ * any intersection with a Voronoi cell, for the case when the closest point
+ * from the cell center to the block is on an edge which points along the z
+ * direction.
+ * \param[in,out] c a reference to a Voronoi cell.
+ * \param[in] (z0,z1) the minimum and maximum relative z coordinates of the block.
+ * \param[in] (xl,yl) the relative x and y coordinates of the corner of the
+ * block closest to the cell center.
+ * \param[in] (xh,yh) the relative x and y coordinates of the corner of the
+ * block furthest away from the cell center.
+ * \return False if the block may intersect, true if does not. */
+template<class c_class>
+template<class v_cell>
+inline bool voro_compute<c_class>::edge_z_test(v_cell &c,double xl,double yl,double z0,double xh,double yh,double z1) {
+ con.r_prime(xl*xl+yl*yl);
+ if(c.plane_intersects_guess(xl,yh,z0,con.r_cutoff(xl*xl+yl*yh))) return false;
+ if(c.plane_intersects(xl,yh,z1,con.r_cutoff(xl*xl+yl*yh))) return false;
+ if(c.plane_intersects(xl,yl,z1,con.r_cutoff(xl*xl+yl*yl))) return false;
+ if(c.plane_intersects(xl,yl,z0,con.r_cutoff(xl*xl+yl*yl))) return false;
+ if(c.plane_intersects(xh,yl,z0,con.r_cutoff(xl*xh+yl*yl))) return false;
+ if(c.plane_intersects(xh,yl,z1,con.r_cutoff(xl*xh+yl*yl))) return false;
+ return true;
+}
+
+/** This function checks to see whether a particular block can possibly have
+ * any intersection with a Voronoi cell, for the case when the closest point
+ * from the cell center to the block is on a face aligned with the x direction.
+ * \param[in,out] c a reference to a Voronoi cell.
+ * \param[in] xl the minimum distance from the cell center to the face.
+ * \param[in] (y0,y1) the minimum and maximum relative y coordinates of the
+ * block.
+ * \param[in] (z0,z1) the minimum and maximum relative z coordinates of the
+ * block.
+ * \return False if the block may intersect, true if does not. */
+template<class c_class>
+template<class v_cell>
+inline bool voro_compute<c_class>::face_x_test(v_cell &c,double xl,double y0,double z0,double y1,double z1) {
+ con.r_prime(xl*xl);
+ if(c.plane_intersects_guess(xl,y0,z0,con.r_cutoff(xl*xl))) return false;
+ if(c.plane_intersects(xl,y0,z1,con.r_cutoff(xl*xl))) return false;
+ if(c.plane_intersects(xl,y1,z1,con.r_cutoff(xl*xl))) return false;
+ if(c.plane_intersects(xl,y1,z0,con.r_cutoff(xl*xl))) return false;
+ return true;
+}
+
+/** This function checks to see whether a particular block can possibly have
+ * any intersection with a Voronoi cell, for the case when the closest point
+ * from the cell center to the block is on a face aligned with the y direction.
+ * \param[in,out] c a reference to a Voronoi cell.
+ * \param[in] yl the minimum distance from the cell center to the face.
+ * \param[in] (x0,x1) the minimum and maximum relative x coordinates of the
+ * block.
+ * \param[in] (z0,z1) the minimum and maximum relative z coordinates of the
+ * block.
+ * \return False if the block may intersect, true if does not. */
+template<class c_class>
+template<class v_cell>
+inline bool voro_compute<c_class>::face_y_test(v_cell &c,double x0,double yl,double z0,double x1,double z1) {
+ con.r_prime(yl*yl);
+ if(c.plane_intersects_guess(x0,yl,z0,con.r_cutoff(yl*yl))) return false;
+ if(c.plane_intersects(x0,yl,z1,con.r_cutoff(yl*yl))) return false;
+ if(c.plane_intersects(x1,yl,z1,con.r_cutoff(yl*yl))) return false;
+ if(c.plane_intersects(x1,yl,z0,con.r_cutoff(yl*yl))) return false;
+ return true;
+}
+
+/** This function checks to see whether a particular block can possibly have
+ * any intersection with a Voronoi cell, for the case when the closest point
+ * from the cell center to the block is on a face aligned with the z direction.
+ * \param[in,out] c a reference to a Voronoi cell.
+ * \param[in] zl the minimum distance from the cell center to the face.
+ * \param[in] (x0,x1) the minimum and maximum relative x coordinates of the
+ * block.
+ * \param[in] (y0,y1) the minimum and maximum relative y coordinates of the
+ * block.
+ * \return False if the block may intersect, true if does not. */
+template<class c_class>
+template<class v_cell>
+inline bool voro_compute<c_class>::face_z_test(v_cell &c,double x0,double y0,double zl,double x1,double y1) {
+ con.r_prime(zl*zl);
+ if(c.plane_intersects_guess(x0,y0,zl,con.r_cutoff(zl*zl))) return false;
+ if(c.plane_intersects(x0,y1,zl,con.r_cutoff(zl*zl))) return false;
+ if(c.plane_intersects(x1,y1,zl,con.r_cutoff(zl*zl))) return false;
+ if(c.plane_intersects(x1,y0,zl,con.r_cutoff(zl*zl))) return false;
+ return true;
+}
+
+
+/** This routine checks to see whether a point is within a particular distance
+ * of a nearby region. If the point is within the distance of the region, then
+ * the routine returns true, and computes the maximum distance from the point
+ * to the region. Otherwise, the routine returns false.
+ * \param[in] (di,dj,dk) the position of the nearby region to be tested,
+ * relative to the region that the point is in.
+ * \param[in] (fx,fy,fz) the displacement of the point within its region.
+ * \param[in] (gxs,gys,gzs) the maximum squared distances from the point to the
+ * sides of its region.
+ * \param[out] crs a reference in which to return the maximum distance to the
+ * region (only computed if the routine returns false).
+ * \param[in] mrs the distance to be tested.
+ * \return True if the region is further away than mrs, false if the region in
+ * within mrs. */
+template<class c_class>
+bool voro_compute<c_class>::compute_min_max_radius(int di,int dj,int dk,double fx,double fy,double fz,double gxs,double gys,double gzs,double &crs,double mrs) {
+ double xlo,ylo,zlo;
+ if(di>0) {
+ xlo=di*boxx-fx;
+ crs=xlo*xlo;
+ if(dj>0) {
+ ylo=dj*boxy-fy;
+ crs+=ylo*ylo;
+ if(dk>0) {
+ zlo=dk*boxz-fz;
+ crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true;
+ crs+=bxsq+2*(boxx*xlo+boxy*ylo+boxz*zlo);
+ } else if(dk<0) {
+ zlo=(dk+1)*boxz-fz;
+ crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true;
+ crs+=bxsq+2*(boxx*xlo+boxy*ylo-boxz*zlo);
+ } else {
+ if(con.r_ctest(crs,mrs)) return true;
+ crs+=boxx*(2*xlo+boxx)+boxy*(2*ylo+boxy)+gzs;
+ }
+ } else if(dj<0) {
+ ylo=(dj+1)*boxy-fy;
+ crs+=ylo*ylo;
+ if(dk>0) {
+ zlo=dk*boxz-fz;
+ crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true;
+ crs+=bxsq+2*(boxx*xlo-boxy*ylo+boxz*zlo);
+ } else if(dk<0) {
+ zlo=(dk+1)*boxz-fz;
+ crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true;
+ crs+=bxsq+2*(boxx*xlo-boxy*ylo-boxz*zlo);
+ } else {
+ if(con.r_ctest(crs,mrs)) return true;
+ crs+=boxx*(2*xlo+boxx)+boxy*(-2*ylo+boxy)+gzs;
+ }
+ } else {
+ if(dk>0) {
+ zlo=dk*boxz-fz;
+ crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true;
+ crs+=boxz*(2*zlo+boxz);
+ } else if(dk<0) {
+ zlo=(dk+1)*boxz-fz;
+ crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true;
+ crs+=boxz*(-2*zlo+boxz);
+ } else {
+ if(con.r_ctest(crs,mrs)) return true;
+ crs+=gzs;
+ }
+ crs+=gys+boxx*(2*xlo+boxx);
+ }
+ } else if(di<0) {
+ xlo=(di+1)*boxx-fx;
+ crs=xlo*xlo;
+ if(dj>0) {
+ ylo=dj*boxy-fy;
+ crs+=ylo*ylo;
+ if(dk>0) {
+ zlo=dk*boxz-fz;
+ crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true;
+ crs+=bxsq+2*(-boxx*xlo+boxy*ylo+boxz*zlo);
+ } else if(dk<0) {
+ zlo=(dk+1)*boxz-fz;
+ crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true;
+ crs+=bxsq+2*(-boxx*xlo+boxy*ylo-boxz*zlo);
+ } else {
+ if(con.r_ctest(crs,mrs)) return true;
+ crs+=boxx*(-2*xlo+boxx)+boxy*(2*ylo+boxy)+gzs;
+ }
+ } else if(dj<0) {
+ ylo=(dj+1)*boxy-fy;
+ crs+=ylo*ylo;
+ if(dk>0) {
+ zlo=dk*boxz-fz;
+ crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true;
+ crs+=bxsq+2*(-boxx*xlo-boxy*ylo+boxz*zlo);
+ } else if(dk<0) {
+ zlo=(dk+1)*boxz-fz;
+ crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true;
+ crs+=bxsq+2*(-boxx*xlo-boxy*ylo-boxz*zlo);
+ } else {
+ if(con.r_ctest(crs,mrs)) return true;
+ crs+=boxx*(-2*xlo+boxx)+boxy*(-2*ylo+boxy)+gzs;
+ }
+ } else {
+ if(dk>0) {
+ zlo=dk*boxz-fz;
+ crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true;
+ crs+=boxz*(2*zlo+boxz);
+ } else if(dk<0) {
+ zlo=(dk+1)*boxz-fz;
+ crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true;
+ crs+=boxz*(-2*zlo+boxz);
+ } else {
+ if(con.r_ctest(crs,mrs)) return true;
+ crs+=gzs;
+ }
+ crs+=gys+boxx*(-2*xlo+boxx);
+ }
+ } else {
+ if(dj>0) {
+ ylo=dj*boxy-fy;
+ crs=ylo*ylo;
+ if(dk>0) {
+ zlo=dk*boxz-fz;
+ crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true;
+ crs+=boxz*(2*zlo+boxz);
+ } else if(dk<0) {
+ zlo=(dk+1)*boxz-fz;
+ crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true;
+ crs+=boxz*(-2*zlo+boxz);
+ } else {
+ if(con.r_ctest(crs,mrs)) return true;
+ crs+=gzs;
+ }
+ crs+=boxy*(2*ylo+boxy);
+ } else if(dj<0) {
+ ylo=(dj+1)*boxy-fy;
+ crs=ylo*ylo;
+ if(dk>0) {
+ zlo=dk*boxz-fz;
+ crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true;
+ crs+=boxz*(2*zlo+boxz);
+ } else if(dk<0) {
+ zlo=(dk+1)*boxz-fz;
+ crs+=zlo*zlo;if(con.r_ctest(crs,mrs)) return true;
+ crs+=boxz*(-2*zlo+boxz);
+ } else {
+ if(con.r_ctest(crs,mrs)) return true;
+ crs+=gzs;
+ }
+ crs+=boxy*(-2*ylo+boxy);
+ } else {
+ if(dk>0) {
+ zlo=dk*boxz-fz;crs=zlo*zlo;if(con.r_ctest(crs,mrs)) return true;
+ crs+=boxz*(2*zlo+boxz);
+ } else if(dk<0) {
+ zlo=(dk+1)*boxz-fz;crs=zlo*zlo;if(con.r_ctest(crs,mrs)) return true;
+ crs+=boxz*(-2*zlo+boxz);
+ } else {
+ crs=0;
+ voro_fatal_error("Min/max radius function called for central block, which should never\nhappen.",VOROPP_INTERNAL_ERROR);
+ }
+ crs+=gys;
+ }
+ crs+=gxs;
+ }
+ return false;
+}
+
+template<class c_class>
+bool voro_compute<c_class>::compute_min_radius(int di,int dj,int dk,double fx,double fy,double fz,double mrs) {
+ double t,crs;
+
+ if(di>0) {t=di*boxx-fx;crs=t*t;}
+ else if(di<0) {t=(di+1)*boxx-fx;crs=t*t;}
+ else crs=0;
+
+ if(dj>0) {t=dj*boxy-fy;crs+=t*t;}
+ else if(dj<0) {t=(dj+1)*boxy-fy;crs+=t*t;}
+
+ if(dk>0) {t=dk*boxz-fz;crs+=t*t;}
+ else if(dk<0) {t=(dk+1)*boxz-fz;crs+=t*t;}
+
+ return crs>con.r_max_add(mrs);
+}
+
+/** Adds memory to the queue.
+ * \param[in,out] qu_s a reference to the queue start pointer.
+ * \param[in,out] qu_e a reference to the queue end pointer. */
+template<class c_class>
+inline void voro_compute<c_class>::add_list_memory(int*& qu_s,int*& qu_e) {
+ qu_size<<=1;
+ int *qu_n=new int[qu_size],*qu_c=qu_n;
+#if VOROPP_VERBOSE >=2
+ fprintf(stderr,"List memory scaled up to %d\n",qu_size);
+#endif
+ if(qu_s<=qu_e) {
+ while(qu_s<qu_e) *(qu_c++)=*(qu_s++);
+ } else {
+ while(qu_s<qu_l) *(qu_c++)=*(qu_s++);qu_s=qu;
+ while(qu_s<qu_e) *(qu_c++)=*(qu_s++);
+ }
+ delete [] qu;
+ qu_s=qu=qu_n;
+ qu_l=qu+qu_size;
+ qu_e=qu_c;
+}
+
+// Explicit template instantiation
+template voro_compute<container>::voro_compute(container&,int,int,int);
+template voro_compute<container_poly>::voro_compute(container_poly&,int,int,int);
+template bool voro_compute<container>::compute_cell(voronoicell&,int,int,int,int,int);
+template bool voro_compute<container>::compute_cell(voronoicell_neighbor&,int,int,int,int,int);
+template void voro_compute<container>::find_voronoi_cell(double,double,double,int,int,int,int,particle_record&,double&);
+template bool voro_compute<container_poly>::compute_cell(voronoicell&,int,int,int,int,int);
+template bool voro_compute<container_poly>::compute_cell(voronoicell_neighbor&,int,int,int,int,int);
+template void voro_compute<container_poly>::find_voronoi_cell(double,double,double,int,int,int,int,particle_record&,double&);
+
+// Explicit template instantiation
+template voro_compute<container_periodic>::voro_compute(container_periodic&,int,int,int);
+template voro_compute<container_periodic_poly>::voro_compute(container_periodic_poly&,int,int,int);
+template bool voro_compute<container_periodic>::compute_cell(voronoicell&,int,int,int,int,int);
+template bool voro_compute<container_periodic>::compute_cell(voronoicell_neighbor&,int,int,int,int,int);
+template void voro_compute<container_periodic>::find_voronoi_cell(double,double,double,int,int,int,int,particle_record&,double&);
+template bool voro_compute<container_periodic_poly>::compute_cell(voronoicell&,int,int,int,int,int);
+template bool voro_compute<container_periodic_poly>::compute_cell(voronoicell_neighbor&,int,int,int,int,int);
+template void voro_compute<container_periodic_poly>::find_voronoi_cell(double,double,double,int,int,int,int,particle_record&,double&);
+
+}
Index: extern/voro++/src/README
===================================================================
--- extern/voro++/src/README (revision 0)
+++ extern/voro++/src/README (revision 0)
@@ -0,0 +1,21 @@
+Voro++ library source files
+===========================
+This directory contains the source code for the library. For full details of
+each file, see the files section of the online reference manual at
+http://math.lbl.gov/voro++/doc/refman/
+
+Several other files are present:
+
+Makefile - the GNU Makefile controlling the compilation.
+
+Makefile.dep - a file containing all the dependencies of the source files,
+automatically generated by the GNU compiler.
+
+cmd_line.cc - source file for creating the command-line utility that makes use
+of the library.
+
+Doxyfile - configuration file for Doxygen, used to automatically generate
+documentation based on the source code comments.
+
+worklist_gen.pl - perl script for automatically generating the worklist.hh and
+v_base_wl.cc files.
Index: extern/voro++/src/c_interface.hh
===================================================================
--- extern/voro++/src/c_interface.hh (revision 0)
+++ extern/voro++/src/c_interface.hh (revision 0)
@@ -0,0 +1,19 @@
+typedef void container;
+typedef void particle_order;
+#include <stdio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ container* container_new(double ax_,double bx_,double ay_,double by_,double az_,double bz_,
+ int nx_,int ny_,int nz_,int xperiodic_,int yperiodic_,int zperiodic_,int init_mem);
+ particle_order* particle_order_new();
+
+ void container_put(container* container, particle_order* po, int n, double x, double y, double z);
+ void container_print_custom(container* container, const char* format, FILE* fp);
+
+#ifdef __cplusplus
+}
+#endif
+
Index: extern/voro++/src/config.hh
===================================================================
--- extern/voro++/src/config.hh (revision 0)
+++ extern/voro++/src/config.hh (revision 0)
@@ -0,0 +1,127 @@
+// Voro++, a 3D cell-based Voronoi library
+//
+// Author : Chris H. Rycroft (LBL / UC Berkeley)
+// Email : chr@alum.mit.edu
+// Date : August 30th 2011
+
+/** \file config.hh
+ * \brief Master configuration file for setting various compile-time options. */
+
+#ifndef VOROPP_CONFIG_HH
+#define VOROPP_CONFIG_HH
+
+namespace voro {
+
+// These constants set the initial memory allocation for the Voronoi cell
+/** The initial memory allocation for the number of vertices. */
+const int init_vertices=256;
+/** The initial memory allocation for the maximum vertex order. */
+const int init_vertex_order=64;
+/** The initial memory allocation for the number of regular vertices of order
+ * 3. */
+const int init_3_vertices=256;
+/** The initial memory allocation for the number of vertices of higher order.
+ */
+const int init_n_vertices=8;
+/** The initial buffer size for marginal cases used by the suretest class. */
+const int init_marginal=64;
+/** The initial size for the delete stack. */
+const int init_delete_size=256;
+/** The initial size for the auxiliary delete stack. */
+const int init_delete2_size=256;
+/** The initial size for the wall pointer array. */
+const int init_wall_size=32;
+/** The default initial size for the ordering class. */
+const int init_ordering_size=4096;
+/** The initial size of the pre_container chunk index. */
+const int init_chunk_size=256;
+
+// If the initial memory is too small, the program dynamically allocates more.
+// However, if the limits below are reached, then the program bails out.
+/** The maximum memory allocation for the number of vertices. */
+const int max_vertices=16777216;
+/** The maximum memory allocation for the maximum vertex order. */
+const int max_vertex_order=2048;
+/** The maximum memory allocation for the any particular order of vertex. */
+const int max_n_vertices=16777216;
+/** The maximum buffer size for marginal cases used by the suretest class. */
+const int max_marginal=16777216;
+/** The maximum size for the delete stack. */
+const int max_delete_size=16777216;
+/** The maximum size for the auxiliary delete stack. */
+const int max_delete2_size=16777216;
+/** The maximum amount of particle memory allocated for a single region. */
+const int max_particle_memory=16777216;
+/** The maximum size for the wall pointer array. */
+const int max_wall_size=2048;
+/** The maximum size for the ordering class. */
+const int max_ordering_size=67108864;
+/** The maximum size for the pre_container chunk index. */
+const int max_chunk_size=65536;
+
+/** The chunk size in the pre_container classes. */
+const int pre_container_chunk_size=1024;
+
+#ifndef VOROPP_VERBOSE
+/** Voro++ can print a number of different status and debugging messages to
+ * notify the user of special behavior, and this macro sets the amount which
+ * are displayed. At level 0, no messages are printed. At level 1, messages
+ * about unusual cases during cell construction are printed, such as when the
+ * plane routine bails out due to floating point problems. At level 2, general
+ * messages about memory expansion are printed. At level 3, technical details
+ * about memory management are printed. */
+#define VOROPP_VERBOSE 0
+#endif
+
+/** If a point is within this distance of a cutting plane, then the code
+ * assumes that point exactly lies on the plane. */
+const double tolerance=1e-11;
+
+/** If a point is within this distance of a cutting plane, then the code stores
+ * whether this point is inside, outside, or exactly on the cutting plane in
+ * the marginal cases buffer, to prevent the test giving a different result on
+ * a subsequent evaluation due to floating point rounding errors. */
+const double tolerance2=2e-11;
+
+/** The square of the tolerance, used when deciding whether some squared
+ * quantities are large enough to be used. */
+const double tolerance_sq=tolerance*tolerance;
+
+/** A large number that is used in the computation. */
+const double large_number=1e30;
+
+/** A radius to use as a placeholder when no other information is available. */
+const double default_radius=0.5;
+
+/** The maximum number of shells of periodic images to test over. */
+const int max_unit_voro_shells=10;
+
+/** A guess for the optimal number of particles per block, used to set up the
+ * container grid. */
+const double optimal_particles=5.6;
+
+/** If this is set to 1, then the code reports any instances of particles being
+ * put outside of the container geometry. */
+#define VOROPP_REPORT_OUT_OF_BOUNDS 0
+
+/** Voro++ returns this status code if there is a file-related error, such as
+ * not being able to open file. */
+#define VOROPP_FILE_ERROR 1
+
+/** Voro++ returns this status code if there is a memory allocation error, if
+ * one of the safe memory limits is exceeded. */
+#define VOROPP_MEMORY_ERROR 2
+
+/** Voro++ returns this status code if there is any type of internal error, if
+ * it detects that representation of the Voronoi cell is inconsistent. This
+ * status code will generally indicate a bug, and the developer should be
+ * contacted. */
+#define VOROPP_INTERNAL_ERROR 3
+
+/** Voro++ returns this status code if it could not interpret the command line
+ * arguments passed to the command line utility. */
+#define VOROPP_CMD_LINE_ERROR 4
+
+}
+
+#endif
Index: extern/voro++/src/voro++.hh
===================================================================
--- extern/voro++/src/voro++.hh (revision 0)
+++ extern/voro++/src/voro++.hh (revision 0)
@@ -0,0 +1,333 @@
+// Voro++, a 3D cell-based Voronoi library
+//
+// Author : Chris H. Rycroft (LBL / UC Berkeley)
+// Email : chr@alum.mit.edu
+// Date : August 30th 2011
+
+/** \file voro++.hh
+ * \brief A file that loads all of the Voro++ header files. */
+
+/** \mainpage Voro++ class reference manual
+ * \section intro Introduction
+ * Voro++ is a software library for carrying out three-dimensional computations
+ * of the Voronoi tessellation. A distinguishing feature of the Voro++ library
+ * is that it carries out cell-based calculations, computing the Voronoi cell
+ * for each particle individually, rather than computing the Voronoi
+ * tessellation as a global network of vertices and edges. It is particularly
+ * well-suited for applications that rely on cell-based statistics, where
+ * features of Voronoi cells (eg. volume, centroid, number of faces) can be
+ * used to analyze a system of particles.
+ *
+ * Voro++ is written in C++ and can be built as a static library that can be
+ * linked to. This manual provides a reference for every function in the class
+ * structure. For a general overview of the program, see the Voro++ website at
+ * http://math.lbl.gov/voro++/ and in particular the example programs at
+ * http://math.lbl.gov/voro++/examples/ that demonstrate many of the library's
+ * features.
+ *
+ * \section class C++ class structure
+ * The code is structured around several C++ classes. The voronoicell_base
+ * class contains all of the routines for constructing a single Voronoi cell.
+ * It represents the cell as a collection of vertices that are connected by
+ * edges, and there are routines for initializing, making, and outputting the
+ * cell. The voronoicell_base class form the base of the voronoicell and
+ * voronoicell_neighbor classes, which add specialized routines depending on
+ * whether neighboring particle ID information for each face must be tracked or
+ * not. Collectively, these classes are referred to as "voronoicell classes"
+ * within the documentation.
+ *
+ * There is a hierarchy of classes that represent three-dimensional particle
+ * systems. All of these are derived from the voro_base class, which contains
+ * constants that divide a three-dimensional system into a rectangular grid of
+ * equally-sized rectangular blocks; this grid is used for computational
+ * efficiency during the Voronoi calculations.
+ *
+ * The container_base, container, and container_poly are then derived from the
+ * voro_base class to represent a particle system in a specific
+ * three-dimensional rectangular box using both periodic and non-periodic
+ * boundary conditions. In addition, the container_periodic_base,
+ * container_periodic, and container_periodic_poly classes represent
+ * a particle system in a three-dimensional non-orthogonal periodic domain,
+ * defined by three periodicity vectors that represent a parallelepiped.
+ * Collectively, these classes are referred to as "container classes" within
+ * the documentation.
+ *
+ * The voro_compute template encapsulates all of the routines for computing
+ * Voronoi cells. Each container class has a voro_compute template within
+ * it, that accesses the container's particle system, and computes the Voronoi
+ * cells.
+ *
+ * There are several wall classes that can be used to apply certain boundary
+ * conditions using additional plane cuts during the Voronoi cell compution.
+ * The code also contains a number of small loop classes, c_loop_all,
+ * c_loop_subset, c_loop_all_periodic, and c_loop_order that can be used to
+ * iterate over a certain subset of particles in a container. The latter class
+ * makes use of a special particle_order class that stores a specific order of
+ * particles within the container. The library also contains the classes
+ * pre_container_base, pre_container, and pre_container_poly, that can be used
+ * as temporary storage when importing data of unknown size.
+ *
+ * \section voronoicell The voronoicell classes
+ * The voronoicell class represents a single Voronoi cell as a convex
+ * polyhedron, with a set of vertices that are connected by edges. The class
+ * contains a variety of functions that can be used to compute and output the
+ * Voronoi cell corresponding to a particular particle. The command init()
+ * can be used to initialize a cell as a large rectangular box. The Voronoi cell
+ * can then be computed by repeatedly cutting it with planes that correspond to
+ * the perpendicular bisectors between that particle and its neighbors.
+ *
+ * This is achieved by using the plane() routine, which will recompute the
+ * cell's vertices and edges after cutting it with a single plane. This is the
+ * key routine in voronoicell class. It begins by exploiting the convexity
+ * of the underlying cell, tracing between edges to work out if the cell
+ * intersects the cutting plane. If it does not intersect, then the routine
+ * immediately exits. Otherwise, it finds an edge or vertex that intersects
+ * the plane, and from there, traces out a new face on the cell, recomputing
+ * the edge and vertex structure accordingly.
+ *
+ * Once the cell is computed, there are many routines for computing features of
+ * the the Voronoi cell, such as its volume, surface area, or centroid. There
+ * are also many routines for outputting features of the Voronoi cell, or
+ * writing its shape in formats that can be read by Gnuplot or POV-Ray.
+ *
+ * \subsection internal Internal data representation
+ * The voronoicell class has a public member p representing the
+ * number of vertices. The polyhedral structure of the cell is stored
+ * in the following arrays:
+ *
+ * - pts: a one-dimensional array of floating point numbers, that represent the
+ * position vectors x_0, x_1, ..., x_{p-1} of the polyhedron vertices.
+ * - nu: the order of each vertex n_0, n_1, ..., n_{p-1}, corresponding to
+ * the number of other vertices to which each is connected.
+ * - ed: a two-dimensional table of edges and relations. For the ith vertex,
+ * ed[i] has 2n_i+1 elements. The first n_i elements are the edges e(j,i),
+ * where e(j,i) is the jth neighbor of vertex i. The edges are ordered
+ * according to a right-hand rule with respect to an outward-pointing normal.
+ * The next n_i elements are the relations l(j,i) which satisfy the property
+ * e(l(j,i),e(j,i)) = i. The final element of the ed[i] list is a back
+ * pointer used in memory allocation.
+ *
+ * In a very large number of cases, the values of n_i will be 3. This is because
+ * the only way that a higher-order vertex can be created in the plane()
+ * routine is if the cutting plane perfectly intersects an existing vertex. For
+ * random particle arrangements with position vectors specified to double
+ * precision this should happen very rarely. A preliminary version of this code
+ * was quite successful with only making use of vertices of order 3. However,
+ * when calculating millions of cells, it was found that this approach is not
+ * robust, since a single floating point error can invalidate the computation.
+ * This can also be a problem for cases featuring crystalline arrangements of
+ * particles where the corresponding Voronoi cells may have high-order vertices
+ * by construction.
+ *
+ * Because of this, Voro++ takes the approach that it if an existing vertex is
+ * within a small numerical tolerance of the cutting plane, it is treated as
+ * being exactly on the plane, and the polyhedral topology is recomputed
+ * accordingly. However, while this improves robustness, it also adds the
+ * complexity that n_i may no longer always be 3. This causes memory management
+ * to be significantly more complicated, as different vertices require a
+ * different number of elements in the ed[][] array. To accommodate this, the
+ * voronoicell class allocated edge memory in a different array called mep[][],
+ * in such a way that all vertices of order k are held in mep[k]. If vertex
+ * i has order k, then ed[i] points to memory within mep[k]. The array ed[][]
+ * is never directly initialized as a two-dimensional array itself, but points
+ * at allocations within mep[][]. To the user, it appears as though each row of
+ * ed[][] has a different number of elements. When vertices are added or
+ * deleted, care must be taken to reorder and reassign elements in these
+ * arrays.
+ *
+ * During the plane() routine, the code traces around the vertices of the cell,
+ * and adds new vertices along edges which intersect the cutting plane to
+ * create a new face. The values of l(j,i) are used in this computation, as
+ * when the code is traversing from one vertex on the cell to another, this
+ * information allows the code to immediately work out which edge of a vertex
+ * points back to the one it came from. As new vertices are created, the l(j,i)
+ * are also updated to ensure consistency. To ensure robustness, the plane
+ * cutting algorithm should work with any possible combination of vertices
+ * which are inside, outside, or exactly on the cutting plane.
+ *
+ * Vertices exactly on the cutting plane create some additional computational
+ * difficulties. If there are two marginal vertices connected by an existing
+ * edge, then it would be possible for duplicate edges to be created between
+ * those two vertices, if the plane routine traces along both sides of this
+ * edge while constructing the new face. The code recognizes these cases and
+ * prevents the double edge from being formed. Another possibility is the
+ * formation of vertices of order two or one. At the end of the plane cutting
+ * routine, the code checks to see if any of these are present, removing the
+ * order one vertices by just deleting them, and removing the order two
+ * vertices by connecting the two neighbors of each vertex together. It is
+ * possible that the removal of a single low-order vertex could result in the
+ * creation of additional low-order vertices, so the process is applied
+ * recursively until no more are left.
+ *
+ * \section container The container classes
+ * There are four container classes available for general usage: container,
+ * container_poly, container_periodic, and container_periodic_poly. Each of
+ * these represent a system of particles in a specific three-dimensional
+ * geometry. They contain routines for importing particles from a text file,
+ * and adding particles individually. They also contain a large number of
+ * analyzing and outputting the particle system. Internally, the routines that
+ * compute Voronoi cells do so by making use of the voro_compute template.
+ * Each container class contains routines that tell the voro_compute template
+ * about the specific geometry of this container.
+ *
+ * \section voro_compute The voro_compute template
+ * The voro_compute template encapsulates the routines for carrying out the
+ * Voronoi cell computations. It contains data structures suchs as a mask and a
+ * queue that are used in the computations. The voro_compute template is
+ * associated with a specific container class, and during the computation, it
+ * calls routines in the container class to access the particle positions that
+ * are stored there.
+ *
+ * The key routine in this class is compute_cell(), which makes use of a
+ * voronoicell class to construct a Voronoi cell for a specific particle in the
+ * container. The basic approach that this function takes is to repeatedly cut
+ * the Voronoi cell by planes corresponding neighboring particles, and stop
+ * when it recognizes that all the remaining particles in the container are too
+ * far away to possibly influence cell's shape. The code makes use of two
+ * possible methods for working out when a cell computation is complete:
+ *
+ * - Radius test: if the maximum distance of a Voronoi cell
+ * vertex from the cell center is R, then no particles more than a distance
+ * 2R away can possibly influence the cell. This a very fast computation to
+ * do, but it has no directionality: if the cell extends a long way in one
+ * direction then particles a long distance in other directions will still
+ * need to be tested.
+ * - Region test: it is possible to test whether a specific region can
+ * possibly influence the cell by applying a series of plane tests at the
+ * point on the region which is closest to the Voronoi cell center. This is a
+ * slower computation to do, but it has directionality.
+ *
+ * Another useful observation is that the regions that need to be tested are
+ * simply connected, meaning that if a particular region does not need to be
+ * tested, then neighboring regions which are further away do not need to be
+ * tested.
+ *
+ * For maximum efficiency, it was found that a hybrid approach making use of
+ * both of the above tests worked well in practice. Radius tests work well for
+ * the first few blocks, but switching to region tests after then prevent the
+ * code from becoming extremely slow, due to testing over very large spherical
+ * shells of particles. The compute_cell() routine therefore takes the
+ * following approach:
+ *
+ * - Initialize the voronoicell class to fill the entire computational domain.
+ * - Cut the cell by any wall objects that have been added to the container.
+ * - Apply plane cuts to the cell corresponding to the other particles which
+ * are within the current particle's region.
+ * - Test over a pre-computed worklist of neighboring regions, that have been
+ * ordered according to the minimum distance away from the particle's
+ * position. Apply radius tests after every few regions to see if the
+ * calculation can terminate.
+ * - If the code reaches the end of the worklist, add all the neighboring
+ * regions to a new list.
+ * - Carry out a region test on the first item of the list. If the region needs
+ * to be tested, apply the plane() routine for all of its particles, and then
+ * add any neighboring regions to the end of the list that need to be tested.
+ * Continue until the list has no elements left.
+ *
+ * The compute_cell() routine forms the basis of many other routines, such as
+ * store_cell_volumes() and draw_cells_gnuplot() that can be used to calculate
+ * and draw the cells in a container.
+ *
+ * \section walls Wall computation
+ * Wall computations are handled by making use of a pure virtual wall class.
+ * Specific wall types are derived from this class, and require the
+ * specification of two routines: point_inside() that tests to see if a point
+ * is inside a wall or not, and cut_cell() that cuts a cell according to the
+ * wall's position. The walls can be added to the container using the
+ * add_wall() command, and these are called each time a compute_cell() command
+ * is carried out. At present, wall types for planes, spheres, cylinders, and
+ * cones are provided, although custom walls can be added by creating new
+ * classes derived from the pure virtual class. Currently all wall types
+ * approximate the wall surface with a single plane, which produces some small
+ * errors, but generally gives good results for dense particle packings in
+ * direct contact with a wall surface. It would be possible to create more
+ * accurate walls by making cut_cell() routines that approximate the curved
+ * surface with multiple plane cuts.
+ *
+ * The wall objects can used for periodic calculations, although to obtain
+ * valid results, the walls should also be periodic as well. For example, in a
+ * domain that is periodic in the x direction, a cylinder aligned along the x
+ * axis could be added. At present, the interior of all wall objects are convex
+ * domains, and consequently any superposition of them will be a convex domain
+ * also. Carrying out computations in non-convex domains poses some problems,
+ * since this could theoretically lead to non-convex Voronoi cells, which the
+ * internal data representation of the voronoicell class does not support. For
+ * non-convex cases where the wall surfaces feature just a small amount of
+ * negative curvature (eg. a torus) approximating the curved surface with a
+ * single plane cut may give an acceptable level of accuracy. For non-convex
+ * cases that feature internal angles, the best strategy may be to decompose
+ * the domain into several convex subdomains, carry out a calculation in each,
+ * and then add the results together. The voronoicell class cannot be easily
+ * modified to handle non-convex cells as this would fundamentally alter the
+ * algorithms that it uses, and cases could arise where a single plane cut
+ * could create several new faces as opposed to just one.
+ *
+ * \section loops Loop classes
+ * The container classes have a number of simple routines for calculating
+ * Voronoi cells for all particles within them. However, in some situations it
+ * is desirable to iterate over a specific subset of particles. This can be
+ * achieved with the c_loop classes that are all derived from the c_loop_base
+ * class. Each class can iterate over a specific subset of particles in a
+ * container. There are three loop classes for use with the container and
+ * container_poly classes:
+ *
+ * - c_loop_all will loop over all of the particles in a container.
+ * - c_loop_subset will loop over a subset of particles in a container that lie
+ * within some geometrical region. It can loop over particles in a
+ * rectangular box, particles in a sphere, or particles that lie within
+ * specific internal computational blocks.
+ * - c_loop_order will loop over a specific list of particles that were
+ * previously stored in a particle_order class.
+ *
+ * Several of the key routines within the container classes (such as
+ * draw_cells_gnuplot and print_custom) have versions where they can be passed
+ * a loop class to use. Loop classes can also be used directly and there are
+ * some examples on the library website that demonstrate this. It is also
+ * possible to write custom loop classes.
+ *
+ * In addition to the loop classes mentioned above, there is also a
+ * c_loop_all_periodic class, that is specifically for use with the
+ * container_periodic and container_periodic_poly classes. Since the data
+ * structures of these containers differ considerably, it requires a different
+ * loop class that is not interoperable with the others.
+ *
+ * \section pre_container The pre_container classes
+ * Voro++ makes use of internal computational grid of blocks that are used to
+ * configure the code for maximum efficiency. As discussed on the library
+ * website, the best performance is achieved for around 5 particles per block,
+ * with anything in the range from 3 to 12 giving good performance. Usually
+ * the size of the grid can be chosen by ensuring that the number of blocks is
+ * equal to the number of particles divided by 5.
+ *
+ * However, this can be difficult to choose in cases when the number of
+ * particles is not known a priori, and in thes cases the pre_container classes
+ * can be used. They can import an arbitrary number of particle positions from
+ * a file, dynamically allocating memory in chunks as necessary. Once particles
+ * are imported, they can guess an optimal block arrangement to use for the
+ * container class, and then transfer the particles to the container. By
+ * default, this procedure is used by the command-line utility to enable it to
+ * work well with arbitrary sizes of input data.
+ *
+ * The pre_container class can be used when no particle radius information is
+ * available, and the pre_container_poly class can be used when radius
+ * information is available. At present, the pre_container classes can only be
+ * used with the container and container_poly classes. They do not support
+ * the container_periodic and container_periodic_poly classes. */
+
+#ifndef VOROPP_HH
+#define VOROPP_HH
+
+#include "config.hh"
+#include "common.hh"
+#include "cell.hh"
+#include "v_base.hh"
+#include "rad_option.hh"
+#include "container.hh"
+#include "unitcell.hh"
+#include "container_prd.hh"
+#include "pre_container.hh"
+#include "v_compute.hh"
+#include "c_loops.hh"
+#include "wall.hh"
+
+#endif
Index: extern/voro++/src/container_prd.cc
===================================================================
--- extern/voro++/src/container_prd.cc (revision 0)
+++ extern/voro++/src/container_prd.cc (revision 0)
@@ -0,0 +1,768 @@
+// Voro++, a 3D cell-based Voronoi library
+//
+// Author : Chris H. Rycroft (LBL / UC Berkeley)
+// Email : chr@alum.mit.edu
+// Date : August 30th 2011
+
+/** \file container_prd.cc
+ * \brief Function implementations for the container_periodic_base and
+ * related classes. */
+
+#include "container_prd.hh"
+
+namespace voro {
+
+/** The class constructor sets up the geometry of container, initializing the
+ * minimum and maximum coordinates in each direction, and setting whether each
+ * direction is periodic or not. It divides the container into a rectangular
+ * grid of blocks, and allocates memory for each of these for storing particle
+ * positions and IDs.
+ * \param[in] (bx_) The x coordinate of the first unit vector.
+ * \param[in] (bxy_,by_) The x and y coordinates of the second unit vector.
+ * \param[in] (bxz_,byz_,bz_) The x, y, and z coordinates of the third unit
+ * vector.
+ * \param[in] (nx_,ny_,nz_) the number of grid blocks in each of the three
+ * coordinate directions.
+ * \param[in] init_mem_ the initial memory allocation for each block.
+ * \param[in] ps_ the number of floating point entries to store for each
+ * particle. */
+container_periodic_base::container_periodic_base(double bx_,double bxy_,double by_,
+ double bxz_,double byz_,double bz_,int nx_,int ny_,int nz_,int init_mem_,int ps_)
+ : unitcell(bx_,bxy_,by_,bxz_,byz_,bz_), voro_base(nx_,ny_,nz_,bx_/nx_,by_/ny_,bz_/nz_),
+ ey(int(max_uv_y*ysp+1)), ez(int(max_uv_z*zsp+1)), wy(ny+ey), wz(nz+ez),
+ oy(ny+2*ey), oz(nz+2*ez), oxyz(nx*oy*oz), id(new int*[oxyz]), p(new double*[oxyz]),
+ co(new int[oxyz]), mem(new int[oxyz]), img(new char[oxyz]), init_mem(init_mem_), ps(ps_) {
+ int i,j,k,l;
+
+ // Clear the global arrays
+ int *pp=co;while(pp<co+oxyz) *(pp++)=0;
+ pp=mem;while(pp<mem+oxyz) *(pp++)=0;
+ char *cp=img;while(cp<img+oxyz) *(cp++)=0;
+
+ // Set up memory for the blocks in the primary domain
+ for(k=ez;k<wz;k++) for(j=ey;j<wy;j++) for(i=0;i<nx;i++) {
+ l=i+nx*(j+oy*k);
+ mem[l]=init_mem;
+ id[l]=new int[init_mem];
+ p[l]=new double[ps*init_mem];
+ }
+}
+
+/** The container destructor frees the dynamically allocated memory. */
+container_periodic_base::~container_periodic_base() {
+ for(int l=oxyz-1;l>=0;l--) if(mem[l]>0) {
+ delete [] p[l];
+ delete [] id[l];
+ }
+ delete [] img;
+ delete [] mem;
+ delete [] co;
+ delete [] id;
+ delete [] p;
+}
+
+/** The class constructor sets up the geometry of container.
+ * \param[in] (bx_) The x coordinate of the first unit vector.
+ * \param[in] (bxy_,by_) The x and y coordinates of the second unit vector.
+ * \param[in] (bxz_,byz_,bz_) The x, y, and z coordinates of the third unit
+ * vector.
+ * \param[in] (nx_,ny_,nz_) the number of grid blocks in each of the three
+ * coordinate directions.
+ * \param[in] init_mem_ the initial memory allocation for each block. */
+container_periodic::container_periodic(double bx_,double bxy_,double by_,double bxz_,double byz_,double bz_,
+ int nx_,int ny_,int nz_,int init_mem_)
+ : container_periodic_base(bx_,bxy_,by_,bxz_,byz_,bz_,nx_,ny_,nz_,init_mem_,3),
+ vc(*this,2*nx_+1,2*ey+1,2*ez+1) {}
+
+/** The class constructor sets up the geometry of container.
+ * \param[in] (bx_) The x coordinate of the first unit vector.
+ * \param[in] (bxy_,by_) The x and y coordinates of the second unit vector.
+ * \param[in] (bxz_,byz_,bz_) The x, y, and z coordinates of the third unit
+ * vector.
+ * \param[in] (nx_,ny_,nz_) the number of grid blocks in each of the three
+ * coordinate directions.
+ * \param[in] init_mem_ the initial memory allocation for each block. */
+container_periodic_poly::container_periodic_poly(double bx_,double bxy_,double by_,double bxz_,double byz_,double bz_,
+ int nx_,int ny_,int nz_,int init_mem_)
+ : container_periodic_base(bx_,bxy_,by_,bxz_,byz_,bz_,nx_,ny_,nz_,init_mem_,4),
+ vc(*this,2*nx_+1,2*ey+1,2*ez+1) {ppr=p;}
+
+/** Put a particle into the correct region of the container.
+ * \param[in] n the numerical ID of the inserted particle.
+ * \param[in] (x,y,z) the position vector of the inserted particle. */
+void container_periodic::put(int n,double x,double y,double z) {
+ int ijk;
+ put_locate_block(ijk,x,y,z);
+ id[ijk][co[ijk]]=n;
+ double *pp=p[ijk]+3*co[ijk]++;
+ *(pp++)=x;*(pp++)=y;*pp=z;
+}
+
+/** Put a particle into the correct region of the container.
+ * \param[in] n the numerical ID of the inserted particle.
+ * \param[in] (x,y,z) the position vector of the inserted particle.
+ * \param[in] r the radius of the particle. */
+void container_periodic_poly::put(int n,double x,double y,double z,double r) {
+ int ijk;
+ put_locate_block(ijk,x,y,z);
+ id[ijk][co[ijk]]=n;
+ double *pp=p[ijk]+4*co[ijk]++;
+ *(pp++)=x;*(pp++)=y;*(pp++)=z;*pp=r;
+ if(max_radius<r) max_radius=r;
+}
+
+/** Put a particle into the correct region of the container.
+ * \param[in] n the numerical ID of the inserted particle.
+ * \param[in] (x,y,z) the position vector of the inserted particle.
+ * \param[out] (ai,aj,ak) the periodic image displacement that the particle is
+ * in, with (0,0,0) corresponding to the primary domain.
+ */
+void container_periodic::put(int n,double x,double y,double z,int &ai,int &aj,int &ak) {
+ int ijk;
+ put_locate_block(ijk,x,y,z,ai,aj,ak);
+ id[ijk][co[ijk]]=n;
+ double *pp=p[ijk]+3*co[ijk]++;
+ *(pp++)=x;*(pp++)=y;*pp=z;
+}
+
+/** Put a particle into the correct region of the container.
+ * \param[in] n the numerical ID of the inserted particle.
+ * \param[in] (x,y,z) the position vector of the inserted particle.
+ * \param[in] r the radius of the particle.
+ * \param[out] (ai,aj,ak) the periodic image displacement that the particle is
+ * in, with (0,0,0) corresponding to the primary domain.
+ */
+void container_periodic_poly::put(int n,double x,double y,double z,double r,int &ai,int &aj,int &ak) {
+ int ijk;
+ put_locate_block(ijk,x,y,z,ai,aj,ak);
+ id[ijk][co[ijk]]=n;
+ double *pp=p[ijk]+4*co[ijk]++;
+ *(pp++)=x;*(pp++)=y;*(pp++)=z;*pp=r;
+ if(max_radius<r) max_radius=r;
+}
+
+/** Put a particle into the correct region of the container, also recording
+ * into which region it was stored.
+ * \param[in] vo the ordering class in which to record the region.
+ * \param[in] n the numerical ID of the inserted particle.
+ * \param[in] (x,y,z) the position vector of the inserted particle. */
+void container_periodic::put(particle_order &vo,int n,double x,double y,double z) {
+ int ijk;
+ put_locate_block(ijk,x,y,z);
+ id[ijk][co[ijk]]=n;
+ vo.add(ijk,co[ijk]);
+ double *pp=p[ijk]+3*co[ijk]++;
+ *(pp++)=x;*(pp++)=y;*pp=z;
+}
+
+/** Put a particle into the correct region of the container, also recording
+ * into which region it was stored.
+ * \param[in] vo the ordering class in which to record the region.
+ * \param[in] n the numerical ID of the inserted particle.
+ * \param[in] (x,y,z) the position vector of the inserted particle.
+ * \param[in] r the radius of the particle. */
+void container_periodic_poly::put(particle_order &vo,int n,double x,double y,double z,double r) {
+ int ijk;
+ put_locate_block(ijk,x,y,z);
+ id[ijk][co[ijk]]=n;
+ vo.add(ijk,co[ijk]);
+ double *pp=p[ijk]+4*co[ijk]++;
+ *(pp++)=x;*(pp++)=y;*(pp++)=z;*pp=r;
+ if(max_radius<r) max_radius=r;
+}
+
+/** Takes a particle position vector and computes the region index into which
+ * it should be stored. If the container is periodic, then the routine also
+ * maps the particle position to ensure it is in the primary domain. If the
+ * container is not periodic, the routine bails out.
+ * \param[out] ijk the region index.
+ * \param[in,out] (x,y,z) the particle position, remapped into the primary
+ * domain if necessary.
+ * \return True if the particle can be successfully placed into the container,
+ * false otherwise. */
+void container_periodic_base::put_locate_block(int &ijk,double &x,double &y,double &z) {
+
+ // Remap particle in the z direction if necessary
+ int k=step_int(z*zsp);
+ if(k<0||k>=nz) {
+ int ak=step_div(k,nz);
+ z-=ak*bz;y-=ak*byz;x-=ak*bxz;k-=ak*nz;
+ }
+
+ // Remap particle in the y direction if necessary
+ int j=step_int(y*ysp);
+ if(j<0||j>=ny) {
+ int aj=step_div(j,ny);
+ y-=aj*by;x-=aj*bxy;j-=aj*ny;
+ }
+
+ // Remap particle in the x direction if necessary
+ ijk=step_int(x*xsp);
+ if(ijk<0||ijk>=nx) {
+ int ai=step_div(ijk,nx);
+ x-=ai*bx;ijk-=ai*nx;
+ }
+
+ // Compute the block index and check memory allocation
+ j+=ey;k+=ez;
+ ijk+=nx*(j+oy*k);
+ if(co[ijk]==mem[ijk]) add_particle_memory(ijk);
+}
+
+/** Takes a particle position vector and computes the region index into which
+ * it should be stored. If the container is periodic, then the routine also
+ * maps the particle position to ensure it is in the primary domain. If the
+ * container is not periodic, the routine bails out.
+ * \param[out] ijk the region index.
+ * \param[in,out] (x,y,z) the particle position, remapped into the primary
+ * domain if necessary.
+ * \param[out] (ai,aj,ak) the periodic image displacement that the particle is
+ * in, with (0,0,0) corresponding to the primary domain.
+ * \return True if the particle can be successfully placed into the container,
+ * false otherwise. */
+void container_periodic_base::put_locate_block(int &ijk,double &x,double &y,double &z,int &ai,int &aj,int &ak) {
+
+ // Remap particle in the z direction if necessary
+ int k=step_int(z*zsp);
+ if(k<0||k>=nz) {
+ ak=step_div(k,nz);
+ z-=ak*bz;y-=ak*byz;x-=ak*bxz;k-=ak*nz;
+ } else ak=0;
+
+ // Remap particle in the y direction if necessary
+ int j=step_int(y*ysp);
+ if(j<0||j>=ny) {
+ aj=step_div(j,ny);
+ y-=aj*by;x-=aj*bxy;j-=aj*ny;
+ } else aj=0;
+
+ // Remap particle in the x direction if necessary
+ ijk=step_int(x*xsp);
+ if(ijk<0||ijk>=nx) {
+ ai=step_div(ijk,nx);
+ x-=ai*bx;ijk-=ai*nx;
+ } else ai=0;
+
+ // Compute the block index and check memory allocation
+ j+=ey;k+=ez;
+ ijk+=nx*(j+oy*k);
+ if(co[ijk]==mem[ijk]) add_particle_memory(ijk);
+}
+
+/** Takes a position vector and remaps it into the primary domain.
+ * \param[out] (ai,aj,ak) the periodic image displacement that the vector is in,
+ * with (0,0,0) corresponding to the primary domain.
+ * \param[out] (ci,cj,ck) the index of the block that the position vector is
+ * within, once it has been remapped.
+ * \param[in,out] (x,y,z) the position vector to consider, which is remapped
+ * into the primary domain during the routine.
+ * \param[out] ijk the block index that the vector is within. */
+inline void container_periodic_base::remap(int &ai,int &aj,int &ak,int &ci,int &cj,int &ck,double &x,double &y,double &z,int &ijk) {
+
+ // Remap particle in the z direction if necessary
+ ck=step_int(z*zsp);
+ if(ck<0||ck>=nz) {
+ ak=step_div(ck,nz);
+ z-=ak*bz;y-=ak*byz;x-=ak*bxz;ck-=ak*nz;
+ } else ak=0;
+
+ // Remap particle in the y direction if necessary
+ cj=step_int(y*ysp);
+ if(cj<0||cj>=ny) {
+ aj=step_div(cj,ny);
+ y-=aj*by;x-=aj*bxy;cj-=aj*ny;
+ } else aj=0;
+
+ // Remap particle in the x direction if necessary
+ ci=step_int(x*xsp);
+ if(ci<0||ci>=nx) {
+ ai=step_div(ci,nx);
+ x-=ai*bx;ci-=ai*nx;
+ } else ai=0;
+
+ cj+=ey;ck+=ez;
+ ijk=ci+nx*(cj+oy*ck);
+}
+
+/** Takes a vector and finds the particle whose Voronoi cell contains that
+ * vector. This is equivalent to finding the particle which is nearest to the
+ * vector.
+ * \param[in] (x,y,z) the vector to test.
+ * \param[out] (rx,ry,rz) the position of the particle whose Voronoi cell
+ * contains the vector. This may point to a particle in
+ * a periodic image of the primary domain.
+ * \param[out] pid the ID of the particle.
+ * \return True if a particle was found. If the container has no particles,
+ * then the search will not find a Voronoi cell and false is returned. */
+bool container_periodic::find_voronoi_cell(double x,double y,double z,double &rx,double &ry,double &rz,int &pid) {
+ int ai,aj,ak,ci,cj,ck,ijk;
+ particle_record w;
+ double mrs;
+
+ // Remap the vector into the primary domain and then search for the
+ // Voronoi cell that it is within
+ remap(ai,aj,ak,ci,cj,ck,x,y,z,ijk);
+ vc.find_voronoi_cell(x,y,z,ci,cj,ck,ijk,w,mrs);
+
+ if(w.ijk!=-1) {
+
+ // Assemble the position vector of the particle to be returned,
+ // applying a periodic remapping if necessary
+ ci+=w.di;if(ci<0||ci>=nx) ai+=step_div(ci,nx);
+ rx=p[w.ijk][3*w.l]+ak*bxz+aj*bxy+ai*bx;
+ ry=p[w.ijk][3*w.l+1]+ak*byz+aj*by;
+ rz=p[w.ijk][3*w.l+2]+ak*bz;
+ pid=id[w.ijk][w.l];
+ return true;
+ }
+ return false;
+}
+
+/** Takes a vector and finds the particle whose Voronoi cell contains that
+ * vector. Additional wall classes are not considered by this routine.
+ * \param[in] (x,y,z) the vector to test.
+ * \param[out] (rx,ry,rz) the position of the particle whose Voronoi cell
+ * contains the vector. If the container is periodic,
+ * this may point to a particle in a periodic image of
+ * the primary domain.
+ * \param[out] pid the ID of the particle.
+ * \return True if a particle was found. If the container has no particles,
+ * then the search will not find a Voronoi cell and false is returned. */
+bool container_periodic_poly::find_voronoi_cell(double x,double y,double z,double &rx,double &ry,double &rz,int &pid) {
+ int ai,aj,ak,ci,cj,ck,ijk;
+ particle_record w;
+ double mrs;
+
+ // Remap the vector into the primary domain and then search for the
+ // Voronoi cell that it is within
+ remap(ai,aj,ak,ci,cj,ck,x,y,z,ijk);
+ vc.find_voronoi_cell(x,y,z,ci,cj,ck,ijk,w,mrs);
+
+ if(w.ijk!=-1) {
+
+ // Assemble the position vector of the particle to be returned,
+ // applying a periodic remapping if necessary
+ ci+=w.di;if(ci<0||ci>=nx) ai+=step_div(ci,nx);
+ rx=p[w.ijk][4*w.l]+ak*bxz+aj*bxy+ai*bx;
+ ry=p[w.ijk][4*w.l+1]+ak*byz+aj*by;
+ rz=p[w.ijk][4*w.l+2]+ak*bz;
+ pid=id[w.ijk][w.l];
+ return true;
+ }
+ return false;
+}
+
+/** Increase memory for a particular region.
+ * \param[in] i the index of the region to reallocate. */
+void container_periodic_base::add_particle_memory(int i) {
+
+ // Handle the case when no memory has been allocated for this block
+ if(mem[i]==0) {
+ mem[i]=init_mem;
+ id[i]=new int[init_mem];
+ p[i]=new double[ps*init_mem];
+ return;
+ }
+
+ // Otherwise, double the memory allocation for this block. Carry out a
+ // check on the memory allocation size, and print a status message if
+ // requested.
+ int l,nmem(mem[i]<<1);
+ if(nmem>max_particle_memory)
+ voro_fatal_error("Absolute maximum memory allocation exceeded",VOROPP_MEMORY_ERROR);
+#if VOROPP_VERBOSE >=3
+ fprintf(stderr,"Particle memory in region %d scaled up to %d\n",i,nmem);
+#endif
+
+ // Allocate new memory and copy in the contents of the old arrays
+ int *idp=new int[nmem];
+ for(l=0;l<co[i];l++) idp[l]=id[i][l];
+ double *pp=new double[ps*nmem];
+ for(l=0;l<ps*co[i];l++) pp[l]=p[i][l];
+
+ // Update pointers and delete old arrays
+ mem[i]=nmem;
+ delete [] id[i];id[i]=idp;
+ delete [] p[i];p[i]=pp;
+}
+
+/** Import a list of particles from an open file stream into the container.
+ * Entries of four numbers (Particle ID, x position, y position, z position)
+ * are searched for. If the file cannot be successfully read, then the routine
+ * causes a fatal error.
+ * \param[in] fp the file handle to read from. */
+void container_periodic::import(FILE *fp) {
+ int i,j;
+ double x,y,z;
+ while((j=fscanf(fp,"%d %lg %lg %lg",&i,&x,&y,&z))==4) put(i,x,y,z);
+ if(j!=EOF) voro_fatal_error("File import error",VOROPP_FILE_ERROR);
+}
+
+/** Import a list of particles from an open file stream, also storing the order
+ * of that the particles are read. Entries of four numbers (Particle ID, x
+ * position, y position, z position) are searched for. If the file cannot be
+ * successfully read, then the routine causes a fatal error.
+ * \param[in,out] vo a reference to an ordering class to use.
+ * \param[in] fp the file handle to read from. */
+void container_periodic::import(particle_order &vo,FILE *fp) {
+ int i,j;
+ double x,y,z;
+ while((j=fscanf(fp,"%d %lg %lg %lg",&i,&x,&y,&z))==4) put(vo,i,x,y,z);
+ if(j!=EOF) voro_fatal_error("File import error",VOROPP_FILE_ERROR);
+}
+
+/** Import a list of particles from an open file stream into the container.
+ * Entries of five numbers (Particle ID, x position, y position, z position,
+ * radius) are searched for. If the file cannot be successfully read, then the
+ * routine causes a fatal error.
+ * \param[in] fp the file handle to read from. */
+void container_periodic_poly::import(FILE *fp) {
+ int i,j;
+ double x,y,z,r;
+ while((j=fscanf(fp,"%d %lg %lg %lg %lg",&i,&x,&y,&z,&r))==5) put(i,x,y,z,r);
+ if(j!=EOF) voro_fatal_error("File import error",VOROPP_FILE_ERROR);
+}
+
+/** Import a list of particles from an open file stream, also storing the order
+ * of that the particles are read. Entries of four numbers (Particle ID, x
+ * position, y position, z position, radius) are searched for. If the file
+ * cannot be successfully read, then the routine causes a fatal error.
+ * \param[in,out] vo a reference to an ordering class to use.
+ * \param[in] fp the file handle to read from. */
+void container_periodic_poly::import(particle_order &vo,FILE *fp) {
+ int i,j;
+ double x,y,z,r;
+ while((j=fscanf(fp,"%d %lg %lg %lg %lg",&i,&x,&y,&z,&r))==5) put(vo,i,x,y,z,r);
+ if(j!=EOF) voro_fatal_error("File import error",VOROPP_FILE_ERROR);
+}
+
+/** Outputs the a list of all the container regions along with the number of
+ * particles stored within each. */
+void container_periodic_base::region_count() {
+ int i,j,k,*cop=co;
+ for(k=0;k<nz;k++) for(j=0;j<ny;j++) for(i=0;i<nx;i++)
+ printf("Region (%d,%d,%d): %d particles\n",i,j,k,*(cop++));
+}
+
+/** Clears a container of particles. */
+void container_periodic::clear() {
+ for(int *cop=co;cop<co+nxyz;cop++) *cop=0;
+}
+
+/** Clears a container of particles, also clearing resetting the maximum radius
+ * to zero. */
+void container_periodic_poly::clear() {
+ for(int *cop=co;cop<co+nxyz;cop++) *cop=0;
+ max_radius=0;
+}
+
+/** Computes all the Voronoi cells and saves customized information about them.
+ * \param[in] format the custom output string to use.
+ * \param[in] fp a file handle to write to. */
+void container_periodic::print_custom(const char *format,FILE *fp) {
+ c_loop_all_periodic vl(*this);
+ print_custom(vl,format,fp);
+}
+
+/** Computes all the Voronoi cells and saves customized
+ * information about them.
+ * \param[in] format the custom output string to use.
+ * \param[in] fp a file handle to write to. */
+void container_periodic_poly::print_custom(const char *format,FILE *fp) {
+ c_loop_all_periodic vl(*this);
+ print_custom(vl,format,fp);
+}
+
+/** Computes all the Voronoi cells and saves customized information about them.
+ * \param[in] format the custom output string to use.
+ * \param[in] filename the name of the file to write to. */
+void container_periodic::print_custom(const char *format,const char *filename) {
+ FILE *fp=safe_fopen(filename,"w");
+ print_custom(format,fp);
+ fclose(fp);
+}
+
+/** Computes all the Voronoi cells and saves customized
+ * information about them
+ * \param[in] format the custom output string to use.
+ * \param[in] filename the name of the file to write to. */
+void container_periodic_poly::print_custom(const char *format,const char *filename) {
+ FILE *fp=safe_fopen(filename,"w");
+ print_custom(format,fp);
+ fclose(fp);
+}
+
+/** Computes all of the Voronoi cells in the container, but does nothing
+ * with the output. It is useful for measuring the pure computation time
+ * of the Voronoi algorithm, without any additional calculations such as
+ * volume evaluation or cell output. */
+void container_periodic::compute_all_cells() {
+ voronoicell c;
+ c_loop_all_periodic vl(*this);
+ if(vl.start()) do compute_cell(c,vl);
+ while(vl.inc());
+}
+
+/** Computes all of the Voronoi cells in the container, but does nothing
+ * with the output. It is useful for measuring the pure computation time
+ * of the Voronoi algorithm, without any additional calculations such as
+ * volume evaluation or cell output. */
+void container_periodic_poly::compute_all_cells() {
+ voronoicell c;
+ c_loop_all_periodic vl(*this);
+ if(vl.start()) do compute_cell(c,vl);while(vl.inc());
+}
+
+/** Calculates all of the Voronoi cells and sums their volumes. In most cases
+ * without walls, the sum of the Voronoi cell volumes should equal the volume
+ * of the container to numerical precision.
+ * \return The sum of all of the computed Voronoi volumes. */
+double container_periodic::sum_cell_volumes() {
+ voronoicell c;
+ double vol=0;
+ c_loop_all_periodic vl(*this);
+ if(vl.start()) do if(compute_cell(c,vl)) vol+=c.volume();while(vl.inc());
+ return vol;
+}
+
+/** Calculates all of the Voronoi cells and sums their volumes. In most cases
+ * without walls, the sum of the Voronoi cell volumes should equal the volume
+ * of the container to numerical precision.
+ * \return The sum of all of the computed Voronoi volumes. */
+double container_periodic_poly::sum_cell_volumes() {
+ voronoicell c;
+ double vol=0;
+ c_loop_all_periodic vl(*this);
+ if(vl.start()) do if(compute_cell(c,vl)) vol+=c.volume();while(vl.inc());
+ return vol;
+}
+
+/** This routine creates all periodic images of the particles. It is meant for
+ * diagnostic purposes only, since usually periodic images are dynamically
+ * created in when they are referenced. */
+void container_periodic_base::create_all_images() {
+ int i,j,k;
+ for(k=0;k<oz;k++) for(j=0;j<oy;j++) for(i=0;i<nx;i++) create_periodic_image(i,j,k);
+}
+
+/** Checks that the particles within each block lie within that block's bounds.
+ * This is useful for diagnosing problems with periodic image computation. */
+void container_periodic_base::check_compartmentalized() {
+ int c,l,i,j,k;
+ double mix,miy,miz,max,may,maz,*pp;
+ for(k=l=0;k<oz;k++) for(j=0;j<oy;j++) for(i=0;i<nx;i++,l++) if(mem[l]>0) {
+
+ // Compute the block's bounds, adding in a small tolerance
+ mix=i*boxx-tolerance;max=mix+boxx+tolerance;
+ miy=(j-ey)*boxy-tolerance;may=miy+boxy+tolerance;
+ miz=(k-ez)*boxz-tolerance;maz=miz+boxz+tolerance;
+
+ // Print entries for any particles that lie outside the block's
+ // bounds
+ for(pp=p[l],c=0;c<co[l];c++,pp+=ps) if(*pp<mix||*pp>max||pp[1]<miy||pp[1]>may||pp[2]<miz||pp[2]>maz)
+ printf("%d %d %d %d %f %f %f %f %f %f %f %f %f\n",
+ id[l][c],i,j,k,*pp,pp[1],pp[2],mix,max,miy,may,miz,maz);
+ }
+}
+
+/** Creates particles within an image block that is aligned with the primary
+ * domain in the z axis. In this case, the image block may be comprised of
+ * particles from two primary blocks. The routine considers these two primary
+ * blocks, and adds the needed particles to the image. The remaining particles
+ * from the primary blocks are also filled into the neighboring images.
+ * \param[in] (di,dj,dk) the index of the block to consider. The z index must
+ * satisfy ez<=dk<wz. */
+void container_periodic_base::create_side_image(int di,int dj,int dk) {
+ int l,dijk=di+nx*(dj+oy*dk),odijk,ima=step_div(dj-ey,ny);
+ int qua=di+step_int(-ima*bxy*xsp),quadiv=step_div(qua,nx);
+ int fi=qua-quadiv*nx,fijk=fi+nx*(dj-ima*ny+oy*dk);
+ double dis=ima*bxy+quadiv*bx,switchx=di*boxx-ima*bxy-quadiv*bx,adis;
+
+ // Left image computation
+ if((img[dijk]&1)==0) {
+ if(di>0) {
+ odijk=dijk-1;adis=dis;
+ } else {
+ odijk=dijk+nx-1;adis=dis+bx;
+ }
+ img[odijk]|=2;
+ for(l=0;l<co[fijk];l++) {
+ if(p[fijk][ps*l]>switchx) put_image(dijk,fijk,l,dis,by*ima,0);
+ else put_image(odijk,fijk,l,adis,by*ima,0);
+ }
+ }
+
+ // Right image computation
+ if((img[dijk]&2)==0) {
+ if(fi==nx-1) {
+ fijk+=1-nx;switchx+=(1-nx)*boxx;dis+=bx;
+ } else {
+ fijk++;switchx+=boxx;
+ }
+ if(di==nx-1) {
+ odijk=dijk-nx+1;adis=dis-bx;
+ } else {
+ odijk=dijk+1;adis=dis;
+ }
+ img[odijk]|=1;
+ for(l=0;l<co[fijk];l++) {
+ if(p[fijk][ps*l]<switchx) put_image(dijk,fijk,l,dis,by*ima,0);
+ else put_image(odijk,fijk,l,adis,by*ima,0);
+ }
+ }
+
+ // All contributions to the block now added, so set both two bits of
+ // the image information
+ img[dijk]=3;
+}
+
+/** Creates particles within an image block that is not aligned with the
+ * primary domain in the z axis. In this case, the image block may be comprised
+ * of particles from four primary blocks. The routine considers these four
+ * primary blocks, and adds the needed particles to the image. The remaining
+ * particles from the primary blocks are also filled into the neighboring
+ * images.
+ * \param[in] (di,dj,dk) the index of the block to consider. The z index must
+ * satisfy dk<ez or dk>=wz. */
+void container_periodic_base::create_vertical_image(int di,int dj,int dk) {
+ int l,dijk=di+nx*(dj+oy*dk),dijkl,dijkr,ima=step_div(dk-ez,nz);
+ int qj=dj+step_int(-ima*byz*ysp),qjdiv=step_div(qj-ey,ny);
+ int qi=di+step_int((-ima*bxz-qjdiv*bxy)*xsp),qidiv=step_div(qi,nx);
+ int fi=qi-qidiv*nx,fj=qj-qjdiv*ny,fijk=fi+nx*(fj+oy*(dk-ima*nz)),fijk2;
+ double disy=ima*byz+qjdiv*by,switchy=(dj-ey)*boxy-ima*byz-qjdiv*by;
+ double disx=ima*bxz+qjdiv*bxy+qidiv*bx,switchx=di*boxx-ima*bxz-qjdiv*bxy-qidiv*bx;
+ double switchx2,disxl,disxr,disx2,disxr2;
+
+ if(di==0) {dijkl=dijk+nx-1;disxl=disx+bx;}
+ else {dijkl=dijk-1;disxl=disx;}
+
+ if(di==nx-1) {dijkr=dijk-nx+1;disxr=disx-bx;}
+ else {dijkr=dijk+1;disxr=disx;}
+
+ // Down-left image computation
+ bool y_exist=dj!=0;
+ if((img[dijk]&1)==0) {
+ img[dijkl]|=2;
+ if(y_exist) {
+ img[dijkl-nx]|=8;
+ img[dijk-nx]|=4;
+ }
+ for(l=0;l<co[fijk];l++) {
+ if(p[fijk][ps*l+1]>switchy) {
+ if(p[fijk][ps*l]>switchx) put_image(dijk,fijk,l,disx,disy,bz*ima);
+ else put_image(dijkl,fijk,l,disxl,disy,bz*ima);
+ } else {
+ if(!y_exist) continue;
+ if(p[fijk][ps*l]>switchx) put_image(dijk-nx,fijk,l,disx,disy,bz*ima);
+ else put_image(dijkl-nx,fijk,l,disxl,disy,bz*ima);
+ }
+ }
+ }
+
+ // Down-right image computation
+ if((img[dijk]&2)==0) {
+ if(fi==nx-1) {
+ fijk2=fijk+1-nx;switchx2=switchx+(1-nx)*boxx;disx2=disx+bx;disxr2=disxr+bx;
+ } else {
+ fijk2=fijk+1;switchx2=switchx+boxx;disx2=disx;disxr2=disxr;
+ }
+ img[dijkr]|=1;
+ if(y_exist) {
+ img[dijkr-nx]|=4;
+ img[dijk-nx]|=8;
+ }
+ for(l=0;l<co[fijk2];l++) {
+ if(p[fijk2][ps*l+1]>switchy) {
+ if(p[fijk2][ps*l]>switchx2) put_image(dijkr,fijk2,l,disxr2,disy,bz*ima);
+ else put_image(dijk,fijk2,l,disx2,disy,bz*ima);
+ } else {
+ if(!y_exist) continue;
+ if(p[fijk2][ps*l]>switchx2) put_image(dijkr-nx,fijk2,l,disxr2,disy,bz*ima);
+ else put_image(dijk-nx,fijk2,l,disx2,disy,bz*ima);
+ }
+ }
+ }
+
+ // Recomputation of some intermediate quantities for boundary cases
+ if(fj==wy-1) {
+ fijk+=nx*(1-ny)-fi;
+ switchy+=(1-ny)*boxy;
+ disy+=by;
+ qi=di+step_int(-(ima*bxz+(qjdiv+1)*bxy)*xsp);
+ int dqidiv=step_div(qi,nx)-qidiv;qidiv+=dqidiv;
+ fi=qi-qidiv*nx;
+ fijk+=fi;
+ disx+=bxy+bx*dqidiv;
+ disxl+=bxy+bx*dqidiv;
+ disxr+=bxy+bx*dqidiv;
+ switchx-=bxy+bx*dqidiv;
+ } else {
+ fijk+=nx;switchy+=boxy;
+ }
+
+ // Up-left image computation
+ y_exist=dj!=oy-1;
+ if((img[dijk]&4)==0) {
+ img[dijkl]|=8;
+ if(y_exist) {
+ img[dijkl+nx]|=2;
+ img[dijk+nx]|=1;
+ }
+ for(l=0;l<co[fijk];l++) {
+ if(p[fijk][ps*l+1]>switchy) {
+ if(!y_exist) continue;
+ if(p[fijk][ps*l]>switchx) put_image(dijk+nx,fijk,l,disx,disy,bz*ima);
+ else put_image(dijkl+nx,fijk,l,disxl,disy,bz*ima);
+ } else {
+ if(p[fijk][ps*l]>switchx) put_image(dijk,fijk,l,disx,disy,bz*ima);
+ else put_image(dijkl,fijk,l,disxl,disy,bz*ima);
+ }
+ }
+ }
+
+ // Up-right image computation
+ if((img[dijk]&8)==0) {
+ if(fi==nx-1) {
+ fijk2=fijk+1-nx;switchx2=switchx+(1-nx)*boxx;disx2=disx+bx;disxr2=disxr+bx;
+ } else {
+ fijk2=fijk+1;switchx2=switchx+boxx;disx2=disx;disxr2=disxr;
+ }
+ img[dijkr]|=4;
+ if(y_exist) {
+ img[dijkr+nx]|=1;
+ img[dijk+nx]|=2;
+ }
+ for(l=0;l<co[fijk2];l++) {
+ if(p[fijk2][ps*l+1]>switchy) {
+ if(!y_exist) continue;
+ if(p[fijk2][ps*l]>switchx2) put_image(dijkr+nx,fijk2,l,disxr2,disy,bz*ima);
+ else put_image(dijk+nx,fijk2,l,disx2,disy,bz*ima);
+ } else {
+ if(p[fijk2][ps*l]>switchx2) put_image(dijkr,fijk2,l,disxr2,disy,bz*ima);
+ else put_image(dijk,fijk2,l,disx2,disy,bz*ima);
+ }
+ }
+ }
+
+ // All contributions to the block now added, so set all four bits of
+ // the image information
+ img[dijk]=15;
+}
+
+/** Copies a particle position from the primary domain into an image block.
+ * \param[in] reg the block index within the primary domain that the particle
+ * is within.
+ * \param[in] fijk the index of the image block.
+ * \param[in] l the index of the particle entry within the primary block.
+ * \param[in] (dx,dy,dz) the displacement vector to add to the particle. */
+void container_periodic_base::put_image(int reg,int fijk,int l,double dx,double dy,double dz) {
+ if(co[reg]==mem[reg]) add_particle_memory(reg);
+ double *p1=p[reg]+ps*co[reg],*p2=p[fijk]+ps*l;
+ *(p1++)=*(p2++)+dx;
+ *(p1++)=*(p2++)+dy;
+ *p1=*p2+dz;
+ if(ps==4) *(++p1)=*(++p2);
+ id[reg][co[reg]++]=id[fijk][l];
+}
+
+}
Index: extern/voro++/src/unitcell.hh
===================================================================
--- extern/voro++/src/unitcell.hh (revision 0)
+++ extern/voro++/src/unitcell.hh (revision 0)
@@ -0,0 +1,79 @@
+// Voro++, a 3D cell-based Voronoi library
+//
+// Author : Chris H. Rycroft (LBL / UC Berkeley)
+// Email : chr@alum.mit.edu
+// Date : August 30th 2011
+
+/** \file unitcell.hh
+ * \brief Header file for the unitcell class. */
+
+#ifndef VOROPP_UNITCELL_HH
+#define VOROPP_UNITCELL_HH
+
+#include <vector>
+
+#include "config.hh"
+#include "cell.hh"
+
+namespace voro {
+
+/** \brief Class for computation of the unit Voronoi cell associated with
+ * a 3D non-rectangular periodic domain. */
+class unitcell {
+ public:
+ /** The x coordinate of the first vector defining the periodic
+ * domain. */
+ const double bx;
+ /** The x coordinate of the second vector defining the periodic
+ * domain. */
+ const double bxy;
+ /** The y coordinate of the second vector defining the periodic
+ * domain. */
+ const double by;
+ /** The x coordinate of the third vector defining the periodic
+ * domain. */
+ const double bxz;
+ /** The y coordinate of the third vector defining the periodic
+ * domain. */
+ const double byz;
+ /** The z coordinate of the third vector defining the periodic
+ * domain. */
+ const double bz;
+ /** The computed unit Voronoi cell corresponding the given
+ * 3D non-rectangular periodic domain geometry. */
+ voronoicell unit_voro;
+ unitcell(double bx_,double bxy_,double by_,double bxz_,double byz_,double bz_);
+ /** Draws an outline of the domain in Gnuplot format.
+ * \param[in] filename the filename to write to. */
+ inline void draw_domain_gnuplot(const char* filename) {
+ FILE *fp(safe_fopen(filename,"w"));
+ draw_domain_gnuplot(fp);
+ fclose(fp);
+ }
+ void draw_domain_gnuplot(FILE *fp=stdout);
+ /** Draws an outline of the domain in Gnuplot format.
+ * \param[in] filename the filename to write to. */
+ inline void draw_domain_pov(const char* filename) {
+ FILE *fp(safe_fopen(filename,"w"));
+ draw_domain_pov(fp);
+ fclose(fp);
+ }
+ void draw_domain_pov(FILE *fp=stdout);
+ bool intersects_image(double dx,double dy,double dz,double &vol);
+ void images(std::vector<int> &vi,std::vector<double> &vd);
+ protected:
+ /** The maximum y-coordinate that could possibly cut the
+ * computed unit Voronoi cell. */
+ double max_uv_y;
+ /** The maximum z-coordinate that could possibly cut the
+ * computed unit Voronoi cell. */
+ double max_uv_z;
+ private:
+ inline void unit_voro_apply(int i,int j,int k);
+ bool unit_voro_intersect(int l);
+ inline bool unit_voro_test(int i,int j,int k);
+};
+
+}
+
+#endif
Index: extern/voro++/src/cell.hh
===================================================================
--- extern/voro++/src/cell.hh (revision 0)
+++ extern/voro++/src/cell.hh (revision 0)
@@ -0,0 +1,514 @@
+// Voro++, a 3D cell-based Voronoi library
+//
+// Author : Chris H. Rycroft (LBL / UC Berkeley)
+// Email : chr@alum.mit.edu
+// Date : August 30th 2011
+
+/** \file cell.hh
+ * \brief Header file for the voronoicell and related classes. */
+
+#ifndef VOROPP_CELL_HH
+#define VOROPP_CELL_HH
+
+#include <vector>
+
+#include "config.hh"
+#include "common.hh"
+
+namespace voro {
+
+/** \brief A class representing a single Voronoi cell.
+ *
+ * This class represents a single Voronoi cell, as a collection of vertices
+ * that are connected by edges. The class contains routines for initializing
+ * the Voronoi cell to be simple shapes such as a box, tetrahedron, or octahedron.
+ * It the contains routines for recomputing the cell based on cutting it
+ * by a plane, which forms the key routine for the Voronoi cell computation.
+ * It contains numerous routine for computing statistics about the Voronoi cell,
+ * and it can output the cell in several formats.
+ *
+ * This class is not intended for direct use, but forms the base of the
+ * voronoicell and voronoicell_neighbor classes, which extend it based on
+ * whether neighboring particle ID information needs to be tracked. */
+class voronoicell_base {
+ public:
+ /** This holds the current size of the arrays ed and nu, which
+ * hold the vertex information. If more vertices are created
+ * than can fit in this array, then it is dynamically extended
+ * using the add_memory_vertices routine. */
+ int current_vertices;
+ /** This holds the current maximum allowed order of a vertex,
+ * which sets the size of the mem, mep, and mec arrays. If a
+ * vertex is created with more vertices than this, the arrays
+ * are dynamically extended using the add_memory_vorder routine.
+ */
+ int current_vertex_order;
+ /** This sets the size of the main delete stack. */
+ int current_delete_size;
+ /** This sets the size of the auxiliary delete stack. */
+ int current_delete2_size;
+ /** This sets the total number of vertices in the current cell.
+ */
+ int p;
+ /** This is the index of particular point in the cell, which is
+ * used to start the tracing routines for plane intersection
+ * and cutting. These routines will work starting from any
+ * point, but it's often most efficient to start from the last
+ * point considered, since in many cases, the cell construction
+ * algorithm may consider many planes with similar vectors
+ * concurrently. */
+ int up;
+ /** This is a two dimensional array that holds information
+ * about the edge connections of the vertices that make up the
+ * cell. The two dimensional array is not allocated in the
+ * usual method. To account for the fact the different vertices
+ * have different orders, and thus require different amounts of
+ * storage, the elements of ed[i] point to one-dimensional
+ * arrays in the mep[] array of different sizes.
+ *
+ * More specifically, if vertex i has order m, then ed[i]
+ * points to a one-dimensional array in mep[m] that has 2*m+1
+ * entries. The first m elements hold the neighboring edges, so
+ * that the jth edge of vertex i is held in ed[i][j]. The next
+ * m elements hold a table of relations which is redundant but
+ * helps speed up the computation. It satisfies the relation
+ * ed[ed[i][j]][ed[i][m+j]]=i. The final entry holds a back
+ * pointer, so that ed[i+2*m]=i. The back pointers are used
+ * when rearranging the memory. */
+ int **ed;
+ /** This array holds the order of the vertices in the Voronoi
+ * cell. This array is dynamically allocated, with its current
+ * size held by current_vertices. */
+ int *nu;
+ /** This in an array with size 3*current_vertices for holding
+ * the positions of the vertices. */
+ double *pts;
+ voronoicell_base();
+ ~voronoicell_base();
+ void init_base(double xmin,double xmax,double ymin,double ymax,double zmin,double zmax);
+ void init_octahedron_base(double l);
+ void 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);
+ void translate(double x,double y,double z);
+ void draw_pov(double x,double y,double z,FILE *fp=stdout);
+ /** Outputs the cell in POV-Ray format, using cylinders for edges
+ * and spheres for vertices, to a given file.
+ * \param[in] (x,y,z) a displacement to add to the cell's
+ * position.
+ * \param[in] filename the name of the file to write to. */
+ inline void draw_pov(double x,double y,double z,const char *filename) {
+ FILE *fp=safe_fopen(filename,"w");
+ draw_pov(x,y,z,fp);
+ fclose(fp);
+ };
+ void draw_pov_mesh(double x,double y,double z,FILE *fp=stdout);
+ /** Outputs the cell in POV-Ray format as a mesh2 object to a
+ * given file.
+ * \param[in] (x,y,z) a displacement to add to the cell's
+ * position.
+ * \param[in] filename the name of the file to write to. */
+ inline void draw_pov_mesh(double x,double y,double z,const char *filename) {
+ FILE *fp=safe_fopen(filename,"w");
+ draw_pov_mesh(x,y,z,fp);
+ fclose(fp);
+ }
+ void draw_gnuplot(double x,double y,double z,FILE *fp=stdout);
+ /** Outputs the cell in Gnuplot format a given file.
+ * \param[in] (x,y,z) a displacement to add to the cell's
+ * position.
+ * \param[in] filename the name of the file to write to. */
+ inline void draw_gnuplot(double x,double y,double z,const char *filename) {
+ FILE *fp=safe_fopen(filename,"w");
+ draw_gnuplot(x,y,z,fp);
+ fclose(fp);
+ }
+ double volume();
+ double max_radius_squared();
+ double total_edge_distance();
+ double surface_area();
+ void centroid(double &cx,double &cy,double &cz);
+ int number_of_faces();
+ int number_of_edges();
+ void vertex_orders(std::vector<int> &v);
+ void output_vertex_orders(FILE *fp=stdout);
+ void vertices(std::vector<double> &v);
+ void output_vertices(FILE *fp=stdout);
+ void vertices(double x,double y,double z,std::vector<double> &v);
+ void output_vertices(double x,double y,double z,FILE *fp=stdout);
+ void face_areas(std::vector<double> &v);
+ /** Outputs the areas of the faces.
+ * \param[in] fp the file handle to write to. */
+ inline void output_face_areas(FILE *fp=stdout) {
+ std::vector<double> v;face_areas(v);
+ voro_print_vector(v,fp);
+ }
+ void face_orders(std::vector<int> &v);
+ /** Outputs a list of the number of sides of each face.
+ * \param[in] fp the file handle to write to. */
+ inline void output_face_orders(FILE *fp=stdout) {
+ std::vector<int> v;face_orders(v);
+ voro_print_vector(v,fp);
+ }
+ void face_freq_table(std::vector<int> &v);
+ /** Outputs a */
+ inline void output_face_freq_table(FILE *fp=stdout) {
+ std::vector<int> v;face_freq_table(v);
+ voro_print_vector(v,fp);
+ }
+ void face_vertices(std::vector<int> &v);
+ /** Outputs the */
+ inline void output_face_vertices(FILE *fp=stdout) {
+ std::vector<int> v;face_vertices(v);
+ voro_print_face_vertices(v,fp);
+ }
+ void face_perimeters(std::vector<double> &v);
+ /** Outputs a list of the perimeters of each face.
+ * \param[in] fp the file handle to write to. */
+ inline void output_face_perimeters(FILE *fp=stdout) {
+ std::vector<double> v;face_perimeters(v);
+ voro_print_vector(v,fp);
+ }
+ void normals(std::vector<double> &v);
+ /** Outputs a list of the perimeters of each face.
+ * \param[in] fp the file handle to write to. */
+ inline void output_normals(FILE *fp=stdout) {
+ std::vector<double> v;normals(v);
+ voro_print_positions(v,fp);
+ }
+ /** Outputs a custom string of information about the Voronoi
+ * cell to a file. It assumes the cell is at (0,0,0) and has a
+ * the default_radius associated with it.
+ * \param[in] format the custom format string to use.
+ * \param[in] fp the file handle to write to. */
+ inline void output_custom(const char *format,FILE *fp=stdout) {output_custom(format,0,0,0,0,default_radius,fp);}
+ void output_custom(const char *format,int i,double x,double y,double z,double r,FILE *fp=stdout);
+ template<class vc_class>
+ bool nplane(vc_class &vc,double x,double y,double z,double rsq,int p_id);
+ bool plane_intersects(double x,double y,double z,double rsq);
+ bool plane_intersects_guess(double x,double y,double z,double rsq);
+ void construct_relations();
+ void check_relations();
+ void check_duplicates();
+ void print_edges();
+ /** Returns a list of IDs of neighboring particles
+ * corresponding to each face.
+ * \param[out] v a reference to a vector in which to return the
+ * results. If no neighbor information is
+ * available, a blank vector is returned. */
+ virtual void neighbors(std::vector<int> &v) {v.clear();}
+ /** This is a virtual function that is overridden by a routine
+ * to print a list of IDs of neighboring particles
+ * corresponding to each face. By default, when no neighbor
+ * information is available, the routine does nothing.
+ * \param[in] fp the file handle to write to. */
+ virtual void output_neighbors(FILE *fp=stdout) {}
+ /** This a virtual function that is overridden by a routine to
+ * print the neighboring particle IDs for a given vertex. By
+ * default, when no neighbor information is available, the
+ * routine does nothing.
+ * \param[in] i the vertex to consider. */
+ virtual void print_edges_neighbors(int i) {};
+ /** This is a simple inline function for picking out the index
+ * of the next edge counterclockwise at the current vertex.
+ * \param[in] a the index of an edge of the current vertex.
+ * \param[in] p the number of the vertex.
+ * \return 0 if a=nu[p]-1, or a+1 otherwise. */
+ inline int cycle_up(int a,int p) {return a==nu[p]-1?0:a+1;}
+ /** This is a simple inline function for picking out the index
+ * of the next edge clockwise from the current vertex.
+ * \param[in] a the index of an edge of the current vertex.
+ * \param[in] p the number of the vertex.
+ * \return nu[p]-1 if a=0, or a-1 otherwise. */
+ inline int cycle_down(int a,int p) {return a==0?nu[p]-1:a-1;}
+ protected:
+ /** This a one dimensional array that holds the current sizes
+ * of the memory allocations for them mep array.*/
+ int *mem;
+ /** This is a one dimensional array that holds the current
+ * number of vertices of order p that are stored in the mep[p]
+ * array. */
+ int *mec;
+ /** This is a two dimensional array for holding the information
+ * about the edges of the Voronoi cell. mep[p] is a
+ * one-dimensional array for holding the edge information about
+ * all vertices of order p, with each vertex holding 2*p+1
+ * integers of information. The total number of vertices held
+ * on mep[p] is stored in mem[p]. If the space runs out, the
+ * code allocates more using the add_memory() routine. */
+ int **mep;
+ inline void reset_edges();
+ template<class vc_class>
+ void check_memory_for_copy(vc_class &vc,voronoicell_base* vb);
+ void copy(voronoicell_base* vb);
+ private:
+ /** This is the delete stack, used to store the vertices which
+ * are going to be deleted during the plane cutting procedure.
+ */
+ int *ds,*stacke;
+ /** This is the auxiliary delete stack, which has size set by
+ * current_delete2_size. */
+ int *ds2,*stacke2;
+ /** This stores the current memory allocation for the marginal
+ * cases. */
+ int current_marginal;
+ /** This stores the total number of marginal points which are
+ * currently in the buffer. */
+ int n_marg;
+ /** This array contains a list of the marginal points, and also
+ * the outcomes of the marginal tests. */
+ int *marg;
+ /** The x coordinate of the normal vector to the test plane. */
+ double px;
+ /** The y coordinate of the normal vector to the test plane. */
+ double py;
+ /** The z coordinate of the normal vector to the test plane. */
+ double pz;
+ /** The magnitude of the normal vector to the test plane. */
+ double prsq;
+ template<class vc_class>
+ void add_memory(vc_class &vc,int i,int *stackp2);
+ template<class vc_class>
+ void add_memory_vertices(vc_class &vc);
+ template<class vc_class>
+ void add_memory_vorder(vc_class &vc);
+ void add_memory_ds(int *&stackp);
+ void add_memory_ds2(int *&stackp2);
+ template<class vc_class>
+ inline bool collapse_order1(vc_class &vc);
+ template<class vc_class>
+ inline bool collapse_order2(vc_class &vc);
+ template<class vc_class>
+ inline bool delete_connection(vc_class &vc,int j,int k,bool hand);
+ template<class vc_class>
+ inline bool search_for_outside_edge(vc_class &vc,int &up);
+ template<class vc_class>
+ inline void add_to_stack(vc_class &vc,int lp,int *&stackp2);
+ inline bool plane_intersects_track(double x,double y,double z,double rs,double g);
+ inline void normals_search(std::vector<double> &v,int i,int j,int k);
+ inline bool search_edge(int l,int &m,int &k);
+ inline int m_test(int n,double &ans);
+ int check_marginal(int n,double &ans);
+ friend class voronoicell;
+ friend class voronoicell_neighbor;
+};
+
+/** \brief Extension of the voronoicell_base class to represent a Voronoi
+ * cell without neighbor information.
+ *
+ * This class is an extension of the voronoicell_base class, in cases when
+ * is not necessary to track the IDs of neighboring particles associated
+ * with each face of the Voronoi cell. */
+class voronoicell : public voronoicell_base {
+ public:
+ using voronoicell_base::nplane;
+ /** Copies the information from another voronoicell class into
+ * this class, extending memory allocation if necessary.
+ * \param[in] c the class to copy. */
+ inline void operator=(voronoicell &c) {
+ voronoicell_base* vb((voronoicell_base*) &c);
+ check_memory_for_copy(*this,vb);copy(vb);
+ }
+ /** Cuts a Voronoi cell using by the plane corresponding to the
+ * perpendicular bisector of a particle.
+ * \param[in] (x,y,z) the position of the particle.
+ * \param[in] rsq the modulus squared of the vector.
+ * \param[in] p_id the plane ID, ignored for this case where no
+ * neighbor tracking is enabled.
+ * \return False if the plane cut deleted the cell entirely,
+ * true otherwise. */
+ inline bool nplane(double x,double y,double z,double rsq,int p_id) {
+ return nplane(*this,x,y,z,rsq,0);
+ }
+ /** Cuts a Voronoi cell using by the plane corresponding to the
+ * perpendicular bisector of a particle.
+ * \param[in] (x,y,z) the position of the particle.
+ * \param[in] p_id the plane ID, ignored for this case where no
+ * neighbor tracking is enabled.
+ * \return False if the plane cut deleted the cell entirely,
+ * true otherwise. */
+ inline bool nplane(double x,double y,double z,int p_id) {
+ double rsq=x*x+y*y+z*z;
+ return nplane(*this,x,y,z,rsq,0);
+ }
+ /** Cuts a Voronoi cell using by the plane corresponding to the
+ * perpendicular bisector of a particle.
+ * \param[in] (x,y,z) the position of the particle.
+ * \param[in] rsq the modulus squared of the vector.
+ * \return False if the plane cut deleted the cell entirely,
+ * true otherwise. */
+ inline bool plane(double x,double y,double z,double rsq) {
+ return nplane(*this,x,y,z,rsq,0);
+ }
+ /** Cuts a Voronoi cell using by the plane corresponding to the
+ * perpendicular bisector of a particle.
+ * \param[in] (x,y,z) the position of the particle.
+ * \return False if the plane cut deleted the cell entirely,
+ * true otherwise. */
+ inline bool plane(double x,double y,double z) {
+ double rsq=x*x+y*y+z*z;
+ return nplane(*this,x,y,z,rsq,0);
+ }
+ /** Initializes the Voronoi cell to be rectangular box with the
+ * given dimensions.
+ * \param[in] (xmin,xmax) the minimum and maximum x coordinates.
+ * \param[in] (ymin,ymax) the minimum and maximum y coordinates.
+ * \param[in] (zmin,zmax) the minimum and maximum z coordinates. */
+ inline void init(double xmin,double xmax,double ymin,double ymax,double zmin,double zmax) {
+ init_base(xmin,xmax,ymin,ymax,zmin,zmax);
+ }
+ /** Initializes the cell to be an octahedron with vertices at
+ * (l,0,0), (-l,0,0), (0,l,0), (0,-l,0), (0,0,l), and (0,0,-l).
+ * \param[in] l a parameter setting the size of the octahedron.
+ */
+ inline void init_octahedron(double l) {
+ init_octahedron_base(l);
+ }
+ /** Initializes the cell to be a tetrahedron.
+ * \param[in] (x0,y0,z0) the coordinates of the first vertex.
+ * \param[in] (x1,y1,z1) the coordinates of the second vertex.
+ * \param[in] (x2,y2,z2) the coordinates of the third vertex.
+ * \param[in] (x3,y3,z3) the coordinates of the fourth vertex.
+ */
+ inline void 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) {
+ init_tetrahedron_base(x0,y0,z0,x1,y1,z1,x2,y2,z2,x3,y3,z3);
+ }
+ private:
+ inline void n_allocate(int i,int m) {};
+ inline void n_add_memory_vertices(int i) {};
+ inline void n_add_memory_vorder(int i) {};
+ inline void n_set_pointer(int p,int n) {};
+ inline void n_copy(int a,int b,int c,int d) {};
+ inline void n_set(int a,int b,int c) {};
+ inline void n_set_aux1(int k) {};
+ inline void n_copy_aux1(int a,int b) {};
+ inline void n_copy_aux1_shift(int a,int b) {};
+ inline void n_set_aux2_copy(int a,int b) {};
+ inline void n_copy_pointer(int a,int b) {};
+ inline void n_set_to_aux1(int j) {};
+ inline void n_set_to_aux2(int j) {};
+ inline void n_allocate_aux1(int i) {};
+ inline void n_switch_to_aux1(int i) {};
+ inline void n_copy_to_aux1(int i,int m) {};
+ inline void n_set_to_aux1_offset(int k,int m) {};
+ inline void n_neighbors(std::vector<int> &v) {v.clear();};
+ friend class voronoicell_base;
+};
+
+/** \brief Extension of the voronoicell_base class to represent a Voronoi cell
+ * with neighbor information.
+ *
+ * This class is an extension of the voronoicell_base class, in cases when the
+ * IDs of neighboring particles associated with each face of the Voronoi cell.
+ * It contains additional data structures mne and ne for storing this
+ * information. */
+class voronoicell_neighbor : public voronoicell_base {
+ public:
+ using voronoicell_base::nplane;
+ /** This two dimensional array holds the neighbor information
+ * associated with each vertex. mne[p] is a one dimensional
+ * array which holds all of the neighbor information for
+ * vertices of order p. */
+ int **mne;
+ /** This is a two dimensional array that holds the neighbor
+ * information associated with each vertex. ne[i] points to a
+ * one-dimensional array in mne[nu[i]]. ne[i][j] holds the
+ * neighbor information associated with the jth edge of vertex
+ * i. It is set to the ID number of the plane that made the
+ * face that is clockwise from the jth edge. */
+ int **ne;
+ voronoicell_neighbor();
+ ~voronoicell_neighbor();
+ void operator=(voronoicell &c);
+ void operator=(voronoicell_neighbor &c);
+ /** Cuts the Voronoi cell by a particle whose center is at a
+ * separation of (x,y,z) from the cell center. The value of rsq
+ * should be initially set to \f$x^2+y^2+z^2\f$.
+ * \param[in] (x,y,z) the normal vector to the plane.
+ * \param[in] rsq the distance along this vector of the plane.
+ * \param[in] p_id the plane ID (for neighbor tracking only).
+ * \return False if the plane cut deleted the cell entirely,
+ * true otherwise. */
+ inline bool nplane(double x,double y,double z,double rsq,int p_id) {
+ return nplane(*this,x,y,z,rsq,p_id);
+ }
+ /** This routine calculates the modulus squared of the vector
+ * before passing it to the main nplane() routine with full
+ * arguments.
+ * \param[in] (x,y,z) the vector to cut the cell by.
+ * \param[in] p_id the plane ID (for neighbor tracking only).
+ * \return False if the plane cut deleted the cell entirely,
+ * true otherwise. */
+ inline bool nplane(double x,double y,double z,int p_id) {
+ double rsq=x*x+y*y+z*z;
+ return nplane(*this,x,y,z,rsq,p_id);
+ }
+ /** This version of the plane routine just makes up the plane
+ * ID to be zero. It will only be referenced if neighbor
+ * tracking is enabled.
+ * \param[in] (x,y,z) the vector to cut the cell by.
+ * \param[in] rsq the modulus squared of the vector.
+ * \return False if the plane cut deleted the cell entirely,
+ * true otherwise. */
+ inline bool plane(double x,double y,double z,double rsq) {
+ return nplane(*this,x,y,z,rsq,0);
+ }
+ /** Cuts a Voronoi cell using the influence of a particle at
+ * (x,y,z), first calculating the modulus squared of this
+ * vector before passing it to the main nplane() routine. Zero
+ * is supplied as the plane ID, which will be ignored unless
+ * neighbor tracking is enabled.
+ * \param[in] (x,y,z) the vector to cut the cell by.
+ * \return False if the plane cut deleted the cell entirely,
+ * true otherwise. */
+ inline bool plane(double x,double y,double z) {
+ double rsq=x*x+y*y+z*z;
+ return nplane(*this,x,y,z,rsq,0);
+ }
+ void init(double xmin,double xmax,double ymin,double ymax,double zmin,double zmax);
+ void init_octahedron(double l);
+ void 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);
+ void check_facets();
+ virtual void neighbors(std::vector<int> &v);
+ virtual void print_edges_neighbors(int i);
+ virtual void output_neighbors(FILE *fp=stdout) {
+ std::vector<int> v;neighbors(v);
+ voro_print_vector(v,fp);
+ }
+ private:
+ int *paux1;
+ int *paux2;
+ inline void n_allocate(int i,int m) {mne[i]=new int[m*i];}
+ inline void n_add_memory_vertices(int i) {
+ int **pp=new int*[i];
+ for(int j=0;j<current_vertices;j++) pp[j]=ne[j];
+ delete [] ne;ne=pp;
+ }
+ inline void n_add_memory_vorder(int i) {
+ int **p2=new int*[i];
+ for(int j=0;j<current_vertex_order;j++) p2[j]=mne[j];
+ delete [] mne;mne=p2;
+ }
+ inline void n_set_pointer(int p,int n) {
+ ne[p]=mne[n]+n*mec[n];
+ }
+ inline void n_copy(int a,int b,int c,int d) {ne[a][b]=ne[c][d];}
+ inline void n_set(int a,int b,int c) {ne[a][b]=c;}
+ inline void n_set_aux1(int k) {paux1=mne[k]+k*mec[k];}
+ inline void n_copy_aux1(int a,int b) {paux1[b]=ne[a][b];}
+ inline void n_copy_aux1_shift(int a,int b) {paux1[b]=ne[a][b+1];}
+ inline void n_set_aux2_copy(int a,int b) {
+ paux2=mne[b]+b*mec[b];
+ for(int i=0;i<b;i++) ne[a][i]=paux2[i];
+ }
+ inline void n_copy_pointer(int a,int b) {ne[a]=ne[b];}
+ inline void n_set_to_aux1(int j) {ne[j]=paux1;}
+ inline void n_set_to_aux2(int j) {ne[j]=paux2;}
+ inline void n_allocate_aux1(int i) {paux1=new int[i*mem[i]];}
+ inline void n_switch_to_aux1(int i) {delete [] mne[i];mne[i]=paux1;}
+ inline void n_copy_to_aux1(int i,int m) {paux1[m]=mne[i][m];}
+ inline void n_set_to_aux1_offset(int k,int m) {ne[k]=paux1+m;}
+ friend class voronoicell_base;
+};
+
+}
+
+#endif
Index: extern/voro++/CMakeLists.txt
===================================================================
--- extern/voro++/CMakeLists.txt (revision 0)
+++ extern/voro++/CMakeLists.txt (revision 0)
@@ -0,0 +1,40 @@
+set (INC
+ src
+)
+
+set (INC_SYS
+)
+
+set (SRC
+ src/c_loops.cc
+ src/cell.cc
+ src/cmd_line.cc
+ src/common.cc
+ src/container_prd.cc
+ src/container.cc
+ src/pre_container.cc
+ src/unitcell.cc
+ src/v_base.cc
+ src/v_compute.cc
+ src/voro++.cc
+ src/wall.cc
+ src/c_interface.cc
+
+ src/c_loops.hh
+ src/cell.hh
+ src/common.hh
+ src/config.hh
+ src/container_prd.hh
+ src/container.hh
+ src/pre_container.hh
+ src/rad_option.hh
+ src/unitcell.hh
+ src/v_base.hh
+ src/v_compute.hh
+ src/voro++.hh
+ src/wall.hh
+ src/worklist.hh
+ src/c_interface.hh
+)
+
+blender_add_lib(extern_voro++ "${SRC}" "${INC}" "${INC_SYS}")
\ No newline at end of file
Index: extern/CMakeLists.txt
===================================================================
--- extern/CMakeLists.txt (revision 53656)
+++ extern/CMakeLists.txt (working copy)
@@ -81,3 +81,5 @@
add_subdirectory(xdnd)
endif()
endif()
+
+add_subdirectory(voro++)
Index: SConstruct
===================================================================
--- SConstruct (revision 53656)
+++ SConstruct (working copy)
@@ -266,6 +266,7 @@
target_env_defs['WITH_BF_SMOKE'] = False
target_env_defs['WITH_BF_BOOLEAN'] = False
target_env_defs['WITH_BF_REMESH'] = False
+ target_env_defs['WITH_BF_VORONOI'] = False
target_env_defs['WITH_BF_PYTHON'] = False
target_env_defs['WITH_BF_3DMOUSE'] = False
target_env_defs['WITH_BF_LIBMV'] = False
Index: CMakeLists.txt
===================================================================
--- CMakeLists.txt (revision 53656)
+++ CMakeLists.txt (working copy)
@@ -198,6 +198,7 @@
# option(WITH_MOD_CLOTH_ELTOPO "Enable Experimental cloth solver" OFF) # this is now only available in a branch
# mark_as_advanced(WITH_MOD_CLOTH_ELTOPO)
option(WITH_MOD_OCEANSIM "Enable Ocean Modifier" OFF)
+option(WITH_MOD_VORONOI "Enable Voronoi Cells in Explode Modifier" ON)
# Image format support
option(WITH_IMAGE_OPENEXR "Enable OpenEXR Support (http://www.openexr.com)" ON)
@@ -2159,6 +2160,7 @@
info_cfg_option(WITH_MOD_REMESH)
info_cfg_option(WITH_MOD_FLUID)
info_cfg_option(WITH_MOD_OCEANSIM)
+ info_cfg_option(WITH_MOD_VORONOI)
info_cfg_text("")

File Metadata

Mime Type
text/x-diff
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
dd/ba/2bfb84b5deb79c85d8ca6e72074c

Event Timeline