From d508045ec348cd3530495715cdd024b0a8d1aca5 Mon Sep 17 00:00:00 2001 From: tovjemam Date: Fri, 26 Sep 2025 11:00:11 +0200 Subject: [PATCH] Init --- .clang-format | 6 ++++ .gitignore | 2 ++ CMakeLists.txt | 17 +++++++++ bitmap.hpp | 45 +++++++++++++++++++++++ contexts/drawing_context.hpp | 14 ++++++++ contexts/pgm_drawing_context.cpp | 60 +++++++++++++++++++++++++++++++ contexts/pgm_drawing_context.hpp | 25 +++++++++++++ input_file.cpp | 2 ++ input_file.hpp | 17 +++++++++ main.cpp | 8 +++++ math/transforms.cpp | 20 +++++++++++ math/transforms.hpp | 15 ++++++++ math/vector.hpp | 62 ++++++++++++++++++++++++++++++++ primitives/circle.cpp | 24 +++++++++++++ primitives/circle.hpp | 38 ++++++++++++++++++++ primitives/line.cpp | 26 ++++++++++++++ primitives/line.hpp | 27 ++++++++++++++ primitives/primitive.hpp | 24 +++++++++++++ primitives/rectangle.cpp | 25 +++++++++++++ primitives/rectangle.hpp | 45 +++++++++++++++++++++++ 20 files changed, 502 insertions(+) create mode 100644 .clang-format create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 bitmap.hpp create mode 100644 contexts/drawing_context.hpp create mode 100644 contexts/pgm_drawing_context.cpp create mode 100644 contexts/pgm_drawing_context.hpp create mode 100644 input_file.cpp create mode 100644 input_file.hpp create mode 100644 main.cpp create mode 100644 math/transforms.cpp create mode 100644 math/transforms.hpp create mode 100644 math/vector.hpp create mode 100644 primitives/circle.cpp create mode 100644 primitives/circle.hpp create mode 100644 primitives/line.cpp create mode 100644 primitives/line.hpp create mode 100644 primitives/primitive.hpp create mode 100644 primitives/rectangle.cpp create mode 100644 primitives/rectangle.hpp diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..0836977 --- /dev/null +++ b/.clang-format @@ -0,0 +1,6 @@ +BasedOnStyle: Microsoft +DerivePointerAlignment: false +PointerAlignment: Left +IndentWidth: 4 # spaces per indent level +TabWidth: 4 # width of a tab character +UseTab: Never # options: Never, ForIndentation, Alwayss \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bc95448 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.cache/ +build/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..25b4786 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.10) + +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" + "input_file.cpp" +) + +target_include_directories(drawing PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/bitmap.hpp b/bitmap.hpp new file mode 100644 index 0000000..ce89a2c --- /dev/null +++ b/bitmap.hpp @@ -0,0 +1,45 @@ +#pragma once + +#include +#include +#include +#include +#include + +struct Color +{ + uint8_t l{}; +}; + +class Bitmap +{ + public: + Bitmap(size_t width, size_t height, const Color& clearColor) + : m_width(width), m_height(height), m_data(width * height, clearColor) + { + } + + // std::span operator[](size_t row) + // { + // return {&m_data[row * m_width], m_width}; + // }; + + // std::span operator[](size_t row) const + // { + // return {&m_data[row * m_width], m_width}; + // }; + + Color* operator[](size_t row) + { + return &m_data[row * m_width]; + }; + + const Color* operator[](size_t row) const + { + return &m_data[row * m_width]; + }; + + private: + size_t m_width, m_height; + std::vector m_data; +}; \ No newline at end of file diff --git a/contexts/drawing_context.hpp b/contexts/drawing_context.hpp new file mode 100644 index 0000000..9bfb084 --- /dev/null +++ b/contexts/drawing_context.hpp @@ -0,0 +1,14 @@ +#pragma once +#include "math/vector.hpp" + +class DrawingContext +{ + public: + 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 DrawCircle(const math::Vector& center, float radius) = 0; + + virtual void Flush() = 0; + + virtual ~DrawingContext() = 0; +}; diff --git a/contexts/pgm_drawing_context.cpp b/contexts/pgm_drawing_context.cpp new file mode 100644 index 0000000..bdc8aea --- /dev/null +++ b/contexts/pgm_drawing_context.cpp @@ -0,0 +1,60 @@ +#include "pgm_drawing_context.hpp" +#include + +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(p0.x), static_cast(p0.y), static_cast(p1.x), static_cast(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 */ + } +} diff --git a/contexts/pgm_drawing_context.hpp b/contexts/pgm_drawing_context.hpp new file mode 100644 index 0000000..03efc4d --- /dev/null +++ b/contexts/pgm_drawing_context.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include "bitmap.hpp" +#include "drawing_context.hpp" +#include +#include + +class PgmDrawingContext : public DrawingContext +{ + public: + PgmDrawingContext(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; + void DrawCircle(const math::Vector& center, float radius) override; + + void Flush() override; + + ~PgmDrawingContext() override = default; + + private: + Bitmap m_bitmap; + + void RasterizeLine(int x0, int y0, int x1, int y1); +}; \ No newline at end of file diff --git a/input_file.cpp b/input_file.cpp new file mode 100644 index 0000000..58b9eff --- /dev/null +++ b/input_file.cpp @@ -0,0 +1,2 @@ +#include "input_file.hpp" + diff --git a/input_file.hpp b/input_file.hpp new file mode 100644 index 0000000..cb9d256 --- /dev/null +++ b/input_file.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include +#include + +#include "drawing_context.hpp" + +class InputFile +{ + public: + InputFile(const std::filesystem::path& path); + + void Parse(DrawingContext& ctx); + + private: + std::ifstream m_file; +}; diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..070f1d7 --- /dev/null +++ b/main.cpp @@ -0,0 +1,8 @@ + +#include +#include "pgm_drawing_context.hpp" + +int main() +{ + std::cout << "sjeta" << std::endl; +} diff --git a/math/transforms.cpp b/math/transforms.cpp new file mode 100644 index 0000000..771cee3 --- /dev/null +++ b/math/transforms.cpp @@ -0,0 +1,20 @@ +#include "transforms.hpp" +#include + +math::Vector math::RotatePoint(const Vector& center, float angle, const Vector& p) +{ + const float sina = std::sin(angle); + const float cosa = std::cos(angle); + const Vector p1 = p - center; + return Vector(p1.x * cosa - p1.y * sina, p1.x * sina + p1.y * cosa) + center; +} + +math::Vector math::ScalePoint(const Vector& center, float factor, const Vector& p) +{ + return ((p - center) * factor) + center; +} + +float math::RotateAngle(float originalAngle, float rotationAngle) +{ + return std::fmodf(originalAngle + rotationAngle, PI * 2.0f); +} diff --git a/math/transforms.hpp b/math/transforms.hpp new file mode 100644 index 0000000..770c5ea --- /dev/null +++ b/math/transforms.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include "vector.hpp" + +namespace math +{ + +constexpr float PI = 3.14159265358979323846f; + +Vector RotatePoint(const Vector& center, float angle, const Vector& p); +Vector ScalePoint(const Vector& center, float factor, const Vector& p); +float RotateAngle(float originalAngle, float rotationAngle); + + +} // namespace math \ No newline at end of file diff --git a/math/vector.hpp b/math/vector.hpp new file mode 100644 index 0000000..e66cc55 --- /dev/null +++ b/math/vector.hpp @@ -0,0 +1,62 @@ +#pragma once + +#include + +namespace math +{ + +class Vector +{ + public: + Vector() : x{0.0f}, y{0.0f} + { + } + + Vector(float x, float y) : x{x}, y{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) + { + x += other.x; + y += other.y; + return *this; + } + + Vector operator-(const Vector& other) const + { + return Vector(x - other.x, y - other.y); + } + + Vector& operator-=(const Vector& other) + { + x -= other.x; + y -= other.y; + return *this; + } + + Vector operator*(float factor) const + { + return Vector(x * factor, y * factor); + } + + Vector& operator*=(float factor) + { + x *= factor; + y *= factor; + return *this; + } + + public: + float x, y; +}; + +} // namespace math diff --git a/primitives/circle.cpp b/primitives/circle.cpp new file mode 100644 index 0000000..98b5aa5 --- /dev/null +++ b/primitives/circle.cpp @@ -0,0 +1,24 @@ +#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); +} diff --git a/primitives/circle.hpp b/primitives/circle.hpp new file mode 100644 index 0000000..f099f53 --- /dev/null +++ b/primitives/circle.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include "primitive.hpp" + +namespace primitives +{ + +class Circle : public Primitive +{ + public: + Circle(const math::Vector& center, float radius) : m_center(center), m_radius(radius) + { + } + + const math::Vector& GetCenter() const + { + return m_center; + } + + float GetRadius() const + { + return m_radius; + } + + void Translate(const math::Vector& offset) override; + void Rotate(const math::Vector& center, float angle) override; + void Scale(const math::Vector& center, float factor) override; + + void Draw(DrawingContext& ctx) override; + + ~Circle() override = default; + + private: + math::Vector m_center; + float m_radius; +}; + +} // namespace primitives \ No newline at end of file diff --git a/primitives/line.cpp b/primitives/line.cpp new file mode 100644 index 0000000..d9ab04e --- /dev/null +++ b/primitives/line.cpp @@ -0,0 +1,26 @@ +#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); +} diff --git a/primitives/line.hpp b/primitives/line.hpp new file mode 100644 index 0000000..a0af59a --- /dev/null +++ b/primitives/line.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include "math/vector.hpp" +#include "primitive.hpp" + +namespace primitives +{ + +class Line : public Primitive +{ + public: + Line(const math::Vector& p0, const math::Vector& p1) : m_p0(p0), m_p1(p1) + { + } + + void Translate(const math::Vector& offset) override; + void Rotate(const math::Vector& center, float angle) override; + void Scale(const math::Vector& center, float factor) override; + + void Draw(DrawingContext& ctx) override; + + private: + math::Vector m_p0; + math::Vector m_p1; +}; + +} // namespace primitives \ No newline at end of file diff --git a/primitives/primitive.hpp b/primitives/primitive.hpp new file mode 100644 index 0000000..88302f9 --- /dev/null +++ b/primitives/primitive.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include "math/vector.hpp" + +class DrawingContext; + +namespace primitives +{ + +class Primitive +{ + public: + Primitive() = 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 ~Primitive() = 0; +}; + +} // namespace primitives diff --git a/primitives/rectangle.cpp b/primitives/rectangle.cpp new file mode 100644 index 0000000..82049f3 --- /dev/null +++ b/primitives/rectangle.cpp @@ -0,0 +1,25 @@ +#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); +} diff --git a/primitives/rectangle.hpp b/primitives/rectangle.hpp new file mode 100644 index 0000000..2f4c671 --- /dev/null +++ b/primitives/rectangle.hpp @@ -0,0 +1,45 @@ +#pragma once + +#include "math/vector.hpp" +#include "primitive.hpp" + +namespace primitives +{ + +class Rectangle : public Primitive +{ + public: + Rectangle(const math::Vector& pos, const math::Vector& size) : m_pos(pos), m_size(size), m_angle(0.0f) + { + } + + const math::Vector& GetPosition() const + { + return m_pos; + } + + const math::Vector& GetSize() const + { + return m_size; + } + + const float GetAngle() const + { + return m_angle; + } + + void Translate(const math::Vector& offset) override; + void Rotate(const math::Vector& center, float angle) override; + void Scale(const math::Vector& center, float factor) override; + + void Draw(DrawingContext& ctx) override; + + ~Rectangle() override = default; + + private: + math::Vector m_pos; + math::Vector m_size; + float m_angle; +}; + +} // namespace primitives \ No newline at end of file