neartree 0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: