processing 0.5.29 → 0.5.31

Sign up to get free protection for your applications and to get access to all the features.
@@ -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