Improve node to svg conversion.

This commit is contained in:
jmsgrogan 2023-01-12 17:45:06 +00:00
parent 64f0b3e77a
commit 26ecae46b3
22 changed files with 403 additions and 126 deletions

View file

@ -1,6 +1,7 @@
#include "BlochSphereNode.h" #include "BlochSphereNode.h"
#include "CircleNode.h" #include "CircleNode.h"
#include "LineNode.h"
BlochSphereNode::BlochSphereNode(const Point& location) BlochSphereNode::BlochSphereNode(const Point& location)
: AbstractVisualNode(location, "BlochSphereNode") : AbstractVisualNode(location, "BlochSphereNode")
@ -32,9 +33,17 @@ void BlochSphereNode::update(SceneInfo* sceneInfo)
mChildren.clear(); mChildren.clear();
auto loc = DiscretePoint(mSize / 2.0, mSize / 2.0); auto loc = Point(mSize / 2.0, mSize / 2.0);
mInnerCircle = std::make_unique<CircleNode>(loc, mSize / 2.0);
mInnerCircle->setMinorRadius(mSize / 4.0);
mOuterCircle = std::make_unique<CircleNode>(loc, mSize/2.0); mOuterCircle = std::make_unique<CircleNode>(loc, mSize/2.0);
addChild(mOuterCircle.get()); mCentreCircle = std::make_unique<CircleNode>(loc, mSize / 50.0);
mCentreCircle->setFillColor(Color(0, 0, 0));
mCentreCircle->setHasStrokeColor(false);
addChild(mInnerCircle.get());
addChild(mOuterCircle.get());
addChild(mCentreCircle.get());
} }

View file

@ -1,36 +1,23 @@
#include "TestFramework.h" #include "TestFramework.h"
#include "TestUtils.h" #include "TestUtils.h"
#include "TestRenderUtils.h"
#include "BlochSphereNode.h" #include "BlochSphereNode.h"
#include "BlochSphere.h" #include "BlochSphere.h"
#include "Scene.h"
#include "SvgConverter.h"
#include "SvgWriter.h"
#include "SvgDocument.h"
#include "File.h"
TEST_CASE(TestBlochSphereNode, "quantum_computing") TEST_CASE(TestBlochSphereNode, "quantum_computing")
{ {
TestRenderer renderer(100, 100);
auto node = std::make_unique<BlochSphereNode>(Point(0.5, 0.5)); auto node = std::make_unique<BlochSphereNode>(Point(0.5, 0.5));
Qubit state({ 1.0, 0.0 }, { 0.0, 0.0 }); Qubit state({ 1.0, 0.0 }, { 0.0, 0.0 });
auto bloch_sphere = std::make_unique<BlochSphere>(state); auto bloch_sphere = std::make_unique<BlochSphere>(state);
node->setSize(100);
node->setContent(bloch_sphere.get()); node->setContent(bloch_sphere.get());
auto scene = std::make_unique<Scene>(); renderer.getScene()->addNode(node.get());
scene->addNode(node.get()); renderer.writeSvg(TestUtils::getTestOutputDir(__FILE__) / "bloch_sphere.svg");
scene->update();
SvgConverter converter;
auto svg_document = converter.convert(scene.get());
SvgWriter writer;
auto svg_content = writer.toString(svg_document.get());
File svg_file(TestUtils::getTestOutputDir(__FILE__) / "bloch_sphere.svg");
svg_file.writeText(svg_content);
} }

View file

@ -17,6 +17,7 @@ public:
TRIANGLE, TRIANGLE,
POINT, POINT,
PATH, PATH,
CIRCLE,
UNKNOWN UNKNOWN
}; };

View file

@ -0,0 +1,43 @@
#include "Circle.h"
Circle::Circle(const Point& centre, double radius)
: mCentre(centre),
mRadius(radius)
{
mMinorRadius = mRadius;
}
const Point& Circle::getLocation() const
{
return mCentre;
}
double Circle::getRadius() const
{
return mRadius;
}
double Circle::getMinorRadius() const
{
return mMinorRadius;
}
void Circle::setMinorRadius(double radius)
{
mMinorRadius = radius;
}
void Circle::sample(Grid<unsigned char>* grid) const
{
}
Circle::Bounds Circle::getSize() const
{
return Bounds{ 2.0 * mRadius, 2.0 * mMinorRadius };
}
Circle::Type Circle::getType() const
{
return Type::CIRCLE;
}

