It's drawing
This commit is contained in:
parent
d508045ec3
commit
9b517b6aee
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,2 +1,3 @@
|
||||
.cache/
|
||||
.vscode/
|
||||
build/
|
||||
|
||||
@ -5,13 +5,14 @@ project(CppDrawing)
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
|
||||
add_executable(drawing
|
||||
"contexts/pgm_drawing_context.cpp"
|
||||
"math/transforms.cpp"
|
||||
"primitives/line.cpp"
|
||||
"primitives/rectangle.cpp"
|
||||
"primitives/circle.cpp"
|
||||
"main.cpp"
|
||||
"renderers/pgm_renderer.cpp"
|
||||
"shapes/circle.cpp"
|
||||
"shapes/group.cpp"
|
||||
"shapes/line.cpp"
|
||||
"shapes/rectangle.cpp"
|
||||
"input_file.cpp"
|
||||
"main.cpp"
|
||||
)
|
||||
|
||||
target_include_directories(drawing PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
@ -1,60 +0,0 @@
|
||||
#include "pgm_drawing_context.hpp"
|
||||
#include <cmath>
|
||||
|
||||
PgmDrawingContext::PgmDrawingContext(const std::filesystem::path& path, size_t width, size_t height)
|
||||
: m_bitmap(width, height, Color{0xFF})
|
||||
{
|
||||
}
|
||||
|
||||
void PgmDrawingContext::DrawLine(const math::Vector& p0, const math::Vector& p1)
|
||||
{
|
||||
RasterizeLine(static_cast<int>(p0.x), static_cast<int>(p0.y), static_cast<int>(p1.x), static_cast<int>(p1.y));
|
||||
}
|
||||
|
||||
void PgmDrawingContext::DrawRectangle(const math::Vector& pos, const math::Vector& size, float angle)
|
||||
{
|
||||
const float sina = std::sin(angle);
|
||||
const float cosa = std::cos(angle);
|
||||
|
||||
const math::Vector bX{cosa, sina};
|
||||
const math::Vector bY{-sina, cosa};
|
||||
|
||||
const auto& pD = pos;
|
||||
const auto pA = pD + bY * size.y;
|
||||
const auto pB = pA + bX * size.x;
|
||||
const auto pC = pD + bX * size.x;
|
||||
|
||||
PgmDrawingContext::DrawLine(pA, pB);
|
||||
PgmDrawingContext::DrawLine(pB, pC);
|
||||
PgmDrawingContext::DrawLine(pC, pD);
|
||||
PgmDrawingContext::DrawLine(pD, pA);
|
||||
}
|
||||
|
||||
void PgmDrawingContext::DrawCircle(const math::Vector& center, float radius)
|
||||
{
|
||||
}
|
||||
|
||||
void PgmDrawingContext::RasterizeLine(int x0, int y0, int x1, int y1)
|
||||
{
|
||||
int dx = abs(x1 - x0), sx = x0 < x1 ? 1 : -1;
|
||||
int dy = -abs(y1 - y0), sy = y0 < y1 ? 1 : -1;
|
||||
int err = dx + dy, e2; /* error value e_xy */
|
||||
|
||||
for (;;)
|
||||
{ /* loop */
|
||||
m_bitmap[y0][x0].l = 0x00;
|
||||
if (x0 == x1 && y0 == y1)
|
||||
break;
|
||||
e2 = 2 * err;
|
||||
if (e2 >= dy)
|
||||
{
|
||||
err += dy;
|
||||
x0 += sx;
|
||||
} /* e_xy+e_x > 0 */
|
||||
if (e2 <= dx)
|
||||
{
|
||||
err += dx;
|
||||
y0 += sy;
|
||||
} /* e_xy+e_y < 0 */
|
||||
}
|
||||
}
|
||||
137
input_file.cpp
137
input_file.cpp
@ -1,2 +1,139 @@
|
||||
#include "input_file.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "shapes/circle.hpp"
|
||||
#include "shapes/line.hpp"
|
||||
#include "shapes/rectangle.hpp"
|
||||
|
||||
InputFile::InputFile(const std::filesystem::path& path) : m_file(path)
|
||||
{
|
||||
if (m_file.bad())
|
||||
{
|
||||
throw std::runtime_error("Cannot open " + path.string() + " for reading");
|
||||
}
|
||||
}
|
||||
|
||||
static std::istream& operator>>(std::istream& is, math::Vector& vec)
|
||||
{
|
||||
return is >> vec.x >> vec.y;
|
||||
}
|
||||
|
||||
using CommandMap = std::map<std::string, std::function<void(std::istream&)>>;
|
||||
|
||||
template <class T> static const char* GetTypeName()
|
||||
{
|
||||
return typeid(T).name();
|
||||
}
|
||||
|
||||
template <> static const char* GetTypeName<int>()
|
||||
{
|
||||
return "integer";
|
||||
}
|
||||
|
||||
template <> static const char* GetTypeName<float>()
|
||||
{
|
||||
return "float";
|
||||
}
|
||||
|
||||
template <> static const char* GetTypeName<math::Vector>()
|
||||
{
|
||||
return "vector";
|
||||
}
|
||||
|
||||
template <> static const char* GetTypeName<std::string>()
|
||||
{
|
||||
return "string";
|
||||
}
|
||||
|
||||
template <class T> static T ReadVal(std::istream& is)
|
||||
{
|
||||
T v;
|
||||
is >> v;
|
||||
|
||||
// std::cout << "Reading " << GetTypeName<T>() << ' ' << v << std::endl;
|
||||
|
||||
if (is.bad())
|
||||
{
|
||||
throw std::runtime_error("Could not parse " + std::string(GetTypeName<T>()) + " from stream");
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
template <class... TArgs, class THandler>
|
||||
static void RegisterCommand(CommandMap& cmdMap, const std::string& cmd, THandler&& handler)
|
||||
{
|
||||
cmdMap[cmd] = [handler = std::forward<THandler>(handler)](std::istream& is) {
|
||||
std::apply(handler, std::tuple<TArgs...>{ReadVal<TArgs>(is)...}); // pres tuple kvuli poradi evaluace...
|
||||
};
|
||||
}
|
||||
|
||||
static float DegToRad(float deg)
|
||||
{
|
||||
return deg * 0.0174532925f;
|
||||
}
|
||||
|
||||
shapes::Group InputFile::Parse()
|
||||
{
|
||||
shapes::Group shapes;
|
||||
|
||||
CommandMap cmds;
|
||||
|
||||
/* SHAPES */
|
||||
|
||||
RegisterCommand<int, int, int, int>(cmds, "line", [&shapes](int x1, int y1, int x2, int y2) {
|
||||
shapes.AddShape<shapes::Line>(math::Vector{x1, y1}, math::Vector{x2, y2});
|
||||
});
|
||||
|
||||
RegisterCommand<int, int, int>(cmds, "circle", [&shapes](int x, int y, int r) {
|
||||
if (r <= 0)
|
||||
throw std::runtime_error("Circle radius must be a positive integer");
|
||||
|
||||
shapes.AddShape<shapes::Circle>(math::Vector{x, y}, static_cast<float>(r));
|
||||
});
|
||||
|
||||
RegisterCommand<int, int, int, int>(cmds, "rect", [&shapes](int x, int y, int w, int h) {
|
||||
if (w <= 0 || h <= 0)
|
||||
throw std::runtime_error("Rect dimensions must be positive integers");
|
||||
|
||||
shapes.AddShape<shapes::Rectangle>(math::Vector{x, y}, math::Vector{w, h});
|
||||
});
|
||||
|
||||
/* TRANSFORMS */
|
||||
|
||||
RegisterCommand<int, int>(cmds, "translate", [&shapes](int ox, int oy) { shapes.Translate(math::Vector{ox, oy}); });
|
||||
|
||||
RegisterCommand<int, int, float>(
|
||||
cmds, "rotate", [&shapes](int cx, int cy, float deg) { shapes.Rotate(math::Vector{cx, cy}, DegToRad(deg)); });
|
||||
|
||||
RegisterCommand<int, int, float>(cmds, "scale", [&shapes](int cx, int cy, float factor) {
|
||||
if (factor < std::numeric_limits<decltype(factor)>().epsilon())
|
||||
throw std::runtime_error("Scale factor must be a non-zero real number");
|
||||
|
||||
shapes.Scale(math::Vector{cx, cy}, factor);
|
||||
});
|
||||
|
||||
std::string line;
|
||||
|
||||
while (std::getline(m_file, line))
|
||||
{
|
||||
if (line.empty() || line[0] == '#')
|
||||
continue;
|
||||
|
||||
std::istringstream iss(line);
|
||||
std::string cmdName = ReadVal<std::string>(iss);
|
||||
|
||||
auto cmd = cmds.find(cmdName);
|
||||
|
||||
if (cmd == cmds.end())
|
||||
throw std::runtime_error("Unknown command: " + cmdName);
|
||||
|
||||
cmd->second(iss);
|
||||
}
|
||||
|
||||
return shapes;
|
||||
}
|
||||
|
||||
@ -2,15 +2,17 @@
|
||||
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "drawing_context.hpp"
|
||||
#include "shapes/group.hpp"
|
||||
|
||||
class InputFile
|
||||
{
|
||||
public:
|
||||
InputFile(const std::filesystem::path& path);
|
||||
|
||||
void Parse(DrawingContext& ctx);
|
||||
shapes::Group Parse();
|
||||
|
||||
private:
|
||||
std::ifstream m_file;
|
||||
|
||||
25
main.cpp
25
main.cpp
@ -1,8 +1,29 @@
|
||||
|
||||
#include <iostream>
|
||||
#include "pgm_drawing_context.hpp"
|
||||
#include <stdexcept>
|
||||
|
||||
#include "input_file.hpp"
|
||||
#include "renderers/pgm_renderer.hpp"
|
||||
|
||||
int main()
|
||||
{
|
||||
std::cout << "sjeta" << std::endl;
|
||||
try
|
||||
{
|
||||
InputFile file("test.txt");
|
||||
|
||||
std::ofstream("here.txt");
|
||||
|
||||
auto shapes = file.Parse();
|
||||
|
||||
PgmRenderer renderer("output.pgm", 300, 300);
|
||||
shapes.Draw(renderer);
|
||||
|
||||
renderer.Flush();
|
||||
|
||||
std::cout << "sjeta" << std::endl;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::cerr << "Error: " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,6 +16,10 @@ class Vector
|
||||
{
|
||||
}
|
||||
|
||||
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]; }
|
||||
|
||||
|
||||
@ -1,24 +0,0 @@
|
||||
#include "circle.hpp"
|
||||
#include "math/transforms.hpp"
|
||||
#include "contexts/drawing_context.hpp"
|
||||
|
||||
void primitives::Circle::Translate(const math::Vector& offset)
|
||||
{
|
||||
m_center += offset;
|
||||
}
|
||||
|
||||
void primitives::Circle::Rotate(const math::Vector& center, float angle)
|
||||
{
|
||||
m_center = math::RotatePoint(center, angle, m_center);
|
||||
}
|
||||
|
||||
void primitives::Circle::Scale(const math::Vector& center, float factor)
|
||||
{
|
||||
m_center = math::ScalePoint(center, factor, m_center);
|
||||
m_radius *= factor;
|
||||
}
|
||||
|
||||
void primitives::Circle::Draw(DrawingContext& ctx)
|
||||
{
|
||||
ctx.DrawCircle(m_center, m_radius);
|
||||
}
|
||||
@ -1,26 +0,0 @@
|
||||
#include "line.hpp"
|
||||
#include "math/transforms.hpp"
|
||||
#include "contexts/drawing_context.hpp"
|
||||
|
||||
void primitives::Line::Translate(const math::Vector& offset)
|
||||
{
|
||||
m_p0 += offset;
|
||||
m_p1 += offset;
|
||||
}
|
||||
|
||||
void primitives::Line::Rotate(const math::Vector& center, float angle)
|
||||
{
|
||||
m_p0 = math::RotatePoint(center, angle, m_p0);
|
||||
m_p1 = math::RotatePoint(center, angle, m_p1);
|
||||
}
|
||||
|
||||
void primitives::Line::Scale(const math::Vector& center, float factor)
|
||||
{
|
||||
m_p0 = math::ScalePoint(center, factor, m_p0);
|
||||
m_p1 = math::ScalePoint(center, factor, m_p1);
|
||||
}
|
||||
|
||||
void primitives::Line::Draw(DrawingContext& ctx)
|
||||
{
|
||||
ctx.DrawLine(m_p0, m_p1);
|
||||
}
|
||||
@ -1,25 +0,0 @@
|
||||
#include "rectangle.hpp"
|
||||
#include "math/transforms.hpp"
|
||||
#include "contexts/drawing_context.hpp"
|
||||
|
||||
void primitives::Rectangle::Translate(const math::Vector& offset)
|
||||
{
|
||||
m_pos += offset;
|
||||
}
|
||||
|
||||
void primitives::Rectangle::Rotate(const math::Vector& center, float angle)
|
||||
{
|
||||
m_pos = math::RotatePoint(center, angle, m_pos);
|
||||
m_angle = math::RotateAngle(m_angle, angle);
|
||||
}
|
||||
|
||||
void primitives::Rectangle::Scale(const math::Vector& center, float factor)
|
||||
{
|
||||
m_pos = math::ScalePoint(center, factor, m_pos);
|
||||
m_size *= factor;
|
||||
}
|
||||
|
||||
void primitives::Rectangle::Draw(DrawingContext& ctx)
|
||||
{
|
||||
ctx.DrawRectangle(m_pos, m_size, m_angle);
|
||||
}
|
||||
@ -39,6 +39,16 @@ class Bitmap
|
||||
return &m_data[row * m_width];
|
||||
};
|
||||
|
||||
size_t GetWidth() const
|
||||
{
|
||||
return m_width;
|
||||
}
|
||||
|
||||
size_t GetHeight() const
|
||||
{
|
||||
return m_height;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t m_width, m_height;
|
||||
std::vector<Color> m_data;
|
||||
106
renderers/pgm_renderer.cpp
Normal file
106
renderers/pgm_renderer.cpp
Normal file
@ -0,0 +1,106 @@
|
||||
#include "pgm_renderer.hpp"
|
||||
#include <cmath>
|
||||
#include <stdexcept>
|
||||
|
||||
PgmRenderer::PgmRenderer(const std::filesystem::path& path, size_t width, size_t height)
|
||||
: 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)
|
||||
{
|
||||
RasterizeLine(static_cast<int>(p0.x), static_cast<int>(p0.y), static_cast<int>(p1.x), static_cast<int>(p1.y));
|
||||
}
|
||||
|
||||
void PgmRenderer::DrawRectangle(const math::Vector& pos, const math::Vector& size, float angle)
|
||||
{
|
||||
const float sina = std::sin(angle);
|
||||
const float cosa = std::cos(angle);
|
||||
|
||||
const math::Vector bX{cosa, sina};
|
||||
const math::Vector bY{-sina, cosa};
|
||||
|
||||
const auto& pD = pos;
|
||||
const auto pA = pD + bY * size.y;
|
||||
const auto pB = pA + bX * size.x;
|
||||
const auto pC = pD + bX * size.x;
|
||||
|
||||
PgmRenderer::DrawLine(pA, pB);
|
||||
PgmRenderer::DrawLine(pB, pC);
|
||||
PgmRenderer::DrawLine(pC, pD);
|
||||
PgmRenderer::DrawLine(pD, pA);
|
||||
}
|
||||
|
||||
void PgmRenderer::DrawCircle(const math::Vector& center, float radius)
|
||||
{
|
||||
RasterizeCircle(center.x, center.y, radius);
|
||||
}
|
||||
|
||||
void PgmRenderer::Flush()
|
||||
{
|
||||
m_file << "P2" << std::endl;
|
||||
m_file << "# KIV/CPP" << std::endl;
|
||||
m_file << m_bitmap.GetWidth() << ' ' << m_bitmap.GetHeight() << std::endl;
|
||||
m_file << 255 << std::endl;
|
||||
|
||||
for (size_t y = 0; y < m_bitmap.GetHeight(); ++y)
|
||||
{
|
||||
for (size_t x = 0; x < m_bitmap.GetWidth(); ++x)
|
||||
{
|
||||
m_file << static_cast<int>(m_bitmap[y][x].l) << ' ';
|
||||
}
|
||||
|
||||
m_file << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void PgmRenderer::RasterizeLine(int x0, int y0, int x1, int y1)
|
||||
{
|
||||
int dx = abs(x1 - x0), sx = x0 < x1 ? 1 : -1;
|
||||
int dy = -abs(y1 - y0), sy = y0 < y1 ? 1 : -1;
|
||||
int err = dx + dy, e2; /* error value e_xy */
|
||||
|
||||
for (;;)
|
||||
{ /* loop */
|
||||
m_bitmap[y0][x0].l = 0x00;
|
||||
if (x0 == x1 && y0 == y1)
|
||||
break;
|
||||
e2 = 2 * err;
|
||||
if (e2 >= dy)
|
||||
{
|
||||
err += dy;
|
||||
x0 += sx;
|
||||
} /* e_xy+e_x > 0 */
|
||||
if (e2 <= dx)
|
||||
{
|
||||
err += dx;
|
||||
y0 += sy;
|
||||
} /* e_xy+e_y < 0 */
|
||||
}
|
||||
}
|
||||
|
||||
void PgmRenderer::RasterizeCircle(int xm, int ym, int r)
|
||||
{
|
||||
int x = -r, y = 0, err = 2 - 2 * r; /* II. Quadrant */
|
||||
do
|
||||
{
|
||||
m_bitmap[ym + y][xm - x].l = 0x00;
|
||||
m_bitmap[ym - x][xm - y].l = 0x00;
|
||||
m_bitmap[ym - y][xm + x].l = 0x00;
|
||||
m_bitmap[ym + x][xm + y].l = 0x00;
|
||||
|
||||
// setPixel(xm - x, ym + y); /* I. Quadrant */
|
||||
// setPixel(xm - y, ym - x); /* II. Quadrant */
|
||||
// setPixel(xm + x, ym - y); /* III. Quadrant */
|
||||
// setPixel(xm + y, ym + x); /* IV. Quadrant */
|
||||
r = err;
|
||||
if (r <= y)
|
||||
err += ++y * 2 + 1; /* e_xy+e_y < 0 */
|
||||
if (r > x || err > y)
|
||||
err += ++x * 2 + 1; /* e_xy+e_x > 0 or no 2nd y-step */
|
||||
} while (x < 0);
|
||||
}
|
||||
@ -1,14 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include "bitmap.hpp"
|
||||
#include "drawing_context.hpp"
|
||||
#include <cstddef>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
|
||||
class PgmDrawingContext : public DrawingContext
|
||||
#include "renderer.hpp"
|
||||
#include "bitmap.hpp"
|
||||
|
||||
class PgmRenderer : public Renderer
|
||||
{
|
||||
public:
|
||||
PgmDrawingContext(const std::filesystem::path& path, size_t width, size_t height);
|
||||
PgmRenderer(const std::filesystem::path& path, size_t width, size_t height);
|
||||
|
||||
void DrawLine(const math::Vector& p0, const math::Vector& p1) override;
|
||||
void DrawRectangle(const math::Vector& pos, const math::Vector& size, float angle) override;
|
||||
@ -16,10 +18,12 @@ class PgmDrawingContext : public DrawingContext
|
||||
|
||||
void Flush() override;
|
||||
|
||||
~PgmDrawingContext() override = default;
|
||||
~PgmRenderer() override = default;
|
||||
|
||||
private:
|
||||
std::ofstream m_file;
|
||||
Bitmap m_bitmap;
|
||||
|
||||
void RasterizeLine(int x0, int y0, int x1, int y1);
|
||||
void RasterizeCircle(int xm, int ym, int r);
|
||||
};
|
||||
@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
#include "math/vector.hpp"
|
||||
|
||||
class DrawingContext
|
||||
class Renderer
|
||||
{
|
||||
public:
|
||||
virtual void DrawLine(const math::Vector& p0, const math::Vector& p1) = 0;
|
||||
@ -10,5 +10,5 @@ class DrawingContext
|
||||
|
||||
virtual void Flush() = 0;
|
||||
|
||||
virtual ~DrawingContext() = 0;
|
||||
virtual ~Renderer() = default;
|
||||
};
|
||||
24
shapes/circle.cpp
Normal file
24
shapes/circle.cpp
Normal file
@ -0,0 +1,24 @@
|
||||
#include "circle.hpp"
|
||||
#include "math/transforms.hpp"
|
||||
#include "renderers/renderer.hpp"
|
||||
|
||||
void shapes::Circle::Translate(const math::Vector& offset)
|
||||
{
|
||||
m_center += offset;
|
||||
}
|
||||
|
||||
void shapes::Circle::Rotate(const math::Vector& center, float angle)
|
||||
{
|
||||
m_center = math::RotatePoint(center, angle, m_center);
|
||||
}
|
||||
|
||||
void shapes::Circle::Scale(const math::Vector& center, float factor)
|
||||
{
|
||||
m_center = math::ScalePoint(center, factor, m_center);
|
||||
m_radius *= factor;
|
||||
}
|
||||
|
||||
void shapes::Circle::Draw(Renderer& renderer)
|
||||
{
|
||||
renderer.DrawCircle(m_center, m_radius);
|
||||
}
|
||||
@ -1,11 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "primitive.hpp"
|
||||
#include "shape.hpp"
|
||||
|
||||
namespace primitives
|
||||
namespace shapes
|
||||
{
|
||||
|
||||
class Circle : public Primitive
|
||||
class Circle : public Shape
|
||||
{
|
||||
public:
|
||||
Circle(const math::Vector& center, float radius) : m_center(center), m_radius(radius)
|
||||
@ -26,7 +26,7 @@ class Circle : public Primitive
|
||||
void Rotate(const math::Vector& center, float angle) override;
|
||||
void Scale(const math::Vector& center, float factor) override;
|
||||
|
||||
void Draw(DrawingContext& ctx) override;
|
||||
void Draw(Renderer& renderer) override;
|
||||
|
||||
~Circle() override = default;
|
||||
|
||||
@ -35,4 +35,4 @@ class Circle : public Primitive
|
||||
float m_radius;
|
||||
};
|
||||
|
||||
} // namespace primitives
|
||||
} // namespace shapes
|
||||
33
shapes/group.cpp
Normal file
33
shapes/group.cpp
Normal file
@ -0,0 +1,33 @@
|
||||
#include "group.hpp"
|
||||
|
||||
// void shapes::Group::Translate(const math::Vector& offset)
|
||||
// {
|
||||
// for (const auto& shape : m_shapes)
|
||||
// {
|
||||
// shape->Translate(offset);
|
||||
// }
|
||||
// }
|
||||
|
||||
// void shapes::Group::Rotate(const math::Vector& center, float angle)
|
||||
// {
|
||||
// for (const auto& shape : m_shapes)
|
||||
// {
|
||||
// shape->Rotate(center, angle);
|
||||
// }
|
||||
// }
|
||||
|
||||
// void shapes::Group::Scale(const math::Vector& center, float factor)
|
||||
// {
|
||||
// for (const auto& shape : m_shapes)
|
||||
// {
|
||||
// shape->Scale(center, factor);
|
||||
// }
|
||||
// }
|
||||
|
||||
// void shapes::Group::Draw(Renderer& renderer)
|
||||
// {
|
||||
// for (const auto& shape : m_shapes)
|
||||
// {
|
||||
// shape->Draw(renderer);
|
||||
// }
|
||||
// }
|
||||
65
shapes/group.hpp
Normal file
65
shapes/group.hpp
Normal file
@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
|
||||
#include "shape.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace shapes
|
||||
{
|
||||
|
||||
class Group : public Shape
|
||||
{
|
||||
public:
|
||||
Group() = default;
|
||||
|
||||
Group(Group&& other)
|
||||
{
|
||||
}
|
||||
|
||||
Group& operator=(Group&& other)
|
||||
{
|
||||
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)...));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void Scale(const math::Vector& center, float factor) override
|
||||
{
|
||||
ShapesCall<&Shape::Scale>(center, factor);
|
||||
}
|
||||
|
||||
void Draw(Renderer& renderer) override
|
||||
{
|
||||
ShapesCall<&Shape::Draw>(renderer);
|
||||
}
|
||||
|
||||
~Group() override = default;
|
||||
|
||||
private:
|
||||
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)...);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace shapes
|
||||
26
shapes/line.cpp
Normal file
26
shapes/line.cpp
Normal file
@ -0,0 +1,26 @@
|
||||
#include "line.hpp"
|
||||
#include "math/transforms.hpp"
|
||||
#include "renderers/renderer.hpp"
|
||||
|
||||
void shapes::Line::Translate(const math::Vector& offset)
|
||||
{
|
||||
m_p0 += offset;
|
||||
m_p1 += offset;
|
||||
}
|
||||
|
||||
void shapes::Line::Rotate(const math::Vector& center, float angle)
|
||||
{
|
||||
m_p0 = math::RotatePoint(center, angle, m_p0);
|
||||
m_p1 = math::RotatePoint(center, angle, m_p1);
|
||||
}
|
||||
|
||||
void shapes::Line::Scale(const math::Vector& center, float factor)
|
||||
{
|
||||
m_p0 = math::ScalePoint(center, factor, m_p0);
|
||||
m_p1 = math::ScalePoint(center, factor, m_p1);
|
||||
}
|
||||
|
||||
void shapes::Line::Draw(Renderer& renderer)
|
||||
{
|
||||
renderer.DrawLine(m_p0, m_p1);
|
||||
}
|
||||
@ -1,12 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "math/vector.hpp"
|
||||
#include "primitive.hpp"
|
||||
#include "shape.hpp"
|
||||
|
||||
namespace primitives
|
||||
namespace shapes
|
||||
{
|
||||
|
||||
class Line : public Primitive
|
||||
class Line : public Shape
|
||||
{
|
||||
public:
|
||||
Line(const math::Vector& p0, const math::Vector& p1) : m_p0(p0), m_p1(p1)
|
||||
@ -17,11 +17,11 @@ class Line : public Primitive
|
||||
void Rotate(const math::Vector& center, float angle) override;
|
||||
void Scale(const math::Vector& center, float factor) override;
|
||||
|
||||
void Draw(DrawingContext& ctx) override;
|
||||
void Draw(Renderer& renderer) override;
|
||||
|
||||
private:
|
||||
math::Vector m_p0;
|
||||
math::Vector m_p1;
|
||||
};
|
||||
|
||||
} // namespace primitives
|
||||
} // namespace shapes
|
||||
25
shapes/rectangle.cpp
Normal file
25
shapes/rectangle.cpp
Normal file
@ -0,0 +1,25 @@
|
||||
#include "rectangle.hpp"
|
||||
#include "math/transforms.hpp"
|
||||
#include "renderers/renderer.hpp"
|
||||
|
||||
void shapes::Rectangle::Translate(const math::Vector& offset)
|
||||
{
|
||||
m_pos += offset;
|
||||
}
|
||||
|
||||
void shapes::Rectangle::Rotate(const math::Vector& center, float angle)
|
||||
{
|
||||
m_pos = math::RotatePoint(center, angle, m_pos);
|
||||
m_angle = math::RotateAngle(m_angle, angle);
|
||||
}
|
||||
|
||||
void shapes::Rectangle::Scale(const math::Vector& center, float factor)
|
||||
{
|
||||
m_pos = math::ScalePoint(center, factor, m_pos);
|
||||
m_size *= factor;
|
||||
}
|
||||
|
||||
void shapes::Rectangle::Draw(Renderer& renderer)
|
||||
{
|
||||
renderer.DrawRectangle(m_pos, m_size, m_angle);
|
||||
}
|
||||
@ -1,12 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "math/vector.hpp"
|
||||
#include "primitive.hpp"
|
||||
#include "shape.hpp"
|
||||
|
||||
namespace primitives
|
||||
namespace shapes
|
||||
{
|
||||
|
||||
class Rectangle : public Primitive
|
||||
class Rectangle : public Shape
|
||||
{
|
||||
public:
|
||||
Rectangle(const math::Vector& pos, const math::Vector& size) : m_pos(pos), m_size(size), m_angle(0.0f)
|
||||
@ -32,7 +32,7 @@ class Rectangle : public Primitive
|
||||
void Rotate(const math::Vector& center, float angle) override;
|
||||
void Scale(const math::Vector& center, float factor) override;
|
||||
|
||||
void Draw(DrawingContext& ctx) override;
|
||||
void Draw(Renderer& renderer) override;
|
||||
|
||||
~Rectangle() override = default;
|
||||
|
||||
@ -42,4 +42,4 @@ class Rectangle : public Primitive
|
||||
float m_angle;
|
||||
};
|
||||
|
||||
} // namespace primitives
|
||||
} // namespace shapes
|
||||
@ -2,23 +2,23 @@
|
||||
|
||||
#include "math/vector.hpp"
|
||||
|
||||
class DrawingContext;
|
||||
class Renderer;
|
||||
|
||||
namespace primitives
|
||||
namespace shapes
|
||||
{
|
||||
|
||||
class Primitive
|
||||
class Shape
|
||||
{
|
||||
public:
|
||||
Primitive() = default;
|
||||
Shape() = default;
|
||||
|
||||
virtual void Translate(const math::Vector& offset) = 0;
|
||||
virtual void Rotate(const math::Vector& center, float angle) = 0;
|
||||
virtual void Scale(const math::Vector& center, float factor) = 0;
|
||||
|
||||
virtual void Draw(DrawingContext& ctx) = 0;
|
||||
virtual void Draw(Renderer& renderer) = 0;
|
||||
|
||||
virtual ~Primitive() = 0;
|
||||
virtual ~Shape() = default;
|
||||
};
|
||||
|
||||
} // namespace primitives
|
||||
} // namespace shapes
|
||||
Loading…
x
Reference in New Issue
Block a user