ruby-sfml 3.0.0.6 → 3.0.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/.rdoc_options +44 -0
  3. data/CHANGELOG.md +40 -3
  4. data/README.md +71 -25
  5. data/lib/sfml/app.rb +5 -0
  6. data/lib/sfml/assets.rb +2 -0
  7. data/lib/sfml/audio/listener.rb +7 -0
  8. data/lib/sfml/audio/music.rb +51 -0
  9. data/lib/sfml/audio/sound.rb +41 -0
  10. data/lib/sfml/audio/sound_buffer.rb +4 -0
  11. data/lib/sfml/audio/sound_buffer_recorder.rb +2 -0
  12. data/lib/sfml/audio/sound_cone.rb +1 -0
  13. data/lib/sfml/audio/sound_recorder.rb +5 -0
  14. data/lib/sfml/audio/sound_stream.rb +46 -0
  15. data/lib/sfml/graphics/animation.rb +10 -0
  16. data/lib/sfml/graphics/blend_mode.rb +1 -0
  17. data/lib/sfml/graphics/circle_shape.rb +16 -0
  18. data/lib/sfml/graphics/color.rb +31 -0
  19. data/lib/sfml/graphics/convex_shape.rb +9 -0
  20. data/lib/sfml/graphics/font.rb +2 -0
  21. data/lib/sfml/graphics/image.rb +2 -0
  22. data/lib/sfml/graphics/particle_system.rb +5 -0
  23. data/lib/sfml/graphics/rectangle_shape.rb +11 -0
  24. data/lib/sfml/graphics/render_target.rb +1 -0
  25. data/lib/sfml/graphics/render_texture.rb +7 -0
  26. data/lib/sfml/graphics/render_window.rb +26 -0
  27. data/lib/sfml/graphics/shape.rb +5 -0
  28. data/lib/sfml/graphics/shape_inspectable.rb +2 -0
  29. data/lib/sfml/graphics/sprite.rb +4 -0
  30. data/lib/sfml/graphics/sprite_sheet.rb +2 -0
  31. data/lib/sfml/graphics/stencil_mode.rb +1 -0
  32. data/lib/sfml/graphics/text.rb +19 -0
  33. data/lib/sfml/graphics/texture.rb +5 -0
  34. data/lib/sfml/graphics/texture_atlas.rb +2 -0
  35. data/lib/sfml/graphics/transform.rb +2 -0
  36. data/lib/sfml/graphics/transformable.rb +12 -0
  37. data/lib/sfml/graphics/vertex_array.rb +18 -0
  38. data/lib/sfml/graphics/vertex_buffer.rb +4 -0
  39. data/lib/sfml/graphics/view.rb +5 -0
  40. data/lib/sfml/network/ftp.rb +34 -0
  41. data/lib/sfml/network/http.rb +1 -0
  42. data/lib/sfml/network/ip_address.rb +1 -0
  43. data/lib/sfml/network/packet.rb +28 -0
  44. data/lib/sfml/network/socket_selector.rb +1 -0
  45. data/lib/sfml/network/tcp_listener.rb +3 -0
  46. data/lib/sfml/network/tcp_socket.rb +4 -0
  47. data/lib/sfml/network/udp_socket.rb +3 -0
  48. data/lib/sfml/scene.rb +2 -0
  49. data/lib/sfml/system/clock.rb +1 -0
  50. data/lib/sfml/system/rect.rb +27 -5
  51. data/lib/sfml/system/time.rb +22 -2
  52. data/lib/sfml/system/vector2.rb +42 -2
  53. data/lib/sfml/system/vector3.rb +45 -2
  54. data/lib/sfml/version.rb +1 -1
  55. data/lib/sfml/window/clipboard.rb +1 -0
  56. data/lib/sfml/window/context_settings.rb +1 -0
  57. data/lib/sfml/window/event.rb +2 -0
  58. data/lib/sfml/window/joystick.rb +3 -0
  59. data/lib/sfml/window/sensor.rb +1 -0
  60. data/lib/sfml/window/video_mode.rb +2 -0
  61. data/lib/sfml/window/window.rb +12 -0
  62. data/ruby-sfml.gemspec +6 -2
  63. metadata +3 -2
