processing 0.5.31 → 0.5.32

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[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
- @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
66
121
  nil
67
122
  end
68
123
 
69
- def vertex(x, y)
70
- raise "vertex() must be called after beginShape()" unless @points
71
- @points << x << y
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)
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.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
+
91
192
  def addChild(child)
92
193
  return unless @children
93
194
  @children.push child
@@ -125,6 +226,11 @@ module Processing
125
226
  def rotateY = nil
126
227
  def rotateZ = nil
127
228
 
229
+ # @private
230
+ def clearCache__()
231
+ @polygon = nil# clear cache
232
+ end
233
+
128
234
  # @private
129
235
  def matrix__()
130
236
  @matrix ||= Rays::Matrix.new
@@ -133,8 +239,10 @@ module Processing
133
239
  # @private
134
240
  def getInternal__()
135
241
  unless @polygon
136
- return nil unless @points && @closed != nil
137
- @polygon = self.class.createPolygon__ @mode, @points, @closed
242
+ return nil unless @points && @close != nil
243
+ @polygon = self.class.createPolygon__(
244
+ @type, @points, @close, @colors, @texcoords)
245
+ @polygon += @contours if @polygon
138
246
  end
139
247
  @polygon
140
248
  end
@@ -162,18 +270,21 @@ module Processing
162
270
  end
163
271
 
164
272
  # @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}'"
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}'"
177
288
  end
178
289
  end
179
290
 
data/processing.gemspec CHANGED
@@ -26,9 +26,9 @@ Gem::Specification.new do |s|
26
26
  s.required_ruby_version = '>= 3.0.0'
27
27
 
28
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'
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
@@ -59,7 +59,6 @@ def get_pixels(image)
59
59
  .map {image.instance_variable_get _1}
60
60
  .compact
61
61
  .first
62
- .bitmap
63
62
  .pixels
64
63
  end
65
64
 
@@ -107,14 +106,14 @@ end
107
106
 
108
107
  def assert_p5_draw(
109
108
  *sources, default_header: DEFAULT_DRAW_HEADER,
110
- width: 1000, height: 1000, threshold: 0.99, label: test_label)
109
+ width: 1000, height: 1000, threshold: 0.99, label: test_label, **kwargs)
111
110
 
112
111
  return unless test_with_p5?
113
112
 
114
113
  source = [default_header, *sources].compact.join("\n")
115
114
  path = draw_output_path "#{label}_expected", source
116
115
 
117
- pd = draw_p5rb width, height, source, path, headless: true
116
+ pd = draw_p5rb width, height, source, path, **kwargs
118
117
  actual = test_draw source, width: width, height: height, pixelDensity: pd
119
118
  actual.save path.sub('_expected', '_actual')
120
119
 
data/test/p5.rb CHANGED
@@ -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,9 +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
+ #{webgl ? 'translate -width / 2, -height / 2' : ''}
40
41
  #{draw_src}
41
42
  JS.global.completed
42
43
  end
@@ -62,11 +63,11 @@ def sleep_until (try: 3, timeout: 10, &block)
62
63
  end
63
64
  end
64
65
 
65
- def draw_p5rb(width, height, draw_src, path, headless: true)
66
+ def draw_p5rb(width, height, draw_src, path, headless: true, webgl: false)
66
67
  b = browser width, height, headless: headless
67
68
  unless File.exist? path
68
69
  b.reset
69
- b.main_frame.content = get_p5rb_html width, height, draw_src
70
+ b.main_frame.content = get_p5rb_html width, height, draw_src, webgl: webgl
70
71
  sleep_until do
71
72
  b.evaluate 'document.querySelector("#completed") != null'
72
73
  end
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 Rays::Font.new(nil, 10)
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
@@ -76,6 +76,67 @@ class TestGraphicsContext < Test::Unit::TestCase
76
76
  END
77
77
  end
78
78
 
