processing 0.4.0 → 0.5.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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/{release.yml → release-gem.yml} +21 -21
  3. data/.github/workflows/tag.yml +35 -0
  4. data/.github/workflows/test.yml +8 -9
  5. data/.github/workflows/utils.rb +55 -0
  6. data/ChangeLog.md +40 -28
  7. data/LICENSE +1 -1
  8. data/Rakefile +9 -34
  9. data/RubyProcessing.podspec +1 -1
  10. data/VERSION +1 -1
  11. data/examples/breakout.rb +2 -1
  12. data/examples/camera.rb +2 -1
  13. data/examples/clock.rb +3 -1
  14. data/examples/delay_camera.rb +2 -1
  15. data/examples/hello.rb +2 -1
  16. data/examples/image.rb +2 -1
  17. data/examples/shapes.rb +2 -1
  18. data/lib/processing/all.rb +20 -0
  19. data/lib/processing/capture.rb +119 -0
  20. data/lib/processing/context.rb +471 -0
  21. data/lib/processing/{module.rb → extension.rb} +3 -3
  22. data/lib/processing/font.rb +62 -0
  23. data/lib/processing/graphics.rb +40 -0
  24. data/lib/processing/graphics_context.rb +1676 -0
  25. data/lib/processing/image.rb +128 -0
  26. data/lib/processing/shader.rb +157 -0
  27. data/lib/processing/touch.rb +28 -0
  28. data/lib/processing/vector.rb +559 -0
  29. data/lib/processing.rb +30 -11
  30. data/lib/rubysketch-processing.rb +2 -1
  31. data/lib/rubysketch.rb +1 -1
  32. data/processing.gemspec +12 -6
  33. data/src/RubyProcessing.mm +1 -1
  34. data/test/helper.rb +8 -2
  35. data/test/{processing/test_graphics.rb → test_graphics.rb} +3 -3
  36. data/test/{processing/test_shader.rb → test_shader.rb} +3 -3
  37. data/test/{processing/test_utility.rb → test_utility.rb} +3 -3
  38. data/test/{processing/test_vector.rb → test_vector.rb} +8 -4
  39. metadata +112 -20
  40. data/lib/processing/include.rb +0 -25
  41. data/lib/processing/processing.rb +0 -3211
  42. data/test/processing/helper.rb +0 -11
