processing 0.5.31 → 0.5.33

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.
@@ -25,6 +25,14 @@ module Processing
25
25
  #
26
26
  TAU = PI * 2
27
27
 
28
+ # Processing mode for renderMode().
29
+ #
30
+ PROCESSING = :processing
31
+
32
+ # p5.js mode for renderMode().
33
+ #
34
+ P5JS = :p5js
35
+
28
36
  # RGBA format for createImage().
29
37
  #
30
38
  RGBA = :rgba
@@ -129,6 +137,18 @@ module Processing
129
137
  # Mode for textAlign().
130
138
  BASELINE = :baseline
131
139
 
140
+ # Mode for textureMode().
141
+ IMAGE = :image
142
+
143
+ # Mode for textureMode().
144
+ NORMAL = :normal
145
+
146
+ # Mode for textureWrap().
147
+ CLAMP = :clamp
148
+
149
+ # Mode for textureWrap().
150
+ REPEAT = :repeat
151
+
132
152
  # Filter type for filter()
133
153
  THRESHOLD = :threshold
134
154
 
@@ -247,29 +267,50 @@ module Processing
247
267
  # @private
248
268
  RAD2DEG__ = 180.0 / Math::PI
249
269
 
270
+ # @private
271
+ FONT_SIZE_DEFAULT__ = 12
272
+
273
+ # @private
274
+ FONT_SIZE_MAX__ = 256
275
+
250
276
  # @private
251
277
  def init__(image, painter)
252
- @drawing__ = false
253
- @colorMode__ = nil
254
- @hsbColor__ = false
255
- @colorMaxes__ = [1.0] * 4
256
- @angleMode__ = nil
257
- @angleScale__ = 1.0
258
- @rectMode__ = nil
259
- @ellipseMode__ = nil
260
- @imageMode__ = nil
261
- @shapeMode__ = nil
262
- @blendMode__ = nil
263
- @textAlignH__ = nil
264
- @textAlignV__ = nil
265
- @tint__ = nil
266
- @filter__ = nil
267
- @matrixStack__ = []
268
- @styleStack__ = []
269
- @fontCache__ = {}
278
+ @drawing__ = false
279
+ @renderMode__ = nil
280
+ @p5jsMode__ = false
281
+ @colorMode__ = nil
282
+ @hsbColor__ = false
283
+ @colorMaxes__ = [1.0] * 4
284
+ @angleMode__ = nil
285
+ @toRad__ = 1.0
286
+ @toDeg__ = 1.0
287
+ @fromRad__ = 1.0
288
+ @fromDeg__ = 1.0
289
+ @rectMode__ = nil
290
+ @ellipseMode__ = nil
291
+ @imageMode__ = nil
292
+ @shapeMode__ = nil
293
+ @blendMode__ = nil
294
+ @curveDetail__ = nil
295
+ @curveTightness__ = nil
296
+ @bezierDetail__ = nil
297
+ @textAlignH__ = nil
298
+ @textAlignV__ = nil
299
+ @textFont__ = nil
300
+ @tint__ = nil
301
+ @filter__ = nil
302
+ @pixels__ = nil
303
+ @random__ = nil
304
+ @nextGaussian__ = nil
305
+ @noiseSeed__ = nil
306
+ @noiseOctaves__ = nil
307
+ @noiseFallOff__ = nil
308
+ @matrixStack__ = []
309
+ @styleStack__ = []
270
310
 
271
311
  updateCanvas__ image, painter
272
312
 
313
+ renderMode PROCESSING
273
314
  colorMode RGB, 255
274
315
  angleMode RADIANS
275
316
  rectMode CORNER
@@ -280,11 +321,20 @@ module Processing
280
321
  strokeCap ROUND
281
322
  strokeJoin MITER
282
323
  textAlign LEFT
324
+ textFont createFont(nil, nil)
325
+ textureMode IMAGE
326
+ textureWrap CLAMP
283
327
 
284
- fill 255
285
- stroke 0
286
- strokeWeight 1
328
+ fill 255
329
+ stroke 0
330
+ strokeWeight 1
287
331
  noTint
332
+ curveDetail 20
333
+ curveTightness 0
334
+ bezierDetail 20
335
+ randomSeed Random.new_seed
336
+ noiseSeed Random.new_seed
337
+ noiseDetail 4, 0.5
288
338
  end
289
339
 
290
340
  # @private
@@ -300,6 +350,26 @@ module Processing
300
350
  @matrixStack__.clear
301
351
  @styleStack__.clear
302
352
  @drawing__ = true
353
+ setupMatrix__
354
+ end
355
+
356
+ # @private
357
+ def setupMatrix__()
358
+ w, h = width.to_f, height.to_f
359
+ x, y = w / 2.0, h / 2.0
360
+
361
+ fov, z = nil
362
+ if @p5jsMode__
363
+ z = 800
364
+ fov = degrees Math.atan(y / z) * 2.0
365
+ else
366
+ fov = 60
367
+ z = y / Math.tan(radians(fov) / 2.0)
368
+ end
369
+
370
+ @painter__.matrix =
371
+ Rays::Matrix.perspective(fov, w / h, z / 10.0, z * 10.0) *
372
+ Rays::Matrix.look_at(x, y, z, x, y, 0)
303
373
  end
304
374
 
305
375
  # @private
@@ -313,7 +383,7 @@ module Processing
313
383
  # @return [Numeric] width
314
384
  #
315
385
  def width()
316
- @image__.width
386
+ getInternal__.width
317
387
  end
318
388
 
319
389
  # Returns the height of the graphics object.
@@ -321,7 +391,7 @@ module Processing
321
391
  # @return [Numeric] height
322
392
  #
323
393
  def height()
324
- @image__.height
394
+ getInternal__.height
325
395
  end
326
396
 
327
397
  # Returns the width of the graphics object in pixels.
@@ -348,6 +418,20 @@ module Processing
348
418
  @painter__.pixel_density
349
419
  end
350
420
 
421
+ # Sets render mode.
422
+ #
423
+ # @param mode [PROCESSING, P5JS] compatible to Processing or p5.js
424
+ #
425
+ # @return [PROCESSING, P5JS] current mode
426
+ #
427
+ def renderMode(mode = nil)
428
+ if mode
429
+ @renderMode__ = mode
430
+ @p5jsMode__ = mode == P5JS
431
+ end
432
+ @renderMode__
433
+ end
434
+
351
435
  # Sets color mode and max color values.
352
436
  #
353
437
  # @overload colorMode(mode)
@@ -441,19 +525,56 @@ module Processing
441
525
  ((color >> 24) & 0xff) / 255.0 * @colorMaxes__[3]
