gamebox 0.2.1 → 0.3.2

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.
Files changed (95) hide show
  1. data/Gemfile +1 -8
  2. data/Rakefile +13 -37
  3. data/TODO.txt +27 -27
  4. data/docs/getting_started.rdoc +2 -2
  5. data/gamebox.gemspec +33 -191
  6. data/lib/gamebox.rb +18 -14
  7. data/lib/gamebox/actor.rb +37 -27
  8. data/lib/gamebox/actor_factory.rb +4 -5
  9. data/lib/gamebox/actor_view.rb +8 -0
  10. data/lib/gamebox/actors/collidable_debugger.rb +2 -2
  11. data/lib/gamebox/actors/curtain.rb +3 -3
  12. data/lib/gamebox/actors/emitter.rb +51 -0
  13. data/lib/gamebox/actors/label.rb +27 -7
  14. data/lib/gamebox/actors/logo.rb +1 -1
  15. data/lib/gamebox/actors/spatial_debugger.rb +25 -10
  16. data/lib/gamebox/arbiter.rb +61 -34
  17. data/lib/gamebox/behavior.rb +3 -3
  18. data/lib/gamebox/behaviors/animated.rb +1 -1
  19. data/lib/gamebox/behaviors/audible.rb +1 -1
  20. data/lib/gamebox/behaviors/collidable.rb +9 -4
  21. data/lib/gamebox/behaviors/collidable/aabb_collidable.rb +26 -1
  22. data/lib/gamebox/behaviors/collidable/circle_collidable.rb +3 -3
  23. data/lib/gamebox/behaviors/collidable/polygon_collidable.rb +1 -1
  24. data/lib/gamebox/behaviors/graphical.rb +30 -4
  25. data/lib/gamebox/behaviors/layered.rb +1 -1
  26. data/lib/gamebox/behaviors/physical.rb +113 -30
  27. data/lib/gamebox/behaviors/timed.rb +33 -0
  28. data/lib/gamebox/behaviors/updatable.rb +1 -1
  29. data/lib/gamebox/class_finder.rb +1 -21
  30. data/lib/gamebox/console_app.rb +33 -31
  31. data/lib/gamebox/constants.rb +481 -0
  32. data/lib/gamebox/data/config/objects.yml +7 -0
  33. data/lib/gamebox/gamebox_application.rb +10 -33
  34. data/lib/gamebox/gamebox_generator.rb +32 -32
  35. data/lib/gamebox/input_manager.rb +73 -32
  36. data/lib/gamebox/lib/inflector.rb +1 -1
  37. data/lib/gamebox/lib/range_ext.rb +5 -0
  38. data/lib/gamebox/lib/rect.rb +548 -548
  39. data/lib/gamebox/lib/sorted_list.rb +1 -1
  40. data/lib/gamebox/lib/symbol_ext.rb +8 -0
  41. data/lib/gamebox/physical_director.rb +1 -1
  42. data/lib/gamebox/physical_stage.rb +3 -3
  43. data/lib/gamebox/physics.rb +0 -3
  44. data/lib/gamebox/resource_manager.rb +22 -17
  45. data/lib/gamebox/sound_manager.rb +3 -2
  46. data/lib/gamebox/spatial_hash.rb +60 -31
  47. data/lib/gamebox/spatial_stagehand.rb +30 -6
  48. data/lib/gamebox/spec/helper.rb +7 -7
  49. data/lib/gamebox/stage.rb +18 -19
  50. data/lib/gamebox/stage_manager.rb +33 -23
  51. data/lib/gamebox/stagehand.rb +3 -0
  52. data/lib/gamebox/svg_document.rb +1 -1
  53. data/lib/gamebox/tasks/gamebox_tasks.rake +133 -0
  54. data/lib/gamebox/templates/actor.erb +0 -2
  55. data/lib/gamebox/templates/actor_view.erb +1 -3
  56. data/lib/gamebox/templates/template_app/Gemfile +3 -2
  57. data/lib/gamebox/templates/template_app/Rakefile +3 -8
  58. data/lib/gamebox/templates/template_app/config/environment.rb +7 -39
  59. data/lib/gamebox/templates/template_app/src/demo_stage.rb +1 -2
  60. data/lib/gamebox/templates/template_app/src/my_actor.rb +0 -3
  61. data/lib/gamebox/version.rb +2 -2
  62. data/lib/gamebox/viewport.rb +44 -8
  63. data/lib/gamebox/views/graphical_actor_view.rb +22 -16
  64. data/lib/gamebox/wrapped_screen.rb +9 -1
  65. data/script/perf_spatial_hash.rb +49 -58
  66. data/script/perf_struct_vs_array.rb +32 -0
  67. data/spec/actor_factory_spec.rb +61 -0
  68. data/spec/actor_spec.rb +24 -18
  69. data/spec/actor_view_spec.rb +51 -6
  70. data/spec/animated_spec.rb +27 -6
  71. data/spec/arbiter_spec.rb +12 -24
  72. data/spec/backstage_spec.rb +1 -1
  73. data/spec/behavior_spec.rb +3 -3
  74. data/spec/class_finder_spec.rb +13 -0
  75. data/spec/collidable_spec.rb +30 -10
  76. data/spec/emitter_spec.rb +20 -0
  77. data/spec/helper.rb +5 -21
  78. data/spec/input_manager_spec.rb +134 -0
  79. data/spec/label_spec.rb +0 -1
  80. data/spec/physical_spec.rb +114 -5
  81. data/spec/resource_manager_spec.rb +1 -2
  82. data/spec/spatial_hash_spec.rb +23 -7
  83. data/spec/spatial_stagehand_spec.rb +97 -0
  84. data/spec/stage_manager_spec.rb +0 -1
  85. data/spec/stage_spec.rb +2 -2
  86. data/spec/viewport_spec.rb +92 -48
  87. metadata +223 -119
  88. data/.gitignore +0 -11
  89. data/History.txt +0 -80
  90. data/VERSION +0 -1
  91. data/lib/gamebox/event_compat.rb +0 -285
  92. data/lib/gamebox/lib/diy.rb +0 -371
  93. data/lib/gamebox/lib/numbers_ext.rb +0 -3
  94. data/lib/gamebox/tasks/gamebox_tasks.rb +0 -61
  95. data/load_paths.rb +0 -20
@@ -5,9 +5,11 @@ game:
5
5
  - sound_manager
6
6
  - stage_manager
7
7
  resource_manager:
8
+ auto_require: false
8
9
  compose:
9
10
  - wrapped_screen
10
11
  stage_manager:
12
+ auto_require: false
11
13
  compose:
12
14
  - input_manager
13
15
  - resource_manager
@@ -15,18 +17,23 @@ stage_manager:
15
17
  - config_manager
16
18
  - actor_factory
17
19
  sound_manager:
20
+ auto_require: false
18
21
  compose:
19
22
  - resource_manager
20
23
  - config_manager
21
24
  input_manager:
25
+ auto_require: false
22
26
  compose:
23
27
  - wrapped_screen
24
28
  - config_manager
25
29
  wrapped_screen:
30
+ auto_require: false
26
31
  compose:
27
32
  - config_manager
28
33
  config_manager:
34
+ auto_require: false
29
35
  actor_factory:
36
+ auto_require: false
30
37
  compose:
31
38
  - input_manager
32
39
  - wrapped_screen
@@ -1,22 +1,11 @@
1
1
  #!/usr/bin/env ruby
2
2
  $: << "#{File.dirname(__FILE__)}/../config"
3
3
 
4
- require 'gosu'
5
- include Gosu
6
-
7
- require "environment"
8
-
9
- require 'metaclass'
10
- require 'publisher'
11
- require 'publisher_ext'
12
- require 'inflector'
13
- require 'constructor'
14
- require 'diy'
15
- require 'class_finder'
16
- require 'actor_factory'
17
- require 'input_manager'
18
-
19
- require 'stagehand'
4
+ begin
5
+ # optional file
6
+ require "environment"
7
+ rescue LoadError => err
8
+ end
20
9
 
