Completely remove custom SVG implementation and pugixml library in favour of JUCE classes

pull/170/head
James Ball 2023-07-30 14:01:56 +01:00
rodzic 4a3ba6c99c
commit c9a46c29d0
20 zmienionych plików z 71 dodań i 15468 usunięć

Wyświetl plik

@ -97,9 +97,12 @@ Vector2 Shape::maxVector(std::vector<std::unique_ptr<Shape>>& shapes) {
}
void Shape::removeOutOfBounds(std::vector<std::unique_ptr<Shape>>& shapes) {
std::vector<int> toRemove;
for (int i = 0; i < shapes.size(); i++) {
Vector2 start = shapes[i]->nextVector(0);
Vector2 end = shapes[i]->nextVector(1);
bool keep = false;
if ((start.x < 1 && start.x > -1) || (start.y < 1 && start.y > -1)) {
if ((end.x < 1 && end.x > -1) || (end.y < 1 && end.y > -1)) {
@ -108,7 +111,16 @@ void Shape::removeOutOfBounds(std::vector<std::unique_ptr<Shape>>& shapes) {
Vector2 newEnd(std::min(std::max(end.x, -1.0), 1.0), std::min(std::max(end.y, -1.0), 1.0));
shapes[i] = std::make_unique<Line>(newStart.x, newStart.y, newEnd.x, newEnd.y);
}
keep = true;
}
}
if (!keep) {
toRemove.push_back(i);
}
}
for (int i = toRemove.size() - 1; i >= 0; i--) {
shapes.erase(shapes.begin() + toRemove[i]);
}
}

Wyświetl plik

@ -1,19 +0,0 @@
#include "ClosePath.h"
#include "../shape/Line.h"
std::vector<std::unique_ptr<Shape>> ClosePath::absolute(SvgState& state, std::vector<float>& args) {
return parseClosePath(state, args);
}
std::vector<std::unique_ptr<Shape>> ClosePath::relative(SvgState& state, std::vector<float>& args) {
return parseClosePath(state, args);
}
std::vector<std::unique_ptr<Shape>> ClosePath::parseClosePath(SvgState& state, std::vector<float>& args) {
auto shapes = std::vector<std::unique_ptr<Shape>>();
if (state.currPoint.x != state.initialPoint.x || state.currPoint.y != state.initialPoint.y) {
state.currPoint = state.initialPoint;
shapes.push_back(std::make_unique<Line>(state.currPoint.x, state.currPoint.y, state.initialPoint.x, state.initialPoint.y));
}
return shapes;
}

Wyświetl plik

@ -1,12 +0,0 @@
#pragma once
#include <vector>
#include "../shape/Shape.h"
#include "SvgState.h"
class ClosePath {
public:
static std::vector<std::unique_ptr<Shape>> absolute(SvgState& state, std::vector<float>& args);
static std::vector<std::unique_ptr<Shape>> relative(SvgState& state, std::vector<float>& args);
private:
static std::vector<std::unique_ptr<Shape>> parseClosePath(SvgState& state, std::vector<float>& args);
};

Wyświetl plik