@@ -45,6 +45,8 @@ module SFML
45
45
  1002 => :connection_closed, 1003 => :invalid_file,
46
46
  }.freeze
47
47
 
48
+ # Create a new FTP client. Call `#connect` next to open a
49
+ # session.
48
50
  def initialize
49
51
  ptr = C::Network.sfFtp_create
50
52
  raise NetworkError, "sfFtp_create returned NULL" if ptr.null?
@@ -61,61 +63,78 @@ module SFML
61
63
  Response._take_ownership(C::Network.sfFtp_connect(@handle, addr, Integer(port), t.to_native))
62
64
  end
63
65
 
66
+ # Log in as anonymous. Returns a `Response`.
64
67
  def login_anonymous
65
68
  Response._take_ownership(C::Network.sfFtp_loginAnonymous(@handle))
66
69
  end
67
70
 
71
+ # Log in with credentials.
68
72
  def login(user, password)
69
73
  Response._take_ownership(C::Network.sfFtp_login(@handle, user.to_s, password.to_s))
70
74
  end
71
75
 
76
+ # Close the connection.
72
77
  def disconnect = Response._take_ownership(C::Network.sfFtp_disconnect(@handle))
78
+ # Send a no-op to keep the connection alive against server timeouts.
73
79
  def keep_alive = Response._take_ownership(C::Network.sfFtp_keepAlive(@handle))
74
80
 
81
+ # Current working directory as a `DirectoryResponse`.
75
82
  def working_directory
76
83
  DirectoryResponse._take_ownership(C::Network.sfFtp_getWorkingDirectory(@handle))
77
84
  end
78
85
 
86
+ # List `directory` (default = current). Returns a `ListingResponse`.
79
87
  def directory_listing(directory = "")
80
88
  ListingResponse._take_ownership(C::Network.sfFtp_getDirectoryListing(@handle, directory.to_s))
81
89
  end
82
90
 
91
+ # `cd` to a directory.
83
92
  def change_directory(directory)
84
93
  Response._take_ownership(C::Network.sfFtp_changeDirectory(@handle, directory.to_s))
85
94
  end
86
95
 
96
+ # `cd ..`.
87
97
  def parent_directory
88
98
  Response._take_ownership(C::Network.sfFtp_parentDirectory(@handle))
89
99
  end
90
100
 
101
+ # `mkdir` — returns a `DirectoryResponse` with the new path.
91
102
  def create_directory(name)
92
103
  DirectoryResponse._take_ownership(C::Network.sfFtp_createDirectory(@handle, name.to_s))
93
104
  end
94
105
 
106
+ # `rmdir`.
95
107
  def delete_directory(name)
96
108
  Response._take_ownership(C::Network.sfFtp_deleteDirectory(@handle, name.to_s))
97
109
  end
98
110
 
111
+ # Rename / move a file on the server.
99
112
  def rename_file(file, new_name)
100
113
  Response._take_ownership(C::Network.sfFtp_renameFile(@handle, file.to_s, new_name.to_s))
101
114
  end
102
115
 
116
+ # Delete a single file.
103
117
  def delete_file(name)
104
118
  Response._take_ownership(C::Network.sfFtp_deleteFile(@handle, name.to_s))
105
119
  end
106
120
 
121
+ # Pull `remote` from the server to local path. `mode` is
122
+ # `:binary` (default), `:ascii`, or `:ebcdic`.
107
123
  def download(remote, local, mode: :binary)
108
124
  idx = C::Network::FTP_TRANSFER_MODES.index(mode) ||
109
125
  raise(ArgumentError, "Unknown FTP transfer mode: #{mode.inspect}")
