processing 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,3211 @@
1
+ module Processing
2
+
3
+
4
+ # Processing compatible API
5
+ #
6
+ module Processing
7
+
8
+
9
+ # @private
10
+ DEG2RAD__ = Math::PI / 180.0
11
+
12
+ # @private
13
+ RAD2DEG__ = 180.0 / Math::PI
14
+
15
+
16
+ # Vector class.
17
+ #
18
+ class Vector
19
+
20
+ include Comparable
21
+
22
+ # Initialize vector object.
23
+ #
24
+ # @overload new()
25
+ # @overload new(x)
26
+ # @overload new(x, y)
27
+ # @overload new(x, y, z)
28
+ # @overload new(v)
29
+ # @overload new(a)
30
+ #
31
+ # @param x [Numeric] x of vector
32
+ # @param y [Numeric] y of vector
33
+ # @param z [Numeric] z of vector
34
+ # @param v [Vector] vector object to copy
35
+ # @param a [Array] array like [x, y, z]
36
+ #
37
+ def initialize(x = 0, y = 0, z = 0, context: nil)
38
+ @point = case x
39
+ when Rays::Point then x.dup
40
+ when Vector then x.getInternal__.dup
41
+ when Array then Rays::Point.new x[0] || 0, x[1] || 0, x[2] || 0
42
+ else Rays::Point.new x || 0, y || 0, z || 0
43
+ end
44
+ @context = context || Context.context__
45
+ end
46
+
47
+ # Initializer for dup or clone
48
+ #
49
+ def initialize_copy(o)
50
+ @point = o.getInternal__.dup
51
+ end
52
+
53
+ # Copy vector object
54
+ #
55
+ # @return [Vector] duplicated vector object
56
+ #
57
+ alias copy dup
58
+
59
+ # Sets x, y and z.
60
+ #
61
+ # @overload set(x)
62
+ # @overload set(x, y)
63
+ # @overload set(x, y, z)
64
+ # @overload set(v)
65
+ # @overload set(a)
66
+ #
67
+ # @param x [Numeric] x of vector
68
+ # @param y [Numeric] y of vector
69
+ # @param z [Numeric] z of vector
70
+ # @param v [Vector] vector object to copy
71
+ # @param a [Array] array with x, y, z
72
+ #
73
+ # @return [nil] nil
74
+ #
75
+ def set(*args)
76
+ initialize(*args)
77
+ self
78
+ end
79
+
80
+ # Gets x value.
81
+ #
82
+ # @return [Numeric] x value of vector
83
+ #
84
+ def x()
85
+ @point.x
86
+ end
87
+
88
+ # Gets y value.
89
+ #
90
+ # @return [Numeric] y value of vector
91
+ #
92
+ def y()
93
+ @point.y
94
+ end
95
+
96
+ # Gets z value.
97
+ #
98
+ # @return [Numeric] z value of vector
99
+ #
100
+ def z()
101
+ @point.z
102
+ end
103
+
104
+ # Sets x value.
105
+ #
106
+ # @return [Numeric] x value of vector
107
+ #
108
+ def x=(x)
109
+ @point.x = x
110
+ end
111
+
112
+ # Sets y value.
113
+ #
114
+ # @return [Numeric] y value of vector
115
+ #
116
+ def y=(y)
117
+ @point.y = y
118
+ end
119
+
120
+ # Sets z value.
121
+ #
122
+ # @return [Numeric] z value of vector
123
+ #
124
+ def z=(z)
125
+ @point.z = z
126
+ end
127
+
128
+ # Returns the interpolated vector between 2 vectors.
129
+ #
130
+ # @overload lerp(v, amount)
131
+ # @overload lerp(x, y, amount)
132
+ # @overload lerp(x, y, z, amount)
133
+ #
134
+ # @param v [Vector] vector to interpolate
135
+ # @param x [Numeric] x of vector to interpolate
136
+ # @param y [Numeric] y of vector to interpolate
137
+ # @param z [Numeric] z of vector to interpolate
138
+ # @param amount [Numeric] amount to interpolate
139
+ #
140
+ # @return [Vector] interporated vector
141
+ #
142
+ def lerp(*args, amount)
143
+ v = toVector__(*args)
144
+ self.x = x + (v.x - x) * amount
145
+ self.y = y + (v.y - y) * amount
146
+ self.z = z + (v.z - z) * amount
147
+ self
148
+ end
149
+
150
+ # Returns the interpolated vector between 2 vectors.
151
+ #
152
+ # @param v1 [Vector] vector to interpolate
153
+ # @param v2 [Vector] vector to interpolate
154
+ # @param amount [Numeric] amount to interpolate
155
+ #
156
+ # @return [Vector] interporated vector
157
+ #
158
+ def self.lerp(v1, v2, amount)
159
+ v1.dup.lerp v2, amount
160
+ end
161
+
162
+ # Returns x, y, z as an array
163
+ #
164
+ # @return [Array] array of x, y, z
165
+ #
166
+ def array()
167
+ @point.to_a 3
168
+ end
169
+
170
+ # Adds a vector.
171
+ #
172
+ # @overload add(v)
173
+ # @overload add(x, y)
174
+ # @overload add(x, y, z)
175
+ #
176
+ # @param v [Vector] vector to add
177
+ # @param x [Vector] x of vector to add
178
+ # @param y [Vector] y of vector to add
179
+ # @param z [Vector] z of vector to add
180
+ #
181
+ # @return [Vector] added vector
182
+ #
183
+ def add(*args)
184
+ @point += toVector__(*args).getInternal__
185
+ self
186
+ end
187
+
188
+ # Subtracts a vector.
189
+ #
190
+ # @overload sub(v)
191
+ # @overload sub(x, y)
192
+ # @overload sub(x, y, z)
193
+ #
194
+ # @param v [Vector] vector to subtract
195
+ # @param x [Vector] x of vector to subtract
196
+ # @param y [Vector] y of vector to subtract
197
+ # @param z [Vector] z of vector to subtract
198
+ #
199
+ # @return [Vector] subtracted vector
200
+ #
201
+ def sub(*args)
202
+ @point -= toVector__(*args).getInternal__
203
+ self
204
+ end
205
+
206
+ # Multiplies a vector by scalar.
207
+ #
208
+ # @param num [Numeric] number to multiply the vector
209
+ #
210
+ # @return [Vector] multiplied vector
211
+ #
212
+ def mult(num)
213
+ @point *= num
214
+ self
215
+ end
216
+
217
+ # Divides a vector by scalar.
218
+ #
219
+ # @param num [Numeric] number to divide the vector
220
+ #
221
+ # @return [Vector] divided vector
222
+ #
223
+ def div(num)
224
+ @point /= num
225
+ self
226
+ end
227
+
228
+ # Adds a vector.
229
+ #
230
+ # @param v [Vector] vector to add
231
+ #
232
+ # @return [Vector] added vector
233
+ #
234
+ def +(v)
235
+ dup.add v
236
+ end
237
+
238
+ # Subtracts a vector.
239
+ #
240
+ # @param v [Vector] vector to subtract
241
+ #
242
+ # @return [Vector] subtracted vector
243
+ #
244
+ def -(v)
245
+ dup.sub v
246
+ end
247
+
248
+ # Multiplies a vector by scalar.
249
+ #
250
+ # @param num [Numeric] number to multiply the vector
251
+ #
252
+ # @return [Vector] multiplied vector
253
+ #
254
+ def *(num)
255
+ dup.mult num
256
+ end
257
+
258
+ # Divides a vector by scalar.
259
+ #
260
+ # @param num [Numeric] number to divide the vector
261
+ #
262
+ # @return [Vector] divided vector
263
+ #
264
+ def /(num)
265
+ dup.div num
266
+ end
267
+
268
+ # Adds 2 vectors.
269
+ #
270
+ # @overload add(v1, v2)
271
+ # @overload add(v1, v2, target)
272
+ #
273
+ # @param v1 [Vector] a vector
274
+ # @param v2 [Vector] another vector
275
+ # @param target [Vector] vector to store added vector
276
+ #
277
+ # @return [Vector] added vector
278
+ #
279
+ def self.add(v1, v2, target = nil)
280
+ v = v1 + v2
281
+ target.set v if self === target
282
+ v
283
+ end
284
+
285
+ # Subtracts 2 vectors.
286
+ #
287
+ # @overload sub(v1, v2)
288
+ # @overload sub(v1, v2, target)
289
+ #
290
+ # @param v1 [Vector] a vector
291
+ # @param v2 [Vector] another vector
292
+ # @param target [Vector] vector to store subtracted vector
293
+ #
294
+ # @return [Vector] subtracted vector
295
+ #
296
+ def self.sub(v1, v2, target = nil)
297
+ v = v1 - v2
298
+ target.set v if self === target
299
+ v
300
+ end
301
+
302
+ # Multiplies a vector by scalar.
303
+ #
304
+ # @overload mult(v, num)
305
+ # @overload mult(v, num, target)
306
+ #
307
+ # @param v [Vector] a vector
308
+ # @param num [Numeric] number to multiply the vector
309
+ # @param target [Vector] vector to store multiplied vector
310
+ #
311
+ # @return [Vector] multiplied vector
312
+ #
313
+ def self.mult(v1, num, target = nil)
314
+ v = v1 * num
315
+ target.set v if self === target
316
+ v
317
+ end
318
+
319
+ # Divides a vector by scalar.
320
+ #
321
+ # @overload div(v, num)
322
+ # @overload div(v, num, target)
323
+ #
324
+ # @param v [Vector] a vector
325
+ # @param num [Numeric] number to divide the vector
326
+ # @param target [Vector] vector to store divided vector
327
+ #
328
+ # @return [Vector] divided vector
329
+ #
330
+ def self.div(v1, num, target = nil)
331
+ v = v1 / num
332
+ target.set v if self === target
333
+ v
334
+ end
335
+
336
+ # Returns the length of the vector.
337
+ #
338
+ # @return [Numeric] length
339
+ #
340
+ def mag()
341
+ @point.length
342
+ end
343
+
344
+ # Returns squared length of the vector.
345
+ #
346
+ # @return [Numeric] squared length
347
+ #
348
+ def magSq()
349
+ Rays::Point::dot(@point, @point)
350
+ end
351
+
352
+ # Changes the length of the vector.
353
+ #
354
+ # @overload setMag(len)
355
+ # @overload setMag(target, len)
356
+ #
357
+ # @param len [Numeric] length of new vector
358
+ # @param target [Vector] vector to store new vector
359
+ #
360
+ # @return [Vector] vector with new length
361
+ #
362
+ def setMag(target = nil, len)
363
+ (target || self).set @point.normal * len
364
+ end
365
+
366
+ # Changes the length of the vector to 1.0.
367
+ #
368
+ # @param target [Vector] vector to store the normalized vector
369
+ #
370
+ # @return [Vector] normalized vector
371
+ #
372
+ def normalize(target = nil)
373
+ (target || self).set @point.normal
374
+ end
375
+
376
+ # Changes the length of the vector if it's length is greater than the max value.
377
+ #
378
+ # @param max [Numeric] max length
379
+ #
380
+ # @return [Vector] new vector
381
+ #
382
+ def limit(max)
383
+ setMag max if magSq > max ** 2
384
+ self
385
+ end
386
+
387
+ # Returns the distance of 2 vectors.
388
+ #
389
+ # @param v [Vector] a vector
390
+ #
391
+ # @return [Numeric] the distance
392
+ #
393
+ def dist(v)
394
+ (self - v).mag
395
+ end
396
+
397
+ # Returns the distance of 2 vectors.
398
+ #
399
+ # @param v1 [Vector] a vector
400
+ # @param v2 [Vector] another vector
401
+ #
402
+ # @return [Numeric] the distance
403
+ #
404
+ def self.dist(v1, v2)
405
+ v1.dist v2
406
+ end
407
+
408
+ # Calculates the dot product of 2 vectors.
409
+ #
410
+ # @overload dot(v)
411
+ # @overload dot(x, y)
412
+ # @overload dot(x, y, z)
413
+ #
414
+ # @param v [Vector] a vector
415
+ # @param x [Numeric] x of vector
416
+ # @param y [Numeric] y of vector
417
+ # @param z [Numeric] z of vector
418
+ #
419
+ # @return [Numeric] result of dot product
420
+ #
421
+ def dot(*args)
422
+ Rays::Point::dot getInternal__, toVector__(*args).getInternal__
423
+ end
424
+
425
+ # Calculates the dot product of 2 vectors.
426
+ #
427
+ # @param v1 [Vector] a vector
428
+ # @param v2 [Vector] another vector
429
+ #
430
+ # @return [Numeric] result of dot product
431
+ #
432
+ def self.dot(v1, v2)
433
+ v1.dot v2
434
+ end
435
+
436
+ # Calculates the cross product of 2 vectors.
437
+ #
438
+ # @overload cross(v)
439
+ # @overload cross(x, y)
440
+ # @overload cross(x, y, z)
441
+ #
442
+ # @param v [Vector] a vector
443
+ # @param x [Numeric] x of vector
444
+ # @param y [Numeric] y of vector
445
+ # @param z [Numeric] z of vector
446
+ #
447
+ # @return [Numeric] result of cross product
448
+ #
449
+ def cross(a, *rest)
450
+ target = self.class === rest.last ? rest.pop : nil
451
+ v = self.class.new Rays::Point::cross getInternal__, toVector__(a, *rest).getInternal__
452
+ target.set v if self.class === target
453
+ v
454
+ end
455
+
456
+ # Calculates the cross product of 2 vectors.
457
+ #
458
+ # @param v1 [Vector] a vector
459
+ # @param v2 [Vector] another vector
460
+ #
461
+ # @return [Numeric] result of cross product
462
+ #
463
+ def self.cross(v1, v2, target = nil)
464
+ v1.cross v2, target
465
+ end
466
+
467
+ # Rotate the vector.
468
+ #
469
+ # @param angle [Numeric] the angle of rotation
470
+ #
471
+ # @return [Vector] rotated this object
472
+ #
473
+ def rotate(angle)
474
+ angle = @context ? @context.toAngle__(angle) : angle * RAD2DEG__
475
+ @point.rotate! angle
476
+ self
477
+ end
478
+
479
+ # Returns the angle of rotation for this vector.
480
+ #
481
+ # @return [Numeric] the angle in radians
482
+ #
483
+ def heading()
484
+ Math.atan2 y, x
485
+ end
486
+
487
+ # Returns rotated new vector.
488
+ #
489
+ # @param angle [Numeric] the angle of rotation
490
+ # @param target [Vector] vector to store new vector
491
+ #
492
+ # @return [Vector] rotated vector
493
+ #
494
+ def self.fromAngle(angle, target = nil)
495
+ v = self.new(1, 0, 0).rotate(angle)
496
+ target.set v if target
497
+ v
498
+ end
499
+
500
+ # Returns angle between 2 vectors.
501
+ #
502
+ # @param v1 [Vector] a vector
503
+ # @param v2 [Vector] another vector
504
+ #
505
+ # @return [Numeric] angle in radians
506
+ #
507
+ def self.angleBetween(v1, v2)
508
+ x1, y1, z1 = v1.array
509
+ x2, y2, z2 = v2.array
510
+ return 0 if (x1 == 0 && y1 == 0 && z1 == 0) || (x2 == 0 && y2 == 0 && z2 == 0)
511
+
512
+ x = dot(v1, v2) / (v1.mag * v2.mag)
513
+ return Math::PI if x <= -1
514
+ return 0 if x >= 1
515
+ return Math.acos x
516
+ end
517
+
518
+ # Returns a new 2D unit vector with a random direction.
519
+ #
520
+ # @param target [Vector] a vector to store the new vector
521
+ #
522
+ # @return [Vector] a random vector
523
+ #
524
+ def self.random2D(target = nil)
525
+ v = self.fromAngle rand 0.0...(Math::PI * 2)
526
+ target.set v if target
527
+ v
528
+ end
529
+
530
+ # Returns a new 3D unit vector with a random direction.
531
+ #
532
+ # @param target [Vector] a vector to store the new vector
533
+ #
534
+ # @return [Vector] a random vector
535
+ #
536
+ def self.random3D(target = nil)
537
+ angle = rand 0.0...(Math::PI * 2)
538
+ z = rand(-1.0..1.0)
539
+ z2 = z ** 2
540
+ x = Math.sqrt(1.0 - z2) * Math.cos(angle)
541
+ y = Math.sqrt(1.0 - z2) * Math.sin(angle)
542
+ v = self.new x, y, z
543
+ target.set v if target
544
+ v
545
+ end
546
+
547
+ # @private
548
+ def inspect()
549
+ "<##{self.class.name} #{x}, #{y}, #{z}>"
550
+ end
551
+
552
+ # @private
553
+ def <=>(o)
554
+ @point <=> o.getInternal__
555
+ end
556
+
557
+ # @private
558
+ def getInternal__()
559
+ @point
560
+ end
561
+
562
+ # @private
563
+ private def toVector__(*args)
564
+ self.class === args.first ? args.first : self.class.new(*args)
565
+ end
566
+
567
+ end# Vector
568
+
569
+
570
+ # Image object.
571
+ #
572
+ class Image
573
+
574
+ # @private
575
+ def initialize(image)
576
+ @image = image
577
+ end
578
+
579
+ # Gets width of image.
580
+ #
581
+ # @return [Numeric] width of image
582
+ #
583
+ def width()
584
+ @image.width
585
+ end
586
+
587
+ # Gets height of image.
588
+ #
589
+ # @return [Numeric] height of image
590
+ #
591
+ def height()
592
+ @image.height
593
+ end
594
+
595
+ # Applies an image filter.
596
+ #
597
+ # overload filter(shader)
598
+ # overload filter(type)
599
+ # overload filter(type, param)
600
+ #
601
+ # @param shader [Shader] a fragment shader to apply
602
+ # @param type [THRESHOLD, GRAY, INVERT, BLUR] filter type
603
+ # @param param [Numeric] a parameter for each filter
604
+ #
605
+ def filter(*args)
606
+ @filter = Shader.createFilter__(*args)
607
+ end
608
+
609
+ # Resizes image.
610
+ #
611
+ # @param width [Numeric] width for resized image
612
+ # @param height [Numeric] height for resized image
613
+ #
614
+ # @return [nil] nil
615
+ #
616
+ def resize(width, height)
617
+ @image = Rays::Image.new(width, height).paint do |painter|
618
+ painter.image @image, 0, 0, width, height
619
+ end
620
+ nil
621
+ end
622
+
623
+ # Copies image.
624
+ #
625
+ # @overload copy(sx, sy, sw, sh, dx, dy, dw, dh)
626
+ # @overload copy(img, sx, sy, sw, sh, dx, dy, dw, dh)
627
+ #
628
+ # @param img [Image] image for copy source
629
+ # @param sx [Numrtic] x position of source region
630
+ # @param sy [Numrtic] y position of source region
631
+ # @param sw [Numrtic] width of source region
632
+ # @param sh [Numrtic] height of source region
633
+ # @param dx [Numrtic] x position of destination region
634
+ # @param dy [Numrtic] y position of destination region
635
+ # @param dw [Numrtic] width of destination region
636
+ # @param dh [Numrtic] height of destination region
637
+ #
638
+ # @return [nil] nil
639
+ #
640
+ def copy(img = nil, sx, sy, sw, sh, dx, dy, dw, dh)
641
+ blend img, sx, sy, sw, sh, dx, dy, dw, dh, :normal
642
+ end
643
+
644
+ # Blends image.
645
+ #
646
+ # @overload blend(sx, sy, sw, sh, dx, dy, dw, dh, mode)
647
+ # @overload blend(img, sx, sy, sw, sh, dx, dy, dw, dh, mode)
648
+ #
649
+ # @param img [Image] image for blend source
650
+ # @param sx [Numeric] x position of source region
651
+ # @param sy [Numeric] y position of source region
652
+ # @param sw [Numeric] width of source region
653
+ # @param sh [Numeric] height of source region
654
+ # @param dx [Numeric] x position of destination region
655
+ # @param dy [Numeric] y position of destination region
656
+ # @param dw [Numeric] width of destination region
657
+ # @param dh [Numeric] height of destination region
658
+ # @param mode [BLEND, ADD, SUBTRACT, LIGHTEST, DARKEST, EXCLUSION, MULTIPLY, SCREEN, REPLACE] blend mode
659
+ #
660
+ # @return [nil] nil
661
+ #
662
+ def blend(img = nil, sx, sy, sw, sh, dx, dy, dw, dh, mode)
663
+ img ||= self
664
+ @image.paint do |painter|
665
+ img.drawImage__ painter, sx, sy, sw, sh, dx, dy, dw, dh, blend_mode: mode
666
+ end
667
+ nil
668
+ end
669
+
670
+ # Saves image to file.
671
+ #
672
+ # @param filename [String] file name to save image
673
+ #
674
+ def save(filename)
675
+ @image.save filename
676
+ end
677
+
678
+ # @private
679
+ def getInternal__()
680
+ @image
681
+ end
682
+
683
+ # @private
684
+ def drawImage__(painter, *args, **states)
685
+ shader = painter.shader || @filter&.getInternal__
686
+ painter.push shader: shader, **states do |_|
687
+ painter.image getInternal__, *args
688
+ end
689
+ end
690
+
691
+ end# Image
692
+
693
+
694
+ # Font object.
695
+ #
696
+ class Font
697
+
698
+ # @private
699
+ def initialize(font)
700
+ @font = font
701
+ end
702
+
703
+ # Returns bounding box.
704
+ #
705
+ # @overload textBounds(str)
706
+ # @overload textBounds(str, x, y)
707
+ # @overload textBounds(str, x, y, fontSize)
708
+ #
709
+ # @param str [String] text to calculate bounding box
710
+ # @param x [Numeric] horizontal position of bounding box
711
+ # @param y [Numeric] vertical position of bounding box
712
+ # @param fontSize [Numeric] font size
713
+ #
714
+ # @return [TextBounds] bounding box for text
715
+ #
716
+ def textBounds(str, x = 0, y = 0, fontSize = nil)
717
+ f = fontSize ? Rays::Font.new(@font.name, fontSize) : @font
718
+ TextBounds.new x, y, x + f.width(str), y + f.height
719
+ end
720
+
721
+ end# Font
722
+
723
+
724
+ # Bounding box for text.
725
+ #
726
+ class TextBounds
727
+
728
+ # Horizontal position
729
+ #
730
+ attr_reader :x
731
+
732
+ # Vertical position
733
+ #
734
+ attr_reader :y
735
+
736
+ # Width of bounding box
737
+ #
738
+ attr_reader :w
739
+
740
+ # Height of bounding box
741
+ #
742
+ attr_reader :h
743
+
744
+ # @private
745
+ def initialize(x, y, w, h)
746
+ @x, @y, @w, @h = x, y, w, h
747
+ end
748
+
749
+ end# TextBounds
750
+
751
+
752
+ # Touch object.
753
+ #
754
+ class Touch
755
+
756
+ # Identifier of each touch
757
+ #
758
+ attr_reader :id
759
+
760
+ # Horizontal position of touch
761
+ #
762
+ attr_reader :x
763
+
764
+ # Vertical position of touch
765
+ #
766
+ attr_reader :y
767
+
768
+ # @private
769
+ def initialize(id, x, y)
770
+ @id, @x, @y = id, x, y
771
+ end
772
+
773
+ end# Touch
774
+
775
+
776
+ # Shader object.
777
+ #
778
+ class Shader
779
+
780
+ # Initialize shader object.
781
+ #
782
+ # @param vertSrc [String] vertex shader source
783
+ # @param fragSrc [String] fragment shader source
784
+ #
785
+ def initialize(vertSrc, fragSrc)
786
+ @shader = Rays::Shader.new fragSrc, vertSrc, ENV__
787
+ end
788
+
789
+ # Sets uniform variables.
790
+ #
791
+ # @overload set(name, a)
792
+ # @overload set(name, a, b)
793
+ # @overload set(name, a, b, c)
794
+ # @overload set(name, a, b, c, d)
795
+ # @overload set(name, nums)
796
+ # @overload set(name, vec)
797
+ # @overload set(name, vec, ncoords)
798
+ # @overload set(name, tex)
799
+ #
800
+ # @param name [String] uniform variable name
801
+ # @param a [Numeric] int or float value
802
+ # @param b [Numeric] int or float value
803
+ # @param c [Numeric] int or float value
804
+ # @param d [Numeric] int or float value
805
+ # @param nums [Array] int or float array
806
+ # @param vec [Vector] vector
807
+ # @param ncoords [Integer] number of coordinates, max 4
808
+ # @param tex [Image] texture image
809
+ #
810
+ def setUniform(name, *args)
811
+ arg = args.first
812
+ case
813
+ when Array === arg
814
+ @shader.uniform name, *arg
815
+ when Numeric === arg
816
+ @shader.uniform name, *args
817
+ when Vector === arg
818
+ vec, ncoords = args
819
+ @shader.uniform name, vec.getInternal__.to_a(ncoords || 3)
820
+ when arg.respond_to?(:getInternal__)
821
+ @shader.uniform name, arg.getInternal__
822
+ else
823
+ raise ArgumentError
824
+ end
825
+ end
826
+
827
+ alias set setUniform
828
+
829
+ # @private
830
+ def getInternal__()
831
+ @shader
832
+ end
833
+
834
+ # @private
835
+ ENV__ = {
836
+ attribute_position: [:vertex, :position],
837
+ attribute_texcoord: :texCoord,
838
+ attribute_color: :color,
839
+ varying_position: :vertPosition,
840
+ varying_texcoord: :vertTexCoord,
841
+ varying_color: :vertColor,
842
+ uniform_position_matrix: [:transform, :transformMatrix],
843
+ uniform_texcoord_matrix: :texMatrix,
844
+ uniform_texcoord_min: :texMin,
845
+ uniform_texcoord_max: :texMax,
846
+ uniform_texcoord_offset: :texOffset,
847
+ uniform_texture: [:texMap, :texture]
848
+ }.freeze
849
+
850
+ # @private
851
+ def self.createFilter__(*args)
852
+ case arg = args.shift
853
+ when Shader
854
+ arg
855
+ when :threshold
856
+ self.new(nil, <<~END).tap {|sh| sh.set :threshold, (args.shift || 0.5)}
857
+ uniform float threshold;
858
+ uniform sampler2D texMap;
859
+ varying vec4 vertTexCoord;
860
+ varying vec4 vertColor;
861
+ void main() {
862
+ vec4 col = texture2D(texMap, vertTexCoord.xy) * vertColor;
863
+ float gray = col.r * 0.3 + col.g * 0.59 + col.b * 0.11;
864
+ gl_FragColor = vec4(vec3(gray > threshold ? 1.0 : 0.0), 1.0);
865
+ }
866
+ END
867
+ when :gray
868
+ self.new nil, <<~END
869
+ uniform sampler2D texMap;
870
+ varying vec4 vertTexCoord;
871
+ varying vec4 vertColor;
872
+ void main() {
873
+ vec4 col = texture2D(texMap, vertTexCoord.xy);
874
+ float gray = col.r * 0.3 + col.g * 0.59 + col.b * 0.11;
875
+ gl_FragColor = vec4(vec3(gray), 1.0) * vertColor;
876
+ }
877
+ END
878
+ when :invert
879
+ self.new nil, <<~END
880
+ uniform sampler2D texMap;
881
+ varying vec4 vertTexCoord;
882
+ varying vec4 vertColor;
883
+ void main() {
884
+ vec4 col = texture2D(texMap, vertTexCoord.xy);
885
+ gl_FragColor = vec4(vec3(1.0 - col.rgb), 1.0) * vertColor;
886
+ }
887
+ END
888
+ when :blur
889
+ self.new(nil, <<~END).tap {|sh| sh.set :radius, (args.shift || 1).to_f}
890
+ #define PI 3.1415926538
891
+ uniform float radius;
892
+ uniform sampler2D texMap;
893
+ uniform vec3 texMin;
894
+ uniform vec3 texMax;
895
+ uniform vec3 texOffset;
896
+ varying vec4 vertTexCoord;
897
+ varying vec4 vertColor;
898
+ float gaussian(vec2 pos, float sigma) {
899
+ float s2 = sigma * sigma;
900
+ return 1.0 / (2.0 * PI * s2) * exp(-(dot(pos, pos) / (2.0 * s2)));
901
+ }
902
+ void main() {
903
+ float sigma = radius * 0.5;
904
+ vec3 color = vec3(0.0);
905
+ float total_weight = 0.0;
906
+ for (float y = -radius; y < radius; y += 1.0)
907
+ for (float x = -radius; x < radius; x += 1.0) {
908
+ vec2 offset = vec2(x, y);
909
+ float weight = gaussian(offset, sigma);
910
+ vec2 texcoord = vertTexCoord.xy + offset * texOffset.xy;
911
+ if (
912
+ texcoord.x < texMin.x || texMax.x < texcoord.x ||
913
+ texcoord.y < texMin.y || texMax.y < texcoord.y
914
+ ) continue;
915
+ color += texture2D(texMap, texcoord).rgb * weight;
916
+ total_weight += weight;
917
+ }
918
+ gl_FragColor = vec4(color / total_weight, 1.0) * vertColor;
919
+ }
920
+ END
921
+ else
922
+ nil
923
+ end
924
+ end
925
+
926
+ end# Shader
927
+
928
+
929
+ # Camera object.
930
+ #
931
+ class Capture
932
+
933
+ # Returns a list of available camera device names
934
+ #
935
+ # @return [Array] device name list
936
+ #
937
+ def self.list()
938
+ Rays::Camera.device_names
939
+ end
940
+
941
+ # Initialize camera object.
942
+ #
943
+ # @overload Capture.new()
944
+ # @overload Capture.new(cameraName)
945
+ # @overload Capture.new(requestWidth, requestHeight)
946
+ # @overload Capture.new(requestWidth, requestHeight, cameraName)
947
+ #
948
+ # @param requestWidth [Integer] captured image width
949
+ # @param requestHeight [Integer] captured image height
950
+ # @param cameraName [String] camera device name
951
+ #
952
+ def initialize(*args)
953
+ width, height, name =
954
+ if args.empty?
955
+ [-1, -1, nil]
956
+ elsif args[0].kind_of?(String)
957
+ [-1, -1, args[0]]
958
+ elsif args[0].kind_of?(Numeric) && args[1].kind_of?(Numeric)
959
+ [args[0], args[1], args[2]]
960
+ else
961
+ raise ArgumentError
962
+ end
963
+ @camera = Rays::Camera.new width, height, device_name: name
964
+ end
965
+
966
+ # Start capturing.
967
+ #
968
+ # @return [nil] nil
969
+ #
970
+ def start()
971
+ raise "Failed to start capture" unless @camera.start
972
+ nil
973
+ end
974
+
975
+ # Stop capturing.
976
+ #
977
+ # @return [nil] nil
978
+ #
979
+ def stop()
980
+ @camera.stop
981
+ nil
982
+ end
983
+
984
+ # Returns is the next captured image available?
985
+ #
986
+ # @return [Boolean] true means object has next frame
987
+ #
988
+ def available()
989
+ @camera.active?
990
+ end
991
+
992
+ # Reads next frame image
993
+ #
994
+ def read()
995
+ @camera.image
996
+ end
997
+
998
+ # Returns the width of captured image
999
+ #
1000
+ # @return [Numeric] the width of captured image
1001
+ #
1002
+ def width()
1003
+ @camera.image&.width || 0
1004
+ end
1005
+
1006
+ # Returns the height of captured image
1007
+ #
1008
+ # @return [Numeric] the height of captured image
1009
+ #
1010
+ def height()
1011
+ @camera.image&.height || 0
1012
+ end
1013
+
1014
+ # Applies an image filter.
1015
+ #
1016
+ # overload filter(shader)
1017
+ # overload filter(type)
1018
+ # overload filter(type, param)
1019
+ #
1020
+ # @param shader [Shader] a fragment shader to apply
1021
+ # @param type [THRESHOLD, GRAY, INVERT, BLUR] filter type
1022
+ # @param param [Numeric] a parameter for each filter
1023
+ #
1024
+ def filter(*args)
1025
+ @filter = Shader.createFilter__(*args)
1026
+ end
1027
+
1028
+ # @private
1029
+ def getInternal__()
1030
+ @camera.image || (@dummyImage ||= Rays::Image.new 1, 1)
1031
+ end
1032
+
1033
+ # @private
1034
+ def drawImage__(painter, *args, **states)
1035
+ shader = painter.shader || @filter&.getInternal__
1036
+ painter.push shader: shader, **states do |_|
1037
+ painter.image getInternal__, *args
1038
+ end
1039
+ end
1040
+
1041
+ end# Capture
1042
+
1043
+
1044
+ # Drawing context
1045
+ #
1046
+ module GraphicsContext
1047
+
1048
+ # PI
1049
+ #
1050
+ PI = Math::PI
1051
+
1052
+ # PI / 2
1053
+ #
1054
+ HALF_PI = PI / 2
1055
+
1056
+ # PI / 4
1057
+ #
1058
+ QUARTER_PI = PI / 4
1059
+
1060
+ # PI * 2
1061
+ #
1062
+ TWO_PI = PI * 2
1063
+
1064
+ # PI * 2
1065
+ #
1066
+ TAU = PI * 2
1067
+
1068
+ # RGBA format for createImage().
1069
+ #
1070
+ RGBA = :rgba
1071
+
1072
+ # RGB format for createImage, or RGB mode for colorMode().
1073
+ #
1074
+ RGB = :rgb
1075
+
1076
+ # HSB mode for colorMode().
1077
+ #
1078
+ HSB = :hsb
1079
+
1080
+ # Radian mode for angleMode().
1081
+ #
1082
+ RADIANS = :radians
1083
+
1084
+ # Degree mode for angleMode().
1085
+ #
1086
+ DEGREES = :degrees
1087
+
1088
+ # Mode for rectMode(), ellipseMode() and imageMode().
1089
+ #
1090
+ CORNER = :corner
1091
+
1092
+ # Mode for rectMode(), ellipseMode() and imageMode().
1093
+ #
1094
+ CORNERS = :corners
1095
+
1096
+ # Mode for rectMode(), ellipseMode(), imageMode() and textAlign().
1097
+ #
1098
+ CENTER = :center
1099
+
1100
+ # Mode for rectMode() and ellipseMode().
1101
+ #
1102
+ RADIUS = :radius
1103
+
1104
+ # Mode for strokeCap().
1105
+ #
1106
+ PROJECT = :square
1107
+
1108
+ # Mode for strokeJoin().
1109
+ #
1110
+ MITER = :miter
1111
+
1112
+ # Mode for strokeCap() and strokeJoin().
1113
+ #
1114
+ ROUND = :round
1115
+
1116
+ # Mode for strokeCap() and strokeJoin().
1117
+ #
1118
+ SQUARE = :butt
1119
+
1120
+ # Mode for blendMode().
1121
+ #
1122
+ BLEND = :normal
1123
+
1124
+ # Mode for blendMode().
1125
+ #
1126
+ ADD = :add
1127
+
1128
+ # Mode for blendMode().
1129
+ #
1130
+ SUBTRACT = :subtract
1131
+
1132
+ # Mode for blendMode().
1133
+ #
1134
+ LIGHTEST = :lightest
1135
+
1136
+ # Mode for blendMode().
1137
+ #
1138
+ DARKEST = :darkest
1139
+
1140
+ # Mode for blendMode().
1141
+ #
1142
+ EXCLUSION = :exclusion
1143
+
1144
+ # Mode for blendMode().
1145
+ #
1146
+ MULTIPLY = :multiply
1147
+
1148
+ # Mode for blendMode().
1149
+ #
1150
+ SCREEN = :screen
1151
+
1152
+ # Mode for blendMode().
1153
+ #
1154
+ REPLACE = :replace
1155
+
1156
+ # Key code or Mode for textAlign().
1157
+ LEFT = :left
1158
+
1159
+ # Key code or Mode for textAlign().
1160
+ RIGHT = :right
1161
+
1162
+ # Mode for textAlign().
1163
+ TOP = :top
1164
+
1165
+ # Mode for textAlign().
1166
+ BOTTOM = :bottom
1167
+
1168
+ # Mode for textAlign().
1169
+ BASELINE = :baseline
1170
+
1171
+ # Filter type for filter()
1172
+ THRESHOLD = :threshold
1173
+
1174
+ # Filter type for filter()
1175
+ GRAY = :gray
1176
+
1177
+ # Filter type for filter()
1178
+ INVERT = :invert
1179
+
1180
+ # Filter type for filter()
1181
+ BLUR = :blur
1182
+
1183
+ # Key codes.
1184
+ ENTER = :enter
1185
+ SPACE = :space
1186
+ TAB = :tab
1187
+ DELETE = :delete
1188
+ BACKSPACE = :backspace
1189
+ ESC = :escape
1190
+ HOME = :home
1191
+ #END = :end
1192
+ PAGEUP = :pageup
1193
+ PAGEDOWN = :pagedown
1194
+ CLEAR = :clear
1195
+ SHIFT = :shift
1196
+ CONTROL = :control
1197
+ ALT = :alt
1198
+ WIN = :win
1199
+ COMMAND = :command
1200
+ OPTION = :option
1201
+ FUNCTION = :function
1202
+ CAPSLOCK = :capslock
1203
+ SECTION = :section
1204
+ HELP = :help
1205
+ F1 = :f1
1206
+ F2 = :f2
1207
+ F3 = :f3
1208
+ F4 = :f4
1209
+ F5 = :f5
1210
+ F6 = :f6
1211
+ F7 = :f7
1212
+ F8 = :f8
1213
+ F9 = :f9
1214
+ F10 = :f10
1215
+ F11 = :f11
1216
+ F12 = :f12
1217
+ F13 = :f13
1218
+ F14 = :f14
1219
+ F15 = :f15
1220
+ F16 = :f16
1221
+ F17 = :f17
1222
+ F18 = :f18
1223
+ F19 = :f19
1224
+ F20 = :f20
1225
+ F21 = :f21
1226
+ F22 = :f22
1227
+ F23 = :f23
1228
+ F24 = :f24
1229
+ UP = :up
1230
+ DOWN = :down
1231
+
1232
+ # @private
1233
+ def init__(image, painter)
1234
+ @drawing__ = false
1235
+ @hsbColor__ = false
1236
+ @colorMaxes__ = [1.0] * 4
1237
+ @angleScale__ = 1.0
1238
+ @rectMode__ = nil
1239
+ @ellipseMode__ = nil
1240
+ @imageMode__ = nil
1241
+ @textAlignH__ = nil
1242
+ @textAlignV__ = nil
1243
+ @tint__ = nil
1244
+ @filter__ = nil
1245
+ @matrixStack__ = []
1246
+ @styleStack__ = []
1247
+ @fontCache__ = {}
1248
+
1249
+ updateCanvas__ image, painter
1250
+
1251
+ colorMode RGB, 255
1252
+ angleMode RADIANS
1253
+ rectMode CORNER
1254
+ ellipseMode CENTER
1255
+ imageMode CORNER
1256
+ blendMode BLEND
1257
+ strokeCap ROUND
1258
+ strokeJoin MITER
1259
+ textAlign LEFT
1260
+
1261
+ fill 255
1262
+ stroke 0
1263
+ strokeWeight 1
1264
+ noTint
1265
+ end
1266
+
1267
+ # @private
1268
+ def updateCanvas__(image, painter)
1269
+ @image__, @painter__ = image, painter
1270
+ end
1271
+
1272
+ # @private
1273
+ def beginDraw__()
1274
+ raise "call beginDraw() before drawing" if @drawing__
1275
+ @matrixStack__.clear
1276
+ @styleStack__.clear
1277
+ @drawing__ = true
1278
+ end
1279
+
1280
+ # @private
1281
+ def endDraw__()
1282
+ raise unless @drawing__
1283
+ @drawing__ = false
1284
+ end
1285
+
1286
+ def width()
1287
+ @image__.width
1288
+ end
1289
+
1290
+ def height()
1291
+ @image__.height
1292
+ end
1293
+
1294
+ # Sets color mode and max color values.
1295
+ #
1296
+ # @overload colorMode(mode)
1297
+ # @overload colorMode(mode, max)
1298
+ # @overload colorMode(mode, max1, max2, max3)
1299
+ # @overload colorMode(mode, max1, max2, max3, maxA)
1300
+ #
1301
+ # @param mode [RGB, HSB] RGB or HSB
1302
+ # @param max [Numeric] max values for all color values
1303
+ # @param max1 [Numeric] max value for red or hue
1304
+ # @param max2 [Numeric] max value for green or saturation
1305
+ # @param max3 [Numeric] max value for blue or brightness
1306
+ # @param maxA [Numeric] max value for alpha
1307
+ #
1308
+ # @return [nil] nil
1309
+ #
1310
+ def colorMode(mode, *maxes)
1311
+ mode = mode.downcase.to_sym
1312
+ raise ArgumentError, "invalid color mode: #{mode}" unless [RGB, HSB].include?(mode)
1313
+ raise ArgumentError unless [0, 1, 3, 4].include?(maxes.size)
1314
+
1315
+ @hsbColor__ = mode == HSB
1316
+ case maxes.size
1317
+ when 1 then @colorMaxes__ = [maxes.first.to_f] * 4
1318
+ when 3, 4 then @colorMaxes__[0...maxes.size] = maxes.map &:to_f
1319
+ end
1320
+ nil
1321
+ end
1322
+
1323
+ # @private
1324
+ private def toRGBA__(*args)
1325
+ a, b, c, d = args
1326
+ return parseColor__(a, b || alphaMax__) if a.kind_of?(String)
1327
+
1328
+ rgba = case args.size
1329
+ when 1, 2 then [a, a, a, b || alphaMax__]
1330
+ when 3, 4 then [a, b, c, d || alphaMax__]
1331
+ else raise ArgumentError
1332
+ end
1333
+ rgba = rgba.map.with_index {|value, i| value / @colorMaxes__[i]}
1334
+ color = @hsbColor__ ? Rays::Color.hsv(*rgba) : Rays::Color.new(*rgba)
1335
+ color.to_a
1336
+ end
1337
+
1338
+ # @private
1339
+ private def parseColor__(str, alpha)
1340
+ result = str.match(/^\s*##{'([0-9a-f]{2})' * 3}\s*$/i)
1341
+ raise ArgumentError, "invalid color code: '#{str}'" unless result
1342
+
1343
+ rgb = result[1..3].map.with_index {|hex, i| hex.to_i(16) / 255.0}
1344
+ return *rgb, (alpha / alphaMax__)
1345
+ end
1346
+
1347
+ # @private
1348
+ private def alphaMax__()
1349
+ @colorMaxes__[3]
1350
+ end
1351
+
1352
+ # Sets angle mode.
1353
+ #
1354
+ # @param mode [RADIANS, DEGREES] RADIANS or DEGREES
1355
+ #
1356
+ # @return [nil] nil
1357
+ #
1358
+ def angleMode(mode)
1359
+ @angleScale__ = case mode.downcase.to_sym
1360
+ when RADIANS then RAD2DEG__
1361
+ when DEGREES then 1.0
1362
+ else raise ArgumentError, "invalid angle mode: #{mode}"
1363
+ end
1364
+ nil
1365
+ end
1366
+
1367
+ # @private
1368
+ def toAngle__(angle)
1369
+ angle * @angleScale__
1370
+ end
1371
+
1372
+ # Sets rect mode. Default is CORNER.
1373
+ #
1374
+ # CORNER -> rect(left, top, width, height)
1375
+ # CORNERS -> rect(left, top, right, bottom)
1376
+ # CENTER -> rect(center_x, center_y, width, height)
1377
+ # RADIUS -> rect(center_x, center_y, radius_h, radius_v)
1378
+ #
1379
+ # @param mode [CORNER, CORNERS, CENTER, RADIUS]
1380
+ #
1381
+ # @return [nil] nil
1382
+ #
1383
+ def rectMode(mode)
1384
+ @rectMode__ = mode
1385
+ end
1386
+
1387
+ # Sets ellipse mode. Default is CENTER.
1388
+ #
1389
+ # CORNER -> ellipse(left, top, width, height)
1390
+ # CORNERS -> ellipse(left, top, right, bottom)
1391
+ # CENTER -> ellipse(center_x, center_y, width, height)
1392
+ # RADIUS -> ellipse(center_x, center_y, radius_h, radius_v)
1393
+ #
1394
+ # @param mode [CORNER, CORNERS, CENTER, RADIUS]
1395
+ #
1396
+ # @return [nil] nil
1397
+ #
1398
+ def ellipseMode(mode)
1399
+ @ellipseMode__ = mode
1400
+ end
1401
+
1402
+ # Sets image mode. Default is CORNER.
1403
+ #
1404
+ # CORNER -> image(img, left, top, width, height)
1405
+ # CORNERS -> image(img, left, top, right, bottom)
1406
+ # CENTER -> image(img, center_x, center_y, width, height)
1407
+ #
1408
+ # @param mode [CORNER, CORNERS, CENTER]
1409
+ #
1410
+ # @return [nil] nil
1411
+ #
1412
+ def imageMode(mode)
1413
+ @imageMode__ = mode
1414
+ end
1415
+
1416
+ # @private
1417
+ private def toXYWH__(mode, a, b, c, d)
1418
+ case mode
1419
+ when CORNER then [a, b, c, d]
1420
+ when CORNERS then [a, b, c - a, d - b]
1421
+ when CENTER then [a - c / 2.0, b - d / 2.0, c, d]
1422
+ when RADIUS then [a - c, b - d, c * 2, d * 2]
1423
+ else raise ArgumentError # ToDo: refine error message
1424
+ end
1425
+ end
1426
+
1427
+ # Sets blend mode. Default is BLEND.
1428
+ #
1429
+ # @param mode [BLEND, ADD, SUBTRACT, LIGHTEST, DARKEST, EXCLUSION, MULTIPLY, SCREEN, REPLACE]
1430
+ #
1431
+ # @return [nil] nil
1432
+ #
1433
+ def blendMode(mode)
1434
+ @painter__.blend_mode = mode
1435
+ nil
1436
+ end
1437
+
1438
+ # Sets fill color.
1439
+ #
1440
+ # @overload fill(rgb)
1441
+ # @overload fill(rgb, alpha)
1442
+ # @overload fill(gray)
1443
+ # @overload fill(gray, alpha)
1444
+ # @overload fill(r, g, b)
1445
+ # @overload fill(r, g, b, alpha)
1446
+ #
1447
+ # @param rgb [String] color code like '#00AAFF'
1448
+ # @param gray [Integer] gray value (0..255)
1449
+ # @param r [Integer] red value (0..255)
1450
+ # @param g [Integer] green value (0..255)
1451
+ # @param b [Integer] blue value (0..255)
1452
+ # @param alpha [Integer] alpha value (0..255)
1453
+ #
1454
+ # @return [nil] nil
1455
+ #
1456
+ def fill(*args)
1457
+ @painter__.fill(*toRGBA__(*args))
1458
+ nil
1459
+ end
1460
+
1461
+ # Disables filling.
1462
+ #
1463
+ # @return [nil] nil
1464
+ #
1465
+ def noFill()
1466
+ @painter__.fill nil
1467
+ nil
1468
+ end
1469
+
1470
+ # Sets stroke color.
1471
+ #
1472
+ # @overload stroke(rgb)
1473
+ # @overload stroke(rgb, alpha)
1474
+ # @overload stroke(gray)
1475
+ # @overload stroke(gray, alpha)
1476
+ # @overload stroke(r, g, b)
1477
+ # @overload stroke(r, g, b, alpha)
1478
+ #
1479
+ # @param rgb [String] color code like '#00AAFF'
1480
+ # @param gray [Integer] gray value (0..255)
1481
+ # @param r [Integer] red value (0..255)
1482
+ # @param g [Integer] green value (0..255)
1483
+ # @param b [Integer] blue value (0..255)
1484
+ # @param alpha [Integer] alpha value (0..255)
1485
+ #
1486
+ # @return [nil] nil
1487
+ #
1488
+ def stroke(*args)
1489
+ @painter__.stroke(*toRGBA__(*args))
1490
+ nil
1491
+ end
1492
+
1493
+ # Disables drawing stroke.
1494
+ #
1495
+ # @return [nil] nil
1496
+ #
1497
+ def noStroke()
1498
+ @painter__.stroke nil
1499
+ nil
1500
+ end
1501
+
1502
+ # Sets stroke weight.
1503
+ #
1504
+ # @param weight [Numeric] width of stroke
1505
+ #
1506
+ # @return [nil] nil
1507
+ #
1508
+ def strokeWeight(weight)
1509
+ @painter__.stroke_width weight
1510
+ nil
1511
+ end
1512
+
1513
+ # Sets stroke cap mode.
1514
+ #
1515
+ # @param cap [BUTT, ROUND, SQUARE]
1516
+ #
1517
+ # @return [nil] nil
1518
+ #
1519
+ def strokeCap(cap)
1520
+ @painter__.stroke_cap cap
1521
+ nil
1522
+ end
1523
+
1524
+ # Sets stroke join mode.
1525
+ #
1526
+ # @param join [MITER, ROUND, SQUARE]
1527
+ #
1528
+ # @return [nil] nil
1529
+ #
1530
+ def strokeJoin(join)
1531
+ @painter__.stroke_join join
1532
+ nil
1533
+ end
1534
+
1535
+ # Sets fill color for drawing images.
1536
+ #
1537
+ # @overload tint(rgb)
1538
+ # @overload tint(rgb, alpha)
1539
+ # @overload tint(gray)
1540
+ # @overload tint(gray, alpha)
1541
+ # @overload tint(r, g, b)
1542
+ # @overload tint(r, g, b, alpha)
1543
+ #
1544
+ # @param rgb [String] color code like '#00AAFF'
1545
+ # @param gray [Integer] gray value (0..255)
1546
+ # @param r [Integer] red value (0..255)
1547
+ # @param g [Integer] green value (0..255)
1548
+ # @param b [Integer] blue value (0..255)
1549
+ # @param alpha [Integer] alpha value (0..255)
1550
+ #
1551
+ # @return [nil] nil
1552
+ #
1553
+ def tint(*args)
1554
+ @tint__ = args
1555
+ nil
1556
+ end
1557
+
1558
+ # Resets tint color.
1559
+ #
1560
+ # @return [nil] nil
1561
+ #
1562
+ def noTint()
1563
+ @tint__ = nil
1564
+ end
1565
+
1566
+ # Limits the drawable rectangle.
1567
+ #
1568
+ # The parameters a, b, c, and d are determined by rectMode().
1569
+ #
1570
+ # @param a [Numeric] horizontal position of the drawable area, by default
1571
+ # @param b [Numeric] vertical position of the drawable area, by default
1572
+ # @param c [Numeric] width of the drawable area, by default
1573
+ # @param d [Numeric] height of the drawable area, by default
1574
+ #
1575
+ # @return [nil] nil
1576
+ #
1577
+ def clip(a, b, c, d)
1578
+ x, y, w, h = toXYWH__ @imageMode__, a, b, c, d
1579
+ @painter__.clip x, y, w, h
1580
+ nil
1581
+ end
1582
+
1583
+ # Disables clipping.
1584
+ #
1585
+ # @return [nil] nil
1586
+ #
1587
+ def noClip()
1588
+ @painter__.no_clip
1589
+ nil
1590
+ end
1591
+
1592
+ # Sets font.
1593
+ #
1594
+ # @overload textFont(font)
1595
+ # @overload textFont(name)
1596
+ # @overload textFont(font, size)
1597
+ # @overload textFont(name, size)
1598
+ #
1599
+ # @param font [Font] font
1600
+ # @param name [String] font name
1601
+ # @param size [Numeric] font size (max 256)
1602
+ #
1603
+ # @return [Font] current font
1604
+ #
1605
+ def textFont(font = nil, size = nil)
1606
+ setFont__ font, size if font || size
1607
+ Font.new @painter__.font
1608
+ end
1609
+
1610
+ # Sets text size.
1611
+ #
1612
+ # @param size [Numeric] font size (max 256)
1613
+ #
1614
+ # @return [nil] nil
1615
+ #
1616
+ def textSize(size)
1617
+ setFont__ nil, size
1618
+ nil
1619
+ end
1620
+
1621
+ def textWidth(str)
1622
+ @painter__.font.width str
1623
+ end
1624
+
1625
+ def textAscent()
1626
+ @painter__.font.ascent
1627
+ end
1628
+
1629
+ def textDescent()
1630
+ @painter__.font.descent
1631
+ end
1632
+
1633
+ def textAlign(horizontal, vertical = BASELINE)
1634
+ @textAlignH__ = horizontal
1635
+ @textAlignV__ = vertical
1636
+ end
1637
+
1638
+ # @private
1639
+ def setFont__(fontOrName, size)
1640
+ name = case fontOrName
1641
+ when Font then fontOrName.name
1642
+ else fontOrName || @painter__.font.name
1643
+ end
1644
+ size ||= @painter__.font.size
1645
+ size = 256 if size > 256
1646
+ font = @fontCache__[[name, size]] ||= Rays::Font.new name, size
1647
+ @painter__.font = font
1648
+ end
1649
+
1650
+ # Sets shader.
1651
+ #
1652
+ # @param shader [Shader] a shader to apply
1653
+ #
1654
+ # @return [nil] nil
1655
+ #
1656
+ def shader(shader)
1657
+ @painter__.shader shader&.getInternal__
1658
+ nil
1659
+ end
1660
+
1661
+ # Resets shader.
1662
+ #
1663
+ # @return [nil] nil
1664
+ #
1665
+ def resetShader()
1666
+ @painter__.no_shader
1667
+ nil
1668
+ end
1669
+
1670
+ # Applies an image filter to screen.
1671
+ #
1672
+ # overload filter(shader)
1673
+ # overload filter(type)
1674
+ # overload filter(type, param)
1675
+ #
1676
+ # @param shader [Shader] a shader to apply
1677
+ # @param type [THRESHOLD, GRAY, INVERT, BLUR] filter type
1678
+ # @param param [Numeric] a parameter for each filter
1679
+ #
1680
+ def filter(*args)
1681
+ @filter__ = Shader.createFilter__(*args)
1682
+ end
1683
+
1684
+ # Clears screen.
1685
+ #
1686
+ # @overload background(str)
1687
+ # @overload background(str, alpha)
1688
+ # @overload background(gray)
1689
+ # @overload background(gray, alpha)
1690
+ # @overload background(r, g, b)
1691
+ # @overload background(r, g, b, alpha)
1692
+ #
1693
+ # @param str [String] color code like '#00AAFF'
1694
+ # @param gray [Integer] gray value (0..255)
1695
+ # @param r [Integer] red value (0..255)
1696
+ # @param g [Integer] green value (0..255)
1697
+ # @param b [Integer] blue value (0..255)
1698
+ # @param alpha [Integer] alpha value (0..255)
1699
+ #
1700
+ # @return [nil] nil
1701
+ #
1702
+ def background(*args)
1703
+ assertDrawing__
1704
+ rgba = toRGBA__(*args)
1705
+ if rgba[3] == 1
1706
+ @painter__.background(*rgba)
1707
+ else
1708
+ @painter__.push fill: rgba, stroke: :none do |_|
1709
+ @painter__.rect 0, 0, width, height
1710
+ end
1711
+ end
1712
+ nil
1713
+ end
1714
+
1715
+ # Draws a point.
1716
+ #
1717
+ # @param x [Numeric] horizontal position
1718
+ # @param y [Numeric] vertical position
1719
+ #
1720
+ # @return [nil] nil
1721
+ #
1722
+ def point(x, y)
1723
+ assertDrawing__
1724
+ @painter__.line x, y, x, y
1725
+ nil
1726
+ end
1727
+
1728
+ # Draws a line.
1729
+ #
1730
+ # @param x1 [Numeric] horizontal position of first point
1731
+ # @param y1 [Numeric] vertical position of first point
1732
+ # @param x2 [Numeric] horizontal position of second point
1733
+ # @param y2 [Numeric] vertical position of second point
1734
+ #
1735
+ # @return [nil] nil
1736
+ #
1737
+ def line(x1, y1, x2, y2)
1738
+ assertDrawing__
1739
+ @painter__.line x1, y1, x2, y2
1740
+ nil
1741
+ end
1742
+
1743
+ # Draws a rectangle.
1744
+ #
1745
+ # The parameters a, b, c, and d are determined by rectMode().
1746
+ #
1747
+ # @overload rect(a, b, c, d)
1748
+ # @overload rect(a, b, c, d, r)
1749
+ # @overload rect(a, b, c, d, tl, tr, br, bl)
1750
+ #
1751
+ # @param a [Numeric] horizontal position of the shape, by default
1752
+ # @param b [Numeric] vertical position of the shape, by default
1753
+ # @param c [Numeric] width of the shape, by default
1754
+ # @param d [Numeric] height of the shape, by default
1755
+ # @param r [Numeric] radius for all corners
1756
+ # @param tl [Numeric] radius for top-left corner
1757
+ # @param tr [Numeric] radius for top-right corner
1758
+ # @param br [Numeric] radius for bottom-right corner
1759
+ # @param bl [Numeric] radius for bottom-left corner
1760
+ #
1761
+ # @return [nil] nil
1762
+ #
1763
+ def rect(a, b, c, d, *args)
1764
+ assertDrawing__
1765
+ x, y, w, h = toXYWH__ @rectMode__, a, b, c, d
1766
+ case args.size
1767
+ when 0 then @painter__.rect x, y, w, h
1768
+ when 1 then @painter__.rect x, y, w, h, round: args[0]
1769
+ when 4 then @painter__.rect x, y, w, h, lt: args[0], rt: args[1], rb: args[2], lb: args[3]
1770
+ else raise ArgumentError # ToDo: refine error message
1771
+ end
1772
+ nil
1773
+ end
1774
+
1775
+ # Draws an ellipse.
1776
+ #
1777
+ # The parameters a, b, c, and d are determined by ellipseMode().
1778
+ #
1779
+ # @param a [Numeric] horizontal position of the shape, by default
1780
+ # @param b [Numeric] vertical position of the shape, by default
1781
+ # @param c [Numeric] width of the shape, by default
1782
+ # @param d [Numeric] height of the shape, by default
1783
+ #
1784
+ # @return [nil] nil
1785
+ #
1786
+ def ellipse(a, b, c, d)
1787
+ assertDrawing__
1788
+ x, y, w, h = toXYWH__ @ellipseMode__, a, b, c, d
1789
+ @painter__.ellipse x, y, w, h
1790
+ nil
1791
+ end
1792
+
1793
+ # Draws a circle.
1794
+ #
1795
+ # @param x [Numeric] horizontal position of the shape
1796
+ # @param y [Numeric] vertical position of the shape
1797
+ # @param extent [Numeric] width and height of the shape
1798
+ #
1799
+ # @return [nil] nil
1800
+ #
1801
+ def circle(x, y, extent)
1802
+ ellipse x, y, extent, extent
1803
+ end
1804
+
1805
+ # Draws an arc.
1806
+ #
1807
+ # The parameters a, b, c, and d are determined by ellipseMode().
1808
+ #
1809
+ # @param a [Numeric] horizontal position of the shape, by default
1810
+ # @param b [Numeric] vertical position of the shape, by default
1811
+ # @param c [Numeric] width of the shape, by default
1812
+ # @param d [Numeric] height of the shape, by default
1813
+ # @param start [Numeric] angle to start the arc
1814
+ # @param stop [Numeric] angle to stop the arc
1815
+ #
1816
+ # @return [nil] nil
1817
+ #
1818
+ def arc(a, b, c, d, start, stop)
1819
+ assertDrawing__
1820
+ x, y, w, h = toXYWH__ @ellipseMode__, a, b, c, d
1821
+ start = toAngle__(-start)
1822
+ stop = toAngle__(-stop)
1823
+ @painter__.ellipse x, y, w, h, from: start, to: stop
1824
+ nil
1825
+ end
1826
+
1827
+ # Draws a square.
1828
+ #
1829
+ # @param x [Numeric] horizontal position of the shape
1830
+ # @param y [Numeric] vertical position of the shape
1831
+ # @param extent [Numeric] width and height of the shape
1832
+ #
1833
+ # @return [nil] nil
1834
+ #
1835
+ def square(x, y, extent)
1836
+ rect x, y, extent, extent
1837
+ end
1838
+
1839
+ # Draws a triangle.
1840
+ #
1841
+ # @param x1 [Numeric] horizontal position of first point
1842
+ # @param y1 [Numeric] vertical position of first point
1843
+ # @param x2 [Numeric] horizontal position of second point
1844
+ # @param y2 [Numeric] vertical position of second point
1845
+ # @param x3 [Numeric] horizontal position of third point
1846
+ # @param y3 [Numeric] vertical position of third point
1847
+ #
1848
+ # @return [nil] nil
1849
+ #
1850
+ def triangle(x1, y1, x2, y2, x3, y3)
1851
+ assertDrawing__
1852
+ @painter__.line x1, y1, x2, y2, x3, y3, loop: true
1853
+ nil
1854
+ end
1855
+
1856
+ # Draws a quad.
1857
+ #
1858
+ # @param x1 [Numeric] horizontal position of first point
1859
+ # @param y1 [Numeric] vertical position of first point
1860
+ # @param x2 [Numeric] horizontal position of second point
1861
+ # @param y2 [Numeric] vertical position of second point
1862
+ # @param x3 [Numeric] horizontal position of third point
1863
+ # @param y3 [Numeric] vertical position of third point
1864
+ # @param x4 [Numeric] horizontal position of fourth point
1865
+ # @param y4 [Numeric] vertical position of fourth point
1866
+ #
1867
+ # @return [nil] nil
1868
+ #
1869
+ def quad(x1, y1, x2, y2, x3, y3, x4, y4)
1870
+ assertDrawing__
1871
+ @painter__.line x1, y1, x2, y2, x3, y3, x4, y4, loop: true
1872
+ nil
1873
+ end
1874
+
1875
+ # Draws a Catmull-Rom spline curve.
1876
+ #
1877
+ # @param cx1 [Numeric] horizontal position of beginning control point
1878
+ # @param cy1 [Numeric] vertical position of beginning control point
1879
+ # @param x1 [Numeric] horizontal position of first point
1880
+ # @param y1 [Numeric] vertical position of first point
1881
+ # @param x2 [Numeric] horizontal position of second point
1882
+ # @param y2 [Numeric] vertical position of second point
1883
+ # @param cx2 [Numeric] horizontal position of ending control point
1884
+ # @param cy2 [Numeric] vertical position of ending control point
1885
+ #
1886
+ # @return [nil] nil
1887
+ #
1888
+ def curve(cx1, cy1, x1, y1, x2, y2, cx2, cy2)
1889
+ assertDrawing__
1890
+ @painter__.curve cx1, cy1, x1, y1, x2, y2, cx2, cy2
1891
+ nil
1892
+ end
1893
+
1894
+ # Draws a Bezier spline curve.
1895
+ #
1896
+ # @param x1 [Numeric] horizontal position of first point
1897
+ # @param y1 [Numeric] vertical position of first point
1898
+ # @param cx1 [Numeric] horizontal position of first control point
1899
+ # @param cy1 [Numeric] vertical position of first control point
1900
+ # @param cx2 [Numeric] horizontal position of second control point
1901
+ # @param cy2 [Numeric] vertical position of second control point
1902
+ # @param x2 [Numeric] horizontal position of second point
1903
+ # @param y2 [Numeric] vertical position of second point
1904
+ #
1905
+ # @return [nil] nil
1906
+ #
1907
+ def bezier(x1, y1, cx1, cy1, cx2, cy2, x2, y2)
1908
+ assertDrawing__
1909
+ @painter__.bezier x1, y1, cx1, cy1, cx2, cy2, x2, y2
1910
+ nil
1911
+ end
1912
+
1913
+ # Draws a text.
1914
+ #
1915
+ # The parameters a, b, c, and d are determined by rectMode().
1916
+ #
1917
+ # @overload text(str)
1918
+ # @overload text(str, x, y)
1919
+ # @overload text(str, a, b, c, d)
1920
+ #
1921
+ # @param str [String] text to draw
1922
+ # @param x [Numeric] horizontal position of the text
1923
+ # @param y [Numeric] vertical position of the text
1924
+ # @param a [Numeric] horizontal position of the text, by default
1925
+ # @param b [Numeric] vertical position of the text, by default
1926
+ # @param c [Numeric] width of the text, by default
1927
+ # @param d [Numeric] height of the text, by default
1928
+ #
1929
+ # @return [nil] nil
1930
+ #
1931
+ def text(str, x, y, x2 = nil, y2 = nil)
1932
+ assertDrawing__
1933
+ if x2
1934
+ raise ArgumentError, "missing y2 parameter" unless y2
1935
+ x, y, w, h = toXYWH__ @rectMode__, x, y, x2, y2
1936
+ case @textAlignH__
1937
+ when RIGHT then x += w - @painter__.font.width(str)
1938
+ when CENTER then x += (w - @painter__.font.width(str)) / 2
1939
+ end
1940
+ case @textAlignV__
1941
+ when BOTTOM then y += h - @painter__.font.height
1942
+ when CENTER then y += (h - @painter__.font.height) / 2
1943
+ else
1944
+ end
1945
+ else
1946
+ y -= @painter__.font.ascent
1947
+ end
1948
+ @painter__.text str, x, y
1949
+ nil
1950
+ end
1951
+
1952
+ # Draws an image.
1953
+ #
1954
+ # The parameters a, b, c, and d are determined by imageMode().
1955
+ #
1956
+ # @overload image(img, a, b)
1957
+ # @overload image(img, a, b, c, d)
1958
+ #
1959
+ # @param img [Image] image to draw
1960
+ # @param a [Numeric] horizontal position of the image, by default
1961
+ # @param b [Numeric] vertical position of the image, by default
1962
+ # @param c [Numeric] width of the image, by default
1963
+ # @param d [Numeric] height of the image, by default
1964
+ #
1965
+ # @return [nil] nil
1966
+ #
1967
+ def image(img, a, b, c = nil, d = nil)
1968
+ assertDrawing__
1969
+ x, y, w, h = toXYWH__ @imageMode__, a, b, c || img.width, d || img.height
1970
+ tint = @tint__ ? toRGBA__(*@tint__) : 1
1971
+ img.drawImage__ @painter__, x, y, w, h, fill: tint, stroke: :none
1972
+ nil
1973
+ end
1974
+
1975
+ # Copies image.
1976
+ #
1977
+ # @overload copy(sx, sy, sw, sh, dx, dy, dw, dh)
1978
+ # @overload copy(img, sx, sy, sw, sh, dx, dy, dw, dh)
1979
+ #
1980
+ # @param img [Image] image for copy source
1981
+ # @param sx [Numrtic] x position of source region
1982
+ # @param sy [Numrtic] y position of source region
1983
+ # @param sw [Numrtic] width of source region
1984
+ # @param sh [Numrtic] height of source region
1985
+ # @param dx [Numrtic] x position of destination region
1986
+ # @param dy [Numrtic] y position of destination region
1987
+ # @param dw [Numrtic] width of destination region
1988
+ # @param dh [Numrtic] height of destination region
1989
+ #
1990
+ # @return [nil] nil
1991
+ #
1992
+ def copy(img = nil, sx, sy, sw, sh, dx, dy, dw, dh)
1993
+ blend img, sx, sy, sw, sh, dx, dy, dw, dh, BLEND
1994
+ end
1995
+
1996
+ # Blends image.
1997
+ #
1998
+ # @overload blend(sx, sy, sw, sh, dx, dy, dw, dh, mode)
1999
+ # @overload blend(img, sx, sy, sw, sh, dx, dy, dw, dh, mode)
2000
+ #
2001
+ # @param img [Image] image for blend source
2002
+ # @param sx [Numrtic] x position of source region
2003
+ # @param sy [Numrtic] y position of source region
2004
+ # @param sw [Numrtic] width of source region
2005
+ # @param sh [Numrtic] height of source region
2006
+ # @param dx [Numrtic] x position of destination region
2007
+ # @param dy [Numrtic] y position of destination region
2008
+ # @param dw [Numrtic] width of destination region
2009
+ # @param dh [Numrtic] height of destination region
2010
+ # @param mode [BLEND, ADD, SUBTRACT, LIGHTEST, DARKEST, EXCLUSION, MULTIPLY, SCREEN, REPLACE] blend mode
2011
+ #
2012
+ # @return [nil] nil
2013
+ #
2014
+ def blend(img = nil, sx, sy, sw, sh, dx, dy, dw, dh, mode)
2015
+ assertDrawing__
2016
+ img ||= self
2017
+ img.drawImage__ @painter__, sx, sy, sw, sh, dx, dy, dw, dh, blend_mode: mode
2018
+ end
2019
+
2020
+ # Saves screen image to file.
2021
+ #
2022
+ # @param filename [String] file name to save image
2023
+ #
2024
+ def save(filename)
2025
+ @window__.canvas_image.save filename
2026
+ end
2027
+
2028
+ # Applies translation matrix to current transformation matrix.
2029
+ #
2030
+ # @overload translate(x, y)
2031
+ # @overload translate(x, y, z)
2032
+ #
2033
+ # @param x [Numeric] left/right translation
2034
+ # @param y [Numeric] up/down translation
2035
+ # @param y [Numeric] forward/backward translation
2036
+ #
2037
+ # @return [nil] nil
2038
+ #
2039
+ def translate(x, y, z = 0)
2040
+ assertDrawing__
2041
+ @painter__.translate x, y, z
2042
+ nil
2043
+ end
2044
+
2045
+ # Applies scale matrix to current transformation matrix.
2046
+ #
2047
+ # @overload scale(s)
2048
+ # @overload scale(x, y)
2049
+ #
2050
+ # @param s [Numeric] horizontal and vertical scale
2051
+ # @param x [Numeric] horizontal scale
2052
+ # @param y [Numeric] vertical scale
2053
+ #
2054
+ # @return [nil] nil
2055
+ #
2056
+ def scale(x, y)
2057
+ assertDrawing__
2058
+ @painter__.scale x, y
2059
+ nil
2060
+ end
2061
+
2062
+ # Applies rotation matrix to current transformation matrix.
2063
+ #
2064
+ # @param angle [Numeric] angle for rotation
2065
+ #
2066
+ # @return [nil] nil
2067
+ #
2068
+ def rotate(angle)
2069
+ assertDrawing__
2070
+ @painter__.rotate toAngle__ angle
2071
+ nil
2072
+ end
2073
+
2074
+ # Pushes the current transformation matrix to stack.
2075
+ #
2076
+ # @return [nil] nil
2077
+ #
2078
+ def pushMatrix(&block)
2079
+ assertDrawing__
2080
+ @matrixStack__.push @painter__.matrix
2081
+ block.call if block
2082
+ nil
2083
+ ensure
2084
+ popMatrix if block
2085
+ end
2086
+
2087
+ # Pops the current transformation matrix from stack.
2088
+ #
2089
+ # @return [nil] nil
2090
+ #
2091
+ def popMatrix()
2092
+ assertDrawing__
2093
+ raise "matrix stack underflow" if @matrixStack__.empty?
2094
+ @painter__.matrix = @matrixStack__.pop
2095
+ nil
2096
+ end
2097
+
2098
+ # Reset current transformation matrix with identity matrix.
2099
+ #
2100
+ # @return [nil] nil
2101
+ #
2102
+ def resetMatrix()
2103
+ assertDrawing__
2104
+ @painter__.matrix = 1
2105
+ nil
2106
+ end
2107
+
2108
+ # Save current style values to the style stack.
2109
+ #
2110
+ # @return [nil] nil
2111
+ #
2112
+ def pushStyle(&block)
2113
+ assertDrawing__
2114
+ @styleStack__.push [
2115
+ @painter__.fill,
2116
+ @painter__.stroke,
2117
+ @painter__.stroke_width,
2118
+ @painter__.stroke_cap,
2119
+ @painter__.stroke_join,
2120
+ @painter__.clip,
2121
+ @painter__.blend_mode,
2122
+ @painter__.font,
2123
+ @painter__.shader,
2124
+ @hsbColor__,
2125
+ @colorMaxes__,
2126
+ @angleScale__,
2127
+ @rectMode__,
2128
+ @ellipseMode__,
2129
+ @imageMode__,
2130
+ @textAlignH__,
2131
+ @textAlignV__,
2132
+ @tint__,
2133
+ @filter__,
2134
+ ]
2135
+ block.call if block
2136
+ nil
2137
+ ensure
2138
+ popStyle if block
2139
+ end
2140
+
2141
+ # Restore style values from the style stack.
2142
+ #
2143
+ # @return [nil] nil
2144
+ #
2145
+ def popStyle()
2146
+ assertDrawing__
2147
+ raise "style stack underflow" if @styleStack__.empty?
2148
+ @painter__.fill,
2149
+ @painter__.stroke,
2150
+ @painter__.stroke_width,
2151
+ @painter__.stroke_cap,
2152
+ @painter__.stroke_join,
2153
+ @painter__.clip,
2154
+ @painter__.blend_mode,
2155
+ @painter__.font,
2156
+ @painter__.shader,
2157
+ @hsbColor__,
2158
+ @colorMaxes__,
2159
+ @angleScale__,
2160
+ @rectMode__,
2161
+ @ellipseMode__,
2162
+ @imageMode__,
2163
+ @textAlignH__,
2164
+ @textAlignV__,
2165
+ @tint__,
2166
+ @filter__ = @styleStack__.pop
2167
+ nil
2168
+ end
2169
+
2170
+ # Save current styles and transformations to stack.
2171
+ #
2172
+ # @return [nil] nil
2173
+ #
2174
+ def push(&block)
2175
+ pushMatrix
2176
+ pushStyle
2177
+ block.call if block
2178
+ ensure
2179
+ pop if block
2180
+ end
2181
+
2182
+ # Restore styles and transformations from stack.
2183
+ #
2184
+ # @return [nil] nil
2185
+ #
2186
+ def pop()
2187
+ popMatrix
2188
+ popStyle
2189
+ end
2190
+
2191
+ # @private
2192
+ def getInternal__()
2193
+ @image__
2194
+ end
2195
+
2196
+ # @private
2197
+ def drawImage__(painter, *args, **states)
2198
+ shader = painter.shader || @filter__&.getInternal__
2199
+ painter.push shader: shader, **states do |_|
2200
+ painter.image getInternal__, *args
2201
+ end
2202
+ end
2203
+
2204
+ # @private
2205
+ private def assertDrawing__()
2206
+ raise "call beginDraw() before drawing" unless @drawing__
2207
+ end
2208
+
2209
+ #
2210
+ # Utilities
2211
+ #
2212
+
2213
+ # Returns the absolute number of the value.
2214
+ #
2215
+ # @param value [Numeric] number
2216
+ #
2217
+ # @return [Numeric] absolute number
2218
+ #
2219
+ def abs(value)
2220
+ value.abs
2221
+ end
2222
+
2223
+ # Returns the closest integer number greater than or equal to the value.
2224
+ #
2225
+ # @param value [Numeric] number
2226
+ #
2227
+ # @return [Numeric] rounded up number
2228
+ #
2229
+ def ceil(value)
2230
+ value.ceil
2231
+ end
2232
+
2233
+ # Returns the closest integer number less than or equal to the value.
2234
+ #
2235
+ # @param value [Numeric] number
2236
+ #
2237
+ # @return [Numeric] rounded down number
2238
+ #
2239
+ def floor(value)
2240
+ value.floor
2241
+ end
2242
+
2243
+ # Returns the closest integer number.
2244
+ #
2245
+ # @param value [Numeric] number
2246
+ #
2247
+ # @return [Numeric] rounded number
2248
+ #
2249
+ def round(value)
2250
+ value.round
2251
+ end
2252
+
2253
+ # Returns the natural logarithm (the base-e logarithm) of a number.
2254
+ #
2255
+ # @param value [Numeric] number (> 0.0)
2256
+ #
2257
+ # @return [Numeric] result number
2258
+ #
2259
+ def log(n)
2260
+ Math.log n
2261
+ end
2262
+
2263
+ # Returns Euler's number e raised to the power of value.
2264
+ #
2265
+ # @param value [Numeric] number
2266
+ #
2267
+ # @return [Numeric] result number
2268
+ #
2269
+ def exp(n)
2270
+ Math.exp n
2271
+ end
2272
+
2273
+ # Returns value raised to the power of exponent.
2274
+ #
2275
+ # @param value [Numeric] base number
2276
+ # @param exponent [Numeric] exponent number
2277
+ #
2278
+ # @return [Numeric] value ** exponent
2279
+ #
2280
+ def pow(value, exponent)
2281
+ value ** exponent
2282
+ end
2283
+
2284
+ # Returns squared value.
2285
+ #
2286
+ # @param value [Numeric] number
2287
+ #
2288
+ # @return [Numeric] squared value
2289
+ #
2290
+ def sq(value)
2291
+ value * value
2292
+ end
2293
+
2294
+ # Returns squared value.
2295
+ #
2296
+ # @param value [Numeric] number
2297
+ #
2298
+ # @return [Numeric] squared value
2299
+ #
2300
+ def sqrt(value)
2301
+ Math.sqrt value
2302
+ end
2303
+
2304
+ # Returns the magnitude (or length) of a vector.
2305
+ #
2306
+ # @overload mag(x, y)
2307
+ # @overload mag(x, y, z)
2308
+ #
2309
+ # @param x [Numeric] x of point
2310
+ # @param y [Numeric] y of point
2311
+ # @param z [Numeric] z of point
2312
+ #
2313
+ # @return [Numeric] magnitude
2314
+ #
2315
+ def mag(*args)
2316
+ x, y, z = *args
2317
+ case args.size
2318
+ when 2 then Math.sqrt x * x + y * y
2319
+ when 3 then Math.sqrt x * x + y * y + z * z
2320
+ else raise ArgumentError
2321
+ end
2322
+ end
2323
+
2324
+ # Returns distance between 2 points.
2325
+ #
2326
+ # @overload dist(x1, y1, x2, y2)
2327
+ # @overload dist(x1, y1, z1, x2, y2, z2)
2328
+ #
2329
+ # @param x1 [Numeric] x of first point
2330
+ # @param y1 [Numeric] y of first point
2331
+ # @param z1 [Numeric] z of first point
2332
+ # @param x2 [Numeric] x of second point
2333
+ # @param y2 [Numeric] y of second point
2334
+ # @param z2 [Numeric] z of second point
2335
+ #
2336
+ # @return [Numeric] distance between 2 points
2337
+ #
2338
+ def dist(*args)
2339
+ case args.size
2340
+ when 4
2341
+ x1, y1, x2, y2 = *args
2342
+ xx, yy = x2 - x1, y2 - y1
2343
+ Math.sqrt xx * xx + yy * yy
2344
+ when 3
2345
+ x1, y1, z1, x2, y2, z2 = *args
2346
+ xx, yy, zz = x2 - x1, y2 - y1, z2 - z1
2347
+ Math.sqrt xx * xx + yy * yy + zz * zz
2348
+ else raise ArgumentError
2349
+ end
2350
+ end
2351
+
2352
+ # Normalize the value from range start..stop into 0..1.
2353
+ #
2354
+ # @param value [Numeric] number to be normalized
2355
+ # @param start [Numeric] lower bound of the range
2356
+ # @param stop [Numeric] upper bound of the range
2357
+ #
2358
+ # @return [Numeric] normalized value between 0..1
2359
+ #
2360
+ def norm(value, start, stop)
2361
+ (value.to_f - start.to_f) / (stop.to_f - start.to_f)
2362
+ end
2363
+
2364
+ # Returns the interpolated number between range start..stop.
2365
+ #
2366
+ # @param start [Numeric] lower bound of the range
2367
+ # @param stop [Numeric] upper bound of the range
2368
+ # @param amount [Numeric] amount to interpolate
2369
+ #
2370
+ # @return [Numeric] interporated number
2371
+ #
2372
+ def lerp(start, stop, amount)
2373
+ start + (stop - start) * amount
2374
+ end
2375
+
2376
+ # Maps a number from range start1..stop1 to range start2..stop2.
2377
+ #
2378
+ # @param value [Numeric] number to be mapped
2379
+ # @param start1 [Numeric] lower bound of the range1
2380
+ # @param stop1 [Numeric] upper bound of the range1
2381
+ # @param start2 [Numeric] lower bound of the range2
2382
+ # @param stop2 [Numeric] upper bound of the range2
2383
+ #
2384
+ # @return [Numeric] mapped number
2385
+ #
2386
+ def map(value, start1, stop1, start2, stop2)
2387
+ lerp start2, stop2, norm(value, start1, stop1)
2388
+ end
2389
+
2390
+ # Returns minimum value.
2391
+ #
2392
+ # @overload min(a, b)
2393
+ # @overload min(a, b, c)
2394
+ # @overload min(array)
2395
+ #
2396
+ # @param a [Numeric] value to compare
2397
+ # @param b [Numeric] value to compare
2398
+ # @param c [Numeric] value to compare
2399
+ # @param array [Numeric] values to compare
2400
+ #
2401
+ # @return [Numeric] minimum value
2402
+ #
2403
+ def min(*args)
2404
+ args.flatten.min
2405
+ end
2406
+
2407
+ # Returns maximum value.
2408
+ #
2409
+ # @overload max(a, b)
2410
+ # @overload max(a, b, c)
2411
+ # @overload max(array)
2412
+ #
2413
+ # @param a [Numeric] value to compare
2414
+ # @param b [Numeric] value to compare
2415
+ # @param c [Numeric] value to compare
2416
+ # @param array [Numeric] values to compare
2417
+ #
2418
+ # @return [Numeric] maximum value
2419
+ #
2420
+ def max(*args)
2421
+ args.flatten.max
2422
+ end
2423
+
2424
+ # Constrains the number between min..max.
2425
+ #
2426
+ # @param value [Numeric] number to be constrained
2427
+ # @param min [Numeric] lower bound of the range
2428
+ # @param max [Numeric] upper bound of the range
2429
+ #
2430
+ # @return [Numeric] constrained number
2431
+ #
2432
+ def constrain(value, min, max)
2433
+ value < min ? min : (value > max ? max : value)
2434
+ end
2435
+
2436
+ # Converts degree to radian.
2437
+ #
2438
+ # @param degree [Numeric] degree to convert
2439
+ #
2440
+ # @return [Numeric] radian
2441
+ #
2442
+ def radians(degree)
2443
+ degree * DEG2RAD__
2444
+ end
2445
+
2446
+ # Converts radian to degree.
2447
+ #
2448
+ # @param radian [Numeric] radian to convert
2449
+ #
2450
+ # @return [Numeric] degree
2451
+ #
2452
+ def degrees(radian)
2453
+ radian * RAD2DEG__
2454
+ end
2455
+
2456
+ # Returns the sine of an angle.
2457
+ #
2458
+ # @param angle [Numeric] angle in radians
2459
+ #
2460
+ # @return [Numeric] the sine
2461
+ #
2462
+ def sin(angle)
2463
+ Math.sin angle
2464
+ end
2465
+
2466
+ # Returns the cosine of an angle.
2467
+ #
2468
+ # @param angle [Numeric] angle in radians
2469
+ #
2470
+ # @return [Numeric] the cosine
2471
+ #
2472
+ def cos(angle)
2473
+ Math.cos angle
2474
+ end
2475
+
2476
+ # Returns the ratio of the sine and cosine of an angle.
2477
+ #
2478
+ # @param angle [Numeric] angle in radians
2479
+ #
2480
+ # @return [Numeric] the tangent
2481
+ #
2482
+ def tan(angle)
2483
+ Math.tan angle
2484
+ end
2485
+
2486
+ # Returns the inverse of sin().
2487
+ #
2488
+ # @param value [Numeric] value for calculation
2489
+ #
2490
+ # @return [Numeric] the arc sine
2491
+ #
2492
+ def asin(value)
2493
+ Math.asin value
2494
+ end
2495
+
2496
+ # Returns the inverse of cos().
2497
+ #
2498
+ # @param value [Numeric] value for calculation
2499
+ #
2500
+ # @return [Numeric] the arc cosine
2501
+ #
2502
+ def acos(value)
2503
+ Math.acos value
2504
+ end
2505
+
2506
+ # Returns the inverse of tan().
2507
+ #
2508
+ # @param value [Numeric] value for valculation
2509
+ #
2510
+ # @return [Numeric] the arc tangent
2511
+ #
2512
+ def atan(value)
2513
+ Math.atan value
2514
+ end
2515
+
2516
+ # Returns the angle from a specified point.
2517
+ #
2518
+ # @param y [Numeric] y of the point
2519
+ # @param x [Numeric] x of the point
2520
+ #
2521
+ # @return [Numeric] the angle in radians
2522
+ #
2523
+ def atan2(y, x)
2524
+ Math.atan2 y, x
2525
+ end
2526
+
2527
+ # Returns the perlin noise value.
2528
+ #
2529
+ # @overload noise(x)
2530
+ # @overload noise(x, y)
2531
+ # @overload noise(x, y, z)
2532
+ #
2533
+ # @param x [Numeric] horizontal point in noise space
2534
+ # @param y [Numeric] vertical point in noise space
2535
+ # @param z [Numeric] depth point in noise space
2536
+ #
2537
+ # @return [Numeric] noise value (0.0..1.0)
2538
+ #
2539
+ def noise(x, y = 0, z = 0)
2540
+ Rays.perlin(x, y, z) / 2.0 + 0.5
2541
+ end
2542
+
2543
+ # Returns a random number in range low...high
2544
+ #
2545
+ # @overload random()
2546
+ # @overload random(high)
2547
+ # @overload random(low, high)
2548
+ # @overload random(choices)
2549
+ #
2550
+ # @param low [Numeric] lower limit
2551
+ # @param high [Numeric] upper limit
2552
+ # @param choices [Array] array to choose from
2553
+ #
2554
+ # @return [Float] random number
2555
+ #
2556
+ def random(*args)
2557
+ return args.first.sample if args.first.kind_of? Array
2558
+ high, low = args.reverse
2559
+ rand (low || 0).to_f...(high || 1).to_f
2560
+ end
2561
+
2562
+ # Creates a new vector.
2563
+ #
2564
+ # @overload createVector()
2565
+ # @overload createVector(x, y)
2566
+ # @overload createVector(x, y, z)
2567
+ #
2568
+ # @param x [Numeric] x of new vector
2569
+ # @param y [Numeric] y of new vector
2570
+ # @param z [Numeric] z of new vector
2571
+ #
2572
+ # @return [Vector] new vector
2573
+ #
2574
+ def createVector(*args)
2575
+ Vector.new(*args, context: self)
2576
+ end
2577
+
2578
+ # Creates a new image.
2579
+ #
2580
+ # @overload createImage(w, h)
2581
+ # @overload createImage(w, h, format)
2582
+ #
2583
+ # @param w [Numeric] width of new image
2584
+ # @param h [Numeric] height of new image
2585
+ # @param format [RGB, RGBA] image format
2586
+ #
2587
+ # @return [Image] new image
2588
+ #
2589
+ def createImage(w, h, format = RGBA)
2590
+ colorspace = {RGB => Rays::RGB, RGBA => Rays::RGBA}[format]
2591
+ raise ArgumentError, "Unknown image format" unless colorspace
2592
+ Image.new Rays::Image.new(w, h, colorspace).paint {background 0, 0}
2593
+ end
2594
+
2595
+ # Creates a new off-screen graphics context object.
2596
+ #
2597
+ # @param width [Numeric] width of graphics image
2598
+ # @param height [Numeric] height of graphics image
2599
+ #
2600
+ # @return [Graphics] graphics object
2601
+ #
2602
+ def createGraphics(width, height)
2603
+ Graphics.new width, height
2604
+ end
2605
+
2606
+ # Creates a shader object.
2607
+ #
2608
+ # Passing nil for a vertex shader parameter causes the following default vertex shader to be used.
2609
+ # ```
2610
+ # attribute vec3 position;
2611
+ # attribute vec3 texCoord;
2612
+ # attribute vec4 color;
2613
+ # varying vec4 vertPosition;
2614
+ # varying vec4 vertTexCoord;
2615
+ # varying vec4 vertColor;
2616
+ # uniform mat4 transform;
2617
+ # uniform mat4 texMatrix;
2618
+ # void main ()
2619
+ # {
2620
+ # vec4 pos__ = vec4(position, 1.0);
2621
+ # vertPosition = pos__;
2622
+ # vertTexCoord = texMatrix * vec4(texCoord, 1.0);
2623
+ # vertColor = color;
2624
+ # gl_Position = transform * pos__;
2625
+ # }
2626
+ # ```
2627
+ #
2628
+ # @overload createShader(vertPath, fragPath)
2629
+ # @overload createShader(vertSource, fragSource)
2630
+ #
2631
+ # @param vertPath [String] vertex shader file path
2632
+ # @param fragPath [String] fragment shader file path
2633
+ # @param vertSource [String] vertex shader source
2634
+ # @param fragSource [String] fragment shader source
2635
+ #
2636
+ # @return [Shader] shader object
2637
+ #
2638
+ def createShader(vert, frag)
2639
+ vert = File.read if vert && File.exist?(vert)
2640
+ frag = File.read if frag && File.exist?(frag)
2641
+ Shader.new vert, frag
2642
+ end
2643
+
2644
+ # Creates a camera object as a video input device.
2645
+ #
2646
+ # @return [Capture] camera object
2647
+ #
2648
+ def createCapture(*args)
2649
+ Capture.new(*args)
2650
+ end
2651
+
2652
+ # Loads image.
2653
+ #
2654
+ # @param filename [String] file name to load image
2655
+ # @param extension [String] type of image to load (ex. 'png')
2656
+ #
2657
+ # @return [Image] loaded image object
2658
+ #
2659
+ def loadImage(filename, extension = nil)
2660
+ filename = getImage__ filename, extension if filename =~ %r|^https?://|
2661
+ Image.new Rays::Image.load filename
2662
+ end
2663
+
2664
+ # Loads shader file.
2665
+ #
2666
+ # @overload loadShader(fragPath)
2667
+ # @overload loadShader(fragPath, vertPath)
2668
+ #
2669
+ # @param fragPath [String] fragment shader file path
2670
+ # @param vertPath [String] vertex shader file path
2671
+ #
2672
+ # @return [Shader] loaded shader object
2673
+ #
2674
+ def loadShader(fragPath, vertPath = nil)
2675
+ createShader vertPath, fragPath
2676
+ end
2677
+
2678
+ # @private
2679
+ private def getImage__(uri, ext)
2680
+ ext ||= File.extname uri
2681
+ raise "unsupported image type -- #{ext}" unless ext =~ /^\.?(png|jpg|gif)$/i
2682
+
2683
+ tmpdir = tmpdir__
2684
+ path = tmpdir + Digest::SHA1.hexdigest(uri)
2685
+ path = path.sub_ext ext
2686
+
2687
+ unless path.file?
2688
+ URI.open uri do |input|
2689
+ input.set_encoding nil# disable default_internal
2690
+ tmpdir.mkdir unless tmpdir.directory?
2691
+ path.open('w') do |output|
2692
+ output.set_encoding Encoding::ASCII_8BIT
2693
+ while buf = input.read(2 ** 16)
2694
+ output.write buf
2695
+ end
2696
+ end
2697
+ end
2698
+ end
2699
+ path.to_s
2700
+ end
2701
+
2702
+ # @private
2703
+ private def tmpdir__()
2704
+ Pathname(Dir.tmpdir) + Digest::SHA1.hexdigest(self.class.name)
2705
+ end
2706
+
2707
+ end# GraphicsContext
2708
+
2709
+
2710
+ # Draws graphics into an offscreen buffer
2711
+ #
2712
+ class Graphics
2713
+
2714
+ include GraphicsContext
2715
+
2716
+ # Initialize graphics object.
2717
+ #
2718
+ def initialize(width, height)
2719
+ image = Rays::Image.new width, height
2720
+ init__ image, image.painter
2721
+ end
2722
+
2723
+ # Start drawing.
2724
+ #
2725
+ def beginDraw(&block)
2726
+ beginDraw__
2727
+ @painter__.__send__ :begin_paint
2728
+ push
2729
+ if block
2730
+ block.call
2731
+ endDraw
2732
+ end
2733
+ end
2734
+
2735
+ # End drawing.
2736
+ #
2737
+ def endDraw()
2738
+ pop
2739
+ @painter__.__send__ :end_paint
2740
+ endDraw__
2741
+ end
2742
+
2743
+ end# Graphics
2744
+
2745
+
2746
+ # Processing context
2747
+ #
2748
+ class Context
2749
+
2750
+ include GraphicsContext
2751
+
2752
+ Vector = Processing::Vector
2753
+ Capture = Processing::Capture
2754
+ Graphics = Processing::Graphics
2755
+ Shader = Processing::Shader
2756
+
2757
+ # @private
2758
+ @@context__ = nil
2759
+
2760
+ # @private
2761
+ def self.context__()
2762
+ @@context__
2763
+ end
2764
+
2765
+ # @private
2766
+ def initialize(window)
2767
+ @@context__ = self
2768
+
2769
+ tmpdir__.tap {|dir| FileUtils.rm_r dir.to_s if dir.directory?}
2770
+
2771
+ @window__ = window
2772
+ init__(
2773
+ @window__.canvas_image,
2774
+ @window__.canvas_painter.paint {background 0.8})
2775
+
2776
+ @loop__ = true
2777
+ @redraw__ = false
2778
+ @frameCount__ = 0
2779
+ @key__ = nil
2780
+ @keyCode__ = nil
2781
+ @keysPressed__ = Set.new
2782
+ @pointerPos__ =
2783
+ @pointerPrevPos__ = Rays::Point.new 0
2784
+ @pointersPressed__ = []
2785
+ @touches__ = []
2786
+ @motionGravity__ = createVector 0, 0
2787
+
2788
+ @window__.before_draw = proc {beginDraw__}
2789
+ @window__.after_draw = proc {endDraw__}
2790
+ @window__.update_canvas = proc {|i, p| updateCanvas__ i, p}
2791
+
2792
+ @window__.instance_variable_set :@context, self
2793
+ def @window__.draw_screen(painter)
2794
+ @context.drawImage__ painter
2795
+ end
2796
+
2797
+ drawFrame = -> {
2798
+ begin
2799
+ push
2800
+ @drawBlock__.call if @drawBlock__
2801
+ ensure
2802
+ pop
2803
+ @frameCount__ += 1
2804
+ end
2805
+ }
2806
+
2807
+ @window__.draw = proc do |e|
2808
+ if @loop__ || @redraw__
2809
+ @redraw__ = false
2810
+ drawFrame.call
2811
+ end
2812
+ end
2813
+
2814
+ updateKeyStates = -> event, pressed {
2815
+ @key__ = event.chars
2816
+ @keyCode__ = event.key
2817
+ if pressed != nil
2818
+ set, key = @keysPressed__, event.key
2819
+ pressed ? set.add(key) : set.delete(key)
2820
+ end
2821
+ }
2822
+
2823
+ mouseButtonMap = {
2824
+ mouse_left: LEFT,
2825
+ mouse_right: RIGHT,
2826
+ mouse_middle: CENTER
2827
+ }
2828
+
2829
+ updatePointerStates = -> event, pressed = nil {
2830
+ @pointerPrevPos__ = @pointerPos__
2831
+ @pointerPos__ = event.pos.dup
2832
+ @touches__ = event.pointers.map {|p| Touch.new(p.id, *p.pos.to_a)}
2833
+ if pressed != nil
2834
+ array = @pointersPressed__
2835
+ event.types
2836
+ .tap {|types| types.delete :mouse}
2837
+ .map {|type| mouseButtonMap[type] || type}
2838
+ .each {|type| pressed ? array.push(type) : array.delete(type)}
2839
+ end
2840
+ }
2841
+
2842
+ @window__.key_down = proc do |e|
2843
+ updateKeyStates.call e, true
2844
+ @keyPressedBlock__&.call
2845
+ @keyTypedBlock__&.call if @key__ && !@key__.empty?
2846
+ end
2847
+
2848
+ @window__.key_up = proc do |e|
2849
+ updateKeyStates.call e, false
2850
+ @keyReleasedBlock__&.call
2851
+ end
2852
+
2853
+ @window__.pointer_down = proc do |e|
2854
+ updatePointerStates.call e, true
2855
+ @pointerDownStartPos__ = @pointerPos__.dup
2856
+ (@touchStartedBlock__ || @mousePressedBlock__)&.call
2857
+ end
2858
+
2859
+ @window__.pointer_up = proc do |e|
2860
+ updatePointerStates.call e, false
2861
+ (@touchEndedBlock__ || @mouseReleasedBlock__)&.call
2862
+ if startPos = @pointerDownStartPos__
2863
+ @mouseClickedBlock__&.call if (@pointerPos__ - startPos).length < 3
2864
+ @pointerDownStartPos__ = nil
2865
+ end
2866
+ end
2867
+
2868
+ @window__.pointer_move = proc do |e|
2869
+ updatePointerStates.call e
2870
+ (@touchMovedBlock__ || @mouseMovedBlock__)&.call
2871
+ end
2872
+
2873
+ @window__.pointer_drag = proc do |e|
2874
+ updatePointerStates.call e
2875
+ (@touchMovedBlock__ || @mouseDraggedBlock__)&.call
2876
+ end
2877
+
2878
+ @window__.motion = proc do |e|
2879
+ @motionGravity__ = createVector(*e.gravity.to_a(3))
2880
+ @motionBlock__&.call
2881
+ end
2882
+ end
2883
+
2884
+ # Defines setup block.
2885
+ #
2886
+ # @return [nil] nil
2887
+ #
2888
+ def setup(&block)
2889
+ @window__.setup = block
2890
+ nil
2891
+ end
2892
+
2893
+ # Defines draw block.
2894
+ #
2895
+ # @return [nil] nil
2896
+ #
2897
+ def draw(&block)
2898
+ @drawBlock__ = block if block
2899
+ nil
2900
+ end
2901
+
2902
+ # Defines keyPressed block.
2903
+ #
2904
+ # @return [Boolean] is any key pressed or not
2905
+ #
2906
+ def keyPressed(&block)
2907
+ @keyPressedBlock__ = block if block
2908
+ not @keysPressed__.empty?
2909
+ end
2910
+
2911
+ # Defines keyReleased block.
2912
+ #
2913
+ # @return [nil] nil
2914
+ #
2915
+ def keyReleased(&block)
2916
+ @keyReleasedBlock__ = block if block
2917
+ nil
2918
+ end
2919
+
2920
+ # Defines keyTyped block.
2921
+ #
2922
+ # @return [nil] nil
2923
+ #
2924
+ def keyTyped(&block)
2925
+ @keyTypedBlock__ = block if block
2926
+ nil
2927
+ end
2928
+
2929
+ # Defines mousePressed block.
2930
+ #
2931
+ # @return [Boolean] is any mouse button pressed or not
2932
+ #
2933
+ def mousePressed(&block)
2934
+ @mousePressedBlock__ = block if block
2935
+ not @pointersPressed__.empty?
2936
+ end
2937
+
2938
+ # Defines mouseReleased block.
2939
+ #
2940
+ # @return [nil] nil
2941
+ #
2942
+ def mouseReleased(&block)
2943
+ @mouseReleasedBlock__ = block if block
2944
+ nil
2945
+ end
2946
+
2947
+ # Defines mouseMoved block.
2948
+ #
2949
+ # @return [nil] nil
2950
+ #
2951
+ def mouseMoved(&block)
2952
+ @mouseMovedBlock__ = block if block
2953
+ nil
2954
+ end
2955
+
2956
+ # Defines mouseDragged block.
2957
+ #
2958
+ # @return [nil] nil
2959
+ #
2960
+ def mouseDragged(&block)
2961
+ @mouseDraggedBlock__ = block if block
2962
+ nil
2963
+ end
2964
+
2965
+ # Defines mouseClicked block.
2966
+ #
2967
+ # @return [nil] nil
2968
+ #
2969
+ def mouseClicked(&block)
2970
+ @mouseClickedBlock__ = block if block
2971
+ nil
2972
+ end
2973
+
2974
+ # Defines touchStarted block.
2975
+ #
2976
+ # @return [nil] nil
2977
+ #
2978
+ def touchStarted(&block)
2979
+ @touchStartedBlock__ = block if block
2980
+ nil
2981
+ end
2982
+
2983
+ # Defines touchEnded block.
2984
+ #
2985
+ # @return [nil] nil
2986
+ #
2987
+ def touchEnded(&block)
2988
+ @touchEndedBlock__ = block if block
2989
+ nil
2990
+ end
2991
+
2992
+ # Defines touchMoved block.
2993
+ #
2994
+ # @return [nil] nil
2995
+ #
2996
+ def touchMoved(&block)
2997
+ @touchMovedBlock__ = block if block
2998
+ nil
2999
+ end
3000
+
3001
+ # Defines motion block.
3002
+ #
3003
+ # @return [nil] nil
3004
+ #
3005
+ def motion(&block)
3006
+ @motionBlock__ = block if block
3007
+ nil
3008
+ end
3009
+
3010
+ # Changes canvas size.
3011
+ #
3012
+ # @param width [Integer] new width
3013
+ # @param height [Integer] new height
3014
+ # @param pixelDensity [Numeric] new pixel density
3015
+ #
3016
+ # @return [nil] nil
3017
+ #
3018
+ def size(width, height, pixelDensity: self.pixelDensity)
3019
+ resizeCanvas__ :size, width, height, pixelDensity
3020
+ nil
3021
+ end
3022
+
3023
+ # Changes canvas size.
3024
+ #
3025
+ # @param width [Integer] new width
3026
+ # @param height [Integer] new height
3027
+ # @param pixelDensity [Numeric] new pixel density
3028
+ #
3029
+ # @return [nil] nil
3030
+ #
3031
+ def createCanvas(width, height, pixelDensity: self.pixelDensity)
3032
+ resizeCanvas__ :createCanvas, width, height, pixelDensity
3033
+ nil
3034
+ end
3035
+
3036
+ # Changes title of window.
3037
+ #
3038
+ # @param title [String] new title
3039
+ #
3040
+ # @return [nil] nil
3041
+ #
3042
+ def setTitle(title)
3043
+ @window__.title = title
3044
+ nil
3045
+ end
3046
+
3047
+ # Changes and returns canvas pixel density.
3048
+ #
3049
+ # @param density [Numeric] new pixel density
3050
+ #
3051
+ # @return [Numeric] current pixel density
3052
+ #
3053
+ def pixelDensity(density = nil)
3054
+ resizeCanvas__ :pixelDensity, width, height, density if density
3055
+ @painter__.pixel_density
3056
+ end
3057
+
3058
+ # @private
3059
+ def resizeCanvas__(name, width, height, pixelDensity)
3060
+ raise '#{name}() must be called on startup or setup block' if @started__
3061
+
3062
+ @painter__.__send__ :end_paint
3063
+ @window__.resize_canvas width, height, pixelDensity
3064
+ @window__.auto_resize = false
3065
+ ensure
3066
+ @painter__.__send__ :begin_paint
3067
+ end
3068
+
3069
+ # Returns pixel density of display.
3070
+ #
3071
+ # @return [Numeric] pixel density
3072
+ #
3073
+ def displayDensity()
3074
+ @window__.painter.pixel_density
3075
+ end
3076
+
3077
+ # Returns window width.
3078
+ #
3079
+ # @return [Numeric] window width
3080
+ #
3081
+ def windowWidth()
3082
+ @window__.width
3083
+ end
3084
+
3085
+ # Returns window height.
3086
+ #
3087
+ # @return [Numeric] window height
3088
+ #
3089
+ def windowHeight()
3090
+ @window__.height
3091
+ end
3092
+
3093
+ # Returns number of frames since program started.
3094
+ #
3095
+ # @return [Integer] total number of frames
3096
+ #
3097
+ def frameCount()
3098
+ @frameCount__
3099
+ end
3100
+
3101
+ # Returns number of frames per second.
3102
+ #
3103
+ # @return [Float] frames per second
3104
+ #
3105
+ def frameRate()
3106
+ @window__.event.fps
3107
+ end
3108
+
3109
+ # Returns the last key that was pressed or released.
3110
+ #
3111
+ # @return [String] last key
3112
+ #
3113
+ def key()
3114
+ @key__
3115
+ end
3116
+
3117
+ # Returns the last key code that was pressed or released.
3118
+ #
3119
+ # @return [Symbol] last key code
3120
+ #
3121
+ def keyCode()
3122
+ @keyCode__
3123
+ end
3124
+
3125
+ # Returns mouse x position
3126
+ #
3127
+ # @return [Numeric] horizontal position of mouse
3128
+ #
3129
+ def mouseX()
3130
+ @pointerPos__.x
3131
+ end
3132
+
3133
+ # Returns mouse y position
3134
+ #
3135
+ # @return [Numeric] vertical position of mouse
3136
+ #
3137
+ def mouseY()
3138
+ @pointerPos__.y
3139
+ end
3140
+
3141
+ # Returns mouse x position in previous frame
3142
+ #
3143
+ # @return [Numeric] horizontal position of mouse
3144
+ #
3145
+ def pmouseX()
3146
+ @pointerPrevPos__.x
3147
+ end
3148
+
3149
+ # Returns mouse y position in previous frame
3150
+ #
3151
+ # @return [Numeric] vertical position of mouse
3152
+ #
3153
+ def pmouseY()
3154
+ @pointerPrevPos__.y
3155
+ end
3156
+
3157
+ # Returns which mouse button was pressed
3158
+ #
3159
+ # @return [Numeric] LEFT, RIGHT, CENTER or 0
3160
+ #
3161
+ def mouseButton()
3162
+ (@pointersPressed__ & [LEFT, RIGHT, CENTER]).last || 0
3163
+ end
3164
+
3165
+ # Returns array of touches
3166
+ #
3167
+ # @return [Array] Touch objects
3168
+ #
3169
+ def touches()
3170
+ @touches__
3171
+ end
3172
+
3173
+ # Returns vector for real world gravity
3174
+ #
3175
+ # @return [Vector] gravity vector
3176
+ #
3177
+ def motionGravity()
3178
+ @motionGravity__
3179
+ end
3180
+
3181
+ # Enables calling draw block on every frame.
3182
+ #
3183
+ # @return [nil] nil
3184
+ #
3185
+ def loop()
3186
+ @loop__ = true
3187
+ end
3188
+
3189
+ # Disables calling draw block on every frame.
3190
+ #
3191
+ # @return [nil] nil
3192
+ #
3193
+ def noLoop()
3194
+ @loop__ = false
3195
+ end
3196
+
3197
+ # Calls draw block to redraw frame.
3198
+ #
3199
+ # @return [nil] nil
3200
+ #
3201
+ def redraw()
3202
+ @redraw__ = true
3203
+ end
3204
+
3205
+ end# Context
3206
+
3207
+
3208
+ end# Processing
3209
+
3210
+
3211
+ end# Processing