442
526
  end
443
527
 
528
+ # Returns the hue value of the color.
529
+ #
530
+ # @param color [Numeric] color value
531
+ #
532
+ # @return [Numeric] the hue value
533
+ #
534
+ def hue(color)
535
+ h, = toRawColor__(color).to_hsv
536
+ h * (@hsbColor__ ? @colorMaxes__[0] : 1)
537
+ end
538
+
539
+ # Returns the saturation value of the color.
540
+ #
541
+ # @param color [Numeric] color value
542
+ #
543
+ # @return [Numeric] the saturation value
544
+ #
545
+ def saturation(color)
546
+ _, s, = toRawColor__(color).to_hsv
547
+ s * (@hsbColor__ ? @colorMaxes__[1] : 1)
548
+ end
549
+
550
+ # Returns the brightness value of the color.
551
+ #
552
+ # @param color [Numeric] color value
553
+ #
554
+ # @return [Numeric] the brightness value
555
+ #
556
+ def brightness(color)
557
+ _, _, b = toRawColor__(color).to_hsv
558
+ b * (@hsbColor__ ? @colorMaxes__[2] : 1)
559
+ end
560
+
444
561
  # @private
445
562
  private def toRGBA__(*args)
446
- a, b, c, d = args
563
+ a, b = args
447
564
  return parseColor__(a, b || alphaMax__) if a.kind_of?(String)
565
+ rawColor__(*args).to_a
566
+ end
448
567
 
568
+ # @private
569
+ def rawColor__(*args)
570
+ a, b, c, d = args
449
571
  rgba = case args.size
450
572
  when 1, 2 then [a, a, a, b || alphaMax__]
451
573
  when 3, 4 then [a, b, c, d || alphaMax__]
452
574
  else raise ArgumentError
453
575
  end
454
- rgba = rgba.map.with_index {|value, i| value / @colorMaxes__[i]}
455
- color = @hsbColor__ ? Rays::Color.hsv(*rgba) : Rays::Color.new(*rgba)
456
- color.to_a
576
+ rgba = rgba.map.with_index {|value, i| value / @colorMaxes__[i]}
577
+ @hsbColor__ ? Rays::Color.hsv(*rgba) : Rays::Color.new(*rgba)
457
578
  end
458
579
 
459
580
  # @private
@@ -470,6 +591,15 @@ module Processing
470
591
  @colorMaxes__[3]
471
592
  end
472
593
 
594
+ # @private
595
+ private def toRawColor__(color)
596
+ Rays::Color.new(
597
+ ((color >> 16) & 0xff) / 255.0,
598
+ ((color >> 8) & 0xff) / 255.0,
599
+ ( color & 0xff) / 255.0,
600
+ ((color >> 24) & 0xff) / 255.0)
601
+ end
602
+
473
603
  # Sets angle mode.
474
604
  #
475
605
  # @param mode [RADIANS, DEGREES] RADIANS or DEGREES
@@ -479,37 +609,34 @@ module Processing
479
609
  def angleMode(mode = nil)
480
610
  if mode != nil
481
611
  @angleMode__ = mode
482
- @angleScale__ =
612
+ @toRad__, @toDeg__, @fromRad__, @fromDeg__ =
483
613
  case mode.downcase.to_sym
484
- when RADIANS then RAD2DEG__
485
- when DEGREES then 1.0
614
+ when RADIANS then [1.0, RAD2DEG__, 1.0, DEG2RAD__]
615
+ when DEGREES then [DEG2RAD__, 1.0, RAD2DEG__, 1.0]
486
616
  else raise ArgumentError, "invalid angle mode: #{mode}"
487
617
  end
488
618
  end
489
619
  @angleMode__
490
620
  end
491
621
 
622
+ # @private
623
+ def toRadians__(angle)
624
+ angle * @toRad__
625
+ end
626
+
492
627
  # @private
493
628
  def toDegrees__(angle)
494
- angle * @angleScale__
629
+ angle * @toDeg__
495
630
  end
496
631
 
497
632
  # @private
498
633
  def fromRadians__(radians)
499
- case @angleMode__
500
- when RADIANS then radians
501
- when DEGREES then radians * RAD2DEG__
502
- else raise "invalid angle mode: #{mode}"
503
- end
634
+ radians * @fromRad__
504
635
  end
505
636
 
506
637
  # @private
507
638
  def fromDegrees__(degrees)
508
- case @angleMode__
509
- when RADIANS then degrees * DEG2RAD__
510
- when DEGREES then degrees
511
- else raise "invalid angle mode: #{mode}"
512
- end
639
+ degrees * @fromDeg__
513
640
  end
514
641
 
515
642
  # Sets rect mode. Default is CORNER.
@@ -618,6 +745,11 @@ module Processing
618
745
  nil
619
746
  end
620
747
 
748
+ # @private
749
+ def getFill__()
750
+ @painter__.fill
751
+ end
752
+
621
753
  # Disables filling.
622
754
  #
623
755
  # @return [nil] nil
@@ -692,6 +824,52 @@ module Processing
692
824
  nil
693
825
  end
694
826
 
827
+ # Sets the resolution at which curves display.
828
+ # The default value is 20 while the minimum value is 3.
829
+ #
830
+ # @param detail [Numeric] resolution of the curves
831
+ #
832
+ # @return [nil] nil
833
+ #
834
+ # @see https://processing.org/reference/curveDetail_.html
835
+ # @see https://p5js.org/reference/#/p5/curveDetail
836
+ #
837
+ def curveDetail(detail)
838
+ detail = 3 if detail < 3
839
+ @curveDetail__ = detail
840
+ nil
841
+ end
842
+
843
+ # Sets the quality of curve forms.
844
+ #
845
+ # @param tightness [Numeric] determines how the curve fits to the vertex points
846
+ #
847
+ # @return [nil] nil
848
+ #
849
+ # @see https://processing.org/reference/curveTightness_.html
850
+ # @see https://p5js.org/reference/#/p5/curveTightness
851
+ #
852
+ def curveTightness(tightness)
853
+ @curveTightness__ = tightness
854
+ nil
855
+ end
856
+
857
+ # Sets the resolution at which Bezier's curve is displayed.
858
+ # The default value is 20.
859
+ #
860
+ # @param detail [Numeric] resolution of the curves
861
+ #
862
+ # @return [nil] nil
863
+ #
864
+ # @see https://processing.org/reference/bezierDetail_.html
865
+ # @see https://p5js.org/reference/#/p5/bezierDetail
866
+ #
867
+ def bezierDetail(detail)
868
+ detail = 1 if detail < 1
869
+ @bezierDetail__ = detail
870
+ nil
871
+ end
872
+
695
873
  # Sets fill color for drawing images.
