Add path rendering and svg line output.

This commit is contained in:
jmsgrogan 2023-01-24 11:04:47 +00:00
parent 73051a5f27
commit 6274c41a80
15 changed files with 281 additions and 79 deletions

View file

@ -3,78 +3,105 @@
#include "PointParser.h" #include "PointParser.h"
#include <sstream> #include <sstream>
Arc::Arc(const Point& startPoint, const Point& endPoint, double rX, double rY, double rotation, bool largeArc, bool sweep) namespace ntk {
: mStartPoint(startPoint), Arc::Arc(const Point& startPoint, const Point& endPoint, double rX, double rY, double rotation, bool largeArc, bool sweep)
mEndPoint(endPoint), : mStartPoint(startPoint),
mRx(rX), mEndPoint(endPoint),
mRy(rY), mRx(rX),
mRotation(rotation), mRy(rY),
mLargeArc(largeArc), mRotation(rotation),
mSweep(sweep) mLargeArc(largeArc),
{ mSweep(sweep)
}
Point Arc::getFirstPoint() const
{
return mStartPoint;
}
Point Arc::getEndPoint() const
{
return mEndPoint;
}
std::string Arc::toPostScriptString(std::size_t precision) const
{
const auto large = mLargeArc ? "1" : "0";
const auto sweep = mSweep ? "1" : "0";
std::stringstream sstr;
if (precision > 0)
{ {
sstr.precision(precision);
} }
if (mPostscriptPositioning == PostscriptPositioning::RELATIVE_TO)
Point Arc::getFirstPoint() const
{ {
sstr << "a"; return mStartPoint;
} }
else
Point Arc::getEndPoint() const
{ {
sstr << "A"; return mEndPoint;
} }
sstr << mRx << " " << mRy << " " << mRotation << " " << large << " " << sweep << " ";
if (mPostscriptPositioning == PostscriptPositioning::RELATIVE_TO) std::string Arc::toPostScriptString(std::size_t precision) const
{ {
sstr << PointParser::toStringRelative(mEndPoint, mStartPoint, 2, " ", precision); const auto large = mLargeArc ? "1" : "0";
const auto sweep = mSweep ? "1" : "0";
std::stringstream sstr;
if (precision > 0)
{
sstr.precision(precision);
}
if (mPostscriptPositioning == PostscriptPositioning::RELATIVE_TO)
{
sstr << "a";
}
else
{
sstr << "A";
}
sstr << mRx << " " << mRy << " " << mRotation << " " << large << " " << sweep << " ";
if (mPostscriptPositioning == PostscriptPositioning::RELATIVE_TO)
{
sstr << PointParser::toStringRelative(mEndPoint, mStartPoint, 2, " ", precision);
}
else
{
sstr << PointParser::toString(mEndPoint, 2, " ", precision);
}
return sstr.str();
} }
else
Bounds Arc::getBounds() const
{ {
sstr << PointParser::toString(mEndPoint, 2, " ", precision); return {};
} }
return sstr.str();
}
Bounds Arc::getBounds() const const Point& Arc::getLocation() const
{ {
return {}; return mStartPoint;
} }
const Point& Arc::getLocation() const void Arc::sample(SparseGrid<bool>*) const
{ {
return mStartPoint;
}
void Arc::sample(SparseGrid<bool>*) const }
{
} Arc::Type Arc::getType() const
{
return Type::CURVE;
}
Arc::Type Arc::getType() const Arc::CurveType Arc::getCurveType() const
{ {
return Type::CURVE; return CurveType::ARC;
} }
Arc::CurveType Arc::getCurveType() const double Arc::getRx() const
{ {
return CurveType::ARC; return mRx;
}
double Arc::getRy() const
{
return mRy;
}
double Arc::getRotation() const
{
return mRotation;
}
bool Arc::getUseLargeArc() const
{
return mLargeArc;
}
bool Arc::getSweepParam() const
{
return mSweep;
}
} }

View file

