rubysketch 0.9.5 → 0.11.0

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.
@@ -0,0 +1,422 @@
1
+ module RubySketch
2
+
3
+
4
+ # Constrains the motion of sprites.
5
+ # Constraints are created with Sprite#snap, #link, #wheel and #chase
6
+ # (or the same verbs on Sprite#pin), not with new.
7
+ #
8
+ class Constraint
9
+
10
+ include Xot::Setter
11
+
12
+ # @private
13
+ def initialize(constraint)
14
+ @constraint__ = constraint
15
+ end
16
+
17
+ # Removes the constraint permanently.
18
+ #
19
+ # @return [Constraint] self
20
+ #
21
+ def remove()
22
+ @constraint__.remove
23
+ self
24
+ end
25
+
26
+ # Sets the spring stiffness in hertz.
27
+ #
28
+ # @param [Numeric, nil] hertz stiffness, or nil to make it rigid
29
+ #
30
+ # @return [Numeric, nil] hertz
31
+ #
32
+ def spring=(hertz)
33
+ @constraint__.spring = hertz
34
+ end
35
+
36
+ # Returns the spring stiffness in hertz.
37
+ #
38
+ # @return [Numeric, nil] hertz, or nil if the constraint is rigid
39
+ #
40
+ def spring()
41
+ @constraint__.spring
42
+ end
43
+
44
+ # Sets the spring damping ratio.
45
+ #
46
+ # @param [Numeric] ratio 0 (no damping) to 1 (critical damping)
47
+ #
48
+ # @return [Numeric] damping ratio
49
+ #
50
+ def damping=(ratio)
51
+ @constraint__.damping = ratio
52
+ end
53
+
54
+ # Returns the spring damping ratio.
55
+ #
56
+ # @return [Numeric] damping ratio
57
+ #
58
+ def damping()
59
+ @constraint__.damping
60
+ end
61
+
62
+ # Sets whether the constrained sprites collide with each other.
63
+ #
64
+ # @param [Boolean] bool collide or not
65
+ #
66
+ # @return [Boolean] collide or not
67
+ #
68
+ def collide=(bool)
69
+ @constraint__.collide = bool
70
+ end
71
+
72
+ # Returns whether the constrained sprites collide with each other.
73
+ #
74
+ # @return [Boolean] collide or not
75
+ #
76
+ def collide?()
77
+ @constraint__.collide?
78
+ end
79
+
80
+ # Sets the maximum force (or torque) that drives the constraint.
81
+ #
82
+ # @param [Numeric, nil] force
83
+ # maximum force, or nil for the default strength.
84
+ # 0 makes the drive powerless
85
+ #
86
+ # @return [Numeric, nil] force
87
+ #
88
+ def force=(force)
89
+ @constraint__.force = force
90
+ end
91
+
92
+ # Returns the maximum force that drives the constraint.
93
+ #
94
+ # @return [Numeric, nil] force, or nil for the default strength
95
+ #
96
+ def force()
97
+ @constraint__.force
98
+ end
99
+
100
+ universal_accessor :spring, :damping, :force, collide: {reader: :collide?}
101
+
102
+ # Returns whether the constraint is active in a physics world.
103
+ #
104
+ # @return [Boolean] active or not
105
+ #
106
+ def active?()
107
+ @constraint__.active?
108
+ end
109
+
110
+ # Returns whether the constraint was removed.
111
+ #
112
+ # @return [Boolean] removed or not
113
+ #
114
+ def removed?()
115
+ @constraint__.removed?
116
+ end
117
+
118
+ # Returns the sprites the constraint is fastened to.
119
+ #
120
+ # @return [Array<Sprite, nil>] nil for a world anchor
121
+ #
122
+ def sprites()
123
+ @sprites__ ||= @constraint__.views.map {_1&.sprite}.freeze
124
+ end
125
+
126
+ # @private
127
+ def ==(o)
128
+ o.is_a?(Constraint) && @constraint__ == o.getInternal__
129
+ end
130
+
131
+ alias eql? ==
132
+
133
+ # @private
134
+ def hash()
135
+ @constraint__.hash
136
+ end
137
+
138
+ # @private
139
+ def getInternal__()
140
+ @constraint__
141
+ end
142
+
143
+ # @private
144
+ def self.wrap__(constraint)
145
+ klass =
146
+ case constraint
147
+ when Reflex::SnapConstraint then SnapConstraint
148
+ when Reflex::LinkConstraint then LinkConstraint
149
+ when Reflex::WheelConstraint then WheelConstraint
150
+ when Reflex::ChaseConstraint then ChaseConstraint
151
+ else raise ArgumentError, "unknown constraint type: #{constraint.class}"
152
+ end
153
+ klass.new constraint
154
+ end
155
+
156
+ private
157
+
158
+ def getContext__()
159
+ sprites.first&.getContext__ || Processing.context
160
+ end
161
+
162
+ def toDegrees__(angle)
163
+ getContext__.toDegrees__ angle
164
+ end
165
+
166
+ def fromDegrees__(degrees)
167
+ getContext__.fromDegrees__ degrees
168
+ end
169
+
170
+ end# Constraint
171
+
172
+
173
+ # Snaps two points together like a hinge.
174
+ #
175
+ class SnapConstraint < Constraint
176
+
177
+ # Sets the range of the relative rotation angle.
178
+ #
179
+ # @param [Range, Numeric, nil] angle
180
+ # radians or degrees depending on angleMode().
181
+ # nil rotates freely, 0 fixes the rotation
182
+ #
183
+ # @return [Range, Numeric, nil] angle
184
+ #
185
+ def angle=(angle)
186
+ @constraint__.angle =
187
+ case angle
188
+ when Range then Range.new toDegrees__(angle.begin), toDegrees__(angle.end)
189
+ when Numeric then toDegrees__ angle
190
+ else angle
191
+ end
192
+ end
193
+
194
+ # Returns the range of the relative rotation angle.
195
+ #
196
+ # @return [Range, nil] radians or degrees depending on angleMode()
197
+ #
198
+ def angle()
199
+ angle = @constraint__.angle
200
+ angle ? Range.new(fromDegrees__(angle.begin), fromDegrees__(angle.end)) : nil
201
+ end
202
+
203
+ # Sets the motor speed that drives the relative rotation.
204
+ #
205
+ # @param [Numeric, nil] speed
206
+ # angular velocity per second, radians or degrees depending on
207
+ # angleMode(). nil stops the motor
208
+ #
209
+ # @return [Numeric, nil] speed
210
+ #
211
+ def motor=(speed)
212
+ @constraint__.motor = speed && toDegrees__(speed)
213
+ end
214
+
215
+ # Returns the motor speed.
216
+ #
217
+ # @return [Numeric, nil] angular velocity per second
218
+ #
219
+ def motor()
220
+ speed = @constraint__.motor
221
+ speed && fromDegrees__(speed)
222
+ end
223
+
224
+ universal_accessor :angle, :motor
225
+
226
+ end# SnapConstraint
227
+
228
+
229
+ # Keeps two points apart at a distance, or slides them along an axis
230
+ # like a rail.
231
+ #
232
+ class LinkConstraint < Constraint
233
+
234
+ # Sets the axis to slide along.
235
+ # With an axis, the separation is measured along it instead of
236
+ # radially, and the relative rotation is locked like a rail.
237
+ #
238
+ # @param [Vector, Array<Numeric>, nil] axis
239
+ # direction in the target local space, or nil to link radially
240
+ #
241
+ # @return [Vector, Array<Numeric>, nil] axis
242
+ #
243
+ def axis=(axis)
244
+ @constraint__.axis = RubySketch.unwrap__ axis
245
+ end
246
+
247
+ # Returns the axis to slide along.
248
+ #
249
+ # @return [Vector, nil] axis, or nil for a radial link
250
+ #
251
+ def axis()
252
+ @constraint__.axis&.toVector
253
+ end
254
+
255
+ # Sets the distance to keep.
256
+ #
257
+ # @param [Numeric] distance distance in pixels
258
+ #
259
+ # @return [Numeric] distance
260
+ #
261
+ def distance=(distance)
262
+ @constraint__.distance = distance
263
+ end
264
+
265
+ # Returns the distance to keep.
266
+ #
267
+ # @return [Numeric] distance in pixels
268
+ #
269
+ def distance()
270
+ @constraint__.distance
271
+ end
272
+
273
+ # Returns the current distance between the two points.
274
+ #
275
+ # @return [Numeric] distance in pixels, or 0 if not active in a world
276
+ #
277
+ def currentDistance()
278
+ @constraint__.current_distance
279
+ end
280
+
281
+ # Sets the range of the separation.
282
+ #
283
+ # @param [Range, Numeric, nil] range
284
+ # distance range like a rope, or the translation range along the axis
285
+ #
286
+ # @return [Range, Numeric, nil] range
287
+ #
288
+ def range=(range)
289
+ @constraint__.range = range
290
+ end
291
+
292
+ # Returns the range of the separation.
293
+ #
294
+ # @return [Range, nil] separation range
295
+ #
296
+ def range()
297
+ @constraint__.range
298
+ end
299
+
300
+ # Sets the motor speed that drives the separation.
301
+ #
302
+ # @param [Numeric, nil] speed speed in pixels per second, or nil to stop
303
+ #
304
+ # @return [Numeric, nil] speed
305
+ #
306
+ def motor=(speed)
307
+ @constraint__.motor = speed
308
+ end
309
+
310
+ # Returns the motor speed.
311
+ #
312
+ # @return [Numeric, nil] speed in pixels per second
313
+ #
314
+ def motor()
315
+ @constraint__.motor
316
+ end
317
+
318
+ universal_accessor :axis, :distance, :range, :motor
319
+
320
+ alias dist= distance=
321
+ alias dist distance
322
+
323
+ end# LinkConstraint
324
+
325
+
326
+ # Rides on the target like a wheel: slides along an axis for the
327
+ # suspension while spinning freely.
328
+ #
329
+ class WheelConstraint < Constraint
330
+
331
+ # Sets the axis to slide along for the suspension.
332
+ #
333
+ # @param [Vector, Array<Numeric>] axis direction in the target local space
334
+ #
335
+ # @return [Vector, Array<Numeric>] axis
336
+ #
337
+ def axis=(axis)
338
+ @constraint__.axis = RubySketch.unwrap__ axis
339
+ end
340
+
341
+ # Returns the axis to slide along.
342
+ #
343
+ # @return [Vector] axis
344
+ #
345
+ def axis()
346
+ @constraint__.axis&.toVector
347
+ end
348
+
349
+ # Sets the range of the translation along the axis.
350
+ #
351
+ # @param [Range, Numeric, nil] range translation range in pixels
352
+ #
353
+ # @return [Range, Numeric, nil] range
354
+ #
355
+ def range=(range)
356
+ @constraint__.range = range
357
+ end
358
+
359
+ # Returns the range of the translation along the axis.
360
+ #
361
+ # @return [Range, nil] translation range
362
+ #
363
+ def range()
364
+ @constraint__.range
365
+ end
366
+
367
+ # Sets the motor speed that spins the wheel.
368
+ #
369
+ # @param [Numeric, nil] speed
370
+ # angular velocity per second, radians or degrees depending on
371
+ # angleMode(). nil stops the motor
372
+ #
373
+ # @return [Numeric, nil] speed
374
+ #
375
+ def motor=(speed)
376
+ @constraint__.motor = speed && toDegrees__(speed)
377
+ end
378
+
379
+ # Returns the motor speed.
380
+ #
381
+ # @return [Numeric, nil] angular velocity per second
382
+ #
383
+ def motor()
384
+ speed = @constraint__.motor
385
+ speed && fromDegrees__(speed)
386
+ end
387
+
388
+ universal_accessor :axis, :range, :motor
389
+
390
+ end# WheelConstraint
391
+
392
+
393
+ # Moves a point toward the target every frame.
394
+ #
395
+ class ChaseConstraint < Constraint
396
+
397
+ # Sets the target to chase.
398
+ #
399
+ # @param [Sprite, Pin, Vector, Array<Numeric>, nil] target
400
+ # the center of a sprite, a pin, or a point in the world.
401
+ # nil chases nothing
402
+ #
403
+ # @return [Sprite, Pin, Vector, Array<Numeric>, nil] target
404
+ #
405
+ def target=(target)
406
+ @constraint__.target = RubySketch.unwrap__ target
407
+ end
408
+
409
+ # Returns the target to chase.
410
+ #
411
+ # @return [Pin] the target pin
412
+ #
413
+ def target()
414
+ Pin.new nil, pin__: @constraint__.target
415
+ end
416
+
417
+ universal_accessor :target
418
+
419
+ end# ChaseConstraint
420
+
421
+
422
+ end# RubySketch
@@ -14,15 +14,15 @@ module RubySketch
14
14
  # @private
