rubysketch 0.2.6 → 0.3.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9068a9fe7e5239420f8e32fb2e30912748bf9c90c52f40516604997b1166830a
4
- data.tar.gz: 2a75b3ec215aeb15bfba929f561abd2a4f94c4c8093b27bad4de1ee74dd15efa
3
+ metadata.gz: 90fb6bf8a945c62f9ac122dc4df7515aa2ebf271cb100836c76cb8cc1d4f7c50
4
+ data.tar.gz: 53e61d592dbc5090d98e3c6bb015d406f018683767997c3f2f4d53462a9f27b4
5
5
  SHA512:
6
- metadata.gz: d7a6252c38ab1fdda0c761c390bb5d0bb3583e740d7beea5efe3c5939a1f52840aac93c41aee693870dde4539df3f69dba20476c34b85a2eb5791fb3bcd63498
7
- data.tar.gz: ccd6c65ff6b5e0340e7a8e779c2d1da2f277294ae9e03386a7d1317568badade0d4fe51758559680ef22ccc080486a3d2df43dab0501c91c9a6da3cc7144eb1d
6
+ metadata.gz: deb1f851efbcfd3feff20a7495ad55c9ef3a2d1e9ee416d6b87de89069197cc397a64303c69929f212cb0dda658f739a6ca1f1e83bea3050afa219b889509a9d
7
+ data.tar.gz: ba13f0ce8a03a0b86f1ad20078e0b473d2e6058a446a3059c21f8c9e5ccabcaa72f065bcbfee13bb7b105960f0e658b1ee3e609c44c1429f1d4a9851ce122eab
@@ -1,10 +1,39 @@
1
1
  # RubySketch ChangeLog
2
2
 
3
3
 
4
+ ## [0.3.3] - 2020-08-01
5
+
6
+ - add Vector class
7
+
8
+
9
+ ## [0.3.2] - 2020-07-22
10
+
11
+ - text() draws to the baseline by default
12
+ - add textWidth(), textAscent(), textDescent() and textAlign()
13
+ - change initial color for fill() and stroke()
14
+ - change initial background color to grayscale 0.8
15
+
16
+
17
+ ## [0.3.1] - 2020-07-17
18
+
19
+ - add touchStarted(), touchEnded(), touchMoved() and touches()
20
+ - make all event handler drawable
21
+ - limit font max size to 256
22
+
23
+
24
+ ## [0.3.0] - 2020-05-21
25
+
26
+ - add createGraphics()
27
+
28
+
29
+ ## [0.2.7] - 2020-04-17
30
+
31
+ - add strokeCap() and strokeJoin()
32
+
33
+
4
34
  ## [0.2.6] - 2020-04-17
5
35
 
6
36
  - push(), pushMatrix() and pushStyle() take block to automatic pop
7
- - add stroke_cap() and stroke_join()
8
37
  - refine startup process
9
38
  - add curve() and bezier()
10
39
  - add imageMode(), Image#resize(), Image#copy() and copy()
data/Rakefile CHANGED
@@ -18,6 +18,7 @@ MODULES = [Xot, Rucy, Rays, Reflex, RubySketch]
18
18
 
19
19
  ENV['RDOC'] = 'yardoc --no-private'
20
20
 
21
+ test_ruby_extension
21
22
  generate_documents
22
23
  build_ruby_gem
23
24
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.6
1
+ 0.3.3
@@ -2,32 +2,14 @@ require 'rubysketch'
2
2
 
3
3
 
4
4
  begin
5
- context = RubySketch::Processing.new
6
- methods = context.class.instance_methods(false)
7
- .reject {|name| name =~ /__$/}
8
- consts = context.class.constants
9
- .reject {|name| name =~ /__$/}
10
- .each_with_object({}) {|name, h| h[name] = context.class.const_get name}
5
+ include RubySketch::Processing::Context
11
6
 
12
- self.class.class_eval do
13
- methods.each do |name|
14
- define_method name do |*args, &block|
15
- context.__send__ name, *args, &block
16
- end
17
- end
18
- consts.each do |(name, value)|
19
- const_set name, value
20
- end
21
- end
22
-
23
- window = RubySketch::Window.new do |_|
24
- window.start
25
- end
26
- context.setup__ window
7
+ window = RubySketch::Window.new {start}
8
+ setup__ window
27
9
 
28
- window.canvas_painter.__send__ :begin_paint
10
+ window.__send__ :begin_draw
29
11
  at_exit do
30
- window.canvas_painter.__send__ :end_paint
31
- Reflex.start {window.show}
12
+ window.__send__ :end_draw
13
+ Reflex.start {window.show} unless $!
32
14
  end
33
15
  end
@@ -3,1067 +3,1548 @@ module RubySketch
3
3
 
4
4
  # Processing compatible API
5
5
  #
6
- class Processing
6
+ module Processing
7
+
8
+
9
+ # Vector class.
10
+ #
11
+ class Vector
12
+
13
+ include Comparable
14
+
15
+ # Initialize vector object.
16
+ #
17
+ # @overload new()
18
+ # @overload new(x)
19
+ # @overload new(x, y)
20
+ # @overload new(x, y, z)
21
+ # @overload new(v)
22
+ # @overload new(a)
23
+ #
24
+ # @param x [Numeric] x of vector
25
+ # @param y [Numeric] y of vector
26
+ # @param z [Numeric] z of vector
27
+ # @param v [Vector] vector object to copy
28
+ # @param a [Array] array like [x, y, z]
29
+ #
30
+ def initialize (x = 0, y = 0, z = 0, context: context)
31
+ @point = case x
32
+ when Rays::Point then x.dup
33
+ when Vector then x.getInternal__.dup
34
+ when Array then Rays::Point.new x[0] || 0, x[1] || 0, x[2] || 0
35
+ else Rays::Point.new x || 0, y || 0, z || 0
36
+ end
37
+ @context = context
38
+ end
7
39
 
8
- include Math
40
+ # Initializer for dup or clone
41
+ #
42
+ def initialize_copy (o)
43
+ @point = o.getInternal__.dup
44
+ end
9
45
 
10
- HALF_PI = PI / 2
11
- QUARTER_PI = PI / 4
12
- TWO_PI = PI * 2
13
- TAU = PI * 2
46
+ # Copy vector object
47
+ #
48
+ # @return [Vector] duplicated vector object
49
+ #
50
+ alias copy dup
51
+
52
+ # Sets x, y and z.
53
+ #
54
+ # @overload set(x)
55
+ # @overload set(x, y)
56
+ # @overload set(x, y, z)
57
+ # @overload set(v)
58
+ # @overload set(a)
59
+ #
60
+ # @param x [Numeric] x of vector
61
+ # @param y [Numeric] y of vector
62
+ # @param z [Numeric] z of vector
63
+ # @param v [Vector] vector object to copy
64
+ # @param a [Array] array with x, y, z
65
+ #
66
+ # @return [nil] nil
67
+ #
68
+ def set (*args)
69
+ initialize *args
70
+ self
71
+ end
14
72
 
15
- # RGB mode for colorMode() function.
16
- #
17
- RGB = :RGB
73
+ # Gets x value.
74
+ #
75
+ # @return [Numeric] x value of vector
76
+ #
77
+ def x ()
78
+ @point.x
79
+ end
18
80
 
19
- # HSB mode for colorMode() function.
20
- #
21
- HSB = :HSB
81
+ # Gets y value.
82
+ #
83
+ # @return [Numeric] y value of vector
84
+ #
85
+ def y ()
86
+ @point.y
87
+ end
22
88
 
23
- # Radian mode for angleMode() function.
24
- #
25
- RADIANS = :RADIANS
89
+ # Gets z value.
90
+ #
91
+ # @return [Numeric] z value of vector
92
+ #
93
+ def z ()
94
+ @point.z
95
+ end
26
96
 
27
- # Degree mode for angleMode() function.
28
- #
29
- DEGREES = :DEGREES
30
-
31
- CORNER = :CORNER
32
- CORNERS = :CORNERS
33
- CENTER = :CENTER
34
- RADIUS = :RADIUS
35
-
36
- # @private
37
- DEG2RAD__ = PI / 180.0
38
-
39
- # @private
40
- RAD2DEG__ = 180.0 / PI
41
-
42
- # @private
43
- def initialize ()
44
- @loop__ = true
45
- @redraw__ = false
46
- @matrixStack__ = []
47
- @styleStack__ = []
48
- @frameCount__ = 0
49
- @hsbColor__ = false
50
- @colorMaxes__ = [1.0] * 4
51
- @angleScale__ = 1.0
52
- @mouseX__ =
53
- @mouseY__ =
54
- @mousePrevX__ =
55
- @mousePrevY__ = 0
56
- @mousePressed__ = false
57
-
58
- colorMode RGB, 255
59
- angleMode RADIANS
60
- rectMode CORNER
61
- ellipseMode CENTER
62
- imageMode CORNER
63
- end
64
-
65
- # @private
66
- def setup__ (window)
67
- @window__ = window
68
- @painter__ = window.canvas_painter.tap do |o|
69
- o.miter_limit = 10
70
- end
71
-
72
- drawFrame = -> event {
73
- @painter__ = window.canvas_painter
74
- @matrixStack__.clear
75
- @styleStack__.clear
76
- begin
77
- push
78
- @drawBlock__.call event if @drawBlock__
79
- ensure
80
- pop
81
- end
82
- @frameCount__ += 1
83
- }
84
-
85
- updateMouseState = -> x, y, pressed = nil {
86
- @mouseX__ = x
87
- @mouseY__ = y
88
- @mousePressed__ = pressed if pressed != nil
89
- }
90
-
91
- updateMousePrevPos = -> {
92
- @mousePrevX__ = @mouseX__
93
- @mousePrevY__ = @mouseY__
94
- }
95
-
96
- @window__.draw = proc do |e|
97
- if @loop__ || @redraw__
98
- @redraw__ = false
99
- drawFrame.call e
100
- end
101
- updateMousePrevPos.call
97
+ # Sets x value.
98
+ #
99
+ # @return [Numeric] x value of vector
100
+ #
101
+ def x= (x)
102
+ @point.x = x
102
103
  end
103
104
 
104
- @window__.pointer_down = proc do |e|
105
- updateMouseState.call e.x, e.y, true
106
- @mousePressedBlock__.call e if @mousePressedBlock__
105
+ # Sets y value.
106
+ #
107
+ # @return [Numeric] y value of vector
108
+ #
109
+ def y= (y)
110
+ @point.y = y
107
111
  end
108
112
 
109
- @window__.pointer_up = proc do |e|
110
- updateMouseState.call e.x, e.y, false
111
- @mouseReleasedBlock__.call e if @mouseReleasedBlock__
113
+ # Sets z value.
114
+ #
115
+ # @return [Numeric] z value of vector
116
+ #
117
+ def z= (z)
118
+ @point.z = z
112
119
  end
113
120
 
114
- @window__.pointer_move = proc do |e|
115
- updateMouseState.call e.x, e.y
116
- @mouseMovedBlock__.call e if @mouseMovedBlock__
121
+ # Returns the interpolated vector between 2 vectors.
122
+ #
123
+ # @overload lerp(v, amount)
124
+ # @overload lerp(x, y, amount)
125
+ # @overload lerp(x, y, z, amount)
126
+ #
127
+ # @param v [Vector] vector to interpolate
128
+ # @param x [Numeric] x of vector to interpolate
129
+ # @param y [Numeric] y of vector to interpolate
130
+ # @param z [Numeric] z of vector to interpolate
131
+ # @param amount [Numeric] amount to interpolate
132
+ #
133
+ # @return [Vector] interporated vector
134
+ #
135
+ def lerp (*args, amount)
136
+ v = toVector__ *args
137
+ self.x = x + (v.x - x) * amount
138
+ self.y = y + (v.y - y) * amount
139
+ self.z = z + (v.z - z) * amount
140
+ self
117
141
  end
118
142
 
119
- @window__.pointer_drag = proc do |e|
120
- updateMouseState.call e.x, e.y
121
- @mouseDraggedBlock__.call e if @mouseDraggedBlock__
143
+ # Returns the interpolated vector between 2 vectors.
144
+ #
145
+ # @param v1 [Vector] vector to interpolate
146
+ # @param v2 [Vector] vector to interpolate
147
+ # @param amount [Numeric] amount to interpolate
148
+ #
149
+ # @return [Vector] interporated vector
150
+ #
151
+ def self.lerp (v1, v2, amount)
152
+ v1.dup.lerp v2, amount
122
153
  end
123
- end
124
154
 
125
- # Returns the absolute number of the value.
126
- #
127
- # @param value [Numeric] number
128
- #
129
- # @return [Numeric] absolute number
130
- #
131
- def abs (value)
132
- value.abs
133
- end
155
+ # Returns x, y, z as an array
156
+ #
157
+ # @return [Array] array of x, y, z
158
+ #
159
+ def array ()
160
+ @point.to_a 3
161
+ end
134
162
 
135
- # Returns the closest integer number greater than or equal to the value.
136
- #
137
- # @param value [Numeric] number
138
- #
139
- # @return [Numeric] rounded up number
140
- #
141
- def ceil (value)
142
- value.ceil
143
- end
163
+ # Adds a vector.
164
+ #
165
+ # @overload add(v)
166
+ # @overload add(x, y)
167
+ # @overload add(x, y, z)
168
+ #
169
+ # @param v [Vector] vector to add
170
+ # @param x [Vector] x of vector to add
171
+ # @param y [Vector] y of vector to add
172
+ # @param z [Vector] z of vector to add
173
+ #
174
+ # @return [Vector] added vector
175
+ #
176
+ def add (*args)
177
+ @point += toVector__(*args).getInternal__
178
+ self
179
+ end
144
180
 
145
- # Returns the closest integer number less than or equal to the value.
146
- #
147
- # @param value [Numeric] number
148
- #
149
- # @return [Numeric] rounded down number
150
- #
151
- def floor (value)
152
- value.floor
153
- end
181
+ # Subtracts a vector.
182
+ #
183
+ # @overload sub(v)
184
+ # @overload sub(x, y)
185
+ # @overload sub(x, y, z)
186
+ #
187
+ # @param v [Vector] vector to subtract
188
+ # @param x [Vector] x of vector to subtract
189
+ # @param y [Vector] y of vector to subtract
190
+ # @param z [Vector] z of vector to subtract
191
+ #
192
+ # @return [Vector] subtracted vector
193
+ #
194
+ def sub (*args)
195
+ @point -= toVector__(*args).getInternal__
196
+ self
197
+ end
154
198
 