@ -3,6 +3,7 @@
#include "Curve.h" #include "Curve.h"
#include "Point.h" #include "Point.h"
namespace ntk{
class Arc : public Curve class Arc : public Curve
{ {
public: public:
@ -20,6 +21,16 @@ public:
CurveType getCurveType() const override; CurveType getCurveType() const override;
double getRx() const;
double getRy() const;
double getRotation() const;
bool getUseLargeArc() const;
bool getSweepParam() const;
void sample(SparseGrid<bool>*) const override; void sample(SparseGrid<bool>*) const override;
std::string toPostScriptString(std::size_t precision = 0) const override; std::string toPostScriptString(std::size_t precision = 0) const override;
@ -33,3 +44,4 @@ private:
bool mLargeArc{ false }; bool mLargeArc{ false };
bool mSweep{ false }; bool mSweep{ false };
}; };
}

View file

@ -21,6 +21,16 @@ Point CubicBezierCurve::getEndPoint() const
return mEndPoint; return mEndPoint;
} }
const Point& CubicBezierCurve::getStartControlPoint() const
{
return mStartControlPoint;
}
const Point& CubicBezierCurve::getEndControlPoint() const
{
return mEndControlPoint;
}
std::string CubicBezierCurve::toPostScriptString(std::size_t precision) const std::string CubicBezierCurve::toPostScriptString(std::size_t precision) const
{ {
if (mPostscriptPositioning == PostscriptPositioning::RELATIVE_TO) if (mPostscriptPositioning == PostscriptPositioning::RELATIVE_TO)

View file

@ -12,6 +12,10 @@ public:
Point getEndPoint() const override; Point getEndPoint() const override;
const Point& getStartControlPoint() const;
const Point& getEndControlPoint() const;
Bounds getBounds() const override; Bounds getBounds() const override;
const Point& getLocation() const override; const Point& getLocation() const override;

View file

@ -249,12 +249,12 @@ PathElementPtr PathPostScriptConverter::onArc()
if (mPositionState == PositionState::RELATIVE) if (mPositionState == PositionState::RELATIVE)
{ {
const auto end_point = Point(mCurrentPoint.getX() + mPointBuffer[5], mCurrentPoint.getY() + mPointBuffer[6]); const auto end_point = Point(mCurrentPoint.getX() + mPointBuffer[5], mCurrentPoint.getY() + mPointBuffer[6]);
element = std::make_unique<Arc>(mCurrentPoint, end_point, rx, ry, rotation, large_arc, sweep); element = std::make_unique<ntk::Arc>(mCurrentPoint, end_point, rx, ry, rotation, large_arc, sweep);
} }
else else
{ {
const auto end_point = Point(mPointBuffer[5], mPointBuffer[6]); const auto end_point = Point(mPointBuffer[5], mPointBuffer[6]);
element = std::make_unique<Arc>(mCurrentPoint, end_point, rx, ry, rotation, large_arc, sweep); element = std::make_unique<ntk::Arc>(mCurrentPoint, end_point, rx, ry, rotation, large_arc, sweep);
} }
} }
return element; return element;

View file

