Add path rendering.
This commit is contained in:
parent
f2ab532005
commit
97afa782a0
39 changed files with 1148 additions and 131 deletions
|
@ -14,6 +14,7 @@ list(APPEND HEADERS
|
||||||
path/Line.h
|
path/Line.h
|
||||||
path/LineSegment.h
|
path/LineSegment.h
|
||||||
path/Path.h
|
path/Path.h
|
||||||
|
path/PathPostScriptConverter.h
|
||||||
path/PathElement.h
|
path/PathElement.h
|
||||||
points/Point.h
|
points/Point.h
|
||||||
points/PointCollection.h
|
points/PointCollection.h
|
||||||
|
@ -34,6 +35,7 @@ list(APPEND SOURCES
|
||||||
path/Line.cpp
|
path/Line.cpp
|
||||||
path/LineSegment.cpp
|
path/LineSegment.cpp
|
||||||
path/Path.cpp
|
path/Path.cpp
|
||||||
|
path/PathPostScriptConverter.cpp
|
||||||
path/PathElement.cpp
|
path/PathElement.cpp
|
||||||
points/Point.cpp
|
points/Point.cpp
|
||||||
points/PointCollection.cpp
|
points/PointCollection.cpp
|
||||||
|
|
|
@ -7,6 +7,81 @@ Line::Line(const Point& start, const PointCollection& points)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Line::Line(const Point& start, InputBufferType bufferType, const std::vector<double>& buffer)
|
||||||
|
: mStartPoint(start)
|
||||||
|
{
|
||||||
|
if (bufferType == InputBufferType::XY_REL)
|
||||||
|
{
|
||||||
|
for (std::size_t idx = 0; idx < buffer.size(); idx += 2)
|
||||||
|
{
|
||||||
|
const auto x = buffer[idx];
|
||||||
|
const auto y = buffer[idx + 1];
|
||||||
|
mPoints.addPoint(Point(mStartPoint.getX() + x, mStartPoint.getY() + y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (bufferType == InputBufferType::XY_ABS)
|
||||||
|
{
|
||||||
|
for (std::size_t idx = 0; idx < buffer.size(); idx += 2)
|
||||||
|
{
|
||||||
|
const auto x = buffer[idx];
|
||||||
|
const auto y = buffer[idx + 1];
|
||||||
|
mPoints.addPoint(Point(x, y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (bufferType == InputBufferType::HORIZONTAL_REL)
|
||||||
|
{
|
||||||
|
for (std::size_t idx = 0; idx < buffer.size(); idx ++)
|
||||||
|
{
|
||||||
|
const auto x = buffer[idx];
|
||||||
|
mPoints.addPoint(Point(mStartPoint.getX() + x, mStartPoint.getY()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (bufferType == InputBufferType::HORIZONTAL_ABS)
|
||||||
|
{
|
||||||
|
for (std::size_t idx = 0; idx < buffer.size(); idx++)
|
||||||
|
{
|
||||||
|
const auto x = buffer[idx];
|
||||||
|
mPoints.addPoint(Point(x, mStartPoint.getY()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (bufferType == InputBufferType::VERTICAL_REL)
|
||||||
|
{
|
||||||
|
for (std::size_t idx = 0; idx < buffer.size(); idx++)
|
||||||
|
{
|
||||||
|
const auto y = buffer[idx];
|
||||||
|
mPoints.addPoint(Point(mStartPoint.getX(), mStartPoint.getY() + y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (bufferType == InputBufferType::VERTICAL_ABS)
|
||||||
|
{
|
||||||
|
for (std::size_t idx = 0; idx < buffer.size(); idx++)
|
||||||
|
{
|
||||||
|
const auto y = buffer[idx];
|
||||||
|
mPoints.addPoint(Point(mStartPoint.getX(), y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Line::toPostScriptString() const
|
||||||
|
{
|
||||||
|
std::string path = "L ";
|
||||||
|
for (const auto& point : mPoints.getPoints())
|
||||||
|
{
|
||||||
|
path += std::to_string(point.getX()) + " " + std::to_string(point.getY()) + " ";
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
Point Line::getFirstPoint() const
|
||||||
|
{
|
||||||
|
return getLocation();
|
||||||
|
}
|
||||||
|
|
||||||
|
Point Line::getEndPoint() const
|
||||||
|
{
|
||||||
|
return mPoints.getEndPoint();
|
||||||
|
}
|
||||||
|
|
||||||
const PointCollection& Line::getPoints() const
|
const PointCollection& Line::getPoints() const
|
||||||
{
|
{
|
||||||
return mPoints;
|
return mPoints;
|
||||||
|
|
|
@ -4,12 +4,25 @@
|
||||||
#include "PointCollection.h"
|
#include "PointCollection.h"
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
class Line : public PathElement
|
class Line : public PathElement
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
enum class InputBufferType
|
||||||
|
{
|
||||||
|
HORIZONTAL_REL,
|
||||||
|
HORIZONTAL_ABS,
|
||||||
|
VERTICAL_REL,
|
||||||
|
VERTICAL_ABS,
|
||||||
|
XY_REL,
|
||||||
|
XY_ABS
|
||||||
|
};
|
||||||
|
|
||||||
Line(const Point& start, const PointCollection& points);
|
Line(const Point& start, const PointCollection& points);
|
||||||
|
|
||||||
|
Line(const Point& start, InputBufferType bufferType, const std::vector<double>& buffer);
|
||||||
|
|
||||||
const PointCollection& getPoints() const;
|
const PointCollection& getPoints() const;
|
||||||
|
|
||||||
Line::Type getType() const override;
|
Line::Type getType() const override;
|
||||||
|
@ -18,8 +31,14 @@ public:
|
||||||
|
|
||||||
Bounds getBounds() const override;
|
Bounds getBounds() const override;
|
||||||
|
|
||||||
|
Point getFirstPoint() const override;
|
||||||
|
|
||||||
|
Point getEndPoint() const override;
|
||||||
|
|
||||||
void sample(SparseGrid<bool>* grid) const override {};
|
void sample(SparseGrid<bool>* grid) const override {};
|
||||||
|
|
||||||
|
std::string toPostScriptString() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Point mStartPoint;
|
Point mStartPoint;
|
||||||
PointCollection mPoints;
|
PointCollection mPoints;
|
||||||
|
|
|
@ -32,6 +32,11 @@ void LineSegment::sample(SparseGrid<bool>* grid) const
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string LineSegment::toPostScriptString() const
|
||||||
|
{
|
||||||
|
return "L " + std::to_string(mP1.getX()) + " " + std::to_string(mP1.getY());
|
||||||
|
}
|
||||||
|
|
||||||
Bounds LineSegment::getBounds() const
|
Bounds LineSegment::getBounds() const
|
||||||
{
|
{
|
||||||
const auto minX = std::min(mP0.getX(), mP1.getX());
|
const auto minX = std::min(mP0.getX(), mP1.getX());
|
||||||
|
@ -47,3 +52,18 @@ const Point& LineSegment::getLocation() const
|
||||||
{
|
{
|
||||||
return mP0;
|
return mP0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Point LineSegment::getFirstPoint() const
|
||||||
|
{
|
||||||
|
return getPoint0();
|
||||||
|
}
|
||||||
|
|
||||||
|
Point LineSegment::getEndPoint() const
|
||||||
|
{
|
||||||
|
return getPoint1();
|
||||||
|
}
|
||||||
|
|
||||||
|
LineSegment::Type LineSegment::getType() const
|
||||||
|
{
|
||||||
|
return AbstractGeometricItem::Type::LINE_SEGMENT;
|
||||||
|
}
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
#include "PathElement.h"
|
#include "PathElement.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
class LineSegment : public PathElement
|
class LineSegment : public PathElement
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -21,6 +23,14 @@ public:
|
||||||
|
|
||||||
const Point& getLocation() const override;
|
const Point& getLocation() const override;
|
||||||
|
|
||||||
|
Point getFirstPoint() const override;
|
||||||
|
|
||||||
|
Point getEndPoint() const override;
|
||||||
|
|
||||||
|
std::string toPostScriptString() const override;
|
||||||
|
|
||||||
|
Type getType() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Point mP0;
|
Point mP0;
|
||||||
Point mP1;
|
Point mP1;
|
||||||
|
|
|
@ -1,11 +1,55 @@
|
||||||
#include "Path.h"
|
#include "Path.h"
|
||||||
|
|
||||||
Path::~Path()
|
#include "StringUtils.h"
|
||||||
|
#include "FileLogger.h"
|
||||||
|
|
||||||
|
#include "Line.h"
|
||||||
|
#include "LineSegment.h"
|
||||||
|
#include "PathPostScriptConverter.h"
|
||||||
|
|
||||||
|
GeometryPath::~GeometryPath()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::vector<PathElementPtr>& Path::getElements() const
|
const std::vector<GeometryPathFeaturePtr>& GeometryPath::getFeatures() const
|
||||||
{
|
{
|
||||||
return mElements;
|
return mFeatures;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GeometryPath::buildFromPostscript(const std::string& psString)
|
||||||
|
{
|
||||||
|
PathPostScriptConverter converter;
|
||||||
|
converter.fromPostScript(this, psString);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GeometryPath::addFeature(GeometryPathFeaturePtr feature)
|
||||||
|
{
|
||||||
|
mFeatures.push_back(std::move(feature));
|
||||||
|
}
|
||||||
|
|
||||||
|
const Point& GeometryPath::getLocation() const
|
||||||
|
{
|
||||||
|
return mLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bounds GeometryPath::getBounds() const
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
GeometryPath::Type GeometryPath::getType() const
|
||||||
|
{
|
||||||
|
return GeometryPath::Type::PATH;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GeometryPath::sample(SparseGrid<bool>* grid) const
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GeometryPath::getAsPostScript() const
|
||||||
|
{
|
||||||
|
PathPostScriptConverter converter;
|
||||||
|
return converter.toPostScript(this);
|
||||||
}
|
}
|
|
@ -4,15 +4,60 @@
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
using PathElementPtr = std::unique_ptr<PathElement>;
|
using PathElementPtr = std::unique_ptr<PathElement>;
|
||||||
|
|
||||||
class Path : public AbstractGeometricItem
|
class GeometryPathFeature
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
~Path();
|
void addElement(PathElementPtr element)
|
||||||
const std::vector<PathElementPtr>& getElements() const;
|
{
|
||||||
|
if (mElements.empty())
|
||||||
|
{
|
||||||
|
mLocation = element->getFirstPoint();
|
||||||
|
}
|
||||||
|
mElements.push_back(std::move(element));
|
||||||
|
}
|
||||||
|
|
||||||
|
const Point& getLocation() const
|
||||||
|
{
|
||||||
|
return mLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<PathElementPtr>& getElements() const
|
||||||
|
{
|
||||||
|
return mElements;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
Point mLocation;
|
||||||
std::vector<PathElementPtr> mElements;
|
std::vector<PathElementPtr> mElements;
|
||||||
};
|
};
|
||||||
|
using GeometryPathFeaturePtr = std::unique_ptr<GeometryPathFeature>;
|
||||||
|
|
||||||
|
class GeometryPath : public AbstractGeometricItem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
~GeometryPath();
|
||||||
|
|
||||||
|
void addFeature(GeometryPathFeaturePtr feature);
|
||||||
|
|
||||||
|
void buildFromPostscript(const std::string& psString);
|
||||||
|
|
||||||
|
std::string getAsPostScript() const;
|
||||||
|
|
||||||
|
const Point& getLocation() const override;
|
||||||
|
|
||||||
|
Bounds getBounds() const override;
|
||||||
|
|
||||||
|
Type getType() const override;
|
||||||
|
|
||||||
|
const std::vector<GeometryPathFeaturePtr>& getFeatures() const;
|
||||||
|
|
||||||
|
void sample(SparseGrid<bool>* grid) const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Point mLocation;
|
||||||
|
std::vector<GeometryPathFeaturePtr> mFeatures;
|
||||||
|
};
|
|
@ -2,8 +2,16 @@
|
||||||
|
|
||||||
#include "AbstractGeometricItem.h"
|
#include "AbstractGeometricItem.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
class PathElement : public AbstractGeometricItem
|
class PathElement : public AbstractGeometricItem
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
~PathElement();
|
~PathElement();
|
||||||
|
|
||||||
|
virtual Point getFirstPoint() const = 0;
|
||||||
|
|
||||||
|
virtual Point getEndPoint() const = 0;
|
||||||
|
|
||||||
|
virtual std::string toPostScriptString() const = 0;
|
||||||
};
|
};
|
235
src/base/geometry/path/PathPostScriptConverter.cpp
Normal file
235
src/base/geometry/path/PathPostScriptConverter.cpp
Normal file
|
@ -0,0 +1,235 @@
|
||||||
|
#include "PathPostScriptConverter.h"
|
||||||
|
|
||||||
|
#include "Path.h"
|
||||||
|
|
||||||
|
#include "StringUtils.h"
|
||||||
|
#include "FileLogger.h"
|
||||||
|
|
||||||
|
#include "Line.h"
|
||||||
|
#include "LineSegment.h"
|
||||||
|
|
||||||
|
void PathPostScriptConverter::fromPostScript(GeometryPath* targetPath, const std::string& postScriptPath)
|
||||||
|
{
|
||||||
|
mCurrentPoint = Point();
|
||||||
|
|
||||||
|
for (auto c : postScriptPath)
|
||||||
|
{
|
||||||
|
if (c == 'M' || c == 'm')
|
||||||
|
{
|
||||||
|
mLineState = LineState::IN_FIRST_POINT;
|
||||||
|
mPositionState = getPositionState(c);
|
||||||
|
|
||||||
|
mWorkingFeature = std::make_unique<GeometryPathFeature>();
|
||||||
|
}
|
||||||
|
else if (c == 'H' || c == 'h')
|
||||||
|
{
|
||||||
|
onNewElement(c);
|
||||||
|
mLineState = LineState::IN_HORIZONTAL;
|
||||||
|
}
|
||||||
|
else if (c == 'V' || c == 'v')
|
||||||
|
{
|
||||||
|
onNewElement(c);
|
||||||
|
mLineState = LineState::IN_VERTICAL;
|
||||||
|
}
|
||||||
|
else if (c == 'L' || c == 'l')
|
||||||
|
{
|
||||||
|
onNewElement(c);
|
||||||
|
mLineState = LineState::IN_LINE;
|
||||||
|
}
|
||||||
|
else if (c == 'C' || c == 'c')
|
||||||
|
{
|
||||||
|
onNewElement(c);
|
||||||
|
mLineState = LineState::IN_CUBIC_BEZIER;
|
||||||
|
}
|
||||||
|
else if (c == 'Q' || c == 'q')
|
||||||
|
{
|
||||||
|
onNewElement(c);
|
||||||
|
mLineState = LineState::IN_QUADRATIC_BEZIER;
|
||||||
|
}
|
||||||
|
else if (c == 'A' || c == 'a')
|
||||||
|
{
|
||||||
|
onNewElement(c);
|
||||||
|
mLineState = LineState::IN_ARC;
|
||||||
|
}
|
||||||
|
else if (c == 'Z' || c == 'z')
|
||||||
|
{
|
||||||
|
onNewElement(c);
|
||||||
|
|
||||||
|
mLineState = LineState::START;
|
||||||
|
targetPath->addFeature(std::move(mWorkingFeature));
|
||||||
|
mWorkingFeature = nullptr;
|
||||||
|
}
|
||||||
|
else if (std::isblank(c))
|
||||||
|
{
|
||||||
|
onNonNumeric();
|
||||||
|
}
|
||||||
|
else if (std::isalpha(c))
|
||||||
|
{
|
||||||
|
onNewElement(c);
|
||||||
|
mLineState = LineState::IN_UNSUPPORTED;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mBuffer.push_back(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PathPostScriptConverter::onNewElement(char c)
|
||||||
|
{
|
||||||
|
onNonNumeric();
|
||||||
|
|
||||||
|
onElementEnd();
|
||||||
|
|
||||||
|
mPositionState = getPositionState(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PathPostScriptConverter::onNonNumeric()
|
||||||
|
{
|
||||||
|
if (!mBuffer.empty())
|
||||||
|
{
|
||||||
|
mPointBuffer.push_back(std::stod(mBuffer));
|
||||||
|
mBuffer.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PathPostScriptConverter::PositionState PathPostScriptConverter::getPositionState(char c) const
|
||||||
|
{
|
||||||
|
return std::isupper(c) ? PositionState::ABSOLUTE : PositionState::RELATIVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PathPostScriptConverter::onElementEnd()
|
||||||
|
{
|
||||||
|
if (mPointBuffer.empty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PathElementPtr element;
|
||||||
|
if (mLineState == LineState::IN_HORIZONTAL)
|
||||||
|
{
|
||||||
|
element = onHorizontalLineTo();
|
||||||
|
}
|
||||||
|
else if (mLineState == LineState::IN_VERTICAL)
|
||||||
|
{
|
||||||
|
element = onVerticalLineTo();
|
||||||
|
}
|
||||||
|
else if (mLineState == LineState::IN_LINE)
|
||||||
|
{
|
||||||
|
element = onLineTo();
|
||||||
|
}
|
||||||
|
else if (mLineState == LineState::IN_FIRST_POINT)
|
||||||
|
{
|
||||||
|
onMoveTo();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (element)
|
||||||
|
{
|
||||||
|
mCurrentPoint = element->getEndPoint();
|
||||||
|
mWorkingFeature->addElement(std::move(element));
|
||||||
|
}
|
||||||
|
|
||||||
|
mPointBuffer.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PathPostScriptConverter::onMoveTo()
|
||||||
|
{
|
||||||
|
if (mPointBuffer.size() != 2)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (mPositionState == PositionState::RELATIVE)
|
||||||
|
{
|
||||||
|
mCurrentPoint = Point(mCurrentPoint.getX() + mPointBuffer[0], mCurrentPoint.getY() + mPointBuffer[1]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mCurrentPoint = Point(mPointBuffer[0], mPointBuffer[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PathElementPtr PathPostScriptConverter::onHorizontalLineTo()
|
||||||
|
{
|
||||||
|
PathElementPtr element;
|
||||||
|
if (mPointBuffer.size() == 1)
|
||||||
|
{
|
||||||
|
if (mPositionState == PositionState::RELATIVE)
|
||||||
|
{
|
||||||
|
element = std::make_unique<LineSegment>(mCurrentPoint, Point(mCurrentPoint.getX() + mPointBuffer[0], mCurrentPoint.getY()));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
element = std::make_unique<LineSegment>(mCurrentPoint, Point(mPointBuffer[0], mCurrentPoint.getY()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Line::InputBufferType buffer_type = mPositionState == PositionState::RELATIVE ? Line::InputBufferType::HORIZONTAL_REL : Line::InputBufferType::HORIZONTAL_ABS;
|
||||||
|
element = std::make_unique<Line>(mCurrentPoint, buffer_type, mPointBuffer);
|
||||||
|
}
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
PathElementPtr PathPostScriptConverter::onVerticalLineTo()
|
||||||
|
{
|
||||||
|
PathElementPtr element;
|
||||||
|
if (mPointBuffer.size() == 1)
|
||||||
|
{
|
||||||
|
if (mPositionState == PositionState::RELATIVE)
|
||||||
|
{
|
||||||
|
element = std::make_unique<LineSegment>(mCurrentPoint, Point(mCurrentPoint.getX(), mCurrentPoint.getY() + mPointBuffer[0]));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
element = std::make_unique<LineSegment>(mCurrentPoint, Point(mCurrentPoint.getX(), mPointBuffer[0]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Line::InputBufferType buffer_type = mPositionState == PositionState::RELATIVE ? Line::InputBufferType::VERTICAL_REL : Line::InputBufferType::VERTICAL_ABS;
|
||||||
|
element = std::make_unique<Line>(mCurrentPoint, buffer_type, mPointBuffer);
|
||||||
|
}
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
PathElementPtr PathPostScriptConverter::onLineTo()
|
||||||
|
{
|
||||||
|
PathElementPtr element;
|
||||||
|
if (mPointBuffer.size() == 2)
|
||||||
|
{
|
||||||
|
if (mPositionState == PositionState::RELATIVE)
|
||||||
|
{
|
||||||
|
const auto next_point = Point(mCurrentPoint.getX() + mPointBuffer[0], mCurrentPoint.getY() + mPointBuffer[1]);
|
||||||
|
element = std::make_unique<LineSegment>(mCurrentPoint, next_point);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const auto next_point = Point(mPointBuffer[0], mPointBuffer[1]);
|
||||||
|
element = std::make_unique<LineSegment>(mCurrentPoint, next_point);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Line::InputBufferType buffer_type = mPositionState == PositionState::RELATIVE ? Line::InputBufferType::XY_REL : Line::InputBufferType::XY_ABS;
|
||||||
|
element = std::make_unique<Line>(mCurrentPoint, buffer_type, mPointBuffer);
|
||||||
|
}
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string PathPostScriptConverter::toPostScript(const GeometryPath* targetPath)
|
||||||
|
{
|
||||||
|
std::string path;
|
||||||
|
for (const auto& feature : targetPath->getFeatures())
|
||||||
|
{
|
||||||
|
auto start_loc = feature->getLocation();
|
||||||
|
path += "M " + std::to_string(start_loc.getX()) + " " + std::to_string(start_loc.getY());
|
||||||
|
|
||||||
|
for (const auto& path_element : feature->getElements())
|
||||||
|
{
|
||||||
|
path += " " + path_element->toPostScriptString();
|
||||||
|
}
|
||||||
|
path += "Z ";
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
61
src/base/geometry/path/PathPostScriptConverter.h
Normal file
61
src/base/geometry/path/PathPostScriptConverter.h
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "PathElement.h"
|
||||||
|
#include "Point.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class GeometryPath;
|
||||||
|
using PathElementPtr = std::unique_ptr<PathElement>;
|
||||||
|
|
||||||
|
class GeometryPathFeature;
|
||||||
|
using GeometryPathFeaturePtr = std::unique_ptr<GeometryPathFeature>;
|
||||||
|
|
||||||
|
class PathPostScriptConverter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void fromPostScript(GeometryPath* targetPath, const std::string& postScriptPath);
|
||||||
|
|
||||||
|
std::string toPostScript(const GeometryPath* targetPath);
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum class LineState
|
||||||
|
{
|
||||||
|
START,
|
||||||
|
IN_FIRST_POINT,
|
||||||
|
IN_HORIZONTAL,
|
||||||
|
IN_VERTICAL,
|
||||||
|
IN_LINE,
|
||||||
|
IN_CUBIC_BEZIER,
|
||||||
|
IN_QUADRATIC_BEZIER,
|
||||||
|
IN_ARC,
|
||||||
|
IN_UNSUPPORTED
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class PositionState
|
||||||
|
{
|
||||||
|
ABSOLUTE,
|
||||||
|
RELATIVE
|
||||||
|
};
|
||||||
|
|
||||||
|
PositionState getPositionState(char c) const;
|
||||||
|
|
||||||
|
void onNewElement(char c);
|
||||||
|
void onNonNumeric();
|
||||||
|
void onElementEnd();
|
||||||
|
|
||||||
|
void onMoveTo();
|
||||||
|
PathElementPtr onHorizontalLineTo();
|
||||||
|
PathElementPtr onVerticalLineTo();
|
||||||
|
PathElementPtr onLineTo();
|
||||||
|
|
||||||
|
LineState mLineState{ LineState::START };
|
||||||
|
PositionState mPositionState{ PositionState::ABSOLUTE };
|
||||||
|
std::string mBuffer;
|
||||||
|
std::vector<double> mPointBuffer;
|
||||||
|
|
||||||
|
GeometryPathFeaturePtr mWorkingFeature;
|
||||||
|
Point mCurrentPoint;
|
||||||
|
};
|
|
@ -14,6 +14,11 @@ void PointCollection::apply(const Transform& transform)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PointCollection::addPoint(const Point& point)
|
||||||
|
{
|
||||||
|
mPoints.push_back(point);
|
||||||
|
}
|
||||||
|
|
||||||
Bounds PointCollection::getBounds() const
|
Bounds PointCollection::getBounds() const
|
||||||
{
|
{
|
||||||
Bounds bounds{0.0, 0.0, 0.0, 0.0};
|
Bounds bounds{0.0, 0.0, 0.0, 0.0};
|
||||||
|
@ -30,3 +35,20 @@ Bounds PointCollection::getBounds() const
|
||||||
}
|
}
|
||||||
return bounds;
|
return bounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Point PointCollection::getEndPoint() const
|
||||||
|
{
|
||||||
|
if (mPoints.empty())
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return mPoints[mPoints.size() - 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<Point>& PointCollection::getPoints() const
|
||||||
|
{
|
||||||
|
return mPoints;
|
||||||
|
}
|
|
@ -9,10 +9,16 @@ class PointCollection
|
||||||
public:
|
public:
|
||||||
PointCollection(const std::vector<Point> points = {});
|
PointCollection(const std::vector<Point> points = {});
|
||||||
|
|
||||||
|
void addPoint(const Point& point);
|
||||||
|
|
||||||
void apply(const Transform& transform);
|
void apply(const Transform& transform);
|
||||||
|
|
||||||
Bounds getBounds() const;
|
Bounds getBounds() const;
|
||||||
|
|
||||||
|
Point getEndPoint() const;
|
||||||
|
|
||||||
|
const std::vector<Point>& getPoints() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<Point> mPoints;
|
std::vector<Point> mPoints;
|
||||||
};
|
};
|
||||||
|
|
|
@ -15,7 +15,6 @@ list(APPEND publishing_HEADERS
|
||||||
latex/LatexDocument.h
|
latex/LatexDocument.h
|
||||||
latex/LatexMathExpression.h
|
latex/LatexMathExpression.h
|
||||||
latex/LatexSymbols.h
|
latex/LatexSymbols.h
|
||||||
svg/SvgPainter.h
|
|
||||||
DocumentConverter.h
|
DocumentConverter.h
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -37,7 +36,6 @@ list(APPEND publishing_LIB_INCLUDES
|
||||||
plotting/PlotNode.cpp
|
plotting/PlotNode.cpp
|
||||||
plotting/EquationNode.cpp
|
plotting/EquationNode.cpp
|
||||||
DocumentConverter.cpp
|
DocumentConverter.cpp
|
||||||
svg/SvgPainter.cpp
|
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(publishing SHARED ${publishing_LIB_INCLUDES} ${publishing_INCLUDES} ${publishing_HEADERS})
|
add_library(publishing SHARED ${publishing_LIB_INCLUDES} ${publishing_INCLUDES} ${publishing_HEADERS})
|
||||||
|
@ -47,7 +45,6 @@ target_include_directories(publishing PUBLIC
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/pdf
|
${CMAKE_CURRENT_SOURCE_DIR}/pdf
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/plotting
|
${CMAKE_CURRENT_SOURCE_DIR}/plotting
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/latex
|
${CMAKE_CURRENT_SOURCE_DIR}/latex
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/svg
|
|
||||||
)
|
)
|
||||||
set_target_properties( publishing PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON )
|
set_target_properties( publishing PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON )
|
||||||
target_link_libraries( publishing PUBLIC core web graphics visual_elements)
|
target_link_libraries( publishing PUBLIC core web graphics visual_elements)
|
||||||
|
|
|
@ -1,9 +1,17 @@
|
||||||
#include "DirectX2dPainter.h"
|
#include "DirectX2dPainter.h"
|
||||||
|
|
||||||
#include "DirectX2dInterface.h"
|
#include "DirectX2dInterface.h"
|
||||||
|
|
||||||
#include "AbstractGeometricItem.h"
|
#include "AbstractGeometricItem.h"
|
||||||
#include "Rectangle.h"
|
#include "Rectangle.h"
|
||||||
#include "Circle.h"
|
#include "Circle.h"
|
||||||
|
#include "Path.h"
|
||||||
|
|
||||||
|
#include "LineSegment.h"
|
||||||
|
#include "Line.h"
|
||||||
|
#include "Path.h"
|
||||||
|
|
||||||
|
#include "FileLogger.h"
|
||||||
|
|
||||||
#include "SceneModel.h"
|
#include "SceneModel.h"
|
||||||
|
|
||||||
|
@ -34,6 +42,22 @@ void DirectX2dPainter::paint(SceneModel* model)
|
||||||
|
|
||||||
if (model->getGeometry()->getType() == AbstractGeometricItem::Type::RECTANGLE)
|
if (model->getGeometry()->getType() == AbstractGeometricItem::Type::RECTANGLE)
|
||||||
{
|
{
|
||||||
|
paintRect(model);
|
||||||
|
}
|
||||||
|
else if (model->getGeometry()->getType() == AbstractGeometricItem::Type::CIRCLE)
|
||||||
|
{
|
||||||
|
paintCircle(model);
|
||||||
|
}
|
||||||
|
else if (model->getGeometry()->getType() == AbstractGeometricItem::Type::PATH)
|
||||||
|
{
|
||||||
|
paintPath(model);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DirectX2dPainter::paintRect(SceneModel* model)
|
||||||
|
{
|
||||||
|
auto rt = mD2dInterface->getRenderTarget();
|
||||||
|
|
||||||
auto rect = dynamic_cast<ntk::Rectangle*>(model->getGeometry());
|
auto rect = dynamic_cast<ntk::Rectangle*>(model->getGeometry());
|
||||||
|
|
||||||
const auto loc = model->getTransform().getLocation();
|
const auto loc = model->getTransform().getLocation();
|
||||||
|
@ -41,7 +65,7 @@ void DirectX2dPainter::paint(SceneModel* model)
|
||||||
const auto scale_y = model->getTransform().getScaleY();
|
const auto scale_y = model->getTransform().getScaleY();
|
||||||
|
|
||||||
const auto min_x = static_cast<float>(loc.getX());
|
const auto min_x = static_cast<float>(loc.getX());
|
||||||
const auto max_x = static_cast<float>(loc.getX() + rect->getWidth()* scale_x);
|
const auto max_x = static_cast<float>(loc.getX() + rect->getWidth() * scale_x);
|
||||||
|
|
||||||
const auto min_y = static_cast<float>(loc.getY());
|
const auto min_y = static_cast<float>(loc.getY());
|
||||||
const auto max_y = static_cast<float>(loc.getY() + rect->getHeight() * scale_y);
|
const auto max_y = static_cast<float>(loc.getY() + rect->getHeight() * scale_y);
|
||||||
|
@ -74,9 +98,12 @@ void DirectX2dPainter::paint(SceneModel* model)
|
||||||
rt->DrawRoundedRectangle(rounded_rect, mSolidBrush.Get(), 1.0f);
|
rt->DrawRoundedRectangle(rounded_rect, mSolidBrush.Get(), 1.0f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (model->getGeometry()->getType() == AbstractGeometricItem::Type::CIRCLE)
|
|
||||||
{
|
void DirectX2dPainter::paintCircle(SceneModel* model)
|
||||||
|
{
|
||||||
|
auto rt = mD2dInterface->getRenderTarget();
|
||||||
|
|
||||||
const auto loc = model->getTransform().getLocation();
|
const auto loc = model->getTransform().getLocation();
|
||||||
const auto scale_x = model->getTransform().getScaleX();
|
const auto scale_x = model->getTransform().getScaleX();
|
||||||
const auto scale_y = model->getTransform().getScaleY();
|
const auto scale_y = model->getTransform().getScaleY();
|
||||||
|
@ -88,14 +115,75 @@ void DirectX2dPainter::paint(SceneModel* model)
|
||||||
D2D1_POINT_2F d2d_centre{ static_cast<float>(loc.getX()), static_cast<float>(loc.getY()) };
|
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) };
|
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())
|
if (model->hasOutlineColor())
|
||||||
{
|
{
|
||||||
mSolidBrush->SetColor(toD2dColor(model->getOutlineColor()));
|
mSolidBrush->SetColor(toD2dColor(model->getOutlineColor()));
|
||||||
rt->DrawEllipse(ellipse, mSolidBrush.Get(), 1.0f);
|
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)
|
void DirectX2dPainter::setD2dInterface(DirectX2dInterface* d2dIterface)
|
||||||
{
|
{
|
||||||
mD2dInterface = d2dIterface;
|
mD2dInterface = d2dIterface;
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include <wrl.h>
|
#include <wrl.h>
|
||||||
|
|
||||||
class SceneModel;
|
class SceneModel;
|
||||||
|
class Point;
|
||||||
class DirectX2dInterface;
|
class DirectX2dInterface;
|
||||||
|
|
||||||
struct ID2D1SolidColorBrush;
|
struct ID2D1SolidColorBrush;
|
||||||
|
@ -13,6 +14,7 @@ namespace D2D1
|
||||||
{
|
{
|
||||||
class ColorF;
|
class ColorF;
|
||||||
}
|
}
|
||||||
|
struct D2D_POINT_2F;
|
||||||
|
|
||||||
class DirectX2dPainter
|
class DirectX2dPainter
|
||||||
{
|
{
|
||||||
|
@ -30,6 +32,14 @@ public:
|
||||||
private:
|
private:
|
||||||
static D2D1::ColorF toD2dColor(const Color& color);
|
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;
|
Microsoft::WRL::ComPtr<ID2D1SolidColorBrush> mSolidBrush;
|
||||||
|
|
||||||
DirectX2dInterface* mD2dInterface{ nullptr };
|
DirectX2dInterface* mD2dInterface{ nullptr };
|
||||||
|
|
|
@ -21,6 +21,8 @@ list(APPEND visual_elements_LIB_INCLUDES
|
||||||
svg/SvgDocument.h
|
svg/SvgDocument.h
|
||||||
svg/SvgWriter.h
|
svg/SvgWriter.h
|
||||||
svg/SvgReader.h
|
svg/SvgReader.h
|
||||||
|
svg/SvgPainter.h
|
||||||
|
svg/SvgPainter.cpp
|
||||||
svg/SvgShapeElement.h
|
svg/SvgShapeElement.h
|
||||||
svg/SvgElement.h
|
svg/SvgElement.h
|
||||||
svg/SvgTextElement.h
|
svg/SvgTextElement.h
|
||||||
|
@ -34,6 +36,8 @@ list(APPEND visual_elements_LIB_INCLUDES
|
||||||
svg/elements/SvgShapeElements.cpp
|
svg/elements/SvgShapeElements.cpp
|
||||||
nodes/MaterialNode.h
|
nodes/MaterialNode.h
|
||||||
nodes/MaterialNode.cpp
|
nodes/MaterialNode.cpp
|
||||||
|
nodes/PathNode.h
|
||||||
|
nodes/PathNode.cpp
|
||||||
nodes/ImageNode.h
|
nodes/ImageNode.h
|
||||||
nodes/ImageNode.cpp
|
nodes/ImageNode.cpp
|
||||||
nodes/MeshNode.h
|
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 "SvgNode.h"
|
||||||
|
|
||||||
#include "CircleNode.h"
|
#include "CircleNode.h"
|
||||||
|
#include "PathNode.h"
|
||||||
|
|
||||||
|
#include "SvgElement.h"
|
||||||
#include "SvgShapeElements.h"
|
#include "SvgShapeElements.h"
|
||||||
|
|
||||||
SvgNode::SvgNode(const Point& location)
|
SvgNode::SvgNode(const Point& location)
|
||||||
|
@ -39,26 +41,11 @@ void SvgNode::createOrUpdateGeometry(SceneInfo* sceneInfo)
|
||||||
|
|
||||||
if (svg_element->getTagName() == "circle")
|
if (svg_element->getTagName() == "circle")
|
||||||
{
|
{
|
||||||
auto svg_circle = dynamic_cast<SvgCircle*>(svg_element.get());
|
onCircle(svg_element.get(), node);
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
else if (svg_element->getTagName() == "path")
|
||||||
if (svg_element->hasAttribute("transform"))
|
|
||||||
{
|
{
|
||||||
const auto transform = svg_circle->getTransform();
|
onPath(svg_element.get(), node);
|
||||||
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)
|
if (!node)
|
||||||
|
@ -71,7 +58,6 @@ void SvgNode::createOrUpdateGeometry(SceneInfo* sceneInfo)
|
||||||
|
|
||||||
addChild(raw_node);
|
addChild(raw_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SvgNode::update(SceneInfo* sceneInfo)
|
void SvgNode::update(SceneInfo* sceneInfo)
|
||||||
|
@ -88,3 +74,37 @@ void SvgNode::update(SceneInfo* sceneInfo)
|
||||||
mTransformIsDirty = false;
|
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 createOrUpdateGeometry(SceneInfo* sceneInfo);
|
||||||
void updateTransform();
|
void updateTransform();
|
||||||
|
|
||||||
|
void onCircle(XmlElement* element, std::unique_ptr<AbstractVisualNode>& node);
|
||||||
|
void onPath(XmlElement* element, std::unique_ptr<AbstractVisualNode>& node);
|
||||||
|
|
||||||
bool mContentDirty{ true };
|
bool mContentDirty{ true };
|
||||||
|
|
||||||
std::vector<std::unique_ptr<AbstractVisualNode> > mManagedChildren;
|
std::vector<std::unique_ptr<AbstractVisualNode> > mManagedChildren;
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
|
|
||||||
#include "Circle.h"
|
#include "Circle.h"
|
||||||
#include "Rectangle.h"
|
#include "Rectangle.h"
|
||||||
|
#include "Path.h"
|
||||||
|
|
||||||
#include "SvgShapeElements.h"
|
#include "SvgShapeElements.h"
|
||||||
#include "SvgTextElement.h"
|
#include "SvgTextElement.h"
|
||||||
|
@ -127,6 +128,20 @@ void SvgPainter::paintPrimitive(SvgDocument* document, SceneModel* model) const
|
||||||
{
|
{
|
||||||
if (model->getGeometry()->getType() == AbstractGeometricItem::Type::RECTANGLE)
|
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 model_rect = dynamic_cast<ntk::Rectangle*>(model->getGeometry());
|
||||||
|
|
||||||
auto rect = std::make_unique<SvgRectangle>();
|
auto rect = std::make_unique<SvgRectangle>();
|
||||||
|
@ -140,9 +155,10 @@ void SvgPainter::paintPrimitive(SvgDocument* document, SceneModel* model) const
|
||||||
|
|
||||||
setStyle(model, rect.get());
|
setStyle(model, rect.get());
|
||||||
document->getRoot()->addChild(std::move(rect));
|
document->getRoot()->addChild(std::move(rect));
|
||||||
}
|
}
|
||||||
else if (model->getGeometry()->getType() == AbstractGeometricItem::Type::CIRCLE)
|
|
||||||
{
|
void SvgPainter::paintCircle(SvgDocument* document, SceneModel* model) const
|
||||||
|
{
|
||||||
auto model_circle = dynamic_cast<Circle*>(model->getGeometry());
|
auto model_circle = dynamic_cast<Circle*>(model->getGeometry());
|
||||||
|
|
||||||
auto is_ellipse = model_circle->getMinorRadius() != model_circle->getRadius();
|
auto is_ellipse = model_circle->getMinorRadius() != model_circle->getRadius();
|
||||||
|
@ -155,7 +171,19 @@ void SvgPainter::paintPrimitive(SvgDocument* document, SceneModel* model) const
|
||||||
|
|
||||||
setStyle(model, circle.get());
|
setStyle(model, circle.get());
|
||||||
document->getRoot()->addChild(std::move(circle));
|
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
|
void SvgPainter::paintText(SvgDocument* document, SceneText* text) const
|
||||||
|
@ -163,7 +191,8 @@ void SvgPainter::paintText(SvgDocument* document, SceneText* text) const
|
||||||
auto svg_text = std::make_unique<SvgTextElement>();
|
auto svg_text = std::make_unique<SvgTextElement>();
|
||||||
svg_text->setContent(text->getTextData().mContent);
|
svg_text->setContent(text->getTextData().mContent);
|
||||||
auto loc = text->getTransform().getLocation();
|
auto loc = text->getTransform().getLocation();
|
||||||
loc.move(0.0, text->getTextHeight());
|
|
||||||
|
loc.move(text->getTextWidth() / 2.0, text->getTextHeight()/2.0);
|
||||||
svg_text->setLocation(loc);
|
svg_text->setLocation(loc);
|
||||||
|
|
||||||
svg_text->setFontFamily(text->getTextData().mFont.getFaceName());
|
svg_text->setFontFamily(text->getTextData().mFont.getFaceName());
|
|
@ -1,7 +1,5 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "AbstractPainter.h"
|
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
class SvgDocument;
|
class SvgDocument;
|
||||||
|
@ -24,6 +22,12 @@ private:
|
||||||
|
|
||||||
void paintPrimitive(SvgDocument* document, SceneModel* model) 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 paintText(SvgDocument* document, SceneText* model) const;
|
||||||
|
|
||||||
void setStyle(SceneModel* model, SvgShapeElement* element) const;
|
void setStyle(SceneModel* model, SvgShapeElement* element) const;
|
|
@ -58,6 +58,10 @@ void SvgReader::onChild(XmlElement* element, XmlElement* svg_parent) const
|
||||||
{
|
{
|
||||||
new_svg = std::make_unique<SvgRectangle>();
|
new_svg = std::make_unique<SvgRectangle>();
|
||||||
}
|
}
|
||||||
|
else if (element->getTagName() == "path")
|
||||||
|
{
|
||||||
|
new_svg = std::make_unique<SvgPath>();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -7,7 +7,13 @@
|
||||||
SvgTextElement::SvgTextElement()
|
SvgTextElement::SvgTextElement()
|
||||||
:SvgElement("text")
|
: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)
|
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)
|
void SvgPath::setPath(const std::string& mPath)
|
||||||
{
|
{
|
||||||
auto path = std::make_unique<XmlAttribute>("d");
|
auto path = std::make_unique<XmlAttribute>("d");
|
||||||
|
|
|
@ -68,6 +68,8 @@ class SvgPath : public SvgShapeElement
|
||||||
public:
|
public:
|
||||||
SvgPath();
|
SvgPath();
|
||||||
|
|
||||||
|
std::string getPath() const;
|
||||||
|
|
||||||
void setPath(const std::string& mPath);
|
void setPath(const std::string& mPath);
|
||||||
void setFillRule(const std::string& fillRule);
|
void setFillRule(const std::string& fillRule);
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,12 +3,15 @@
|
||||||
#include "TextNode.h"
|
#include "TextNode.h"
|
||||||
#include "GeometryNode.h"
|
#include "GeometryNode.h"
|
||||||
#include "TransformNode.h"
|
#include "TransformNode.h"
|
||||||
#include "ThemeManager.h"
|
#include "IconNode.h"
|
||||||
#include "PaintEvent.h"
|
|
||||||
|
|
||||||
#include "FontTokens.h"
|
#include "FontTokens.h"
|
||||||
|
#include "ThemeManager.h"
|
||||||
|
#include "MediaResourceManager.h"
|
||||||
|
|
||||||
|
#include "PaintEvent.h"
|
||||||
#include "MouseEvent.h"
|
#include "MouseEvent.h"
|
||||||
|
|
||||||
#include "FileLogger.h"
|
#include "FileLogger.h"
|
||||||
|
|
||||||
Button::Button(ButtonData::Component component)
|
Button::Button(ButtonData::Component component)
|
||||||
|
@ -48,6 +51,15 @@ void Button::setLabel(const std::string& text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Button::setSvgIcon(Resource::Icon::Svg icon)
|
||||||
|
{
|
||||||
|
if (icon != mIcon)
|
||||||
|
{
|
||||||
|
mIcon = icon;
|
||||||
|
mContentDirty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Button::setState(ButtonData::State state)
|
void Button::setState(ButtonData::State state)
|
||||||
{
|
{
|
||||||
if (mStyle.mState != state)
|
if (mStyle.mState != state)
|
||||||
|
@ -170,6 +182,10 @@ void Button::doPaint(const PaintEvent* event)
|
||||||
{
|
{
|
||||||
updateBackground(event);
|
updateBackground(event);
|
||||||
updateLabel(event);
|
updateLabel(event);
|
||||||
|
|
||||||
|
updateIcon(event);
|
||||||
|
|
||||||
|
mContentDirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Button::updateLabel(const PaintEvent* event)
|
void Button::updateLabel(const PaintEvent* event)
|
||||||
|
@ -204,7 +220,6 @@ void Button::updateLabel(const PaintEvent* event)
|
||||||
if (mContentDirty)
|
if (mContentDirty)
|
||||||
{
|
{
|
||||||
mTextNode->setContent(mLabel);
|
mTextNode->setContent(mLabel);
|
||||||
mContentDirty = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mVisibilityDirty)
|
if (mVisibilityDirty)
|
||||||
|
@ -212,3 +227,40 @@ void Button::updateLabel(const PaintEvent* event)
|
||||||
mTextNode->setIsVisible(mVisible);
|
mTextNode->setIsVisible(mVisible);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Button::updateIcon(const PaintEvent* event)
|
||||||
|
{
|
||||||
|
if (!mIconNode && mIcon != Resource::Icon::Svg::NONE)
|
||||||
|
{
|
||||||
|
mIconNode = std::make_unique<IconNode>(mLocation);
|
||||||
|
mIconNode->setName(mName + "_IconNode");
|
||||||
|
mIconNode->setContent(IconNode::IconType::Svg, MediaResourceManager::getSvgIconNode(mIcon));
|
||||||
|
mRootNode->addChild(mIconNode.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mIconNode)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mTransformDirty)
|
||||||
|
{
|
||||||
|
mIconNode->setLocation(mLocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mMaterialDirty)
|
||||||
|
{
|
||||||
|
auto icon_fill = event->getThemesManager()->getColor(mIconColor);
|
||||||
|
icon_fill.setAlpha(mIconOpacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mContentDirty)
|
||||||
|
{
|
||||||
|
mIconNode->setContent(IconNode::IconType::Svg, MediaResourceManager::getSvgIconNode(mIcon));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mVisibilityDirty)
|
||||||
|
{
|
||||||
|
mIconNode->setIsVisible(mVisible);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
#include "Widget.h"
|
#include "Widget.h"
|
||||||
#include "Color.h"
|
#include "Color.h"
|
||||||
|
|
||||||
|
#include "MediaResources.h"
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
@ -23,6 +25,8 @@ public:
|
||||||
|
|
||||||
static std::unique_ptr<Button> Create(ButtonData::Component component = ButtonData::Component::Elevated);
|
static std::unique_ptr<Button> Create(ButtonData::Component component = ButtonData::Component::Elevated);
|
||||||
|
|
||||||
|
void setSvgIcon(Resource::Icon::Svg icon);
|
||||||
|
|
||||||
void setLabel(const std::string& text);
|
void setLabel(const std::string& text);
|
||||||
|
|
||||||
void setEnabled(bool isEnabled);
|
void setEnabled(bool isEnabled);
|
||||||
|
@ -40,6 +44,7 @@ protected:
|
||||||
void setLabelTextTypescale(Theme::Sys::Typescale typescale);
|
void setLabelTextTypescale(Theme::Sys::Typescale typescale);
|
||||||
|
|
||||||
void updateLabel(const PaintEvent* event);
|
void updateLabel(const PaintEvent* event);
|
||||||
|
void updateIcon(const PaintEvent* event);
|
||||||
|
|
||||||
void setState(ButtonData::State state);
|
void setState(ButtonData::State state);
|
||||||
void resetState();
|
void resetState();
|
||||||
|
@ -52,14 +57,16 @@ private:
|
||||||
Theme::Sys::Color mLabelTextColor;
|
Theme::Sys::Color mLabelTextColor;
|
||||||
Theme::Sys::Typescale mLabelTextTypescale;
|
Theme::Sys::Typescale mLabelTextTypescale;
|
||||||
float mLabelOpacity{ 1.0 };
|
float mLabelOpacity{ 1.0 };
|
||||||
|
|
||||||
clickFunc mClickFunc;
|
|
||||||
|
|
||||||
std::unique_ptr<TextNode> mTextNode;
|
std::unique_ptr<TextNode> mTextNode;
|
||||||
bool mContentDirty{true};
|
|
||||||
|
|
||||||
|
Resource::Icon::Svg mIcon{ Resource::Icon::Svg::NONE};
|
||||||
|
Theme::Sys::Color mIconColor;
|
||||||
|
float mIconOpacity{ 1.0 };
|
||||||
std::unique_ptr<IconNode> mIconNode;
|
std::unique_ptr<IconNode> mIconNode;
|
||||||
|
|
||||||
|
bool mContentDirty{ true };
|
||||||
|
|
||||||
|
clickFunc mClickFunc;
|
||||||
bool mEnabled{ true };
|
bool mEnabled{ true };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
#include "IconNode.h"
|
||||||
|
|
||||||
|
IconNode::IconNode(const Point& loc)
|
||||||
|
: AbstractVisualNode(loc)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void IconNode::setContent(IconType type, std::unique_ptr<AbstractVisualNode> content)
|
||||||
|
{
|
||||||
|
mType = type;
|
||||||
|
mContent = std::move(content);
|
||||||
|
mContentDirty = true;
|
||||||
|
}
|
|
@ -2,8 +2,23 @@
|
||||||
|
|
||||||
#include "AbstractVisualNode.h"
|
#include "AbstractVisualNode.h"
|
||||||
|
|
||||||
class IconNode
|
class IconNode : public AbstractVisualNode
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
enum class IconType
|
||||||
|
{
|
||||||
|
Image,
|
||||||
|
Svg
|
||||||
|
};
|
||||||
|
|
||||||
|
IconNode(const Point& loc);
|
||||||
|
|
||||||
|
void setContent(IconType type, std::unique_ptr<AbstractVisualNode>);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
std::unique_ptr<AbstractVisualNode> mContent;
|
||||||
|
IconType mType{ IconType::Svg };
|
||||||
|
bool mContentDirty{ true };
|
||||||
|
|
||||||
};
|
};
|
|
@ -0,0 +1,33 @@
|
||||||
|
#include "MediaResourceManager.h"
|
||||||
|
|
||||||
|
#include "SvgReader.h"
|
||||||
|
#include "SvgDocument.h"
|
||||||
|
#include "SvgNode.h"
|
||||||
|
|
||||||
|
const Path MediaResourceManager::mResourceLocation = "C:\\dev\\MediaResources";
|
||||||
|
|
||||||
|
Path MediaResourceManager::getSvgIconPath(Resource::Icon::Svg icon)
|
||||||
|
{
|
||||||
|
return mResourceLocation / MediaResources::getPath(icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<AbstractVisualNode> MediaResourceManager::getSvgIconNode(Resource::Icon::Svg icon)
|
||||||
|
{
|
||||||
|
const auto path = getSvgIconPath(icon);
|
||||||
|
|
||||||
|
if (path.empty())
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
SvgReader svg_reader;
|
||||||
|
auto svg_doc = svg_reader.read(path);
|
||||||
|
if (!svg_doc)
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto svg_node = std::make_unique<SvgNode>(Point(0.0, 0.0));
|
||||||
|
svg_node->setContent(std::move(svg_doc));
|
||||||
|
return std::move(svg_node);
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "MediaResources.h"
|
||||||
|
#include "AbstractVisualNode.h"
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
using Path = std::filesystem::path;
|
||||||
|
|
||||||
|
class MediaResourceManager
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static std::unique_ptr<AbstractVisualNode> getSvgIconNode(Resource::Icon::Svg icon);
|
||||||
|
|
||||||
|
static Path getSvgIconPath(Resource::Icon::Svg icon);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static const Path mResourceLocation;
|
||||||
|
};
|
|
@ -0,0 +1,12 @@
|
||||||
|
#include "MediaResources.h"
|
||||||
|
|
||||||
|
Path MediaResources::getPath(Resource::Icon::Svg icon)
|
||||||
|
{
|
||||||
|
switch (icon)
|
||||||
|
{
|
||||||
|
case Resource::Icon::Svg::HOME_MEDIUM:
|
||||||
|
return "svg/home_medium.svg";
|
||||||
|
default:
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
using Path = std::filesystem::path;
|
||||||
|
|
||||||
|
namespace Resource
|
||||||
|
{
|
||||||
|
namespace Icon
|
||||||
|
{
|
||||||
|
enum class Svg
|
||||||
|
{
|
||||||
|
NONE,
|
||||||
|
HOME_MEDIUM
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MediaResources
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static Path getPath(Resource::Icon::Svg icon);
|
||||||
|
};
|
|
@ -1,5 +1,6 @@
|
||||||
add_subdirectory(test_utils)
|
add_subdirectory(test_utils)
|
||||||
|
|
||||||
|
add_subdirectory(geometry)
|
||||||
add_subdirectory(ui_controls)
|
add_subdirectory(ui_controls)
|
||||||
|
|
||||||
file(COPY data/ DESTINATION ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test_data)
|
file(COPY data/ DESTINATION ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test_data)
|
||||||
|
@ -11,7 +12,6 @@ set(TEST_MODULES
|
||||||
core
|
core
|
||||||
database
|
database
|
||||||
fonts
|
fonts
|
||||||
geometry
|
|
||||||
graphics
|
graphics
|
||||||
image
|
image
|
||||||
ipc
|
ipc
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
|
set(MODULE_NAME geometry)
|
||||||
|
|
||||||
set(GEOMETRY_UNIT_TEST_FILES
|
list(APPEND UNIT_TEST_FILES
|
||||||
geometry/TestGeometryCollection.cpp
|
TestGeometryCollection.cpp
|
||||||
geometry/TestGrid.cpp
|
TestGrid.cpp
|
||||||
PARENT_SCOPE
|
TestPath.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set(GEOMETRY_UNIT_TEST_DEPENDENCIES
|
set(UNIT_TEST_TARGET_NAME ${MODULE_NAME}_unit_tests)
|
||||||
geometry
|
|
||||||
PARENT_SCOPE
|
add_executable(${UNIT_TEST_TARGET_NAME} ${CMAKE_SOURCE_DIR}/test/test_runner.cpp ${UNIT_TEST_FILES})
|
||||||
)
|
target_link_libraries(${UNIT_TEST_TARGET_NAME} PUBLIC test_utils geometry)
|
||||||
|
set_property(TARGET ${UNIT_TEST_TARGET_NAME} PROPERTY FOLDER test/${MODULE_NAME})
|
10
test/geometry/TestPath.cpp
Normal file
10
test/geometry/TestPath.cpp
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
#include "TestFramework.h"
|
||||||
|
#include "TestUtils.h"
|
||||||
|
|
||||||
|
#include "Path.h"
|
||||||
|
|
||||||
|
TEST_CASE(TestPath, "geometry")
|
||||||
|
{
|
||||||
|
GeometryPath path;
|
||||||
|
path.buildFromPostscript("M11 39h7.5V26.5h11V39H37V19.5L24 9.75 11 19.5Zm-3 3V18L24 6l16 12v24H26.5V29.5h-5V42Zm16-17.65Z");
|
||||||
|
};
|
|
@ -6,14 +6,16 @@
|
||||||
#include "SvgWriter.h"
|
#include "SvgWriter.h"
|
||||||
|
|
||||||
#include "SvgNode.h"
|
#include "SvgNode.h"
|
||||||
|
#include "MediaResourceManager.h"
|
||||||
|
|
||||||
TEST_CASE(TestSvgToNodeConverter, "publishing")
|
|
||||||
|
TEST_CASE(TestSvgToNodeConverter_Circles, "publishing")
|
||||||
{
|
{
|
||||||
SvgReader svg_reader;
|
SvgReader svg_reader;
|
||||||
auto svg_doc = svg_reader.read(TestUtils::getTestDataDir() / "circles.svg");
|
auto svg_doc = svg_reader.read(TestUtils::getTestDataDir() / "circles.svg");
|
||||||
|
|
||||||
SvgWriter svg_writer;
|
SvgWriter svg_writer;
|
||||||
svg_writer.toFile(TestUtils::getTestOutputDir(__FILE__) / "TestSvgToNodeConverter.svg", svg_doc.get());
|
svg_writer.toFile(TestUtils::getTestOutputDir(__FILE__) / "TestSvgToNodeConverter_Circles.svg", svg_doc.get());
|
||||||
|
|
||||||
TestRenderer renderer;
|
TestRenderer renderer;
|
||||||
|
|
||||||
|
@ -21,5 +23,21 @@ TEST_CASE(TestSvgToNodeConverter, "publishing")
|
||||||
svg_node->setContent(std::move(svg_doc));
|
svg_node->setContent(std::move(svg_doc));
|
||||||
renderer.getScene()->addNode(svg_node.get());
|
renderer.getScene()->addNode(svg_node.get());
|
||||||
|
|
||||||
renderer.write(TestUtils::getTestOutputDir(__FILE__) / "TestSvgToNodeConverter.png");
|
renderer.write(TestUtils::getTestOutputDir(__FILE__) / "TestSvgToNodeConverter_Circles.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE(TestSvgToNodeConverter_Path, "publishing")
|
||||||
|
{
|
||||||
|
SvgReader svg_reader;
|
||||||
|
auto svg_doc = svg_reader.read(MediaResourceManager::getSvgIconPath(Resource::Icon::Svg::HOME_MEDIUM));
|
||||||
|
|
||||||
|
auto svg_node = std::make_unique<SvgNode>(Point(0.0, 0.0));
|
||||||
|
svg_node->setContent(std::move(svg_doc));
|
||||||
|
|
||||||
|
TestRenderer renderer;
|
||||||
|
renderer.getScene()->addNode(svg_node.get());
|
||||||
|
|
||||||
|
renderer.writeSvg(TestUtils::getTestOutputDir(__FILE__) / "TestSvgToNodeConverter_Path.svg");
|
||||||
|
|
||||||
|
renderer.write(TestUtils::getTestOutputDir(__FILE__) / "TestSvgToNodTestSvgToNodeConverter_PathConverter.png");
|
||||||
}
|
}
|
|
@ -7,6 +7,7 @@
|
||||||
#include "MouseEvent.h"
|
#include "MouseEvent.h"
|
||||||
|
|
||||||
#include "VerticalSpacer.h"
|
#include "VerticalSpacer.h"
|
||||||
|
#include "MediaResources.h"
|
||||||
|
|
||||||
#include "Button.h"
|
#include "Button.h"
|
||||||
|
|
||||||
|
@ -20,7 +21,9 @@ TEST_CASE(TestButton_Elevated, "ui_controls")
|
||||||
|
|
||||||
auto enabled_button = Button::Create(ButtonData::Component::Elevated);
|
auto enabled_button = Button::Create(ButtonData::Component::Elevated);
|
||||||
enabled_button->setLabel("Enabled");
|
enabled_button->setLabel("Enabled");
|
||||||
|
enabled_button->setSvgIcon(Resource::Icon::Svg::HOME_MEDIUM);
|
||||||
|
|
||||||
|
/*
|
||||||
auto disabled_button = Button::Create(ButtonData::Component::Elevated);
|
auto disabled_button = Button::Create(ButtonData::Component::Elevated);
|
||||||
disabled_button->setEnabled(false);
|
disabled_button->setEnabled(false);
|
||||||
disabled_button->setLabel("Disabled");
|
disabled_button->setLabel("Disabled");
|
||||||
|
@ -28,9 +31,11 @@ TEST_CASE(TestButton_Elevated, "ui_controls")
|
||||||
auto pressed_button = Button::Create(ButtonData::Component::Elevated);
|
auto pressed_button = Button::Create(ButtonData::Component::Elevated);
|
||||||
pressed_button->setLabel("Pressed");
|
pressed_button->setLabel("Pressed");
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
spacer.addWidget(std::move(enabled_button));
|
spacer.addWidget(std::move(enabled_button));
|
||||||
spacer.addWidget(std::move(disabled_button));
|
//spacer.addWidget(std::move(disabled_button));
|
||||||
spacer.addWidget(std::move(pressed_button));
|
//spacer.addWidget(std::move(pressed_button));
|
||||||
|
|
||||||
auto node = spacer.getRootNode();
|
auto node = spacer.getRootNode();
|
||||||
|
|
||||||
|
@ -40,7 +45,7 @@ TEST_CASE(TestButton_Elevated, "ui_controls")
|
||||||
auto mouse_event = MouseEvent::Create();
|
auto mouse_event = MouseEvent::Create();
|
||||||
mouse_event->setAction(MouseEvent::Action::Pressed);
|
mouse_event->setAction(MouseEvent::Action::Pressed);
|
||||||
mouse_event->setClientLocation({ 250, 20 });
|
mouse_event->setClientLocation({ 250, 20 });
|
||||||
spacer.onMouseEvent(mouse_event.get());
|
//spacer.onMouseEvent(mouse_event.get());
|
||||||
|
|
||||||
auto paint_event = PaintEvent::Create(theme_manager.get(), nullptr);
|
auto paint_event = PaintEvent::Create(theme_manager.get(), nullptr);
|
||||||
spacer.onPaintEvent(paint_event.get());
|
spacer.onPaintEvent(paint_event.get());
|
||||||
|
|
Loading…
Reference in a new issue