15
15
  def initialize(window)
16
16
  super
17
- @window__ = window
18
- @timers__ = {}
19
- @firingTimers__ = {}
20
- @nextTimerID__ = 0
21
- @noteNumber__ = 69
22
- @noteFrequency__ = 440
23
- @noteVelocity__ = 1
24
- @controllerIndex__ = 0
25
- @controllerValue__ = 0
17
+ @window__ = window
18
+ @timers__ = {}
19
+ @firingTimers__ = {}
20
+ @nextTimerID__ = 0
21
+ @noteNumber__ = 69
22
+ @noteFrequency__ = 440
23
+ @noteVelocity__ = 1
24
+ @controllerNumber__ = 0
25
+ @controllerValue__ = 0
26
26
 
27
27
  @world__ = createWorld
28
28
 
@@ -33,14 +33,14 @@ module RubySketch
33
33
  }
34
34
 
35
35
  updateControllerStates = -> event {
36
- @controllerIndex__ = event.controller
37
- @controllerValue__ = event.value
36
+ @controllerNumber__ = event.controller
37
+ @controllerValue__ = event.value
38
38
  }
39
39
 
40
40
  @window__.update_window = proc do
41
41
  fireTimers__
42
42
  Beeps.process_streams!
43
- Reflex.process_events!
43
+ Reflex.process_events!# TODO: delete this
44
44
  end