79
+ def test_textFont()
80
+ graphics do |g|
81
+ arial10 = g.createFont 'Arial', 10
82
+ helvetica11 = g.createFont 'Helvetica', 11
83
+
84
+ font = -> {g.instance_variable_get(:@textFont__).getInternal__}
85
+ painterFont = -> {g.instance_variable_get(:@painter__).font}
86
+
87
+ assert_not_nil font[] .name
88
+ assert_not_equal 'Arial', font[] .name
89
+ assert_equal 12, font[] .size
90
+ assert_equal 12, painterFont[].size
91
+
92
+ g.textFont arial10
93
+ assert_equal 'Arial', font[] .name
94
+ assert_equal 10, font[] .size
95
+ assert_equal 10, painterFont[].size
96
+
97
+ g.push do
98
+ g.textFont helvetica11
99
+ assert_equal 'Helvetica', font[] .name
100
+ assert_equal 11, font[] .size
101
+ assert_equal 11, painterFont[].size
102
+ end
103
+
104
+ assert_equal 'Arial', font[] .name
105
+ assert_equal 10, font[] .size
106
+ assert_equal 10, painterFont[].size
107
+
108
+ g.push do
109
+ g.textFont arial10, 13
110
+ assert_equal 'Arial', font[] .name
111
+ assert_equal 13, font[] .size
112
+ assert_equal 13, painterFont[].size
113
+ end
114
+
115
+ assert_equal 'Arial', font[] .name
116
+ assert_equal 10, font[] .size
117
+ assert_equal 10, painterFont[].size
118
+ end
119
+ end
120
+
121
+ def test_textSize()
122
+ graphics do |g|
123
+ font = -> {g.instance_variable_get(:@textFont__).getInternal__}
124
+ painterFont = -> {g.instance_variable_get(:@painter__).font}
125
+
126
+ assert_equal 12, font[] .size
127
+ assert_equal 12, painterFont[].size
128
+
129
+ g.push do
130
+ g.textSize 10
131
+ assert_equal 10, font[] .size
132
+ assert_equal 10, painterFont[].size
133
+ end
134
+
135
+ assert_equal 12, font[] .size
136
+ assert_equal 12, painterFont[].size
137
+ end
138
+ end
139
+
79
140
  def test_clear()
80
141
  colors = -> g {get_pixels(g).uniq}
81
142
 
@@ -468,6 +529,155 @@ class TestGraphicsContext < Test::Unit::TestCase
468
529
  assert_p5_fill_stroke src, 'endShape CLOSE'
469
530
  end
470
531
 
