rubysketch 0.3.2

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 83aecf951db1320693714768446ccc95aa0a87f23012e02fff31b032c88be3c6
4
+ data.tar.gz: 6d1f67cbe65c15468cb606666acceacf19f56bf8c83dd1abae2a1b21d7daf94b
5
+ SHA512:
6
+ metadata.gz: 9946ba3029ce56a90042daa9be9e6c2f74f5d7ef7d318b5263b10c03ca35c26f5f7e67c61e6f78bd90f4224c267f732dcb937d86030adbe2b0ac51350a225f6e
7
+ data.tar.gz: 627eb17aa1e83da474f9ab9457b449b278ecf95d84956f98b82d3185c6bc87fa9b5788ad65c1144f80364171b14a4f7322d86a42066925ccbca8f2e19ff46cc3
@@ -0,0 +1,6 @@
1
+ *.gem
2
+ *~
3
+
4
+ .yardoc
5
+ .DS_Store
6
+ doc
@@ -0,0 +1 @@
1
+ --no-private
@@ -0,0 +1,41 @@
1
+ # RubySketch ChangeLog
2
+
3
+
4
+ ## [0.3.2] - 2020-07-22
5
+
6
+ - text() draws to the baseline by default
7
+ - add textWidth(), textAscent(), textDescent() and textAlign()
8
+ - change initial color for fill() and stroke()
9
+ - change initial background color to grayscale 0.8
10
+
11
+
12
+ ## [0.3.1] - 2020-07-17
13
+
14
+ - add touchStarted(), touchEnded(), touchMoved() and touches()
15
+ - make all event handler drawable
16
+ - limit font max size to 256
17
+
18
+
19
+ ## [0.3.0] - 2020-05-21
20
+
21
+ - add createGraphics()
22
+
23
+
24
+ ## [0.2.7] - 2020-04-17
25
+
26
+ - add strokeCap() and strokeJoin()
27
+
28
+
29
+ ## [0.2.6] - 2020-04-17
30
+
31
+ - push(), pushMatrix() and pushStyle() take block to automatic pop
32
+ - refine startup process
33
+ - add curve() and bezier()
34
+ - add imageMode(), Image#resize(), Image#copy() and copy()
35
+ - add loop(), noLoop() and redraw()
36
+
37
+
38
+ ## [0.2.5] - 2020-03-29
39
+
40
+ - delete debug prints
41
+ - show unsupported image type
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2019 xord.org
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,3 @@
1
+ # RubySketch - Processing like Creative Coding Framework
2
+
3
+ by xordog@gmail.com
@@ -0,0 +1,24 @@
1
+ # -*- mode: ruby -*-
2
+
3
+
4
+ %w[../xot ../rucy ../rays ../reflex .]
5
+ .map {|s| File.expand_path "#{s}/lib", __dir__}
6
+ .each {|s| $:.unshift s if !$:.include?(s) && File.directory?(s)}
7
+
8
+ require 'rucy/rake'
9
+
10
+ require 'xot/module'
11
+ require 'rucy/module'
12
+ require 'rays/module'
13
+ require 'reflex/module'
14
+ require 'rubysketch/module'
15
+
16
+
17
+ MODULES = [Xot, Rucy, Rays, Reflex, RubySketch]
18
+
19
+ ENV['RDOC'] = 'yardoc --no-private'
20
+
21
+ generate_documents
22
+ build_ruby_gem
23
+
24
+ task :default => :test
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.3.2
@@ -0,0 +1,57 @@
1
+ %w[xot rays reflex rubysketch]
2
+ .map {|s| File.expand_path "../../#{s}/lib", __dir__}
3
+ .each {|s| $:.unshift s if !$:.include?(s) && File.directory?(s)}
4
+
5
+ require 'rubysketch-processing'
6
+
7
+ COLORS = %w[ #F99292 #FFBC61 #FFC679 #FFF4E0 ]
8
+
9
+ def now ()
10
+ Time.now.to_f
11
+ end
12
+
13
+ start = now
14
+
15
+ setup do
16
+ colorMode RGB, 1
17
+ angleMode DEGREES
18
+ end
19
+
20
+ draw do
21
+ background 0
22
+
23
+ pushMatrix do
24
+ translate width / 2, height / 2
25
+
26
+ pushMatrix do
27
+ fill COLORS[0]
28
+ ellipse 0, 0, 20, 20
29
+ rotate (now - start) / 60.0 * 360
30
+ stroke COLORS[0]
31
+ strokeWeight 5
32
+ line 0, 0, 200, 0
33
+ fill 1
34
+ end
35
+
36
+ pushMatrix do
37
+ strokeWeight 3
38
+ 60.times do
39
+ rotate 6
40
+ stroke COLORS[1]
41
+ line 200, 0, 210, 0
42
+ end
43
+ end
44
+
45
+ pushMatrix do
46
+ strokeWeight 5
47
+ 12.times do
48
+ rotate 30
49
+ stroke COLORS[3]
50
+ line 190, 0, 210, 0
51
+ end
52
+ end
53
+ end
54
+
55
+ textSize 20
56
+ text "#{frameRate.to_i} FPS", 10, 10
57
+ end
@@ -0,0 +1,17 @@
1
+ %w[xot rays reflex rubysketch]
2
+ .map {|s| File.expand_path "../../#{s}/lib", __dir__}
3
+ .each {|s| $:.unshift s if !$:.include?(s) && File.directory?(s)}
4
+
5
+ require 'rubysketch'
6
+
7
+
8
+ RubySketch::GLSL.start <<END
9
+ varying vec4 v_TexCoord;
10
+ uniform vec2 resolution;
11
+ uniform float time;
12
+ void main ()
13
+ {
14
+ vec2 pos = v_TexCoord.xy / resolution;
15
+ gl_FragColor = vec4(pos, float(mod(time, 1.0)), 1);
16
+ }
17
+ END
@@ -0,0 +1,12 @@
1
+ %w[xot rays reflex rubysketch]
2
+ .map {|s| File.expand_path "../../#{s}/lib", __dir__}
3
+ .each {|s| $:.unshift s if !$:.include?(s) && File.directory?(s)}
4
+
5
+ require 'rubysketch-processing'
6
+
7
+
8
+ draw do
9
+ background 0, 10
10
+ textSize 50
11
+ text 'hello, world!', mouseX, mouseY
12
+ end
@@ -0,0 +1,13 @@
1
+ %w[xot rays reflex rubysketch]
2
+ .map {|s| File.expand_path "../../#{s}/lib", __dir__}
3
+ .each {|s| $:.unshift s if !$:.include?(s) && File.directory?(s)}
4
+
5
+ require 'rubysketch-processing'
6
+
7
+
8
+ icon = loadImage 'https://xord.org/rubysketch/rubysketch.png'
9
+
10
+ draw do
11
+ background 0, 10
12
+ image icon, mouseX, mouseY, icon.width / 10, icon.height / 10
13
+ end
@@ -0,0 +1,121 @@
1
+ %w[xot rays reflex rubysketch]
2
+ .map {|s| File.expand_path "../../#{s}/lib", __dir__}
3
+ .each {|s| $:.unshift s if !$:.include?(s) && File.directory?(s)}
4
+
5
+ require 'rubysketch-processing'
6
+
7
+
8
+ setup do
9
+ colorMode RGB, 1
10
+ angleMode DEGREES
11
+ end
12
+
13
+ draw do
14
+ background 0
15
+
16
+ fill 1
17
+ stroke 1, 0.5, 0.2
18
+
19
+ translate 10, 10
20
+
21
+ push
22
+
23
+ text 'point', 0, 0
24
+ point 0, 30
25
+
26
+ translate 0, 100
27
+
28
+ text 'point with strokeWeight', 0, 0
29
+ strokeWeight 10
30
+ point 0, 30
31
+ strokeWeight 0
32
+
33
+ translate 0, 100
34
+
35
+ text 'line', 0, 0
36
+ line 0, 30, 100, 50
37
+
38
+ translate 0, 100
39
+
40
+ text 'line with strokeWeight (very slow)', 0, 0
41
+ strokeWeight 10
42
+ line 0, 30, 100, 50
43
+ strokeWeight 1
44
+
45
+ translate 0, 100
46
+
47
+ text 'rect with rectMode(CORNER)', 0, 0
48
+ rectMode CORNER
49
+ rect 20, 30, 100, 50
50
+
51
+ translate 0, 100
52
+
53
+ text 'rect with rectMode(CORNERS)', 0, 0
54
+ rectMode CORNERS
55
+ rect 20, 30, 120, 80
56
+
57
+ translate 0, 100
58
+
59
+ text 'rect with rectMode(CENTER)', 0, 0
60
+ rectMode CENTER
61
+ rect 70, 55, 100, 50
62
+
63
+ translate 0, 100
64
+
65
+ text 'rect with rectMode(RADIUS)', 0, 0
66
+ rectMode RADIUS
67
+ rect 70, 55, 50, 25
68
+
69
+ pop
70
+ translate 200, 0
71
+ push
72
+
73
+ text 'circle', 0, 0
74
+ circle 70, 55, 25
75
+
76
+ translate 0, 100
77
+
78
+ text 'arc', 0, 0
79
+ arc 70, 55, 100, 50, 45, 270
80
+
81
+ translate 0, 100
82
+
83
+ text 'square', 0, 0
84
+ square 20, 30, 50
85
+
86
+ translate 0, 100
87
+
88
+ text 'triangle', 0, 0
89
+ triangle 70, 30, 120, 80, 20, 80
90
+
91
+ translate 0, 100
92
+
93
+ text 'quad', 0, 0
94
+ quad 20, 30, 120, 30, 150, 80, 50, 80
95
+
96
+ translate 0, 100
97
+
98
+ text 'ellipse with ellipseMode(CORNER)', 0, 0
99
+ ellipseMode CORNER
100
+ ellipse 20, 30, 100, 50
101
+
102
+ translate 0, 100
103
+
104
+ text 'ellipse with ellipseMode(CORNERS)', 0, 0
105
+ ellipseMode CORNERS
106
+ ellipse 20, 30, 120, 80
107
+
108
+ translate 0, 100
109
+
110
+ text 'ellipse with ellipseMode(CENTER)', 0, 0
111
+ ellipseMode CENTER
112
+ ellipse 70, 55, 100, 50
113
+
114
+ translate 0, 100
115
+
116
+ text 'ellipse with ellipseMode(RADIUS)', 0, 0
117
+ ellipseMode RADIUS
118
+ ellipse 70, 55, 50, 25
119
+
120
+ pop
121
+ end
@@ -0,0 +1,15 @@
1
+ require 'rubysketch'
2
+
3
+
4
+ begin
5
+ include RubySketch::Processing::Context
6
+
7
+ window = RubySketch::Window.new {start}
8
+ setup__ window
9
+
10
+ window.__send__ :begin_draw
11
+ at_exit do
12
+ window.__send__ :end_draw
13
+ Reflex.start {window.show} unless $!
14
+ end
15
+ end
@@ -0,0 +1,10 @@
1
+ require 'digest/sha1'
2
+ require 'pathname'
3
+ require 'tmpdir'
4
+ require 'open-uri'
5
+
6
+ require 'reflex'
7
+ require 'rubysketch/module'
8
+ require 'rubysketch/window'
9
+ require 'rubysketch/processing'
10
+ require 'rubysketch/glsl'
@@ -0,0 +1,31 @@
1
+ module RubySketch
2
+
3
+
4
+ # @private
5
+ class GLSL
6
+
7
+ def initialize (glsl)
8
+ @shader = Reflex::Shader.new glsl
9
+ end
10
+
11
+ # @private
12
+ def on_start__ (window)
13
+ start = Time.now.to_f
14
+
15
+ window.draw = proc do |e, painter|
16
+ painter.paint do |p|
17
+ c = window.canvas
18
+ w = c.width
19
+ h = c.height
20
+ t = Time.now.to_f - start
21
+ p.shader @shader, resolution: [w, h], time: t if @shader
22
+ p.fill 1
23
+ p.rect 0, 0, w, h
24
+ end
25
+ end
26
+ end
27
+
28
+ end# GLSL
29
+
30
+
31
+ end# RubySketch
@@ -0,0 +1,23 @@
1
+ module RubySketch
2
+
3
+
4
+ module Module
5
+
6
+ module_function
7
+
8
+ def name ()
9
+ super.split('::')[-2]
10
+ end
11
+
12
+ def version ()
13
+ open(root_dir 'VERSION') {|f| f.readline.chomp}
14
+ end
15
+
16
+ def root_dir (path = '')
17
+ File.expand_path "../../#{path}", __dir__
18
+ end
19
+
20
+ end# Module
21
+
22
+
23
+ end# RubySketch
@@ -0,0 +1,1581 @@
1
+ module RubySketch
2
+
3
+
4
+ # Processing compatible API
5
+ #
6
+ module Processing
7
+
8
+
9
+ # Image object.
10
+ #
11
+ class Image
12
+
13
+ # @private
14
+ def initialize (image)
15
+ @image__ = image
16
+ end
17
+
18
+ # Gets width of image.
19
+ #
20
+ # @return [Numeric] width of image
21
+ #
22
+ def width ()
23
+ @image__.width
24
+ end
25
+
26
+ # Gets height of image.
27
+ #
28
+ # @return [Numeric] height of image
29
+ #
30
+ def height ()
31
+ @image__.height
32
+ end
33
+
34
+ # Resizes image.
35
+ #
36
+ # @param width [Numeric] width for resized image
37
+ # @param height [Numeric] height for resized image
38
+ #
39
+ # @return [nil] nil
40
+ #
41
+ def resize (width, height)
42
+ @image__ = Rays::Image.new(width, height).paint do |painter|
43
+ painter.image @image__, 0, 0, width, height
44
+ end
45
+ nil
46
+ end
47
+
48
+ # Copies image.
49
+ #
50
+ # @overload copy(sx, sy, sw, sh, dx, dy, dw, dh)
51
+ # @overload copy(img, sx, sy, sw, sh, dx, dy, dw, dh)
52
+ #
53
+ # @param img [Image] image for copy source
54
+ # @param sx [Numrtic] x position of source region
55
+ # @param sy [Numrtic] y position of source region
56
+ # @param sw [Numrtic] width of source region
57
+ # @param sh [Numrtic] height of source region
58
+ # @param dx [Numrtic] x position of destination region
59
+ # @param dy [Numrtic] y position of destination region
60
+ # @param dw [Numrtic] width of destination region
61
+ # @param dh [Numrtic] height of destination region
62
+ #
63
+ # @return [nil] nil
64
+ #
65
+ def copy (img = nil, sx, sy, sw, sh, dx, dy, dw, dh)
66
+ img ||= self
67
+ @image__.paint do |painter|
68
+ painter.image img.getInternal__, sx, sy, sw, sh, dx, dy, dw, dh
69
+ end
70
+ end
71
+
72
+ # Saves image to file.
73
+ #
74
+ # @param filename [String] file name to save image
75
+ #
76
+ def save (filename)
77
+ @image__.save filename
78
+ end
79
+
80
+ # @private
81
+ def getInternal__ ()
82
+ @image__
83
+ end
84
+
85
+ end# Image
86
+
87
+
88
+ # Font object.
89
+ #
90
+ class Font
91
+
92
+ # @private
93
+ def initialize (font)
94
+ @font = font
95
+ end
96
+
97
+ # Returns bounding box.
98
+ #
99
+ # @overload textBounds(str)
100
+ # @overload textBounds(str, x, y)
101
+ # @overload textBounds(str, x, y, fontSize)
102
+ #
103
+ # @param str [String] text to calculate bounding box
104
+ # @param x [Numeric] horizontal position of bounding box
105
+ # @param y [Numeric] vertical position of bounding box
106
+ # @param fontSize [Numeric] font size
107
+ #
108
+ # @return [TextBounds] bounding box for text
109
+ #
110
+ def textBounds (str, x = 0, y = 0, fontSize = nil)
111
+ f = fontSize ? Rays::Font.new(@font.name, fontSize) : @font
112
+ TextBounds.new x, y, x + f.width(str), y + f.height
113
+ end
114
+
115
+ end# Font
116
+
117
+
118
+ # Bounding box for text.
119
+ #
120
+ class TextBounds
121
+
122
+ # Horizontal position
123
+ #
124
+ attr_reader :x
125
+
126
+ # Vertical position
127
+ #
128
+ attr_reader :y
129
+
130
+ # Width of bounding box
131
+ #
132
+ attr_reader :w
133
+
134
+ # Height of bounding box
135
+ #
136
+ attr_reader :h
137
+
138
+ # @private
139
+ def initialize (x, y, w, h)
140
+ @x, @y, @w, @h = x, y, w, h
141
+ end
142
+
143
+ end# TextBounds
144
+
145
+
146
+ # Touch object.
147
+ #
148
+ class Touch
149
+
150
+ # Horizontal position of touch
151
+ #
152
+ attr_reader :x
153
+
154
+ # Vertical position of touch
155
+ #
156
+ attr_reader :y
157
+
158
+ # @private
159
+ def initialize (x, y)
160
+ @x, @y = x, y
161
+ end
162
+
163
+ def id ()
164
+ raise NotImplementedError
165
+ end
166
+
167
+ end# Touch
168
+
169
+
170
+ # Drawing context
171
+ #
172
+ module GraphicsContext
173
+
174
+ # PI / 2
175
+ #
176
+ HALF_PI = Math::PI / 2
177
+
178
+ # PI / 4
179
+ #
180
+ QUARTER_PI = Math::PI / 4
181
+
182
+ # PI * 2
183
+ #
184
+ TWO_PI = Math::PI * 2
185
+
186
+ # PI * 2
187
+ #
188
+ TAU = Math::PI * 2
189
+
190
+ # RGB mode for colorMode().
191
+ #
192
+ RGB = :RGB
193
+
194
+ # HSB mode for colorMode().
195
+ #
196
+ HSB = :HSB
197
+
198
+ # Radian mode for angleMode().
199
+ #
200
+ RADIANS = :RADIANS
201
+
202
+ # Degree mode for angleMode().
203
+ #
204
+ DEGREES = :DEGREES
205
+
206
+ # Mode for rectMode(), ellipseMode() and imageMode().
207
+ #
208
+ CORNER = :CORNER
209
+
210
+ # Mode for rectMode(), ellipseMode() and imageMode().
211
+ #
212
+ CORNERS = :CORNERS
213
+
214
+ # Mode for rectMode(), ellipseMode(), imageMode() and textAlign().
215
+ #
216
+ CENTER = :CENTER
217
+
218
+ # Mode for rectMode() and ellipseMode().
219
+ #
220
+ RADIUS = :RADIUS
221
+
222
+ # Mode for textAlign().
223
+ LEFT = :LEFT
224
+
225
+ # Mode for textAlign().
226
+ RIGHT = :RIGHT
227
+
228
+ # Mode for textAlign().
229
+ TOP = :TOP
230
+
231
+ # Mode for textAlign().
232
+ BOTTOM = :BOTTOM
233
+
234
+ # Mode for textAlign().
235
+ BASELINE = :BASELINE
236
+
237
+ # Mode for strokeCap().
238
+ #
239
+ BUTT = :butt
240
+
241
+ # Mode for strokeJoin().
242
+ #
243
+ MITER = :miter
244
+
245
+ # Mode for strokeCap() and strokeJoin().
246
+ #
247
+ ROUND = :round
248
+
249
+ # Mode for strokeCap() and strokeJoin().
250
+ #
251
+ SQUARE = :square
252
+
253
+ def setup__ (painter)
254
+ @painter__ = painter
255
+ @painter__.miter_limit = 10
256
+
257
+ @drawing = false
258
+ @hsbColor__ = false
259
+ @colorMaxes__ = [1.0] * 4
260
+ @angleScale__ = 1.0
261
+ @rectMode__ = nil
262
+ @ellipseMode__ = nil
263
+ @imageMode__ = nil
264
+ @textAlignH__ = nil
265
+ @textAlignV__ = nil
266
+ @matrixStack__ = []
267
+ @styleStack__ = []
268
+
269
+ colorMode RGB, 255
270
+ angleMode RADIANS
271
+ rectMode CORNER
272
+ ellipseMode CENTER
273
+ imageMode CORNER
274
+ textAlign LEFT
275
+
276
+ fill 255
277
+ stroke 0
278
+ end
279
+
280
+ def beginDraw ()
281
+ @matrixStack__.clear
282
+ @styleStack__.clear
283
+ @drawing = true
284
+ end
285
+
286
+ def endDraw ()
287
+ @drawing = false
288
+ end
289
+
290
+ def width ()
291
+ @image__.width
292
+ end
293
+
294
+ def height ()
295
+ @image__.height
296
+ end
297
+
298
+ # Sets color mode and max color values.
299
+ #
300
+ # @overload colorMode(mode)
301
+ # @overload colorMode(mode, max)
302
+ # @overload colorMode(mode, max1, max2, max3)
303
+ # @overload colorMode(mode, max1, max2, max3, maxA)
304
+ #
305
+ # @param mode [RGB, HSB] RGB or HSB
306
+ # @param max [Numeric] max values for all color values
307
+ # @param max1 [Numeric] max value for red or hue
308
+ # @param max2 [Numeric] max value for green or saturation
309
+ # @param max3 [Numeric] max value for blue or brightness
310
+ # @param maxA [Numeric] max value for alpha
311
+ #
312
+ # @return [nil] nil
313
+ #
314
+ def colorMode (mode, *maxes)
315
+ mode = mode.upcase.to_sym
316
+ raise ArgumentError, "invalid color mode: #{mode}" unless [RGB, HSB].include?(mode)
317
+ raise ArgumentError unless [0, 1, 3, 4].include?(maxes.size)
318
+
319
+ @hsbColor__ = mode == HSB
320
+ case maxes.size
321
+ when 1 then @colorMaxes__ = [maxes.first.to_f] * 4
322
+ when 3, 4 then @colorMaxes__[0...maxes.size] = maxes.map &:to_f
323
+ end
324
+ nil
325
+ end
326
+
327
+ # @private
328
+ private def toRGBA__ (*args)
329
+ a, b, c, d = args
330
+ return parseColor__(a, b || alphaMax__) if a.kind_of?(String)
331
+
332
+ rgba = case args.size
333
+ when 1, 2 then [a, a, a, b || alphaMax__]
334
+ when 3, 4 then [a, b, c, d || alphaMax__]
335
+ else raise ArgumentError
336
+ end
337
+ rgba = rgba.map.with_index {|value, i| value / @colorMaxes__[i]}
338
+ color = @hsbColor__ ? Rays::Color.hsv(*rgba) : Rays::Color.new(*rgba)
339
+ color.to_a
340
+ end
341
+
342
+ # @private
343
+ private def parseColor__ (str, alpha)
344
+ result = str.match /^\s*##{'([0-9a-f]{2})' * 3}\s*$/i
345
+ raise ArgumentError, "invalid color code: '#{str}'" unless result
346
+
347
+ rgb = result[1..3].map.with_index {|hex, i| hex.to_i(16) / 255.0}
348
+ return *rgb, (alpha / alphaMax__)
349
+ end
350
+
351
+ # @private
352
+ private def alphaMax__ ()
353
+ @colorMaxes__[3]
354
+ end
355
+
356
+ # Sets angle mode.
357
+ #
358
+ # @param mode [RADIANS, DEGREES] RADIANS or DEGREES
359
+ #
360
+ # @return [nil] nil
361
+ #
362
+ def angleMode (mode)
363
+ @angleScale__ = case mode.upcase.to_sym
364
+ when RADIANS then Utility::RAD2DEG__
365
+ when DEGREES then 1.0
366
+ else raise ArgumentError, "invalid angle mode: #{mode}"
367
+ end
368
+ nil
369
+ end
370
+
371
+ # @private
372
+ private def toAngle__ (angle)
373
+ angle * @angleScale__
374
+ end
375
+
376
+ # Sets rect mode. Default is CORNER.
377
+ #
378
+ # CORNER -> rect(left, top, width, height)
379
+ # CORNERS -> rect(left, top, right, bottom)
380
+ # CENTER -> rect(center_x, center_y, width, height)
381
+ # RADIUS -> rect(center_x, center_y, radius_h, radius_v)
382
+ #
383
+ # @param mode [CORNER, CORNERS, CENTER, RADIUS]
384
+ #
385
+ # @return [nil] nil
386
+ #
387
+ def rectMode (mode)
388
+ @rectMode__ = mode
389
+ end
390
+
391
+ # Sets ellipse mode. Default is CENTER.
392
+ #
393
+ # CORNER -> ellipse(left, top, width, height)
394
+ # CORNERS -> ellipse(left, top, right, bottom)
395
+ # CENTER -> ellipse(center_x, center_y, width, height)
396
+ # RADIUS -> ellipse(center_x, center_y, radius_h, radius_v)
397
+ #
398
+ # @param mode [CORNER, CORNERS, CENTER, RADIUS]
399
+ #
400
+ # @return [nil] nil
401
+ #
402
+ def ellipseMode (mode)
403
+ @ellipseMode__ = mode
404
+ end
405
+
406
+ # Sets image mode. Default is CORNER.
407
+ #
408
+ # CORNER -> image(img, left, top, width, height)
409
+ # CORNERS -> image(img, left, top, right, bottom)
410
+ # CENTER -> image(img, center_x, center_y, width, height)
411
+ #
412
+ # @param mode [CORNER, CORNERS, CENTER]
413
+ #
414
+ # @return [nil] nil
415
+ #
416
+ def imageMode (mode)
417
+ @imageMode__ = mode
418
+ end
419
+
420
+ # @private
421
+ private def toXYWH__ (mode, a, b, c, d)
422
+ case mode
423
+ when CORNER then [a, b, c, d]
424
+ when CORNERS then [a, b, c - a, d - b]
425
+ when CENTER then [a - c / 2.0, b - d / 2.0, c, d]
426
+ when RADIUS then [a - c, b - d, c * 2, d * 2]
427
+ else raise ArgumentError # ToDo: refine error message
428
+ end
429
+ end
430
+
431
+ # Sets fill color.
432
+ #
433
+ # @overload fill(rgb)
434
+ # @overload fill(rgb, alpha)
435
+ # @overload fill(gray)
436
+ # @overload fill(gray, alpha)
437
+ # @overload fill(r, g, b)
438
+ # @overload fill(r, g, b, alpha)
439
+ #
440
+ # @param rgb [String] color code like '#00AAFF'
441
+ # @param gray [Integer] gray value (0..255)
442
+ # @param r [Integer] red value (0..255)
443
+ # @param g [Integer] green value (0..255)
444
+ # @param b [Integer] blue value (0..255)
445
+ # @param alpha [Integer] alpha value (0..255)
446
+ #
447
+ # @return [nil] nil
448
+ #
449
+ def fill (*args)
450
+ @painter__.fill(*toRGBA__(*args))
451
+ nil
452
+ end
453
+
454
+ # Sets stroke color.
455
+ #
456
+ # @overload stroke(rgb)
457
+ # @overload stroke(rgb, alpha)
458
+ # @overload stroke(gray)
459
+ # @overload stroke(gray, alpha)
460
+ # @overload stroke(r, g, b)
461
+ # @overload stroke(r, g, b, alpha)
462
+ #
463
+ # @param rgb [String] color code like '#00AAFF'
464
+ # @param gray [Integer] gray value (0..255)
465
+ # @param r [Integer] red value (0..255)
466
+ # @param g [Integer] green value (0..255)
467
+ # @param b [Integer] blue value (0..255)
468
+ # @param alpha [Integer] alpha value (0..255)
469
+ #
470
+ # @return [nil] nil
471
+ #
472
+ def stroke (*args)
473
+ @painter__.stroke(*toRGBA__(*args))
474
+ nil
475
+ end
476
+
477
+ # Sets stroke weight.
478
+ #
479
+ # @param weight [Numeric] width of stroke
480
+ #
481
+ # @return [nil] nil
482
+ #
483
+ def strokeWeight (weight)
484
+ @painter__.stroke_width weight
485
+ nil
486
+ end
487
+
488
+ # Sets stroke cap mode.
489
+ #
490
+ # @param cap [BUTT, ROUND, SQUARE]
491
+ #
492
+ # @return [nil] nil
493
+ #
494
+ def strokeCap (cap)
495
+ @painter__.stroke_cap cap
496
+ nil
497
+ end
498
+
499
+ # Sets stroke join mode.
500
+ #
501
+ # @param join [MITER, ROUND, SQUARE]
502
+ #
503
+ # @return [nil] nil
504
+ #
505
+ def strokeJoin (join)
506
+ @painter__.stroke_join join
507
+ nil
508
+ end
509
+
510
+ # Disables filling.
511
+ #
512
+ # @return [nil] nil
513
+ #
514
+ def noFill ()
515
+ @painter__.fill nil
516
+ nil
517
+ end
518
+
519
+ # Disables drawing stroke.
520
+ #
521
+ # @return [nil] nil
522
+ #
523
+ def noStroke ()
524
+ @painter__.stroke nil
525
+ nil
526
+ end
527
+
528
+ # Sets font.
529
+ #
530
+ # @param name [String] font name
531
+ # @param size [Numeric] font size (max 256)
532
+ #
533
+ # @return [Font] current font
534
+ #
535
+ def textFont (name = nil, size = nil)
536
+ setFont__ name, size if name || size
537
+ Font.new @painter__.font
538
+ end
539
+
540
+ # Sets text size.
541
+ #
542
+ # @param size [Numeric] font size (max 256)
543
+ #
544
+ # @return [nil] nil
545
+ #
546
+ def textSize (size)
547
+ setFont__ @painter__.font.name, size
548
+ nil
549
+ end
550
+
551
+ def textWidth (str)
552
+ @painter__.font.width str
553
+ end
554
+
555
+ def textAscent ()
556
+ @painter__.font.ascent
557
+ end
558
+
559
+ def textDescent ()
560
+ @painter__.font.descent
561
+ end
562
+
563
+ def textAlign (horizontal, vertical = BASELINE)
564
+ @textAlignH__ = horizontal
565
+ @textAlignV__ = vertical
566
+ end
567
+
568
+ # @private
569
+ def setFont__ (name, size)
570
+ size = 256 if size && size > 256
571
+ @painter__.font name, size
572
+ end
573
+
574
+ # Clears screen.
575
+ #
576
+ # @overload background(str)
577
+ # @overload background(str, alpha)
578
+ # @overload background(gray)
579
+ # @overload background(gray, alpha)
580
+ # @overload background(r, g, b)
581
+ # @overload background(r, g, b, alpha)
582
+ #
583
+ # @param str [String] color code like '#00AAFF'
584
+ # @param gray [Integer] gray value (0..255)
585
+ # @param r [Integer] red value (0..255)
586
+ # @param g [Integer] green value (0..255)
587
+ # @param b [Integer] blue value (0..255)
588
+ # @param alpha [Integer] alpha value (0..255)
589
+ #
590
+ # @return [nil] nil
591
+ #
592
+ def background (*args)
593
+ assertDrawing__
594
+ rgba = toRGBA__ *args
595
+ if rgba[3] == 1
596
+ @painter__.background *rgba
597
+ else
598
+ @painter__.push fill: rgba, stroke: nil do |_|
599
+ @painter__.rect 0, 0, width, height
600
+ end
601
+ end
602
+ nil
603
+ end
604
+
605
+ # Draws a point.
606
+ #
607
+ # @param x [Numeric] horizontal position
608
+ # @param y [Numeric] vertical position
609
+ #
610
+ # @return [nil] nil
611
+ #
612
+ def point (x, y)
613
+ assertDrawing__
614
+ w = @painter__.stroke_width
615
+ w = 1 if w == 0
616
+ @painter__.ellipse x - (w / 2.0), y - (w / 2.0), w, w
617
+ nil
618
+ end
619
+
620
+ # Draws a line.
621
+ #
622
+ # @param x1 [Numeric] horizontal position of first point
623
+ # @param y1 [Numeric] vertical position of first point
624
+ # @param x2 [Numeric] horizontal position of second point
625
+ # @param y2 [Numeric] vertical position of second point
626
+ #
627
+ # @return [nil] nil
628
+ #
629
+ def line (x1, y1, x2, y2)
630
+ assertDrawing__
631
+ @painter__.line x1, y1, x2, y2
632
+ nil
633
+ end
634
+
635
+ # Draws a rectangle.
636
+ #
637
+ # @overload rect(a, b, c, d)
638
+ # @overload rect(a, b, c, d, r)
639
+ # @overload rect(a, b, c, d, tl, tr, br, bl)
640
+ #
641
+ # @param a [Numeric] horizontal position of the shape by default
642
+ # @param b [Numeric] vertical position of the shape by default
643
+ # @param c [Numeric] width of the shape by default
644
+ # @param d [Numeric] height of the shape by default
645
+ # @param r [Numeric] radius for all corners
646
+ # @param tl [Numeric] radius for top-left corner
647
+ # @param tr [Numeric] radius for top-right corner
648
+ # @param br [Numeric] radius for bottom-right corner
649
+ # @param bl [Numeric] radius for bottom-left corner
650
+ #
651
+ # @return [nil] nil
652
+ #
653
+ def rect (a, b, c, d, *args)
654
+ assertDrawing__
655
+ x, y, w, h = toXYWH__ @rectMode__, a, b, c, d
656
+ case args.size
657
+ when 0 then @painter__.rect x, y, w, h
658
+ when 1 then @painter__.rect x, y, w, h, round: args[0]
659
+ when 4 then @painter__.rect x, y, w, h, lt: args[0], rt: args[1], rb: args[2], lb: args[3]
660
+ else raise ArgumentError # ToDo: refine error message
661
+ end
662
+ nil
663
+ end
664
+
665
+ # Draws an ellipse.
666
+ #
667
+ # @param a [Numeric] horizontal position of the shape
668
+ # @param b [Numeric] vertical position of the shape
669
+ # @param c [Numeric] width of the shape
670
+ # @param d [Numeric] height of the shape
671
+ #
672
+ # @return [nil] nil
673
+ #
674
+ def ellipse (a, b, c, d)
675
+ assertDrawing__
676
+ x, y, w, h = toXYWH__ @ellipseMode__, a, b, c, d
677
+ @painter__.ellipse x, y, w, h
678
+ nil
679
+ end
680
+
681
+ # Draws a circle.
682
+ #
683
+ # @param x [Numeric] horizontal position of the shape
684
+ # @param y [Numeric] vertical position of the shape
685
+ # @param extent [Numeric] width and height of the shape
686
+ #
687
+ # @return [nil] nil
688
+ #
689
+ def circle (x, y, extent)
690
+ ellipse x, y, extent, extent
691
+ end
692
+
693
+ # Draws an arc.
694
+ #
695
+ # @param a [Numeric] horizontal position of the shape
696
+ # @param b [Numeric] vertical position of the shape
697
+ # @param c [Numeric] width of the shape
698
+ # @param d [Numeric] height of the shape
699
+ # @param start [Numeric] angle to start the arc
700
+ # @param stop [Numeric] angle to stop the arc
701
+ #
702
+ # @return [nil] nil
703
+ #
704
+ def arc (a, b, c, d, start, stop)
705
+ assertDrawing__
706
+ x, y, w, h = toXYWH__ @ellipseMode__, a, b, c, d
707
+ start = toAngle__ start
708
+ stop = toAngle__ stop
709
+ @painter__.ellipse x, y, w, h, from: start, to: stop
710
+ nil
711
+ end
712
+
713
+ # Draws a square.
714
+ #
715
+ # @param x [Numeric] horizontal position of the shape
716
+ # @param y [Numeric] vertical position of the shape
717
+ # @param extent [Numeric] width and height of the shape
718
+ #
719
+ # @return [nil] nil
720
+ #
721
+ def square (x, y, extent)
722
+ rect x, y, extent, extent
723
+ end
724
+
725
+ # Draws a triangle.
726
+ #
727
+ # @param x1 [Numeric] horizontal position of first point
728
+ # @param y1 [Numeric] vertical position of first point
729
+ # @param x2 [Numeric] horizontal position of second point
730
+ # @param y2 [Numeric] vertical position of second point
731
+ # @param x3 [Numeric] horizontal position of third point
732
+ # @param y3 [Numeric] vertical position of third point
733
+ #
734
+ # @return [nil] nil
735
+ #
736
+ def triangle (x1, y1, x2, y2, x3, y3)
737
+ assertDrawing__
738
+ @painter__.line x1, y1, x2, y2, x3, y3, loop: true
739
+ nil
740
+ end
741
+
742
+ # Draws a quad.
743
+ #
744
+ # @param x1 [Numeric] horizontal position of first point
745
+ # @param y1 [Numeric] vertical position of first point
746
+ # @param x2 [Numeric] horizontal position of second point
747
+ # @param y2 [Numeric] vertical position of second point
748
+ # @param x3 [Numeric] horizontal position of third point
749
+ # @param y3 [Numeric] vertical position of third point
750
+ # @param x4 [Numeric] horizontal position of fourth point
751
+ # @param y4 [Numeric] vertical position of fourth point
752
+ #
753
+ # @return [nil] nil
754
+ #
755
+ def quad (x1, y1, x2, y2, x3, y3, x4, y4)
756
+ assertDrawing__
757
+ @painter__.line x1, y1, x2, y2, x3, y3, x4, y4, loop: true
758
+ nil
759
+ end
760
+
761
+ # Draws a Catmull-Rom spline curve.
762
+ #
763
+ # @param cx1 [Numeric] horizontal position of beginning control point
764
+ # @param cy1 [Numeric] vertical position of beginning control point
765
+ # @param x1 [Numeric] horizontal position of first point
766
+ # @param y1 [Numeric] vertical position of first point
767
+ # @param x2 [Numeric] horizontal position of second point
768
+ # @param y2 [Numeric] vertical position of second point
769
+ # @param cx2 [Numeric] horizontal position of ending control point
770
+ # @param cy2 [Numeric] vertical position of ending control point
771
+ #
772
+ # @return [nil] nil
773
+ #
774
+ def curve (cx1, cy1, x1, y1, x2, y2, cx2, cy2)
775
+ assertDrawing__
776
+ @painter__.curve cx1, cy1, x1, y1, x2, y2, cx2, cy2
777
+ nil
778
+ end
779
+
780
+ # Draws a Bezier spline curve.
781
+ #
782
+ # @param x1 [Numeric] horizontal position of first point
783
+ # @param y1 [Numeric] vertical position of first point
784
+ # @param cx1 [Numeric] horizontal position of first control point
785
+ # @param cy1 [Numeric] vertical position of first control point
786
+ # @param cx2 [Numeric] horizontal position of second control point
787
+ # @param cy2 [Numeric] vertical position of second control point
788
+ # @param x2 [Numeric] horizontal position of second point
789
+ # @param y2 [Numeric] vertical position of second point
790
+ #
791
+ # @return [nil] nil
792
+ #
793
+ def bezier (x1, y1, cx1, cy1, cx2, cy2, x2, y2)
794
+ assertDrawing__
795
+ @painter__.bezier x1, y1, cx1, cy1, cx2, cy2, x2, y2
796
+ nil
797
+ end
798
+
799
+ # Draws a text.
800
+ #
801
+ # @overload text(str)
802
+ # @overload text(str, x, y)
803
+ # @overload text(str, a, b, c, d)
804
+ #
805
+ # @param str [String] text to draw
806
+ # @param x [Numeric] horizontal position of the text
807
+ # @param y [Numeric] vertical position of the text
808
+ # @param a [Numeric] equivalent to parameters of the rect(), see rectMode()
809
+ # @param b [Numeric] equivalent to parameters of the rect(), see rectMode()
810
+ # @param c [Numeric] equivalent to parameters of the rect(), see rectMode()
811
+ # @param d [Numeric] equivalent to parameters of the rect(), see rectMode()
812
+ #
813
+ # @return [nil] nil
814
+ #
815
+ def text (str, x, y, x2 = nil, y2 = nil)
816
+ assertDrawing__
817
+ if x2
818
+ raise ArgumentError, "missing y2 parameter" unless y2
819
+ x, y, w, h = toXYWH__ @rectMode__, x, y, x2, y2
820
+ case @textAlignH__
821
+ when RIGHT then x += w - @painter__.font.width(str)
822
+ when CENTER then x += (w - @painter__.font.width(str)) / 2
823
+ end
824
+ case @textAlignV__
825
+ when BOTTOM then y += h - @painter__.font.height
826
+ when CENTER then y += (h - @painter__.font.height) / 2
827
+ else
828
+ end
829
+ else
830
+ y -= @painter__.font.ascent
831
+ end
832
+ @painter__.text str, x, y
833
+ nil
834
+ end
835
+
836
+ # Draws an image.
837
+ #
838
+ # @overload image(img, a, b)
839
+ # @overload image(img, a, b, c, d)
840
+ #
841
+ # @param img [Image] image to draw
842
+ # @param a [Numeric] horizontal position of the image
843
+ # @param b [Numeric] vertical position of the image
844
+ # @param c [Numeric] width of the image
845
+ # @param d [Numeric] height of the image
846
+ #
847
+ # @return [nil] nil
848
+ #
849
+ def image (img, a, b, c = nil, d = nil)
850
+ assertDrawing__
851
+ x, y, w, h = toXYWH__ @imageMode__, a, b, c || img.width, d || img.height
852
+ @painter__.image img.getInternal__, x, y, w, h
853
+ nil
854
+ end
855
+
856
+ # Copies image.
857
+ #
858
+ # @overload copy(sx, sy, sw, sh, dx, dy, dw, dh)
859
+ # @overload copy(img, sx, sy, sw, sh, dx, dy, dw, dh)
860
+ #
861
+ # @param img [Image] image for copy source
862
+ # @param sx [Numrtic] x position of source region
863
+ # @param sy [Numrtic] y position of source region
864
+ # @param sw [Numrtic] width of source region
865
+ # @param sh [Numrtic] height of source region
866
+ # @param dx [Numrtic] x position of destination region
867
+ # @param dy [Numrtic] y position of destination region
868
+ # @param dw [Numrtic] width of destination region
869
+ # @param dh [Numrtic] height of destination region
870
+ #
871
+ # @return [nil] nil
872
+ #
873
+ def copy (img = nil, sx, sy, sw, sh, dx, dy, dw, dh)
874
+ assertDrawing__
875
+ src = img&.getInternal__ || @window__.canvas
876
+ @painter__.image src, sx, sy, sw, sh, dx, dy, dw, dh
877
+ end
878
+
879
+ # Applies translation matrix to current transformation matrix.
880
+ #
881
+ # @param x [Numeric] horizontal transformation
882
+ # @param y [Numeric] vertical transformation
883
+ #
884
+ # @return [nil] nil
885
+ #
886
+ def translate (x, y)
887
+ assertDrawing__
888
+ @painter__.translate x, y
889
+ nil
890
+ end
891
+
892
+ # Applies scale matrix to current transformation matrix.
893
+ #
894
+ # @overload scale(s)
895
+ # @overload scale(x, y)
896
+ #
897
+ # @param s [Numeric] horizontal and vertical scale
898
+ # @param x [Numeric] horizontal scale
899
+ # @param y [Numeric] vertical scale
900
+ #
901
+ # @return [nil] nil
902
+ #
903
+ def scale (x, y)
904
+ assertDrawing__
905
+ @painter__.scale x, y
906
+ nil
907
+ end
908
+
909
+ # Applies rotation matrix to current transformation matrix.
910
+ #
911
+ # @param angle [Numeric] angle for rotation
912
+ #
913
+ # @return [nil] nil
914
+ #
915
+ def rotate (angle)
916
+ assertDrawing__
917
+ @painter__.rotate toAngle__ angle
918
+ nil
919
+ end
920
+
921
+ # Pushes the current transformation matrix to stack.
922
+ #
923
+ # @return [nil] nil
924
+ #
925
+ def pushMatrix (&block)
926
+ assertDrawing__
927
+ @matrixStack__.push @painter__.matrix
928
+ if block
929
+ block.call
930
+ popMatrix
931
+ end
932
+ nil
933
+ end
934
+
935
+ # Pops the current transformation matrix from stack.
936
+ #
937
+ # @return [nil] nil
938
+ #
939
+ def popMatrix ()
940
+ assertDrawing__
941
+ raise "matrix stack underflow" if @matrixStack__.empty?
942
+ @painter__.matrix = @matrixStack__.pop
943
+ nil
944
+ end
945
+
946
+ # Reset current transformation matrix with identity matrix.
947
+ #
948
+ # @return [nil] nil
949
+ #
950
+ def resetMatrix ()
951
+ assertDrawing__
952
+ @painter__.matrix = 1
953
+ nil
954
+ end
955
+
956
+ # Save current style values to the style stack.
957
+ #
958
+ # @return [nil] nil
959
+ #
960
+ def pushStyle (&block)
961
+ assertDrawing__
962
+ @styleStack__.push [
963
+ @painter__.fill,
964
+ @painter__.stroke,
965
+ @painter__.stroke_width,
966
+ @painter__.stroke_cap,
967
+ @painter__.stroke_join,
968
+ @painter__.font,
969
+ @hsbColor__,
970
+ @colorMaxes__,
971
+ @angleScale__,
972
+ @rectMode__,
973
+ @ellipseMode__,
974
+ @imageMode__
975
+ ]
976
+ if block
977
+ block.call
978
+ popStyle
979
+ end
980
+ nil
981
+ end
982
+
983
+ # Restore style values from the style stack.
984
+ #
985
+ # @return [nil] nil
986
+ #
987
+ def popStyle ()
988
+ assertDrawing__
989
+ raise "style stack underflow" if @styleStack__.empty?
990
+ @painter__.fill,
991
+ @painter__.stroke,
992
+ @painter__.stroke_width,
993
+ @painter__.stroke_cap,
994
+ @painter__.stroke_join,
995
+ @painter__.font,
996
+ @hsbColor__,
997
+ @colorMaxes__,
998
+ @angleScale__,
999
+ @rectMode__,
1000
+ @ellipseMode__,
1001
+ @imageMode__ = @styleStack__.pop
1002
+ nil
1003
+ end
1004
+
1005
+ # Save current styles and transformations to stack.
1006
+ #
1007
+ # @return [nil] nil
1008
+ #
1009
+ def push (&block)
1010
+ pushMatrix
1011
+ pushStyle
1012
+ if block
1013
+ block.call
1014
+ pop
1015
+ end
1016
+ end
1017
+
1018
+ # Restore styles and transformations from stack.
1019
+ #
1020
+ # @return [nil] nil
1021
+ #
1022
+ def pop ()
1023
+ popMatrix
1024
+ popStyle
1025
+ end
1026
+
1027
+ # @private
1028
+ def getInternal__ ()
1029
+ @image__
1030
+ end
1031
+
1032
+ # @private
1033
+ def assertDrawing__ ()
1034
+ raise "call beginDraw() before drawing" unless @drawing
1035
+ end
1036
+
1037
+ end# GraphicsContext
1038
+
1039
+
1040
+ # Draws graphics into an offscreen buffer
1041
+ #
1042
+ class Graphics
1043
+
1044
+ include GraphicsContext
1045
+
1046
+ def initialize (width, height)
1047
+ @image__ = Rays::Image.new width, height
1048
+ setup__ @image__.painter
1049
+ end
1050
+
1051
+ def beginDraw ()
1052
+ @painter__.__send__ :begin_paint
1053
+ super
1054
+ push
1055
+ end
1056
+
1057
+ def endDraw ()
1058
+ pop
1059
+ super
1060
+ @painter__.__send__ :end_paint
1061
+ end
1062
+
1063
+ end# Graphics
1064
+
1065
+
1066
+ module Utility
1067
+
1068
+ # @private
1069
+ DEG2RAD__ = Math::PI / 180.0
1070
+
1071
+ # @private
1072
+ RAD2DEG__ = 180.0 / Math::PI
1073
+
1074
+ # Converts degree to radian.
1075
+ #
1076
+ # @param degree [Numeric] degree to convert
1077
+ #
1078
+ # @return [Numeric] radian
1079
+ #
1080
+ def radians (degree)
1081
+ degree * DEG2RAD__
1082
+ end
1083
+
1084
+ # Converts radian to degree.
1085
+ #
1086
+ # @param radian [Numeric] radian to convert
1087
+ #
1088
+ # @return [Numeric] degree
1089
+ #
1090
+ def degrees (radian)
1091
+ radian * RAD2DEG__
1092
+ end
1093
+
1094
+ # Returns the absolute number of the value.
1095
+ #
1096
+ # @param value [Numeric] number
1097
+ #
1098
+ # @return [Numeric] absolute number
1099
+ #
1100
+ def abs (value)
1101
+ value.abs
1102
+ end
1103
+
1104
+ # Returns the closest integer number greater than or equal to the value.
1105
+ #
1106
+ # @param value [Numeric] number
1107
+ #
1108
+ # @return [Numeric] rounded up number
1109
+ #
1110
+ def ceil (value)
1111
+ value.ceil
1112
+ end
1113
+
1114
+ # Returns the closest integer number less than or equal to the value.
1115
+ #
1116
+ # @param value [Numeric] number
1117
+ #
1118
+ # @return [Numeric] rounded down number
1119
+ #
1120
+ def floor (value)
1121
+ value.floor
1122
+ end
1123
+
1124
+ # Returns the closest integer number.
1125
+ #
1126
+ # @param value [Numeric] number
1127
+ #
1128
+ # @return [Numeric] rounded number
1129
+ #
1130
+ def round (value)
1131
+ value.round
1132
+ end
1133
+
1134
+ # Returns value raised to the power of exponent.
1135
+ #
1136
+ # @param value [Numeric] base number
1137
+ # @param exponent [Numeric] exponent number
1138
+ #
1139
+ # @return [Numeric] value ** exponent
1140
+ #
1141
+ def pow (value, exponent)
1142
+ value ** exponent
1143
+ end
1144
+
1145
+ # Returns squared value.
1146
+ #
1147
+ # @param value [Numeric] number
1148
+ #
1149
+ # @return [Numeric] squared value
1150
+ #
1151
+ def sq (value)
1152
+ value * value
1153
+ end
1154
+
1155
+ # Returns the magnitude (or length) of a vector.
1156
+ #
1157
+ # @overload mag(x, y)
1158
+ # @overload mag(x, y, z)
1159
+ #
1160
+ # @param x [Numeric] x of point
1161
+ # @param y [Numeric] y of point
1162
+ # @param z [Numeric] z of point
1163
+ #
1164
+ # @return [Numeric] magnitude
1165
+ #
1166
+ def mag (*args)
1167
+ x, y, z = *args
1168
+ case args.size
1169
+ when 2 then Math.sqrt x * x + y * y
1170
+ when 3 then Math.sqrt x * x + y * y + z * z
1171
+ else raise ArgumentError
1172
+ end
1173
+ end
1174
+
1175
+ # Returns distance between 2 points.
1176
+ #
1177
+ # @overload dist(x1, y1, x2, y2)
1178
+ # @overload dist(x1, y1, z1, x2, y2, z2)
1179
+ #
1180
+ # @param x1 [Numeric] x of first point
1181
+ # @param y1 [Numeric] y of first point
1182
+ # @param z1 [Numeric] z of first point
1183
+ # @param x2 [Numeric] x of second point
1184
+ # @param y2 [Numeric] y of second point
1185
+ # @param z2 [Numeric] z of second point
1186
+ #
1187
+ # @return [Numeric] distance between 2 points
1188
+ #
1189
+ def dist (*args)
1190
+ case args.size
1191
+ when 4
1192
+ x1, y1, x2, y2 = *args
1193
+ xx, yy = x2 - x1, y2 - y1
1194
+ Math.sqrt xx * xx + yy * yy
1195
+ when 3
1196
+ x1, y1, z1, x2, y2, z2 = *args
1197
+ xx, yy, zz = x2 - x1, y2 - y1, z2 - z1
1198
+ Math.sqrt xx * xx + yy * yy + zz * zz
1199
+ else raise ArgumentError
1200
+ end
1201
+ end
1202
+
1203
+ # Normalize the value from range start..stop into 0..1.
1204
+ #
1205
+ # @param value [Numeric] number to be normalized
1206
+ # @param start [Numeric] lower bound of the range
1207
+ # @param stop [Numeric] upper bound of the range
1208
+ #
1209
+ # @return [Numeric] normalized value between 0..1
1210
+ #
1211
+ def norm (value, start, stop)
1212
+ (value.to_f - start.to_f) / (stop.to_f - start.to_f)
1213
+ end
1214
+
1215
+ # Returns the interpolated number between range start..stop.
1216
+ #
1217
+ # @param start [Numeric] lower bound of the range
1218
+ # @param stop [Numeric] upper bound of the range
1219
+ # @param amount [Numeric] amount to interpolate
1220
+ #
1221
+ # @return [Numeric] interporated number
1222
+ #
1223
+ def lerp (start, stop, amount)
1224
+ start + (stop - start) * amount
1225
+ end
1226
+
1227
+ # Maps a number from range start1..stop1 to range start2..stop2.
1228
+ #
1229
+ # @param value [Numeric] number to be mapped
1230
+ # @param start1 [Numeric] lower bound of the range1
1231
+ # @param stop1 [Numeric] upper bound of the range1
1232
+ # @param start2 [Numeric] lower bound of the range2
1233
+ # @param stop2 [Numeric] upper bound of the range2
1234
+ #
1235
+ # @return [Numeric] mapped number
1236
+ #
1237
+ def map (value, start1, stop1, start2, stop2)
1238
+ lerp start2, stop2, norm(value, start1, stop1)
1239
+ end
1240
+
1241
+ # Returns minimum value.
1242
+ #
1243
+ # @overload min(a, b)
1244
+ # @overload min(a, b, c)
1245
+ # @overload min(array)
1246
+ #
1247
+ # @param a [Numeric] value to compare
1248
+ # @param b [Numeric] value to compare
1249
+ # @param c [Numeric] value to compare
1250
+ # @param array [Numeric] values to compare
1251
+ #
1252
+ # @return [Numeric] minimum value
1253
+ #
1254
+ def min (*args)
1255
+ args.flatten.min
1256
+ end
1257
+
1258
+ # Returns maximum value.
1259
+ #
1260
+ # @overload max(a, b)
1261
+ # @overload max(a, b, c)
1262
+ # @overload max(array)
1263
+ #
1264
+ # @param a [Numeric] value to compare
1265
+ # @param b [Numeric] value to compare
1266
+ # @param c [Numeric] value to compare
1267
+ # @param array [Numeric] values to compare
1268
+ #
1269
+ # @return [Numeric] maximum value
1270
+ #
1271
+ def max (*args)
1272
+ args.flatten.max
1273
+ end
1274
+
1275
+ # Constrains the number between min..max.
1276
+ #
1277
+ # @param value [Numeric] number to be constrained
1278
+ # @param min [Numeric] lower bound of the range
1279
+ # @param max [Numeric] upper bound of the range
1280
+ #
1281
+ # @return [Numeric] constrained number
1282
+ #
1283
+ def constrain (value, min, max)
1284
+ value < min ? min : (value > max ? max : value)
1285
+ end
1286
+
1287
+ # Returns the perlin noise value.
1288
+ #
1289
+ # @overload noise(x)
1290
+ # @overload noise(x, y)
1291
+ # @overload noise(x, y, z)
1292
+ #
1293
+ # @param x [Numeric] horizontal point in noise space
1294
+ # @param y [Numeric] vertical point in noise space
1295
+ # @param z [Numeric] depth point in noise space
1296
+ #
1297
+ # @return [Numeric] noise value (0.0..1.0)
1298
+ #
1299
+ def noise (x, y = 0, z = 0)
1300
+ Rays.perlin(x, y, z) / 2.0 + 0.5
1301
+ end
1302
+
1303
+ # Loads image.
1304
+ #
1305
+ # @param filename [String] file name to load image
1306
+ # @param extension [String] type of image to load (ex. 'png')
1307
+ #
1308
+ # @return [Image] loaded image object
1309
+ #
1310
+ def loadImage (filename, extension = nil)
1311
+ filename = getImage__ filename, extension if filename =~ %r|^https?://|
1312
+ Image.new Rays::Image.load filename
1313
+ end
1314
+
1315
+ # @private
1316
+ private def getImage__ (uri, ext)
1317
+ ext ||= File.extname uri
1318
+ raise "unsupported image type -- #{ext}" unless ext =~ /^\.?(png)$/i
1319
+
1320
+ tmpdir = Pathname(Dir.tmpdir) + Digest::SHA1.hexdigest(self.class.name)
1321
+ path = tmpdir + Digest::SHA1.hexdigest(uri)
1322
+ path = path.sub_ext ext
1323
+
1324
+ unless path.file?
1325
+ URI.open uri do |input|
1326
+ tmpdir.mkdir unless tmpdir.directory?
1327
+ path.open('w') do |output|
1328
+ while buf = input.read(2 ** 16)
1329
+ output.write buf
1330
+ end
1331
+ end
1332
+ end
1333
+ end
1334
+ path.to_s
1335
+ end
1336
+
1337
+ def createGraphics (width, height)
1338
+ Graphics.new width, height
1339
+ end
1340
+
1341
+ end# Utility
1342
+
1343
+
1344
+ # Processing context
1345
+ #
1346
+ module Context
1347
+
1348
+ include GraphicsContext, Utility, Math
1349
+
1350
+ # @private
1351
+ def setup__ (window)
1352
+ @window__ = window
1353
+ @image__ = @window__.canvas
1354
+ super @window__.canvas_painter.paint {background 0.8}
1355
+
1356
+ @loop__ = true
1357
+ @redraw__ = false
1358
+ @frameCount__ = 0
1359
+ @mousePos__ =
1360
+ @mousePrevPos__ = Rays::Point.new 0
1361
+ @mousePressed__ = false
1362
+ @touches__ = []
1363
+
1364
+ @window__.before_draw = proc {beginDraw}
1365
+ @window__.after_draw = proc {endDraw}
1366
+
1367
+ drawFrame = -> {
1368
+ @image__ = @window__.canvas
1369
+ @painter__ = @window__.canvas_painter
1370
+ begin
1371
+ push
1372
+ @drawBlock__.call if @drawBlock__
1373
+ ensure
1374
+ pop
1375
+ @frameCount__ += 1
1376
+ end
1377
+ }
1378
+
1379
+ @window__.draw = proc do |e|
1380
+ if @loop__ || @redraw__
1381
+ @redraw__ = false
1382
+ drawFrame.call
1383
+ end
1384
+ @mousePrevPos__ = @mousePos__
1385
+ end
1386
+
1387
+ updatePointerStates = -> event, pressed = nil {
1388
+ @mousePos__ = event.pos
1389
+ @mousePressed__ = pressed if pressed != nil
1390
+ @touches__ = event.positions.map {|pos| Touch.new pos.x, pos.y}
1391
+ }
1392
+
1393
+ @window__.pointer_down = proc do |e|
1394
+ updatePointerStates.call e, true
1395
+ (@touchStartedBlock__ || @mousePressedBlock__)&.call
1396
+ end
1397
+
1398
+ @window__.pointer_up = proc do |e|
1399
+ updatePointerStates.call e, false
1400
+ (@touchEndedBlock__ || @mouseReleasedBlock__)&.call
1401
+ end
1402
+
1403
+ @window__.pointer_move = proc do |e|
1404
+ updatePointerStates.call e
1405
+ (@touchMovedBlock__ || @mouseMovedBlock__)&.call
1406
+ end
1407
+
1408
+ @window__.pointer_drag = proc do |e|
1409
+ updatePointerStates.call e
1410
+ (@touchMovedBlock__ || @mouseDraggedBlock__)&.call
1411
+ end
1412
+ end
1413
+
1414
+ # Define setup block.
1415
+ #
1416
+ def setup (&block)
1417
+ @window__.setup = block
1418
+ nil
1419
+ end
1420
+
1421
+ # Define draw block.
1422
+ #
1423
+ def draw (&block)
1424
+ @drawBlock__ = block if block
1425
+ nil
1426
+ end
1427
+
1428
+ def key (&block)
1429
+ @window__.key = block
1430
+ nil
1431
+ end
1432
+
1433
+ def mousePressed (&block)
1434
+ @mousePressedBlock__ = block if block
1435
+ @mousePressed__
1436
+ end
1437
+
1438
+ def mouseReleased (&block)
1439
+ @mouseReleasedBlock__ = block if block
1440
+ nil
1441
+ end
1442
+
1443
+ def mouseMoved (&block)
1444
+ @mouseMovedBlock__ = block if block
1445
+ nil
1446
+ end
1447
+
1448
+ def mouseDragged (&block)
1449
+ @mouseDraggedBlock__ = block if block
1450
+ nil
1451
+ end
1452
+
1453
+ def touchStarted (&block)
1454
+ @touchStartedBlock__ = block if block
1455
+ nil
1456
+ end
1457
+
1458
+ def touchEnded (&block)
1459
+ @touchEndedBlock__ = block if block
1460
+ nil
1461
+ end
1462
+
1463
+ def touchMoved (&block)
1464
+ @touchMovedBlock__ = block if block
1465
+ nil
1466
+ end
1467
+
1468
+ # @private
1469
+ private def size__ (width, height)
1470
+ raise 'size() must be called on startup or setup block' if @started__
1471
+
1472
+ @painter__.__send__ :end_paint
1473
+ @window__.__send__ :reset_canvas, width, height
1474
+ @painter__.__send__ :begin_paint
1475
+
1476
+ @auto_resize__ = false
1477
+ end
1478
+
1479
+ def windowWidth ()
1480
+ @window__.width
1481
+ end
1482
+
1483
+ def windowHeight ()
1484
+ @window__.height
1485
+ end
1486
+
1487
+ # Returns number of frames since program started.
1488
+ #
1489
+ # @return [Integer] total number of frames
1490
+ #
1491
+ def frameCount ()
1492
+ @frameCount__
1493
+ end
1494
+
1495
+ # Returns number of frames per second.
1496
+ #
1497
+ # @return [Float] frames per second
1498
+ #
1499
+ def frameRate ()
1500
+ @window__.event.fps
1501
+ end
1502
+
1503
+ # Returns pixel density
1504
+ #
1505
+ # @return [Numeric] pixel density
1506
+ #
1507
+ def displayDensity ()
1508
+ @painter__.pixel_density
1509
+ end
1510
+
1511
+ # Returns mouse x position
1512
+ #
1513
+ # @return [Numeric] horizontal position of mouse
1514
+ #
1515
+ def mouseX ()
1516
+ @mousePos__.x
1517
+ end
1518
+
1519
+ # Returns mouse y position
1520
+ #
1521
+ # @return [Numeric] vertical position of mouse
1522
+ #
1523
+ def mouseY ()
1524
+ @mousePos__.y
1525
+ end
1526
+
1527
+ # Returns mouse x position in previous frame
1528
+ #
1529
+ # @return [Numeric] horizontal position of mouse
1530
+ #
1531
+ def pmouseX ()
1532
+ @mousePrevPos__.x
1533
+ end
1534
+
1535
+ # Returns mouse y position in previous frame
1536
+ #
1537
+ # @return [Numeric] vertical position of mouse
1538
+ #
1539
+ def pmouseY ()
1540
+ @mousePrevPos__.y
1541
+ end
1542
+
1543
+ # Returns array of touches
1544
+ #
1545
+ # @return [Array] Touch objects
1546
+ #
1547
+ def touches ()
1548
+ @touches__
1549
+ end
1550
+
1551
+ # Enables calling draw block on every frame.
1552
+ #
1553
+ # @return [nil] nil
1554
+ #
1555
+ def loop ()
1556
+ @loop__ = true
1557
+ end
1558
+
1559
+ # Disables calling draw block on every frame.
1560
+ #
1561
+ # @return [nil] nil
1562
+ #
1563
+ def noLoop ()
1564
+ @loop__ = false
1565
+ end
1566
+
1567
+ # Calls draw block to redraw frame.
1568
+ #
1569
+ # @return [nil] nil
1570
+ #
1571
+ def redraw ()
1572
+ @redraw__ = true
1573
+ end
1574
+
1575
+ end# Context
1576
+
1577
+
1578
+ end# Processing
1579
+
1580
+
1581
+ end# RubySketch