processing 1.1.13 → 1.1.14

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 520edd002c7033054088f9ae142fe1b4ed325604a20ba382a6fc2619c4346234
4
- data.tar.gz: c82348b712cd16403d7554a06c56b484ad594435f1f471c8520bef9f11529dc1
3
+ metadata.gz: 7f18e4e393f628f012fd86b5e22cadc1376ed3e5e057cb246c0c905876cc91ae
4
+ data.tar.gz: f9b9b760ebbe8e10d5b7daa90f11f73a8f1b020de1a55583282bcbeededda2ef
5
5
  SHA512:
6
- metadata.gz: 4757f375457a2366d32cf127669c7e61d06ae15b0a326ed1210cf1165604a84851a83f47042be5d0dbe3f674213de54199b5bcfc1812ac825bf2ab6708aa8ddd
7
- data.tar.gz: b54ab5f3c6f58a65d32e8f21e13fbbe5b009f66f77a0c60806a8f5c2115b4b26a9d65a8242856e2f4be01f4509f6fe38e08787a34b27fa4fcf7001f458a60115
6
+ metadata.gz: bfc3729dc47ad1846dc1036e4dc8d5b5e69ef35e0bbe00f0f3b69e7c7337bde9ea3bfe0a9cc554295da4dc3dd438ff07dd25f5c95b506df7f757eff9836a964b
7
+ data.tar.gz: 306b81eb048502285e1ac6dce41cd4ad4bd9dab3eec645ec0485aeb220ec26ea2d673f99691765a3277e5d6266d48459883de8e8d2e0781a3ec525b38188d061
data/CLAUDE.md ADDED
@@ -0,0 +1,22 @@
1
+ # Processing
2
+
3
+ Processing/p5.js-compatible creative coding framework for Ruby.
4
+
5
+ ## Dual API
6
+
7
+ - Default: Processing-compatible camelCase (`colorMode`, `ellipseMode`)
8
+ - Optional: `Processing(snake_case: true)` enables snake_case aliases
9
+
10
+ ## Naming Convention
11
+
12
+ - Internal methods use `__` suffix (e.g., `init__`, `beginDraw__`, `@context__`)
13
+ - Methods matching `/__[!?]?$/` are excluded from the public API
14
+
15
+ ## Visual Regression Testing
16
+
17
+ Set `TEST_WITH_BROWSER=1` to enable browser-based drawing tests via Ferrum.
18
+ `assert_p5_draw` performs screenshot comparison.
19
+
20
+ ## GraphicsContext
21
+
22
+ Drawing logic is concentrated in the `GraphicsContext` module, mixed into both `Context` (top-level) and `Graphics` (offscreen buffer).
data/ChangeLog.md CHANGED
@@ -1,6 +1,19 @@
1
1
  # processing ChangeLog
2
2
 
3
3
 
4
+ ## [v1.1.14] - 2026-04-09
5
+
6
+ - Add Graphics#size
7
+ - Add Graphics#initialize_copy
8
+ - Add the 'smooth' param to createFont() and loadFont()
9
+ - loadFont() can take size parameter
10
+ - WheelEvent#delta returns dy and dx
11
+ - Image#resize does nothing when called with the same width and height
12
+ - Text is now vertically centered based on ascent rather than total height
13
+ - Handle key and midi events in focused canvas view
14
+ - Update dependencies
15
+
16
+
4
17
  ## [v1.1.13] - 2025-07-06
5
18
 
6
19
  - Add method docs for background(), fill(), stroke(), point(), line(), rect(), ellipse(), circle(), image(), translate(), scale(), and rotate()
data/Gemfile.lock CHANGED
@@ -12,7 +12,7 @@ GEM
12
12
  power_assert (2.0.3)
13
13
  public_suffix (5.0.3)
14
14
  rake (13.1.0)
15
- rexml (3.4.1)
15
+ rexml (3.4.4)
16
16
  test-unit (3.6.1)