110
126
  Response._take_ownership(C::Network.sfFtp_download(@handle, remote.to_s, local.to_s, idx))
111
127
  end
112
128
 
129
+ # Push `local` to `remote`. `append: true` extends an existing
130
+ # remote file rather than replacing it.
113
131
  def upload(local, remote, mode: :binary, append: false)
114
132
  idx = C::Network::FTP_TRANSFER_MODES.index(mode) ||
115
133
  raise(ArgumentError, "Unknown FTP transfer mode: #{mode.inspect}")
116
134
  Response._take_ownership(C::Network.sfFtp_upload(@handle, local.to_s, remote.to_s, idx, !!append))
117
135
  end
118
136
 
137
+ # Send a raw FTP command (e.g. "STAT") and an optional parameter.
119
138
  def send_command(command, parameter = "")
120
139
  Response._take_ownership(C::Network.sfFtp_sendCommand(@handle, command.to_s, parameter.to_s))
121
140
  end
@@ -124,9 +143,13 @@ module SFML
124
143
 
125
144
  # Generic response: most FTP operations return this.
126
145
  class Response
146
+ # `true` if ok.
127
147
  def ok? = C::Network.sfFtpResponse_isOk(@handle)
148
+ # Returns the status.
128
149
  def status = C::Network.sfFtpResponse_getStatus(@handle)
150
+ # Returns the status symbol.
129
151
  def status_symbol = STATUS_NAMES[status] || status
152
+ # Returns the message.
130
153
  def message = C::Network.sfFtpResponse_getMessage(@handle).to_s
131
154
  attr_reader :handle # :nodoc:
132
155
 
@@ -142,10 +165,15 @@ module SFML
142
165
  # Returned by working_directory and create_directory — adds the
143
166
  # `#directory` accessor on top of Response.
144
167
  class DirectoryResponse
168
+ # `true` if ok.
145
169
  def ok? = C::Network.sfFtpDirectoryResponse_isOk(@handle)
170
+ # Returns the status.
146
171
  def status = C::Network.sfFtpDirectoryResponse_getStatus(@handle)
172
+ # Returns the status symbol.
147
173
  def status_symbol = STATUS_NAMES[status] || status
174
+ # Returns the message.
148
175
  def message = C::Network.sfFtpDirectoryResponse_getMessage(@handle).to_s
176
+ # Returns the directory.
149
177
  def directory = C::Network.sfFtpDirectoryResponse_getDirectory(@handle).to_s
150
178
  attr_reader :handle # :nodoc:
151
179
 
@@ -160,13 +188,19 @@ module SFML
160
188
 
161
189
  # Returned by directory_listing — adds the `#names` array.
162
190
  class ListingResponse
191
+ # `true` if ok.
163
192
  def ok? = C::Network.sfFtpListingResponse_isOk(@handle)
193
+ # Returns the status.
164
194
  def status = C::Network.sfFtpListingResponse_getStatus(@handle)
195
+ # Returns the status symbol.
165
196
  def status_symbol = STATUS_NAMES[status] || status
197
+ # Returns the message.
166
198
  def message = C::Network.sfFtpListingResponse_getMessage(@handle).to_s
199
+ # Returns the count.
167
200
  def count = C::Network.sfFtpListingResponse_getCount(@handle)
168
201
  attr_reader :handle # :nodoc:
169
202
 
203
+ # Filenames returned by the listing, as an Array of Strings.
170
204
  def names
171
205
  Array.new(count) { |i| C::Network.sfFtpListingResponse_getName(@handle, i).to_s }
172
206
  end
@@ -92,6 +92,7 @@ module SFML
92
92
  STATUS_NAMES[status] || status
93
93
  end
94
94
 
95
+ # Returns the body.
95
96
  def body = C::Network.sfHttpResponse_getBody(@handle).to_s
96
97
  def field(name) = C::Network.sfHttpResponse_getField(@handle, name.to_s)
97
98
 