@@ -0,0 +1,119 @@
1
+ module Processing
2
+
3
+
4
+ # Camera object.
5
+ #
6
+ class Capture
7
+
8
+ # Returns a list of available camera device names
9
+ #
10
+ # @return [Array] device name list
11
+ #
12
+ def self.list()
13
+ Rays::Camera.device_names
14
+ end
15
+
16
+ # Initialize camera object.
17
+ #
18
+ # @overload Capture.new()
19
+ # @overload Capture.new(cameraName)
20
+ # @overload Capture.new(requestWidth, requestHeight)
21
+ # @overload Capture.new(requestWidth, requestHeight, cameraName)
22
+ #
23
+ # @param requestWidth [Integer] captured image width
24
+ # @param requestHeight [Integer] captured image height
25
+ # @param cameraName [String] camera device name
26
+ #
27
+ def initialize(*args)
28
+ width, height, name =
29
+ if args.empty?
30
+ [-1, -1, nil]
31
+ elsif args[0].kind_of?(String)
32
+ [-1, -1, args[0]]
33
+ elsif args[0].kind_of?(Numeric) && args[1].kind_of?(Numeric)
34
+ [args[0], args[1], args[2]]
35
+ else
36
+ raise ArgumentError
37
+ end
38
+ @camera = Rays::Camera.new width, height, device_name: name
39
+ end
40
+
41
+ # Start capturing.
42
+ #
43
+ # @return [nil] nil
44
+ #
45
+ def start()
46
+ raise "Failed to start capture" unless @camera.start
47
+ nil
48
+ end
49
+
50
+ # Stop capturing.
51
+ #
52
+ # @return [nil] nil
53
+ #
54
+ def stop()
55
+ @camera.stop
56
+ nil
57
+ end
58
+
59
+ # Returns is the next captured image available?
60
+ #
61
+ # @return [Boolean] true means object has next frame
62
+ #
63
+ def available()
64
+ @camera.active?
65
+ end
66
+
67
+ # Reads next frame image
68
+ #
69
+ def read()
70
+ @camera.image
71
+ end
72
+
73
+ # Returns the width of captured image
74
+ #
75
+ # @return [Numeric] the width of captured image
76
+ #
77
+ def width()
78
+ @camera.image&.width || 0
79
+ end
80
+
81
+ # Returns the height of captured image
82
+ #
83
+ # @return [Numeric] the height of captured image
84
+ #
85
+ def height()
86
+ @camera.image&.height || 0
87
+ end
88
+
89
+ # Applies an image filter.
90
+ #
91
+ # overload filter(shader)
92
+ # overload filter(type)
93
+ # overload filter(type, param)
94
+ #
95
+ # @param shader [Shader] a fragment shader to apply
96
+ # @param type [THRESHOLD, GRAY, INVERT, BLUR] filter type
97
+ # @param param [Numeric] a parameter for each filter
98
+ #
99
+ def filter(*args)
100
+ @filter = Shader.createFilter__(*args)
101
+ end
102
+
103
+ # @private
104
+ def getInternal__()
105
+ @camera.image || (@dummyImage ||= Rays::Image.new 1, 1)
106
+ end
107
+
108
+ # @private
109
+ def drawImage__(painter, *args, **states)
110
+ shader = painter.shader || @filter&.getInternal__
111
+ painter.push shader: shader, **states do |_|
112
+ painter.image getInternal__, *args
113
+ end
114
+ end
115
+
116
+ end# Capture
117
+
118
+
119
+ end# Processing
@@ -0,0 +1,471 @@
1
+ module Processing
2
+
3
+
4
+ # Processing context
5
+ #
6
+ class Context
7
+
8
+ include GraphicsContext
9
+
10
+ Vector = Processing::Vector
11
+ Capture = Processing::Capture
12
+ Graphics = Processing::Graphics
13
+ Shader = Processing::Shader
14
+
15
+ # @private
16
+ @@context__ = nil
17
+
18
+ # @private
19
+ def self.context__()
20
+ @@context__
21
+ end
22
+
23
+ # @private
24
+ def initialize(window)
25
+ @@context__ = self
26
+
27
+ tmpdir__.tap {|dir| FileUtils.rm_r dir.to_s if dir.directory?}
28
+
29
+ @window__ = window
30
+ init__(
31
+ @window__.canvas_image,
32
+ @window__.canvas_painter.paint {background 0.8})
33
+
34
+ @loop__ = true
35
+ @redraw__ = false
36
+ @frameCount__ = 0
37
+ @key__ = nil
38
+ @keyCode__ = nil
39
+ @keysPressed__ = Set.new
40
+ @pointerPos__ =
41
+ @pointerPrevPos__ = Rays::Point.new 0
42
+ @pointersPressed__ = []
43
+ @touches__ = []
44
+ @motionGravity__ = createVector 0, 0
45
+
46
+ @window__.before_draw = proc {beginDraw__}
47
+ @window__.after_draw = proc {endDraw__}
48
+ @window__.update_canvas = proc {|i, p| updateCanvas__ i, p}
49
+
50
+ @window__.instance_variable_set :@context, self
51
+ def @window__.draw_screen(painter)
52
+ @context.drawImage__ painter
53
+ end
54
+
55
+ drawFrame = -> {
56
+ begin
57
+ push
58
+ @drawBlock__.call if @drawBlock__
59
+ ensure
60
+ pop
61
+ @frameCount__ += 1
62
+ end
63
+ }
64
+
65
+ @window__.draw = proc do |e|
66
+ if @loop__ || @redraw__
67
+ @redraw__ = false
68
+ drawFrame.call
69
+ end
70
+ end
71
+
72
+ updateKeyStates = -> event, pressed {
73
+ @key__ = event.chars
74
+ @keyCode__ = event.key
75
+ if pressed != nil
76
+ set, key = @keysPressed__, event.key
77
+ pressed ? set.add(key) : set.delete(key)
78
+ end
79
+ }
80
+
81
+ mouseButtonMap = {
82
+ mouse_left: LEFT,
83
+ mouse_right: RIGHT,
84
+ mouse_middle: CENTER
85
+ }
86
+
87
+ updatePointerStates = -> event, pressed = nil {
88
+ @pointerPrevPos__ = @pointerPos__
89
+ @pointerPos__ = event.pos.dup
90
+ @touches__ = event.pointers.map {|p| Touch.new(p.id, *p.pos.to_a)}
91
+ if pressed != nil
92
+ array = @pointersPressed__
93
+ event.types
94
+ .tap {|types| types.delete :mouse}
95
+ .map {|type| mouseButtonMap[type] || type}
96
+ .each {|type| pressed ? array.push(type) : array.delete(type)}
97
+ end
98
+ }
99
+
100
+ @window__.key_down = proc do |e|
101
+ updateKeyStates.call e, true
102
+ @keyPressedBlock__&.call
103
+ @keyTypedBlock__&.call if @key__ && !@key__.empty?
104
+ end
105
+
106
+ @window__.key_up = proc do |e|
107
+ updateKeyStates.call e, false
108
+ @keyReleasedBlock__&.call
109
+ end
110
+
111
+ @window__.pointer_down = proc do |e|
112
+ updatePointerStates.call e, true
113
+ @pointerDownStartPos__ = @pointerPos__.dup
114
+ (@touchStartedBlock__ || @mousePressedBlock__)&.call
115
+ end
116
+
117
+ @window__.pointer_up = proc do |e|
118
+ updatePointerStates.call e, false
119
+ (@touchEndedBlock__ || @mouseReleasedBlock__)&.call
120
+ if startPos = @pointerDownStartPos__
121
+ @mouseClickedBlock__&.call if (@pointerPos__ - startPos).length < 3
122
+ @pointerDownStartPos__ = nil
123
+ end
124
+ end
125
+
126
+ @window__.pointer_move = proc do |e|
127
+ updatePointerStates.call e
128
+ (@touchMovedBlock__ || @mouseMovedBlock__)&.call
129
+ end
130
+
131
+ @window__.pointer_drag = proc do |e|
132
+ updatePointerStates.call e
133
+ (@touchMovedBlock__ || @mouseDraggedBlock__)&.call
134
+ end
135
+
136
+ @window__.motion = proc do |e|
137
+ @motionGravity__ = createVector(*e.gravity.to_a(3))
138
+ @motionBlock__&.call
139
+ end
140
+ end
141
+
142
+ # Defines setup block.
143
+ #
144
+ # @return [nil] nil
145
+ #
146
+ def setup(&block)
147
+ @window__.setup = block
148
+ nil
149
+ end
150
+
151
+ # Defines draw block.
152
+ #
153
+ # @return [nil] nil
154
+ #
155
+ def draw(&block)
156
+ @drawBlock__ = block if block
157
+ nil
158
+ end
159
+
160
+ # @private
161
+ def hasDrawBlock__()
162
+ @drawBlock__ != nil
163
+ end
164
+
165
+ # Defines keyPressed block.
166
+ #
167
+ # @return [Boolean] is any key pressed or not
168
+ #
169
+ def keyPressed(&block)
170
+ @keyPressedBlock__ = block if block
171
+ not @keysPressed__.empty?
172
+ end
173
+
174
+ # Defines keyReleased block.
175
+ #
176
+ # @return [nil] nil
177
+ #
178
+ def keyReleased(&block)
179
+ @keyReleasedBlock__ = block if block
180
+ nil
181
+ end
182
+
183
+ # Defines keyTyped block.
184
+ #
185
+ # @return [nil] nil
186
+ #
187
+ def keyTyped(&block)
188
+ @keyTypedBlock__ = block if block
189
+ nil
190
+ end
191
+
192
+ # Defines mousePressed block.
193
+ #
194
+ # @return [Boolean] is any mouse button pressed or not
195
+ #
196
+ def mousePressed(&block)
197
+ @mousePressedBlock__ = block if block
198
+ not @pointersPressed__.empty?
199
+ end
200
+
201
+ # Defines mouseReleased block.
202
+ #
203
+ # @return [nil] nil
204
+ #
205
+ def mouseReleased(&block)
206
+ @mouseReleasedBlock__ = block if block
207
+ nil
208
+ end
209
+
210
+ # Defines mouseMoved block.
211
+ #
212
+ # @return [nil] nil
213
+ #
214
+ def mouseMoved(&block)
215
+ @mouseMovedBlock__ = block if block
216
+ nil
217
+ end
218
+
219
+ # Defines mouseDragged block.
220
+ #
221
+ # @return [nil] nil
222
+ #
223
+ def mouseDragged(&block)
224
+ @mouseDraggedBlock__ = block if block
225
+ nil
226
+ end
227
+
228
+ # Defines mouseClicked block.
229
+ #
230
+ # @return [nil] nil
231
+ #
232
+ def mouseClicked(&block)
233
+ @mouseClickedBlock__ = block if block
234
+ nil
235
+ end
236
+
237
+ # Defines touchStarted block.
238
+ #
239
+ # @return [nil] nil
240
+ #
241
+ def touchStarted(&block)
242
+ @touchStartedBlock__ = block if block
243
+ nil
244
+ end
245
+
246
+ # Defines touchEnded block.
247
+ #
248
+ # @return [nil] nil
249
+ #
250
+ def touchEnded(&block)
251
+ @touchEndedBlock__ = block if block
252
+ nil
253
+ end
254
+
255
+ # Defines touchMoved block.
256
+ #
257
+ # @return [nil] nil
258
+ #
259
+ def touchMoved(&block)
260
+ @touchMovedBlock__ = block if block
261
+ nil
262
+ end
263
+
264
+ # Defines motion block.
265
+ #
266
+ # @return [nil] nil
267
+ #
268
+ def motion(&block)
269
+ @motionBlock__ = block if block
270
+ nil
271
+ end
272
+
273
+ # Changes canvas size.
274
+ #
275
+ # @param width [Integer] new width
276
+ # @param height [Integer] new height
277
+ # @param pixelDensity [Numeric] new pixel density
278
+ #
279
+ # @return [nil] nil
280
+ #
281
+ def size(width, height, pixelDensity: self.pixelDensity)
282
+ resizeCanvas__ :size, width, height, pixelDensity
283
+ nil
284
+ end
285
+
286
+ # Changes canvas size.
287
+ #
288
+ # @param width [Integer] new width
289
+ # @param height [Integer] new height
290
+ # @param pixelDensity [Numeric] new pixel density
291
+ #
292
+ # @return [nil] nil
293
+ #
294
+ def createCanvas(width, height, pixelDensity: self.pixelDensity)
295
+ resizeCanvas__ :createCanvas, width, height, pixelDensity
296
+ nil
297
+ end
298
+
299
+ # Changes title of window.
300
+ #
301
+ # @param title [String] new title
302
+ #
303
+ # @return [nil] nil
304
+ #
305
+ def setTitle(title)
306
+ @window__.title = title
307
+ nil
308
+ end
309
+
310
+ # Changes and returns canvas pixel density.
311
+ #
312
+ # @param density [Numeric] new pixel density
313
+ #
314
+ # @return [Numeric] current pixel density
315
+ #
316
+ def pixelDensity(density = nil)
317
+ resizeCanvas__ :pixelDensity, width, height, density if density
318
+ @painter__.pixel_density
319
+ end
320
+
321
+ # @private
322
+ def resizeCanvas__(name, width, height, pixelDensity)
323
+ raise '#{name}() must be called on startup or setup block' if @started__
324
+
325
+ @painter__.__send__ :end_paint
326
+ @window__.resize_canvas width, height, pixelDensity
327
+ @window__.auto_resize = false
328
+ ensure
329
+ @painter__.__send__ :begin_paint
330
+ end
331
+
332
+ # Returns pixel density of display.
333
+ #
334
+ # @return [Numeric] pixel density
335
+ #
336
+ def displayDensity()
337
+ @window__.painter.pixel_density
338
+ end
339
+
340
+ # Returns window width.
341
+ #
342
+ # @return [Numeric] window width
343
+ #
344
+ def windowWidth()
345
+ @window__.width
346
+ end
347
+
348
+ # Returns window height.
349
+ #
350
+ # @return [Numeric] window height
351
+ #
352
+ def windowHeight()
353
+ @window__.height
354
+ end
355
+
356
+ # Returns number of frames since program started.
357
+ #
358
+ # @return [Integer] total number of frames
359
+ #
360
+ def frameCount()
361
+ @frameCount__
362
+ end
363
+
364
+ # Returns number of frames per second.
365
+ #
366
+ # @return [Float] frames per second
367
+ #
368
+ def frameRate()
369
+ @window__.event.fps
370
+ end
371
+
372
+ # Returns the last key that was pressed or released.
373
+ #
374
+ # @return [String] last key
375
+ #
376
+ def key()
377
+ @key__
378
+ end
379
+
380
+ # Returns the last key code that was pressed or released.
381
+ #
382
+ # @return [Symbol] last key code
383
+ #
384
+ def keyCode()
385
+ @keyCode__
386
+ end
387
+
388
+ # Returns mouse x position
389
+ #
390
+ # @return [Numeric] horizontal position of mouse
391
+ #
392
+ def mouseX()
393
+ @pointerPos__.x
394
+ end
395
+
396
+ # Returns mouse y position
397
+ #
398
+ # @return [Numeric] vertical position of mouse
399
+ #
400
+ def mouseY()
401
+ @pointerPos__.y
402
+ end
403
+
404
+ # Returns mouse x position in previous frame
405
+ #
406
+ # @return [Numeric] horizontal position of mouse
407
+ #
408
+ def pmouseX()
409
+ @pointerPrevPos__.x
410
+ end
411
+
412
+ # Returns mouse y position in previous frame
413
+ #
414
+ # @return [Numeric] vertical position of mouse
415
+ #
416
+ def pmouseY()
417
+ @pointerPrevPos__.y
418
+ end
419
+
420
+ # Returns which mouse button was pressed
421
+ #
422
+ # @return [Numeric] LEFT, RIGHT, CENTER or 0
423
+ #
424
+ def mouseButton()
425
+ (@pointersPressed__ & [LEFT, RIGHT, CENTER]).last || 0
426
+ end
427
+
428
+ # Returns array of touches
429
+ #
430
+ # @return [Array] Touch objects
431
+ #
432
+ def touches()
433
+ @touches__
434
+ end
435
+
436
+ # Returns vector for real world gravity
437
+ #
438
+ # @return [Vector] gravity vector
439
+ #
440
+ def motionGravity()
441
+ @motionGravity__
442
+ end
443
+
444
+ # Enables calling draw block on every frame.
445
+ #
446
+ # @return [nil] nil
447
+ #
448
+ def loop()
449
+ @loop__ = true
450
+ end
451
+
452
+ # Disables calling draw block on every frame.
453
+ #
454
+ # @return [nil] nil
455
+ #
456
+ def noLoop()
457
+ @loop__ = false
458
+ end
459
+
460
+ # Calls draw block to redraw frame.
461
+ #
462
+ # @return [nil] nil
463
+ #
464
+ def redraw()
465
+ @redraw__ = true
466
+ end
467
+
468
+ end# Context
469
+
470
+
471
+ end# Processing
@@ -1,7 +1,7 @@
1
1
  module Processing