21
10
  class GameboxApp
22
11
  attr_reader :context, :game
@@ -45,13 +34,11 @@ class GameboxApp
45
34
  end
46
35
 
47
36
  def setup_debug_server
48
-
49
- require 'drb'
50
- self.class.extend DRbUndumped
51
- puts "Starting debug server..."
52
-
53
- DRb.start_service "druby://localhost:7373", self
54
- puts "on #{DRb.uri}"
37
+ Thread.new do
38
+ loop do
39
+ binding.remote_pry
40
+ end
41
+ end
55
42
  end
56
43
 
57
44
  def main_loop
@@ -61,10 +48,6 @@ class GameboxApp
61
48
 
62
49
  def shutdown
63
50
  end
64
-
65
- def debug_eval(eval_str)
66
- instance_eval eval_str
67
- end
68
51
 
69
52
  def start(argv,env)
70
53
  setup
@@ -75,12 +58,6 @@ class GameboxApp
75
58
  end
76
59
  end
77
60
 
78
- # TODO move this to some logging class
79
- def log(output, level = :debug)
80
- t = Time.now
81
- puts "[#{t.min}:#{t.sec}:#{t.usec}] [#{level}] #{output}"
82
- end
83
-
84
61
  if $0 == __FILE__
85
62
  GameboxApp.run ARGV, ENV
86
63
  end
@@ -1,32 +1,32 @@
1
- # This is the generator for gamebox
2
- require 'inflector'
3
- $: << File.join(File.dirname(__FILE__),'generators')
4
-
5
- unless "".respond_to? :end_with?
6
- class String
7
- def end_with?(ending)
8
- self[size-ending.size..-1] == ending
9
- end
10
- end
11
- end
12
-
13
- def print_usage
14
- puts "generate what *opts"
15
- puts "TODO list all generators here"
16
- end
17
- # TODO pull these out into generators/ dir for listing
18
-
19
- if ARGV.size < 1
20
- print_usage
21
- exit 0
22
- end
23
-
24
- # TODO load generator file based on ARGV[0]
25
- generator_klass_name =
26
- Inflector.camelize ARGV[0].downcase+"_generator"
27
-
28
- require Inflector.underscore(generator_klass_name)
29
-
30
- generator_klass = Object.const_get generator_klass_name
31
- generator_klass.new.generate(ARGV[1..-1])
32
-
1
+ # # This is the generator for gamebox
2
+ #
3
+ # $: << File.join(File.dirname(__FILE__),'generators')
4
+ #
5
+ # unless "".respond_to? :end_with?
6
+ # class String
7
+ # def end_with?(ending)
8
+ # self[size-ending.size..-1] == ending
9
+ # end
10
+ # end
11
+ # end
12
+ #
13
+ # def print_usage
14
+ # puts "generate what *opts"
15
+ # puts "TODO list all generators here"
16
+ # end
17
+ # # TODO pull these out into generators/ dir for listing
18
+ #
19
+ # if ARGV.size < 1
20
+ # print_usage
21
+ # exit 0
22
+ # end
23
+ #
24
+ # # TODO load generator file based on ARGV[0]
25
+ # generator_klass_name =
26
+ # Inflector.camelize ARGV[0].downcase+"_generator"
27
+ #
28
+ # require Inflector.underscore(generator_klass_name)
29
+ #
30
+ # generator_klass = Object.const_get generator_klass_name
31
+ # generator_klass.new.generate(ARGV[1..-1])
32
+ #
@@ -1,5 +1,7 @@
1
1
  require 'publisher'
2
- require 'hooked_gosu_window'
2
+
3
+ # looks like theres a bug in Gosu
4
+ KbRangeBegin = 0
3
5
 
4
6
  # InputManager handles the pumping for events and distributing of said events.
5
7
  # You can gain access to these events by registering for all events,
@@ -20,6 +22,9 @@ class InputManager
20
22
  extend Publisher
21
23
  can_fire :key_up, :event_received
22
24
 
25
+ DOWN_EVENTS = [:mouse_down, :keyboard_down, :game_pad_down]
26
+ UP_EVENTS = [:mouse_up, :keyboard_up, :game_pad_up]
27
+
23
28
  attr_accessor :window
24
29
  constructor :config_manager, :wrapped_screen
25
30
 
@@ -28,10 +33,12 @@ class InputManager
28
33
  def setup
29
34
  @window = @wrapped_screen.screen
30
35
 
31
- @auto_quit = instance_eval(@config_manager[:auto_quit])
36
+ auto_quit = @config_manager[:auto_quit]
37
+ @auto_quit = instance_eval(auto_quit) if auto_quit
32
38
 
33
39
  @hooks = {}
34
40
  @non_id_hooks = {}
41
+ @down_ids = {}
35
42
  end
36
43
 
37
44
  # This gets called from game app and sets up all the
@@ -46,17 +53,17 @@ class InputManager
46
53
  end
47
54
  @window.when :update do |millis|
48
55
 
49
- @last_mouse_x ||= @window.mouse_x
50
- @last_mouse_y ||= @window.mouse_y
56
+ @last_mouse_x ||= mouse_x
57
+ @last_mouse_y ||= mouse_y
51
58
 
52
- x_diff = @last_mouse_x - @window.mouse_x
53
- y_diff = @last_mouse_y - @window.mouse_y
59
+ x_diff = @last_mouse_x - mouse_x
60
+ y_diff = @last_mouse_y - mouse_y
54
61
 
55
62
  unless x_diff < 0.1 && x_diff > -0.1 && y_diff < 0.1 && y_diff > -0.1
56
63
  _handle_event nil, :motion
57
64
 
58
- @last_mouse_x = @window.mouse_x
59
- @last_mouse_y = @window.mouse_y
65
+ @last_mouse_x = mouse_x
66
+ @last_mouse_y = mouse_y
60
67
  end
61
68
 
62
69
  game.update millis
@@ -69,51 +76,69 @@ class InputManager
69
76
  end
70
77
 
71
78
  def _handle_event(gosu_id, action) #:nodoc:
72
- @window.close if gosu_id == @auto_quit
79
+ @window.close if @auto_quit && gosu_id == @auto_quit
80
+ if action == :down
81
+ @down_ids[gosu_id] = true
82
+ else
83
+ @down_ids.delete gosu_id
84
+ end
85
+
73
86
  event_data = nil
87
+ mouse_dragged = false
74
88
 
89
+ callback_key = action
75
90
  if gosu_id.nil?
76
91
  event_type = :mouse_motion
77
92
  callback_key = :mouse_motion
78
- event_data = [@window.mouse_x, @window.mouse_y]
93
+ @mouse_dragging = true if @mouse_down
94
+ event_data = [mouse_x, mouse_y]
79
95
  elsif gosu_id >= MsRangeBegin && gosu_id <= MsRangeEnd
80
96
  event_type = :mouse
97
+ event_data = [mouse_x, mouse_y]
81
98
  if action == :up
82
- callback_key = :mouse_up
99
+ # callback_key = :mouse_up
100
+ @mouse_down = false
101
+ mouse_dragged = true if @mouse_dragging
102
+ @mouse_dragging = false
83
103
  else
84
- callback_key = :mouse_down
104
+ # callback_key = :mouse_down
105
+ @mouse_down = true
106
+ @last_click_x = mouse_x
107
+ @last_click_y = mouse_y
85
108
  end
86
109
  elsif gosu_id >= KbRangeBegin && gosu_id <= KbRangeEnd
87
110
  event_type = :keyboard
88
- if action == :up
89
- callback_key = :keyboard_up
90
- else
91
- callback_key = :keyboard_down
92
- end
93
111
  elsif gosu_id >= GpRangeBegin && gosu_id <= GpRangeEnd
94
112
  event_type = :game_pad