155
- # Returns the closest integer number.
156
- #
157
- # @param value [Numeric] number
158
- #
159
- # @return [Numeric] rounded number
160
- #
161
- def round (value)
162
- value.round
163
- end
199
+ # Multiplies a vector by scalar.
200
+ #
201
+ # @param num [Numeric] number to multiply the vector
202
+ #
203
+ # @return [Vector] multiplied vector
204
+ #
205
+ def mult (num)
206
+ @point *= num
207
+ self
208
+ end
164
209
 
165
- # Returns value raised to the power of exponent.
166
- #
167
- # @param value [Numeric] base number
168
- # @param exponent [Numeric] exponent number
169
- #
170
- # @return [Numeric] value ** exponent
171
- #
172
- def pow (value, exponent)
173
- value ** exponent
174
- end
210
+ # Divides a vector by scalar.
211
+ #
212
+ # @param num [Numeric] number to divide the vector
213
+ #
214
+ # @return [Vector] divided vector
215
+ #
216
+ def div (num)
217
+ @point /= num
218
+ self
219
+ end
175
220
 
176
- # Returns squared value.
177
- #
178
- # @param value [Numeric] number
179
- #
180
- # @return [Numeric] squared value
181
- #
182
- def sq (value)
183
- value * value
184
- end
221
+ # Adds a vector.
222
+ #
223
+ # @param v [Vector] vector to add
224
+ #
225
+ # @return [Vector] added vector
226
+ #
227
+ def + (v)
228
+ dup.add v
229
+ end
185
230
 
186
- # Returns the magnitude (or length) of a vector.
187
- #
188
- # @overload mag(x, y)
189
- # @overload mag(x, y, z)
190
- #
191
- # @param x [Numeric] x of point
192
- # @param y [Numeric] y of point
193
- # @param z [Numeric] z of point
194
- #
195
- # @return [Numeric] magnitude
196
- #
197
- def mag (*args)
198
- x, y, z = *args
199
- case args.size
200
- when 2 then sqrt x * x + y * y
201
- when 3 then sqrt x * x + y * y + z * z
202
- else raise ArgumentError
231
+ # Subtracts a vector.
232
+ #
233
+ # @param v [Vector] vector to subtract
234
+ #
235
+ # @return [Vector] subtracted vector
236
+ #
237
+ def - (v)
238
+ dup.sub v
203
239
  end
204
- end
205
240
 
206
- # Returns distance between 2 points.
207
- #
208
- # @overload dist(x1, y1, x2, y2)
209
- # @overload dist(x1, y1, z1, x2, y2, z2)
210
- #
211
- # @param x1 [Numeric] x of first point
212
- # @param y1 [Numeric] y of first point
213
- # @param z1 [Numeric] z of first point
214
- # @param x2 [Numeric] x of second point
215
- # @param y2 [Numeric] y of second point
216
- # @param z2 [Numeric] z of second point
217
- #
218
- # @return [Numeric] distance between 2 points
219
- #
220
- def dist (*args)
221
- case args.size
222
- when 4
223
- x1, y1, x2, y2 = *args
224
- xx, yy = x2 - x1, y2 - y1
225
- sqrt xx * xx + yy * yy
226
- when 3
227
- x1, y1, z1, x2, y2, z2 = *args
228
- xx, yy, zz = x2 - x1, y2 - y1, z2 - z1
229
- sqrt xx * xx + yy * yy + zz * zz
230
- else raise ArgumentError
241
+ # Multiplies a vector by scalar.
242
+ #
243
+ # @param num [Numeric] number to multiply the vector
244
+ #
245
+ # @return [Vector] multiplied vector
246
+ #
247
+ def * (num)
248
+ dup.mult num
231
249
  end
232
- end
233
250
 
234
- # Normalize the value from range start..stop into 0..1.
235
- #
236
- # @param value [Numeric] number to be normalized
237
- # @param start [Numeric] lower bound of the range
238
- # @param stop [Numeric] upper bound of the range
239
- #
240
- # @return [Numeric] normalized value between 0..1
241
- #
242
- def norm (value, start, stop)
243
- (value.to_f - start.to_f) / (stop.to_f - start.to_f)
244
- end
251
+ # Divides a vector by scalar.
252
+ #
253
+ # @param num [Numeric] number to divide the vector
254
+ #
255
+ # @return [Vector] divided vector
256
+ #
257
+ def / (num)
258
+ dup.div num
259
+ end
245
260
 
246
- # Returns the interpolated number between range start..stop.
247
- #
248
- # @param start [Numeric] lower bound of the range
249
- # @param stop [Numeric] upper bound of the range
250
- # @param amount [Numeric] amount to interpolate
251
- #
252
- # @return [Numeric] interporated number
253
- #
254
- def lerp (start, stop, amount)
255
- start + (stop - start) * amount
256
- end
261
+ # Adds 2 vectors.
262
+ #
263
+ # @overload add(v1, v2)
264
+ # @overload add(v1, v2, target)
265
+ #
266
+ # @param v1 [Vector] a vector
267
+ # @param v2 [Vector] another vector
268
+ # @param target [Vector] vector to store added vector
269
+ #
270
+ # @return [Vector] added vector
271
+ #
272
+ def self.add (v1, v2, target = nil)
273
+ v = v1 + v2
274
+ target.set v if self === target
275
+ v
276
+ end
257
277
 
258
- # Maps a number from range start1..stop1 to range start2..stop2.
259
- #
260
- # @param value [Numeric] number to be mapped
261
- # @param start1 [Numeric] lower bound of the range1
262
- # @param stop1 [Numeric] upper bound of the range1
263
- # @param start2 [Numeric] lower bound of the range2
264
- # @param stop2 [Numeric] upper bound of the range2
265
- #
266
- # @return [Numeric] mapped number
267
- #
268
- def map (value, start1, stop1, start2, stop2)
269
- lerp start2, stop2, norm(value, start1, stop1)
270
- end
278
+ # Subtracts 2 vectors.
279
+ #
280
+ # @overload sub(v1, v2)
281
+ # @overload sub(v1, v2, target)
282
+ #
283
+ # @param v1 [Vector] a vector
284
+ # @param v2 [Vector] another vector
285
+ # @param target [Vector] vector to store subtracted vector
286
+ #
287
+ # @return [Vector] subtracted vector
288
+ #
289
+ def self.sub (v1, v2, target = nil)
290
+ v = v1 - v2
291
+ target.set v if self === target
292
+ v
293
+ end
271
294
 
272
- # Returns minimum value.
273
- #
274
- # @overload min(a, b)
275
- # @overload min(a, b, c)
276
- # @overload min(array)
277
- #
278
- # @param a [Numeric] value to compare
279
- # @param b [Numeric] value to compare
280
- # @param c [Numeric] value to compare
281
- # @param array [Numeric] values to compare
282
- #
283
- # @return [Numeric] minimum value
284
- #
285
- def min (*args)
286
- args.flatten.min
287
- end
295
+ # Multiplies a vector by scalar.
296
+ #
297
+ # @overload mult(v, num)
298
+ # @overload mult(v, num, target)
299
+ #
300
+ # @param v [Vector] a vector
301
+ # @param num [Numeric] number to multiply the vector
302
+ # @param target [Vector] vector to store multiplied vector
303
+ #
304
+ # @return [Vector] multiplied vector
305
+ #
306
+ def self.mult (v1, num, target = nil)
307
+ v = v1 * num
308
+ target.set v if self === target
309
+ v
310
+ end
288
311
 
289
- # Returns maximum value.
290
- #
291
- # @overload max(a, b)
292
- # @overload max(a, b, c)
293
- # @overload max(array)
294
- #
295
- # @param a [Numeric] value to compare
296
- # @param b [Numeric] value to compare
297
- # @param c [Numeric] value to compare
298
- # @param array [Numeric] values to compare
299
- #
300
- # @return [Numeric] maximum value
301
- #
302
- def max (*args)
303
- args.flatten.max
304
- end
312
+ # Divides a vector by scalar.
313
+ #
314
+ # @overload div(v, num)
315
+ # @overload div(v, num, target)
316
+ #
317
+ # @param v [Vector] a vector
318
+ # @param num [Numeric] number to divide the vector
319
+ # @param target [Vector] vector to store divided vector
320
+ #
321
+ # @return [Vector] divided vector
322
+ #
323
+ def self.div (v1, num, target = nil)
324
+ v = v1 / num
325
+ target.set v if self === target
326
+ v
327
+ end
305
328
 
306
- # Constrains the number between min..max.
307
- #
308
- # @param value [Numeric] number to be constrained
309
- # @param min [Numeric] lower bound of the range
310
- # @param max [Numeric] upper bound of the range
311
- #
312
- # @return [Numeric] constrained number
313
- #
314
- def constrain (value, min, max)
315
- value < min ? min : (value > max ? max : value)
316
- end
329
+ # Returns the length of the vector.
330
+ #
331
+ # @return [Numeric] length
332
+ #
333
+ def mag ()
334
+ @point.length
335
+ end
317
336
 
318
- # Converts degree to radian.
319
- #
320
- # @param degree [Numeric] degree to convert
321
- #
322
- # @return [Numeric] radian
323
- #
324
- def radians (degree)
325
- degree * DEG2RAD__
326
- end
337
+ # Returns squared length of the vector.
338
+ #
339
+ # @return [Numeric] squared length
340
+ #
341
+ def magSq ()
342
+ Rays::Point::dot(@point, @point)
343
+ end
327
344
 
328
- # Converts radian to degree.
329
- #
330
- # @param radian [Numeric] radian to convert
331
- #
332
- # @return [Numeric] degree
333
- #
334
- def degrees (radian)
335
- radian * RAD2DEG__
336
- end
345
+ # Changes the length of the vector.
346
+ #
347
+ # @overload setMag(len)
348
+ # @overload setMag(target, len)
349
+ #
350
+ # @param len [Numeric] length of new vector
351
+ # @param target [Vector] vector to store new vector
352
+ #
353
+ # @return [Vector] vector with new length
354
+ #
355
+ def setMag (target = nil, len)
356
+ (target || self).set @point.normal * len
357
+ end
337
358
 
338
- # Define setup block.
339
- #
340
- def setup (&block)
341
- @window__.setup = block
342
- nil
343
- end
359
+ # Changes the length of the vector to 1.0.
360
+ #
361
+ # @param target [Vector] vector to store the normalized vector
362
+ #
363
+ # @return [Vector] normalized vector
364
+ #
365
+ def normalize (target = nil)
366
+ (target || self).set @point.normal
367
+ end
344
368
 
345
- # Define draw block.
346
- #
347
- def draw (&block)
348
- @drawBlock__ = block if block
349
- nil
350
- end
351
-
352
- def key (&block)
353
- @window__.key = block
354
- nil
355
- end
356
-
357
- def mousePressed (&block)
358
- @mousePressedBlock__ = block if block
359
- @mousePressed__
360
- end
361
-
362
- def mouseReleased (&block)
363
- @mouseReleasedBlock__ = block if block
364
- nil
365
- end
366
-
367
- def mouseMoved (&block)
368
- @mouseMovedBlock__ = block if block
369
- nil
370
- end
371
-
372
- def mouseDragged (&block)
373
- @mouseDraggedBlock__ = block if block
374
- nil
375
- end
376
-
377
- # @private
378
- private def size__ (width, height)
379
- raise 'size() must be called on startup or setup block' if @started__
380
-
381
- @painter__.send :end_paint
382
- reset_canvas width, height
383
- @painter__.send :begin_paint
384
-
385
- @auto_resize__ = false
386
- end
387
-
388
- def width ()
389
- @window__.canvas.width
390
- end
391
-
392
- def height ()
393
- @window__.canvas.height
394
- end
395
-
396
- def windowWidth ()
397
- @window__.width
398
- end
399
-
400
- def windowHeight ()
401
- @window__.height
402
- end
403
-
404
- # Returns number of frames since program started.
405
- #
406
- # @return [Integer] total number of frames
407
- #
408
- def frameCount ()
409
- @frameCount__
410
- end
369
+ # Changes the length of the vector if it's length is greater than the max value.
370
+ #
371
+ # @param max [Numeric] max length
372
+ #
373
+ # @return [Vector] new vector
374
+ #
375
+ def limit (max)
376
+ setMag max if magSq > max ** 2
377
+ self
378
+ end
411
379
 
412
- # Returns number of frames per second.
413
- #
414
- # @return [Float] frames per second
415
- #
416
- def frameRate ()
417
- @window__.event.fps
418
- end
380
+ # Returns the distance of 2 vectors.
381
+ #
382
+ # @param v [Vector] a vector
383
+ #
384
+ # @return [Numeric] the distance
385
+ #
386
+ def dist (v)
387
+ (self - v).mag
388
+ end
419
389
 
420
- # Returns pixel density
421
- #
422
- # @return [Numeric] pixel density
423
- #
424
- def displayDensity ()
425
- @painter__.pixel_density
426
- end
390
+ # Returns the distance of 2 vectors.
391
+ #
392
+ # @param v1 [Vector] a vector
393
+ # @param v2 [Vector] another vector
394
+ #
395
+ # @return [Numeric] the distance
396
+ #
397
+ def self.dist (v1, v2)
398
+ v1.dist v2
399
+ end
427
400
 
428
- # Returns mouse x position
429
- #
430
- # @return [Numeric] horizontal position of mouse
431
- #
432
- def mouseX ()
433
- @mouseX__
434
- end
401
+ # Calculates the dot product of 2 vectors.
402
+ #
403
+ # @overload dot(v)
404
+ # @overload dot(x, y)
405
+ # @overload dot(x, y, z)
406
+ #
407
+ # @param v [Vector] a vector
408
+ # @param x [Numeric] x of vector
409
+ # @param y [Numeric] y of vector
410
+ # @param z [Numeric] z of vector
411
+ #
412
+ # @return [Numeric] result of dot product
413
+ #
414
+ def dot (*args)
415
+ Rays::Point::dot getInternal__, toVector__(*args).getInternal__
416
+ end
435
417
 
436
- # Returns mouse y position
437
- #
438
- # @return [Numeric] vertical position of mouse
439
- #
440
- def mouseY ()
441
- @mouseY__
442
- end
418
+ # Calculates the dot product of 2 vectors.
419
+ #
420
+ # @param v1 [Vector] a vector
421
+ # @param v2 [Vector] another vector
422
+ #
423
+ # @return [Numeric] result of dot product
424
+ #
425
+ def self.dot (v1, v2)
426
+ v1.dot v2
427
+ end
443
428
 
444
- # Returns mouse x position in previous frame
445
- #
446
- # @return [Numeric] horizontal position of mouse
447
- #
448
- def pmouseX ()
449
- @mousePrevX__
450
- end
429
+ # Calculates the cross product of 2 vectors.
430
+ #
431
+ # @overload cross(v)
432
+ # @overload cross(x, y)
433
+ # @overload cross(x, y, z)
434
+ #
435
+ # @param v [Vector] a vector
436
+ # @param x [Numeric] x of vector
437
+ # @param y [Numeric] y of vector
438
+ # @param z [Numeric] z of vector
439
+ #
440
+ # @return [Numeric] result of cross product
441
+ #
442
+ def cross (a, *rest)
443
+ target = self.class === rest.last ? rest.pop : nil
444
+ v = self.class.new Rays::Point::cross getInternal__, toVector__(a, *rest).getInternal__
445
+ target.set v if self.class === target
446
+ v
447
+ end
451
448
 
452
- # Returns mouse y position in previous frame
453
- #
454
- # @return [Numeric] vertical position of mouse
455
- #
456
- def pmouseY ()
457
- @mousePrevY__
458
- end
449
+ # Calculates the cross product of 2 vectors.
450
+ #
451
+ # @param v1 [Vector] a vector
452
+ # @param v2 [Vector] another vector
453
+ #
454
+ # @return [Numeric] result of cross product
455
+ #
456
+ def self.cross (v1, v2, target = nil)
457
+ v1.cross v2, target
458
+ end
459
459
 
460
- # Sets color mode and max color values.
461
- #
462
- # @overload colorMode(mode)
463
- # @overload colorMode(mode, max)
464
- # @overload colorMode(mode, max1, max2, max3)
465
- # @overload colorMode(mode, max1, max2, max3, maxA)
466
- #
467
- # @param mode [RGB, HSB] RGB or HSB
468
- # @param max [Numeric] max values for all color values
469
- # @param max1 [Numeric] max value for red or hue
470
- # @param max2 [Numeric] max value for green or saturation
471
- # @param max3 [Numeric] max value for blue or brightness
472
- # @param maxA [Numeric] max value for alpha
473
- #
474
- # @return [nil] nil
475
- #
476
- def colorMode (mode, *maxes)
477
- raise ArgumentError, "invalid color mode: #{mode}" unless [RGB, HSB].include?(mode)
478
- raise ArgumentError unless [0, 1, 3, 4].include?(maxes.size)
460
+ # Rotate the vector.
461
+ #
462
+ # @param angle [Numeric] the angle of rotation
463
+ #
464
+ # @return [Vector] rotated this object
465
+ #
466
+ def rotate (angle)
467
+ angle = @context ? @context.toAngle__(angle) : angle * Utility::RAD2DEG__
468
+ @point.rotate! angle
469
+ self
470
+ end
479
471
 
480
- @hsbColor__ = mode.upcase == HSB
481
- case maxes.size
482
- when 1 then @colorMaxes__ = [maxes.first.to_f] * 4
483
- when 3, 4 then @colorMaxes__[0...maxes.size] = maxes.map &:to_f
472
+ # Returns the angle of rotation for this vector.
473
+ #
474
+ # @return [Numeric] the angle in radians
475
+ #
476
+ def heading ()
477
+ Math.atan2 y, x
484
478
  end
485
- nil
486
- end
487
479
 
488
- # @private
489
- private def toRGBA__ (*args)
490
- a, b, c, d = args
491
- return parseColor__(a, b || alphaMax__) if a.kind_of?(String)
480
+ # Returns rotated new vector.
481
+ #
482
+ # @param angle [Numeric] the angle of rotation
483
+ # @param target [Vector] vector to store new vector
484
+ #
485
+ # @return [Vector] rotated vector
486
+ #
487
+ def self.fromAngle (angle, target = nil)
488
+ v = self.new(1, 0, 0).rotate(angle)
489
+ target.set v if target
490
+ v
491
+ end
492
492
 
493
- rgba = case args.size
494
- when 1, 2 then [a, a, a, b || alphaMax__]
495
- when 3, 4 then [a, b, c, d || alphaMax__]
496
- else raise ArgumentError
493
+ # Returns angle between 2 vectors.
494
+ #
495
+ # @param v1 [Vector] a vector
496
+ # @param v2 [Vector] another vector
497
+ #
498
+ # @return [Numeric] angle in radians
499
+ #
500
+ def self.angleBetween (v1, v2)
501
+ x1, y1, z1 = v1.array
502
+ x2, y2, z2 = v2.array
503
+ return 0 if (x1 == 0 && y1 == 0 && z1 == 0) || (x2 == 0 && y2 == 0 && z2 == 0)
504
+
505
+ x = dot(v1, v2) / (v1.mag * v2.mag)
506
+ return Math::PI if x <= -1
507
+ return 0 if x >= 1
508
+ return Math.acos x
497
509
  end
498
- rgba = rgba.map.with_index {|value, i| value / @colorMaxes__[i]}
499
- color = @hsbColor__ ? Rays::Color.hsv(*rgba) : Rays::Color.new(*rgba)
500
- color.to_a
501
- end
502
510
 
503
- # @private
504
- private def parseColor__ (str, alpha)
505
- result = str.match /^\s*##{'([0-9a-f]{2})' * 3}\s*$/i
506
- raise ArgumentError, "invalid color code: '#{str}'" unless result
511
+ # Returns a new 2D unit vector with a random direction.
512
+ #
513
+ # @param target [Vector] a vector to store the new vector
514
+ #
515
+ # @return [Vector] a random vector
516
+ #
517
+ def self.random2D (target = nil)
518
+ v = self.fromAngle rand 0.0...(Math::PI * 2)
519
+ target.set v if target
520
+ v
521
+ end
507
522
 
508
- rgb = result[1..3].map.with_index {|hex, i| hex.to_i(16) / 255.0}
509
- return *rgb, (alpha / alphaMax__)
510
- end
523
+ # Returns a new 3D unit vector with a random direction.
524
+ #
525
+ # @param target [Vector] a vector to store the new vector
526
+ #
527
+ # @return [Vector] a random vector
528
+ #
529
+ def self.random3D (target = nil)
530
+ angle = rand 0.0...(Math::PI * 2)
531
+ z = rand -1.0..1.0
532
+ z2 = z ** 2
533
+ x = Math.sqrt(1.0 - z2) * Math.cos(angle)
534
+ y = Math.sqrt(1.0 - z2) * Math.sin(angle)
535
+ v = self.new x, y, z
536
+ target.set v if target
537
+ v
538
+ end
511
539
 
512
- # @private
513
- private def alphaMax__ ()
514
- @colorMaxes__[3]
515
- end
540
+ # @private
541
+ def inspect ()
542
+ "<##{self.class.name} #{x}, #{y}, #{z}>"
543
+ end
516
544
 
517
- # Sets angle mode.
518
- #
519
- # @param mode [RADIANS, DEGREES] RADIANS or DEGREES
520
- #
521
- # @return [nil] nil
522
- #
523
- def angleMode (mode)
524
- @angleScale__ = case mode
525
- when RADIANS then RAD2DEG__
526
- when DEGREES then 1.0
527
- else raise ArgumentError, "invalid angle mode: #{mode}"
545
+ # @private
546
+ def <=> (o)
547
+ @point <=> o.getInternal__
528
548
  end
529
- nil
530
- end
531
549
 
532
- # @private
533
- private def toAngle__ (angle)
534
- angle * @angleScale__
535
- end
550
+ # @private
551
+ protected def getInternal__ ()
552
+ @point
553
+ end
536
554
 
537
- # Sets rect mode. Default is CORNER.
538
- #
539
- # CORNER -> rect(left, top, width, height)
540
- # CORNERS -> rect(left, top, right, bottom)
541
- # CENTER -> rect(center_x, center_y, width, height)
542
- # RADIUS -> rect(center_x, center_y, radius_h, radius_v)
543
- #
544
- # @param mode [CORNER, CORNERS, CENTER, RADIUS]
545
- #
546
- # @return [nil] nil
547
- #
548
- def rectMode (mode)
549
- @rectMode__ = mode
550
- end
555
+ # @private
556
+ private def toVector__ (*args)
557
+ self.class === args.first ? args.first : self.class.new(*args)
558
+ end
551
559
 
552
- # Sets ellipse mode. Default is CENTER.
553
- #
554
- # CORNER -> ellipse(left, top, width, height)
555
- # CORNERS -> ellipse(left, top, right, bottom)
556
- # CENTER -> ellipse(center_x, center_y, width, height)
557
- # RADIUS -> ellipse(center_x, center_y, radius_h, radius_v)
558
- #
559
- # @param mode [CORNER, CORNERS, CENTER, RADIUS]
560
- #
561
- # @return [nil] nil
562
- #
563
- def ellipseMode (mode)
564
- @ellipseMode__ = mode
565
- end
560
+ end# Vector
566
561
 
567
- # Sets image mode. Default is CORNER.
568
- #
569
- # CORNER -> image(img, left, top, width, height)
570
- # CORNERS -> image(img, left, top, right, bottom)
571
- # CENTER -> image(img, center_x, center_y, width, height)
572
- #
573
- # @param mode [CORNER, CORNERS, CENTER]
574
- #
575
- # @return [nil] nil
576
- #
577
- def imageMode (mode)
578
- @imageMode__ = mode
579
- end
580
-
581
- # @private
582
- private def toXYWH__ (mode, a, b, c, d)
583
- case mode
584
- when CORNER then [a, b, c, d]
585
- when CORNERS then [a, b, c - a, d - b]
586
- when CENTER then [a - c / 2.0, b - d / 2.0, c, d]
587
- when RADIUS then [a - c, b - d, c * 2, d * 2]
588
- else raise ArgumentError # ToDo: refine error message
589
- end
590
- end
591
-
592
- # Enables calling draw block on every frame.
593
- #
594
- # @return [nil] nil
595
- #
596
- def loop ()
597
- @loop__ = true
598
- end
599
562
 
600
- # Disables calling draw block on every frame.
601
- #
602
- # @return [nil] nil
563
+ # Image object.
603
564
  #
604
- def noLoop ()
605
- @loop__ = false
606
- end
565
+ class Image
607
566
 
608
- # Calls draw block to redraw frame.
609
- #
610
- # @return [nil] nil
611
- #
612
- def redraw ()
613
- @redraw__ = true
614
- end
567
+ # @private
568
+ def initialize (image)
569
+ @image = image
570
+ end
615
571
 
616
- # Clears screen.
617
- #
618
- # @overload background(str)
619
- # @overload background(str, alpha)
620
- # @overload background(gray)
621
- # @overload background(gray, alpha)
622
- # @overload background(r, g, b)
623
- # @overload background(r, g, b, alpha)
624
- #
625
- # @param str [String] color code like '#00AAFF'
626
- # @param gray [Integer] gray value (0..255)
627
- # @param r [Integer] red value (0..255)
628
- # @param g [Integer] green value (0..255)
629
- # @param b [Integer] blue value (0..255)
630
- # @param alpha [Integer] alpha value (0..255)
631
- #
632
- # @return [nil] nil
633
- #
634
- def background (*args)
635
- rgba = toRGBA__ *args
636
- if rgba[3] == 1
637
- @painter__.background *rgba
638
- else
639
- @painter__.push fill: rgba, stroke: nil do |_|
640
- @painter__.rect 0, 0, width, height
641
- end
572
+ # Gets width of image.
573
+ #
574
+ # @return [Numeric] width of image
575
+ #
576
+ def width ()
577
+ @image.width
642
578
  end
643
- nil
644
- end
645
579
 
646
- # Sets fill color.
647
- #
648
- # @overload fill(rgb)
649
- # @overload fill(rgb, alpha)
650
- # @overload fill(gray)
651
- # @overload fill(gray, alpha)
652
- # @overload fill(r, g, b)
653
- # @overload fill(r, g, b, alpha)
654
- #
655
- # @param rgb [String] color code like '#00AAFF'
656
- # @param gray [Integer] gray value (0..255)
657
- # @param r [Integer] red value (0..255)
658
- # @param g [Integer] green value (0..255)
659
- # @param b [Integer] blue value (0..255)
660
- # @param alpha [Integer] alpha value (0..255)
661
- #
662
- # @return [nil] nil
663
- #
664
- def fill (*args)
665
- @painter__.fill(*toRGBA__(*args))
666
- nil
667
- end
580
+ # Gets height of image.
581
+ #
582
+ # @return [Numeric] height of image
583
+ #
584
+ def height ()
585
+ @image.height
586
+ end
668
587
 
669
- # Sets stroke color.
670
- #
671
- # @overload stroke(rgb)
672
- # @overload stroke(rgb, alpha)
673
- # @overload stroke(gray)
674
- # @overload stroke(gray, alpha)
675
- # @overload stroke(r, g, b)
676
- # @overload stroke(r, g, b, alpha)
677
- #
678
- # @param rgb [String] color code like '#00AAFF'
679
- # @param gray [Integer] gray value (0..255)
680
- # @param r [Integer] red value (0..255)
681
- # @param g [Integer] green value (0..255)
682
- # @param b [Integer] blue value (0..255)
683
- # @param alpha [Integer] alpha value (0..255)
684
- #
685
- # @return [nil] nil
686
- #
687
- def stroke (*args)
688
- @painter__.stroke(*toRGBA__(*args))
689
- nil
690
- end
588
+ # Resizes image.
589
+ #
590
+ # @param width [Numeric] width for resized image
591
+ # @param height [Numeric] height for resized image
592
+ #
593
+ # @return [nil] nil
594
+ #
595
+ def resize (width, height)
596
+ @image = Rays::Image.new(width, height).paint do |painter|
597
+ painter.image @image, 0, 0, width, height
598
+ end
599
+ nil
600
+ end
691
601
 
