lulu 0.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.
- checksums.yaml +15 -0
- data/.gitignore +22 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +676 -0
- data/README.md +91 -0
- data/Rakefile +16 -0
- data/ext/lulu/Makefile +220 -0
- data/ext/lulu/extconf.rb +6 -0
- data/ext/lulu/lulu.c +271 -0
- data/ext/lulu/marker.c +111 -0
- data/ext/lulu/marker.h +70 -0
- data/ext/lulu/merger.c +191 -0
- data/ext/lulu/merger.h +17 -0
- data/ext/lulu/pq.c +244 -0
- data/ext/lulu/pq.h +70 -0
- data/ext/lulu/qt.c +299 -0
- data/ext/lulu/qt.h +57 -0
- data/ext/lulu/test.c +82 -0
- data/ext/lulu/test.h +20 -0
- data/ext/lulu/utility.c +49 -0
- data/ext/lulu/utility.h +74 -0
- data/lib/lulu/lulu.bundle +0 -0
- data/lib/lulu/version.rb +3 -0
- data/lib/lulu.rb +14 -0
- data/lulu.gemspec +32 -0
- data/spec/lib/lulu_spec.rb +14 -0
- data/spec/lib/marker_list_spec.rb +39 -0
- data/spec/spec_helper.rb +1 -0
- metadata +142 -0
data/README.md
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
# Lulu
|
2
|
+
|
3
|
+
Lulu - A ruby gem for merging map markers by agglomerative clustering.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'lulu'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install lulu
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
require 'lulu'
|
22
|
+
|
23
|
+
# Make an empty marker list
|
24
|
+
list = Lulu::MarkerList.new
|
25
|
+
|
26
|
+
# Add some markers. It's list.add(x, y, size). The size is actually area.
|
27
|
+
10000.times{ |n| list.add(Random.rand(1000), Random.rand(1000), Random.rand(100)) }
|
28
|
+
|
29
|
+
# Get the list length.
|
30
|
+
p list.length # Produces 10000
|
31
|
+
|
32
|
+
# Optionally set merge parameters. Marker calculations can assume circular or
|
33
|
+
# square markers, and a scale factor may be set. The scale factor is applied
|
34
|
+
# to marker radii so that the marker size exists in a different coordinate
|
35
|
+
# space from the inter-marker distances. This makes sense, for example, if
|
36
|
+
# the marker coordinates are pixels on a map. A pixel on the map represents
|
37
|
+
# distance, a pixel in a marker something else. Returns self.
|
38
|
+
list.set_info(:circle, 1)
|
39
|
+
|
40
|
+
# Merge the markers. Pairs that are merged are deleted and replaced by a
|
41
|
+
# fresh marker added to the list. If the original list has N, this can
|
42
|
+
# result in 2N-1 markers in the result. Subsequent merges will remove
|
43
|
+
# all deleted markers from previous merges. This is called compression.
|
44
|
+
# See below. Returns list.length.
|
45
|
+
list.merge
|
46
|
+
|
47
|
+
# Get the list length again. If markers were merged, the list grew.
|
48
|
+
p list.length # Produces 10415
|
49
|
+
|
50
|
+
# Squeeze out deleted markers, i.e. ones that have been merged to form
|
51
|
+
# new ones. All remaining nodes are marked :single (see method parts below)
|
52
|
+
# Returns list.length
|
53
|
+
list.compress
|
54
|
+
|
55
|
+
# Merge after compress is idempotent.
|
56
|
+
list.merge # doesn't change list
|
57
|
+
|
58
|
+
# Get the marker at index 1000. This is a list [x, y, size].
|
59
|
+
# If the index is out of range, nil is returned.
|
60
|
+
p list.marker(1000) # Produces [748.0, 187.0, 80.0]
|
61
|
+
|
62
|
+
# Get whether the marker at index 100 was deleted during a merge.
|
63
|
+
p list.deleted(100) # returns true
|
64
|
+
|
65
|
+
# Print the list of all undeleted markers
|
66
|
+
p list.markers # Produces [[601.0, 715.0, 38.0]...[647.3314285714285, 179.19619047619048, 1050.0]]
|
67
|
+
|
68
|
+
# Clear the list, returning it to the empty state. Returns self.
|
69
|
+
list.clear
|
70
|
+
|
71
|
+
# For a given marker index, get the two indices of the markers (its _parts_)
|
72
|
+
# that were merged to create it, if any.
|
73
|
+
list.parts(5324)
|
74
|
+
# For the marker with index 5324, returns
|
75
|
+
# [:root, i, j] if it is the undeleted result of a merge of markers i and j
|
76
|
+
# [:merge, i, j] if it is the subsequently deleted result of a merge of markers i and j
|
77
|
+
# [:single] if it is an undeleted original node
|
78
|
+
# [:leaf] if ti is a deleted original node
|
79
|
+
# nil if 5324 is out of range
|
80
|
+
|
81
|
+
The method `parts` is sufficient to walk the tree of all nodes formed by merging.
|
82
|
+
In this manner, a parallel array of attribute information can be merged to match
|
83
|
+
so that merged attributes are available for each `:root`.
|
84
|
+
|
85
|
+
## Contributing
|
86
|
+
|
87
|
+
1. Fork it ( http://github.com/<my-github-username>/lulu/fork )
|
88
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
89
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
90
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
91
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require "rspec/core/rake_task"
|
3
|
+
require 'rake/extensiontask'
|
4
|
+
|
5
|
+
RSpec::Core::RakeTask.new
|
6
|
+
|
7
|
+
# Rake compiler's standard doesn't match the Ruby Guides
|
8
|
+
# http://guides.rubygems.org/gems-with-extensions/. So we need this.
|
9
|
+
Rake::ExtensionTask.new do |ext|
|
10
|
+
ext.name = 'lulu'
|
11
|
+
ext.ext_dir = 'ext/lulu'
|
12
|
+
ext.lib_dir = 'lib/lulu'
|
13
|
+
end
|
14
|
+
|
15
|
+
task :default => :spec
|
16
|
+
task :test => :spec
|
data/ext/lulu/Makefile
ADDED
@@ -0,0 +1,220 @@
|
|
1
|
+
|
2
|
+
SHELL = /bin/sh
|
3
|
+
|
4
|
+
# V=0 quiet, V=1 verbose. other values don't work.
|
5
|
+
V = 0
|
6
|
+
Q1 = $(V:1=)
|
7
|
+
Q = $(Q1:0=@)
|
8
|
+
n=$(NULLCMD)
|
9
|
+
ECHO1 = $(V:1=@$n)
|
10
|
+
ECHO = $(ECHO1:0=@echo)
|
11
|
+
|
12
|
+
#### Start of system configuration section. ####
|
13
|
+
|
14
|
+
srcdir = .
|
15
|
+
topdir = /usr/local/rvm/rubies/ruby-1.9.3-p484/include/ruby-1.9.1
|
16
|
+
hdrdir = /usr/local/rvm/rubies/ruby-1.9.3-p484/include/ruby-1.9.1
|
17
|
+
arch_hdrdir = /usr/local/rvm/rubies/ruby-1.9.3-p484/include/ruby-1.9.1/$(arch)
|
18
|
+
VPATH = $(srcdir):$(arch_hdrdir)/ruby:$(hdrdir)/ruby
|
19
|
+
prefix = $(DESTDIR)/usr/local/rvm/rubies/ruby-1.9.3-p484
|
20
|
+
rubylibprefix = $(libdir)/$(RUBY_BASE_NAME)
|
21
|
+
exec_prefix = $(prefix)
|
22
|
+
vendorhdrdir = $(rubyhdrdir)/vendor_ruby
|
23
|
+
sitehdrdir = $(rubyhdrdir)/site_ruby
|
24
|
+
rubyhdrdir = $(includedir)/$(RUBY_BASE_NAME)-$(ruby_version)
|
25
|
+
vendordir = $(rubylibprefix)/vendor_ruby
|
26
|
+
sitedir = $(rubylibprefix)/site_ruby
|
27
|
+
ridir = $(datarootdir)/$(RI_BASE_NAME)
|
28
|
+
mandir = $(datarootdir)/man
|
29
|
+
localedir = $(datarootdir)/locale
|
30
|
+
libdir = $(exec_prefix)/lib
|
31
|
+
psdir = $(docdir)
|
32
|
+
pdfdir = $(docdir)
|
33
|
+
dvidir = $(docdir)
|
34
|
+
htmldir = $(docdir)
|
35
|
+
infodir = $(datarootdir)/info
|
36
|
+
docdir = $(datarootdir)/doc/$(PACKAGE)
|
37
|
+
oldincludedir = $(DESTDIR)/usr/include
|
38
|
+
includedir = $(prefix)/include
|
39
|
+
localstatedir = $(prefix)/var
|
40
|
+
sharedstatedir = $(prefix)/com
|
41
|
+
sysconfdir = $(prefix)/etc
|
42
|
+
datadir = $(datarootdir)
|
43
|
+
datarootdir = $(prefix)/share
|
44
|
+
libexecdir = $(exec_prefix)/libexec
|
45
|
+
sbindir = $(exec_prefix)/sbin
|
46
|
+
bindir = $(exec_prefix)/bin
|
47
|
+
rubylibdir = $(rubylibprefix)/$(ruby_version)
|
48
|
+
archdir = $(rubylibdir)/$(arch)
|
49
|
+
sitelibdir = $(sitedir)/$(ruby_version)
|
50
|
+
sitearchdir = $(sitelibdir)/$(sitearch)
|
51
|
+
vendorlibdir = $(vendordir)/$(ruby_version)
|
52
|
+
vendorarchdir = $(vendorlibdir)/$(sitearch)
|
53
|
+
|
54
|
+
NULLCMD = :
|
55
|
+
|
56
|
+
CC = /usr/local/opt/apple-gcc42/bin/gcc-4.2
|
57
|
+
CXX = g++
|
58
|
+
LIBRUBY = $(LIBRUBY_SO)
|
59
|
+
LIBRUBY_A = lib$(RUBY_SO_NAME)-static.a
|
60
|
+
LIBRUBYARG_SHARED = -l$(RUBY_SO_NAME)
|
61
|
+
LIBRUBYARG_STATIC = -l$(RUBY_SO_NAME)-static
|
62
|
+
empty =
|
63
|
+
OUTFLAG = -o $(empty)
|
64
|
+
COUTFLAG = -o $(empty)
|
65
|
+
|
66
|
+
RUBY_EXTCONF_H =
|
67
|
+
cflags = $(optflags) $(debugflags) $(warnflags)
|
68
|
+
optflags = -O3
|
69
|
+
debugflags = -ggdb
|
70
|
+
warnflags = -Wall -Wextra -Wno-unused-parameter -Wno-parentheses -Wno-long-long -Wno-missing-field-initializers -Wpointer-arith -Wwrite-strings -Wdeclaration-after-statement -Wshorten-64-to-32 -Wimplicit-function-declaration
|
71
|
+
CFLAGS = -fno-common $(cflags) -fno-common -pipe -std=c99 -Wno-declaration-after-statement $(ARCH_FLAG)
|
72
|
+
INCFLAGS = -I. -I$(arch_hdrdir) -I$(hdrdir)/ruby/backward -I$(hdrdir) -I$(srcdir)
|
73
|
+
DEFS =
|
74
|
+
CPPFLAGS = -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE $(DEFS) $(cppflags) -I/usr/local/opt/libyaml/include -I/usr/local/opt/readline/include -I/usr/local/opt/libksba/include -I/usr/local/opt/openssl/include
|
75
|
+
CXXFLAGS = $(CFLAGS) $(cxxflags)
|
76
|
+
ldflags = -L. -L/usr/local/lib -L/usr/local/opt/libyaml/lib -L/usr/local/opt/readline/lib -L/usr/local/opt/libksba/lib -L/usr/local/opt/openssl/lib
|
77
|
+
dldflags = -Wl,-undefined,dynamic_lookup -Wl,-multiply_defined,suppress -Wl,-flat_namespace -L/usr/local/opt/libyaml/lib -L/usr/local/opt/readline/lib -L/usr/local/opt/libksba/lib -L/usr/local/opt/openssl/lib
|
78
|
+
ARCH_FLAG =
|
79
|
+
DLDFLAGS = $(ldflags) $(dldflags) $(ARCH_FLAG)
|
80
|
+
LDSHARED = $(CC) -dynamic -bundle
|
81
|
+
LDSHAREDXX = $(CXX) -dynamic -bundle
|
82
|
+
AR = ar
|
83
|
+
EXEEXT =
|
84
|
+
|
85
|
+
RUBY_BASE_NAME = ruby
|
86
|
+
RUBY_INSTALL_NAME = ruby
|
87
|
+
RUBY_SO_NAME = ruby.1.9.1
|
88
|
+
arch = x86_64-darwin13.0.0
|
89
|
+
sitearch = $(arch)
|
90
|
+
ruby_version = 1.9.1
|
91
|
+
ruby = /usr/local/rvm/rubies/ruby-1.9.3-p484/bin/ruby
|
92
|
+
RUBY = $(ruby)
|
93
|
+
RM = rm -f
|
94
|
+
RM_RF = $(RUBY) -run -e rm -- -rf
|
95
|
+
RMDIRS = rmdir -p
|
96
|
+
MAKEDIRS = mkdir -p
|
97
|
+
INSTALL = /usr/bin/install -c
|
98
|
+
INSTALL_PROG = $(INSTALL) -m 0755
|
99
|
+
INSTALL_DATA = $(INSTALL) -m 644
|
100
|
+
COPY = cp
|
101
|
+
TOUCH = exit >
|
102
|
+
|
103
|
+
#### End of system configuration section. ####
|
104
|
+
|
105
|
+
preload =
|
106
|
+
|
107
|
+
libpath = . $(libdir) /usr/local/opt/libyaml/lib /usr/local/opt/readline/lib /usr/local/opt/libksba/lib /usr/local/opt/openssl/lib
|
108
|
+
LIBPATH = -L. -L$(libdir) -L/usr/local/opt/libyaml/lib -L/usr/local/opt/readline/lib -L/usr/local/opt/libksba/lib -L/usr/local/opt/openssl/lib
|
109
|
+
DEFFILE =
|
110
|
+
|
111
|
+
CLEANFILES = mkmf.log
|
112
|
+
DISTCLEANFILES =
|
113
|
+
DISTCLEANDIRS =
|
114
|
+
|
115
|
+
extout =
|
116
|
+
extout_prefix =
|
117
|
+
target_prefix = /lulu
|
118
|
+
LOCAL_LIBS =
|
119
|
+
LIBS = $(LIBRUBYARG_SHARED) -lpthread -ldl -lobjc
|
120
|
+
SRCS = lulu.c marker.c merger.c pq.c qt.c test.c utility.c
|
121
|
+
OBJS = lulu.o marker.o merger.o pq.o qt.o test.o utility.o
|
122
|
+
TARGET = lulu
|
123
|
+
TARGET_NAME = lulu
|
124
|
+
TARGET_ENTRY = Init_$(TARGET_NAME)
|
125
|
+
DLLIB = $(TARGET).bundle
|
126
|
+
EXTSTATIC =
|
127
|
+
STATIC_LIB =
|
128
|
+
|
129
|
+
BINDIR = $(bindir)
|
130
|
+
RUBYCOMMONDIR = $(sitedir)$(target_prefix)
|
131
|
+
RUBYLIBDIR = $(sitelibdir)$(target_prefix)
|
132
|
+
RUBYARCHDIR = $(sitearchdir)$(target_prefix)
|
133
|
+
HDRDIR = $(rubyhdrdir)/ruby$(target_prefix)
|
134
|
+
ARCHHDRDIR = $(rubyhdrdir)/$(arch)/ruby$(target_prefix)
|
135
|
+
|
136
|
+
TARGET_SO = $(DLLIB)
|
137
|
+
CLEANLIBS = $(TARGET).bundle
|
138
|
+
CLEANOBJS = *.o *.bak
|
139
|
+
|
140
|
+
all: $(DLLIB)
|
141
|
+
static: $(STATIC_LIB)
|
142
|
+
.PHONY: all install static install-so install-rb
|
143
|
+
.PHONY: clean clean-so clean-rb
|
144
|
+
|
145
|
+
clean-static::
|
146
|
+
clean-rb-default::
|
147
|
+
clean-rb::
|
148
|
+
clean-so::
|
149
|
+
clean: clean-so clean-static clean-rb-default clean-rb
|
150
|
+
-$(Q)$(RM) $(CLEANLIBS) $(CLEANOBJS) $(CLEANFILES) .*.time
|
151
|
+
|
152
|
+
distclean-rb-default::
|
153
|
+
distclean-rb::
|
154
|
+
distclean-so::
|
155
|
+
distclean: clean distclean-so distclean-rb-default distclean-rb
|
156
|
+
@-$(RM) Makefile $(RUBY_EXTCONF_H) conftest.* mkmf.log
|
157
|
+
@-$(RM) core ruby$(EXEEXT) *~ $(DISTCLEANFILES)
|
158
|
+
@-$(RMDIRS) $(DISTCLEANDIRS) 2> /dev/null || true
|
159
|
+
|
160
|
+
realclean: distclean
|
161
|
+
install: install-so install-rb
|
162
|
+
|
163
|
+
install-so: $(RUBYARCHDIR)/$(DLLIB)
|
164
|
+
$(RUBYARCHDIR)/$(DLLIB): $(DLLIB)
|
165
|
+
-$(Q)$(MAKEDIRS) $(@D)
|
166
|
+
$(INSTALL_PROG) $(DLLIB) $(@D)
|
167
|
+
clean-static::
|
168
|
+
-$(Q)$(RM) $(STATIC_LIB)
|
169
|
+
install-rb: pre-install-rb install-rb-default
|
170
|
+
install-rb-default: pre-install-rb-default
|
171
|
+
pre-install-rb: Makefile
|
172
|
+
pre-install-rb-default: Makefile
|
173
|
+
pre-install-rb-default:
|
174
|
+
$(ECHO) installing default lulu libraries
|
175
|
+
./.RUBYARCHDIR.time:
|
176
|
+
$(Q) $(MAKEDIRS) $(RUBYARCHDIR)
|
177
|
+
$(Q) $(TOUCH) $@
|
178
|
+
|
179
|
+
site-install: site-install-so site-install-rb
|
180
|
+
site-install-so: install-so
|
181
|
+
site-install-rb: install-rb
|
182
|
+
|
183
|
+
.SUFFIXES: .c .m .cc .mm .cxx .cpp .C .o
|
184
|
+
|
185
|
+
.cc.o:
|
186
|
+
$(ECHO) compiling $(<)
|
187
|
+
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $<
|
188
|
+
|
189
|
+
.mm.o:
|
190
|
+
$(ECHO) compiling $(<)
|
191
|
+
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $<
|
192
|
+
|
193
|
+
.cxx.o:
|
194
|
+
$(ECHO) compiling $(<)
|
195
|
+
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $<
|
196
|
+
|
197
|
+
.cpp.o:
|
198
|
+
$(ECHO) compiling $(<)
|
199
|
+
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $<
|
200
|
+
|
201
|
+
.C.o:
|
202
|
+
$(ECHO) compiling $(<)
|
203
|
+
$(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $<
|
204
|
+
|
205
|
+
.c.o:
|
206
|
+
$(ECHO) compiling $(<)
|
207
|
+
$(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $<
|
208
|
+
|
209
|
+
.m.o:
|
210
|
+
$(ECHO) compiling $(<)
|
211
|
+
$(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $<
|
212
|
+
|
213
|
+
$(DLLIB): $(OBJS) Makefile
|
214
|
+
$(ECHO) linking shared-object lulu/$(DLLIB)
|
215
|
+
-$(Q)$(RM) $(@)
|
216
|
+
$(Q) $(LDSHARED) -o $@ $(OBJS) $(LIBPATH) $(DLDFLAGS) $(LOCAL_LIBS) $(LIBS)
|
217
|
+
|
218
|
+
|
219
|
+
|
220
|
+
$(OBJS): $(hdrdir)/ruby.h $(hdrdir)/ruby/defines.h $(arch_hdrdir)/ruby/config.h
|
data/ext/lulu/extconf.rb
ADDED
data/ext/lulu/lulu.c
ADDED
@@ -0,0 +1,271 @@
|
|
1
|
+
/*
|
2
|
+
============================================================================
|
3
|
+
Name : lulu.c
|
4
|
+
Author : Eugene K. Ressler
|
5
|
+
Version : 0.1
|
6
|
+
Copyright : GPLv3
|
7
|
+
Description : The Lulu gem for merging map markers.
|
8
|
+
============================================================================
|
9
|
+
*/
|
10
|
+
|
11
|
+
#include <stdio.h>
|
12
|
+
#include <stdlib.h>
|
13
|
+
#include <string.h>
|
14
|
+
#include "ruby.h"
|
15
|
+
#include "utility.h"
|
16
|
+
#include "marker.h"
|
17
|
+
#include "merger.h"
|
18
|
+
#include "pq.h"
|
19
|
+
#include "qt.h"
|
20
|
+
|
21
|
+
static char EXT_VERSION[] = "0.1.2";
|
22
|
+
|
23
|
+
// -------- C marker list to be wrapped in a Ruby object -----------------------
|
24
|
+
|
25
|
+
typedef struct marker_list_s {
|
26
|
+
MARKER_INFO info[1];
|
27
|
+
MARKER *markers;
|
28
|
+
int size, max_size;
|
29
|
+
} MARKER_LIST;
|
30
|
+
|
31
|
+
#define MARKER_LIST_DECL(Name) MARKER_LIST Name[1]; init_marker_list(Name)
|
32
|
+
#define ml_set_marker_list_info(L, Kind, Scale) mr_info_set((L)->info, (Kind), (Scale))
|
33
|
+
|
34
|
+
void init_marker_list(MARKER_LIST *list) {
|
35
|
+
mr_info_init(list->info);
|
36
|
+
list->markers = NULL;
|
37
|
+
list->size = list->max_size = 0;
|
38
|
+
}
|
39
|
+
|
40
|
+
MARKER_LIST *new_marker_list(void) {
|
41
|
+
NewDecl(MARKER_LIST, list);
|
42
|
+
init_marker_list(list);
|
43
|
+
return list;
|
44
|
+
}
|
45
|
+
|
46
|
+
void clear_marker_list(MARKER_LIST *list) {
|
47
|
+
Free(list->markers);
|
48
|
+
init_marker_list(list);
|
49
|
+
}
|
50
|
+
|
51
|
+
void free_marker_list(MARKER_LIST *list) {
|
52
|
+
clear_marker_list(list);
|
53
|
+
Free(list);
|
54
|
+
}
|
55
|
+
|
56
|
+
void add_marker(MARKER_LIST *list, COORD x, COORD y, SIZE size) {
|
57
|
+
if (list->size >= list->max_size) {
|
58
|
+
list->max_size = 4 + 2 * list->max_size;
|
59
|
+
RenewArray(list->markers, list->max_size);
|
60
|
+
}
|
61
|
+
MARKER *marker = list->markers + list->size++;
|
62
|
+
mr_set(list->info, marker, x, y, size);
|
63
|
+
}
|
64
|
+
|
65
|
+
void ensure_headroom(MARKER_LIST *list) {
|
66
|
+
int needed_size = 2 * list->size - 1;
|
67
|
+
if (list->max_size < needed_size) {
|
68
|
+
list->max_size = needed_size;
|
69
|
+
RenewArray(list->markers, list->max_size);
|
70
|
+
}
|
71
|
+
}
|
72
|
+
|
73
|
+
void compress(MARKER_LIST *list) {
|
74
|
+
int dst = 0;
|
75
|
+
for (int src = 0; src < list->size; src++)
|
76
|
+
if (!mr_deleted_p(list->markers + src)) {
|
77
|
+
if (src != dst)
|
78
|
+
list->markers[dst] = list->markers[src];
|
79
|
+
mr_reset_parts(list->markers + dst);
|
80
|
+
dst++;
|
81
|
+
}
|
82
|
+
list->size = dst;
|
83
|
+
}
|
84
|
+
|
85
|
+
// -------- Ruby API implementation --------------------------------------------
|
86
|
+
|
87
|
+
static void rb_api_free_marker_list(void *list) {
|
88
|
+
free_marker_list(list);
|
89
|
+
}
|
90
|
+
|
91
|
+
static VALUE rb_api_new_marker_list(VALUE klass) {
|
92
|
+
MARKER_LIST *list = new_marker_list();
|
93
|
+
return Data_Wrap_Struct(klass, 0, rb_api_free_marker_list, list);
|
94
|
+
}
|
95
|
+
|
96
|
+
#define MARKER_LIST_FOR_VALUE_DECL(Var) MARKER_LIST *Var; Data_Get_Struct(Var ## _value, MARKER_LIST, Var)
|
97
|
+
|
98
|
+
static VALUE rb_api_initialize_copy(VALUE dst_value, VALUE src_value)
|
99
|
+
#define ARGC_initialize_copy 1
|
100
|
+
{
|
101
|
+
if (dst_value == src_value)
|
102
|
+
return src_value;
|
103
|
+
|
104
|
+
if (TYPE(src_value) != T_DATA || RDATA(src_value)->dfree != (RUBY_DATA_FUNC)rb_api_free_marker_list)
|
105
|
+
rb_raise(rb_eTypeError, "type mismatch (copy_marker_list)");
|
106
|
+
|
107
|
+
MARKER_LIST_FOR_VALUE_DECL(src);
|
108
|
+
MARKER_LIST_FOR_VALUE_DECL(dst);
|
109
|
+
|
110
|
+
// Shallow copy contents, then deep copy array of markers.
|
111
|
+
*dst = *src;
|
112
|
+
NewArray(dst->markers, dst->max_size);
|
113
|
+
CopyArray(dst->markers, src->markers, dst->max_size);
|
114
|
+
|
115
|
+
return dst_value;
|
116
|
+
}
|
117
|
+
|
118
|
+
static VALUE rb_api_clear(VALUE self_value)
|
119
|
+
#define ARGC_clear 0
|
120
|
+
{
|
121
|
+
MARKER_LIST_FOR_VALUE_DECL(self);
|
122
|
+
clear_marker_list(self);
|
123
|
+
return self_value;
|
124
|
+
}
|
125
|
+
|
126
|
+
static VALUE rb_api_set_info(VALUE self_value, VALUE kind_value, VALUE scale_value)
|
127
|
+
#define ARGC_set_info 2
|
128
|
+
{
|
129
|
+
MARKER_LIST_FOR_VALUE_DECL(self);
|
130
|
+
|
131
|
+
// Get the valid symbol values.
|
132
|
+
VALUE square_sym = ID2SYM(rb_intern("square"));
|
133
|
+
VALUE circle_sym = ID2SYM(rb_intern("circle"));
|
134
|
+
|
135
|
+
// Try to convert the kind into a symbol. This could cause an exception.
|
136
|
+
VALUE kind_as_sym = rb_funcall(kind_value, rb_intern("to_sym"), 0);
|
137
|
+
|
138
|
+
// Also cause an exception if it's an incorrect symbol value.
|
139
|
+
if (kind_as_sym != square_sym && kind_as_sym != circle_sym)
|
140
|
+
rb_raise(rb_eTypeError, "invalid symbol for marker kind (set_info)");
|
141
|
+
|
142
|
+
mr_info_set(self->info, kind_as_sym == square_sym ? SQUARE : CIRCLE, rb_num2dbl(scale_value));
|
143
|
+
|
144
|
+
return self_value;
|
145
|
+
}
|
146
|
+
|
147
|
+
static VALUE rb_api_add(VALUE self_value, VALUE x_value, VALUE y_value, VALUE size_value)
|
148
|
+
#define ARGC_add 3
|
149
|
+
{
|
150
|
+
MARKER_LIST_FOR_VALUE_DECL(self);
|
151
|
+
add_marker(self, rb_num2dbl(x_value), rb_num2dbl(y_value), rb_num2dbl(size_value));
|
152
|
+
return INT2FIX(self->size);
|
153
|
+
}
|
154
|
+
|
155
|
+
static VALUE rb_api_length(VALUE self_value)
|
156
|
+
#define ARGC_length 0
|
157
|
+
{
|
158
|
+
MARKER_LIST_FOR_VALUE_DECL(self);
|
159
|
+
return INT2FIX(self->size);
|
160
|
+
}
|
161
|
+
|
162
|
+
static VALUE rb_api_marker(VALUE self_value, VALUE index)
|
163
|
+
#define ARGC_marker 1
|
164
|
+
{
|
165
|
+
MARKER_LIST_FOR_VALUE_DECL(self);
|
166
|
+
int i = NUM2INT(index);
|
167
|
+
if (0 <= i && i < self->size) {
|
168
|
+
MARKER *marker = self->markers + i;
|
169
|
+
VALUE triple = rb_ary_new2(3);
|
170
|
+
rb_ary_store(triple, 0, rb_float_new(mr_x(marker)));
|
171
|
+
rb_ary_store(triple, 1, rb_float_new(mr_y(marker)));
|
172
|
+
rb_ary_store(triple, 2, rb_float_new(marker->size));
|
173
|
+
return triple;
|
174
|
+
}
|
175
|
+
return Qnil;
|
176
|
+
}
|
177
|
+
|
178
|
+
static VALUE rb_api_parts(VALUE self_value, VALUE index)
|
179
|
+
#define ARGC_parts 1
|
180
|
+
{
|
181
|
+
MARKER_LIST_FOR_VALUE_DECL(self);
|
182
|
+
int i = NUM2INT(index);
|
183
|
+
if (0 <= i && i < self->size) {
|
184
|
+
MARKER *marker = self->markers + i;
|
185
|
+
VALUE rtn;
|
186
|
+
if (mr_merged(marker)) {
|
187
|
+
rtn = rb_ary_new2(3);
|
188
|
+
rb_ary_store(rtn, 0, ID2SYM(rb_intern(mr_deleted_p(marker) ? "merge" : "root")));
|
189
|
+
rb_ary_store(rtn, 1, INT2FIX(marker->part_a));
|
190
|
+
rb_ary_store(rtn, 2, INT2FIX(marker->part_b));
|
191
|
+
} else {
|
192
|
+
rtn = rb_ary_new2(1);
|
193
|
+
rb_ary_store(rtn, 0, ID2SYM(rb_intern(mr_deleted_p(marker) ? "leaf" : "single")));
|
194
|
+
}
|
195
|
+
return rtn;
|
196
|
+
}
|
197
|
+
return Qnil;
|
198
|
+
}
|
199
|
+
|
200
|
+
static VALUE rb_api_deleted(VALUE self_value, VALUE index)
|
201
|
+
#define ARGC_deleted 1
|
202
|
+
{
|
203
|
+
MARKER_LIST_FOR_VALUE_DECL(self);
|
204
|
+
int i = NUM2INT(index);
|
205
|
+
if (0 <= i && i < self->size)
|
206
|
+
return mr_deleted_p(self->markers + i) ? Qtrue : Qfalse;
|
207
|
+
return Qnil;
|
208
|
+
}
|
209
|
+
|
210
|
+
static VALUE rb_api_compress(VALUE self_value)
|
211
|
+
#define ARGC_compress 0
|
212
|
+
{
|
213
|
+
MARKER_LIST_FOR_VALUE_DECL(self);
|
214
|
+
compress(self);
|
215
|
+
return INT2FIX(self->size);
|
216
|
+
}
|
217
|
+
|
218
|
+
static VALUE rb_api_merge(VALUE self_value)
|
219
|
+
#define ARGC_merge 0
|
220
|
+
{
|
221
|
+
MARKER_LIST_FOR_VALUE_DECL(self);
|
222
|
+
ensure_headroom(self);
|
223
|
+
compress(self);
|
224
|
+
self->size = merge_markers_fast(self->info, self->markers, self->size);
|
225
|
+
return INT2FIX(self->size);
|
226
|
+
}
|
227
|
+
|
228
|
+
#define FUNCTION_TABLE_ENTRY(Name) { #Name, RUBY_METHOD_FUNC(rb_api_ ## Name), ARGC_ ## Name }
|
229
|
+
|
230
|
+
static struct ft_entry {
|
231
|
+
const char *name;
|
232
|
+
VALUE (*func)(ANYARGS);
|
233
|
+
int argc;
|
234
|
+
} function_table[] = {
|
235
|
+
FUNCTION_TABLE_ENTRY(add),
|
236
|
+
FUNCTION_TABLE_ENTRY(compress),
|
237
|
+
FUNCTION_TABLE_ENTRY(clear),
|
238
|
+
FUNCTION_TABLE_ENTRY(deleted),
|
239
|
+
FUNCTION_TABLE_ENTRY(initialize_copy),
|
240
|
+
FUNCTION_TABLE_ENTRY(length),
|
241
|
+
FUNCTION_TABLE_ENTRY(marker),
|
242
|
+
FUNCTION_TABLE_ENTRY(merge),
|
243
|
+
FUNCTION_TABLE_ENTRY(parts),
|
244
|
+
FUNCTION_TABLE_ENTRY(set_info),
|
245
|
+
};
|
246
|
+
|
247
|
+
#define STRING_CONST_TABLE_ENTRY(Name) { #Name, Name }
|
248
|
+
|
249
|
+
static struct sct_entry {
|
250
|
+
const char *name;
|
251
|
+
const char *val;
|
252
|
+
} string_const_table[] = {
|
253
|
+
STRING_CONST_TABLE_ENTRY(EXT_VERSION)
|
254
|
+
};
|
255
|
+
|
256
|
+
void Init_lulu(void)
|
257
|
+
{
|
258
|
+
VALUE module = rb_define_module("Lulu");
|
259
|
+
VALUE klass = rb_define_class_under(module, "MarkerList", rb_cObject);
|
260
|
+
rb_define_alloc_func(klass, rb_api_new_marker_list);
|
261
|
+
|
262
|
+
for (int i = 0; i < STATIC_ARRAY_SIZE(function_table); i++) {
|
263
|
+
struct ft_entry *e = function_table + i;
|
264
|
+
rb_define_method(klass, e->name, e->func, e->argc);
|
265
|
+
}
|
266
|
+
|
267
|
+
for (int i = 0; i < STATIC_ARRAY_SIZE(string_const_table); i++) {
|
268
|
+
struct sct_entry *e = string_const_table + i;
|
269
|
+
rb_define_const(module, e->name, rb_str_new2(e->val));
|
270
|
+
}
|
271
|
+
}
|