processing 0.5.29 → 0.5.31

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.
@@ -0,0 +1,183 @@
1
+ module Processing
2
+
3
+
4
+ # Shape object.
5
+ #
6
+ class Shape
7
+
8
+ # @private
9
+ def initialize(polygon = nil, children = nil, context: nil)
10
+ @polygon, @children = polygon, children
11
+ @context = context || Context.context__
12
+ @visible, @matrix = true, nil
13
+ @mode = @points = @closed = nil
14
+ end
15
+
16
+ # Gets width of shape.
17
+ #
18
+ # @return [Numeric] width of shape
19
+ #
20
+ def width()
21
+ polygon = getInternal__ or return 0
22
+ (@bounds ||= polygon.bounds).width
23
+ end
24
+
25
+ # Gets height of shape.
26
+ #
27
+ # @return [Numeric] height of shape
28
+ #
29
+ def height()
30
+ polygon = getInternal__ or return 0
31
+ (@bounds ||= polygon.bounds).height
32
+ end
33
+
34
+ alias w width
35
+ alias h height
36
+
37
+ # Returns whether the shape is visible or not.
38
+ #
39
+ # @return [Boolean] visible or not
40
+ #
41
+ def isVisible()
42
+ @visible
43
+ end
44
+
45
+ alias visible? isVisible
46
+
47
+ # Sets whether to display the shape or not.
48
+ #
49
+ # @return [nil] nil
50
+ #
51
+ def setVisible(visible)
52
+ @visible = !!visible
53
+ nil
54
+ end
55
+
56
+ def beginShape(mode = nil)
57
+ @mode = mode
58
+ @points ||= []
59
+ @polygon = nil# clear cache
60
+ nil
61
+ end
62
+
63
+ def endShape(close = nil)
64
+ raise "endShape() must be called after beginShape()" unless @points
65
+ @closed = close == GraphicsContext::CLOSE
66
+ nil
67
+ end
68
+
69
+ def vertex(x, y)
70
+ raise "vertex() must be called after beginShape()" unless @points
71
+ @points << x << y
72
+ end
73
+
74
+ def setVertex(index, point)
75
+ return nil unless @points && @points[index * 2, 2]&.size == 2
76
+ @points[index * 2, 2] = [point.x, point.y]
77
+ end
78
+
79
+ def getVertex(index)
80
+ return nil unless @points
81
+ point = @points[index * 2, 2]
82
+ return nil unless point&.size == 2
83
+ @context.createVector(*point)
84
+ end
85
+
86
+ def getVertexCount()
87
+ return 0 unless @points
88
+ @points.size / 2
89
+ end
90
+
91
+ def addChild(child)
92
+ return unless @children
93
+ @children.push child
94
+ nil
95
+ end
96
+
97
+ def getChild(index)
98
+ @children&.[](index)
99
+ end
100
+
101
+ def getChildCount()
102
+ @children&.size || 0
103
+ end
104
+
105
+ def translate(x, y, z = 0)
106
+ matrix__.translate x, y, z
107
+ nil
108
+ end
109
+
110
+ def rotate(angle)
111
+ matrix__.rotate @context.toDegrees__(angle)
112
+ nil
113
+ end
114
+
115
+ def scale(x, y, z = 1)
116
+ matrix__.scale x, y, z
117
+ nil
118
+ end
119
+
120
+ def resetMatrix()
121
+ @matrix = nil
122
+ end
123
+
124
+ def rotateX = nil
125
+ def rotateY = nil
126
+ def rotateZ = nil
127
+
128
+ # @private
129
+ def matrix__()
130
+ @matrix ||= Rays::Matrix.new
131
+ end
132
+
133
+ # @private
134
+ def getInternal__()
135
+ unless @polygon
136
+ return nil unless @points && @closed != nil
137
+ @polygon = self.class.createPolygon__ @mode, @points, @closed
138
+ end
139
+ @polygon
140
+ end
141
+
142
+ # @private
143
+ def draw__(painter, x, y, w = nil, h = nil)
144
+ poly = getInternal__
145
+
146
+ backup = nil
147
+ if @matrix && (poly || @children)
148
+ backup = painter.matrix
149
+ painter.matrix = backup * @matrix
150
+ end
151
+
152
+ if poly
153
+ if w || h
154
+ painter.polygon poly, x, y, w,h
155
+ else
156
+ painter.polygon poly, x, y
157
+ end
158
+ end
159
+ @children&.each {|o| o.draw__ painter, x, y, w, h}
160
+
161
+ painter.matrix = backup if backup
162
+ end
163
+
164
+ # @private
165
+ def self.createPolygon__(mode, points, close = false)
166
+ g = GraphicsContext
167
+ case mode
168
+ when g::POINTS then Rays::Polygon.points( *points)
169
+ when g::LINES then Rays::Polygon.lines( *points)
170
+ when g::TRIANGLES then Rays::Polygon.triangles( *points)
171
+ when g::TRIANGLE_FAN then Rays::Polygon.triangle_fan( *points)
172
+ when g::TRIANGLE_STRIP then Rays::Polygon.triangle_strip(*points)
173
+ when g::QUADS then Rays::Polygon.quads( *points)
174
+ when g::QUAD_STRIP then Rays::Polygon.quad_strip( *points)
175
+ when g::TESS, nil then Rays::Polygon.new(*points, loop: close)
176
+ else raise ArgumentError, "invalid polygon mode '#{mode}'"
177
+ end
178
+ end
179
+
180
+ end# Shape
181
+
182
+
183
+ end# Processing
@@ -463,9 +463,9 @@ module Processing
463
463
  # @return [Vector] rotated this object