95
- if action == :up
96
- callback_key = :game_pad_up
97
- else
98
- callback_key = :game_pad_down
99
- end
100
113
  end
101
114
 
102
115
  event = {
103
116
  :type => event_type,
104
117
  :id => gosu_id,
105
118
  :action => action,
106
- :callback_key => event_type,
119
+ :callback_key => callback_key,
107
120
  :data => event_data
108
121
  }
109
122
 
123
+ fire_event(event)
124
+
125
+ if mouse_dragged
126
+ drag_data = {:to => [mouse_x, mouse_y], :from => [@last_click_x, @last_click_y]}
127
+ event[:data] = drag_data
128
+ event[:callback_key] = :mouse_drag
129
+ fire_event(event)
130
+ end
131
+ end
132
+
133
+ def fire_event(event)
110
134
  fire :event_received, event
111
135
 
112
136
  # fix for pause bug?
113
137
  @hooks ||= {}
114
138
  @non_id_hooks ||= {}
115
139
 
116
- event_hooks = @hooks[callback_key]
140
+ event_hooks = @hooks[event[:callback_key]]
141
+ gosu_id = event[:id]
117
142
 
118
143
  unless gosu_id.nil?
119
144
  event_action_hooks = event_hooks[gosu_id] if event_hooks
@@ -124,7 +149,7 @@ class InputManager
124
149
  end
125
150
  end
126
151
 
127
- non_id_event_hooks = @non_id_hooks[callback_key]
152
+ non_id_event_hooks = @non_id_hooks[event[:callback_key]]
128
153
  if non_id_event_hooks
129
154
  for callback in non_id_event_hooks
130
155
  callback.call event
@@ -141,6 +166,8 @@ class InputManager
141
166
  # # will be called on every spacebar key press
142
167
  # end
143
168
  def register_hook(event_class, *event_ids, &block)
169
+ event_class = :down if DOWN_EVENTS.include? event_class
170
+ event_class = :up if UP_EVENTS.include? event_class
144
171
  return unless block_given?
145
172
  listener = eval("self", block.binding)
146
173
  _register_hook listener, event_class, *event_ids, &block
@@ -170,6 +197,8 @@ class InputManager
170
197
  # input_manager.unregister_hook KeyPressed, :space, registered_block
171
198
  # also see InputManager#clear_hooks for clearing many hooks
172
199
  def unregister_hook(event_class, *event_ids, &block)
200
+ event_class = :down if DOWN_EVENTS.include? event_class
201
+ event_class = :up if UP_EVENTS.include? event_class
173
202
  @hooks[event_class] ||= {}
174
203
  for event_id in event_ids
175
204
  @hooks[event_class][event_id] ||= []
@@ -206,13 +235,17 @@ class InputManager
206
235
  end
207
236
  end
208
237
 
209
- # autohook a boolean to be set to true while a key is pressed
210
- def while_key_pressed(key, target, accessor)
211
- _register_hook target, :keyboard_down, key do
212
- target.send "#{accessor}=", true
213
- end
214
- _register_hook target, :keyboard_up, key do
215
- target.send "#{accessor}=", false
238
+ # autohook a boolean to be set to true while any of the keys are pressed
239
+ def while_pressed(id_or_ids, target, accessor)
240
+ ids = [id_or_ids].flatten
241
+ ids.each do |id|
242
+ _register_hook target, :down, id do
243
+ target.send "#{accessor}=", true
244
+ end
245
+
246
+ _register_hook target, :up, id do
247
+ target.send "#{accessor}=", false if (@down_ids.keys & ids).size == 0
248
+ end
216
249
  end
217
250
  end
218
251
 
@@ -230,4 +263,12 @@ class InputManager
230
263
  @paused_non_id_hooks = nil
231
264
  end
232
265
 
266
+ private
267
+ def mouse_x
268
+ @window.mouse_x
269
+ end
270
+ def mouse_y
271
+ @window.mouse_y
272
+ end
273
+
233
274
  end
@@ -275,4 +275,4 @@ module Inflector
275
275
  end
276
276
  end
277
277
 
278
- require 'inflections'
278
+
@@ -0,0 +1,5 @@
1
+ class Range
2
+ def sample
3
+ num = self.min + rand(self.max-self.min)
4
+ end
5
+ end
@@ -1,56 +1,56 @@
1
1
  #--
2
- # Rubygame -- Ruby code and bindings to SDL to facilitate game creation
3
- # Copyright (C) 2004-2007 John Croisant
2
+ # Rubygame -- Ruby code and bindings to SDL to facilitate game creation
3
+ # Copyright (C) 2004-2007 John Croisant
4
4
  #
5
- # This library is free software; you can redistribute it and/or
6
- # modify it under the terms of the GNU Lesser General Public
7
- # License as published by the Free Software Foundation; either
8
- # version 2.1 of the License, or (at your option) any later version.
5
+ # This library is free software; you can redistribute it and/or
6
+ # modify it under the terms of the GNU Lesser General Public
7
+ # License as published by the Free Software Foundation; either
8
+ # version 2.1 of the License, or (at your option) any later version.
9
9
  #
10
- # This library is distributed in the hope that it will be useful,
11
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
- # Lesser General Public License for more details.
10
+ # This library is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
+ # Lesser General Public License for more details.
14
14
  #
15
- # You should have received a copy of the GNU Lesser General Public
16
- # License along with this library; if not, write to the Free Software
17
- # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15
+ # You should have received a copy of the GNU Lesser General Public
16
+ # License along with this library; if not, write to the Free Software
17
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
18
  #++
19
19
 
20
20
  #--
21
21
  # Table of Contents:
22
22
  #
23
23
  # class Rect
24
- # GENERAL:
25
- # initialize
26
- # new_from_object
27
- # to_s
28
- # to_a, to_ary
29
- # []
30
- # ATTRIBUTES:
31
- # x, y, w, h [<- accessors]
32
- # width, height, size
33
- # left, top, right, bottom
34
- # center, centerx, centery
35
- # topleft, topright
36
- # bottomleft, bottomright
37
- # midleft, midtop, midright, midbottom
38
- # UTILITY METHODS:
39
- # clamp, clamp!
40
- # clip, clip!
41
- # collide_hash, collide_hash_all
42
- # collide_array, collide_array_all
43
- # collide_point?
44
- # collide_rect?
45
- # contain?
46
- # inflate, inflate!
47
- # move, move!
48
- # normalize, normalize!
49
- # union, union!
50
- # union_all, union_all!
24
+ # GENERAL:
25
+ # initialize
26
+ # new_from_object
27
+ # to_s
28
+ # to_a, to_ary
29
+ # []
30
+ # ATTRIBUTES:
31
+ # x, y, w, h [<- accessors]
32
+ # width, height, size
33
+ # left, top, right, bottom
34
+ # center, centerx, centery
35
+ # topleft, topright
36
+ # bottomleft, bottomright
37
+ # midleft, midtop, midright, midbottom
38
+ # UTILITY METHODS:
39
+ # clamp, clamp!
40
+ # clip, clip!
41
+ # collide_hash, collide_hash_all
42
+ # collide_array, collide_array_all
43
+ # collide_point?
44
+ # collide_rect?
45
+ # contain?
46
+ # inflate, inflate!
47
+ # move, move!
48
+ # normalize, normalize!
49
+ # union, union!
50
+ # union_all, union_all!
51
51
  #
52
- # class Surface
53
- # make_rect
52
+ # class Surface
53
+ # make_rect
54
54
  #
55
55
  #++
56
56
 
@@ -69,77 +69,77 @@
69
69
  # the area of a Surface to operate on.
70
70
  class Rect < Array
71
71
 
