141 lines
3.2 KiB
C++
141 lines
3.2 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);
|
|
}
|
|
}
|
|
}
|
|
}
|