464
464
  #
465
465
  def rotate(angle)
466
- angle = @context ?
467
- @context.toAngle__(angle) : angle * GraphicsContext::RAD2DEG__
468
- @point.rotate! angle
466
+ deg = @context ?
467
+ @context.toDegrees__(angle) : angle * GraphicsContext::RAD2DEG__
468
+ @point.rotate! deg
469
469
  self
470
470
  end
471
471
 
data/processing.gemspec CHANGED
@@ -25,14 +25,10 @@ Gem::Specification.new do |s|
25
25
  s.platform = Gem::Platform::RUBY
26
26
  s.required_ruby_version = '>= 3.0.0'
27
27
 
28
- s.add_runtime_dependency 'xot', '~> 0.1.39'
29
- s.add_runtime_dependency 'rucy', '~> 0.1.40'
30
- s.add_runtime_dependency 'rays', '~> 0.1.45'
31
- s.add_runtime_dependency 'reflexion', '~> 0.1.53'
32
-
33
- s.add_development_dependency 'rake'
34
- s.add_development_dependency 'test-unit'
35
- s.add_development_dependency 'yard'
28
+ s.add_runtime_dependency 'xot', '~> 0.1.41'
29
+ s.add_runtime_dependency 'rucy', '~> 0.1.42'
30
+ s.add_runtime_dependency 'rays', '~> 0.1.47'
31
+ s.add_runtime_dependency 'reflexion', '~> 0.1.55'
36
32
 
37
33
  s.files = `git ls-files`.split $/
38
34
  s.test_files = s.files.grep %r{^(test|spec|features)/}
data/test/helper.rb CHANGED
@@ -5,38 +5,133 @@
5
5
  require 'xot/test'
6
6
  require 'processing/all'
7
7
 
8
+ require 'digest/md5'
9
+ require 'fileutils'
8
10
  require 'tempfile'
9
11
  require 'test/unit'
10
12
 
11
13
  include Xot::Test
12
14
 
13
15
 
14
- def temppath(ext: nil, &block)
16
+ DEFAULT_DRAW_HEADER = <<~END
17
+ background 100
18
+ fill 255, 0, 0
19
+ stroke 0, 255, 0
20
+ strokeWeight 50
21
+ END
22
+
23
+
24
+ def test_with_p5?()
25
+ ENV['TEST_WITH_P5'] == '1'
26
+ end
27
+
28
+ def md5(s)
29
+ Digest::MD5.hexdigest s
30
+ end
31
+
32
+ def mkdir(dir: nil, filename: nil)
33
+ path = dir || File.dirname(filename)
34
+ FileUtils.mkdir_p path unless File.exist? path
35
+ end
36
+
37
+ def test_label(index = 1)
38
+ caller_locations[index].then {|loc| "#{loc.label}_#{loc.lineno}"}
39
+ end
40
+
41
+ def temp_path(ext: nil, &block)
15
42
  f = Tempfile.new