72
- #--
73
- # GENERAL
74
- #++
75
-
76
- # Create a new Rect, attempting to extract its own information from
77
- # the given arguments. The arguments must fall into one of these cases:
78
- #
79
- # - 4 integers +(x, y, w, h)+.
80
- # - 1 Rect or Array containing 4 integers +([x, y, w, h])+.
81
- # - 2 Arrays containing 2 integers each +([x,y], [w,h])+.
82
- # - 1 object with a +rect+ attribute which is a valid Rect object.
83
- #
84
- # All rect core attributes (x,y,w,h) must be integers.
85
- #
86
- def initialize(*argv)
87
- case argv.length
88
- when 1
89
- if argv[0].kind_of? Array; super(argv[0])
90
- elsif argv[0].respond_to? :rect; super(argv[0])
91
- end
92
- when 2
93
- super(argv[0].concat(argv[1]))
94
- when 4
95
- super(argv)
96
- end
97
- return self
98
- end
99
-
100
- # Extract or generate a Rect from the given object, if possible, using the
101
- # following process:
102
- #
103
- # 1. If it's a Rect already, return a duplicate Rect.
104
- # 2. Elsif it's an Array with at least 4 values, make a Rect from it.
105
- # 3. Elsif it has a +rect+ attribute., perform (1) and (2) on that.
106
- # 4. Otherwise, raise TypeError.
107
- #
108
- # See also Surface#make_rect()
109
- def Rect.new_from_object(object)
110
- case(object)
111
- when Rect
112
- return object.dup
113
- when Array
114
- if object.length >= 4
115
- return Rect.new(object)
116
- else
117
- raise(ArgumentError,"Array does not have enough indices to be made into a Rect (%d for 4)."%object.length )
118
- end
119
- else
120
- begin
121
- case(object.rect)
122
- when Rect
123
- return object.rect.dup
124
- when Array
125
- if object.rect.length >= 4
126
- return Rect.new(object.rect)
127
- else
128
- raise(ArgumentError,"Array does not have enough indices to be made into a Rect (%d for 4)."%object.rect.length )
129
- end
130
- end # case object.rect
131
- rescue NoMethodError # if no rect.rect
132
- raise(TypeError,"Object must be a Rect or Array [x,y,w,h], or have an attribute called 'rect'. (Got %s instance.)"%object.class)
133
- end
134
- end # case object
135
- end
136
-
137
-
138
- # Print the Rect in the form "+#<Rect [x,y,w,h]>+"
139
- def to_s; "#<Rect [%s,%s,%s,%s]>"%self; end
140
-
141
- # Print the Rect in the form "+#<Rect:id [x,y,w,h]>+"
142
- def inspect; "#<Rect:#{self.object_id} [%s,%s,%s,%s]>"%self; end
72
+ #--
73
+ # GENERAL
74
+ #++
75
+
76
+ # Create a new Rect, attempting to extract its own information from
77
+ # the given arguments. The arguments must fall into one of these cases:
78
+ #
79
+ # - 4 integers +(x, y, w, h)+.
80
+ # - 1 Rect or Array containing 4 integers +([x, y, w, h])+.
81
+ # - 2 Arrays containing 2 integers each +([x,y], [w,h])+.
82
+ # - 1 object with a +rect+ attribute which is a valid Rect object.
83
+ #
84
+ # All rect core attributes (x,y,w,h) must be integers.
85
+ #
86
+ def initialize(*argv)
87
+ case argv.length
88
+ when 1
89
+ if argv[0].kind_of? Array; super(argv[0])
90
+ elsif argv[0].respond_to? :rect; super(argv[0])
91
+ end
92
+ when 2
93
+ super(argv[0].concat(argv[1]))
94
+ when 4
95
+ super(argv)
96
+ end
97
+ return self
98
+ end
99
+
100
+ # Extract or generate a Rect from the given object, if possible, using the
101
+ # following process:
102
+ #
103
+ # 1. If it's a Rect already, return a duplicate Rect.
104
+ # 2. Elsif it's an Array with at least 4 values, make a Rect from it.
105
+ # 3. Elsif it has a +rect+ attribute., perform (1) and (2) on that.
106
+ # 4. Otherwise, raise TypeError.
107
+ #
108
+ # See also Surface#make_rect()
109
+ def Rect.new_from_object(object)
110
+ case(object)
111
+ when Rect
112
+ return object.dup
113
+ when Array
114
+ if object.length >= 4
115
+ return Rect.new(object)
116
+ else
117
+ raise(ArgumentError,"Array does not have enough indices to be made into a Rect (%d for 4)."%object.length )
118
+ end
119
+ else
120
+ begin
121
+ case(object.rect)
122
+ when Rect
123
+ return object.rect.dup
124
+ when Array
125
+ if object.rect.length >= 4
126
+ return Rect.new(object.rect)
127
+ else
128
+ raise(ArgumentError,"Array does not have enough indices to be made into a Rect (%d for 4)."%object.rect.length )
129
+ end
130
+ end # case object.rect
131
+ rescue NoMethodError # if no rect.rect
132
+ raise(TypeError,"Object must be a Rect or Array [x,y,w,h], or have an attribute called 'rect'. (Got %s instance.)"%object.class)
133
+ end
134
+ end # case object
135
+ end
136
+
137
+
138
+ # Print the Rect in the form "+#<Rect [x,y,w,h]>+"
139
+ def to_s; "#<Rect [%s,%s,%s,%s]>"%self; end
140
+
141
+ # Print the Rect in the form "+#<Rect:id [x,y,w,h]>+"
142
+ def inspect; "#<Rect:#{self.object_id} [%s,%s,%s,%s]>"%self; end
143
143
 
144
144
 
145
145
  # Returns an SDL::Rect version of this Rect. Float values are
@@ -150,461 +150,461 @@ class Rect < Array
150
150
  end
151
151
 
152
152
 
153
- #--
154
- # ATTRIBUTES
155
- #++
153
+ #--
154
+ # ATTRIBUTES
155
+ #++
156
+
157
+ # Returns self.at(0)
158
+ def x; return self.at(0); end
159
+ # Sets self[0] to +val+
160
+ def x=(val); self[0] = val; end
161
+
162
+ alias left x
163
+ alias left= x=;
164
+ alias l x
165
+ alias l= x=;
166
+
167
+ # Returns self.at(1)
168
+ def y; return self.at(1); end
169
+ # Sets self[1] to +val+
170
+ def y=(val); self[1] = val; end
171
+
172
+ alias top y
173
+ alias top= y=;
174
+ alias t y
175
+ alias t= y=;
176
+
177
+ # Returns self.at(2)
178
+ def w; return self.at(2); end
179
+ # Sets self[2] to +val+
180
+ def w=(val); self[2] = val; end
181
+
182
+ alias width w
183
+ alias width= w=;
184
+
185
+ # Returns self.at(3)
186
+ def h; return self.at(3); end
187
+ # Sets self[3] to +val+
188
+ def h=(val); self[3] = val; end
189
+
190
+ alias height h
191
+ alias height= h=;
192
+
193
+ # Return the width and height of the Rect.
194
+ def size; return self[2,2]; end
195
+
196
+ # Set the width and height of the Rect.
197
+ def size=(size)
198
+ raise ArgumentError, "Rect#size= takes an Array of form [width, height]." if size.size != 2
199
+ self[2,2] = size
200
+ size
201
+ end
202
+
203
+ # Return the x coordinate of the right side of the Rect.
204
+ def right; return self.at(0)+self.at(2); end
205
+
206
+ # Set the x coordinate of the right side of the Rect by translating the
207
+ # Rect (adjusting the x offset).
208
+ def right=(r); self[0] = r - self.at(2); return r; end
156
209
 
157
- # Returns self.at(0)
158
- def x; return self.at(0); end
159
- # Sets self[0] to +val+
160
- def x=(val); self[0] = val; end
210
+ alias r right
211
+ alias r= right=;
161
212
 
162
- alias left x
163
- alias left= x=;
164
- alias l x
165
- alias l= x=;
213
+ # Return the y coordinate of the bottom side of the Rect.
214
+ def bottom; return self.at(1)+self.at(3); end
166
215
 
