Move xml and svg to lower levels.

This commit is contained in:
jmsgrogan 2023-01-18 10:55:32 +00:00
parent 942cc2539c
commit 7cab70f839
32 changed files with 35 additions and 33 deletions

View file

@ -23,6 +23,7 @@ list(APPEND graphics_HEADERS
AbstractPainter.h
DrawingSurface.h
)
if(UNIX)
set(OpenGL_GL_PREFERENCE "GLVND")
find_package(OpenGL QUIET)

View file

@ -16,6 +16,20 @@ list(APPEND visual_elements_LIB_INCLUDES
scene/SceneItem.cpp
scene/SceneText.h
scene/SceneText.cpp
svg/SvgNode.h
svg/SvgNode.cpp
svg/SvgDocument.h
svg/SvgWriter.h
svg/SvgReader.h
svg/SvgShapeElement.h
svg/SvgElement.h
svg/elements/SvgShapeElements.h
svg/SvgDocument.cpp
svg/SvgReader.cpp
svg/SvgWriter.cpp
svg/SvgShapeElement.cpp
svg/SvgElement.cpp
svg/elements/SvgShapeElements.cpp
nodes/MaterialNode.h
nodes/MaterialNode.cpp
nodes/MeshNode.h
@ -38,6 +52,8 @@ target_include_directories(${MODULE_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/basic_shapes
${CMAKE_CURRENT_SOURCE_DIR}/scene
${CMAKE_CURRENT_SOURCE_DIR}/nodes
${CMAKE_CURRENT_SOURCE_DIR}/svg
${CMAKE_CURRENT_SOURCE_DIR}/svg/elements
)
target_link_libraries(${MODULE_NAME} PUBLIC core geometry fonts mesh image)

View file

@ -0,0 +1,58 @@
#include "SvgDocument.h"
#include "XmlAttribute.h"
#include "StringUtils.h"
#include <sstream>
SvgDocument::SvgDocument()
: XmlDocument()
{
auto root = XmlElement::Create("svg");
auto xmlns = std::make_unique<XmlAttribute>("xmlns");
xmlns->setValue("http://www.w3.org/2000/svg");
root->addAttribute(std::move(xmlns));
setRoot(std::move(root));
}
void SvgDocument::setViewBox(double x, double y, double w, double h)
{
std::stringstream sstr;
sstr << x << " " << y << " " << w << " " << h;
setViewBox(sstr.str());
}
void SvgDocument::setViewBox(const std::string& data)
{
auto viewbox = std::make_unique<XmlAttribute>("viewBox");
viewbox->setValue(data);
getRoot()->addAttribute(std::move(viewbox));
}
SvgDocument::ViewBox SvgDocument::getViewBox() const
{
ViewBox viewBox;
if (!getRoot())
{
return viewBox;
}
if (auto vb_attr = getRoot()->getAttribute("viewBox"); vb_attr)
{
auto entries = StringUtils::split(vb_attr->getValue());
if (entries.size() != 4)
{
return viewBox;
}
viewBox.mX = std::stod(entries[0]);
viewBox.mY = std::stod(entries[1]);
viewBox.mW = std::stod(entries[2]);
viewBox.mH = std::stod(entries[3]);
}
return viewBox;
}

View file

@ -0,0 +1,22 @@
#pragma once
#include "XmlDocument.h"
class SvgDocument : public XmlDocument
{
public:
struct ViewBox
{
double mX{ 0 };
double mY{ 0 };
double mW{ 0 };
double mH{ 0 };
};
SvgDocument();
ViewBox getViewBox() const;
void setViewBox(const std::string& data);
void setViewBox(double x, double y, double w, double h);
};

View file

@ -0,0 +1,7 @@
#include "SvgElement.h"
SvgElement::SvgElement(const std::string& tagName)
: XmlElement(tagName)
{
}

View file

@ -0,0 +1,9 @@
#pragma once
#include "XmlElement.h"
class SvgElement : public XmlElement
{
public:
SvgElement(const std::string& tagName);
};

View file

@ -0,0 +1,90 @@
#include "SvgNode.h"
#include "CircleNode.h"
#include "SvgShapeElements.h"
SvgNode::SvgNode(const Point& location)
: AbstractVisualNode(location)
{
}
void SvgNode::setContent(std::unique_ptr<SvgDocument> doc)
{
mContent = std::move(doc);
mContentDirty = true;
mChildren.clear();
mManagedChildren.clear();
}
void SvgNode::updateTransform()
{
}
void SvgNode::createOrUpdateGeometry(SceneInfo* sceneInfo)
{
if (!mContent->getRoot())
{
return;
}
auto viewbox = mContent->getViewBox();
for (const auto& svg_element : mContent->getRoot()->getChildren())
{
std::unique_ptr<AbstractVisualNode> node;
if (svg_element->getTagName() == "circle")
{
auto svg_circle = dynamic_cast<SvgCircle*>(svg_element.get());
auto loc = svg_circle->getLocation();
auto radius = svg_circle->getRadius();
auto minor_radius = radius;
if (svg_circle->getType() == SvgCircle::Type::ELLIPSE)
{
minor_radius = svg_circle->getMinorRadius();
}
if (svg_element->hasAttribute("transform"))
{
const auto transform = svg_circle->getTransform();
loc.move(transform.getLocation().getX(), transform.getLocation().getY());
radius *= transform.getScaleX();
minor_radius *= transform.getScaleY();
}
auto circle_node = std::make_unique<CircleNode>(loc, radius);
circle_node->setMinorRadius(minor_radius);
node = std::move(circle_node);
}
if (!node)
{
continue;
}
auto raw_node = node.get();
mManagedChildren.push_back(std::move(node));
addChild(raw_node);
}
}
void SvgNode::update(SceneInfo* sceneInfo)
{
if (mContent && mContentDirty)
{
createOrUpdateGeometry(sceneInfo);
mContentDirty = false;
}
if (mTransformIsDirty)
{
updateTransform();
mTransformIsDirty = false;
}
}

View file

@ -0,0 +1,25 @@
#pragma once
#include "AbstractVisualNode.h"
#include "SvgDocument.h"
#include <memory>
#include <vector>
class SvgNode : public AbstractVisualNode
{
public:
SvgNode(const Point& location);
void setContent(std::unique_ptr<SvgDocument> doc);
void update(SceneInfo* sceneInfo);
private:
void createOrUpdateGeometry(SceneInfo* sceneInfo);
void updateTransform();
bool mContentDirty{ true };
std::vector<std::unique_ptr<AbstractVisualNode> > mManagedChildren;
std::unique_ptr<SvgDocument> mContent;
};

View file

@ -0,0 +1,76 @@
#include "SvgReader.h"
#include "File.h"
#include "XmlParser.h"
#include "XmlAttribute.h"
#include "SvgShapeElements.h"
#include "FileLogger.h"
std::unique_ptr<SvgDocument> SvgReader::read(const Path& path) const
{
auto svg_doc = std::make_unique<SvgDocument>();
File in_file(path);
auto lines = in_file.readLines();
XmlParser parser;
for (const auto& line : lines)
{
parser.processLine(line);
}
auto xml_doc = parser.getDocument();
if (xml_doc->getRoot() && xml_doc->getRoot()->getTagName() == "svg")
{
onRoot(xml_doc->getRoot(), svg_doc.get());
}
return svg_doc;
}
void SvgReader::onRoot(XmlElement* element, SvgDocument* doc) const
{
for (const auto& attr : element->getAttributes())
{
doc->getRoot()->addAttribute(attr.first, attr.second->getValue());
}
for (const auto& child : element->getChildren())
{
onChild(child.get(), doc->getRoot());
}
}
void SvgReader::onChild(XmlElement* element, XmlElement* svg_parent) const
{
std::unique_ptr<XmlElement> new_svg;
if (element->getTagName() == "circle")
{
new_svg = std::make_unique<SvgCircle>();
}
else if (element->getTagName() == "ellipse")
{
new_svg = std::make_unique<SvgCircle>(SvgCircle::Type::ELLIPSE);
}
else if (element->getTagName() == "rect")
{
new_svg = std::make_unique<SvgRectangle>();
}
else
{
return;
}
for (const auto& attr : element->getAttributes())
{
new_svg->addAttribute(attr.first, attr.second->getValue());
}
for (const auto& child : element->getChildren())
{
onChild(child.get(), new_svg.get());
}
svg_parent->addChild(std::move(new_svg));
}

View file

@ -0,0 +1,20 @@
#pragma once
#include "SvgDocument.h"
#include <filesystem>
using Path = std::filesystem::path;
class SvgElement;
class SvgReader
{
public:
std::unique_ptr<SvgDocument> read(const Path& path) const;
private:
void onRoot(XmlElement* element, SvgDocument* doc) const;
void onChild(XmlElement* element, XmlElement* svg_parent) const;
};

View file

@ -0,0 +1,146 @@
#include "SvgShapeElement.h"
#include "XmlAttribute.h"
#include "StringUtils.h"
#include <sstream>
SvgShapeElement::SvgShapeElement(const std::string& tagName)
: SvgElement(tagName)
{
}
Transform SvgShapeElement::getTransform() const
{
Transform transform;
const std::string translate_key = "translate";
const std::string scale_key = "scale";
if (auto attr = getAttribute("transform"); attr)
{
const auto val = attr->getValue();
std::string working_string;
bool in_translate = false;
bool in_scale = false;
for (auto c : val)
{
if (StringUtils::stripSurroundingWhitepsace(working_string) == translate_key)
{
in_translate = true;
working_string.clear();
continue;
}
else if (StringUtils::stripSurroundingWhitepsace(working_string) == scale_key)
{
in_scale = true;
working_string.clear();
continue;
}
else if (c == '(')
{
continue;
}
else if (c == ')')
{
if (in_translate)
{
const auto loc = parsePoint(working_string, 0.0);
transform.setLocation(loc);
in_translate = false;
}
else if (in_scale)
{
const auto loc = parsePoint(working_string, 1.0);
transform.setScale(loc.getX(), loc.getY(), loc.getZ());
in_scale = false;
}
working_string.clear();
}
else
{
working_string += c;
}
}
}
return transform;
}
std::string SvgShapeElement::getLabelledContent(const std::string& key, const std::string& content) const
{
return content.substr(key.size(), content.size() - key.size() - 2);
}
Point SvgShapeElement::parsePoint(const std::string& pointString, double defaultVal) const
{
double x = defaultVal;
double y = defaultVal;
double z = defaultVal;
const auto split = StringUtils::split(pointString);
for (std::size_t idx = 0; idx < split.size(); idx++)
{
if (idx == 0)
{
x = std::stod(split[idx]);
}
else if (idx == 1)
{
y = std::stod(split[idx]);
}
else if (idx == 2)
{
z = std::stod(split[idx]);
}
}
return { x, y, z };
}
bool SvgShapeElement::hasTransform() const
{
return bool(getAttribute("transform"));
}
void SvgShapeElement::setNoFill()
{
auto attr = std::make_unique<XmlAttribute>("fill");
attr->setValue("none");
addAttribute(std::move(attr));
}
void SvgShapeElement::setNoStroke()
{
auto attr = std::make_unique<XmlAttribute>("stroke");
attr->setValue("none");
addAttribute(std::move(attr));
}
void SvgShapeElement::setFill(const Color& fill)
{
auto attr = std::make_unique<XmlAttribute>("fill");
std::stringstream sstr;
sstr << "rgb(" << fill.toString() << ")";
attr->setValue(sstr.str());
addAttribute(std::move(attr));
}
void SvgShapeElement::setStrokeWidth(double width)
{
auto attr = std::make_unique<XmlAttribute>("stroke-width");
attr->setValue(std::to_string(width));
addAttribute(std::move(attr));
}
void SvgShapeElement::setStrokeColor(const Color& stroke)
{
auto attr = std::make_unique<XmlAttribute>("stroke");
std::stringstream sstr;
sstr << "rgb(" << stroke.toString() << ")";
attr->setValue(sstr.str());
addAttribute(std::move(attr));
}

View file

@ -0,0 +1,29 @@
#pragma once
#include "SvgElement.h"
#include "Color.h"
#include "Transform.h"
class SvgShapeElement : public SvgElement
{
public:
SvgShapeElement(const std::string& tagName);
Transform getTransform() const;
bool hasTransform() const;
void setFill(const Color& fill);
void setNoFill();
void setStrokeWidth(double width);
void setStrokeColor(const Color& stroke);
void setNoStroke();
private:
std::string getLabelledContent(const std::string& key, const std::string& content) const;
Point parsePoint(const std::string& pointString, double defaultVal = 0.0) const;
};

View file

@ -0,0 +1,20 @@
#include "SvgWriter.h"
#include "SvgDocument.h"
#include "File.h"
std::string SvgWriter::toString(SvgDocument* document) const
{
std::string content = "";
if (auto root = document->getRoot())
{
content += root->toString();
}
return content;
}
void SvgWriter::toFile(const Path& path, SvgDocument* document) const
{
File out_file(path);
out_file.writeText(toString(document));
}

View file

@ -0,0 +1,16 @@
#pragma once
#include <string>
#include <filesystem>
using Path = std::filesystem::path;
class SvgDocument;
class SvgWriter
{
public:
std::string toString(SvgDocument* document) const;
void toFile(const Path& path, SvgDocument* document) const;
};

View file

@ -0,0 +1,187 @@
#include "SvgShapeElements.h"
#include <sstream>
SvgCircle::SvgCircle(Type type)
: SvgShapeElement(type == Type::REGULAR ? "circle" : "ellipse"),
mType(type)
{
}
Point SvgCircle::getLocation() const
{
double cx = 0.0;
double cy = 0.0;
if (auto attr = getAttribute("cx"); attr)
{
cx = std::stod(attr->getValue());
}
if (auto attr = getAttribute("cy"); attr)
{
cy = std::stod(attr->getValue());
}
return { cx, cy };
}
double SvgCircle::getRadius() const
{
double radius = 1.0;
if (auto attr = getAttribute("rx"); attr)
{
radius = std::stod(attr->getValue());
}
else if (auto attr = getAttribute("r"); attr)
{
radius = std::stod(attr->getValue());
}
return radius;
}
SvgCircle::Type SvgCircle::getType() const
{
return mType;
}
double SvgCircle::getMinorRadius() const
{
double radius = 0.0;
if (auto attr = getAttribute("ry"); attr)
{
radius = std::stod(attr->getValue());
}
return radius;
}
void SvgCircle::setLocation(const Point& loc)
{
auto cx = std::make_unique<XmlAttribute>("cx");
auto cy = std::make_unique<XmlAttribute>("cy");
cx->setValue(std::to_string(loc.getX()));
cy->setValue(std::to_string(loc.getY()));
addAttribute(std::move(cx));
addAttribute(std::move(cy));
}
void SvgCircle::setRadius(double rad)
{
if (mType == Type::REGULAR)
{
auto r = std::make_unique<XmlAttribute>("r");
r->setValue(std::to_string(rad));
addAttribute(std::move(r));
}
else
{
auto r = std::make_unique<XmlAttribute>("rx");
r->setValue(std::to_string(rad));
addAttribute(std::move(r));
}
}
void SvgCircle::setMinorRadius(double rad)
{
auto r = std::make_unique<XmlAttribute>("ry");
r->setValue(std::to_string(rad));
addAttribute(std::move(r));
}
SvgRectangle::SvgRectangle()
: SvgShapeElement("rect")
{
}
void SvgRectangle::setLocation(const Point& loc)
{
auto x = std::make_unique<XmlAttribute>("x");
auto y = std::make_unique<XmlAttribute>("y");
x->setValue(std::to_string(loc.getX()));
y->setValue(std::to_string(loc.getY()));
addAttribute(std::move(x));
addAttribute(std::move(y));
}
void SvgRectangle::setWidth(double w)
{
auto width = std::make_unique<XmlAttribute>("width");
width->setValue(std::to_string(w));
addAttribute(std::move(width));
}
void SvgRectangle::setHeight(double h)
{
auto height = std::make_unique<XmlAttribute>("height");
height->setValue(std::to_string(h));
addAttribute(std::move(height));
}
SvgPolygon::SvgPolygon()
: SvgShapeElement("polygon")
{
}
void SvgPolygon::setPoints(const std::vector<Point>& locs)
{
auto points = std::make_unique<XmlAttribute>("points");
std::stringstream sstr;
for (const auto& loc : locs)
{
sstr << loc.getX() << "," << loc.getY() << " ";
}
points->setValue(sstr.str());
addAttribute(std::move(points));
}
SvgPolyline::SvgPolyline()
: SvgShapeElement("polyline")
{
auto fill = std::make_unique<XmlAttribute>("fill");
fill->setValue("none");
addAttribute(std::move(fill));
}
void SvgPolyline::setPoints(const std::vector<Point>& locs)
{
auto points = std::make_unique<XmlAttribute>("points");
std::stringstream sstr;
for (const auto& loc : locs)
{
sstr << loc.getX() << "," << loc.getY() << " ";
}
points->setValue(sstr.str());
addAttribute(std::move(points));
}
SvgPath::SvgPath()
: SvgShapeElement("path")
{
}
void SvgPath::setPath(const std::string& mPath)
{
auto path = std::make_unique<XmlAttribute>("d");
path->setValue(mPath);
addAttribute(std::move(path));
}
void SvgPath::setFillRule(const std::string& fillRule)
{
auto rule = std::make_unique<XmlAttribute>("fill-rule");
rule->setValue(fillRule);
addAttribute(std::move(rule));
}

View file

@ -0,0 +1,71 @@
#pragma once
#include "SvgShapeElement.h"
#include "Point.h"
#include "XmlAttribute.h"
class SvgCircle : public SvgShapeElement
{
public:
enum class Type
{
REGULAR,
ELLIPSE
};
SvgCircle(Type type = Type::REGULAR);
Point getLocation() const;
double getRadius() const;
Type getType() const;
double getMinorRadius() const;
void setLocation(const Point& loc);
void setRadius(double rad);
void setMinorRadius(double rad);
private:
Type mType{ Type::REGULAR };
};
class SvgRectangle : public SvgShapeElement
{
public:
SvgRectangle();
void setLocation(const Point& loc);
void setWidth(double width);
void setHeight(double height);
};
class SvgPolygon : public SvgShapeElement
{
public:
SvgPolygon();
void setPoints(const std::vector<Point>& loc);
};
class SvgPolyline : public SvgShapeElement
{
public:
SvgPolyline();
void setPoints(const std::vector<Point>& loc);
};
class SvgPath : public SvgShapeElement
{
public:
SvgPath();
void setPath(const std::string& mPath);
void setFillRule(const std::string& fillRule);
};