reight 0.1.6 → 0.1.7

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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/ChangeLog.md +52 -0
  3. data/Rakefile +4 -1
  4. data/VERSION +1 -1
  5. data/lib/reight/all.rb +6 -2
  6. data/lib/reight/app/map/brush.rb +1 -1
  7. data/lib/reight/app/map/brush_base.rb +3 -3
  8. data/lib/reight/app/map/canvas.rb +4 -13
  9. data/lib/reight/app/map/chips.rb +6 -2
  10. data/lib/reight/app/map/editor.rb +4 -4
  11. data/lib/reight/app/map/line.rb +5 -5
  12. data/lib/reight/app/map/rect.rb +2 -2
  13. data/lib/reight/app/navigator.rb +27 -27
  14. data/lib/reight/app/runner.rb +106 -84
  15. data/lib/reight/app/sound/brush.rb +32 -0
  16. data/lib/reight/app/sound/canvas.rb +190 -0
  17. data/lib/reight/app/sound/editor.rb +170 -10
  18. data/lib/reight/app/sound/eraser.rb +28 -0
  19. data/lib/reight/app/sound/tool.rb +29 -0
  20. data/lib/reight/app/sound.rb +4 -0
  21. data/lib/reight/app/sprite/canvas.rb +18 -15
  22. data/lib/reight/app/sprite/chips.rb +6 -1
  23. data/lib/reight/app/sprite/color.rb +3 -1
  24. data/lib/reight/app/sprite/editor.rb +15 -28
  25. data/lib/reight/app/sprite/line.rb +1 -1
  26. data/lib/reight/app/sprite/shape.rb +1 -1
  27. data/lib/reight/app.rb +19 -1
  28. data/lib/reight/button.rb +7 -4
  29. data/lib/reight/chip.rb +24 -6
  30. data/lib/reight/context.rb +168 -0
  31. data/lib/reight/helpers.rb +2 -2
  32. data/lib/reight/index.rb +76 -0
  33. data/lib/reight/map.rb +131 -11
  34. data/lib/reight/project.rb +34 -6
  35. data/lib/reight/reight.rb +1 -3
  36. data/lib/reight/sound.rb +238 -0
  37. data/lib/reight/sprite.rb +42 -0
  38. data/lib/reight/text.rb +116 -0
  39. data/lib/reight.rb +7 -3
  40. data/reight.gemspec +7 -7
  41. data/res/icons.png +0 -0
  42. data/test/helper.rb +16 -0
  43. data/test/test_map.rb +7 -7
  44. data/test/test_map_chunk.rb +6 -6
  45. data/test/test_sprite.rb +28 -0
  46. metadata +39 -30
  47. data/lib/reight/app/music/editor.rb +0 -25
  48. data/lib/reight/app/music.rb +0 -1
@@ -1,117 +1,139 @@
1
- using Reight
1
+ class Reight::Runner < Reight::App
2
2
 
3
+ include Xot::Inspectable
3
4
 
4
- class Reight::Runner < Reight::App
5
+ ROOT_CONTEXT = Reight::CONTEXT__
5
6
 
6
7
  TEMPORARY_HASH = {}
7
8
 
8
9
  def activated()
9
- super
10
10
  run force: true
11
- @context.call_activated__
11
+ @context.call_activated__ {|&b| call_event(ignore_pause: true, &b)}
12
+ super
12
13
  end
13
14
 
14
15
  def deactivated()
15
16
  super
16
- @context.call_deactivated__
17
+ @context.call_deactivated__ {|&b| call_event(ignore_pause: true, &b)}
17
18
  pause
19
+ cleanup
18
20
  end
19
21
 
20
22
  def draw()
21
- push do
22
- @context&.call_draw__ unless paused?
23
+ return unless @context
24
+ @initial_resize ||= true.tap do
25
+ call_event {@context.size ROOT_CONTEXT.width, ROOT_CONTEXT.height}
26
+ end
27
+ @context.call_draw__ {|push: true, &b| call_event(push: push, &b)}
28
+ if canvasFrame = @context.canvasFrame__
29
+ ROOT_CONTEXT.background 0
30
+ ROOT_CONTEXT.image @context, *canvasFrame
31
+ else
32
+ ROOT_CONTEXT.image @context, 0, 0
23
33
  end
24
34
  super
25
35
  end
26
36
 
27
37
  def key_pressed()
28
38
  super
29
- @context&.key_pressed unless paused?
39
+ call_event {@context.key_pressed}
30
40
  end
31
41
 
