1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376
|
/*
* Copyright 2011 Vincent Sanders <vince@kyllikki.org>
*
* Licenced under the MIT License,
* http://www.opensource.org/licenses/mit-license.php
*
* This file is part of png23d.
*
* Routines to handle mesh vertex list construction.
*
* The bloom implementation found here draws inspiration from several sources
* including:
*
* - Simon Howard C algorithms (licenced under ISC licence)
* - Lars Wirzenius publib (which he said I could licence as I saw fit ;-)
* - The paper "Hash Function for Triangular Mesh Reconstruction" by Václav
* Skala, Jan Hrádek, Martin Kuchař (Department of Computer Science and
* Engineering, University of West Bohemia) which provided inspiration
* for hash functions used in early implementations. Turns out the the
* simple FNV outperformed them in the end.
* - Fowler/Noll/Vo hash From its reference implementation which is *not*
* subject to my copyright and is in the public domain.
*
* These served as sources of code snippets and algorihms but none of them
* (except the FNV hash implementation) are responsible for this specific
* implementation which is my fault.
*/
#include <stdint.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include "option.h"
#include "bitmap.h"
#include "mesh.h"
#include "mesh_gen.h"
#include "mesh_index.h"
/* Salt values. These salts are XORed with the output of the hash function to
* give multiple unique hashes.
*
* These are "nothing up my sleeve" numbers: they are derived from the first
* 256 numbers in the book "A Million Random Digits with 100,000 Normal
* Deviates" published by the RAND corporation, ISBN 0-8330-3047-7.
*
* The numbers here were derived by taking each number from the book in turn,
* then multiplying by 256 and dividing by 100,000 to give a byte range value.
* Groups of four numbers were then combined to give 32-bit integers, most
* significant byte first.
*/
static const unsigned int salts[] = {
0x1953c322, 0x588ccf17, 0x64bf600c, 0xa6be3f3d,
0x341a02ea, 0x15b03217, 0x3b062858, 0x5956fd06,
0x18b5624f, 0xe3be0b46, 0x20ffcd5c, 0xa35dfd2b,
0x1fc4a9bf, 0x57c45d5c, 0xa8661c4a, 0x4f1b74d2,
0x5a6dde13, 0x3b18dac6, 0x05a8afbf, 0xbbda2fe2,
0xa2520d78, 0xe7934849, 0xd541bc75, 0x09a55b57,
0x9b345ae2, 0xfc2d26af, 0x38679cef, 0x81bd1e0d,
0x654681ae, 0x4b3d87ad, 0xd5ff10fb, 0x23b32f67,
0xafc7e366, 0xdd955ead, 0xe7c34b1c, 0xfeace0a6,
0xeb16f09d, 0x3c57a72d, 0x2c8294c5, 0xba92662a,
0xcd5b2d14, 0x743936c8, 0x2489beff, 0xc6c56e00,
0x74a4f606, 0xb244a94a, 0x5edfc423, 0xf1901934,
0x24af7691, 0xf6c98b25, 0xea25af46, 0x76d5f2e6,
0x5e33cdf2, 0x445eb357, 0x88556bd2, 0x70d1da7a,
0x54449368, 0x381020bc, 0x1c0520bf, 0xf7e44942,
0xa27e2a58, 0x66866fc5, 0x12519ce7, 0x437a8456,
};
/** Initialise bloom filter */
static bool
mesh_bloom_init(struct mesh *mesh,
unsigned int entries,
unsigned int iterations)
{
/* The salt table size imposes a limit on the number of iterations which
* can be applied
*/
if (iterations > sizeof(salts) / sizeof(*salts)) {
return false;
}
/* Always burn at least 256K for the table */
if (entries < (256 * 1024 * 8)) {
entries = (256 * 1024 * 8);
}
/* Allocate table, each entry is one bit, packed into bytes. */
mesh->bloom_table = calloc(1, (entries + 7) / 8);
if (mesh->bloom_table == NULL) {
return false;
}
mesh->bloom_iterations = iterations;
mesh->bloom_table_entries = entries;
return true;
}
/** perform a 32 bit Fowler/Noll/Vo hash on a vetex point
*
* Direct from reference code which has
*
* Please do not copyright this code. This code is in the public domain.
*/
static inline uint32_t
mesh_bloom_hash(struct pnt *pnt)
{
uint32_t hval = 0; /* recommended 32 bit FNV-1 hash init */
unsigned char *bp = (unsigned char *)pnt; /* start of buffer */
unsigned char *be = bp + sizeof(struct pnt);/* beyond end of buffer */
/*
* FNV-1 hash each octet in the buffer
*/
while (bp < be) {
/* multiply by the 32 bit FNV magic prime mod 2^32 */
/*
* 32 bit magic FNV-1 prime 0x01000193
*/
hval += (hval<<1) + (hval<<4) + (hval<<7) + (hval<<8) + (hval<<24);
/* xor the bottom with the current octet */
hval ^= (uint32_t)*bp++;
}
/* return our new hash value */
return hval;
}
static void
mesh_bloom_insert(struct mesh *mesh, struct pnt *pnt)
{
unsigned int hash;
unsigned int subhash;
unsigned int index;
unsigned int iloop; /* iteration loop */
uint8_t entry;
/* Generate hash of the point to insert */
hash = mesh_bloom_hash(pnt);
/* Generate multiple unique hashes by XORing with values in the
* salt table.
*/
for (iloop = 0; iloop < mesh->bloom_iterations; ++iloop) {
/* Generate a unique hash */
subhash = hash ^ salts[iloop];
/* Find the index into the table */
index = subhash % mesh->bloom_table_entries;
/* Insert into the table.
* index / 8 finds the byte index of the table,
* index % 8 gives the bit index within that byte to set.
*/
entry = (uint8_t) (1 << (index % 8));
mesh->bloom_table[index / 8] |= entry;
}
}
static bool
mesh_bloom_query(struct mesh *mesh, struct pnt *pnt)
{
unsigned int hash;
unsigned int subhash;
unsigned int index;
unsigned int iloop;
unsigned char entry;
int bit;
/* Generate hash of the value to lookup */
hash = mesh_bloom_hash(pnt);
/* Generate multiple unique hashes by XORing with values in the
* salt table. */
for (iloop = 0; iloop < mesh->bloom_iterations; ++iloop) {
/* Generate a unique hash */
subhash = hash ^ salts[iloop];
/* Find the index into the table to test */
index = subhash % mesh->bloom_table_entries;
/* The byte at index / 8 holds the value to test */
entry = mesh->bloom_table[index / 8];
bit = 1 << (index % 8);
/* Test if the particular bit is set; if it is not set,
* this value can not have been inserted. */
if ((entry & bit) == 0) {
return false;
}
}
/* All necessary bits were set. This may indicate that the value was
* inserted, or a collision has occoured and the values were set by other
* insertions.
*/
return true;
}
/** Find a point in the vertex list.
*
* Search through the unsorted vertex list for a vertex at a given location.
* Because of the locality of vertices being added a backward search requires
* vastly fewer comparisons.
*
* In all tests backwards search reduced the mean number of comparisons before
* a match. One representative test case this approach reduced the mean number
* of comparisons before a match from 67803 to 606 or less than 1% of the
* initial comparison costs.
*
* @param mesh The mesh to search for vertices within.
* @param pnt The 3d point to search for.
* @return The vertex index if it is found or the next place to insert one.
*/
static inline uint32_t
find_pnt(struct mesh *mesh, struct pnt *pnt)
{
uint32_t idx = mesh->vcount;
struct vertex *vertex;
mesh->find_count++; /* update stat */
while (idx > 0) {
idx--;
vertex = vertex_from_index(mesh, idx);
if ((vertex->pnt.x == pnt->x) &&
(vertex->pnt.y == pnt->y) &&
(vertex->pnt.z == pnt->z)) {
mesh->find_cost += (mesh->vcount - idx); /* update stat */
return idx;
}
}
idx = mesh->vcount;
mesh->find_cost += idx; /* update stat */
return idx;
}
/** Add vertex to indexed list */
static idxvtx
mesh_add_pnt(struct mesh *mesh, struct pnt *npnt)
{
uint32_t idx;
bool in_bloom;
struct vertex *vertex;
in_bloom = mesh_bloom_query(mesh, npnt);
if (in_bloom == false) {
idx = mesh->vcount; /* not already in list */
} else {
idx = find_pnt(mesh, npnt);
if (idx == mesh->vcount) {
/* seems the bloom failed to filter this one */
mesh->bloom_miss++;
}
}
if (idx == mesh->vcount) {
/* not in array already */
if ((mesh->vcount + 1) > mesh->valloc) {
/* pnt array needs extending */
mesh->v = realloc(mesh->v,
(mesh->valloc + 1000) *
(sizeof(struct vertex) + (sizeof(struct facet*) * mesh->vertex_fcount)));
mesh->valloc += 1000;
}
mesh_bloom_insert(mesh, npnt);
vertex = vertex_from_index(mesh, idx);
vertex->pnt = *npnt;
vertex->fcount = 0;
mesh->vcount++;
}
return idx;
}
/* exported interface documented in mesh_index.h */
bool
add_facet_to_vertex(struct mesh *mesh,
struct facet *facet,
idxvtx ivertex)
{
struct vertex *vertex;
vertex = vertex_from_index(mesh, ivertex);
assert(vertex->fcount < mesh->vertex_fcount);
vertex->facets[vertex->fcount++] = facet;
return true;
}
/* exported interface documented in mesh_index.h */
bool
remove_facet_from_vertex(struct mesh *mesh,
struct facet *facet,
idxvtx ivertex)
{
unsigned int floop;
struct vertex *vertex;
vertex = vertex_from_index(mesh, ivertex);
for (floop = 0; floop < vertex->fcount; floop++) {
if (vertex->facets[floop] == facet) {
vertex->fcount--;
for (; floop < vertex->fcount; floop++) {
vertex->facets[floop] = vertex->facets[floop + 1];
}
return true;
}
}
fprintf(stderr,"failed to remove facet from vertex\n");
return false;
}
/* exported method documented in mesh_index.h */
bool
index_mesh(struct mesh *mesh,
unsigned int bloom_complexity,
unsigned int vertex_fcount)
{
struct facet *facet;
struct facet *fend;
mesh->vertex_fcount = vertex_fcount;
/* initialise the bloom filter with enough entries for three vertex per
* point and the complexity parameter (ok how many functions get run)
*/
mesh_bloom_init(mesh,
mesh->fcount * bloom_complexity * 3,
bloom_complexity * 2);
fend = mesh->f + mesh->fcount;
/* manufacture pointlist and update indexed geometry */
for (facet = mesh->f; facet < fend; facet++) {
/* update facet with indexed points */
facet->i[0] = mesh_add_pnt(mesh, &facet->v[0]);
facet->i[1] = mesh_add_pnt(mesh, &facet->v[1]);
facet->i[2] = mesh_add_pnt(mesh, &facet->v[2]);
add_facet_to_vertex(mesh, facet, facet->i[0]);
add_facet_to_vertex(mesh, facet, facet->i[1]);
add_facet_to_vertex(mesh, facet, facet->i[2]);
}
return true;;
}
|