processing 0.5.30 → 0.5.32
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 +4 -4
- data/.github/workflows/test-draw.yml +0 -1
- data/.github/workflows/test.yml +0 -1
- data/ChangeLog.md +26 -0
- data/Rakefile +14 -6
- data/VERSION +1 -1
- data/lib/processing/all.rb +1 -0
- data/lib/processing/font.rb +30 -3
- data/lib/processing/graphics.rb +0 -11
- data/lib/processing/graphics_context.rb +526 -53
- data/lib/processing/image.rb +38 -10
- data/lib/processing/shape.rb +294 -0
- data/lib/processing/vector.rb +3 -3
- data/processing.gemspec +4 -4
- data/test/helper.rb +86 -4
- data/test/{draw/p5.rb → p5.rb} +22 -8
- data/test/test_font.rb +33 -2
- data/test/test_graphics.rb +1 -7
- data/test/test_graphics_context.rb +671 -5
- data/test/test_image.rb +21 -0
- data/test/test_shape.rb +512 -0
- data/test/test_vector.rb +1 -1
- metadata +15 -16
- data/test/draw/helper.rb +0 -31
- data/test/draw/test_draw.rb +0 -22
data/lib/processing/image.rb
CHANGED
@@ -10,6 +10,7 @@ module Processing
|
|
10
10
|
# @private
|
11
11
|
def initialize(image)
|
12
12
|
@image = image
|
13
|
+
@pixels, @error = nil, false
|
13
14
|
end
|
14
15
|
|
15
16
|
# Gets width of image.
|
@@ -17,7 +18,7 @@ module Processing
|
|
17
18
|
# @return [Numeric] width of image
|
18
19
|
#
|
19
20
|
def width()
|
20
|
-
@image
|
21
|
+
@image&.width || (@error ? -1 : 0)
|
21
22
|
end
|
22
23
|
|
23
24
|
# Gets height of image.
|
@@ -25,7 +26,7 @@ module Processing
|
|
25
26
|
# @return [Numeric] height of image
|
26
27
|
#
|
27
28
|
def height()
|
28
|
-
@image
|
29
|
+
@image&.height || (@error ? -1 : 0)
|
29
30
|
end
|
30
31
|
|
31
32
|
alias w width
|
@@ -36,7 +37,7 @@ module Processing
|
|
36
37
|
# @return [Array<Numeric>] [width, height]
|
37
38
|
#
|
38
39
|
def size()
|
39
|
-
|
40
|
+
[width, height]
|
40
41
|
end
|
41
42
|
|
42
43
|
# Sets the color of the pixel.
|
@@ -48,21 +49,43 @@ module Processing
|
|
48
49
|
# @return [nil] nil
|
49
50
|
#
|
50
51
|
def set(x, y, c)
|
51
|
-
|
52
|
+
getInternal__.bitmap[x, y] = self.class.fromColor__ c
|
52
53
|
nil
|
53
54
|
end
|
54
55
|
|
55
|
-
|
56
56
|
# Returns the color of the pixel.
|
57
57
|
#
|
58
58
|
# @return [Integer] color value (0xAARRGGBB)
|
59
59
|
#
|
60
60
|
def get(x, y)
|
61
|
-
|
61
|
+
getInternal__.bitmap[x, y]
|
62
62
|
.map {|n| (n * 255).to_i.clamp 0, 255}
|
63
63
|
.then {|r, g, b, a| self.class.toColor__ r, g, b, a}
|
64
64
|
end
|
65
65
|
|
66
|
+
# Loads all pixels to the 'pixels' array.
|
67
|
+
#
|
68
|
+
# @return [nil] nil
|
69
|
+
#
|
70
|
+
def loadPixels()
|
71
|
+
@pixels = getInternal__.pixels
|
72
|
+
end
|
73
|
+
|
74
|
+
# Update the image pixels with the 'pixels' array.
|
75
|
+
#
|
76
|
+
# @return [nil] nil
|
77
|
+
#
|
78
|
+
def updatePixels()
|
79
|
+
return unless @pixels
|
80
|
+
getInternal__.pixels = @pixels
|
81
|
+
@pixels = nil
|
82
|
+
end
|
83
|
+
|
84
|
+
# An array of all pixels.
|
85
|
+
# Call loadPixels() before accessing the array.
|
86
|
+
#
|
87
|
+
attr_reader :pixels
|
88
|
+
|
66
89
|
# Applies an image filter.
|
67
90
|
#
|
68
91
|
# overload filter(shader)
|
@@ -86,7 +109,7 @@ module Processing
|
|
86
109
|
#
|
87
110
|
def resize(width, height)
|
88
111
|
@image = Rays::Image.new(width, height).paint do |painter|
|
89
|
-
painter.image
|
112
|
+
painter.image getInternal__, 0, 0, width, height
|
90
113
|
end
|
91
114
|
nil
|
92
115
|
end
|
@@ -132,7 +155,7 @@ module Processing
|
|
132
155
|
#
|
133
156
|
def blend(img = nil, sx, sy, sw, sh, dx, dy, dw, dh, mode)
|
134
157
|
img ||= self
|
135
|
-
|
158
|
+
getInternal__.paint do |painter|
|
136
159
|
img.drawImage__ painter, sx, sy, sw, sh, dx, dy, dw, dh, blend_mode: mode
|
137
160
|
end
|
138
161
|
nil
|
@@ -145,13 +168,18 @@ module Processing
|
|
145
168
|
# @return [nil] nil
|
146
169
|
#
|
147
170
|
def save(filename)
|
148
|
-
|
171
|
+
getInternal__.save filename
|
149
172
|
nil
|
150
173
|
end
|
151
174
|
|
152
175
|
# @private
|
153
176
|
def getInternal__()
|
154
|
-
@image
|
177
|
+
@image or raise 'Invalid image object'
|
178
|
+
end
|
179
|
+
|
180
|
+
# @private
|
181
|
+
def setInternal__(image, error = false)
|
182
|
+
@image, @error = image, error
|
155
183
|
end
|
156
184
|
|
157
185
|
# @private
|
@@ -0,0 +1,294 @@
|
|
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, @fill, @matrix = true, nil, nil
|
13
|
+
@type = @points = @curvePoints = @colors = @texcoords = @close = nil
|
14
|
+
@contours = @contourPoints = @contourColors = @contourTexCoords = nil
|
15
|
+
end
|
16
|
+
|
17
|
+
# Gets width of shape.
|
18
|
+
#
|
19
|
+
# @return [Numeric] width of shape
|
20
|
+
#
|
21
|
+
def width()
|
22
|
+
polygon = getInternal__ or return 0
|
23
|
+
(@bounds ||= polygon.bounds).width
|
24
|
+
end
|
25
|
+
|
26
|
+
# Gets height of shape.
|
27
|
+
#
|
28
|
+
# @return [Numeric] height of shape
|
29
|
+
#
|
30
|
+
def height()
|
31
|
+
polygon = getInternal__ or return 0
|
32
|
+
(@bounds ||= polygon.bounds).height
|
33
|
+
end
|
34
|
+
|
35
|
+
alias w width
|
36
|
+
alias h height
|
37
|
+
|
38
|
+
# Returns whether the shape is visible or not.
|
39
|
+
#
|
40
|
+
# @return [Boolean] visible or not
|
41
|
+
#
|
42
|
+
def isVisible()
|
43
|
+
@visible
|
44
|
+
end
|
45
|
+
|
46
|
+
alias visible? isVisible
|
47
|
+
|
48
|
+
# Sets whether to display the shape or not.
|
49
|
+
#
|
50
|
+
# @return [nil] nil
|
51
|
+
#
|
52
|
+
def setVisible(visible)
|
53
|
+
@visible = !!visible
|
54
|
+
nil
|
55
|
+
end
|
56
|
+
|
57
|
+
def beginShape(type = nil)
|
58
|
+
raise "beginShape() cannot be called twice" if drawingShape__
|
59
|
+
@type = type
|
60
|
+
@points ||= []
|
61
|
+
@curvePoints = []
|
62
|
+
@colors ||= []
|
63
|
+
@texcoords ||= []
|
64
|
+
@close = nil
|
65
|
+
@contours ||= []
|
66
|
+
clearCache__
|
67
|
+
nil
|
68
|
+
end
|
69
|
+
|
70
|
+
def endShape(close = nil)
|
71
|
+
raise "endShape() must be called after beginShape()" unless drawingShape__
|
72
|
+
@close = close == GraphicsContext::CLOSE || @contours.size > 0
|
73
|
+
if @close && @curvePoints.size >= 8
|
74
|
+
x, y = @curvePoints[0, 2]
|
75
|
+
2.times {curveVertex x, y}
|
76
|
+
end
|
77
|
+
@curvePoints = nil
|
78
|
+
nil
|
79
|
+
end
|
80
|
+
|
81
|
+
def beginContour()
|
82
|
+
raise "beginContour() must be called after beginShape()" unless drawingShape__
|
83
|
+
@contourPoints, @contourColors, @contourTexCoords = [], [], []
|
84
|
+
nil
|
85
|
+
end
|
86
|
+
|
87
|
+
def endContour()
|
88
|
+
raise "endContour() must be called after beginContour()" unless drawingContour__
|
89
|
+
@contours << Rays::Polyline.new(
|
90
|
+
*@contourPoints, colors: @contourColors, texcoords: @contourTexCoords,
|
91
|
+
loop: true, hole: true)
|
92
|
+
@contoursPoints = @contoursColors = @contoursTexCoords = nil
|
93
|
+
nil
|
94
|
+
end
|
95
|
+
|
96
|
+
def vertex(x, y, u = nil, v = nil)
|
97
|
+
raise "vertex() must be called after beginShape()" unless drawingShape__
|
98
|
+
raise "Either 'u' or 'v' is missing" if (u == nil) != (v == nil)
|
99
|
+
u ||= x
|
100
|
+
v ||= y
|
101
|
+
color = @fill || @context.getFill__
|
102
|
+
if drawingContour__
|
103
|
+
@contourPoints << x << y
|
104
|
+
@contourColors << color
|
105
|
+
@contourTexCoords << u << v
|
106
|
+
else
|
107
|
+
@points << x << y
|
108
|
+
@colors << color
|
109
|
+
@texcoords << u << v
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def curveVertex(x, y)
|
114
|
+
raise "curveVertex() must be called after beginShape()" unless drawingShape__
|
115
|
+
@curvePoints << x << y
|
116
|
+
if @curvePoints.size >= 8
|
117
|
+
Rays::Polygon.curve(*@curvePoints[-8, 8])
|
118
|
+
.first.to_a.tap {|a| a.shift if @curvePoints.size > 8}
|
119
|
+
.each {|p| vertex p.x, p.y}
|
120
|
+
end
|
121
|
+
nil
|
122
|
+
end
|
123
|
+
|
124
|
+
def bezierVertex(x2, y2, x3, y3, x4, y4)
|
125
|
+
raise "bezierVertex() must be called after beginShape()" unless drawingShape__
|
126
|
+
x1, y1 = @points[-2, 2]
|
127
|
+
raise "vertex() is required before calling bezierVertex()" unless x1 && y1
|
128
|
+
Rays::Polygon.bezier(x1, y1, x2, y2, x3, y3, x4, y4)
|
129
|
+
.first.to_a.tap {|a| a.shift}
|
130
|
+
.each {|p| vertex p.x, p.y}
|
131
|
+
nil
|
132
|
+
end
|
133
|
+
|
134
|
+
def quadraticVertex(cx, cy, x3, y3)
|
135
|
+
x1, y1 = @points[-2, 2]
|
136
|
+
raise "vertex() is required before calling quadraticVertex()" unless x1 && y1
|
137
|
+
bezierVertex(
|
138
|
+
x1 + (cx - x1) * 2.0 / 3.0, y1 + (cy - y1) * 2.0 / 3.0,
|
139
|
+
x3 + (cx - x3) * 2.0 / 3.0, y3 + (cy - y3) * 2.0 / 3.0,
|
140
|
+
x3, y3)
|
141
|
+
nil
|
142
|
+
end
|
143
|
+
|
144
|
+
# @private
|
145
|
+
def drawingShape__()
|
146
|
+
@curvePoints
|
147
|
+
end
|
148
|
+
|
149
|
+
# @private
|
150
|
+
def drawingContour__()
|
151
|
+
@contourPoints
|
152
|
+
end
|
153
|
+
|
154
|
+
def fill(*args)
|
155
|
+
@fill = @context.toRaysColor__(*args)
|
156
|
+
end
|
157
|
+
|
158
|
+
def setVertex(index, point)
|
159
|
+
return nil unless @points && @points[index * 2, 2]&.size == 2
|
160
|
+
@points[index * 2, 2] = [point.x, point.y]
|
161
|
+
end
|
162
|
+
|
163
|
+
def getVertex(index)
|
164
|
+
return nil unless @points
|
165
|
+
point = @points[index * 2, 2]
|
166
|
+
return nil unless point&.size == 2
|
167
|
+
@context.createVector(*point)
|
168
|
+
end
|
169
|
+
|
170
|
+
def getVertexCount()
|
171
|
+
return 0 unless @points
|
172
|
+
@points.size / 2
|
173
|
+
end
|
174
|
+
|
175
|
+
def setFill(*args)
|
176
|
+
color = @context.toRaysColor__(*args)
|
177
|
+
count = getVertexCount
|
178
|
+
if count > 0
|
179
|
+
if @colors
|
180
|
+
@colors.fill color
|
181
|
+
else
|
182
|
+
@colors = [color] * count
|
183
|
+
end
|
184
|
+
clearCache__
|
185
|
+
elsif @polygon
|
186
|
+
@polygon = @polygon.transform do |polylines|
|
187
|
+
polylines.map {|pl| pl.with colors: pl.points.map {color}}
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
def addChild(child)
|
193
|
+
return unless @children
|
194
|
+
@children.push child
|
195
|
+
nil
|
196
|
+
end
|
197
|
+
|
198
|
+
def getChild(index)
|
199
|
+
@children&.[](index)
|
200
|
+
end
|
201
|
+
|
202
|
+
def getChildCount()
|
203
|
+
@children&.size || 0
|
204
|
+
end
|
205
|
+
|
206
|
+
def translate(x, y, z = 0)
|
207
|
+
matrix__.translate x, y, z
|
208
|
+
nil
|
209
|
+
end
|
210
|
+
|
211
|
+
def rotate(angle)
|
212
|
+
matrix__.rotate @context.toDegrees__(angle)
|
213
|
+
nil
|
214
|
+
end
|
215
|
+
|
216
|
+
def scale(x, y, z = 1)
|
217
|
+
matrix__.scale x, y, z
|
218
|
+
nil
|
219
|
+
end
|
220
|
+
|
221
|
+
def resetMatrix()
|
222
|
+
@matrix = nil
|
223
|
+
end
|
224
|
+
|
225
|
+
def rotateX = nil
|
226
|
+
def rotateY = nil
|
227
|
+
def rotateZ = nil
|
228
|
+
|
229
|
+
# @private
|
230
|
+
def clearCache__()
|
231
|
+
@polygon = nil# clear cache
|
232
|
+
end
|
233
|
+
|
234
|
+
# @private
|
235
|
+
def matrix__()
|
236
|
+
@matrix ||= Rays::Matrix.new
|
237
|
+
end
|
238
|
+
|
239
|
+
# @private
|
240
|
+
def getInternal__()
|
241
|
+
unless @polygon
|
242
|
+
return nil unless @points && @close != nil
|
243
|
+
@polygon = self.class.createPolygon__(
|
244
|
+
@type, @points, @close, @colors, @texcoords)
|
245
|
+
@polygon += @contours if @polygon
|
246
|
+
end
|
247
|
+
@polygon
|
248
|
+
end
|
249
|
+
|
250
|
+
# @private
|
251
|
+
def draw__(painter, x, y, w = nil, h = nil)
|
252
|
+
poly = getInternal__
|
253
|
+
|
254
|
+
backup = nil
|
255
|
+
if @matrix && (poly || @children)
|
256
|
+
backup = painter.matrix
|
257
|
+
painter.matrix = backup * @matrix
|
258
|
+
end
|
259
|
+
|
260
|
+
if poly
|
261
|
+
if w || h
|
262
|
+
painter.polygon poly, x, y, w,h
|
263
|
+
else
|
264
|
+
painter.polygon poly, x, y
|
265
|
+
end
|
266
|
+
end
|
267
|
+
@children&.each {|o| o.draw__ painter, x, y, w, h}
|
268
|
+
|
269
|
+
painter.matrix = backup if backup
|
270
|
+
end
|
271
|
+
|
272
|
+
# @private
|
273
|
+
def self.createPolygon__(
|
274
|
+
type, points, close = false, colors = nil, texcoords = nil)
|
275
|
+
|
276
|
+
kwargs = {colors: colors, texcoords: texcoords}
|
277
|
+
g, p = GraphicsContext, Rays::Polygon
|
278
|
+
case type
|
279
|
+
when g::POINTS then p.points( *points)
|
280
|
+
when g::LINES then p.lines( *points)
|
281
|
+
when g::TRIANGLES then p.triangles( *points, **kwargs)
|
282
|
+
when g::TRIANGLE_FAN then p.triangle_fan( *points, **kwargs)
|
283
|
+
when g::TRIANGLE_STRIP then p.triangle_strip(*points, **kwargs)
|
284
|
+
when g::QUADS then p.quads( *points, **kwargs)
|
285
|
+
when g::QUAD_STRIP then p.quad_strip( *points, **kwargs)
|
286
|
+
when g::TESS, nil then p.new( *points, **kwargs, loop: close)
|
287
|
+
else raise ArgumentError, "invalid polygon type '#{type}'"
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
end# Shape
|
292
|
+
|
293
|
+
|
294
|
+
end# Processing
|
data/lib/processing/vector.rb
CHANGED
@@ -463,9 +463,9 @@ module Processing
|
|
463
463
|
# @return [Vector] rotated this object
|
464
464
|
#
|
465
465
|
def rotate(angle)
|
466
|
-
|
467
|
-
@context.
|
468
|
-
@point.rotate!
|
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,10 +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.
|
29
|
-
s.add_runtime_dependency 'rucy', '~> 0.1.
|
30
|
-
s.add_runtime_dependency 'rays', '~> 0.1.
|
31
|
-
s.add_runtime_dependency 'reflexion', '~> 0.1.
|
28
|
+
s.add_runtime_dependency 'xot', '~> 0.1.41'
|
29
|
+
s.add_runtime_dependency 'rucy', '~> 0.1.43'
|
30
|
+
s.add_runtime_dependency 'rays', '~> 0.1.48'
|
31
|
+
s.add_runtime_dependency 'reflexion', '~> 0.1.56'
|
32
32
|
|
33
33
|
s.files = `git ls-files`.split $/
|
34
34
|
s.test_files = s.files.grep %r{^(test|spec|features)/}
|
data/test/helper.rb
CHANGED
@@ -5,28 +5,61 @@
|
|
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
|
-
|
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 +=
|
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
|
-
.
|
29
|
-
.to_a
|
62
|
+
.pixels
|
30
63
|
end
|
31
64
|
|
32
65
|
def graphics(width = 10, height = 10, *args, &block)
|
@@ -35,6 +68,14 @@ def graphics(width = 10, height = 10, *args, &block)
|
|
35
68
|
end
|
36
69
|
end
|
37
70
|
|
71
|
+
def test_draw(*sources, width: 1000, height: 1000, pixelDensity: 1, label: nil)
|
72
|
+
graphics(width, height, pixelDensity).tap do |g|
|
73
|
+
g.beginDraw {g.instance_eval sources.compact.join("\n")}
|
74
|
+
g.save draw_output_path(label, *sources) if label
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
|
38
79
|
def assert_equal_vector(v1, v2, delta = 0.000001)
|
39
80
|
assert_in_delta v1.x, v2.x, delta
|
40
81
|
assert_in_delta v1.y, v2.y, delta
|
@@ -52,3 +93,44 @@ def assert_equal_pixels(expected, actual, threshold: 1.0)
|
|
52
93
|
The rate of the same pixel #{equal_rate} is below the threshold #{threshold}
|
53
94
|
EOS
|
54
95
|
end
|
96
|
+
|
97
|
+
def assert_equal_draw(
|
98
|
+
*shared_header, expected, actual, default_header: DEFAULT_DRAW_HEADER,
|
99
|
+
width: 1000, height: 1000, threshold: 1.0, label: test_label)
|
100
|
+
|
101
|
+
e = test_draw default_header, *shared_header, expected, label: "#{label}_expected"
|
102
|
+
a = test_draw default_header, *shared_header, actual, label: "#{label}_actual"
|
103
|
+
|
104
|
+
assert_equal_pixels e, a, threshold: threshold
|
105
|
+
end
|
106
|
+
|
107
|
+
def assert_p5_draw(
|
108
|
+
*sources, default_header: DEFAULT_DRAW_HEADER,
|
109
|
+
width: 1000, height: 1000, threshold: 0.99, label: test_label, **kwargs)
|
110
|
+
|
111
|
+
return unless test_with_p5?
|
112
|
+
|
113
|
+
source = [default_header, *sources].compact.join("\n")
|
114
|
+
path = draw_output_path "#{label}_expected", source
|
115
|
+
|
116
|
+
pd = draw_p5rb width, height, source, path, **kwargs
|
117
|
+
actual = test_draw source, width: width, height: height, pixelDensity: pd
|
118
|
+
actual.save path.sub('_expected', '_actual')
|
119
|
+
|
120
|
+
assert_equal_pixels actual.loadImage(path), actual, threshold: threshold
|
121
|
+
end
|
122
|
+
|
123
|
+
def assert_p5_fill(*sources, **kwargs)
|
124
|
+
assert_p5_draw 'noStroke', *sources, label: test_label, **kwargs
|
125
|
+
end
|
126
|
+
|
127
|
+
def assert_p5_stroke(*sources, **kwargs)
|
128
|
+
assert_p5_draw 'noFill; stroke 0, 255, 0', *sources, label: test_label, **kwargs
|
129
|
+
end
|
130
|
+
|
131
|
+
def assert_p5_fill_stroke(*sources, **kwargs)
|
132
|
+
assert_p5_draw 'stroke 0, 255, 0', *sources, label: test_label, **kwargs
|
133
|
+
end
|
134
|
+
|
135
|
+
|
136
|
+
require_relative 'p5' if test_with_p5?
|
data/test/{draw/p5.rb → p5.rb}
RENAMED
@@ -13,7 +13,7 @@ def browser(width, height, headless: true)
|
|
13
13
|
hash[key] ||= Ferrum::Browser.new headless: headless, window_size: [width, height]
|
14
14
|
end
|
15
15
|
|
16
|
-
def get_p5rb_html(width, height, draw_src)
|
16
|
+
def get_p5rb_html(width, height, draw_src, webgl: false)
|
17
17
|
<<~END
|
18
18
|
<html>
|
19
19
|
<head>
|
@@ -34,12 +34,10 @@ def get_p5rb_html(width, height, draw_src)
|
|
34
34
|
</script>
|
35
35
|
<script type="text/ruby">
|
36
36
|
def setup()
|
37
|
-
createCanvas #{width}, #{height}
|
37
|
+
createCanvas #{width}, #{height}#{webgl ? ', WEBGL' : ''}
|
38
38
|
end
|
39
39
|
def draw()
|
40
|
-
|
41
|
-
fill 255, 0, 0
|
42
|
-
noStroke
|
40
|
+
#{webgl ? 'translate -width / 2, -height / 2' : ''}
|
43
41
|
#{draw_src}
|
44
42
|
JS.global.completed
|
45
43
|
end
|
@@ -51,12 +49,28 @@ def get_p5rb_html(width, height, draw_src)
|
|
51
49
|
END
|
52
50
|
end
|
53
51
|
|
54
|
-
def
|
52
|
+
def sleep_until (try: 3, timeout: 10, &block)
|
53
|
+
now = -> offset = 0 {Time.now.to_f + offset}
|
54
|
+
limit = now[timeout]
|
55
|
+
until block.call
|
56
|
+
if now[] > limit
|
57
|
+
limit = now[timeout]
|
58
|
+
try -= 1
|
59
|
+
next if try > 0
|
60
|
+
raise 'Drawing timed out in p5.rb'
|
61
|
+
end
|
62
|
+
sleep 0.1
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def draw_p5rb(width, height, draw_src, path, headless: true, webgl: false)
|
55
67
|
b = browser width, height, headless: headless
|
56
68
|
unless File.exist? path
|
57
69
|
b.reset
|
58
|
-
b.main_frame.content = get_p5rb_html width, height, draw_src
|
59
|
-
|
70
|
+
b.main_frame.content = get_p5rb_html width, height, draw_src, webgl: webgl
|
71
|
+
sleep_until do
|
72
|
+
b.evaluate 'document.querySelector("#completed") != null'
|
73
|
+
end
|
60
74
|
b.screenshot path: path
|
61
75
|
end
|
62
76
|
b.device_pixel_ratio
|
data/test/test_font.rb
CHANGED
@@ -5,12 +5,43 @@ class TestFont < Test::Unit::TestCase
|
|
5
5
|
|
6
6
|
P = Processing
|
7
7
|
|
8
|
-
def font()
|
9
|
-
P::Font.new
|
8
|
+
def font(*args)
|
9
|
+
P::Font.new(Rays::Font.new *args).tap {|font|
|
10
|
+
def font.intern()
|
11
|
+
getInternal__
|
12
|
+
end
|
13
|
+
}
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_initialize()
|
17
|
+
assert_not_nil font(nil) .intern.name
|
18
|
+
assert_equal 'Arial', font('Arial').intern.name
|
19
|
+
|
20
|
+
assert_equal 12, font .intern.size
|
21
|
+
assert_equal 10, font(nil, 10).intern.size
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_size()
|
25
|
+
f = font nil, 10
|
26
|
+
id = f.intern.object_id
|
27
|
+
|
28
|
+
assert_equal 10, f.intern.size
|
29
|
+
|
30
|
+
f.setSize__ 11
|
31
|
+
assert_equal 11, f.intern.size
|
32
|
+
assert_not_equal id, f.intern.object_id
|
33
|
+
|
34
|
+
f.setSize__ 10
|
35
|
+
assert_equal 10, f.intern.size
|
36
|
+
assert_equal id, f.intern.object_id
|
10
37
|
end
|
11
38
|
|
12
39
|
def test_inspect()
|
13
40
|
assert_match %r|#<Processing::Font: name:'[\w\s]+' size:[\d\.]+>|, font.inspect
|
14
41
|
end
|
15
42
|
|
43
|
+
def test_list()
|
44
|
+
assert_not P::Font.list.empty?
|
45
|
+
end
|
46
|
+
|
16
47
|
end# TestFont
|
data/test/test_graphics.rb
CHANGED
@@ -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,7 +18,7 @@ class TestGraphics < Test::Unit::TestCase
|
|
24
18
|
g.ellipseMode :corner
|
25
19
|
g.ellipse 0, 0, g.width, g.height
|
26
20
|
end
|
27
|
-
|
21
|
+
temp_path(ext: '.png') do |path|
|
28
22
|
assert_nothing_raised {g.save path}
|
29
23
|
assert_equal_pixels g, g.loadImage(path)
|
30
24
|
end
|