32
42
  def key_released()
33
43
  super
34
- @context&.key_released unless paused?
44
+ call_event {@context.key_released}
35
45
  end
36
46
 
37
47
  def key_typed()
38
48
  super
39
- @context&.key_typed unless paused?
49
+ call_event {@context.key_typed}
40
50
  end
41
51
 
42
52
  def mouse_pressed()
43
53
  super
44
- @context&.mouse_pressed unless paused?
54
+ call_event {@context.mouse_pressed}
45
55
  end
46
56
 
47
57
  def mouse_released()
48
58
  super
49
- @context&.mouse_released unless paused?
59
+ call_event {@context.mouse_released}
50
60
  end
51
61
 
52
62
  def mouse_moved()
53
63
  super
54
- @context&.mouse_moved unless paused?
55
- navigator.visible = mouse_y < NAVIGATOR_HEIGHT
64
+ navigator.visible = ROOT_CONTEXT.mouse_y < NAVIGATOR_HEIGHT
65
+ call_event {@context.mouse_moved}
56
66
  end
57
67
 
58
68
  def mouse_dragged()
59
69
  super
60
- @context&.mouse_dragged unless paused?
70
+ call_event {@context.mouse_dragged}
61
71
  end
62
72
 
63
73
  def mouse_clicked()
64
74
  super
65
- @context&.mouse_clicked unless paused?
75
+ call_event {@context.mouse_clicked}
66
76
  end
67
77
 
68
78
  def double_clicked()
69
79
  super
70
- @context&.double_clicked unless paused?
80
+ call_event {@context.double_clicked}
71
81
  end
72
82
 
73
83
  def mouse_wheel()
74
84
  super
75
- @context&.mouse_wheel unless paused?
85
+ call_event {@context.mouse_wheel}
76
86
  end
77
87
 
78
88
  def touch_started()
79
89
  super
80
- @context&.touch_started unless paused?
90
+ call_event {@context.touch_started}
81
91
  end
82
92
 
83
93
  def touch_ended()
84
94
  super
85
- @context&.touch_ended unless paused?
95
+ call_event {@context.touch_ended}
86
96
  end
87
97
 
88
98
  def touch_moved()
89
99
  super
90
- @context&.touch_moved unless paused?
100
+ call_event {@context.touch_moved}
91
101
  end
92
102
 
93
103
  def window_moved()
94
104
  super
95
- @context&.window_moved
105
+ call_event(ignore_pause: true) {@context.window_moved}
96
106
  end
97
107
 
98
108
  def window_resized()
99
109
  super
100
- @context&.window_resized
110
+ call_event(ignore_pause: true) {@context.window_resized}
101
111
  end
102
112
 
103
113
  private
104
114
 
115
+ def call_event(push: true, ignore_pause: false, &block)
116
+ return unless @context
117
+ @context.beginDraw__
118
+ @context.push if push
119
+ block.call unless paused?
120
+ rescue ScriptError, StandardError => e
121
+ puts e.full_message
122
+ ensure
123
+ @context.pop if push
124
+ @context.endDraw__
125
+ end
126
+
105
127
  def running? = @context && !@paused
106
128
 
107
129
  def paused? = @context && @paused
108
130
 
109
131
  def run(force: false)
110
132
  return pause false if paused? && !force
111
- cleanup
112
133
  backup_global_vars
113
134
  @context = create_context
114
135
  @paused = false
136
+ Processing::Context.setContext__ @context
115
137
  begin_wrapping_user_classes @context
116
138
  eval_user_script @context, project.code_paths.zip(project.codes).to_h
117
139
  end
@@ -121,9 +143,12 @@ class Reight::Runner < Reight::App
121
143
  end
122
144
 
123
145
  def cleanup()
124
- remove_world @context.sprite_world__ if @context
146
+ ROOT_CONTEXT.remove_world @context.spriteWorld__ if @context
147
+ Processing::Context.setContext__ nil
125
148
  @context = nil
126
149
  end_wrapping_user_classes
150
+ clear_all_timers
151
+ project.clear_all_sprites
127
152
  restore_global_vars
128
153
  GC.enable
129
154
  GC.start
@@ -131,55 +156,52 @@ class Reight::Runner < Reight::App
131
156
 
132
157
  def create_context()
133
158
  klass = Class.new do
134
- def project = @project__
159
+ include Reight::Context
135
160
 