45
45
 
46
46
  @window__.note_on = proc do |e|
@@ -265,29 +265,29 @@ module RubySketch
265
265
  # @return [Sprite] the new sprite object
266
266
  #
267
267
  def createSprite(*args, **kwargs)
268
- @world__.createSprite(*args, context: self, **kwargs)
268
+ @world__.createSprite(*args, **kwargs)
269
269
  end
270
270
 
271
271
  # Adds sprite to the physics engine.
272
272
  #
273
- # @param [Array] array user array to store the sprite
274
273
  # @param [Sprite] sprite sprite object
274
+ # @param [Array] to user array to store the sprite
275
275
  #
276
276
  # @return [Sprite] the added sprite
277
277
  #
278
- def addSprite(array = nil, sprite)
279
- @world__.addSprite array, sprite
278
+ def addSprite(...)
279
+ @world__.addSprite(...)
280
280
  end
281
281
 
282
282
  # Removes sprite from the physics engine.
283
283
  #
284
- # @param [Array] array user array to remove the sprite
285
284
  # @param [Sprite] sprite sprite object
285
+ # @param [Array] from user array to remove the sprite
286
286
  #
287
287
  # @return [Sprite] the removed sprite
288
288
  #
289
- def removeSprite(array = nil, sprite)
290
- @world__.removeSprite array, sprite
289
+ def removeSprite(...)
290
+ @world__.removeSprite(...)
291
291
  end