@ -1,107 +0,0 @@
#include "CurveTo.h"
#include "../shape/CubicBezierCurve.h"
#include "../shape/QuadraticBezierCurve.h"
std::vector<std::unique_ptr<Shape>> CurveTo::absolute(SvgState& state, std::vector<float>& args) {
return parseCurveTo(state, args, true, true, false);
}
std::vector<std::unique_ptr<Shape>> CurveTo::relative(SvgState& state, std::vector<float>& args) {
return parseCurveTo(state, args, false, true, false);
}
std::vector<std::unique_ptr<Shape>> CurveTo::smoothAbsolute(SvgState& state, std::vector<float>& args) {
return parseCurveTo(state, args, true, true, true);
}
std::vector<std::unique_ptr<Shape>> CurveTo::smoothRelative(SvgState& state, std::vector<float>& args) {
return parseCurveTo(state, args, false, true, true);
}
std::vector<std::unique_ptr<Shape>> CurveTo::quarticAbsolute(SvgState& state, std::vector<float>& args) {
return parseCurveTo(state, args, true, false, false);
}
std::vector<std::unique_ptr<Shape>> CurveTo::quarticRelative(SvgState& state, std::vector<float>& args) {
return parseCurveTo(state, args, false, false, false);
}
std::vector<std::unique_ptr<Shape>> CurveTo::quarticSmoothAbsolute(SvgState& state, std::vector<float>& args) {
return parseCurveTo(state, args, true, false, true);
}
std::vector<std::unique_ptr<Shape>> CurveTo::quarticSmoothRelative(SvgState& state, std::vector<float>& args) {
return parseCurveTo(state, args, false, false, true);
}
// Parses curveto commands (C, c, S, s, Q, q, T, and t commands)
// isCubic should be true for parsing C, c, S, and s commands
// isCubic should be false for parsing Q, q, T, and t commands
// isSmooth should be true for parsing S, s, T, and t commands
// isSmooth should be false for parsing C, c, Q, and q commands
std::vector<std::unique_ptr<Shape>> CurveTo::parseCurveTo(SvgState& state, std::vector<float>& args, bool isAbsolute, bool isCubic, bool isSmooth) {
int expectedArgs = isCubic ? 4 : 2;
if (!isSmooth) {
expectedArgs += 2;
}
if (args.size() % expectedArgs != 0 || args.size() < expectedArgs) {
return {};
}
auto curves = std::vector<std::unique_ptr<Shape>>();
for (int i = 0; i < args.size(); i += expectedArgs) {
Vector2 controlPoint1;
Vector2 controlPoint2;
if (isSmooth) {
if (isCubic) {
if (!state.prevCubicControlPoint.has_value()) {
controlPoint1 = state.currPoint;
} else {
controlPoint1 = state.prevCubicControlPoint.value();
}
} else {
if (!state.prevQuadraticControlPoint.has_value()) {
controlPoint1 = state.currPoint;
} else {
controlPoint1 = state.prevQuadraticControlPoint.value();
}
}
} else {
controlPoint1 = Vector2(args[i], args[i + 1]);
}
if (isCubic) {
controlPoint2 = Vector2(args[i + expectedArgs - 4], args[i + expectedArgs - 3]);
}
Vector2 newPoint(args[i + expectedArgs - 2], args[i + expectedArgs - 1]);
if (!isAbsolute) {
if (!isSmooth) {
controlPoint1.translate(state.currPoint.x, state.currPoint.y);
}
controlPoint2.translate(state.currPoint.x, state.currPoint.y);
newPoint.translate(state.currPoint.x, state.currPoint.y);
}
if (isSmooth) {
controlPoint1.reflectRelativeToVector(state.currPoint.x, state.currPoint.y);
}
if (isCubic) {
curves.push_back(std::make_unique<CubicBezierCurve>(state.currPoint.x, state.currPoint.y, controlPoint1.x, controlPoint1.y, controlPoint2.x, controlPoint2.y, newPoint.x, newPoint.y));
state.currPoint = newPoint;
state.prevCubicControlPoint = controlPoint2;
} else {
curves.push_back(std::make_unique<QuadraticBezierCurve>(state.currPoint.x, state.currPoint.y, controlPoint1.x, controlPoint1.y, newPoint.x, newPoint.y));
state.currPoint = newPoint;
state.prevQuadraticControlPoint = controlPoint1;
}
}
return curves;
}

Wyświetl plik

@ -1,18 +0,0 @@
#pragma once
#include <vector>
#include "../shape/Shape.h"
#include "SvgState.h"
class CurveTo {
public:
static std::vector<std::unique_ptr<Shape>> absolute(SvgState& state, std::vector<float>& args);
static std::vector<std::unique_ptr<Shape>> relative(SvgState& state, std::vector<float>& args);
static std::vector<std::unique_ptr<Shape>> smoothAbsolute(SvgState& state, std::vector<float>& args);
static std::vector<std::unique_ptr<Shape>> smoothRelative(SvgState& state, std::vector<float>& args);
static std::vector<std::unique_ptr<Shape>> quarticAbsolute(SvgState& state, std::vector<float>& args);
static std::vector<std::unique_ptr<Shape>> quarticRelative(SvgState& state, std::vector<float>& args);
static std::vector<std::unique_ptr<Shape>> quarticSmoothAbsolute(SvgState& state, std::vector<float>& args);
static std::vector<std::unique_ptr<Shape>> quarticSmoothRelative(SvgState& state, std::vector<float>& args);
private:
static std::vector<std::unique_ptr<Shape>> parseCurveTo(SvgState& state, std::vector<float>& args, bool isAbsolute, bool isCubic, bool isSmooth);
};

Wyświetl plik