696
874
  #
697
875
  # @overload tint(rgb)
@@ -723,6 +901,11 @@ module Processing
723
901
  @tint__ = nil
724
902
  end
725
903
 
904
+ # @private
905
+ def getTint__()
906
+ @tint__ ? toRGBA__(*@tint__) : 1
907
+ end
908
+
726
909
  # Limits the drawable rectangle.
727
910
  #
728
911
  # The parameters a, b, c, and d are determined by rectMode().
@@ -749,12 +932,14 @@ module Processing
749
932
  nil
750
933
  end
751
934
 
752
- # Sets font.
935
+ # Sets text font.
936
+ # (Passing a font name as the first parameter is deprecated)
753
937
  #
938
+ # @overload textFont()
754
939
  # @overload textFont(font)
755
- # @overload textFont(name)
940
+ # @overload textFont(name) [DEPRECATED]
756
941
  # @overload textFont(font, size)
757
- # @overload textFont(name, size)
942
+ # @overload textFont(name, size) [DEPRECATED]
758
943
  #
759
944
  # @param font [Font] font
760
945
  # @param name [String] font name
@@ -763,8 +948,17 @@ module Processing
763
948
  # @return [Font] current font
764
949
  #
765
950
  def textFont(font = nil, size = nil)
766
- setFont__ font, size if font || size
767
- Font.new @painter__.font
951
+ if font != nil || size != nil
952
+ size = FONT_SIZE_MAX__ if size && size > FONT_SIZE_MAX__
953
+ if font.nil? || font.kind_of?(String)
954
+ font = createFont font, size
955
+ elsif size
956
+ font.setSize__ size
957
+ end
958
+ @painter__.font = font.getInternal__
959
+ @textFont__ = font
960
+ end
961
+ @textFont__
768
962
  end
769
963
 
770
964
  # Sets text size.
@@ -774,8 +968,7 @@ module Processing
774
968
  # @return [nil] nil
775
969
  #
776
970
  def textSize(size)
777
- setFont__ nil, size
778
- nil
971
+ textFont @textFont__, size
779
972
  end
780
973
 
781
974
  def textWidth(str)
@@ -795,16 +988,60 @@ module Processing
795
988
  @textAlignV__ = vertical
796
989
  end
797
990
 
991
+ # Sets the spacing between lines of text in units of pixels.
992
+ #
993
+ # @overload textLeading()
994
+ # @overload textLeading(leading)
995
+ #
996
+ # @param leading [Numeric] the size in pixels for spacing between lines
997
+ #
998
+ # @return [Numeric] current spacing
999
+ #
1000
+ def textLeading(leading = nil)
1001
+ @painter__.line_height = leading if leading
1002
+ @painter__.line_height
1003
+ end
1004
+
1005
+ def texture(image)
1006
+ @painter__.texture image&.getInternal__
1007
+ nil
1008
+ end
1009
+
798
1010
  # @private
799
- def setFont__(fontOrName, size)
800
- name = case fontOrName
801
- when Font then fontOrName.name
802
- else fontOrName || @painter__.font.name
803
- end
804
- size ||= @painter__.font.size
805
- size = 256 if size > 256
806
- font = @fontCache__[[name, size]] ||= Rays::Font.new name, size
807
- @painter__.font = font
1011
+ def drawWithTexture__(&block)
1012
+ if @painter__.texture
1013
+ @painter__.push fill: getTint__, &block
1014
+ else
1015
+ block.call
1016
+ end
1017
+ end
1018
+
1019
+ # Sets the coordinate space for texture mapping.
1020
+ #
1021
+ # @param mode [IMAGE, NORMAL] image coordinate, or normalized coordinate
1022
+ #
1023
+ # @return [nil] nil
1024
+ #
1025
+ # @see https://processing.org/reference/textureMode_.html
1026
+ # @see https://p5js.org/reference/#/p5/textureMode
1027
+ #
1028
+ def textureMode(mode)
1029
+ @painter__.texcoord_mode = mode
1030
+ nil
1031
+ end
1032
+
1033
+ # Sets the texture wrapping mode.
1034
+ #
1035
+ # @param wrap [CLAMP, REPEAT] how texutres behave when go outside of the range
1036
+ #
1037
+ # @return [nil] nil
1038
+ #
1039
+ # @see https://processing.org/reference/textureWrap_.html
1040
+ # @see https://p5js.org/reference/#/p5/textureWrap
1041
+ #
1042
+ def textureWrap(wrap)
1043
+ @painter__.texcoord_wrap = wrap
1044
+ nil
808
1045
  end
809
1046
 
810
1047
  # Sets shader.
@@ -887,7 +1124,7 @@ module Processing
887
1124
  #
888
1125
  def point(x, y)
889
1126
  assertDrawing__
890
- @painter__.line x, y, x, y
1127
+ @painter__.point x, y
891
1128
  nil
892
1129
  end
893
1130
 
@@ -1070,7 +1307,9 @@ module Processing
1070
1307
  #
1071
1308
  def curve(cx1, cy1, x1, y1, x2, y2, cx2, cy2)
1072
1309
  assertDrawing__
1310
+ @painter__.nsegment = @curveDetail__
1073
1311
  @painter__.curve cx1, cy1, x1, y1, x2, y2, cx2, cy2
1312
+ @painter__.nsegment = 0
1074
1313
  nil
1075
1314
  end
1076
1315
 
@@ -1091,7 +1330,9 @@ module Processing
1091
1330
  #
1092
1331
  def bezier(x1, y1, cx1, cy1, cx2, cy2, x2, y2)
1093
1332
  assertDrawing__
1333
+ @painter__.nsegment = @bezierDetail__
1094
1334
  @painter__.bezier x1, y1, cx1, cy1, cx2, cy2, x2, y2
1335
+ @painter__.nsegment = 0
1095
1336
  nil
1096
1337
  end
1097
1338
 
@@ -1156,8 +1397,7 @@ module Processing
1156
1397
  def image(img, a, b, c = nil, d = nil)
1157
1398
  assertDrawing__
1158
1399
  x, y, w, h = toXYWH__ @imageMode__, a, b, c || img.width, d || img.height
1159
- tint = @tint__ ? toRGBA__(*@tint__) : 1
1160
- img.drawImage__ @painter__, x, y, w, h, fill: tint, stroke: :none
1400
+ img.drawImage__ @painter__, x, y, w, h, fill: getTint__, stroke: :none
1161
1401
  nil
1162
1402
  end
1163
1403
 
@@ -1182,11 +1422,13 @@ module Processing
1182
1422
  assertDrawing__
