cpp_drawing/renderers/pgm_renderer.cpp
2025-09-26 18:39:24 +02:00

123 lines
3.0 KiB
C++

#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)
{
// TODO: cast
RasterizeCircle(center.x, center.y, radius);
}
PgmRenderer::~PgmRenderer()
{
Flush();
}
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)
{
// TODO: fix
const Color lineColor{0x00};
float w = 2.0f;
int dx = abs(x1 - x0);
int dy = abs(y1 - y0);
int sx = (x0 < x1) ? 1 : -1;
int sy = (y0 < y1) ? 1 : -1;
int err = dx - dy;
// perpendicular offsets for thickness
int ox = 0, oy = 0;
if (dx > dy) {
oy = 1;
} else {
ox = 1;
}
while (1) {
// draw a square of pixels for thickness
for (int i = -w/2; i <= w/2; i++) {
for (int j = -w/2; j <= w/2; j++) {
m_bitmap.Put(x0 + i*ox, y0 + j*oy, lineColor, 0xFF);
}
}
if (x0 == x1 && y0 == y1) break;
int e2 = 2 * err;
if (e2 > -dy) { err -= dy; x0 += sx; }
if (e2 < dx) { err += dx; y0 += sy; }
}
}
void PgmRenderer::RasterizeCircle(int cx, int cy, int r)
{
// TODO: fix
const Color circleColor{0x00};
float w = 2.0f;
int r_outer = r + w / 2;
int r_inner = r - w / 2;
if (r_inner < 0) r_inner = 0;
for (int y = -r_outer; y <= r_outer; y++) {
for (int x = -r_outer; x <= r_outer; x++) {
int dist2 = x*x + y*y;
if (dist2 <= r_outer*r_outer && dist2 >= r_inner*r_inner) {
m_bitmap.Put(cx + x, cy + y, circleColor, 0xFF);
}
}
}
}