@@ -53,6 +53,7 @@ module SFML
53
53
  other.is_a?(IpAddress) && to_s == other.to_s
54
54
  end
55
55
  alias eql? ==
56
+ # Returns the hash.
56
57
  def hash = to_s.hash
57
58
 
58
59
  # @!visibility private
@@ -52,8 +52,11 @@ module SFML
52
52
  ptr.read_bytes(size).force_encoding(Encoding::ASCII_8BIT)
53
53
  end
54
54
 
55
+ # Returns the size.
55
56
  def size = C::Network.sfPacket_getDataSize(@handle)
57
+ # Returns the read position.
56
58
  def read_position = C::Network.sfPacket_getReadPosition(@handle)
59
+ # `true` if end of packet.
57
60
  def end_of_packet? = C::Network.sfPacket_endOfPacket(@handle)
58
61
 
59
62
  # `false` if the last read overran the packet — sfPacket "fails"
@@ -62,20 +65,35 @@ module SFML
62
65
  def ok? = C::Network.sfPacket_canRead(@handle)
63
66
 
64
67
  # ---- typed writers ----
68
+ # All writers append to the end of the packet and return self
69
+ # for chaining.
70
+
71
+ # Append a Bool.
65
72
  def write_bool(v)
66
73
  C::Network.sfPacket_writeBool(@handle, v ? true : false); self
67
74
  end
75
+ # Append a signed 8-bit Integer.
68
76
  def write_int8(v) = (C::Network.sfPacket_writeInt8(@handle, Integer(v)); self)
77
+ # Append an unsigned 8-bit Integer.
69
78
  def write_uint8(v) = (C::Network.sfPacket_writeUint8(@handle, Integer(v)); self)
79
+ # Append a signed 16-bit Integer.
70
80
  def write_int16(v) = (C::Network.sfPacket_writeInt16(@handle, Integer(v)); self)
81
+ # Append an unsigned 16-bit Integer.
71
82
  def write_uint16(v) = (C::Network.sfPacket_writeUint16(@handle, Integer(v)); self)
83
+ # Append a signed 32-bit Integer.
72
84
  def write_int32(v) = (C::Network.sfPacket_writeInt32(@handle, Integer(v)); self)
85
+ # Append an unsigned 32-bit Integer.
73
86
  def write_uint32(v) = (C::Network.sfPacket_writeUint32(@handle, Integer(v)); self)
87
+ # Append a signed 64-bit Integer.
74
88
  def write_int64(v) = (C::Network.sfPacket_writeInt64(@handle, Integer(v)); self)
89
+ # Append an unsigned 64-bit Integer.
75
90
  def write_uint64(v) = (C::Network.sfPacket_writeUint64(@handle, Integer(v)); self)
91
+ # Append a single-precision Float.
76
92
  def write_float(v) = (C::Network.sfPacket_writeFloat(@handle, Float(v)); self)
93
+ # Append a double-precision Float.
77
94
  def write_double(v) = (C::Network.sfPacket_writeDouble(@handle, Float(v)); self)
78
95
 
96
+ # Append a length-prefixed UTF-8 String.
79
97
  def write_string(str)
80
98
  C::Network.sfPacket_writeString(@handle, str.to_s)
81
99
  self
@@ -83,15 +101,25 @@ module SFML
83
101
 
84
102
  # ---- typed readers ----
85
103
  def read_bool = C::Network.sfPacket_readBool(@handle)
104
+ # Returns the read int8.
86
105
  def read_int8 = C::Network.sfPacket_readInt8(@handle)
106
+ # Returns the read uint8.
87
107
  def read_uint8 = C::Network.sfPacket_readUint8(@handle)
108
+ # Returns the read int16.
88
109
  def read_int16 = C::Network.sfPacket_readInt16(@handle)
110
+ # Returns the read uint16.
89
111
  def read_uint16 = C::Network.sfPacket_readUint16(@handle)
112
+ # Returns the read int32.
90
113
  def read_int32 = C::Network.sfPacket_readInt32(@handle)