@ -1,125 +0,0 @@
#include "EllipticalArcTo.h"
#include "../shape/Line.h"
#include <numbers>
#include "../shape/CircleArc.h"
std::vector<std::unique_ptr<Shape>> EllipticalArcTo::absolute(SvgState& state, std::vector<float>& args) {
return parseEllipticalArc(state, args, true);
}
std::vector<std::unique_ptr<Shape>> EllipticalArcTo::relative(SvgState& state, std::vector<float>& args) {
return parseEllipticalArc(state, args, false);
}
std::vector<std::unique_ptr<Shape>> EllipticalArcTo::parseEllipticalArc(SvgState& state, std::vector<float>& args, bool isAbsolute) {
if (args.size() % 7 != 0 || args.size() < 7) {
return {};
}
std::vector<std::unique_ptr<Shape>> shapes;
for (int i = 0; i < args.size(); i += 7) {
Vector2 newPoint(args[i + 5], args[i + 6]);
if (!isAbsolute) {
newPoint.translate(state.currPoint.x, state.currPoint.y);
}
createArc(shapes, state.currPoint, args[i], args[i + 1], args[i + 2], args[i + 3] == 1, args[i + 4] == 1, newPoint);
state.currPoint = newPoint;
}
return shapes;
}
// The following algorithm is completely based on https://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
void EllipticalArcTo::createArc(std::vector<std::unique_ptr<Shape>>& shapes, Vector2 start, float rx, float ry, float theta, bool largeArcFlag, bool sweepFlag, Vector2 end) {
double x2 = end.x;
double y2 = end.y;
// Ensure radii are valid
if (rx == 0 || ry == 0) {
shapes.push_back(std::make_unique<Line>(start.x, start.y, end.x, end.y));
return;
}
double x1 = start.x;
double y1 = start.y;
// Compute the half distance between the current and the final point
double dx2 = (x1 - x2) / 2.0;
double dy2 = (y1 - y2) / 2.0;
// Convert theta from degrees to radians
theta = std::fmod(theta, 360.0) * (std::numbers::pi / 180.0);
//
// Step 1 : Compute (x1', y1')
//
double x1prime = std::cos(theta) * dx2 + std::sin(theta) * dy2;
double y1prime = -std::sin(theta) * dx2 + std::cos(theta) * dy2;
// Ensure radii are large enough
rx = std::abs(rx);
ry = std::abs(ry);
double Prx = rx * rx;
double Pry = ry * ry;
double Px1prime = x1prime * x1prime;
double Py1prime = y1prime * y1prime;
double d = Px1prime / Prx + Py1prime / Pry;
if (d > 1) {
rx = std::abs(std::sqrt(d) * rx);
ry = std::abs(std::sqrt(d) * ry);
Prx = rx * rx;
Pry = ry * ry;
}
//
// Step 2 : Compute (cx', cy')
//
double sign = (largeArcFlag == sweepFlag) ? -1.0 : 1.0;
// Forcing the inner term to be positive. It should be >= 0 but can sometimes be negative due
// to double precision.
double coef = sign *
std::sqrt(std::abs((Prx * Pry) - (Prx * Py1prime) - (Pry * Px1prime)) / ((Prx * Py1prime) + (Pry * Px1prime)));
double cxprime = coef * ((rx * y1prime) / ry);
double cyprime = coef * -((ry * x1prime) / rx);
//
// Step 3 : Compute (cx, cy) from (cx', cy')
//
double sx2 = (x1 + x2) / 2.0;
double sy2 = (y1 + y2) / 2.0;
double cx = sx2 + std::cos(theta) * cxprime - std::sin(theta) * cyprime;
double cy = sy2 + std::sin(theta) * cxprime + std::cos(theta) * cyprime;
//
// Step 4 : Compute the angleStart (theta1) and the angleExtent (dtheta)
//
double ux = (x1prime - cxprime) / rx;
double uy = (y1prime - cyprime) / ry;
double vx = (-x1prime - cxprime) / rx;
double vy = (-y1prime - cyprime) / ry;
double p, n;
// Compute the angle start
n = std::sqrt((ux * ux) + (uy * uy));
p = ux; // (1 * ux) + (0 * uy)
sign = (uy < 0) ? -1.0 : 1.0;
double angleStart = sign * std::acos(p / n);
// Compute the angle extent
n = std::sqrt((ux * ux + uy * uy) * (vx * vx + vy * vy));
p = ux * vx + uy * vy;
sign = (ux * vy - uy * vx < 0) ? -1.0 : 1.0;
double angleExtent = sign * std::acos(p / n);
if (!sweepFlag && angleExtent > 0) {
angleExtent -= 2 * std::numbers::pi;
} else if (sweepFlag && angleExtent < 0) {
angleExtent += 2 * std::numbers::pi;
}
angleExtent = std::fmod(angleExtent, 2 * std::numbers::pi);
angleStart = std::fmod(angleStart, 2 * std::numbers::pi);
auto arc = std::make_unique<CircleArc>(cx, cy, rx, ry, angleStart, angleExtent);
arc->translate(-cx, -cy);
arc->rotate(theta);
arc->translate(cx, cy);
Vector2 startPoint = arc->nextVector(0);
Vector2 endPoint = arc->nextVector(1);
shapes.push_back(std::move(arc));
}

Wyświetl plik

@ -1,13 +0,0 @@
#pragma once
#include <vector>
#include "../shape/Shape.h"
#include "SvgState.h"
class EllipticalArcTo {
public:
static std::vector<std::unique_ptr<Shape>> absolute(SvgState& state, std::vector<float>& args);
static std::vector<std::unique_ptr<Shape>> relative(SvgState& state, std::vector<float>& args);
private:
static std::vector<std::unique_ptr<Shape>> parseEllipticalArc(SvgState& state, std::vector<float>& args, bool isAbsolute);
static void createArc(std::vector<std::unique_ptr<Shape>>& shapes, Vector2 start, float rx, float ry, float theta, bool largeArcFlag, bool sweepFlag, Vector2 end);
};

