bvh 1.0.0 → 1.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.
@@ -1,124 +1,124 @@
1
- class Bvh
2
- class Motion
3
-
4
- class Frame
5
- # The array of ChannelData objects for this frame: one ChannelData instance for each bone.
6
- attr_reader :channel_data
7
-
8
- def initialize()
9
- @channel_data = []
10
- end
11
-
12
- # Creates a copy of this frame, including a dup of its channel data.
13
- def copy
14
- ret = self.class.new
15
- channel_data.each do |datum|
16
- ret.channel_data << datum.dup
17
- end
18
- ret
19
- end
20
-
21
- # call-seq:
22
- # frame + frame => new_frame
23
- # frame - frame => new_frame
24
- # frame / frame => new_frame
25
- # frame * frame => new_frame
26
- # frame + number => new_frame
27
- # frame - number => new_frame
28
- # frame / number => new_frame
29
- # frame * number => new_frame
30
- #
31
- # Performs arithmetic on this frame with the target. The second operand may be either a number or another Frame.
32
- # If the target is a number, then that number is added to, subtracted from, multiplied with, or divided against
33
- # each channel of each ChannelData object in this frame.
34
- #
35
- # If the target is another Frame, the arithmetic looks something like this (simplified):
36
- # return_value.channel_data[0] = frame1.channel_data[0] * frame2.channel_data[0]
37
- # return_value.channel_data[0] = frame1.channel_data[1] * frame2.channel_data[1]
38
- # return_value.channel_data[0] = frame1.channel_data[2] * frame2.channel_data[2]
39
- # . . .
40
- #
41
- # Both frames must contain the same number of ChannelData instances, and each instance must
42
- # have the same number of channels, and each instance must also reference the same bone.
43
- #
44
- # Returns a new frame containing the result.
45
- #
46
- def arithmetic_proc(target)
47
- # Fooled you! I metaprogrammed it to save some typing!
48
- end
49
- undef arithmetic_proc
50
-
51
- [:+, :-, :/, :*].each do |operator|
52
- define_method operator do |target|
53
- if target.kind_of?(Frame)
54
- unless channel_data.length == target.channel_data.length
55
- raise "Expected both frames to have the same amount of data"
56
- end
57
- ret = Frame.new
58
- channel_data.each_with_index do |datum, index|
59
- ret.channel_data << datum.send(operator, target.channel_data[index])
60
- end
61
- ret
62
- else
63
- ret = Frame.new
64
- channel_data.each { |datum| ret.channel_data << datum.send(operator, target) }
65
- end
66
- ret
67
- end
68
- end
69
-
70
- # Returns the channel data for the specified bone.
71
- def channel_data_for(bone)
72
- @channel_data.each { |c| return c if c.bone == bone }
73
- raise "Channel data for bone not found: #{bone.name}"
74
- end
75
-
76
- # Returns the relative, or local, transform matrix for the specified bone in this frame.
77
- def relative_transform_matrix(bone)
78
- return channel_data_for(bone).relative_transform_matrix
79
- end
80
-
81
- # Returns the transform matrix of the root node multiplied with its children recursively
82
- # down to the specified bone, the result being the total transformation in worldspace for this frame.
83
- def absolute_transform_matrix(bone)
84
- relative = relative_transform_matrix(bone)
85
- if bone.parent then absolute_transform_matrix(bone.parent) * relative
86
- else relative
87
- end
88
- end
89
-
90
- # Modifies a single attribute of the channel data for this bone in this frame. Returns self.
91
- #
92
- # Ex:
93
- # bvh.last_frame.set_channel bone, 'Xrotation', 180 # => sets the bone to a 180 deg rotation around the X-axis
94
- def set_channel(bone, channel, theta)
95
- channel_data_for(bone).set_channel(channel, theta)
96
- end
97
-
98
- # Retrives the value for a specific channel for a specific bone within this frame.
99
- def get_channel(bone, channel)
100
- channel_data_for(bone).get_channel(channel)
101
- end
102
-
103
- # Modifies the channel data for this bone in this frame, resulting in a rotation around the specified
104
- # channel.
105
- #
106
- # Ex:
107
- # bvh.last_frame.set_channel bone, 'Xrotation', 180 # => rotates the bone by 180 deg rotation around the X-axis
108
- def rotate!(bone, channel, theta)
109
- set_channel(bone, channel, channel_data_for(bone).get_channel(channel) + theta)
110
- end
111
-
112
- # Adds x, y and z to the X, Y and Z position channels for this bone in this frame, resulting in a "movement" or
113
- # translation.
114
- def translate!(bone, x, y, z)
115
- set_channel(bone, 'Xposition', channel_data_for(bone).get_channel('Xposition')+x)
116
- set_channel(bone, 'Yposition', channel_data_for(bone).get_channel('Yposition')+y)
117
- set_channel(bone, 'Zposition', channel_data_for(bone).get_channel('Zposition')+z)
118
- end
119
-
120
- alias transform_matrix absolute_transform_matrix
121
- alias local_transform_matrix relative_transform_matrix
122
- end
123
- end
124
- end
1
+ class Bvh
2
+ class Motion
3
+
4
+ class Frame
5
+ # The array of ChannelData objects for this frame: one ChannelData instance for each bone.
6
+ attr_reader :channel_data
7
+
8
+ def initialize()
9
+ @channel_data = []
10
+ end
11
+
12
+ # Creates a copy of this frame, including a dup of its channel data.
13
+ def copy
14
+ ret = self.class.new
15
+ channel_data.each do |datum|
16
+ ret.channel_data << datum.dup
17
+ end
18
+ ret
19
+ end
20
+
21
+ # call-seq:
22
+ # frame + frame => new_frame
23
+ # frame - frame => new_frame
24
+ # frame / frame => new_frame
25
+ # frame * frame => new_frame
26
+ # frame + number => new_frame
27
+ # frame - number => new_frame
28
+ # frame / number => new_frame
29
+ # frame * number => new_frame
30
+ #
31
+ # Performs arithmetic on this frame with the target. The second operand may be either a number or another Frame.
32
+ # If the target is a number, then that number is added to, subtracted from, multiplied with, or divided against
33
+ # each channel of each ChannelData object in this frame.
34
+ #
35
+ # If the target is another Frame, the arithmetic looks something like this (simplified):
36
+ # return_value.channel_data[0] = frame1.channel_data[0] * frame2.channel_data[0]
37
+ # return_value.channel_data[0] = frame1.channel_data[1] * frame2.channel_data[1]
38
+ # return_value.channel_data[0] = frame1.channel_data[2] * frame2.channel_data[2]
39
+ # . . .
40
+ #
41
+ # Both frames must contain the same number of ChannelData instances, and each instance must
42
+ # have the same number of channels, and each instance must also reference the same bone.
43
+ #
44
+ # Returns a new frame containing the result.
45
+ #
46
+ def arithmetic_proc(target)
47
+ # Fooled you! I metaprogrammed it to save some typing!
48
+ end
49
+ undef arithmetic_proc
50
+
51
+ [:+, :-, :/, :*].each do |operator|
52
+ define_method operator do |target|
53
+ if target.kind_of?(Frame)
54
+ unless channel_data.length == target.channel_data.length
55
+ raise "Expected both frames to have the same amount of data"
56
+ end
57
+ ret = Frame.new
58
+ channel_data.each_with_index do |datum, index|
59
+ ret.channel_data << datum.send(operator, target.channel_data[index])
60
+ end
61
+ ret
62
+ else
63
+ ret = Frame.new
64
+ channel_data.each { |datum| ret.channel_data << datum.send(operator, target) }
65
+ end
66
+ ret
67
+ end
68
+ end
69
+
70
+ # Returns the channel data for the specified bone.
71
+ def channel_data_for(bone)
72
+ @channel_data.each { |c| return c if c.bone == bone }
73
+ raise "Channel data for bone not found: #{bone.name}"
74
+ end
75
+
76
+ # Returns the relative, or local, transform matrix for the specified bone in this frame.
77
+ def relative_transform_matrix(bone)
78
+ return channel_data_for(bone).relative_transform_matrix
79
+ end
80
+
81
+ # Returns the transform matrix of the root node multiplied with its children recursively
82
+ # down to the specified bone, the result being the total transformation in worldspace for this frame.
83
+ def absolute_transform_matrix(bone)
84
+ relative = relative_transform_matrix(bone)
85
+ if bone.parent then absolute_transform_matrix(bone.parent) * relative
86
+ else relative
87
+ end
88
+ end
89
+
90
+ # Modifies a single attribute of the channel data for this bone in this frame. Returns self.
91
+ #
92
+ # Ex:
93
+ # bvh.last_frame.set_channel bone, 'Xrotation', 180 # => sets the bone to a 180 deg rotation around the X-axis
94
+ def set_channel(bone, channel, theta)
95
+ channel_data_for(bone).set_channel(channel, theta)
96
+ end
97
+
98
+ # Retrives the value for a specific channel for a specific bone within this frame.
99
+ def get_channel(bone, channel)
100
+ channel_data_for(bone).get_channel(channel)
101
+ end
102
+
103
+ # Modifies the channel data for this bone in this frame, resulting in a rotation around the specified
104
+ # channel.
105
+ #
106
+ # Ex:
107
+ # bvh.last_frame.set_channel bone, 'Xrotation', 180 # => rotates the bone by 180 deg rotation around the X-axis
108
+ def rotate!(bone, channel, theta)
109
+ set_channel(bone, channel, channel_data_for(bone).get_channel(channel) + theta)
110
+ end
111
+
112
+ # Adds x, y and z to the X, Y and Z position channels for this bone in this frame, resulting in a "movement" or
113
+ # translation.
114
+ def translate!(bone, x, y, z)
115
+ set_channel(bone, 'Xposition', channel_data_for(bone).get_channel('Xposition')+x)
116
+ set_channel(bone, 'Yposition', channel_data_for(bone).get_channel('Yposition')+y)
117
+ set_channel(bone, 'Zposition', channel_data_for(bone).get_channel('Zposition')+z)
118
+ end
119
+
120
+ alias transform_matrix absolute_transform_matrix
121
+ alias local_transform_matrix relative_transform_matrix
122
+ end
123
+ end
124
+ end
@@ -1,177 +1,177 @@
1
- class Bvh
2
- class Parser
3
- attr_reader :filename
4
- attr_reader :source
5
-
6
- def initialize(file)
7
- @filename = file
8
- @source = File.read(file)
9
- end
10
-
11
- def parse(bvh)
12
- @bvh = bvh
13
-
14
- # a little touch-up...
15
- s = source
16
- # it's tempting to just downcase the whole string, but that'd change the case of object names, which might be
17
- # bad for the end user. So we'll swap out specific keywords instead.
18
- s.gsub!(/^((\s*)(root|offset|channels|joint|end site|hierarchy)(([^\n\{]*)(\n|\{)))/mi) do
19
- match = $~
20
- "#{match[2]}#{match[3].downcase.gsub(/\s/, '_')} \"#{match[5].strip}\"#{match[6]}"
21
- end
22
- # make { . . . } into proper Ruby blocks
23
- s.gsub!(/[\n\s]*\{/m, ' do').gsub!(/[\n\s]*\}/m, "\nend")
24
-
25
- # Finally, handle the MOTION segment, which can be treated as a single method call.
26
- s.gsub!(/^((\s*)(motion)(.*))/mi) do
27
- "#{$~[2]}#{$~[3].downcase} <<-EOF\n#{$~[4].strip}\nEOF\n"
28
- end
29
-
30
- eval(s, binding, @filename, 1)
31
- end
32
-
33
- private
34
- attr_reader :bvh, :mode, :current_node
35
-
36
- def bone(type, name, &block)
37
- raise ArgumentError, "#{type} #{name} is unexpected at this time" unless mode == :hierarchy
38
- bone = type.new(name)
39
-
40
- if current_node.nil?
41
- bvh.create_skeleton!.root = bone
42
- else
43
- current_node.add_joint!(bone)
44
- end
45
-
46
- @current_node, bone = bone, @current_node
47
- instance_eval(&block)
48
- @current_node = bone
49
- end
50
-
51
- # Ex:
52
- # root "joint-pelvis" do
53
- # offset "0.00 0.00 0.00"
54
- # channels "6 Xposition Ypoisition Zposition Zrotation Xrotation Yrotation"
55
- # joint "joint-spine3" do
56
- # . . .
57
- # end
58
- # end
59
- def root(name, &block)
60
- @current_node = nil
61
- bone Bvh::Skeleton::Bone, name, &block
62
- end
63
-
64
- # Ex:
65
- # root "joint-pelvis" do
66
- # . . .
67
- # joint "joint-spine3" do
68
- # offset "0.00 -10.65 0.00"
69
- # channels "3 Zrotation Xrotation Yrotation"
70
- # end_site do
71
- # . . .
72
- # end
73
- # end
74
- # end
75
- def joint(name, &block)
76
- bone Bvh::Skeleton::Bone, name, &block
77
- end
78
-
79
- # Ex:
80
- # root "joint-pelvis" do
81
- # . . .
82
- # joint "joint-spine3" do
83
- # . . .
84
- # end_site do
85
- # offset "0.00 -7.00 0.00"
86
- # end
87
- # end
88
- # end
89
- def end_site(*unused, &block)
90
- bone Bvh::Skeleton::Bone, nil, &block
91
- end
92
-
93
- def hierarchy(*unused)
94
- @mode = :hierarchy
95
- end
96
-
97
- def offset(val)
98
- raise ArgumentError, "OFFSET is unexpected at this time" unless current_node
99
- raise ArgumentError, "Already have OFFSET data for this node" unless current_node.offset.length == 0
100
- current_node.offset.concat val.split.collect { |i| i.to_f }
101
- end
102
-
103
- def channels(val)
104
- raise ArgumentError, "CHANNELS is unexpected at this time" unless current_node
105
- raise ArgumentError, "Already have CHANNELS data for this node" unless current_node.channels.length == 0
106
- vals = val.split
107
- count = vals.shift.to_i # how many?
108
- raise ArgumentError, "Expected #{count} channels, found #{vals.length}" if vals.length != count
109
- current_node.channels.concat vals
110
- end
111
-
112
- def motion(motion_data)
113
- raise ArgumentError, "Motion data is unexpected at this time" unless mode == :hierarchy
114
- @mode = :motion
115
- frame_count = nil
116
- motion_data.each_line do |line|
117
- line = line.downcase.strip
118
- words = line.split
119
- case words.first
120
- when 'frames:'
121
- if words[1] =~ /[^0-9]/
122
- raise ArgumentError, "Only one positive numeric integer value expected at this time (not #{words[1]})"
123
- end
124
- frame_count = words[1].to_i
125
- when 'frame'
126
- case words[1]
127
- when 'time:'
128
- if words[2] =~ /[^0-9\.]/
129
- raise ArgumentError, "Only one positive numeric decimal value expected at this time (not #{words[2]})"
130
- end
131
- bvh.frame_time = words[2].to_f
132
- else
133
- raise ArgumentError, "Motion data not understood: #{line}"
134
- end
135
- when nil, '' # blank line, do nothing
136
- else
137
- channels = words.collect do |w|
138
- raise ArgumentError, "Only numeric values are expected at this time (not #{w})" if w =~ /[^0-9\.\-]/
139
- w.to_f
140
- end
141
- add_frame(channels)
142
- end
143
- end
144
- unless frame_count.nil? or frame_count == bvh.frame_count
145
- raise ArgumentError, "Expected %s frames, found %s" % [frame_count, bvh.frame_count]
146
- end
147
- unless frame_count == 0
148
- raise ArgumentError, "Frame time is 0; this would result in infinite FPS!" if bvh.frame_time == 0
149
- raise ArgumentError, "Frame time is #{bvh.frame_time}! Should be a positive number." if bvh.frame_time < 0
150
- end
151
- end
152
-
153
- def add_frame(channels)
154
- frame = Bvh::Motion::Frame.new
155
- frame.channel_data.concat channel_data(channels, bvh.root)
156
- bvh.frames << frame
157
- raise ArgumentError, "Not enough channels: Need #{-channels.length} more" if channels.length < 0
158
- raise ArgumentError, "Too many channels: #{channels.length} unaccounted for" if channels.length > 0
159
- end
160
-
161
- # this is now a binding.
162
- # def channel_data(channels, bone)
163
- # return [] unless bone.respond_to? :channels
164
- # data = Bvh::Motion::ChannelData.new(bone)
165
- # bone.channels.each do |channel|
166
- # data[channel] = channels.shift
167
- # end
168
- # r = [ data ]
169
- # if bone.respond_to? :joints
170
- # bone.joints.each do |child|
171
- # r.concat channel_data(channels, child)
172
- # end
173
- # end
174
- # r
175
- # end
176
- end
177
- end
1
+ class Bvh
2
+ class Parser
3
+ attr_reader :filename
4
+ attr_reader :source
5
+
6
+ def initialize(file)
7
+ @filename = file
8
+ @source = File.read(file)
9
+ end
10
+
11
+ def parse(bvh)
12
+ @bvh = bvh
13
+
14
+ # a little touch-up...
15
+ s = source
16
+ # it's tempting to just downcase the whole string, but that'd change the case of object names, which might be
17
+ # bad for the end user. So we'll swap out specific keywords instead.
18
+ s.gsub!(/^((\s*)(root|offset|channels|joint|end site|hierarchy)(([^\n\{]*)(\n|\{)))/mi) do
19
+ match = $~
20
+ "#{match[2]}#{match[3].downcase.gsub(/\s/, '_')} \"#{match[5].strip}\"#{match[6]}"
21
+ end
22
+ # make { . . . } into proper Ruby blocks
23
+ s.gsub!(/[\n\s]*\{/m, ' do').gsub!(/[\n\s]*\}/m, "\nend")
24
+
25
+ # Finally, handle the MOTION segment, which can be treated as a single method call.
26
+ s.gsub!(/^((\s*)(motion)(.*))/mi) do
27
+ "#{$~[2]}#{$~[3].downcase} <<-EOF\n#{$~[4].strip}\nEOF\n"
28
+ end
29
+
30
+ eval(s, binding, @filename, 1)
31
+ end
32
+
33
+ private
34
+ attr_reader :bvh, :mode, :current_node
35
+
36
+ def bone(type, name, &block)
37
+ raise ArgumentError, "#{type} #{name} is unexpected at this time" unless mode == :hierarchy
38
+ bone = type.new(name)
39
+
40
+ if current_node.nil?
41
+ bvh.create_skeleton!.root = bone
42
+ else
43
+ current_node.add_joint!(bone)
44
+ end
45
+
46
+ @current_node, bone = bone, @current_node
47
+ instance_eval(&block)
48
+ @current_node = bone
49
+ end
50
+
51
+ # Ex:
52
+ # root "joint-pelvis" do
53
+ # offset "0.00 0.00 0.00"
54
+ # channels "6 Xposition Ypoisition Zposition Zrotation Xrotation Yrotation"
55
+ # joint "joint-spine3" do
56
+ # . . .
57
+ # end
58
+ # end
59
+ def root(name, &block)
60
+ @current_node = nil
61
+ bone Bvh::Skeleton::Bone, name, &block
62
+ end
63
+
64
+ # Ex:
65
+ # root "joint-pelvis" do
66
+ # . . .
67
+ # joint "joint-spine3" do
68
+ # offset "0.00 -10.65 0.00"
69
+ # channels "3 Zrotation Xrotation Yrotation"
70
+ # end_site do
71
+ # . . .
72
+ # end
73
+ # end
74
+ # end
75
+ def joint(name, &block)
76
+ bone Bvh::Skeleton::Bone, name, &block
77
+ end
78
+
79
+ # Ex:
80
+ # root "joint-pelvis" do
81
+ # . . .
82
+ # joint "joint-spine3" do
83
+ # . . .
84
+ # end_site do
85
+ # offset "0.00 -7.00 0.00"
86
+ # end
87
+ # end
88
+ # end
89
+ def end_site(*unused, &block)
90
+ bone Bvh::Skeleton::Bone, nil, &block
91
+ end
92
+
93
+ def hierarchy(*unused)
94
+ @mode = :hierarchy
95
+ end
96
+
97
+ def offset(val)
98
+ raise ArgumentError, "OFFSET is unexpected at this time" unless current_node
99
+ raise ArgumentError, "Already have OFFSET data for this node" unless current_node.offset.length == 0
100
+ current_node.offset.concat val.split.collect { |i| i.to_f }
101
+ end
102
+
103
+ def channels(val)
104
+ raise ArgumentError, "CHANNELS is unexpected at this time" unless current_node
105
+ raise ArgumentError, "Already have CHANNELS data for this node" unless current_node.channels.length == 0
106
+ vals = val.split
107
+ count = vals.shift.to_i # how many?
108
+ raise ArgumentError, "Expected #{count} channels, found #{vals.length}" if vals.length != count
109
+ current_node.channels.concat vals
110
+ end
111
+
112
+ def motion(motion_data)
113
+ raise ArgumentError, "Motion data is unexpected at this time" unless mode == :hierarchy
114
+ @mode = :motion
115
+ frame_count = nil
116
+ motion_data.each_line do |line|
117
+ line = line.downcase.strip
118
+ words = line.split
119
+ case words.first
120
+ when 'frames:'
121
+ if words[1] =~ /[^0-9]/
122
+ raise ArgumentError, "Only one positive numeric integer value expected at this time (not #{words[1]})"
123
+ end
124
+ frame_count = words[1].to_i
125
+ when 'frame'
126
+ case words[1]
127
+ when 'time:'
128
+ if words[2] =~ /[^0-9\.]/
129
+ raise ArgumentError, "Only one positive numeric decimal value expected at this time (not #{words[2]})"
130
+ end
131
+ bvh.frame_time = words[2].to_f
132
+ else
133
+ raise ArgumentError, "Motion data not understood: #{line}"
134
+ end
135
+ when nil, '' # blank line, do nothing
136
+ else
137
+ channels = words.collect do |w|
138
+ raise ArgumentError, "Only numeric values are expected at this time (not #{w})" if w =~ /[^0-9\.\-]/
139
+ w.to_f
140
+ end
141
+ add_frame(channels)
142
+ end
143
+ end
144
+ unless frame_count.nil? or frame_count == bvh.frame_count
145
+ raise ArgumentError, "Expected %s frames, found %s" % [frame_count, bvh.frame_count]
146
+ end
147
+ unless frame_count == 0
148
+ raise ArgumentError, "Frame time is 0; this would result in infinite FPS!" if bvh.frame_time == 0
149
+ raise ArgumentError, "Frame time is #{bvh.frame_time}! Should be a positive number." if bvh.frame_time < 0
150
+ end
151
+ end
152
+
153
+ def add_frame(channels)
154
+ frame = Bvh::Motion::Frame.new
155
+ frame.channel_data.concat channel_data(channels, bvh.root)
156
+ bvh.frames << frame
157
+ raise ArgumentError, "Not enough channels: Need #{-channels.length} more" if channels.length < 0
158
+ raise ArgumentError, "Too many channels: #{channels.length} unaccounted for" if channels.length > 0
159
+ end
160
+
161
+ # this is now a binding.
162
+ # def channel_data(channels, bone)
163
+ # return [] unless bone.respond_to? :channels
164
+ # data = Bvh::Motion::ChannelData.new(bone)
165
+ # bone.channels.each do |channel|
166
+ # data[channel] = channels.shift
167
+ # end
168
+ # r = [ data ]
169
+ # if bone.respond_to? :joints
170
+ # bone.joints.each do |child|
171
+ # r.concat channel_data(channels, child)
172
+ # end
173
+ # end
174
+ # r
175
+ # end
176
+ end
177
+ end