17
17
  power_assert
18
18
  webrick (1.7.0)
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.1.13
1
+ 1.1.14
@@ -22,6 +22,8 @@ module Processing
22
22
  windowMoved windowResized motion
23
23
  ]
24
24
 
25
+ SUFFIX_PRIVATE = /__[!?]?$/
26
+
25
27
  # @private
26
28
  def self.setup__(namespace)
27
29
  w = (ENV['WIDTH'] || 500).to_i
@@ -36,7 +38,7 @@ module Processing
36
38
  # @private
37
39
  def self.funcs__(context)
38
40
  (context.methods - Object.instance_methods)
39
- .reject {_1 =~ /__$/} # methods for internal use
41
+ .reject {_1 =~ SUFFIX_PRIVATE} # methods for internal use
40
42
  end
41
43
 
42
44
  # @private
@@ -47,7 +49,7 @@ module Processing
47
49
  # @private
48
50
  def self.alias_snake_case_methods__(klass, recursive = 1)
49
51
  to_snake_case__(klass.instance_methods false)
50
- .reject {|camel, snake| camel =~ /__$/}
52
+ .reject {|camel, snake| camel =~ SUFFIX_PRIVATE}
51
53
  .reject {|camel, snake| klass.method_defined? snake}
52
54
  .each {|camel, snake| klass.alias_method snake, camel}
53
55
  if recursive > 0
@@ -11,10 +11,12 @@ module Processing
11
11
  end
12
12
 
13
13
  def delta()
14
- @event.dy
14
+ [@event.dx, @event.dy]
15
15
  end
16
16
 
17
- alias getCount delta
17
+ def getCount()
18
+ @event.dy
19
+ end
18
20
 
19
21
  end# WheelEvent
20
22
 
@@ -64,8 +64,8 @@ module Processing
64
64
  # @private
65
65
  def setSize__(size)
66
66
  return if size == @font.size
67
- @cachedSizes[@font.size] = @font
68
- @font = getInternal__ size
67
+ @cachedSizes[@font.size.to_f] = @font
68
+ @font = getInternal__ size
69
69
  end
70
70
 
71
71
  end# Font
@@ -21,6 +21,28 @@ module Processing
21
21
  init__ image, image.painter
22
22
  end
23
23
 
24
+ def initialize_copy(o)
25
+ super
26
+ raise 'cannot duplicate during drawing' if @drawing__
27
+ raise 'cannot duplicate because each stack is not empty' unless
28
+ @matrixStack__.empty? && @styleStack__.empty?
29
+
30
+ image = getInternal__.dup
31
+ updateCanvas__ image, image.painter
32
+ restoreStyles__ o.styles__
33
+ end
34
+
35
+ alias w width
36
+ alias h height
37
+
38
+ # Returns the width and height of graphics.
39
+ #
40
+ # @return [Array<Numeric>] [width, height]
41
+ #
42
+ def size()
43
+ [width, height]
44
+ end
45
+
24
46
  # Start drawing.
25
47
  #
26
48
  # @see https://processing.org/reference/PGraphics_beginDraw_.html
@@ -1786,21 +1786,23 @@ module Processing
1786
1786
  #
1787
1787
  def text(str, x, y, x2 = nil, y2 = nil)
1788
1788
  assertDrawing__
1789
+ font = @painter__.font
1789
1790
  if x2
1790
1791
  raise ArgumentError, "missing y2 parameter" unless y2
1791
1792
  x, y, w, h = toXYWH__ @rectMode__, x, y, x2, y2
1792
1793
  case @textAlignH__
1793
- when RIGHT then x += w - @painter__.font.width(str)
1794
- when CENTER then x += (w - @painter__.font.width(str)) / 2
1794
+ when RIGHT then x += w - font.width(str)
1795
+ when CENTER then x += (w - font.width(str)) / 2.0
1795
1796
  end
1796
1797
  case @textAlignV__
