ruby-sfml 3.0.0.0
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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +101 -0
- data/LICENSE.txt +21 -0
- data/README.md +245 -0
- data/ext/ruby-sfml/extconf.rb +69 -0
- data/lib/sfml/assets/fonts/DejaVuSans.LICENSE.txt +78 -0
- data/lib/sfml/assets/fonts/DejaVuSans.ttf +0 -0
- data/lib/sfml/assets.rb +121 -0
- data/lib/sfml/audio/listener.rb +55 -0
- data/lib/sfml/audio/music.rb +88 -0
- data/lib/sfml/audio/sound.rb +102 -0
- data/lib/sfml/audio/sound_buffer.rb +38 -0
- data/lib/sfml/audio/sound_buffer_recorder.rb +71 -0
- data/lib/sfml/audio/sound_recorder.rb +30 -0
- data/lib/sfml/c/audio.rb +106 -0
- data/lib/sfml/c/graphics.rb +425 -0
- data/lib/sfml/c/network.rb +79 -0
- data/lib/sfml/c/system.rb +43 -0
- data/lib/sfml/c/window.rb +186 -0
- data/lib/sfml/c.rb +72 -0
- data/lib/sfml/game.rb +101 -0
- data/lib/sfml/graphics/blend_mode.rb +108 -0
- data/lib/sfml/graphics/circle_shape.rb +67 -0
- data/lib/sfml/graphics/color.rb +89 -0
- data/lib/sfml/graphics/convex_shape.rb +82 -0
- data/lib/sfml/graphics/font.rb +67 -0
- data/lib/sfml/graphics/image.rb +125 -0
- data/lib/sfml/graphics/rectangle_shape.rb +62 -0
- data/lib/sfml/graphics/render_states.rb +56 -0
- data/lib/sfml/graphics/render_target.rb +146 -0
- data/lib/sfml/graphics/render_texture.rb +72 -0
- data/lib/sfml/graphics/render_window.rb +154 -0
- data/lib/sfml/graphics/shader.rb +132 -0
- data/lib/sfml/graphics/sprite.rb +75 -0
- data/lib/sfml/graphics/text.rb +144 -0
- data/lib/sfml/graphics/texture.rb +79 -0
- data/lib/sfml/graphics/transform.rb +150 -0
- data/lib/sfml/graphics/transformable.rb +74 -0
- data/lib/sfml/graphics/vertex.rb +53 -0
- data/lib/sfml/graphics/vertex_array.rb +114 -0
- data/lib/sfml/graphics/view.rb +126 -0
- data/lib/sfml/network/ip_address.rb +67 -0
- data/lib/sfml/network/tcp_listener.rb +61 -0
- data/lib/sfml/network/tcp_socket.rb +74 -0
- data/lib/sfml/network/udp_socket.rb +71 -0
- data/lib/sfml/system/clock.rb +44 -0
- data/lib/sfml/system/rect.rb +64 -0
- data/lib/sfml/system/time.rb +48 -0
- data/lib/sfml/system/vector2.rb +66 -0
- data/lib/sfml/system/vector3.rb +63 -0
- data/lib/sfml/version.rb +19 -0
- data/lib/sfml/window/clipboard.rb +38 -0
- data/lib/sfml/window/cursor.rb +68 -0
- data/lib/sfml/window/event.rb +133 -0
- data/lib/sfml/window/joystick.rb +90 -0
- data/lib/sfml/window/keyboard.rb +60 -0
- data/lib/sfml/window/mouse.rb +71 -0
- data/lib/sfml/window/video_mode.rb +37 -0
- data/lib/sfml/window/window.rb +149 -0
- data/lib/sfml.rb +98 -0
- data/ruby-sfml.gemspec +38 -0
- metadata +163 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
module SFML
|
|
2
|
+
# A 2D affine transformation. Wraps the 3×3 matrix that SFML uses to
|
|
3
|
+
# combine translation, rotation, scaling, and skew. Useful when you
|
|
4
|
+
# want to:
|
|
5
|
+
#
|
|
6
|
+
# - Apply the same transform across many drawables (push it via
|
|
7
|
+
# `RenderStates.new(transform: …)` instead of repeating
|
|
8
|
+
# `position=`/`rotation=` on every shape).
|
|
9
|
+
# - Build a transform from primitive ops without holding a Drawable.
|
|
10
|
+
#
|
|
11
|
+
# t = SFML::Transform.identity
|
|
12
|
+
# .translate([400, 300])
|
|
13
|
+
# .rotate(30)
|
|
14
|
+
# .scale([2, 2])
|
|
15
|
+
#
|
|
16
|
+
# t.transform_point([10, 0]) #=> Vector2(world coord after t)
|
|
17
|
+
# inv = t.inverse #=> reverse mapping
|
|
18
|
+
#
|
|
19
|
+
# Methods are chainable and **mutate in place**, returning self. To
|
|
20
|
+
# work with a fresh copy use `t.dup`. The CSFML constant for the
|
|
21
|
+
# identity is exposed via `Transform.identity`.
|
|
22
|
+
class Transform
|
|
23
|
+
def initialize
|
|
24
|
+
@struct = C::Graphics::Transform.new
|
|
25
|
+
# Start as identity by copying CSFML's sfTransform_Identity.
|
|
26
|
+
identity_bytes = C::Graphics.sfTransform_Identity.pointer.read_bytes(C::Graphics::Transform.size)
|
|
27
|
+
@struct.pointer.write_bytes(identity_bytes)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def self.identity
|
|
31
|
+
new
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Build from a flat row-major 9-element float array. Lays out as:
|
|
35
|
+
# [a, b, c,
|
|
36
|
+
# d, e, f,
|
|
37
|
+
# g, h, i] # SFML stores the 3x3 conceptually as a 4x4 padded
|
|
38
|
+
def self.from_matrix(arr)
|
|
39
|
+
raise ArgumentError, "expected 9-element matrix, got #{arr.length}" if arr.length != 9
|
|
40
|
+
t = new
|
|
41
|
+
9.times { |i| t.struct[:matrix][i] = arr[i].to_f }
|
|
42
|
+
t
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def initialize_dup(other)
|
|
46
|
+
super
|
|
47
|
+
@struct = C::Graphics::Transform.new
|
|
48
|
+
@struct.pointer.write_bytes(other.struct.pointer.read_bytes(C::Graphics::Transform.size))
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Apply a translation. Mutates self.
|
|
52
|
+
def translate(offset)
|
|
53
|
+
vec = _vec2(offset)
|
|
54
|
+
C::Graphics.sfTransform_translate(@struct.pointer, vec.to_native_f)
|
|
55
|
+
self
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Apply a rotation by `degrees`. Optional `center` to pivot around
|
|
59
|
+
# a non-origin point.
|
|
60
|
+
def rotate(degrees, center: nil)
|
|
61
|
+
if center
|
|
62
|
+
c = _vec2(center)
|
|
63
|
+
C::Graphics.sfTransform_rotateWithCenter(@struct.pointer, degrees.to_f, c.to_native_f)
|
|
64
|
+
else
|
|
65
|
+
C::Graphics.sfTransform_rotate(@struct.pointer, degrees.to_f)
|
|
66
|
+
end
|
|
67
|
+
self
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Apply a scale. With `center:`, scales around that pivot.
|
|
71
|
+
def scale(factors, center: nil)
|
|
72
|
+
f = _vec2(factors)
|
|
73
|
+
if center
|
|
74
|
+
c = _vec2(center)
|
|
75
|
+
C::Graphics.sfTransform_scaleWithCenter(@struct.pointer, f.to_native_f, c.to_native_f)
|
|
76
|
+
else
|
|
77
|
+
C::Graphics.sfTransform_scale(@struct.pointer, f.to_native_f)
|
|
78
|
+
end
|
|
79
|
+
self
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Multiply this transform by another. `t.combine(t2)` is `t * t2`
|
|
83
|
+
# in left-to-right order (apply t2's transformation, then t's).
|
|
84
|
+
def combine(other)
|
|
85
|
+
raise ArgumentError, "Transform#combine needs another SFML::Transform" unless other.is_a?(Transform)
|
|
86
|
+
C::Graphics.sfTransform_combine(@struct.pointer, other.struct.pointer)
|
|
87
|
+
self
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Return a new Transform that's the inverse of this one — applying
|
|
91
|
+
# both in sequence yields the identity.
|
|
92
|
+
def inverse
|
|
93
|
+
result = C::Graphics.sfTransform_getInverse(@struct.pointer)
|
|
94
|
+
t = allocate_new_with(result)
|
|
95
|
+
t
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Map a point through the transform. Returns a Vector2.
|
|
99
|
+
def transform_point(point)
|
|
100
|
+
vec = _vec2(point)
|
|
101
|
+
r = C::Graphics.sfTransform_transformPoint(@struct.pointer, vec.to_native_f)
|
|
102
|
+
Vector2.new(r[:x], r[:y])
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Map a SFML::Rect through the transform. Returns a new Rect.
|
|
106
|
+
def transform_rect(rect)
|
|
107
|
+
raise ArgumentError, "Transform#transform_rect needs a SFML::Rect" unless rect.is_a?(Rect)
|
|
108
|
+
native = C::Graphics::FloatRect.new
|
|
109
|
+
native[:position][:x] = rect.x.to_f
|
|
110
|
+
native[:position][:y] = rect.y.to_f
|
|
111
|
+
native[:size][:x] = rect.width.to_f
|
|
112
|
+
native[:size][:y] = rect.height.to_f
|
|
113
|
+
|
|
114
|
+
r = C::Graphics.sfTransform_transformRect(@struct.pointer, native)
|
|
115
|
+
Rect.from_native(r)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Read the matrix as a flat 9-element Array of floats (row-major).
|
|
119
|
+
def matrix
|
|
120
|
+
(0...9).map { |i| @struct[:matrix][i] }
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def ==(other)
|
|
124
|
+
return false unless other.is_a?(Transform)
|
|
125
|
+
C::Graphics.sfTransform_equal(@struct.pointer, other.struct.pointer)
|
|
126
|
+
end
|
|
127
|
+
alias eql? ==
|
|
128
|
+
def hash = matrix.hash
|
|
129
|
+
|
|
130
|
+
def to_s = "Transform(#{matrix.map { |v| v.round(3) }.inspect})"
|
|
131
|
+
alias inspect to_s
|
|
132
|
+
|
|
133
|
+
# @!visibility private
|
|
134
|
+
attr_reader :struct
|
|
135
|
+
|
|
136
|
+
private
|
|
137
|
+
|
|
138
|
+
def allocate_new_with(native_struct)
|
|
139
|
+
t = self.class.allocate
|
|
140
|
+
new_buf = C::Graphics::Transform.new
|
|
141
|
+
new_buf.pointer.write_bytes(native_struct.pointer.read_bytes(C::Graphics::Transform.size))
|
|
142
|
+
t.instance_variable_set(:@struct, new_buf)
|
|
143
|
+
t
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def _vec2(value)
|
|
147
|
+
value.is_a?(Vector2) ? value : Vector2.new(*value)
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
module SFML
|
|
2
|
+
module Graphics
|
|
3
|
+
# Shared transform interface for Sprite, CircleShape, RectangleShape
|
|
4
|
+
# (and Text once added). Including class must:
|
|
5
|
+
# 1. Define a constant CSFML_PREFIX (Symbol), e.g. :sfCircleShape
|
|
6
|
+
# 2. Hold the FFI handle in @handle
|
|
7
|
+
#
|
|
8
|
+
# All wrappers go through C::Graphics.public_send so the same Ruby code
|
|
9
|
+
# picks up sfSprite_*, sfCircleShape_*, sfRectangleShape_* etc. without
|
|
10
|
+
# repetition. The cost (~1µs of method dispatch on a typical call) is
|
|
11
|
+
# negligible against any GPU work, but the wins on maintainability are
|
|
12
|
+
# large — adding a new shape becomes ~5 lines.
|
|
13
|
+
module Transformable
|
|
14
|
+
def position
|
|
15
|
+
Vector2.from_native(_csfml(:getPosition, @handle))
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def position=(value)
|
|
19
|
+
_csfml(:setPosition, @handle, _coerce_vec2(value).to_native_f)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def rotation
|
|
23
|
+
_csfml(:getRotation, @handle)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def rotation=(degrees)
|
|
27
|
+
_csfml(:setRotation, @handle, degrees.to_f)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def scale
|
|
31
|
+
Vector2.from_native(_csfml(:getScale, @handle))
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def scale=(value)
|
|
35
|
+
_csfml(:setScale, @handle, _coerce_vec2(value).to_native_f)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def origin
|
|
39
|
+
Vector2.from_native(_csfml(:getOrigin, @handle))
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def origin=(value)
|
|
43
|
+
_csfml(:setOrigin, @handle, _coerce_vec2(value).to_native_f)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def move(offset)
|
|
47
|
+
_csfml(:move, @handle, _coerce_vec2(offset).to_native_f)
|
|
48
|
+
self
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def rotate(degrees)
|
|
52
|
+
_csfml(:rotate, @handle, degrees.to_f)
|
|
53
|
+
self
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def scale_by(factors)
|
|
57
|
+
_csfml(:scale, @handle, _coerce_vec2(factors).to_native_f)
|
|
58
|
+
self
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
private
|
|
62
|
+
|
|
63
|
+
def _csfml(suffix, *args)
|
|
64
|
+
C::Graphics.public_send(:"#{self.class::CSFML_PREFIX}_#{suffix}", *args)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Accept a Vector2 or any [x, y] pair so users can pass a literal:
|
|
68
|
+
# shape.position = [400, 300]
|
|
69
|
+
def _coerce_vec2(value)
|
|
70
|
+
value.is_a?(Vector2) ? value : Vector2.new(*value)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
module SFML
|
|
2
|
+
# A single point of geometry: position, colour, and texture coordinate.
|
|
3
|
+
# Plain Ruby value object — VertexArray copies it into / out of CSFML
|
|
4
|
+
# storage, so mutating a Vertex after appending doesn't propagate.
|
|
5
|
+
#
|
|
6
|
+
# SFML::Vertex.new([10, 20])
|
|
7
|
+
# SFML::Vertex.new([10, 20], color: SFML::Color.red)
|
|
8
|
+
# SFML::Vertex.new([10, 20], color: SFML::Color.red, tex_coords: [0, 0])
|
|
9
|
+
class Vertex
|
|
10
|
+
attr_accessor :position, :color, :tex_coords
|
|
11
|
+
|
|
12
|
+
def initialize(position = Vector2.zero, color: Color::WHITE, tex_coords: Vector2.zero)
|
|
13
|
+
@position = _coerce_vec2(position)
|
|
14
|
+
@color = color
|
|
15
|
+
@tex_coords = _coerce_vec2(tex_coords)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def to_s
|
|
19
|
+
"Vertex(#{@position.x}, #{@position.y})"
|
|
20
|
+
end
|
|
21
|
+
alias inspect to_s
|
|
22
|
+
|
|
23
|
+
# @!visibility private
|
|
24
|
+
def to_native
|
|
25
|
+
v = C::Graphics::Vertex.new
|
|
26
|
+
v[:position][:x] = @position.x.to_f
|
|
27
|
+
v[:position][:y] = @position.y.to_f
|
|
28
|
+
v[:color][:r] = @color.r
|
|
29
|
+
v[:color][:g] = @color.g
|
|
30
|
+
v[:color][:b] = @color.b
|
|
31
|
+
v[:color][:a] = @color.a
|
|
32
|
+
v[:tex_coords][:x] = @tex_coords.x.to_f
|
|
33
|
+
v[:tex_coords][:y] = @tex_coords.y.to_f
|
|
34
|
+
v
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# @!visibility private
|
|
38
|
+
def self.from_native(struct)
|
|
39
|
+
new(
|
|
40
|
+
Vector2.new(struct[:position][:x], struct[:position][:y]),
|
|
41
|
+
color: Color.new(struct[:color][:r], struct[:color][:g],
|
|
42
|
+
struct[:color][:b], struct[:color][:a]),
|
|
43
|
+
tex_coords: Vector2.new(struct[:tex_coords][:x], struct[:tex_coords][:y]),
|
|
44
|
+
)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
def _coerce_vec2(value)
|
|
50
|
+
value.is_a?(Vector2) ? value : Vector2.new(*value)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
module SFML
|
|
2
|
+
# Batched geometry: a single CSFML draw call shipping many vertices to
|
|
3
|
+
# the GPU at once. This is how you draw thousands of particles, custom
|
|
4
|
+
# meshes, or tile maps without the per-shape overhead.
|
|
5
|
+
#
|
|
6
|
+
# va = SFML::VertexArray.new(:triangles)
|
|
7
|
+
# va << SFML::Vertex.new([100, 100], color: SFML::Color.red)
|
|
8
|
+
# va << SFML::Vertex.new([200, 100], color: SFML::Color.green)
|
|
9
|
+
# va << SFML::Vertex.new([150, 200], color: SFML::Color.blue)
|
|
10
|
+
# window.draw(va)
|
|
11
|
+
#
|
|
12
|
+
# Primitive types control how vertices form geometry:
|
|
13
|
+
# :points one isolated point per vertex
|
|
14
|
+
# :lines pairs of vertices form line segments
|
|
15
|
+
# :line_strip consecutive vertices form a chain of segments
|
|
16
|
+
# :triangles triples form independent triangles
|
|
17
|
+
# :triangle_strip each new vertex extends the strip
|
|
18
|
+
# :triangle_fan every vertex shares a fan-out with vertex 0
|
|
19
|
+
class VertexArray
|
|
20
|
+
include Enumerable
|
|
21
|
+
|
|
22
|
+
# Order matches sfPrimitiveType in CSFML/Graphics/PrimitiveType.h.
|
|
23
|
+
PRIMITIVE_TYPES = %i[points lines line_strip triangles triangle_strip triangle_fan].freeze
|
|
24
|
+
PRIMITIVE_INDEX = PRIMITIVE_TYPES.each_with_index.to_h.freeze
|
|
25
|
+
|
|
26
|
+
def initialize(primitive_type = :points, vertices = nil)
|
|
27
|
+
ptr = C::Graphics.sfVertexArray_create
|
|
28
|
+
raise Error, "sfVertexArray_create returned NULL" if ptr.null?
|
|
29
|
+
@handle = FFI::AutoPointer.new(ptr, C::Graphics.method(:sfVertexArray_destroy))
|
|
30
|
+
|
|
31
|
+
self.primitive_type = primitive_type
|
|
32
|
+
vertices&.each { |v| append(v) }
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def primitive_type
|
|
36
|
+
PRIMITIVE_TYPES[C::Graphics.sfVertexArray_getPrimitiveType(@handle)] || :unknown
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def primitive_type=(type)
|
|
40
|
+
code = PRIMITIVE_INDEX.fetch(type) do
|
|
41
|
+
raise ArgumentError,
|
|
42
|
+
"Unknown primitive type: #{type.inspect}. Expected one of: #{PRIMITIVE_TYPES.inspect}"
|
|
43
|
+
end
|
|
44
|
+
C::Graphics.sfVertexArray_setPrimitiveType(@handle, code)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def size = C::Graphics.sfVertexArray_getVertexCount(@handle)
|
|
48
|
+
alias length size
|
|
49
|
+
alias count size
|
|
50
|
+
|
|
51
|
+
def empty? = size.zero?
|
|
52
|
+
|
|
53
|
+
def clear
|
|
54
|
+
C::Graphics.sfVertexArray_clear(@handle)
|
|
55
|
+
self
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def resize(n)
|
|
59
|
+
C::Graphics.sfVertexArray_resize(@handle, Integer(n))
|
|
60
|
+
self
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def append(vertex)
|
|
64
|
+
C::Graphics.sfVertexArray_append(@handle, vertex.to_native)
|
|
65
|
+
self
|
|
66
|
+
end
|
|
67
|
+
alias << append
|
|
68
|
+
|
|
69
|
+
# Read a vertex by index, or nil if out of range. Returns a fresh
|
|
70
|
+
# SFML::Vertex copy — mutate via `va[i] = new_vertex` to write back,
|
|
71
|
+
# not through the returned object.
|
|
72
|
+
def [](index)
|
|
73
|
+
i = Integer(index)
|
|
74
|
+
return nil if i < 0 || i >= size
|
|
75
|
+
ptr = C::Graphics.sfVertexArray_getVertex(@handle, i)
|
|
76
|
+
Vertex.from_native(C::Graphics::Vertex.new(ptr))
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def []=(index, vertex)
|
|
80
|
+
i = Integer(index)
|
|
81
|
+
# CSFML's sfVertexArray_getVertex aborts the process on out-of-range
|
|
82
|
+
# access (it asserts), so we have to bounds-check on the Ruby side.
|
|
83
|
+
raise IndexError, "vertex index #{i} out of range (size: #{size})" if i < 0 || i >= size
|
|
84
|
+
|
|
85
|
+
ptr = C::Graphics.sfVertexArray_getVertex(@handle, i)
|
|
86
|
+
cv = C::Graphics::Vertex.new(ptr)
|
|
87
|
+
cv[:position][:x] = vertex.position.x.to_f
|
|
88
|
+
cv[:position][:y] = vertex.position.y.to_f
|
|
89
|
+
cv[:color][:r] = vertex.color.r
|
|
90
|
+
cv[:color][:g] = vertex.color.g
|
|
91
|
+
cv[:color][:b] = vertex.color.b
|
|
92
|
+
cv[:color][:a] = vertex.color.a
|
|
93
|
+
cv[:tex_coords][:x] = vertex.tex_coords.x.to_f
|
|
94
|
+
cv[:tex_coords][:y] = vertex.tex_coords.y.to_f
|
|
95
|
+
vertex
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def each
|
|
99
|
+
return enum_for(:each) unless block_given?
|
|
100
|
+
size.times { |i| yield self[i] }
|
|
101
|
+
self
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def bounds
|
|
105
|
+
Rect.from_native(C::Graphics.sfVertexArray_getBounds(@handle))
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def draw_on(target, states_ptr = nil) # :nodoc:
|
|
109
|
+
target._draw_native(:VertexArray, @handle, states_ptr)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
attr_reader :handle # :nodoc:
|
|
113
|
+
end
|
|
114
|
+
end
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
module SFML
|
|
2
|
+
# A 2D camera. A View defines what part of the world is visible in the
|
|
3
|
+
# window: a centre point, a size in world units, an optional rotation,
|
|
4
|
+
# and a viewport rectangle (in normalised window coords [0,1]) that
|
|
5
|
+
# places the view inside the window — useful for split-screen or for
|
|
6
|
+
# rendering a mini-map alongside the main camera.
|
|
7
|
+
#
|
|
8
|
+
# Two flows you'll typically use:
|
|
9
|
+
#
|
|
10
|
+
# # 1. Define from a centre + size:
|
|
11
|
+
# camera = SFML::View.new(center: [400, 300], size: [800, 600])
|
|
12
|
+
#
|
|
13
|
+
# # 2. Define from a world rectangle (top-left + size):
|
|
14
|
+
# camera = SFML::View.from_rect(SFML::Rect.new([0, 0], [1600, 1200]))
|
|
15
|
+
#
|
|
16
|
+
# window.view = camera # apply
|
|
17
|
+
# window.draw(stuff) # everything draws through the camera
|
|
18
|
+
# window.view = window.default_view # restore the 1:1 default
|
|
19
|
+
#
|
|
20
|
+
# All coordinate inputs accept either a SFML::Vector2 or a [x, y] array.
|
|
21
|
+
class View
|
|
22
|
+
def initialize(center: nil, size: nil, rotation: nil, viewport: nil, _handle: nil)
|
|
23
|
+
ptr = _handle || C::Graphics.sfView_create
|
|
24
|
+
raise Error, "sfView_create returned NULL" if ptr.null?
|
|
25
|
+
@handle = FFI::AutoPointer.new(ptr, C::Graphics.method(:sfView_destroy))
|
|
26
|
+
|
|
27
|
+
self.center = center if center
|
|
28
|
+
self.size = size if size
|
|
29
|
+
self.rotation = rotation if rotation
|
|
30
|
+
self.viewport = viewport if viewport
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Build a View whose rectangle covers the given world Rect.
|
|
34
|
+
def self.from_rect(rect)
|
|
35
|
+
raise ArgumentError, "View.from_rect needs a SFML::Rect" unless rect.is_a?(Rect)
|
|
36
|
+
|
|
37
|
+
native = C::Graphics::FloatRect.new
|
|
38
|
+
native[:position][:x] = rect.x.to_f
|
|
39
|
+
native[:position][:y] = rect.y.to_f
|
|
40
|
+
native[:size][:x] = rect.width.to_f
|
|
41
|
+
native[:size][:y] = rect.height.to_f
|
|
42
|
+
|
|
43
|
+
ptr = C::Graphics.sfView_createFromRect(native)
|
|
44
|
+
raise Error, "sfView_createFromRect returned NULL" if ptr.null?
|
|
45
|
+
new(_handle: ptr)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# @!visibility private
|
|
49
|
+
# Wrap a CSFML view pointer that we don't own (e.g. the value returned
|
|
50
|
+
# by sfRenderWindow_getView). We deep-copy via sfView_copy so the Ruby
|
|
51
|
+
# object owns its own lifetime.
|
|
52
|
+
def self.from_borrowed(ptr)
|
|
53
|
+
raise Error, "borrowed view pointer is NULL" if ptr.null?
|
|
54
|
+
copy = C::Graphics.sfView_copy(ptr)
|
|
55
|
+
new(_handle: copy)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def center
|
|
59
|
+
Vector2.from_native(C::Graphics.sfView_getCenter(@handle))
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def center=(value)
|
|
63
|
+
C::Graphics.sfView_setCenter(@handle, _vec2(value).to_native_f)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def size
|
|
67
|
+
Vector2.from_native(C::Graphics.sfView_getSize(@handle))
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def size=(value)
|
|
71
|
+
C::Graphics.sfView_setSize(@handle, _vec2(value).to_native_f)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def rotation
|
|
75
|
+
C::Graphics.sfView_getRotation(@handle)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def rotation=(degrees)
|
|
79
|
+
C::Graphics.sfView_setRotation(@handle, degrees.to_f)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Viewport in normalised [0,1] window coordinates. Default is the full
|
|
83
|
+
# window (Rect.new([0,0], [1,1])). Use [0, 0, 0.5, 1] for left half,
|
|
84
|
+
# [0.75, 0, 0.25, 0.25] for a top-right mini-map, etc.
|
|
85
|
+
def viewport
|
|
86
|
+
Rect.from_native(C::Graphics.sfView_getViewport(@handle))
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def viewport=(rect)
|
|
90
|
+
raise ArgumentError, "View#viewport= needs a SFML::Rect" unless rect.is_a?(Rect)
|
|
91
|
+
native = C::Graphics::FloatRect.new
|
|
92
|
+
native[:position][:x] = rect.x.to_f
|
|
93
|
+
native[:position][:y] = rect.y.to_f
|
|
94
|
+
native[:size][:x] = rect.width.to_f
|
|
95
|
+
native[:size][:y] = rect.height.to_f
|
|
96
|
+
C::Graphics.sfView_setViewport(@handle, native)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Pan the camera by an offset in world units.
|
|
100
|
+
def move(offset)
|
|
101
|
+
C::Graphics.sfView_move(@handle, _vec2(offset).to_native_f)
|
|
102
|
+
self
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Rotate the camera by `degrees`, relative to current rotation.
|
|
106
|
+
def rotate(degrees)
|
|
107
|
+
C::Graphics.sfView_rotate(@handle, degrees.to_f)
|
|
108
|
+
self
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Multiply the visible area by `factor`. Factor < 1.0 zooms in;
|
|
112
|
+
# factor > 1.0 zooms out.
|
|
113
|
+
def zoom(factor)
|
|
114
|
+
C::Graphics.sfView_zoom(@handle, factor.to_f)
|
|
115
|
+
self
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
attr_reader :handle # :nodoc:
|
|
119
|
+
|
|
120
|
+
private
|
|
121
|
+
|
|
122
|
+
def _vec2(value)
|
|
123
|
+
value.is_a?(Vector2) ? value : Vector2.new(*value)
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
module SFML
|
|
2
|
+
module Network
|
|
3
|
+
# An IPv4 address. The CSFML side stores it as a fixed-size 16-byte
|
|
4
|
+
# NUL-terminated dotted-decimal string ("192.168.1.42").
|
|
5
|
+
#
|
|
6
|
+
# SFML::Network::IpAddress.from_string("192.168.1.42")
|
|
7
|
+
# SFML::Network::IpAddress.from_bytes(192, 168, 1, 42)
|
|
8
|
+
# SFML::Network::IpAddress::LOCALHOST # 127.0.0.1
|
|
9
|
+
# SFML::Network::IpAddress::ANY # 0.0.0.0
|
|
10
|
+
# SFML::Network::IpAddress::BROADCAST # 255.255.255.255
|
|
11
|
+
# SFML::Network::IpAddress.local # local network IP
|
|
12
|
+
class IpAddress
|
|
13
|
+
def self.from_string(s)
|
|
14
|
+
wrap(C::Network.sfIpAddress_fromString(s.to_s))
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.from_bytes(a, b, c, d)
|
|
18
|
+
wrap(C::Network.sfIpAddress_fromBytes(Integer(a), Integer(b), Integer(c), Integer(d)))
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.from_integer(n)
|
|
22
|
+
wrap(C::Network.sfIpAddress_fromInteger(Integer(n)))
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# The IP this machine sees on its local network.
|
|
26
|
+
def self.local
|
|
27
|
+
wrap(C::Network.sfIpAddress_getLocalAddress)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Public-facing IP. Performs an outbound query, so optionally
|
|
31
|
+
# cap with a timeout. Slow — minutes if the network is down.
|
|
32
|
+
def self.public(timeout: SFML::Time.zero)
|
|
33
|
+
wrap(C::Network.sfIpAddress_getPublicAddress(timeout.to_native))
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# @!visibility private
|
|
37
|
+
def self.wrap(struct)
|
|
38
|
+
ip = allocate
|
|
39
|
+
ip.instance_variable_set(:@struct, struct)
|
|
40
|
+
ip.freeze
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def to_s
|
|
44
|
+
@struct[:address].to_ptr.read_string_to_null
|
|
45
|
+
end
|
|
46
|
+
alias inspect to_s
|
|
47
|
+
|
|
48
|
+
def to_i
|
|
49
|
+
C::Network.sfIpAddress_toInteger(@struct)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def ==(other)
|
|
53
|
+
other.is_a?(IpAddress) && to_s == other.to_s
|
|
54
|
+
end
|
|
55
|
+
alias eql? ==
|
|
56
|
+
def hash = to_s.hash
|
|
57
|
+
|
|
58
|
+
# @!visibility private
|
|
59
|
+
attr_reader :struct
|
|
60
|
+
|
|
61
|
+
NONE = wrap(C::Network.sfIpAddress_None)
|
|
62
|
+
ANY = wrap(C::Network.sfIpAddress_Any)
|
|
63
|
+
LOCALHOST = wrap(C::Network.sfIpAddress_LocalHost)
|
|
64
|
+
BROADCAST = wrap(C::Network.sfIpAddress_Broadcast)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
module SFML
|
|
2
|
+
module Network
|
|
3
|
+
# The server side of TCP. Listens on a port; #accept blocks until a
|
|
4
|
+
# client connects, then returns a fresh TcpSocket for that peer.
|
|
5
|
+
#
|
|
6
|
+
# listener = SFML::Network::TcpListener.new
|
|
7
|
+
# listener.listen(port: 8080)
|
|
8
|
+
# loop do
|
|
9
|
+
# status, peer = listener.accept
|
|
10
|
+
# break unless status == :done
|
|
11
|
+
# handle_client(peer)
|
|
12
|
+
# end
|
|
13
|
+
class TcpListener
|
|
14
|
+
def initialize
|
|
15
|
+
ptr = C::Network.sfTcpListener_create
|
|
16
|
+
raise Error, "sfTcpListener_create returned NULL" if ptr.null?
|
|
17
|
+
@handle = FFI::AutoPointer.new(ptr, C::Network.method(:sfTcpListener_destroy))
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Bind the listener to a port and start listening.
|
|
21
|
+
# `address` (default: any local interface) restricts which interface
|
|
22
|
+
# to listen on — pass IpAddress::LOCALHOST for loopback-only.
|
|
23
|
+
def listen(port:, address: IpAddress::ANY)
|
|
24
|
+
addr = address.is_a?(IpAddress) ? address : IpAddress.from_string(address)
|
|
25
|
+
code = C::Network.sfTcpListener_listen(@handle, Integer(port), addr.struct)
|
|
26
|
+
C::Network::STATUSES[code]
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def close
|
|
30
|
+
C::Network.sfTcpListener_close(@handle)
|
|
31
|
+
self
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Returns [status, TcpSocket] — the socket is non-nil only when
|
|
35
|
+
# status == :done.
|
|
36
|
+
def accept
|
|
37
|
+
out_ptr = FFI::MemoryPointer.new(:pointer)
|
|
38
|
+
code = C::Network.sfTcpListener_accept(@handle, out_ptr)
|
|
39
|
+
status = C::Network::STATUSES[code]
|
|
40
|
+
|
|
41
|
+
return [status, nil] unless status == :done
|
|
42
|
+
sock_ptr = out_ptr.read_pointer
|
|
43
|
+
return [status, nil] if sock_ptr.null?
|
|
44
|
+
|
|
45
|
+
sock = TcpSocket.allocate
|
|
46
|
+
sock.instance_variable_set(:@handle, FFI::AutoPointer.new(sock_ptr, C::Network.method(:sfTcpSocket_destroy)))
|
|
47
|
+
[status, sock]
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def blocking? = C::Network.sfTcpListener_isBlocking(@handle)
|
|
51
|
+
|
|
52
|
+
def blocking=(value)
|
|
53
|
+
C::Network.sfTcpListener_setBlocking(@handle, value ? true : false)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def local_port = C::Network.sfTcpListener_getLocalPort(@handle)
|
|
57
|
+
|
|
58
|
+
attr_reader :handle # :nodoc:
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|