692
- # Sets stroke weight.
693
- #
694
- # @param weight [Numeric] width of stroke
695
- #
696
- # @return [nil] nil
697
- #
698
- def strokeWeight (weight)
699
- @painter__.stroke_width weight
700
- nil
701
- end
602
+ # Copies image.
603
+ #
604
+ # @overload copy(sx, sy, sw, sh, dx, dy, dw, dh)
605
+ # @overload copy(img, sx, sy, sw, sh, dx, dy, dw, dh)
606
+ #
607
+ # @param img [Image] image for copy source
608
+ # @param sx [Numrtic] x position of source region
609
+ # @param sy [Numrtic] y position of source region
610
+ # @param sw [Numrtic] width of source region
611
+ # @param sh [Numrtic] height of source region
612
+ # @param dx [Numrtic] x position of destination region
613
+ # @param dy [Numrtic] y position of destination region
614
+ # @param dw [Numrtic] width of destination region
615
+ # @param dh [Numrtic] height of destination region
616
+ #
617
+ # @return [nil] nil
618
+ #
619
+ def copy (img = nil, sx, sy, sw, sh, dx, dy, dw, dh)
620
+ img ||= self
621
+ @image.paint do |painter|
622
+ painter.image img.getInternal__, sx, sy, sw, sh, dx, dy, dw, dh
623
+ end
624
+ end
702
625
 
703
- # Disables filling.
704
- #
705
- # @return [nil] nil
706
- #
707
- def noFill ()
708
- @painter__.fill nil
709
- nil
710
- end
626
+ # Saves image to file.
627
+ #
628
+ # @param filename [String] file name to save image
629
+ #
630
+ def save (filename)
631
+ @image.save filename
632
+ end
711
633
 
712
- # Disables drawing stroke.
713
- #
714
- # @return [nil] nil
715
- #
716
- def noStroke ()
717
- @painter__.stroke nil
718
- nil
719
- end
634
+ # @private
635
+ def getInternal__ ()
636
+ @image
637
+ end
720
638
 
721
- # Sets font.
722
- #
723
- # @param name [String] font name
724
- # @param size [Numeric] font size
725
- #
726
- # @return [Font] current font
727
- #
728
- def textFont (name = nil, size = nil)
729
- @painter__.font name, size if name || size
730
- Font.new @painter__.font
731
- end
639
+ end# Image
732
640
 
733
- # Sets text size.
734
- #
735
- # @param size [Numeric] font size
736
- #
737
- # @return [nil] nil
738
- #
739
- def textSize (size)
740
- @painter__.font @painter__.font.name, size
741
- nil
742
- end
743
641
 
744
- # Draws a point.
745
- #
746
- # @param x [Numeric] horizontal position
747
- # @param y [Numeric] vertical position
748
- #
749
- # @return [nil] nil
642
+ # Font object.
750
643
  #
751
- def point (x, y)
752
- w = @painter__.stroke_width
753
- w = 1 if w == 0
754
- @painter__.ellipse x - (w / 2.0), y - (w / 2.0), w, w
755
- nil
756
- end
757
-
758
- # Draws a line.
759
- #
760
- # @param x1 [Numeric] horizontal position of first point
761
- # @param y1 [Numeric] vertical position of first point
762
- # @param x2 [Numeric] horizontal position of second point
763
- # @param y2 [Numeric] vertical position of second point
764
- #
765
- # @return [nil] nil
766
- #
767
- def line (x1, y1, x2, y2)
768
- @painter__.line x1, y1, x2, y2
769
- nil
770
- end
644
+ class Font
771
645
 
772
- # Draws a rectangle.
773
- #
774
- # @overload rect(a, b, c, d)
775
- # @overload rect(a, b, c, d, r)
776
- # @overload rect(a, b, c, d, tl, tr, br, bl)
777
- #
778
- # @param a [Numeric] horizontal position of the shape by default
779
- # @param b [Numeric] vertical position of the shape by default
780
- # @param c [Numeric] width of the shape by default
781
- # @param d [Numeric] height of the shape by default
782
- # @param r [Numeric] radius for all corners
783
- # @param tl [Numeric] radius for top-left corner
784
- # @param tr [Numeric] radius for top-right corner
785
- # @param br [Numeric] radius for bottom-right corner
786
- # @param bl [Numeric] radius for bottom-left corner
787
- #
788
- # @return [nil] nil
789
- #
790
- def rect (a, b, c, d, *args)
791
- x, y, w, h = toXYWH__ @rectMode__, a, b, c, d
792
- case args.size
793
- when 0 then @painter__.rect x, y, w, h
794
- when 1 then @painter__.rect x, y, w, h, round: args[0]
795
- when 4 then @painter__.rect x, y, w, h, lt: args[0], rt: args[1], rb: args[2], lb: args[3]
796
- else raise ArgumentError # ToDo: refine error message
646
+ # @private
647
+ def initialize (font)
648
+ @font = font
797
649
  end
798
- nil
799
- end
800
650
 
801
- # Draws an ellipse.
802
- #
803
- # @param a [Numeric] horizontal position of the shape
804
- # @param b [Numeric] vertical position of the shape
805
- # @param c [Numeric] width of the shape
806
- # @param d [Numeric] height of the shape
807
- #
808
- # @return [nil] nil
809
- #
810
- def ellipse (a, b, c, d)
811
- x, y, w, h = toXYWH__ @ellipseMode__, a, b, c, d
812
- @painter__.ellipse x, y, w, h
813
- nil
814
- end
651
+ # Returns bounding box.
652
+ #
653
+ # @overload textBounds(str)
654
+ # @overload textBounds(str, x, y)
655
+ # @overload textBounds(str, x, y, fontSize)
656
+ #
657
+ # @param str [String] text to calculate bounding box
658
+ # @param x [Numeric] horizontal position of bounding box
659
+ # @param y [Numeric] vertical position of bounding box
660
+ # @param fontSize [Numeric] font size
661
+ #
662
+ # @return [TextBounds] bounding box for text
663
+ #
664
+ def textBounds (str, x = 0, y = 0, fontSize = nil)
665
+ f = fontSize ? Rays::Font.new(@font.name, fontSize) : @font
666
+ TextBounds.new x, y, x + f.width(str), y + f.height
667
+ end
815
668
 
816
- # Draws a circle.
817
- #
818
- # @param x [Numeric] horizontal position of the shape
819
- # @param y [Numeric] vertical position of the shape
820
- # @param extent [Numeric] width and height of the shape
821
- #
822
- # @return [nil] nil
823
- #
824
- def circle (x, y, extent)
825
- ellipse x, y, extent, extent
826
- end
669
+ end# Font
827
670
 
828
- # Draws an arc.
829
- #
830
- # @param a [Numeric] horizontal position of the shape
831
- # @param b [Numeric] vertical position of the shape
832
- # @param c [Numeric] width of the shape
833
- # @param d [Numeric] height of the shape
834
- # @param start [Numeric] angle to start the arc
835
- # @param stop [Numeric] angle to stop the arc
836
- #
837
- # @return [nil] nil
838
- #
839
- def arc (a, b, c, d, start, stop)
840
- x, y, w, h = toXYWH__ @ellipseMode__, a, b, c, d
841
- start = toAngle__ start
842
- stop = toAngle__ stop
843
- @painter__.ellipse x, y, w, h, from: start, to: stop
844
- nil
845
- end
846
-
847
- # Draws a square.
848
- #
849
- # @param x [Numeric] horizontal position of the shape
850
- # @param y [Numeric] vertical position of the shape
851
- # @param extent [Numeric] width and height of the shape
852
- #
853
- # @return [nil] nil
854
- #
855
- def square (x, y, extent)
856
- rect x, y, extent, extent
857
- end
858
671
 
859
- # Draws a triangle.
672
+ # Bounding box for text.
860
673
  #
861
- # @param x1 [Numeric] horizontal position of first point
862
- # @param y1 [Numeric] vertical position of first point
863
- # @param x2 [Numeric] horizontal position of second point
864
- # @param y2 [Numeric] vertical position of second point
865
- # @param x3 [Numeric] horizontal position of third point
866
- # @param y3 [Numeric] vertical position of third point
867
- #
868
- # @return [nil] nil
869
- #
870
- def triangle (x1, y1, x2, y2, x3, y3)
871
- @painter__.line x1, y1, x2, y2, x3, y3, loop: true
872
- nil
873
- end
674
+ class TextBounds
874
675
 
875
- # Draws a quad.
876
- #
877
- # @param x1 [Numeric] horizontal position of first point
878
- # @param y1 [Numeric] vertical position of first point
879
- # @param x2 [Numeric] horizontal position of second point
880
- # @param y2 [Numeric] vertical position of second point
881
- # @param x3 [Numeric] horizontal position of third point
882
- # @param y3 [Numeric] vertical position of third point
883
- # @param x4 [Numeric] horizontal position of fourth point
884
- # @param y4 [Numeric] vertical position of fourth point
885
- #
886
- # @return [nil] nil
887
- #
888
- def quad (x1, y1, x2, y2, x3, y3, x4, y4)
889
- @painter__.line x1, y1, x2, y2, x3, y3, x4, y4, loop: true
890
- nil
891
- end
676
+ # Horizontal position
677
+ #
678
+ attr_reader :x
892
679
 
893
- # Draws a Catmull-Rom spline curve.
894
- #
895
- # @param cx1 [Numeric] horizontal position of beginning control point
896
- # @param cy1 [Numeric] vertical position of beginning control point
897
- # @param x1 [Numeric] horizontal position of first point
898
- # @param y1 [Numeric] vertical position of first point
899
- # @param x2 [Numeric] horizontal position of second point
900
- # @param y2 [Numeric] vertical position of second point
901
- # @param cx2 [Numeric] horizontal position of ending control point
902
- # @param cy2 [Numeric] vertical position of ending control point
903
- #
904
- # @return [nil] nil
905
- #
906
- def curve (cx1, cy1, x1, y1, x2, y2, cx2, cy2)
907
- @painter__.curve cx1, cy1, x1, y1, x2, y2, cx2, cy2
908
- nil
909
- end
680
+ # Vertical position
681
+ #
682
+ attr_reader :y
910
683
 
911
- # Draws a Bezier spline curve.
912
- #
913
- # @param x1 [Numeric] horizontal position of first point
914
- # @param y1 [Numeric] vertical position of first point
915
- # @param cx1 [Numeric] horizontal position of first control point
916
- # @param cy1 [Numeric] vertical position of first control point
917
- # @param cx2 [Numeric] horizontal position of second control point
918
- # @param cy2 [Numeric] vertical position of second control point
919
- # @param x2 [Numeric] horizontal position of second point
920
- # @param y2 [Numeric] vertical position of second point
921
- #
922
- # @return [nil] nil
923
- #
924
- def bezier (x1, y1, cx1, cy1, cx2, cy2, x2, y2)
925
- @painter__.bezier x1, y1, cx1, cy1, cx2, cy2, x2, y2
926
- nil
927
- end
684
+ # Width of bounding box
685
+ #
686
+ attr_reader :w
928
687
 
929
- # Draws a text.
930
- #
931
- # @overload text(str)
932
- # @overload text(str, x, y)
933
- #
934
- # @param str [String] text to draw
935
- # @param x [Numeric] horizontal position of the text
936
- # @param y [Numeric] vertical position of the text
937
- #
938
- # @return [nil] nil
939
- #
940
- def text (str, x, y)
941
- @painter__.text str, x, y
942
- nil
943
- end
688
+ # Height of bounding box
689
+ #
690
+ attr_reader :h
944
691
 
945
- # Draws an image.
946
- #
947
- # @overload image(img, a, b)
948
- # @overload image(img, a, b, c, d)
949
- #
950
- # @param img [Image] image to draw
951
- # @param a [Numeric] horizontal position of the image
952
- # @param b [Numeric] vertical position of the image
953
- # @param c [Numeric] width of the image
954
- # @param d [Numeric] height of the image
955
- #
956
- # @return [nil] nil
957
- #
958
- def image (img, a, b, c = nil, d = nil)
959
- x, y, w, h = toXYWH__ @imageMode__, a, b, c || img.width, d || img.height
960
- @painter__.image img.to_internal__, x, y, w, h
961
- nil
962
- end
692
+ # @private
693
+ def initialize (x, y, w, h)
694
+ @x, @y, @w, @h = x, y, w, h
695
+ end
963
696
 
964
- # Copies image.
965
- #
966
- # @overload copy(sx, sy, sw, sh, dx, dy, dw, dh)
967
- # @overload copy(img, sx, sy, sw, sh, dx, dy, dw, dh)
968
- #
969
- # @param img [Image] image for copy source
970
- # @param sx [Numrtic] x position of source region
971
- # @param sy [Numrtic] y position of source region
972
- # @param sw [Numrtic] width of source region
973
- # @param sh [Numrtic] height of source region
974
- # @param dx [Numrtic] x position of destination region
975
- # @param dy [Numrtic] y position of destination region
976
- # @param dw [Numrtic] width of destination region
977
- # @param dh [Numrtic] height of destination region
978
- #
979
- # @return [nil] nil
980
- #
981
- def copy (img = nil, sx, sy, sw, sh, dx, dy, dw, dh)
982
- src = img&.to_internal__ || @window__.canvas
983
- @painter__.image src, sx, sy, sw, sh, dx, dy, dw, dh
984
- end
697
+ end# TextBounds
985
698
 
986
- # Applies translation matrix to current transformation matrix.
987
- #
988
- # @param x [Numeric] horizontal transformation
989
- # @param y [Numeric] vertical transformation
990
- #
991
- # @return [nil] nil
992
- #
993
- def translate (x, y)
994
- @painter__.translate x, y
995
- nil
996
- end
997
699
 
998
- # Applies scale matrix to current transformation matrix.
999
- #
1000
- # @overload scale(s)
1001
- # @overload scale(x, y)
700
+ # Touch object.
1002
701
  #
1003
- # @param s [Numeric] horizontal and vertical scale
1004
- # @param x [Numeric] horizontal scale
1005
- # @param y [Numeric] vertical scale
1006
- #
1007
- # @return [nil] nil
1008
- #
1009
- def scale (x, y)
1010
- @painter__.scale x, y
1011
- nil
1012
- end
702
+ class Touch
1013
703
 
1014
- # Applies rotation matrix to current transformation matrix.
1015
- #
1016
- # @param angle [Numeric] angle for rotation
1017
- #
1018
- # @return [nil] nil
1019
- #
1020
- def rotate (angle)
1021
- @painter__.rotate toAngle__ angle
1022
- nil
1023
- end
704
+ # Horizontal position of touch
705
+ #
706
+ attr_reader :x
1024
707
 