View file

@ -0,0 +1,29 @@
#pragma once
#include "AbstractGeometricItem.h"
#include "Point.h"
class Circle : public AbstractGeometricItem
{
public:
Circle(const Point& centre, double radius = 0.5);
const Point& getLocation() const override;
double getRadius() const;
double getMinorRadius() const;
void setMinorRadius(double radius);
void sample(Grid<unsigned char>* grid) const override;
Bounds getSize() const override;
Type getType() const override;
private:
double mMinorRadius{ 0.5 };
double mRadius{ 0.5 };
Point mCentre;
};

View file

@ -5,16 +5,22 @@
#include "SceneItem.h" #include "SceneItem.h"
#include "SceneModel.h" #include "SceneModel.h"
#include "SceneText.h"
#include "AbstractGeometricItem.h"
#include "AbstractMesh.h" #include "AbstractMesh.h"
#include "TriMesh.h" #include "TriMesh.h"
#include "AbstractFace.h" #include "AbstractFace.h"
#include "SvgShapeElements.h" #include "Circle.h"
std::unique_ptr<SvgDocument> SvgConverter::convert(Scene* scene) #include "SvgShapeElements.h"
#include "XmlAttribute.h"
std::unique_ptr<SvgDocument> SvgConverter::convert(Scene* scene, double width, double height) const
{ {
auto doc = std::make_unique<SvgDocument>(); auto doc = std::make_unique<SvgDocument>();
doc->setViewBox(0.0, 0.0, width, height);
scene->update(); scene->update();
@ -22,44 +28,125 @@ std::unique_ptr<SvgDocument> SvgConverter::convert(Scene* scene)
{ {
if (item->getType() == SceneItem::Type::MODEL) if (item->getType() == SceneItem::Type::MODEL)
{ {
auto mesh = dynamic_cast<SceneModel*>(item)->getMesh(); auto model = dynamic_cast<SceneModel*>(item);
auto transform = item->getTransform(); if (model->getGeometry())
if (mesh->getType() == AbstractMesh::MeshType::TRI)
{ {
for(const auto& face : dynamic_cast<TriMesh*>(mesh)->getFaces()) convertPrimitive(doc.get(), model);
{
auto svg_tri = std::make_unique<SvgPolygon>();
std::vector<DiscretePoint> points(3);
unsigned count = 0;
auto face_locs = face->getNodeLocations();
for(auto& loc : face_locs)
{
const auto x = loc.getX() * transform.getScaleX() + transform.getLocation().getX();
const auto y = loc.getY() * transform.getScaleY() + transform.getLocation().getY();
points[count] = {static_cast<unsigned>(x), static_cast<unsigned>(y)};
count++;
}
svg_tri->setPoints(points);
svg_tri->setFill(item->getFillColor());
doc->getRoot()->addChild(std::move(svg_tri));
if (scene->shouldShowMeshOutline())
{
auto mesh_outline = std::make_unique<SvgPolyline>();
points.push_back(points[0]);
mesh_outline->setPoints(points);
mesh_outline->setStrokeColor({0, 0, 0});
mesh_outline->setStrokeWidth(0.1);
doc->getRoot()->addChild(std::move(mesh_outline));
}
}
} }
else
{
convertMesh(doc.get(), model, scene->shouldShowMeshOutline());
}
}
else
{
auto text = dynamic_cast<SceneText*>(item);
convertText(doc.get(), text);
} }
} }
return std::move(doc); return std::move(doc);
} }
void SvgConverter::convertMesh(SvgDocument* document, SceneModel* model, bool showOutline) const
{
auto mesh = model->getMesh();
auto transform = model->getTransform();
if (mesh->getType() == AbstractMesh::MeshType::TRI)
{
for (const auto& face : dynamic_cast<TriMesh*>(mesh)->getFaces())
{
auto svg_tri = std::make_unique<SvgPolygon>();
std::vector<Point> points(3);
unsigned count = 0;
auto face_locs = face->getNodeLocations();
for (const auto& loc : face_locs)
{
const auto x = loc.getX() * transform.getScaleX() + transform.getLocation().getX();
const auto y = loc.getY() * transform.getScaleY() + transform.getLocation().getY();
points[count] = { x, y };
count++;
}
svg_tri->setPoints(points);
svg_tri->setFill(model->getFillColor());
document->getRoot()->addChild(std::move(svg_tri));
if (showOutline)
{
auto mesh_outline = std::make_unique<SvgPolyline>();
points.push_back(points[0]);
mesh_outline->setPoints(points);
mesh_outline->setStrokeColor({ 0, 0, 0 });
mesh_outline->setStrokeWidth(0.1);
document->getRoot()->addChild(std::move(mesh_outline));
}
}
}
}
void SvgConverter::setStyle(SceneModel* model, SvgShapeElement* element) const
{
auto transform = model->getTransform();
if (model->hasFillColor())
{
element->setFill(model->getFillColor());
}
else
{
element->setNoFill();
}
if (model->hasOutlineColor())
{
element->setStrokeColor(model->getOutlineColor());
element->setStrokeWidth(1.0 / transform.getScaleX());
}
else
{
element->setNoStroke();
}
element->addAttribute(std::move(toTransform(transform)));
}
void SvgConverter::convertPrimitive(SvgDocument* document, SceneModel* model) const
{
if (model->getGeometry()->getType() == AbstractGeometricItem::Type::RECTANGLE)
{
auto rect = std::make_unique<SvgRectangle>();
rect->setWidth(1.0);
rect->setHeight(1.0);
setStyle(model, rect.get());
document->getRoot()->addChild(std::move(rect));
}
else if (model->getGeometry()->getType() == AbstractGeometricItem::Type::CIRCLE)
{
auto model_circle = dynamic_cast<Circle*>(model->getGeometry());
auto circle = std::make_unique<SvgCircle>();
circle->setRadius(model_circle->getRadius());
setStyle(model, circle.get());
document->getRoot()->addChild(std::move(circle));
}
}
void SvgConverter::convertText(SvgDocument* document, SceneText* model) const
{
}
std::unique_ptr<XmlAttribute> SvgConverter::toTransform(const Transform& transform) const
{
auto svg_transform = std::make_unique<XmlAttribute>("transform");
std::string ops;
ops += "translate(" + std::to_string(transform.getLocation().getX()) + " " + std::to_string(transform.getLocation().getY()) + ") ";
ops += "scale(" + std::to_string(transform.getScaleX()) + " " + std::to_string(transform.getScaleY()) + ") ";
svg_transform->setValue(ops);
return std::move(svg_transform);
}

