File: mesh_index.c

package info (click to toggle)
png23d 1.10-3
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 9,020 kB
  • sloc: ansic: 1,951; makefile: 58; sh: 9
file content (376 lines) | stat: -rw-r--r-- 11,111 bytes parent folder | download | duplicates (4)
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;;
}