1025
- # Pushes the current transformation matrix to stack.
1026
- #
1027
- # @return [nil] nil
1028
- #
1029
- def pushMatrix (&block)
1030
- @matrixStack__.push @painter__.matrix
1031
- if block
1032
- block.call
1033
- popMatrix
708
+ # Vertical position of touch
709
+ #
710
+ attr_reader :y
711
+
712
+ # @private
713
+ def initialize (x, y)
714
+ @x, @y = x, y
1034
715
  end
1035
- nil
1036
- end
1037
716
 
1038
- # Pops the current transformation matrix from stack.
1039
- #
1040
- # @return [nil] nil
1041
- #
1042
- def popMatrix ()
1043
- raise "matrix stack underflow" if @matrixStack__.empty?
1044
- @painter__.matrix = @matrixStack__.pop
1045
- nil
1046
- end
717
+ def id ()
718
+ raise NotImplementedError
719
+ end
1047
720
 
1048
- # Reset current transformation matrix with identity matrix.
1049
- #
1050
- # @return [nil] nil
1051
- #
1052
- def resetMatrix ()
1053
- @painter__.matrix = 1
1054
- nil
1055
- end
721
+ end# Touch
1056
722
 
1057
- # Save current style values to the style stack.
1058
- #
1059
- # @return [nil] nil
723
+
724
+ # Drawing context
1060
725
  #
