processing 0.4.0 → 0.5.0

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