cpp_drawing/renderers/pgm_renderer.cpp

137 lines
3.1 KiB
C++

#include "pgm_renderer.hpp"
#include <cmath>
#include <fstream>
#include <stdexcept>
PgmRenderer::PgmRenderer(size_t width, size_t height) : m_bitmap(width, height, Color{0xFF}) {}
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;
DrawLine(pA, pB);
DrawLine(pB, pC);
DrawLine(pC, pD);
DrawLine(pD, pA);
}
void PgmRenderer::DrawCircle(const math::Vector& center, float radius)
{
// TODO: cast
RasterizeCircle(center.x, center.y, radius);
}
void PgmRenderer::Save(const std::filesystem::path& path)
{
std::ofstream file{path};
if (!file.is_open())
{
throw std::runtime_error{"Cannot open file for writing: " + path.string()};
}
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 x = 0; x < m_bitmap.GetWidth(); ++x)
{
file << static_cast<int>(m_bitmap[y][x].l) << ' ';
}
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 (true)
{
// 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);
}
}
}
}