reight 0.1.2

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 (58) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/release-gem.yml +62 -0
  3. data/.github/workflows/tag.yml +35 -0
  4. data/.github/workflows/test.yml +37 -0
  5. data/.github/workflows/utils.rb +56 -0
  6. data/.gitignore +1 -0
  7. data/ChangeLog.md +23 -0
  8. data/Gemfile +6 -0
  9. data/LICENSE +21 -0
  10. data/README.md +62 -0
  11. data/Rakefile +30 -0
  12. data/VERSION +1 -0
  13. data/bin/r8 +35 -0
  14. data/lib/reight/all.rb +59 -0
  15. data/lib/reight/app/map/brush.rb +27 -0
  16. data/lib/reight/app/map/brush_base.rb +54 -0
  17. data/lib/reight/app/map/canvas.rb +150 -0
  18. data/lib/reight/app/map/chips.rb +84 -0
  19. data/lib/reight/app/map/editor.rb +117 -0
  20. data/lib/reight/app/map/line.rb +35 -0
  21. data/lib/reight/app/map/rect.rb +29 -0
  22. data/lib/reight/app/map/tool.rb +32 -0
  23. data/lib/reight/app/map.rb +8 -0
  24. data/lib/reight/app/music/editor.rb +25 -0
  25. data/lib/reight/app/music.rb +1 -0
  26. data/lib/reight/app/navigator.rb +172 -0
  27. data/lib/reight/app/runner.rb +275 -0
  28. data/lib/reight/app/sound/editor.rb +25 -0
  29. data/lib/reight/app/sound.rb +1 -0
  30. data/lib/reight/app/sprite/brush.rb +43 -0
  31. data/lib/reight/app/sprite/canvas.rb +254 -0
  32. data/lib/reight/app/sprite/chips.rb +92 -0
  33. data/lib/reight/app/sprite/color.rb +30 -0
  34. data/lib/reight/app/sprite/editor.rb +272 -0
  35. data/lib/reight/app/sprite/fill.rb +45 -0
  36. data/lib/reight/app/sprite/line.rb +37 -0
  37. data/lib/reight/app/sprite/select.rb +58 -0
  38. data/lib/reight/app/sprite/shape.rb +43 -0
  39. data/lib/reight/app/sprite/tool.rb +27 -0
  40. data/lib/reight/app/sprite.rb +10 -0
  41. data/lib/reight/app.rb +123 -0
  42. data/lib/reight/button.rb +93 -0
  43. data/lib/reight/chip.rb +150 -0
  44. data/lib/reight/extension.rb +24 -0
  45. data/lib/reight/helpers.rb +69 -0
  46. data/lib/reight/history.rb +135 -0
  47. data/lib/reight/map.rb +264 -0
  48. data/lib/reight/project.rb +115 -0
  49. data/lib/reight/reight.rb +83 -0
  50. data/lib/reight.rb +18 -0
  51. data/reight.gemspec +40 -0
  52. data/res/icons.png +0 -0
  53. data/test/helper.rb +15 -0
  54. data/test/test_chip.rb +108 -0
  55. data/test/test_chip_list.rb +68 -0
  56. data/test/test_map.rb +232 -0
  57. data/test/test_map_chunk.rb +226 -0
  58. metadata +244 -0
