Add path rendering.
This commit is contained in:
parent
f2ab532005
commit
97afa782a0
39 changed files with 1148 additions and 131 deletions
|
@ -1,9 +1,17 @@
|
|||
#include "DirectX2dPainter.h"
|
||||
|
||||
#include "DirectX2dInterface.h"
|
||||
|
||||
#include "AbstractGeometricItem.h"
|
||||
#include "Rectangle.h"
|
||||
#include "Circle.h"
|
||||
#include "Path.h"
|
||||
|
||||
#include "LineSegment.h"
|
||||
#include "Line.h"
|
||||
#include "Path.h"
|
||||
|
||||
#include "FileLogger.h"
|
||||
|
||||
#include "SceneModel.h"
|
||||
|
||||
|
@ -34,68 +42,148 @@ void DirectX2dPainter::paint(SceneModel* model)
|
|||
|
||||
if (model->getGeometry()->getType() == AbstractGeometricItem::Type::RECTANGLE)
|
||||
{
|
||||
auto rect = dynamic_cast<ntk::Rectangle*>(model->getGeometry());
|
||||
|
||||
const auto loc = model->getTransform().getLocation();
|
||||
const auto scale_x = model->getTransform().getScaleX();
|
||||
const auto scale_y = model->getTransform().getScaleY();
|
||||
|
||||
const auto min_x = static_cast<float>(loc.getX());
|
||||
const auto max_x = static_cast<float>(loc.getX() + rect->getWidth()* scale_x);
|
||||
|
||||
const auto min_y = static_cast<float>(loc.getY());
|
||||
const auto max_y = static_cast<float>(loc.getY() + rect->getHeight() * scale_y);
|
||||
|
||||
D2D1_RECT_F d2d_rect{ min_x, max_y, max_x, min_y };
|
||||
if (rect->getRadius() == 0.0)
|
||||
{
|
||||
if (model->hasFillColor())
|
||||
{
|
||||
mSolidBrush->SetColor(toD2dColor(model->getFillColor()));
|
||||
rt->FillRectangle(d2d_rect, mSolidBrush.Get());
|
||||
}
|
||||
if (model->hasOutlineColor())
|
||||
{
|
||||
mSolidBrush->SetColor(toD2dColor(model->getOutlineColor()));
|
||||
rt->DrawRectangle(d2d_rect, mSolidBrush.Get(), 1.0f);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
D2D1_ROUNDED_RECT rounded_rect{ d2d_rect , static_cast<float>(rect->getRadius()), static_cast<float>(rect->getRadius()) };
|
||||
if (model->hasFillColor())
|
||||
{
|
||||
mSolidBrush->SetColor(toD2dColor(model->getFillColor()));
|
||||
rt->FillRoundedRectangle(rounded_rect, mSolidBrush.Get());
|
||||
}
|
||||
if (model->hasOutlineColor())
|
||||
{
|
||||
mSolidBrush->SetColor(toD2dColor(model->getOutlineColor()));
|
||||
rt->DrawRoundedRectangle(rounded_rect, mSolidBrush.Get(), 1.0f);
|
||||
}
|
||||
}
|
||||
paintRect(model);
|
||||
}
|
||||
else if (model->getGeometry()->getType() == AbstractGeometricItem::Type::CIRCLE)
|
||||
{
|
||||
const auto loc = model->getTransform().getLocation();
|
||||
const auto scale_x = model->getTransform().getScaleX();
|
||||
const auto scale_y = model->getTransform().getScaleY();
|
||||
paintCircle(model);
|
||||
}
|
||||
else if (model->getGeometry()->getType() == AbstractGeometricItem::Type::PATH)
|
||||
{
|
||||
paintPath(model);
|
||||
}
|
||||
}
|
||||
|
||||
auto circle = dynamic_cast<Circle*>(model->getGeometry());
|
||||
const auto radius = circle->getRadius() * scale_x;
|
||||
const auto radiusy = circle->getMinorRadius() * scale_y;
|
||||
void DirectX2dPainter::paintRect(SceneModel* model)
|
||||
{
|
||||
auto rt = mD2dInterface->getRenderTarget();
|
||||
|
||||
D2D1_POINT_2F d2d_centre{ static_cast<float>(loc.getX()), static_cast<float>(loc.getY()) };
|
||||
D2D1_ELLIPSE ellipse{ d2d_centre, static_cast<float>(radius), static_cast<float>(radiusy) };
|
||||
auto rect = dynamic_cast<ntk::Rectangle*>(model->getGeometry());
|
||||
|
||||
const auto loc = model->getTransform().getLocation();
|
||||
const auto scale_x = model->getTransform().getScaleX();
|
||||
const auto scale_y = model->getTransform().getScaleY();
|
||||
|
||||
const auto min_x = static_cast<float>(loc.getX());
|
||||
const auto max_x = static_cast<float>(loc.getX() + rect->getWidth() * scale_x);
|
||||
|
||||
const auto min_y = static_cast<float>(loc.getY());
|
||||
const auto max_y = static_cast<float>(loc.getY() + rect->getHeight() * scale_y);
|
||||
|
||||
D2D1_RECT_F d2d_rect{ min_x, max_y, max_x, min_y };
|
||||
if (rect->getRadius() == 0.0)
|
||||
{
|
||||
if (model->hasFillColor())
|
||||
{
|
||||
mSolidBrush->SetColor(toD2dColor(model->getFillColor()));
|
||||
rt->FillRectangle(d2d_rect, mSolidBrush.Get());
|
||||
}
|
||||
if (model->hasOutlineColor())
|
||||
{
|
||||
mSolidBrush->SetColor(toD2dColor(model->getOutlineColor()));
|
||||
rt->DrawEllipse(ellipse, mSolidBrush.Get(), 1.0f);
|
||||
rt->DrawRectangle(d2d_rect, mSolidBrush.Get(), 1.0f);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
D2D1_ROUNDED_RECT rounded_rect{ d2d_rect , static_cast<float>(rect->getRadius()), static_cast<float>(rect->getRadius()) };
|
||||
if (model->hasFillColor())
|
||||
{
|
||||
mSolidBrush->SetColor(toD2dColor(model->getFillColor()));
|
||||
rt->FillRoundedRectangle(rounded_rect, mSolidBrush.Get());
|
||||
}
|
||||
if (model->hasOutlineColor())
|
||||
{
|
||||
mSolidBrush->SetColor(toD2dColor(model->getOutlineColor()));
|
||||
rt->DrawRoundedRectangle(rounded_rect, mSolidBrush.Get(), 1.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DirectX2dPainter::paintCircle(SceneModel* model)
|
||||
{
|
||||
auto rt = mD2dInterface->getRenderTarget();
|
||||
|
||||
const auto loc = model->getTransform().getLocation();
|
||||
const auto scale_x = model->getTransform().getScaleX();
|
||||
const auto scale_y = model->getTransform().getScaleY();
|
||||
|
||||
auto circle = dynamic_cast<Circle*>(model->getGeometry());
|
||||
const auto radius = circle->getRadius() * scale_x;
|
||||
const auto radiusy = circle->getMinorRadius() * scale_y;
|
||||
|
||||
D2D1_POINT_2F d2d_centre{ static_cast<float>(loc.getX()), static_cast<float>(loc.getY()) };
|
||||
D2D1_ELLIPSE ellipse{ d2d_centre, static_cast<float>(radius), static_cast<float>(radiusy) };
|
||||
|
||||
if (model->hasFillColor())
|
||||
{
|
||||
mSolidBrush->SetColor(toD2dColor(model->getFillColor()));
|
||||
rt->FillEllipse(ellipse, mSolidBrush.Get());
|
||||
}
|
||||
if (model->hasOutlineColor())
|
||||
{
|
||||
mSolidBrush->SetColor(toD2dColor(model->getOutlineColor()));
|
||||
rt->DrawEllipse(ellipse, mSolidBrush.Get(), 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
D2D_POINT_2F DirectX2dPainter::toD2dPoint(const Point& point)
|
||||
{
|
||||
return D2D1::Point2F(static_cast<float>(point.getX()), static_cast<float>(point.getY()));
|
||||
}
|
||||
|
||||
void DirectX2dPainter::paintPath(SceneModel* model)
|
||||
{
|
||||
Microsoft::WRL::ComPtr<ID2D1PathGeometry> path_geom;
|
||||
mD2dInterface->getFactory()->CreatePathGeometry(&path_geom);
|
||||
|
||||
Microsoft::WRL::ComPtr<ID2D1GeometrySink> path_sink;
|
||||
|
||||
path_geom->Open(&path_sink);
|
||||
|
||||
auto path_item = dynamic_cast<GeometryPath*>(model->getGeometry());
|
||||
for (const auto& feature : path_item->getFeatures())
|
||||
{
|
||||
const auto loc = feature->getLocation();
|
||||
MLOG_INFO("Starting feature at: " << loc.getX() << " " << loc.getY());
|
||||
path_sink->BeginFigure(toD2dPoint(loc), D2D1_FIGURE_BEGIN_FILLED);
|
||||
|
||||
for (const auto& element : feature->getElements())
|
||||
{
|
||||
if (element->getType() == AbstractGeometricItem::Type::LINE)
|
||||
{
|
||||
for (const auto& point : dynamic_cast<Line*>(element.get())->getPoints().getPoints())
|
||||
{
|
||||
MLOG_INFO("Adding line entry at: " << point.getX() << " " << point.getY());
|
||||
path_sink->AddLine(toD2dPoint(point));
|
||||
}
|
||||
MLOG_INFO("Finished line");
|
||||
}
|
||||
else if (element->getType() == AbstractGeometricItem::Type::LINE_SEGMENT)
|
||||
{
|
||||
const auto loc = element->getEndPoint();
|
||||
MLOG_INFO("Adding segment entry at: " << loc.getX() << " " << loc.getY());
|
||||
path_sink->AddLine(toD2dPoint(loc));
|
||||
}
|
||||
}
|
||||
path_sink->EndFigure(D2D1_FIGURE_END_CLOSED);
|
||||
}
|
||||
path_sink->Close();
|
||||
|
||||
auto rt = mD2dInterface->getRenderTarget();
|
||||
if (model->hasFillColor())
|
||||
{
|
||||
mSolidBrush->SetColor(toD2dColor(model->getFillColor()));
|
||||
rt->FillGeometry(path_geom.Get(), mSolidBrush.Get());
|
||||
}
|
||||
if (model->hasOutlineColor())
|
||||
{
|
||||
mSolidBrush->SetColor(toD2dColor(model->getOutlineColor()));
|
||||
rt->DrawGeometry(path_geom.Get(), mSolidBrush.Get(), 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DirectX2dPainter::setD2dInterface(DirectX2dInterface* d2dIterface)
|
||||
{
|
||||
mD2dInterface = d2dIterface;
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <wrl.h>
|
||||
|
||||
class SceneModel;
|
||||
class Point;
|
||||
class DirectX2dInterface;
|
||||
|
||||
struct ID2D1SolidColorBrush;
|
||||
|
@ -13,6 +14,7 @@ namespace D2D1
|
|||
{
|
||||
class ColorF;
|
||||
}
|
||||
struct D2D_POINT_2F;
|
||||
|
||||
class DirectX2dPainter
|
||||
{
|
||||
|
@ -30,6 +32,14 @@ public:
|
|||
private:
|
||||
static D2D1::ColorF toD2dColor(const Color& color);
|
||||
|
||||
static D2D_POINT_2F toD2dPoint(const Point& point);
|
||||
|
||||
void paintRect(SceneModel* model);
|
||||
|
||||
void paintCircle(SceneModel* model);
|
||||
|
||||
void paintPath(SceneModel* model);
|
||||
|
||||
Microsoft::WRL::ComPtr<ID2D1SolidColorBrush> mSolidBrush;
|
||||
|
||||
DirectX2dInterface* mD2dInterface{ nullptr };
|
||||
|
|
|
@ -21,6 +21,8 @@ list(APPEND visual_elements_LIB_INCLUDES
|
|||
svg/SvgDocument.h
|
||||
svg/SvgWriter.h
|
||||
svg/SvgReader.h
|
||||
svg/SvgPainter.h
|
||||
svg/SvgPainter.cpp
|
||||
svg/SvgShapeElement.h
|
||||
svg/SvgElement.h
|
||||
svg/SvgTextElement.h
|
||||
|
@ -34,6 +36,8 @@ list(APPEND visual_elements_LIB_INCLUDES
|
|||
svg/elements/SvgShapeElements.cpp
|
||||
nodes/MaterialNode.h
|
||||
nodes/MaterialNode.cpp
|
||||
nodes/PathNode.h
|
||||
nodes/PathNode.cpp
|
||||
nodes/ImageNode.h
|
||||
nodes/ImageNode.cpp
|
||||
nodes/MeshNode.h
|
||||
|
|
59
src/rendering/visual_elements/nodes/PathNode.cpp
Normal file
59
src/rendering/visual_elements/nodes/PathNode.cpp
Normal file
|
@ -0,0 +1,59 @@
|
|||
#include "PathNode.h"
|
||||
|
||||
#include "Path.h"
|
||||
#include "SceneInfo.h"
|
||||
|
||||
PathNode::PathNode(const Point& loc, const std::string& psPath)
|
||||
: GeometryNode(loc),
|
||||
mPathString(psPath)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
std::unique_ptr<PathNode> PathNode::Create(const Point& loc, const std::string& psPath)
|
||||
{
|
||||
return std::make_unique<PathNode>(loc, psPath);
|
||||
}
|
||||
|
||||
GeometryNode::Type PathNode::getType()
|
||||
{
|
||||
return GeometryNode::Type::Path;
|
||||
}
|
||||
|
||||
const std::string& PathNode::getPathString() const
|
||||
{
|
||||
return mPathString;
|
||||
}
|
||||
|
||||
void PathNode::setPathString(const std::string& psPath)
|
||||
{
|
||||
mPathString = psPath;
|
||||
}
|
||||
|
||||
void PathNode::createOrUpdateGeometry(SceneInfo* sceneInfo)
|
||||
{
|
||||
if (!mBackgroundItem)
|
||||
{
|
||||
if (sceneInfo->mSupportsGeometryPrimitives)
|
||||
{
|
||||
auto path = std::make_unique<GeometryPath>();
|
||||
path->buildFromPostscript(mPathString);
|
||||
mBackgroundItem = std::make_unique<SceneModel>(std::move(path));
|
||||
mBackgroundItem->setName(mName + "_Model");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (sceneInfo->mSupportsGeometryPrimitives)
|
||||
{
|
||||
auto path = std::make_unique<GeometryPath>();
|
||||
path->buildFromPostscript(mPathString);
|
||||
mBackgroundItem->updateGeometry(std::move(path));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PathNode::updateTransform()
|
||||
{
|
||||
mBackgroundItem->updateTransform({ mLocation });
|
||||
}
|
24
src/rendering/visual_elements/nodes/PathNode.h
Normal file
24
src/rendering/visual_elements/nodes/PathNode.h
Normal file
|
@ -0,0 +1,24 @@
|
|||
#pragma once
|
||||
|
||||
#include "GeometryNode.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
class PathNode : public GeometryNode
|
||||
{
|
||||
public:
|
||||
PathNode(const Point& loc, const std::string& psPath);
|
||||
static std::unique_ptr<PathNode> Create(const Point& loc, const std::string& psPath);
|
||||
|
||||
GeometryNode::Type getType() override;
|
||||
|
||||
const std::string& getPathString() const;
|
||||
|
||||
void setPathString(const std::string& psPath);
|
||||
|
||||
private:
|
||||
void createOrUpdateGeometry(SceneInfo* sceneInfo) override;
|
||||
void updateTransform() override;
|
||||
|
||||
std::string mPathString;
|
||||
};
|
|
@ -1,7 +1,9 @@
|
|||
#include "SvgNode.h"
|
||||
|
||||
#include "CircleNode.h"
|
||||
#include "PathNode.h"
|
||||
|
||||
#include "SvgElement.h"
|
||||
#include "SvgShapeElements.h"
|
||||
|
||||
SvgNode::SvgNode(const Point& location)
|
||||
|
@ -39,26 +41,11 @@ void SvgNode::createOrUpdateGeometry(SceneInfo* sceneInfo)
|
|||
|
||||
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);
|
||||
onCircle(svg_element.get(), node);
|
||||
}
|
||||
else if (svg_element->getTagName() == "path")
|
||||
{
|
||||
onPath(svg_element.get(), node);
|
||||
}
|
||||
|
||||
if (!node)
|
||||
|
@ -71,7 +58,6 @@ void SvgNode::createOrUpdateGeometry(SceneInfo* sceneInfo)
|
|||
|
||||
addChild(raw_node);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void SvgNode::update(SceneInfo* sceneInfo)
|
||||
|
@ -87,4 +73,38 @@ void SvgNode::update(SceneInfo* sceneInfo)
|
|||
updateTransform();
|
||||
mTransformIsDirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
void SvgNode::onCircle(XmlElement* element, std::unique_ptr<AbstractVisualNode>& node)
|
||||
{
|
||||
auto svg_circle = dynamic_cast<SvgCircle*>(element);
|
||||
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 (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);
|
||||
}
|
||||
|
||||
void SvgNode::onPath(XmlElement* element, std::unique_ptr<AbstractVisualNode>& node)
|
||||
{
|
||||
auto svg_path = dynamic_cast<SvgPath*>(element);
|
||||
|
||||
Point loc;
|
||||
auto path_node = std::make_unique<PathNode>(loc, svg_path->getPath());
|
||||
node = std::move(path_node);
|
||||
}
|
|
@ -18,6 +18,9 @@ private:
|
|||
void createOrUpdateGeometry(SceneInfo* sceneInfo);
|
||||
void updateTransform();
|
||||
|
||||
void onCircle(XmlElement* element, std::unique_ptr<AbstractVisualNode>& node);
|
||||
void onPath(XmlElement* element, std::unique_ptr<AbstractVisualNode>& node);
|
||||
|
||||
bool mContentDirty{ true };
|
||||
|
||||
std::vector<std::unique_ptr<AbstractVisualNode> > mManagedChildren;
|
||||
|
|
227
src/rendering/visual_elements/svg/SvgPainter.cpp
Normal file
227
src/rendering/visual_elements/svg/SvgPainter.cpp
Normal file
|
@ -0,0 +1,227 @@
|
|||
#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<SvgDocument> SvgPainter::paint(Scene* scene, double width, double height) const
|
||||
{
|
||||
auto doc = std::make_unique<SvgDocument>();
|
||||
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<SceneModel*>(item);
|
||||
if (model->getGeometry())
|
||||
{
|
||||
paintPrimitive(doc.get(), model);
|
||||
}
|
||||
else
|
||||
{
|
||||
paintMesh(doc.get(), model, scene->shouldShowMeshOutline());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto text = dynamic_cast<SceneText*>(item);
|
||||
paintText(doc.get(), text);
|
||||
}
|
||||
}
|
||||
|
||||
return std::move(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<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 SvgPainter::setStyle(SceneModel* model, SvgShapeElement* element) const
|
||||
{
|
||||
auto transform = model->getTransform();
|
||||
|
||||
if (model->hasFillColor())
|
||||
{
|
||||
element->setFill(model->getFillColor());
|
||||
auto opacity = static_cast<float>(model->getFillColor().getAlpha());
|
||||
if (opacity != 1.0)
|
||||
{
|
||||
element->setFillOpacity(opacity);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
element->setNoFill();
|
||||
}
|
||||
|
||||
if (model->hasOutlineColor())
|
||||
{
|
||||
element->setStrokeColor(model->getOutlineColor());
|
||||
element->setStrokeWidth(model->getOutlineThickness());
|
||||
}
|
||||
else
|
||||
{
|
||||
element->setNoStroke();
|
||||
}
|
||||
|
||||
if (!transform.isDefaultTransform())
|
||||
{
|
||||
element->addAttribute(std::move(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<ntk::Rectangle*>(model->getGeometry());
|
||||
|
||||
auto rect = std::make_unique<SvgRectangle>();
|
||||
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<Circle*>(model->getGeometry());
|
||||
|
||||
auto is_ellipse = model_circle->getMinorRadius() != model_circle->getRadius();
|
||||
auto circle = std::make_unique<SvgCircle>(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<GeometryPath*>(model->getGeometry());
|
||||
|
||||
auto path_string = model_path->getAsPostScript();
|
||||
|
||||
auto svg_path = std::make_unique<SvgPath>();
|
||||
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<SvgTextElement>();
|
||||
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->getFillColor());
|
||||
auto opacity = static_cast<float>(text->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<XmlAttribute> SvgPainter::toTransform(const Transform& transform) const
|
||||
{
|
||||
auto svg_transform = std::make_unique<XmlAttribute>("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 std::move(svg_transform);
|
||||
}
|
36
src/rendering/visual_elements/svg/SvgPainter.h
Normal file
36
src/rendering/visual_elements/svg/SvgPainter.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
class SvgDocument;
|
||||
class SvgShapeElement;
|
||||
class XmlAttribute;
|
||||
|
||||
class Scene;
|
||||
class SceneModel;
|
||||
class SceneText;
|
||||
|
||||
class Transform;
|
||||
|
||||
class SvgPainter
|
||||
{
|
||||
public:
|
||||
std::unique_ptr<SvgDocument> paint(Scene* scene, double width = 800.0, double height = 800.0) const;
|
||||
|
||||
private:
|
||||
void paintMesh(SvgDocument* document, SceneModel* model, bool showOutline = false) const;
|
||||
|
||||
void paintPrimitive(SvgDocument* document, SceneModel* model) const;
|
||||
|
||||
void paintRect(SvgDocument* document, SceneModel* model) const;
|
||||
|
||||
void paintCircle(SvgDocument* document, SceneModel* model) const;
|
||||
|
||||
void paintPath(SvgDocument* document, SceneModel* model) const;
|
||||
|
||||
void paintText(SvgDocument* document, SceneText* model) const;
|
||||
|
||||
void setStyle(SceneModel* model, SvgShapeElement* element) const;
|
||||
|
||||
std::unique_ptr<XmlAttribute> toTransform(const Transform& transform) const;
|
||||
};
|
|
@ -58,6 +58,10 @@ void SvgReader::onChild(XmlElement* element, XmlElement* svg_parent) const
|
|||
{
|
||||
new_svg = std::make_unique<SvgRectangle>();
|
||||
}
|
||||
else if (element->getTagName() == "path")
|
||||
{
|
||||
new_svg = std::make_unique<SvgPath>();
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
|
|
|
@ -7,7 +7,13 @@
|
|||
SvgTextElement::SvgTextElement()
|
||||
:SvgElement("text")
|
||||
{
|
||||
auto baseline = std::make_unique<XmlAttribute>("dominant-baseline");
|
||||
baseline->setValue("middle");
|
||||
addAttribute(std::move(baseline));
|
||||
|
||||
auto anchor = std::make_unique<XmlAttribute>("text-anchor");
|
||||
anchor->setValue("middle");
|
||||
addAttribute(std::move(anchor));
|
||||
}
|
||||
|
||||
void SvgTextElement::setFillOpacity(float opacity)
|
||||
|
|
|
@ -181,6 +181,16 @@ SvgPath::SvgPath()
|
|||
|
||||
}
|
||||
|
||||
std::string SvgPath::getPath() const
|
||||
{
|
||||
std::string d;
|
||||
if (auto attr = getAttribute("d"); attr)
|
||||
{
|
||||
d = attr->getValue();
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
void SvgPath::setPath(const std::string& mPath)
|
||||
{
|
||||
auto path = std::make_unique<XmlAttribute>("d");
|
||||
|
|
|
@ -68,6 +68,8 @@ class SvgPath : public SvgShapeElement
|
|||
public:
|
||||
SvgPath();
|
||||
|
||||
std::string getPath() const;
|
||||
|
||||
void setPath(const std::string& mPath);
|
||||
void setFillRule(const std::string& fillRule);
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue