Add comments, change renderers to have Save instead of saving on destruction
This commit is contained in:
parent
e5bf048d21
commit
895cddc739
@ -3,4 +3,7 @@ DerivePointerAlignment: false
|
|||||||
PointerAlignment: Left
|
PointerAlignment: Left
|
||||||
IndentWidth: 4 # spaces per indent level
|
IndentWidth: 4 # spaces per indent level
|
||||||
TabWidth: 4 # width of a tab character
|
TabWidth: 4 # width of a tab character
|
||||||
UseTab: Never # options: Never, ForIndentation, Alwayss
|
UseTab: Never # options: Never, ForIndentation, Alwayss
|
||||||
|
AccessModifierOffset: -4
|
||||||
|
AllowShortFunctionsOnASingleLine: Inline
|
||||||
|
BreakTemplateDeclarations: Yes
|
||||||
|
|||||||
@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.10)
|
|||||||
|
|
||||||
project(CppDrawing)
|
project(CppDrawing)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 20)
|
set(CMAKE_CXX_STANDARD 23)
|
||||||
|
|
||||||
add_executable(drawing
|
add_executable(drawing
|
||||||
"math/transforms.cpp"
|
"math/transforms.cpp"
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <format>
|
||||||
|
|
||||||
#include "shapes/circle.hpp"
|
#include "shapes/circle.hpp"
|
||||||
#include "shapes/line.hpp"
|
#include "shapes/line.hpp"
|
||||||
@ -19,58 +20,84 @@ InputFile::InputFile(const std::filesystem::path& path) : m_file(path), m_cmdsPr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// istream >> operator for math::Vector
|
||||||
static std::istream& operator>>(std::istream& is, math::Vector& vec)
|
static std::istream& operator>>(std::istream& is, math::Vector& vec)
|
||||||
{
|
{
|
||||||
return is >> vec.x >> vec.y;
|
return is >> vec.x >> vec.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// command name -> command handler map
|
||||||
using CommandMap = std::map<std::string, std::function<void(std::istream&)>>;
|
using CommandMap = std::map<std::string, std::function<void(std::istream&)>>;
|
||||||
|
|
||||||
template <class T> static const char* GetTypeName()
|
/**
|
||||||
|
* @brief Get the type name as a string
|
||||||
|
*
|
||||||
|
* @tparam T Type
|
||||||
|
* @return Type name
|
||||||
|
*/
|
||||||
|
template <class T>
|
||||||
|
static const char* GetTypeName()
|
||||||
{
|
{
|
||||||
return typeid(T).name();
|
return typeid(T).name();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <> const char* GetTypeName<int>()
|
template <>
|
||||||
|
const char* GetTypeName<int>()
|
||||||
{
|
{
|
||||||
return "integer";
|
return "integer";
|
||||||
}
|
}
|
||||||
|
|
||||||
template <> const char* GetTypeName<float>()
|
template <>
|
||||||
|
const char* GetTypeName<float>()
|
||||||
{
|
{
|
||||||
return "float";
|
return "float";
|
||||||
}
|
}
|
||||||
|
|
||||||
template <> const char* GetTypeName<math::Vector>()
|
template <>
|
||||||
|
const char* GetTypeName<math::Vector>()
|
||||||
{
|
{
|
||||||
return "vector";
|
return "vector";
|
||||||
}
|
}
|
||||||
|
|
||||||
template <> const char* GetTypeName<std::string>()
|
template <>
|
||||||
|
const char* GetTypeName<std::string>()
|
||||||
{
|
{
|
||||||
return "string";
|
return "string";
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T> static T ReadVal(std::istream& is)
|
/**
|
||||||
|
* @brief Read a value from an `istream` and check for errors
|
||||||
|
*
|
||||||
|
* @tparam T Type of the value to read
|
||||||
|
* @param is Input stream
|
||||||
|
* @return Read value
|
||||||
|
*/
|
||||||
|
template <class T>
|
||||||
|
static T ReadVal(std::istream& is)
|
||||||
{
|
{
|
||||||
T v;
|
T v;
|
||||||
is >> v;
|
is >> v;
|
||||||
|
|
||||||
// std::cout << "Reading " << GetTypeName<T>() << ' ' << v << std::endl;
|
|
||||||
|
|
||||||
if (is.bad())
|
if (is.bad())
|
||||||
{
|
throw std::runtime_error(std::format("Could not parse {} from stream", GetTypeName<T>()));
|
||||||
throw std::runtime_error("Could not parse " + std::string(GetTypeName<T>()) + " from stream");
|
|
||||||
}
|
|
||||||
|
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Register a command in a command map
|
||||||
|
*
|
||||||
|
* @tparam TArgs Argument types
|
||||||
|
* @tparam THandler Handler type
|
||||||
|
* @param cmdMap Command map
|
||||||
|
* @param cmd Command name
|
||||||
|
* @param handler Command handler
|
||||||
|
*/
|
||||||
template <class... TArgs, class THandler>
|
template <class... TArgs, class THandler>
|
||||||
static void RegisterCommand(CommandMap& cmdMap, const std::string& cmd, THandler&& handler)
|
static void RegisterCommand(CommandMap& cmdMap, const std::string& cmd, THandler&& handler)
|
||||||
{
|
{
|
||||||
cmdMap[cmd] = [handler = std::forward<THandler>(handler)](std::istream& is) {
|
cmdMap[cmd] = [handler = std::forward<THandler>(handler)](std::istream& is) {
|
||||||
std::apply(handler, std::tuple<TArgs...>{ReadVal<TArgs>(is)...}); // pres tuple kvuli poradi evaluace...
|
std::apply(handler, std::tuple<TArgs...>{ReadVal<TArgs>(is)...}); // tuple because of order of evaluation...
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,8 +131,9 @@ shapes::Group InputFile::Parse()
|
|||||||
|
|
||||||
RegisterCommand<int, int>(cmds, "translate", [&shapes](int ox, int oy) { shapes.Translate(math::Vector{ox, oy}); });
|
RegisterCommand<int, int>(cmds, "translate", [&shapes](int ox, int oy) { shapes.Translate(math::Vector{ox, oy}); });
|
||||||
|
|
||||||
RegisterCommand<int, int, float>(
|
RegisterCommand<int, int, float>(cmds, "rotate", [&shapes](int cx, int cy, float deg) {
|
||||||
cmds, "rotate", [&shapes](int cx, int cy, float deg) { shapes.Rotate(math::Vector{cx, cy}, deg * math::DEG_TO_RAD); });
|
shapes.Rotate(math::Vector{cx, cy}, deg * math::DEG_TO_RAD);
|
||||||
|
});
|
||||||
|
|
||||||
RegisterCommand<int, int, float>(cmds, "scale", [&shapes](int cx, int cy, float factor) {
|
RegisterCommand<int, int, float>(cmds, "scale", [&shapes](int cx, int cy, float factor) {
|
||||||
if (factor < std::numeric_limits<decltype(factor)>().epsilon())
|
if (factor < std::numeric_limits<decltype(factor)>().epsilon())
|
||||||
@ -114,24 +142,39 @@ shapes::Group InputFile::Parse()
|
|||||||
shapes.Scale(math::Vector{cx, cy}, factor);
|
shapes.Scale(math::Vector{cx, cy}, factor);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/* ---------------------------- */
|
||||||
|
|
||||||
std::string line;
|
std::string line;
|
||||||
|
size_t lineNum = 0;
|
||||||
|
|
||||||
while (std::getline(m_file, line))
|
try
|
||||||
{
|
{
|
||||||
// TODO: find # mid line
|
while (std::getline(m_file, line))
|
||||||
if (line.empty() || line[0] == '#')
|
{
|
||||||
continue;
|
++lineNum;
|
||||||
|
|
||||||
std::istringstream iss(line);
|
if (line.empty() || line[0] == '#')
|
||||||
std::string cmdName = ReadVal<std::string>(iss);
|
continue;
|
||||||
|
|
||||||
auto cmd = cmds.find(cmdName);
|
auto commentPos = line.find('#');
|
||||||
|
if (commentPos != std::string::npos)
|
||||||
|
line = line.substr(0, commentPos);
|
||||||
|
|
||||||
if (cmd == cmds.end())
|
std::istringstream iss(line);
|
||||||
throw std::runtime_error("Unknown command: " + cmdName);
|
std::string cmdName = ReadVal<std::string>(iss);
|
||||||
|
|
||||||
cmd->second(iss);
|
auto cmd = cmds.find(cmdName);
|
||||||
++m_cmdsProcessed;
|
|
||||||
|
if (cmd == cmds.end())
|
||||||
|
throw std::runtime_error("Unknown command: " + cmdName);
|
||||||
|
|
||||||
|
cmd->second(iss);
|
||||||
|
++m_cmdsProcessed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const std::exception& e)
|
||||||
|
{
|
||||||
|
throw std::runtime_error(std::format("Line {}: {}", lineNum, e.what()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return shapes;
|
return shapes;
|
||||||
|
|||||||
@ -5,19 +5,21 @@
|
|||||||
|
|
||||||
#include "shapes/group.hpp"
|
#include "shapes/group.hpp"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Input file parser
|
||||||
|
*
|
||||||
|
* Parses an input file containing drawing commands and constructs a `Group` of shapes.
|
||||||
|
*/
|
||||||
class InputFile
|
class InputFile
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
InputFile(const std::filesystem::path& path);
|
InputFile(const std::filesystem::path& path);
|
||||||
|
|
||||||
shapes::Group Parse();
|
shapes::Group Parse();
|
||||||
|
|
||||||
size_t GetNumProcessedCmds() const
|
size_t GetNumProcessedCmds() const { return m_cmdsProcessed; }
|
||||||
{
|
|
||||||
return m_cmdsProcessed;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::ifstream m_file;
|
std::ifstream m_file;
|
||||||
size_t m_cmdsProcessed;
|
size_t m_cmdsProcessed;
|
||||||
};
|
};
|
||||||
|
|||||||
35
main.cpp
35
main.cpp
@ -8,13 +8,30 @@
|
|||||||
#include "renderers/svg_renderer.hpp"
|
#include "renderers/svg_renderer.hpp"
|
||||||
#include <charconv>
|
#include <charconv>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Render shapes using a specific renderer and save to a file
|
||||||
|
*
|
||||||
|
* @tparam T Renderer type
|
||||||
|
* @param shapes Shapes to render
|
||||||
|
* @param path Output file path
|
||||||
|
* @param width Width
|
||||||
|
* @param height Height
|
||||||
|
*/
|
||||||
template <class T>
|
template <class T>
|
||||||
void Render(const shapes::Group& shapes, const std::filesystem::path& path, size_t width, size_t height)
|
void Render(const shapes::Group& shapes, const std::filesystem::path& path, size_t width, size_t height)
|
||||||
{
|
{
|
||||||
T renderer(path, width, height);
|
T renderer(width, height);
|
||||||
shapes.Draw(renderer);
|
shapes.Draw(renderer);
|
||||||
|
renderer.Save(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Parse a single dimension from a string
|
||||||
|
*
|
||||||
|
* @param start Pointer to the start of the dimension string
|
||||||
|
* @param end Pointer to the end of the dimension string
|
||||||
|
* @return Parsed dimension
|
||||||
|
*/
|
||||||
static size_t ParseDim(const char* start, const char* end)
|
static size_t ParseDim(const char* start, const char* end)
|
||||||
{
|
{
|
||||||
size_t val;
|
size_t val;
|
||||||
@ -28,14 +45,18 @@ static size_t ParseDim(const char* start, const char* end)
|
|||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a size string
|
||||||
|
*
|
||||||
|
* @param sizeStr Size string in the format of `<width>x<height>`
|
||||||
|
* @return Tuple of width and height
|
||||||
|
*/
|
||||||
static std::tuple<size_t, size_t> ParseSize(const std::string& sizeStr)
|
static std::tuple<size_t, size_t> ParseSize(const std::string& sizeStr)
|
||||||
{
|
{
|
||||||
auto xPos = sizeStr.find('x');
|
auto xPos = sizeStr.find('x');
|
||||||
|
|
||||||
if (xPos == std::string::npos)
|
if (xPos == std::string::npos)
|
||||||
{
|
|
||||||
throw std::runtime_error("Size must be in format of <width>x<height>");
|
throw std::runtime_error("Size must be in format of <width>x<height>");
|
||||||
}
|
|
||||||
|
|
||||||
size_t width = ParseDim(sizeStr.data(), sizeStr.data() + xPos);
|
size_t width = ParseDim(sizeStr.data(), sizeStr.data() + xPos);
|
||||||
size_t height = ParseDim(sizeStr.data() + xPos + 1, sizeStr.data() + sizeStr.size());
|
size_t height = ParseDim(sizeStr.data() + xPos + 1, sizeStr.data() + sizeStr.size());
|
||||||
@ -43,6 +64,9 @@ static std::tuple<size_t, size_t> ParseSize(const std::string& sizeStr)
|
|||||||
return std::make_tuple(width, height);
|
return std::make_tuple(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Run the drawing process
|
||||||
|
*/
|
||||||
static void Run(const std::string& inputFile, const std::string& outputFile, const std::string& sizeStr)
|
static void Run(const std::string& inputFile, const std::string& outputFile, const std::string& sizeStr)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@ -56,7 +80,7 @@ static void Run(const std::string& inputFile, const std::string& outputFile, con
|
|||||||
Render<SvgRenderer>(shapes, outputFile, width, height);
|
Render<SvgRenderer>(shapes, outputFile, width, height);
|
||||||
else
|
else
|
||||||
Render<PgmRenderer>(shapes, outputFile, width, height);
|
Render<PgmRenderer>(shapes, outputFile, width, height);
|
||||||
|
|
||||||
std::cout << "OK" << std::endl;
|
std::cout << "OK" << std::endl;
|
||||||
std::cout << file.GetNumProcessedCmds() << std::endl;
|
std::cout << file.GetNumProcessedCmds() << std::endl;
|
||||||
}
|
}
|
||||||
@ -66,6 +90,9 @@ static void Run(const std::string& inputFile, const std::string& outputFile, con
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Main entry point
|
||||||
|
*/
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
if (argc < 4)
|
if (argc < 4)
|
||||||
|
|||||||
@ -1,10 +1,23 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <numbers>
|
||||||
|
|
||||||
namespace math
|
namespace math
|
||||||
{
|
{
|
||||||
|
|
||||||
constexpr float PI = 3.14159265358979323846f;
|
/**
|
||||||
|
* pi
|
||||||
|
*/
|
||||||
|
constexpr float PI = std::numbers::pi_v<float>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Degrees to radians conversion factor
|
||||||
|
*/
|
||||||
constexpr float DEG_TO_RAD = PI / 180.0f;
|
constexpr float DEG_TO_RAD = PI / 180.0f;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Radians to degrees conversion factor
|
||||||
|
*/
|
||||||
constexpr float RAD_TO_DEG = 1.0f / DEG_TO_RAD;
|
constexpr float RAD_TO_DEG = 1.0f / DEG_TO_RAD;
|
||||||
|
|
||||||
} // namespace math
|
} // namespace math
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
#include "transforms.hpp"
|
#include "transforms.hpp"
|
||||||
#include <cmath>
|
|
||||||
#include "constants.hpp"
|
#include "constants.hpp"
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
math::Vector math::RotatePoint(const Vector& center, float angle, const Vector& p)
|
math::Vector math::RotatePoint(const Vector& center, float angle, const Vector& p)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -5,9 +5,33 @@
|
|||||||
namespace math
|
namespace math
|
||||||
{
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rotate a point `p` around a center point `center` by `angle` radians.
|
||||||
|
*
|
||||||
|
* @param center The center point to rotate around
|
||||||
|
* @param angle The angle in radians to rotate
|
||||||
|
* @param p The point to be rotated
|
||||||
|
* @return The rotated point
|
||||||
|
*/
|
||||||
Vector RotatePoint(const Vector& center, float angle, const Vector& p);
|
Vector RotatePoint(const Vector& center, float angle, const Vector& p);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scale a point `p` relative to a center point `center` by a `factor`.
|
||||||
|
*
|
||||||
|
* @param center The center point to scale relative to
|
||||||
|
* @param factor The scaling factor
|
||||||
|
* @param p The point to be scaled
|
||||||
|
* @return The scaled point
|
||||||
|
*/
|
||||||
Vector ScalePoint(const Vector& center, float factor, const Vector& p);
|
Vector ScalePoint(const Vector& center, float factor, const Vector& p);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rotate an angle by another angle
|
||||||
|
*
|
||||||
|
* @param originalAngle The original angle in radians
|
||||||
|
* @param rotationAngle The angle in radians to rotate by
|
||||||
|
* @return The new angle in radians in range [0, 2*PI)
|
||||||
|
*/
|
||||||
float RotateAngle(float originalAngle, float rotationAngle);
|
float RotateAngle(float originalAngle, float rotationAngle);
|
||||||
|
|
||||||
|
|
||||||
} // namespace math
|
} // namespace math
|
||||||
@ -1,32 +1,21 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
|
|
||||||
namespace math
|
namespace math
|
||||||
{
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 2D Vector
|
||||||
|
*
|
||||||
|
* Represents a point or a vector in 2D. Provides basic vector operations.
|
||||||
|
*/
|
||||||
class Vector
|
class Vector
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Vector() : x{0.0f}, y{0.0f}
|
Vector() : x{0.0f}, y{0.0f} {}
|
||||||
{
|
Vector(float x, float y) : x{x}, y{y} {}
|
||||||
}
|
Vector(int x, int y) : x{static_cast<float>(x)}, y{static_cast<float>(y)} {}
|
||||||
|
|
||||||
Vector(float x, float y) : x{x}, y{y}
|
Vector operator+(const Vector& other) const { return Vector(x + other.x, y + other.y); }
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector(int x, int y) : x{static_cast<float>(x)}, y{static_cast<float>(y)}
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// float& operator[](size_t idx) { return m_v[idx]; }
|
|
||||||
// const float& operator[](size_t idx) const { return m_v[idx]; }
|
|
||||||
|
|
||||||
Vector operator+(const Vector& other) const
|
|
||||||
{
|
|
||||||
return Vector(x + other.x, y + other.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector& operator+=(const Vector& other)
|
Vector& operator+=(const Vector& other)
|
||||||
{
|
{
|
||||||
@ -35,10 +24,7 @@ class Vector
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector operator-(const Vector& other) const
|
Vector operator-(const Vector& other) const { return Vector(x - other.x, y - other.y); }
|
||||||
{
|
|
||||||
return Vector(x - other.x, y - other.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector& operator-=(const Vector& other)
|
Vector& operator-=(const Vector& other)
|
||||||
{
|
{
|
||||||
@ -47,10 +33,7 @@ class Vector
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector operator*(float factor) const
|
Vector operator*(float factor) const { return Vector(x * factor, y * factor); }
|
||||||
{
|
|
||||||
return Vector(x * factor, y * factor);
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector& operator*=(float factor)
|
Vector& operator*=(float factor)
|
||||||
{
|
{
|
||||||
@ -59,7 +42,7 @@ class Vector
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
float x, y;
|
float x, y;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -6,14 +6,10 @@
|
|||||||
|
|
||||||
class Color
|
class Color
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Color() : l{0}
|
Color() : l{0} {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Color(uint8_t l) : l{l}
|
Color(uint8_t l) : l{l} {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t l{}; // luminence
|
uint8_t l{}; // luminence
|
||||||
|
|
||||||
@ -23,18 +19,18 @@ class Color
|
|||||||
// l = alpha;
|
// l = alpha;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static uint8_t BlendChannel(uint8_t a, uint8_t b, uint8_t alpha)
|
static uint8_t BlendChannel(uint8_t a, uint8_t b, uint8_t alpha)
|
||||||
{
|
{
|
||||||
return static_cast<uint8_t>((static_cast<int>(a) * (255 - static_cast<int>(alpha)) +
|
return static_cast<uint8_t>(
|
||||||
static_cast<int>(b) * static_cast<int>(alpha)) /
|
(static_cast<int>(a) * (255 - static_cast<int>(alpha)) + static_cast<int>(b) * static_cast<int>(alpha)) /
|
||||||
255);
|
255);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class Bitmap
|
class Bitmap
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Bitmap(size_t width, size_t height, const Color& clearColor)
|
Bitmap(size_t width, size_t height, const Color& clearColor)
|
||||||
: m_width(width), m_height(height), m_data(width * height, clearColor)
|
: m_width(width), m_height(height), m_data(width * height, clearColor)
|
||||||
{
|
{
|
||||||
@ -50,36 +46,23 @@ class Bitmap
|
|||||||
// return {&m_data[row * m_width], m_width};
|
// return {&m_data[row * m_width], m_width};
|
||||||
// };
|
// };
|
||||||
|
|
||||||
Color* operator[](size_t row)
|
Color* operator[](size_t row) { return &m_data[row * m_width]; };
|
||||||
{
|
|
||||||
return &m_data[row * m_width];
|
|
||||||
};
|
|
||||||
|
|
||||||
const Color* operator[](size_t row) const
|
const Color* operator[](size_t row) const { return &m_data[row * m_width]; };
|
||||||
{
|
|
||||||
return &m_data[row * m_width];
|
|
||||||
};
|
|
||||||
|
|
||||||
size_t GetWidth() const
|
size_t GetWidth() const { return m_width; }
|
||||||
{
|
|
||||||
return m_width;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t GetHeight() const
|
size_t GetHeight() const { return m_height; }
|
||||||
{
|
|
||||||
return m_height;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Put(int x, int y, const Color& color, uint8_t alpha)
|
void Put(int x, int y, const Color& color, uint8_t alpha)
|
||||||
{
|
{
|
||||||
if (x < 0 || y < 0 || x >= m_width || y >= m_height)
|
if (x < 0 || y < 0 || x >= m_width || y >= m_height)
|
||||||
return; // out of bounds
|
return; // out of bounds
|
||||||
|
|
||||||
(*this)[y][x].Blend(color, alpha);
|
(*this)[y][x].Blend(color, alpha);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
size_t m_width, m_height;
|
size_t m_width, m_height;
|
||||||
std::vector<Color> m_data;
|
std::vector<Color> m_data;
|
||||||
};
|
};
|
||||||
@ -1,15 +1,9 @@
|
|||||||
#include "pgm_renderer.hpp"
|
#include "pgm_renderer.hpp"
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <fstream>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
PgmRenderer::PgmRenderer(const std::filesystem::path& path, size_t width, size_t height)
|
PgmRenderer::PgmRenderer(size_t width, size_t height) : m_bitmap(width, height, Color{0xFF}) {}
|
||||||
: m_file(path), m_bitmap(width, height, Color{0xFF})
|
|
||||||
{
|
|
||||||
if (m_file.bad())
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Could not open file for writing: " + path.string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PgmRenderer::DrawLine(const math::Vector& p0, const math::Vector& p1)
|
void PgmRenderer::DrawLine(const math::Vector& p0, const math::Vector& p1)
|
||||||
{
|
{
|
||||||
@ -29,10 +23,10 @@ void PgmRenderer::DrawRectangle(const math::Vector& pos, const math::Vector& siz
|
|||||||
const auto pB = pA + bX * size.x;
|
const auto pB = pA + bX * size.x;
|
||||||
const auto pC = pD + bX * size.x;
|
const auto pC = pD + bX * size.x;
|
||||||
|
|
||||||
PgmRenderer::DrawLine(pA, pB);
|
DrawLine(pA, pB);
|
||||||
PgmRenderer::DrawLine(pB, pC);
|
DrawLine(pB, pC);
|
||||||
PgmRenderer::DrawLine(pC, pD);
|
DrawLine(pC, pD);
|
||||||
PgmRenderer::DrawLine(pD, pA);
|
DrawLine(pD, pA);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PgmRenderer::DrawCircle(const math::Vector& center, float radius)
|
void PgmRenderer::DrawCircle(const math::Vector& center, float radius)
|
||||||
@ -41,26 +35,28 @@ void PgmRenderer::DrawCircle(const math::Vector& center, float radius)
|
|||||||
RasterizeCircle(center.x, center.y, radius);
|
RasterizeCircle(center.x, center.y, radius);
|
||||||
}
|
}
|
||||||
|
|
||||||
PgmRenderer::~PgmRenderer()
|
void PgmRenderer::Save(const std::filesystem::path& path)
|
||||||
{
|
{
|
||||||
Flush();
|
std::ofstream file{path};
|
||||||
}
|
|
||||||
|
|
||||||
void PgmRenderer::Flush()
|
if (!file.is_open())
|
||||||
{
|
{
|
||||||
m_file << "P2" << std::endl;
|
throw std::runtime_error{"Cannot open file for writing: " + path.string()};
|
||||||
m_file << "# KIV/CPP" << std::endl;
|
}
|
||||||
m_file << m_bitmap.GetWidth() << ' ' << m_bitmap.GetHeight() << std::endl;
|
|
||||||
m_file << 255 << std::endl;
|
file << "P2" << std::endl;
|
||||||
|
file << "# KIV/CPP" << std::endl;
|
||||||
|
file << m_bitmap.GetWidth() << ' ' << m_bitmap.GetHeight() << std::endl;
|
||||||
|
file << 255 << std::endl;
|
||||||
|
|
||||||
for (size_t y = 0; y < m_bitmap.GetHeight(); ++y)
|
for (size_t y = 0; y < m_bitmap.GetHeight(); ++y)
|
||||||
{
|
{
|
||||||
for (size_t x = 0; x < m_bitmap.GetWidth(); ++x)
|
for (size_t x = 0; x < m_bitmap.GetWidth(); ++x)
|
||||||
{
|
{
|
||||||
m_file << static_cast<int>(m_bitmap[y][x].l) << ' ';
|
file << static_cast<int>(m_bitmap[y][x].l) << ' ';
|
||||||
}
|
}
|
||||||
|
|
||||||
m_file << std::endl;
|
file << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,7 +83,7 @@ void PgmRenderer::RasterizeLine(int x0, int y0, int x1, int y1)
|
|||||||
ox = 1;
|
ox = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (1)
|
while (true)
|
||||||
{
|
{
|
||||||
// draw a square of pixels for thickness
|
// draw a square of pixels for thickness
|
||||||
for (int i = -w / 2; i <= w / 2; i++)
|
for (int i = -w / 2; i <= w / 2; i++)
|
||||||
|
|||||||
@ -2,28 +2,31 @@
|
|||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <fstream>
|
|
||||||
|
|
||||||
#include "renderer.hpp"
|
|
||||||
#include "bitmap.hpp"
|
#include "bitmap.hpp"
|
||||||
|
#include "renderer.hpp"
|
||||||
|
|
||||||
class PgmRenderer : public Renderer
|
/**
|
||||||
|
* @brief PGM Renderer
|
||||||
|
*
|
||||||
|
* Draws shapes to a PGM format.
|
||||||
|
*/
|
||||||
|
class PgmRenderer final : public Renderer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
PgmRenderer(const std::filesystem::path& path, size_t width, size_t height);
|
PgmRenderer(size_t width, size_t height);
|
||||||
|
|
||||||
void DrawLine(const math::Vector& p0, const math::Vector& p1) override;
|
void DrawLine(const math::Vector& p0, const math::Vector& p1) override;
|
||||||
void DrawRectangle(const math::Vector& pos, const math::Vector& size, float angle) override;
|
void DrawRectangle(const math::Vector& pos, const math::Vector& size, float angle) override;
|
||||||
void DrawCircle(const math::Vector& center, float radius) override;
|
void DrawCircle(const math::Vector& center, float radius) override;
|
||||||
|
|
||||||
~PgmRenderer() override;
|
void Save(const std::filesystem::path& path);
|
||||||
|
|
||||||
private:
|
~PgmRenderer() override = default;
|
||||||
std::ofstream m_file;
|
|
||||||
|
private:
|
||||||
Bitmap m_bitmap;
|
Bitmap m_bitmap;
|
||||||
|
|
||||||
void RasterizeLine(int x0, int y0, int x1, int y1);
|
void RasterizeLine(int x0, int y0, int x1, int y1);
|
||||||
void RasterizeCircle(int xm, int ym, int r);
|
void RasterizeCircle(int xm, int ym, int r);
|
||||||
|
|
||||||
void Flush();
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,9 +1,14 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "math/vector.hpp"
|
#include "math/vector.hpp"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Renderer interface
|
||||||
|
*
|
||||||
|
* Represents an inteface for different renderers that can draw basic shapes.
|
||||||
|
*/
|
||||||
class Renderer
|
class Renderer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual void DrawLine(const math::Vector& p0, const math::Vector& p1) = 0;
|
virtual void DrawLine(const math::Vector& p0, const math::Vector& p1) = 0;
|
||||||
virtual void DrawRectangle(const math::Vector& pos, const math::Vector& size, float angle) = 0;
|
virtual void DrawRectangle(const math::Vector& pos, const math::Vector& size, float angle) = 0;
|
||||||
virtual void DrawCircle(const math::Vector& center, float radius) = 0;
|
virtual void DrawCircle(const math::Vector& center, float radius) = 0;
|
||||||
|
|||||||
@ -1,38 +1,48 @@
|
|||||||
#include "svg_renderer.hpp"
|
#include "svg_renderer.hpp"
|
||||||
|
|
||||||
#include "math/constants.hpp"
|
#include "math/constants.hpp"
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
SvgRenderer::SvgRenderer(const std::filesystem::path& path, size_t width, size_t height) : m_file(path)
|
SvgRenderer::SvgRenderer(size_t width, size_t height) : m_width{width}, m_height{height}
|
||||||
{
|
{
|
||||||
if (m_file.bad())
|
// white bg
|
||||||
throw std::runtime_error("Cannot open " + path.string() + " for writing");
|
m_out << " <rect width=\"100%\" height=\"100%\" fill=\"#FFFFFF\" />" << std::endl;
|
||||||
|
|
||||||
m_file << "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"" << width << "\" height=\"" << height
|
|
||||||
<< "\" viewBox=\"0 0 " << width << " " << height << "\" role=\"img\" aria-label=\"KIV/CPP\">" << std::endl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SvgRenderer::DrawLine(const math::Vector& p0, const math::Vector& p1)
|
void SvgRenderer::DrawLine(const math::Vector& p0, const math::Vector& p1)
|
||||||
{
|
{
|
||||||
m_file << " <line x1=\"" << p0.x << "\" y1=\"" << p0.y << "\" x2=\"" << p1.x << "\" y2=\"" << p1.y << "\" "
|
m_out << " <line x1=\"" << p0.x << "\" y1=\"" << p0.y << "\" x2=\"" << p1.x << "\" y2=\"" << p1.y << "\" "
|
||||||
<< "stroke=\"#000000\" stroke-width=\"2\" stroke-linecap=\"round\" />" << std::endl;
|
<< "stroke=\"#000000\" stroke-width=\"2\" stroke-linecap=\"round\" />" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SvgRenderer::DrawRectangle(const math::Vector& pos, const math::Vector& size, float angle)
|
void SvgRenderer::DrawRectangle(const math::Vector& pos, const math::Vector& size, float angle)
|
||||||
{
|
{
|
||||||
float angleDeg = angle * math::RAD_TO_DEG;
|
float angleDeg = angle * math::RAD_TO_DEG;
|
||||||
|
|
||||||
m_file << " <rect x=\"" << pos.x << "\" y=\"" << pos.y << "\" width=\"" << size.x << "\" height=\"" << size.y
|
m_out << " <rect x=\"" << pos.x << "\" y=\"" << pos.y << "\" width=\"" << size.x << "\" height=\"" << size.y
|
||||||
<< "\" fill=\"none\" stroke=\"#000000\" stroke-width=\"2\" stroke-linejoin=\"round\" transform=\"rotate("
|
<< "\" fill=\"none\" stroke=\"#000000\" stroke-width=\"2\" stroke-linejoin=\"round\" transform=\"rotate("
|
||||||
<< angleDeg << ", " << pos.x << ", " << pos.y << ")\" />" << std::endl;
|
<< angleDeg << ", " << pos.x << ", " << pos.y << ")\" />" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SvgRenderer::DrawCircle(const math::Vector& center, float radius)
|
void SvgRenderer::DrawCircle(const math::Vector& center, float radius)
|
||||||
{
|
{
|
||||||
m_file << " <circle cx=\"" << center.x << "\" cy=\"" << center.y << "\" r=\"" << radius
|
m_out << " <circle cx=\"" << center.x << "\" cy=\"" << center.y << "\" r=\"" << radius
|
||||||
<< "\" fill=\"none\" stroke=\"#000000\" stroke-width=\"2\" />" << std::endl;
|
<< "\" fill=\"none\" stroke=\"#000000\" stroke-width=\"2\" />" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
SvgRenderer::~SvgRenderer()
|
void SvgRenderer::Save(const std::filesystem::path& path)
|
||||||
{
|
{
|
||||||
m_file << "</svg>" << std::endl;
|
std::ofstream file{path};
|
||||||
|
|
||||||
|
if (!file.is_open())
|
||||||
|
{
|
||||||
|
throw std::runtime_error{"Cannot open file for writing: " + path.string()};
|
||||||
|
}
|
||||||
|
|
||||||
|
file << "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"" << m_width << "\" height=\"" << m_height
|
||||||
|
<< "\" viewBox=\"0 0 " << m_width << " " << m_height << "\" role=\"img\" aria-label=\"KIV/CPP\">" << std::endl;
|
||||||
|
|
||||||
|
file << m_out.str();
|
||||||
|
|
||||||
|
file << "</svg>" << std::endl;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,19 +3,27 @@
|
|||||||
#include "renderer.hpp"
|
#include "renderer.hpp"
|
||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <fstream>
|
#include <sstream>
|
||||||
|
|
||||||
class SvgRenderer : public Renderer
|
/**
|
||||||
|
* @brief SVG Renderer
|
||||||
|
*
|
||||||
|
* Draws shapes to an SVG format.
|
||||||
|
*/
|
||||||
|
class SvgRenderer final : public Renderer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
SvgRenderer(const std::filesystem::path& path, size_t width, size_t height);
|
SvgRenderer(size_t width, size_t height);
|
||||||
|
|
||||||
void DrawLine(const math::Vector& p0, const math::Vector& p1) override;
|
void DrawLine(const math::Vector& p0, const math::Vector& p1) override;
|
||||||
void DrawRectangle(const math::Vector& pos, const math::Vector& size, float angle) override;
|
void DrawRectangle(const math::Vector& pos, const math::Vector& size, float angle) override;
|
||||||
void DrawCircle(const math::Vector& center, float radius) override;
|
void DrawCircle(const math::Vector& center, float radius) override;
|
||||||
|
|
||||||
~SvgRenderer() override;
|
void Save(const std::filesystem::path& path);
|
||||||
|
|
||||||
private:
|
~SvgRenderer() override = default;
|
||||||
std::ofstream m_file;
|
|
||||||
|
private:
|
||||||
|
size_t m_width, m_height;
|
||||||
|
std::ostringstream m_out;
|
||||||
};
|
};
|
||||||
@ -5,22 +5,18 @@
|
|||||||
namespace shapes
|
namespace shapes
|
||||||
{
|
{
|
||||||
|
|
||||||
class Circle : public Shape
|
/**
|
||||||
|
* @brief Circle shape
|
||||||
|
*
|
||||||
|
* Represents a circle defined by a center point and a radius.
|
||||||
|
*/
|
||||||
|
class Circle final : public Shape
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Circle(const math::Vector& center, float radius) : m_center(center), m_radius(radius)
|
Circle(const math::Vector& center, float radius) : m_center(center), m_radius(radius) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
const math::Vector& GetCenter() const
|
const math::Vector& GetCenter() const { return m_center; }
|
||||||
{
|
float GetRadius() const { return m_radius; }
|
||||||
return m_center;
|
|
||||||
}
|
|
||||||
|
|
||||||
float GetRadius() const
|
|
||||||
{
|
|
||||||
return m_radius;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Translate(const math::Vector& offset) override;
|
void Translate(const math::Vector& offset) override;
|
||||||
void Rotate(const math::Vector& center, float angle) override;
|
void Rotate(const math::Vector& center, float angle) override;
|
||||||
@ -30,7 +26,7 @@ class Circle : public Shape
|
|||||||
|
|
||||||
~Circle() override = default;
|
~Circle() override = default;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
math::Vector m_center;
|
math::Vector m_center;
|
||||||
float m_radius;
|
float m_radius;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -8,67 +8,46 @@
|
|||||||
namespace shapes
|
namespace shapes
|
||||||
{
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Group shape
|
||||||
|
*
|
||||||
|
* Represents a group of shapes that can be used as a canvas
|
||||||
|
*/
|
||||||
class Group : public Shape
|
class Group : public Shape
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Group() = default;
|
Group() = default;
|
||||||
|
|
||||||
Group(Group&& other) : m_shapes{std::move(other.m_shapes)}
|
Group(const Group&) = delete;
|
||||||
{
|
Group& operator=(const Group&) = delete;
|
||||||
}
|
Group(Group&&) = default;
|
||||||
|
Group& operator=(Group&&) = default;
|
||||||
|
|
||||||
Group& operator=(Group&& other)
|
template <typename TShape, typename... TArgs>
|
||||||
{
|
requires std::derived_from<TShape, Shape>
|
||||||
// TODO: overit
|
void AddShape(TArgs&&... args)
|
||||||
m_shapes = std::move(other.m_shapes);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class TShape, class... TArgs> void AddShape(TArgs&&... args)
|
|
||||||
{
|
{
|
||||||
m_shapes.emplace_back(std::make_unique<TShape>(std::forward<TArgs>(args)...));
|
m_shapes.emplace_back(std::make_unique<TShape>(std::forward<TArgs>(args)...));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Translate(const math::Vector& offset) override
|
void Translate(const math::Vector& offset) override { ShapesCall<&Shape::Translate>(offset); }
|
||||||
{
|
void Rotate(const math::Vector& center, float angle) override { ShapesCall<&Shape::Rotate>(center, angle); }
|
||||||
ShapesCall<&Shape::Translate>(offset);
|
void Scale(const math::Vector& center, float factor) override { ShapesCall<&Shape::Scale>(center, factor); }
|
||||||
}
|
void Draw(Renderer& renderer) const override { ShapesCall<&Shape::Draw>(renderer); }
|
||||||
|
|
||||||
void Rotate(const math::Vector& center, float angle) override
|
|
||||||
{
|
|
||||||
ShapesCall<&Shape::Rotate>(center, angle);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Scale(const math::Vector& center, float factor) override
|
|
||||||
{
|
|
||||||
ShapesCall<&Shape::Scale>(center, factor);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Draw(Renderer& renderer) const override
|
|
||||||
{
|
|
||||||
ShapesCall<&Shape::Draw>(renderer);
|
|
||||||
}
|
|
||||||
|
|
||||||
~Group() override = default;
|
~Group() override = default;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// calls a member function on all shapes in the group
|
||||||
|
template <auto Fun, typename TSelf, typename... TArgs>
|
||||||
|
void ShapesCall(this TSelf&& self, TArgs&&... args)
|
||||||
|
{
|
||||||
|
for (auto& shape : self.m_shapes)
|
||||||
|
(shape.get()->*Fun)(std::forward<TArgs>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
std::vector<std::unique_ptr<Shape>> m_shapes;
|
std::vector<std::unique_ptr<Shape>> m_shapes;
|
||||||
|
|
||||||
template <auto TFun, class... TArgs> void ShapesCall(TArgs&&... args)
|
|
||||||
{
|
|
||||||
for (auto& shape : m_shapes)
|
|
||||||
{
|
|
||||||
(shape.get()->*TFun)(std::forward<TArgs>(args)...);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <auto TFun, class... TArgs> void ShapesCall(TArgs&&... args) const
|
|
||||||
{
|
|
||||||
for (const auto& shape : m_shapes)
|
|
||||||
{
|
|
||||||
(shape.get()->*TFun)(std::forward<TArgs>(args)...);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace shapes
|
} // namespace shapes
|
||||||
@ -6,12 +6,15 @@
|
|||||||
namespace shapes
|
namespace shapes
|
||||||
{
|
{
|
||||||
|
|
||||||
class Line : public Shape
|
/**
|
||||||
|
* @brief Line shape
|
||||||
|
*
|
||||||
|
* Represents a line defined by two points.
|
||||||
|
*/
|
||||||
|
class Line final : public Shape
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Line(const math::Vector& p0, const math::Vector& p1) : m_p0(p0), m_p1(p1)
|
Line(const math::Vector& p0, const math::Vector& p1) : m_p0(p0), m_p1(p1) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void Translate(const math::Vector& offset) override;
|
void Translate(const math::Vector& offset) override;
|
||||||
void Rotate(const math::Vector& center, float angle) override;
|
void Rotate(const math::Vector& center, float angle) override;
|
||||||
@ -19,7 +22,7 @@ class Line : public Shape
|
|||||||
|
|
||||||
void Draw(Renderer& renderer) const override;
|
void Draw(Renderer& renderer) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
math::Vector m_p0;
|
math::Vector m_p0;
|
||||||
math::Vector m_p1;
|
math::Vector m_p1;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -6,27 +6,19 @@
|
|||||||
namespace shapes
|
namespace shapes
|
||||||
{
|
{
|
||||||
|
|
||||||
class Rectangle : public Shape
|
/**
|
||||||
|
* @brief Rectangle shape
|
||||||
|
*
|
||||||
|
* Represents a rectangle defined by a position, size and angle of rotation around it's left-top corner
|
||||||
|
*/
|
||||||
|
class Rectangle final : public Shape
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Rectangle(const math::Vector& pos, const math::Vector& size) : m_pos(pos), m_size(size), m_angle(0.0f)
|
Rectangle(const math::Vector& pos, const math::Vector& size) : m_pos(pos), m_size(size), m_angle(0.0f) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
const math::Vector& GetPosition() const
|
const math::Vector& GetPosition() const { return m_pos; }
|
||||||
{
|
const math::Vector& GetSize() const { return m_size; }
|
||||||
return m_pos;
|
const float GetAngle() const { return m_angle; }
|
||||||
}
|
|
||||||
|
|
||||||
const math::Vector& GetSize() const
|
|
||||||
{
|
|
||||||
return m_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
const float GetAngle() const
|
|
||||||
{
|
|
||||||
return m_angle;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Translate(const math::Vector& offset) override;
|
void Translate(const math::Vector& offset) override;
|
||||||
void Rotate(const math::Vector& center, float angle) override;
|
void Rotate(const math::Vector& center, float angle) override;
|
||||||
@ -36,7 +28,7 @@ class Rectangle : public Shape
|
|||||||
|
|
||||||
~Rectangle() override = default;
|
~Rectangle() override = default;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
math::Vector m_pos;
|
math::Vector m_pos;
|
||||||
math::Vector m_size;
|
math::Vector m_size;
|
||||||
float m_angle;
|
float m_angle;
|
||||||
|
|||||||
@ -7,6 +7,11 @@ class Renderer;
|
|||||||
namespace shapes
|
namespace shapes
|
||||||
{
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Shape interface
|
||||||
|
*
|
||||||
|
* Interface for 2d shapes that can be transformed and drawn using a Renderer.
|
||||||
|
*/
|
||||||
class Shape
|
class Shape
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user