processing 0.5.31 → 0.5.33

Sign up to get free protection for your applications and to get access to all the features.
@@ -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.width
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.height
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
- @image.size
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
- @image.bitmap[x, y] = self.class.fromColor__ c
52
+ getInternal__.bitmap(true)[x, y] = self.class.fromColor__(c).map {|n| n / 255.0}
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
- @image.bitmap[x, y]
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 @image, 0, 0, width, height
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
- @image.paint do |painter|
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
- @image.save filename
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
@@ -7,10 +7,11 @@ module Processing
7
7
 
8
8
  # @private
9
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
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
14
15
  end
15
16
 
16
17
  # Gets width of shape.
@@ -53,22 +54,105 @@ module Processing
53
54
  nil
54
55
  end
55
56
 
56
- def beginShape(mode = nil)
57
- @mode = mode
58
- @points ||= []
59
- @polygon = nil# clear cache
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__
60
67
  nil
61
68
  end
62
69
 
63
70
  def endShape(close = nil)
64
- raise "endShape() must be called after beginShape()" unless @points
65
- @closed = close == GraphicsContext::CLOSE
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)
66
141
  nil
67
142
  end
68
143
 
69
- def vertex(x, y)
70
- raise "vertex() must be called after beginShape()" unless @points
71
- @points << x << y
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.rawColor__(*args)
72
156
  end
73
157
 
74
158
  def setVertex(index, point)
@@ -88,6 +172,23 @@ module Processing
88
172
  @points.size / 2
89
173
  end
90
174
 