167
- # Returns self.at(1)
168
- def y; return self.at(1); end
169
- # Sets self[1] to +val+
170
- def y=(val); self[1] = val; end
216
+ # Set the y coordinate of the bottom side of the Rect by translating the
217
+ # Rect (adjusting the y offset).
218
+ def bottom=(b); self[1] = b - self.at(3); return b; end
171
219
 
172
- alias top y
173
- alias top= y=;
174
- alias t y
175
- alias t= y=;
220
+ alias b bottom
221
+ alias b= bottom=;
176
222
 
177
- # Returns self.at(2)
178
- def w; return self.at(2); end
179
- # Sets self[2] to +val+
180
- def w=(val); self[2] = val; end
223
+ # Return the x and y coordinates of the center of the Rect.
224
+ def center; return self.centerx, self.centery; end
181
225
 
182
- alias width w
183
- alias width= w=;
226
+ # Set the x and y coordinates of the center of the Rect by translating the
227
+ # Rect (adjusting the x and y offsets).
228
+ def center=(center)
229
+ raise ArgumentError, "Rect#center= takes an Array of the form [x,y]." if center.size != 2
230
+ self.centerx, self.centery = center
231
+ center
232
+ end
233
+ alias c center
234
+ alias c= center=;
184
235
 
185
- # Returns self.at(3)
186
- def h; return self.at(3); end
187
- # Sets self[3] to +val+
188
- def h=(val); self[3] = val; end
236
+ # Return the x coordinate of the center of the Rect
237
+ def centerx; return self.at(0)+(self.at(2).div(2)); end
189
238
 
190
- alias height h
191
- alias height= h=;
239
+ # Set the x coordinate of the center of the Rect by translating the
240
+ # Rect (adjusting the x offset).
241
+ def centerx=(x); self[0] = x - (self.at(2).div(2)); return x; end
192
242
 
193
- # Return the width and height of the Rect.
194
- def size; return self[2,2]; end
243
+ alias cx centerx
244
+ alias cx= centerx=;
195
245
 
196
- # Set the width and height of the Rect.
197
- def size=(size)
198
- raise ArgumentError, "Rect#size= takes an Array of form [width, height]." if size.size != 2
199
- self[2,2] = size
200
- size
201
- end
246
+ # Return the y coordinate of the center of the Rect
247
+ def centery; return self.at(1)+(self.at(3).div(2)); end
248
+
249
+ # Set the y coordinate of the center of the Rect by translating the
250
+ # Rect (adjusting the y offset).
251
+ def centery=(y); self[1] = y- (self.at(3).div(2)); return y; end
252
+
253
+ alias cy centery
254
+ alias cy= centery=;
255
+
256
+ # Return the x and y coordinates of the top-left corner of the Rect
257
+ def topleft; return self[0,2].to_a; end
258
+
259
+ # Set the x and y coordinates of the top-left corner of the Rect by
260
+ # translating the Rect (adjusting the x and y offsets).
261
+ def topleft=(topleft)
262
+ raise ArgumentError, "Rect#topright= takes an Array of form [x, y]." if topleft.size != 2
263
+ self[0,2] = topleft
264
+ return topleft
265
+ end
266
+
267
+ alias tl topleft
268
+ alias tl= topleft=;
269
+
270
+ # Return the x and y coordinates of the top-right corner of the Rect
271
+ def topright; return self.right, self.at(1); end
272
+
273
+ # Set the x and y coordinates of the top-right corner of the Rect by
274
+ # translating the Rect (adjusting the x and y offsets).
275
+ def topright=(topright)
276
+ raise ArgumentError, "Rect#topright= takes an Array of form [x, y]." if topright.size != 2
277
+ self.right, self[1] = topright
278
+ return topright
279
+ end
280
+
281
+ alias tr topright
282
+ alias tr= topright=;
283
+
284
+ # Return the x and y coordinates of the bottom-left corner of the Rect
285
+ def bottomleft; return self.at(0), self.bottom; end
286
+
287
+ # Set the x and y coordinates of the bottom-left corner of the Rect by
288
+ # translating the Rect (adjusting the x and y offsets).
289
+ def bottomleft=(bottomleft)
290
+ raise ArgumentError, "Rect#bottomleft= takes an Array of form [x, y]." if bottomleft.size != 2
291
+ self[0], self.bottom = bottomleft
292
+ return bottomleft
293
+ end
294
+
295
+ alias bl bottomleft
296
+ alias bl= bottomleft=;
297
+
298
+ # Return the x and y coordinates of the bottom-right corner of the Rect
299
+ def bottomright; return self.right, self.bottom; end
300
+
301
+ # Set the x and y coordinates of the bottom-right corner of the Rect by
302
+ # translating the Rect (adjusting the x and y offsets).
303
+ def bottomright=(bottomright)
304
+ raise ArgumentError, "Rect#bottomright= takes an Array of form [x, y]." if bottomright.size != 2
305
+ self.right, self.bottom = bottomright
306
+ return bottomright
307
+ end
308
+
309
+ alias br bottomright
310
+ alias br= bottomright=;
311
+
312
+ # Return the x and y coordinates of the midpoint on the left side of the
313
+ # Rect.
314
+ def midleft; return self.at(0), self.centery; end
315
+
316
+ # Set the x and y coordinates of the midpoint on the left side of the Rect
317
+ # by translating the Rect (adjusting the x and y offsets).
318
+ def midleft=(midleft)
319
+ raise ArgumentError, "Rect#midleft= takes an Array of form [x, y]." if midleft.size != 2
320
+ self[0], self.centery = midleft
321
+ return midleft
322
+ end
323
+
324
+ alias ml midleft
325
+ alias ml= midleft=;
326
+
327
+ # Return the x and y coordinates of the midpoint on the left side of the
328
+ # Rect.
329
+ def midtop; return self.centerx, self.at(1); end
330
+
331
+ # Set the x and y coordinates of the midpoint on the top side of the Rect
332
+ # by translating the Rect (adjusting the x and y offsets).
333
+ def midtop=(midtop)
334
+ raise ArgumentError, "Rect#midtop= takes an Array of form [x, y]." if midtop.size != 2
335
+ self.centerx, self[1] = midtop
336
+ return midtop
337
+ end
202
338
 
203
- # Return the x coordinate of the right side of the Rect.
204
- def right; return self.at(0)+self.at(2); end
205
-
206
- # Set the x coordinate of the right side of the Rect by translating the
207
- # Rect (adjusting the x offset).
208
- def right=(r); self[0] = r - self.at(2); return r; end
209
-
210
- alias r right
211
- alias r= right=;
339
+ alias mt midtop
340
+ alias mt= midtop=;
212
341
 
213
- # Return the y coordinate of the bottom side of the Rect.
214
- def bottom; return self.at(1)+self.at(3); end
342
+ # Return the x and y coordinates of the midpoint on the left side of the
343
+ # Rect.
344
+ def midright; return self.right, self.centery; end
215
345
 
