rubysketch 0.3.2

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