1061
- def pushStyle (&block)
1062
- @styleStack__.push [
1063
- @painter__.fill,
1064
- @painter__.stroke,
1065
- @painter__.stroke_width,
1066
- @painter__.stroke_cap,
726
+ module GraphicsContext
727
+
728
+ # PI / 2
729
+ #
730
+ HALF_PI = Math::PI / 2
731
+
732
+ # PI / 4
733
+ #
734
+ QUARTER_PI = Math::PI / 4
735
+
736
+ # PI * 2
737
+ #
738
+ TWO_PI = Math::PI * 2
739
+
740
+ # PI * 2
741
+ #
742
+ TAU = Math::PI * 2
743
+
744
+ # RGB mode for colorMode().
745
+ #
746
+ RGB = :RGB
747
+
748
+ # HSB mode for colorMode().
749
+ #
750
+ HSB = :HSB
751
+
752
+ # Radian mode for angleMode().
753
+ #
754
+ RADIANS = :RADIANS
755
+
756
+ # Degree mode for angleMode().
757
+ #
758
+ DEGREES = :DEGREES
759
+
760
+ # Mode for rectMode(), ellipseMode() and imageMode().
761
+ #
762
+ CORNER = :CORNER
763
+
764
+ # Mode for rectMode(), ellipseMode() and imageMode().
765
+ #
766
+ CORNERS = :CORNERS
767
+
768
+ # Mode for rectMode(), ellipseMode(), imageMode() and textAlign().
769
+ #
770
+ CENTER = :CENTER
771
+
772
+ # Mode for rectMode() and ellipseMode().
773
+ #
774
+ RADIUS = :RADIUS
775
+
776
+ # Mode for textAlign().
777
+ LEFT = :LEFT
778
+
779
+ # Mode for textAlign().
780
+ RIGHT = :RIGHT
781
+
782
+ # Mode for textAlign().
783
+ TOP = :TOP
784
+
785
+ # Mode for textAlign().
786
+ BOTTOM = :BOTTOM
787
+
788
+ # Mode for textAlign().
789
+ BASELINE = :BASELINE
790
+
791
+ # Mode for strokeCap().
792
+ #
793
+ BUTT = :butt
794
+
795
+ # Mode for strokeJoin().
796
+ #
797
+ MITER = :miter
798
+
799
+ # Mode for strokeCap() and strokeJoin().
800
+ #
801
+ ROUND = :round
802
+
803
+ # Mode for strokeCap() and strokeJoin().
804
+ #
805
+ SQUARE = :square
806
+
807
+ def setup__ (painter)
808
+ @painter__ = painter
809
+ @painter__.miter_limit = 10
810
+
811
+ @drawing = false
812
+ @hsbColor__ = false
813
+ @colorMaxes__ = [1.0] * 4
814
+ @angleScale__ = 1.0
815
+ @rectMode__ = nil
816
+ @ellipseMode__ = nil
817
+ @imageMode__ = nil
818
+ @textAlignH__ = nil
819
+ @textAlignV__ = nil
820
+ @matrixStack__ = []
821
+ @styleStack__ = []
822
+
823
+ colorMode RGB, 255
824
+ angleMode RADIANS
825
+ rectMode CORNER
826
+ ellipseMode CENTER
827
+ imageMode CORNER
828
+ textAlign LEFT
829
+
830
+ fill 255
831
+ stroke 0
832
+ end
833
+
834
+ def beginDraw ()
835
+ @matrixStack__.clear
836
+ @styleStack__.clear
837
+ @drawing = true
838
+ end
839
+
840
+ def endDraw ()
841
+ @drawing = false
842
+ end
843
+
844
+ def width ()
845
+ @image__.width
846
+ end
847
+
848
+ def height ()
849
+ @image__.height
850
+ end
851
+
852
+ # Sets color mode and max color values.
853
+ #
854
+ # @overload colorMode(mode)
855
+ # @overload colorMode(mode, max)
856
+ # @overload colorMode(mode, max1, max2, max3)
857
+ # @overload colorMode(mode, max1, max2, max3, maxA)
858
+ #
859
+ # @param mode [RGB, HSB] RGB or HSB
860
+ # @param max [Numeric] max values for all color values
861
+ # @param max1 [Numeric] max value for red or hue
862
+ # @param max2 [Numeric] max value for green or saturation
863
+ # @param max3 [Numeric] max value for blue or brightness
864
+ # @param maxA [Numeric] max value for alpha
865
+ #
866
+ # @return [nil] nil
867
+ #
868
+ def colorMode (mode, *maxes)
869
+ mode = mode.upcase.to_sym
870
+ raise ArgumentError, "invalid color mode: #{mode}" unless [RGB, HSB].include?(mode)
871
+ raise ArgumentError unless [0, 1, 3, 4].include?(maxes.size)
872
+
873
+ @hsbColor__ = mode == HSB
874
+ case maxes.size
875
+ when 1 then @colorMaxes__ = [maxes.first.to_f] * 4
876
+ when 3, 4 then @colorMaxes__[0...maxes.size] = maxes.map &:to_f
877
+ end
878
+ nil
879
+ end
880
+
881
+ # @private
882
+ private def toRGBA__ (*args)
883
+ a, b, c, d = args
884
+ return parseColor__(a, b || alphaMax__) if a.kind_of?(String)
885
+
886
+ rgba = case args.size
887
+ when 1, 2 then [a, a, a, b || alphaMax__]
888
+ when 3, 4 then [a, b, c, d || alphaMax__]
889
+ else raise ArgumentError
890
+ end
891
+ rgba = rgba.map.with_index {|value, i| value / @colorMaxes__[i]}
892
+ color = @hsbColor__ ? Rays::Color.hsv(*rgba) : Rays::Color.new(*rgba)
893
+ color.to_a
894
+ end
895
+
896
+ # @private
897
+ private def parseColor__ (str, alpha)
898
+ result = str.match /^\s*##{'([0-9a-f]{2})' * 3}\s*$/i
899
+ raise ArgumentError, "invalid color code: '#{str}'" unless result
900
+
901
+ rgb = result[1..3].map.with_index {|hex, i| hex.to_i(16) / 255.0}
902
+ return *rgb, (alpha / alphaMax__)
903
+ end
904
+
905
+ # @private
906
+ private def alphaMax__ ()
907
+ @colorMaxes__[3]
908
+ end
909
+
910
+ # Sets angle mode.
911
+ #
912
+ # @param mode [RADIANS, DEGREES] RADIANS or DEGREES
913
+ #
914
+ # @return [nil] nil
915
+ #
916
+ def angleMode (mode)
917
+ @angleScale__ = case mode.upcase.to_sym
918
+ when RADIANS then Utility::RAD2DEG__
919
+ when DEGREES then 1.0
920
+ else raise ArgumentError, "invalid angle mode: #{mode}"
921
+ end
922
+ nil
923
+ end
924
+
925
+ # @private
926
+ protected def toAngle__ (angle)
927
+ angle * @angleScale__
928
+ end
929
+
930
+ # Sets rect mode. Default is CORNER.
931
+ #
932
+ # CORNER -> rect(left, top, width, height)
933
+ # CORNERS -> rect(left, top, right, bottom)
934
+ # CENTER -> rect(center_x, center_y, width, height)
935
+ # RADIUS -> rect(center_x, center_y, radius_h, radius_v)
936
+ #
937
+ # @param mode [CORNER, CORNERS, CENTER, RADIUS]
938
+ #
939
+ # @return [nil] nil
940
+ #
941
+ def rectMode (mode)
942
+ @rectMode__ = mode
943
+ end
944
+
945
+ # Sets ellipse mode. Default is CENTER.
946
+ #
947
+ # CORNER -> ellipse(left, top, width, height)
948
+ # CORNERS -> ellipse(left, top, right, bottom)
949
+ # CENTER -> ellipse(center_x, center_y, width, height)
950
+ # RADIUS -> ellipse(center_x, center_y, radius_h, radius_v)
951
+ #
952
+ # @param mode [CORNER, CORNERS, CENTER, RADIUS]
953
+ #
954
+ # @return [nil] nil
955
+ #
956
+ def ellipseMode (mode)
957
+ @ellipseMode__ = mode
958
+ end
959
+
960
+ # Sets image mode. Default is CORNER.
961
+ #
962
+ # CORNER -> image(img, left, top, width, height)
963
+ # CORNERS -> image(img, left, top, right, bottom)
964
+ # CENTER -> image(img, center_x, center_y, width, height)
965
+ #
966
+ # @param mode [CORNER, CORNERS, CENTER]
967
+ #
968
+ # @return [nil] nil
969
+ #
970
+ def imageMode (mode)
971
+ @imageMode__ = mode
972
+ end
973
+
974
+ # @private
975
+ private def toXYWH__ (mode, a, b, c, d)
976
+ case mode
977
+ when CORNER then [a, b, c, d]
978
+ when CORNERS then [a, b, c - a, d - b]
979
+ when CENTER then [a - c / 2.0, b - d / 2.0, c, d]
980
+ when RADIUS then [a - c, b - d, c * 2, d * 2]
981
+ else raise ArgumentError # ToDo: refine error message
982
+ end
983
+ end
984
+
985
+ # Sets fill color.
986
+ #
987
+ # @overload fill(rgb)
988
+ # @overload fill(rgb, alpha)
989
+ # @overload fill(gray)
990
+ # @overload fill(gray, alpha)
991
+ # @overload fill(r, g, b)
992
+ # @overload fill(r, g, b, alpha)
993
+ #
994
+ # @param rgb [String] color code like '#00AAFF'
995
+ # @param gray [Integer] gray value (0..255)
996
+ # @param r [Integer] red value (0..255)
997
+ # @param g [Integer] green value (0..255)
998
+ # @param b [Integer] blue value (0..255)
999
+ # @param alpha [Integer] alpha value (0..255)
1000
+ #
1001
+ # @return [nil] nil
1002
+ #
1003
+ def fill (*args)
1004
+ @painter__.fill(*toRGBA__(*args))
1005
+ nil
1006
+ end
1007
+
1008
+ # Sets stroke color.
1009
+ #
1010
+ # @overload stroke(rgb)
1011
+ # @overload stroke(rgb, alpha)
1012
+ # @overload stroke(gray)
1013
+ # @overload stroke(gray, alpha)
1014
+ # @overload stroke(r, g, b)
1015
+ # @overload stroke(r, g, b, alpha)
1016
+ #
1017
+ # @param rgb [String] color code like '#00AAFF'
1018
+ # @param gray [Integer] gray value (0..255)
1019
+ # @param r [Integer] red value (0..255)
1020
+ # @param g [Integer] green value (0..255)
1021
+ # @param b [Integer] blue value (0..255)
1022
+ # @param alpha [Integer] alpha value (0..255)
1023
+ #
1024
+ # @return [nil] nil
1025
+ #
1026
+ def stroke (*args)
1027
+ @painter__.stroke(*toRGBA__(*args))
1028
+ nil
1029
+ end
1030
+
1031
+ # Sets stroke weight.
1032
+ #
1033
+ # @param weight [Numeric] width of stroke
1034
+ #
1035
+ # @return [nil] nil
1036
+ #
1037
+ def strokeWeight (weight)
1038
+ @painter__.stroke_width weight
1039
+ nil
1040
+ end
1041
+
1042
+ # Sets stroke cap mode.
1043
+ #
1044
+ # @param cap [BUTT, ROUND, SQUARE]
1045
+ #
1046
+ # @return [nil] nil
1047
+ #
1048
+ def strokeCap (cap)
1049
+ @painter__.stroke_cap cap
1050
+ nil
1051
+ end
1052
+
1053
+ # Sets stroke join mode.
1054
+ #
1055
+ # @param join [MITER, ROUND, SQUARE]
1056
+ #
1057
+ # @return [nil] nil
1058
+ #
1059
+ def strokeJoin (join)
1060
+ @painter__.stroke_join join
1061
+ nil
1062
+ end
1063
+
1064
+ # Disables filling.
1065
+ #
1066
+ # @return [nil] nil
1067
+ #
1068
+ def noFill ()
1069
+ @painter__.fill nil
1070
+ nil
1071
+ end
1072
+
1073
+ # Disables drawing stroke.
1074
+ #
1075
+ # @return [nil] nil
1076
+ #
1077
+ def noStroke ()
1078
+ @painter__.stroke nil
1079
+ nil
1080
+ end
1081
+
1082
+ # Sets font.
1083
+ #
1084
+ # @param name [String] font name
1085
+ # @param size [Numeric] font size (max 256)
1086
+ #
1087
+ # @return [Font] current font
1088
+ #
1089
+ def textFont (name = nil, size = nil)
1090
+ setFont__ name, size if name || size
1091
+ Font.new @painter__.font
1092
+ end
1093
+
1094
+ # Sets text size.
1095
+ #
1096
+ # @param size [Numeric] font size (max 256)
1097
+ #
1098
+ # @return [nil] nil
1099
+ #
1100
+ def textSize (size)
1101
+ setFont__ @painter__.font.name, size
1102
+ nil
1103
+ end
1104
+
1105
+ def textWidth (str)
1106
+ @painter__.font.width str
1107
+ end
1108
+
1109
+ def textAscent ()
1110
+ @painter__.font.ascent
1111
+ end
1112
+
1113
+ def textDescent ()
1114
+ @painter__.font.descent
1115
+ end
1116
+
1117
+ def textAlign (horizontal, vertical = BASELINE)
1118
+ @textAlignH__ = horizontal
1119
+ @textAlignV__ = vertical
1120
+ end
1121
+
1122
+ # @private
1123
+ def setFont__ (name, size)
1124
+ size = 256 if size && size > 256
1125
+ @painter__.font name, size
1126
+ end
1127
+
1128
+ # Clears screen.
1129
+ #
1130
+ # @overload background(str)
1131
+ # @overload background(str, alpha)
1132
+ # @overload background(gray)
1133
+ # @overload background(gray, alpha)
1134
+ # @overload background(r, g, b)
1135
+ # @overload background(r, g, b, alpha)
1136
+ #
1137
+ # @param str [String] color code like '#00AAFF'
1138
+ # @param gray [Integer] gray value (0..255)
1139
+ # @param r [Integer] red value (0..255)
1140
+ # @param g [Integer] green value (0..255)
1141
+ # @param b [Integer] blue value (0..255)
1142
+ # @param alpha [Integer] alpha value (0..255)
1143
+ #
1144
+ # @return [nil] nil
1145
+ #
1146
+ def background (*args)
1147
+ assertDrawing__
1148
+ rgba = toRGBA__ *args
1149
+ if rgba[3] == 1
1150
+ @painter__.background *rgba
1151
+ else
1152
+ @painter__.push fill: rgba, stroke: nil do |_|
1153
+ @painter__.rect 0, 0, width, height
1154
+ end
1155
+ end
1156
+ nil
1157
+ end
1158
+
1159
+ # Draws a point.
1160
+ #
1161
+ # @param x [Numeric] horizontal position
1162
+ # @param y [Numeric] vertical position
1163
+ #
1164
+ # @return [nil] nil
1165
+ #
1166
+ def point (x, y)
1167
+ assertDrawing__
1168
+ w = @painter__.stroke_width
1169
+ w = 1 if w == 0
1170
+ @painter__.ellipse x - (w / 2.0), y - (w / 2.0), w, w
1171
+ nil
1172
+ end
1173
+
1174
+ # Draws a line.
1175
+ #
1176
+ # @param x1 [Numeric] horizontal position of first point
1177
+ # @param y1 [Numeric] vertical position of first point
1178
+ # @param x2 [Numeric] horizontal position of second point
1179
+ # @param y2 [Numeric] vertical position of second point
1180
+ #
1181
+ # @return [nil] nil
1182
+ #
1183
+ def line (x1, y1, x2, y2)
1184
+ assertDrawing__
1185
+ @painter__.line x1, y1, x2, y2
1186
+ nil
1187
+ end
1188
+
1189
+ # Draws a rectangle.
1190
+ #
1191
+ # @overload rect(a, b, c, d)
1192
+ # @overload rect(a, b, c, d, r)
1193
+ # @overload rect(a, b, c, d, tl, tr, br, bl)
1194
+ #
1195
+ # @param a [Numeric] horizontal position of the shape by default
1196
+ # @param b [Numeric] vertical position of the shape by default
1197
+ # @param c [Numeric] width of the shape by default
1198
+ # @param d [Numeric] height of the shape by default
1199
+ # @param r [Numeric] radius for all corners
1200
+ # @param tl [Numeric] radius for top-left corner
1201
+ # @param tr [Numeric] radius for top-right corner
1202
+ # @param br [Numeric] radius for bottom-right corner
1203
+ # @param bl [Numeric] radius for bottom-left corner
1204
+ #
1205
+ # @return [nil] nil
1206
+ #
1207
+ def rect (a, b, c, d, *args)
1208
+ assertDrawing__
1209
+ x, y, w, h = toXYWH__ @rectMode__, a, b, c, d
1210
+ case args.size
1211
+ when 0 then @painter__.rect x, y, w, h
1212
+ when 1 then @painter__.rect x, y, w, h, round: args[0]
1213
+ when 4 then @painter__.rect x, y, w, h, lt: args[0], rt: args[1], rb: args[2], lb: args[3]
1214
+ else raise ArgumentError # ToDo: refine error message
1215
+ end
1216
+ nil
1217
+ end
1218
+
1219
+ # Draws an ellipse.
1220
+ #
1221
+ # @param a [Numeric] horizontal position of the shape
1222
+ # @param b [Numeric] vertical position of the shape
1223
+ # @param c [Numeric] width of the shape
1224
+ # @param d [Numeric] height of the shape
1225
+ #
1226
+ # @return [nil] nil
1227
+ #
1228
+ def ellipse (a, b, c, d)
1229
+ assertDrawing__
1230
+ x, y, w, h = toXYWH__ @ellipseMode__, a, b, c, d
1231
+ @painter__.ellipse x, y, w, h
1232
+ nil
1233
+ end
1234
+
1235
+ # Draws a circle.
1236
+ #
1237
+ # @param x [Numeric] horizontal position of the shape
1238
+ # @param y [Numeric] vertical position of the shape
1239
+ # @param extent [Numeric] width and height of the shape
1240
+ #
1241
+ # @return [nil] nil
1242
+ #
1243
+ def circle (x, y, extent)
1244
+ ellipse x, y, extent, extent
1245
+ end
1246
+
1247
+ # Draws an arc.
1248
+ #
1249
+ # @param a [Numeric] horizontal position of the shape
1250
+ # @param b [Numeric] vertical position of the shape
1251
+ # @param c [Numeric] width of the shape
1252
+ # @param d [Numeric] height of the shape
1253
+ # @param start [Numeric] angle to start the arc
1254
+ # @param stop [Numeric] angle to stop the arc
1255
+ #
1256
+ # @return [nil] nil
1257
+ #
1258
+ def arc (a, b, c, d, start, stop)
1259
+ assertDrawing__
1260
+ x, y, w, h = toXYWH__ @ellipseMode__, a, b, c, d
1261
+ start = toAngle__ start
1262
+ stop = toAngle__ stop
1263
+ @painter__.ellipse x, y, w, h, from: start, to: stop
1264
+ nil
1265
+ end
1266
+
1267
+ # Draws a square.
1268
+ #
1269
+ # @param x [Numeric] horizontal position of the shape
1270
+ # @param y [Numeric] vertical position of the shape
1271
+ # @param extent [Numeric] width and height of the shape
1272
+ #
1273
+ # @return [nil] nil
1274
+ #
1275
+ def square (x, y, extent)
1276
+ rect x, y, extent, extent
1277
+ end
1278
+
1279
+ # Draws a triangle.
1280
+ #
1281
+ # @param x1 [Numeric] horizontal position of first point
1282
+ # @param y1 [Numeric] vertical position of first point
1283
+ # @param x2 [Numeric] horizontal position of second point
1284
+ # @param y2 [Numeric] vertical position of second point
1285
+ # @param x3 [Numeric] horizontal position of third point
1286
+ # @param y3 [Numeric] vertical position of third point
1287
+ #
1288
+ # @return [nil] nil
1289
+ #
1290
+ def triangle (x1, y1, x2, y2, x3, y3)
1291
+ assertDrawing__
1292
+ @painter__.line x1, y1, x2, y2, x3, y3, loop: true
1293
+ nil
1294
+ end
1295
+
1296
+ # Draws a quad.
1297
+ #
1298
+ # @param x1 [Numeric] horizontal position of first point
1299
+ # @param y1 [Numeric] vertical position of first point
1300
+ # @param x2 [Numeric] horizontal position of second point
1301
+ # @param y2 [Numeric] vertical position of second point
1302
+ # @param x3 [Numeric] horizontal position of third point
1303
+ # @param y3 [Numeric] vertical position of third point
1304
+ # @param x4 [Numeric] horizontal position of fourth point
1305
+ # @param y4 [Numeric] vertical position of fourth point
1306
+ #
1307
+ # @return [nil] nil
1308
+ #
1309
+ def quad (x1, y1, x2, y2, x3, y3, x4, y4)
1310
+ assertDrawing__
1311
+ @painter__.line x1, y1, x2, y2, x3, y3, x4, y4, loop: true
1312
+ nil
1313
+ end
1314
+
1315
+ # Draws a Catmull-Rom spline curve.
1316
+ #
1317
+ # @param cx1 [Numeric] horizontal position of beginning control point
1318
+ # @param cy1 [Numeric] vertical position of beginning control point
1319
+ # @param x1 [Numeric] horizontal position of first point
1320
+ # @param y1 [Numeric] vertical position of first point
1321
+ # @param x2 [Numeric] horizontal position of second point
1322
+ # @param y2 [Numeric] vertical position of second point
1323
+ # @param cx2 [Numeric] horizontal position of ending control point
1324
+ # @param cy2 [Numeric] vertical position of ending control point
1325
+ #
1326
+ # @return [nil] nil
1327
+ #
1328
+ def curve (cx1, cy1, x1, y1, x2, y2, cx2, cy2)
1329
+ assertDrawing__
1330
+ @painter__.curve cx1, cy1, x1, y1, x2, y2, cx2, cy2
1331
+ nil
1332
+ end
1333
+
1334
+ # Draws a Bezier spline curve.
1335
+ #
1336
+ # @param x1 [Numeric] horizontal position of first point
1337
+ # @param y1 [Numeric] vertical position of first point
1338
+ # @param cx1 [Numeric] horizontal position of first control point
1339
+ # @param cy1 [Numeric] vertical position of first control point
1340
+ # @param cx2 [Numeric] horizontal position of second control point
1341
+ # @param cy2 [Numeric] vertical position of second control point
1342
+ # @param x2 [Numeric] horizontal position of second point
1343
+ # @param y2 [Numeric] vertical position of second point
1344
+ #
1345
+ # @return [nil] nil
1346
+ #
1347
+ def bezier (x1, y1, cx1, cy1, cx2, cy2, x2, y2)
1348
+ assertDrawing__
1349
+ @painter__.bezier x1, y1, cx1, cy1, cx2, cy2, x2, y2
1350
+ nil
1351
+ end
1352
+
1353
+ # Draws a text.
1354
+ #
1355
+ # @overload text(str)
1356
+ # @overload text(str, x, y)
1357
+ # @overload text(str, a, b, c, d)
1358
+ #
1359
+ # @param str [String] text to draw
1360
+ # @param x [Numeric] horizontal position of the text
1361
+ # @param y [Numeric] vertical position of the text
1362
+ # @param a [Numeric] equivalent to parameters of the rect(), see rectMode()
1363
+ # @param b [Numeric] equivalent to parameters of the rect(), see rectMode()
1364
+ # @param c [Numeric] equivalent to parameters of the rect(), see rectMode()
1365
+ # @param d [Numeric] equivalent to parameters of the rect(), see rectMode()
1366
+ #
1367
+ # @return [nil] nil
1368
+ #
1369
+ def text (str, x, y, x2 = nil, y2 = nil)
1370
+ assertDrawing__
1371
+ if x2
1372
+ raise ArgumentError, "missing y2 parameter" unless y2
1373
+ x, y, w, h = toXYWH__ @rectMode__, x, y, x2, y2
1374
+ case @textAlignH__
1375
+ when RIGHT then x += w - @painter__.font.width(str)
1376
+ when CENTER then x += (w - @painter__.font.width(str)) / 2
1377
+ end
1378
+ case @textAlignV__
1379
+ when BOTTOM then y += h - @painter__.font.height
1380
+ when CENTER then y += (h - @painter__.font.height) / 2
1381
+ else
1382
+ end
1383
+ else
1384
+ y -= @painter__.font.ascent
1385
+ end
1386
+ @painter__.text str, x, y
1387
+ nil
1388
+ end
1389
+
1390
+ # Draws an image.
1391
+ #
1392
+ # @overload image(img, a, b)
1393
+ # @overload image(img, a, b, c, d)
1394
+ #
1395
+ # @param img [Image] image to draw
1396
+ # @param a [Numeric] horizontal position of the image
1397
+ # @param b [Numeric] vertical position of the image
1398
+ # @param c [Numeric] width of the image
1399
+ # @param d [Numeric] height of the image
1400
+ #
1401
+ # @return [nil] nil
1402
+ #
1403
+ def image (img, a, b, c = nil, d = nil)
1404
+ assertDrawing__
1405
+ x, y, w, h = toXYWH__ @imageMode__, a, b, c || img.width, d || img.height
1406
+ @painter__.image img.getInternal__, x, y, w, h
1407
+ nil
1408
+ end
1409
+
1410
+ # Copies image.
1411
+ #
1412
+ # @overload copy(sx, sy, sw, sh, dx, dy, dw, dh)
1413
+ # @overload copy(img, sx, sy, sw, sh, dx, dy, dw, dh)
1414
+ #
1415
+ # @param img [Image] image for copy source
1416
+ # @param sx [Numrtic] x position of source region
1417
+ # @param sy [Numrtic] y position of source region
1418
+ # @param sw [Numrtic] width of source region
1419
+ # @param sh [Numrtic] height of source region
1420
+ # @param dx [Numrtic] x position of destination region
1421
+ # @param dy [Numrtic] y position of destination region
1422
+ # @param dw [Numrtic] width of destination region
1423
+ # @param dh [Numrtic] height of destination region
1424
+ #
1425
+ # @return [nil] nil
1426
+ #
1427
+ def copy (img = nil, sx, sy, sw, sh, dx, dy, dw, dh)
1428
+ assertDrawing__
1429
+ src = img&.getInternal__ || @window__.canvas
1430
+ @painter__.image src, sx, sy, sw, sh, dx, dy, dw, dh
1431
+ end
1432
+
1433
+ # Applies translation matrix to current transformation matrix.
1434
+ #
1435
+ # @param x [Numeric] horizontal transformation
1436
+ # @param y [Numeric] vertical transformation
1437
+ #
1438
+ # @return [nil] nil
1439
+ #
1440
+ def translate (x, y)
1441
+ assertDrawing__
1442
+ @painter__.translate x, y
1443
+ nil
1444
+ end
1445
+
1446
+ # Applies scale matrix to current transformation matrix.
1447
+ #
1448
+ # @overload scale(s)
1449
+ # @overload scale(x, y)
1450
+ #
1451
+ # @param s [Numeric] horizontal and vertical scale
1452
+ # @param x [Numeric] horizontal scale
1453
+ # @param y [Numeric] vertical scale
1454
+ #
1455
+ # @return [nil] nil
1456
+ #
1457
+ def scale (x, y)
1458
+ assertDrawing__
1459
+ @painter__.scale x, y
1460
+ nil
1461
+ end
1462
+
1463
+ # Applies rotation matrix to current transformation matrix.
1464
+ #
1465
+ # @param angle [Numeric] angle for rotation
1466
+ #
1467
+ # @return [nil] nil
1468
+ #
1469
+ def rotate (angle)
1470
+ assertDrawing__
1471
+ @painter__.rotate toAngle__ angle
1472
+ nil
1473
+ end
1474
+
1475
+ # Pushes the current transformation matrix to stack.
1476
+ #
1477
+ # @return [nil] nil
1478
+ #
1479
+ def pushMatrix (&block)
1480
+ assertDrawing__
1481
+ @matrixStack__.push @painter__.matrix
1482
+ if block
1483
+ block.call
1484
+ popMatrix
1485
+ end
1486
+ nil
1487
+ end
1488
+
1489
+ # Pops the current transformation matrix from stack.
1490
+ #
1491
+ # @return [nil] nil
1492
+ #
1493
+ def popMatrix ()
1494
+ assertDrawing__
1495
+ raise "matrix stack underflow" if @matrixStack__.empty?
1496
+ @painter__.matrix = @matrixStack__.pop
1497
+ nil
1498
+ end
1499
+
1500
+ # Reset current transformation matrix with identity matrix.
1501
+ #
1502
+ # @return [nil] nil
1503
+ #
1504
+ def resetMatrix ()
1505
+ assertDrawing__
1506
+ @painter__.matrix = 1
1507
+ nil
1508
+ end
1509
+
1510
+ # Save current style values to the style stack.
1511
+ #
1512
+ # @return [nil] nil
1513
+ #
1514
+ def pushStyle (&block)
1515
+ assertDrawing__
1516
+ @styleStack__.push [
1517
+ @painter__.fill,
1518
+ @painter__.stroke,
1519
+ @painter__.stroke_width,
1520
+ @painter__.stroke_cap,
1521
+ @painter__.stroke_join,
1522
+ @painter__.font,
1523
+ @hsbColor__,
1524
+ @colorMaxes__,
1525
+ @angleScale__,
1526
+ @rectMode__,
1527
+ @ellipseMode__,
1528
+ @imageMode__
1529
+ ]
1530
+ if block
1531
+ block.call
1532
+ popStyle
1533
+ end
1534
+ nil
1535
+ end
1536
+
1537
+ # Restore style values from the style stack.
1538
+ #
1539
+ # @return [nil] nil
1540
+ #
1541
+ def popStyle ()
1542
+ assertDrawing__
1543
+ raise "style stack underflow" if @styleStack__.empty?
1544
+ @painter__.fill,
1545
+ @painter__.stroke,
1546
+ @painter__.stroke_width,
1547
+ @painter__.stroke_cap,
1067
1548
  @painter__.stroke_join,
1068
1549
  @painter__.font,
1069
1550
  @hsbColor__,
@@ -1071,246 +1552,584 @@ module RubySketch
1071
1552
  @angleScale__,
1072
1553
  @rectMode__,
1073
1554
  @ellipseMode__,
1074
- @imageMode__
1075
- ]
1076
- if block
1077
- block.call
1555
+ @imageMode__ = @styleStack__.pop
1556
+ nil
1557
+ end
1558
+
1559
+ # Save current styles and transformations to stack.
1560
+ #
1561
+ # @return [nil] nil
1562
+ #
1563
+ def push (&block)
1564
+ pushMatrix
1565
+ pushStyle
1566
+ if block
1567
+ block.call
1568
+ pop
1569
+ end
1570
+ end
1571
+
1572
+ # Restore styles and transformations from stack.
1573
+ #
1574
+ # @return [nil] nil
1575
+ #
1576
+ def pop ()
1577
+ popMatrix
1078
1578
  popStyle