532
+ def test_beginShape_with_fill()
533
+ assert_equal_draw <<~HEADER, <<~EXPECTED, <<~ACTUAL
534
+ noStroke
535
+ HEADER
536
+ fill 0, 255, 0
537
+ rect 100, 100, 500, 400
538
+ EXPECTED
539
+ beginShape
540
+ fill 0, 255, 0
541
+ vertex 100, 100
542
+ vertex 600, 100
543
+ vertex 600, 500
544
+ vertex 100, 500
545
+ endShape
546
+ ACTUAL
547
+ end
548
+
549
+ def test_curveVertex()
550
+ src = <<~END
551
+ beginShape
552
+ curveVertex 100, 100
553
+ curveVertex 800, 100
554
+ curveVertex 800, 800
555
+ curveVertex 100, 800
556
+ END
557
+ assert_p5_fill src, 'endShape'
558
+ assert_p5_stroke src, 'endShape'
559
+ assert_p5_fill_stroke src, 'endShape'
560
+ assert_p5_fill src, 'endShape CLOSE'
561
+ assert_p5_stroke src, 'endShape CLOSE'
562
+ assert_p5_fill_stroke src, 'endShape CLOSE'
563
+
564
+ src = <<~END
565
+ beginShape
566
+ curveVertex 200, 200
567
+ curveVertex 200, 200
568
+ curveVertex 800, 200
569
+ curveVertex 800, 400
570
+ curveVertex 200, 400
571
+ curveVertex 200, 800
572
+ curveVertex 800, 800
573
+ curveVertex 800, 700
574
+ curveVertex 800, 700
575
+ END
576
+ assert_p5_fill src, 'endShape', threshold: THRESHOLD_TO_BE_FIXED
577
+ assert_p5_stroke src, 'endShape'
578
+ assert_p5_fill_stroke src, 'endShape', threshold: THRESHOLD_TO_BE_FIXED
579
+ assert_p5_fill src, 'endShape CLOSE', threshold: THRESHOLD_TO_BE_FIXED
580
+ assert_p5_stroke src, 'endShape CLOSE'
581
+ assert_p5_fill_stroke src, 'endShape CLOSE', threshold: THRESHOLD_TO_BE_FIXED
582
+ end
583
+
584
+ def test_bezierVertex()
585
+ src = <<~END
586
+ beginShape
587
+ vertex 100, 100
588
+ bezierVertex 900, 100, 900, 900, 200, 500
589
+ END
590
+ assert_p5_fill src, 'endShape'
591
+ assert_p5_stroke src, 'endShape'
592
+ assert_p5_fill_stroke src, 'endShape'
593
+ assert_p5_fill src, 'endShape CLOSE'
594
+ assert_p5_stroke src, 'endShape CLOSE'
595
+ assert_p5_fill_stroke src, 'endShape CLOSE'
596
+
597
+ src = <<~END
598
+ beginShape
599
+ vertex 100, 100
600
+ bezierVertex 900, 100, 900, 500, 300, 500
601
+ bezierVertex 100, 900, 900, 900, 900, 600
602
+ END
603
+ assert_p5_fill src, 'endShape', threshold: THRESHOLD_TO_BE_FIXED
604
+ assert_p5_stroke src, 'endShape'
605
+ assert_p5_fill_stroke src, 'endShape', threshold: THRESHOLD_TO_BE_FIXED
606
+ assert_p5_fill src, 'endShape CLOSE', threshold: THRESHOLD_TO_BE_FIXED
607
+ assert_p5_stroke src, 'endShape CLOSE'
608
+ assert_p5_fill_stroke src, 'endShape CLOSE', threshold: THRESHOLD_TO_BE_FIXED
609
+ end
610
+
611
+ def test_quadraticVertex()
612
+ src = <<~END
613
+ beginShape
614
+ vertex 100, 100
615
+ quadraticVertex 800, 500, 200, 800
616
+ END
617
+ assert_p5_fill src, 'endShape'
618
+ assert_p5_stroke src, 'endShape'
619
+ assert_p5_fill_stroke src, 'endShape'
620
+ assert_p5_fill src, 'endShape CLOSE'
621
+ assert_p5_stroke src, 'endShape CLOSE'
622
+ assert_p5_fill_stroke src, 'endShape CLOSE'
623
+
624
+ src = <<~END
625
+ beginShape
626
+ vertex 100, 100
627
+ quadraticVertex 800, 100, 500, 500
628
+ quadraticVertex 100, 800, 800, 800
629
+ END
630
+ assert_p5_fill src, 'endShape', threshold: THRESHOLD_TO_BE_FIXED
631
+ assert_p5_stroke src, 'endShape'
632
+ assert_p5_fill_stroke src, 'endShape', threshold: THRESHOLD_TO_BE_FIXED
633
+ assert_p5_fill src, 'endShape CLOSE', threshold: THRESHOLD_TO_BE_FIXED
634
+ assert_p5_stroke src, 'endShape CLOSE'
635
+ assert_p5_fill_stroke src, 'endShape CLOSE', threshold: THRESHOLD_TO_BE_FIXED
636
+ end
637
+
638
+ def test_contour()
639
+ src = <<~END
640
+ beginShape
641
+ vertex 100, 100
642
+ vertex 100, 900
643
+ vertex 900, 900
644
+ vertex 900, 100
645
+ beginContour
646
+ vertex 200, 200
647
+ vertex 800, 200
648
+ vertex 700, 700
649
+ vertex 200, 800
650
+ endContour
651
+ END
652
+ assert_p5_fill src, 'endShape'
653
+ assert_p5_stroke src, 'endShape'
654
+ assert_p5_fill_stroke src, 'endShape'
655
+ assert_p5_fill src, 'endShape CLOSE'
656
+ assert_p5_stroke src, 'endShape CLOSE'
657
+ assert_p5_fill_stroke src, 'endShape CLOSE'
658
+ end
659
+
660
+ def test_pixels()
661
+ g = graphics 2, 2
662
+
663
+ g.loadPixels
664
+ assert_equal [0] * 4, g.pixels
665
+ assert_equal [0] * 4, g.getInternal__.pixels
666
+
667
+ g.pixels.replace [0xffff0000, 0xff00ff00, 0xff0000ff, 0xff000000]
668
+ assert_equal [0xffff0000, 0xff00ff00, 0xff0000ff, 0xff000000], g.pixels
669
+ assert_equal [0] * 4, g.getInternal__.pixels
670
+
671
+ g.updatePixels
672
+ assert_nil g.pixels
673
+ assert_equal [0xffff0000, 0xff00ff00, 0xff0000ff, 0xff000000], g.getInternal__.pixels
674
+ assert_nothing_raised {g.updatePixels}
675
+
676
+ g.loadPixels
677
+ g.pixels.replace [0xff000000]
678
+ assert_raise(ArgumentError) {g.updatePixels}
679
+ end
680
+
471
681
  def test_lerp()
472
682
  g = graphics
473
683
 
@@ -491,6 +701,16 @@ class TestGraphicsContext < Test::Unit::TestCase
491
701
  assert_equal c[ 70, 80, 90], g.lerpColor(c[10, 20, 30], c[50, 60, 70], 1.5)
492
702
  end
493
703
 
704
+ def test_createFont()
705
+ g = graphics
706
+
707
+ assert_not_nil g.createFont(nil, nil).getInternal__.name
708
+ assert_equal 'Arial', g.createFont('Arial', nil).getInternal__.name
709
+
710
+ assert_equal 12, g.createFont(nil, nil).getInternal__.size
711
+ assert_equal 10, g.createFont(nil, 10) .getInternal__.size
712
+ end
713
+
494
714
  def test_createShape_line()
495
715
  assert_equal_draw <<~EXPECTED, <<~ACTUAL
496
716
  line 100, 200, 800, 900