processing 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1676 @@
1
+ module Processing
2
+
3
+
4
+ # Drawing context
5
+ #
6
+ module GraphicsContext
7
+
8
+ # PI
9
+ #
10
+ PI = Math::PI
11
+
12
+ # PI / 2
13
+ #
14
+ HALF_PI = PI / 2
15
+
16
+ # PI / 4
17
+ #
18
+ QUARTER_PI = PI / 4
19
+
20
+ # PI * 2
21
+ #
22
+ TWO_PI = PI * 2
23
+
24
+ # PI * 2
25
+ #
26
+ TAU = PI * 2
27
+
28
+ # RGBA format for createImage().
29
+ #
30
+ RGBA = :rgba
31
+
32
+ # RGB format for createImage, or RGB mode for colorMode().
33
+ #
34
+ RGB = :rgb
35
+
36
+ # HSB mode for colorMode().
37
+ #
38
+ HSB = :hsb
39
+
40
+ # Radian mode for angleMode().
41
+ #
42
+ RADIANS = :radians
43
+
44
+ # Degree mode for angleMode().
45
+ #
46
+ DEGREES = :degrees
47
+
48
+ # Mode for rectMode(), ellipseMode() and imageMode().
49
+ #
50
+ CORNER = :corner
51
+
52
+ # Mode for rectMode(), ellipseMode() and imageMode().
53
+ #
54
+ CORNERS = :corners
55
+
56
+ # Mode for rectMode(), ellipseMode(), imageMode() and textAlign().
57
+ #
58
+ CENTER = :center
59
+
60
+ # Mode for rectMode() and ellipseMode().
61
+ #
62
+ RADIUS = :radius
63
+
64
+ # Mode for strokeCap().
65
+ #
66
+ PROJECT = :square
67
+
68
+ # Mode for strokeJoin().
69
+ #
70
+ MITER = :miter
71
+
72
+ # Mode for strokeCap() and strokeJoin().
73
+ #
74
+ ROUND = :round
75
+
76
+ # Mode for strokeCap() and strokeJoin().
77
+ #
78
+ SQUARE = :butt
79
+
80
+ # Mode for blendMode().
81
+ #
82
+ BLEND = :normal
83
+
84
+ # Mode for blendMode().
85
+ #
86
+ ADD = :add
87
+
88
+ # Mode for blendMode().
89
+ #
90
+ SUBTRACT = :subtract
91
+
92
+ # Mode for blendMode().
93
+ #
94
+ LIGHTEST = :lightest
95
+
96
+ # Mode for blendMode().
97
+ #
98
+ DARKEST = :darkest
99
+
100
+ # Mode for blendMode().
101
+ #
102
+ EXCLUSION = :exclusion
103
+
104
+ # Mode for blendMode().
105
+ #
106
+ MULTIPLY = :multiply
107
+
108
+ # Mode for blendMode().
109
+ #
110
+ SCREEN = :screen
111
+
112
+ # Mode for blendMode().
113
+ #
114
+ REPLACE = :replace
115
+
116
+ # Key code or Mode for textAlign().
117
+ LEFT = :left
118
+
119
+ # Key code or Mode for textAlign().
120
+ RIGHT = :right
121
+
122
+ # Mode for textAlign().
123
+ TOP = :top
124
+
125
+ # Mode for textAlign().
126
+ BOTTOM = :bottom
127
+
128
+ # Mode for textAlign().
129
+ BASELINE = :baseline
130
+
131
+ # Filter type for filter()
132
+ THRESHOLD = :threshold
133
+
134
+ # Filter type for filter()
135
+ GRAY = :gray
136
+
137
+ # Filter type for filter()
138
+ INVERT = :invert
139
+
140
+ # Filter type for filter()
141
+ BLUR = :blur
142
+
143
+ # Key codes.
144
+ ENTER = :enter
145
+ SPACE = :space
146
+ TAB = :tab
147
+ DELETE = :delete
148
+ BACKSPACE = :backspace
149
+ ESC = :escape
150
+ HOME = :home
151
+ #END = :end
152
+ PAGEUP = :pageup
153
+ PAGEDOWN = :pagedown
154
+ CLEAR = :clear
155
+ SHIFT = :shift
156
+ CONTROL = :control
157
+ ALT = :alt
158
+ WIN = :win
159
+ COMMAND = :command
160
+ OPTION = :option
161
+ FUNCTION = :function
162
+ CAPSLOCK = :capslock
163
+ SECTION = :section
164
+ HELP = :help
165
+ F1 = :f1
166
+ F2 = :f2
167
+ F3 = :f3
168
+ F4 = :f4
169
+ F5 = :f5
170
+ F6 = :f6
171
+ F7 = :f7
172
+ F8 = :f8
173
+ F9 = :f9
174
+ F10 = :f10
175
+ F11 = :f11
176
+ F12 = :f12
177
+ F13 = :f13
178
+ F14 = :f14
179
+ F15 = :f15
180
+ F16 = :f16
181
+ F17 = :f17
182
+ F18 = :f18
183
+ F19 = :f19
184
+ F20 = :f20
185
+ F21 = :f21
186
+ F22 = :f22
187
+ F23 = :f23
188
+ F24 = :f24
189
+ UP = :up
190
+ DOWN = :down
191
+
192
+ # @private
193
+ DEG2RAD__ = Math::PI / 180.0
194
+
195
+ # @private
196
+ RAD2DEG__ = 180.0 / Math::PI
197
+
198
+ # @private
199
+ def init__(image, painter)
200
+ @drawing__ = false
201
+ @hsbColor__ = false
202
+ @colorMaxes__ = [1.0] * 4
203
+ @angleScale__ = 1.0
204
+ @rectMode__ = nil
205
+ @ellipseMode__ = nil
206
+ @imageMode__ = nil
207
+ @textAlignH__ = nil
208
+ @textAlignV__ = nil
209
+ @tint__ = nil
210
+ @filter__ = nil
211
+ @matrixStack__ = []
212
+ @styleStack__ = []
213
+ @fontCache__ = {}
214
+
215
+ updateCanvas__ image, painter
216
+
217
+ colorMode RGB, 255
218
+ angleMode RADIANS
219
+ rectMode CORNER
220
+ ellipseMode CENTER
221
+ imageMode CORNER
222
+ blendMode BLEND
223
+ strokeCap ROUND
224
+ strokeJoin MITER
225
+ textAlign LEFT
226
+
227
+ fill 255
228
+ stroke 0
229
+ strokeWeight 1
230
+ noTint
231
+ end
232
+
233
+ # @private
234
+ def updateCanvas__(image, painter)
235
+ @image__, @painter__ = image, painter
236
+ end
237
+
238
+ # @private
239
+ def beginDraw__()
240
+ raise "call beginDraw() before drawing" if @drawing__
241
+ @matrixStack__.clear
242
+ @styleStack__.clear
243
+ @drawing__ = true
244
+ end
245
+
246
+ # @private
247
+ def endDraw__()
248
+ raise unless @drawing__
249
+ @drawing__ = false
250
+ end
251
+
252
+ def width()
253
+ @image__.width
254
+ end
255
+
256
+ def height()
257
+ @image__.height
258
+ end
259
+
260
+ # Sets color mode and max color values.
261
+ #
262
+ # @overload colorMode(mode)
263
+ # @overload colorMode(mode, max)
264
+ # @overload colorMode(mode, max1, max2, max3)
265
+ # @overload colorMode(mode, max1, max2, max3, maxA)
266
+ #
267
+ # @param mode [RGB, HSB] RGB or HSB
268
+ # @param max [Numeric] max values for all color values
269
+ # @param max1 [Numeric] max value for red or hue
270
+ # @param max2 [Numeric] max value for green or saturation
271
+ # @param max3 [Numeric] max value for blue or brightness
272
+ # @param maxA [Numeric] max value for alpha
273
+ #
274
+ # @return [nil] nil
275
+ #
276
+ def colorMode(mode, *maxes)
277
+ mode = mode.downcase.to_sym
278
+ raise ArgumentError, "invalid color mode: #{mode}" unless [RGB, HSB].include?(mode)
279
+ raise ArgumentError unless [0, 1, 3, 4].include?(maxes.size)
280
+
281
+ @hsbColor__ = mode == HSB
282
+ case maxes.size
283
+ when 1 then @colorMaxes__ = [maxes.first.to_f] * 4
284
+ when 3, 4 then @colorMaxes__[0...maxes.size] = maxes.map &:to_f
285
+ end
286
+ nil
287
+ end
288
+
289
+ # @private
290
+ private def toRGBA__(*args)
291
+ a, b, c, d = args
292
+ return parseColor__(a, b || alphaMax__) if a.kind_of?(String)
293
+
294
+ rgba = case args.size
295
+ when 1, 2 then [a, a, a, b || alphaMax__]
296
+ when 3, 4 then [a, b, c, d || alphaMax__]
297
+ else raise ArgumentError
298
+ end
299
+ rgba = rgba.map.with_index {|value, i| value / @colorMaxes__[i]}
300
+ color = @hsbColor__ ? Rays::Color.hsv(*rgba) : Rays::Color.new(*rgba)
301
+ color.to_a
302
+ end
303
+
304
+ # @private
305
+ private def parseColor__(str, alpha)
306
+ result = str.match(/^\s*##{'([0-9a-f]{2})' * 3}\s*$/i)
307
+ raise ArgumentError, "invalid color code: '#{str}'" unless result
308
+
309
+ rgb = result[1..3].map.with_index {|hex, i| hex.to_i(16) / 255.0}
310
+ return *rgb, (alpha / alphaMax__)
311
+ end
312
+
313
+ # @private
314
+ private def alphaMax__()
315
+ @colorMaxes__[3]
316
+ end
317
+
318
+ # Sets angle mode.
319
+ #
320
+ # @param mode [RADIANS, DEGREES] RADIANS or DEGREES
321
+ #
322
+ # @return [nil] nil
323
+ #
324
+ def angleMode(mode)
325
+ @angleScale__ = case mode.downcase.to_sym
326
+ when RADIANS then RAD2DEG__
327
+ when DEGREES then 1.0
328
+ else raise ArgumentError, "invalid angle mode: #{mode}"
329
+ end
330
+ nil
331
+ end
332
+
333
+ # @private
334
+ def toAngle__(angle)
335
+ angle * @angleScale__
336
+ end
337
+
338
+ # Sets rect mode. Default is CORNER.
339
+ #
340
+ # CORNER -> rect(left, top, width, height)
341
+ # CORNERS -> rect(left, top, right, bottom)
342
+ # CENTER -> rect(center_x, center_y, width, height)
343
+ # RADIUS -> rect(center_x, center_y, radius_h, radius_v)
344
+ #
345
+ # @param mode [CORNER, CORNERS, CENTER, RADIUS]
346
+ #
347
+ # @return [nil] nil
348
+ #
349
+ def rectMode(mode)
350
+ @rectMode__ = mode
351
+ end
352
+
353
+ # Sets ellipse mode. Default is CENTER.
354
+ #
355
+ # CORNER -> ellipse(left, top, width, height)
356
+ # CORNERS -> ellipse(left, top, right, bottom)
357
+ # CENTER -> ellipse(center_x, center_y, width, height)
358
+ # RADIUS -> ellipse(center_x, center_y, radius_h, radius_v)
359
+ #
360
+ # @param mode [CORNER, CORNERS, CENTER, RADIUS]
361
+ #
362
+ # @return [nil] nil
363
+ #
364
+ def ellipseMode(mode)
365
+ @ellipseMode__ = mode
366
+ end
367
+
368
+ # Sets image mode. Default is CORNER.
369
+ #
370
+ # CORNER -> image(img, left, top, width, height)
371
+ # CORNERS -> image(img, left, top, right, bottom)
372
+ # CENTER -> image(img, center_x, center_y, width, height)
373
+ #
374
+ # @param mode [CORNER, CORNERS, CENTER]
375
+ #
376
+ # @return [nil] nil
377
+ #
378
+ def imageMode(mode)
379
+ @imageMode__ = mode
380
+ end
381
+
382
+ # @private
383
+ private def toXYWH__(mode, a, b, c, d)
384
+ case mode
385
+ when CORNER then [a, b, c, d]
386
+ when CORNERS then [a, b, c - a, d - b]
387
+ when CENTER then [a - c / 2.0, b - d / 2.0, c, d]
388
+ when RADIUS then [a - c, b - d, c * 2, d * 2]
389
+ else raise ArgumentError # ToDo: refine error message
390
+ end
391
+ end
392
+
393
+ # Sets blend mode. Default is BLEND.
394
+ #
395
+ # @param mode [BLEND, ADD, SUBTRACT, LIGHTEST, DARKEST, EXCLUSION, MULTIPLY, SCREEN, REPLACE]
396
+ #
397
+ # @return [nil] nil
398
+ #
399
+ def blendMode(mode)
400
+ @painter__.blend_mode = mode
401
+ nil
402
+ end
403
+
404
+ # Sets fill color.
405
+ #
406
+ # @overload fill(rgb)
407
+ # @overload fill(rgb, alpha)
408
+ # @overload fill(gray)
409
+ # @overload fill(gray, alpha)
410
+ # @overload fill(r, g, b)
411
+ # @overload fill(r, g, b, alpha)
412
+ #
413
+ # @param rgb [String] color code like '#00AAFF'
414
+ # @param gray [Integer] gray value (0..255)
415
+ # @param r [Integer] red value (0..255)
416
+ # @param g [Integer] green value (0..255)
417
+ # @param b [Integer] blue value (0..255)
418
+ # @param alpha [Integer] alpha value (0..255)
419
+ #
420
+ # @return [nil] nil
421
+ #
422
+ def fill(*args)
423
+ @painter__.fill(*toRGBA__(*args))
424
+ nil
425
+ end
426
+
427
+ # Disables filling.
428
+ #
429
+ # @return [nil] nil
430
+ #
431
+ def noFill()
432
+ @painter__.fill nil
433
+ nil
434
+ end
435
+
436
+ # Sets stroke color.
437
+ #
438
+ # @overload stroke(rgb)
439
+ # @overload stroke(rgb, alpha)
440
+ # @overload stroke(gray)
441
+ # @overload stroke(gray, alpha)
442
+ # @overload stroke(r, g, b)
443
+ # @overload stroke(r, g, b, alpha)
444
+ #
445
+ # @param rgb [String] color code like '#00AAFF'
446
+ # @param gray [Integer] gray value (0..255)
447
+ # @param r [Integer] red value (0..255)
448
+ # @param g [Integer] green value (0..255)
449
+ # @param b [Integer] blue value (0..255)
450
+ # @param alpha [Integer] alpha value (0..255)
451
+ #
452
+ # @return [nil] nil
453
+ #
454
+ def stroke(*args)
455
+ @painter__.stroke(*toRGBA__(*args))
456
+ nil
457
+ end
458
+
459
+ # Disables drawing stroke.
460
+ #
461
+ # @return [nil] nil
462
+ #
463
+ def noStroke()
464
+ @painter__.stroke nil
465
+ nil
466
+ end
467
+
468
+ # Sets stroke weight.
469
+ #
470
+ # @param weight [Numeric] width of stroke
471
+ #
472
+ # @return [nil] nil
473
+ #
474
+ def strokeWeight(weight)
475
+ @painter__.stroke_width weight
476
+ nil
477
+ end
478
+
479
+ # Sets stroke cap mode.
480
+ #
481
+ # @param cap [BUTT, ROUND, SQUARE]
482
+ #
483
+ # @return [nil] nil
484
+ #
485
+ def strokeCap(cap)
486
+ @painter__.stroke_cap cap
487
+ nil
488
+ end
489
+
490
+ # Sets stroke join mode.
491
+ #
492
+ # @param join [MITER, ROUND, SQUARE]
493
+ #
494
+ # @return [nil] nil
495
+ #
496
+ def strokeJoin(join)
497
+ @painter__.stroke_join join
498
+ nil
499
+ end
500
+
501
+ # Sets fill color for drawing images.
502
+ #
503
+ # @overload tint(rgb)
504
+ # @overload tint(rgb, alpha)
505
+ # @overload tint(gray)
506
+ # @overload tint(gray, alpha)
507
+ # @overload tint(r, g, b)
508
+ # @overload tint(r, g, b, alpha)
509
+ #
510
+ # @param rgb [String] color code like '#00AAFF'
511
+ # @param gray [Integer] gray value (0..255)
512
+ # @param r [Integer] red value (0..255)
513
+ # @param g [Integer] green value (0..255)
514
+ # @param b [Integer] blue value (0..255)
515
+ # @param alpha [Integer] alpha value (0..255)
516
+ #
517
+ # @return [nil] nil
518
+ #
519
+ def tint(*args)
520
+ @tint__ = args
521
+ nil
522
+ end
523
+
524
+ # Resets tint color.
525
+ #
526
+ # @return [nil] nil
527
+ #
528
+ def noTint()
529
+ @tint__ = nil
530
+ end
531
+
532
+ # Limits the drawable rectangle.
533
+ #
534
+ # The parameters a, b, c, and d are determined by rectMode().
535
+ #
536
+ # @param a [Numeric] horizontal position of the drawable area, by default
537
+ # @param b [Numeric] vertical position of the drawable area, by default
538
+ # @param c [Numeric] width of the drawable area, by default
539
+ # @param d [Numeric] height of the drawable area, by default
540
+ #
541
+ # @return [nil] nil
542
+ #
543
+ def clip(a, b, c, d)
544
+ x, y, w, h = toXYWH__ @imageMode__, a, b, c, d
545
+ @painter__.clip x, y, w, h
546
+ nil
547
+ end
548
+
549
+ # Disables clipping.
550
+ #
551
+ # @return [nil] nil
552
+ #
553
+ def noClip()
554
+ @painter__.no_clip
555
+ nil
556
+ end
557
+
558
+ # Sets font.
559
+ #
560
+ # @overload textFont(font)
561
+ # @overload textFont(name)
562
+ # @overload textFont(font, size)
563
+ # @overload textFont(name, size)
564
+ #
565
+ # @param font [Font] font
566
+ # @param name [String] font name
567
+ # @param size [Numeric] font size (max 256)
568
+ #
569
+ # @return [Font] current font
570
+ #
571
+ def textFont(font = nil, size = nil)
572
+ setFont__ font, size if font || size
573
+ Font.new @painter__.font
574
+ end
575
+
576
+ # Sets text size.
577
+ #
578
+ # @param size [Numeric] font size (max 256)
579
+ #
580
+ # @return [nil] nil
581
+ #
582
+ def textSize(size)
583
+ setFont__ nil, size
584
+ nil
585
+ end
586
+
587
+ def textWidth(str)
588
+ @painter__.font.width str
589
+ end
590
+
591
+ def textAscent()
592
+ @painter__.font.ascent
593
+ end
594
+
595
+ def textDescent()
596
+ @painter__.font.descent
597
+ end
598
+
599
+ def textAlign(horizontal, vertical = BASELINE)
600
+ @textAlignH__ = horizontal
601
+ @textAlignV__ = vertical
602
+ end
603
+
604
+ # @private
605
+ def setFont__(fontOrName, size)
606
+ name = case fontOrName
607
+ when Font then fontOrName.name
608
+ else fontOrName || @painter__.font.name
609
+ end
610
+ size ||= @painter__.font.size
611
+ size = 256 if size > 256
612
+ font = @fontCache__[[name, size]] ||= Rays::Font.new name, size
613
+ @painter__.font = font
614
+ end
615
+
616
+ # Sets shader.
617
+ #
618
+ # @param shader [Shader] a shader to apply
619
+ #
620
+ # @return [nil] nil
621
+ #
622
+ def shader(shader)
623
+ @painter__.shader shader&.getInternal__
624
+ nil
625
+ end
626
+
627
+ # Resets shader.
628
+ #
629
+ # @return [nil] nil
630
+ #
631
+ def resetShader()
632
+ @painter__.no_shader
633
+ nil
634
+ end
635
+
636
+ # Applies an image filter to screen.
637
+ #
638
+ # overload filter(shader)
639
+ # overload filter(type)
640
+ # overload filter(type, param)
641
+ #
642
+ # @param shader [Shader] a shader to apply
643
+ # @param type [THRESHOLD, GRAY, INVERT, BLUR] filter type
644
+ # @param param [Numeric] a parameter for each filter
645
+ #
646
+ def filter(*args)
647
+ @filter__ = Shader.createFilter__(*args)
648
+ end
649
+
650
+ # Clears screen.
651
+ #
652
+ # @overload background(str)
653
+ # @overload background(str, alpha)
654
+ # @overload background(gray)
655
+ # @overload background(gray, alpha)
656
+ # @overload background(r, g, b)
657
+ # @overload background(r, g, b, alpha)
658
+ #
659
+ # @param str [String] color code like '#00AAFF'
660
+ # @param gray [Integer] gray value (0..255)
661
+ # @param r [Integer] red value (0..255)
662
+ # @param g [Integer] green value (0..255)
663
+ # @param b [Integer] blue value (0..255)
664
+ # @param alpha [Integer] alpha value (0..255)
665
+ #
666
+ # @return [nil] nil
667
+ #
668
+ def background(*args)
669
+ assertDrawing__
670
+ rgba = toRGBA__(*args)
671
+ if rgba[3] == 1
672
+ @painter__.background(*rgba)
673
+ else
674
+ @painter__.push fill: rgba, stroke: :none do |_|
675
+ @painter__.rect 0, 0, width, height
676
+ end
677
+ end
678
+ nil
679
+ end
680
+
681
+ # Draws a point.
682
+ #
683
+ # @param x [Numeric] horizontal position
684
+ # @param y [Numeric] vertical position
685
+ #
686
+ # @return [nil] nil
687
+ #
688
+ def point(x, y)
689
+ assertDrawing__
690
+ @painter__.line x, y, x, y
691
+ nil
692
+ end
693
+
694
+ # Draws a line.
695
+ #
696
+ # @param x1 [Numeric] horizontal position of first point
697
+ # @param y1 [Numeric] vertical position of first point
698
+ # @param x2 [Numeric] horizontal position of second point
699
+ # @param y2 [Numeric] vertical position of second point
700
+ #
701
+ # @return [nil] nil
702
+ #
703
+ def line(x1, y1, x2, y2)
704
+ assertDrawing__
705
+ @painter__.line x1, y1, x2, y2
706
+ nil
707
+ end
708
+
709
+ # Draws a rectangle.
710
+ #
711
+ # The parameters a, b, c, and d are determined by rectMode().
712
+ #
713
+ # @overload rect(a, b, c, d)
714
+ # @overload rect(a, b, c, d, r)
715
+ # @overload rect(a, b, c, d, tl, tr, br, bl)
716
+ #
717
+ # @param a [Numeric] horizontal position of the shape, by default
718
+ # @param b [Numeric] vertical position of the shape, by default
719
+ # @param c [Numeric] width of the shape, by default
720
+ # @param d [Numeric] height of the shape, by default
721
+ # @param r [Numeric] radius for all corners
722
+ # @param tl [Numeric] radius for top-left corner
723
+ # @param tr [Numeric] radius for top-right corner
724
+ # @param br [Numeric] radius for bottom-right corner
725
+ # @param bl [Numeric] radius for bottom-left corner
726
+ #
727
+ # @return [nil] nil
728
+ #
729
+ def rect(a, b, c, d, *args)
730
+ assertDrawing__
731
+ x, y, w, h = toXYWH__ @rectMode__, a, b, c, d
732
+ case args.size
733
+ when 0 then @painter__.rect x, y, w, h
734
+ when 1 then @painter__.rect x, y, w, h, round: args[0]
735
+ when 4 then @painter__.rect x, y, w, h, lt: args[0], rt: args[1], rb: args[2], lb: args[3]
736
+ else raise ArgumentError # ToDo: refine error message
737
+ end
738
+ nil
739
+ end
740
+
741
+ # Draws an ellipse.
742
+ #
743
+ # The parameters a, b, c, and d are determined by ellipseMode().
744
+ #
745
+ # @param a [Numeric] horizontal position of the shape, by default
746
+ # @param b [Numeric] vertical position of the shape, by default
747
+ # @param c [Numeric] width of the shape, by default
748
+ # @param d [Numeric] height of the shape, by default
749
+ #
750
+ # @return [nil] nil
751
+ #
752
+ def ellipse(a, b, c, d)
753
+ assertDrawing__
754
+ x, y, w, h = toXYWH__ @ellipseMode__, a, b, c, d
755
+ @painter__.ellipse x, y, w, h
756
+ nil
757
+ end
758
+
759
+ # Draws a circle.
760
+ #
761
+ # @param x [Numeric] horizontal position of the shape
762
+ # @param y [Numeric] vertical position of the shape
763
+ # @param extent [Numeric] width and height of the shape
764
+ #
765
+ # @return [nil] nil
766
+ #
767
+ def circle(x, y, extent)
768
+ ellipse x, y, extent, extent
769
+ end
770
+
771
+ # Draws an arc.
772
+ #
773
+ # The parameters a, b, c, and d are determined by ellipseMode().
774
+ #
775
+ # @param a [Numeric] horizontal position of the shape, by default
776
+ # @param b [Numeric] vertical position of the shape, by default
777
+ # @param c [Numeric] width of the shape, by default
778
+ # @param d [Numeric] height of the shape, by default
779
+ # @param start [Numeric] angle to start the arc
780
+ # @param stop [Numeric] angle to stop the arc
781
+ #
782
+ # @return [nil] nil
783
+ #
784
+ def arc(a, b, c, d, start, stop)
785
+ assertDrawing__
786
+ x, y, w, h = toXYWH__ @ellipseMode__, a, b, c, d
787
+ start = toAngle__(-start)
788
+ stop = toAngle__(-stop)
789
+ @painter__.ellipse x, y, w, h, from: start, to: stop
790
+ nil
791
+ end
792
+
793
+ # Draws a square.
794
+ #
795
+ # @param x [Numeric] horizontal position of the shape
796
+ # @param y [Numeric] vertical position of the shape
797
+ # @param extent [Numeric] width and height of the shape
798
+ #
799
+ # @return [nil] nil
800
+ #
801
+ def square(x, y, extent)
802
+ rect x, y, extent, extent
803
+ end
804
+
805
+ # Draws a triangle.
806
+ #
807
+ # @param x1 [Numeric] horizontal position of first point
808
+ # @param y1 [Numeric] vertical position of first point
809
+ # @param x2 [Numeric] horizontal position of second point
810
+ # @param y2 [Numeric] vertical position of second point
811
+ # @param x3 [Numeric] horizontal position of third point
812
+ # @param y3 [Numeric] vertical position of third point
813
+ #
814
+ # @return [nil] nil
815
+ #
816
+ def triangle(x1, y1, x2, y2, x3, y3)
817
+ assertDrawing__
818
+ @painter__.line x1, y1, x2, y2, x3, y3, loop: true
819
+ nil
820
+ end
821
+
822
+ # Draws a quad.
823
+ #
824
+ # @param x1 [Numeric] horizontal position of first point
825
+ # @param y1 [Numeric] vertical position of first point
826
+ # @param x2 [Numeric] horizontal position of second point
827
+ # @param y2 [Numeric] vertical position of second point
828
+ # @param x3 [Numeric] horizontal position of third point
829
+ # @param y3 [Numeric] vertical position of third point
830
+ # @param x4 [Numeric] horizontal position of fourth point
831
+ # @param y4 [Numeric] vertical position of fourth point
832
+ #
833
+ # @return [nil] nil
834
+ #
835
+ def quad(x1, y1, x2, y2, x3, y3, x4, y4)
836
+ assertDrawing__
837
+ @painter__.line x1, y1, x2, y2, x3, y3, x4, y4, loop: true
838
+ nil
839
+ end
840
+
841
+ # Draws a Catmull-Rom spline curve.
842
+ #
843
+ # @param cx1 [Numeric] horizontal position of beginning control point
844
+ # @param cy1 [Numeric] vertical position of beginning control point
845
+ # @param x1 [Numeric] horizontal position of first point
846
+ # @param y1 [Numeric] vertical position of first point
847
+ # @param x2 [Numeric] horizontal position of second point
848
+ # @param y2 [Numeric] vertical position of second point
849
+ # @param cx2 [Numeric] horizontal position of ending control point
850
+ # @param cy2 [Numeric] vertical position of ending control point
851
+ #
852
+ # @return [nil] nil
853
+ #
854
+ def curve(cx1, cy1, x1, y1, x2, y2, cx2, cy2)
855
+ assertDrawing__
856
+ @painter__.curve cx1, cy1, x1, y1, x2, y2, cx2, cy2
857
+ nil
858
+ end
859
+
860
+ # Draws a Bezier spline curve.
861
+ #
862
+ # @param x1 [Numeric] horizontal position of first point
863
+ # @param y1 [Numeric] vertical position of first point
864
+ # @param cx1 [Numeric] horizontal position of first control point
865
+ # @param cy1 [Numeric] vertical position of first control point
866
+ # @param cx2 [Numeric] horizontal position of second control point
867
+ # @param cy2 [Numeric] vertical position of second control point
868
+ # @param x2 [Numeric] horizontal position of second point
869
+ # @param y2 [Numeric] vertical position of second point
870
+ #
871
+ # @return [nil] nil
872
+ #
873
+ def bezier(x1, y1, cx1, cy1, cx2, cy2, x2, y2)
874
+ assertDrawing__
875
+ @painter__.bezier x1, y1, cx1, cy1, cx2, cy2, x2, y2
876
+ nil
877
+ end
878
+
879
+ # Draws a text.
880
+ #
881
+ # The parameters a, b, c, and d are determined by rectMode().
882
+ #
883
+ # @overload text(str)
884
+ # @overload text(str, x, y)
885
+ # @overload text(str, a, b, c, d)
886
+ #
887
+ # @param str [String] text to draw
888
+ # @param x [Numeric] horizontal position of the text
889
+ # @param y [Numeric] vertical position of the text
890
+ # @param a [Numeric] horizontal position of the text, by default
891
+ # @param b [Numeric] vertical position of the text, by default
892
+ # @param c [Numeric] width of the text, by default
893
+ # @param d [Numeric] height of the text, by default
894
+ #
895
+ # @return [nil] nil
896
+ #
897
+ def text(str, x, y, x2 = nil, y2 = nil)
898
+ assertDrawing__
899
+ if x2
900
+ raise ArgumentError, "missing y2 parameter" unless y2
901
+ x, y, w, h = toXYWH__ @rectMode__, x, y, x2, y2
902
+ case @textAlignH__
903
+ when RIGHT then x += w - @painter__.font.width(str)
904
+ when CENTER then x += (w - @painter__.font.width(str)) / 2
905
+ end
906
+ case @textAlignV__
907
+ when BOTTOM then y += h - @painter__.font.height
908
+ when CENTER then y += (h - @painter__.font.height) / 2
909
+ else
910
+ end
911
+ else
912
+ y -= @painter__.font.ascent
913
+ end
914
+ @painter__.text str, x, y
915
+ nil
916
+ end
917
+
918
+ # Draws an image.
919
+ #
920
+ # The parameters a, b, c, and d are determined by imageMode().
921
+ #
922
+ # @overload image(img, a, b)
923
+ # @overload image(img, a, b, c, d)
924
+ #
925
+ # @param img [Image] image to draw
926
+ # @param a [Numeric] horizontal position of the image, by default
927
+ # @param b [Numeric] vertical position of the image, by default
928
+ # @param c [Numeric] width of the image, by default
929
+ # @param d [Numeric] height of the image, by default
930
+ #
931
+ # @return [nil] nil
932
+ #
933
+ def image(img, a, b, c = nil, d = nil)
934
+ assertDrawing__
935
+ x, y, w, h = toXYWH__ @imageMode__, a, b, c || img.width, d || img.height
936
+ tint = @tint__ ? toRGBA__(*@tint__) : 1
937
+ img.drawImage__ @painter__, x, y, w, h, fill: tint, stroke: :none
938
+ nil
939
+ end
940
+
941
+ # Copies image.
942
+ #
943
+ # @overload copy(sx, sy, sw, sh, dx, dy, dw, dh)
944
+ # @overload copy(img, sx, sy, sw, sh, dx, dy, dw, dh)
945
+ #
946
+ # @param img [Image] image for copy source
947
+ # @param sx [Numrtic] x position of source region
948
+ # @param sy [Numrtic] y position of source region
949
+ # @param sw [Numrtic] width of source region
950
+ # @param sh [Numrtic] height of source region
951
+ # @param dx [Numrtic] x position of destination region
952
+ # @param dy [Numrtic] y position of destination region
953
+ # @param dw [Numrtic] width of destination region
954
+ # @param dh [Numrtic] height of destination region
955
+ #
956
+ # @return [nil] nil
957
+ #
958
+ def copy(img = nil, sx, sy, sw, sh, dx, dy, dw, dh)
959
+ blend img, sx, sy, sw, sh, dx, dy, dw, dh, BLEND
960
+ end
961
+
962
+ # Blends image.
963
+ #
964
+ # @overload blend(sx, sy, sw, sh, dx, dy, dw, dh, mode)
965
+ # @overload blend(img, sx, sy, sw, sh, dx, dy, dw, dh, mode)
966
+ #
967
+ # @param img [Image] image for blend source
968
+ # @param sx [Numrtic] x position of source region
969
+ # @param sy [Numrtic] y position of source region
970
+ # @param sw [Numrtic] width of source region
971
+ # @param sh [Numrtic] height of source region
972
+ # @param dx [Numrtic] x position of destination region
973
+ # @param dy [Numrtic] y position of destination region
974
+ # @param dw [Numrtic] width of destination region
975
+ # @param dh [Numrtic] height of destination region
976
+ # @param mode [BLEND, ADD, SUBTRACT, LIGHTEST, DARKEST, EXCLUSION, MULTIPLY, SCREEN, REPLACE] blend mode
977
+ #
978
+ # @return [nil] nil
979
+ #
980
+ def blend(img = nil, sx, sy, sw, sh, dx, dy, dw, dh, mode)
981
+ assertDrawing__
982
+ img ||= self
983
+ img.drawImage__ @painter__, sx, sy, sw, sh, dx, dy, dw, dh, blend_mode: mode
984
+ end
985
+
986
+ # Saves screen image to file.
987
+ #
988
+ # @param filename [String] file name to save image
989
+ #
990
+ def save(filename)
991
+ @window__.canvas_image.save filename
992
+ end
993
+
994
+ # Applies translation matrix to current transformation matrix.
995
+ #
996
+ # @overload translate(x, y)
997
+ # @overload translate(x, y, z)
998
+ #
999
+ # @param x [Numeric] left/right translation
1000
+ # @param y [Numeric] up/down translation
1001
+ # @param y [Numeric] forward/backward translation
1002
+ #
1003
+ # @return [nil] nil
1004
+ #
1005
+ def translate(x, y, z = 0)
1006
+ assertDrawing__
1007
+ @painter__.translate x, y, z
1008
+ nil
1009
+ end
1010
+
1011
+ # Applies scale matrix to current transformation matrix.
1012
+ #
1013
+ # @overload scale(s)
1014
+ # @overload scale(x, y)
1015
+ #
1016
+ # @param s [Numeric] horizontal and vertical scale
1017
+ # @param x [Numeric] horizontal scale
1018
+ # @param y [Numeric] vertical scale
1019
+ #
1020
+ # @return [nil] nil
1021
+ #
1022
+ def scale(x, y)
1023
+ assertDrawing__
1024
+ @painter__.scale x, y
1025
+ nil
1026
+ end
1027
+
1028
+ # Applies rotation matrix to current transformation matrix.
1029
+ #
1030
+ # @param angle [Numeric] angle for rotation
1031
+ #
1032
+ # @return [nil] nil
1033
+ #
1034
+ def rotate(angle)
1035
+ assertDrawing__
1036
+ @painter__.rotate toAngle__ angle
1037
+ nil
1038
+ end
1039
+
1040
+ # Pushes the current transformation matrix to stack.
1041
+ #
1042
+ # @return [nil] nil
1043
+ #
1044
+ def pushMatrix(&block)
1045
+ assertDrawing__
1046
+ @matrixStack__.push @painter__.matrix
1047
+ block.call if block
1048
+ nil
1049
+ ensure
1050
+ popMatrix if block
1051
+ end
1052
+
1053
+ # Pops the current transformation matrix from stack.
1054
+ #
1055
+ # @return [nil] nil
1056
+ #
1057
+ def popMatrix()
1058
+ assertDrawing__
1059
+ raise "matrix stack underflow" if @matrixStack__.empty?
1060
+ @painter__.matrix = @matrixStack__.pop
1061
+ nil
1062
+ end
1063
+
1064
+ # Reset current transformation matrix with identity matrix.
1065
+ #
1066
+ # @return [nil] nil
1067
+ #
1068
+ def resetMatrix()
1069
+ assertDrawing__
1070
+ @painter__.matrix = 1
1071
+ nil
1072
+ end
1073
+
1074
+ # Save current style values to the style stack.
1075
+ #
1076
+ # @return [nil] nil
1077
+ #
1078
+ def pushStyle(&block)
1079
+ assertDrawing__
1080
+ @styleStack__.push [
1081
+ @painter__.fill,
1082
+ @painter__.stroke,
1083
+ @painter__.stroke_width,
1084
+ @painter__.stroke_cap,
1085
+ @painter__.stroke_join,
1086
+ @painter__.clip,
1087
+ @painter__.blend_mode,
1088
+ @painter__.font,
1089
+ @painter__.shader,
1090
+ @hsbColor__,
1091
+ @colorMaxes__,
1092
+ @angleScale__,
1093
+ @rectMode__,
1094
+ @ellipseMode__,
1095
+ @imageMode__,
1096
+ @textAlignH__,
1097
+ @textAlignV__,
1098
+ @tint__,
1099
+ @filter__,
1100
+ ]
1101
+ block.call if block
1102
+ nil
1103
+ ensure
1104
+ popStyle if block
1105
+ end
1106
+
1107
+ # Restore style values from the style stack.
1108
+ #
1109
+ # @return [nil] nil
1110
+ #
1111
+ def popStyle()
1112
+ assertDrawing__
1113
+ raise "style stack underflow" if @styleStack__.empty?
1114
+ @painter__.fill,
1115
+ @painter__.stroke,
1116
+ @painter__.stroke_width,
1117
+ @painter__.stroke_cap,
1118
+ @painter__.stroke_join,
1119
+ @painter__.clip,
1120
+ @painter__.blend_mode,
1121
+ @painter__.font,
1122
+ @painter__.shader,
1123
+ @hsbColor__,
1124
+ @colorMaxes__,
1125
+ @angleScale__,
1126
+ @rectMode__,
1127
+ @ellipseMode__,
1128
+ @imageMode__,
1129
+ @textAlignH__,
1130
+ @textAlignV__,
1131
+ @tint__,
1132
+ @filter__ = @styleStack__.pop
1133
+ nil
1134
+ end
1135
+
1136
+ # Save current styles and transformations to stack.
1137
+ #
1138
+ # @return [nil] nil
1139
+ #
1140
+ def push(&block)
1141
+ pushMatrix
1142
+ pushStyle
1143
+ block.call if block
1144
+ ensure
1145
+ pop if block
1146
+ end
1147
+
1148
+ # Restore styles and transformations from stack.
1149
+ #
1150
+ # @return [nil] nil
1151
+ #
1152
+ def pop()
1153
+ popMatrix
1154
+ popStyle
1155
+ end
1156
+
1157
+ # @private
1158
+ def getInternal__()
1159
+ @image__
1160
+ end
1161
+
1162
+ # @private
1163
+ def drawImage__(painter, *args, **states)
1164
+ shader = painter.shader || @filter__&.getInternal__
1165
+ painter.push shader: shader, **states do |_|
1166
+ painter.image getInternal__, *args
1167
+ end
1168
+ end
1169
+
1170
+ # @private
1171
+ private def assertDrawing__()
1172
+ raise "call beginDraw() before drawing" unless @drawing__
1173
+ end
1174
+
1175
+ #
1176
+ # Utilities
1177
+ #
1178
+
1179
+ # Returns the absolute number of the value.
1180
+ #
1181
+ # @param value [Numeric] number
1182
+ #
1183
+ # @return [Numeric] absolute number
1184
+ #
1185
+ def abs(value)
1186
+ value.abs
1187
+ end
1188
+
1189
+ # Returns the closest integer number greater than or equal to the value.
1190
+ #
1191
+ # @param value [Numeric] number
1192
+ #
1193
+ # @return [Numeric] rounded up number
1194
+ #
1195
+ def ceil(value)
1196
+ value.ceil
1197
+ end
1198
+
1199
+ # Returns the closest integer number less than or equal to the value.
1200
+ #
1201
+ # @param value [Numeric] number
1202
+ #
1203
+ # @return [Numeric] rounded down number
1204
+ #
1205
+ def floor(value)
1206
+ value.floor
1207
+ end
1208
+
1209
+ # Returns the closest integer number.
1210
+ #
1211
+ # @param value [Numeric] number
1212
+ #
1213
+ # @return [Numeric] rounded number
1214
+ #
1215
+ def round(value)
1216
+ value.round
1217
+ end
1218
+
1219
+ # Returns the natural logarithm (the base-e logarithm) of a number.
1220
+ #
1221
+ # @param value [Numeric] number (> 0.0)
1222
+ #
1223
+ # @return [Numeric] result number
1224
+ #
1225
+ def log(n)
1226
+ Math.log n
1227
+ end
1228
+
1229
+ # Returns Euler's number e raised to the power of value.
1230
+ #
1231
+ # @param value [Numeric] number
1232
+ #
1233
+ # @return [Numeric] result number
1234
+ #
1235
+ def exp(n)
1236
+ Math.exp n
1237
+ end
1238
+
1239
+ # Returns value raised to the power of exponent.
1240
+ #
1241
+ # @param value [Numeric] base number
1242
+ # @param exponent [Numeric] exponent number
1243
+ #
1244
+ # @return [Numeric] value ** exponent
1245
+ #
1246
+ def pow(value, exponent)
1247
+ value ** exponent
1248
+ end
1249
+
1250
+ # Returns squared value.
1251
+ #
1252
+ # @param value [Numeric] number
1253
+ #
1254
+ # @return [Numeric] squared value
1255
+ #
1256
+ def sq(value)
1257
+ value * value
1258
+ end
1259
+
1260
+ # Returns squared value.
1261
+ #
1262
+ # @param value [Numeric] number
1263
+ #
1264
+ # @return [Numeric] squared value
1265
+ #
1266
+ def sqrt(value)
1267
+ Math.sqrt value
1268
+ end
1269
+
1270
+ # Returns the magnitude (or length) of a vector.
1271
+ #
1272
+ # @overload mag(x, y)
1273
+ # @overload mag(x, y, z)
1274
+ #
1275
+ # @param x [Numeric] x of point
1276
+ # @param y [Numeric] y of point
1277
+ # @param z [Numeric] z of point
1278
+ #
1279
+ # @return [Numeric] magnitude
1280
+ #
1281
+ def mag(*args)
1282
+ x, y, z = *args
1283
+ case args.size
1284
+ when 2 then Math.sqrt x * x + y * y
1285
+ when 3 then Math.sqrt x * x + y * y + z * z
1286
+ else raise ArgumentError
1287
+ end
1288
+ end
1289
+
1290
+ # Returns distance between 2 points.
1291
+ #
1292
+ # @overload dist(x1, y1, x2, y2)
1293
+ # @overload dist(x1, y1, z1, x2, y2, z2)
1294
+ #
1295
+ # @param x1 [Numeric] x of first point
1296
+ # @param y1 [Numeric] y of first point
1297
+ # @param z1 [Numeric] z of first point
1298
+ # @param x2 [Numeric] x of second point
1299
+ # @param y2 [Numeric] y of second point
1300
+ # @param z2 [Numeric] z of second point
1301
+ #
1302
+ # @return [Numeric] distance between 2 points
1303
+ #
1304
+ def dist(*args)
1305
+ case args.size
1306
+ when 4
1307
+ x1, y1, x2, y2 = *args
1308
+ xx, yy = x2 - x1, y2 - y1
1309
+ Math.sqrt xx * xx + yy * yy
1310
+ when 3
1311
+ x1, y1, z1, x2, y2, z2 = *args
1312
+ xx, yy, zz = x2 - x1, y2 - y1, z2 - z1
1313
+ Math.sqrt xx * xx + yy * yy + zz * zz
1314
+ else raise ArgumentError
1315
+ end
1316
+ end
1317
+
1318
+ # Normalize the value from range start..stop into 0..1.
1319
+ #
1320
+ # @param value [Numeric] number to be normalized
1321
+ # @param start [Numeric] lower bound of the range
1322
+ # @param stop [Numeric] upper bound of the range
1323
+ #
1324
+ # @return [Numeric] normalized value between 0..1
1325
+ #
1326
+ def norm(value, start, stop)
1327
+ (value.to_f - start.to_f) / (stop.to_f - start.to_f)
1328
+ end
1329
+
1330
+ # Returns the interpolated number between range start..stop.
1331
+ #
1332
+ # @param start [Numeric] lower bound of the range
1333
+ # @param stop [Numeric] upper bound of the range
1334
+ # @param amount [Numeric] amount to interpolate
1335
+ #
1336
+ # @return [Numeric] interporated number
1337
+ #
1338
+ def lerp(start, stop, amount)
1339
+ start + (stop - start) * amount
1340
+ end
1341
+
1342
+ # Maps a number from range start1..stop1 to range start2..stop2.
1343
+ #
1344
+ # @param value [Numeric] number to be mapped
1345
+ # @param start1 [Numeric] lower bound of the range1
1346
+ # @param stop1 [Numeric] upper bound of the range1
1347
+ # @param start2 [Numeric] lower bound of the range2
1348
+ # @param stop2 [Numeric] upper bound of the range2
1349
+ #
1350
+ # @return [Numeric] mapped number
1351
+ #
1352
+ def map(value, start1, stop1, start2, stop2)
1353
+ lerp start2, stop2, norm(value, start1, stop1)
1354
+ end
1355
+
1356
+ # Returns minimum value.
1357
+ #
1358
+ # @overload min(a, b)
1359
+ # @overload min(a, b, c)
1360
+ # @overload min(array)
1361
+ #
1362
+ # @param a [Numeric] value to compare
1363
+ # @param b [Numeric] value to compare
1364
+ # @param c [Numeric] value to compare
1365
+ # @param array [Numeric] values to compare
1366
+ #
1367
+ # @return [Numeric] minimum value
1368
+ #
1369
+ def min(*args)
1370
+ args.flatten.min
1371
+ end
1372
+
1373
+ # Returns maximum value.
1374
+ #
1375
+ # @overload max(a, b)
1376
+ # @overload max(a, b, c)
1377
+ # @overload max(array)
1378
+ #
1379
+ # @param a [Numeric] value to compare
1380
+ # @param b [Numeric] value to compare
1381
+ # @param c [Numeric] value to compare
1382
+ # @param array [Numeric] values to compare
1383
+ #
1384
+ # @return [Numeric] maximum value
1385
+ #
1386
+ def max(*args)
1387
+ args.flatten.max
1388
+ end
1389
+
1390
+ # Constrains the number between min..max.
1391
+ #
1392
+ # @param value [Numeric] number to be constrained
1393
+ # @param min [Numeric] lower bound of the range
1394
+ # @param max [Numeric] upper bound of the range
1395
+ #
1396
+ # @return [Numeric] constrained number
1397
+ #
1398
+ def constrain(value, min, max)
1399
+ value < min ? min : (value > max ? max : value)
1400
+ end
1401
+
1402
+ # Converts degree to radian.
1403
+ #
1404
+ # @param degree [Numeric] degree to convert
1405
+ #
1406
+ # @return [Numeric] radian
1407
+ #
1408
+ def radians(degree)
1409
+ degree * DEG2RAD__
1410
+ end
1411
+
1412
+ # Converts radian to degree.
1413
+ #
1414
+ # @param radian [Numeric] radian to convert
1415
+ #
1416
+ # @return [Numeric] degree
1417
+ #
1418
+ def degrees(radian)
1419
+ radian * RAD2DEG__
1420
+ end
1421
+
1422
+ # Returns the sine of an angle.
1423
+ #
1424
+ # @param angle [Numeric] angle in radians
1425
+ #
1426
+ # @return [Numeric] the sine
1427
+ #
1428
+ def sin(angle)
1429
+ Math.sin angle
1430
+ end
1431
+
1432
+ # Returns the cosine of an angle.
1433
+ #
1434
+ # @param angle [Numeric] angle in radians
1435
+ #
1436
+ # @return [Numeric] the cosine
1437
+ #
1438
+ def cos(angle)
1439
+ Math.cos angle
1440
+ end
1441
+
1442
+ # Returns the ratio of the sine and cosine of an angle.
1443
+ #
1444
+ # @param angle [Numeric] angle in radians
1445
+ #
1446
+ # @return [Numeric] the tangent
1447
+ #
1448
+ def tan(angle)
1449
+ Math.tan angle
1450
+ end
1451
+
1452
+ # Returns the inverse of sin().
1453
+ #
1454
+ # @param value [Numeric] value for calculation
1455
+ #
1456
+ # @return [Numeric] the arc sine
1457
+ #
1458
+ def asin(value)
1459
+ Math.asin value
1460
+ end
1461
+
1462
+ # Returns the inverse of cos().
1463
+ #
1464
+ # @param value [Numeric] value for calculation
1465
+ #
1466
+ # @return [Numeric] the arc cosine
1467
+ #
1468
+ def acos(value)
1469
+ Math.acos value
1470
+ end
1471
+
1472
+ # Returns the inverse of tan().
1473
+ #
1474
+ # @param value [Numeric] value for valculation
1475
+ #
1476
+ # @return [Numeric] the arc tangent
1477
+ #
1478
+ def atan(value)
1479
+ Math.atan value
1480
+ end
1481
+
1482
+ # Returns the angle from a specified point.
1483
+ #
1484
+ # @param y [Numeric] y of the point
1485
+ # @param x [Numeric] x of the point
1486
+ #
1487
+ # @return [Numeric] the angle in radians
1488
+ #
1489
+ def atan2(y, x)
1490
+ Math.atan2 y, x
1491
+ end
1492
+
1493
+ # Returns the perlin noise value.
1494
+ #
1495
+ # @overload noise(x)
1496
+ # @overload noise(x, y)
1497
+ # @overload noise(x, y, z)
1498
+ #
1499
+ # @param x [Numeric] horizontal point in noise space
1500
+ # @param y [Numeric] vertical point in noise space
1501
+ # @param z [Numeric] depth point in noise space
1502
+ #
1503
+ # @return [Numeric] noise value (0.0..1.0)
1504
+ #
1505
+ def noise(x, y = 0, z = 0)
1506
+ Rays.perlin(x, y, z) / 2.0 + 0.5
1507
+ end
1508
+
1509
+ # Returns a random number in range low...high
1510
+ #
1511
+ # @overload random()
1512
+ # @overload random(high)
1513
+ # @overload random(low, high)
1514
+ # @overload random(choices)
1515
+ #
1516
+ # @param low [Numeric] lower limit
1517
+ # @param high [Numeric] upper limit
1518
+ # @param choices [Array] array to choose from
1519
+ #
1520
+ # @return [Float] random number
1521
+ #
1522
+ def random(*args)
1523
+ return args.first.sample if args.first.kind_of? Array
1524
+ high, low = args.reverse
1525
+ rand (low || 0).to_f...(high || 1).to_f
1526
+ end
1527
+
1528
+ # Creates a new vector.
1529
+ #
1530
+ # @overload createVector()
1531
+ # @overload createVector(x, y)
1532
+ # @overload createVector(x, y, z)
1533
+ #
1534
+ # @param x [Numeric] x of new vector
1535
+ # @param y [Numeric] y of new vector
1536
+ # @param z [Numeric] z of new vector
1537
+ #
1538
+ # @return [Vector] new vector
1539
+ #
1540
+ def createVector(*args)
1541
+ Vector.new(*args, context: self)
1542
+ end
1543
+
1544
+ # Creates a new image.
1545
+ #
1546
+ # @overload createImage(w, h)
1547
+ # @overload createImage(w, h, format)
1548
+ #
1549
+ # @param w [Numeric] width of new image
1550
+ # @param h [Numeric] height of new image
1551
+ # @param format [RGB, RGBA] image format
1552
+ #
1553
+ # @return [Image] new image
1554
+ #
1555
+ def createImage(w, h, format = RGBA)
1556
+ colorspace = {RGB => Rays::RGB, RGBA => Rays::RGBA}[format]
1557
+ raise ArgumentError, "Unknown image format" unless colorspace
1558
+ Image.new Rays::Image.new(w, h, colorspace).paint {background 0, 0}
1559
+ end
1560
+
1561
+ # Creates a new off-screen graphics context object.
1562
+ #
1563
+ # @param width [Numeric] width of graphics image
1564
+ # @param height [Numeric] height of graphics image
1565
+ #
1566
+ # @return [Graphics] graphics object
1567
+ #
1568
+ def createGraphics(width, height)
1569
+ Graphics.new width, height
1570
+ end
1571
+
1572
+ # Creates a shader object.
1573
+ #
1574
+ # Passing nil for a vertex shader parameter causes the following default vertex shader to be used.
1575
+ # ```
1576
+ # attribute vec3 position;
1577
+ # attribute vec3 texCoord;
1578
+ # attribute vec4 color;
1579
+ # varying vec4 vertPosition;
1580
+ # varying vec4 vertTexCoord;
1581
+ # varying vec4 vertColor;
1582
+ # uniform mat4 transform;
1583
+ # uniform mat4 texMatrix;
1584
+ # void main ()
1585
+ # {
1586
+ # vec4 pos__ = vec4(position, 1.0);
1587
+ # vertPosition = pos__;
1588
+ # vertTexCoord = texMatrix * vec4(texCoord, 1.0);
1589
+ # vertColor = color;
1590
+ # gl_Position = transform * pos__;
1591
+ # }
1592
+ # ```
1593
+ #
1594
+ # @overload createShader(vertPath, fragPath)
1595
+ # @overload createShader(vertSource, fragSource)
1596
+ #
1597
+ # @param vertPath [String] vertex shader file path
1598
+ # @param fragPath [String] fragment shader file path
1599
+ # @param vertSource [String] vertex shader source
1600
+ # @param fragSource [String] fragment shader source
1601
+ #
1602
+ # @return [Shader] shader object
1603
+ #
1604
+ def createShader(vert, frag)
1605
+ vert = File.read if vert && File.exist?(vert)
1606
+ frag = File.read if frag && File.exist?(frag)
1607
+ Shader.new vert, frag
1608
+ end
1609
+
1610
+ # Creates a camera object as a video input device.
1611
+ #
1612
+ # @return [Capture] camera object
1613
+ #
1614
+ def createCapture(*args)
1615
+ Capture.new(*args)
1616
+ end
1617
+
1618
+ # Loads image.
1619
+ #
1620
+ # @param filename [String] file name to load image
1621
+ # @param extension [String] type of image to load (ex. 'png')
1622
+ #
1623
+ # @return [Image] loaded image object
1624
+ #
1625
+ def loadImage(filename, extension = nil)
1626
+ filename = getImage__ filename, extension if filename =~ %r|^https?://|
1627
+ Image.new Rays::Image.load filename
1628
+ end
1629
+
1630
+ # Loads shader file.
1631
+ #
1632
+ # @overload loadShader(fragPath)
1633
+ # @overload loadShader(fragPath, vertPath)
1634
+ #
1635
+ # @param fragPath [String] fragment shader file path
1636
+ # @param vertPath [String] vertex shader file path
1637
+ #
1638
+ # @return [Shader] loaded shader object
1639
+ #
1640
+ def loadShader(fragPath, vertPath = nil)
1641
+ createShader vertPath, fragPath
1642
+ end
1643
+
1644
+ # @private
1645
+ private def getImage__(uri, ext)
1646
+ ext ||= File.extname uri
1647
+ raise "unsupported image type -- #{ext}" unless ext =~ /^\.?(png|jpg|gif)$/i
1648
+
1649
+ tmpdir = tmpdir__
1650
+ path = tmpdir + Digest::SHA1.hexdigest(uri)
1651
+ path = path.sub_ext ext
1652
+
1653
+ unless path.file?
1654
+ URI.open uri do |input|
1655
+ input.set_encoding nil# disable default_internal
1656
+ tmpdir.mkdir unless tmpdir.directory?
1657
+ path.open('w') do |output|
1658
+ output.set_encoding Encoding::ASCII_8BIT
1659
+ while buf = input.read(2 ** 16)
1660
+ output.write buf
1661
+ end
1662
+ end
1663
+ end
1664
+ end
1665
+ path.to_s
1666
+ end
1667
+
1668
+ # @private
1669
+ private def tmpdir__()
1670
+ Pathname(Dir.tmpdir) + Digest::SHA1.hexdigest(self.class.name)
1671
+ end
1672
+
1673
+ end# GraphicsContext
1674
+
1675
+
1676
+ end# Processing