rubygame 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. data/CREDITS +50 -0
  2. data/Changelog +162 -0
  3. data/LICENSE +504 -0
  4. data/README +122 -0
  5. data/Rakefile +380 -0
  6. data/TODO +45 -0
  7. data/doc/extended_readme.rdoc +49 -0
  8. data/doc/getting_started.rdoc +47 -0
  9. data/doc/macosx_install.rdoc +74 -0
  10. data/doc/windows_install.rdoc +124 -0
  11. data/ext/rubygame/MANIFEST +25 -0
  12. data/ext/rubygame/rubygame_event.c +644 -0
  13. data/ext/rubygame/rubygame_event.h +48 -0
  14. data/ext/rubygame/rubygame_gfx.c +951 -0
  15. data/ext/rubygame/rubygame_gfx.h +102 -0
  16. data/ext/rubygame/rubygame_gl.c +154 -0
  17. data/ext/rubygame/rubygame_gl.h +32 -0
  18. data/ext/rubygame/rubygame_image.c +108 -0
  19. data/ext/rubygame/rubygame_image.h +41 -0
  20. data/ext/rubygame/rubygame_joystick.c +247 -0
  21. data/ext/rubygame/rubygame_joystick.h +41 -0
  22. data/ext/rubygame/rubygame_main.c +155 -0
  23. data/ext/rubygame/rubygame_main.h +33 -0
  24. data/ext/rubygame/rubygame_mixer.c +764 -0
  25. data/ext/rubygame/rubygame_mixer.h +62 -0
  26. data/ext/rubygame/rubygame_screen.c +420 -0
  27. data/ext/rubygame/rubygame_screen.h +41 -0
  28. data/ext/rubygame/rubygame_shared.c +152 -0
  29. data/ext/rubygame/rubygame_shared.h +54 -0
  30. data/ext/rubygame/rubygame_surface.c +1107 -0
  31. data/ext/rubygame/rubygame_surface.h +62 -0
  32. data/ext/rubygame/rubygame_time.c +183 -0
  33. data/ext/rubygame/rubygame_time.h +32 -0
  34. data/ext/rubygame/rubygame_ttf.c +600 -0
  35. data/ext/rubygame/rubygame_ttf.h +69 -0
  36. data/lib/rubygame.rb +40 -0
  37. data/lib/rubygame/MANIFEST +12 -0
  38. data/lib/rubygame/clock.rb +128 -0
  39. data/lib/rubygame/constants.rb +238 -0
  40. data/lib/rubygame/event.rb +313 -0
  41. data/lib/rubygame/ftor.rb +370 -0
  42. data/lib/rubygame/hotspot.rb +265 -0
  43. data/lib/rubygame/keyconstants.rb +237 -0
  44. data/lib/rubygame/mediabag.rb +94 -0
  45. data/lib/rubygame/queue.rb +288 -0
  46. data/lib/rubygame/rect.rb +614 -0
  47. data/lib/rubygame/sfont.rb +223 -0
  48. data/lib/rubygame/sprite.rb +477 -0
  49. data/samples/FreeSans.ttf +0 -0
  50. data/samples/GPL.txt +340 -0
  51. data/samples/README +40 -0
  52. data/samples/chimp.bmp +0 -0
  53. data/samples/chimp.rb +313 -0
  54. data/samples/demo_gl.rb +151 -0
  55. data/samples/demo_gl_tex.rb +197 -0
  56. data/samples/demo_music.rb +75 -0
  57. data/samples/demo_rubygame.rb +279 -0
  58. data/samples/demo_sfont.rb +52 -0
  59. data/samples/demo_ttf.rb +193 -0
  60. data/samples/demo_utf8.rb +53 -0
  61. data/samples/fist.bmp +0 -0
  62. data/samples/load_and_blit.rb +22 -0
  63. data/samples/panda.png +0 -0
  64. data/samples/punch.wav +0 -0
  65. data/samples/ruby.png +0 -0
  66. data/samples/term16.png +0 -0
  67. data/samples/whiff.wav +0 -0
  68. metadata +123 -0
