#include "SvgPainter.h" #include "SvgDocument.h" #include "Scene.h" #include "SceneItem.h" #include "SceneModel.h" #include "SceneText.h" #include "AbstractGeometricItem.h" #include "AbstractMesh.h" #include "TriMesh.h" #include "AbstractFace.h" #include "Circle.h" #include "Rectangle.h" #include "Path.h" #include "SvgShapeElements.h" #include "SvgTextElement.h" #include "XmlAttribute.h" std::unique_ptr SvgPainter::paint(Scene* scene, double width, double height) const { auto doc = std::make_unique(); doc->setViewBox(0.0, 0.0, width, height); scene->update(); for (auto item : scene->getItems()) { if (item->getType() == SceneItem::Type::MODEL) { auto model = dynamic_cast(item); if (model->getGeometry()) { paintPrimitive(doc.get(), model); } else { paintMesh(doc.get(), model, scene->shouldShowMeshOutline()); } } else { auto text = dynamic_cast(item); paintText(doc.get(), text); } } return doc; } void SvgPainter::paintMesh(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(mesh)->getFaces()) { auto svg_tri = std::make_unique(); std::vector 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->getSolidMaterial().getFillColor()); document->getRoot()->addChild(std::move(svg_tri)); if (showOutline) { auto mesh_outline = std::make_unique(); 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 SvgPainter::setStyle(SceneModel* model, SvgShapeElement* element) const { const auto transform = model->getTransform(); const auto material = model->getSolidMaterial(); if (material.hasFillColor()) { element->setFill(material.getFillColor()); auto opacity = static_cast(material.getFillColor().getAlpha()); if (opacity != 1.0) { element->setFillOpacity(opacity); } } else { element->setNoFill(); } if (material.hasStrokeColor()) { element->setStrokeColor(material.getStrokeColor()); element->setStrokeWidth(material.getStrokeThickness()); } else { element->setNoStroke(); } if (!transform.isDefaultTransform()) { element->addAttribute(toTransform(transform)); } } void SvgPainter::paintPrimitive(SvgDocument* document, SceneModel* model) const { if (model->getGeometry()->getType() == AbstractGeometricItem::Type::RECTANGLE) { paintRect(document, model); } else if (model->getGeometry()->getType() == AbstractGeometricItem::Type::CIRCLE) { paintCircle(document, model); } else if (model->getGeometry()->getType() == AbstractGeometricItem::Type::PATH) { paintPath(document, model); } } void SvgPainter::paintRect(SvgDocument* document, SceneModel* model) const { auto model_rect = dynamic_cast(model->getGeometry()); auto rect = std::make_unique(); rect->setWidth(model_rect->getWidth()); rect->setHeight(model_rect->getHeight()); if (model_rect->getRadius() > 0.0) { rect->setRadius(model_rect->getRadius()); } setStyle(model, rect.get()); document->getRoot()->addChild(std::move(rect)); } void SvgPainter::paintCircle(SvgDocument* document, SceneModel* model) const { auto model_circle = dynamic_cast(model->getGeometry()); auto circle = std::make_unique(model_circle->isEllipse() ? SvgCircle::Type::ELLIPSE : SvgCircle::Type::REGULAR); circle->setRadius(model_circle->getRadius()); if (model_circle->isEllipse()) { circle->setMinorRadius(model_circle->getMinorRadius()); } setStyle(model, circle.get()); document->getRoot()->addChild(std::move(circle)); } void SvgPainter::paintPath(SvgDocument* document, SceneModel* model) const { auto model_path = dynamic_cast(model->getGeometry()); auto path_string = model_path->getAsPostScript(); auto svg_path = std::make_unique(); svg_path->setPath(path_string); setStyle(model, svg_path.get()); document->getRoot()->addChild(std::move(svg_path)); } void SvgPainter::paintText(SvgDocument* document, SceneText* text) const { auto svg_text = std::make_unique(); svg_text->setContent(text->getTextData().mContent); auto loc = text->getTransform().getLocation(); loc.move(text->getTextWidth() / 2.0, text->getTextHeight()/2.0); svg_text->setLocation(loc); svg_text->setFontFamily(text->getTextData().mFont.getFaceName()); svg_text->setFill(text->getSolidMaterial().getFillColor()); auto opacity = static_cast(text->getSolidMaterial().getFillColor().getAlpha()); if (opacity != 1.0) { svg_text->setFillOpacity(opacity); } svg_text->setFontSize(text->getTextData().mFont.getSize()); document->getRoot()->addChild(std::move(svg_text)); } std::unique_ptr SvgPainter::toTransform(const Transform& transform) const { auto svg_transform = std::make_unique("transform"); std::string ops; if (!transform.hasDefaultLocation()) { ops += "translate(" + std::to_string(transform.getLocation().getX()) + " " + std::to_string(transform.getLocation().getY()) + ") "; } if (!transform.hasDefaultScale()) { ops += "scale(" + std::to_string(transform.getScaleX()) + " " + std::to_string(transform.getScaleY()) + ") "; } svg_transform->setValue(ops); return svg_transform; }