@ -20,6 +20,11 @@ Point QuadraticBezierCurve::getEndPoint() const
return mEndPoint; return mEndPoint;
} }
Point QuadraticBezierCurve::getControlPoint() const
{
return mControlPoint;
}
std::string QuadraticBezierCurve::toPostScriptString(std::size_t precision) const std::string QuadraticBezierCurve::toPostScriptString(std::size_t precision) const
{ {
if (mPostscriptPositioning == PostscriptPositioning::RELATIVE_TO) if (mPostscriptPositioning == PostscriptPositioning::RELATIVE_TO)

View file

@ -10,6 +10,8 @@ public:
Point getFirstPoint() const override; Point getFirstPoint() const override;
Point getControlPoint() const;
Point getEndPoint() const override; Point getEndPoint() const override;
Bounds getBounds() const override; Bounds getBounds() const override;

View file

@ -195,7 +195,6 @@ void DirectX2dPainter::paintPath(SceneModel* model)
for (const auto& feature : path_item->getFeatures()) for (const auto& feature : path_item->getFeatures())
{ {
const auto loc = feature->getLocation(); const auto loc = feature->getLocation();
MLOG_INFO("Starting feature at: " << loc.getX() << " " << loc.getY());
path_sink->BeginFigure(toD2dPoint(loc), D2D1_FIGURE_BEGIN_FILLED); path_sink->BeginFigure(toD2dPoint(loc), D2D1_FIGURE_BEGIN_FILLED);
for (const auto& element : feature->getElements()) for (const auto& element : feature->getElements())
@ -246,39 +245,48 @@ void DirectX2dPainter::paintPath(SceneModel* model)
mSolidBrush->SetColor(toD2dColor(material.getStrokeColor())); mSolidBrush->SetColor(toD2dColor(material.getStrokeColor()));
rt->DrawGeometry(path_geom.Get(), mSolidBrush.Get(), 1.0f); rt->DrawGeometry(path_geom.Get(), mSolidBrush.Get(), 1.0f);
} }
rt->SetTransform(D2D1::Matrix3x2F::Identity()); rt->SetTransform(D2D1::Matrix3x2F::Identity());
} }
void DirectX2dPainter::onArc(Curve* element, ID2D1GeometrySink* sink) void DirectX2dPainter::onArc(Curve* element, ID2D1GeometrySink* sink)
{ {
auto arc = dynamic_cast<ntk::Arc*>(element);
const auto end = toD2dPoint(arc->getEndPoint());
D2D1_SIZE_F size{static_cast<float>(arc->getRx()), static_cast<float>(arc->getRx()) };
const auto angle = static_cast<float>(arc->getRotation());
D2D1_SWEEP_DIRECTION direction = arc->getSweepParam() ? D2D1_SWEEP_DIRECTION_CLOCKWISE : D2D1_SWEEP_DIRECTION_COUNTER_CLOCKWISE;
D2D1_ARC_SIZE arc_size = arc->getUseLargeArc() ? D2D1_ARC_SIZE_LARGE : D2D1_ARC_SIZE_SMALL;
D2D1_ARC_SEGMENT segment{ end , size , angle , direction , arc_size };
sink->AddArc(segment);
} }
void DirectX2dPainter::onQuadraticBezier(Curve* element, ID2D1GeometrySink* sink) void DirectX2dPainter::onQuadraticBezier(Curve* element, ID2D1GeometrySink* sink)
{ {
auto bezier = dynamic_cast<QuadraticBezierCurve*>(element);
D2D1_QUADRATIC_BEZIER_SEGMENT segment{ toD2dPoint(bezier->getControlPoint()) , toD2dPoint(bezier->getEndPoint()) };
sink->AddQuadraticBezier(segment);
} }
void DirectX2dPainter::onCubicBezier(Curve* element, ID2D1GeometrySink* sink) void DirectX2dPainter::onCubicBezier(Curve* element, ID2D1GeometrySink* sink)
{ {
auto bezier = dynamic_cast<CubicBezierCurve*>(element);
D2D1_BEZIER_SEGMENT segment{ toD2dPoint(bezier->getStartControlPoint()), toD2dPoint(bezier->getEndControlPoint()), toD2dPoint(bezier->getEndPoint()) };
sink->AddBezier(segment);
} }
void DirectX2dPainter::onLine(PathElement* element, ID2D1GeometrySink* sink) void DirectX2dPainter::onLine(PathElement* element, ID2D1GeometrySink* sink)
{ {
for (const auto& point : dynamic_cast<Line*>(element)->getPoints().getPoints()) for (const auto& point : dynamic_cast<Line*>(element)->getPoints().getPoints())
{ {
MLOG_INFO("Adding line entry at: " << point.getX() << " " << point.getY());
sink->AddLine(toD2dPoint(point)); sink->AddLine(toD2dPoint(point));
} }
MLOG_INFO("Finished line");
} }
void DirectX2dPainter::onLineSegment(PathElement* element, ID2D1GeometrySink* sink) void DirectX2dPainter::onLineSegment(PathElement* element, ID2D1GeometrySink* sink)
{ {
const auto loc = element->getEndPoint(); const auto loc = element->getEndPoint();
MLOG_INFO("Adding segment entry at: " << loc.getX() << " " << loc.getY());
sink->AddLine(toD2dPoint(loc)); sink->AddLine(toD2dPoint(loc));
} }

