pangdudu-mamba 0.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc ADDED
@@ -0,0 +1,26 @@
1
+ = Mamba
2
+
3
+ Hi girls and boys!
4
+
5
+ This is a Qt4 ruby tool for analysis of ART tracker data time series generated
6
+ by the ART motion capturing system.
7
+
8
+ == Installation
9
+
10
+ sudo gem install pangdudu-mamba --source=http://gems.github.com
11
+
12
+ == Usage
13
+
14
+ ruby mamba.rb
15
+
16
+ == Config
17
+
18
+ Is still hardcoded in the source files. :)
19
+
20
+ == Rule the universe!
21
+
22
+ Oh mighty gods of ruby, please make the GIL go away!
23
+
24
+ == License
25
+
26
+ GPL -> http://www.gnu.org/licenses/gpl.txt
@@ -0,0 +1,230 @@
1
+ =begin
2
+ This file e.g. the TrackerDataParser fetches the tracker data from the
3
+ database, and parses them according to the "SFB673TPB1 Annotation Manual".
4
+ =end
5
+
6
+ require "rubygems"
7
+ require "narray"
8
+ require "sequel"
9
+ require "rofl"
10
+
11
+ #connect to the database
12
+ DB = Sequel.connect(:adapter=>'mysql', :host=>'localhost', :database=>'database', :user=>'user', :password=>'password')
13
+
14
+ class TrackerDataParser
15
+
16
+ attr_accessor :data
17
+
18
+ def initialize trial,test=false
19
+ @trial = trial
20
+ tablename = "Tracker#{trial}"
21
+ tablename = "TrackerTest" if test
22
+ dlog "Trying to connect to table: #{tablename}"
23
+ #get our rows
24
+ @rows = DB[tablename.to_sym]
25
+ @highpass = 0.25 #for later filtering
26
+ dlog "Table has #{@rows.count} rows."
27
+ #processed data will end up here
28
+ @data = {:left => {}, :right => {}}
29
+ #do we know that one marker has been worn wrong?
30
+ @data[:left_twist] = false
31
+ @data[:right_twist] = false
32
+ #get and parse the tracker data
33
+ operate
34
+ end
35
+
36
+ #main controller function
37
+ def operate
38
+ @data[:reference] = (parsetrackerdata @rows.filter(:identifier => "Neck")).sort
39
+ dlog "Parsed neckdata count is #{@data[:reference].length}"
40
+ left_raw = (parsetrackerdata @rows.filter(:identifier => "Left_Hand")).sort
41
+ dlog "Left hand quality is #{(Float(left_raw.length)/Float(@data[:reference].length))*100}%"
42
+ right_raw = (parsetrackerdata @rows.filter(:identifier => "Right_Hand")).sort
43
+ dlog "Right hand quality is #{(Float(right_raw.length)/Float(@data[:reference].length))*100}%"
44
+
45
+ #sometimes people are wearing the tracker wrong, need to fix this
46
+ fix_marker_twist
47
+
48
+ #now parse the tracker data for both hands
49
+ left_raw.each do |timestamp,matrix|
50
+ d = getannotationdata timestamp,matrix,@data[:left_polarity]
51
+ @data[:left][timestamp] = d unless d.nil?
52
+ end
53
+ right_raw.each do |timestamp,matrix|
54
+ d = getannotationdata timestamp,matrix,@data[:right_polarity]
55
+ @data[:right][timestamp] = d unless d.nil?
56
+ end
57
+ #Debug and info
58
+ ilog "Statistics for trial #{@trial}:"
59
+ ilog " Left hand - parsed: #{@data[:left].length}"
60
+ ilog " Right hand - parsed: #{@data[:right].length}"
61
+ end
62
+
63
+ #method parsing tracking data from the db and loading it into an hash(organized by timestamp)
64
+ def parsetrackerdata trackerdata
65
+ parseddata = {}
66
+ trackerdata.each do |d|
67
+ m = NMatrix.float(4,4)
68
+ #that's how the coordinates would look if the matrix was transposed (now the bottom is 0,0,0,1)
69
+ m[0,0],m[0,1],m[0,2],m[0,3] = d[:m00], d[:m01], d[:m02], d[:m03]
70
+ m[1,0],m[1,1],m[1,2],m[1,3] = d[:m10], d[:m11], d[:m12], d[:m13]
71
+ m[2,0],m[2,1],m[2,2],m[2,3] = d[:m20], d[:m21], d[:m22], d[:m23]
72
+ m[3,0],m[3,1],m[3,2],m[3,3] = d[:m30], d[:m31], d[:m32], d[:m33]
73
+ m = m.transpose #matrix data in the db are transposed
74
+ parseddata[Float(d[:timestamp])] = m
75
+ end
76
+ return parseddata
77
+ end
78
+
79
+ def getannotationdata timestamp,matrix,polarity
80
+ annotationdata = {}
81
+ reference = @data[:reference][timestamp][1]
82
+ relative = reference.inverse * matrix * polarity
83
+ annotationdata[:timestamp] = timestamp
84
+ annotationdata[:reference] = reference
85
+ annotationdata[:data] = matrix
86
+ annotationdata[:relative] = relative
87
+ annotationdata[:boh] = getbackofhanddirection relative
88
+ annotationdata[:pd] = getpalmdirection relative
89
+ annotationdata[:wp] = getwristposition relative
90
+ annotationdata[:we] = getwristextend relative
91
+ return annotationdata
92
+ end
93
+
94
+ #function computing palm direction
95
+ def getpalmdirection data
96
+ #negative z seems to be the palm direction
97
+ #you should check the data in the visualization to be sure
98
+ x,y,z = (-1)*data[2,0],(-1)*data[2,1],(-1)*data[2,2]
99
+ result = []
100
+ result << "PAB" if z < -@highpass
101
+ result << "PDN" if y < -@highpass
102
+ result << "PTB" if z > @highpass
103
+ result << "PTL" if x < -@highpass
104
+ result << "PTR" if x > @highpass
105
+ result << "PUP" if y > @highpass
106
+ return result.join("/")
107
+ end
108
+
109
+ #function computing back of hand direction
110
+ def getbackofhanddirection data
111
+ #ok, it looks like the markers y axis is the boh direction
112
+ #you should check the data in the visualization to be sure
113
+ x,y,z = data[1,0],data[1,1],data[1,2]
114
+ result = []
115
+ result << "BAB" if z < -@highpass
116
+ result << "BDN" if y < -@highpass
117
+ result << "BTB" if z > @highpass
118
+ result << "BTL" if x < -@highpass
119
+ result << "BTR" if x > @highpass
120
+ result << "BUP" if y > @highpass
121
+ return result.join("/")
122
+ end
123
+
124
+ #function computing wrist extend
125
+ def getwristextend data
126
+ result = "aight!"
127
+ x,y,z = data[3,0],data[3,1],data[3,2]
128
+ #puts "wp: x '#{x}', y '#{y}', z '#{z}'"
129
+ zf = 100 #normalizing factor (100 = SH standard human)
130
+ xf = 100 #normalizing factor (100 = SH standard human)
131
+ x = x*xf
132
+ z = z*zf
133
+ r = Math.sqrt(x*x + z*z)
134
+ #ok, need to put real values in here (look at the annotationmanual)
135
+ result = "D-GTO" if r > 80 #greater than length of outstretched arm in front away
136
+ result = "D-KO" if r <= 80 #between knee and length of outstretched arm in front away
137
+ result = "D-EK" if r < 65 #between elbow and knee
138
+ result = "D-CE" if r < 45 #between body and elbows length away
139
+ result = "D-C" if r < 20 #in contact with body
140
+ #puts "extend is '#{result}'"
141
+ return result
142
+ end
143
+
144
+ #function computing wrist position
145
+ def getwristposition data
146
+ result = "aight!"
147
+ #negative z seems to be palm direction
148
+ #you should check the data in the visualization to be sure
149
+ x,y,z = data[3,0],data[3,1],data[3,2]
150
+ yf = 100
151
+ xf = 100
152
+ x = x*xf
153
+ y = y*yf
154
+ #puts "wp: x '#{x}', y '#{y}', z '#{z}'"
155
+ #will not be rock solid, so we go from outwards->inwards
156
+ #extreme periphery
157
+ eperiphery = "EP-"
158
+ eperiphery += "UP" if(y > 30 && x.abs <= 19)
159
+ eperiphery += "UL" if(y > 17 && x < -19)
160
+ eperiphery += "UR" if(y > 17 && x > 19)
161
+ eperiphery += "LT" if(y <= 17 && y > -17 && x < -31.5)
162
+ eperiphery += "RT" if(y <= 17 && y > -17 && x > 31.5)
163
+ eperiphery += "LW" if(y < -30 && x.abs <= 19)
164
+ eperiphery += "LL" if(y < -17 && x < -19)
165
+ eperiphery += "LR" if(y < -17 && x > 19)
166
+ result = eperiphery unless eperiphery.eql?("EP-")
167
+ #periphery
168
+ if (x.abs <= 31.5 && y.abs <= 30)
169
+ periphery = "P-"
170
+ periphery += "UP" if(y > 17 && x.abs <= 13)
171
+ periphery += "UL" if(y > 9 && x < -13)
172
+ periphery += "UR" if(y > 9 && x > 13)
173
+ periphery += "LT" if(y <= 9 && y > -10 && x < -21)
174
+ periphery += "RT" if(y <= 9 && y > -10 && x > 21)
175
+ periphery += "LW" if(y <= -10 && y > -30 && x.abs <= 13)
176
+ periphery += "LL" if(y <= -10 && y > -30 && x < -13)
177
+ periphery += "LR" if(y <= -10 && y > -30 && x > 13)
178
+ result = periphery unless periphery.eql?("P-")
179
+ end
180
+ #center
181
+ if (x.abs <= 21 && y.abs <= 17)
182
+ center = "C-"
183
+ center += "UP" if(y > 9 && x.abs <= 10)
184
+ center += "UL" if(y > 4.5 && x < -10)
185
+ center += "UR" if(y > 4.5 && x > 10)
186
+ center += "LT" if(y <= 4.5 && y > -7 && x < -13)
187
+ center += "RT" if(y <= 4.5 && y > -7 && x > 13)
188
+ center += "LW" if(y <= -7 && y > -17 && x.abs <= 10)
189
+ center += "LL" if(y <= -7 && y > -17 && x < -10)
190
+ center += "LR" if(y <= -7 && y > -17 && x > 10)
191
+ result = center unless center.eql?("C-")
192
+ end
193
+ #center-center
194
+ if (x.abs <= 13 && y.abs <= 9)
195
+ result = "CC"
196
+ end
197
+ #puts "result #{result}"
198
+ return result
199
+ end
200
+
201
+ #method to fix the direction of the marker, if it has been worn wrong
202
+ def fix_marker_twist
203
+ #left hand
204
+ @data[:left_polarity] = NMatrix.float(4,4).unit
205
+ if @data[:left_twist]
206
+ p = NMatrix.float(4,4).unit
207
+ #if y direction of tracker is WRONG we need to rotate around z axis by 180 deg
208
+ p[0,0], p[0,1], p[1,0], p[1,1] = Math.cos(Math::PI),-Math.sin(Math::PI),Math.sin(Math::PI),Math.cos(Math::PI)
209
+ ilog "Will multiply left hand with this polarity matrix: #{p.inspect}"
210
+ @data[:left_polarity] = p
211
+ end
212
+ #and the right hand
213
+ @data[:right_polarity] = NMatrix.float(4,4).unit
214
+ if @data[:right_twist]
215
+ p = NMatrix.float(4,4).unit
216
+ #if y direction of tracker is WRONG we need to rotate around z axis by 180 deg
217
+ p[0,0], p[0,1], p[1,0], p[1,1] = Math.cos(Math::PI),-Math.sin(Math::PI),Math.sin(Math::PI),Math.cos(Math::PI)
218
+ ilog "Will multiply right hand with this polarity matrix: #{p.inspect}"
219
+ @data[:right_polarity] = p
220
+ end
221
+ end
222
+ end
223
+
224
+ #for testing
225
+ if false
226
+ tdp = TrackerDataParser.new "V06",true #will select table TrackerTest
227
+ require "openglviewer"
228
+ ogv = OpenGlViewer.new tdp.data
229
+ ogv.run
230
+ end
@@ -0,0 +1,278 @@
1
+ require 'rubygems'
2
+ require 'opengl'
3
+ require 'rational'
4
+ require 'narray'
5
+ require 'rofl'
6
+ #include Gl,Glu,Glut
7
+
8
+ class OpenGlViewer
9
+
10
+ attr_accessor :currenttimestamp,:timestamps,:pause
11
+
12
+ STDOUT.sync = TRUE
13
+ POS = [0, 0, 0, 0.0]
14
+
15
+ def initialize data
16
+ @data = data
17
+ @base = NMatrix.float(4,4).unit
18
+ @matrices = {}
19
+ @matrices[:base] = @base
20
+ @currenttimestamp = 0.0
21
+ @current = 0
22
+ @timestamps = {}
23
+ @pause = true
24
+ @forward = true
25
+ @leftboh = "none"
26
+ @rightboh = "none"
27
+ filltimestamps
28
+ glShadeModel(GL::SMOOTH)
29
+ glNormal(0.0, 0.0, 1.0)
30
+ glLightfv(GL::LIGHT0, GL::POSITION, POS)
31
+ glEnable(GL::CULL_FACE)
32
+ glEnable(GL::LIGHTING)
33
+ glEnable(GL::LIGHT0)
34
+ glEnable(GL::DEPTH_TEST)
35
+ glEnable(GL::LINE_SMOOTH)
36
+ glEnable(GL::POINT_SMOOTH)
37
+ glClearColor(0.0, 0.0, 0.0, 0.0)
38
+ end
39
+
40
+ def filltimestamps
41
+ i = 0
42
+ @data[:reference].each do |timestamp,value|
43
+ @timestamps[i] = timestamp
44
+ i += 1
45
+ end
46
+ ilog "Found '#{@timestamps.count}'"
47
+ ilog "Will now try to display '#{@data[:left].count}' left and '#{@data[:right].count}' right hand values"
48
+ end
49
+
50
+ def fillmatrices
51
+ unless @data[:left][@currenttimestamp].nil?
52
+ #@matrices[:reference] = @data[:left][@currenttimestamp][:reference]
53
+ #@matrices[:left_hand_data] = @data[:left][@currenttimestamp][:data]
54
+ @matrices[:left_hand_relative] = @data[:left][@currenttimestamp][:relative]
55
+ @leftboh = @data[:left][@currenttimestamp][:boh]
56
+ end
57
+ unless @data[:right][@currenttimestamp].nil?
58
+ #@matrices[:reference] = @data[:right][@currenttimestamp][:reference]
59
+ #@matrices[:right_hand_data] = @data[:right][@currenttimestamp][:data]
60
+ @matrices[:right_hand_relative] = @data[:right][@currenttimestamp][:relative]
61
+ @rightboh = @data[:right][@currenttimestamp][:boh]
62
+ end
63
+ end
64
+
65
+ def setcurrenttimestamp
66
+ unless @pause
67
+ if @forward
68
+ @current += 1
69
+ @currenttimestamp = @timestamps[@current]
70
+ end
71
+ if !@forward
72
+ @current -= 1 unless (@current.eql? 0)
73
+ @currenttimestamp = @timestamps[@current]
74
+ end
75
+ end
76
+ end
77
+
78
+ # gl display method
79
+ def display
80
+ fillmatrices
81
+ setcurrenttimestamp
82
+ fps_control(60)
83
+ glClear(GL::COLOR_BUFFER_BIT | GL::DEPTH_BUFFER_BIT)
84
+ glColor(0.0, 0.0, 0.0)
85
+ glPushMatrix()
86
+ glRotate(@view_rotx, 1.0, 0.0, 0.0)
87
+ glRotate(@view_roty, 0.0, 1.0, 0.0)
88
+ glRotate(@view_rotz, 0.0, 0.0, 1.0)
89
+ @matrices.each { |k,v| draw_object k,v }
90
+ displayinfo
91
+ glPopMatrix()
92
+ glutSwapBuffers()
93
+ @frames = 0 if not defined? @frames
94
+ @t0 = 0 if not defined? @t0
95
+ @frames += 1
96
+ t = GLUT.Get(GLUT::ELAPSED_TIME)
97
+ if t - @t0 >= 5000
98
+ seconds = (t - @t0) / 1000.0
99
+ fps = @frames / seconds
100
+ @t0, @frames = t, 0
101
+ exit if defined? @autoexit and t >= 999.0 * @autoexit
102
+ end
103
+ end
104
+
105
+ def drawOneLine x1,y1,z1,x2,y2,z2
106
+ glBegin(GL_LINES)
107
+ glVertex(x1,y1,z1)
108
+ glVertex(x2,y2,z2)
109
+ glEnd()
110
+ end
111
+
112
+ def drawLineWithText text,scale,x,y,z
113
+ scale2 = scale*scale
114
+ glPushMatrix()
115
+ drawOneLine(0,0,0,scale*x,scale*y,scale*z)
116
+ glTranslate((scale*x),(scale*y),(scale*z))
117
+ glScale(1.0/(scale2),1.0/(scale2),1.0/(scale2))
118
+ text.each_byte { |x| glutStrokeCharacter(GLUT_STROKE_ROMAN, x) }
119
+ glScale(scale2,scale2,scale2)
120
+ glTranslate(-(scale*x),-(scale*y),-(scale*z))
121
+ glPopMatrix()
122
+ end
123
+
124
+ # draw an imd and its childs
125
+ def draw_object key,matrix
126
+ glPushMatrix()
127
+ scale = 10
128
+ translation_scale = 2
129
+ glTranslate(matrix[3,0]*scale*translation_scale,matrix[3,1]*scale*translation_scale,matrix[3,2]*scale*translation_scale)
130
+ glColor(1,0,0)
131
+ drawLineWithText "#{key} x",scale,matrix[0,0],matrix[0,1],matrix[0,2]
132
+ glColor(0,1,0)
133
+ drawLineWithText "#{key} y",scale,matrix[1,0],matrix[1,1],matrix[1,2]
134
+ glColor(0,0,1)
135
+ drawLineWithText "#{key} z",scale,matrix[2,0],matrix[2,1],matrix[2,2]
136
+ glTranslate(-matrix[3,0]*scale*translation_scale,-matrix[3,1]*scale*translation_scale,-matrix[3,2]*scale*translation_scale)
137
+ glPopMatrix()
138
+ end
139
+
140
+ def displayinfo
141
+ info = "Information:"
142
+ info += " timestamp: #{(@timestamps[@current]).to_s}"
143
+ info += " PAUSED" if @pause
144
+ info += " FORWARD" if (!@pause && @forward)
145
+ info += " REVERSE" if (!@pause && !@forward)
146
+ info += " BOH left:#{@leftboh} right:#{@rightboh}"
147
+ scale = 10
148
+ scale2 = scale*scale
149
+ glPushMatrix()
150
+ glTranslate((scale),(scale),(scale))
151
+ glScale(1.0/(scale2),1.0/(scale2),1.0/(scale2))
152
+ info.each_byte { |x| glutStrokeCharacter(GLUT_STROKE_ROMAN, x) }
153
+ glScale(scale2,scale2,scale2)
154
+ glTranslate(-(scale),-(scale),-(scale))
155
+ glPopMatrix()
156
+ end
157
+
158
+ def fps_control(fps)
159
+ t = GLUT.Get(GLUT::ELAPSED_TIME)
160
+ @t0_idle = t if !defined? @t0_idle
161
+ time_to_sleep = (1000./fps) - (t - @t0_idle)
162
+ sleep(time_to_sleep.to_f/1000) if time_to_sleep > 0
163
+ @t0_idle = t
164
+ end
165
+
166
+ # gets called when window is resized etc.
167
+ def reshape width, height
168
+ h = height.to_f / width.to_f
169
+ glViewport(0, 0, width, height)
170
+ glMatrixMode(GL::PROJECTION)
171
+ glLoadIdentity()
172
+ glFrustum(-1.0, 1.0, -h, h, 1.0, 1000.0)
173
+ glMatrixMode(GL::MODELVIEW)
174
+ setViewpoint
175
+ end
176
+
177
+ def idle
178
+ GLUT.PostRedisplay()
179
+ end
180
+
181
+ def setViewpoint
182
+ glMatrixMode(GL_MODELVIEW)
183
+ glLoadIdentity()
184
+ gluLookAt(@eyex, 0, @znear, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0)
185
+ glutPostRedisplay()
186
+ end
187
+
188
+ # Change view angle
189
+ def special (k, x, y)
190
+ case k
191
+ when GLUT::KEY_UP
192
+ @view_rotx += 5.0
193
+ when GLUT::KEY_DOWN
194
+ @view_rotx -= 5.0
195
+ when GLUT::KEY_LEFT
196
+ @view_roty += 5.0
197
+ when GLUT::KEY_RIGHT
198
+ @view_roty -= 5.0
199
+ end
200
+ GLUT.PostRedisplay()
201
+ end
202
+
203
+ # mouse ;)
204
+ def mouse button, state, x, y
205
+ changed = false
206
+ case button
207
+ when 3 then @eyex -= 5; changed = true;
208
+ when 4 then @eyex += 5; changed = true
209
+ end
210
+ setViewpoint if changed
211
+ @mouse = state
212
+ @x0, @y0 = x, y
213
+ end
214
+
215
+ # motion control
216
+ def motion x, y
217
+ if @mouse == GLUT::DOWN then
218
+ @view_rotz += @x0 - x
219
+ @view_roty += @y0 - y
220
+ setViewpoint
221
+ end
222
+ @x0, @y0 = x, y
223
+ end
224
+
225
+ #keyboard control handling
226
+ def keyboard(key, x, y)
227
+ case (key)
228
+ when ?s
229
+ @znear = @znear - 1
230
+ when ?w
231
+ @znear = @znear + 1
232
+ when ?a
233
+ @eyex = @eyex - 1
234
+ when ?d
235
+ @eyex = @eyex + 1
236
+ when ?f
237
+ @forward = true
238
+ when ?r
239
+ @forward = false
240
+ when ?p
241
+ togglepause
242
+ when 27 # Escape
243
+ exit
244
+ end
245
+ setViewpoint
246
+ end
247
+
248
+ def togglepause
249
+ @pause = !@pause
250
+ puts "Toggle pause now '#{@pause}'"
251
+ end
252
+
253
+ def visible(vis)
254
+ GLUT.IdleFunc((vis == GLUT::VISIBLE ? method(:idle).to_proc : nil))
255
+ end
256
+ #the loop
257
+ def run
258
+ @znear = 30
259
+ @eyex = 0
260
+ @view_rotx, @view_roty, @view_rotz = 0, 0, 0
261
+ glutInit
262
+ glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB)
263
+ glutInitWindowSize(640, 480)
264
+ glutInitWindowPosition(0, 0)
265
+ glutCreateWindow($0)
266
+ glutDisplayFunc(method(:display).to_proc)
267
+ glutReshapeFunc(method(:reshape).to_proc)
268
+ glutSpecialFunc(method(:special).to_proc)
269
+ glutKeyboardFunc(method(:keyboard).to_proc)
270
+ glutMouseFunc(method(:mouse).to_proc)
271
+ glutMotionFunc(method(:motion).to_proc)
272
+ glutVisibilityFunc(method(:visible).to_proc)
273
+ glShadeModel(GL::SMOOTH)
274
+ glNormal(0.0, 0.0, 1.0)
275
+ glClearColor(0.0, 0.0, 0.0, 0.0)
276
+ glutMainLoop()
277
+ end
278
+ end
data/lib/graph.rb ADDED
@@ -0,0 +1,194 @@
1
+ =begin
2
+ This class is a little B1 specific. It implements the graph we'll later
3
+ visualize.
4
+ =end
5
+
6
+ require "rubygems"
7
+ require "narray"
8
+ require "Qt4"
9
+ require "rofl"
10
+ require "b1trackerdata/b1_data_parser"
11
+
12
+ class Graph
13
+
14
+ attr_accessor :points,:deltas
15
+
16
+ def initialize trial,deltas,hfrac,test=false
17
+ #deltas we will compute
18
+ @deltas = deltas
19
+ ilog "Will compute deltas for #{@deltas.inspect}."
20
+ #in case the deltas are to small/big change this factor
21
+ @delta_factor = 256
22
+ #in case the angles are to small/big change this factor
23
+ @euler_factor = hfrac
24
+ #this factor governs how many pixels the points are away from another
25
+ @x_factor = 4
26
+ #get the data from the database
27
+ parser = TrackerDataParser.new trial,test
28
+ @data = parser.data
29
+ #generate unique array with all occuring timestamps and prepare @values
30
+ @timestamps = build_timestamps
31
+ #fill values (deltas,etc.)
32
+ @previous = {:left => NMatrix.float(4,4), :right => NMatrix.float(4,4)}
33
+ fill_values
34
+ #build the points array (this will be used later on to draw the graph)
35
+ @points = build_points
36
+ end
37
+
38
+ #build an array with all occuring timestamps
39
+ def build_timestamps
40
+ @values = {}
41
+ tss = []
42
+ @data[:reference].each { |t,v| tss << t }
43
+ @data[:left].each { |t,v| tss << t }
44
+ @data[:right].each { |t,v| tss << t }
45
+ dlog "tss count: #{tss.length}"
46
+ timestamps = (tss.uniq).sort
47
+ dlog "Unique timestamps: #{timestamps.length}"
48
+ dlog "Unique timestamps first: #{timestamps.first}"
49
+ #now preparing @values
50
+ timestamps.each do |t|
51
+ @values[t] = {:left => {}, :right => {}}
52
+ end
53
+ return timestamps
54
+ end
55
+
56
+ #fill values with parsed data
57
+ def fill_values
58
+ [:left,:right].each do |hand|
59
+ dlog "Data for #{hand.inspect} has #{@data[hand].length} entries."
60
+ #it's crucial to do sort here, otherwise the timestamps are mixed up
61
+ @data[hand].sort.each do |t,v|
62
+ wlog "Empty or nil value hash!" if v.nil? or v.empty?
63
+ #we have tracker data
64
+ v[:blackout] = false
65
+ #will only happen the first time
66
+ @previous[hand] = v[:relative] if @previous[hand].abs.sum == 0.0
67
+ #compute euler angles
68
+ v[:euler] = compute_euler_angles(v)
69
+ #compute deltas for plotting
70
+ v[:deltas] = compute_deltas(hand,v)
71
+ #for next delta computation
72
+ @previous[hand] = v[:relative]
73
+ #put values into @values
74
+ @values[t][hand] = v
75
+ end
76
+ end
77
+ dlog "Processed #{@values.length} values."
78
+ end
79
+
80
+ #compute euler angles for plotting
81
+ #http://en.wikipedia.org/wiki/Euler_angles
82
+ def compute_euler_angles values
83
+ #we'll put the angles in here
84
+ e = {}
85
+ #get the hand matrix
86
+ m = values[:relative]
87
+ #alpha = atan2(-Z2, Z1)
88
+ e[:alpha] = { :value => Math.atan2(-1.0*m[2,1],m[2,0]) }
89
+ #beta = atan2(Z3, sqrt(Z1^2+Z2^2))
90
+ e[:beta] = { :value => Math.atan2(m[2,2],Math.sqrt(m[2,0]**2+m[2,1]**2)) }
91
+ #gamma = -atan2(Y3,-X3) if Z3 < 0
92
+ e[:gamma] = { :value => -1.0*Math.atan2(m[1,2],-1.0*m[0,2]) } if m[2,2] <= 0.0
93
+ #gamma = atan2(Y3,X3) if Z3 > 0
94
+ e[:gamma] = { :value => Math.atan2(m[1,2],m[0,2]) } if m[2,2] > 0.0
95
+ return e
96
+ end
97
+
98
+ #compute deltas for plotting
99
+ def compute_deltas hand,values,t=0
100
+ #we'll put the deltas in here
101
+ ds = {}
102
+ #get the matrix
103
+ m = values[:relative]
104
+ #to reduce computation time we only compute what's needed
105
+ if @deltas.include? :simple
106
+ #compute simple delta over the complete 4x4 matrix
107
+ value = (m-@previous[hand]).abs.sum
108
+ ds[:simple] = { :value => value }
109
+ end
110
+ if @deltas.include? :pos
111
+ #compute simple delta over the position vector
112
+ value = (m[3,0..2]-@previous[hand][3,0..2]).abs.sum
113
+ ds[:pos] = { :value => value }
114
+ end
115
+ if @deltas.include? :rot
116
+ #compute simple delta over the 3x3 rotation matrix
117
+ value = (m[0..2,0..2]-@previous[hand][0..2,0..2]).abs.sum
118
+ ds[:rot] = { :value => value }
119
+ end
120
+ if @deltas.include? :xrot
121
+ #compute simple delta over the x-rotation axis vector
122
+ value = (m[0,0..2]-@previous[hand][0,0..2]).abs.sum
123
+ ds[:xrot] = { :value => value }
124
+ end
125
+ if @deltas.include? :yrot
126
+ #compute simple delta over the y-rotation axis vector
127
+ value = (m[1,0..2]-@previous[hand][1,0..2]).abs.sum
128
+ ds[:yrot] = { :value => value }
129
+ end
130
+ if @deltas.include? :zrot
131
+ #compute simple delta over the z-rotation axis vector
132
+ value = (m[2,0..2]-@previous[hand][2,0..2]).abs.sum
133
+ ds[:zrot] = { :value => value }
134
+ end
135
+ #and return the computed deltas
136
+ return ds
137
+ end
138
+
139
+ #build the points array we'll use later on to draw the graph
140
+ def build_points
141
+ @timestamps.each_index do |i|
142
+ ts = @timestamps[i]
143
+ #do the following for both hands
144
+ [:left,:right].each do |h|
145
+ #if tracker data are missing we substitute
146
+ if @values.has_key? ts
147
+ @values[ts][h] = get_missing_values if @values[ts][h].empty?
148
+ end
149
+ #set index and x position
150
+ @values[ts][h][:i] = i
151
+ x = i*@x_factor
152
+ @values[ts][h][:x] = x
153
+ #now we create a Qt::PointF.new(x,y) for each euler angle
154
+ @values[ts][h][:euler].each do |name,a|
155
+ #create Point, mirror hands on x axis and apply y-scaling
156
+ y = 0
157
+ unless @values[ts][h][:blackout]
158
+ y += @euler_factor-a[:value]*@euler_factor/Math::PI
159
+ y *= -1.0 if h.eql? :left
160
+ end
161
+ #and add the point to the euler values
162
+ @values[ts][h][:euler][name][:point] = Qt::PointF.new(x,y)
163
+ end
164
+ #now we create a Qt::PointF.new(x,y) for every delta type
165
+ @values[ts][h][:deltas].each do |type,delta|
166
+ #create Point, mirror hands on x axis and apply y-scaling
167
+ y = delta[:value]*@delta_factor
168
+ y *= -1.0 if h.eql? :left
169
+ #and add the point to the delta values
170
+ @values[ts][h][:deltas][type][:point] = Qt::PointF.new(x,y)
171
+ end
172
+ end
173
+ end
174
+ dlog "Processed #{@values.length} timestamps."
175
+ points = @values.sort
176
+ dlog "points array has #{points.length} entries."
177
+ return points
178
+ end
179
+
180
+ #in case values are missing for timestamps (no tracker data), we'll supply values
181
+ def get_missing_values
182
+ #we'll put the deltas in here
183
+ missing_deltas = {}
184
+ #put 0.0 to deltas
185
+ @deltas.each { |d| missing_deltas[d] = { :value => 0.0 } }
186
+ #we'll put the euler angles in here
187
+ missing_euler = {}
188
+ #put 0.0 to euler angles
189
+ [:alpha,:beta,:gamma].each { |a| missing_euler[a] = { :value => 0.0 } }
190
+ #and build the values array
191
+ v = { :deltas => missing_deltas, :euler => missing_euler, :blackout => true }
192
+ return v
193
+ end
194
+ end
data/lib/mamba.rb ADDED
@@ -0,0 +1,324 @@
1
+ require "rubygems"
2
+ require "rofl"
3
+ require "Qt4"
4
+ require "graph"
5
+ #require "unprof"
6
+
7
+ #oky, this will become a visualizer for large data sequences
8
+
9
+ class Gui < Qt::Widget
10
+
11
+ attr_accessor :width,:height
12
+ attr_accessor :x_delta,:mousex,:mousey
13
+
14
+ def initialize(width,height,parent = nil)
15
+ super()
16
+ @width,@height = width,height
17
+ resize(@width,@height)
18
+ @lfw = 16 # legend font width
19
+ @tfw = 15 # timestamp font width
20
+ @pfw = 11 # predicate font width
21
+ #text will not be filled
22
+ #@brush = Qt::NoBrush
23
+ #text will be filled
24
+ @brush = Qt::Brush.new Qt::Color.new 0, 0, 0
25
+ @current_color = 0
26
+ #generate these deltas
27
+ deltas = []
28
+ #display these predicates
29
+ @predicates = [:boh, :pd, :wp, :we]
30
+ #graph with the data we want to visualize
31
+ #@graph = Graph.new "V06",deltas,@height/12
32
+ @graph = Graph.new "",deltas,@height/12,true #will build graph from table 'TrackerTest'
33
+ #max movement speed
34
+ @speedlimit = 20
35
+ #setup the rest
36
+ setup
37
+ #method doing the x scrolling for us
38
+ dlog "Initialized, starting active scroll loop."
39
+ reactive_scroll
40
+ end
41
+
42
+ #setup all the stuff we need to display
43
+ def setup
44
+ #legend painter path
45
+ @legend = build_legend_path
46
+ #text painter path
47
+ @text = build_text_path
48
+ #graph painter paths
49
+ @graphs = setup_graph_paths
50
+ #now some gui specific stuff
51
+ setPalette(Qt::Palette.new(Qt::Color.new(250, 250, 250)))
52
+ setAutoFillBackground(true)
53
+ #cursor settings
54
+ @cur1 = self.cursor
55
+ @cur2 = Qt::Cursor.new(Qt::PointingHandCursor)
56
+ self.mouseTracking = true
57
+ #used for scrolling objects to the left and right
58
+ @xdelta,@xgoal,@xspeed = 0.0,0.0,0.0
59
+ end
60
+
61
+ #build painter path for the legend
62
+ def build_legend_path
63
+ p = Qt::PainterPath.new
64
+ p.moveTo 0,0
65
+ font = Qt::Font.new "Arial", @lfw
66
+ p.addText @lfw,1.5*@lfw, font, "LEFT: euler angles"
67
+ p.addText @lfw,2*@height/12, font, "RIGHT: euler angles"
68
+ p.addText @lfw,5*@height/12, font, "LEFT: data"
69
+ p.addText @lfw,7*@height/12, font, "RIGHT: data"
70
+ p.addText @lfw,10*@height/12-@lfw, font, "LEFT: blackout"
71
+ p.addText @lfw,10*@height/12+@lfw, font, "RIGHT: blackout"
72
+ p.addText @lfw,11*@height/12-@lfw/2, font, "timestamps"
73
+ dlog "Finished legend path setup."
74
+ return p
75
+ end
76
+
77
+ #build painter path for the text data
78
+ def build_text_path
79
+ #y to timestamps
80
+ ty = 11*@height/12
81
+ #back to left hand data
82
+ ypl = -5*@height/12-2*@lfw
83
+ #and back to right hand data
84
+ ypr = -3*@height/12-2*@lfw
85
+ tfont = Qt::Font.new "Arial", @tfw
86
+ pfont = Qt::Font.new "Arial", @pfw
87
+ every = 25 #display text every ... frames
88
+ current = 0
89
+ p = Qt::PainterPath.new
90
+ p.moveTo 0,0
91
+ @graph.points.each do |ts,hands|
92
+ if current >= every
93
+ p.addText hands[:left][:x],@lfw, tfont, "#{ts}"
94
+ #display the predicates
95
+ @predicates.each_index do |i|
96
+ p.addText hands[:left][:x], ypl+i*@lfw, pfont, "#{hands[:left][@predicates[i]]}"
97
+ p.addText hands[:right][:x], ypr+i*@lfw, pfont, "#{hands[:right][@predicates[i]]}"
98
+ end
99
+ current = 0
100
+ else
101
+ current += 1
102
+ end
103
+ end
104
+ dlog "Finished subtitles path setup."
105
+ return { :y => ty, :path => p }
106
+ end
107
+
108
+ #setup the graph paths we want to paint
109
+ def setup_graph_paths
110
+ dlog "Starting graph path setup."
111
+ graph_paths = {}
112
+ #build following graphs for both hands
113
+ [:left,:right].each do |hand|
114
+ #build painter paths for blackout graph
115
+ blackout = build_blackout_path(hand)
116
+ graph_paths[blackout[:name]] = blackout
117
+ dlog "Finished blackout path setup. Y: #{blackout[:y]}"
118
+ #build painter paths for euler angles
119
+ [:alpha,:beta,:gamma].each do |angle|
120
+ euler = build_euler_path(hand,angle)
121
+ graph_paths[euler[:name]] = euler
122
+ dlog "Finished #{euler[:name]} path setup. Y: #{euler[:y]}"
123
+ end
124
+ #build painter paths for deltas
125
+ @graph.deltas.each do |d|
126
+ delta = build_delta_path(hand,d)
127
+ graph_paths[delta[:name]] = delta
128
+ dlog "Finished #{delta[:name]} path setup. Y: #{delta[:y]}"
129
+ end
130
+ end
131
+ dlog "Finished graph path setup."
132
+ return graph_paths
133
+ end
134
+
135
+ #build painter path for blackout graph
136
+ def build_blackout_path hand
137
+ name = "blackout_#{hand}".to_sym
138
+ color = Qt::Color.new(0, 0, 64) if hand.eql? :left
139
+ color = Qt::Color.new(64, 0, 0) if hand.eql? :right
140
+ #y-translation of the graph, we only need y
141
+ ty = 10*@height/12+@lfw/2
142
+ ty -= @lfw if hand.eql? :left
143
+ ty += @lfw if hand.eql? :right
144
+ path = Qt::PainterPath.new
145
+ path.moveTo 0,0
146
+ #now we construct the path
147
+ @graph.points.each do |ts,v|
148
+ unless v[hand][:blackout]
149
+ path.moveTo v[hand][:x],0
150
+ else
151
+ path.lineTo v[hand][:x],0
152
+ end
153
+ end
154
+ #and return the values
155
+ return { :y => ty, :name => name, :path => path, :color => color, :width => 2.5 }
156
+ end
157
+
158
+ #build painter path for euler graph
159
+ def build_euler_path hand,angle
160
+ name = "#{angle}_#{hand}".to_sym
161
+ color = Qt::Color.new(255, 0, 0) if angle.eql? :alpha
162
+ color = Qt::Color.new(0, 255, 0) if angle.eql? :gamma
163
+ color = Qt::Color.new(0, 0, 255) if angle.eql? :beta
164
+ #y-translation of the graph, we only need y
165
+ ty = @height/6
166
+ path = Qt::PainterPath.new
167
+ path.moveTo 0,0
168
+ #now we construct the path
169
+ @graph.points.each do |ts,v|
170
+ path.lineTo v[hand][:euler][angle][:point]
171
+ end
172
+ #and return the values
173
+ return { :y => ty, :name => name, :path => path, :color => color }
174
+ end
175
+
176
+ #build painter path for delta graph
177
+ def build_delta_path hand,delta
178
+ name = "#{delta}_#{hand}".to_sym
179
+ color = Qt::Color.new(64, 0, @current_color%255) if hand.eql? :left
180
+ color = Qt::Color.new(@current_color%255, 0, 64) if hand.eql? :right
181
+ #change color for next delta
182
+ @current_color += 16
183
+ ty = 7*@height/12
184
+ path = Qt::PainterPath.new
185
+ path.moveTo 0,0
186
+ #now we construct the path
187
+ @graph.points.each do |ts,v|
188
+ path.lineTo v[hand][:deltas][delta][:point]
189
+ end
190
+ #and return the values
191
+ return { :y => ty, :name => name, :path => path, :color => color }
192
+ end
193
+
194
+ #gets called when a repaint is necessary
195
+ def paintEvent(event)
196
+ paint_graphs
197
+ paint_text
198
+ paint_legend
199
+ end
200
+
201
+ #outsourced graph painting
202
+ def paint_graphs
203
+ p = Qt::Painter.new(self)
204
+ p.setRenderHint(Qt::Painter::HighQualityAntialiasing)
205
+ p.setBrush Qt::NoBrush
206
+ @graphs.each do |name,path|
207
+ p.resetMatrix
208
+ p.translate((@width/2)-@xdelta,path[:y])
209
+ pen = Qt::Pen.new(Qt::SolidLine)
210
+ pen.setColor path[:color]
211
+ if path.has_key? :width
212
+ pen.setWidth path[:width]
213
+ else
214
+ pen.setWidth 1.5
215
+ end
216
+ p.setPen(pen)
217
+ p.drawPath path[:path]
218
+ end
219
+ p.end()
220
+ end
221
+
222
+ #outsourced text painting
223
+ def paint_text
224
+ p = Qt::Painter.new(self)
225
+ p.setRenderHint(Qt::Painter::HighQualityAntialiasing)
226
+ p.setBrush Qt::NoBrush
227
+ p.resetMatrix
228
+ p.translate((@width/2)-@xdelta,@text[:y])
229
+ pen = Qt::Pen.new(Qt::SolidLine)
230
+ pen.setWidth 2
231
+ p.setPen(pen)
232
+ p.drawPath @text[:path]
233
+ p.end()
234
+ end
235
+
236
+ #outsourced legend painting
237
+ def paint_legend
238
+ p = Qt::Painter.new(self)
239
+ p.setRenderHint(Qt::Painter::HighQualityAntialiasing)
240
+ p.resetMatrix
241
+ p.translate(0,0)
242
+ p.setBrush @brush
243
+ pen = Qt::Pen.new(Qt::SolidLine)
244
+ pen.setWidth 1
245
+ p.setPen(pen)
246
+ p.drawPath @legend
247
+ p.end()
248
+ end
249
+
250
+ #mouse press event
251
+ def mousePressEvent event
252
+ if event.buttons == Qt::RightButton
253
+ elsif event.buttons == Qt::LeftButton
254
+ #update
255
+ end
256
+ end
257
+
258
+ #a mouse move event
259
+ def mouseMoveEvent event
260
+ @mousex,@mousey = event.x,event.y
261
+ end
262
+
263
+ #ruby thread test
264
+ def reactive_scroll
265
+ fps = 60.0 #frames per second we attempt to compute
266
+ tts = 0.0 #time to sleep
267
+ last_time = Time.now
268
+ Thread.new do
269
+ loop do
270
+ tts = 1.0/fps - (Time.now-last_time)
271
+ sleep tts if tts > 0
272
+ unless @mousex.nil?
273
+ scroll_x
274
+ smooth_delta
275
+ if @xspeed.abs > 1
276
+ #are we speeding?
277
+ if @xspeed < -1.0*@speedlimit
278
+ @xspeed = -1.0*@speedlimit
279
+ @xgoal = @xdelta
280
+ end
281
+ if @xspeed > @speedlimit
282
+ @xspeed = @speedlimit
283
+ @xgoal = @xdelta
284
+ end
285
+ #new delta
286
+ @xdelta = (@xdelta + @xspeed).to_i
287
+ update
288
+ end
289
+ end
290
+ last_time = Time.now
291
+ end
292
+ end
293
+ end
294
+
295
+ #scroll on the x_axis if necessary
296
+ def scroll_x
297
+ damper = 10
298
+ #need some kind of zone that triggers scrolling: 1/4 width left and right
299
+ if @mousex < @width/4
300
+ @xgoal += (@mousex - @width/4)/damper
301
+ end
302
+ if @mousex > 3*@width/4
303
+ @xgoal += (@mousex - 3*@width/4)/damper
304
+ end
305
+ end
306
+
307
+ #method that smoothes the scroll deltas over time
308
+ def smooth_delta
309
+ @xspeed = (@xgoal-@xdelta)*0.05
310
+ end
311
+ end
312
+
313
+ #start the qt application
314
+ app = Qt::Application.new(ARGV)
315
+ gui = Gui.new(1200,760)
316
+ #dirty qt timer magic to make ruby threads work
317
+ block=Proc.new{ Thread.pass }
318
+ timer=Qt::Timer.new(gui)
319
+ invoke=Qt::BlockInvocation.new(timer, block, "invoke()")
320
+ Qt::Object.connect(timer, SIGNAL("timeout()"), invoke, SLOT("invoke()"))
321
+ timer.start(12.5) #in millis
322
+ #end of dirty timer hack
323
+ gui.show()
324
+ app.exec()
@@ -0,0 +1,150 @@
1
+ =begin
2
+ This class is used to generate test Tracker-Data to validate and debug
3
+ the other software.
4
+ =end
5
+
6
+ require "rubygems"
7
+ require "narray"
8
+ require "sequel"
9
+ require "rofl"
10
+
11
+ #connect to the database
12
+ DB = Sequel.connect(:adapter=>'mysql', :host=>'localhost', :database=>'database', :user=>'user', :password=>'password')
13
+
14
+ class TrackerTestDataGenerator
15
+
16
+ def initialize
17
+ tablename = "TrackerTest"
18
+ #drop old test tables
19
+ DB.drop_table tablename.to_sym
20
+ #create new table for test data
21
+ create_test_table tablename
22
+ #our access to the db is here
23
+ @rows = DB[tablename.to_sym]
24
+ @freq = 50.0 #data frequency in Hz
25
+ length = 60.0 #in seconds
26
+ generate_test_data length
27
+ ilog "Generated #{@rows.count} test frames."
28
+ end
29
+
30
+ #this method will generate some easy to debug test tracker data for Neck,Left Hand,Right Hand
31
+ def generate_test_data length
32
+ chunk = 1.0 / @freq #frame distance in seconds
33
+ frames = length * @freq #number of frames per identifier
34
+ #now we generate the test frames
35
+ (0..frames).each do |i|
36
+ timestamp = i*chunk
37
+ m = NMatrix.float(4,4).unit
38
+ #identifier specific changes
39
+ write_matrix_to_db timestamp,"Neck",m
40
+ write_matrix_to_db timestamp,"Left_Hand",get_left_hand(i)
41
+ write_matrix_to_db timestamp,"Right_Hand",get_right_hand(i)
42
+ end
43
+ end
44
+
45
+ #generate test data for left hand
46
+ def get_left_hand index
47
+ m = NMatrix.float(4,4).unit
48
+ #Left hand changes to position vector
49
+ m[3,0] = -1 #x
50
+ m[3,1] = 0.2 #y
51
+ m[3,2] = -0.5 #z
52
+ #make potential BOH point to BAB
53
+ rot_x = x_rotation_matrix -0.5 #rotate 90 degrees around x axis
54
+ m *= rot_x
55
+ #and now rotate around BOH axis at 1Hz
56
+ rot_y = y_rotation_matrix(-index*(2.0/@freq))
57
+ m *= rot_y
58
+ return m
59
+ end
60
+
61
+ #generate test data for right hand
62
+ def get_right_hand index
63
+ m = NMatrix.float(4,4).unit
64
+ #Right hand changes to position vector
65
+ m[3,0] = 1 #x
66
+ m[3,1] = 0.2 #y
67
+ m[3,2] = -0.5 #z
68
+ #make potential BOH point to BAB
69
+ rot_x = x_rotation_matrix -0.5 #rotate 90 degrees around x axis
70
+ m *= rot_x
71
+ #and now rotate around BOH axis at 1Hz
72
+ rot_y = y_rotation_matrix(index*(2.0/@freq))
73
+ m *= rot_y
74
+ return m
75
+ end
76
+
77
+ #get a rotation matrix for a rotation around the x axis
78
+ def x_rotation_matrix radian
79
+ m = NMatrix.float(4,4).unit
80
+ m[1,1] = Math.cos(radian*Math::PI)
81
+ m[1,2] = Math.sin(radian*Math::PI)
82
+ m[2,1] = Math.sin(radian*Math::PI)*(-1.0)
83
+ m[2,2] = Math.cos(radian*Math::PI)
84
+ return m
85
+ end
86
+
87
+ #get a rotation matrix for a rotation around the y axis
88
+ def y_rotation_matrix radian
89
+ m = NMatrix.float(4,4).unit
90
+ m[0,0] = Math.cos(radian*Math::PI)
91
+ m[0,2] = Math.sin(radian*Math::PI)*(-1.0)
92
+ m[2,0] = Math.sin(radian*Math::PI)
93
+ m[2,2] = Math.cos(radian*Math::PI)
94
+ return m
95
+ end
96
+
97
+ #get a rotation matrix for a rotation around the z axis
98
+ def z_rotation_matrix radian
99
+ m = NMatrix.float(4,4).unit
100
+ m[0,0] = Math.cos(radian*Math::PI)
101
+ m[0,1] = Math.sin(radian*Math::PI)
102
+ m[1,0] = Math.sin(radian*Math::PI)*(-1.0)
103
+ m[1,1] = Math.cos(radian*Math::PI)
104
+ return m
105
+ end
106
+
107
+ #write matrix to db
108
+ def write_matrix_to_db timestamp,identifier,matrix
109
+ m = matrix.transpose #matrix data in the db are transposed
110
+ #insert the matrix into the db
111
+ @rows.insert(:timestamp => timestamp, :identifier => identifier,
112
+ :xpos => m[0,3]*1000,:ypos => m[1,3]*1000,:zpos => m[2,3]*1000,
113
+ :m00 => m[0,0],:m10 => m[1,0],:m20 => m[2,0],:m30 => m[3,0],
114
+ :m01 => m[0,1],:m11 => m[1,1],:m21 => m[2,1],:m31 => m[3,1],
115
+ :m02 => m[0,2],:m12 => m[1,2],:m22 => m[2,2],:m32 => m[3,2],
116
+ :m03 => m[0,3],:m13 => m[1,3],:m23 => m[2,3],:m33 => m[3,3])
117
+ end
118
+
119
+ #create table to store test data into
120
+ def create_test_table tablename
121
+ DB.create_table tablename.to_sym do
122
+ primary_key :index
123
+ String :identifier
124
+ Double :timestamp
125
+ Float :xpos
126
+ Float :ypos
127
+ Float :zpos
128
+ #unity matrix is default
129
+ Float :m00, :default => 1.0
130
+ Float :m01, :default => 0.0
131
+ Float :m02, :default => 0.0
132
+ Float :m03, :default => 0.0
133
+ Float :m10, :default => 0.0
134
+ Float :m11, :default => 1.0
135
+ Float :m12, :default => 0.0
136
+ Float :m13, :default => 0.0
137
+ Float :m20, :default => 0.0
138
+ Float :m21, :default => 0.0
139
+ Float :m22, :default => 1.0
140
+ Float :m23, :default => 0.0
141
+ Float :m30, :default => 0.0
142
+ Float :m31, :default => 0.0
143
+ Float :m32, :default => 0.0
144
+ Float :m33, :default => 1.0
145
+ end
146
+ end
147
+ end
148
+
149
+ #generate some test data
150
+ test = TrackerTestDataGenerator.new
metadata ADDED
@@ -0,0 +1,128 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pangdudu-mamba
3
+ version: !ruby/object:Gem::Version
4
+ version: "0.1"
5
+ platform: ruby
6
+ authors:
7
+ - pangdudu
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-08-27 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: pangdudu-rofl
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: sequel
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: "0"
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: sequel_core
37
+ type: :runtime
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: "0"
44
+ version:
45
+ - !ruby/object:Gem::Dependency
46
+ name: sequel_model
47
+ type: :runtime
48
+ version_requirement:
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: "0"
54
+ version:
55
+ - !ruby/object:Gem::Dependency
56
+ name: mysql
57
+ type: :runtime
58
+ version_requirement:
59
+ version_requirements: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: "0"
64
+ version:
65
+ - !ruby/object:Gem::Dependency
66
+ name: narray
67
+ type: :runtime
68
+ version_requirement:
69
+ version_requirements: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: "0"
74
+ version:
75
+ - !ruby/object:Gem::Dependency
76
+ name: ruby-opengl
77
+ type: :runtime
78
+ version_requirement:
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: "0"
84
+ version:
85
+ description: qt-ruby tool for ART tracker data time series analysis.
86
+ email: pangdudu@github
87
+ executables: []
88
+
89
+ extensions: []
90
+
91
+ extra_rdoc_files:
92
+ - README.rdoc
93
+ files:
94
+ - README.rdoc
95
+ - lib/mamba.rb
96
+ - lib/graph.rb
97
+ - lib/b1trackerdata/b1_data_parser.rb
98
+ - lib/b1trackerdata/openglviewer.rb
99
+ - tests/generate_testdata.rb
100
+ has_rdoc: true
101
+ homepage: http://github.com/pangdudu/mamba
102
+ licenses:
103
+ post_install_message:
104
+ rdoc_options: []
105
+
106
+ require_paths:
107
+ - lib
108
+ required_ruby_version: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: "0"
113
+ version:
114
+ required_rubygems_version: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: "0"
119
+ version:
120
+ requirements: []
121
+
122
+ rubyforge_project:
123
+ rubygems_version: 1.3.5
124
+ signing_key:
125
+ specification_version: 2
126
+ summary: a ruby-qt ART tracker data analysis tool
127
+ test_files: []
128
+