rubysketch 0.3.21 → 0.3.22

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.
@@ -555,7 +555,7 @@ module RubySketch
555
555
  end
556
556
 
557
557
  # @private
558
- protected def getInternal__()
558
+ def getInternal__()
559
559
  @point
560
560
  end
561
561
 
@@ -592,6 +592,20 @@ module RubySketch
592
592
  @image.height
593
593
  end
594
594
 
595
+ # Applies an image filter.
596
+ #
597
+ # overload filter(shader)
598
+ # overload filter(type)
599
+ # overload filter(type, param)
600
+ #
601
+ # @param shader [Shader] a fragment shader to apply
602
+ # @param type [THRESHOLD, GRAY, INVERT, BLUR] filter type
603
+ # @param param [Numeric] a parameter for each filter
604
+ #
605
+ def filter(*args)
606
+ @filter = Shader.createFilter__(*args)
607
+ end
608
+
595
609
  # Resizes image.
596
610
  #
597
611
  # @param width [Numeric] width for resized image
@@ -633,14 +647,14 @@ module RubySketch
633
647
  # @overload blend(img, sx, sy, sw, sh, dx, dy, dw, dh, mode)
634
648
  #
635
649
  # @param img [Image] image for blend source
636
- # @param sx [Numrtic] x position of source region
637
- # @param sy [Numrtic] y position of source region
638
- # @param sw [Numrtic] width of source region
639
- # @param sh [Numrtic] height of source region
640
- # @param dx [Numrtic] x position of destination region
641
- # @param dy [Numrtic] y position of destination region
642
- # @param dw [Numrtic] width of destination region
643
- # @param dh [Numrtic] height of destination region
650
+ # @param sx [Numeric] x position of source region
651
+ # @param sy [Numeric] y position of source region
652
+ # @param sw [Numeric] width of source region
653
+ # @param sh [Numeric] height of source region
654
+ # @param dx [Numeric] x position of destination region
655
+ # @param dy [Numeric] y position of destination region
656
+ # @param dw [Numeric] width of destination region
657
+ # @param dh [Numeric] height of destination region
644
658
  # @param mode [BLEND, ADD, SUBTRACT, LIGHTEST, DARKEST, EXCLUSION, MULTIPLY, SCREEN, REPLACE] blend mode
645
659
  #
646
660
  # @return [nil] nil
@@ -648,11 +662,9 @@ module RubySketch
648
662
  def blend(img = nil, sx, sy, sw, sh, dx, dy, dw, dh, mode)
649
663
  img ||= self
650
664
  @image.paint do |painter|
651
- current = painter.blend_mode
652
- painter.blend_mode = mode
653
- painter.image img.getInternal__, sx, sy, sw, sh, dx, dy, dw, dh
654
- painter.blend_mode = current
665
+ img.drawImage__ painter, sx, sy, sw, sh, dx, dy, dw, dh, blend_mode: mode
655
666
  end
667
+ nil
656
668
  end
657
669
 
658
670
  # Saves image to file.
@@ -668,6 +680,14 @@ module RubySketch
668
680
  @image
669
681
  end
670
682
 
683
+ # @private
684
+ def drawImage__(painter, *args, **states)
685
+ shader = painter.shader || @filter&.getInternal__
686
+ painter.push shader: shader, **states do |_|
687
+ painter.image getInternal__, *args
688
+ end
689
+ end
690
+
671
691
  end# Image
672
692
 
673
693
 
@@ -753,6 +773,159 @@ module RubySketch
753
773
  end# Touch
754
774
 
755
775
 
776
+ # Shader object.
777
+ #
778
+ class Shader
779
+
780
+ # Initialize shader object.
781
+ #
782
+ # @param vertSrc [String] vertex shader source
783
+ # @param fragSrc [String] fragment shader source
784
+ #
785
+ def initialize(vertSrc, fragSrc)
786
+ @shader = Rays::Shader.new fragSrc, vertSrc, ENV__
787
+ end
788
+
789
+ # Sets uniform variables.
790
+ #
791
+ # @overload set(name, a)
792
+ # @overload set(name, a, b)
793
+ # @overload set(name, a, b, c)
794
+ # @overload set(name, a, b, c, d)
795
+ # @overload set(name, nums)
796
+ # @overload set(name, vec)
797
+ # @overload set(name, vec, ncoords)
798
+ # @overload set(name, tex)
799
+ #
800
+ # @param name [String] uniform variable name
801
+ # @param a [Numeric] int or float value
802
+ # @param b [Numeric] int or float value
803
+ # @param c [Numeric] int or float value
804
+ # @param d [Numeric] int or float value
805
+ # @param nums [Array] int or float array
806
+ # @param vec [Vector] vector
807
+ # @param ncoords [Integer] number of coordinates, max 4
808
+ # @param tex [Image] texture image
809
+ #
810
+ def setUniform(name, *args)
811
+ arg = args.first
812
+ case
813
+ when Array === arg
814
+ @shader.uniform name, *arg
815
+ when Numeric === arg
816
+ @shader.uniform name, *args
817
+ when Vector === arg
818
+ vec, ncoords = args
819
+ @shader.uniform name, vec.getInternal__.to_a(ncoords || 3)
820
+ when arg.respond_to?(:getInternal__)
821
+ @shader.uniform name, arg.getInternal__
822
+ else
823
+ raise ArgumentError
824
+ end
825
+ end
826
+
827
+ alias set setUniform
828
+
829
+ # @private
830
+ def getInternal__()
831
+ @shader
832
+ end
833
+
834
+ # @private
835
+ ENV__ = {
836
+ attribute_position: [:vertex, :position],
837
+ attribute_texcoord: :texCoord,
838
+ attribute_color: :color,
839
+ varying_position: :vertPosition,
840
+ varying_texcoord: :vertTexCoord,
841
+ varying_color: :vertColor,
842
+ uniform_position_matrix: [:transform, :transformMatrix],
843
+ uniform_texcoord_matrix: :texMatrix,
844
+ uniform_texcoord_min: :texMin,
845
+ uniform_texcoord_max: :texMax,
846
+ uniform_texcoord_offset: :texOffset,
847
+ uniform_texture: [:texMap, :texture]
848
+ }.freeze
849
+
850
+ # @private
851
+ def self.createFilter__(*args)
852
+ case arg = args.shift
853
+ when Shader
854
+ arg
855
+ when :threshold
856
+ self.new(nil, <<~END).tap {|sh| sh.set :threshold, (args.shift || 0.5)}
857
+ uniform float threshold;
858
+ uniform sampler2D texMap;
859
+ varying vec4 vertTexCoord;
860
+ varying vec4 vertColor;
861
+ void main() {
862
+ vec4 col = texture2D(texMap, vertTexCoord.xy) * vertColor;
863
+ float gray = col.r * 0.3 + col.g * 0.59 + col.b * 0.11;
864
+ gl_FragColor = vec4(vec3(gray > threshold ? 1.0 : 0.0), 1.0);
865
+ }
866
+ END
867
+ when :gray
868
+ self.new nil, <<~END
869
+ uniform sampler2D texMap;
870
+ varying vec4 vertTexCoord;
871
+ varying vec4 vertColor;
872
+ void main() {
873
+ vec4 col = texture2D(texMap, vertTexCoord.xy);
874
+ float gray = col.r * 0.3 + col.g * 0.59 + col.b * 0.11;
875
+ gl_FragColor = vec4(vec3(gray), 1.0) * vertColor;
876
+ }
877
+ END
878
+ when :invert
879
+ self.new nil, <<~END
880
+ uniform sampler2D texMap;
881
+ varying vec4 vertTexCoord;
882
+ varying vec4 vertColor;
883
+ void main() {
884
+ vec4 col = texture2D(texMap, vertTexCoord.xy);
885
+ gl_FragColor = vec4(vec3(1.0 - col.rgb), 1.0) * vertColor;
886
+ }
887
+ END
888
+ when :blur
889
+ self.new(nil, <<~END).tap {|sh| sh.set :radius, (args.shift || 1).to_f}
890
+ #define PI 3.1415926538
891
+ uniform float radius;
892
+ uniform sampler2D texMap;
893
+ uniform vec3 texMin;
894
+ uniform vec3 texMax;
895
+ uniform vec3 texOffset;
896
+ varying vec4 vertTexCoord;
897
+ varying vec4 vertColor;
898
+ float gaussian(vec2 pos, float sigma) {
899
+ float s2 = sigma * sigma;
900
+ return 1.0 / (2.0 * PI * s2) * exp(-(dot(pos, pos) / (2.0 * s2)));
901
+ }
902
+ void main() {
903
+ float sigma = radius * 0.5;
904
+ vec3 color = vec3(0.0);
905
+ float total_weight = 0.0;
906
+ for (float y = -radius; y < radius; y += 1.0)
907
+ for (float x = -radius; x < radius; x += 1.0) {
908
+ vec2 offset = vec2(x, y);
909
+ float weight = gaussian(offset, sigma);
910
+ vec2 texcoord = vertTexCoord.xy + offset * texOffset.xy;
911
+ if (
912
+ texcoord.x < texMin.x || texMax.x < texcoord.x ||
913
+ texcoord.y < texMin.y || texMax.y < texcoord.y
914
+ ) continue;
915
+ color += texture2D(texMap, texcoord).rgb * weight;
916
+ total_weight += weight;
917
+ }
918
+ gl_FragColor = vec4(color / total_weight, 1.0) * vertColor;
919
+ }
920
+ END
921
+ else
922
+ nil
923
+ end
924
+ end
925
+
926
+ end# Shader
927
+
928
+
756
929
  # Camera object.
757
930
  #
758
931
  class Capture
@@ -838,14 +1011,31 @@ module RubySketch
838
1011
  @camera.image&.height || 0
839
1012
  end
840
1013
 
1014
+ # Applies an image filter.
1015
+ #
1016
+ # overload filter(shader)
1017
+ # overload filter(type)
1018
+ # overload filter(type, param)
1019
+ #
1020
+ # @param shader [Shader] a fragment shader to apply
1021
+ # @param type [THRESHOLD, GRAY, INVERT, BLUR] filter type
1022
+ # @param param [Numeric] a parameter for each filter
1023
+ #
1024
+ def filter(*args)
1025
+ @filter = Shader.createFilter__(*args)
1026
+ end
1027
+
841
1028
  # @private
842
1029
  def getInternal__()
843
- @camera.image || dummyImage__
1030
+ @camera.image || (@dummyImage ||= Rays::Image.new 1, 1)
844
1031
  end
845
1032
 
846
1033
  # @private
847
- private def dummyImage__()
848
- @dummy ||= Rays::Image.new 1, 1
1034
+ def drawImage__(painter, *args, **states)
1035
+ shader = painter.shader || @filter&.getInternal__
1036
+ painter.push shader: shader, **states do |_|
1037
+ painter.image getInternal__, *args
1038
+ end
849
1039
  end
850
1040
 
851
1041
  end# Capture
@@ -978,6 +1168,18 @@ module RubySketch
978
1168
  # Mode for textAlign().
979
1169
  BASELINE = :baseline
980
1170
 
1171
+ # Filter type for filter()
1172
+ THRESHOLD = :threshold
1173
+
1174
+ # Filter type for filter()
1175
+ GRAY = :gray
1176
+
1177
+ # Filter type for filter()
1178
+ INVERT = :invert
1179
+
1180
+ # Filter type for filter()
1181
+ BLUR = :blur
1182
+
981
1183
  # Key codes.
982
1184
  ENTER = :enter
983
1185
  SPACE = :space
@@ -1036,9 +1238,10 @@ module RubySketch
1036
1238
  @rectMode__ = nil
1037
1239
  @ellipseMode__ = nil
1038
1240
  @imageMode__ = nil
1039
- @tint__ = nil
1040
1241
  @textAlignH__ = nil
1041
1242
  @textAlignV__ = nil
1243
+ @tint__ = nil
1244
+ @filter__ = nil
1042
1245
  @matrixStack__ = []
1043
1246
  @styleStack__ = []
1044
1247
  @fontCache__ = {}
@@ -1068,6 +1271,7 @@ module RubySketch
1068
1271
 
1069
1272
  # @private
1070
1273
  def beginDraw__()
1274
+ raise "call beginDraw() before drawing" if @drawing__
1071
1275
  @matrixStack__.clear
1072
1276
  @styleStack__.clear
1073
1277
  @drawing__ = true
@@ -1075,6 +1279,7 @@ module RubySketch
1075
1279
 
1076
1280
  # @private
1077
1281
  def endDraw__()
1282
+ raise unless @drawing__
1078
1283
  @drawing__ = false
1079
1284
  end
1080
1285
 
@@ -1442,6 +1647,40 @@ module RubySketch
1442
1647
  @painter__.font = font
1443
1648
  end
1444
1649
 
1650
+ # Sets shader.
1651
+ #
1652
+ # @param shader [Shader] a shader to apply
1653
+ #
1654
+ # @return [nil] nil
1655
+ #
1656
+ def shader(shader)
1657
+ @painter__.shader shader&.getInternal__
1658
+ nil
1659
+ end
1660
+
1661
+ # Resets shader.
1662
+ #
1663
+ # @return [nil] nil
1664
+ #
1665
+ def resetShader()
1666
+ @painter__.no_shader
1667
+ nil
1668
+ end
1669
+
1670
+ # Applies an image filter to screen.
1671
+ #
1672
+ # overload filter(shader)
1673
+ # overload filter(type)
1674
+ # overload filter(type, param)
1675
+ #
1676
+ # @param shader [Shader] a shader to apply
1677
+ # @param type [THRESHOLD, GRAY, INVERT, BLUR] filter type
1678
+ # @param param [Numeric] a parameter for each filter
1679
+ #
1680
+ def filter(*args)
1681
+ @filter__ = Shader.createFilter__(*args)
1682
+ end
1683
+
1445
1684
  # Clears screen.
1446
1685
  #
1447
1686
  # @overload background(str)
@@ -1727,12 +1966,9 @@ module RubySketch
1727
1966
  #
1728
1967
  def image(img, a, b, c = nil, d = nil)
1729
1968
  assertDrawing__
1730
- i = img.getInternal__
1731
- x, y, w, h = toXYWH__ @imageMode__, a, b, c || i.width, d || i.height
1969
+ x, y, w, h = toXYWH__ @imageMode__, a, b, c || img.width, d || img.height
1732
1970
  tint = @tint__ ? toRGBA__(*@tint__) : 1
1733
- @painter__.push fill: tint, stroke: :none do |_|
1734
- @painter__.image i, x, y, w, h
1735
- end
1971
+ img.drawImage__ @painter__, x, y, w, h, fill: tint, stroke: :none
1736
1972
  nil
1737
1973
  end
1738
1974
 
@@ -1777,12 +2013,8 @@ module RubySketch
1777
2013
  #
1778
2014
  def blend(img = nil, sx, sy, sw, sh, dx, dy, dw, dh, mode)
1779
2015
  assertDrawing__
1780
- src = img&.getInternal__ || @window__.canvas_image
1781
- current = @painter__.blend_mode
1782
-
1783
- @painter__.blend_mode = mode
1784
- @painter__.image src, sx, sy, sw, sh, dx, dy, dw, dh
1785
- @painter__.blend_mode = current
2016
+ img ||= self
2017
+ img.drawImage__ @painter__, sx, sy, sw, sh, dx, dy, dw, dh, blend_mode: mode
1786
2018
  end
1787
2019
 
1788
2020
  # Saves screen image to file.
@@ -1846,11 +2078,10 @@ module RubySketch
1846
2078
  def pushMatrix(&block)
1847
2079
  assertDrawing__
1848
2080
  @matrixStack__.push @painter__.matrix
1849
- if block
1850
- block.call
1851
- popMatrix
1852
- end
2081
+ block.call if block
1853
2082
  nil
2083
+ ensure
2084
+ popMatrix if block
1854
2085
  end
1855
2086
 
1856
2087
  # Pops the current transformation matrix from stack.
@@ -1889,18 +2120,22 @@ module RubySketch
1889
2120
  @painter__.clip,
1890
2121
  @painter__.blend_mode,
1891
2122
  @painter__.font,
2123
+ @painter__.shader,
1892
2124
  @hsbColor__,
1893
2125
  @colorMaxes__,
1894
2126
  @angleScale__,
1895
2127
  @rectMode__,
1896
2128
  @ellipseMode__,
1897
- @imageMode__
2129
+ @imageMode__,
2130
+ @textAlignH__,
2131
+ @textAlignV__,
2132
+ @tint__,
2133
+ @filter__,
1898
2134
  ]
1899
- if block
1900
- block.call
1901
- popStyle
1902
- end
2135
+ block.call if block
1903
2136
  nil
2137
+ ensure
2138
+ popStyle if block
1904
2139
  end
1905
2140
 
1906
2141
  # Restore style values from the style stack.
@@ -1918,12 +2153,17 @@ module RubySketch
1918
2153
  @painter__.clip,
1919
2154
  @painter__.blend_mode,
1920
2155
  @painter__.font,
2156
+ @painter__.shader,
1921
2157
  @hsbColor__,
1922
2158
  @colorMaxes__,
1923
2159
  @angleScale__,
1924
2160
  @rectMode__,
1925
2161
  @ellipseMode__,
1926
- @imageMode__ = @styleStack__.pop
2162
+ @imageMode__,
2163
+ @textAlignH__,
2164
+ @textAlignV__,
2165
+ @tint__,
2166
+ @filter__ = @styleStack__.pop
1927
2167
  nil
1928
2168
  end
1929
2169
 
@@ -1934,10 +2174,9 @@ module RubySketch
1934
2174
  def push(&block)
1935
2175
  pushMatrix
1936
2176
  pushStyle
1937
- if block
1938
- block.call
1939
- pop
1940
- end
2177
+ block.call if block
2178
+ ensure
2179
+ pop if block
1941
2180
  end
1942
2181
 
1943
2182
  # Restore styles and transformations from stack.
@@ -1954,951 +2193,993 @@ module RubySketch
1954
2193
  @image__
1955
2194
  end
1956
2195
 
2196
+ # @private
2197
+ def drawImage__(painter, *args, **states)
2198
+ shader = painter.shader || @filter__&.getInternal__
2199
+ painter.push shader: shader, **states do |_|
2200
+ painter.image getInternal__, *args
2201
+ end
2202
+ end
2203
+
1957
2204
  # @private
1958
2205
  private def assertDrawing__()
1959
2206
  raise "call beginDraw() before drawing" unless @drawing__
1960
2207
  end
1961
2208
 
1962
- end# GraphicsContext
1963
-
1964
-
1965
- # Draws graphics into an offscreen buffer
1966
- #
1967
- class Graphics
1968
-
1969
- include GraphicsContext
2209
+ #
2210
+ # Utilities
2211
+ #
1970
2212
 
1971
- # Initialize graphics object.
2213
+ # Returns the absolute number of the value.
1972
2214
  #
1973
- def initialize(width, height)
1974
- image = Rays::Image.new width, height
1975
- init__ image, image.painter
2215
+ # @param value [Numeric] number
2216
+ #
2217
+ # @return [Numeric] absolute number
2218
+ #
2219
+ def abs(value)
2220
+ value.abs
1976
2221
  end
1977
2222
 
1978
- # Start drawing.
2223
+ # Returns the closest integer number greater than or equal to the value.
1979
2224
  #
1980
- def beginDraw(&block)
1981
- @painter__.__send__ :begin_paint
1982
- beginDraw__
1983
- push
1984
- if block
1985
- block.call
1986
- endDraw
1987
- end
2225
+ # @param value [Numeric] number
2226
+ #
2227
+ # @return [Numeric] rounded up number
2228
+ #
2229
+ def ceil(value)
2230
+ value.ceil
1988
2231
  end
1989
2232
 
1990
- # End drawing.
2233
+ # Returns the closest integer number less than or equal to the value.
1991
2234
  #
1992
- def endDraw()
1993
- pop
1994
- endDraw__
1995
- @painter__.__send__ :end_paint
2235
+ # @param value [Numeric] number
2236
+ #
2237
+ # @return [Numeric] rounded down number
2238
+ #
2239
+ def floor(value)
2240
+ value.floor
1996
2241
  end
1997
2242
 
1998
- end# Graphics
1999
-
2243
+ # Returns the closest integer number.
2244
+ #
2245
+ # @param value [Numeric] number
2246
+ #
2247
+ # @return [Numeric] rounded number
2248
+ #
2249
+ def round(value)
2250
+ value.round
2251
+ end
2000
2252
 
2001
- # Processing context
2002
- #
2003
- class Context
2253
+ # Returns the natural logarithm (the base-e logarithm) of a number.
2254
+ #
2255
+ # @param value [Numeric] number (> 0.0)
2256
+ #
2257
+ # @return [Numeric] result number
2258
+ #
2259
+ def log(n)
2260
+ Math.log n
2261
+ end
2004
2262
 
2005
- include GraphicsContext
2263
+ # Returns Euler's number e raised to the power of value.
2264
+ #
2265
+ # @param value [Numeric] number
2266
+ #
2267
+ # @return [Numeric] result number
2268
+ #
2269
+ def exp(n)
2270
+ Math.exp n
2271
+ end
2006
2272
 
2007
- Vector = Processing::Vector
2008
- Capture = Processing::Capture
2009
- Graphics = Processing::Graphics
2273
+ # Returns value raised to the power of exponent.
2274
+ #
2275
+ # @param value [Numeric] base number
2276
+ # @param exponent [Numeric] exponent number
2277
+ #
2278
+ # @return [Numeric] value ** exponent
2279
+ #
2280
+ def pow(value, exponent)
2281
+ value ** exponent
2282
+ end
2010
2283
 
2011
- # @private
2012
- @@context__ = nil
2284
+ # Returns squared value.
2285
+ #
2286
+ # @param value [Numeric] number
2287
+ #
2288
+ # @return [Numeric] squared value
2289
+ #
2290
+ def sq(value)
2291
+ value * value
2292
+ end
2013
2293
 
2014
- # @private
2015
- def self.context__()
2016
- @@context__
2294
+ # Returns squared value.
2295
+ #
2296
+ # @param value [Numeric] number
2297
+ #
2298
+ # @return [Numeric] squared value
2299
+ #
2300
+ def sqrt(value)
2301
+ Math.sqrt value
2017
2302
  end
2018
2303
 
2019
- # @private
2020
- def initialize(window)
2021
- @@context__ = self
2304
+ # Returns the magnitude (or length) of a vector.
2305
+ #
2306
+ # @overload mag(x, y)
2307
+ # @overload mag(x, y, z)
2308
+ #
2309
+ # @param x [Numeric] x of point
2310
+ # @param y [Numeric] y of point
2311
+ # @param z [Numeric] z of point
2312
+ #
2313
+ # @return [Numeric] magnitude
2314
+ #
2315
+ def mag(*args)
2316
+ x, y, z = *args
2317
+ case args.size
2318
+ when 2 then Math.sqrt x * x + y * y
2319
+ when 3 then Math.sqrt x * x + y * y + z * z
2320
+ else raise ArgumentError
2321
+ end
2322
+ end
2022
2323
 
2023
- tmpdir__.tap {|dir| FileUtils.rm_r dir.to_s if dir.directory?}
2324
+ # Returns distance between 2 points.
2325
+ #
2326
+ # @overload dist(x1, y1, x2, y2)
2327
+ # @overload dist(x1, y1, z1, x2, y2, z2)
2328
+ #
2329
+ # @param x1 [Numeric] x of first point
2330
+ # @param y1 [Numeric] y of first point
2331
+ # @param z1 [Numeric] z of first point
2332
+ # @param x2 [Numeric] x of second point
2333
+ # @param y2 [Numeric] y of second point
2334
+ # @param z2 [Numeric] z of second point
2335
+ #
2336
+ # @return [Numeric] distance between 2 points
2337
+ #
2338
+ def dist(*args)
2339
+ case args.size
2340
+ when 4
2341
+ x1, y1, x2, y2 = *args
2342
+ xx, yy = x2 - x1, y2 - y1
2343
+ Math.sqrt xx * xx + yy * yy
2344
+ when 3
2345
+ x1, y1, z1, x2, y2, z2 = *args
2346
+ xx, yy, zz = x2 - x1, y2 - y1, z2 - z1
2347
+ Math.sqrt xx * xx + yy * yy + zz * zz
2348
+ else raise ArgumentError
2349
+ end
2350
+ end
2024
2351
 
2025
- @window__ = window
2026
- init__(
2027
- @window__.canvas_image,
2028
- @window__.canvas_painter.paint {background 0.8})
2029
-
2030
- @loop__ = true
2031
- @redraw__ = false
2032
- @frameCount__ = 0
2033
- @key__ = nil
2034
- @keyCode__ = nil
2035
- @keysPressed__ = Set.new
2036
- @pointerPos__ =
2037
- @pointerPrevPos__ = Rays::Point.new 0
2038
- @pointersPressed__ = []
2039
- @touches__ = []
2040
- @motionGravity__ = createVector 0, 0
2041
-
2042
- @window__.before_draw = proc {beginDraw__}
2043
- @window__.after_draw = proc {endDraw__}
2044
-
2045
- drawFrame = -> {
2046
- updateCanvas__ @window__.canvas_image, @window__.canvas_painter
2047
- begin
2048
- push
2049
- @drawBlock__.call if @drawBlock__
2050
- ensure
2051
- pop
2052
- @frameCount__ += 1
2053
- end
2054
- }
2055
-
2056
- @window__.draw = proc do |e|
2057
- if @loop__ || @redraw__
2058
- @redraw__ = false
2059
- drawFrame.call
2060
- end
2061
- end
2062
-
2063
- updateKeyStates = -> event, pressed {
2064
- @key__ = event.chars
2065
- @keyCode__ = event.key
2066
- if pressed != nil
2067
- set, key = @keysPressed__, event.key
2068
- pressed ? set.add(key) : set.delete(key)
2069
- end
2070
- }
2071
-
2072
- mouseButtonMap = {
2073
- mouse_left: LEFT,
2074
- mouse_right: RIGHT,
2075
- mouse_middle: CENTER
2076
- }
2077
-
2078
- updatePointerStates = -> event, pressed = nil {
2079
- @pointerPrevPos__ = @pointerPos__
2080
- @pointerPos__ = event.pos.dup
2081
- @touches__ = event.pointers.map {|p| Touch.new(p.id, *p.pos.to_a)}
2082
- if pressed != nil
2083
- array = @pointersPressed__
2084
- event.types
2085
- .tap {|types| types.delete :mouse}
2086
- .map {|type| mouseButtonMap[type] || type}
2087
- .each {|type| pressed ? array.push(type) : array.delete(type)}
2088
- end
2089
- }
2090
-
2091
- @window__.key_down = proc do |e|
2092
- updateKeyStates.call e, true
2093
- @keyPressedBlock__&.call
2094
- @keyTypedBlock__&.call if @key__ && !@key__.empty?
2095
- end
2096
-
2097
- @window__.key_up = proc do |e|
2098
- updateKeyStates.call e, false
2099
- @keyReleasedBlock__&.call
2100
- end
2101
-
2102
- @window__.pointer_down = proc do |e|
2103
- updatePointerStates.call e, true
2104
- @pointerDownStartPos__ = @pointerPos__.dup
2105
- (@touchStartedBlock__ || @mousePressedBlock__)&.call
2106
- end
2107
-
2108
- @window__.pointer_up = proc do |e|
2109
- updatePointerStates.call e, false
2110
- (@touchEndedBlock__ || @mouseReleasedBlock__)&.call
2111
- if startPos = @pointerDownStartPos__
2112
- @mouseClickedBlock__&.call if (@pointerPos__ - startPos).length < 3
2113
- @pointerDownStartPos__ = nil
2114
- end
2115
- end
2116
-
2117
- @window__.pointer_move = proc do |e|
2118
- updatePointerStates.call e
2119
- (@touchMovedBlock__ || @mouseMovedBlock__)&.call
2120
- end
2121
-
2122
- @window__.pointer_drag = proc do |e|
2123
- updatePointerStates.call e
2124
- (@touchMovedBlock__ || @mouseDraggedBlock__)&.call
2125
- end
2126
-
2127
- @window__.motion = proc do |e|
2128
- @motionGravity__ = createVector(*e.gravity.to_a(3))
2129
- @motionBlock__&.call
2130
- end
2131
- end
2132
-
2133
- # Defines setup block.
2352
+ # Normalize the value from range start..stop into 0..1.
2134
2353
  #
2135
- # @return [nil] nil
2354
+ # @param value [Numeric] number to be normalized
2355
+ # @param start [Numeric] lower bound of the range
2356
+ # @param stop [Numeric] upper bound of the range
2136
2357
  #
2137
- def setup(&block)
2138
- @window__.setup = block
2139
- nil
2358
+ # @return [Numeric] normalized value between 0..1
2359
+ #
2360
+ def norm(value, start, stop)
2361
+ (value.to_f - start.to_f) / (stop.to_f - start.to_f)
2140
2362
  end
2141
2363
 
2142
- # Defines draw block.
2364
+ # Returns the interpolated number between range start..stop.
2143
2365
  #
2144
- # @return [nil] nil
2366
+ # @param start [Numeric] lower bound of the range
2367
+ # @param stop [Numeric] upper bound of the range
2368
+ # @param amount [Numeric] amount to interpolate
2145
2369
  #
2146
- def draw(&block)
2147
- @drawBlock__ = block if block
2148
- nil
2370
+ # @return [Numeric] interporated number
2371
+ #
2372
+ def lerp(start, stop, amount)
2373
+ start + (stop - start) * amount
2149
2374
  end
2150
2375
 
2151
- # Defines keyPressed block.
2376
+ # Maps a number from range start1..stop1 to range start2..stop2.
2152
2377
  #
2153
- # @return [Boolean] is any key pressed or not
2378
+ # @param value [Numeric] number to be mapped
2379
+ # @param start1 [Numeric] lower bound of the range1
2380
+ # @param stop1 [Numeric] upper bound of the range1
2381
+ # @param start2 [Numeric] lower bound of the range2
2382
+ # @param stop2 [Numeric] upper bound of the range2
2154
2383
  #
2155
- def keyPressed(&block)
2156
- @keyPressedBlock__ = block if block
2157
- not @keysPressed__.empty?
2384
+ # @return [Numeric] mapped number
2385
+ #
2386
+ def map(value, start1, stop1, start2, stop2)
2387
+ lerp start2, stop2, norm(value, start1, stop1)
2158
2388
  end
2159
2389
 
2160
- # Defines keyReleased block.
2390
+ # Returns minimum value.
2161
2391
  #
2162
- # @return [nil] nil
2392
+ # @overload min(a, b)
2393
+ # @overload min(a, b, c)
2394
+ # @overload min(array)
2163
2395
  #
2164
- def keyReleased(&block)
2165
- @keyReleasedBlock__ = block if block
2166
- nil
2167
- end
2168
-
2169
- # Defines keyTyped block.
2396
+ # @param a [Numeric] value to compare
2397
+ # @param b [Numeric] value to compare
2398
+ # @param c [Numeric] value to compare
2399
+ # @param array [Numeric] values to compare
2170
2400
  #
2171
- # @return [nil] nil
2401
+ # @return [Numeric] minimum value
2172
2402
  #
2173
- def keyTyped(&block)
2174
- @keyTypedBlock__ = block if block
2175
- nil
2403
+ def min(*args)
2404
+ args.flatten.min
2176
2405
  end
2177
2406
 
2178
- # Defines mousePressed block.
2407
+ # Returns maximum value.
2179
2408
  #
2180
- # @return [Boolean] is any mouse button pressed or not
2409
+ # @overload max(a, b)
2410
+ # @overload max(a, b, c)
2411
+ # @overload max(array)
2181
2412
  #
2182
- def mousePressed(&block)
2183
- @mousePressedBlock__ = block if block
2184
- not @pointersPressed__.empty?
2413
+ # @param a [Numeric] value to compare
2414
+ # @param b [Numeric] value to compare
2415
+ # @param c [Numeric] value to compare
2416
+ # @param array [Numeric] values to compare
2417
+ #
2418
+ # @return [Numeric] maximum value
2419
+ #
2420
+ def max(*args)
2421
+ args.flatten.max
2185
2422
  end
2186
2423
 
2187
- # Defines mouseReleased block.
2424
+ # Constrains the number between min..max.
2188
2425
  #
2189
- # @return [nil] nil
2426
+ # @param value [Numeric] number to be constrained
2427
+ # @param min [Numeric] lower bound of the range
2428
+ # @param max [Numeric] upper bound of the range
2190
2429
  #
2191
- def mouseReleased(&block)
2192
- @mouseReleasedBlock__ = block if block
2193
- nil
2430
+ # @return [Numeric] constrained number
2431
+ #
2432
+ def constrain(value, min, max)
2433
+ value < min ? min : (value > max ? max : value)
2194
2434
  end
2195
2435
 
2196
- # Defines mouseMoved block.
2436
+ # Converts degree to radian.
2197
2437
  #
2198
- # @return [nil] nil
2438
+ # @param degree [Numeric] degree to convert
2199
2439
  #
2200
- def mouseMoved(&block)
2201
- @mouseMovedBlock__ = block if block
2202
- nil
2440
+ # @return [Numeric] radian
2441
+ #
2442
+ def radians(degree)
2443
+ degree * DEG2RAD__
2203
2444
  end
2204
2445
 
2205
- # Defines mouseDragged block.
2446
+ # Converts radian to degree.
2206
2447
  #
2207
- # @return [nil] nil
2448
+ # @param radian [Numeric] radian to convert
2208
2449
  #
2209
- def mouseDragged(&block)
2210
- @mouseDraggedBlock__ = block if block
2211
- nil
2450
+ # @return [Numeric] degree
2451
+ #
2452
+ def degrees(radian)
2453
+ radian * RAD2DEG__
2212
2454
  end
2213
2455
 
2214
- # Defines mouseClicked block.
2456
+ # Returns the sine of an angle.
2215
2457
  #
2216
- # @return [nil] nil
2458
+ # @param angle [Numeric] angle in radians
2217
2459
  #
2218
- def mouseClicked(&block)
2219
- @mouseClickedBlock__ = block if block
2220
- nil
2460
+ # @return [Numeric] the sine
2461
+ #
2462
+ def sin(angle)
2463
+ Math.sin angle
2221
2464
  end
2222
2465
 
2223
- # Defines touchStarted block.
2466
+ # Returns the cosine of an angle.
2224
2467
  #
2225
- # @return [nil] nil
2468
+ # @param angle [Numeric] angle in radians
2226
2469
  #
2227
- def touchStarted(&block)
2228
- @touchStartedBlock__ = block if block
2229
- nil
2470
+ # @return [Numeric] the cosine
2471
+ #
2472
+ def cos(angle)
2473
+ Math.cos angle
2230
2474
  end
2231
2475
 
2232
- # Defines touchEnded block.
2476
+ # Returns the ratio of the sine and cosine of an angle.
2233
2477
  #
2234
- # @return [nil] nil
2478
+ # @param angle [Numeric] angle in radians
2235
2479
  #
2236
- def touchEnded(&block)
2237
- @touchEndedBlock__ = block if block
2238
- nil
2480
+ # @return [Numeric] the tangent
2481
+ #
2482
+ def tan(angle)
2483
+ Math.tan angle
2239
2484
  end
2240
2485
 
2241
- # Defines touchMoved block.
2486
+ # Returns the inverse of sin().
2242
2487
  #
2243
- # @return [nil] nil
2488
+ # @param value [Numeric] value for calculation
2244
2489
  #
2245
- def touchMoved(&block)
2246
- @touchMovedBlock__ = block if block
2247
- nil
2490
+ # @return [Numeric] the arc sine
2491
+ #
2492
+ def asin(value)
2493
+ Math.asin value
2248
2494
  end
2249
2495
 
2250
- # Defines motion block.
2496
+ # Returns the inverse of cos().
2251
2497
  #
2252
- # @return [nil] nil
2498
+ # @param value [Numeric] value for calculation
2253
2499
  #
2254
- def motion(&block)
2255
- @motionBlock__ = block if block
2256
- nil
2500
+ # @return [Numeric] the arc cosine
2501
+ #
2502
+ def acos(value)
2503
+ Math.acos value
2257
2504
  end
2258
2505
 
2259
- # Changes canvas size.
2506
+ # Returns the inverse of tan().
2260
2507
  #
2261
- # @param width [Integer] new width
2262
- # @param height [Integer] new height
2263
- # @param pixelDensity [Numeric] new pixel density
2508
+ # @param value [Numeric] value for valculation
2264
2509
  #
2265
- # @return [nil] nil
2510
+ # @return [Numeric] the arc tangent
2266
2511
  #
2267
- def size(width, height, pixelDensity: self.pixelDensity)
2268
- resizeCanvas__ :size, width, height, pixelDensity
2269
- nil
2512
+ def atan(value)
2513
+ Math.atan value
2270
2514
  end
2271
2515
 
2272
- # Changes canvas size.
2516
+ # Returns the angle from a specified point.
2273
2517
  #
2274
- # @param width [Integer] new width
2275
- # @param height [Integer] new height
2276
- # @param pixelDensity [Numeric] new pixel density
2518
+ # @param y [Numeric] y of the point
2519
+ # @param x [Numeric] x of the point
2277
2520
  #
2278
- # @return [nil] nil
2521
+ # @return [Numeric] the angle in radians
2279
2522
  #
2280
- def createCanvas(width, height, pixelDensity: self.pixelDensity)
2281
- resizeCanvas__ :createCanvas, width, height, pixelDensity
2282
- nil
2523
+ def atan2(y, x)
2524
+ Math.atan2 y, x
2283
2525
  end
2284
2526
 
2285
- # Changes title of window.
2527
+ # Returns the perlin noise value.
2286
2528
  #
2287
- # @param title [String] new title
2529
+ # @overload noise(x)
2530
+ # @overload noise(x, y)
2531
+ # @overload noise(x, y, z)
2288
2532
  #
2289
- # @return [nil] nil
2533
+ # @param x [Numeric] horizontal point in noise space
2534
+ # @param y [Numeric] vertical point in noise space
2535
+ # @param z [Numeric] depth point in noise space
2290
2536
  #
2291
- def setTitle(title)
2292
- @window__.title = title
2293
- nil
2537
+ # @return [Numeric] noise value (0.0..1.0)
2538
+ #
2539
+ def noise(x, y = 0, z = 0)
2540
+ Rays.perlin(x, y, z) / 2.0 + 0.5
2294
2541
  end
2295
2542
 
2296
- # Changes and returns canvas pixel density.
2543
+ # Returns a random number in range low...high
2297
2544
  #
2298
- # @param density [Numeric] new pixel density
2545
+ # @overload random()
2546
+ # @overload random(high)
2547
+ # @overload random(low, high)
2548
+ # @overload random(choices)
2299
2549
  #
2300
- # @return [Numeric] current pixel density
2550
+ # @param low [Numeric] lower limit
2551
+ # @param high [Numeric] upper limit
2552
+ # @param choices [Array] array to choose from
2301
2553
  #
2302
- def pixelDensity(density = nil)
2303
- resizeCanvas__ :pixelDensity, width, height, density if density
2304
- @painter__.pixel_density
2305
- end
2306
-
2307
- # @private
2308
- def resizeCanvas__(name, width, height, pixelDensity)
2309
- raise '#{name}() must be called on startup or setup block' if @started__
2310
-
2311
- @painter__.__send__ :end_paint
2312
- begin
2313
- @window__.__send__ :resize_canvas, width, height, pixelDensity
2314
- updateCanvas__ @window__.canvas_image, @window__.canvas_painter
2315
- ensure
2316
- @painter__.__send__ :begin_paint
2317
- end
2318
-
2319
- @window__.auto_resize = false
2554
+ # @return [Float] random number
2555
+ #
2556
+ def random(*args)
2557
+ return args.first.sample if args.first.kind_of? Array
2558
+ high, low = args.reverse
2559
+ rand (low || 0).to_f...(high || 1).to_f
2320
2560
  end
2321
2561
 
2322
- # Returns pixel density of display.
2562
+ # Creates a new vector.
2323
2563
  #
2324
- # @return [Numeric] pixel density
2564
+ # @overload createVector()
2565
+ # @overload createVector(x, y)
2566
+ # @overload createVector(x, y, z)
2325
2567
  #
2326
- def displayDensity()
2327
- @window__.painter.pixel_density
2328
- end
2329
-
2330
- # Returns window width.
2568
+ # @param x [Numeric] x of new vector
2569
+ # @param y [Numeric] y of new vector
2570
+ # @param z [Numeric] z of new vector
2331
2571
  #
2332
- # @return [Numeric] window width
2572
+ # @return [Vector] new vector
2333
2573
  #
2334
- def windowWidth()
2335
- @window__.width
2574
+ def createVector(*args)
2575
+ Vector.new(*args, context: self)
2336
2576
  end
2337
2577
 
2338
- # Returns window height.
2578
+ # Creates a new image.
2339
2579
  #
2340
- # @return [Numeric] window height
2580
+ # @overload createImage(w, h)
2581
+ # @overload createImage(w, h, format)
2341
2582
  #
2342
- def windowHeight()
2343
- @window__.height
2344
- end
2345
-
2346
- # Returns number of frames since program started.
2583
+ # @param w [Numeric] width of new image
2584
+ # @param h [Numeric] height of new image
2585
+ # @param format [RGB, RGBA] image format
2347
2586
  #
2348
- # @return [Integer] total number of frames
2587
+ # @return [Image] new image
2349
2588
  #
2350
- def frameCount()
2351
- @frameCount__
2589
+ def createImage(w, h, format = RGBA)
2590
+ colorspace = {RGB => Rays::RGB, RGBA => Rays::RGBA}[format]
2591
+ raise ArgumentError, "Unknown image format" unless colorspace
2592
+ Image.new Rays::Image.new(w, h, colorspace).paint {background 0, 0}
2352
2593
  end
2353
2594
 
2354
- # Returns number of frames per second.
2595
+ # Creates a new off-screen graphics context object.
2355
2596
  #
2356
- # @return [Float] frames per second
2597
+ # @param width [Numeric] width of graphics image
2598
+ # @param height [Numeric] height of graphics image
2357
2599
  #
2358
- def frameRate()
2359
- @window__.event.fps
2600
+ # @return [Graphics] graphics object
2601
+ #
2602
+ def createGraphics(width, height)
2603
+ Graphics.new width, height
2360
2604
  end
2361
2605
 
2362
- # Returns the last key that was pressed or released.
2606
+ # Creates a shader object.
2363
2607
  #
2364
- # @return [String] last key
2608
+ # @overload createShader(vertPath, fragPath)
2609
+ # @overload createShader(vertSource, fragSource)
2365
2610
  #
2366
- def key()
2367
- @key__
2368
- end
2369
-
2370
- # Returns the last key code that was pressed or released.
2611
+ # @param vertPath [String] vertex shader file path
2612
+ # @param fragPath [String] fragment shader file path
2613
+ # @param vertSource [String] vertex shader source
2614
+ # @param fragSource [String] fragment shader source
2371
2615
  #
2372
- # @return [Symbol] last key code
2616
+ # @return [Shader] shader object
2373
2617
  #
2374
- def keyCode()
2375
- @keyCode__
2618
+ def createShader(vert, frag)
2619
+ vert = File.read if vert && File.exist?(vert)
2620
+ frag = File.read if frag && File.exist?(frag)
2621
+ Shader.new vert, frag
2376
2622
  end
2377
2623
 
2378
- # Returns mouse x position
2624
+ # Creates a camera object as a video input device.
2379
2625
  #
2380
- # @return [Numeric] horizontal position of mouse
2626
+ # @return [Capture] camera object
2381
2627
  #
2382
- def mouseX()
2383
- @pointerPos__.x
2628
+ def createCapture(*args)
2629
+ Capture.new(*args)
2384
2630
  end
2385
2631
 
2386
- # Returns mouse y position
2632
+ # Loads image.
2387
2633
  #
2388
- # @return [Numeric] vertical position of mouse
2634
+ # @param filename [String] file name to load image
2635
+ # @param extension [String] type of image to load (ex. 'png')
2389
2636
  #
2390
- def mouseY()
2391
- @pointerPos__.y
2637
+ # @return [Image] loaded image object
2638
+ #
2639
+ def loadImage(filename, extension = nil)
2640
+ filename = getImage__ filename, extension if filename =~ %r|^https?://|
2641
+ Image.new Rays::Image.load filename
2392
2642
  end
2393
2643
 
2394
- # Returns mouse x position in previous frame
2644
+ # Loads shader file.
2395
2645
  #
2396
- # @return [Numeric] horizontal position of mouse
2646
+ # @overload loadShader(fragPath)
2647
+ # @overload loadShader(fragPath, vertPath)
2397
2648
  #
2398
- def pmouseX()
2399
- @pointerPrevPos__.x
2400
- end
2401
-
2402
- # Returns mouse y position in previous frame
2649
+ # @param fragPath [String] fragment shader file path
2650
+ # @param vertPath [String] vertex shader file path
2403
2651
  #
2404
- # @return [Numeric] vertical position of mouse
2652
+ # @return [Shader] loaded shader object
2405
2653
  #
2406
- def pmouseY()
2407
- @pointerPrevPos__.y
2654
+ def loadShader(fragPath, vertPath = nil)
2655
+ createShader vertPath, fragPath
2408
2656
  end
2409
2657
 
2410
- # Returns which mouse button was pressed
2411
- #
2412
- # @return [Numeric] LEFT, RIGHT, CENTER or 0
2413
- #
2414
- def mouseButton()
2415
- (@pointersPressed__ & [LEFT, RIGHT, CENTER]).last || 0
2658
+ # @private
2659
+ private def getImage__(uri, ext)
2660
+ ext ||= File.extname uri
2661
+ raise "unsupported image type -- #{ext}" unless ext =~ /^\.?(png|jpg|gif)$/i
2662
+
2663
+ tmpdir = tmpdir__
2664
+ path = tmpdir + Digest::SHA1.hexdigest(uri)
2665
+ path = path.sub_ext ext
2666
+
2667
+ unless path.file?
2668
+ URI.open uri do |input|
2669
+ input.set_encoding nil# disable default_internal
2670
+ tmpdir.mkdir unless tmpdir.directory?
2671
+ path.open('w') do |output|
2672
+ output.set_encoding Encoding::ASCII_8BIT
2673
+ while buf = input.read(2 ** 16)
2674
+ output.write buf
2675
+ end
2676
+ end
2677
+ end
2678
+ end
2679
+ path.to_s
2416
2680
  end
2417
2681
 
2418
- # Returns array of touches
2419
- #
2420
- # @return [Array] Touch objects
2421
- #
2422
- def touches()
2423
- @touches__
2682
+ # @private
2683
+ private def tmpdir__()
2684
+ Pathname(Dir.tmpdir) + Digest::SHA1.hexdigest(self.class.name)
2424
2685
  end
2425
2686
 
2426
- # Returns vector for real world gravity
2427
- #
2428
- # @return [Vector] gravity vector
2687
+ end# GraphicsContext
2688
+
2689
+
2690
+ # Draws graphics into an offscreen buffer
2691
+ #
2692
+ class Graphics
2693
+
2694
+ include GraphicsContext
2695
+
2696
+ # Initialize graphics object.
2429
2697
  #
2430
- def motionGravity()
2431
- @motionGravity__
2698
+ def initialize(width, height)
2699
+ image = Rays::Image.new width, height
2700
+ init__ image, image.painter
2432
2701
  end
2433
2702
 
2434
- # Enables calling draw block on every frame.
2435
- #
2436
- # @return [nil] nil
2703
+ # Start drawing.
2437
2704
  #
2438
- def loop()
2439
- @loop__ = true
2705
+ def beginDraw(&block)
2706
+ beginDraw__
2707
+ @painter__.__send__ :begin_paint
2708
+ push
2709
+ if block
2710
+ block.call
2711
+ endDraw
2712
+ end
2440
2713
  end
2441
2714
 
2442
- # Disables calling draw block on every frame.
2443
- #
2444
- # @return [nil] nil
2715
+ # End drawing.
2445
2716
  #
2446
- def noLoop()
2447
- @loop__ = false
2717
+ def endDraw()
2718
+ pop
2719
+ @painter__.__send__ :end_paint
2720
+ endDraw__
2448
2721
  end
2449
2722
 
2450
- # Calls draw block to redraw frame.
2451
- #
2452
- # @return [nil] nil
2453
- #
2454
- def redraw()
2455
- @redraw__ = true
2723
+ end# Graphics
2724
+
2725
+
2726
+ # Processing context
2727
+ #
2728
+ class Context
2729
+
2730
+ include GraphicsContext
2731
+
2732
+ Vector = Processing::Vector
2733
+ Capture = Processing::Capture
2734
+ Graphics = Processing::Graphics
2735
+ Shader = Processing::Shader
2736
+
2737
+ # @private
2738
+ @@context__ = nil
2739
+
2740
+ # @private
2741
+ def self.context__()
2742
+ @@context__
2456
2743
  end
2457
2744
 
2458
- #
2459
- # Utilities
2460
- #
2745
+ # @private
2746
+ def initialize(window)
2747
+ @@context__ = self
2748
+
2749
+ tmpdir__.tap {|dir| FileUtils.rm_r dir.to_s if dir.directory?}
2750
+
2751
+ @window__ = window
2752
+ init__(
2753
+ @window__.canvas_image,
2754
+ @window__.canvas_painter.paint {background 0.8})
2755
+
2756
+ @loop__ = true
2757
+ @redraw__ = false
2758
+ @frameCount__ = 0
2759
+ @key__ = nil
2760
+ @keyCode__ = nil
2761
+ @keysPressed__ = Set.new
2762
+ @pointerPos__ =
2763
+ @pointerPrevPos__ = Rays::Point.new 0
2764
+ @pointersPressed__ = []
2765
+ @touches__ = []
2766
+ @motionGravity__ = createVector 0, 0
2767
+
2768
+ @window__.before_draw = proc {beginDraw__}
2769
+ @window__.after_draw = proc {endDraw__}
2770
+ @window__.update_canvas = proc {|i, p| updateCanvas__ i, p}
2771
+
2772
+ @window__.instance_variable_set :@context, self
2773
+ def @window__.draw_screen(painter)
2774
+ @context.drawImage__ painter
2775
+ end
2776
+
2777
+ drawFrame = -> {
2778
+ begin
2779
+ push
2780
+ @drawBlock__.call if @drawBlock__
2781
+ ensure
2782
+ pop
2783
+ @frameCount__ += 1
2784
+ end
2785
+ }
2786
+
2787
+ @window__.draw = proc do |e|
2788
+ if @loop__ || @redraw__
2789
+ @redraw__ = false
2790
+ drawFrame.call
2791
+ end
2792
+ end
2793
+
2794
+ updateKeyStates = -> event, pressed {
2795
+ @key__ = event.chars
2796
+ @keyCode__ = event.key
2797
+ if pressed != nil
2798
+ set, key = @keysPressed__, event.key
2799
+ pressed ? set.add(key) : set.delete(key)
2800
+ end
2801
+ }
2802
+
2803
+ mouseButtonMap = {
2804
+ mouse_left: LEFT,
2805
+ mouse_right: RIGHT,
2806
+ mouse_middle: CENTER
2807
+ }
2808
+
2809
+ updatePointerStates = -> event, pressed = nil {
2810
+ @pointerPrevPos__ = @pointerPos__
2811
+ @pointerPos__ = event.pos.dup
2812
+ @touches__ = event.pointers.map {|p| Touch.new(p.id, *p.pos.to_a)}
2813
+ if pressed != nil
2814
+ array = @pointersPressed__
2815
+ event.types
2816
+ .tap {|types| types.delete :mouse}
2817
+ .map {|type| mouseButtonMap[type] || type}
2818
+ .each {|type| pressed ? array.push(type) : array.delete(type)}
2819
+ end
2820
+ }
2821
+
2822
+ @window__.key_down = proc do |e|
2823
+ updateKeyStates.call e, true
2824
+ @keyPressedBlock__&.call
2825
+ @keyTypedBlock__&.call if @key__ && !@key__.empty?
2826
+ end
2827
+
2828
+ @window__.key_up = proc do |e|
2829
+ updateKeyStates.call e, false
2830
+ @keyReleasedBlock__&.call
2831
+ end
2832
+
2833
+ @window__.pointer_down = proc do |e|
2834
+ updatePointerStates.call e, true
2835
+ @pointerDownStartPos__ = @pointerPos__.dup
2836
+ (@touchStartedBlock__ || @mousePressedBlock__)&.call
2837
+ end
2461
2838
 
2462
- # Returns the absolute number of the value.
2463
- #
2464
- # @param value [Numeric] number
2465
- #
2466
- # @return [Numeric] absolute number
2467
- #
2468
- def abs(value)
2469
- value.abs
2470
- end
2839
+ @window__.pointer_up = proc do |e|
2840
+ updatePointerStates.call e, false
2841
+ (@touchEndedBlock__ || @mouseReleasedBlock__)&.call
2842
+ if startPos = @pointerDownStartPos__
2843
+ @mouseClickedBlock__&.call if (@pointerPos__ - startPos).length < 3
2844
+ @pointerDownStartPos__ = nil
2845
+ end
2846
+ end
2471
2847
 
2472
- # Returns the closest integer number greater than or equal to the value.
2473
- #
2474
- # @param value [Numeric] number
2475
- #
2476
- # @return [Numeric] rounded up number
2477
- #
2478
- def ceil(value)
2479
- value.ceil
2480
- end
2848
+ @window__.pointer_move = proc do |e|
2849
+ updatePointerStates.call e
2850
+ (@touchMovedBlock__ || @mouseMovedBlock__)&.call
2851
+ end
2481
2852
 
2482
- # Returns the closest integer number less than or equal to the value.
2483
- #
2484
- # @param value [Numeric] number
2485
- #
2486
- # @return [Numeric] rounded down number
2487
- #
2488
- def floor(value)
2489
- value.floor
2490
- end
2853
+ @window__.pointer_drag = proc do |e|
2854
+ updatePointerStates.call e
2855
+ (@touchMovedBlock__ || @mouseDraggedBlock__)&.call
2856
+ end
2491
2857
 
2492
- # Returns the closest integer number.
2493
- #
2494
- # @param value [Numeric] number
2495
- #
2496
- # @return [Numeric] rounded number
2497
- #
2498
- def round(value)
2499
- value.round
2858
+ @window__.motion = proc do |e|
2859
+ @motionGravity__ = createVector(*e.gravity.to_a(3))
2860
+ @motionBlock__&.call
2861
+ end
2500
2862
  end
2501
2863
 
2502
- # Returns the natural logarithm (the base-e logarithm) of a number.
2503
- #
2504
- # @param value [Numeric] number (> 0.0)
2864
+ # Defines setup block.
2505
2865
  #
2506
- # @return [Numeric] result number
2866
+ # @return [nil] nil
2507
2867
  #
2508
- def log(n)
2509
- Math.log n
2868
+ def setup(&block)
2869
+ @window__.setup = block
2870
+ nil
2510
2871
  end
2511
2872
 
2512
- # Returns Euler's number e raised to the power of value.
2513
- #
2514
- # @param value [Numeric] number
2873
+ # Defines draw block.
2515
2874
  #
2516
- # @return [Numeric] result number
2875
+ # @return [nil] nil
2517
2876
  #
2518
- def exp(n)
2519
- Math.exp n
2877
+ def draw(&block)
2878
+ @drawBlock__ = block if block
2879
+ nil
2520
2880
  end
2521
2881
 
2522
- # Returns value raised to the power of exponent.
2523
- #
2524
- # @param value [Numeric] base number
2525
- # @param exponent [Numeric] exponent number
2882
+ # Defines keyPressed block.
2526
2883
  #
2527
- # @return [Numeric] value ** exponent
2884
+ # @return [Boolean] is any key pressed or not
2528
2885
  #
2529
- def pow(value, exponent)
2530
- value ** exponent
2886
+ def keyPressed(&block)
2887
+ @keyPressedBlock__ = block if block
2888
+ not @keysPressed__.empty?
2531
2889
  end
2532
2890
 
2533
- # Returns squared value.
2534
- #
2535
- # @param value [Numeric] number
2891
+ # Defines keyReleased block.
2536
2892
  #
2537
- # @return [Numeric] squared value
2893
+ # @return [nil] nil
2538
2894
  #
2539
- def sq(value)
2540
- value * value
2895
+ def keyReleased(&block)
2896
+ @keyReleasedBlock__ = block if block
2897
+ nil
2541
2898
  end
2542
2899
 
2543
- # Returns squared value.
2544
- #
2545
- # @param value [Numeric] number
2900
+ # Defines keyTyped block.
2546
2901
  #
2547
- # @return [Numeric] squared value
2902
+ # @return [nil] nil
2548
2903
  #
2549
- def sqrt(value)
2550
- Math.sqrt value
2904
+ def keyTyped(&block)
2905
+ @keyTypedBlock__ = block if block
2906
+ nil
2551
2907
  end
2552
2908
 
2553
- # Returns the magnitude (or length) of a vector.
2554
- #
2555
- # @overload mag(x, y)
2556
- # @overload mag(x, y, z)
2557
- #
2558
- # @param x [Numeric] x of point
2559
- # @param y [Numeric] y of point
2560
- # @param z [Numeric] z of point
2909
+ # Defines mousePressed block.
2561
2910
  #
2562
- # @return [Numeric] magnitude
2911
+ # @return [Boolean] is any mouse button pressed or not
2563
2912
  #
2564
- def mag(*args)
2565
- x, y, z = *args
2566
- case args.size
2567
- when 2 then Math.sqrt x * x + y * y
2568
- when 3 then Math.sqrt x * x + y * y + z * z
2569
- else raise ArgumentError
2570
- end
2913
+ def mousePressed(&block)
2914
+ @mousePressedBlock__ = block if block
2915
+ not @pointersPressed__.empty?
2571
2916
  end
2572
2917
 
2573
- # Returns distance between 2 points.
2574
- #
2575
- # @overload dist(x1, y1, x2, y2)
2576
- # @overload dist(x1, y1, z1, x2, y2, z2)
2577
- #
2578
- # @param x1 [Numeric] x of first point
2579
- # @param y1 [Numeric] y of first point
2580
- # @param z1 [Numeric] z of first point
2581
- # @param x2 [Numeric] x of second point
2582
- # @param y2 [Numeric] y of second point
2583
- # @param z2 [Numeric] z of second point
2918
+ # Defines mouseReleased block.
2584
2919
  #
2585
- # @return [Numeric] distance between 2 points
2920
+ # @return [nil] nil
2586
2921
  #
2587
- def dist(*args)
2588
- case args.size
2589
- when 4
2590
- x1, y1, x2, y2 = *args
2591
- xx, yy = x2 - x1, y2 - y1
2592
- Math.sqrt xx * xx + yy * yy
2593
- when 3
2594
- x1, y1, z1, x2, y2, z2 = *args
2595
- xx, yy, zz = x2 - x1, y2 - y1, z2 - z1
2596
- Math.sqrt xx * xx + yy * yy + zz * zz
2597
- else raise ArgumentError
2598
- end
2922
+ def mouseReleased(&block)
2923
+ @mouseReleasedBlock__ = block if block
2924
+ nil
2599
2925
  end
2600
2926
 
2601
- # Normalize the value from range start..stop into 0..1.
2602
- #
2603
- # @param value [Numeric] number to be normalized
2604
- # @param start [Numeric] lower bound of the range
2605
- # @param stop [Numeric] upper bound of the range
2927
+ # Defines mouseMoved block.
2606
2928
  #
2607
- # @return [Numeric] normalized value between 0..1
2929
+ # @return [nil] nil
2608
2930
  #
2609
- def norm(value, start, stop)
2610
- (value.to_f - start.to_f) / (stop.to_f - start.to_f)
2931
+ def mouseMoved(&block)
2932
+ @mouseMovedBlock__ = block if block
2933
+ nil
2611
2934
  end
2612
2935
 
2613
- # Returns the interpolated number between range start..stop.
2614
- #
2615
- # @param start [Numeric] lower bound of the range
2616
- # @param stop [Numeric] upper bound of the range
2617
- # @param amount [Numeric] amount to interpolate
2936
+ # Defines mouseDragged block.
2618
2937
  #
2619
- # @return [Numeric] interporated number
2938
+ # @return [nil] nil
2620
2939
  #
2621
- def lerp(start, stop, amount)
2622
- start + (stop - start) * amount
2940
+ def mouseDragged(&block)
2941
+ @mouseDraggedBlock__ = block if block
2942
+ nil
2623
2943
  end
2624
2944
 
2625
- # Maps a number from range start1..stop1 to range start2..stop2.
2626
- #
2627
- # @param value [Numeric] number to be mapped
2628
- # @param start1 [Numeric] lower bound of the range1
2629
- # @param stop1 [Numeric] upper bound of the range1
2630
- # @param start2 [Numeric] lower bound of the range2
2631
- # @param stop2 [Numeric] upper bound of the range2
2945
+ # Defines mouseClicked block.
2632
2946
  #
2633
- # @return [Numeric] mapped number
2947
+ # @return [nil] nil
2634
2948
  #
2635
- def map(value, start1, stop1, start2, stop2)
2636
- lerp start2, stop2, norm(value, start1, stop1)
2949
+ def mouseClicked(&block)
2950
+ @mouseClickedBlock__ = block if block
2951
+ nil
2637
2952
  end
2638
2953
 
2639
- # Returns minimum value.
2640
- #
2641
- # @overload min(a, b)
2642
- # @overload min(a, b, c)
2643
- # @overload min(array)
2644
- #
2645
- # @param a [Numeric] value to compare
2646
- # @param b [Numeric] value to compare
2647
- # @param c [Numeric] value to compare
2648
- # @param array [Numeric] values to compare
2954
+ # Defines touchStarted block.
2649
2955
  #
2650
- # @return [Numeric] minimum value
2956
+ # @return [nil] nil
2651
2957
  #
2652
- def min(*args)
2653
- args.flatten.min
2958
+ def touchStarted(&block)
2959
+ @touchStartedBlock__ = block if block
2960
+ nil
2654
2961
  end
2655
2962
 
2656
- # Returns maximum value.
2657
- #
2658
- # @overload max(a, b)
2659
- # @overload max(a, b, c)
2660
- # @overload max(array)
2661
- #
2662
- # @param a [Numeric] value to compare
2663
- # @param b [Numeric] value to compare
2664
- # @param c [Numeric] value to compare
2665
- # @param array [Numeric] values to compare
2963
+ # Defines touchEnded block.
2666
2964
  #
2667
- # @return [Numeric] maximum value
2965
+ # @return [nil] nil
2668
2966
  #
2669
- def max(*args)
2670
- args.flatten.max
2967
+ def touchEnded(&block)
2968
+ @touchEndedBlock__ = block if block
2969
+ nil
2671
2970
  end
2672
2971
 
2673
- # Constrains the number between min..max.
2674
- #
2675
- # @param value [Numeric] number to be constrained
2676
- # @param min [Numeric] lower bound of the range
2677
- # @param max [Numeric] upper bound of the range
2972
+ # Defines touchMoved block.
2678
2973
  #
2679
- # @return [Numeric] constrained number
2974
+ # @return [nil] nil
2680
2975
  #
2681
- def constrain(value, min, max)
2682
- value < min ? min : (value > max ? max : value)
2976
+ def touchMoved(&block)
2977
+ @touchMovedBlock__ = block if block
2978
+ nil
2683
2979
  end
2684
2980
 
2685
- # Converts degree to radian.
2686
- #
2687
- # @param degree [Numeric] degree to convert
2981
+ # Defines motion block.
2688
2982
  #
2689
- # @return [Numeric] radian
2983
+ # @return [nil] nil
2690
2984
  #
2691
- def radians(degree)
2692
- degree * DEG2RAD__
2985
+ def motion(&block)
2986
+ @motionBlock__ = block if block
2987
+ nil
2693
2988
  end
2694
2989
 
2695
- # Converts radian to degree.
2990
+ # Changes canvas size.
2696
2991
  #
2697
- # @param radian [Numeric] radian to convert
2992
+ # @param width [Integer] new width
2993
+ # @param height [Integer] new height
2994
+ # @param pixelDensity [Numeric] new pixel density
2698
2995
  #
2699
- # @return [Numeric] degree
2996
+ # @return [nil] nil
2700
2997
  #
2701
- def degrees(radian)
2702
- radian * RAD2DEG__
2998
+ def size(width, height, pixelDensity: self.pixelDensity)
2999
+ resizeCanvas__ :size, width, height, pixelDensity
3000
+ nil
2703
3001
  end
2704
3002
 
2705
- # Returns the sine of an angle.
3003
+ # Changes canvas size.
2706
3004
  #
2707
- # @param angle [Numeric] angle in radians
3005
+ # @param width [Integer] new width
3006
+ # @param height [Integer] new height
3007
+ # @param pixelDensity [Numeric] new pixel density
2708
3008
  #
2709
- # @return [Numeric] the sine
3009
+ # @return [nil] nil
2710
3010
  #
2711
- def sin(angle)
2712
- Math.sin angle
3011
+ def createCanvas(width, height, pixelDensity: self.pixelDensity)
3012
+ resizeCanvas__ :createCanvas, width, height, pixelDensity
3013
+ nil
2713
3014
  end
2714
3015
 
2715
- # Returns the cosine of an angle.
3016
+ # Changes title of window.
2716
3017
  #
2717
- # @param angle [Numeric] angle in radians
3018
+ # @param title [String] new title
2718
3019
  #
2719
- # @return [Numeric] the cosine
3020
+ # @return [nil] nil
2720
3021
  #
2721
- def cos(angle)
2722
- Math.cos angle
3022
+ def setTitle(title)
3023
+ @window__.title = title
3024
+ nil
2723
3025
  end
2724
3026
 
2725
- # Returns the ratio of the sine and cosine of an angle.
3027
+ # Changes and returns canvas pixel density.
2726
3028
  #
2727
- # @param angle [Numeric] angle in radians
3029
+ # @param density [Numeric] new pixel density
2728
3030
  #
2729
- # @return [Numeric] the tangent
3031
+ # @return [Numeric] current pixel density
2730
3032
  #
2731
- def tan(angle)
2732
- Math.tan angle
3033
+ def pixelDensity(density = nil)
3034
+ resizeCanvas__ :pixelDensity, width, height, density if density
3035
+ @painter__.pixel_density
2733
3036
  end
2734
3037
 
2735
- # Returns the inverse of sin().
2736
- #
2737
- # @param value [Numeric] value for calculation
3038
+ # @private
3039
+ def resizeCanvas__(name, width, height, pixelDensity)
3040
+ raise '#{name}() must be called on startup or setup block' if @started__
3041
+
3042
+ @painter__.__send__ :end_paint
3043
+ @window__.resize_canvas width, height, pixelDensity
3044
+ @window__.auto_resize = false
3045
+ ensure
3046
+ @painter__.__send__ :begin_paint
3047
+ end
3048
+
3049
+ # Returns pixel density of display.
2738
3050
  #
2739
- # @return [Numeric] the arc sine
3051
+ # @return [Numeric] pixel density
2740
3052
  #
2741
- def asin(value)
2742
- Math.asin value
3053
+ def displayDensity()
3054
+ @window__.painter.pixel_density
2743
3055
  end
2744
3056
 
2745
- # Returns the inverse of cos().
2746
- #
2747
- # @param value [Numeric] value for calculation
3057
+ # Returns window width.
2748
3058
  #
2749
- # @return [Numeric] the arc cosine
3059
+ # @return [Numeric] window width
2750
3060
  #
2751
- def acos(value)
2752
- Math.acos value
3061
+ def windowWidth()
3062
+ @window__.width
2753
3063
  end
2754
3064
 
2755
- # Returns the inverse of tan().
2756
- #
2757
- # @param value [Numeric] value for valculation
3065
+ # Returns window height.
2758
3066
  #
2759
- # @return [Numeric] the arc tangent
3067
+ # @return [Numeric] window height
2760
3068
  #
2761
- def atan(value)
2762
- Math.atan value
3069
+ def windowHeight()
3070
+ @window__.height
2763
3071
  end
2764
3072
 
2765
- # Returns the angle from a specified point.
2766
- #
2767
- # @param y [Numeric] y of the point
2768
- # @param x [Numeric] x of the point
3073
+ # Returns number of frames since program started.
2769
3074
  #
2770
- # @return [Numeric] the angle in radians
3075
+ # @return [Integer] total number of frames
2771
3076
  #
2772
- def atan2(y, x)
2773
- Math.atan2 y, x
3077
+ def frameCount()
3078
+ @frameCount__
2774
3079
  end
2775
3080
 
2776
- # Returns the perlin noise value.
3081
+ # Returns number of frames per second.
2777
3082
  #
2778
- # @overload noise(x)
2779
- # @overload noise(x, y)
2780
- # @overload noise(x, y, z)
3083
+ # @return [Float] frames per second
2781
3084
  #
2782
- # @param x [Numeric] horizontal point in noise space
2783
- # @param y [Numeric] vertical point in noise space
2784
- # @param z [Numeric] depth point in noise space
3085
+ def frameRate()
3086
+ @window__.event.fps
3087
+ end
3088
+
3089
+ # Returns the last key that was pressed or released.
2785
3090
  #
2786
- # @return [Numeric] noise value (0.0..1.0)
3091
+ # @return [String] last key
2787
3092
  #
2788
- def noise(x, y = 0, z = 0)
2789
- Rays.perlin(x, y, z) / 2.0 + 0.5
3093
+ def key()
3094
+ @key__
2790
3095
  end
2791
3096
 
2792
- # Returns a random number in range low...high
3097
+ # Returns the last key code that was pressed or released.
2793
3098
  #
2794
- # @overload random()
2795
- # @overload random(high)
2796
- # @overload random(low, high)
2797
- # @overload random(choices)
3099
+ # @return [Symbol] last key code
2798
3100
  #
2799
- # @param low [Numeric] lower limit
2800
- # @param high [Numeric] upper limit
2801
- # @param choices [Array] array to choose from
3101
+ def keyCode()
3102
+ @keyCode__
3103
+ end
3104
+
3105
+ # Returns mouse x position
2802
3106
  #
2803
- # @return [Float] random number
3107
+ # @return [Numeric] horizontal position of mouse
2804
3108
  #
2805
- def random(*args)
2806
- return args.first.sample if args.first.kind_of? Array
2807
- high, low = args.reverse
2808
- rand (low || 0).to_f...(high || 1).to_f
3109
+ def mouseX()
3110
+ @pointerPos__.x
2809
3111
  end
2810
3112
 
2811
- # Creates a new vector.
3113
+ # Returns mouse y position
2812
3114
  #
2813
- # @overload createVector()
2814
- # @overload createVector(x, y)
2815
- # @overload createVector(x, y, z)
3115
+ # @return [Numeric] vertical position of mouse
2816
3116
  #
2817
- # @param x [Numeric] x of new vector
2818
- # @param y [Numeric] y of new vector
2819
- # @param z [Numeric] z of new vector
3117
+ def mouseY()
3118
+ @pointerPos__.y
3119
+ end
3120
+
3121
+ # Returns mouse x position in previous frame
2820
3122
  #
2821
- # @return [Vector] new vector
3123
+ # @return [Numeric] horizontal position of mouse
2822
3124
  #
2823
- def createVector(*args)
2824
- Vector.new(*args, context: self)
3125
+ def pmouseX()
3126
+ @pointerPrevPos__.x
2825
3127
  end
2826
3128
 
2827
- # Creates a new image.
3129
+ # Returns mouse y position in previous frame
2828
3130
  #
2829
- # @overload createImage(w, h)
2830
- # @overload createImage(w, h, format)
3131
+ # @return [Numeric] vertical position of mouse
2831
3132
  #
2832
- # @param w [Numeric] width of new image
2833
- # @param h [Numeric] height of new image
2834
- # @param format [RGB, RGBA] image format
3133
+ def pmouseY()
3134
+ @pointerPrevPos__.y
3135
+ end
3136
+
3137
+ # Returns which mouse button was pressed
2835
3138
  #
2836
- # @return [Image] new image
3139
+ # @return [Numeric] LEFT, RIGHT, CENTER or 0
2837
3140
  #
2838
- def createImage(w, h, format = RGBA)
2839
- colorspace = {RGB => Rays::RGB, RGBA => Rays::RGBA}[format]
2840
- raise ArgumentError, "Unknown image format" unless colorspace
2841
- Image.new Rays::Image.new(w, h, colorspace).paint {background 0, 0}
3141
+ def mouseButton()
3142
+ (@pointersPressed__ & [LEFT, RIGHT, CENTER]).last || 0
2842
3143
  end
2843
3144
 
2844
- # Creates a camera object as a video input device.
3145
+ # Returns array of touches
2845
3146
  #
2846
- # @return [Capture] camera object
3147
+ # @return [Array] Touch objects
2847
3148
  #
2848
- def createCapture(*args)
2849
- Capture.new(*args)
3149
+ def touches()
3150
+ @touches__
2850
3151
  end
2851
3152
 
2852
- # Creates a new off-screen graphics context object.
2853
- #
2854
- # @param width [Numeric] width of graphics image
2855
- # @param height [Numeric] height of graphics image
3153
+ # Returns vector for real world gravity
2856
3154
  #
2857
- # @return [Graphics] graphics object
3155
+ # @return [Vector] gravity vector
2858
3156
  #
2859
- def createGraphics(width, height)
2860
- Graphics.new width, height
3157
+ def motionGravity()
3158
+ @motionGravity__
2861
3159
  end
2862
3160
 
2863
- # Loads image.
2864
- #
2865
- # @param filename [String] file name to load image
2866
- # @param extension [String] type of image to load (ex. 'png')
3161
+ # Enables calling draw block on every frame.
2867
3162
  #
2868
- # @return [Image] loaded image object
3163
+ # @return [nil] nil
2869
3164
  #
2870
- def loadImage(filename, extension = nil)
2871
- filename = getImage__ filename, extension if filename =~ %r|^https?://|
2872
- Image.new Rays::Image.load filename
3165
+ def loop()
3166
+ @loop__ = true
2873
3167
  end
2874
3168
 
2875
- # @private
2876
- private def getImage__(uri, ext)
2877
- ext ||= File.extname uri
2878
- raise "unsupported image type -- #{ext}" unless ext =~ /^\.?(png)$/i
2879
-
2880
- tmpdir = tmpdir__
2881
- path = tmpdir + Digest::SHA1.hexdigest(uri)
2882
- path = path.sub_ext ext
2883
-
2884
- unless path.file?
2885
- URI.open uri do |input|
2886
- input.set_encoding nil# disable default_internal
2887
- tmpdir.mkdir unless tmpdir.directory?
2888
- path.open('w') do |output|
2889
- output.set_encoding Encoding::ASCII_8BIT
2890
- while buf = input.read(2 ** 16)
2891
- output.write buf
2892
- end
2893
- end
2894
- end
2895
- end
2896
- path.to_s
3169
+ # Disables calling draw block on every frame.
3170
+ #
3171
+ # @return [nil] nil
3172
+ #
3173
+ def noLoop()
3174
+ @loop__ = false
2897
3175
  end
2898
3176
 
2899
- # @private
2900
- private def tmpdir__()
2901
- Pathname(Dir.tmpdir) + Digest::SHA1.hexdigest(self.class.name)
3177
+ # Calls draw block to redraw frame.
3178
+ #
3179
+ # @return [nil] nil
3180
+ #
3181
+ def redraw()
3182
+ @redraw__ = true
2902
3183
  end
2903
3184
 
2904
3185
  end# Context