rubysketch 0.2.3 → 0.3.0

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