136
- def sprite_world__ = @sprite_world__ ||= SpriteWorld.new(pixels_per_meter: 5)
137
-
138
- def call_activated__()
139
- add_world sprite_world__
140
- activated
161
+ def call_activated__(&caller)
162
+ add_world spriteWorld__
163
+ caller.call {activated}
141
164
  end
142
165
 
143
- def call_deactivated__()
144
- deactivated
145
- remove_world sprite_world__
166
+ def call_deactivated__(&caller)
167
+ caller.call {deactivated}
168
+ remove_world spriteWorld__
146
169
  @background_cleared__ = false
147
170
  end
148
171
 
149
- def call_draw__()
150
- unless @setup_done__
151
- @setup_done__ = true
152
- setup
153
- end
154
- unless @background_cleared__
155
- @background_cleared__ = true
156
- background 100, 100, 100
157
- end
158
- draw
172
+ def call_draw__(&caller)
173
+ @setup_done__ ||= true.tap {caller.call(push: false) {setup}}
174
+ @background_cleared__ ||= true.tap {caller.call {background 100}}
175
+ caller.call {draw}
159
176
  end
160
177
 
161
- def createSprite(...) = sprite_world__.createSprite(...)
162
- def addSprite(...) = sprite_world__.addSprite(...)
163
- def removeSprite(...) = sprite_world__.removeSprite(...)
164
- def gravity(...) = sprite_world__.gravity(...)
165
- end
166
- Processing.to_snake_case__(
167
- %i[activated deactivated] + Processing::EVENT_NAMES__
168
- ).each do |camel, snake|
169
- klass.class_eval <<~END
170
- def #{camel}(&block)
171
- if block
172
- @#{camel}_block__ = block
173
- else
174
- @#{camel}_block__&.call
178
+ methods = (instance_methods - Object.instance_methods)
179
+ .reject {_1.end_with? '__'}
180
+ Processing.to_snake_case__(methods).each do |camel, snake|
181
+ alias_method snake, camel if snake != camel
182
+ end
183
+
184
+ Processing.to_snake_case__(
185
+ %i[activated deactivated] + Processing::EVENT_NAMES__
186
+ ).each do |camel, snake|
187
+ class_eval <<~END
188
+ def #{camel}(&block)
189
+ if block
190
+ @#{camel}_block__ = block
191
+ else
192
+ @#{camel}_block__&.call
193
+ end
175
194
  end
176
- end
177
- END
178
- klass.alias_method snake, camel if snake != camel
179
- end
180
- klass.new.tap do |context|
181
- context.instance_variable_set :@project__, project
195
+ END
196
+ alias_method snake, camel if snake != camel
197
+ end
198
+
199
+ Processing.funcs__(ROOT_CONTEXT).each do |func|
200
+ next if method_defined? func
201
+ define_method(func) {|*a, **k, &b| ROOT_CONTEXT.__send__ func, *a, **k, &b}
202
+ end
182
203
  end
204
+ klass.new(ROOT_CONTEXT, project)
183
205
  end
184
206
 
185
207
  def begin_wrapping_user_classes(context)
@@ -207,27 +229,17 @@ class Reight::Runner < Reight::App
207
229
 
208
230
  def create_user_class_wrapper(context)
209
231
  Module.new.tap do |wrapper|
210
- wrap_methods context, wrapper
211
- end
212
- end
213
-
214
- def wrap_methods(context, klass)
215
- klass.define_method :respond_to_missing? do |name, include_private = false|
216
- context.respond_to?(name, false) || super(name, include_private)
217
- end
218
- klass.define_method :method_missing do |name, *args, **kwargs, &block|
219
- if context.respond_to? name
220
- klass.define_method(name) {|*a, **k, &b| context.public_send name, *a, **k, &b}
221
- context.public_send name, *args, **kwargs, &block
222
- else
223
- super name, *args, **kwargs, &block
232
+ wrapper.define_method :respond_to_missing? do |name, include_private = false|
233
+ context.respond_to?(name, false) || super(name, include_private)
234
+ end
235
+ wrapper.define_method :method_missing do |name, *args, **kwargs, &block|
236
+ if context.respond_to? name
237
+ wrapper.define_method(name) {|*a, **k, &b| context.public_send name, *a, **k, &b}
238
+ context.public_send name, *args, **kwargs, &block
239
+ else
240
+ super name, *args, **kwargs, &block
241
+ end
224
242
  end
225
- end
226
- Processing.to_snake_case__(%i[
227
- project createSprite addSprite removeSprite gravity
228
- ]).each do |camel, snake|
229
- klass.define_method(camel) {|*a, **k, &b| context.public_send camel, *a, **k, &b}
230
- klass.alias_method snake, camel if snake != camel
231
243
  end