View file

@ -15,6 +15,8 @@
#include "Circle.h" #include "Circle.h"
#include "Rectangle.h" #include "Rectangle.h"
#include "Path.h" #include "Path.h"
#include "Line.h"
#include "LineSegment.h"
#include "SvgShapeElements.h" #include "SvgShapeElements.h"
#include "SvgTextElement.h" #include "SvgTextElement.h"
@ -139,6 +141,14 @@ void SvgPainter::paintPrimitive(SvgDocument* document, SceneModel* model) const
{ {
paintPath(document, model); paintPath(document, model);
} }
else if (model->getGeometry()->getType() == AbstractGeometricItem::Type::LINE)
{
paintLine(document, model);
}
else if (model->getGeometry()->getType() == AbstractGeometricItem::Type::LINE_SEGMENT)
{
paintLineSegment(document, model);
}
} }
void SvgPainter::paintRect(SvgDocument* document, SceneModel* model) const void SvgPainter::paintRect(SvgDocument* document, SceneModel* model) const
@ -173,6 +183,25 @@ void SvgPainter::paintCircle(SvgDocument* document, SceneModel* model) const
document->getRoot()->addChild(std::move(circle)); document->getRoot()->addChild(std::move(circle));
} }
void SvgPainter::paintLine(SvgDocument* document, SceneModel* model) const
{
auto model_line = dynamic_cast<Line*>(model->getGeometry());
auto svg_line = std::make_unique<SvgPolyline>();
svg_line->setPoints(model_line->getFirstPoint(), model_line->getPoints().getPoints());
setStyle(model, svg_line.get());
document->getRoot()->addChild(std::move(svg_line));
}
void SvgPainter::paintLineSegment(SvgDocument* document, SceneModel* model) const
{
auto model_line = dynamic_cast<LineSegment*>(model->getGeometry());
auto svg_line = std::make_unique<SvgLine>(model_line->getFirstPoint(), model_line->getEndPoint());
setStyle(model, svg_line.get());
document->getRoot()->addChild(std::move(svg_line));
}
void SvgPainter::paintPath(SvgDocument* document, SceneModel* model) const void SvgPainter::paintPath(SvgDocument* document, SceneModel* model) const
{ {
auto model_path = dynamic_cast<GeometryPath*>(model->getGeometry()); auto model_path = dynamic_cast<GeometryPath*>(model->getGeometry());

View file

@ -26,6 +26,10 @@ private:
void paintCircle(SvgDocument* document, SceneModel* model) const; void paintCircle(SvgDocument* document, SceneModel* model) const;
void paintLine(SvgDocument* document, SceneModel* model) const;
void paintLineSegment(SvgDocument* document, SceneModel* model) const;
void paintPath(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;

View file

@ -5,8 +5,9 @@
#include <sstream> #include <sstream>
SvgShapeElement::SvgShapeElement(const std::string& tagName) SvgShapeElement::SvgShapeElement(const std::string& tagName, std::size_t precision)
: SvgElement(tagName) : SvgElement(tagName),
mPrecision(precision)
{ {
} }

View file

@ -7,7 +7,7 @@
class SvgShapeElement : public SvgElement class SvgShapeElement : public SvgElement
{ {
public: public:
SvgShapeElement(const std::string& tagName); SvgShapeElement(const std::string& tagName, std::size_t precision = 0);
Transform getTransform() const; Transform getTransform() const;
@ -25,6 +25,9 @@ public:
void setNoStroke(); void setNoStroke();
protected:
std::size_t mPrecision{ 0 };
private: private:
std::string getLabelledContent(const std::string& key, const std::string& content) const; std::string getLabelledContent(const std::string& key, const std::string& content) const;
Point parsePoint(const std::string& pointString, double defaultVal = 0.0) const; Point parsePoint(const std::string& pointString, double defaultVal = 0.0) const;

View file

@ -1,5 +1,6 @@
#include "SvgShapeElements.h" #include "SvgShapeElements.h"
#include "PointParser.h"
#include <sstream> #include <sstream>
SvgCircle::SvgCircle(Type type) SvgCircle::SvgCircle(Type type)
@ -174,6 +175,45 @@ void SvgPolyline::setPoints(const std::vector<Point>& locs)
addAttribute(std::move(points)); addAttribute(std::move(points));
} }
void SvgPolyline::setPoints(const Point& startPoint, const std::vector<Point>& locs)
{
auto points = std::make_unique<XmlAttribute>("points");
std::stringstream sstr;
sstr << startPoint.getX() << "," << startPoint.getY() << " ";
for (const auto& loc : locs)
{
sstr << loc.getX() << "," << loc.getY() << " ";
}
points->setValue(sstr.str());
addAttribute(std::move(points));
}
SvgLine::SvgLine(const Point& startPoint, const Point& endPoint, std::size_t precision)
: SvgShapeElement("line", precision)
{
auto fill = std::make_unique<XmlAttribute>("fill");
fill->setValue("none");
addAttribute(std::move(fill));
auto x1 = std::make_unique<XmlAttribute>("x1");
x1->setValue(PointParser::toString(startPoint.getX(), mPrecision));
addAttribute(std::move(x1));
auto y1 = std::make_unique<XmlAttribute>("y1");
y1->setValue(PointParser::toString(startPoint.getY(), mPrecision));
addAttribute(std::move(y1));
auto x2 = std::make_unique<XmlAttribute>("x2");
x2->setValue(PointParser::toString(endPoint.getX(), mPrecision));
addAttribute(std::move(x2));
auto y2 = std::make_unique<XmlAttribute>("y2");
y2->setValue(PointParser::toString(endPoint.getY(), mPrecision));
addAttribute(std::move(y2));
}
SvgPath::SvgPath() SvgPath::SvgPath()
: SvgShapeElement("path") : SvgShapeElement("path")

View file

@ -60,9 +60,17 @@ class SvgPolyline : public SvgShapeElement
public: public:
SvgPolyline(); SvgPolyline();
void setPoints(const Point& startPoint, const std::vector<Point>& loc);
void setPoints(const std::vector<Point>& loc); void setPoints(const std::vector<Point>& loc);
}; };
class SvgLine : public SvgShapeElement
{
public:
SvgLine(const Point& startPoint, const Point& endPoint, std::size_t precision = 0);
};
class SvgPath : public SvgShapeElement class SvgPath : public SvgShapeElement
{ {
public: public:

View file

@ -7,14 +7,14 @@
#include "LineNode.h" #include "LineNode.h"
#include "PathNode.h" #include "PathNode.h"
void addRect(const Point& loc, std::vector<std::unique_ptr<AbstractVisualNode> >& nodes, double radius = 0.0) void addRect(const Point& loc, std::vector<std::unique_ptr<MaterialNode> >& nodes, double radius = 0.0)
{ {
auto node = std::make_unique<RectangleNode>(loc, 150.0, 100.0); auto node = std::make_unique<RectangleNode>(loc, 150.0, 100.0);
node->setRadius(radius); node->setRadius(radius);
nodes.push_back(std::move(node)); nodes.push_back(std::move(node));
} }
void addCircle(const Point& loc, std::vector<std::unique_ptr<AbstractVisualNode> >& nodes, double minorRadius = 0.0) void addCircle(const Point& loc, std::vector<std::unique_ptr<MaterialNode> >& nodes, double minorRadius = 0.0)
{ {
const auto radius = 50.0; const auto radius = 50.0;
auto centre_loc = loc; auto centre_loc = loc;
@ -30,26 +30,25 @@ void addCircle(const Point& loc, std::vector<std::unique_ptr<AbstractVisualNode>
nodes.push_back(std::move(node)); nodes.push_back(std::move(node));
} }
void addLine(const Point& loc, std::vector<std::unique_ptr<AbstractVisualNode> >& nodes) void addLine(const Point& loc, std::vector<std::unique_ptr<MaterialNode> >& nodes)
{ {
std::vector<Point> points = { Point(150.0, 100.0) }; std::vector<Point> points = { Point(150.0, 100.0) };
auto node = std::make_unique<LineNode>(loc, points); auto node = std::make_unique<LineNode>(loc, points);
nodes.push_back(std::move(node)); nodes.push_back(std::move(node));
} }
void addPath(const Point& loc, const std::string& path, std::vector<std::unique_ptr<AbstractVisualNode> >& nodes) void addPath(const Point& loc, const std::string& path, std::vector<std::unique_ptr<MaterialNode> >& nodes)
{ {
auto node = std::make_unique<PathNode>(loc, path); auto node = std::make_unique<PathNode>(loc, path);
nodes.push_back(std::move(node)); nodes.push_back(std::move(node));
} }
TEST_CASE(TestD2dOffScreenRendering, "graphics") void addShapes(const Point& start_loc, std::vector<std::unique_ptr<MaterialNode> >& nodes, bool use_fill = false)
{ {
TestRenderer renderer(800, 800); auto loc = start_loc;
std::vector<std::unique_ptr<AbstractVisualNode> > nodes; auto fill_color = Color(200, 0, 200);
auto loc = Point(10, 10);
addRect(loc, nodes); addRect(loc, nodes);
loc.move(250, 0); loc.move(250, 0);
@ -67,10 +66,60 @@ TEST_CASE(TestD2dOffScreenRendering, "graphics")
loc.move(100, 0); loc.move(100, 0);
addPath(loc, "M0 0 h150 v100 h-150Z", nodes); addPath(loc, "M0 0 h150 v100 h-150Z", nodes);
loc = Point(10, 300);
addPath(loc, "M0 0 h150 q50 50 0 100 h-150Z", nodes);
loc.move(250, 0);
addPath(loc, "M0 0 h150 c25 25 25 75 0 100 h-150Z", nodes);
loc.move(250, 0);
addPath(loc, "M0 0 h150 a50 50 0 0 1 0 100 h-150Z", nodes);
if (use_fill)
{
for (auto& node : nodes)
{
node->setFillColor(fill_color);
}
}
}
TEST_CASE(TestD2dOffScreenRendering_Outlines, "graphics")
{
TestRenderer renderer(800, 800);
std::vector<std::unique_ptr<MaterialNode> > nodes;
auto loc = Point(10, 10);
addShapes(loc, nodes, false);
auto scene = renderer.getScene(); auto scene = renderer.getScene();
for (const auto& node : nodes) for (const auto& node : nodes)
{ {
scene->addNode(node.get()); scene->addNode(node.get());
} }
renderer.write(TestUtils::getTestOutputDir(__FILE__) / "out.png");
renderer.writeSvg(TestUtils::getTestOutputDir(__FILE__) / "outlines.svg");
renderer.write(TestUtils::getTestOutputDir(__FILE__) / "outlines.png");
};
TEST_CASE(TestD2dOffScreenRendering_Fill, "graphics")
{
TestRenderer renderer(800, 800);
std::vector<std::unique_ptr<MaterialNode> > nodes;
auto loc = Point(10, 10);
addShapes(loc, nodes, true);
auto scene = renderer.getScene();
for (const auto& node : nodes)
{
scene->addNode(node.get());
}
renderer.writeSvg(TestUtils::getTestOutputDir(__FILE__) / "fill.svg");
renderer.write(TestUtils::getTestOutputDir(__FILE__) / "fill.png");
}; };