View file

@ -3,10 +3,28 @@
#include <memory> #include <memory>
class SvgDocument; class SvgDocument;
class SvgShapeElement;
class XmlAttribute;
class Scene; class Scene;
class SceneModel;
class SceneText;
class Transform;
class SvgConverter class SvgConverter
{ {
public: public:
std::unique_ptr<SvgDocument> convert(Scene* scene); std::unique_ptr<SvgDocument> convert(Scene* scene, double width = 800.0, double height = 800.0) const;
private:
void convertMesh(SvgDocument* document, SceneModel* model, bool showOutline = false) const;
void convertPrimitive(SvgDocument* document, SceneModel* model) const;
void convertText(SvgDocument* document, SceneText* model) const;
void setStyle(SceneModel* model, SvgShapeElement* element) const;
std::unique_ptr<XmlAttribute> toTransform(const Transform& transform) const;
}; };

View file

@ -5,8 +5,7 @@
#include "AbstractMesh.h" #include "AbstractMesh.h"
#include "MeshPrimitives.h" #include "MeshPrimitives.h"
#include "SceneInfo.h" #include "SceneInfo.h"
#include "Circle.h"
#include <iostream>
CircleNode::CircleNode(const Point& location, double radius) CircleNode::CircleNode(const Point& location, double radius)
: GeometryNode(location), : GeometryNode(location),
@ -51,22 +50,53 @@ void CircleNode::setRadius(double radius)
} }
} }
void CircleNode::setMinorRadius(double radius)
{
if (mMinorRadius != radius)
{
mMinorRadius = radius;
mTransformIsDirty = true;
}
}
void CircleNode::createOrUpdateGeometry(SceneInfo* sceneInfo)
{
if (sceneInfo->mSupportsGeometryPrimitives)
{
auto circle = std::make_unique<Circle>(Point{ 0, 0 }, 0.5);
circle->setMinorRadius(mMinorRadius);
mBackgroundItem = std::make_unique<SceneModel>(std::move(circle));
}
else
{
auto mesh = MeshPrimitives::buildCircleAsTriMesh();
mBackgroundItem = std::make_unique<SceneModel>(std::move(mesh));
}
mBackgroundItem->setName(mName + "_Model");
}
void CircleNode::updateMaterial()
{
if (!mBackgroundItem)
{
return;
}
if (mHasFillColor)
{
mBackgroundItem->setFillColor(mFillColor);
}
if (mHasStrokeColor)
{
mBackgroundItem->setOutlineColor(mStrokeColor);
}
}
void CircleNode::update(SceneInfo* sceneInfo) void CircleNode::update(SceneInfo* sceneInfo)
{ {
if (!mBackgroundItem || mGeometryIsDirty) if (!mBackgroundItem || mGeometryIsDirty)
{ {
auto mesh = MeshPrimitives::buildCircleAsTriMesh(); createOrUpdateGeometry(sceneInfo);
if (!mBackgroundItem)
{
mBackgroundItem = std::make_unique<SceneModel>(std::move(mesh));
mBackgroundItem->setName(mName + "_Model");
mBackgroundItem->setFillColor(mFillColor);
}
else
{
mBackgroundItem->updateMesh(std::move(mesh));
}
mGeometryIsDirty = false; mGeometryIsDirty = false;
} }
@ -78,7 +108,7 @@ void CircleNode::update(SceneInfo* sceneInfo)
if (mMaterialIsDirty) if (mMaterialIsDirty)
{ {
mBackgroundItem->setFillColor(mFillColor); updateMaterial();
mMaterialIsDirty = false; mMaterialIsDirty = false;
} }
} }