114
+ # Returns the read uint32.
91
115
  def read_uint32 = C::Network.sfPacket_readUint32(@handle)
116
+ # Returns the read int64.
92
117
  def read_int64 = C::Network.sfPacket_readInt64(@handle)
118
+ # Returns the read uint64.
93
119
  def read_uint64 = C::Network.sfPacket_readUint64(@handle)
120
+ # Returns the read float.
94
121
  def read_float = C::Network.sfPacket_readFloat(@handle)
122
+ # Returns the read double.
95
123
  def read_double = C::Network.sfPacket_readDouble(@handle)
96
124
 
97
125
  # Read a length-prefixed string. CSFML expects a caller-allocated
@@ -76,6 +76,7 @@ module SFML
76
76
  C::Network.sfSocketSelector_wait(@handle, t.to_native)
77
77
  end
78
78
 
79
+ # `true` if ready.
79
80
  def ready?(socket)
80
81
  case socket
81
82
  when TcpListener then C::Network.sfSocketSelector_isTcpListenerReady(@handle, socket.handle)
@@ -47,12 +47,15 @@ module SFML
47
47
  [status, sock]
48
48
  end
49
49
 
50
+ # `true` if blocking.
50
51
  def blocking? = C::Network.sfTcpListener_isBlocking(@handle)
51
52
 
53
+ # Set the blocking.
52
54
  def blocking=(value)
53
55
  C::Network.sfTcpListener_setBlocking(@handle, value ? true : false)
54
56
  end
55
57
 
58
+ # Returns the local port.
56
59
  def local_port = C::Network.sfTcpListener_getLocalPort(@handle)
57
60
 
58
61
  attr_reader :handle # :nodoc:
@@ -74,13 +74,17 @@ module SFML
74
74
  [status, status == :done ? pkt : nil]
75
75
  end
76
76
 
77
+ # `true` if blocking.
77
78
  def blocking? = C::Network.sfTcpSocket_isBlocking(@handle)
78
79
 
80
+ # Set the blocking.
79
81
  def blocking=(value)
80
82
  C::Network.sfTcpSocket_setBlocking(@handle, value ? true : false)
81
83
  end
82
84
 
85
+ # Returns the local port.
83
86
  def local_port = C::Network.sfTcpSocket_getLocalPort(@handle)
87
+ # Returns the remote port.
84
88
  def remote_port = C::Network.sfTcpSocket_getRemotePort(@handle)
85
89
 
86
90
  def remote_address
@@ -80,12 +80,15 @@ module SFML
80
80
  [status, pkt, IpAddress.wrap(sender_addr), sender_port.read(:uint16)]
81
81
  end
82
82
 
83
+ # `true` if blocking.
83
84
  def blocking? = C::Network.sfUdpSocket_isBlocking(@handle)
84
85
 
86
+ # Set the blocking.
85
87
  def blocking=(value)
86
88
  C::Network.sfUdpSocket_setBlocking(@handle, value ? true : false)
87
89
  end
88
90
 
91
+ # Returns the local port.
89
92
  def local_port = C::Network.sfUdpSocket_getLocalPort(@handle)
90
93
 
91
94
  attr_reader :handle # :nodoc:
data/lib/sfml/scene.rb CHANGED
@@ -48,7 +48,9 @@ module SFML
48
48
 
49
49
  # Convenience accessors that match `SFML::App`'s.
50
50
  def window = @app.window
51
+ # Returns the width.
51
52
  def width = @app.width
53
+ # Returns the height.
52
54
  def height = @app.height
53
55
 
54
56
  # Switch the host app to a new scene from inside this one.
@@ -17,6 +17,7 @@ module SFML
17
17
  end
18
18
  alias elapsed elapsed_time
19
19
 
20
+ # `true` if running.
20
21
  def running?
21
22
  C::System.sfClock_isRunning(@handle)
22
23
  end