16
43
  path = f.path
17
- path += ".#{ext}" if ext
44
+ path += ext if ext
18
45
  f.close!
19
46
  block.call path
20
47
  File.delete path
21
48
  end
22
49
 
50
+ def draw_output_path(label, *sources, ext: '.png', dir: ext)
51
+ src = sources.compact.then {|ary| ary.empty? ? '' : "_#{md5 ary.join("\n")}"}
52
+ path = File.join __dir__, dir, label + src + ext
53
+ mkdir filename: path
54
+ path
55
+ end
56
+
23
57
  def get_pixels(image)
24
58
  %i[@image @image__]
25
59
  .map {image.instance_variable_get _1}
26
60
  .compact
27
61
  .first
28
62
  .bitmap
29
- .to_a
63
+ .pixels
30
64
  end
31
65
 
32
- def graphics(width = 10, height = 10, &block)
33
- Processing::Graphics.new(width, height).tap do |g|
66
+ def graphics(width = 10, height = 10, *args, &block)
67
+ Processing::Graphics.new(width, height, *args).tap do |g|
34
68
  g.beginDraw {block.call g, g.getInternal__} if block
35
69
  end
36
70
  end
37
71
 
72
+ def test_draw(*sources, width: 1000, height: 1000, pixelDensity: 1, label: nil)
73
+ graphics(width, height, pixelDensity).tap do |g|
74
+ g.beginDraw {g.instance_eval sources.compact.join("\n")}
75
+ g.save draw_output_path(label, *sources) if label
76
+ end
77
+ end
78
+
79
+
38
80
  def assert_equal_vector(v1, v2, delta = 0.000001)
39
81
  assert_in_delta v1.x, v2.x, delta
40
82
  assert_in_delta v1.y, v2.y, delta
41
83
  assert_in_delta v1.z, v2.z, delta
42
84
  end