@@ -0,0 +1,313 @@
1
+ #--
2
+ # Rubygame -- Ruby code and bindings to SDL to facilitate game creation
3
+ # Copyright (C) 2004-2007 John Croisant
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.
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.
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
18
+ #++
19
+
20
+ module Rubygame
21
+
22
+ # List of all Rubygame hardware event classes. *Do not modify!*
23
+ SDL_EVENTS = [ActiveEvent, KeyDownEvent, KeyUpEvent,\
24
+ MouseMotionEvent,MouseDownEvent,MouseUpEvent,JoyAxisEvent,\
25
+ JoyBallEvent, JoyHatEvent,JoyDownEvent, JoyUpEvent,\
26
+ ResizeEvent, QuitEvent]
27
+
28
+ # Converts a keyboard symbol (keysym) into a human-readable text string.
29
+ # If either Shift key was being pressed, alphanumeric or punctuation keys
30
+ # will be made uppercase or alternate, based on U.S. keyboard layout.
31
+ # E.g. "a" becomes "A", "1" becomes "!", and "/" becomes "?".
32
+ def Rubygame.key2str( sym, mods )
33
+ if (mods.include? K_LSHIFT) or (mods.include? K_RSHIFT)
34
+ return (Rubygame::Key::KEY2UPPER[sym]\
35
+ or Rubygame::Key::KEY2ASCII[sym] or "")
36
+ else
37
+ return (Rubygame::Key::KEY2LOWER[sym]\
38
+ or Rubygame::Key::KEY2ASCII[sym] or "")
39
+ end
40
+ end
41
+
42
+ # The parent class for all event classes. You can make custom event classes,
43
+ # if desired; inheriting this class is not necessary, but makes it easier
44
+ # to check if an object is an event or not.
45
+ class Event
46
+ end
47
+
48
+ # Indicates that the Rubygame window has gained or lost focus from the
49
+ # window manager.
50
+ #
51
+ # This event has these attributes:
52
+ # gain:: true if the window gained focus, and false if it lost focus.
53
+ # state:: string indicating what type of focus was gained or lost:
54
+ # "mouse":: the mouse entered/exited the window
55
+ # "keyboard":: the window gained or lost input focus
56
+ # "active":: the window was minimized/iconified.
57
+ class ActiveEvent < Event
58
+ attr_accessor :gain, :state
59
+ def initialize(gain,state)
60
+ @gain = gain
61
+ @state = state
62
+ end
63
+ end
64
+
65
+ # Indicates that a keyboard key was pressed.
66
+ #
67
+ # This event has these attributes:
68
+ # string:: a human-readable string telling what key was pressed, or nil.
69
+ # See #key2str.
70
+ # key:: the integer keysym for the key. These can be compared with the
71
+ # K_* constants in the Rubygame module, e.g. Rubygame::K_A.
72
+ # mods:: an Array of zero or more keysyms indicating which modifier keys
73
+ # were being pressed when the key was pressed. You can compare
74
+ # with these constants in the Rubygame module:
75
+ # K_RSHIFT:: shift key (right side)
76
+ # K_LSHIFT:: shift key (left side)
77
+ # K_RCTRL:: ctrl key (right side)
78
+ # K_LCTRL:: ctrl key (left side)
79
+ # K_RALT:: alt key (right side)
80
+ # K_LALT:: alt key (left side)
81
+ # K_RMETA:: meta key (right side)
82
+ # K_LMETA:: meta key (left side)
83
+ # K_RSUPER:: super key, aka. Windows key (right side)
84
+ # K_LSUPER:: super key, aka. Windows key (left side)
85
+ # K_RALT:: alt key (right side)
86
+ # K_NUMLOCK:: num lock
87
+ # K_CAPSLOCK:: caps lock
88
+ # K_MODE:: mode key
89
+ #
90
+ #
91
+ class KeyDownEvent < Event
92
+ attr_accessor :string,:key,:mods
93
+
94
+ # Create a new KeyDownEvent.
95
+ #
96
+ # key:: either an integer keysym (e.g. Rubygame::K_A) or string (e.g. "a")
97
+ # mods:: array of modifier keysyms
98
+ def initialize(key,mods)
99
+ if key.kind_of? Integer
100
+ @key = key
101
+ @string = Rubygame.key2str(key, mods) #a string or nil
102
+ elsif key.kind_of? String
103
+ @key = Rubygame::Key::ASCII2KEY[key]
104
+ if @key != nil
105
+ @string = key
106
+ else
107
+ raise(ArgumentError,"First argument of KeyDownEvent.new() must be an Integer KeySym (like K_A) or a ASCII-like String (like \"a\" or \"A\"). Got %s (%s)"%[key,key.class])
108
+ end
109
+ end
110
+ @mods = mods
111
+ end
112
+ end
113
+
114
+ # Indicates that a keyboard key was released.
115
+ #
116
+ # See KeyDownEvent.
117
+ class KeyUpEvent < Event
118
+ attr_accessor :string,:key,:mods
119
+ def initialize(key,mods)
120
+ if key.kind_of? Integer
121
+ @key = key
122
+ @string = Rubygame.key2str(key, mods) #a string or nil
123
+ elsif key.kind_of? String
124
+ @key = Rubygame::Key::ASCII2KEY[key]
125
+ if @key != nil
126
+ @string = key
127
+ else
128
+ raise(ArgumentError,"First argument of KeyUpEvent.new() must be an Integer KeySym (like K_A) or a ASCII-like String (like \"a\" or \"A\"). Got %s (%s)"%[key,key.class])
129
+ end
130
+ end
131
+ @mods = mods
132
+ end
133
+ end
134
+
135
+ # Indicates that the mouse cursor moved.
136
+ #
137
+ # This event has these attributes:
138
+ # pos:: the new position of the cursor, in the form [x,y].
139
+ # rel:: the relative movement of the cursor since the last update, [x,y].
140
+ # buttons:: the mouse buttons that were being held during the movement,
141
+ # an Array of zero or more of these constants in module Rubygame
142
+ # (or the corresponding button number):
143
+ # MOUSE_LEFT:: 1; left mouse button
144
+ # MOUSE_MIDDLE:: 2; middle mouse button
145
+ # MOUSE_RIGHT:: 3; right mouse button
146
+ class MouseMotionEvent < Event
147
+ attr_accessor :pos,:rel,:buttons
148
+ def initialize(pos,rel,buttons)
149
+ @pos, @rel, @buttons = pos, rel, buttons
150
+ end
151
+ end
152
+
153
+ # Indicates that a mouse button was pressed.
154
+ #
155
+ # This event has these attributes:
156
+ # string:: string indicating the button that was pressed ("left","middle", or
157
+ # "right").
158
+ # pos:: the position of the mouse cursor when the button was pressed,
159
+ # in the form [x,y].
160
+ # button:: the mouse button that was pressed; one of these constants in
161
+ # module Rubygame (or the corresponding button number):
162
+ # MOUSE_LEFT:: 1; left mouse button
163
+ # MOUSE_MIDDLE:: 2; middle mouse button
164
+ # MOUSE_RIGHT:: 3; right mouse button
165
+ class MouseDownEvent < Event
166
+ attr_accessor :string,:pos,:button
167
+ def initialize(pos,button)
168
+ @pos = pos
169
+ if button.kind_of? Integer
170
+ @button = button
171
+ @string = Rubygame::Mouse::MOUSE2STR[button] #a string or nil
172
+ elsif key.kind_of? String
173
+ @button = Rubygame::Mouse::STR2MOUSE[key]
174
+ if @button != nil
175
+ @string = button
176
+ else
177
+ raise(ArgumentError,"First argument of MouseDownEvent.new() must be an Integer Mouse button indentifier (like MOUSE_LEFT) or a String (like \"left\"). Got %s (%s)"%[button,button.class])
178
+ end
179
+ end
180
+ end
181
+ end
182
+
183
+ # Indicates that a mouse button was released.
184
+ #
185
+ # See MouseDownEvent.
186
+ class MouseUpEvent < Event
187
+ attr_accessor :string,:pos,:button
188
+ def initialize(pos,button)
189
+ @pos = pos
190
+ if button.kind_of? Integer
191
+ @button = button
192
+ @string = Rubygame::Mouse::MOUSE2STR[button] #a string or nil
193
+ elsif key.kind_of? String
194
+ @button = Rubygame::Mouse::STR2MOUSE[key]
195
+ if @button != nil
196
+ @string = button
197
+ else
198
+ raise(ArgumentError,"First argument of MouseUpEvent.new() must be an Integer Mouse button indentifier (like MOUSE_LEFT) or a String (like \"left\"). Got %s (%s)"%[button,button.class])
199
+ end
200
+ end
201
+ end
202
+ end
203
+
204
+ # Indicates that a Joystick axis was moved.
205
+ #
206
+ # This event has these attributes:
207
+ # joynum:: the identifier number of the affected Joystick.
208
+ # axis:: the identifier number of the axis.
209
+ # value:: the new value of the axis, between -32768 and 32767
210
+ class JoyAxisEvent < Event
211
+ attr_accessor :joynum,:axis,:value
212
+ def initialize(joy,axis,value)
213
+ # eventually, joy could be int OR a Rubygame::Joystick instance,
214
+ # which would be stored as joy or maybe joyinstance?
215
+ @joynum = joy
216
+ @axis, @value = axis, value
217
+ end
218
+ end
219
+
220
+ # Indicates that a Joystick trackball was moved.
221
+ #
222
+ # This event has these attributes:
223
+ # joynum:: the identifier number of the affected Joystick.
224
+ # ball:: the identifier number of the trackball.
225
+ # rel:: the relative movement of the trackball, [x,y].
226
+ class JoyBallEvent < Event
227
+ attr_accessor :joynum,:ball,:rel
228
+ def initialize(joy,ball,rel)
229
+ # eventually, joy could be int OR a Rubygame::Joystick instance,
230
+ # which would be stored as joy or maybe joyinstance?
231
+ @joynum = joy
232
+ @ball, @rel = ball, rel
233
+ end
234
+ end
235
+
236
+ # Indicates that a Joystick POV hat was moved.
237
+ #
238
+ # This event has these attributes:
239
+ # joynum:: the identifier number of the affected Joystick.
240
+ # hat:: the identifier number of the hat.
241
+ # value:: the new direction of the hat, one of these constants in module
242
+ # Rubygame (or the corresponding number):
243
+ # HAT_CENTERED:: 0
244
+ # HAT_UP:: 1
245
+ # HAT_RIGHT:: 2
246
+ # HAT_DOWN:: 4
247
+ # HAT_LEFT:: 8
248
+ # HAT_RIGHTUP:: 3
249
+ # HAT_RIGHTDOWN:: 6
250
+ # HAT_LEFTUP:: 9
251
+ # HAT_LEFTDOWN:: 12
252
+ class JoyHatEvent < Event
253
+ attr_accessor :joynum,:hat,:value
254
+ def initialize(joy,hat,value)
255
+ # eventually, joy could be int OR a Rubygame::Joystick instance,
256
+ # which would be stored as joy or maybe joyinstance?
257
+ @joynum = joy
258
+ @hat, @value = hat, value
259
+ end
260
+ end
261
+
262
+ # Indicates that a Joystick button was pressed.
263
+ #
264
+ # This event has these attributes:
265
+ # joynum:: the identifier number of the affected Joystick.
266
+ # hat:: the identifier number of the button.
267
+ class JoyDownEvent < Event
268
+ attr_accessor :joynum, :button
269
+ def initialize(joy,button)
270
+ # eventually, joy could be int OR a Rubygame::Joystick instance,
271
+ # which would be stored as joy or maybe joyinstance?
272
+ @joynum = joy
273
+ @button = button
274
+ end
275
+ end
276
+
277
+ # Indicates that a Joystick button was released.
278
+ #
279
+ # See JoyDownEvent.
280
+ class JoyUpEvent < Event
281
+ attr_accessor :joynum, :button
282
+ def initialize(joy,button)
283
+ # eventually, joy could be int OR a Rubygame::Joystick instance,
284
+ # which would be stored as joy or maybe joyinstance?
285
+ @joynum = joy
286
+ @button = button
287
+ end
288
+ end
289
+
290
+ # Indicates that the application window was resized. (After this event
291
+ # occurs, you should use Screen#set_mode to change the display surface to
292
+ # the new size.)
293
+ #
294
+ # This event has these attributes:
295
+ # size:: the new size of the window, in pixels [w,h].
296
+ class ResizeEvent < Event
297
+ attr_accessor :size
298
+ def initialize(new_size)
299
+ @size = new_size
300
+ end
301
+ end
302
+
303
+ # Indicates that part of the application window was exposed or otherwise
304
+ # changed, and perhaps the window needs to be refreshed. This event occurs,
305
+ # for example, when an OpenGL display should be updated.
306
+ class ExposeEvent < Event
307
+ end
308
+
309
+ # Indicates that the application has been signaled to quit. (E.g. the user
310
+ # pressed the close button.)
311
+ class QuitEvent < Event
312
+ end
313
+ end # module Rubygame
@@ -0,0 +1,370 @@
1
+ # Ftor ("Fake vecTOR"), a vector-like class for 2D position/movement.
2
+ #--
3
+ # Rubygame -- Ruby code and bindings to SDL to facilitate game creation
4
+ # Copyright (C) 2004-2007 John Croisant
5
+ #
6
+ # This library is free software; you can redistribute it and/or
7
+ # modify it under the terms of the GNU Lesser General Public
8
+ # License as published by the Free Software Foundation; either
9
+ # version 2.1 of the License, or (at your option) any later version.
10
+ #
11
+ # This library is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
+ # Lesser General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Lesser General Public
17
+ # License along with this library; if not, write to the Free Software
18
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
+ #++
20
+
21
+ module Rubygame
22
+
23
+ # *NOTE*: you must require 'rubygame/ftor' manually to gain access to
24
+ # Rubygame::Ftor. It is not imported with Rubygame by default!
25
+ #
26
+ # Ftor ("Fake vecTOR"), a vector-like class for 2D position/movement.
27
+ #
28
+ # (NB: See #angle for an important note about why angles appear to be the
29
+ # opposite of what you may expect.)
30
+ #
31
+ # Ftor is useful for storing 2D coordinates (x,y) as well as
32
+ # vector quantities such as velocity and acceleration (representationally,
33
+ # points and vectors are equivalent.) Although Ftors are always represented
34
+ # internally as Cartesian coordinates (x, y), it is possible to deal with an
35
+ # Ftor as polar coordinates (#angle, #magnitude) instead.
36
+ # See #new_am and #set_am!, for example.
37
+ #
38
+ # Ftor is a "fake" vector because it has certain convenient properties which
39
+ # differ from "true" vectors (i.e. vectors in a strict mathematical sense).
40
+ #
41
+ # Unlike vectors, Ftors may be multiplied or divided to another Ftor. This is
42
+ # equivalent to multiplying or dividing each component by the corresponding
43
+ # component in the second Ftor. If you like, you can think of this feature as
44
+ # scaling each component of the Ftor by a separate factor:
45
+ #
46
+ # Ftor(a,b) * Ftor(c,d) = Ftor(a*c, b*d)
47
+ #
48
+ # Of course, Ftors also have the usual vector behavior for addition/subraction
49
+ # between two Ftors, and multiplication/division of an Ftor by a scalar:
50
+ #
51
+ # Ftor(a,b) + Ftor(c,d) = Ftor(a+c, b+d)
52
+ # Ftor(a,b) * n = Ftor(a*n, b*n)
53
+ #
54
+ # Additionally, Ftor contains functions for manipulating itself.
55
+ # You can both get and set such properties as #angle, #magnitude, #unit,
56
+ # and #normal, and the Ftor will change in-place as needed. For example,
57
+ # if you set #angle=, the vector will change to have the new angle,
58
+ # but keeps the same magnitude as before.
59
+ #
60
+ # Ftor attempts to save processing time (at the expense of memory) by
61
+ # storing secondary properties (angle, magnitude, etc.) whenever they are
62
+ # calculated,so that they need not be calculated repeatedly. If the vector
63
+ # changes, the properties will be calculated again the next time they
64
+ # are needed.
65
+ # (In future versions, it may be possible to disable this feature for
66
+ # certain Ftors, for example if they will change very often, to save memory.)
67
+ #
68
+ class Ftor
69
+ PI = Math::PI
70
+ HALF_PI = PI*0.5
71
+ THREE_HALF_PI = PI*1.5
72
+ TWO_PI = PI*2
73
+
74
+ # Create a new Ftor by specifying its x and y components. See also #new_am
75
+ # and #new_from_to.
76
+ def initialize(x,y)
77
+ @x, @y = x, y
78
+ end
79
+
80
+ # Create a new Ftor by specifying its #angle (in radians) and #magnitude.
81
+ # See also #new.
82
+ def self.new_am(a,m)
83
+ v = Ftor.new(1,0)
84
+ v.a, v.m = a, m
85
+ return v
86
+ end
87
+
88
+ # Returns a new Ftor which represents the difference in position of two
89
+ # points +p1+ and +p2+. (+p1+ and +p2+ can be Ftors, size-2 Arrays, or
90
+ # anything else which has two numerical components and responds to #[].)
91
+ #
92
+ # In other words, assuming +v+ is the Ftor returned by this function:
93
+ # p1 + v = p2
94
+ def self.new_from_to(p1,p2)
95
+ return self.class.new(p2[0]-p1[0],p2[1]-p1[1])
96
+ end
97
+
98
+ attr_reader :x # The x component of the Ftor.
99
+ # Set the x component of the Ftor.
100
+ def x=(value)
101
+ @x = value
102
+ _clear()
103
+ end
104
+
105
+ attr_reader :y # The y component of the Ftor.
106
+ # Set the y component of the Ftor.
107
+ def y=(value)
108
+ @y = value
109
+ _clear()
110
+ end
111
+
112
+ # Modify the x and y components of the Ftor in-place
113
+ def set!(x,y)
114
+ @x, @y = x,y
115
+ _clear()
116
+ end
117
+
118
+ # Modify the #angle (in radians) and #magnitude of the Ftor in-place
119
+ def set_am!(a,m)
120
+ self.angle, self.magnitude = a, m
121
+ end
122
+
123
+ # Same as #to_s, but this Ftor's #object_id is also displayed.
124
+ def inspect
125
+ "#<#{self.class}:#{object_id}: %f, %f>"%[@x,@y]
126
+ end
127
+
128
+ # Display this Ftor in the format: "#<Ftor: [x, y]>". x and y are displayed
129
+ # as floats at full precision. See also #pp.
130
+ def to_s
131
+ "#<#{self.class}: [%f, %f]>"%[@x,@y]
132
+ end
133
+
134
+ # "Pretty print". Same as #to_s, but components are displayed as rounded
135
+ # floats to 3 decimal places, for easy viewing.
136
+ def pretty
137
+ "#<#{self.class}: [%0.3f, %0.3f]>"%[@x,@y]
138
+ end
139
+
140
+ # Same as #to_s_am, but this Ftor's #object_id is also displayed.
141
+ def inspect_am
142
+ "#<#{self.class}:AM:#{object_id}: %f, %f>"%[angle(),magnitude()]
143
+ end
144
+
145
+ # Display this Ftor in the format: "#<Ftor:AM: [angle, magnitude]>".
146
+ # angle and magnitude are displayed as floats at full precision.
147
+ # See also #to_s and #pp_am.
148
+ def to_s_am
149
+ "#<#{self.class}:AM: [%f, %f]>"%[angle(),magnitude()]
150
+ end
151
+
152
+ # "Pretty print" with angle and magnitude.
153
+ # Same as #to_s_am, but components are displayed as rounded floats to 3
154
+ # decimal places, for easy viewing.
155
+ def pretty_am
156
+ "#<#{self.class}:AM: [%0.3f, %0.3f]>"%[angle(),magnitude()]
157
+ end
158
+
159
+ # Returns an Array of this Ftor's components, [x,y].
160
+ def to_a
161
+ [@x,@y]
162
+ end
163
+ alias :to_ary :to_a
164
+
165
+ # Return the +i+th component of this Ftor, as if it were the Array
166
+ # returned by #to_a.
167
+ def [](i)
168
+ [@x,@y][i]
169
+ end
170
+
171
+ # True if this Ftor is equal to +other+, when both have been converted to
172
+ # Arrays via #to_a. In other words, a component-by-component equality check.
173
+ def ==(other)
174
+ to_a() == other.to_a
175
+ end
176
+
177
+ # The reverse of this Ftor. I.e., all components are negated. See also
178
+ # #reverse!.
179
+ def -@
180
+ self.class.new(-@x,-@y)
181
+ end
182
+
183
+ # Like #-@, but reverses this Ftor in-place.
184
+ def reverse!
185
+ set!(-@x,-@y)
186
+ end
187
+
188
+ # Perform vector addition with this Ftor and +other+, adding them on a
189
+ # component-by-component basis, like so:
190
+ # Ftor(a,b) + Ftor(c,d) = Ftor(a+c, b+d)
191
+ def +(other)
192
+ return self.class.new(@x+other[0],@y+other[1])
193
+ end
194
+
195
+ # Like #+, but performs subtraction instead of addition.
196
+ def -(other)
197
+ return self.class.new(@x-other[0],@y-other[1])
198
+ end
199
+
200
+ # Perform multiplication of this Ftor by the scalar +other+, like so:
201
+ # Ftor(a,b) * n = Ftor(a*n, b*n)
202
+ #
203
+ # However, if this causes TypeError, attempt to extract indices 0 and 1
204
+ # with +other+'s #[] operator, and multiply them into the corresponding
205
+ # components of this Ftor, like so:
206
+ # Ftor(a,b) * Ftor(c,d) = Ftor(a*c, b*d)
207
+ # Ftor(a,b) * [c,d] = Ftor(a*c, b*d)
208
+ def *(other)
209
+ return self.class.new(@x*other,@y*other)
210
+ rescue TypeError
211
+ return self.class.new(@x*other[0],@y*other[1])
212
+ end
213
+
214
+ # Like #*, but performs division instead of multiplication.
215
+ def /(other)
216
+ x, y = @x.to_f, @y.to_f
217
+ return self.class.new(x/other,y/other)
218
+ rescue TypeError
219
+ return self.class.new(x/other[0],y/other[1])
220
+ end
221
+
222
+ # Return the angle (radians) this Ftor forms with the positive X axis.
223
+ # This is the same as the Ftor's angle in a polar coordinate system.
224
+ #
225
+ # *IMPORTANT*: Because the positive Y axis on the Rubygame::Screen points
226
+ # *downwards*, an angle in the range 0..PI will appear to point *downwards*,
227
+ # rather than upwards!
228
+ # This also means that positive rotation will appear *clockwise*, and
229
+ # negative rotation will appear *counterclockwise*!
230
+ # This is the opposite of what you would expect in geometry class!
231
+ def angle
232
+ @angle or @angle = Math.atan2(@y,@x)
233
+ end
234
+
235
+ # Set the angle (radians) of this Ftor from the positive X axis.
236
+ # Magnitude is preserved.
237
+ def angle=(a)
238
+ m = magnitude()
239
+ set!( Math.cos(a)*m, Math.sin(a)*m )
240
+ end
241
+
242
+ alias :a :angle
243
+ alias :a= :angle= ;
244
+
245
+ # Returns the magnitude of the Ftor, i.e. its length from tail to head.
246
+ # This is the same as the Ftor's magnitude in a polar coordinate system.
247
+ def magnitude
248
+ @magnitude or @magnitude = Math.hypot(@x,@y)
249
+ end
250
+
251
+ # Modifies the #magnitude of the Ftor, preserving its #angle.
252
+ #
253
+ # In other words, the Ftor will point in the same direction, but it will
254
+ # be a different length from tail to head.
255
+ def magnitude=(m)
256
+ new = unit() * m
257
+ set!(new.x, new.y)
258
+ end
259
+
260
+ alias :m :magnitude
261
+ alias :m= :magnitude= ;
262
+
263
+ # Return a new unit Ftor which is perpendicular to this Ftor (rotated by
264
+ # pi/2 radians, to be specific).
265
+ def normal
266
+ @normal or @normal = unit().rotate(HALF_PI)
267
+ end
268
+
269
+ # Rotate this Ftor in-place, so that it is perpendicular to +other+.
270
+ # This Ftor will be at an angle of -pi/2 to +other+.
271
+ def normal=(other)
272
+ set!( *(self.class.new(*other).unit().rotate(-HALF_PI) * magnitude()) )
273
+ end
274
+
275
+ alias :n :normal
276
+ alias :n= :normal= ;
277
+
278
+ # Return the unit vector of the Ftor, i.e. an Ftor with the same direction,
279
+ # but a #magnitude of 1. (This is sometimes called a "normalized" vector,
280
+ # not to be confused with a vector's #normal.)
281
+ def unit
282
+ m = magnitude().to_f
283
+ @unit or @unit = Ftor.new(@x/m, @y/m)
284
+ end
285
+
286
+ # Rotates this Ftor in-place, so that its #unit vector matches the unit
287
+ # vector of the given Ftor.
288
+ #
289
+ # In other words, changes the #angle of this Ftor to be the same as the angle
290
+ # of the given Ftor, but this Ftor's #magnitude does not change.
291
+ #--
292
+ # TODO: investigate efficiency of using `self.angle = other.angle` instead
293
+ #++
294
+ def unit=(other)
295
+ set!( *(self.class.new(*other).unit() * magnitude()) )
296
+ end
297
+
298
+ alias :u :unit
299
+ alias :u= :unit=
300
+ alias :align! :unit=;
301
+
302
+ # Return the dot product (aka inner product) of this Ftor and +other+.
303
+ # The dot product of two vectors +v1+ and +v2+ is:
304
+ # v1.x * v2.x + v1.y * v2.y
305
+ def dot(other)
306
+ @x*other[0] + @y*other[1]
307
+ end
308
+
309
+ # Return the #dot product of #unit vectors of this Ftor and +other+.
310
+ def udot(other)
311
+ unit().dot(self.class.new(*other).unit)
312
+ end
313
+
314
+ #Return the difference in angles (radians) between this Ftor and +other+.
315
+ def angle_with(other)
316
+ Math.acos( self.udot(other) )
317
+ end
318
+
319
+ # Rotate this Ftor in-place by +angle+ (radians). This is the same as
320
+ # adding +angle+ to this Ftor's #angle.
321
+ #
322
+ #--
323
+ # , with one important difference:
324
+ # This method will be much more efficient when rotating at a right angle,
325
+ # i.e.rotating by any multiple of PI/2 radians from -2*PI to 2*PI radians.
326
+ #
327
+ # For convenience, and to ensure exactitude, several numerical constants
328
+ # have been defined for multiples of PI/2:
329
+ # * Ftor::PI:: (same as Math::PI)
330
+ # * Ftor::HALF_PI:: PI * 0.5 (or PI/2)
331
+ # * Ftor::THREE_HALF_PI:: PI * 1.5 (or 3*PI/2)
332
+ # * Ftor::TWO_PI:: PI * 2
333
+ #++
334
+ #
335
+ # *IMPORTANT*: Positive rotation will appear *clockwise*, and negative
336
+ # rotation will appear *counterclockwise*! See #angle for the reason.
337
+ def rotate!(angle)
338
+ # case(angle)
339
+ # when HALF_PI, -THREE_HALF_PI
340
+ # self.set!(@y,-@x)
341
+ # when THREE_HALF_PI, -HALF_PI
342
+ # self.set!(-@y,@x)
343
+ # when PI, -PI
344
+ # self.set!(@y,-@x)
345
+ # when 0, TWO_PI, -TWO_PI
346
+ # self.set!(@y,-@x)
347
+ # else
348
+ self.a += angle
349
+ # end
350
+ return self
351
+ end
352
+
353
+ # Like #rotate!, but returns a duplicate instead of rotating this Ftor
354
+ # in-place.
355
+ def rotate(radians)
356
+ self.dup.rotate!(radians)
357
+ end
358
+
359
+ # Clears stored values for #angle, #magnitude, #normal, and #unit,
360
+ # so that they will be recalculated the next time they are needed.
361
+ # Intended for internal use, but might be useful in other situations.
362
+ def _clear
363
+ @angle = nil
364
+ @magnitude = nil
365
+ @normal = nil
366
+ @unit = nil
367
+ return self
368
+ end
369
+ end
370
+ end