1183
1423
  return nil unless shp.isVisible
1184
1424
 
1185
- if c || d || @shapeMode__ != CORNER
1186
- x, y, w, h = toXYWH__ @shapeMode__, a, b, c || shp.width, d || shp.height
1187
- shp.draw__ @painter__, x, y, w, h
1188
- else
1189
- shp.draw__ @painter__, a, b
1425
+ drawWithTexture__ do |_|
1426
+ if c || d || @shapeMode__ != CORNER
1427
+ x, y, w, h = toXYWH__ @shapeMode__, a, b, c || shp.width, d || shp.height
1428
+ shp.draw__ @painter__, x, y, w, h
1429
+ else
1430
+ shp.draw__ @painter__, a, b
1431
+ end
1190
1432
  end
1191
1433
  nil
1192
1434
  end
@@ -1195,7 +1437,7 @@ module Processing
1195
1437
 
1196
1438
  # Begins drawing complex shapes.
1197
1439
  #
1198
- # @param mode [POINTS, LINES, TRIANGLES, TRIANGLE_FAN, TRIANGLE_STRIP, QUADS, QUAD_STRIP, TESS]
1440
+ # @param type [POINTS, LINES, TRIANGLES, TRIANGLE_FAN, TRIANGLE_STRIP, QUADS, QUAD_STRIP, TESS]
1199
1441
  #
1200
1442
  # @return [nil] nil
1201
1443
  #
@@ -1217,9 +1459,10 @@ module Processing
1217
1459
  #
1218
1460
  # @see https://processing.org/reference/beginShape_.html
1219
1461
  #
1220
- def beginShape(mode = nil)
1221
- @shapeMode__, @shapePoints__ = mode, []
1222
- nil
1462
+ def beginShape(type = nil)
1463
+ raise "beginShape() cannot be called twice" if @drawingShape__
1464
+ @drawingShape__ = createShape
1465
+ @drawingShape__.beginShape type
1223
1466
  end
1224
1467
 
1225
1468
  # Ends drawing complex shapes.
@@ -1234,25 +1477,114 @@ module Processing
1234
1477
  # @see https://processing.org/reference/endShape_.html
1235
1478
  #
1236
1479
  def endShape(mode = nil)
1237
- raise "endShape() must be called after beginShape()" unless @shapePoints__
1238
- polygon = Shape.createPolygon__ @shapeMode__, @shapePoints__, mode == CLOSE
1239
- @painter__.polygon polygon if polygon
1240
- @shapeMode__ = @shapePoints__ = nil
1480
+ s = @drawingShape__ or raise "endShape() must be called after beginShape()"
1481
+ s.endShape mode
1482
+ shape s
1483
+ @drawingShape__ = nil
1241
1484
  nil
1242
1485
  end
1243
1486
 
1487
+ # Begins drawing a hole inside shape.
1488
+ #
1489
+ # @return [nil] nil
1490
+ #
1491
+ # @example
1492
+ # beginShape
1493
+ # vertex 10, 10
1494
+ # vertex 10, 50
1495
+ # vertex 50, 50
1496
+ # vertex 90, 10
1497
+ # beginContour
1498
+ # vertex 20, 20
1499
+ # vertex 30, 20
1500
+ # vertex 30, 30
1501
+ # vertex 20, 30
1502
+ # endContour
1503
+ # endShape CLOSE
1504
+ #
1505
+ # @see https://processing.org/reference/beginContour_.html
1506
+ # @see https://p5js.org/reference/#/p5/beginContour
1507
+ #
1508
+ def beginContour()
1509
+ (@drawingShape__ or raise "beginContour() must be called after beginShape()")
1510
+ .beginContour
1511
+ end
1512
+
1513
+ # Ends drawing a hole.
1514
+ #
1515
+ # @return [nil] nil
1516
+ #
1517
+ # @see https://processing.org/reference/endContour_.html
1518
+ # @see https://p5js.org/reference/#/p5/endContour
1519
+ #
1520
+ def endContour()
1521
+ (@drawingShape__ or raise "endContour() must be called after beginShape()")
1522
+ .endContour
1523
+ end
1524
+
1244
1525
  # Append vertex for shape polygon.
1245
1526
  #
1527
+ # @overload vertex(x, y)
1528
+ # @overload vertex(x, y, u, v)
1529
+ #
1246
1530
  # @param x [Numeric] x position of vertex
1247
1531
  # @param y [Numeric] y position of vertex
1532
+ # @param u [Numeric] u texture coordinate of vertex
1533
+ # @param v [Numeric] v texture coordinate of vertex
1248
1534
  #
1249
1535
  # @return [nil] nil
1250
1536
  #
1251
1537
  # @see https://processing.org/reference/vertex_.html
1538
+ # @see https://p5js.org/reference/#/p5/vertex
1539
+ #
1540
+ def vertex(x, y, u = nil, v = nil)
1541
+ (@drawingShape__ or raise "vertex() must be called after beginShape()")
1542
+ .vertex x, y, u, v
1543
+ end
1544
+
1545
+ # Append curve vertex for shape polygon.
1546
+ #
1547
+ # @param x [Numeric] x position of vertex
1548
+ # @param y [Numeric] y position of vertex
1549
+ #
1550
+ # @return [nil] nil
1551
+ #
1552
+ # @see https://processing.org/reference/curveVertex_.html
1553
+ # @see https://p5js.org/reference/#/p5/curveVertex
1252
1554
  #
1253
- def vertex(x, y)
1254
- raise "vertex() must be called after beginShape()" unless @shapePoints__
1255
- @shapePoints__ << x << y
1555
+ def curveVertex(x, y)
1556
+ (@drawingShape__ or raise "curveVertex() must be called after beginShape()")
1557
+ .curveVertex x, y
1558
+ end
1559
+
1560
+ # Append bezier vertex for shape polygon.
1561
+ #
1562
+ # @param x [Numeric] x position of vertex
1563
+ # @param y [Numeric] y position of vertex
1564
+ #
1565
+ # @return [nil] nil
1566
+ #
1567
+ # @see https://processing.org/reference/bezierVertex_.html
1568
+ # @see https://p5js.org/reference/#/p5/bezierVertex
1569
+ #
1570
+ def bezierVertex(x2, y2, x3, y3, x4, y4)
1571
+ (@drawingShape__ or raise "bezierVertex() must be called after beginShape()")
1572
+ .bezierVertex x2, y2, x3, y3, x4, y4
1573
+ end
1574
+
1575
+ # Append quadratic vertex for shape polygon.
1576
+ #
1577
+ # @param x [Numeric] x position of vertex
1578
+ # @param y [Numeric] y position of vertex
1579
+ #
1580
+ # @return [nil] nil
1581
+ #
1582
+ # @see https://processing.org/reference/quadraticVertex_.html
1583
+ # @see https://p5js.org/reference/#/p5/quadraticVertex
1584
+ #
1585
+ def quadraticVertex(cx, cy, x3, y3)
1586
+ (@drawingShape__ or raise "quadraticVertex() must be called after beginShape()")
1587
+ .quadraticVertex cx, cy, x3, y3
1256
1588
  end