View file

@ -7,18 +7,22 @@ class CircleNode : public GeometryNode
public: public:
CircleNode(const Point& location, double radius); CircleNode(const Point& location, double radius);
Type getType(); Type getType() override;
double getRadius() const; double getRadius() const;
SceneItem* getSceneItem(std::size_t idx) const override; SceneItem* getSceneItem(std::size_t idx) const override;
std::size_t getNumSceneItems() const override; std::size_t getNumSceneItems() const override;
void setMinorRadius(double radius);
void setRadius(double radius); void setRadius(double radius);
void update(SceneInfo* sceneInfo) override; void update(SceneInfo* sceneInfo) override;
private: private:
double mRadius{1}; void createOrUpdateGeometry(SceneInfo* sceneInfo);
void updateMaterial();
double mRadius{1.0};
double mMinorRadius{ 1.0 };
std::unique_ptr<SceneModel> mBackgroundItem; std::unique_ptr<SceneModel> mBackgroundItem;
std::unique_ptr<SceneModel> mOutlineItem; std::unique_ptr<SceneModel> mOutlineItem;

View file

@ -0,0 +1,18 @@
#pragma once
#include "GeometryNode.h"
class LineNode : public GeometryNode
{
public:
LineNode(const Point& location)
: GeometryNode(location)
{
}
Type getType()
{
return Type::Line;
}
};

View file

