It's drawing
This commit is contained in:
parent
d508045ec3
commit
9b517b6aee
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,2 +1,3 @@
|
|||||||
.cache/
|
.cache/
|
||||||
|
.vscode/
|
||||||
build/
|
build/
|
||||||
|
|||||||
@ -5,13 +5,14 @@ project(CppDrawing)
|
|||||||
set(CMAKE_CXX_STANDARD 20)
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
|
||||||
add_executable(drawing
|
add_executable(drawing
|
||||||
"contexts/pgm_drawing_context.cpp"
|
|
||||||
"math/transforms.cpp"
|
"math/transforms.cpp"
|
||||||
"primitives/line.cpp"
|
"renderers/pgm_renderer.cpp"
|
||||||
"primitives/rectangle.cpp"
|
"shapes/circle.cpp"
|
||||||
"primitives/circle.cpp"
|
"shapes/group.cpp"
|
||||||
"main.cpp"
|
"shapes/line.cpp"
|
||||||
|
"shapes/rectangle.cpp"
|
||||||
"input_file.cpp"
|
"input_file.cpp"
|
||||||
|
"main.cpp"
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(drawing PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
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 "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 <filesystem>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "drawing_context.hpp"
|
#include "shapes/group.hpp"
|
||||||
|
|
||||||
class InputFile
|
class InputFile
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
InputFile(const std::filesystem::path& path);
|
InputFile(const std::filesystem::path& path);
|
||||||
|
|
||||||
void Parse(DrawingContext& ctx);
|
shapes::Group Parse();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::ifstream m_file;
|
std::ifstream m_file;
|
||||||
|
|||||||
25
main.cpp
25
main.cpp
@ -1,8 +1,29 @@
|
|||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include "pgm_drawing_context.hpp"
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#include "input_file.hpp"
|
||||||
|
#include "renderers/pgm_renderer.hpp"
|
||||||
|
|
||||||
int main()
|
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]; }
|
// float& operator[](size_t idx) { return m_v[idx]; }
|
||||||
// const float& operator[](size_t idx) const { 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);
|
|
||||||
}
|
|
||||||
@ -23,7 +23,7 @@ class Bitmap
|
|||||||
// {
|
// {
|
||||||
// return {&m_data[row * m_width], m_width};
|
// return {&m_data[row * m_width], m_width};
|
||||||
// };
|
// };
|
||||||
|
|
||||||
// std::span<const Color> operator[](size_t row) const
|
// std::span<const Color> operator[](size_t row) const
|
||||||
// {
|
// {
|
||||||
// return {&m_data[row * m_width], m_width};
|
// return {&m_data[row * m_width], m_width};
|
||||||
@ -33,12 +33,22 @@ class Bitmap
|
|||||||
{
|
{
|
||||||
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
|
||||||
|
{
|
||||||
|
return m_width;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t GetHeight() const
|
||||||
|
{
|
||||||
|
return m_height;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
size_t m_width, m_height;
|
size_t m_width, m_height;
|
||||||
std::vector<Color> m_data;
|
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
|
#pragma once
|
||||||
|
|
||||||
#include "bitmap.hpp"
|
|
||||||
#include "drawing_context.hpp"
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
class PgmDrawingContext : public DrawingContext
|
#include "renderer.hpp"
|
||||||
|
#include "bitmap.hpp"
|
||||||
|
|
||||||
|
class PgmRenderer : public Renderer
|
||||||
{
|
{
|
||||||
public:
|
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 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;
|
||||||
@ -16,10 +18,12 @@ class PgmDrawingContext : public DrawingContext
|
|||||||
|
|
||||||
void Flush() override;
|
void Flush() override;
|
||||||
|
|
||||||
~PgmDrawingContext() override = default;
|
~PgmRenderer() override = default;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
std::ofstream m_file;
|
||||||
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);
|
||||||
};
|
};
|
||||||
@ -1,7 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "math/vector.hpp"
|
#include "math/vector.hpp"
|
||||||
|
|
||||||
class DrawingContext
|
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;
|
||||||
@ -10,5 +10,5 @@ class DrawingContext
|
|||||||
|
|
||||||
virtual void Flush() = 0;
|
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
|
#pragma once
|
||||||
|
|
||||||
#include "primitive.hpp"
|
#include "shape.hpp"
|
||||||
|
|
||||||
namespace primitives
|
namespace shapes
|
||||||
{
|
{
|
||||||
|
|
||||||
class Circle : public Primitive
|
class Circle : 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)
|
||||||
@ -26,7 +26,7 @@ class Circle : public Primitive
|
|||||||
void Rotate(const math::Vector& center, float angle) override;
|
void Rotate(const math::Vector& center, float angle) override;
|
||||||
void Scale(const math::Vector& center, float factor) override;
|
void Scale(const math::Vector& center, float factor) override;
|
||||||
|
|
||||||
void Draw(DrawingContext& ctx) override;
|
void Draw(Renderer& renderer) override;
|
||||||
|
|
||||||
~Circle() override = default;
|
~Circle() override = default;
|
||||||
|
|
||||||
@ -35,4 +35,4 @@ class Circle : public Primitive
|
|||||||
float m_radius;
|
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
|
#pragma once
|
||||||
|
|
||||||
#include "math/vector.hpp"
|
#include "math/vector.hpp"
|
||||||
#include "primitive.hpp"
|
#include "shape.hpp"
|
||||||
|
|
||||||
namespace primitives
|
namespace shapes
|
||||||
{
|
{
|
||||||
|
|
||||||
class Line : public Primitive
|
class Line : 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)
|
||||||
@ -17,11 +17,11 @@ class Line : public Primitive
|
|||||||
void Rotate(const math::Vector& center, float angle) override;
|
void Rotate(const math::Vector& center, float angle) override;
|
||||||
void Scale(const math::Vector& center, float factor) override;
|
void Scale(const math::Vector& center, float factor) override;
|
||||||
|
|
||||||
void Draw(DrawingContext& ctx) override;
|
void Draw(Renderer& renderer) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
math::Vector m_p0;
|
math::Vector m_p0;
|
||||||
math::Vector m_p1;
|
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
|
#pragma once
|
||||||
|
|
||||||
#include "math/vector.hpp"
|
#include "math/vector.hpp"
|
||||||
#include "primitive.hpp"
|
#include "shape.hpp"
|
||||||
|
|
||||||
namespace primitives
|
namespace shapes
|
||||||
{
|
{
|
||||||
|
|
||||||
class Rectangle : public Primitive
|
class Rectangle : 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)
|
||||||
@ -32,7 +32,7 @@ class Rectangle : public Primitive
|
|||||||
void Rotate(const math::Vector& center, float angle) override;
|
void Rotate(const math::Vector& center, float angle) override;
|
||||||
void Scale(const math::Vector& center, float factor) override;
|
void Scale(const math::Vector& center, float factor) override;
|
||||||
|
|
||||||
void Draw(DrawingContext& ctx) override;
|
void Draw(Renderer& renderer) override;
|
||||||
|
|
||||||
~Rectangle() override = default;
|
~Rectangle() override = default;
|
||||||
|
|
||||||
@ -42,4 +42,4 @@ class Rectangle : public Primitive
|
|||||||
float m_angle;
|
float m_angle;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace primitives
|
} // namespace shapes
|
||||||
@ -2,23 +2,23 @@
|
|||||||
|
|
||||||
#include "math/vector.hpp"
|
#include "math/vector.hpp"
|
||||||
|
|
||||||
class DrawingContext;
|
class Renderer;
|
||||||
|
|
||||||
namespace primitives
|
namespace shapes
|
||||||
{
|
{
|
||||||
|
|
||||||
class Primitive
|
class Shape
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Primitive() = default;
|
Shape() = default;
|
||||||
|
|
||||||
virtual void Translate(const math::Vector& offset) = 0;
|
virtual void Translate(const math::Vector& offset) = 0;
|
||||||
virtual void Rotate(const math::Vector& center, float angle) = 0;
|
virtual void Rotate(const math::Vector& center, float angle) = 0;
|
||||||
virtual void Scale(const math::Vector& center, float factor) = 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