Wyświetl plik

@ -1,74 +0,0 @@
#include "LineTo.h"
#include "../shape/Line.h"
std::vector<std::unique_ptr<Shape>> LineTo::absolute(SvgState& state, std::vector<float>& args) {
return parseLineTo(state, args, true, true, true);
}
std::vector<std::unique_ptr<Shape>> LineTo::relative(SvgState& state, std::vector<float>& args) {
return parseLineTo(state, args, false, true, true);
}
std::vector<std::unique_ptr<Shape>> LineTo::horizontalAbsolute(SvgState& state, std::vector<float>& args) {
return parseLineTo(state, args, true, true, false);
}
std::vector<std::unique_ptr<Shape>> LineTo::horizontalRelative(SvgState& state, std::vector<float>& args) {
return parseLineTo(state, args, false, true, false);
}
std::vector<std::unique_ptr<Shape>> LineTo::verticalAbsolute(SvgState& state, std::vector<float>& args) {
return parseLineTo(state, args, true, false, true);
}
std::vector<std::unique_ptr<Shape>> LineTo::verticalRelative(SvgState& state, std::vector<float>& args) {
return parseLineTo(state, args, false, false, true);
}
// Parses lineto commands (L, l, H, h, V, and v commands)
// isHorizontal and isVertical should be true for parsing L and l commands
// Only isHorizontal should be true for parsing H and h commands
// Only isVertical should be true for parsing V and v commands
std::vector<std::unique_ptr<Shape>> LineTo::parseLineTo(SvgState& state, std::vector<float>& args, bool isAbsolute, bool isHorizontal, bool isVertical) {
int expectedArgs = isHorizontal && isVertical ? 2 : 1;
if (args.size() % expectedArgs != 0 || args.size() < expectedArgs) {
return {};
}
auto lines = std::vector<std::unique_ptr<Shape>>();
for (int i = 0; i < args.size(); i += expectedArgs) {
Vector2 newPoint;
if (expectedArgs == 1) {
newPoint = Vector2(args[i], args[i]);
} else {
newPoint = Vector2(args[i], args[i + 1]);
}
if (isHorizontal && !isVertical) {
if (isAbsolute) {
newPoint.y = state.currPoint.y;
} else {
newPoint.y = 0;
}
} else if (isVertical && !isHorizontal) {
if (isAbsolute) {
newPoint.x = state.currPoint.x;
} else {
newPoint.x = 0;
}
}
if (!isAbsolute) {
newPoint.translate(state.currPoint.x, state.currPoint.y);
}
lines.push_back(std::make_unique<Line>(state.currPoint.x, state.currPoint.y, newPoint.x, newPoint.y));
state.currPoint = newPoint;
}
return lines;
}

Wyświetl plik

@ -1,16 +0,0 @@
#pragma once
#include <vector>
#include "../shape/Shape.h"
#include "SvgState.h"
class LineTo {
public:
static std::vector<std::unique_ptr<Shape>> absolute(SvgState& state, std::vector<float>& args);
static std::vector<std::unique_ptr<Shape>> relative(SvgState& state, std::vector<float>& args);
static std::vector<std::unique_ptr<Shape>> horizontalAbsolute(SvgState& state, std::vector<float>& args);
static std::vector<std::unique_ptr<Shape>> horizontalRelative(SvgState& state, std::vector<float>& args);
static std::vector<std::unique_ptr<Shape>> verticalAbsolute(SvgState& state, std::vector<float>& args);
static std::vector<std::unique_ptr<Shape>> verticalRelative(SvgState& state, std::vector<float>& args);
private:
static std::vector<std::unique_ptr<Shape>> parseLineTo(SvgState& state, std::vector<float>& args, bool isAbsolute, bool isHorizontal, bool isVertical);
};

Wyświetl plik

@ -1,36 +0,0 @@
#include "MoveTo.h"
#include "LineTo.h"
std::vector<std::unique_ptr<Shape>> MoveTo::absolute(SvgState& state, std::vector<float>& args) {
return parseMoveTo(state, args, true);
}
std::vector<std::unique_ptr<Shape>> MoveTo::relative(SvgState& state, std::vector<float>& args) {
return parseMoveTo(state, args, false);
}
std::vector<std::unique_ptr<Shape>> MoveTo::parseMoveTo(SvgState& state, std::vector<float>& args, bool isAbsolute) {
if (args.size() % 2 != 0 || args.size() < 2) {
return {};
}
Vector2 vec(args[0], args[1]);
if (isAbsolute) {
state.currPoint = vec;
state.initialPoint = state.currPoint;
if (args.size() > 2) {
std::vector<float> newArgs = std::vector<float>(args.begin() + 2, args.end());
return LineTo::absolute(state, newArgs);
}
} else {
state.currPoint.translate(vec.x, vec.y);
state.initialPoint = state.currPoint;
if (args.size() > 2) {
std::vector<float> newArgs = std::vector<float>(args.begin() + 2, args.end());
return LineTo::relative(state, newArgs);
}
}
return {};
}

