227 lines
8.0 KiB
C++
227 lines
8.0 KiB
C++
/* +------------------------------------------------------+
|
|
____/ \____ /| - Open source game framework licensed to freely use, |
|
|
\ / / | copy, modify and sell without restriction |
|
|
+--\ ^__^ /--+ | |
|
|
| ~/ \~ | | - created for <https://foam.shampoo.ooo> |
|
|
| ~~~~~~~~~~~~ | +------------------------------------------------------+
|
|
| SPACE ~~~~~ | /
|
|
| ~~~~~~~ BOX |/
|
|
+-------------*/
|
|
|
|
#pragma once
|
|
|
|
#include <iostream>
|
|
#include <algorithm>
|
|
#include <stdexcept>
|
|
#include <functional>
|
|
#include <optional>
|
|
|
|
#include "SDL.h"
|
|
|
|
#define GLM_ENABLE_EXPERIMENTAL
|
|
#include "glm/common.hpp"
|
|
#include "glm/vec2.hpp"
|
|
|
|
class Segment;
|
|
|
|
/*!
|
|
* A ::Box object represents a rectangle, a two-dimensional four-sided polygon with two parallel sides.
|
|
*
|
|
* Rotating by three dimensions into the Z-dimension is not currently supported and must be implemented by the user,
|
|
* but 3D transformation is planned to be added in a future update.
|
|
*/
|
|
class Box : public SDL_FRect
|
|
{
|
|
|
|
private:
|
|
|
|
bool invert_y_state = true;
|
|
|
|
public:
|
|
|
|
/*!
|
|
* Construct a Box object by giving a corner coordinate (top left if Y-axis is not inverted, bottom left otherwise) and
|
|
* size (as width, height). The invert_y argument indicates that the Y-coordinate increases from the bottom of the screen
|
|
* to the top (like in OpenGL) rather than from the top to the bottom (like in Pygame).
|
|
*
|
|
* @param corner top left coordinate in the x, y axis
|
|
* @param size size of the box in width and height
|
|
* @param invert_y if true, y-coordinates increase as they move up on the screen, rather than decrease
|
|
*/
|
|
Box(const glm::vec2& corner = {0.0f, 0.0f}, const glm::vec2& size = {0.0f, 0.0f}, bool invert_y = true);
|
|
|
|
/*!
|
|
* Construct a Box object lfrom float arguments: x, y, width, height. If invert_y is set, the Y-axis increases from bottom to
|
|
* top instead of top to bottom.
|
|
*
|
|
* @see Box::Box(const glm::vec2&, const glm::vec2&, bool)
|
|
*
|
|
* @param x
|
|
* @param y
|
|
* @param width size of the x-dimension
|
|
* @param height size of the y-dimension
|
|
* @param invert_y if true, y-coordinates increase as they move up on the screen, rather than decrease
|
|
*/
|
|
Box(float x, float y, float width, float height, bool invert_y = true);
|
|
|
|
/*!
|
|
* Construct a ::Box object by passing an SDL_Rect struct, which is of the form {x, y, w, h} and limited to int arguments.
|
|
*
|
|
* @param rect see SDL's rect documentation http://doc.here/sdlwiki/SDL_FRect.html
|
|
* @param invert_y if true, y-coordinates increase as they move up on the screen, rather than decrease
|
|
*/
|
|
Box(const SDL_Rect& rect, bool invert_y = true);
|
|
|
|
void invert_y(bool);
|
|
bool inverted_y() const;
|
|
float width() const;
|
|
void width(float);
|
|
float height() const;
|
|
void height(float);
|
|
|
|
/*!
|
|
* @return the size as a vector `{width, height}`
|
|
*/
|
|
glm::vec2 size() const;
|
|
|
|
/*!
|
|
* Set the size. Negative values will be clamped to zero.
|
|
*
|
|
* @param size new size in width, height
|
|
* @param preserve_center if true, expand or contract the box from the center, leaving its center value unchanged
|
|
*/
|
|
void size(const glm::vec2& size, bool preserve_center = false);
|
|
|
|
/*!
|
|
* If the flag is not set, this will return width divided by height. Otherwise, it will check which side is longer and return the longer side
|
|
* divided by the shorter side.
|
|
*
|
|
* @param larger_ratio if true, divide by the shorter side, causing the result to always be 1.0 or greater
|
|
*/
|
|
float aspect(bool larger_ratio = false) const;
|
|
|
|
float area() const;
|
|
float top() const;
|
|
float right() const;
|
|
float bottom() const;
|
|
float left() const;
|
|
float cx() const;
|
|
float cy() const;
|
|
void top(float, bool = false);
|
|
void drag_top(float);
|
|
void right(float, bool = false);
|
|
void drag_right(float);
|
|
void bottom(float, bool = false);
|
|
void drag_bottom(float);
|
|
void left(float, bool = false);
|
|
void drag_left(float);
|
|
void cx(float);
|
|
void cy(float);
|
|
glm::vec2 nw() const;
|
|
glm::vec2 north() const;
|
|
glm::vec2 ne() const;
|
|
glm::vec2 east() const;
|
|
glm::vec2 se() const;
|
|
glm::vec2 south() const;
|
|
glm::vec2 sw() const;
|
|
glm::vec2 west() const;
|
|
glm::vec2 center() const;
|
|
void nw(const glm::vec2&);
|
|
void north(const glm::vec2&);
|
|
void ne(const glm::vec2&);
|
|
void east(const glm::vec2&);
|
|
void se(const glm::vec2&);
|
|
void south(const glm::vec2&);
|
|
void sw(const glm::vec2&);
|
|
void west(const glm::vec2&);
|
|
void center(const glm::vec2&);
|
|
|
|
/*!
|
|
* Called when a Box instance is converted into an SDL_Rect. Allows passing Box to SDL functions that
|
|
* expect an SDL_Rect instead of an SDL_FRect. Corner values are converted from float to int using a static
|
|
* cast.
|
|
*
|
|
* @return box with all corners cast to int and converted to SDL_Rect
|
|
*/
|
|
operator SDL_Rect() const;
|
|
|
|
void clear();
|
|
|
|
/*!
|
|
* Scale the box by multiplying size by {delta.x, delta.y}. If preserve center is set, the box will be scaled
|
|
* around the center, otherwise the top left corner will remain in place.
|
|
*
|
|
* @param delta amount to scale
|
|
* @param preserve_center after scaling, move the box so the center is the same as it was before scaling
|
|
*/
|
|
void scale(glm::vec2 delta, bool preserve_center = false);
|
|
|
|
/*!
|
|
* Scale the box by multiplying both width and height by delta. If preserve center is set, the box will be scaled
|
|
* around the center, otherwise the top left corner will remain in place.
|
|
*
|
|
* @param delta amount to scale
|
|
* @param preserve_center after scaling, move the box so the center is the same as it was before scaling
|
|
*/
|
|
void scale(float delta, bool preserve_center = false);
|
|
|
|
void expand(glm::vec2, bool = false);
|
|
void expand(float, bool = false);
|
|
void move(const glm::vec2&);
|
|
Box stamp(const glm::vec2&) const;
|
|
bool fits(const Box&) const;
|
|
void crop(const Box&);
|
|
|
|
/*!
|
|
* Collide a 2D point with the box. Includes the edges, so points on the edges will return `true`.
|
|
*
|
|
* @return `true` if point is inside the box, `false` otherwise
|
|
*/
|
|
bool collide(const glm::vec2& point) const;
|
|
|
|
/*!
|
|
* Collide a line segment with the box. If intrseection is passed and there is a collision, intersection will be filled with
|
|
* the coordinates of the first intersection found unless the segment is fully within the box not touching any edges.
|
|
*
|
|
* @param segment line segment to collide with the box
|
|
* @param intersection optional reference to 2D vector to fill with the first intersection point
|
|
* @return `true` if segment collides with the box, `false` otherwies
|
|
*/
|
|
bool collide(const Segment& segment, std::optional<std::reference_wrapper<glm::vec2>> intersection = std::nullopt) const;
|
|
|
|
/*!
|
|
* Collide with another box object. If overlap is passed, it is set to the box representing the area where the two boxes overlap.
|
|
*
|
|
* @param box another box object to collide with
|
|
* @param overlap optional reference to a box object to be filled with the overlapping area
|
|
* @return `true` if box collides with the passed box, `false` otherwise
|
|
*/
|
|
bool collide(const Box& box, std::optional<std::reference_wrapper<Box>> overlap = std::nullopt) const;
|
|
|
|
std::string string() const;
|
|
|
|
/*!
|
|
* Convert the box to a string when it is passed in a string context.
|
|
*
|
|
* @return string representation of the box
|
|
*/
|
|
operator std::string() const;
|
|
|
|
};
|
|
|
|
namespace std
|
|
{
|
|
std::ostream& operator<<(std::ostream&, const Box&);
|
|
}
|
|
|
|
/* Add Box class to the sb namespace. This should be the default location, but Box is left in the global namespace
|
|
* for backward compatibility.
|
|
*/
|
|
namespace sb
|
|
{
|
|
using ::Box;
|
|
}
|
|
|
|
#include "extension.hpp"
|
|
#include "Segment.hpp"
|