cpp_drawing/input_file.cpp
2025-09-26 15:42:44 +02:00

140 lines
3.5 KiB
C++

#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;
}