1257
1589
 
1258
1590
  # Copies image.
@@ -1296,11 +1628,41 @@ module Processing
1296
1628
  #
1297
1629
  def blend(img = nil, sx, sy, sw, sh, dx, dy, dw, dh, mode)
1298
1630
  assertDrawing__
1299
- tint = @tint__ ? toRGBA__(*@tint__) : 1
1300
- img ||= self
1301
- img.drawImage__(
1631
+ (img || self).drawImage__(
1302
1632
  @painter__, sx, sy, sw, sh, dx, dy, dw, dh,
1303
- fill: tint, stroke: :none, blend_mode: mode)
1633
+ fill: getTint__, stroke: :none, blend_mode: mode)
1634
+ end
1635
+
1636
+ # Loads all pixels to the 'pixels' array.
1637
+ #
1638
+ # @return [nil] nil
1639
+ #
1640
+ def loadPixels()
1641
+ @pixels__ = getInternal__.pixels
1642
+ end
1643
+
1644
+ # Update the image pixels with the 'pixels' array.
1645
+ #
1646
+ # @return [nil] nil
1647
+ #
1648
+ def updatePixels(&block)
1649
+ return if !block && !@pixels__
1650
+ if block
1651
+ loadPixels
1652
+ block.call pixels
1653
+ end
1654
+ getInternal__.tap do |img|
1655
+ img.pixels = @pixels__
1656
+ img.paint {} # update texture and set modifiied
1657
+ end
1658
+ @pixels__ = nil
1659
+ end
1660
+
1661
+ # An array of all pixels.
1662
+ # Call loadPixels() before accessing the array.
1663
+ #
1664
+ def pixels()
1665
+ @pixels__
1304
1666
  end
1305
1667
 
1306
1668
  # Saves screen image to file.
@@ -1310,7 +1672,7 @@ module Processing
1310
1672
  # @return [nil] nil
1311
1673
  #
1312
1674
  def save(filename)
1313
- @window__.canvas_image.save filename
1675
+ getInternal__.save filename
1314
1676
  nil
1315
1677
  end
1316
1678
 
@@ -1360,6 +1722,42 @@ module Processing
1360
1722
  nil
1361
1723
  end
1362
1724
 
1725
+ def rotateX(angle)
1726
+ assertDrawing__
1727
+ @painter__.rotate toDegrees__(angle), 1, 0, 0
1728
+ nil
1729
+ end
1730
+
1731
+ def rotateY(angle)
1732
+ assertDrawing__
1733
+ @painter__.rotate toDegrees__(angle), 0, 1, 0
1734
+ nil
1735
+ end
1736
+
1737
+ def rotateZ(angle)
1738
+ assertDrawing__
1739
+ @painter__.rotate toDegrees__(angle), 0, 0, 1
1740
+ nil
1741
+ end
1742
+
1743
+ def shearX(angle)
1744
+ t = Math.tan toRadians__(angle)
1745
+ @painter__.matrix *= Rays::Matrix.new(
1746
+ 1, t, 0, 0,
1747
+ 0, 1, 0, 0,
1748
+ 0, 0, 1, 0,
1749
+ 0, 0, 0, 1)
1750
+ end
1751
+
1752
+ def shearY(angle)
1753
+ t = Math.tan toRadians__(angle)
1754
+ @painter__.matrix *= Rays::Matrix.new(
1755
+ 1, 0, 0, 0,
1756
+ t, 1, 0, 0,
1757
+ 0, 0, 1, 0,
1758
+ 0, 0, 0, 1)
1759
+ end
1760
+
1363
1761
  # Pushes the current transformation matrix to stack.
1364
1762
  #
1365
1763
  # @return [Object] result of the expression at the end of the block
@@ -1383,6 +1781,51 @@ module Processing
1383
1781
  nil
1384
1782
  end
1385
1783
 
1784
+ # Reset current transformation matrix with 2x3, or 4x4 matrix.
1785
+ #
1786
+ # @overload applyMatrix(array)
1787
+ # @overload applyMatrix(a, b, c, d, e, f)
1788
+ # @overload applyMatrix(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p)
1789
+ #
1790
+ # @param array [Array] 6 or 16 numbers which define the matrix
1791
+ # @param a [Numeric] number which defines the matrix
1792
+ # @param b [Numeric] number which defines the matrix
1793
+ # @param c [Numeric] number which defines the matrix
1794
+ # @param d [Numeric] number which defines the matrix
1795
+ # @param e [Numeric] number which defines the matrix
1796
+ # @param f [Numeric] number which defines the matrix
1797
+ # @param g [Numeric] number which defines the matrix
1798
+ # @param h [Numeric] number which defines the matrix
1799
+ # @param i [Numeric] number which defines the matrix
1800
+ # @param j [Numeric] number which defines the matrix
1801
+ # @param k [Numeric] number which defines the matrix
1802
+ # @param l [Numeric] number which defines the matrix
1803
+ # @param m [Numeric] number which defines the matrix
1804
+ # @param n [Numeric] number which defines the matrix
1805
+ # @param o [Numeric] number which defines the matrix
1806
+ # @param p [Numeric] number which defines the matrix
1807
+ #
1808
+ # @return [nil] nil
1809
+ #
1810
+ def applyMatrix(*args)
1811
+ assertDrawing__
1812
+ args = args.first if args.first.kind_of?(Array)
1813
+ if args.size == 6
1814
+ a, b, c, d, e, f = args
1815
+ args = [
1816
+ a, b, 0, 0,
1817
+ c, d, 0, 0,
1818
+ 0, 0, 1, 0,
1819
+ e, f, 0, 1
1820
+ ]
1821
+ end
1822
+ raise ArgumentError unless args.size == 16
1823
+ m = Rays::Matrix.new(*args)
1824
+ m.transpose! if @p5jsMode__
1825
+ @painter__.matrix *= m
1826
+ nil
1827
+ end
1828
+
1386
1829
  # Reset current transformation matrix with identity matrix.
1387
1830
  #
1388
1831
  # @return [nil] nil
@@ -1393,6 +1836,17 @@ module Processing
1393
1836
  nil
1394
1837
  end
1395
1838
 