1079
1579
  end
1080
- nil
1081
- end
1082
1580
 
1083
- # Restore style values from the style stack.
1084
- #
1085
- # @return [nil] nil
1086
- #
1087
- def popStyle ()
1088
- raise "style stack underflow" if @styleStack__.empty?
1089
- @painter__.fill,
1090
- @painter__.stroke,
1091
- @painter__.stroke_width,
1092
- @painter__.stroke_cap,
1093
- @painter__.stroke_join,
1094
- @painter__.font,
1095
- @hsbColor__,
1096
- @colorMaxes__,
1097
- @angleScale__,
1098
- @rectMode__,
1099
- @ellipseMode__,
1100
- @imageMode__ = @styleStack__.pop
1101
- nil
1102
- end
1103
-
1104
- # Save current styles and transformations to stack.
1105
- #
1106
- # @return [nil] nil
1581
+ # @private
1582
+ private def getInternal__ ()
1583
+ @image__
1584
+ end
1585
+
1586
+ # @private
1587
+ private def assertDrawing__ ()
1588
+ raise "call beginDraw() before drawing" unless @drawing
1589
+ end
1590
+
1591
+ end# GraphicsContext
1592
+
1593
+
1594
+ # Draws graphics into an offscreen buffer
1107
1595
  #
1108
- def push (&block)
1109
- pushMatrix
1110
- pushStyle
1111
- if block
1112
- block.call
1596
+ class Graphics
1597
+
1598
+ include GraphicsContext
1599
+
1600
+ def initialize (width, height)
1601
+ @image__ = Rays::Image.new width, height
1602
+ setup__ @image__.painter
1603
+ end
1604
+
1605
+ def beginDraw ()
1606
+ @painter__.__send__ :begin_paint
1607
+ super
1608
+ push
1609
+ end
1610
+
1611
+ def endDraw ()
1113
1612
  pop
1613
+ super
1614
+ @painter__.__send__ :end_paint
1114
1615
  end
1115
- end
1116
1616
 
1117
- # Restore styles and transformations from stack.
1118
- #
1119
- # @return [nil] nil
1120
- #
1121
- def pop ()
1122
- popMatrix
1123
- popStyle
1124
- end
1617
+ end# Graphics
1125
1618
 
1126
- # Returns the perlin noise value.
1127
- #
1128
- # @overload noise(x)
1129
- # @overload noise(x, y)
1130
- # @overload noise(x, y, z)
1131
- #
1132
- # @param x [Numeric] horizontal point in noise space
1133
- # @param y [Numeric] vertical point in noise space
1134
- # @param z [Numeric] depth point in noise space
1135
- #
1136
- # @return [Numeric] noise value (0.0..1.0)
1137
- #
1138
- def noise (x, y = 0, z = 0)
1139
- Rays.perlin(x, y, z) / 2.0 + 0.5
1140
- end
1141
1619
 
1142
- # Loads image.
1143
- #
1144
- # @param filename [String] file name to load image
1145
- # @param extension [String] type of image to load (ex. 'png')
1146
- #
1147
- # @return [Image] loaded image object
1148
- #
1149
- def loadImage (filename, extension = nil)
1150
- filename = getImage__ filename, extension if filename =~ %r|^https?://|
1151
- Image.new Rays::Image.load filename
1152
- end
1153
-
1154
- # @private
1155
- private def getImage__ (uri, ext)
1156
- ext ||= File.extname uri
1157
- raise "unsupported image type -- #{ext}" unless ext =~ /^\.?(png)$/i
1158
-
1159
- tmpdir = Pathname(Dir.tmpdir) + Digest::SHA1.hexdigest(self.class.name)
1160
- path = tmpdir + Digest::SHA1.hexdigest(uri)
1161
- path = path.sub_ext ext
1162
-
1163
- unless path.file?
1164
- URI.open uri do |input|
1165
- tmpdir.mkdir unless tmpdir.directory?
1166
- path.open('w') do |output|
1167
- while buf = input.read(2 ** 16)
1168
- output.write buf
1620
+ module Utility
1621
+
1622
+ # @private
1623
+ DEG2RAD__ = Math::PI / 180.0
1624
+
1625
+ # @private
1626
+ RAD2DEG__ = 180.0 / Math::PI
1627
+
1628
+ # Converts degree to radian.
1629
+ #
1630
+ # @param degree [Numeric] degree to convert
1631
+ #
1632
+ # @return [Numeric] radian
1633
+ #
1634
+ def radians (degree)
1635
+ degree * DEG2RAD__
1636
+ end
1637
+
1638
+ # Converts radian to degree.
1639
+ #
1640
+ # @param radian [Numeric] radian to convert
1641
+ #
1642
+ # @return [Numeric] degree
1643
+ #
1644
+ def degrees (radian)
1645
+ radian * RAD2DEG__
1646
+ end
1647
+
1648
+ # Returns the absolute number of the value.
1649
+ #
1650
+ # @param value [Numeric] number
1651
+ #
1652
+ # @return [Numeric] absolute number
1653
+ #
1654
+ def abs (value)
1655
+ value.abs
1656
+ end
1657
+
1658
+ # Returns the closest integer number greater than or equal to the value.
1659
+ #
1660
+ # @param value [Numeric] number
1661
+ #
1662
+ # @return [Numeric] rounded up number
1663
+ #
1664
+ def ceil (value)
1665
+ value.ceil
1666
+ end
1667
+
1668
+ # Returns the closest integer number less than or equal to the value.
1669
+ #
1670
+ # @param value [Numeric] number
1671
+ #
1672
+ # @return [Numeric] rounded down number
1673
+ #
1674
+ def floor (value)
1675
+ value.floor
1676
+ end
1677
+
1678
+ # Returns the closest integer number.
1679
+ #
1680
+ # @param value [Numeric] number
1681
+ #
1682
+ # @return [Numeric] rounded number
1683
+ #
1684
+ def round (value)
1685
+ value.round
1686
+ end
1687
+
1688
+ # Returns value raised to the power of exponent.
1689
+ #
1690
+ # @param value [Numeric] base number
1691
+ # @param exponent [Numeric] exponent number
1692
+ #
1693
+ # @return [Numeric] value ** exponent
1694
+ #
1695
+ def pow (value, exponent)
1696
+ value ** exponent
1697
+ end
1698
+
1699
+ # Returns squared value.
1700
+ #
1701
+ # @param value [Numeric] number
1702
+ #
1703
+ # @return [Numeric] squared value
1704
+ #
1705
+ def sq (value)
1706
+ value * value
1707
+ end
1708
+
1709
+ # Returns the magnitude (or length) of a vector.
1710
+ #
1711
+ # @overload mag(x, y)
1712
+ # @overload mag(x, y, z)
1713
+ #
1714
+ # @param x [Numeric] x of point
1715
+ # @param y [Numeric] y of point
1716
+ # @param z [Numeric] z of point
1717
+ #
1718
+ # @return [Numeric] magnitude
1719
+ #
1720
+ def mag (*args)
1721
+ x, y, z = *args
1722
+ case args.size
1723
+ when 2 then Math.sqrt x * x + y * y
1724
+ when 3 then Math.sqrt x * x + y * y + z * z
1725
+ else raise ArgumentError
1726
+ end
1727
+ end
1728
+
1729
+ # Returns distance between 2 points.
1730
+ #
1731
+ # @overload dist(x1, y1, x2, y2)
1732
+ # @overload dist(x1, y1, z1, x2, y2, z2)
1733
+ #
1734
+ # @param x1 [Numeric] x of first point
1735
+ # @param y1 [Numeric] y of first point
1736
+ # @param z1 [Numeric] z of first point
1737
+ # @param x2 [Numeric] x of second point
1738
+ # @param y2 [Numeric] y of second point
1739
+ # @param z2 [Numeric] z of second point
1740
+ #
1741
+ # @return [Numeric] distance between 2 points
1742
+ #
1743
+ def dist (*args)
1744
+ case args.size
1745
+ when 4
1746
+ x1, y1, x2, y2 = *args
1747
+ xx, yy = x2 - x1, y2 - y1
1748
+ Math.sqrt xx * xx + yy * yy
1749
+ when 3
1750
+ x1, y1, z1, x2, y2, z2 = *args
1751
+ xx, yy, zz = x2 - x1, y2 - y1, z2 - z1
1752
+ Math.sqrt xx * xx + yy * yy + zz * zz
1753
+ else raise ArgumentError
1754
+ end
1755
+ end
1756
+
1757
+ # Normalize the value from range start..stop into 0..1.
1758
+ #
1759
+ # @param value [Numeric] number to be normalized
1760
+ # @param start [Numeric] lower bound of the range
1761
+ # @param stop [Numeric] upper bound of the range
1762
+ #
1763
+ # @return [Numeric] normalized value between 0..1
1764
+ #
1765
+ def norm (value, start, stop)
1766
+ (value.to_f - start.to_f) / (stop.to_f - start.to_f)
1767
+ end
1768
+
1769
+ # Returns the interpolated number between range start..stop.
1770
+ #
1771
+ # @param start [Numeric] lower bound of the range
1772
+ # @param stop [Numeric] upper bound of the range
1773
+ # @param amount [Numeric] amount to interpolate
1774
+ #
1775
+ # @return [Numeric] interporated number
1776
+ #
1777
+ def lerp (start, stop, amount)
1778
+ start + (stop - start) * amount
1779
+ end
1780
+
1781
+ # Maps a number from range start1..stop1 to range start2..stop2.
1782
+ #
1783
+ # @param value [Numeric] number to be mapped
1784
+ # @param start1 [Numeric] lower bound of the range1
1785
+ # @param stop1 [Numeric] upper bound of the range1
1786
+ # @param start2 [Numeric] lower bound of the range2
1787
+ # @param stop2 [Numeric] upper bound of the range2
1788
+ #
1789
+ # @return [Numeric] mapped number
1790
+ #
1791
+ def map (value, start1, stop1, start2, stop2)
1792
+ lerp start2, stop2, norm(value, start1, stop1)
1793
+ end
1794
+
1795
+ # Returns minimum value.
1796
+ #
1797
+ # @overload min(a, b)
1798
+ # @overload min(a, b, c)
1799
+ # @overload min(array)
1800
+ #
1801
+ # @param a [Numeric] value to compare
1802
+ # @param b [Numeric] value to compare
1803
+ # @param c [Numeric] value to compare
1804
+ # @param array [Numeric] values to compare
1805
+ #
1806
+ # @return [Numeric] minimum value
1807
+ #
1808
+ def min (*args)
1809
+ args.flatten.min
1810
+ end
1811
+
1812
+ # Returns maximum value.
1813
+ #
1814
+ # @overload max(a, b)
1815
+ # @overload max(a, b, c)
1816
+ # @overload max(array)
1817
+ #
1818
+ # @param a [Numeric] value to compare
1819
+ # @param b [Numeric] value to compare
1820
+ # @param c [Numeric] value to compare
1821
+ # @param array [Numeric] values to compare
1822
+ #
1823
+ # @return [Numeric] maximum value
1824
+ #
1825
+ def max (*args)
1826
+ args.flatten.max
1827
+ end
1828
+
1829
+ # Constrains the number between min..max.
1830
+ #
1831
+ # @param value [Numeric] number to be constrained
1832
+ # @param min [Numeric] lower bound of the range
1833
+ # @param max [Numeric] upper bound of the range
1834
+ #
1835
+ # @return [Numeric] constrained number
1836
+ #
1837
+ def constrain (value, min, max)
1838
+ value < min ? min : (value > max ? max : value)
1839
+ end
1840
+
1841
+ # Returns the perlin noise value.
1842
+ #
1843
+ # @overload noise(x)
1844
+ # @overload noise(x, y)
1845
+ # @overload noise(x, y, z)
1846
+ #
1847
+ # @param x [Numeric] horizontal point in noise space
1848
+ # @param y [Numeric] vertical point in noise space
1849
+ # @param z [Numeric] depth point in noise space
1850
+ #
1851
+ # @return [Numeric] noise value (0.0..1.0)
1852
+ #
1853
+ def noise (x, y = 0, z = 0)
1854
+ Rays.perlin(x, y, z) / 2.0 + 0.5
1855
+ end
1856
+
1857
+ # Loads image.
1858
+ #
1859
+ # @param filename [String] file name to load image
1860
+ # @param extension [String] type of image to load (ex. 'png')
1861
+ #
1862
+ # @return [Image] loaded image object
1863
+ #
1864
+ def loadImage (filename, extension = nil)
1865
+ filename = getImage__ filename, extension if filename =~ %r|^https?://|
1866
+ Image.new Rays::Image.load filename
1867
+ end
1868
+
1869
+ # @private
1870
+ private def getImage__ (uri, ext)
1871
+ ext ||= File.extname uri
1872
+ raise "unsupported image type -- #{ext}" unless ext =~ /^\.?(png)$/i
1873
+
1874
+ tmpdir = Pathname(Dir.tmpdir) + Digest::SHA1.hexdigest(self.class.name)
1875
+ path = tmpdir + Digest::SHA1.hexdigest(uri)
1876
+ path = path.sub_ext ext
1877
+
1878
+ unless path.file?
1879
+ URI.open uri do |input|
1880
+ tmpdir.mkdir unless tmpdir.directory?
1881
+ path.open('w') do |output|
1882
+ while buf = input.read(2 ** 16)
1883
+ output.write buf
1884
+ end
1169
1885
  end