@@ -1,9 +1,9 @@
1
1
  module SFML
2
- # An axis-aligned rectangle. Mirrors sfFloatRect / sfIntRect — those structs
3
- # are just (position: Vector2, size: Vector2). We deliberately keep one
4
- # Ruby class for both the float and int variants since pattern-matching
5
- # `case bounds in {position: {x:, y:}, size: {x: w, y: h}}` is what the
6
- # users reach for either way.
2
+ # An axis-aligned rectangle. Mirrors sfFloatRect / sfIntRect — those
3
+ # structs are just `(position: Vector2, size: Vector2)`. We
4
+ # deliberately keep one Ruby class for both the float and int
5
+ # variants since pattern-matching `case bounds in {position: {x:,
6
+ # y:}, size: {x: w, y: h}}` is what users reach for either way.
7
7
  #
8
8
  # r = SFML::Rect.new([10, 20], [100, 50])
9
9
  # r.contains?([50, 30]) #=> true
@@ -13,26 +13,38 @@ module SFML
13
13
  class Rect
14
14
  attr_reader :position, :size
15
15
 
16
+ # `position` and `size` may each be a `Vector2` OR a `[x, y]` Array.
16
17
  def initialize(position, size)
17
18
  @position = position.is_a?(Vector2) ? position : Vector2.new(*position)
18
19
  @size = size.is_a?(Vector2) ? size : Vector2.new(*size)
19
20
  freeze
20
21
  end
21
22
 
23
+ # Top-left X.
22
24
  def x = @position.x
25
+ # Top-left Y.
23
26
  def y = @position.y
27
+ # Width.
24
28
  def width = @size.x
29
+ # Height.
25
30
  def height = @size.y
26
31
  alias left x
27
32
  alias top y
33
+
34
+ # X of the right edge (`x + width`).
28
35
  def right = @position.x + @size.x
36
+ # Y of the bottom edge (`y + height`).
29
37
  def bottom = @position.y + @size.y
30
38
 
39
+ # `true` if `point` (Vector2 or `[x, y]`) is inside this rect.
40
+ # The right and bottom edges are exclusive.
31
41
  def contains?(point)
32
42
  px, py = point.is_a?(Vector2) ? [point.x, point.y] : [point[0], point[1]]
33
43
  px >= left && px < right && py >= top && py < bottom
34
44
  end
35
45
 
46
+ # `true` if this rect overlaps `other` at all (any non-empty
47
+ # intersection counts).
36
48
  def intersects?(other)
37
49
  left < other.right &&
38
50
  right > other.left &&
@@ -40,17 +52,27 @@ module SFML
40
52
  bottom > other.top
41
53
  end
42
54
 
55
+ # Value equality — same position + same size.
43
56
  def ==(other)
44
57
  other.is_a?(Rect) && @position == other.position && @size == other.size
45
58
  end
46
59
  alias eql? ==
60
+ # Hash key support.
47
61
  def hash = [@position, @size].hash
48
62
 
63
+ # `[x, y, width, height]`.
49
64
  def to_a = [x, y, width, height]
65
+
66
+ # `{position:, size:}`.
50
67
  def to_h = { position: @position, size: @size }
68
+
69
+ # Pattern-match support for `in [x, y, w, h]`.
51
70
  def deconstruct = [x, y, width, height]
71
+
72
+ # Pattern-match support for `in {position:, size:}`.
52
73
  def deconstruct_keys(_keys) = { position: @position, size: @size }
53
74
 
75
+ # String representation for debugging.
54
76
  def to_s = "Rect(x=#{x}, y=#{y}, w=#{width}, h=#{height})"
55
77
  alias inspect to_s
56
78
 
@@ -1,19 +1,27 @@
1
1
  module SFML
2
- # Represents a time value. Stored internally as microseconds (int64), same
3
- # as sfTime in CSFML. Immutable and comparable.
2
+ # Represents a time value. Stored internally as microseconds (int64),
3
+ # same as `sfTime` in CSFML. Immutable and Comparable, so two Times
4
+ # can be `==` / `<` / `>` / sorted.
4
5
  #