1839
+ # Prints matrix elements to console.
1840
+ #
1841
+ # @return [nil] nil
1842
+ #
1843
+ def printMatrix()
1844
+ m = @painter__.matrix
1845
+ m.transpose! if @p5jsMode__
1846
+ print "%f %f %f %f\n%f %f %f %f\n%f %f %f %f\n%f %f %f %f\n" % m.to_a
1847
+ nil
1848
+ end
1849
+
1396
1850
  # Save current style values to the style stack.
1397
1851
  #
1398
1852
  # @return [Object] result of the expression at the end of the block
@@ -1405,19 +1859,34 @@ module Processing
1405
1859
  @painter__.stroke_width,
1406
1860
  @painter__.stroke_cap,
1407
1861
  @painter__.stroke_join,
1862
+ @painter__.miter_limit,
1863
+ @painter__.line_height!,
1408
1864
  @painter__.clip,
1409
1865
  @painter__.blend_mode,
1410
1866
  @painter__.font,
1867
+ @painter__.texture,
1868
+ @painter__.texcoord_mode,
1869
+ @painter__.texcoord_wrap,
1411
1870
  @painter__.shader,
1871
+ @colorMode__,
1412
1872
  @hsbColor__,
1413
1873
  @colorMaxes__,
1414
- @angleScale__,
1874
+ @angleMode__,
1875
+ @toRad__,
1876
+ @toDeg__,
1877
+ @fromRad__,
1878
+ @fromDeg__,
1415
1879
  @rectMode__,
1416
1880
  @ellipseMode__,
1417
1881
  @imageMode__,
1418
1882
  @shapeMode__,
1883
+ @blendMode__,
1884
+ @curveDetail__,
1885
+ @curveTightness__,
1886
+ @bezierDetail__,
1419
1887
  @textAlignH__,
1420
1888
  @textAlignV__,
1889
+ @textFont__,
1421
1890
  @tint__,
1422
1891
  ]
1423
1892
  block.call if block
@@ -1437,20 +1906,36 @@ module Processing
1437
1906
  @painter__.stroke_width,
1438
1907
  @painter__.stroke_cap,
1439
1908
  @painter__.stroke_join,
1909
+ @painter__.miter_limit,
1910
+ @painter__.line_height,
1440
1911
  @painter__.clip,
1441
1912
  @painter__.blend_mode,
1442
1913
  @painter__.font,
1914
+ @painter__.texture,
1915
+ @painter__.texcoord_mode,
1916
+ @painter__.texcoord_wrap,
1443
1917
  @painter__.shader,
1918
+ @colorMode__,
1444
1919
  @hsbColor__,
1445
1920
  @colorMaxes__,
1446
- @angleScale__,
1921
+ @angleMode__,
1922
+ @toRad__,
1923
+ @toDeg__,
1924
+ @fromRad__,
1925
+ @fromDeg__,
1447
1926
  @rectMode__,
1448
1927
  @ellipseMode__,
1449
1928
  @imageMode__,
1450
1929
  @shapeMode__,
1930
+ @blendMode__,
1931
+ @curveDetail__,
1932
+ @curveTightness__,
1933
+ @bezierDetail__,
1451
1934
  @textAlignH__,
1452
1935
  @textAlignV__,
1936
+ @textFont__,
1453
1937
  @tint__ = @styleStack__.pop
1938
+ @textFont__.setSize__ @painter__.font.size
1454
1939
  nil
1455
1940
  end
1456
1941
 
@@ -1481,10 +1966,10 @@ module Processing
1481
1966
  end
1482
1967
 
1483
1968
  # @private
1484
- def drawImage__(painter, *args, **states)
1969
+ def drawImage__(painter, *args, image__: getInternal__, **states)
1485
1970
  shader = painter.shader || @filter__&.getInternal__
1486
1971
  painter.push shader: shader, **states do |_|
1487
- painter.image getInternal__, *args
1972
+ painter.image image__, *args
1488
1973
  end
1489
1974
  end
1490
1975
 
@@ -1827,6 +2312,98 @@ module Processing
1827
2312
  Math.atan2 y, x
1828
2313
  end
1829
2314
 