216
- # Set the y coordinate of the bottom side of the Rect by translating the
217
- # Rect (adjusting the y offset).
218
- def bottom=(b); self[1] = b - self.at(3); return b; end
219
-
220
- alias b bottom
221
- alias b= bottom=;
222
-
223
- # Return the x and y coordinates of the center of the Rect.
224
- def center; return self.centerx, self.centery; end
225
-
226
- # Set the x and y coordinates of the center of the Rect by translating the
227
- # Rect (adjusting the x and y offsets).
228
- def center=(center)
229
- raise ArgumentError, "Rect#center= takes an Array of the form [x,y]." if center.size != 2
230
- self.centerx, self.centery = center
231
- center
232
- end
233
- alias c center
234
- alias c= center=;
235
-
236
- # Return the x coordinate of the center of the Rect
237
- def centerx; return self.at(0)+(self.at(2).div(2)); end
238
-
239
- # Set the x coordinate of the center of the Rect by translating the
240
- # Rect (adjusting the x offset).
241
- def centerx=(x); self[0] = x - (self.at(2).div(2)); return x; end
242
-
243
- alias cx centerx
244
- alias cx= centerx=;
245
-
246
- # Return the y coordinate of the center of the Rect
247
- def centery; return self.at(1)+(self.at(3).div(2)); end
248
-
249
- # Set the y coordinate of the center of the Rect by translating the
250
- # Rect (adjusting the y offset).
251
- def centery=(y); self[1] = y- (self.at(3).div(2)); return y; end
252
-
253
- alias cy centery
254
- alias cy= centery=;
255
-
256
- # Return the x and y coordinates of the top-left corner of the Rect
257
- def topleft; return self[0,2].to_a; end
258
-
259
- # Set the x and y coordinates of the top-left corner of the Rect by
260
- # translating the Rect (adjusting the x and y offsets).
261
- def topleft=(topleft)
262
- raise ArgumentError, "Rect#topright= takes an Array of form [x, y]." if topleft.size != 2
263
- self[0,2] = topleft
264
- return topleft
265
- end
266
-
267
- alias tl topleft
268
- alias tl= topleft=;
269
-
270
- # Return the x and y coordinates of the top-right corner of the Rect
271
- def topright; return self.right, self.at(1); end
272
-
273
- # Set the x and y coordinates of the top-right corner of the Rect by
274
- # translating the Rect (adjusting the x and y offsets).
275
- def topright=(topright)
276
- raise ArgumentError, "Rect#topright= takes an Array of form [x, y]." if topright.size != 2
277
- self.right, self[1] = topright
278
- return topright
279
- end
280
-
281
- alias tr topright
282
- alias tr= topright=;
283
-
284
- # Return the x and y coordinates of the bottom-left corner of the Rect
285
- def bottomleft; return self.at(0), self.bottom; end
286
-
287
- # Set the x and y coordinates of the bottom-left corner of the Rect by
288
- # translating the Rect (adjusting the x and y offsets).
289
- def bottomleft=(bottomleft)
290
- raise ArgumentError, "Rect#bottomleft= takes an Array of form [x, y]." if bottomleft.size != 2
291
- self[0], self.bottom = bottomleft
292
- return bottomleft
293
- end
294
-
295
- alias bl bottomleft
296
- alias bl= bottomleft=;
297
-
298
- # Return the x and y coordinates of the bottom-right corner of the Rect
299
- def bottomright; return self.right, self.bottom; end
300
-
301
- # Set the x and y coordinates of the bottom-right corner of the Rect by
302
- # translating the Rect (adjusting the x and y offsets).
303
- def bottomright=(bottomright)
304
- raise ArgumentError, "Rect#bottomright= takes an Array of form [x, y]." if bottomright.size != 2
305
- self.right, self.bottom = bottomright
306
- return bottomright
307
- end
308
-
309
- alias br bottomright
310
- alias br= bottomright=;
311
-
312
- # Return the x and y coordinates of the midpoint on the left side of the
313
- # Rect.
314
- def midleft; return self.at(0), self.centery; end
315
-
316
- # Set the x and y coordinates of the midpoint on the left side of the Rect
317
- # by translating the Rect (adjusting the x and y offsets).
318
- def midleft=(midleft)
319
- raise ArgumentError, "Rect#midleft= takes an Array of form [x, y]." if midleft.size != 2
320
- self[0], self.centery = midleft
321
- return midleft
322
- end
323
-
324
- alias ml midleft
325
- alias ml= midleft=;
326
-
327
- # Return the x and y coordinates of the midpoint on the left side of the
328
- # Rect.
329
- def midtop; return self.centerx, self.at(1); end
330
-
331
- # Set the x and y coordinates of the midpoint on the top side of the Rect
332
- # by translating the Rect (adjusting the x and y offsets).
333
- def midtop=(midtop)
334
- raise ArgumentError, "Rect#midtop= takes an Array of form [x, y]." if midtop.size != 2
335
- self.centerx, self[1] = midtop
336
- return midtop
337
- end
338
-
339
- alias mt midtop
340
- alias mt= midtop=;
341
-
342
- # Return the x and y coordinates of the midpoint on the left side of the
343
- # Rect.
344
- def midright; return self.right, self.centery; end
345
-
346
- # Set the x and y coordinates of the midpoint on the right side of the Rect
347
- # by translating the Rect (adjusting the x and y offsets).
348
- def midright=(midright)
349
- raise ArgumentError, "Rect#midright= takes an Array of form [x, y]." if midright.size != 2
350
- self.right, self.centery = midright
351
- return midright
352
- end
353
-
354
- alias mr midright
355
- alias mr= midright=;
356
-
357
- # Return the x and y coordinates of the midpoint on the left side of the
358
- # Rect.
359
- def midbottom; return self.centerx, self.bottom; end
360
-
361
- # Set the x and y coordinates of the midpoint on the bottom side of the
362
- # Rect by translating the Rect (adjusting the x and y offsets).
363
- def midbottom=(midbottom)
364
- raise ArgumentError, "Rect#midbottom= takes an Array of form [x, y]." if midbottom.size != 2
365
- self.centerx, self.bottom = midbottom
366
- return midbottom
367
- end
368
-
369
- alias mb midbottom
370
- alias mb= midbottom=;
371
-
372
- #--
373
- # UTILITY METHODS
374
- #++
375
-
376
-
377
- # As #clamp!, but the original caller is not changed.
378
- def clamp(rect)
379
- self.dup.clamp!(rect)
380
- end
381
-
382
- # Translate the calling Rect to be entirely inside the given Rect. If
383
- # the caller is too large along either axis to fit in the given rect,
384
- # it is centered with respect to the given rect, along that axis.
385
- def clamp!(rect)
386
- nself = self.normalize
387
- rect = Rect.new_from_object(rect)
388
- #If self is inside given, there is no need to move self
389
- unless rect.contain?(nself)
390
-
391
- #If self is too wide:
392
- if nself.at(2) >= rect.at(2)
393
- self[0] = rect.centerx - nself.at(2).div(2)
394
- #Else self is not too wide
395
- else
396
- #If self is to the left of arg
397
- if nself.at(0) < rect.at(0)
398
- self[0] = rect.at(0)
399
- #If self is to the right of arg
400
- elsif nself.right > rect.right
401
- self[0] = rect.right - nself.at(2)
402
- #Otherwise, leave x alone
403
- end
404
- end
405
-
406
- #If self is too tall:
407
- if nself.at(3) >= rect.at(3)
408
- self[1] = rect.centery - nself.at(3).div(2)
409
- #Else self is not too tall
410
- else
411
- #If self is above arg
412
- if nself.at(1) < rect.at(1)
413
- self[1] = rect.at(1)
414
- #If self below arg
415
- elsif nself.bottom > rect.bottom
416
- self[1] = rect.bottom - nself.at(3)
417
- #Otherwise, leave y alone
418
- end
419
- end
420
- end
421
- return self
422
- end
423
-
424
- # As #clip!, but the original caller is not changed.
425
- def clip(rect)
426
- self.dup.clip!(rect)
427
- end
428
-
429
- # Crop the calling Rect to be entirely inside the given Rect. If the
430
- # caller does not intersect the given Rect at all, its width and height
431
- # are set to zero, but its x and y offsets are not changed.
432
- #
433
- # As a side effect, the Rect is normalized.
434
- def clip!(rect)
435
- nself = self.normalize
436
- other = Rect.new_from_object(rect).normalize!
437
- if self.collide_rect?(other)
438
- self[0] = [nself.at(0), other.at(0)].max
439
- self[1] = [nself.at(1), other.at(1)].max
440
- self[2] = [nself.right, other.right].min - self.at(0)
441
- self[3] = [nself.bottom, other.bottom].min - self.at(1)
442
- else #if they do not intersect at all:
443
- self[0], self[1] = nself.topleft
444
- self[2], self[3] = 0, 0
445
- end
446
- return self
447
- end
448
-
449
- # Iterate through all key/value pairs in the given hash table, and
450
- # return the first pair whose value is a Rect that collides with the
451
- # caller.
452
- #
453
- # Because a hash table is unordered, you should not expect any
454
- # particular Rect to be returned first.
455
- def collide_hash(hash_rects)
456
- hash_rects.each { |key,value|
457
- if value.collide_rect?+(self); return [key,value]; end
458
- }
459
- return nil
460
- end
461
-
462
- # Iterate through all key/value pairs in the given hash table, and
463
- # return an Array of every pair whose value is a Rect that collides
464
- # the caller.
465
- #
466
- # Because a hash table is unordered, you should not expect the returned
467
- # pairs to be in any particular order.
468
- def collide_hash_all(hash_rects)
469
- hash_rects.select { |key,value|
470
- value.collide_rect?+(self)
471
- }
472
- end
473
-
474
- # Iterate through all elements in the given Array, and return
475
- # the *index* of the first element which is a Rect that collides with
476
- # the caller.
477
- def collide_array(array_rects)
478
- for i in (0...(array_rects.length))
479
- if array_rects[i].collide_rect?(self)
480
- return i
481
- end
482
- end
483
- return nil
484
- end
485
-
486
- # Iterate through all elements in the given Array, and return
487
- # an Array containing the *indices* of every element that is a Rect
488
- # that collides with the caller.
489
- def collide_array_all(array_rects)
490
- indexes = []
491
- for i in (0...(array_rects.length))
492
- if array_rects[i].collide_rect?(self)
493
- indexes += [i]
494
- end
495
- end
496
- return indexes
497
- end
498
-
499
- # True if the point is inside (including on the border) of the caller.
500
- # If you have Array of coordinates, you can use collide_point?(*coords).
501
- def collide_point?(x,y)
502
- nself = normalize()
503
- x.between?(nself.left,nself.right) && y.between?(nself.top,nself.bottom)
504
- end
505
-
506
- # True if the caller and the given Rect overlap (or touch) at all.
507
- def collide_rect?(rect)
508
- nself = self.normalize
509
- rect = Rect.new_from_object(rect).normalize!
510
- return ((nself.l >= rect.l && nself.l <= rect.r) or (rect.l >= nself.l && rect.l <= nself.r)) &&
511
- ((nself.t >= rect.t && nself.t <= rect.b) or (rect.t >= nself.t && rect.t <= nself.b))
512
- end
513
-
514
- # True if the given Rect is totally within the caller. Borders may
515
- # overlap.
516
- def contain?(rect)
517
- nself = self.normalize
518
- rect = Rect.new_from_object(rect).normalize!
519
- return (nself.left <= rect.left and rect.right <= nself.right and
520
- nself.top <= rect.top and rect.bottom <= nself.bottom)
521
- end
522
-
523
- # As #inflate!, but the original caller is not changed.
524
- def inflate(x,y)
525
- return self.class.new(self.at(0) - x.div(2),
526
- self.at(1) - y.div(2),
527
- self.at(2) + x,
528
- self.at(3) + y)
529
- end
530
-
531
- # Increase the Rect's size is the x and y directions, while keeping the
532
- # same center point. For best results, expand by an even number.
533
- # X and y inflation can be given as an Array or as separate values.
534
- def inflate!(x,y)
535
- self[0] -= x.div(2)
536
- self[1] -= y.div(2)
537
- self[2] += x
538
- self[3] += y
539
- return self
540
- end
541
-
542
- # As #move!, but the original caller is not changed.
543
- def move(x,y)
544
- self.dup.move!(x,y)
545
- end
546
-
547
- # Translate the Rect by the given amounts in the x and y directions.
548
- # Positive values are rightward for x and downward for y.
549
- # X and y movement can be given as an Array or as separate values.
550
- def move!(x,y)
551
- self[0]+=x; self[1]+=y
552
- return self
553
- end
554
-
555
- # As #normalize!, but the original caller is not changed.
556
- def normalize
557
- self.dup.normalize!()
558
- end
559
-
560
- # Fix Rects that have negative width or height, without changing the
561
- # area it represents. Has no effect on Rects with non-negative width
562
- # and height. Some Rect methods will automatically normalize the Rect.
563
- def normalize!
564
- if self.at(2) < 0
565
- self[0], self[2] = self.at(0)+self.at(2), -self.at(2)
566
- end
567
- if self.at(3) < 0
568
- self[1], self[3] = self.at(1)+self.at(3), -self.at(3)
569
- end
570
- self
571
- end
572
-
573
- # As #union!, but the original caller is not changed.
574
- def union(rect)
575
- self.dup.union!(rect)
576
- end
577
-
578
- # Expand the caller to also cover the given Rect. The Rect is still a
579
- # rectangle, so it may also cover areas that neither of the original
580
- # Rects did, for example areas between the two Rects.
581
- def union!(rect)
582
- self.normalize!
346
+ # Set the x and y coordinates of the midpoint on the right side of the Rect
347
+ # by translating the Rect (adjusting the x and y offsets).
348
+ def midright=(midright)
349
+ raise ArgumentError, "Rect#midright= takes an Array of form [x, y]." if midright.size != 2
350
+ self.right, self.centery = midright
351
+ return midright
352
+ end
353
+
354
+ alias mr midright
355
+ alias mr= midright=;
356
+
357
+ # Return the x and y coordinates of the midpoint on the left side of the
358
+ # Rect.
359
+ def midbottom; return self.centerx, self.bottom; end
360
+
361
+ # Set the x and y coordinates of the midpoint on the bottom side of the
362
+ # Rect by translating the Rect (adjusting the x and y offsets).
363
+ def midbottom=(midbottom)
364
+ raise ArgumentError, "Rect#midbottom= takes an Array of form [x, y]." if midbottom.size != 2
365
+ self.centerx, self.bottom = midbottom
366
+ return midbottom
367
+ end
368
+
369
+ alias mb midbottom
370
+ alias mb= midbottom=;
371
+
372
+ #--
373
+ # UTILITY METHODS
374
+ #++
375
+
376
+
377
+ # As #clamp!, but the original caller is not changed.
378
+ def clamp(rect)
379
+ self.dup.clamp!(rect)
380
+ end
381
+
382
+ # Translate the calling Rect to be entirely inside the given Rect. If
383
+ # the caller is too large along either axis to fit in the given rect,
384
+ # it is centered with respect to the given rect, along that axis.
385
+ def clamp!(rect)
386
+ nself = self.normalize
387
+ rect = Rect.new_from_object(rect)
388
+ #If self is inside given, there is no need to move self
389
+ unless rect.contain?(nself)
390
+
391
+ #If self is too wide:
392
+ if nself.at(2) >= rect.at(2)
393
+ self[0] = rect.centerx - nself.at(2).div(2)
394
+ #Else self is not too wide
395
+ else
396
+ #If self is to the left of arg
397
+ if nself.at(0) < rect.at(0)
398
+ self[0] = rect.at(0)
399
+ #If self is to the right of arg
400
+ elsif nself.right > rect.right
401
+ self[0] = rect.right - nself.at(2)
402
+ #Otherwise, leave x alone
403
+ end
404
+ end
405
+
406
+ #If self is too tall:
407
+ if nself.at(3) >= rect.at(3)
408
+ self[1] = rect.centery - nself.at(3).div(2)
409
+ #Else self is not too tall
410
+ else
411
+ #If self is above arg
412
+ if nself.at(1) < rect.at(1)
413
+ self[1] = rect.at(1)
414
+ #If self below arg
415
+ elsif nself.bottom > rect.bottom
416
+ self[1] = rect.bottom - nself.at(3)
417
+ #Otherwise, leave y alone
418
+ end
419
+ end
420
+ end
421
+ return self
422
+ end
423
+
424
+ # As #clip!, but the original caller is not changed.
425
+ def clip(rect)
426
+ self.dup.clip!(rect)
427
+ end
428
+
429
+ # Crop the calling Rect to be entirely inside the given Rect. If the
430
+ # caller does not intersect the given Rect at all, its width and height
431
+ # are set to zero, but its x and y offsets are not changed.
432
+ #
433
+ # As a side effect, the Rect is normalized.
434
+ def clip!(rect)
435
+ nself = self.normalize
436
+ other = Rect.new_from_object(rect).normalize!
437
+ if self.collide_rect?(other)
438
+ self[0] = [nself.at(0), other.at(0)].max
439
+ self[1] = [nself.at(1), other.at(1)].max
440
+ self[2] = [nself.right, other.right].min - self.at(0)
441
+ self[3] = [nself.bottom, other.bottom].min - self.at(1)
442
+ else #if they do not intersect at all:
443
+ self[0], self[1] = nself.topleft
444
+ self[2], self[3] = 0, 0
445
+ end
446
+ return self
447
+ end
448
+
449
+ # Iterate through all key/value pairs in the given hash table, and
450
+ # return the first pair whose value is a Rect that collides with the
451
+ # caller.
452
+ #
453
+ # Because a hash table is unordered, you should not expect any
454
+ # particular Rect to be returned first.
455
+ def collide_hash(hash_rects)
456
+ hash_rects.each { |key,value|
457
+ if value.collide_rect?+(self); return [key,value]; end
458
+ }
459
+ return nil
460
+ end
461
+
462
+ # Iterate through all key/value pairs in the given hash table, and
463
+ # return an Array of every pair whose value is a Rect that collides
464
+ # the caller.
465
+ #
466
+ # Because a hash table is unordered, you should not expect the returned
467
+ # pairs to be in any particular order.
468
+ def collide_hash_all(hash_rects)
469
+ hash_rects.select { |key,value|
470
+ value.collide_rect?+(self)
471
+ }
472
+ end
473
+
474
+ # Iterate through all elements in the given Array, and return
475
+ # the *index* of the first element which is a Rect that collides with
476
+ # the caller.
477
+ def collide_array(array_rects)
478
+ for i in (0...(array_rects.length))
479
+ if array_rects[i].collide_rect?(self)
480
+ return i
481
+ end
482
+ end
483
+ return nil
484
+ end
485
+
486
+ # Iterate through all elements in the given Array, and return
487
+ # an Array containing the *indices* of every element that is a Rect
488
+ # that collides with the caller.
489
+ def collide_array_all(array_rects)
490
+ indexes = []
491
+ for i in (0...(array_rects.length))
492
+ if array_rects[i].collide_rect?(self)
493
+ indexes += [i]
494
+ end
495
+ end
496
+ return indexes
497
+ end
498
+
499
+ # True if the point is inside (including on the border) of the caller.
500
+ # If you have Array of coordinates, you can use collide_point?(*coords).
501
+ def collide_point?(x,y)
502
+ nself = normalize()
503
+ x.between?(nself.left,nself.right) && y.between?(nself.top,nself.bottom)
504
+ end
505
+
506
+ # True if the caller and the given Rect overlap (or touch) at all.
507
+ def collide_rect?(rect)
508
+ nself = self.normalize
509
+ rect = Rect.new_from_object(rect).normalize!
510
+ return ((nself.l >= rect.l && nself.l <= rect.r) or (rect.l >= nself.l && rect.l <= nself.r)) &&
511
+ ((nself.t >= rect.t && nself.t <= rect.b) or (rect.t >= nself.t && rect.t <= nself.b))
512
+ end
513
+
514
+ # True if the given Rect is totally within the caller. Borders may
515
+ # overlap.
516
+ def contain?(rect)
517
+ nself = self.normalize
518
+ rect = Rect.new_from_object(rect).normalize!
519
+ return (nself.left <= rect.left and rect.right <= nself.right and
520
+ nself.top <= rect.top and rect.bottom <= nself.bottom)
521
+ end
522
+
523
+ # As #inflate!, but the original caller is not changed.
524
+ def inflate(x,y)
525
+ return self.class.new(self.at(0) - x.div(2),
526
+ self.at(1) - y.div(2),
527
+ self.at(2) + x,
528
+ self.at(3) + y)
529
+ end
530
+
531
+ # Increase the Rect's size is the x and y directions, while keeping the
532
+ # same center point. For best results, expand by an even number.
533
+ # X and y inflation can be given as an Array or as separate values.
534
+ def inflate!(x,y)
535
+ self[0] -= x.div(2)
536
+ self[1] -= y.div(2)
537
+ self[2] += x
538
+ self[3] += y
539
+ return self
540
+ end
541
+
542
+ # As #move!, but the original caller is not changed.
543
+ def move(x,y)
544
+ self.dup.move!(x,y)
545
+ end
546
+
547
+ # Translate the Rect by the given amounts in the x and y directions.
548
+ # Positive values are rightward for x and downward for y.
549
+ # X and y movement can be given as an Array or as separate values.
550
+ def move!(x,y)
551
+ self[0]+=x; self[1]+=y
552
+ return self
553
+ end
554
+
555
+ # As #normalize!, but the original caller is not changed.
556
+ def normalize
557
+ self.dup.normalize!()
558
+ end
559
+
560
+ # Fix Rects that have negative width or height, without changing the
561
+ # area it represents. Has no effect on Rects with non-negative width
562
+ # and height. Some Rect methods will automatically normalize the Rect.
563
+ def normalize!
564
+ if self.at(2) < 0
565
+ self[0], self[2] = self.at(0)+self.at(2), -self.at(2)
566
+ end
567
+ if self.at(3) < 0
568
+ self[1], self[3] = self.at(1)+self.at(3), -self.at(3)
569
+ end
570
+ self
571
+ end
572
+
573
+ # As #union!, but the original caller is not changed.
574
+ def union(rect)
575
+ self.dup.union!(rect)
576
+ end
577
+
578
+ # Expand the caller to also cover the given Rect. The Rect is still a
579
+ # rectangle, so it may also cover areas that neither of the original
580
+ # Rects did, for example areas between the two Rects.
581
+ def union!(rect)
582
+ self.normalize!
583
583
  rleft, rtop = self.topleft