@ -86,6 +86,23 @@ void RectangleNode::createOrUpdateGeometry(SceneInfo* sceneInfo)
mBackgroundItem->setName(mName + "_Model"); mBackgroundItem->setName(mName + "_Model");
} }
void RectangleNode::updateMaterial()
{
if (!mBackgroundItem)
{
return;
}
if (mHasFillColor)
{
mBackgroundItem->setFillColor(mFillColor);
}
if (mHasStrokeColor)
{
mBackgroundItem->setOutlineColor(mStrokeColor);
}
}
void RectangleNode::update(SceneInfo* sceneInfo) void RectangleNode::update(SceneInfo* sceneInfo)
{ {
if (!mBackgroundItem || mGeometryIsDirty) if (!mBackgroundItem || mGeometryIsDirty)
@ -96,20 +113,13 @@ void RectangleNode::update(SceneInfo* sceneInfo)
if (mTransformIsDirty) if (mTransformIsDirty)
{ {
mBackgroundItem->updateTransform({mLocation, static_cast<double>(mWidth), static_cast<double>(mHeight)}); mBackgroundItem->updateTransform({mLocation, mWidth, mHeight});
mTransformIsDirty = false; mTransformIsDirty = false;
} }
if (mMaterialIsDirty) if (mMaterialIsDirty)
{ {
if (mHasFillColor) updateMaterial();
{
mBackgroundItem->setFillColor(mFillColor);
}
if (mHasStrokeColor)
{
mBackgroundItem->setOutlineColor(mStrokeColor);
}
mMaterialIsDirty = false; mMaterialIsDirty = false;
} }
} }

View file

@ -24,6 +24,7 @@ public:
private: private:
void createOrUpdateGeometry(SceneInfo* sceneInfo); void createOrUpdateGeometry(SceneInfo* sceneInfo);
void updateMaterial();
double mWidth{1}; double mWidth{1};
double mHeight{1}; double mHeight{1};

View file

@ -27,23 +27,7 @@ public:
protected: protected:
unsigned mStrokeThickness{0}; unsigned mStrokeThickness{0};
Type mType; Type mType;
bool mGeometryIsDirty{true}; bool mGeometryIsDirty{true};
}; };
class LineNode : public GeometryNode
{
public:
LineNode(const Point& location)
: GeometryNode(location)
{
}
Type getType()
{
return Type::Line;
}
};
using GeometryNodePtr = std::unique_ptr<GeometryNode>; using GeometryNodePtr = std::unique_ptr<GeometryNode>;

View file

@ -20,20 +20,33 @@ const Color& MaterialNode::getStrokeColor() const
return mStrokeColor; return mStrokeColor;
} }
void MaterialNode::setHasStrokeColor(bool hasStroke)
{
if (mHasStrokeColor != hasStroke)
{
mHasStrokeColor = hasStroke;
mMaterialIsDirty = true;
}
}
void MaterialNode::setFillColor(const Color& color) void MaterialNode::setFillColor(const Color& color)
{ {
mHasFillColor = true;
if (mFillColor != color) if (mFillColor != color)
{ {
mMaterialIsDirty = true; mMaterialIsDirty = true;
mFillColor = color; mFillColor = color;
} }
} }
void MaterialNode::setStrokeColor(const Color& color) void MaterialNode::setStrokeColor(const Color& color)
{ {
mHasStrokeColor = true;
if (mStrokeColor != color) if (mStrokeColor != color)
{ {
mMaterialIsDirty = true; mMaterialIsDirty = true;
mStrokeColor = color; mStrokeColor = color;
} }
} }

View file

@ -12,6 +12,8 @@ public:
const Color& getFillColor() const; const Color& getFillColor() const;
const Color& getStrokeColor() const; const Color& getStrokeColor() const;
void setHasStrokeColor(bool hasStroke);
void setFillColor(const Color& color); void setFillColor(const Color& color);
void setStrokeColor(const Color& color); void setStrokeColor(const Color& color);

View file

