rubysketch 0.2.7 → 0.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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