neartree 0.2

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc ADDED
@@ -0,0 +1,34 @@
1
+ = neartree
2
+
3
+ == Ruby bindings for the NearTree C library
4
+
5
+ This project implements Ruby bindings for the NearTree C library. NearTree
6
+ provides an R-Tree structure with fast nearest k-neighbour searches.
7
+
8
+ The NearTree C library is at:
9
+
10
+ http://neartree.sourceforge.net/
11
+
12
+ The bindings have only been tested on NearTree 2.3.2.
13
+
14
+
15
+ == Usage
16
+
17
+ require 'neartree'
18
+
19
+ rtree = NearTree::RTree.new(2) # number of dimensions
20
+
21
+ # insert coordinates/value pair
22
+ # coordinates must be an array of Numeric objects
23
+ # The value can be any ruby object
24
+ rtree.insert([ 0.0, 0.0], 10.0) # coordinates, value
25
+ rtree.insert([ 2.0, 0.0], 2.0)
26
+ rtree.insert([-2.0, -2.0], 0.0)
27
+
28
+ rtree.find_nearest([ 0.5, 0.5]) # => [[0.0, 0.0], 10.0]
29
+ rtree.find_nearest([100.0, 0.0]) # => [[2.0, 0.0], 2.0]
30
+
31
+ # coordinates, number_of_neighbours, radius
32
+ rtree.find_k_nearest([1.2, 0.5], 3, 2.0) # => [[[2.0, 0.0], 2.0], [[0.0, 0.0], 10.0]]
33
+ rtree.find_k_nearest([1.2, 0.5], 1, 2.0) # => [[[2.0, 0.0], 2.0]]
34
+
@@ -0,0 +1,316 @@
1
+ #include <ruby.h>
2
+ #include <CNearTree.h>
3
+ #include <limits.h>
4
+ #include <stdbool.h>
5
+
6
+ VALUE neartree_module;
7
+ VALUE neartree_class;
8
+
9
+ typedef struct {
10
+ int dimension; /* dimensionality of the space */
11
+ // bool has_radius; /* is radius used for searchs ? */
12
+ // double radius; /* radius for the searchs */
13
+ CNearTreeHandle* tree_handle; /* pointer to the actual CNearTree struct */
14
+ VALUE points; /* ruby array of the points inserted */
15
+ VALUE values; /* ruby array of the values inserted */
16
+ } NearTreeRTree;
17
+
18
+ /* aux: error handling */
19
+ void check_error_code(int error_code) {
20
+ if (error_code != CNEARTREE_SUCCESS) {
21
+ char* message = "NearTree error: %s"; /* message */
22
+ char* error_string = "unkown error"; /* default error */
23
+ VALUE exception = rb_eRuntimeError; /* default exception */
24
+
25
+ switch(error_code) {
26
+ case CNEARTREE_MALLOC_FAILED:
27
+ error_string = "malloc failed";
28
+ break;
29
+ case CNEARTREE_BAD_ARGUMENT:
30
+ error_string = "bad argument";
31
+ exception = rb_eArgError;
32
+ break;
33
+ case CNEARTREE_NOT_FOUND:
34
+ error_string = "not found";
35
+ exception = rb_eKeyError;
36
+ break;
37
+ case CNEARTREE_FREE_FAILED:
38
+ error_string = "free failed";
39
+ break;
40
+ case CNEARTREE_CVECTOR_FAILED:
41
+ error_string = "CVector failed";
42
+ break;
43
+ }
44
+
45
+ rb_raise(exception, message, error_string);
46
+ }
47
+ }
48
+
49
+ /* aux: check CVector error */
50
+ void check_cvector_error(int error_code) {
51
+ if (error_code < 0) {
52
+ rb_raise(rb_eRuntimeError, "CVector error %d", error_code);
53
+ }
54
+ }
55
+
56
+ /* aux: get struct handle for an object */
57
+ NearTreeRTree* get_object_handle(VALUE self) {
58
+ NearTreeRTree* handle;
59
+ Data_Get_Struct(self, NearTreeRTree, handle);
60
+ return handle;
61
+ }
62
+
63
+ /* aux: get C dobule array from ruby index array, must be xfree()d when done with */
64
+ double* get_array_from_index(int dimension, VALUE index) {
65
+ /* check index is Array */
66
+ if (TYPE(index) != T_ARRAY) {
67
+ rb_raise(rb_eTypeError, "Index is not an Array");
68
+ }
69
+
70
+ /* check index array length */
71
+ int index_length = RARRAY_LEN(index);
72
+ if (index_length != dimension) {
73
+ rb_raise(rb_eArgError,
74
+ "Invalid index array, should be %i, is %i", dimension, index_length);
75
+ }
76
+
77
+ /* copy to double array */
78
+ VALUE* index_ptr = RARRAY_PTR(index);
79
+ double* array_index = ALLOC_N(double, dimension);
80
+ for(int i = 0; i != dimension; ++i) { array_index[i] = NUM2DBL(index_ptr[i]); }
81
+
82
+ return array_index;
83
+ }
84
+
85
+ /* aux: extract the radius parameter */
86
+ double get_radius_from_value(int argc, VALUE* argv, int index) {
87
+ if (index < argc && argv[index] != Qnil) {
88
+ double radius = NUM2DBL(argv[index]);
89
+ if (radius < 0.0) {
90
+ rb_raise(rb_eArgError, "Radius must not be negative.");
91
+ }
92
+
93
+ return radius;
94
+ } else { // no radius parameter : default
95
+ return DBL_MAX;
96
+ }
97
+ }
98
+
99
+ /* NearTree#initialize */
100
+ // FEATURE add support for other types of vectors (integer, string)
101
+ // FEATURE add support for NearTree norms
102
+ VALUE method_initialize(VALUE self, VALUE dimension) {
103
+ NearTreeRTree* handle = get_object_handle(self); /* handle */
104
+
105
+ /* dimension */
106
+ if (!FIXNUM_P(dimension)) {
107
+ rb_raise(rb_eTypeError, "Invalid dimension");
108
+ }
109
+ int d = NUM2INT(dimension);
110
+ if (d <= 0) { rb_raise(rb_eArgError, "Invalid dimension %i", d); }
111
+ handle->dimension = d;
112
+
113
+ /* create CNearTree */
114
+ check_error_code(CNearTreeCreate(handle->tree_handle, d, CNEARTREE_TYPE_DOUBLE));
115
+
116
+ return self;
117
+ }
118
+
119
+ /* NearTree#insert */
120
+ VALUE method_insert(VALUE self, VALUE index, VALUE value) {
121
+ NearTreeRTree* handle = get_object_handle(self); /* handle */
122
+
123
+ /* array index, check for bad parameter */
124
+ double* array_index = get_array_from_index(handle->dimension, index);
125
+
126
+ /* insert point/value */
127
+ rb_ary_push(handle->points, index);
128
+ rb_ary_push(handle->values, value);
129
+
130
+ /* insert element */
131
+ check_error_code(
132
+ CNearTreeImmediateInsert(*(handle->tree_handle), (void*)array_index, (void*)value));
133
+
134
+ xfree(array_index); /* free array index */
135
+
136
+ return Qnil;
137
+ }
138
+
139
+ /* NearTree#find_nearest(index, radius = nil) */
140
+ VALUE method_find_nearest(int argc, VALUE* argv, VALUE self) {
141
+ if (!(argc == 1 || argc == 2)) {
142
+ rb_raise(rb_eArgError, "Invalid parameter number: %d", argc);
143
+ }
144
+
145
+ NearTreeRTree* handle = get_object_handle(self); /* handle */
146
+
147
+ /* array index */
148
+ double* array_index = get_array_from_index(handle->dimension, argv[0]);
149
+
150
+ /* radius */
151
+ double radius = get_radius_from_value(argc, argv, 1);
152
+
153
+ VALUE* value; /* nearest value */
154
+ double* nearest_point; /* nearest point */
155
+
156
+ check_error_code(CNearTreeNearestNeighbor(*(handle->tree_handle),
157
+ radius, (void*)&nearest_point, (void*)&value, array_index));
158
+
159
+ xfree(array_index); /* free array index */
160
+
161
+ /* nearest_point to ruby array */
162
+ VALUE point_array = rb_ary_new();
163
+ for(int i = 0; i != handle->dimension; ++i) {
164
+ rb_ary_push(point_array, DBL2NUM(nearest_point[i]));
165
+ }
166
+
167
+ /* result array */
168
+ VALUE result_array = rb_ary_new();
169
+ rb_ary_push(result_array, point_array);
170
+ rb_ary_push(result_array, *value);
171
+
172
+ return result_array;
173
+ }
174
+
175
+ /* NearTree#find_k_nearest(index, k, radius = nil) */
176
+ VALUE method_find_k_nearest(int argc, VALUE* argv, VALUE self) {
177
+ if (!(argc == 2 || argc == 3)) {
178
+ rb_raise(rb_eArgError, "Invalid parameter number: %d", argc);
179
+ }
180
+
181
+ NearTreeRTree* handle = get_object_handle(self); /* handle */
182
+
183
+ /* array index */
184
+ double* array_index = get_array_from_index(handle->dimension, argv[0]);
185
+
186
+ /* k */
187
+ if (!FIXNUM_P(argv[1])) {
188
+ rb_raise(rb_eTypeError, "Invalid k");
189
+ }
190
+ long k = FIX2INT(argv[1]);
191
+ // if (INT2FIX((int)k) != argv[1]) {
192
+ // }
193
+ if (k <= 0) {
194
+ rb_raise(rb_eArgError, "k must be superior to 0");
195
+ }
196
+
197
+ /* radius */
198
+ double radius = get_radius_from_value(argc, argv, 2);
199
+
200
+ CVectorHandle nearest_points;
201
+ CVectorHandle nearest_values;
202
+ check_cvector_error(CVectorCreate(&nearest_points, sizeof(void*), k));
203
+ check_cvector_error(CVectorCreate(&nearest_values, sizeof(void*), k));
204
+
205
+ check_error_code(CNearTreeFindKNearest(*(handle->tree_handle), (size_t)k, radius,
206
+ nearest_points, nearest_values, array_index, 0));
207
+
208
+ xfree(array_index); /* free array index */
209
+
210
+ /* result array */
211
+ VALUE result_array = rb_ary_new();
212
+ size_t points_size;
213
+ size_t values_size;
214
+ check_cvector_error(CVectorGetSize(nearest_points, &points_size));
215
+ check_cvector_error(CVectorGetSize(nearest_values, &values_size));
216
+
217
+ if (points_size != values_size) {
218
+ rb_raise(rb_eRuntimeError, "Vector sizes error");
219
+ }
220
+
221
+ for(int i = 0; i != points_size; ++i) {
222
+ VALUE result = rb_ary_new();
223
+ /* coord */
224
+ VALUE result_coord = rb_ary_new();
225
+ for(int j = 0; j != handle->dimension; ++j) {
226
+ rb_ary_push(result_coord,
227
+ DBL2NUM(((double**)(nearest_points->array))[i][j]));
228
+ }
229
+ rb_ary_push(result, result_coord);
230
+ rb_ary_push(result, *(((VALUE**)nearest_values->array)[i]));
231
+
232
+ rb_ary_push(result_array, result);
233
+ }
234
+
235
+ return result_array;
236
+ }
237
+
238
+ /* writer : radius */
239
+ /*VALUE method_radius_equal(VALUE self, VALUE radius) {
240
+ NearTreeRTree* handle = get_object_handle(self);
241
+ if (radius == Qnil) {
242
+ handle->has_radius = false;
243
+ handle->radius = DBL_MAX;
244
+ } else {
245
+ handle->has_radius = true;
246
+ handle->radius = NUM2DBL(radius);
247
+ }
248
+ return DBL2NUM(handle->radius);
249
+ } */
250
+
251
+ /* readers: dimension/points/radius/values */
252
+ VALUE method_dimension(VALUE self) { return INT2NUM(get_object_handle(self)->dimension); }
253
+ VALUE method_points( VALUE self) { return get_object_handle(self)->points; }
254
+ VALUE method_values( VALUE self) { return get_object_handle(self)->values; }
255
+
256
+ VALUE method_empty(VALUE self) {
257
+ return RARRAY_LEN(get_object_handle(self)->points) == 0 ? Qtrue : Qfalse;
258
+ }
259
+
260
+ /*VALUE method_radius(VALUE self) {
261
+ NearTreeRTree* handle = get_object_handle(self);
262
+ if (handle->has_radius) {
263
+ return DBL2NUM(handle->radius);
264
+ } else {
265
+ return Qnil;
266
+ }
267
+ } */
268
+
269
+ /* called by GC on marking of the object */
270
+ void neartree_gc_mark(NearTreeRTree* ptr) {
271
+ rb_gc_mark(ptr->points); /* points */
272
+ rb_gc_mark(ptr->values); /* values */
273
+ }
274
+
275
+ /* called by GC on collection of the object */
276
+ void neartree_gc_free(NearTreeRTree* ptr) {
277
+ xfree(ptr->tree_handle); /* free CNearTreeHandle */
278
+ xfree(ptr); /* free NearTreeRTree */
279
+ }
280
+
281
+ /* allocate */
282
+ VALUE neartree_allocate(VALUE class) {
283
+ NearTreeRTree* handle = ALLOC(NearTreeRTree); /* alloc NearTreeRTree */
284
+ handle->tree_handle = ALLOC(CNearTreeHandle); /* alloc CNearTreeHandle */
285
+ // handle->has_radius = false; /* by default, don't use radius */
286
+ // handle->radius = DBL_MAX;
287
+ handle->points = rb_ary_new(); /* points */
288
+ handle->values = rb_ary_new(); /* values */
289
+
290
+ return Data_Wrap_Struct(class, neartree_gc_mark, neartree_gc_free, handle);
291
+ }
292
+
293
+ /* extension init */
294
+ void Init_neartree() {
295
+ /* module/class */
296
+ neartree_module = rb_define_module("NearTree");
297
+ neartree_class = rb_define_class_under(neartree_module, "RTree", rb_cObject);
298
+
299
+ /* methods */
300
+ rb_define_method(neartree_class, "initialize" , method_initialize , 1);
301
+ rb_define_method(neartree_class, "insert" , method_insert , 2);
302
+ rb_define_method(neartree_class, "find_nearest" , method_find_nearest , -1);
303
+ rb_define_method(neartree_class, "find_k_nearest", method_find_k_nearest, -1);
304
+
305
+ rb_define_method(neartree_class, "dimension", method_dimension, 0);
306
+ rb_define_method(neartree_class, "points" , method_points , 0);
307
+ rb_define_method(neartree_class, "values" , method_values , 0);
308
+ rb_define_method(neartree_class, "empty?" , method_empty , 0);
309
+ // rb_define_method(neartree_class, "radius", method_radius, 0);
310
+
311
+ // rb_define_method(neartree_class, "radius=", method_radius_equal, 1);
312
+
313
+
314
+ rb_define_alloc_func(neartree_class, neartree_allocate); /* register allocate */
315
+ }
316
+
@@ -0,0 +1,12 @@
1
+ require 'mkmf'
2
+
3
+ extension_name = 'neartree'
4
+
5
+ abort 'need CNearTree.h' unless have_header('CNearTree.h')
6
+
7
+ $CFLAGS = '-std=c99 -g -Wall ' + $CFLAGS
8
+
9
+ dir_config(extension_name + '/src')
10
+ have_library('CNearTree')
11
+ create_makefile(extension_name)
12
+
data/neartree.gemspec ADDED
@@ -0,0 +1,21 @@
1
+
2
+ Gem::Specification.new do |s|
3
+ s.name = 'neartree'
4
+ s.date = '2010-12-19'
5
+ s.version = '0.2'
6
+ s.summary = 'Ruby NearTree bindings.'
7
+ s.description = 'neartree is a Ruby library binding to the NearTree C library for storing point/value
8
+ pairs in an R-Tree structure and searching it for the nearest neighbour for any given point.'
9
+ s.authors = ['Jean Krohn']
10
+ s.email = 'jb.krohn@gmail.com'
11
+ s.homepage = 'http://github.com/susano/ruby-neartree'
12
+ s.extensions = 'ext/neartree/extconf.rb'
13
+ s.rubyforge_project = 'neartree'
14
+ s.files = %w[
15
+ README.rdoc
16
+ neartree.gemspec
17
+ ext/neartree/extconf.rb
18
+ ext/neartree/NearTree.c
19
+ ]
20
+ end
21
+
metadata ADDED
@@ -0,0 +1,54 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: neartree
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.2'
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Jean Krohn
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2010-12-19 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: ! 'neartree is a Ruby library binding to the NearTree C library for storing
15
+ point/value
16
+
17
+ pairs in an R-Tree structure and searching it for the nearest neighbour for any
18
+ given point.'
19
+ email: jb.krohn@gmail.com
20
+ executables: []
21
+ extensions:
22
+ - ext/neartree/extconf.rb
23
+ extra_rdoc_files: []
24
+ files:
25
+ - README.rdoc
26
+ - neartree.gemspec
27
+ - ext/neartree/extconf.rb
28
+ - ext/neartree/NearTree.c
29
+ homepage: http://github.com/susano/ruby-neartree
30
+ licenses: []
31
+ post_install_message:
32
+ rdoc_options: []
33
+ require_paths:
34
+ - lib
35
+ required_ruby_version: !ruby/object:Gem::Requirement
36
+ none: false
37
+ requirements:
38
+ - - ! '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ required_rubygems_version: !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ! '>='
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ requirements: []
48
+ rubyforge_project: neartree
49
+ rubygems_version: 1.8.24
50
+ signing_key:
51
+ specification_version: 3
52
+ summary: Ruby NearTree bindings.
53
+ test_files: []
54
+ has_rdoc: