rubysketch 0.3.21 → 0.3.22

Sign up to get free protection for your applications and to get access to all the features.
@@ -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