pangdudu-mamba 0.1

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,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
+