1797
- when BOTTOM then y += h - @painter__.font.height
1798
- when CENTER then y += (h - @painter__.font.height) / 2
1798
+ when BOTTOM then y += h - font.ascent
1799
+ when CENTER then y += (h - font.ascent) / 2.0
1799
1800
  else
1800
1801
  end
1801
1802
  else
1802
- y -= @painter__.font.ascent
1803
+ y -= font.ascent
1803
1804
  end
1805
+ x, y = x.round, y.round unless font.smooth
1804
1806
  @painter__.text str, x, y
1805
1807
  nil
1806
1808
  end
@@ -2397,7 +2399,28 @@ module Processing
2397
2399
  #
2398
2400
  def pushStyle(&block)
2399
2401
  assertDrawing__
2400
- @styleStack__.push [
2402
+ @styleStack__.push styles__
2403
+ block.call if block
2404
+ ensure
2405
+ popStyle if block
2406
+ end
2407
+
2408
+ # Restore style values from the style stack.
2409
+ #
2410
+ # @return [nil] nil
2411
+ #
2412
+ # @see https://processing.org/reference/popStyle_.html
2413
+ #
2414
+ def popStyle()
2415
+ assertDrawing__
2416
+ raise "style stack underflow" if @styleStack__.empty?
2417
+ restoreStyles__ @styleStack__.pop
2418
+ nil
2419
+ end
2420
+
2421
+ # @private
2422
+ def styles__()
2423
+ [
2401
2424
  @painter__.fill,
2402
2425
  @painter__.stroke,
2403
2426
  @painter__.stroke_width,
@@ -2433,20 +2456,10 @@ module Processing
2433
2456
  @textFont__,
2434
2457
  @tint__,
2435
2458
  ]
2436
- block.call if block
2437
- ensure
2438
- popStyle if block
2439
2459
  end
2440
2460
 
2441
- # Restore style values from the style stack.
2442
- #
2443
- # @return [nil] nil
2444
- #
2445
- # @see https://processing.org/reference/popStyle_.html
2446
- #
2447
- def popStyle()
2448
- assertDrawing__
2449
- raise "style stack underflow" if @styleStack__.empty?
2461
+ # @private
2462
+ def restoreStyles__(styles)
2450
2463
  @painter__.fill,
2451
2464
  @painter__.stroke,
2452
2465
  @painter__.stroke_width,
@@ -2480,9 +2493,8 @@ module Processing
2480
2493
  @textAlignH__,
2481
2494
  @textAlignV__,
2482
2495
  @textFont__,
2483
- @tint__ = @styleStack__.pop
2496
+ @tint__ = styles
2484
2497
  @textFont__.setSize__ @painter__.font.size
2485
- nil
2486
2498
  end
2487
2499
 
2488
2500
  # Save current styles and transformations to stack.
@@ -3186,16 +3198,18 @@ module Processing
3186
3198
 
3187
3199
  # Creates a new font object.
3188
3200
  #
3189
- # @param name [String] font name
3190
- # @param size [Numeric] font size (max 256)
3201
+ # @param name [String] font name
3202
+ # @param size [Numeric] font size (max 256)
3203
+ # @param smooth [Boolean] anti-aliased or pixel-perfect
3191
3204
  #
3192
3205
  # @return [Font] new font
3193
3206
  #
3194
3207
  # @see https://processing.org/reference/createFont_.html
3195
3208
  #
3196
- def createFont(name, size)
3197
- size = FONT_SIZE_MAX__ if size && size > FONT_SIZE_MAX__
3198
- Font.new Rays::Font.new(name, size || FONT_SIZE_DEFAULT__)
3209
+ def createFont(name, size, smooth: true)
3210
+ size ||= FONT_SIZE_DEFAULT__
3211
+ size = FONT_SIZE_MAX__ if size > FONT_SIZE_MAX__
3212
+ Font.new Rays::Font.new(name, size, smooth)
3199
3213
  end
3200
3214
 
3201
3215
  # Creates a new image object.
@@ -3348,19 +3362,23 @@ module Processing
3348
3362
 
3349
3363
  # Loads font from file.
3350
3364
  #
3351
- # @param filename [String] file name to load font file
3365
+ # @param filename [String] file name to load font file
3366
+ # @param smooth [Boolean] anti-aliased or pixel-perfect
3352
3367
  #
3353
3368
  # @return [Font] loaded font object
3354
3369
  #
3355
3370
  # @see https://processing.org/reference/loadFont_.html
3356
3371
  # @see https://p5js.org/reference/p5/loadFont/
3357
3372
  #
3358
- def loadFont(filename)
3373
+ def loadFont(filename, size: nil, smooth: true)
3359
3374
  ext = File.extname filename
3360
3375
  raise "unsupported font type -- '#{ext}'" unless ext =~ /^\.?(ttf|otf)$/i
3361
3376
 
3362
- filename = httpGet__ filename, ext if filename =~ %r|^https?://|
3363
- Font.new Rays::Font.load filename
3377
+ filename = httpGet__ filename, ext if filename =~ %r|^https?://|
3378
+ font = Rays::Font.load filename
3379
+ font.size = size if size
3380
+ font.smooth = smooth
3381
+ Font.new font
3364
3382
  end
3365
3383
 
3366
3384
  # Loads image.
@@ -140,6 +140,7 @@ module Processing
140
140
  # @see https://p5js.org/reference/p5.Image/resize/
141
141
  #
142
142
  def resize(width, height)
143
+ return nil if width == @image.width && height == @image.height
143
144
  @image = Rays::Image.new(width, height).paint do |painter|
144
145
  painter.image getInternal__, 0, 0, width, height
145
146
  end
@@ -325,7 +325,7 @@ module Processing
325
325
  # @return [Vector] negated vector
326
326
  #
327
327
  def -@()
328
- dup.mult -1
328
+ dup.mult(-1)
329
329
  end
330
330
 
331
331
  # Adds 2 vectors.
@@ -18,7 +18,9 @@ module Processing
18
18
  @overlay_view = @canvas_view.add Reflex::View.new name: :overlay
19
19
 
20
20
  super(*args, size: [width, height], **kwargs, &block)
21
+
21
22
  self.center = screen.center
23
+ @canvas_view.focus
22
24
  end
23
25
 
24
26
  attr_accessor :setup, :update, :draw, :move, :resize, :motion,
@@ -83,26 +85,6 @@ module Processing
83
85
  update_canvas_view
84
86
  end
85
87
 
86
- def on_key_down(e)
87
- draw_canvas {call_block @key_down, e} if @key_down
88
- end
89
-
90
- def on_key_up(e)
91
- draw_canvas {call_block @key_up, e} if @key_up
92
- end
93
-
94
- def on_note_on(e)
95
- draw_canvas {call_block @note_on, e} if @note_on
96
- end
97
-
98
- def on_note_off(e)
99
- draw_canvas {call_block @note_off, e} if @note_off
100
- end
101
-
102
- def on_control_change(e)
103
- draw_canvas {call_block @control_change, e} if @control_change
104
- end
105
-
106
88
  def on_move(e)
107
89
  draw_canvas {call_block @move, e} if @move
108
90
  end
@@ -126,6 +108,14 @@ module Processing
126
108
  draw_screen e.painter
127
109
  end
128
110
 
111
+ def on_canvas_key_down(e)
112
+ draw_canvas {call_block @key_down, e} if @key_down
113
+ end
114
+
115
+ def on_canvas_key_up(e)
116
+ draw_canvas {call_block @key_up, e} if @key_up
117
+ end
118
+
129
119
  def on_canvas_pointer(e)
130
120
  block = case e.action
131
121
  when :down then @pointer_down
@@ -139,6 +129,18 @@ module Processing
139
129
  draw_canvas {call_block @wheel, e} if @wheel
140
130
  end
141
131
 
132
+ def on_canvas_note_on(e)
133
+ draw_canvas {call_block @note_on, e} if @note_on
134
+ end
135
+
136
+ def on_canvas_note_off(e)
137
+ draw_canvas {call_block @note_off, e} if @note_off
138
+ end
139
+
140
+ def on_canvas_control_change(e)
141
+ draw_canvas {call_block @control_change, e} if @control_change
142
+ end
143
+
142
144
  def on_canvas_resize(e)
143
145
  resize_canvas e.width, e.height if @auto_resize
144
146
  draw_canvas {call_block @resize, e} if @resize
@@ -330,6 +332,14 @@ module Processing
330
332
  window.on_canvas_draw e
331
333
  end
332
334
 
335
+ def on_key_down(e)
336
+ window.on_canvas_key_down e
337
+ end
338
+
339
+ def on_key_up(e)
340
+ window.on_canvas_key_up e
341
+ end
342
+
333
343
  def on_pointer(e)
334
344
  window.on_canvas_pointer e
335
345
  end
@@ -338,6 +348,18 @@ module Processing
338
348
  window.on_canvas_wheel e
339
349
  end
340
350
 
351
+ def on_note_on(e)
352
+ window.on_canvas_note_on e
353
+ end
354
+
355
+ def on_note_off(e)
356
+ window.on_canvas_note_off e
357
+ end
358
+
359
+ def on_control_change(e)
360
+ window.on_canvas_control_change e
361
+ end
362
+
341
363
  def on_resize(e)
342
364
  window.on_canvas_resize e
343
365
  end
data/processing.gemspec CHANGED
@@ -26,10 +26,10 @@ Gem::Specification.new do |s|
26
26
  s.required_ruby_version = '>= 3.0.0'
27
27
 
28
28
  s.add_dependency 'rexml'
29
- s.add_dependency 'xot', '~> 0.3.9', '>= 0.3.9'
30
- s.add_dependency 'rucy', '~> 0.3.9', '>= 0.3.9'
31
- s.add_dependency 'rays', '~> 0.3.9', '>= 0.3.9'
32
- s.add_dependency 'reflexion', '~> 0.3.10', '>= 0.3.10'
29
+ s.add_dependency 'xot', '~> 0.3.10'
30
+ s.add_dependency 'rucy', '~> 0.3.10'
31
+ s.add_dependency 'rays', '~> 0.3.10'
32
+ s.add_dependency 'reflexion', '~> 0.3.11'
33
33
 
34
34
  s.files = `git ls-files`.split $/
35
35
  s.test_files = s.files.grep %r{^(test|spec|features)/}
@@ -3,6 +3,25 @@ require_relative 'helper'
3
3
 
4
4
  class TestGraphics < Test::Unit::TestCase
5
5
 
6
+ def test_dup()
7
+ g = graphics 1, 1
8
+ assert_equal g.color(0, 0, 0, 0), g.dup.loadPixels[0]
9
+
10
+ g.fill 255, 0, 0
11
+ g.noStroke
12
+ g.beginDraw {g.rect 0, 0, 1, 1}
13
+ assert_equal g.color(255, 0, 0, 255), g.dup.loadPixels[0]
14
+
15
+ g.fill 0, 255, 0
16
+ assert_equal(
17
+ g.color(0, 255, 0, 255),
18
+ g.dup.tap {|gg| gg.beginDraw {gg.rect 0, 0, 1, 1}}.loadPixels[0])
19
+ end
20
+
21
+ def test_size()
22
+ assert_equal [1, 2], graphics(1, 2).size
23
+ end
24
+
6
25
  def test_beginDraw()
7
26
  g = graphics
8
27
  g.beginDraw
@@ -10,8 +29,7 @@ class TestGraphics < Test::Unit::TestCase
10
29
  end
11
30
 
12
31
  def test_save()
13
- g = graphics 100, 100
14
- g.beginDraw do
32
+ g = graphics 100, 100 do |g|
15
33
  g.background 200
16
34
  g.fill 255
17
35
  g.stroke 0
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: processing
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.13
4
+ version: 1.1.14
5
5
  platform: ruby
6
6
  authors:
7
7
  - xordog
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-07-05 00:00:00.000000000 Z
11
+ date: 2026-04-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rexml
@@ -30,80 +30,56 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 0.3.9
34
- - - ">="
35
- - !ruby/object:Gem::Version
36
- version: 0.3.9
33
+ version: 0.3.10
37
34
  type: :runtime
38
35
  prerelease: false
39
36
  version_requirements: !ruby/object:Gem::Requirement
40
37
  requirements:
41
38
  - - "~>"
42
39
  - !ruby/object:Gem::Version
43
- version: 0.3.9
44
- - - ">="
45
- - !ruby/object:Gem::Version
46
- version: 0.3.9
40
+ version: 0.3.10
47
41
  - !ruby/object:Gem::Dependency
48
42
  name: rucy
49
43
  requirement: !ruby/object:Gem::Requirement
50
44
  requirements:
51
45
  - - "~>"
52
46
  - !ruby/object:Gem::Version
53
- version: 0.3.9
54
- - - ">="
55
- - !ruby/object:Gem::Version
56
- version: 0.3.9
47
+ version: 0.3.10
57
48
  type: :runtime
58
49
  prerelease: false
59
50
  version_requirements: !ruby/object:Gem::Requirement
60
51
  requirements:
61
52
  - - "~>"
62
53
  - !ruby/object:Gem::Version
63
- version: 0.3.9
64
- - - ">="
65
- - !ruby/object:Gem::Version
66
- version: 0.3.9
54
+ version: 0.3.10
67
55
  - !ruby/object:Gem::Dependency
68
56
  name: rays
69
57
  requirement: !ruby/object:Gem::Requirement
70
58
  requirements:
71
59
  - - "~>"
72
60
  - !ruby/object:Gem::Version
73
- version: 0.3.9
74
- - - ">="
75
- - !ruby/object:Gem::Version
76
- version: 0.3.9
61
+ version: 0.3.10
77
62
  type: :runtime
78
63
  prerelease: false
79
64
  version_requirements: !ruby/object:Gem::Requirement
80
65
  requirements:
81
66
  - - "~>"
82
67
  - !ruby/object:Gem::Version
83
- version: 0.3.9
84
- - - ">="
85
- - !ruby/object:Gem::Version
86
- version: 0.3.9
68
+ version: 0.3.10
87
69
  - !ruby/object:Gem::Dependency
88
70
  name: reflexion
89
71
  requirement: !ruby/object:Gem::Requirement
90
72
  requirements:
91
73
  - - "~>"
92
74
  - !ruby/object:Gem::Version
93
- version: 0.3.10
94
- - - ">="
95
- - !ruby/object:Gem::Version
96
- version: 0.3.10
75
+ version: 0.3.11
97
76
  type: :runtime
98
77
  prerelease: false
99
78
  version_requirements: !ruby/object:Gem::Requirement
100
79
  requirements:
101
80
  - - "~>"
102
81
  - !ruby/object:Gem::Version
103
- version: 0.3.10
104
- - - ">="
105
- - !ruby/object:Gem::Version
106
- version: 0.3.10
82
+ version: 0.3.11
107
83
  description: Creative Coding Framework has API compatible to Processing or p5.js.
108
84
  email: xordog@gmail.com
109
85
  executables: []
@@ -118,6 +94,7 @@ files:
118
94
  - ".github/workflows/utils.rb"
119
95
  - ".gitignore"
120
96
  - ".yardopts"
97
+ - CLAUDE.md
121
98
  - CONTRIBUTING.md
122
99
  - ChangeLog.md
123
100
  - Gemfile