Wyświetl plik

@ -1,12 +0,0 @@
#pragma once
#include <vector>
#include "../shape/Shape.h"
#include "SvgState.h"
class MoveTo {
public:
static std::vector<std::unique_ptr<Shape>> absolute(SvgState& state, std::vector<float>& args);
static std::vector<std::unique_ptr<Shape>> relative(SvgState& state, std::vector<float>& args);
private:
static std::vector<std::unique_ptr<Shape>> parseMoveTo(SvgState& state, std::vector<float>& args, bool isAbsolute);
};

Wyświetl plik

@ -1,46 +1,64 @@
#include "SvgParser.h"
#include <regex>
#include "MoveTo.h"
#include "LineTo.h"
#include "CurveTo.h"
#include "EllipticalArcTo.h"
#include "ClosePath.h"
#include "../shape/Line.h"
#include "../shape/QuadraticBezierCurve.h"
SvgParser::SvgParser(juce::String svgFile) {
pugi::xml_document doc;
pugi::xml_parse_result result = doc.load_string(svgFile.toStdString().c_str());
if (result) {
pugi::xml_node svg = doc.select_node("//svg").node();
pugi::xpath_node_set paths = svg.select_nodes("//path");
auto doc = juce::XmlDocument::parse(svgFile);
std::unique_ptr<juce::Drawable> svg = juce::Drawable::createFromSVG(*doc);
juce::DrawableComposite* composite = dynamic_cast<juce::DrawableComposite*>(svg.get());
auto contentArea = composite->getContentArea();
auto path = svg->getOutlineAsPath();
// apply transform to path to get the content area in the bounds -1 to 1
path.applyTransform(juce::AffineTransform::translation(-contentArea.getX(), -contentArea.getY()));
path.applyTransform(juce::AffineTransform::scale(2 / contentArea.getWidth(), 2 / contentArea.getHeight()));
path.applyTransform(juce::AffineTransform::translation(-1, -1));
shapes = std::vector<std::unique_ptr<Shape>>();
for (pugi::xpath_node xPath : paths) {
pugi::xml_node path = xPath.node();
for (pugi::xml_attribute attr : path.attributes()) {
if (std::strcmp(attr.name(), "d") == 0) {
auto path = parsePath(juce::String(attr.value()));
shapes.insert(shapes.end(), std::make_move_iterator(path.begin()), std::make_move_iterator(path.end()));
}
}
}
std::pair<double, double> dimensions = getDimensions(svg);
if (dimensions.first != -1.0 && dimensions.second != -1.0) {
double width = dimensions.first;
double height = dimensions.second;
Shape::normalize(shapes, width, height);
} else {
Shape::normalize(shapes);
}
}
pathToShapes(path, shapes);
Shape::removeOutOfBounds(shapes);
}
SvgParser::~SvgParser() {
SvgParser::~SvgParser() {}
void SvgParser::pathToShapes(juce::Path& path, std::vector<std::unique_ptr<Shape>>& shapes) {
juce::Path::Iterator pathIterator(path);
double x = 0;
double y = 0;
double startX = 0;
double startY = 0;
while (pathIterator.next()) {
auto type = pathIterator.elementType;
switch (type) {
case juce::Path::Iterator::PathElementType::startNewSubPath:
x = pathIterator.x1;
y = pathIterator.y1;
startX = x;
startY = y;
break;
case juce::Path::Iterator::PathElementType::lineTo:
shapes.push_back(std::make_unique<Line>(x, -y, pathIterator.x1, -pathIterator.y1));
x = pathIterator.x1;
y = pathIterator.y1;
break;
case juce::Path::Iterator::PathElementType::quadraticTo:
shapes.push_back(std::make_unique<QuadraticBezierCurve>(x, -y, pathIterator.x1, -pathIterator.y1, pathIterator.x2, -pathIterator.y2));
x = pathIterator.x2;
y = pathIterator.y2;
break;
case juce::Path::Iterator::PathElementType::cubicTo:
shapes.push_back(std::make_unique<CubicBezierCurve>(x, -y, pathIterator.x1, -pathIterator.y1, pathIterator.x2, -pathIterator.y2, pathIterator.x3, -pathIterator.y3));
x = pathIterator.x3;
y = pathIterator.y3;
break;
case juce::Path::Iterator::PathElementType::closePath:
shapes.push_back(std::make_unique<Line>(x, -y, startX, -startY));
x = startX;
y = startY;
break;
}
}
}
std::vector<std::unique_ptr<Shape>> SvgParser::draw() {
@ -51,185 +69,4 @@ std::vector<std::unique_ptr<Shape>> SvgParser::draw() {
tempShapes.push_back(shape->clone());
}
return tempShapes;
}
std::vector<std::string> SvgParser::preProcessPath(std::string path) {
std::regex re1(",");
std::regex re2("-(?!e)");
std::regex re3("\\s+");
std::regex re4("(^\\s|\\s$)");
path = std::regex_replace(path, re1, " ");
// reverse path and use a negative lookahead
std::reverse(path.begin(), path.end());
path = std::regex_replace(path, re2, "- ");
std::reverse(path.begin(), path.end());
path = std::regex_replace(path, re3, " ");
path = std::regex_replace(path, re4, "");
std::regex commands("(?=[mlhvcsqtazMLHVCSQTAZ])");
std::vector<std::string> commandsVector;
std::sregex_token_iterator iter(path.begin(), path.end(), commands, -1);
for (; iter != std::sregex_token_iterator(); ++iter) {
commandsVector.push_back(iter->str());
}
return commandsVector;
}
juce::String SvgParser::simplifyLength(juce::String length) {
return length.replace("em|ex|px|in|cm|mm|pt|pc", "");
}
std::pair<double, double> SvgParser::getDimensions(pugi::xml_node& svg) {
double width = -1.0;
double height = -1.0;
if (!svg.attribute("viewBox").empty()) {
juce::String viewBox = juce::String(svg.attribute("viewBox").as_string());
juce::StringArray viewBoxValues = juce::StringArray::fromTokens(viewBox, " ", "");
if (viewBoxValues.size() == 4) {
width = viewBoxValues[2].getDoubleValue();
height = viewBoxValues[3].getDoubleValue();
}
}
std::regex re("em|ex|px|in|cm|mm|pt|pc");
if (!svg.attribute("width").empty()) {
std::string widthString = svg.attribute("width").as_string();
widthString = std::regex_replace(widthString, re, "");
width = juce::String(widthString).getDoubleValue();
}
if (!svg.attribute("height").empty()) {
std::string heightString = svg.attribute("height").as_string();
heightString = std::regex_replace(heightString, re, "");
height = juce::String(heightString).getDoubleValue();
}
return std::make_pair<>(width, height);
}
std::vector<std::unique_ptr<Shape>> SvgParser::parsePath(juce::String path) {
if (path.isEmpty()) {
return std::vector<std::unique_ptr<Shape>>();
}
state.currPoint = Vector2();
state.prevCubicControlPoint = std::nullopt;
state.prevQuadraticControlPoint = std::nullopt;
std::vector<std::string> commands = preProcessPath(path.toStdString());
std::vector<std::unique_ptr<Shape>> pathShapes;
for (auto& stdCommand : commands) {
if (!stdCommand.empty()) {
juce::String command(stdCommand);
char commandChar = command[0];
std::vector<float> nums;
if (commandChar != 'z' && commandChar != 'Z') {
juce::StringArray tokens = juce::StringArray::fromTokens(command.substring(1), " ", "");
for (juce::String token : tokens) {
token = token.trim();
if (token.isNotEmpty()) {
juce::StringArray decimalSplit = juce::StringArray::fromTokens(token, ".", "");
if (decimalSplit.size() == 1) {
auto num = decimalSplit[0].getFloatValue();
nums.push_back(num);
} else {
juce::String decimal = decimalSplit[0] + "." + decimalSplit[1];
nums.push_back(decimal.getFloatValue());
for (int i = 2; i < decimalSplit.size(); i++) {
decimal = "." + decimalSplit[i];
nums.push_back(decimal.getFloatValue());
}
}
}
}
}
std::vector<std::unique_ptr<Shape>> commandShapes;
switch (commandChar) {
case 'M':
commandShapes = MoveTo::absolute(state, nums);
break;
case 'm':
commandShapes = MoveTo::relative(state, nums);
break;
case 'L':
commandShapes = LineTo::absolute(state, nums);
break;
case 'l':
commandShapes = LineTo::relative(state, nums);
break;
case 'H':
commandShapes = LineTo::horizontalAbsolute(state, nums);
break;
case 'h':
commandShapes = LineTo::horizontalRelative(state, nums);
break;
case 'V':
commandShapes = LineTo::verticalAbsolute(state, nums);
break;
case 'v':
commandShapes = LineTo::verticalRelative(state, nums);
break;
case 'C':
commandShapes = CurveTo::absolute(state, nums);
break;
case 'c':
commandShapes = CurveTo::relative(state, nums);
break;
case 'S':
commandShapes = CurveTo::smoothAbsolute(state, nums);
break;
case 's':
commandShapes = CurveTo::smoothRelative(state, nums);
break;
case 'Q':
commandShapes = CurveTo::quarticAbsolute(state, nums);
break;
case 'q':
commandShapes = CurveTo::quarticRelative(state, nums);
break;
case 'T':
commandShapes = CurveTo::quarticSmoothAbsolute(state, nums);
break;
case 't':
commandShapes = CurveTo::quarticSmoothRelative(state, nums);
break;
case 'A':
commandShapes = EllipticalArcTo::absolute(state, nums);
break;
case 'a':
commandShapes = EllipticalArcTo::relative(state, nums);
break;
case 'Z':
commandShapes = ClosePath::absolute(state, nums);
break;
case 'z':
commandShapes = ClosePath::relative(state, nums);
break;
}
pathShapes.insert(pathShapes.end(), std::make_move_iterator(commandShapes.begin()), std::make_move_iterator(commandShapes.end()));
if (commandChar != 'c' && commandChar != 'C' && commandChar != 's' && commandChar != 'S') {
state.prevCubicControlPoint = std::nullopt;
}
if (commandChar != 'q' && commandChar != 'Q' && commandChar != 't' && commandChar != 'T') {
state.prevQuadraticControlPoint = std::nullopt;
}
}
}
return pathShapes;
}
}