175
+ def setFill(*args)
176
+ color = @context.rawColor__(*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
+
91
192
  def addChild(child)
92
193
  return unless @children
93
194
  @children.push child
@@ -103,17 +204,17 @@ module Processing
103
204
  end
104
205
 
105
206
  def translate(x, y, z = 0)
106
- matrix__.translate x, y, z
207
+ matrix__.translate! x, y, z
107
208
  nil
108
209
  end
109
210
 
110
211
  def rotate(angle)
111
- matrix__.rotate @context.toDegrees__(angle)
212
+ matrix__.rotate! @context.toDegrees__(angle)
112
213
  nil
113
214
  end
114
215
 
115
216
  def scale(x, y, z = 1)
116
- matrix__.scale x, y, z
217
+ matrix__.scale! x, y, z
117
218
  nil
118
219
  end
119
220
 
@@ -121,9 +222,22 @@ module Processing
121
222
  @matrix = nil
122
223
  end
123
224
 
124
- def rotateX = nil
125
- def rotateY = nil
126
- def rotateZ = nil
225
+ def rotateX(angle)
226
+ matrix__.rotate! @context.toDegrees__(angle), 1, 0, 0
227
+ end
228
+
229
+ def rotateY(angle)
230
+ matrix__.rotate! @context.toDegrees__(angle), 0, 1, 0
231
+ end
232
+
233
+ def rotateZ(angle)
234
+ matrix__.rotate! @context.toDegrees__(angle), 0, 0, 1
235
+ end
236
+
237
+ # @private
238
+ def clearCache__()
239
+ @polygon = nil# clear cache
240
+ end
127
241
 
128
242
  # @private
129
243
  def matrix__()
@@ -133,8 +247,10 @@ module Processing
133
247
  # @private
134
248
  def getInternal__()
135
249
  unless @polygon
136
- return nil unless @points && @closed != nil
137
- @polygon = self.class.createPolygon__ @mode, @points, @closed
250
+ return nil unless @points && @close != nil
251
+ @polygon = self.class.createPolygon__(
252
+ @type, @points, @close, @colors, @texcoords)
253
+ @polygon += @contours if @polygon
138
254
  end
139
255
  @polygon
140
256
  end
@@ -162,18 +278,21 @@ module Processing
162
278
  end
163
279
 
164
280
  # @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}'"
281
+ def self.createPolygon__(
282
+ type, points, close = false, colors = nil, texcoords = nil)
283
+
284
+ kwargs = {colors: colors, texcoords: texcoords}
285
+ g, p = GraphicsContext, Rays::Polygon
286
+ case type
287
+ when g::POINTS then p.points( *points)
288
+ when g::LINES then p.lines( *points)
289
+ when g::TRIANGLES then p.triangles( *points, **kwargs)
290
+ when g::TRIANGLE_FAN then p.triangle_fan( *points, **kwargs)
291
+ when g::TRIANGLE_STRIP then p.triangle_strip(*points, **kwargs)
292
+ when g::QUADS then p.quads( *points, **kwargs)
293
+ when g::QUAD_STRIP then p.quad_strip( *points, **kwargs)
294
+ when g::TESS, nil then p.new( *points, **kwargs, loop: close)
295
+ else raise ArgumentError, "invalid polygon type '#{type}'"
177
296
  end
178
297
  end
179
298
 
@@ -6,14 +6,6 @@ module Processing
6
6
 
7
7
  include Xot::Inspectable
8
8
 
9
- attr_accessor :setup, :update, :draw,
10
- :key_down, :key_up,
11
- :pointer_down, :pointer_up, :pointer_move,
12
- :move, :resize, :motion,
13
- :before_draw, :after_draw, :update_window, :update_canvas
14
-
15
- attr_accessor :auto_resize
16
-
17
9
  def initialize(width = 500, height = 500, *args, **kwargs, &block)
18
10
  Processing.instance_variable_set :@window, self
19
11
 
@@ -26,19 +18,16 @@ module Processing
26
18
  @overlay_view = @canvas_view.add Reflex::View.new name: :overlay
27
19
 
28
20
  super(*args, size: [width, height], **kwargs, &block)
21
+ self.center = screen.center
29
22
  end
30
23
 
31
- def canvas_image()
32
- @canvas.image
33
- end
24
+ attr_accessor :setup, :update, :draw, :move, :resize, :motion,
25
+ :key_down, :key_up, :pointer_down, :pointer_up, :pointer_move, :wheel,
26
+ :before_draw, :after_draw, :update_window, :update_canvas
34
27
 
35
- def canvas_painter()
36
- @canvas.painter
37
- end
28
+ attr_accessor :auto_resize
38
29
 
39
- def window_painter()
40
- self.painter
41
- end
30
+ attr_reader :canvas
42
31
 
43
32
  def event()
44
33
  @events.last
@@ -64,7 +53,9 @@ module Processing
64
53
  end
65
54
 
66
55
  def on_change_pixel_density(pixel_density)
67
- resize_canvas width, height, window_pixel_density: pixel_density
56
+ resize_canvas(
57
+ @canvas.width, @canvas.height,
58
+ window_pixel_density: pixel_density)
68
59
  end
69
60
 
70
61
  def on_activate(e)
@@ -80,7 +71,7 @@ module Processing
80
71
  end
81
72
 
82
73
  def on_draw(e)
83
- window_painter.pixel_density.tap do |pd|
74
+ painter.pixel_density.tap do |pd|
84
75
  prev, @prev_pixel_density = @prev_pixel_density, pd
85
76
  on_change_pixel_density pd if prev && pd != prev
86
77
  end
@@ -127,28 +118,35 @@ module Processing
127
118
  draw_canvas {call_block block, e} if block
128
119
  end
129
120
 
121
+ def on_canvas_wheel(e)
122
+ draw_canvas {call_block @wheel, e} if @wheel
123
+ end
124
+
130
125
  def on_canvas_resize(e)
131
126
  resize_canvas e.width, e.height if @auto_resize
132
127
  draw_canvas {call_block @resize, e} if @resize
133
128
  end
134
129
 
135
- def resize_canvas(width, height, pixel_density = nil, window_pixel_density: nil)
136
- painting = canvas_painter.painting?
137
- canvas_painter.__send__ :end_paint if painting
130
+ def resize_canvas(
131
+ width, height,
132
+ pixel_density = nil,
133
+ window_pixel_density: nil,
134
+ antialiasing: nil)
135
+
136
+ painting = @canvas.painter.painting?
137
+ @canvas.painter.__send__ :end_paint if painting
138
138
 
139
139
  @pixel_density = pixel_density if pixel_density
140
140
 
141
141
  resized =
142
142
  begin
143
- @canvas.resize width, height, @pixel_density || window_pixel_density
143
+ pd = @pixel_density || window_pixel_density
144
+ @canvas.resize width, height, pd, antialiasing
144
145
  ensure
145
- canvas_painter.__send__ :begin_paint if painting
146
+ @canvas.painter.__send__ :begin_paint if painting
146
147
  end
147
148
 
148
- if resized
149
- @update_canvas.call canvas_image, canvas_painter if @update_canvas
150
- size width, height
151
- end
149
+ @update_canvas&.call @canvas.image, @canvas.painter if resized
152
150
  end
153
151
 
154
152
  private
@@ -157,12 +155,12 @@ module Processing
157
155
  scrollx, scrolly, zoom = get_scroll_and_zoom
158
156
  @canvas_view.scroll_to scrollx, scrolly
159
157
  @canvas_view.zoom = zoom
160
- @overlay_view.size = canvas_image.size
158
+ @overlay_view.size = @canvas.image.size
161
159
  end
162
160
 
163
161
  def get_scroll_and_zoom()
164
- ww, wh = width.to_f, height.to_f
165
- cw, ch = canvas_image.width.to_f, canvas_image.height.to_f
162
+ ww, wh = width.to_f, height.to_f
163
+ cw, ch = @canvas.image.width.to_f, @canvas.image.height.to_f
166
164
  return [0, 0, 1] if ww == 0 || wh == 0 || cw == 0 || ch == 0
167
165
 
168
166
  wratio, cratio = ww / wh, cw / ch
@@ -184,21 +182,21 @@ module Processing
184
182
  end
185
183
 
186
184
  def begin_draw()
187
- canvas_painter.__send__ :begin_paint
185
+ @canvas.painter.__send__ :begin_paint
188
186
  @before_draw&.call
189
187
  end
190
188
 
191
189
  def end_draw()
192
190
  @after_draw&.call
193
- canvas_painter.__send__ :end_paint
191
+ @canvas.painter.__send__ :end_paint
194
192
  end
195
193
 
196
194
  def drawing?()
197
- canvas_painter.painting?
195
+ @canvas.painter.painting?
198
196
  end
199
197
 
200
198
  def draw_screen(painter)
201
- window_painter.image canvas_image
199
+ painter.image @canvas.render
202
200
  end
203
201
 
204
202
  def call_block(block, event, *args)
@@ -216,49 +214,84 @@ module Processing
216
214
 
217
215
  class Window::Canvas
218
216
 
219
- attr_reader :image, :painter
220
-
221
217
  def initialize(window, width, height)
222
- @image = nil
223
- @painter = window.painter
218
+ @framebuffer = nil
219
+ @paintable = nil
220
+ @painter = window.painter
221
+
222
+ @painter.miter_limit = 10
224
223
 
225
224
  resize width, height
226
- painter.miter_limit = 10
227
225
  end
228
226
 
229
- def resize(width, height, pixel_density = nil)
227
+ attr_reader :painter
228
+
229
+ def resize(width, height, pixel_density = nil, antialiasing = nil)
230
230
  return false if width <= 0 || height <= 0
231
231
 
232
+ cs = @framebuffer&.color_space || Rays::RGBA
233
+ pd = pixel_density || (@framebuffer || @painter).pixel_density
234
+ aa = antialiasing == nil ? antialiasing? : (antialiasing && pd < 2)
232
235
  return false if
233
- width == @image&.width &&
234
- height == @image&.height &&
235
- pixel_density == @painter.pixel_density
236
+ width == @framebuffer&.width &&
237
+ height == @framebuffer&.height &&
238
+ pd == @framebuffer&.pixel_density &&
239
+ aa == antialiasing?
236
240
 
237
- old_image = @image
238
- old_painter = @painter
239
- cs = old_image&.color_space || Rays::RGBA
240
- pd = pixel_density || old_painter.pixel_density
241
+ old_paintable, old_painter = @paintable, @painter
241
242
 
242
- @image = Rays::Image.new width, height, cs, pd
243
- @painter = @image.painter
243
+ @framebuffer = Rays::Image.new width, height, cs, pd
244
+ @paintable = aa ? Rays::Image.new(width, height, cs, pd * 2) : @framebuffer
245
+ @painter = @paintable.painter
244
246
 
245
- @painter.paint {image old_image} if old_image
247
+ @painter.paint {image old_paintable} if old_paintable
246
248
  copy_painter old_painter, @painter
247
249
 
248
250
  GC.start
249
251
  return true
250
252
  end
251
253
 
254
+ def render()
255
+ @framebuffer.paint {|p| p.image @paintable} if antialiasing?
256
+ @framebuffer
257
+ end
258
+
259
+ def image()
260
+ @paintable
261
+ end
262
+
263
+ def width()
264
+ @framebuffer.width
265
+ end
266
+
267
+ def height()
268
+ @framebuffer.height
269
+ end
270
+
271
+ def pixel_density()
272
+ @framebuffer.pixel_density
273
+ end
274
+
275
+ def antialiasing?()
276
+ !!@framebuffer && !!@paintable && @framebuffer != @paintable
277
+ end
278
+
252
279
  private
253
280
 
254
281
  def copy_painter(from, to)
255
- to.fill = from.fill
256
- to.stroke = from.stroke
257
- to.stroke_width = from.stroke_width
258
- to.stroke_cap = from.stroke_cap
259
- to.stroke_join = from.stroke_join
260
- to.miter_limit = from.miter_limit
261
- to.font = from.font
282
+ to.fill = from.fill
283
+ to.stroke = from.stroke
284
+ to.stroke_width = from.stroke_width
285
+ to.stroke_cap = from.stroke_cap
286
+ to.stroke_join = from.stroke_join
287
+ to.miter_limit = from.miter_limit
288
+ to.clip = from.clip
289
+ to.blend_mode = from.blend_mode
290
+ to.font = from.font
291
+ to.texture = from.texture
292
+ to.texcoord_mode = from.texcoord_mode
293
+ to.texcoord_wrap = from.texcoord_wrap
294
+ to.shader = from.shader
262
295
  end
263
296
 
264
297
  end# Window::Canvas
@@ -268,6 +301,7 @@ module Processing
268
301
 
269
302
  def on_update(e)
270
303
  window.on_canvas_update e
304
+ Thread.pass
271
305
  end
272
306
 
273
307
  def on_draw(e)
@@ -278,6 +312,10 @@ module Processing
278
312
  window.on_canvas_pointer e
279
313
  end
280
314
 
315
+ def on_wheel(e)
316
+ window.on_canvas_wheel e
317
+ end
318
+
281
319
  def on_resize(e)
282
320
  window.on_canvas_resize e
283
321
  end
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.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'
28
+ s.add_runtime_dependency 'xot', '~> 0.1.42'
29
+ s.add_runtime_dependency 'rucy', '~> 0.1.44'
30
+ s.add_runtime_dependency 'rays', '~> 0.1.49'
31
+ s.add_runtime_dependency 'reflexion', '~> 0.1.57'
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
@@ -34,8 +34,10 @@ def mkdir(dir: nil, filename: nil)
34
34
  FileUtils.mkdir_p path unless File.exist? path
35
35
  end
36
36
 
37
- def test_label(index = 1)
38
- caller_locations[index].then {|loc| "#{loc.label}_#{loc.lineno}"}
37
+ def test_label(frame_offset = 1, suffix: nil)
38
+ suffix = suffix ? "_#{suffix}" : ''
39
+ caller_locations[frame_offset]
40
+ .then {|loc| "#{loc.label}_#{loc.lineno}#{suffix}"}
39
41
  end
40
42
 
41
43
  def temp_path(ext: nil, &block)
@@ -59,7 +61,6 @@ def get_pixels(image)
59
61
  .map {image.instance_variable_get _1}
60
62
  .compact
61
63
  .first
62
- .bitmap
63
64
  .pixels
64
65
  end
65
66
 
@@ -71,6 +72,7 @@ end
71
72
 
72
73
  def test_draw(*sources, width: 1000, height: 1000, pixelDensity: 1, label: nil)
73
74
  graphics(width, height, pixelDensity).tap do |g|
75
+ g.renderMode :p5js
74
76
  g.beginDraw {g.instance_eval sources.compact.join("\n")}
75
77
  g.save draw_output_path(label, *sources) if label
76
78
  end
@@ -107,14 +109,14 @@ end
107
109
 
108
110
  def assert_p5_draw(
109
111
  *sources, default_header: DEFAULT_DRAW_HEADER,
110
- width: 1000, height: 1000, threshold: 0.99, label: test_label)
112
+ width: 1000, height: 1000, threshold: 0.99, label: test_label, **kwargs)
111
113
 
112
114
  return unless test_with_p5?
113
115
 
114
116
  source = [default_header, *sources].compact.join("\n")
115
117
  path = draw_output_path "#{label}_expected", source
116
118
 
117
- pd = draw_p5rb width, height, source, path, headless: true
119
+ pd = draw_p5rb width, height, source, path, **kwargs
118
120
  actual = test_draw source, width: width, height: height, pixelDensity: pd
119
121
  actual.save path.sub('_expected', '_actual')
120
122