2
2
 
3
3
 
4
- module Module
4
+ module Extension
5
5
 
6
6
  module_function
7
7
 
@@ -10,14 +10,14 @@ module Processing
10
10
  end
11
11
 
12
12
  def version()
13
- open(root_dir 'VERSION') {|f| f.readline.chomp}
13
+ File.read(root_dir 'VERSION')[/[\d\.]+/]
14
14
  end
15
15
 
16
16
  def root_dir(path = '')
17
17
  File.expand_path "../../#{path}", __dir__
18
18
  end
19
19
 
20
- end# Module
20
+ end# Extension
21
21
 
22
22
 
23
23
  end# Processing
@@ -0,0 +1,62 @@
1
+ module Processing
2
+
3
+
4
+ # Font object.
5
+ #
6
+ class Font
7
+
8
+ # @private
9
+ def initialize(font)
10
+ @font = font
11
+ end
12
+
13
+ # Returns bounding box.
14
+ #
15
+ # @overload textBounds(str)
16
+ # @overload textBounds(str, x, y)
17
+ # @overload textBounds(str, x, y, fontSize)
18
+ #
19
+ # @param str [String] text to calculate bounding box
20
+ # @param x [Numeric] horizontal position of bounding box
21
+ # @param y [Numeric] vertical position of bounding box
22
+ # @param fontSize [Numeric] font size
23
+ #
24
+ # @return [TextBounds] bounding box for text
25
+ #
26
+ def textBounds(str, x = 0, y = 0, fontSize = nil)
27
+ f = fontSize ? Rays::Font.new(@font.name, fontSize) : @font
28
+ TextBounds.new x, y, x + f.width(str), y + f.height
29
+ end
30
+
31
+ end# Font
32
+
33
+
34
+ # Bounding box for text.
35
+ #
36
+ class TextBounds
37
+
38
+ # Horizontal position
39
+ #
40
+ attr_reader :x
41
+
42
+ # Vertical position
43
+ #
44
+ attr_reader :y
45
+
46
+ # Width of bounding box
47
+ #
48
+ attr_reader :w
49
+
50
+ # Height of bounding box
51
+ #
52
+ attr_reader :h
53
+
54
+ # @private
55
+ def initialize(x, y, w, h)
56
+ @x, @y, @w, @h = x, y, w, h
57
+ end
58
+
59
+ end# TextBounds
60
+
61
+
62
+ end# Processing
@@ -0,0 +1,40 @@
1
+ module Processing
2
+
3
+
4
+ # Draws graphics into an offscreen buffer
5
+ #
6
+ class Graphics
7
+
8
+ include GraphicsContext
9
+
10
+ # Initialize graphics object.
11
+ #
12
+ def initialize(width, height)
13
+ image = Rays::Image.new width, height
14
+ init__ image, image.painter
15
+ end
16
+
17
+ # Start drawing.
18
+ #
19
+ def beginDraw(&block)
20
+ beginDraw__
21
+ @painter__.__send__ :begin_paint
22
+ push
23
+ if block
24
+ block.call
25
+ endDraw
26
+ end
27
+ end
28
+
29
+ # End drawing.
30
+ #
31
+ def endDraw()
32
+ pop
33
+ @painter__.__send__ :end_paint
34
+ endDraw__
35
+ end
36
+
37
+ end# Graphics
38
+
39
+
40
+ end# Processing