2315
+ # Evaluates the curve at point t for points a, b, c, d.
2316
+ #
2317
+ # @param a [Numeric] coordinate of first control point
2318
+ # @param b [Numeric] coordinate of first point on the curve
2319
+ # @param c [Numeric] coordinate of second point on the curve
2320
+ # @param d [Numeric] coordinate of second control point
2321
+ # @param t [Numeric] value between 0.0 and 1.0
2322
+ #
2323
+ # @return [Numeric] interpolated value
2324
+ #
2325
+ # @see https://processing.org/reference/curvePoint_.html
2326
+ # @see https://p5js.org/reference/#/p5/curvePoint
2327
+ #
2328
+ def curvePoint(a, b, c, d, t)
2329
+ s = @curveTightness__
2330
+ t3 = t * t * t
2331
+ t2 = t * t
2332
+ f1 = ( s - 1.0) / 2.0 * t3 + ( 1.0 - s) * t2 + (s - 1.0) / 2.0 * t
2333
+ f2 = ( s + 3.0) / 2.0 * t3 + (-5.0 - s) / 2.0 * t2 + 1.0
2334
+ f3 = (-3.0 - s) / 2.0 * t3 + ( s + 2.0) * t2 + (1.0 - s) / 2.0 * t
2335
+ f4 = ( 1.0 - s) / 2.0 * t3 + ( s - 1.0) / 2.0 * t2
2336
+ a * f1 + b * f2 + c * f3 + d * f4
2337
+ end
2338
+
2339
+ # Calculates the tangent of a point on a curve.
2340
+ #
2341
+ # @param a [Numeric] coordinate of first control point
2342
+ # @param b [Numeric] coordinate of first point on the curve
2343
+ # @param c [Numeric] coordinate of second point on the curve
2344
+ # @param d [Numeric] coordinate of second control point
2345
+ # @param t [Numeric] value between 0.0 and 1.0
2346
+ #
2347
+ # @return [Numeric] tangent value
2348
+ #
2349
+ # @see https://processing.org/reference/curveTangent_.html
2350
+ # @see https://p5js.org/reference/#/p5/curveTangent
2351
+ #
2352
+ def curveTangent(a, b, c, d, t)
2353
+ s = @curveTightness__
2354
+ tt3 = t * t * 3.0
2355
+ t2 = t * 2.0
2356
+ f1 = ( s - 1.0) / 2.0 * tt3 + ( 1.0 - s) * t2 + (s - 1.0) / 2.0
2357
+ f2 = ( s + 3.0) / 2.0 * tt3 + (-5.0 - s) / 2.0 * t2
2358
+ f3 = (-3.0 - s) / 2.0 * tt3 + ( s + 2.0) * t2 + (1.0 - s) / 2.0
2359
+ f4 = ( 1.0 - s) / 2.0 * tt3 + ( s - 1.0) / 2.0 * t2
2360
+ a * f1 + b * f2 + c * f3 + d * f4
2361
+ end
2362
+
2363
+ # Evaluates the Bezier at point t for points a, b, c, d.
2364
+ #
2365
+ # @param a [Numeric] coordinate of first point on the curve
2366
+ # @param b [Numeric] coordinate of first control point
2367
+ # @param c [Numeric] coordinate of second control point
2368
+ # @param d [Numeric] coordinate of second point on the curve
2369
+ # @param t [Numeric] value between 0.0 and 1.0
2370
+ #
2371
+ # @return [Numeric] interpolated value
2372
+ #
2373
+ # @see https://processing.org/reference/bezierPoint_.html
2374
+ # @see https://p5js.org/reference/#/p5/bezierPoint
2375
+ #
2376
+ def bezierPoint(a, b, c, d, t)
2377
+ tt = 1.0 - t
2378
+ tt ** 3.0 * a +
2379
+ tt ** 2.0 * b * 3.0 * t +
2380
+ t ** 2.0 * c * 3.0 * tt +
2381
+ t ** 3.0 * d
2382
+ end
2383
+
2384
+ # Calculates the tangent of a point on a Bezier curve.
2385
+ #
2386
+ # @param a [Numeric] coordinate of first point on the curve
2387
+ # @param b [Numeric] coordinate of first control point
2388
+ # @param c [Numeric] coordinate of second control point
2389
+ # @param d [Numeric] coordinate of second point on the curve
2390
+ # @param t [Numeric] value between 0.0 and 1.0
2391
+ #
2392
+ # @return [Numeric] tangent value
2393
+ #
2394
+ # @see https://processing.org/reference/bezierTangent_.html
2395
+ # @see https://p5js.org/reference/#/p5/bezierTangent
2396
+ #
2397
+ def bezierTangent(a, b, c, d, t)
2398
+ tt = 1.0 - t
2399
+ 3.0 * d * t ** 2.0 -
2400
+ 3.0 * c * t ** 2.0 +
2401
+ 6.0 * c * tt * t -
2402
+ 6.0 * b * tt * t +
2403
+ 3.0 * b * tt ** 2.0 -
2404
+ 3.0 * a * tt ** 2.0
2405
+ end
2406
+
1830
2407
  # Returns the perlin noise value.
1831
2408
  #
1832
2409
  # @overload noise(x)
@@ -1839,13 +2416,52 @@ module Processing
1839
2416
  #
1840
2417
  # @return [Numeric] noise value (0.0..1.0)
1841
2418
  #
2419
+ # @see https://processing.org/reference/noise_.html
2420
+ # @see https://p5js.org/reference/#/p5/noise
2421
+ #
1842
2422
  def noise(x, y = 0, z = 0)
1843
- Rays.perlin(x, y, z) / 2.0 + 0.5
2423
+ seed, falloff = @noiseSeed__, @noiseFallOff__
2424
+ amp = 0.5
2425
+ @noiseOctaves__.times.reduce(0) do |sum|
2426
+ value = (Rays.perlin(x, y, z, seed) / 2.0 + 0.5) * amp
2427
+ x *= 2
2428
+ y *= 2
2429
+ z *= 2
2430
+ amp *= falloff
2431
+ sum + value
2432
+ end
2433
+ end
2434
+
2435
+ # Sets the seed value for noise()
2436
+ #
2437
+ # @param seed [Numeric] seed value
2438
+ #
2439
+ # @return [nil] nil
2440
+ #
2441
+ # @see https://processing.org/reference/noiseSeed_.html
2442
+ # @see https://p5js.org/reference/#/p5/noiseSeed
2443
+ #
2444
+ def noiseSeed(seed)
2445
+ @noiseSeed__ = Random.new(seed).rand 0.0..1.0
2446
+ end
2447
+
2448
+ # Adjusts the character and level of detail produced by the Perlin noise function.
2449
+ #
2450
+ # @param lod [Numeric] number of octaves to be used by the noise
2451
+ # @param falloff [Numeric] falloff factor for each octave
2452
+ #
2453
+ # @return [nil] nil
2454
+ #
2455
+ # @see https://processing.org/reference/noiseDetail_.html
2456
+ # @see https://p5js.org/reference/#/p5/noiseDetail
2457
+ #
2458
+ def noiseDetail(lod, falloff = nil)
2459
+ @noiseOctaves__ = lod if lod && lod > 0
2460
+ @noiseFallOff__ = falloff if falloff && falloff > 0
1844
2461
  end
1845
2462
 
1846
2463
  # Returns a random number in range low...high
1847
2464
  #
1848
- # @overload random()
1849
2465
  # @overload random(high)
1850
2466
  # @overload random(low, high)
1851
2467
  # @overload random(choices)
@@ -1856,13 +2472,63 @@ module Processing
1856
2472
  #
1857
2473
  # @return [Float] random number
1858
2474
  #
2475
+ # @see https://processing.org/reference/random_.html
2476
+ # @see https://p5js.org/reference/#/p5/random
2477
+ #
1859
2478
  def random(*args)
1860
- return args.first.sample if args.first.kind_of? Array
1861
- high, low = args.reverse
1862
- rand (low || 0).to_f...(high || 1).to_f
2479
+ if args.first.kind_of? Array
2480
+ a = args.first
2481
+ a.empty? ? nil : a[@random__.rand a.size]
2482
+ else
2483
+ high, low = args.reverse
2484
+ @random__.rand (low || 0).to_f...(high || 1).to_f
2485
+ end
2486
+ end
2487
+
2488
+ # Sets the seed value for random()
2489
+ #
2490
+ # @param seed [Numeric] seed value
2491
+ #
2492
+ # @return [nil] nil
2493
+ #
2494
+ # @see https://processing.org/reference/randomSeed_.html
2495
+ # @see https://p5js.org/reference/#/p5/randomSeed
2496
+ #
2497
+ def randomSeed(seed)
2498
+ @random__ = Random.new seed
2499
+ @nextGaussian__ = nil
1863
2500
  end
1864
2501
 
