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 <sstream>
Arc::Arc(const Point& startPoint, const Point& endPoint, double rX, double rY, double rotation, bool largeArc, bool sweep)
: mStartPoint(startPoint),
mEndPoint(endPoint),
mRx(rX),
mRy(rY),
mRotation(rotation),
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)
namespace ntk {
Arc::Arc(const Point& startPoint, const Point& endPoint, double rX, double rY, double rotation, bool largeArc, bool sweep)
: mStartPoint(startPoint),
mEndPoint(endPoint),
mRx(rX),
mRy(rY),
mRotation(rotation),
mLargeArc(largeArc),
mSweep(sweep)
{
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
{
return {};
}
const Point& Arc::getLocation() const
{
return mStartPoint;
}
const Point& Arc::getLocation() const
{
return mStartPoint;
}
void Arc::sample(SparseGrid<bool>*) const
{
void Arc::sample(SparseGrid<bool>*) const
{
}
}
Arc::Type Arc::getType() const
{
return Type::CURVE;
}
Arc::Type Arc::getType() const
{
return Type::CURVE;
}
Arc::CurveType Arc::getCurveType() const
{
return CurveType::ARC;
}
Arc::CurveType Arc::getCurveType() const
{
return CurveType::ARC;
double Arc::getRx() const
{
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 "Point.h"
namespace ntk{
class Arc : public Curve
{
public:
@ -20,6 +21,16 @@ public:
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;
std::string toPostScriptString(std::size_t precision = 0) const override;
@ -33,3 +44,4 @@ private:
bool mLargeArc{ false };
bool mSweep{ false };
};
}

View file

@ -21,6 +21,16 @@ Point CubicBezierCurve::getEndPoint() const
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
{
if (mPostscriptPositioning == PostscriptPositioning::RELATIVE_TO)

View file

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

View file

@ -249,12 +249,12 @@ PathElementPtr PathPostScriptConverter::onArc()
if (mPositionState == PositionState::RELATIVE)
{
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
{
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;

View file

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

View file

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

View file

@ -195,7 +195,6 @@ void DirectX2dPainter::paintPath(SceneModel* model)
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())
@ -246,39 +245,48 @@ void DirectX2dPainter::paintPath(SceneModel* model)
mSolidBrush->SetColor(toD2dColor(material.getStrokeColor()));
rt->DrawGeometry(path_geom.Get(), mSolidBrush.Get(), 1.0f);
}
rt->SetTransform(D2D1::Matrix3x2F::Identity());
}
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)
{
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)
{
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)
{
for (const auto& point : dynamic_cast<Line*>(element)->getPoints().getPoints())
{
MLOG_INFO("Adding line entry at: " << point.getX() << " " << point.getY());
sink->AddLine(toD2dPoint(point));
}
MLOG_INFO("Finished line");
}
void DirectX2dPainter::onLineSegment(PathElement* element, ID2D1GeometrySink* sink)
{
const auto loc = element->getEndPoint();
MLOG_INFO("Adding segment entry at: " << loc.getX() << " " << loc.getY());
sink->AddLine(toD2dPoint(loc));
}

View file

@ -15,6 +15,8 @@
#include "Circle.h"
#include "Rectangle.h"
#include "Path.h"
#include "Line.h"
#include "LineSegment.h"
#include "SvgShapeElements.h"
#include "SvgTextElement.h"
@ -139,6 +141,14 @@ void SvgPainter::paintPrimitive(SvgDocument* document, SceneModel* model) const
{
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
@ -173,6 +183,25 @@ void SvgPainter::paintCircle(SvgDocument* document, SceneModel* model) const
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
{
auto model_path = dynamic_cast<GeometryPath*>(model->getGeometry());

View file

@ -26,6 +26,10 @@ private:
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 paintText(SvgDocument* document, SceneText* model) const;

View file

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

View file

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

View file

@ -1,5 +1,6 @@
#include "SvgShapeElements.h"
#include "PointParser.h"
#include <sstream>
SvgCircle::SvgCircle(Type type)
@ -174,6 +175,45 @@ void SvgPolyline::setPoints(const std::vector<Point>& locs)
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()
: SvgShapeElement("path")

View file

@ -60,9 +60,17 @@ class SvgPolyline : public SvgShapeElement
public:
SvgPolyline();
void setPoints(const Point& startPoint, 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
{
public:

View file

@ -7,14 +7,14 @@
#include "LineNode.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);
node->setRadius(radius);
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;
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));
}
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) };
auto node = std::make_unique<LineNode>(loc, points);
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);
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);
loc.move(250, 0);
@ -67,10 +66,60 @@ TEST_CASE(TestD2dOffScreenRendering, "graphics")
loc.move(100, 0);
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();
for (const auto& node : nodes)
{
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");
};