@ -47,6 +47,8 @@ void Scene::setBackgroundColor(const Color& color)
void Scene::updateNode(AbstractVisualNode* node) void Scene::updateNode(AbstractVisualNode* node)
{ {
node->update(mSceneInfo.get());
for (auto child : node->getChildren()) for (auto child : node->getChildren())
{ {
if (child->getIsVisible()) if (child->getIsVisible())
@ -55,8 +57,6 @@ void Scene::updateNode(AbstractVisualNode* node)
} }
} }
node->update(mSceneInfo.get());
for (std::size_t idx=0; idx< node->getNumSceneItems(); idx++) for (std::size_t idx=0; idx< node->getNumSceneItems(); idx++)
{ {
if (auto item = node->getSceneItem(idx)) if (auto item = node->getSceneItem(idx))

View file

@ -27,6 +27,11 @@ list(APPEND web_LIB_INCLUDES
html/elements/HtmlHeadElement.cpp html/elements/HtmlHeadElement.cpp
html/elements/HtmlBodyElement.cpp html/elements/HtmlBodyElement.cpp
html/elements/HtmlParagraphElement.cpp html/elements/HtmlParagraphElement.cpp
svg/SvgDocument.h
svg/SvgWriter.h
svg/SvgShapeElement.h
svg/SvgElement.h
svg/elements/SvgShapeElements.h
svg/SvgDocument.cpp svg/SvgDocument.cpp
svg/SvgWriter.cpp svg/SvgWriter.cpp
svg/SvgShapeElement.cpp svg/SvgShapeElement.cpp

View file

@ -10,6 +10,20 @@ SvgShapeElement::SvgShapeElement(const std::string& tagName)
} }
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) void SvgShapeElement::setFill(const Color& fill)
{ {
auto attr = std::make_unique<XmlAttribute>("fill"); auto attr = std::make_unique<XmlAttribute>("fill");

View file

@ -10,8 +10,11 @@ public:
void setFill(const Color& fill); void setFill(const Color& fill);
void setNoFill();
void setStrokeWidth(double width); void setStrokeWidth(double width);
void setStrokeColor(const Color& stroke); void setStrokeColor(const Color& stroke);
void setNoStroke();
}; };

View file

@ -8,19 +8,19 @@ SvgCircle::SvgCircle()
} }
void SvgCircle::setLocation(const DiscretePoint& loc) void SvgCircle::setLocation(const Point& loc)
{ {
auto cx = std::make_unique<XmlAttribute>("cx"); auto cx = std::make_unique<XmlAttribute>("cx");
auto cy = std::make_unique<XmlAttribute>("cy"); auto cy = std::make_unique<XmlAttribute>("cy");
cx->setValue(std::to_string(loc.GetX())); cx->setValue(std::to_string(loc.getX()));
cy->setValue(std::to_string(loc.GetY())); cy->setValue(std::to_string(loc.getY()));
addAttribute(std::move(cx)); addAttribute(std::move(cx));
addAttribute(std::move(cy)); addAttribute(std::move(cy));
} }
void SvgCircle::setRadius(unsigned rad) void SvgCircle::setRadius(double rad)
{ {
auto r = std::make_unique<XmlAttribute>("r"); auto r = std::make_unique<XmlAttribute>("r");
r->setValue(std::to_string(rad)); r->setValue(std::to_string(rad));
@ -33,19 +33,19 @@ SvgRectangle::SvgRectangle()
} }
void SvgRectangle::setLocation(const DiscretePoint& loc) void SvgRectangle::setLocation(const Point& loc)
{ {
auto x = std::make_unique<XmlAttribute>("x"); auto x = std::make_unique<XmlAttribute>("x");
auto y = std::make_unique<XmlAttribute>("y"); auto y = std::make_unique<XmlAttribute>("y");
x->setValue(std::to_string(loc.GetX())); x->setValue(std::to_string(loc.getX()));
y->setValue(std::to_string(loc.GetY())); y->setValue(std::to_string(loc.getY()));
addAttribute(std::move(x)); addAttribute(std::move(x));
addAttribute(std::move(y)); addAttribute(std::move(y));
} }
void SvgRectangle::setWidth(unsigned w) void SvgRectangle::setWidth(double w)
{ {
auto width = std::make_unique<XmlAttribute>("width"); auto width = std::make_unique<XmlAttribute>("width");
@ -54,7 +54,7 @@ void SvgRectangle::setWidth(unsigned w)
addAttribute(std::move(width)); addAttribute(std::move(width));
} }
void SvgRectangle::setHeight(unsigned h) void SvgRectangle::setHeight(double h)
{ {
auto height = std::make_unique<XmlAttribute>("height"); auto height = std::make_unique<XmlAttribute>("height");
@ -70,14 +70,14 @@ SvgPolygon::SvgPolygon()
} }
void SvgPolygon::setPoints(const std::vector<DiscretePoint>& locs) void SvgPolygon::setPoints(const std::vector<Point>& locs)
{ {
auto points = std::make_unique<XmlAttribute>("points"); auto points = std::make_unique<XmlAttribute>("points");
std::stringstream sstr; std::stringstream sstr;
for (const auto& loc : locs) for (const auto& loc : locs)
{ {
sstr << loc.GetX() << "," << loc.GetY() << " "; sstr << loc.getX() << "," << loc.getY() << " ";
} }
points->setValue(sstr.str()); points->setValue(sstr.str());
addAttribute(std::move(points)); addAttribute(std::move(points));
@ -91,14 +91,14 @@ SvgPolyline::SvgPolyline()
addAttribute(std::move(fill)); addAttribute(std::move(fill));
} }
void SvgPolyline::setPoints(const std::vector<DiscretePoint>& locs) void SvgPolyline::setPoints(const std::vector<Point>& locs)
{ {
auto points = std::make_unique<XmlAttribute>("points"); auto points = std::make_unique<XmlAttribute>("points");
std::stringstream sstr; std::stringstream sstr;
for (const auto& loc : locs) for (const auto& loc : locs)
{ {
sstr << loc.GetX() << "," << loc.GetY() << " "; sstr << loc.getX() << "," << loc.getY() << " ";
} }
points->setValue(sstr.str()); points->setValue(sstr.str());
addAttribute(std::move(points)); addAttribute(std::move(points));

View file

@ -1,7 +1,7 @@
#pragma once #pragma once
#include "SvgShapeElement.h" #include "SvgShapeElement.h"
#include "DiscretePoint.h" #include "Point.h"
#include "XmlAttribute.h" #include "XmlAttribute.h"
@ -10,9 +10,9 @@ class SvgCircle : public SvgShapeElement
public: public:
SvgCircle(); SvgCircle();
void setLocation(const DiscretePoint& loc); void setLocation(const Point& loc);
void setRadius(unsigned rad); void setRadius(double rad);
}; };
class SvgRectangle : public SvgShapeElement class SvgRectangle : public SvgShapeElement
@ -20,11 +20,11 @@ class SvgRectangle : public SvgShapeElement
public: public:
SvgRectangle(); SvgRectangle();
void setLocation(const DiscretePoint& loc); void setLocation(const Point& loc);
void setWidth(unsigned width); void setWidth(double width);
void setHeight(unsigned height); void setHeight(double height);
}; };
class SvgPolygon : public SvgShapeElement class SvgPolygon : public SvgShapeElement
@ -32,7 +32,7 @@ class SvgPolygon : public SvgShapeElement
public: public:
SvgPolygon(); SvgPolygon();
void setPoints(const std::vector<DiscretePoint>& loc); void setPoints(const std::vector<Point>& loc);
}; };
class SvgPolyline : public SvgShapeElement class SvgPolyline : public SvgShapeElement
@ -40,7 +40,7 @@ class SvgPolyline : public SvgShapeElement
public: public:
SvgPolyline(); SvgPolyline();
void setPoints(const std::vector<DiscretePoint>& loc); void setPoints(const std::vector<Point>& loc);
}; };
class SvgPath : public SvgShapeElement class SvgPath : public SvgShapeElement

View file

@ -3,9 +3,16 @@
#include "DrawingSurface.h" #include "DrawingSurface.h"
#include "DrawingContext.h" #include "DrawingContext.h"
#include "AbstractPainter.h" #include "AbstractPainter.h"
#include "Scene.h"
#include "SvgConverter.h"
#include "SvgWriter.h"
#include "SvgDocument.h"
#include "Image.h" #include "Image.h"
#include "PngWriter.h" #include "PngWriter.h"
#include "Scene.h"
#include "File.h"
class TestRenderer class TestRenderer
{ {
@ -34,6 +41,18 @@ public:
writer.write(image); writer.write(image);
} }
void writeSvg(const Path& path)
{
SvgConverter converter;
auto svg_document = converter.convert(mSurface->getScene());
SvgWriter writer;
auto svg_content = writer.toString(svg_document.get());
File svg_file(path);
svg_file.writeText(svg_content);
}
private: private:
std::unique_ptr<DrawingSurface> mSurface; std::unique_ptr<DrawingSurface> mSurface;
std::unique_ptr<DrawingContext> mDrawingContext; std::unique_ptr<DrawingContext> mDrawingContext;