Wyświetl plik

@ -2,21 +2,16 @@
#include "../shape/Vector2.h"
#include <JuceHeader.h>
#include "../shape/Shape.h"
#include "SvgState.h"
#include "../xml/pugixml.hpp"
class SvgParser {
public:
SvgParser(juce::String svgFile);
~SvgParser();
static void pathToShapes(juce::Path& path, std::vector<std::unique_ptr<Shape>>& shapes);
std::vector<std::unique_ptr<Shape>> draw();
private:
std::vector<std::string> preProcessPath(std::string path);
juce::String simplifyLength(juce::String length);
std::pair<double, double> getDimensions(pugi::xml_node&);
std::vector<std::unique_ptr<Shape>> parsePath(juce::String);
std::vector<std::unique_ptr<Shape>> shapes;
SvgState state = SvgState();
};

Wyświetl plik

@ -1 +0,0 @@
#include "SvgState.h"

Wyświetl plik

@ -1,10 +0,0 @@
#pragma once
#include "../shape/Vector2.h"
#include <optional>
struct SvgState {
Vector2 currPoint;
Vector2 initialPoint;
std::optional<Vector2> prevCubicControlPoint;
std::optional<Vector2> prevQuadraticControlPoint;
};

Wyświetl plik

@ -1,6 +1,5 @@
#include "TextParser.h"
#include "../shape/Line.h"
#include "../shape/QuadraticBezierCurve.h"
#include "../svg/SvgParser.h"
TextParser::TextParser(juce::String text, juce::Font font) {
@ -9,44 +8,7 @@ TextParser::TextParser(juce::String text, juce::Font font) {
glyphs.addFittedText(font, text, -2, -2, 4, 4, juce::Justification::centred, 2);
glyphs.createPath(textPath);
juce::Path::Iterator pathIterator(textPath);
double x = 0;
double y = 0;
double startX = 0;
double startY = 0;
while (pathIterator.next()) {
auto type = pathIterator.elementType;
switch (type) {
case juce::Path::Iterator::PathElementType::startNewSubPath:
x = pathIterator.x1;
y = pathIterator.y1;
startX = x;
startY = y;
break;
case juce::Path::Iterator::PathElementType::lineTo:
shapes.push_back(std::make_unique<Line>(x, -y, pathIterator.x1, -pathIterator.y1));
x = pathIterator.x1;
y = pathIterator.y1;
break;
case juce::Path::Iterator::PathElementType::quadraticTo:
shapes.push_back(std::make_unique<QuadraticBezierCurve>(x, -y, pathIterator.x1, -pathIterator.y1, pathIterator.x2, -pathIterator.y2));
x = pathIterator.x2;
y = pathIterator.y2;
break;
case juce::Path::Iterator::PathElementType::cubicTo:
shapes.push_back(std::make_unique<CubicBezierCurve>(x, -y, pathIterator.x1, -pathIterator.y1, pathIterator.x2, -pathIterator.y2, pathIterator.x3, -pathIterator.y3));
x = pathIterator.x3;
y = pathIterator.y3;
break;
case juce::Path::Iterator::PathElementType::closePath:
shapes.push_back(std::make_unique<Line>(x, -y, startX, -startY));
x = startX;
y = startY;
break;
}
}
SvgParser::pathToShapes(textPath, shapes);
}
TextParser::~TextParser() {

Wyświetl plik

@ -1,77 +0,0 @@
/**
* pugixml parser - version 1.13
* --------------------------------------------------------
* Copyright (C) 2006-2022, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
* Report bugs and download new versions at https://pugixml.org/
*
* This library is distributed under the MIT License. See notice at the end
* of this file.
*
* This work is based on the pugxml parser, which is:
* Copyright (C) 2003, by Kristen Wegner (kristen@tima.net)
*/
#ifndef HEADER_PUGICONFIG_HPP
#define HEADER_PUGICONFIG_HPP
// Uncomment this to enable wchar_t mode
// #define PUGIXML_WCHAR_MODE
// Uncomment this to enable compact mode
// #define PUGIXML_COMPACT
// Uncomment this to disable XPath
// #define PUGIXML_NO_XPATH
// Uncomment this to disable STL
// #define PUGIXML_NO_STL
// Uncomment this to disable exceptions
// #define PUGIXML_NO_EXCEPTIONS
// Set this to control attributes for public classes/functions, i.e.:
// #define PUGIXML_API __declspec(dllexport) // to export all public symbols from DLL
// #define PUGIXML_CLASS __declspec(dllimport) // to import all classes from DLL
// #define PUGIXML_FUNCTION __fastcall // to set calling conventions to all public functions to fastcall
// In absence of PUGIXML_CLASS/PUGIXML_FUNCTION definitions PUGIXML_API is used instead
// Tune these constants to adjust memory-related behavior
// #define PUGIXML_MEMORY_PAGE_SIZE 32768
// #define PUGIXML_MEMORY_OUTPUT_STACK 10240
// #define PUGIXML_MEMORY_XPATH_PAGE_SIZE 4096
// Tune this constant to adjust max nesting for XPath queries
// #define PUGIXML_XPATH_DEPTH_LIMIT 1024
// Uncomment this to switch to header-only version
// #define PUGIXML_HEADER_ONLY
// Uncomment this to enable long long support
// #define PUGIXML_HAS_LONG_LONG
#endif
/**
* Copyright (c) 2006-2022 Arseny Kapoulkine
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/

Plik diff jest za duży Load Diff

Plik diff jest za duży Load Diff

Wyświetl plik

@ -428,32 +428,13 @@
<FILE id="NmptSY" name="Shape.h" compile="0" resource="0" file="Source/shape/Shape.h"/>
</GROUP>
<GROUP id="{56A27063-1FE7-31C3-8263-98389240A8CB}" name="svg">
<FILE id="FVWkba" name="ClosePath.cpp" compile="1" resource="0" file="Source/svg/ClosePath.cpp"/>
<FILE id="pJz3ek" name="ClosePath.h" compile="0" resource="0" file="Source/svg/ClosePath.h"/>
<FILE id="PS8Qfk" name="CurveTo.cpp" compile="1" resource="0" file="Source/svg/CurveTo.cpp"/>
<FILE id="Sjh7ne" name="CurveTo.h" compile="0" resource="0" file="Source/svg/CurveTo.h"/>
<FILE id="DQJJdr" name="EllipticalArcTo.cpp" compile="1" resource="0"
file="Source/svg/EllipticalArcTo.cpp"/>
<FILE id="iVMpEw" name="EllipticalArcTo.h" compile="0" resource="0"
file="Source/svg/EllipticalArcTo.h"/>
<FILE id="DSmqNL" name="LineTo.cpp" compile="1" resource="0" file="Source/svg/LineTo.cpp"/>
<FILE id="fjo7Oy" name="LineTo.h" compile="0" resource="0" file="Source/svg/LineTo.h"/>
<FILE id="c2y2ap" name="MoveTo.cpp" compile="1" resource="0" file="Source/svg/MoveTo.cpp"/>
<FILE id="SfIJEj" name="MoveTo.h" compile="0" resource="0" file="Source/svg/MoveTo.h"/>
<FILE id="cTec1H" name="SvgParser.cpp" compile="1" resource="0" file="Source/svg/SvgParser.cpp"/>
<FILE id="gvkrDH" name="SvgParser.h" compile="0" resource="0" file="Source/svg/SvgParser.h"/>
<FILE id="OvloZU" name="SvgState.cpp" compile="1" resource="0" file="Source/svg/SvgState.cpp"/>
<FILE id="GCBO9x" name="SvgState.h" compile="0" resource="0" file="Source/svg/SvgState.h"/>
</GROUP>
<GROUP id="{E81B1D7B-B0F7-1967-B271-71B3F838720F}" name="txt">
<FILE id="vIYWRG" name="TextParser.cpp" compile="1" resource="0" file="Source/txt/TextParser.cpp"/>
<FILE id="LlefOK" name="TextParser.h" compile="0" resource="0" file="Source/txt/TextParser.h"/>
</GROUP>
<GROUP id="{022CB910-9A16-C4AE-4C3B-9CB57BE87FC2}" name="xml">
<FILE id="pW7WRh" name="pugiconfig.hpp" compile="0" resource="0" file="Source/xml/pugiconfig.hpp"/>
<FILE id="CnkgyF" name="pugixml.cpp" compile="1" resource="0" file="Source/xml/pugixml.cpp"/>
<FILE id="SrvH3B" name="pugixml.hpp" compile="0" resource="0" file="Source/xml/pugixml.hpp"/>
</GROUP>
</GROUP>
</MAINGROUP>
<JUCEOPTIONS JUCE_STRICT_REFCOUNTEDPOINTER="1" JUCE_VST3_CAN_REPLACE_VST2="0"/>