1170
1886
  end
1171
1887
  end
1888
+ path.to_s
1172
1889
  end
1173
- path.to_s
1174
- end
1175
-
1176
- end# Processing
1177
1890
 
1891
+ def createGraphics (width, height)
1892
+ Graphics.new width, height
1893
+ end
1178
1894
 
1179
- # Image object.
1180
- #
1181
- class Processing::Image
1895
+ end# Utility
1182
1896
 
1183
- # @private
1184
- def initialize (image)
1185
- @image = image
1186
- end
1187
1897
 
1188
- # Gets width of image.
1898
+ # Processing context
1189
1899
  #
1190
- # @return [Numeric] width of image
1191
- #
1192
- def width ()
1193
- @image.width
1194
- end
1900
+ module Context
1195
1901
 
1196
- # Gets height of image.
1197
- #
1198
- # @return [Numeric] height of image
1199
- #
1200
- def height ()
1201
- @image.height
1202
- end
1902
+ include GraphicsContext, Utility, Math
1203
1903
 
1204
- # Resizes image.
1205
- #
1206
- # @param width [Numeric] width for resized image
1207
- # @param height [Numeric] height for resized image
1208
- #
1209
- # @return [nil] nil
1210
- #
1211
- def resize (width, height)
1212
- @image = Rays::Image.new(width, height).paint do |painter|
1213
- painter.image @image, 0, 0, width, height
1904
+ # @private
1905
+ def setup__ (window)
1906
+ @window__ = window
1907
+ @image__ = @window__.canvas
1908
+ super @window__.canvas_painter.paint {background 0.8}
1909
+
1910
+ @loop__ = true
1911
+ @redraw__ = false
1912
+ @frameCount__ = 0
1913
+ @mousePos__ =
1914
+ @mousePrevPos__ = Rays::Point.new 0
1915
+ @mousePressed__ = false
1916
+ @touches__ = []
1917
+
1918
+ @window__.before_draw = proc {beginDraw}
1919
+ @window__.after_draw = proc {endDraw}
1920
+
1921
+ drawFrame = -> {
1922
+ @image__ = @window__.canvas
1923
+ @painter__ = @window__.canvas_painter
1924
+ begin
1925
+ push
1926
+ @drawBlock__.call if @drawBlock__
1927
+ ensure
1928
+ pop
1929
+ @frameCount__ += 1
1930
+ end
1931
+ }
1932
+
1933
+ @window__.draw = proc do |e|
1934
+ if @loop__ || @redraw__
1935
+ @redraw__ = false
1936
+ drawFrame.call
1937
+ end
1938
+ @mousePrevPos__ = @mousePos__
1939
+ end
1940
+
1941
+ updatePointerStates = -> event, pressed = nil {
1942
+ @mousePos__ = event.pos
1943
+ @mousePressed__ = pressed if pressed != nil
1944
+ @touches__ = event.positions.map {|pos| Touch.new pos.x, pos.y}
1945
+ }
1946
+
1947
+ @window__.pointer_down = proc do |e|
1948
+ updatePointerStates.call e, true
1949
+ (@touchStartedBlock__ || @mousePressedBlock__)&.call
1950
+ end
1951
+
1952
+ @window__.pointer_up = proc do |e|
1953
+ updatePointerStates.call e, false
1954
+ (@touchEndedBlock__ || @mouseReleasedBlock__)&.call
1955
+ end
1956
+
1957
+ @window__.pointer_move = proc do |e|
1958
+ updatePointerStates.call e
1959
+ (@touchMovedBlock__ || @mouseMovedBlock__)&.call
1960
+ end
1961
+
1962
+ @window__.pointer_drag = proc do |e|
1963
+ updatePointerStates.call e
1964
+ (@touchMovedBlock__ || @mouseDraggedBlock__)&.call
1965
+ end
1214
1966
  end
1215
- nil
1216
- end
1217
1967
 
1218
- # Copies image.
1219
- #
1220
- # @overload copy(sx, sy, sw, sh, dx, dy, dw, dh)
1221
- # @overload copy(img, sx, sy, sw, sh, dx, dy, dw, dh)
1222
- #
1223
- # @param img [Image] image for copy source
1224
- # @param sx [Numrtic] x position of source region
1225
- # @param sy [Numrtic] y position of source region
1226
- # @param sw [Numrtic] width of source region
1227
- # @param sh [Numrtic] height of source region
1228
- # @param dx [Numrtic] x position of destination region
1229
- # @param dy [Numrtic] y position of destination region
1230
- # @param dw [Numrtic] width of destination region
1231
- # @param dh [Numrtic] height of destination region
1232
- #
1233
- # @return [nil] nil
1234
- #
1235
- def copy (img = nil, sx, sy, sw, sh, dx, dy, dw, dh)
1236
- img ||= self
1237
- @image.paint do |painter|
1238
- painter.image img.to_internal__, sx, sy, sw, sh, dx, dy, dw, dh
1968
+ # Define setup block.
1969
+ #
1970
+ def setup (&block)
1971
+ @window__.setup = block
1972
+ nil
1239
1973
  end
1240
- end
1241
1974
 
1242
- # Saves image to file.
1243
- #
1244
- # @param filename [String] file name to save image
1245
- #
1246
- def save (filename)
1247
- @image.save filename
1248
- end
1975
+ # Define draw block.
1976
+ #
1977
+ def draw (&block)
1978
+ @drawBlock__ = block if block
1979
+ nil
1980
+ end
1249
1981
 
1250
- # @private
1251
- def to_internal__ ()
1252
- @image
1253
- end
1982
+ def key (&block)
1983
+ @window__.key = block
1984
+ nil
1985
+ end
1254
1986
 
1255
- end# Processing::Image
1987
+ def mousePressed (&block)
1988
+ @mousePressedBlock__ = block if block
1989
+ @mousePressed__
1990
+ end
1256
1991
 
1992
+ def mouseReleased (&block)
1993
+ @mouseReleasedBlock__ = block if block
1994
+ nil
1995
+ end
1257
1996
 
1258
- # Font object.
1259
- #
1260
- class Processing::Font
1997
+ def mouseMoved (&block)
1998
+ @mouseMovedBlock__ = block if block
1999
+ nil
2000
+ end
1261
2001
 
1262
- # @private
1263
- def initialize (font)
1264
- @font = font
1265
- end
2002
+ def mouseDragged (&block)
2003
+ @mouseDraggedBlock__ = block if block
2004
+ nil
2005
+ end
1266
2006
 
1267
- # Returns bounding box.
1268
- #
1269
- # @overload textBounds(str)
1270
- # @overload textBounds(str, x, y)
1271
- # @overload textBounds(str, x, y, fontSize)
1272
- #
1273
- # @param str [String] text to calculate bounding box
1274
- # @param x [Numeric] horizontal position of bounding box
1275
- # @param y [Numeric] vertical position of bounding box
1276
- # @param fontSize [Numeric] font size
1277
- #
1278
- # @return [TextBounds] bounding box for text
1279
- #
1280
- def textBounds (str, x = 0, y = 0, fontSize = nil)
1281
- f = fontSize ? Rays::Font.new(@font.name, fontSize) : @font
1282
- TextBounds.new x, y, x + f.width(str), y + f.height
1283
- end
2007
+ def touchStarted (&block)
2008
+ @touchStartedBlock__ = block if block
2009
+ nil
2010
+ end
1284
2011
 
1285
- end# Processing::Font
2012
+ def touchEnded (&block)
2013
+ @touchEndedBlock__ = block if block
2014
+ nil
2015
+ end
1286
2016
 
2017
+ def touchMoved (&block)
2018
+ @touchMovedBlock__ = block if block
2019
+ nil
2020
+ end
1287
2021
 
1288
- # Bounding box for text.
1289
- #
1290
- class Processing::TextBounds
2022
+ # @private
2023
+ private def size__ (width, height)
2024
+ raise 'size() must be called on startup or setup block' if @started__
1291
2025
 
1292
- # Horizontal position
1293
- #
1294
- attr_reader :x
2026
+ @painter__.__send__ :end_paint
2027
+ @window__.__send__ :reset_canvas, width, height
2028
+ @painter__.__send__ :begin_paint
1295
2029
 
1296
- # Vertical position
1297
- #
1298
- attr_reader :y
2030
+ @auto_resize__ = false
2031
+ end
1299
2032
 
1300
- # Width of bounding box
1301
- #
1302
- attr_reader :w
2033
+ def windowWidth ()
2034
+ @window__.width
2035
+ end
1303
2036
 
1304
- # Height of bounding box
1305
- #
1306
- attr_reader :h
2037
+ def windowHeight ()
2038
+ @window__.height
2039
+ end
2040
+
2041
+ # Returns number of frames since program started.
2042
+ #
2043
+ # @return [Integer] total number of frames
2044
+ #
2045
+ def frameCount ()
2046
+ @frameCount__
2047
+ end
2048
+
2049
+ # Returns number of frames per second.
2050
+ #
2051
+ # @return [Float] frames per second
2052
+ #
2053
+ def frameRate ()
2054
+ @window__.event.fps
2055
+ end
2056
+
2057
+ # Returns pixel density
2058
+ #
2059
+ # @return [Numeric] pixel density
2060
+ #
2061
+ def displayDensity ()
2062
+ @painter__.pixel_density
2063
+ end
2064
+
2065
+ # Returns mouse x position
2066
+ #
2067
+ # @return [Numeric] horizontal position of mouse
2068
+ #
2069
+ def mouseX ()
2070
+ @mousePos__.x
2071
+ end
2072
+
2073
+ # Returns mouse y position
2074
+ #
2075
+ # @return [Numeric] vertical position of mouse
2076
+ #
2077
+ def mouseY ()
2078
+ @mousePos__.y
2079
+ end
1307
2080
 
1308
- # @private
1309
- def initialize (x, y, w, h)
1310
- @x, @y, @w, @h = x, y, w, h
1311
- end
2081
+ # Returns mouse x position in previous frame
2082
+ #
2083
+ # @return [Numeric] horizontal position of mouse
2084
+ #
2085
+ def pmouseX ()
2086
+ @mousePrevPos__.x
2087
+ end
2088
+
2089
+ # Returns mouse y position in previous frame
2090
+ #
2091
+ # @return [Numeric] vertical position of mouse
2092
+ #
2093
+ def pmouseY ()
2094
+ @mousePrevPos__.y
2095
+ end
2096
+
2097
+ # Returns array of touches
2098
+ #
2099
+ # @return [Array] Touch objects
2100
+ #
2101
+ def touches ()
2102
+ @touches__
2103
+ end
1312
2104
 
1313
- end# Processing::TextBounds
2105
+ # Enables calling draw block on every frame.
2106
+ #
2107
+ # @return [nil] nil
2108
+ #
2109
+ def loop ()
2110
+ @loop__ = true
2111
+ end
2112
+
2113
+ # Disables calling draw block on every frame.
2114
+ #
2115
+ # @return [nil] nil
2116
+ #
2117
+ def noLoop ()
2118
+ @loop__ = false
2119
+ end
2120
+
2121
+ # Calls draw block to redraw frame.
2122
+ #
2123
+ # @return [nil] nil
2124
+ #
2125
+ def redraw ()
2126
+ @redraw__ = true
2127
+ end
2128
+
2129
+ end# Context
2130
+
2131
+
2132
+ end# Processing
1314
2133
 
1315
2134
 
1316
2135
  end# RubySketch