rubysketch 0.2.6 → 0.3.3

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