292
292
 
293
293
  alias drawSprite sprite
@@ -307,7 +307,11 @@ module RubySketch
307
307
  # @return [SpriteWorld] first added world
308
308
  #
309
309
  def addWorld(*worlds)
310
- worlds.each {@window__.add_overlay _1.getInternal__}
310
+ raise ArgumentError if worlds.any? {_1.getContext__}
311
+ worlds.each do |world|
312
+ @window__.add_overlay world.getInternal__
313
+ world.attached__
314
+ end
311
315
  worlds.first
312
316
  end
313
317
 
@@ -318,7 +322,11 @@ module RubySketch
318
322
  # @return [SpriteWorld] first removed world
319
323
  #
320
324
  def removeWorld(*worlds)
321
- worlds.each {@window__.remove_overlay _1.getInternal__}
325
+ raise ArgumentError if worlds.any? {_1.getContext__ != self}
326
+ worlds.each do |world|
327
+ @window__.remove_overlay world.getInternal__
328
+ world.detached__
329
+ end
322
330
  worlds.first
323
331
  end
324
332
 
@@ -356,12 +364,12 @@ module RubySketch
356
364
  @noteVelocity__
357
365
  end
358
366
 
359
- # Returns the last controller index that was changed.
367
+ # Returns the last controller number that was changed.
360
368
  #
361
- # @return [Numeric] last controller index
369
+ # @return [Numeric] last controller number
362
370
  #
363
- def controllerIndex()
364
- @controllerIndex__
371
+ def controllerNumber()
372
+ @controllerNumber__
365
373
  end
366
374
 
367
375
  # Returns the last controller value that was changed.
@@ -7,7 +7,7 @@ module RubySketch
7
7
  module_function
8
8
 
9
9
  def name(downcase = false)