1865
- # Creates a new vector.
2502
+ # Returns a random number fitting a Gaussian, or normal, distribution.
2503
+ #
2504
+ # @param mean [Numeric] mean
2505
+ # @param sd [Numeric] standard deviation
2506
+ #
2507
+ # @return [Float] random number
2508
+ #
2509
+ # @see https://processing.org/reference/randomGaussian_.html
2510
+ # @see https://p5js.org/reference/#/p5/randomGaussian
2511
+ #
2512
+ def randomGaussian(mean = 0, sd = 1)
2513
+ value =
2514
+ if @nextGaussian__
2515
+ x, @nextGaussian__ = @nextGaussian__, nil
2516
+ x
2517
+ else
2518
+ a, b, w = 0, 0, 1
2519
+ until w < 1
2520
+ a = random(2) - 1
2521
+ b = random(2) - 1
2522
+ w = a ** 2 + b ** 2
2523
+ end
2524
+ w = Math.sqrt(-2 * Math.log(w) / w)
2525
+ @randomGaussian__ = a * w
2526
+ b * w
2527
+ end
2528
+ value * sd + mean
2529
+ end
2530
+
2531
+ # Creates a new vector object.
1866
2532
  #
1867
2533
  # @overload createVector()
1868
2534
  # @overload createVector(x, y)
@@ -1878,7 +2544,17 @@ module Processing
1878
2544
  Vector.new(*args, context: self)
1879
2545
  end
1880
2546
 
1881
- # Creates a new image.
2547
+ # Creates a new font object.
2548
+ #
2549
+ # @param name [String] font name
2550
+ # @param size [Numeric] font size (max 256)
2551
+ #
2552
+ def createFont(name, size)
2553
+ size = FONT_SIZE_MAX__ if size && size > FONT_SIZE_MAX__
2554
+ Font.new Rays::Font.new(name, size || FONT_SIZE_DEFAULT__)
2555
+ end
2556
+
2557
+ # Creates a new image object.
1882
2558
  #
1883
2559
  # @overload createImage(w, h)
1884
2560
  # @overload createImage(w, h, format)
@@ -1895,7 +2571,7 @@ module Processing
1895
2571
  Image.new Rays::Image.new(w, h, colorspace).paint {background 0, 0}
1896
2572
  end
1897
2573
 
1898
- # Creates a new shape.
2574
+ # Creates a new shape object.
1899
2575
  #
1900
2576
  # @overload createShape()
1901
2577
  # @overload createShape(LINE, x1, y1, x2, y2)
@@ -1924,7 +2600,7 @@ module Processing
1924
2600
 
1925
2601
  # @private
1926
2602
  private def createLineShape__(x1, y1, x2, y2)
1927
- Shape.new Rays::Polygon.lines(x1, y1, x2, y2), context: self
2603
+ Shape.new Rays::Polygon.line(x1, y1, x2, y2), context: self
1928
2604
  end
1929
2605
 
1930
2606
  # @private
@@ -2014,6 +2690,23 @@ module Processing
2014
2690
  Capture.new(*args)
2015
2691
  end
2016
2692
 
2693
+ # Loads font from file.
2694
+ #
2695
+ # @param filename [String] file name to load font file
2696
+ #
2697
+ # @return [Font] loaded font object
2698
+ #
2699
+ # @see https://processing.org/reference/loadFont_.html
2700
+ # @see https://p5js.org/reference/#/p5/loadFont
2701
+ #
2702
+ def loadFont(filename)
2703
+ ext = File.extname filename
2704
+ raise "unsupported font type -- '#{ext}'" unless ext =~ /^\.?(ttf|otf)$/i
2705
+
2706
+ filename = httpGet__ filename, ext if filename =~ %r|^https?://|
2707
+ Font.new Rays::Font.load filename
2708
+ end
2709
+
2017
2710
  # Loads image.
2018
2711
  #
2019
2712
  # @param filename [String] file name to load image
@@ -2021,11 +2714,39 @@ module Processing
2021
2714
  #
2022
2715
  # @return [Image] loaded image object
2023
2716
  #
2717
+ # @see https://processing.org/reference/loadImage_.html
2718
+ # @see https://p5js.org/reference/#/p5/loadImage
2719
+ #
2024
2720
  def loadImage(filename, extension = nil)
2025
- filename = getImage__ filename, extension if filename =~ %r|^https?://|
2721
+ ext = extension || File.extname(filename)
2722
+ raise "unsupported image type -- '#{ext}'" unless ext =~ /^\.?(png|jpg|gif)$/i
2723
+
2724
+ filename = httpGet__ filename, ext if filename =~ %r|^https?://|
2026
2725
  Image.new Rays::Image.load filename
2027
2726
  end
2028
2727
 
2728
+ # Loads image on a new thread.
2729
+ # When the image is loading, its width and height will be 0.
2730
+ # If an error occurs while loading the image, its width and height wil be -1.
2731
+ #
2732
+ # @param filename [String] file name to load image
2733
+ # @param extension [String] type of image to load (ex. 'png')
2734
+ #
2735
+ # @return [Image] loading image object
2736
+ #
2737
+ # @see https://processing.org/reference/requestImage_.html
2738
+ #
2739
+ def requestImage(filename, extension = nil)
2740
+ img = Image.new nil
2741
+ Thread.new filename, extension do |fn, ext|
2742
+ loaded = loadImage(fn, ext) or raise
2743
+ img.setInternal__ loaded.getInternal__
2744
+ rescue
2745
+ img.setInternal__ nil, true
2746
+ end
2747
+ img
2748
+ end
2749
+
2029
2750
  # Loads shader file.
2030
2751
  #
2031
2752
  # @overload loadShader(fragPath)
@@ -2041,24 +2762,16 @@ module Processing
2041
2762
  end
2042
2763
 
2043
2764
  # @private
2044
- private def getImage__(uri, ext)
2045
- ext ||= File.extname uri
2046
- raise "unsupported image type -- #{ext}" unless ext =~ /^\.?(png|jpg|gif)$/i
2047
-
2765
+ private def httpGet__(uri, ext)
2048
2766
  tmpdir = tmpdir__
2049
2767
  path = tmpdir + Digest::SHA1.hexdigest(uri)
2050
2768
  path = path.sub_ext ext
2051
2769
 
2052
2770
  unless path.file?
2053
- URI.open uri do |input|
2054
- input.set_encoding nil# disable default_internal
2771
+ Net::HTTP.get_response URI.parse(uri) do |res|
2772
+ res.value # raise an error unless successful
2055
2773
  tmpdir.mkdir unless tmpdir.directory?
2056
- path.open('w') do |output|
2057
- output.set_encoding Encoding::ASCII_8BIT
2058
- while buf = input.read(2 ** 16)
2059
- output.write buf
2060
- end
2061
- end
2774
+ path.open('wb') {|file| res.read_body {|body| file.write body}}
2062
2775
  end
2063
2776
  end
2064
2777
  path.to_s