gamebox 0.2.1 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
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