584
584
  rright, rbottom = self.bottomright
585
- r2 = Rect.new_from_object(rect).normalize!
585
+ r2 = Rect.new_from_object(rect).normalize!
586
586
 
587
- rleft = [rleft, r2.left].min
588
- rtop = [rtop, r2.top].min
589
- rright = [rright, r2.right].max
590
- rbottom = [rbottom, r2.bottom].max
587
+ rleft = [rleft, r2.left].min
588
+ rtop = [rtop, r2.top].min
589
+ rright = [rright, r2.right].max
590
+ rbottom = [rbottom, r2.bottom].max
591
591
 
592
- self[0,4] = rleft, rtop, rright - rleft, rbottom - rtop
593
- return self
594
- end
592
+ self[0,4] = rleft, rtop, rright - rleft, rbottom - rtop
593
+ return self
594
+ end
595
595
 
596
- # As #union_all!, but the original caller is not changed.
597
- def union_all(array_rects)
598
- self.dup.union_all!(array_rects)
599
- end
596
+ # As #union_all!, but the original caller is not changed.
597
+ def union_all(array_rects)
598
+ self.dup.union_all!(array_rects)
599
+ end
600
600
 
601
- # Expand the caller to cover all of the given Rects. See also #union!
602
- def union_all!(array_rects)
603
- array_rects.each do |r|
601
+ # Expand the caller to cover all of the given Rects. See also #union!
602
+ def union_all!(array_rects)
603
+ array_rects.each do |r|
604
604
  self.union!(r)
605
- end
606
- return self
607
- end
605
+ end
606
+ return self
607
+ end
608
608
 
609
609
 
610
610
  end # class Rect