5
6
  # SFML::Time.seconds(1.5) #=> 1_500_000 µs
6
7
  # SFML::Time.milliseconds(500)
7
8
  # SFML::Time.microseconds(42)
8
9
  # SFML::Time.zero
10
+ #
11
+ # t1 + t2 # sum of two Times
12
+ # t.as_seconds # Float seconds
9
13
  class Time
10
14
  include Comparable
11
15
 
12
16
  attr_reader :microseconds
13
17
 
18
+ # Build a Time from a Float of seconds.
14
19
  def self.seconds(value) = new((value * 1_000_000).to_i)
20
+ # Build a Time from an Integer of milliseconds.
15
21
  def self.milliseconds(value) = new(Integer(value) * 1_000)
22
+ # Build a Time directly from microseconds.
16
23
  def self.microseconds(value) = new(Integer(value))
24
+ # The zero Time.
17
25
  def self.zero = new(0)
18
26
 
19
27
  def initialize(microseconds)
@@ -21,19 +29,31 @@ module SFML
21
29
  freeze
22
30
  end
23
31
 
32
+ # As Float seconds.
24
33
  def as_seconds = @microseconds / 1_000_000.0
34
+ # As Integer milliseconds (truncated).
25
35
  def as_milliseconds = @microseconds / 1_000
36
+ # As Integer microseconds — the raw stored value.
26
37
  def as_microseconds = @microseconds
27
38
 
39
+ # Sum of two Times — returns a fresh Time.
28
40
  def +(other) = Time.new(@microseconds + other.microseconds)
41
+ # Difference between two Times.
29
42
  def -(other) = Time.new(@microseconds - other.microseconds)
43
+ # Negate (useful when computing a "ago" offset).
30
44
  def -@ = Time.new(-@microseconds)
45
+
46
+ # Compare two Times — implements `Comparable`, so `<`, `<=`, `>=`,
47
+ # `>`, `clamp`, and `Array#sort` all work.
31
48
  def <=>(other) = @microseconds <=> other.microseconds
32
49
 
50
+ # Hash key support — Times can be Hash keys / Set members.
33
51
  def hash = @microseconds.hash
52
+ # Value equality.
34
53
  def eql?(other) = other.is_a?(Time) && @microseconds == other.microseconds
35
54
  alias == eql?
36
55
 
56
+ # String representation for debugging.
37
57
  def to_s = "#<SFML::Time #{as_seconds}s>"
38
58
  alias inspect to_s
39
59
 
@@ -9,8 +9,12 @@ module SFML
9
9
  class Vector2
10
10
  attr_reader :x, :y
11
11
 
12
+ # Compact constructor: `Vector2[3, 4]` reads naturally as a literal.
12
13
  def self.[](x, y) = new(x, y)
13
- def self.zero = new(0, 0)
14
+
15
+ # The (0, 0) vector. Reused as a singleton-style fallback (e.g.
16
+ # `#normalize` returns this for the zero vector).
17
+ def self.zero = new(0, 0)
14
18
 
15
19
  def initialize(x = 0, y = 0)
16
20
  @x = x
@@ -18,10 +22,15 @@ module SFML
18
22
  freeze
19
23
  end
20
24
 
25
+ # Component-wise addition.
21
26
  def +(other) = Vector2.new(@x + other.x, @y + other.y)
27
+ # Component-wise subtraction.
22
28
  def -(other) = Vector2.new(@x - other.x, @y - other.y)
29
+ # Multiply both components by `scalar`.
23
30
  def *(scalar) = Vector2.new(@x * scalar, @y * scalar)
31
+ # Divide both components by `scalar`. Always promotes to Float.
24
32
  def /(scalar) = Vector2.new(@x / scalar.to_f, @y / scalar.to_f)
