chipmunk 4.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,390 @@
1
+ /* Copyright (c) 2007 Scott Lembcke
2
+ *
3
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ * of this software and associated documentation files (the "Software"), to deal
5
+ * in the Software without restriction, including without limitation the rights
6
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ * copies of the Software, and to permit persons to whom the Software is
8
+ * furnished to do so, subject to the following conditions:
9
+ *
10
+ * The above copyright notice and this permission notice shall be included in
11
+ * all copies or substantial portions of the Software.
12
+ *
13
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ * SOFTWARE.
20
+ */
21
+
22
+ #include <stdlib.h>
23
+ #include <math.h>
24
+ #include <stdio.h>
25
+ #include <assert.h>
26
+
27
+ #include "chipmunk.h"
28
+
29
+ typedef int (*collisionFunc)(cpShape*, cpShape*, cpContact**);
30
+
31
+ static collisionFunc *colfuncs = NULL;
32
+
33
+ // Add contact points for circle to circle collisions.
34
+ // Used by several collision tests.
35
+ static int
36
+ circle2circleQuery(cpVect p1, cpVect p2, cpFloat r1, cpFloat r2, cpContact **con)
37
+ {
38
+ cpFloat mindist = r1 + r2;
39
+ cpVect delta = cpvsub(p2, p1);
40
+ cpFloat distsq = cpvlengthsq(delta);
41
+ if(distsq >= mindist*mindist) return 0;
42
+
43
+ cpFloat dist = sqrtf(distsq);
44
+ // To avoid singularities, do nothing in the case of dist = 0.
45
+ cpFloat non_zero_dist = (dist ? dist : INFINITY);
46
+
47
+ // Allocate and initialize the contact.
48
+ (*con) = (cpContact *)malloc(sizeof(cpContact));
49
+ cpContactInit(
50
+ (*con),
51
+ cpvadd(p1, cpvmult(delta, 0.5 + (r1 - 0.5*mindist)/non_zero_dist)),
52
+ cpvmult(delta, 1.0/non_zero_dist),
53
+ dist - mindist,
54
+ 0
55
+ );
56
+
57
+ return 1;
58
+ }
59
+
60
+ // Collide circle shapes.
61
+ static int
62
+ circle2circle(cpShape *shape1, cpShape *shape2, cpContact **arr)
63
+ {
64
+ cpCircleShape *circ1 = (cpCircleShape *)shape1;
65
+ cpCircleShape *circ2 = (cpCircleShape *)shape2;
66
+
67
+ return circle2circleQuery(circ1->tc, circ2->tc, circ1->r, circ2->r, arr);
68
+ }
69
+
70
+ // Collide circles to segment shapes.
71
+ static int
72
+ circle2segment(cpShape *circleShape, cpShape *segmentShape, cpContact **con)
73
+ {
74
+ cpCircleShape *circ = (cpCircleShape *)circleShape;
75
+ cpSegmentShape *seg = (cpSegmentShape *)segmentShape;
76
+
77
+ // Radius sum
78
+ cpFloat rsum = circ->r + seg->r;
79
+
80
+ // Calculate normal distance from segment.
81
+ cpFloat dn = cpvdot(seg->tn, circ->tc) - cpvdot(seg->ta, seg->tn);
82
+ cpFloat dist = fabs(dn) - rsum;
83
+ if(dist > 0.0f) return 0;
84
+
85
+ // Calculate tangential distance along segment.
86
+ cpFloat dt = -cpvcross(seg->tn, circ->tc);
87
+ cpFloat dtMin = -cpvcross(seg->tn, seg->ta);
88
+ cpFloat dtMax = -cpvcross(seg->tn, seg->tb);
89
+
90
+ // Decision tree to decide which feature of the segment to collide with.
91
+ if(dt < dtMin){
92
+ if(dt < (dtMin - rsum)){
93
+ return 0;
94
+ } else {
95
+ return circle2circleQuery(circ->tc, seg->ta, circ->r, seg->r, con);
96
+ }
97
+ } else {
98
+ if(dt < dtMax){
99
+ cpVect n = (dn < 0.0f) ? seg->tn : cpvneg(seg->tn);
100
+ (*con) = (cpContact *)malloc(sizeof(cpContact));
101
+ cpContactInit(
102
+ (*con),
103
+ cpvadd(circ->tc, cpvmult(n, circ->r + dist*0.5f)),
104
+ n,
105
+ dist,
106
+ 0
107
+ );
108
+ return 1;
109
+ } else {
110
+ if(dt < (dtMax + rsum)) {
111
+ return circle2circleQuery(circ->tc, seg->tb, circ->r, seg->r, con);
112
+ } else {
113
+ return 0;
114
+ }
115
+ }
116
+ }
117
+
118
+ return 1;
119
+ }
120
+
121
+ // Helper function for allocating contact point lists.
122
+ static cpContact *
123
+ addContactPoint(cpContact **arr, int *max, int *num)
124
+ {
125
+ if(*arr == NULL){
126
+ // Allocate the array if it hasn't been done.
127
+ (*max) = 2;
128
+ (*num) = 0;
129
+ (*arr) = (cpContact *)malloc((*max)*sizeof(cpContact));
130
+ } else if(*num == *max){
131
+ // Extend it if necessary.
132
+ (*max) *= 2;
133
+ (*arr) = (cpContact *)realloc(*arr, (*max)*sizeof(cpContact));
134
+ }
135
+
136
+ cpContact *con = &(*arr)[*num];
137
+ (*num)++;
138
+
139
+ return con;
140
+ }
141
+
142
+ // Find the minimum separating axis for the give poly and axis list.
143
+ static inline int
144
+ findMSA(cpPolyShape *poly, cpPolyShapeAxis *axes, int num, cpFloat *min_out)
145
+ {
146
+ int min_index = 0;
147
+ cpFloat min = cpPolyShapeValueOnAxis(poly, axes->n, axes->d);
148
+ if(min > 0.0) return -1;
149
+
150
+ for(int i=1; i<num; i++){
151
+ cpFloat dist = cpPolyShapeValueOnAxis(poly, axes[i].n, axes[i].d);
152
+ if(dist > 0.0) {
153
+ return -1;
154
+ } else if(dist > min){
155
+ min = dist;
156
+ min_index = i;
157
+ }
158
+ }
159
+
160
+ (*min_out) = min;
161
+ return min_index;
162
+ }
163
+
164
+ // Add contacts for penetrating vertexes.
165
+ static inline int
166
+ findVerts(cpContact **arr, cpPolyShape *poly1, cpPolyShape *poly2, cpVect n, cpFloat dist)
167
+ {
168
+ int max = 0;
169
+ int num = 0;
170
+
171
+ for(int i=0; i<poly1->numVerts; i++){
172
+ cpVect v = poly1->tVerts[i];
173
+ if(cpPolyShapeContainsVertPartial(poly2, v, cpvneg(n)))
174
+ cpContactInit(addContactPoint(arr, &max, &num), v, n, dist, CP_HASH_PAIR(poly1, i));
175
+ }
176
+
177
+ for(int i=0; i<poly2->numVerts; i++){
178
+ cpVect v = poly2->tVerts[i];
179
+ if(cpPolyShapeContainsVertPartial(poly1, v, n))
180
+ cpContactInit(addContactPoint(arr, &max, &num), v, n, dist, CP_HASH_PAIR(poly2, i));
181
+ }
182
+
183
+ // if(!num)
184
+ // addContactPoint(arr, &size, &num, cpContactNew(shape1->body->p, n, dist, 0));
185
+
186
+ return num;
187
+ }
188
+
189
+ // Collide poly shapes together.
190
+ static int
191
+ poly2poly(cpShape *shape1, cpShape *shape2, cpContact **arr)
192
+ {
193
+ cpPolyShape *poly1 = (cpPolyShape *)shape1;
194
+ cpPolyShape *poly2 = (cpPolyShape *)shape2;
195
+
196
+ cpFloat min1;
197
+ int mini1 = findMSA(poly2, poly1->tAxes, poly1->numVerts, &min1);
198
+ if(mini1 == -1) return 0;
199
+
200
+ cpFloat min2;
201
+ int mini2 = findMSA(poly1, poly2->tAxes, poly2->numVerts, &min2);
202
+ if(mini2 == -1) return 0;
203
+
204
+ // There is overlap, find the penetrating verts
205
+ if(min1 > min2)
206
+ return findVerts(arr, poly1, poly2, poly1->tAxes[mini1].n, min1);
207
+ else
208
+ return findVerts(arr, poly1, poly2, cpvneg(poly2->tAxes[mini2].n), min2);
209
+ }
210
+
211
+ // Like cpPolyValueOnAxis(), but for segments.
212
+ static inline float
213
+ segValueOnAxis(cpSegmentShape *seg, cpVect n, cpFloat d)
214
+ {
215
+ cpFloat a = cpvdot(n, seg->ta) - seg->r;
216
+ cpFloat b = cpvdot(n, seg->tb) - seg->r;
217
+ return cpfmin(a, b) - d;
218
+ }
219
+
220
+ // Identify vertexes that have penetrated the segment.
221
+ static inline void
222
+ findPointsBehindSeg(cpContact **arr, int *max, int *num, cpSegmentShape *seg, cpPolyShape *poly, cpFloat pDist, cpFloat coef)
223
+ {
224
+ cpFloat dta = cpvcross(seg->tn, seg->ta);
225
+ cpFloat dtb = cpvcross(seg->tn, seg->tb);
226
+ cpVect n = cpvmult(seg->tn, coef);
227
+
228
+ for(int i=0; i<poly->numVerts; i++){
229
+ cpVect v = poly->tVerts[i];
230
+ if(cpvdot(v, n) < cpvdot(seg->tn, seg->ta)*coef + seg->r){
231
+ cpFloat dt = cpvcross(seg->tn, v);
232
+ if(dta >= dt && dt >= dtb){
233
+ cpContactInit(addContactPoint(arr, max, num), v, n, pDist, CP_HASH_PAIR(poly, i));
234
+ }
235
+ }
236
+ }
237
+ }
238
+
239
+ // This one is complicated and gross. Just don't go there...
240
+ // TODO: Comment me!
241
+ static int
242
+ seg2poly(cpShape *shape1, cpShape *shape2, cpContact **arr)
243
+ {
244
+ cpSegmentShape *seg = (cpSegmentShape *)shape1;
245
+ cpPolyShape *poly = (cpPolyShape *)shape2;
246
+ cpPolyShapeAxis *axes = poly->tAxes;
247
+
248
+ cpFloat segD = cpvdot(seg->tn, seg->ta);
249
+ cpFloat minNorm = cpPolyShapeValueOnAxis(poly, seg->tn, segD) - seg->r;
250
+ cpFloat minNeg = cpPolyShapeValueOnAxis(poly, cpvneg(seg->tn), -segD) - seg->r;
251
+ if(minNeg > 0.0f || minNorm > 0.0f) return 0;
252
+
253
+ int mini = 0;
254
+ cpFloat poly_min = segValueOnAxis(seg, axes->n, axes->d);
255
+ if(poly_min > 0.0f) return 0;
256
+ for(int i=0; i<poly->numVerts; i++){
257
+ cpFloat dist = segValueOnAxis(seg, axes[i].n, axes[i].d);
258
+ if(dist > 0.0f){
259
+ return 0;
260
+ } else if(dist > poly_min){
261
+ poly_min = dist;
262
+ mini = i;
263
+ }
264
+ }
265
+
266
+ int max = 0;
267
+ int num = 0;
268
+
269
+ cpVect poly_n = cpvneg(axes[mini].n);
270
+
271
+ cpVect va = cpvadd(seg->ta, cpvmult(poly_n, seg->r));
272
+ cpVect vb = cpvadd(seg->tb, cpvmult(poly_n, seg->r));
273
+ if(cpPolyShapeContainsVert(poly, va))
274
+ cpContactInit(addContactPoint(arr, &max, &num), va, poly_n, poly_min, CP_HASH_PAIR(seg, 0));
275
+ if(cpPolyShapeContainsVert(poly, vb))
276
+ cpContactInit(addContactPoint(arr, &max, &num), vb, poly_n, poly_min, CP_HASH_PAIR(seg, 1));
277
+
278
+ // Floating point precision problems here.
279
+ // This will have to do for now.
280
+ poly_min -= cp_collision_slop;
281
+ if(minNorm >= poly_min || minNeg >= poly_min) {
282
+ if(minNorm > minNeg)
283
+ findPointsBehindSeg(arr, &max, &num, seg, poly, minNorm, 1.0f);
284
+ else
285
+ findPointsBehindSeg(arr, &max, &num, seg, poly, minNeg, -1.0f);
286
+ }
287
+
288
+ // If no other collision points are found, try colliding endpoints.
289
+ if(num == 0){
290
+ cpVect poly_a = poly->tVerts[mini];
291
+ cpVect poly_b = poly->tVerts[(mini + 1)%poly->numVerts];
292
+
293
+ if(circle2circleQuery(seg->ta, poly_a, seg->r, 0.0f, arr))
294
+ return 1;
295
+
296
+ if(circle2circleQuery(seg->tb, poly_a, seg->r, 0.0f, arr))
297
+ return 1;
298
+
299
+ if(circle2circleQuery(seg->ta, poly_b, seg->r, 0.0f, arr))
300
+ return 1;
301
+
302
+ if(circle2circleQuery(seg->tb, poly_b, seg->r, 0.0f, arr))
303
+ return 1;
304
+ }
305
+
306
+ return num;
307
+ }
308
+
309
+ // This one is less gross, but still gross.
310
+ // TODO: Comment me!
311
+ static int
312
+ circle2poly(cpShape *shape1, cpShape *shape2, cpContact **con)
313
+ {
314
+ cpCircleShape *circ = (cpCircleShape *)shape1;
315
+ cpPolyShape *poly = (cpPolyShape *)shape2;
316
+ cpPolyShapeAxis *axes = poly->tAxes;
317
+
318
+ int mini = 0;
319
+ cpFloat min = cpvdot(axes->n, circ->tc) - axes->d - circ->r;
320
+ for(int i=0; i<poly->numVerts; i++){
321
+ cpFloat dist = cpvdot(axes[i].n, circ->tc) - axes[i].d - circ->r;
322
+ if(dist > 0.0){
323
+ return 0;
324
+ } else if(dist > min) {
325
+ min = dist;
326
+ mini = i;
327
+ }
328
+ }
329
+
330
+ cpVect n = axes[mini].n;
331
+ cpVect a = poly->tVerts[mini];
332
+ cpVect b = poly->tVerts[(mini + 1)%poly->numVerts];
333
+ cpFloat dta = cpvcross(n, a);
334
+ cpFloat dtb = cpvcross(n, b);
335
+ cpFloat dt = cpvcross(n, circ->tc);
336
+
337
+ if(dt < dtb){
338
+ return circle2circleQuery(circ->tc, b, circ->r, 0.0f, con);
339
+ } else if(dt < dta) {
340
+ (*con) = (cpContact *)malloc(sizeof(cpContact));
341
+ cpContactInit(
342
+ (*con),
343
+ cpvsub(circ->tc, cpvmult(n, circ->r + min/2.0f)),
344
+ cpvneg(n),
345
+ min,
346
+ 0
347
+ );
348
+
349
+ return 1;
350
+ } else {
351
+ return circle2circleQuery(circ->tc, a, circ->r, 0.0f, con);
352
+ }
353
+ }
354
+
355
+ static void
356
+ addColFunc(cpShapeType a, cpShapeType b, collisionFunc func)
357
+ {
358
+ colfuncs[a + b*CP_NUM_SHAPES] = func;
359
+ }
360
+
361
+ #ifdef __cplusplus
362
+ extern "C" {
363
+ #endif
364
+ // Initializes the array of collision functions.
365
+ // Called by cpInitChipmunk().
366
+ void
367
+ cpInitCollisionFuncs(void)
368
+ {
369
+ if(!colfuncs)
370
+ colfuncs = (collisionFunc *)calloc(CP_NUM_SHAPES*CP_NUM_SHAPES, sizeof(collisionFunc));
371
+
372
+ addColFunc(CP_CIRCLE_SHAPE, CP_CIRCLE_SHAPE, circle2circle);
373
+ addColFunc(CP_CIRCLE_SHAPE, CP_SEGMENT_SHAPE, circle2segment);
374
+ addColFunc(CP_SEGMENT_SHAPE, CP_POLY_SHAPE, seg2poly);
375
+ addColFunc(CP_CIRCLE_SHAPE, CP_POLY_SHAPE, circle2poly);
376
+ addColFunc(CP_POLY_SHAPE, CP_POLY_SHAPE, poly2poly);
377
+ }
378
+ #ifdef __cplusplus
379
+ }
380
+ #endif
381
+
382
+ int
383
+ cpCollideShapes(cpShape *a, cpShape *b, cpContact **arr)
384
+ {
385
+ // Their shape types must be in order.
386
+ assert(a->klass->type <= b->klass->type);
387
+
388
+ collisionFunc cfunc = colfuncs[a->klass->type + b->klass->type*CP_NUM_SHAPES];
389
+ return (cfunc) ? cfunc(a, b, arr) : 0;
390
+ }
@@ -0,0 +1,23 @@
1
+ /* Copyright (c) 2007 Scott Lembcke
2
+ *
3
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ * of this software and associated documentation files (the "Software"), to deal
5
+ * in the Software without restriction, including without limitation the rights
6
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ * copies of the Software, and to permit persons to whom the Software is
8
+ * furnished to do so, subject to the following conditions:
9
+ *
10
+ * The above copyright notice and this permission notice shall be included in
11
+ * all copies or substantial portions of the Software.
12
+ *
13
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ * SOFTWARE.
20
+ */
21
+
22
+ // Collides two cpShape structures. (this function is lonely :( )
23
+ int cpCollideShapes(cpShape *a, cpShape *b, cpContact **arr);
@@ -0,0 +1,219 @@
1
+ /* Copyright (c) 2007 Scott Lembcke
2
+ *
3
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ * of this software and associated documentation files (the "Software"), to deal
5
+ * in the Software without restriction, including without limitation the rights
6
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ * copies of the Software, and to permit persons to whom the Software is
8
+ * furnished to do so, subject to the following conditions:
9
+ *
10
+ * The above copyright notice and this permission notice shall be included in
11
+ * all copies or substantial portions of the Software.
12
+ *
13
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ * SOFTWARE.
20
+ */
21
+
22
+ #include <stdlib.h>
23
+ #include <assert.h>
24
+
25
+ #include "chipmunk.h"
26
+ #include "prime.h"
27
+
28
+ void
29
+ cpHashSetDestroy(cpHashSet *set)
30
+ {
31
+ // Free the chains.
32
+ for(int i=0; i<set->size; i++){
33
+ // Free the bins in the chain.
34
+ cpHashSetBin *bin = set->table[i];
35
+ while(bin){
36
+ cpHashSetBin *next = bin->next;
37
+ free(bin);
38
+ bin = next;
39
+ }
40
+ }
41
+
42
+ // Free the table.
43
+ free(set->table);
44
+ }
45
+
46
+ void
47
+ cpHashSetFree(cpHashSet *set)
48
+ {
49
+ if(set) cpHashSetDestroy(set);
50
+ free(set);
51
+ }
52
+
53
+ cpHashSet *
54
+ cpHashSetAlloc(void)
55
+ {
56
+ return (cpHashSet *)calloc(1, sizeof(cpHashSet));
57
+ }
58
+
59
+ cpHashSet *
60
+ cpHashSetInit(cpHashSet *set, int size, cpHashSetEqlFunc eqlFunc, cpHashSetTransFunc trans)
61
+ {
62
+ set->size = next_prime(size);
63
+ set->entries = 0;
64
+
65
+ set->eql = eqlFunc;
66
+ set->trans = trans;
67
+
68
+ set->default_value = NULL;
69
+
70
+ set->table = (cpHashSetBin **)calloc(set->size, sizeof(cpHashSetBin *));
71
+
72
+ return set;
73
+ }
74
+
75
+ cpHashSet *
76
+ cpHashSetNew(int size, cpHashSetEqlFunc eqlFunc, cpHashSetTransFunc trans)
77
+ {
78
+ return cpHashSetInit(cpHashSetAlloc(), size, eqlFunc, trans);
79
+ }
80
+
81
+ static int
82
+ setIsFull(cpHashSet *set)
83
+ {
84
+ return (set->entries >= set->size);
85
+ }
86
+
87
+ static void
88
+ cpHashSetResize(cpHashSet *set)
89
+ {
90
+ // Get the next approximate doubled prime.
91
+ int newSize = next_prime(set->size + 1);
92
+ // Allocate a new table.
93
+ cpHashSetBin **newTable = (cpHashSetBin **)calloc(newSize, sizeof(cpHashSetBin *));
94
+
95
+ // Iterate over the chains.
96
+ for(int i=0; i<set->size; i++){
97
+ // Rehash the bins into the new table.
98
+ cpHashSetBin *bin = set->table[i];
99
+ while(bin){
100
+ cpHashSetBin *next = bin->next;
101
+
102
+ int index = bin->hash%newSize;
103
+ bin->next = newTable[index];
104
+ newTable[index] = bin;
105
+
106
+ bin = next;
107
+ }
108
+ }
109
+
110
+ free(set->table);
111
+
112
+ set->table = newTable;
113
+ set->size = newSize;
114
+ }
115
+
116
+ void *
117
+ cpHashSetInsert(cpHashSet *set, unsigned int hash, void *ptr, void *data)
118
+ {
119
+ int index = hash%set->size;
120
+
121
+ // Find the bin with the matching element.
122
+ cpHashSetBin *bin = set->table[index];
123
+ while(bin && !set->eql(ptr, bin->elt))
124
+ bin = bin->next;
125
+
126
+ // Create it necessary.
127
+ if(!bin){
128
+ bin = (cpHashSetBin *)malloc(sizeof(cpHashSetBin));
129
+ bin->hash = hash;
130
+ bin->elt = set->trans(ptr, data); // Transform the pointer.
131
+
132
+ bin->next = set->table[index];
133
+ set->table[index] = bin;
134
+
135
+ set->entries++;
136
+
137
+ // Resize the set if it's full.
138
+ if(setIsFull(set))
139
+ cpHashSetResize(set);
140
+ }
141
+
142
+ return bin->elt;
143
+ }
144
+
145
+ void *
146
+ cpHashSetRemove(cpHashSet *set, unsigned int hash, void *ptr)
147
+ {
148
+ int index = hash%set->size;
149
+
150
+ // Pointer to the previous bin pointer.
151
+ cpHashSetBin **prev_ptr = &set->table[index];
152
+ // Pointer the the current bin.
153
+ cpHashSetBin *bin = set->table[index];
154
+
155
+ // Find the bin
156
+ while(bin && !set->eql(ptr, bin->elt)){
157
+ prev_ptr = &bin->next;
158
+ bin = bin->next;
159
+ }
160
+
161
+ // Remove it if it exists.
162
+ if(bin){
163
+ // Update the previos bin pointer to point to the next bin.
164
+ (*prev_ptr) = bin->next;
165
+ set->entries--;
166
+
167
+ void *return_value = bin->elt;
168
+ free(bin);
169
+ return return_value;
170
+ }
171
+
172
+ return NULL;
173
+ }
174
+
175
+ void *
176
+ cpHashSetFind(cpHashSet *set, unsigned int hash, void *ptr)
177
+ {
178
+ int index = hash%set->size;
179
+ cpHashSetBin *bin = set->table[index];
180
+ while(bin && !set->eql(ptr, bin->elt))
181
+ bin = bin->next;
182
+
183
+ return (bin ? bin->elt : set->default_value);
184
+ }
185
+
186
+ void
187
+ cpHashSetEach(cpHashSet *set, cpHashSetIterFunc func, void *data)
188
+ {
189
+ for(int i=0; i<set->size; i++){
190
+ cpHashSetBin *bin;
191
+ for(bin = set->table[i]; bin; bin = bin->next)
192
+ func(bin->elt, data);
193
+ }
194
+ }
195
+
196
+ void
197
+ cpHashSetReject(cpHashSet *set, cpHashSetRejectFunc func, void *data)
198
+ {
199
+ // Iterate over all the chains.
200
+ for(int i=0; i<set->size; i++){
201
+ // The rest works similarly to cpHashSetRemove() above.
202
+ cpHashSetBin **prev_ptr = &set->table[i];
203
+ cpHashSetBin *bin = set->table[i];
204
+ while(bin){
205
+ cpHashSetBin *next = bin->next;
206
+
207
+ if(func(bin->elt, data)){
208
+ prev_ptr = &bin->next;
209
+ } else {
210
+ (*prev_ptr) = next;
211
+
212
+ set->entries--;
213
+ free(bin);
214
+ }
215
+
216
+ bin = next;
217
+ }
218
+ }
219
+ }
@@ -0,0 +1,79 @@
1
+ /* Copyright (c) 2007 Scott Lembcke
2
+ *
3
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ * of this software and associated documentation files (the "Software"), to deal
5
+ * in the Software without restriction, including without limitation the rights
6
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ * copies of the Software, and to permit persons to whom the Software is
8
+ * furnished to do so, subject to the following conditions:
9
+ *
10
+ * The above copyright notice and this permission notice shall be included in
11
+ * all copies or substantial portions of the Software.
12
+ *
13
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ * SOFTWARE.
20
+ */
21
+
22
+ // cpHashSet uses a chained hashtable implementation.
23
+ // Other than the transformation functions, there is nothing fancy going on.
24
+
25
+ // cpHashSetBin's form the linked lists in the chained hash table.
26
+ typedef struct cpHashSetBin {
27
+ // Pointer to the element.
28
+ void *elt;
29
+ // Hash value of the element.
30
+ unsigned int hash;
31
+ // Next element in the chain.
32
+ struct cpHashSetBin *next;
33
+ } cpHashSetBin;
34
+
35
+ // Equality function. Returns true if ptr is equal to elt.
36
+ typedef int (*cpHashSetEqlFunc)(void *ptr, void *elt);
37
+ // Used by cpHashSetInsert(). Called to transform the ptr into an element.
38
+ typedef void *(*cpHashSetTransFunc)(void *ptr, void *data);
39
+ // Iterator function for a hashset.
40
+ typedef void (*cpHashSetIterFunc)(void *elt, void *data);
41
+ // Reject function. Returns true if elt should be dropped.
42
+ typedef int (*cpHashSetRejectFunc)(void *elt, void *data);
43
+
44
+ typedef struct cpHashSet {
45
+ // Number of elements stored in the table.
46
+ int entries;
47
+ // Number of cells in the table.
48
+ int size;
49
+
50
+ cpHashSetEqlFunc eql;
51
+ cpHashSetTransFunc trans;
52
+
53
+ // Default value returned by cpHashSetFind() when no element is found.
54
+ // Defaults to NULL.
55
+ void *default_value;
56
+
57
+ cpHashSetBin **table;
58
+ } cpHashSet;
59
+
60
+ // Basic allocation/destruction functions.
61
+ void cpHashSetDestroy(cpHashSet *set);
62
+ void cpHashSetFree(cpHashSet *set);
63
+
64
+ cpHashSet *cpHashSetAlloc(void);
65
+ cpHashSet *cpHashSetInit(cpHashSet *set, int size, cpHashSetEqlFunc eqlFunc, cpHashSetTransFunc trans);
66
+ cpHashSet *cpHashSetNew(int size, cpHashSetEqlFunc eqlFunc, cpHashSetTransFunc trans);
67
+
68
+ // Insert an element into the set, returns the element.
69
+ // If it doesn't already exist, the transformation function is applied.
70
+ void *cpHashSetInsert(cpHashSet *set, unsigned int hash, void *ptr, void *data);
71
+ // Remove and return an element from the set.
72
+ void *cpHashSetRemove(cpHashSet *set, unsigned int hash, void *ptr);
73
+ // Find an element in the set. Returns the default value if the element isn't found.
74
+ void *cpHashSetFind(cpHashSet *set, unsigned int hash, void *ptr);
75
+
76
+ // Iterate over a hashset.
77
+ void cpHashSetEach(cpHashSet *set, cpHashSetIterFunc func, void *data);
78
+ // Iterate over a hashset while rejecting certain elements.
79
+ void cpHashSetReject(cpHashSet *set, cpHashSetRejectFunc func, void *data);