rubysketch 0.2.4 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 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