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.
- data/Gemfile +1 -8
- data/Rakefile +13 -37
- data/TODO.txt +27 -27
- data/docs/getting_started.rdoc +2 -2
- data/gamebox.gemspec +33 -191
- data/lib/gamebox.rb +18 -14
- data/lib/gamebox/actor.rb +37 -27
- data/lib/gamebox/actor_factory.rb +4 -5
- data/lib/gamebox/actor_view.rb +8 -0
- data/lib/gamebox/actors/collidable_debugger.rb +2 -2
- data/lib/gamebox/actors/curtain.rb +3 -3
- data/lib/gamebox/actors/emitter.rb +51 -0
- data/lib/gamebox/actors/label.rb +27 -7
- data/lib/gamebox/actors/logo.rb +1 -1
- data/lib/gamebox/actors/spatial_debugger.rb +25 -10
- data/lib/gamebox/arbiter.rb +61 -34
- data/lib/gamebox/behavior.rb +3 -3
- data/lib/gamebox/behaviors/animated.rb +1 -1
- data/lib/gamebox/behaviors/audible.rb +1 -1
- data/lib/gamebox/behaviors/collidable.rb +9 -4
- data/lib/gamebox/behaviors/collidable/aabb_collidable.rb +26 -1
- data/lib/gamebox/behaviors/collidable/circle_collidable.rb +3 -3
- data/lib/gamebox/behaviors/collidable/polygon_collidable.rb +1 -1
- data/lib/gamebox/behaviors/graphical.rb +30 -4
- data/lib/gamebox/behaviors/layered.rb +1 -1
- data/lib/gamebox/behaviors/physical.rb +113 -30
- data/lib/gamebox/behaviors/timed.rb +33 -0
- data/lib/gamebox/behaviors/updatable.rb +1 -1
- data/lib/gamebox/class_finder.rb +1 -21
- data/lib/gamebox/console_app.rb +33 -31
- data/lib/gamebox/constants.rb +481 -0
- data/lib/gamebox/data/config/objects.yml +7 -0
- data/lib/gamebox/gamebox_application.rb +10 -33
- data/lib/gamebox/gamebox_generator.rb +32 -32
- data/lib/gamebox/input_manager.rb +73 -32
- data/lib/gamebox/lib/inflector.rb +1 -1
- data/lib/gamebox/lib/range_ext.rb +5 -0
- data/lib/gamebox/lib/rect.rb +548 -548
- data/lib/gamebox/lib/sorted_list.rb +1 -1
- data/lib/gamebox/lib/symbol_ext.rb +8 -0
- data/lib/gamebox/physical_director.rb +1 -1
- data/lib/gamebox/physical_stage.rb +3 -3
- data/lib/gamebox/physics.rb +0 -3
- data/lib/gamebox/resource_manager.rb +22 -17
- data/lib/gamebox/sound_manager.rb +3 -2
- data/lib/gamebox/spatial_hash.rb +60 -31
- data/lib/gamebox/spatial_stagehand.rb +30 -6
- data/lib/gamebox/spec/helper.rb +7 -7
- data/lib/gamebox/stage.rb +18 -19
- data/lib/gamebox/stage_manager.rb +33 -23
- data/lib/gamebox/stagehand.rb +3 -0
- data/lib/gamebox/svg_document.rb +1 -1
- data/lib/gamebox/tasks/gamebox_tasks.rake +133 -0
- data/lib/gamebox/templates/actor.erb +0 -2
- data/lib/gamebox/templates/actor_view.erb +1 -3
- data/lib/gamebox/templates/template_app/Gemfile +3 -2
- data/lib/gamebox/templates/template_app/Rakefile +3 -8
- data/lib/gamebox/templates/template_app/config/environment.rb +7 -39
- data/lib/gamebox/templates/template_app/src/demo_stage.rb +1 -2
- data/lib/gamebox/templates/template_app/src/my_actor.rb +0 -3
- data/lib/gamebox/version.rb +2 -2
- data/lib/gamebox/viewport.rb +44 -8
- data/lib/gamebox/views/graphical_actor_view.rb +22 -16
- data/lib/gamebox/wrapped_screen.rb +9 -1
- data/script/perf_spatial_hash.rb +49 -58
- data/script/perf_struct_vs_array.rb +32 -0
- data/spec/actor_factory_spec.rb +61 -0
- data/spec/actor_spec.rb +24 -18
- data/spec/actor_view_spec.rb +51 -6
- data/spec/animated_spec.rb +27 -6
- data/spec/arbiter_spec.rb +12 -24
- data/spec/backstage_spec.rb +1 -1
- data/spec/behavior_spec.rb +3 -3
- data/spec/class_finder_spec.rb +13 -0
- data/spec/collidable_spec.rb +30 -10
- data/spec/emitter_spec.rb +20 -0
- data/spec/helper.rb +5 -21
- data/spec/input_manager_spec.rb +134 -0
- data/spec/label_spec.rb +0 -1
- data/spec/physical_spec.rb +114 -5
- data/spec/resource_manager_spec.rb +1 -2
- data/spec/spatial_hash_spec.rb +23 -7
- data/spec/spatial_stagehand_spec.rb +97 -0
- data/spec/stage_manager_spec.rb +0 -1
- data/spec/stage_spec.rb +2 -2
- data/spec/viewport_spec.rb +92 -48
- metadata +223 -119
- data/.gitignore +0 -11
- data/History.txt +0 -80
- data/VERSION +0 -1
- data/lib/gamebox/event_compat.rb +0 -285
- data/lib/gamebox/lib/diy.rb +0 -371
- data/lib/gamebox/lib/numbers_ext.rb +0 -3
- data/lib/gamebox/tasks/gamebox_tasks.rb +0 -61
- 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
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
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
|
-
|
50
|
-
|
51
|
-
|
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
|
-
|
3
|
-
$: << File.join(File.dirname(__FILE__),'generators')
|
4
|
-
|
5
|
-
unless "".respond_to? :end_with?
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
end
|
12
|
-
|
13
|
-
def print_usage
|
14
|
-
|
15
|
-
|
16
|
-
end
|
17
|
-
# TODO pull these out into generators/ dir for listing
|
18
|
-
|
19
|
-
if ARGV.size < 1
|
20
|
-
|
21
|
-
|
22
|
-
end
|
23
|
-
|
24
|
-
# TODO load generator file based on ARGV[0]
|
25
|
-
generator_klass_name =
|
26
|
-
|
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
|
-
|
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
|
-
|
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 ||=
|
50
|
-
@last_mouse_y ||=
|
56
|
+
@last_mouse_x ||= mouse_x
|
57
|
+
@last_mouse_y ||= mouse_y
|
51
58
|
|
52
|
-
x_diff = @last_mouse_x -
|
53
|
-
y_diff = @last_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 =
|
59
|
-
@last_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
|
-
|
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 =>
|
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
|
210
|
-
def
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
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
|
data/lib/gamebox/lib/rect.rb
CHANGED
@@ -1,56 +1,56 @@
|
|
1
1
|
#--
|
2
|
-
#
|
3
|
-
#
|
2
|
+
# Rubygame -- Ruby code and bindings to SDL to facilitate game creation
|
3
|
+
# Copyright (C) 2004-2007 John Croisant
|
4
4
|
#
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
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
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
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
|
-
#
|
16
|
-
#
|
17
|
-
#
|
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
|
-
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
28
|
-
#
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
32
|
-
#
|
33
|
-
#
|
34
|
-
#
|
35
|
-
#
|
36
|
-
#
|
37
|
-
#
|
38
|
-
#
|
39
|
-
#
|
40
|
-
#
|
41
|
-
#
|
42
|
-
#
|
43
|
-
#
|
44
|
-
#
|
45
|
-
#
|
46
|
-
#
|
47
|
-
#
|
48
|
-
#
|
49
|
-
#
|
50
|
-
#
|
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
|
-
#
|
53
|
-
#
|
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
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
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
|
-
|
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
|
-
|
158
|
-
|
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
|
-
|
163
|
-
|
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
|
-
|
168
|
-
|
169
|
-
|
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
|
-
|
173
|
-
|
174
|
-
alias t y
|
175
|
-
alias t= y=;
|
220
|
+
alias b bottom
|
221
|
+
alias b= bottom=;
|
176
222
|
|
177
|
-
|
178
|
-
|
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
|
-
|
183
|
-
|
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
|
-
|
186
|
-
|
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
|
-
|
191
|
-
|
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
|
-
|
194
|
-
|
243
|
+
alias cx centerx
|
244
|
+
alias cx= centerx=;
|
195
245
|
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
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
|
-
|
204
|
-
|
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
|
-
|
214
|
-
|
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
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
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
|
-
|
585
|
+
r2 = Rect.new_from_object(rect).normalize!
|
586
586
|
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
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
|
-
|
593
|
-
|
594
|
-
|
592
|
+
self[0,4] = rleft, rtop, rright - rleft, rbottom - rtop
|
593
|
+
return self
|
594
|
+
end
|
595
595
|
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
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
|
-
|
602
|
-
|
603
|
-
|
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
|
-
|
606
|
-
|
607
|
-
|
605
|
+
end
|
606
|
+
return self
|
607
|
+
end
|
608
608
|
|
609
609
|
|
610
610
|
end # class Rect
|