232
244
  end
233
245
 
@@ -237,6 +249,8 @@ class Reight::Runner < Reight::App
237
249
  ::Reight::Runner::TEMPORARY_HASH[:params] => {context:, codes:}
238
250
  codes.each {|path, code| context.instance_eval code, path if code}
239
251
  END
252
+ rescue ScriptError, StandardError => e
253
+ puts e.full_message
240
254
  ensure
241
255
  TEMPORARY_HASH.delete :params
242
256
  end
@@ -247,6 +261,14 @@ class Reight::Runner < Reight::App
247
261
  .freeze
248
262
  end
249
263
 
264
+ def clear_all_timers()
265
+ prefix = Reight::Context::TIMER_PREFIX__
266
+ ROOT_CONTEXT.instance_eval do
267
+ @timers__ .delete_if {|id| id in [prefix, _]}
268
+ @firingTimers__.delete_if {|id| id in [prefix, _]}
269
+ end
270
+ end
271
+
250
272
  def restore_global_vars()
251
273
  return unless @global_vars
252
274
  global_variables
@@ -0,0 +1,32 @@
1
+ class Reight::SoundEditor::Brush < Reight::SoundEditor::Tool
2
+
3
+ def initialize(app, &block)
4
+ super app, icon: app.icon(1, 2, 8), &block
5
+ set_help left: name, right: 'Pick Tone'
6
+ end
7
+
8
+ def brush(x, y, button)
9
+ canvas.put x, y
10
+ end
11
+
12
+ def canvas_pressed(x, y, button)
13
+ return unless button == LEFT
14
+ canvas.begin_editing
15
+ brush x, y, button
16
+ end
17
+
18
+ def canvas_released(x, y, button)
19
+ return unless button == LEFT
20
+ canvas.end_editing
21
+ end
22
+
23
+ def canvas_dragged(x, y, button)
24
+ return unless button == LEFT
25
+ brush x, y, button
26
+ end
27
+
28
+ def canvas_clicked(x, y, button)
29
+ pick_tone x, y if button == RIGHT
30
+ end
31
+
32
+ end# Brush
@@ -0,0 +1,190 @@
1
+ using Reight
2
+
3
+
4
+ class Reight::SoundEditor::Canvas
5
+
6
+ include Reight::Hookable
7
+
8
+ SEQUENCE_LEN = 32
9
+ NOTE_HEIGHT = 3
10
+
11
+ def initialize(app)
12
+ hook :sound_changed
13
+
14
+ @app, @sound = app, app.project.sounds.first
15
+ @scrolly = NOTE_HEIGHT * Reight::Sound::Note::MAX / 3
16
+ end
17
+
18
+ attr_accessor :tone, :tool
19
+
20
+ attr_reader :sound
21
+
22
+ def sound=(sound)
23
+ @sound = sound
24
+ sound_changed! sound
25
+ end
26
+
27
+ def save()
28
+ @app.project.save
29
+ end
30
+
31
+ def begin_editing(&block)
32
+ @app.history.begin_grouping
33
+ block.call if block
34
+ ensure
35
+ end_editing if block
36
+ end
37
+
38
+ def end_editing()
39
+ @app.history.end_grouping
40
+ save
41
+ end
42
+
43
+ def note_pos_at(x, y)
44
+ sp = sprite
45
+ notew = sp.w / SEQUENCE_LEN
46
+ max = Reight::Sound::Note::MAX
47
+ time_index = (x / notew).floor
48
+ note_index = max - ((@scrolly + y) / NOTE_HEIGHT).floor
49
+ return time_index, note_index.clamp(0, max)
50
+ end
51
+
52
+ def put(x, y)
53
+ time_i, note_i = note_pos_at x, y
54
+
55
+ @sound.each_note(time_index: time_i)
56
+ .select {|note,| (note.index == note_i) != (note.tone == tone)}
57
+ .each {|note,| remove_note time_i, note.index, note.tone}
58
+ add_note time_i, note_i, tone
59
+
60
+ @app.flash note_name y
61
+ end
62
+
63
+ def delete(x, y)
64
+ time_i, note_i = note_pos_at x, y
65
+ note = @sound.at time_i, note_i
66
+ return unless note
67
+
68
+ remove_note time_i, note_i, note.tone
69
+
70
+ @app.flash note_name y
71
+ end
72
+
73
+ def sprite()
74
+ @sprite ||= RubySketch::Sprite.new.tap do |sp|
75
+ pos = -> {return sp.mouse_x, sp.mouse_y}
76
+ sp.draw {draw}
77
+ sp.mouse_pressed {mouse_pressed( *pos.call, sp.mouse_button)}
78
+ sp.mouse_released {mouse_released(*pos.call, sp.mouse_button)}
79
+ sp.mouse_moved {mouse_moved( *pos.call)}
80
+ sp.mouse_dragged {mouse_dragged( *pos.call, sp.mouse_button)}
81
+ sp.mouse_clicked {mouse_clicked( *pos.call, sp.mouse_button)}
82
+ end
83
+ end
84
+
85
+ private
86
+
87
+ def add_note(ti, ni, tone)
88
+ return if @sound.at(ti, ni)&.tone == tone
89
+ @sound.add ti, ni, tone
90
+ @sound.at(ti, ni)&.play 120
91
+ @app.history.append [:put_note, ti, ni, tone]
92
+ end
93
+
94
+ def remove_note(ti, ni, tone)
95
+ #@sound.at(ti, ni)&.play @sound.bpm
96
+ @sound.remove ti, ni
97
+ @app.history.append [:delete_note, ti, ni, tone]
98
+ end
99
+
100
+ def draw()
101
+ sp = sprite
102
+ clip sp.x, sp.y, sp.w, sp.h
103
+
104
+ no_stroke
105
+ fill 0
106
+ rect 0, 0, sp.w, sp.h
107
+
108
+ translate 0, -@scrolly
109
+ draw_grids
110
+ draw_notes
111
+ draw_note_names
112
+ end
113
+
114
+ TONE_COLORS = {
115
+ sine: 5,
116
+ triangle: 29,
117
+ square: 19,
118
+ sawtooth: 30,
119
+ pulse12_5: 27,
120
+ pulse25: 14,
121
+ noise: 12
122
+ }.transform_values {Reight::App::PALETTE_COLORS[_1]}
123
+
124
+ GRID_COLORS = [1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1]
125
+ .map.with_index {|n, i| i == 0 ? 100 : (n == 1 ? 80 : 60)}
126
+
127
+ def draw_grids()
128
+ sp = sprite
129
+ noteh = NOTE_HEIGHT
130
+ Reight::Sound::Note::MAX.downto(0).with_index do |y, index|
131
+ fill GRID_COLORS[index % GRID_COLORS.size]
132
+ rect 0, y * noteh, sp.w, noteh
133
+ end
134
+ end
135
+
136
+ def draw_notes()
137
+ sp = sprite
138
+ notew, noteh = sp.w / SEQUENCE_LEN, NOTE_HEIGHT
139
+ tones, max = Reight::Sound::Note::TONES, Reight::Sound::Note::MAX
140
+ @sound.each_note do |note, index|
141
+ fill TONE_COLORS[note.tone]
142
+ rect index * notew, (max - note.index) * noteh, notew, noteh
143
+ end
144
+ end
145
+
146
+ def draw_note_names()
147
+ fill 200
148
+ text_size 4
149
+ text_align LEFT, CENTER
150
+ noteh = NOTE_HEIGHT
151
+ max = Reight::Sound::Note::MAX
152
+ (0..Reight::Sound::Note::MAX).step(12).with_index do |y, index|
153
+ text "C#{index}", 2, (max - y) * noteh, 10, noteh
154
+ end
155
+ end
156
+
157
+ def mouse_pressed(...)
158
+ tool&.canvas_pressed(...) unless hand?
159
+ end
160
+
161
+ def mouse_released(...)
162
+ tool&.canvas_released(...) unless hand?
163
+ end
164
+
165
+ def mouse_moved(x, y)
166
+ tool&.canvas_moved(x, y)
167
+ @app.flash note_name y
168
+ end
169
+
170
+ def mouse_dragged(...)
171
+ if hand?
172
+ sp = sprite
173
+ @scrolly -= sp.mouse_y - sp.pmouse_y
174
+ else
175
+ tool&.canvas_dragged(...)
176
+ end
177
+ end
178
+
179
+ def mouse_clicked(...)
180
+ tool&.canvas_clicked(...) unless hand?
181
+ end
182
+
183
+ def hand? = @app.pressing?(SPACE)
184
+
185
+ def note_name(y)
186
+ _, note_i = note_pos_at 0, y
187
+ Reight::Sound::Note.new(note_i).to_s.split(':').first.capitalize
188
+ end
189
+
190
+ end# Canvas