rubysketch 0.2.7 → 0.3.4

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