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
/*============================================================================
Title: weld.h
Author: Ignacio CastaƄo
Date: 07/10/2002
License: Public Domain
============================================================================*/

#ifndef _WELD_H_
#define _WELD_H_

/*----------------------------------------------------------------------------
Doc:
----------------------------------------------------------------------------*/

/** @file weld.h
* @brief Weld function to remove array duplicates in linear time.
**/


/*----------------------------------------------------------------------------
Headers
----------------------------------------------------------------------------*/

#include <vector>		// vector<T>
#include <functional>	// equal_to<T>


/*----------------------------------------------------------------------------
Functions:
----------------------------------------------------------------------------*/

inline size_t NextPowerOfTwo(size_t x)
{
	size_t p = 1;
	while( x > p ) {
		p += p;
	}
	return p;
}

/** Generic welding routine. This function welds the elements of the vector p
* and returns the cross references in the xrefs array. To compare the elements
* it uses the standard hash and key_equal functors.
*
* This code is based on the ideas of Ville Miettinen and Pierre Terdiman.
*/
template <class T, class HashFunction, class BinaryPredicate>
size_t weldVoxel( std::vector<T> & p, std::vector<size_t> & xrefs, HashFunction hash, BinaryPredicate equal )
{
	size_t const NIL = size_t(~0);							// linked list terminator symbol.
	size_t const N = p.size();								// # of input vertices.
	size_t outputCount = 0;									// # of output vertices
	size_t hashSize = NextPowerOfTwo(N);					// size of the hash table
	size_t * const hashTable = new size_t[hashSize + N];	// hash table + linked list
	size_t * const next = hashTable + hashSize;				// use bottom part as linked list

	memset( hashTable, NIL, hashSize*sizeof(size_t) );		// init hash table (NIL = 0xFFFFFFFF so memset works)

	// xrefs and p have the same size.
	xrefs.resize(N);

	for (size_t i = 0; i < N; ++i)
	{

		const T & e = p[i];
		size_t hashValue = hash(e) & (hashSize-1);
		size_t offset = hashTable[hashValue];

		// traverse linked list
		while( offset != NIL && !equal(p[offset], e) )
		{
			offset = next[offset];
		}

		xrefs[i] = offset;

		// no match found - copy vertex & add to hash
		if( offset == NIL ) {

			// save xref
			xrefs[i] = outputCount;

			// copy vertex
			p[outputCount] = e;

			// link to hash table
			next[outputCount] = hashTable[hashValue];

			// update hash heads and increase output counter
			hashTable[hashValue] = outputCount++;
		}
	}

	// cleanup
	delete [] hashTable;

	// drop duplicates.
	p.resize(outputCount);

	// number of output vertices
	return outputCount;
}

template <class T, class HashFunction, class BinaryPredicate>
size_t uniqueVector( std::vector<T> & p, std::vector<int> & xrefs, HashFunction hash, BinaryPredicate equal )
{
	size_t const NIL = size_t(~0);							// linked list terminator symbol.
	size_t const N = p.size();								// # of input vertices.
	size_t outputCount = 0;									// # of output vertices
	size_t hashSize = NextPowerOfTwo(N);					// size of the hash table
	size_t * const hashTable = new size_t[hashSize + N];	// hash table + linked list
	size_t * const next = hashTable + hashSize;				// use bottom part as linked list

	memset( hashTable, NIL, hashSize*sizeof(size_t) );		// init hash table (NIL = 0xFFFFFFFF so memset works)

	// xrefs and p have the same size.
	xrefs.resize(N);

	for (size_t i = 0; i < N; ++i)
	{

		const T & e = p[i];
		size_t hashValue = hash(e) & (hashSize-1);
		size_t offset = hashTable[hashValue];

		// traverse linked list
		while( offset != NIL && !equal(p[offset], e) )
		{
			offset = next[offset];
		}

		xrefs[i] = offset;

		// no match found - copy vertex & add to hash
		if( offset == NIL ) {

			// save xref
			xrefs[i] = outputCount;

			// copy vertex
			p[outputCount] = e;

			// link to hash table
			next[outputCount] = hashTable[hashValue];

			// update hash heads and increase output counter
			hashTable[hashValue] = outputCount++;
		}
		else
		{
			xrefs[i] = -1;
		}
	}

	// cleanup
	delete [] hashTable;

	// drop duplicates.
	p.resize(outputCount);

	// number of output vertices
	return outputCount;
}

/** Reorder the given array accoding to the indices given in xrefs.
* Use this after weld to reorder an array according to its result:
* @code
* size_t num = weld(points, xrefs);
* reorder(texcoords, num, xrefs);
* @endcode
*/
template <class T>
void reorder(std::vector<T> & array, const std::vector<size_t> & xrefs, const size_t num)
{
	std::vector<T> new_array;
	new_array.resize(num);

	for(size_t i = 0; i < num; ++i) {
		new_array[i] = array[xrefs[i]];
	}

	// replace old array by the new one.
	std::swap(array, new_array);
}



#endif // _PI_WELDING_H_