@@ -0,0 +1,275 @@
1
+ using Reight
2
+
3
+
4
+ class Reight::Runner < Reight::App
5
+
6
+ TEMPORARY_HASH = {}
7
+
8
+ def activated()
9
+ super
10
+ run force: true
11
+ @context.call_activated__
12
+ end
13
+
14
+ def deactivated()
15
+ super
16
+ @context.call_deactivated__
17
+ pause
18
+ end
19
+
20
+ def draw()
21
+ push do
22
+ @context&.call_draw__ unless paused?
23
+ end
24
+ super
25
+ end
26
+
27
+ def key_pressed()
28
+ super
29
+ @context&.key_pressed unless paused?
30
+ end
31
+
32
+ def key_released()
33
+ super
34
+ @context&.key_released unless paused?
35
+ end
36
+
37
+ def key_typed()
38
+ super
39
+ @context&.key_typed unless paused?
40
+ end
41
+
42
+ def mouse_pressed()
43
+ super
44
+ @context&.mouse_pressed unless paused?
45
+ end
46
+
47
+ def mouse_released()
48
+ super
49
+ @context&.mouse_released unless paused?
50
+ end
51
+
52
+ def mouse_moved()
53
+ super
54
+ @context&.mouse_moved unless paused?
55
+ navigator.visible = mouse_y < NAVIGATOR_HEIGHT
56
+ end
57
+
58
+ def mouse_dragged()
59
+ super
60
+ @context&.mouse_dragged unless paused?
61
+ end
62
+
63
+ def mouse_clicked()
64
+ super
65
+ @context&.mouse_clicked unless paused?
66
+ end
67
+
68
+ def double_clicked()
69
+ super
70
+ @context&.double_clicked unless paused?
71
+ end
72
+
73
+ def mouse_wheel()
74
+ super
75
+ @context&.mouse_wheel unless paused?
76
+ end
77
+
78
+ def touch_started()
79
+ super
80
+ @context&.touch_started unless paused?
81
+ end
82
+
83
+ def touch_ended()
84
+ super
85
+ @context&.touch_ended unless paused?
86
+ end
87
+
88
+ def touch_moved()
89
+ super
90
+ @context&.touch_moved unless paused?
91
+ end
92
+
93
+ def window_moved()
94
+ super
95
+ @context&.window_moved
96
+ end
97
+
98
+ def window_resized()
99
+ super
100
+ @context&.window_resized
101
+ end
102
+
103
+ private
104
+
105
+ def running? = @context && !@paused
106
+
107
+ def paused? = @context && @paused
108
+
109
+ def run(force: false)
110
+ return pause false if paused? && !force
111
+ cleanup
112
+ backup_global_vars
113
+ @context = create_context
114
+ @paused = false
115
+ begin_wrapping_user_classes @context
116
+ eval_user_script @context, project.code_paths.zip(project.codes).to_h
117
+ end
118
+
119
+ def pause(state = true)
120
+ @paused = state
121
+ end
122
+
123
+ def cleanup()
124
+ remove_world @context.sprite_world__ if @context
125
+ @context = nil
126
+ end_wrapping_user_classes
127
+ restore_global_vars
128
+ GC.enable
129
+ GC.start
130
+ end
131
+
132
+ def create_context()
133
+ klass = Class.new do
134
+ def project = @project__
135
+
136
+ def sprite_world__ = @sprite_world__ ||= create_world(pixels_per_meter: 5)
137
+
138
+ def call_activated__()
139
+ add_world sprite_world__
140
+ activated
141
+ end
142
+
143
+ def call_deactivated__()
144
+ deactivated
145
+ remove_world sprite_world__
146
+ @background_cleared__ = false
147
+ end
148
+
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
159
+ end
160
+
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
+ Reight.to_snake_case(%i[
167
+ activated deactivated setup draw
168
+ keyPressed keyReleased keyRyped
169
+ mousePressed mouseReleased mouseMoved mouseDragged
170
+ mouseClicked doubleClicked mouseWheel
171
+ touchStarted touchEnded touchMoved
172
+ windowMoved windowResized
173
+ ]).each do |camel, snake|
174
+ klass.class_eval <<~END
175
+ def #{camel}(&block)
176
+ if block
177
+ @#{camel}_block__ = block
178
+ else
179
+ @#{camel}_block__&.call
180
+ end
181
+ end
182
+ END
183
+ klass.alias_method snake, camel if snake != camel
184
+ end
185
+ klass.new.tap do |context|
186
+ context.instance_variable_set :@project__, project
187
+ end
188
+ end
189
+
190
+ def begin_wrapping_user_classes(context)
191
+ prefix = get_user_class_prefix context
192
+ wrapper = create_user_class_wrapper context
193
+ @trace_point = TracePoint.trace :class do |tp|
194
+ tp.self.include wrapper if tp.self.name&.start_with? prefix
195
+ end
196
+ end
197
+
198
+ def end_wrapping_user_classes()
199
+ @trace_point&.disable
200
+ @trace_point = nil
201
+ end
202
+
203
+ def get_user_class_prefix(context)
204
+ prefix = nil
205
+ context.instance_eval <<~EVAL
206
+ class Reight_Dummy__; end
207
+ prefix = Reight_Dummy__.name[/^#<Class:0x[0-9a-zA-Z]+>::/]
208
+ singleton_class.__send__ :remove_const, :Reight_Dummy__
209
+ EVAL
210
+ prefix
211
+ end
212
+
213
+ def create_user_class_wrapper(context)
214
+ Module.new.tap do |wrapper|
215
+ wrap_methods context, wrapper
216
+ end
217
+ end
218
+
219
+ def wrap_methods(context, klass)
220
+ klass.define_method :respond_to_missing? do |name, include_private = false|
221
+ context.respond_to?(name, false) || super(name, include_private)
222
+ end
223
+ klass.define_method :method_missing do |name, *args, **kwargs, &block|
224
+ if context.respond_to? name
225
+ klass.define_method(name) {|*a, **k, &b| context.public_send name, *a, **k, &b}
226
+ context.public_send name, *args, **kwargs, &block
227
+ else
228
+ super name, *args, **kwargs, &block
229
+ end
230
+ end
231
+ Reight.to_snake_case(%i[
232
+ project createSprite addSprite removeSprite gravity
233
+ ]).each do |camel, snake|
234
+ klass.define_method(camel) {|*a, **k, &b| context.public_send camel, *a, **k, &b}
235
+ klass.alias_method snake, camel if snake != camel
236
+ end
237
+ end
238
+
239
+ def eval_user_script(context, codes)
240
+ TEMPORARY_HASH[:params] = {context: context, codes: codes}
241
+ context.class.class_eval <<~END
242
+ ::Reight::Runner::TEMPORARY_HASH[:params] => {context:, codes:}
243
+ codes.each {|path, code| context.instance_eval code, path if code}
244
+ END
245
+ ensure
246
+ TEMPORARY_HASH.delete :params
247
+ end
248
+
249
+ def backup_global_vars()
250
+ @global_vars = global_variables
251
+ .each.with_object({}) {|name, hash| hash[name] = eval name.to_s}
252
+ .freeze
253
+ end
254
+
255
+ def restore_global_vars()
256
+ return unless @global_vars
257
+ global_variables
258
+ .map {|name| [name, eval(name.to_s)]}
259
+ .select {|name, value| value != nil && @global_vars[name] == nil}
260
+ .each {|name, value| global_var_set name, nil}
261
+ %i[$, $/ $-0 $; $-F $-d $-i $-v $-w $. $\\ $_ $~ $DEBUG $VERBOSE]
262
+ .select {|name| @global_vars.key? name}
263
+ .each {|name| global_var_set name, @global_vars[name]}
264
+ /x/ =~ '' # clear vars about last result for regexp
265
+ @global_vars = nil
266
+ end
267
+
268
+ def global_var_set(name, value)
269
+ TEMPORARY_HASH[:value] = value
270
+ eval "#{name} = ::Reight::Runner::TEMPORARY_HASH[:value]"
271
+ ensure
272
+ TEMPORARY_HASH.delete :value
273
+ end
274
+
275
+ end# Runner
@@ -0,0 +1,25 @@
1
+ using Reight
2
+
3
+
4
+ class Reight::SoundEditor < Reight::App
5
+
6
+ def draw()
7
+ background 200
8
+
9
+ push do
10
+ text_align LEFT, CENTER
11
+
12
+ text_size 10
13
+ fill 220; text "Sound Editor", 150, 1, 200, height
14
+ fill 150; text "Sound Editor", 150, 0, 200, height
15
+
16
+ text_size 8
17
+ dots = '.' * (frame_count / 60 % 4).to_i
18
+ fill 220; text "is under construction#{dots}", 150, 11, 200, height
19
+ fill 150; text "is under construction#{dots}", 150, 10, 200, height
20
+ end
21
+
22
+ super
23
+ end
24
+
25
+ end# SoundEditor
@@ -0,0 +1 @@
1
+ require 'reight/app/sound/editor'
@@ -0,0 +1,43 @@
1
+ using Reight
2
+
3
+
4
+ class Reight::SpriteEditor::Brush < Reight::SpriteEditor::Tool
5
+
6
+ def initialize(app, &block)
7
+ super app, icon: app.icon(1, 2, 8), &block
8
+ @size = 1
9
+ set_help left: 'Brush', right: 'Pick Color'
10
+ end
11
+
12
+ attr_accessor :size
13
+
14
+ def brush(x, y, button)
15
+ canvas.paint do |g|
16
+ g.no_fill
17
+ g.stroke(*canvas.color)
18
+ g.stroke_weight size
19
+ g.point x, y
20
+ end
21
+ end
22
+
23
+ def canvas_pressed(x, y, button)
24
+ return unless button == LEFT
25
+ canvas.begin_editing
26
+ brush x, y, button
27
+ end
28
+
29
+ def canvas_released(x, y, button)
30
+ return unless button == LEFT
31
+ canvas.end_editing
32
+ end
33
+
34
+ def canvas_dragged(x, y, button)
35
+ return unless button == LEFT
36
+ brush x, y, button
37
+ end
38
+
39
+ def canvas_clicked(x, y, button)
40
+ pick_color x, y if button == RIGHT
41
+ end
42
+
43
+ end# Brush
@@ -0,0 +1,254 @@
1
+ using Reight
2
+
3
+
4
+ class Reight::SpriteEditor::Canvas
5
+
6
+ include Reight::Hookable
7
+
8
+ def initialize(app, image, path)
9
+ hook :tool_changed, :color_changed
10
+
11
+ @app, @image, @path = app, image, path
12
+ @tool, @color, @selection = nil, [255, 255, 255], nil
13
+
14
+ @app.history.disable do
15
+ set_frame 0, 0, 16, 16
16
+ end
17
+ end
18
+
19
+ attr_reader :x, :y, :w, :h, :tool, :color
20
+
21
+ def width = @image.width
22
+
23
+ def height = @image.height
24
+
25
+ def save()
26
+ @image.save @path
27
+ @app.project.save
28
+ end
29
+
30
+ def tool=(tool)
31
+ @tool = tool
32
+ tool_changed! tool
33
+ end
34
+
35
+ def set_frame(x, y, w, h)
36
+ old = [@x, @y, @w, @h]
37
+ new = correct_bounds x, y, w, h
38
+ return if new == old
39
+ @x, @y, @w, @h = new
40
+ @app.history.append [:frame, old, new]
41
+ end
42
+
43
+ def frame = [x, y, w, h]
44
+
45
+ def color=(color)
46
+ return if color == @color
47
+ @color = color
48
+ color_changed! color
49
+ end
50
+
51
+ def select(x, y, w, h)
52
+ old = @selection
53
+ new = correct_bounds x, y, w, h
54
+ return if new == old
55
+ @selection = new
56
+ @app.history.append [:select, old, new]
57
+ end
58
+
59
+ def selection()
60
+ xrange, yrange = x..(x + w), y..(y + h)
61
+ sx, sy, sw, sh = @selection
62
+ return nil unless
63
+ xrange.include?(sx) && xrange.include?(sx + sw) &&
64
+ yrange.include?(sy) && yrange.include?(sy + sh)
65
+ @selection
66
+ end
67
+
68
+ def deselect()
69
+ return if @selection == nil
70
+ old, @selection = @selection, nil
71
+ @app.history.append [:deselect, old]
72
+ end
73
+
74
+ def paint(&block)
75
+ @image.begin_draw do |g|
76
+ g.clip(*(selection || frame))
77
+ g.push do
78
+ g.translate x, y
79
+ block.call g
80
+ end
81
+ end
82
+ end
83
+
84
+ def update_pixels(&block)
85
+ tmp = sub_image x, y, w, h
86
+ tmp.update_pixels {|pixels| block.call pixels}
87
+ @image.begin_draw do |g|
88
+ g.copy tmp, 0, 0, w, h, x, y, w, h
89
+ end
90
+ end
91
+
92
+ def sub_image(x, y, w, h)
93
+ create_graphics(w, h).tap do |g|
94
+ g.begin_draw {g.copy @image, x, y, w, h, 0, 0, w, h}
95
+ end
96
+ end
97
+
98
+ def pixel_at(x, y)
99
+ img = sub_image x, y, 1, 1
100
+ img.load_pixels
101
+ c = img.pixels[0]
102
+ [red(c), green(c), blue(c), alpha(c)].map &:to_i
103
+ end
104
+
105
+ def clear(frame, color: [0, 0, 0])
106
+ paint do |g|
107
+ g.fill(*color)
108
+ g.no_stroke
109
+ g.rect(*frame)
110
+ end
111
+ end
112
+
113
+ def begin_editing(&block)
114
+ @before = capture_frame
115
+ block.call if block
116
+ ensure
117
+ end_editing if block
118
+ end
119
+
120
+ def end_editing()
121
+ return unless @before
122
+ @app.history.append [:capture, @before, capture_frame, x, y]
123
+ save
124
+ end
125
+
126
+ def capture_frame(frame = self.frame)
127
+ x, y, w, h = frame
128
+ create_graphics(w, h).tap do |g|
129
+ g.begin_draw do
130
+ g.copy @image, x, y, w, h, 0, 0, w, h
131
+ end
132
+ end
133
+ end
134
+
135
+ def apply_frame(image, x, y)
136
+ @image.begin_draw do |g|
137
+ w, h = image.width, image.height
138
+ g.copy image, 0, 0, w, h, x, y, w, h
139
+ end
140
+ save
141
+ end
142
+
143
+ def sprite()
144
+ @sprite ||= Sprite.new.tap do |sp|
145
+ pos = -> {to_image sp.mouse_x, sp.mouse_y}
146
+ sp.draw {draw}
147
+ sp.mouse_pressed {tool&.canvas_pressed( *pos.call, sp.mouse_button)}
148
+ sp.mouse_released {tool&.canvas_released(*pos.call, sp.mouse_button)}
149
+ sp.mouse_moved {tool&.canvas_moved( *pos.call)}
150
+ sp.mouse_dragged {tool&.canvas_dragged( *pos.call, sp.mouse_button)}
151
+ sp.mouse_clicked {tool&.canvas_clicked( *pos.call, sp.mouse_button)}
152
+ end
153
+ end
154
+
155
+ private
156
+
157
+ def to_image(x, y)
158
+ return x, y unless @w && @h
159
+ sp = sprite
160
+ return x * (@w.to_f / sp.w), y * (@h.to_f / sp.h)
161
+ end
162
+
163
+ def correct_bounds(x, y, w, h)
164
+ x2, y2 = x + w, y + h
165
+ x, x2 = x2, x if x > x2
166
+ y, y2 = y2, y if y > y2
167
+ x, y = x.floor, y.floor
168
+ return x, y, (x2 - x).ceil, (y2 - y).ceil
169
+ end
170
+
171
+ def draw()
172
+ sp = sprite
173
+ clip sp.x, sp.y, sp.w, sp.h
174
+ copy @image, x, y, w, h, 0, 0, sp.w, sp.h if @image && x && y && w && h
175
+
176
+ sx, sy = sp.w / w, sp.h / h
177
+ scale sx, sy
178
+ translate(-x, -y)
179
+ no_fill
180
+ stroke_weight 0
181
+
182
+ draw_grids
183
+ draw_selection sx, sy
184
+ end
185
+
186
+ def draw_grids()
187
+ push do
188
+ stroke 50, 50, 50
189
+ shape grid 8
190
+ stroke 100, 100, 100
191
+ shape grid 16
192
+ stroke 150, 150, 150
193
+ shape grid 32
194
+ end
195
+ end
196
+
197
+ def grid(interval)
198
+ (@grids ||= {})[interval] ||= create_shape.tap do |sh|
199
+ w, h = @image.width, @image.height
200
+ sh.begin_shape LINES
201
+ (0..w).step(interval).each do |x|
202
+ sh.vertex x, 0
203
+ sh.vertex x, h
204
+ end
205
+ (0..h).step(interval).each do |y|
206
+ sh.vertex 0, y
207
+ sh.vertex w, y
208
+ end
209
+ sh.end_shape
210
+ end
211
+ end
212
+
213
+ def draw_selection(scale_x, scale_y)
214
+ return unless @selection&.size == 4
215
+ push do
216
+ stroke 255, 255, 255
217
+ shader selection_shader.tap {|sh|
218
+ sh.set :time, frame_count.to_f / 60
219
+ sh.set :scale, scale_x, scale_y
220
+ }
221
+ =begin
222
+ begin_shape LINES
223
+ x, y, w, h = @selection
224
+ vertex x, y, x, 0
225
+ vertex x + w, y, x + w, 0
226
+ vertex x + w, y, x, 0
227
+ vertex x + w, y + h, x + w, 0
228
+ vertex x + w, y + h, x, 0
229
+ vertex x, y + h, x + w, 0
230
+ vertex x, y + h, x, 0
231
+ vertex x, y, x + w, 0
232
+ end_shape
233
+ =end
234
+ rect(*@selection)
235
+ end
236
+ end
237
+
238
+ def selection_shader()
239
+ @selection_shader ||= create_shader nil, <<~END
240
+ varying vec4 vertTexCoord;
241
+ uniform float time;
242
+ uniform vec2 scale;
243
+ void main()
244
+ {
245
+ vec2 pos = vertTexCoord.xy * scale;
246
+ float t = floor(time * 4.) / 4.;
247
+ float x = mod( pos.x + time, 4.) < 2. ? 1. : 0.;
248
+ float y = mod(-pos.y + time, 4.) < 2. ? 1. : 0.;
249
+ gl_FragColor = x != y ? vec4(0., 0., 0., 1.) : vec4(1., 1., 1., 1.);
250
+ }
251
+ END
252
+ end
253
+
254
+ end# Canvas
@@ -0,0 +1,92 @@
1
+ using Reight
2
+
3
+
4
+ class Reight::SpriteEditor::Chips
5
+
6
+ include Reight::Hookable
7
+
8
+ def initialize(app, image, size = 8, &block)
9
+ hook :frame_changed
10
+
11
+ @app, @image = app, image
12
+ @offset = create_vector
13
+
14
+ @app.history.disable do
15
+ set_frame 0, 0, size, size
16
+ end
17
+
18
+ frame_changed(&block)
19
+ end
20
+
21
+ attr_reader :x, :y, :size
22
+
23
+ def set_frame(x, y, w, h)
24
+ raise 'Chips: width != height' if w != h
25
+ @x = align_to_grid(x).clamp(0..@image.width)
26
+ @y = align_to_grid(y).clamp(0..@image.height)
27
+ @size = w
28
+ frame_changed! @x, @y, @size, @size
29
+ end
30
+
31
+ def draw()
32
+ sp = sprite
33
+ clip sp.x, sp.y, sp.w, sp.h
34
+ translate(*clamp_offset(@offset).to_a)
35
+ image @image, 0, 0
36
+
37
+ no_fill
38
+ stroke 255, 255, 255
39
+ stroke_weight 1
40
+ rect @x, @y, @size, @size
41
+ end
42
+
43
+ def mouse_pressed(x, y)
44
+ @prev_pos = create_vector x, y
45
+ end
46
+
47
+ def mouse_released(x, y)
48
+ @offset = clamp_offset @offset
49
+ end
50
+
51
+ def mouse_dragged(x, y)
52
+ pos = create_vector x, y
53
+ @offset += pos - @prev_pos if @prev_pos
54
+ @prev_pos = pos
55
+ end
56
+
57
+ def mouse_clicked(x, y)
58
+ set_frame(
59
+ -@offset.x + align_to_grid(x),
60
+ -@offset.y + align_to_grid(y),
61
+ size,
62
+ size)
63
+ end
64
+
65
+ def sprite()
66
+ @sprite ||= Sprite.new.tap do |sp|
67
+ sp.draw {draw}
68
+ sp.mouse_pressed {mouse_pressed sp.mouse_x, sp.mouse_y}
69
+ sp.mouse_released {mouse_released sp.mouse_x, sp.mouse_y}
70
+ sp.mouse_dragged {mouse_dragged sp.mouse_x, sp.mouse_y}
71
+ sp.mouse_clicked {mouse_clicked sp.mouse_x, sp.mouse_y}
72
+ end
73
+ end
74
+
75
+ private
76
+
77
+ def clamp_offset(offset)
78
+ sp = sprite
79
+ x = offset.x.clamp([-(@image.width - sp.w), 0].min..0)
80
+ y = offset.y.clamp([-(@image.height - sp.h), 0].min..0)
81
+ create_vector align_to_grid(x), align_to_grid(y)
82
+ end
83
+
84
+ def align_to_grid(n)
85
+ n.to_i / 8 * 8
86
+ end
87
+
88
+ def selected()
89
+ @selected.call @x, @y, @size, @size
90
+ end
91
+
92
+ end# Chips
@@ -0,0 +1,30 @@
1
+ using Reight
2
+
3
+
4
+ class Reight::SpriteEditor::Color < Reight::Button
5
+
6
+ def initialize(color, &clicked)
7
+ super name: '', &clicked
8
+ @color = color
9
+ end
10
+
11
+ attr_reader :color
12
+
13
+ def draw()
14
+ sp = sprite
15
+
16
+ fill(*color)
17
+ no_stroke
18
+ rect 0, 0, sp.w, sp.h
19
+
20
+ if active?
21
+ no_fill
22
+ stroke_weight 1
23
+ stroke '#000000'
24
+ rect 2, 2, sp.w - 4, sp.h - 4
25
+ stroke '#ffffff'
26
+ rect 1, 1, sp.w - 2, sp.h - 2
27
+ end
28
+ end
29
+
30
+ end# Color