tef-animation 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3bc7c194c8425278ad0369c293684b65008f293f
4
+ data.tar.gz: b59ce88b52067a8c5b2b8c2070e7abf408d073e5
5
+ SHA512:
6
+ metadata.gz: 9e1c4ed9339a1054e69a5b2074e6f1741da37c3490e7cac055c1f696ad3e6365ecc2b2dda615265df8d3ebd072f1a9b9665d3ca9ef9e31bb9a8fd42c53a5c02a
7
+ data.tar.gz: 13d28689ecdd5bba1fbb8028076b39ba3623b62b0f7b8f7e38cafdf108941395907790509bef2d340939eb4b1e817561f12d5a01185e95466d20fb2b5e417b74
data/README.md ADDED
@@ -0,0 +1,6 @@
1
+
2
+ # TheElectricFursuit Animation and Sequencing
3
+
4
+ This... Is a stub for now. Stay tuned for documentation!
5
+ OR!
6
+ Head to github.com/Xasin/system-synth-suit/Ruby/VoiceControl and see how this works.
@@ -0,0 +1,213 @@
1
+
2
+ require_relative 'Value.rb'
3
+ require_relative 'Color.rb'
4
+ require_relative 'Coordinate.rb'
5
+
6
+ module TEF
7
+ module Animation
8
+ class Animatable
9
+ attr_reader :module_id
10
+ attr_reader :death_time
11
+
12
+ attr_reader :death_delay
13
+
14
+ def self.get_attr_list
15
+ @class_attribute_list ||= {}
16
+ end
17
+ def self.get_color_list
18
+ @class_color_list ||= {}
19
+ end
20
+ def self.get_coordinate_list
21
+ @class_coordinate_list ||= {}
22
+ end
23
+
24
+ def self.animatable_attr(name, id)
25
+ get_attr_list()[name] = id
26
+
27
+ define_method(name.to_s) do
28
+ @animatable_attributes[name]
29
+ end
30
+
31
+ define_method("#{name}=") do |arg|
32
+ if arg.is_a? Numeric
33
+ @animatable_attributes[name].add = arg
34
+ else
35
+ @animatable_attributes[name].from = arg
36
+ end
37
+ end
38
+ end
39
+
40
+ def self.animatable_color(name, id)
41
+ get_color_list()[name] = id
42
+
43
+ define_method(name.to_s) do
44
+ @animatable_colors[name]
45
+ end
46
+
47
+ define_method("#{name}=") do |arg|
48
+ @animatable_colors[name].target = arg
49
+ end
50
+ end
51
+
52
+ def self.animatable_coordinate(name, start)
53
+ get_coordinate_list()[name] = start
54
+
55
+ define_method(name.to_s) do
56
+ @animatable_coordinates[name]
57
+ end
58
+ end
59
+
60
+ def initialize()
61
+ @animatable_attributes = {}
62
+ @animatable_colors = {}
63
+ @animatable_coordinates = {}
64
+
65
+ self.class.get_attr_list.each do |key, val|
66
+ @animatable_attributes[key] = Value.new(val)
67
+ end
68
+
69
+ self.class.get_color_list.each do |key, val|
70
+ @animatable_colors[key] = Color.new(val)
71
+ end
72
+
73
+ self.class.get_coordinate_list.each do |key, offset|
74
+ @animatable_coordinates[key] = Coordinate.new(offset)
75
+ end
76
+ end
77
+
78
+ def death_time=(n_time)
79
+ raise ArgumentError, 'Must be a Time!' unless n_time.is_a? Time || n_time.nil?
80
+
81
+ return if n_time == @death_time
82
+
83
+ @death_time = n_time
84
+ @death_time_changed = true
85
+ end
86
+
87
+ def die_in(t)
88
+ if t.nil?
89
+ self.death_time = nil
90
+ @death_delay = nil
91
+
92
+ return
93
+ end
94
+
95
+ raise ArgumentError, "Time must be num!" unless t.is_a? Numeric
96
+
97
+ self.death_time = Time.now() + t
98
+ @death_delay = t
99
+ end
100
+
101
+ def die!
102
+ self.death_time = Time.at(0)
103
+ end
104
+
105
+ def configure(h = nil, **opts)
106
+ h ||= opts;
107
+
108
+ raise ArgumentError, 'Config must be a hash!' unless h.is_a? Hash
109
+
110
+ h.each do |key, data|
111
+ value = @animatable_attributes[key] ||
112
+ @animatable_colors[key] ||
113
+ @animatable_coordinates[key]
114
+
115
+ raise ArgumentError, "Parameter #{key} does not exist!" unless value
116
+
117
+ value.configure(data);
118
+ end
119
+ end
120
+
121
+ def creation_string
122
+ ""
123
+ end
124
+
125
+ def all_animatable_attributes
126
+ out = @animatable_attributes.values
127
+ out += @animatable_coordinates.values.map(&:animatable_attributes)
128
+
129
+ out.flatten
130
+ end
131
+
132
+ def module_id=(new_str)
133
+ unless new_str =~ /^S[\d]{1,3}M[\d]{1,3}$/ || new_str.nil?
134
+ raise ArgumentError, 'Target must be a valid Animation Value'
135
+ end
136
+
137
+ all_animatable_attributes.each do |value|
138
+ value.module_id = new_str
139
+ end
140
+
141
+ die_in @death_delay if @death_delay
142
+
143
+ @module_id = new_str
144
+ end
145
+
146
+ def death_time_string
147
+ return nil unless @death_time_changed
148
+
149
+ @death_time_changed = false
150
+
151
+ return "#{@module_id} N;" if @death_time.nil?
152
+
153
+ remaining_time = (@death_time - Time.now()).round(2)
154
+
155
+ "#{@module_id} #{remaining_time};"
156
+ end
157
+
158
+ def get_set_strings()
159
+ return [] unless @module_id
160
+
161
+ out_elements = []
162
+
163
+ all_animatable_attributes.each do |val|
164
+ o_str = val.set_string
165
+ next if o_str.nil?
166
+
167
+ out_elements << { module: @module_id, value: val.ID, str: o_str };
168
+ end
169
+
170
+ out_elements
171
+ end
172
+
173
+ def get_setc_strings()
174
+ return [] unless @module_id
175
+
176
+ out_elements = []
177
+
178
+ @animatable_colors.values.each do |val|
179
+ o_str = val.set_string
180
+ next if o_str.nil?
181
+
182
+ out_elements << "#{@module_id}#{o_str}"
183
+ end
184
+
185
+ out_elements
186
+ end
187
+ end
188
+
189
+ class Box < Animatable
190
+ animatable_color :color, 0
191
+
192
+ animatable_attr :rotation, 0
193
+ animatable_attr :up, 1
194
+ animatable_attr :down, 2
195
+ animatable_attr :left, 3
196
+ animatable_attr :right, 4
197
+
198
+ animatable_coordinate :x_dir, 0xC000
199
+ animatable_coordinate :y_dir, 0xC100
200
+ animatable_coordinate :center, 0xC200
201
+
202
+ def initialize(layer_no = 0)
203
+ super();
204
+
205
+ @layer = layer_no;
206
+ end
207
+
208
+ def creation_string
209
+ "BOX #{@module_id} #{@layer}"
210
+ end
211
+ end
212
+ end
213
+ end
@@ -0,0 +1,180 @@
1
+
2
+ require 'xasin_logger'
3
+
4
+ require_relative 'Animatable.rb'
5
+
6
+ module TEF
7
+ module Animation
8
+ class Handler
9
+ include XasLogger::Mix
10
+
11
+ def initialize(furcoms_bus)
12
+ @furcoms = furcoms_bus
13
+
14
+ @animation_mutex = Mutex.new
15
+ @active_animations = {}
16
+
17
+ @pending_deletions = {}
18
+ @pending_creations = {}
19
+
20
+ init_x_log('Animation Handler')
21
+ end
22
+
23
+ def clean_key(key)
24
+ key = 'S%<S>dM%<M>d' % key if key.is_a? Hash
25
+
26
+ unless key =~ /^S[\d]{1,3}M[\d]{1,3}$/
27
+ raise ArgumentError, 'Target must be a valid Animation Value'
28
+ end
29
+
30
+ key
31
+ end
32
+
33
+ def [](key)
34
+ @active_animations[clean_key key]
35
+ end
36
+
37
+ def []=(key, new_obj)
38
+ @animation_mutex.synchronize {
39
+ key = clean_key key
40
+
41
+ @active_animations[key]&.module_id = nil;
42
+
43
+ if new_obj.nil?
44
+ @pending_deletions[key] = true
45
+
46
+ elsif new_obj.is_a? Animatable
47
+ new_obj.module_id = key
48
+
49
+ @active_animations[key] = new_obj
50
+ @pending_creations[key] = new_obj.creation_string
51
+ else
52
+ raise ArgumentError, 'New animation object is of invalid type'
53
+ end
54
+ }
55
+ end
56
+
57
+ private def join_and_send(topic, data)
58
+ return if data.empty?
59
+
60
+ out_str = '';
61
+ data.each do |str|
62
+ if(out_str.length + str.length > 200)
63
+ @furcoms.send_message topic, out_str
64
+ out_str = ''
65
+ end
66
+
67
+ out_str += str
68
+ end
69
+
70
+ @furcoms.send_message topic, out_str
71
+ end
72
+
73
+ # TODO Replace this with a time-synched system
74
+ # using the main synch time
75
+ private def update_deaths
76
+ death_reconfigs = [];
77
+
78
+ @animation_mutex.synchronize {
79
+ @active_animations.each do |key, animation|
80
+ if (!animation.death_time.nil?) && animation.death_time < Time.now()
81
+ @pending_deletions[key] = :silent
82
+ end
83
+
84
+ new_death = animation.death_time_string
85
+ next if new_death.nil?
86
+
87
+ death_reconfigs << new_death
88
+ end
89
+ }
90
+
91
+ deletions = []
92
+ @pending_deletions.each do |key, val|
93
+ deletions << "#{key};" unless val == :silent
94
+ @active_animations.delete key
95
+ end
96
+ @pending_deletions = {}
97
+
98
+ join_and_send('DTIME', death_reconfigs)
99
+ join_and_send('DELETE', deletions)
100
+ end
101
+
102
+ private def update_creations
103
+ @pending_creations.each do |key, val|
104
+ @furcoms.send_message('NEW', val)
105
+ end
106
+
107
+ @pending_creations = {}
108
+ end
109
+
110
+ private def optimize_and_send(messages)
111
+ last_change = {}
112
+ out_str = '';
113
+ can_optimize = false
114
+
115
+ messages.each do |change|
116
+ opt_string = change[:str]
117
+
118
+ can_optimize = false if last_change[:module] != change[:module]
119
+ can_optimize = false if last_change[:value] != (change[:value] - 1)
120
+ can_optimize = false if (out_str.length + opt_string.length) > 200
121
+ can_optimize = false if opt_string[0] == 'S'
122
+
123
+ opt_string = "#{change[:module]}V#{change[:value].to_s(16)} #{opt_string}" unless can_optimize
124
+
125
+ if opt_string.length + out_str.length > 200
126
+ @furcoms.send_message 'SET', out_str
127
+ out_str = ''
128
+ end
129
+
130
+ out_str += opt_string
131
+
132
+
133
+ can_optimize = true
134
+ last_change = change
135
+ end
136
+
137
+ @furcoms.send_message 'SET', out_str
138
+ end
139
+
140
+ private def update_values()
141
+ pending_changes = []
142
+
143
+ @animation_mutex.synchronize {
144
+ @active_animations.each do |key, anim|
145
+ pending_changes += anim.get_set_strings
146
+ end
147
+ }
148
+
149
+ return if pending_changes.empty?
150
+ x_logd "Pending changes are #{pending_changes}"
151
+
152
+ optimize_and_send pending_changes
153
+ end
154
+
155
+ private def update_colors
156
+ pending_changes = []
157
+
158
+ @animation_mutex.synchronize {
159
+ @active_animations.each do |key, anim|
160
+ pending_changes += anim.get_setc_strings
161
+ end
162
+ }
163
+
164
+ return if pending_changes.empty?
165
+ x_logd "Pending changes are #{pending_changes}"
166
+
167
+ join_and_send 'CSET', pending_changes
168
+ end
169
+
170
+ def update_tick()
171
+ update_creations
172
+
173
+ update_deaths
174
+
175
+ update_values
176
+ update_colors
177
+ end
178
+ end
179
+ end
180
+ end
@@ -0,0 +1,112 @@
1
+
2
+ module TEF
3
+ module Animation
4
+ class Color
5
+ PARAM_TYPES = [:jump, :velocity, :target, :delay_a, :delay_b];
6
+
7
+ attr_reader :ID
8
+
9
+ attr_reader :module_id
10
+
11
+ def initialize(value_num)
12
+ @ID = value_num;
13
+
14
+ @current = Hash.new(0);
15
+ @changes = {}
16
+
17
+ @is_animated = false;
18
+ end
19
+
20
+ def module_id=(new_id)
21
+ @module_id = new_id
22
+
23
+ PARAM_TYPES.each do |key|
24
+ @changes[:key] = true if @current[key] != 0
25
+ end
26
+ end
27
+
28
+ def total_id()
29
+ "#{@module_id}V#{@ID}"
30
+ end
31
+
32
+ def generic_set(key, value)
33
+ raise ArgumentError, 'Key does not exist!' unless PARAM_TYPES.include? key
34
+ raise ArgumentError, "Input must be numeric!" unless value.is_a? Numeric
35
+
36
+ return if ![:jump, :velocity].include?(key) && value == @current[key]
37
+
38
+ if [:delay_a, :delay_b].include? key
39
+ @is_animated = true
40
+ end
41
+
42
+ @current[key] = value
43
+ @changes[key] = true
44
+ end
45
+
46
+ PARAM_TYPES.each do |key|
47
+ define_method(key.to_s) do
48
+ @current[key]
49
+ end
50
+
51
+ define_method("#{key}=") do |input|
52
+ generic_set key, input
53
+ end
54
+ end
55
+
56
+ def configure(data)
57
+ if data.is_a? Numeric
58
+ self.target = data
59
+ elsif data.is_a? Hash
60
+ data.each do |key, value|
61
+ generic_set key, value
62
+ end
63
+ else
64
+ raise ArgumentError, 'Config data must be Hash or Numeric'
65
+ end
66
+ end
67
+
68
+ def has_changes?
69
+ return !@changes.empty?
70
+ end
71
+
72
+ private def rcut(value)
73
+ value.to_s.gsub(/(\.)0+$/, '')
74
+ end
75
+
76
+ def set_string()
77
+ return nil unless has_changes?
78
+
79
+ if !@is_animated
80
+ return nil unless @changes[:target]
81
+
82
+ out_str = "V#{@ID} J#{@current[:target].to_s(16)};"
83
+
84
+ @changes = {}
85
+
86
+ return out_str
87
+ end
88
+
89
+ out_str = ["V#{@ID}"];
90
+
91
+ out_str << "J#{@current[:jump].to_s(16)}" if @changes[:jump]
92
+ out_str << "V#{@current[:velocity].to_s(16)}" if @changes[:velocity]
93
+
94
+ config_strs = [];
95
+ config_strs_out = [];
96
+ [:target, :delay_a, :delay_b].each do |k|
97
+ if k == :target
98
+ config_strs << @current[k].to_s(16)
99
+ else
100
+ config_strs << rcut(@current[k])
101
+ end
102
+
103
+ config_strs_out = config_strs.dup if @changes[k]
104
+ end
105
+
106
+ @changes = {}
107
+
108
+ (out_str + config_strs_out).join(' ') + ';'
109
+ end
110
+ end
111
+ end
112
+ end