118 lines
2.8 KiB
C++
118 lines
2.8 KiB
C++
|
#include "SVG.hpp"
|
||
|
|
||
|
#include <format>
|
||
|
|
||
|
namespace Kanimaji::SVG
|
||
|
{
|
||
|
struct Vec2
|
||
|
{
|
||
|
double x;
|
||
|
double y;
|
||
|
|
||
|
friend Vec2 operator+(const Vec2& u, const Vec2& v)
|
||
|
{
|
||
|
return Vec2 {
|
||
|
.x = u.x + v.x,
|
||
|
.y = u.y + v.y,
|
||
|
};
|
||
|
}
|
||
|
};
|
||
|
|
||
|
class Path::Element
|
||
|
{
|
||
|
public:
|
||
|
virtual ~Element() = default;
|
||
|
|
||
|
virtual double Length(Vec2 origin) const = 0;
|
||
|
virtual Vec2 Endpoint(Vec2 origin) const = 0;
|
||
|
};
|
||
|
|
||
|
namespace
|
||
|
{
|
||
|
class ParseError : public Error
|
||
|
{
|
||
|
public:
|
||
|
ParseError(std::size_t current, std::string_view message)
|
||
|
: Error(std::format("[At: {}] {}", current, message))
|
||
|
{}
|
||
|
};
|
||
|
|
||
|
class Move : public Path::Element
|
||
|
{
|
||
|
public:
|
||
|
Move(bool relative, Vec2 move)
|
||
|
: mRelative(relative), mMove(move)
|
||
|
{}
|
||
|
|
||
|
double Length(Vec2 origin) const override
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
Vec2 Endpoint(Vec2 origin) const override
|
||
|
{
|
||
|
return mRelative ? origin + mMove : mMove;
|
||
|
}
|
||
|
private:
|
||
|
bool mRelative; // 7B padding, oof
|
||
|
Vec2 mMove;
|
||
|
};
|
||
|
|
||
|
class CubicBezier : public Path::Element
|
||
|
{
|
||
|
public:
|
||
|
CubicBezier(bool relative, Vec2 control1, Vec2 control2, Vec2 end)
|
||
|
: mRelative(relative), mControl1(control1), mControl2(control2), mEnd(end)
|
||
|
{}
|
||
|
|
||
|
double Length(Vec2 origin) const override
|
||
|
{
|
||
|
throw Error("not implemented");
|
||
|
}
|
||
|
|
||
|
Vec2 Endpoint(Vec2 origin) const override
|
||
|
{
|
||
|
return mEnd;
|
||
|
}
|
||
|
private:
|
||
|
bool mRelative; // 7B padding, oof
|
||
|
Vec2 mControl1;
|
||
|
Vec2 mControl2;
|
||
|
Vec2 mEnd;
|
||
|
};
|
||
|
|
||
|
using ElementPtr = std::unique_ptr<Path::Element>;
|
||
|
void Parse(std::string_view definition, std::vector<ElementPtr>& elements)
|
||
|
{
|
||
|
std::size_t current = 0;
|
||
|
|
||
|
while (current < definition.size()) {
|
||
|
switch (definition[current]) {
|
||
|
case 'M': [[fallthrough]];
|
||
|
case 'm':
|
||
|
// ParseMove
|
||
|
break;
|
||
|
case 'C': [[fallthrough]];
|
||
|
case 'c':
|
||
|
// ParseCubicBezier
|
||
|
break;
|
||
|
default:
|
||
|
throw ParseError(current, "Unrecognized character");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Path::Path(std::string_view definition)
|
||
|
{
|
||
|
Parse(definition, mSegments);
|
||
|
}
|
||
|
|
||
|
Path::~Path() = default;
|
||
|
|
||
|
double Path::Length() const
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
}
|