85
+
86
+ def assert_equal_pixels(expected, actual, threshold: 1.0)
87
+ exp_pixels = get_pixels expected
88
+ act_pixels = get_pixels actual
89
+ raise "Number of pixels does not match" if act_pixels.size != exp_pixels.size
90
+
91
+ equal_count = exp_pixels.zip(act_pixels).count {|a, b| a == b}
92
+ equal_rate = equal_count.to_f / act_pixels.size.to_f
93
+ assert equal_rate >= threshold, <<~EOS
94
+ The rate of the same pixel #{equal_rate} is below the threshold #{threshold}
95
+ EOS
96
+ end
97
+
98
+ def assert_equal_draw(
99
+ *shared_header, expected, actual, default_header: DEFAULT_DRAW_HEADER,
100
+ width: 1000, height: 1000, threshold: 1.0, label: test_label)
101
+
102
+ e = test_draw default_header, *shared_header, expected, label: "#{label}_expected"
103
+ a = test_draw default_header, *shared_header, actual, label: "#{label}_actual"
104
+
105
+ assert_equal_pixels e, a, threshold: threshold
106
+ end
107
+
108
+ def assert_p5_draw(
109
+ *sources, default_header: DEFAULT_DRAW_HEADER,
110
+ width: 1000, height: 1000, threshold: 0.99, label: test_label)
111
+
112
+ return unless test_with_p5?
113
+
114
+ source = [default_header, *sources].compact.join("\n")
115
+ path = draw_output_path "#{label}_expected", source
116
+
117
+ pd = draw_p5rb width, height, source, path, headless: true
118
+ actual = test_draw source, width: width, height: height, pixelDensity: pd
119
+ actual.save path.sub('_expected', '_actual')
120
+
121
+ assert_equal_pixels actual.loadImage(path), actual, threshold: threshold
122
+ end
123
+
124
+ def assert_p5_fill(*sources, **kwargs)
125
+ assert_p5_draw 'noStroke', *sources, label: test_label, **kwargs
126
+ end
127
+
128
+ def assert_p5_stroke(*sources, **kwargs)
129
+ assert_p5_draw 'noFill; stroke 0, 255, 0', *sources, label: test_label, **kwargs
130
+ end
131
+
132
+ def assert_p5_fill_stroke(*sources, **kwargs)
133
+ assert_p5_draw 'stroke 0, 255, 0', *sources, label: test_label, **kwargs
134
+ end
135
+
136
+
137
+ require_relative 'p5' if test_with_p5?
data/test/p5.rb ADDED
@@ -0,0 +1,76 @@
1
+ require 'open-uri'
2
+ require 'ferrum'
3
+
4
+ RUBY_URL = 'https://cdn.jsdelivr.net/npm/ruby-3_2-wasm-wasi@next/dist/browser.script.iife.js'
5
+ P5JS_URL = 'https://cdn.jsdelivr.net/npm/p5@1.5.0/lib/p5.js'
6
+ P5RB_URL = 'https://raw.githubusercontent.com/ongaeshi/p5rb/master/docs/lib/p5.rb'
7
+
8
+ P5RB_SRC = URI.open(P5RB_URL) {|f| f.read}
9
+
10
+ def browser(width, height, headless: true)
11
+ hash = ($browsers ||= {})
12
+ key = [width, height, headless]
13
+ hash[key] ||= Ferrum::Browser.new headless: headless, window_size: [width, height]
14
+ end
15
+
16
+ def get_p5rb_html(width, height, draw_src)
17
+ <<~END
18
+ <html>
19
+ <head>
20
+ <script src="#{RUBY_URL}"></script>
21
+ <script src="#{P5JS_URL}"></script>
22
+ <script type="text/ruby">#{P5RB_SRC}</script>
23
+ <style type="text/css">
24
+ body {margin: 0;}
25
+ </style>
26
+ <script type="text/javascript">
27
+ function completed() {
28
+ id = 'completed'
29
+ if (document.querySelector("#" + id)) return;
30
+ let e = document.createElement("span");
31
+ e.id = id;
32
+ document.body.appendChild(e);
33
+ }
34
+ </script>
35
+ <script type="text/ruby">
36
+ def setup()
37
+ createCanvas #{width}, #{height}
38
+ end
39
+ def draw()
40
+ #{draw_src}
41
+ JS.global.completed
42
+ end
43
+ P5::init
44
+ </script>
45
+ </head>
46
+ <body><main></main></body>
47
+ </html>
48
+ END
49
+ end
50
+
51
+ def sleep_until (try: 3, timeout: 10, &block)
52
+ now = -> offset = 0 {Time.now.to_f + offset}
53
+ limit = now[timeout]
54
+ until block.call
55
+ if now[] > limit
56
+ limit = now[timeout]
57
+ try -= 1
58
+ next if try > 0
59
+ raise 'Drawing timed out in p5.rb'
60
+ end
61
+ sleep 0.1
62
+ end
63
+ end
64
+
65
+ def draw_p5rb(width, height, draw_src, path, headless: true)
66
+ b = browser width, height, headless: headless
67
+ unless File.exist? path
68
+ b.reset
69
+ b.main_frame.content = get_p5rb_html width, height, draw_src
70
+ sleep_until do
71
+ b.evaluate 'document.querySelector("#completed") != null'
72
+ end
73
+ b.screenshot path: path
74
+ end
75
+ b.device_pixel_ratio
76
+ end
@@ -3,12 +3,6 @@ require_relative 'helper'
3
3
 
4
4
  class TestGraphics < Test::Unit::TestCase
5
5
 
6
- P = Processing
7
-
8
- def graphics(w = 10, h = 10)
9
- P::Graphics.new w, h
10
- end
11
-
12
6
  def test_beginDraw()
13
7
  g = graphics
14
8
  g.beginDraw
@@ -24,9 +18,9 @@ class TestGraphics < Test::Unit::TestCase
24
18
  g.ellipseMode :corner
25
19
  g.ellipse 0, 0, g.width, g.height
26
20
  end
27
- temppath(ext: 'png') do |path|
21
+ temp_path(ext: '.png') do |path|
28
22
  assert_nothing_raised {g.save path}
29
- assert_equal get_pixels(g), get_pixels(g.loadImage path)
23
+ assert_equal_pixels g, g.loadImage(path)
30
24
  end
31
25
  end
32
26