// // Created by natty on 15.7.23. // #include #include #include #include #include "svgpathextractor.h" #include "renderwindow.h" template std::pair map(std::pair in); template<> std::pair map(std::pair in) { return { std::stod(std::string(in.first)), in.second }; } template<> std::pair map(std::pair in) { if (in.first == "0") { return { false, in.second }; } else if (in.first == "1") { return { true, in.second }; } else { throw std::runtime_error(std::format("Flag parse error: {}", in.first)); } } std::string_view::const_iterator skipWhitespace(std::string_view::const_iterator path, std::string_view::const_iterator end) { auto it = path; while (it != end && (*it == ' ' || *it == '\t' || *it == '\xA' || *it == '\xC' || *it == '\xD')) { ++it; } return it; } std::string_view::const_iterator skipComma(std::string_view::const_iterator path, std::string_view::const_iterator end) { auto it = path; while (it != end && (*it == ',')) { ++it; } return it; } std::string_view::const_iterator skipSeparators(std::string_view::const_iterator path, std::string_view::const_iterator end) { return skipWhitespace(skipComma(skipWhitespace(path, end), end), end); } std::pair throwIfEmpty(std::pair in) { if (in.first.empty()) { throw std::runtime_error("Unexpect end of input"); } return in; } std::pair nomDouble(std::string_view::const_iterator path, std::string_view::const_iterator end) { auto it = path; while (it != end && (*it == '-' && it == path || *it == '.' || (*it >= '0' && *it <= '9'))) { ++it; } return {{ &*path, static_cast(it - path) }, it}; } std::pair nomFlag(std::string_view::const_iterator path, std::string_view::const_iterator end) { auto it = path; while (it != end && ((*it == '0' || *it == '1'))) { ++it; } return {{ &*path, static_cast(it - path) }, it}; } enum class CommandType { Move, Line, HorLine, VerLine, Curve, SmoothCurve, Quadratic, SmoothQuadratic, EllipticalArc, ClosePath }; std::ostream& operator<< (std::ostream& stream, const CommandType& type) { switch (type) { case CommandType::Move: stream << "Move"; break; case CommandType::Line: stream << "Line"; break; case CommandType::HorLine: stream << "HorLine"; break; case CommandType::VerLine: stream << "VerLine"; break; case CommandType::Curve: stream << "Curve"; break; case CommandType::SmoothCurve: stream << "SmoothCurve"; break; case CommandType::Quadratic: stream << "Quadratic"; break; case CommandType::SmoothQuadratic: stream << "SmoothQuadratic"; break; case CommandType::EllipticalArc: stream << "EllipticalArc"; break; case CommandType::ClosePath: stream << "ClosePath"; break; } return stream; } struct Command { CommandType type; bool relative; }; std::ostream& operator<< (std::ostream& stream, const Command& command) { stream << command.type; if (command.relative) { stream << "Relative"; } return stream; } struct Token { enum class Type { Command, Number, End, Unknown } type; union TokenData { double value; Command command; } data; }; std::ostream& operator<< (std::ostream& stream, const Token& token) { switch (token.type) { case Token::Type::Command: stream << token.data.command; break; case Token::Type::Number: stream << token.data.value; break; case Token::Type::End: stream << "End"; break; case Token::Type::Unknown: stream << "Unknown"; break; } return stream; } std::pair nomToken(bool skipSeparator, std::string_view::const_iterator it, std::string_view::const_iterator end) { if (skipSeparator) it = skipSeparators(it, end); else it = skipWhitespace(it, end); if (it == end) { return {{ .type = Token::Type::End }, it}; } std::optional commandType; switch (std::tolower(*it)) { case 'm': commandType = CommandType::Move; break; case 'z': commandType = CommandType::ClosePath; break; case 'l': commandType = CommandType::Line; break; case 'h': commandType = CommandType::HorLine; break; case 'v': commandType = CommandType::VerLine; break; case 'c': commandType = CommandType::Curve; break; case 's': commandType = CommandType::SmoothCurve; break; case 'q': commandType = CommandType::Quadratic; break; case 't': commandType = CommandType::SmoothQuadratic; break; case 'a': commandType = CommandType::EllipticalArc; break; } if (commandType) { return {{ .type = Token::Type::Command, .data = { .command = { .type = *commandType, .relative = !static_cast(isupper(*it)) } } }, it + 1}; } if (*it == '-' || *it == '.' || (*it >= '0' && *it <= '9')) { auto [value, it2] = map(nomDouble(it, end)); return {{ .type = Token::Type::Number, .data = { .value = value } }, it2}; } return {{ .type = Token::Type::Unknown }, it}; } void interpolateCubic(std::vector& output, size_t segments, double sx, double sy, double x1, double y1, double x2, double y2, double x, double y) { auto segmentsScaled = static_cast(std::round(std::hypot(x - sx, y - sy) / 50.0 * static_cast(segments))); for (size_t i = 1; i < segmentsScaled; ++i) { const double t = static_cast(i) / static_cast(segmentsScaled); const double t2 = t * t; const double t3 = t2 * t; const double inv_t = 1.0 - t; const double inv_t2 = inv_t * inv_t; const double inv_t3 = inv_t2 * inv_t; const double px = inv_t3 * sx + 3 * inv_t2 * t * x1 + 3 * inv_t * t2 * x2 + t3 * x; const double py = inv_t3 * sy + 3 * inv_t2 * t * y1 + 3 * inv_t * t2 * y2 + t3 * y; output.emplace_back(px, py); } } void interpolateQuadratic(std::vector& output, size_t segments, double sx, double sy, double x1, double y1, double x, double y) { auto segmentsScaled = static_cast(std::round(std::hypot(x - sx, y - sy) / 50.0 * static_cast(segments))); for (size_t i = 1; i < segmentsScaled; ++i) { const double t = static_cast(i) / static_cast(segmentsScaled); const double t2 = t * t; const double inv_t = 1.0 - t; const double inv_t2 = inv_t * inv_t; const double px = inv_t2 * sx + 2 * inv_t * t * x1 + t2 * x; const double py = inv_t2 * sy + 2 * inv_t * t * y1 + t2 * y; output.emplace_back(px, py); } } void interpolateLine(std::vector& output, size_t segments, double sx, double sy, double x, double y) { auto segmentsScaled = static_cast(std::round(std::hypot(x - sx, y - sy) / 50.0 * static_cast(segments))); for (size_t i = 1; i < segmentsScaled; ++i) { const double t = static_cast(i) / static_cast(segmentsScaled); const double inv_t = 1.0 - t; const double px = inv_t * sx + t * x; const double py = inv_t * sy + t * y; output.emplace_back(px, py); } } double vecAngle(double ux, double uy, double vx, double vy) { double sign = ux * vy - uy * vx < 0.0 ? -1.0 : 1.0; double dot = ux * vx + uy * vy; double uLen = std::hypot(ux, uy); double vLen = std::hypot(vx, vy); return sign * std::acos(dot / (uLen * vLen)); } void interpolateArc(std::vector& output, const size_t segments, double rx, double ry, double rot, bool largeArcFlag, bool sweepFlag, double x1, double y1, double x2, double y2) { constexpr double PI = 3.14159; constexpr double PI2 = 2.0 * PI; double phi = rot * PI / 180.0; double dx = x1 - x2; double dy = y1 - y2; double dxHalf = dx / 2.0; double dyHalf = dy / 2.0; double xAvg = (x1 + x2) / 2.0; double yAvg = (y1 + y2) / 2.0; double sinPhi = std::sin(phi); double cosPhi = std::cos(phi); double x1p = cosPhi * dxHalf + sinPhi * dyHalf; double y1p = -sinPhi * dxHalf + cosPhi * dyHalf; double x1p2 = x1p * x1p; double y1p2 = y1p * y1p; double rx2 = rx * rx; double ry2 = ry * ry; double lambda = std::sqrt(x1p2 / rx2 + y1p2 / ry2); if (lambda > 1) { rx *= lambda; ry *= lambda; } double sig = largeArcFlag == sweepFlag ? -1.0 : 1.0; double aNum = rx2 * ry2 - rx2 * y1p2 - ry2 * x1p2; double aDenom = rx2 * y1p2 + ry2 * x1p2; double a = sig * std::sqrt(std::abs(aNum / aDenom)); double cxp = a * (rx * y1p / ry); double cyp = a * -(ry * x1p / rx); double cx = cosPhi * cxp - sinPhi * cyp + xAvg; double cy = sinPhi * cxp + cosPhi * cyp + yAvg; double svx = (x1p - cxp) / rx; double svy = (y1p - cyp) / ry; double theta = vecAngle(1.0, 0.0, svx, svy); double deltaThetaUnbound = vecAngle(svx, svy, (-x1p - cxp) / rx, (-y1p - cyp) / ry); double deltaTheta = std::fmod(deltaThetaUnbound, PI2); if (deltaTheta > 0.0 && !sweepFlag) deltaTheta -= PI2; if (deltaTheta < 0.0 && sweepFlag) deltaTheta += PI2; auto segmentsScaled = static_cast(std::round(std::hypot(rx, ry) / 50.0 * static_cast(segments))); for (size_t i = 0; i < segmentsScaled; ++i) { const double t = static_cast(i) / static_cast(segmentsScaled - 1); double px = rx * std::cos(theta + deltaTheta * t); double py = ry * std::sin(theta + deltaTheta * t); double x = cx + px * cosPhi - py * sinPhi; double y = cy + px * sinPhi + py * cosPhi; output.emplace_back(x, y); } } static constexpr size_t SEGMENTS = 20; std::vector extractVertices(std::string_view path) { std::vector vertices; std::optional lastCommand{}; bool repeatLastCommand = false; Vertex pos{}; Vertex lastControlPoint{}; Vertex subpathStart{}; auto it = path.cbegin(); while (it != path.end()) { auto lastCommandType = lastCommand ? lastCommand->type : CommandType::ClosePath; auto [token, itNext] = nomToken(lastCommandType != CommandType::ClosePath, it, path.end()); it = itNext; switch (token.type) { case Token::Type::End: break; case Token::Type::Number: switch (lastCommandType) { case CommandType::Move: { if (!repeatLastCommand) { vertices.push_back(pos); } auto x = token.data.value; it = skipSeparators(it, path.end()); auto [y, it2] = map(throwIfEmpty(nomDouble(it, path.end()))); double xAbs = lastCommand->relative ? pos.x + x : x; double yAbs = lastCommand->relative ? pos.y + y : y; interpolateLine( vertices, SEGMENTS, pos.x, pos.y, xAbs, yAbs ); pos = { xAbs, yAbs }; lastControlPoint = pos; vertices.push_back(pos); break; } case CommandType::Line: { auto x = token.data.value; it = skipSeparators(it, path.end()); auto [y, it2] = map(throwIfEmpty(nomDouble(it, path.end()))); double xAbs = lastCommand->relative ? pos.x + x : x; double yAbs = lastCommand->relative ? pos.y + y : y; interpolateLine( vertices, SEGMENTS, pos.x, pos.y, xAbs, yAbs ); pos = { xAbs, yAbs }; lastControlPoint = pos; vertices.push_back(pos); break; } case CommandType::HorLine: { auto x = token.data.value; double xAbs = lastCommand->relative ? pos.x + x : x; interpolateLine( vertices, SEGMENTS, pos.x, pos.y, xAbs, pos.y ); pos = { xAbs, pos.y }; lastControlPoint = pos; vertices.push_back(pos); break; } case CommandType::VerLine: { auto y = token.data.value; double yAbs = lastCommand->relative ? pos.y + y : y; interpolateLine( vertices, SEGMENTS, pos.x, pos.y, pos.x, yAbs ); pos = { pos.x, yAbs }; lastControlPoint = pos; vertices.push_back(pos); break; } case CommandType::Curve: { auto x1 = token.data.value; it = skipSeparators(it, path.end()); auto [y1, it3] = map(throwIfEmpty(nomDouble(it, path.end()))); it3 = skipSeparators(it3, path.end()); auto [x2, it4] = map(throwIfEmpty(nomDouble(it3, path.end()))); it4 = skipSeparators(it4, path.end()); auto [y2, it5] = map(throwIfEmpty(nomDouble(it4, path.end()))); it5 = skipSeparators(it5, path.end()); auto [x, it6] = map(throwIfEmpty(nomDouble(it5, path.end()))); it6 = skipSeparators(it6, path.end()); auto [y, it7] = map(throwIfEmpty(nomDouble(it6, path.end()))); it = it7; double x2abs = lastCommand->relative ? pos.x + x2 : x2; double y2abs = lastCommand->relative ? pos.y + y2 : y2; double xAbs = lastCommand->relative ? pos.x + x : x; double yAbs = lastCommand->relative ? pos.y + y : y; interpolateCubic( vertices, SEGMENTS, pos.x, pos.y, lastCommand->relative ? pos.x + x1 : x1, lastCommand->relative ? pos.y + y1 : y1, x2abs, y2abs, xAbs, yAbs ); lastControlPoint = { x2abs, y2abs }; pos = { xAbs, yAbs }; vertices.push_back(pos); break; } case CommandType::SmoothCurve: { auto x2 = token.data.value; it = skipSeparators(it, path.end()); auto [y2, it5] = map(throwIfEmpty(nomDouble(it, path.end()))); it5 = skipSeparators(it5, path.end()); auto [x, it6] = map(throwIfEmpty(nomDouble(it5, path.end()))); it6 = skipSeparators(it6, path.end()); auto [y, it7] = map(throwIfEmpty(nomDouble(it6, path.end()))); it = it7; double x1abs = 2.0 * pos.x - lastControlPoint.x; double y1abs = 2.0 * pos.y - lastControlPoint.y; double x2abs = lastCommand->relative ? pos.x + x2 : x2; double y2abs = lastCommand->relative ? pos.y + y2 : y2; double xAbs = lastCommand->relative ? pos.x + x : x; double yAbs = lastCommand->relative ? pos.y + y : y; interpolateCubic( vertices, SEGMENTS, pos.x, pos.y, x1abs, y1abs, x2abs, y2abs, xAbs, yAbs ); lastControlPoint = { x2abs, y2abs }; pos = { xAbs, yAbs }; vertices.push_back(pos); break; } case CommandType::Quadratic: { auto x1 = token.data.value; it = skipSeparators(it, path.end()); auto [y1, it3] = map(throwIfEmpty(nomDouble(it, path.end()))); it3 = skipSeparators(it3, path.end()); auto [x, it4] = map(throwIfEmpty(nomDouble(it3, path.end()))); it4 = skipSeparators(it4, path.end()); auto [y, it5] = map(throwIfEmpty(nomDouble(it4, path.end()))); it = it5; double x1abs = lastCommand->relative ? pos.x + x1 : x1; double y1abs = lastCommand->relative ? pos.y + y1 : y1; double xAbs = lastCommand->relative ? pos.x + x : x; double yAbs = lastCommand->relative ? pos.y + y : y; interpolateQuadratic( vertices, SEGMENTS, pos.x, pos.y, x1abs, y1abs, xAbs, yAbs ); lastControlPoint = { x1abs, y1abs }; pos = { xAbs, yAbs }; vertices.push_back(pos); break; } case CommandType::SmoothQuadratic: { auto x = token.data.value; it = skipSeparators(it, path.end()); auto [y, it2] = map(throwIfEmpty(nomDouble(it, path.end()))); it = it2; double x1abs = 2.0 * pos.x - lastControlPoint.x; double y1abs = 2.0 * pos.y - lastControlPoint.y; double xAbs = lastCommand->relative ? pos.x + x : x; double yAbs = lastCommand->relative ? pos.y + y : y; interpolateQuadratic( vertices, SEGMENTS, pos.x, pos.y, x1abs, y1abs, xAbs, yAbs ); lastControlPoint = { x1abs, y1abs }; pos = { xAbs, yAbs }; vertices.push_back(pos); break; } case CommandType::EllipticalArc: { it = skipSeparators(it, path.end()); auto rx = token.data.value; auto [ry, it3] = map(throwIfEmpty(nomDouble(it, path.end()))); it3 = skipSeparators(it3, path.end()); auto [rot, it4] = map(throwIfEmpty(nomDouble(it3, path.end()))); it4 = skipSeparators(it4, path.end()); auto [largeArcFlag, it5] = map(throwIfEmpty(nomFlag(it4, path.end()))); it5 = skipSeparators(it5, path.end()); auto [sweepFlag, it6] = map(throwIfEmpty(nomFlag(it5, path.end()))); it6 = skipSeparators(it6, path.end()); auto [x, it7] = map(throwIfEmpty(nomDouble(it6, path.end()))); it7 = skipSeparators(it6, path.end()); auto [y, it8] = map(throwIfEmpty(nomDouble(it7, path.end()))); it = it8; rx = abs(rx); ry = abs(ry); double xAbs = lastCommand->relative ? pos.x + x : x; double yAbs = lastCommand->relative ? pos.y + y : y; interpolateArc(vertices, SEGMENTS, rx, ry, rot, largeArcFlag, sweepFlag, pos.x, pos.y, xAbs, yAbs); pos = { xAbs, yAbs }; lastControlPoint = pos; break; } case CommandType::ClosePath: throw std::runtime_error("Unexpected number"); } if (!repeatLastCommand) { repeatLastCommand = true; } break; case Token::Type::Command: switch (token.data.command.type) { case CommandType::Move: { it = skipWhitespace(it, path.end()); auto [x, it2] = map(throwIfEmpty(nomDouble(it, path.end()))); it2 = skipSeparators(it2, path.end()); auto [y, it3] = map(throwIfEmpty(nomDouble(it2, path.end()))); it = it3; if (token.data.command.relative) { pos.x += x; pos.y += y; } else { pos = { x, y }; } subpathStart = pos; lastControlPoint = pos; break; } case CommandType::Line: { if (!repeatLastCommand && (lastCommand && lastCommand->type == CommandType::Move)) { vertices.push_back(pos); } it = skipWhitespace(it, path.end()); auto [x, it2] = map(throwIfEmpty(nomDouble(it, path.end()))); it2 = skipSeparators(it2, path.end()); auto [y, it3] = map(throwIfEmpty(nomDouble(it2, path.end()))); it = it3; double xAbs = token.data.command.relative ? pos.x + x : x; double yAbs = token.data.command.relative ? pos.y + y : y; interpolateLine( vertices, SEGMENTS, pos.x, pos.y, xAbs, yAbs ); pos = { xAbs, yAbs }; lastControlPoint = pos; vertices.push_back(pos); break; } case CommandType::HorLine: { if (!repeatLastCommand && (lastCommand && lastCommand->type == CommandType::Move)) { vertices.push_back(pos); } it = skipWhitespace(it, path.end()); auto [x, it2] = map(throwIfEmpty(nomDouble(it, path.end()))); it = it2; double xAbs = token.data.command.relative ? pos.x + x : x; interpolateLine( vertices, SEGMENTS, pos.x, pos.y, xAbs, pos.y ); pos = { xAbs, pos.y }; lastControlPoint = pos; vertices.push_back(pos); break; } case CommandType::VerLine: { if (!repeatLastCommand && (lastCommand && lastCommand->type == CommandType::Move)) { vertices.push_back(pos); } it = skipWhitespace(it, path.end()); auto [y, it2] = map(throwIfEmpty(nomDouble(it, path.end()))); it = it2; double yAbs = token.data.command.relative ? pos.y + y : y; interpolateLine( vertices, SEGMENTS, pos.x, pos.y, pos.x, yAbs ); pos = { pos.x, yAbs }; lastControlPoint = pos; vertices.push_back(pos); break; } case CommandType::Curve: { if (!repeatLastCommand && (lastCommand && lastCommand->type == CommandType::Move)) { vertices.push_back(pos); } it = skipWhitespace(it, path.end()); auto [x1, it2] = map(throwIfEmpty(nomDouble(it, path.end()))); it2 = skipSeparators(it2, path.end()); auto [y1, it3] = map(throwIfEmpty(nomDouble(it2, path.end()))); it3 = skipSeparators(it3, path.end()); auto [x2, it4] = map(throwIfEmpty(nomDouble(it3, path.end()))); it4 = skipSeparators(it4, path.end()); auto [y2, it5] = map(throwIfEmpty(nomDouble(it4, path.end()))); it5 = skipSeparators(it5, path.end()); auto [x, it6] = map(throwIfEmpty(nomDouble(it5, path.end()))); it6 = skipSeparators(it6, path.end()); auto [y, it7] = map(throwIfEmpty(nomDouble(it6, path.end()))); it = it7; double x2abs = token.data.command.relative ? pos.x + x2 : x2; double y2abs = token.data.command.relative ? pos.y + y2 : y2; double xAbs = token.data.command.relative ? pos.x + x : x; double yAbs = token.data.command.relative ? pos.y + y : y; interpolateCubic( vertices, SEGMENTS, pos.x, pos.y, token.data.command.relative ? pos.x + x1 : x1, token.data.command.relative ? pos.y + y1 : y1, x2abs, y2abs, xAbs, yAbs ); lastControlPoint = { x2abs, y2abs }; pos = { xAbs, yAbs }; vertices.push_back(pos); break; } case CommandType::SmoothCurve: { if (!repeatLastCommand && (lastCommand && lastCommand->type == CommandType::Move)) { vertices.push_back(pos); } it = skipWhitespace(it, path.end()); auto [x2, it4] = map(throwIfEmpty(nomDouble(it, path.end()))); it4 = skipSeparators(it4, path.end()); auto [y2, it5] = map(throwIfEmpty(nomDouble(it4, path.end()))); it5 = skipSeparators(it5, path.end()); auto [x, it6] = map(throwIfEmpty(nomDouble(it5, path.end()))); it6 = skipSeparators(it6, path.end()); auto [y, it7] = map(throwIfEmpty(nomDouble(it6, path.end()))); it = it7; double x1abs, y1abs; if (!lastCommand || (lastCommand->type != CommandType::Curve && lastCommand->type != CommandType::SmoothCurve)) { x1abs = pos.x; y1abs = pos.y; } else { x1abs = 2.0 * pos.x - lastControlPoint.x; y1abs = 2.0 * pos.y - lastControlPoint.y; } double x2abs = token.data.command.relative ? pos.x + x2 : x2; double y2abs = token.data.command.relative ? pos.y + y2 : y2; double xAbs = token.data.command.relative ? pos.x + x : x; double yAbs = token.data.command.relative ? pos.y + y : y; interpolateCubic( vertices, SEGMENTS, pos.x, pos.y, x1abs, y1abs, x2abs, y2abs, xAbs, yAbs ); lastControlPoint = { x2abs, y2abs }; pos = { xAbs, yAbs }; vertices.push_back(pos); break; } case CommandType::Quadratic: { if (!repeatLastCommand && (lastCommand && lastCommand->type == CommandType::Move)) { vertices.push_back(pos); } it = skipWhitespace(it, path.end()); auto [x1, it4] = map(throwIfEmpty(nomDouble(it, path.end()))); it4 = skipSeparators(it4, path.end()); auto [y1, it5] = map(throwIfEmpty(nomDouble(it4, path.end()))); it5 = skipSeparators(it5, path.end()); auto [x, it6] = map(throwIfEmpty(nomDouble(it5, path.end()))); it6 = skipSeparators(it6, path.end()); auto [y, it7] = map(throwIfEmpty(nomDouble(it6, path.end()))); it = it7; double x1abs = token.data.command.relative ? pos.x + x1 : x1; double y1abs = token.data.command.relative ? pos.y + y1 : y1; double xAbs = token.data.command.relative ? pos.x + x : x; double yAbs = token.data.command.relative ? pos.y + y : y; interpolateQuadratic( vertices, SEGMENTS, pos.x, pos.y, x1abs, y1abs, xAbs, yAbs ); pos = { xAbs, yAbs }; lastControlPoint = { x1abs, y1abs }; vertices.push_back(pos); break; } case CommandType::SmoothQuadratic: { if (!repeatLastCommand && (lastCommand && lastCommand->type == CommandType::Move)) { vertices.push_back(pos); } it = skipWhitespace(it, path.end()); auto [x, it2] = map(throwIfEmpty(nomDouble(it, path.end()))); it2 = skipSeparators(it2, path.end()); auto [y, it3] = map(throwIfEmpty(nomDouble(it2, path.end()))); it = it3; double x1abs, y1abs; if (!lastCommand || (lastCommand->type != CommandType::Quadratic && lastCommand->type != CommandType::SmoothQuadratic)) { x1abs = pos.x; y1abs = pos.y; } else { x1abs = 2.0 * pos.x - lastControlPoint.x; y1abs = 2.0 * pos.y - lastControlPoint.y; } double xAbs = token.data.command.relative ? pos.x + x : x; double yAbs = token.data.command.relative ? pos.y + y : y; interpolateQuadratic( vertices, SEGMENTS, pos.x, pos.y, x1abs, y1abs, xAbs, yAbs ); lastControlPoint = { x1abs, y1abs }; pos = { xAbs, yAbs }; vertices.push_back(pos); break; } case CommandType::EllipticalArc: { it = skipWhitespace(it, path.end()); auto [rx, it2] = map(throwIfEmpty(nomDouble(it, path.end()))); it2 = skipSeparators(it2, path.end()); auto [ry, it3] = map(throwIfEmpty(nomDouble(it2, path.end()))); it3 = skipSeparators(it3, path.end()); auto [rot, it4] = map(throwIfEmpty(nomDouble(it3, path.end()))); it4 = skipSeparators(it4, path.end()); auto [largeArcFlag, it5] = map(throwIfEmpty(nomFlag(it4, path.end()))); it5 = skipSeparators(it5, path.end()); auto [sweepFlag, it6] = map(throwIfEmpty(nomFlag(it5, path.end()))); it6 = skipSeparators(it6, path.end()); auto [x, it7] = map(throwIfEmpty(nomDouble(it6, path.end()))); it7 = skipSeparators(it7, path.end()); auto [y, it8] = map(throwIfEmpty(nomDouble(it7, path.end()))); it = it8; rx = abs(rx); ry = abs(ry); double xAbs = token.data.command.relative ? pos.x + x : x; double yAbs = token.data.command.relative ? pos.y + y : y; interpolateArc(vertices, SEGMENTS, rx, ry, rot, largeArcFlag, sweepFlag, pos.x, pos.y, xAbs, yAbs); pos = { xAbs, yAbs }; lastControlPoint = pos; break; } case CommandType::ClosePath: { interpolateLine( vertices, SEGMENTS, pos.x, pos.y, subpathStart.x, subpathStart.y ); break; } } lastCommand = token.data.command; repeatLastCommand = false; break; case Token::Type::Unknown: throw std::runtime_error("Unknown token"); } } return vertices; }