rubysketch 0.2.3 → 0.3.0

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