rubysketch 0.2.4 → 0.3.1

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