26using namespace Qt::StringLiterals;
42 auto res = std::make_unique< QgsTiledSceneTextureRenderer >();
43 res->setFillSymbol( mFillSymbol->clone() );
52 auto r = std::make_unique< QgsTiledSceneTextureRenderer >();
54 const QDomElement fillSymbolElem = element.firstChildElement( u
"fillSymbol"_s );
55 if ( !fillSymbolElem.isNull() )
57 const QDomElement symbolElem = fillSymbolElem.firstChildElement( u
"symbol"_s );
64 r->restoreCommonProperties( element, context );
71 QVariantMap properties;
72 properties.insert( u
"color"_s, u
"224,224,224"_s );
73 properties.insert( u
"style"_s, u
"solid"_s );
74 properties.insert( u
"style_border"_s, u
"solid"_s );
75 properties.insert( u
"color_border"_s, u
"124,124,124"_s );
76 properties.insert( u
"width_border"_s, u
"0.1"_s );
77 properties.insert( u
"joinstyle"_s, u
"round"_s );
84 return mFillSymbol.get();
89 mFillSymbol.reset( symbol );
94 QDomElement rendererElem = doc.createElement( u
"renderer"_s );
96 rendererElem.setAttribute( u
"type"_s, u
"texture"_s );
99 QDomElement fillSymbolElem = doc.createElement( u
"fillSymbol"_s );
101 fillSymbolElem.appendChild( symbolElement );
102 rendererElem.appendChild( fillSymbolElem );
122 mFillSymbol->renderPolygon( triangle,
nullptr,
nullptr, context.
renderContext() );
132 context.
textureCoordinates( textureX1, textureY1, textureX2, textureY2, textureX3, textureY3 );
135 painter->setPen( Qt::NoPen );
137 auto unitNormal = [](
const QPointF p1,
const QPointF p2 ) {
138 const float dx = p2.x() - p1.x();
139 const float dy = p2.y() - p1.y();
140 QPointF n( -dy, dx );
141 const double length = std::sqrt( n.x() * n.x() + n.y() * n.y() );
142 return QPointF( n.x() / length, n.y() / length );
145 auto intersect = [](
const QPointF p1,
const QPointF p2,
const QPointF q1,
const QPointF q2 ) {
146 const double a1 = p2.y() - p1.y();
147 const double b1 = p1.x() - p2.x();
148 const double c1 = a1 * p1.x() + b1 * p1.y();
150 const double a2 = q2.y() - q1.y();
151 const double b2 = q1.x() - q2.x();
152 const double c2 = a2 * q1.x() + b2 * q1.y();
154 const double det = a1 * b2 - a2 * b1;
162 return QPointF( ( b2 * c1 - b1 * c2 ) / det, ( a1 * c2 - a2 * c1 ) / det );
166 auto smallestAngleInTriangle = [](
const QPolygonF &triangle ) {
167 const QPointF p1 = triangle.at( 0 );
168 const QPointF p2 = triangle.at( 1 );
169 const QPointF p3 = triangle.at( 2 );
171 const QPointF v1 = p2 - p1;
172 const QPointF v2 = p3 - p2;
173 const QPointF v3 = p1 - p3;
175 const double a = std::sqrt( v1.x() * v1.x() + v1.y() * v1.y() );
176 const double b = std::sqrt( v2.x() * v2.x() + v2.y() * v2.y() );
177 const double c = std::sqrt( v3.x() * v3.x() + v3.y() * v3.y() );
179 return std::min( std::min( std::acos( ( b * b +
c *
c - a * a ) / ( 2 * b *
c ) ), std::acos( ( a * a +
c *
c - b * b ) / ( 2 * a *
c ) ) ), std::acos( ( a * a + b * b -
c *
c ) / ( 2 * a * b ) ) );
182 auto growTriangle = [&unitNormal, &intersect](
const QPolygonF &triangle,
float pixels ) {
183 QPair< QPointF, QPointF > offsetEdges[3];
184 for (
int i = 0; i < 3; ++i )
186 const QPointF p1 = triangle.at( i );
187 const QPointF p2 = triangle.at( i + 1 );
188 const QPointF n = unitNormal( p1, p2 );
190 const QPointF offsetP1( p1.x() + n.x() * pixels, p1.y() + n.y() * pixels );
191 const QPointF offsetP2( p2.x() + n.x() * pixels, p2.y() + n.y() * pixels );
193 offsetEdges[i] = { offsetP1, offsetP2 };
200 static double constexpr MAX_TRIANGLE_GROW_PIXELS_SQUARED = 3 * 3;
201 for (
int i = 0; i < 3; ++i )
203 const auto &edge1 = offsetEdges[i];
204 const auto &edge2 = offsetEdges[i == 0 ? 2 : ( i - 1 )];
206 const QPointF vertex = intersect( edge1.first, edge1.second, edge2.first, edge2.second );
207 if ( vertex.isNull() )
210 const QPointF originalPoint = triangle.at( i );
213 double delta = std::pow( vertex.x() - originalPoint.x(), 2 ) + std::pow( vertex.y() - originalPoint.y(), 2 );
214 if ( delta > MAX_TRIANGLE_GROW_PIXELS_SQUARED )
216 double dx = ( vertex.x() - originalPoint.x() ) * MAX_TRIANGLE_GROW_PIXELS_SQUARED / delta;
217 double dy = ( vertex.y() - originalPoint.y() ) * MAX_TRIANGLE_GROW_PIXELS_SQUARED / delta;
218 result << triangle.at( i ) + QPointF( dx, dy );
223 result << result.at( 0 );
229 const double minAngle = smallestAngleInTriangle( triangle ) * 180 / M_PI;
230 if ( std::isnan( minAngle ) || minAngle < 0.1 )
@ RequiresTextures
Renderer requires textures.
@ ForceRasterRender
Layer should always be rendered as a raster image.
@ RendersTriangles
Renderer can render triangle primitives.
QFlags< TiledSceneRendererFlag > TiledSceneRendererFlags
Flags which control how tiled scene 2D renderers behave.
A fill symbol type, for rendering Polygon and MultiPolygon geometries.
static std::unique_ptr< QgsFillSymbol > createSimple(const QVariantMap &properties)
Create a fill symbol with one symbol layer: SimpleFill with specified properties.
static bool drawTriangleUsingTexture(QPainter *painter, const QPolygonF &triangle, const QImage &textureImage, float textureX1, float textureY1, float textureX2, float textureY2, float textureX3, float textureY3)
Draws a triangle onto a painter using a mapped texture image.
A container for the context for various read/write operations on objects.
QPainter * painter()
Returns the destination QPainter for the render operation.
static std::unique_ptr< QgsSymbol > loadSymbol(const QDomElement &element, const QgsReadWriteContext &context)
Attempts to load a symbol from a DOM element.
static QDomElement saveSymbol(const QString &symbolName, const QgsSymbol *symbol, QDomDocument &doc, const QgsReadWriteContext &context)
Writes a symbol definition to XML.
Encapsulates the render context for a 2D tiled scene rendering operation.
void textureCoordinates(float &textureX1, float &textureY1, float &textureX2, float &textureY2, float &textureX3, float &textureY3) const
Returns the current texture coordinates.
QgsRenderContext & renderContext()
Returns a reference to the context's render context.
QImage textureImage() const
Returns the current texture image.
void saveCommonProperties(QDomElement &element, const QgsReadWriteContext &context) const
Saves common renderer properties (such as point size and screen error) to the specified DOM element.
virtual void stopRender(QgsTiledSceneRenderContext &context)
Must be called when a render cycle has finished, to allow the renderer to clean up.
QgsTiledSceneRenderer()=default
virtual void startRender(QgsTiledSceneRenderContext &context)
Must be called when a new render cycle is started.
void copyCommonProperties(QgsTiledSceneRenderer *destination) const
Copies common tiled scene renderer properties (such as screen error) to the destination renderer.
void startRender(QgsTiledSceneRenderContext &context) override
Must be called when a new render cycle is started.
QDomElement save(QDomDocument &doc, const QgsReadWriteContext &context) const override
Saves the renderer configuration to an XML element.
~QgsTiledSceneTextureRenderer() override
QgsTiledSceneTextureRenderer()
Constructor for QgsTiledSceneTextureRenderer.
QgsFillSymbol * fillSymbol() const
Returns the fill symbol used to render triangles without textures.
void renderLine(QgsTiledSceneRenderContext &context, const QPolygonF &line) override
Renders a line.
QgsTiledSceneRenderer * clone() const override
Create a deep copy of this renderer.
void setFillSymbol(QgsFillSymbol *symbol)
Sets the fill symbol used to render triangles without textures.
QString type() const override
Returns the identifier of the renderer type.
static std::unique_ptr< QgsFillSymbol > createDefaultFillSymbol()
Returns a copy of the default fill symbol used to render triangles without textures.
static QgsTiledSceneRenderer * create(QDomElement &element, const QgsReadWriteContext &context)
Creates a textured renderer from an XML element.
void stopRender(QgsTiledSceneRenderContext &context) override
Must be called when a render cycle has finished, to allow the renderer to clean up.
void renderTriangle(QgsTiledSceneRenderContext &context, const QPolygonF &triangle) override
Renders a triangle.
Qgis::TiledSceneRendererFlags flags() const override
Returns flags which control how the renderer behaves.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).