SFGF
Collider.hpp
Go to the documentation of this file.
1 #ifndef COLLIDER_HPP
2 #define COLLIDER_HPP
3 
4 /* =========================================================== *
5  * SFGF (c) Kamil Koczurek | koczurekk@gmail.com *
6  * GNU GPL v3 License http://www.gnu.org/licenses/gpl-3.0.html *
7  * =========================================================== */
8 
11 
12 #include <SFML/Graphics/Transform.hpp>
13 #include <SFML/System/Vector2.hpp>
14 #include <iostream>
15 #include <vector>
16 
19 namespace sfgf {
37  class Collider {
38  std::vector<sf::Vector2f> m_arr;
39  sf::FloatRect m_globalBounds;
40 
41  static bool nonnegatively_dimensional_rect_intersection(sf::FloatRect lhs, sf::FloatRect rhs) {
42  lhs.top = std::abs(lhs.top);
43  lhs.left = std::abs(lhs.left);
44 
45  rhs.top = std::abs(rhs.top);
46  rhs.left = std::abs(rhs.left);
47 
48  if(lhs.left > rhs.left + rhs.width) {
49  return false;
50  }
51  if(lhs.left + lhs.width < rhs.left) {
52  return false;
53  }
54  if(lhs.top > rhs.top + rhs.height) {
55  return false;
56  }
57  if(lhs.top + lhs.height < rhs.top) {
58  return false;
59  }
60 
61  return true;
62  }
63 
64  void updateGlobalBounds() {
65  sf::Vector2f max {0, 0};
66  sf::Vector2f pos {m_arr.empty() ? sf::Vector2f{0, 0} : m_arr[0]};
67 
68  for(const sf::Vector2f& pt: m_arr) {
69  max.x = pt.x > max.x ? pt.x : max.x;
70  max.y = pt.y > max.y ? pt.y : max.y;
71 
72  pos.x = pt.x < pos.x ? pt.x : pos.x;
73  pos.y = pt.y < pos.y ? pt.y : pos.y;
74  }
75 
76  m_globalBounds.top = pos.y;
77  m_globalBounds.left = pos.x;
78  m_globalBounds.width = max.x - pos.x;
79  m_globalBounds.height = max.y - pos.y;
80  }
81 
82  public:
92  static Collider circle(float radius, size_t cnt = 128);
93 
102  static Collider rectangle(sf::Vector2f size);
103 
115  static bool lineIntersection(sf::Vector2f p1, sf::Vector2f q1, sf::Vector2f p2, sf::Vector2f q2);
116 
125  void applyTransform(const sf::Transform& t);
126 
137  void pushBack(sf::Vector2f pt);
138 
142  void clear();
143 
151  sf::FloatRect getGlobalBounds() const {
152  return m_globalBounds;
153  }
154 
161  bool intersects(const Collider& poly) const;
162 
169  bool contains(sf::Vector2f point) const;
170 
177  bool collides(const Collider& poly) const;
178  };
179 
180  Collider Collider::circle(float radius, size_t cnt) {
181  Collider result;
182  for(auto i = 0u; i < cnt; ++i) {
183  result.pushBack({
184  std::sin(6.28 / cnt * i) * radius + radius,
185  std::cos(6.28 / cnt * i) * radius + radius
186  });
187  }
188 
189  return result;
190  }
191  Collider Collider::rectangle(sf::Vector2f size) {
192  Collider result;
193  result.pushBack({0, 0});
194  result.pushBack({size.x, 0});
195  result.pushBack(size);
196  result.pushBack({0, size.y});
197 
198  return result;
199  }
200 
201  bool Collider::lineIntersection(sf::Vector2f p1, sf::Vector2f q1, sf::Vector2f p2, sf::Vector2f q2) {
202  auto isOnSegment = [](sf::Vector2f p, sf::Vector2f q, sf::Vector2f r) {
203  return (q.x <= std::max(p.x, r.x) && q.x >= std::min(p.x, r.x) &&
204  q.y <= std::max(p.y, r.y) && q.y >= std::min(p.y, r.y));
205  };
206  auto orientation = [](sf::Vector2f p, sf::Vector2f q, sf::Vector2f r) {
207  int val = (q.y - p.y) * (r.x - q.x) -
208  (q.x - p.x) * (r.y - q.y);
209 
210  if (val == 0) {
211  return 0;
212  }
213 
214  return (val > 0) ? 1 : 2;
215  };
216 
217  // Find the four orientations needed for general and
218  // special cases
219  int o1 = orientation(p1, q1, p2);
220  int o2 = orientation(p1, q1, q2);
221  int o3 = orientation(p2, q2, p1);
222  int o4 = orientation(p2, q2, q1);
223 
224  // General case
225  if (o1 != o2 && o3 != o4)
226  return true;
227 
228  // Special Cases
229  if ((o1 == 0 && isOnSegment(p1, p2, q1))
230  || (o2 == 0 && isOnSegment(p1, q2, q1))
231  || (o3 == 0 && isOnSegment(p2, p1, q2))
232  || (o4 == 0 && isOnSegment(p2, q1, q2))) {
233  return true;
234  }
235 
236  return false;
237  }
238 
239  void Collider::applyTransform(const sf::Transform& t) {
240  std::transform(
241  m_arr.begin(),
242  m_arr.end(),
243  m_arr.begin(),
244  [&t](sf::Vector2f p) {
245  return t.transformPoint(p);
246  }
247  );
248 
249  updateGlobalBounds();
250  }
251  void Collider::pushBack(sf::Vector2f pt) {
252  m_arr.push_back(pt);
253  updateGlobalBounds();
254  }
256  m_arr.clear();
257  updateGlobalBounds();
258  }
259 
260  bool Collider::intersects(const Collider& poly) const {
261  if(!nonnegatively_dimensional_rect_intersection(getGlobalBounds(), poly.getGlobalBounds())
262  || poly.m_arr.empty()
263  || m_arr.empty()) {
264  return false;
265  }
266 
267  auto arr = m_arr;
268  arr.push_back(arr.front());
269 
270  auto poly_arr = poly.m_arr;
271  poly_arr.push_back(poly_arr.front());
272 
273  for(auto i = 1u; i < arr.size(); ++i) {
274  auto p1 = arr[i - 1], q1 = arr[i];
275 
276  for(auto j = 1u; j < poly_arr.size(); ++j) {
277  auto p2 = poly_arr[j - 1], q2 = poly_arr[j];
278 
279  if(lineIntersection(p1, q1, p2, q2)) {
280  return true;
281  }
282  }
283  }
284 
285  return false;
286  }
287  bool Collider::contains(sf::Vector2f point) const {
288  int i, j, nvert = m_arr.size();
289  bool c = false;
290 
291  for(i = 0, j = nvert - 1; i < nvert; j = i++) {
292  auto pti = m_arr[i];
293  auto ptj = m_arr[j];
294 
295  if(((pti.y >= point.y) != (ptj.y >= point.y)) &&
296  (point.x <= (ptj.x - pti.x) * (point.y - pti.y) / (ptj.y - pti.y) + pti.x)) {
297  c = !c;
298  }
299  }
300 
301  return c;
302  }
303 
304  bool Collider::collides(const Collider& poly) const {
306  return false;
307  }
308 
309  if(intersects(poly)) {
310  return true;
311  }
312 
313  for(auto v: poly.m_arr) {
314  if(!contains(v)) {
315  return false;
316  }
317  }
318 
319  return true;
320  }
321 }
322 
323 #endif // COLLIDER_HPP
static bool lineIntersection(sf::Vector2f p1, sf::Vector2f q1, sf::Vector2f p2, sf::Vector2f q2)
Check two lines for intersection.
Definition: Collider.hpp:201
static Collider rectangle(sf::Vector2f size)
Returns collider in shape of rectangle.
Definition: Collider.hpp:191
Contains all SFGF classes.
void clear()
Clears points.
Definition: Collider.hpp:255
static Collider circle(float radius, size_t cnt=128)
Returns collider in shape of circle.
Definition: Collider.hpp:180
bool intersects(const Collider &poly) const
Check for intersection.
Definition: Collider.hpp:260
void applyTransform(const sf::Transform &t)
Applies transform.
Definition: Collider.hpp:239
bool collides(const Collider &poly) const
Check if two colliders collide.
Definition: Collider.hpp:304
Handles collisions.
Definition: Collider.hpp:37
void pushBack(sf::Vector2f pt)
Adds point to internal array.
Definition: Collider.hpp:251
sf::FloatRect getGlobalBounds() const
Returns global bounds.
Definition: Collider.hpp:151
bool contains(sf::Vector2f point) const
Check if point is inside of collider.
Definition: Collider.hpp:287