33
+ # Negate both components.
25
34
  def -@ = Vector2.new(-@x, -@y)
26
35
 
27
36
  # Lets Ruby evaluate `2 * vec` as `vec * 2`. Without this, Numeric#*
@@ -31,24 +40,40 @@ module SFML
31
40
  [self, other]
32
41
  end
33
42
 
43
+ # Value equality — two Vector2s are equal when both components match.
34
44
  def ==(other) = other.is_a?(Vector2) && @x == other.x && @y == other.y
35
45
  alias eql? ==
46
+ # Hash key support — Vector2s can be Hash keys / Set members.
36
47
  def hash = [@x, @y].hash
37
48
 
49
+ # Euclidean length √(x² + y²). For comparisons prefer `length_sq` —
50
+ # avoids the square root.
38
51
  def length = Math.sqrt(length_sq)
52
+ # Squared length (x² + y²) — comparisons can use this without the
53
+ # `sqrt` call.
39
54
  def length_sq = (@x * @x) + (@y * @y)
40
55
 
56
+ # Unit vector pointing the same way as `self`. Returns the zero
57
+ # vector unchanged (no division by zero).
41
58
  def normalize
42
59
  len = length
43
60
  return Vector2.zero if len.zero?
44
61
  self / len
45
62
  end
46
63
 
64
+ # Scalar dot product. Positive when both vectors point roughly the
65
+ # same way, negative when opposite, zero when perpendicular.
47
66
  def dot(other) = (@x * other.x) + (@y * other.y)
67
+
68
+ # Scalar 2D cross product (a.k.a. perpendicular dot). Positive when
69
+ # `other` is counter-clockwise from `self`, negative when clockwise.
48
70
  def cross(other) = (@x * other.y) - (@y * other.x)
49
71
 
50
- # Euclidean distance between two points.
72
+ # Euclidean distance to `other` — accepts a Vector2 or `[x, y]`.
51
73
  def distance(other) = (self - _coerce(other)).length
74
+
75
+ # Squared distance to `other` — skip the `sqrt` if you only need to
76
+ # compare two distances.
52
77
  def distance_sq(other) = (self - _coerce(other)).length_sq
53
78
 
54
79
  # Angle of this vector relative to +X axis, in radians (-π..π].
@@ -64,6 +89,7 @@ module SFML
64
89
  # if you already have radians.
65
90
  def rotated(degrees) = rotated_rad(degrees * Math::PI / 180.0)
66
91
 
92
+ # Vector rotated by `radians` counter-clockwise.
67
93
  def rotated_rad(radians)
68
94
  c, s = Math.cos(radians), Math.sin(radians)
69
95
  Vector2.new(@x * c - @y * s, @x * s + @y * c)
@@ -109,15 +135,29 @@ module SFML
109
135
  self * (target / len)
110
136
  end
111
137
 
138
+ # `true` iff both components are zero.
112
139
  def zero? = @x.zero? && @y.zero?
140
+
141
+ # Per-component absolute value.
113
142
  def abs = Vector2.new(@x.abs, @y.abs)
143
+
144
+ # Promote to a Vector3 with optional `z` (default 0).
114
145
  def to_v3(z = 0.0) = Vector3.new(@x, @y, z)
115
146
 
147
+ # As a 2-element `[x, y]` Array — supports splat destructuring:
148
+ # `x, y = vec`.
116
149
  def to_a = [@x, @y]
150
+
151
+ # As a `{x:, y:}` Hash.
117
152
  def to_h = { x: @x, y: @y }
153
+
154
+ # Pattern-match support for `in [x, y]`.
118
155
  def deconstruct = [@x, @y]
156
+
157
+ # Pattern-match support for `in {x:, y:}`.
119
158
  def deconstruct_keys(_keys) = { x: @x, y: @y }
120
159
 
160
+ # String representation for debugging.
121
161
  def to_s = "Vector2(#{@x}, #{@y})"
122
162
  alias inspect to_s
123
163