10
- super().split('::')[-2].then {|s|
10
+ super().split('::')[..-2].join.then {|s|
11
11
  downcase ? s.downcase : s
12
12
  }
13
13
  end
@@ -4,3 +4,13 @@ class Rays::Point
4
4
  Processing::Vector.new x, y, z
5
5
  end
6
6
  end
7
+
8
+
9
+ module RubySketch
10
+
11
+ # @private
12
+ def self.unwrap__(arg)
13
+ arg.respond_to?(:getInternal__) ? arg.getInternal__ : arg
14
+ end
15
+
16
+ end# RubySketch
@@ -0,0 +1,108 @@
1
+ module RubySketch
2
+
3
+
4
+ # A pin fastened at a point on a sprite.
5
+ # Creating a constraint between sprites starts from a pin.
6
+ #
7
+ class Pin
8
+
9
+ # @private
10
+ def initialize(sprite, x = nil, y = nil, pin__: nil)
11
+ if pin__
12
+ @sprite__ = pin__.view&.sprite
13
+ @pin__ = pin__
14
+ else
15
+ @sprite__ = sprite
16
+ @pin__ = x ? sprite.getInternal__.pin(x, y) : sprite.getInternal__.pin
17
+ end
18
+ end
19
+
20
+ # Creates a constraint that makes the pin and the target point coincide.
21
+ #
22
+ # @param [Sprite, Pin, Vector, Array<Numeric>, nil] target
23
+ # the center of a sprite, a pin, or a point in the world.
24
+ # snaps to the world when target is omitted
25
+ #
26
+ # @return [SnapConstraint] a new constraint
27
+ #
28
+ def snap(*args, **options, &block)
29
+ create__ SnapConstraint, @pin__.snap(*unwrap__(args)), options, block
30
+ end
31
+
32
+ # Creates a constraint that keeps the distance to the target point.
33
+ #
34
+ # @param [Sprite, Pin, Vector, Array<Numeric>, nil] target
35
+ # the center of a sprite, a pin, or a point in the world.
36
+ # links to the world when target is omitted
37
+ #
38
+ # @return [LinkConstraint] a new constraint
39
+ #
40
+ def link(*args, **options, &block)
41
+ create__ LinkConstraint, @pin__.link(*unwrap__(args)), options, block
42
+ end
43
+
44
+ # Creates a constraint that makes the pin ride on the target like a
45
+ # wheel: it slides along an axis for the suspension and spins freely.
46
+ #
47
+ # @param [Sprite, Pin, Vector, Array<Numeric>, nil] target
48
+ # the center of a sprite, a pin, or a point in the world.
49
+ # rides on the world when target is omitted
50
+ #
51
+ # @return [WheelConstraint] a new constraint
52
+ #
53
+ def wheel(*args, **options, &block)
54
+ create__ WheelConstraint, @pin__.wheel(*unwrap__(args)), options, block
55
+ end
56
+
57
+ # Creates a constraint that moves the pin toward the target every frame.
58
+ #
59
+ # @param [Sprite, Pin, Vector, Array<Numeric>, nil] target
60
+ # the center of a sprite, a pin, or a point in the world.
61
+ # chases nothing when target is omitted
62
+ #
63
+ # @return [ChaseConstraint] a new constraint
64
+ #
65
+ def chase(*args, **options, &block)
66
+ create__ ChaseConstraint, @pin__.chase(*unwrap__(args)), options, block
67
+ end
68
+
69
+ # Returns the sprite the pin is fastened to.
70
+ #
71
+ # @return [Sprite, nil] nil for a pin in the world
72
+ #
73
+ def sprite()
74
+ @sprite__
75
+ end
76
+
77
+ # Returns the position of the pin.
78
+ #
79
+ # @return [Vector, nil] sprite-local position, or nil for the center
80
+ #
81
+ def position()
82
+ @pin__.position&.toVector
83
+ end
84
+
85
+ alias pos position
86
+
87
+ # @private
88
+ def getInternal__()
89
+ @pin__
90
+ end
91
+
92
+ private
93
+
94
+ def create__(klass, constraint, options, block)
95
+ klass.new(constraint).tap do |c|
96
+ c.set options unless options.empty?
97
+ Xot::BlockUtil.instance_eval_or_block_call c, &block if block
98
+ end
99
+ end
100
+
101
+ def unwrap__(args)
102
+ args.map {|arg| RubySketch.unwrap__ arg}
103
+ end
104
+
105
+ end# Pin
106
+
107
+
108
+ end# RubySketch
@@ -83,7 +83,7 @@ module RubySketch
83
83
 
84
84
  # @deprecated Use {#contactEnd} instead.
85
85
  def contact_end(&block)
86
- warn "Shape#contact_end is deprecated, use #contactEnd instead", uniq: true
86
+ Xot.warn "Shape#contact_end is deprecated, use #contactEnd instead", uniq: true
87
87
  contactEnd(&block)
88
88
  end
89
89