mirror of
				https://github.com/qgis/QGIS.git
				synced 2025-11-04 00:04:25 -05:00 
			
		
		
		
	New "Filled line" symbol layer type
This adds a new line symbol type which renders lines using a fill symbol. The interior of the line is drawn using any standard QGIS fill symbol, allowing for lines filled with gradients, line hatches, etc. Sponsored by North Road, thanks to SLYR
This commit is contained in:
		
							parent
							
								
									6d58958a54
								
							
						
					
					
						commit
						16c8c88247
					
				@ -1499,6 +1499,107 @@ Sets the color for endpoint of gradient, only used if the gradient color type is
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class QgsFilledLineSymbolLayer : QgsLineSymbolLayer
 | 
			
		||||
{
 | 
			
		||||
%Docstring(signature="appended")
 | 
			
		||||
 | 
			
		||||
A line symbol layer type which fills a stroked line with a :py:class:`QgsFillSymbol`.
 | 
			
		||||
 | 
			
		||||
.. versionadded:: 3.36
 | 
			
		||||
%End
 | 
			
		||||
 | 
			
		||||
%TypeHeaderCode
 | 
			
		||||
#include "qgslinesymbollayer.h"
 | 
			
		||||
%End
 | 
			
		||||
  public:
 | 
			
		||||
 | 
			
		||||
    QgsFilledLineSymbolLayer( double width = DEFAULT_SIMPLELINE_WIDTH, QgsFillSymbol *fillSymbol /Transfer/ = 0 );
 | 
			
		||||
%Docstring
 | 
			
		||||
Constructor for QgsFilledLineSymbolLayer.
 | 
			
		||||
 | 
			
		||||
If a ``fillSymbol`` is specified, it will be transferred to the symbol layer and used
 | 
			
		||||
to fill the inside of the stroked line. If no ``fillSymbol`` is specified then a default
 | 
			
		||||
symbol will be used.
 | 
			
		||||
%End
 | 
			
		||||
    ~QgsFilledLineSymbolLayer();
 | 
			
		||||
 | 
			
		||||
    static QgsSymbolLayer *create( const QVariantMap &properties = QVariantMap() ) /Factory/;
 | 
			
		||||
%Docstring
 | 
			
		||||
Creates a new QgsFilledLineSymbolLayer, using the settings
 | 
			
		||||
serialized in the ``properties`` map (corresponding to the output from
 | 
			
		||||
:py:func:`QgsFilledLineSymbolLayer.properties()` ).
 | 
			
		||||
%End
 | 
			
		||||
 | 
			
		||||
    virtual QString layerType() const;
 | 
			
		||||
 | 
			
		||||
    virtual void startRender( QgsSymbolRenderContext &context );
 | 
			
		||||
 | 
			
		||||
    virtual void stopRender( QgsSymbolRenderContext &context );
 | 
			
		||||
 | 
			
		||||
    virtual void renderPolyline( const QPolygonF &points, QgsSymbolRenderContext &context );
 | 
			
		||||
 | 
			
		||||
    virtual QVariantMap properties() const;
 | 
			
		||||
 | 
			
		||||
    virtual QgsFilledLineSymbolLayer *clone() const /Factory/;
 | 
			
		||||
 | 
			
		||||
    virtual QgsSymbol *subSymbol();
 | 
			
		||||
 | 
			
		||||
    virtual bool setSubSymbol( QgsSymbol *symbol /Transfer/ );
 | 
			
		||||
 | 
			
		||||
    virtual bool hasDataDefinedProperties() const;
 | 
			
		||||
 | 
			
		||||
    virtual void setColor( const QColor &c );
 | 
			
		||||
 | 
			
		||||
    virtual QColor color() const;
 | 
			
		||||
 | 
			
		||||
    virtual void setOutputUnit( Qgis::RenderUnit unit );
 | 
			
		||||
 | 
			
		||||
    virtual Qgis::RenderUnit outputUnit() const;
 | 
			
		||||
 | 
			
		||||
    virtual bool usesMapUnits() const;
 | 
			
		||||
 | 
			
		||||
    virtual void setMapUnitScale( const QgsMapUnitScale &scale );
 | 
			
		||||
 | 
			
		||||
    virtual QgsMapUnitScale mapUnitScale() const;
 | 
			
		||||
 | 
			
		||||
    virtual double estimateMaxBleed( const QgsRenderContext &context ) const;
 | 
			
		||||
 | 
			
		||||
    virtual QSet<QString> usedAttributes( const QgsRenderContext &context ) const;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    Qt::PenJoinStyle penJoinStyle() const;
 | 
			
		||||
%Docstring
 | 
			
		||||
Returns the pen join style used to render the line (e.g. miter, bevel, round, etc).
 | 
			
		||||
 | 
			
		||||
.. seealso:: :py:func:`setPenJoinStyle`
 | 
			
		||||
%End
 | 
			
		||||
 | 
			
		||||
    void setPenJoinStyle( Qt::PenJoinStyle style );
 | 
			
		||||
%Docstring
 | 
			
		||||
Sets the pen join ``style`` used to render the line (e.g. miter, bevel, round, etc).
 | 
			
		||||
 | 
			
		||||
.. seealso:: :py:func:`penJoinStyle`
 | 
			
		||||
%End
 | 
			
		||||
 | 
			
		||||
    Qt::PenCapStyle penCapStyle() const;
 | 
			
		||||
%Docstring
 | 
			
		||||
Returns the pen cap style used to render the line (e.g. flat, square, round, etc).
 | 
			
		||||
 | 
			
		||||
.. seealso:: :py:func:`setPenCapStyle`
 | 
			
		||||
%End
 | 
			
		||||
 | 
			
		||||
    void setPenCapStyle( Qt::PenCapStyle style );
 | 
			
		||||
%Docstring
 | 
			
		||||
Sets the pen cap ``style`` used to render the line (e.g. flat, square, round, etc).
 | 
			
		||||
 | 
			
		||||
.. seealso:: :py:func:`penCapStyle`
 | 
			
		||||
%End
 | 
			
		||||
 | 
			
		||||
  private:
 | 
			
		||||
    QgsFilledLineSymbolLayer( const QgsFilledLineSymbolLayer & );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/************************************************************************
 | 
			
		||||
 * This file has been generated automatically from                      *
 | 
			
		||||
 | 
			
		||||
@ -703,6 +703,48 @@ Creates a new QgsLineburstSymbolLayerWidget.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class QgsFilledLineSymbolLayerWidget : QgsSymbolLayerWidget
 | 
			
		||||
{
 | 
			
		||||
%Docstring(signature="appended")
 | 
			
		||||
A widget for configuring :py:class:`QgsFilledLineSymbolLayer`.
 | 
			
		||||
 | 
			
		||||
.. versionadded:: 3.36
 | 
			
		||||
%End
 | 
			
		||||
 | 
			
		||||
%TypeHeaderCode
 | 
			
		||||
#include "qgssymbollayerwidget.h"
 | 
			
		||||
%End
 | 
			
		||||
  public:
 | 
			
		||||
 | 
			
		||||
    QgsFilledLineSymbolLayerWidget( QgsVectorLayer *vl, QWidget *parent /TransferThis/ = 0 );
 | 
			
		||||
%Docstring
 | 
			
		||||
Constructor for QgsFilledLineSymbolLayerWidget.
 | 
			
		||||
 | 
			
		||||
:param vl: associated vector layer
 | 
			
		||||
:param parent: parent widget
 | 
			
		||||
%End
 | 
			
		||||
 | 
			
		||||
    ~QgsFilledLineSymbolLayerWidget();
 | 
			
		||||
 | 
			
		||||
    static QgsSymbolLayerWidget *create( QgsVectorLayer *vl ) /Factory/;
 | 
			
		||||
%Docstring
 | 
			
		||||
Creates a new QgsFilledLineSymbolLayerWidget.
 | 
			
		||||
 | 
			
		||||
:param vl: associated vector layer
 | 
			
		||||
%End
 | 
			
		||||
 | 
			
		||||
    virtual void setSymbolLayer( QgsSymbolLayer *layer );
 | 
			
		||||
 | 
			
		||||
    virtual QgsSymbolLayer *symbolLayer();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class QgsSVGFillSymbolLayerWidget : QgsSymbolLayerWidget
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -31,6 +31,7 @@
 | 
			
		||||
#include "qgsfeedback.h"
 | 
			
		||||
#include "qgsimageoperation.h"
 | 
			
		||||
#include "qgscolorrampimpl.h"
 | 
			
		||||
#include "qgsfillsymbol.h"
 | 
			
		||||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <QPainter>
 | 
			
		||||
@ -3788,3 +3789,309 @@ void QgsLineburstSymbolLayer::setColorRamp( QgsColorRamp *ramp )
 | 
			
		||||
{
 | 
			
		||||
  mGradientRamp.reset( ramp );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// QgsFilledLineSymbolLayer
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
QgsFilledLineSymbolLayer::QgsFilledLineSymbolLayer( double width, QgsFillSymbol *fillSymbol )
 | 
			
		||||
  : QgsLineSymbolLayer()
 | 
			
		||||
{
 | 
			
		||||
  mWidth = width;
 | 
			
		||||
  mFill.reset( fillSymbol ? fillSymbol : static_cast<QgsFillSymbol *>( QgsFillSymbol::createSimple( QVariantMap() ) ) );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QgsFilledLineSymbolLayer::~QgsFilledLineSymbolLayer() = default;
 | 
			
		||||
 | 
			
		||||
QgsSymbolLayer *QgsFilledLineSymbolLayer::create( const QVariantMap &props )
 | 
			
		||||
{
 | 
			
		||||
  double width = DEFAULT_SIMPLELINE_WIDTH;
 | 
			
		||||
 | 
			
		||||
  if ( props.contains( QStringLiteral( "line_width" ) ) )
 | 
			
		||||
  {
 | 
			
		||||
    width = props[QStringLiteral( "line_width" )].toDouble();
 | 
			
		||||
  }
 | 
			
		||||
  else if ( props.contains( QStringLiteral( "outline_width" ) ) )
 | 
			
		||||
  {
 | 
			
		||||
    width = props[QStringLiteral( "outline_width" )].toDouble();
 | 
			
		||||
  }
 | 
			
		||||
  else if ( props.contains( QStringLiteral( "width" ) ) )
 | 
			
		||||
  {
 | 
			
		||||
    //pre 2.5 projects used "width"
 | 
			
		||||
    width = props[QStringLiteral( "width" )].toDouble();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  std::unique_ptr<QgsFilledLineSymbolLayer > l = std::make_unique< QgsFilledLineSymbolLayer >( width, QgsFillSymbol::createSimple( props ) );
 | 
			
		||||
 | 
			
		||||
  if ( props.contains( QStringLiteral( "line_width_unit" ) ) )
 | 
			
		||||
  {
 | 
			
		||||
    l->setWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "line_width_unit" )].toString() ) );
 | 
			
		||||
  }
 | 
			
		||||
  else if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
 | 
			
		||||
  {
 | 
			
		||||
    l->setWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )].toString() ) );
 | 
			
		||||
  }
 | 
			
		||||
  else if ( props.contains( QStringLiteral( "width_unit" ) ) )
 | 
			
		||||
  {
 | 
			
		||||
    //pre 2.5 projects used "width_unit"
 | 
			
		||||
    l->setWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "width_unit" )].toString() ) );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if ( props.contains( QStringLiteral( "width_map_unit_scale" ) ) )
 | 
			
		||||
    l->setWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "width_map_unit_scale" )].toString() ) );
 | 
			
		||||
  if ( props.contains( QStringLiteral( "offset" ) ) )
 | 
			
		||||
    l->setOffset( props[QStringLiteral( "offset" )].toDouble() );
 | 
			
		||||
  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
 | 
			
		||||
    l->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
 | 
			
		||||
  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
 | 
			
		||||
    l->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
 | 
			
		||||
  if ( props.contains( QStringLiteral( "joinstyle" ) ) )
 | 
			
		||||
    l->setPenJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( props[QStringLiteral( "joinstyle" )].toString() ) );
 | 
			
		||||
  if ( props.contains( QStringLiteral( "capstyle" ) ) )
 | 
			
		||||
    l->setPenCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( props[QStringLiteral( "capstyle" )].toString() ) );
 | 
			
		||||
 | 
			
		||||
  l->restoreOldDataDefinedProperties( props );
 | 
			
		||||
 | 
			
		||||
  return l.release();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QString QgsFilledLineSymbolLayer::layerType() const
 | 
			
		||||
{
 | 
			
		||||
  return QStringLiteral( "FilledLine" );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void QgsFilledLineSymbolLayer::startRender( QgsSymbolRenderContext &context )
 | 
			
		||||
{
 | 
			
		||||
  if ( mFill )
 | 
			
		||||
  {
 | 
			
		||||
    mFill->startRender( context.renderContext(), context.fields() );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void QgsFilledLineSymbolLayer::stopRender( QgsSymbolRenderContext &context )
 | 
			
		||||
{
 | 
			
		||||
  if ( mFill )
 | 
			
		||||
  {
 | 
			
		||||
    mFill->stopRender( context.renderContext() );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void QgsFilledLineSymbolLayer::renderPolyline( const QPolygonF &points, QgsSymbolRenderContext &context )
 | 
			
		||||
{
 | 
			
		||||
  QPainter *p = context.renderContext().painter();
 | 
			
		||||
  if ( !p || !mFill )
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  double width = mWidth;
 | 
			
		||||
  if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyStrokeWidth ) )
 | 
			
		||||
  {
 | 
			
		||||
    context.setOriginalValueVariable( mWidth );
 | 
			
		||||
    width = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertyStrokeWidth, context.renderContext().expressionContext(), mWidth );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const double scaledWidth = context.renderContext().convertToPainterUnits( width, mWidthUnit, mWidthMapUnitScale );
 | 
			
		||||
 | 
			
		||||
  Qt::PenJoinStyle join = mPenJoinStyle;
 | 
			
		||||
  if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyJoinStyle ) )
 | 
			
		||||
  {
 | 
			
		||||
    context.setOriginalValueVariable( QgsSymbolLayerUtils::encodePenJoinStyle( join ) );
 | 
			
		||||
    QVariant exprVal = mDataDefinedProperties.value( QgsSymbolLayer::PropertyJoinStyle, context.renderContext().expressionContext() );
 | 
			
		||||
    if ( !QgsVariantUtils::isNull( exprVal ) )
 | 
			
		||||
      join = QgsSymbolLayerUtils::decodePenJoinStyle( exprVal.toString() );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Qt::PenCapStyle cap = mPenCapStyle;
 | 
			
		||||
  if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyCapStyle ) )
 | 
			
		||||
  {
 | 
			
		||||
    context.setOriginalValueVariable( QgsSymbolLayerUtils::encodePenCapStyle( cap ) );
 | 
			
		||||
    QVariant exprVal = mDataDefinedProperties.value( QgsSymbolLayer::PropertyCapStyle, context.renderContext().expressionContext() );
 | 
			
		||||
    if ( !QgsVariantUtils::isNull( exprVal ) )
 | 
			
		||||
      cap = QgsSymbolLayerUtils::decodePenCapStyle( exprVal.toString() );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  double offset = mOffset;
 | 
			
		||||
  if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyOffset ) )
 | 
			
		||||
  {
 | 
			
		||||
    context.setOriginalValueVariable( mOffset );
 | 
			
		||||
    offset = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertyOffset, context.renderContext().expressionContext(), offset );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const double prevOpacity = mFill->opacity();
 | 
			
		||||
  mFill->setOpacity( mFill->opacity() * context.opacity() );
 | 
			
		||||
 | 
			
		||||
  const bool prevIsSubsymbol = context.renderContext().flags() & Qgis::RenderContextFlag::RenderingSubSymbol;
 | 
			
		||||
  context.renderContext().setFlag( Qgis::RenderContextFlag::RenderingSubSymbol );
 | 
			
		||||
 | 
			
		||||
  const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
 | 
			
		||||
 | 
			
		||||
  // stroke out the path using the correct line cap/join style. We'll then use this as the fill polygon
 | 
			
		||||
  QPainterPathStroker stroker;
 | 
			
		||||
  stroker.setWidth( scaledWidth );
 | 
			
		||||
  stroker.setCapStyle( cap );
 | 
			
		||||
  stroker.setJoinStyle( join );
 | 
			
		||||
 | 
			
		||||
  QPolygonF polygon;
 | 
			
		||||
  if ( qgsDoubleNear( offset, 0 ) )
 | 
			
		||||
  {
 | 
			
		||||
    QPainterPath path;
 | 
			
		||||
    path.addPolygon( points );
 | 
			
		||||
    const QPainterPath stroke = stroker.createStroke( path ).simplified();
 | 
			
		||||
    const QPolygonF polygon = stroke.toFillPolygon();
 | 
			
		||||
    if ( !polygon.isEmpty() )
 | 
			
		||||
    {
 | 
			
		||||
      mFill->renderPolygon( polygon, /* rings */ nullptr, context.feature(), context.renderContext(), -1, useSelectedColor );
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  else
 | 
			
		||||
  {
 | 
			
		||||
    double scaledOffset = context.renderContext().convertToPainterUnits( offset, mOffsetUnit, mOffsetMapUnitScale );
 | 
			
		||||
    if ( mOffsetUnit == Qgis::RenderUnit::MetersInMapUnits && context.renderContext().flags() & Qgis::RenderContextFlag::RenderSymbolPreview )
 | 
			
		||||
    {
 | 
			
		||||
      // rendering for symbol previews -- a size in meters in map units can't be calculated, so treat the size as millimeters
 | 
			
		||||
      // and clamp it to a reasonable range. It's the best we can do in this situation!
 | 
			
		||||
      scaledOffset = std::min( std::max( context.renderContext().convertToPainterUnits( offset, Qgis::RenderUnit::Millimeters ), 3.0 ), 100.0 );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const QList<QPolygonF> mline = ::offsetLine( points, scaledOffset, context.originalGeometryType() != Qgis::GeometryType::Unknown ? context.originalGeometryType() : Qgis::GeometryType::Line );
 | 
			
		||||
    for ( const QPolygonF &part : mline )
 | 
			
		||||
    {
 | 
			
		||||
      QPainterPath path;
 | 
			
		||||
      path.addPolygon( part );
 | 
			
		||||
      const QPainterPath stroke = stroker.createStroke( path ).simplified();
 | 
			
		||||
      const QPolygonF polygon = stroke.toFillPolygon();
 | 
			
		||||
      if ( !polygon.isEmpty() )
 | 
			
		||||
      {
 | 
			
		||||
        mFill->renderPolygon( polygon, /* rings */ nullptr, context.feature(), context.renderContext(), -1, useSelectedColor );
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  context.renderContext().setFlag( Qgis::RenderContextFlag::RenderingSubSymbol, prevIsSubsymbol );
 | 
			
		||||
 | 
			
		||||
  mFill->setOpacity( prevOpacity );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QVariantMap QgsFilledLineSymbolLayer::properties() const
 | 
			
		||||
{
 | 
			
		||||
  QVariantMap map;
 | 
			
		||||
 | 
			
		||||
  map[QStringLiteral( "line_width" )] = QString::number( mWidth );
 | 
			
		||||
  map[QStringLiteral( "line_width_unit" )] = QgsUnitTypes::encodeUnit( mWidthUnit );
 | 
			
		||||
  map[QStringLiteral( "width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mWidthMapUnitScale );
 | 
			
		||||
  map[QStringLiteral( "joinstyle" )] = QgsSymbolLayerUtils::encodePenJoinStyle( mPenJoinStyle );
 | 
			
		||||
  map[QStringLiteral( "capstyle" )] = QgsSymbolLayerUtils::encodePenCapStyle( mPenCapStyle );
 | 
			
		||||
  map[QStringLiteral( "offset" )] = QString::number( mOffset );
 | 
			
		||||
  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
 | 
			
		||||
  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
 | 
			
		||||
  if ( mFill )
 | 
			
		||||
  {
 | 
			
		||||
    map[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mFill->color() );
 | 
			
		||||
  }
 | 
			
		||||
  return map;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QgsFilledLineSymbolLayer *QgsFilledLineSymbolLayer::clone() const
 | 
			
		||||
{
 | 
			
		||||
  std::unique_ptr< QgsFilledLineSymbolLayer > res( qgis::down_cast< QgsFilledLineSymbolLayer * >( QgsFilledLineSymbolLayer::create( properties() ) ) );
 | 
			
		||||
  copyPaintEffect( res.get() );
 | 
			
		||||
  copyDataDefinedProperties( res.get() );
 | 
			
		||||
  res->setSubSymbol( mFill->clone() );
 | 
			
		||||
  return res.release();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QgsSymbol *QgsFilledLineSymbolLayer::subSymbol()
 | 
			
		||||
{
 | 
			
		||||
  return mFill.get();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool QgsFilledLineSymbolLayer::setSubSymbol( QgsSymbol *symbol )
 | 
			
		||||
{
 | 
			
		||||
  if ( symbol && symbol->type() == Qgis::SymbolType::Fill )
 | 
			
		||||
  {
 | 
			
		||||
    mFill.reset( static_cast<QgsFillSymbol *>( symbol ) );
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
  else
 | 
			
		||||
  {
 | 
			
		||||
    delete symbol;
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
double QgsFilledLineSymbolLayer::estimateMaxBleed( const QgsRenderContext &context ) const
 | 
			
		||||
{
 | 
			
		||||
  if ( mFill )
 | 
			
		||||
  {
 | 
			
		||||
    return QgsSymbolLayerUtils::estimateMaxSymbolBleed( mFill.get(), context );
 | 
			
		||||
  }
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QSet<QString> QgsFilledLineSymbolLayer::usedAttributes( const QgsRenderContext &context ) const
 | 
			
		||||
{
 | 
			
		||||
  QSet<QString> attr = QgsLineSymbolLayer::usedAttributes( context );
 | 
			
		||||
  if ( mFill )
 | 
			
		||||
    attr.unite( mFill->usedAttributes( context ) );
 | 
			
		||||
  return attr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool QgsFilledLineSymbolLayer::hasDataDefinedProperties() const
 | 
			
		||||
{
 | 
			
		||||
  if ( QgsSymbolLayer::hasDataDefinedProperties() )
 | 
			
		||||
    return true;
 | 
			
		||||
  if ( mFill && mFill->hasDataDefinedProperties() )
 | 
			
		||||
    return true;
 | 
			
		||||
  return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void QgsFilledLineSymbolLayer::setColor( const QColor &c )
 | 
			
		||||
{
 | 
			
		||||
  mColor = c;
 | 
			
		||||
  if ( mFill )
 | 
			
		||||
    mFill->setColor( c );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QColor QgsFilledLineSymbolLayer::color() const
 | 
			
		||||
{
 | 
			
		||||
  return mFill ? mFill->color() : mColor;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool QgsFilledLineSymbolLayer::usesMapUnits() const
 | 
			
		||||
{
 | 
			
		||||
  return mWidthUnit == Qgis::RenderUnit::MapUnits || mWidthUnit == Qgis::RenderUnit::MetersInMapUnits
 | 
			
		||||
         || mOffsetUnit == Qgis::RenderUnit::MapUnits || mOffsetUnit == Qgis::RenderUnit::MetersInMapUnits
 | 
			
		||||
         || ( mFill && mFill->usesMapUnits() );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void QgsFilledLineSymbolLayer::setMapUnitScale( const QgsMapUnitScale &scale )
 | 
			
		||||
{
 | 
			
		||||
  QgsLineSymbolLayer::setMapUnitScale( scale );
 | 
			
		||||
  if ( mFill )
 | 
			
		||||
    mFill->setMapUnitScale( scale );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QgsMapUnitScale QgsFilledLineSymbolLayer::mapUnitScale() const
 | 
			
		||||
{
 | 
			
		||||
  if ( mFill )
 | 
			
		||||
  {
 | 
			
		||||
    return mFill->mapUnitScale();
 | 
			
		||||
  }
 | 
			
		||||
  return QgsMapUnitScale();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void QgsFilledLineSymbolLayer::setOutputUnit( Qgis::RenderUnit unit )
 | 
			
		||||
{
 | 
			
		||||
  QgsLineSymbolLayer::setOutputUnit( unit );
 | 
			
		||||
  if ( mFill )
 | 
			
		||||
    mFill->setOutputUnit( unit );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Qgis::RenderUnit QgsFilledLineSymbolLayer::outputUnit() const
 | 
			
		||||
{
 | 
			
		||||
  if ( mFill )
 | 
			
		||||
  {
 | 
			
		||||
    return mFill->outputUnit();
 | 
			
		||||
  }
 | 
			
		||||
  return Qgis::RenderUnit::Unknown;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -28,6 +28,7 @@ class QgsMarkerSymbol;
 | 
			
		||||
class QgsLineSymbol;
 | 
			
		||||
class QgsPathResolver;
 | 
			
		||||
class QgsColorRamp;
 | 
			
		||||
class QgsFillSymbol;
 | 
			
		||||
 | 
			
		||||
#define DEFAULT_SIMPLELINE_COLOR     QColor(35,35,35)
 | 
			
		||||
#define DEFAULT_SIMPLELINE_WIDTH     DEFAULT_LINE_WIDTH
 | 
			
		||||
@ -1363,6 +1364,97 @@ class CORE_EXPORT QgsLineburstSymbolLayer : public QgsAbstractBrushedLineSymbolL
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \ingroup core
 | 
			
		||||
 * \class QgsFilledLineSymbolLayer
 | 
			
		||||
 *
 | 
			
		||||
 * \brief A line symbol layer type which fills a stroked line with a QgsFillSymbol.
 | 
			
		||||
 *
 | 
			
		||||
 * \since QGIS 3.36
 | 
			
		||||
 */
 | 
			
		||||
class CORE_EXPORT QgsFilledLineSymbolLayer : public QgsLineSymbolLayer
 | 
			
		||||
{
 | 
			
		||||
  public:
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Constructor for QgsFilledLineSymbolLayer.
 | 
			
		||||
     *
 | 
			
		||||
     * If a \a fillSymbol is specified, it will be transferred to the symbol layer and used
 | 
			
		||||
     * to fill the inside of the stroked line. If no \a fillSymbol is specified then a default
 | 
			
		||||
     * symbol will be used.
 | 
			
		||||
     */
 | 
			
		||||
    QgsFilledLineSymbolLayer( double width = DEFAULT_SIMPLELINE_WIDTH, QgsFillSymbol *fillSymbol SIP_TRANSFER = nullptr );
 | 
			
		||||
    ~QgsFilledLineSymbolLayer() override;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Creates a new QgsFilledLineSymbolLayer, using the settings
 | 
			
		||||
     * serialized in the \a properties map (corresponding to the output from
 | 
			
		||||
     * QgsFilledLineSymbolLayer::properties() ).
 | 
			
		||||
     */
 | 
			
		||||
    static QgsSymbolLayer *create( const QVariantMap &properties = QVariantMap() ) SIP_FACTORY;
 | 
			
		||||
 | 
			
		||||
    QString layerType() const override;
 | 
			
		||||
    void startRender( QgsSymbolRenderContext &context ) override;
 | 
			
		||||
    void stopRender( QgsSymbolRenderContext &context ) override;
 | 
			
		||||
    void renderPolyline( const QPolygonF &points, QgsSymbolRenderContext &context ) override;
 | 
			
		||||
    QVariantMap properties() const override;
 | 
			
		||||
    QgsFilledLineSymbolLayer *clone() const override SIP_FACTORY;
 | 
			
		||||
    QgsSymbol *subSymbol() override;
 | 
			
		||||
    bool setSubSymbol( QgsSymbol *symbol SIP_TRANSFER ) override;
 | 
			
		||||
    bool hasDataDefinedProperties() const override;
 | 
			
		||||
    void setColor( const QColor &c ) override;
 | 
			
		||||
    QColor color() const override;
 | 
			
		||||
    void setOutputUnit( Qgis::RenderUnit unit ) override;
 | 
			
		||||
    Qgis::RenderUnit outputUnit() const override;
 | 
			
		||||
    bool usesMapUnits() const override;
 | 
			
		||||
    void setMapUnitScale( const QgsMapUnitScale &scale ) override;
 | 
			
		||||
    QgsMapUnitScale mapUnitScale() const override;
 | 
			
		||||
    double estimateMaxBleed( const QgsRenderContext &context ) const override;
 | 
			
		||||
    QSet<QString> usedAttributes( const QgsRenderContext &context ) const override;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns the pen join style used to render the line (e.g. miter, bevel, round, etc).
 | 
			
		||||
     *
 | 
			
		||||
     * \see setPenJoinStyle()
 | 
			
		||||
     */
 | 
			
		||||
    Qt::PenJoinStyle penJoinStyle() const { return mPenJoinStyle; }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Sets the pen join \a style used to render the line (e.g. miter, bevel, round, etc).
 | 
			
		||||
     *
 | 
			
		||||
     * \see penJoinStyle()
 | 
			
		||||
     */
 | 
			
		||||
    void setPenJoinStyle( Qt::PenJoinStyle style ) { mPenJoinStyle = style; }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns the pen cap style used to render the line (e.g. flat, square, round, etc).
 | 
			
		||||
     *
 | 
			
		||||
     * \see setPenCapStyle()
 | 
			
		||||
     */
 | 
			
		||||
    Qt::PenCapStyle penCapStyle() const { return mPenCapStyle; }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Sets the pen cap \a style used to render the line (e.g. flat, square, round, etc).
 | 
			
		||||
     *
 | 
			
		||||
     * \see penCapStyle()
 | 
			
		||||
     */
 | 
			
		||||
    void setPenCapStyle( Qt::PenCapStyle style ) { mPenCapStyle = style; }
 | 
			
		||||
 | 
			
		||||
  private:
 | 
			
		||||
 | 
			
		||||
#ifdef SIP_RUN
 | 
			
		||||
    QgsFilledLineSymbolLayer( const QgsFilledLineSymbolLayer & );
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    //! Fill subsymbol
 | 
			
		||||
    std::unique_ptr< QgsFillSymbol > mFill;
 | 
			
		||||
    Qt::PenJoinStyle mPenJoinStyle = DEFAULT_SIMPLELINE_JOINSTYLE;
 | 
			
		||||
    Qt::PenCapStyle mPenCapStyle = DEFAULT_SIMPLELINE_CAPSTYLE;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -42,6 +42,8 @@ QgsSymbolLayerRegistry::QgsSymbolLayerRegistry()
 | 
			
		||||
                      QgsRasterLineSymbolLayer::create, nullptr, QgsRasterLineSymbolLayer::resolvePaths ) );
 | 
			
		||||
  addSymbolLayerType( new QgsSymbolLayerMetadata( QStringLiteral( "Lineburst" ), QObject::tr( "Lineburst" ), Qgis::SymbolType::Line,
 | 
			
		||||
                      QgsLineburstSymbolLayer::create ) );
 | 
			
		||||
  addSymbolLayerType( new QgsSymbolLayerMetadata( QStringLiteral( "FilledLine" ), QObject::tr( "Filled Line" ), Qgis::SymbolType::Line,
 | 
			
		||||
                      QgsFilledLineSymbolLayer::create ) );
 | 
			
		||||
 | 
			
		||||
  addSymbolLayerType( new QgsSymbolLayerMetadata( QStringLiteral( "SimpleMarker" ), QObject::tr( "Simple Marker" ), Qgis::SymbolType::Marker,
 | 
			
		||||
                      QgsSimpleMarkerSymbolLayer::create, QgsSimpleMarkerSymbolLayer::createFromSld ) );
 | 
			
		||||
 | 
			
		||||
@ -82,6 +82,7 @@ static void _initWidgetFunctions()
 | 
			
		||||
  _initWidgetFunction( QStringLiteral( "InterpolatedLine" ), QgsInterpolatedLineSymbolLayerWidget::create );
 | 
			
		||||
  _initWidgetFunction( QStringLiteral( "RasterLine" ), QgsRasterLineSymbolLayerWidget::create );
 | 
			
		||||
  _initWidgetFunction( QStringLiteral( "Lineburst" ), QgsLineburstSymbolLayerWidget::create );
 | 
			
		||||
  _initWidgetFunction( QStringLiteral( "FilledLine" ), QgsFilledLineSymbolLayerWidget::create );
 | 
			
		||||
 | 
			
		||||
  _initWidgetFunction( QStringLiteral( "SimpleMarker" ), QgsSimpleMarkerSymbolLayerWidget::create );
 | 
			
		||||
  _initWidgetFunction( QStringLiteral( "FilledMarker" ), QgsFilledMarkerSymbolLayerWidget::create );
 | 
			
		||||
 | 
			
		||||
@ -5333,3 +5333,117 @@ QgsSymbolLayer *QgsLineburstSymbolLayerWidget::symbolLayer()
 | 
			
		||||
{
 | 
			
		||||
  return mLayer;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// QgsFilledLineSymbolLayerWidget
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
QgsFilledLineSymbolLayerWidget::QgsFilledLineSymbolLayerWidget( QgsVectorLayer *vl, QWidget *parent )
 | 
			
		||||
  : QgsSymbolLayerWidget( parent, vl )
 | 
			
		||||
{
 | 
			
		||||
  mLayer = nullptr;
 | 
			
		||||
  setupUi( this );
 | 
			
		||||
 | 
			
		||||
  mPenWidthUnitWidget->setUnits( QgsUnitTypes::RenderUnitList() << Qgis::RenderUnit::Millimeters << Qgis::RenderUnit::MetersInMapUnits << Qgis::RenderUnit::MapUnits << Qgis::RenderUnit::Pixels
 | 
			
		||||
                                 << Qgis::RenderUnit::Points << Qgis::RenderUnit::Inches );
 | 
			
		||||
  mOffsetUnitWidget->setUnits( QgsUnitTypes::RenderUnitList() << Qgis::RenderUnit::Millimeters << Qgis::RenderUnit::MetersInMapUnits << Qgis::RenderUnit::MapUnits << Qgis::RenderUnit::Pixels
 | 
			
		||||
                               << Qgis::RenderUnit::Points << Qgis::RenderUnit::Inches );
 | 
			
		||||
 | 
			
		||||
  connect( mPenWidthUnitWidget, &QgsUnitSelectionWidget::changed, this, [ = ]
 | 
			
		||||
  {
 | 
			
		||||
    if ( mLayer )
 | 
			
		||||
    {
 | 
			
		||||
      mLayer->setWidthUnit( mPenWidthUnitWidget->unit() );
 | 
			
		||||
      mLayer->setWidthMapUnitScale( mPenWidthUnitWidget->getMapUnitScale() );
 | 
			
		||||
      emit changed();
 | 
			
		||||
    }
 | 
			
		||||
  } );
 | 
			
		||||
 | 
			
		||||
  connect( spinWidth, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, [ = ]
 | 
			
		||||
  {
 | 
			
		||||
    if ( mLayer )
 | 
			
		||||
    {
 | 
			
		||||
      mLayer->setWidth( spinWidth->value() );
 | 
			
		||||
      emit changed();
 | 
			
		||||
    }
 | 
			
		||||
  } );
 | 
			
		||||
 | 
			
		||||
  connect( mOffsetUnitWidget, &QgsUnitSelectionWidget::changed, this, [ = ]
 | 
			
		||||
  {
 | 
			
		||||
    if ( mLayer )
 | 
			
		||||
    {
 | 
			
		||||
      mLayer->setOffsetUnit( mOffsetUnitWidget->unit() );
 | 
			
		||||
      mLayer->setOffsetMapUnitScale( mOffsetUnitWidget->getMapUnitScale() );
 | 
			
		||||
      emit changed();
 | 
			
		||||
    }
 | 
			
		||||
  } );
 | 
			
		||||
 | 
			
		||||
  spinOffset->setClearValue( 0.0 );
 | 
			
		||||
  connect( spinOffset, qOverload< double >( &QDoubleSpinBox::valueChanged ), this, [ = ]( double val )
 | 
			
		||||
  {
 | 
			
		||||
    if ( mLayer )
 | 
			
		||||
    {
 | 
			
		||||
      mLayer->setOffset( val );
 | 
			
		||||
      emit changed();
 | 
			
		||||
    }
 | 
			
		||||
  } );
 | 
			
		||||
 | 
			
		||||
  connect( cboCapStyle, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, [ = ]
 | 
			
		||||
  {
 | 
			
		||||
    if ( mLayer )
 | 
			
		||||
    {
 | 
			
		||||
      mLayer->setPenCapStyle( cboCapStyle->penCapStyle() );
 | 
			
		||||
      emit changed();
 | 
			
		||||
    }
 | 
			
		||||
  } );
 | 
			
		||||
  connect( cboJoinStyle, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, [ = ]
 | 
			
		||||
  {
 | 
			
		||||
    if ( mLayer )
 | 
			
		||||
    {
 | 
			
		||||
      mLayer->setPenJoinStyle( cboJoinStyle->penJoinStyle() );
 | 
			
		||||
      emit changed();
 | 
			
		||||
    }
 | 
			
		||||
  } );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QgsFilledLineSymbolLayerWidget::~QgsFilledLineSymbolLayerWidget() = default;
 | 
			
		||||
 | 
			
		||||
void QgsFilledLineSymbolLayerWidget::setSymbolLayer( QgsSymbolLayer *layer )
 | 
			
		||||
{
 | 
			
		||||
  if ( !layer )
 | 
			
		||||
  {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if ( layer->layerType() != QLatin1String( "FilledLine" ) )
 | 
			
		||||
  {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  mLayer = dynamic_cast<QgsFilledLineSymbolLayer *>( layer );
 | 
			
		||||
  if ( !mLayer )
 | 
			
		||||
  {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  whileBlocking( spinWidth )->setValue( mLayer->width() );
 | 
			
		||||
  whileBlocking( mPenWidthUnitWidget )->setUnit( mLayer->widthUnit() );
 | 
			
		||||
  whileBlocking( mPenWidthUnitWidget )->setMapUnitScale( mLayer->widthMapUnitScale() );
 | 
			
		||||
 | 
			
		||||
  whileBlocking( mOffsetUnitWidget )->setUnit( mLayer->offsetUnit() );
 | 
			
		||||
  whileBlocking( mOffsetUnitWidget )->setMapUnitScale( mLayer->offsetMapUnitScale() );
 | 
			
		||||
  whileBlocking( spinOffset )->setValue( mLayer->offset() );
 | 
			
		||||
 | 
			
		||||
  whileBlocking( cboJoinStyle )->setPenJoinStyle( mLayer->penJoinStyle() );
 | 
			
		||||
  whileBlocking( cboCapStyle )->setPenCapStyle( mLayer->penCapStyle() );
 | 
			
		||||
 | 
			
		||||
  registerDataDefinedButton( mPenWidthDDBtn, QgsSymbolLayer::PropertyStrokeWidth );
 | 
			
		||||
  registerDataDefinedButton( mOffsetDDBtn, QgsSymbolLayer::PropertyOffset );
 | 
			
		||||
  registerDataDefinedButton( mJoinStyleDDBtn, QgsSymbolLayer::PropertyJoinStyle );
 | 
			
		||||
  registerDataDefinedButton( mCapStyleDDBtn, QgsSymbolLayer::PropertyCapStyle );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QgsSymbolLayer *QgsFilledLineSymbolLayerWidget::symbolLayer()
 | 
			
		||||
{
 | 
			
		||||
  return mLayer;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -915,6 +915,50 @@ class GUI_EXPORT QgsLineburstSymbolLayerWidget : public QgsSymbolLayerWidget, pr
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
///////////
 | 
			
		||||
 | 
			
		||||
#include "ui_widget_filledline.h"
 | 
			
		||||
 | 
			
		||||
class QgsFilledLineSymbolLayer;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * \ingroup gui
 | 
			
		||||
 * \class QgsFilledLineSymbolLayerWidget
 | 
			
		||||
 * A widget for configuring QgsFilledLineSymbolLayer.
 | 
			
		||||
 * \since QGIS 3.36
 | 
			
		||||
 */
 | 
			
		||||
class GUI_EXPORT QgsFilledLineSymbolLayerWidget : public QgsSymbolLayerWidget, private Ui::WidgetFilledLine
 | 
			
		||||
{
 | 
			
		||||
    Q_OBJECT
 | 
			
		||||
 | 
			
		||||
  public:
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Constructor for QgsFilledLineSymbolLayerWidget.
 | 
			
		||||
     * \param vl associated vector layer
 | 
			
		||||
     * \param parent parent widget
 | 
			
		||||
     */
 | 
			
		||||
    QgsFilledLineSymbolLayerWidget( QgsVectorLayer *vl, QWidget *parent SIP_TRANSFERTHIS = nullptr );
 | 
			
		||||
 | 
			
		||||
    ~QgsFilledLineSymbolLayerWidget() override;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Creates a new QgsFilledLineSymbolLayerWidget.
 | 
			
		||||
     * \param vl associated vector layer
 | 
			
		||||
     */
 | 
			
		||||
    static QgsSymbolLayerWidget *create( QgsVectorLayer *vl ) SIP_FACTORY { return new QgsFilledLineSymbolLayerWidget( vl ); }
 | 
			
		||||
 | 
			
		||||
    void setSymbolLayer( QgsSymbolLayer *layer ) override;
 | 
			
		||||
    QgsSymbolLayer *symbolLayer() override;
 | 
			
		||||
 | 
			
		||||
  private:
 | 
			
		||||
 | 
			
		||||
    QgsFilledLineSymbolLayer *mLayer = nullptr;
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
///////////
 | 
			
		||||
 | 
			
		||||
#include "ui_widget_svgfill.h"
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										223
									
								
								src/ui/symbollayer/widget_filledline.ui
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										223
									
								
								src/ui/symbollayer/widget_filledline.ui
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,223 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<ui version="4.0">
 | 
			
		||||
 <class>WidgetFilledLine</class>
 | 
			
		||||
 <widget class="QWidget" name="WidgetFilledLine">
 | 
			
		||||
  <property name="geometry">
 | 
			
		||||
   <rect>
 | 
			
		||||
    <x>0</x>
 | 
			
		||||
    <y>0</y>
 | 
			
		||||
    <width>432</width>
 | 
			
		||||
    <height>232</height>
 | 
			
		||||
   </rect>
 | 
			
		||||
  </property>
 | 
			
		||||
  <property name="windowTitle">
 | 
			
		||||
   <string notr="true">Form</string>
 | 
			
		||||
  </property>
 | 
			
		||||
  <layout class="QGridLayout" name="gridLayout">
 | 
			
		||||
   <item row="3" column="2">
 | 
			
		||||
    <widget class="QgsPropertyOverrideButton" name="mCapStyleDDBtn">
 | 
			
		||||
     <property name="text">
 | 
			
		||||
      <string>…</string>
 | 
			
		||||
     </property>
 | 
			
		||||
    </widget>
 | 
			
		||||
   </item>
 | 
			
		||||
   <item row="3" column="1">
 | 
			
		||||
    <widget class="QgsPenCapStyleComboBox" name="cboCapStyle"/>
 | 
			
		||||
   </item>
 | 
			
		||||
   <item row="1" column="2">
 | 
			
		||||
    <widget class="QgsPropertyOverrideButton" name="mOffsetDDBtn">
 | 
			
		||||
     <property name="text">
 | 
			
		||||
      <string>…</string>
 | 
			
		||||
     </property>
 | 
			
		||||
    </widget>
 | 
			
		||||
   </item>
 | 
			
		||||
   <item row="0" column="1">
 | 
			
		||||
    <layout class="QHBoxLayout" name="horizontalLayout_2">
 | 
			
		||||
     <item>
 | 
			
		||||
      <widget class="QgsDoubleSpinBox" name="spinWidth">
 | 
			
		||||
       <property name="sizePolicy">
 | 
			
		||||
        <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
 | 
			
		||||
         <horstretch>1</horstretch>
 | 
			
		||||
         <verstretch>0</verstretch>
 | 
			
		||||
        </sizepolicy>
 | 
			
		||||
       </property>
 | 
			
		||||
       <property name="specialValueText">
 | 
			
		||||
        <string>Hairline</string>
 | 
			
		||||
       </property>
 | 
			
		||||
       <property name="decimals">
 | 
			
		||||
        <number>6</number>
 | 
			
		||||
       </property>
 | 
			
		||||
       <property name="maximum">
 | 
			
		||||
        <double>100000.000000000000000</double>
 | 
			
		||||
       </property>
 | 
			
		||||
       <property name="singleStep">
 | 
			
		||||
        <double>0.200000000000000</double>
 | 
			
		||||
       </property>
 | 
			
		||||
       <property name="value">
 | 
			
		||||
        <double>1.000000000000000</double>
 | 
			
		||||
       </property>
 | 
			
		||||
       <property name="showClearButton" stdset="0">
 | 
			
		||||
        <bool>true</bool>
 | 
			
		||||
       </property>
 | 
			
		||||
      </widget>
 | 
			
		||||
     </item>
 | 
			
		||||
     <item>
 | 
			
		||||
      <widget class="QgsUnitSelectionWidget" name="mPenWidthUnitWidget" native="true">
 | 
			
		||||
       <property name="minimumSize">
 | 
			
		||||
        <size>
 | 
			
		||||
         <width>0</width>
 | 
			
		||||
         <height>0</height>
 | 
			
		||||
        </size>
 | 
			
		||||
       </property>
 | 
			
		||||
       <property name="focusPolicy">
 | 
			
		||||
        <enum>Qt::StrongFocus</enum>
 | 
			
		||||
       </property>
 | 
			
		||||
      </widget>
 | 
			
		||||
     </item>
 | 
			
		||||
    </layout>
 | 
			
		||||
   </item>
 | 
			
		||||
   <item row="0" column="0">
 | 
			
		||||
    <widget class="QLabel" name="label_2">
 | 
			
		||||
     <property name="text">
 | 
			
		||||
      <string>Stroke width</string>
 | 
			
		||||
     </property>
 | 
			
		||||
    </widget>
 | 
			
		||||
   </item>
 | 
			
		||||
   <item row="3" column="0">
 | 
			
		||||
    <widget class="QLabel" name="label_6">
 | 
			
		||||
     <property name="text">
 | 
			
		||||
      <string>Cap style</string>
 | 
			
		||||
     </property>
 | 
			
		||||
    </widget>
 | 
			
		||||
   </item>
 | 
			
		||||
   <item row="6" column="0">
 | 
			
		||||
    <spacer name="verticalSpacer">
 | 
			
		||||
     <property name="orientation">
 | 
			
		||||
      <enum>Qt::Vertical</enum>
 | 
			
		||||
     </property>
 | 
			
		||||
     <property name="sizeHint" stdset="0">
 | 
			
		||||
      <size>
 | 
			
		||||
       <width>20</width>
 | 
			
		||||
       <height>40</height>
 | 
			
		||||
      </size>
 | 
			
		||||
     </property>
 | 
			
		||||
    </spacer>
 | 
			
		||||
   </item>
 | 
			
		||||
   <item row="2" column="1">
 | 
			
		||||
    <widget class="QgsPenJoinStyleComboBox" name="cboJoinStyle"/>
 | 
			
		||||
   </item>
 | 
			
		||||
   <item row="1" column="0">
 | 
			
		||||
    <widget class="QLabel" name="label_4">
 | 
			
		||||
     <property name="text">
 | 
			
		||||
      <string>Offset</string>
 | 
			
		||||
     </property>
 | 
			
		||||
    </widget>
 | 
			
		||||
   </item>
 | 
			
		||||
   <item row="0" column="2">
 | 
			
		||||
    <widget class="QgsPropertyOverrideButton" name="mPenWidthDDBtn">
 | 
			
		||||
     <property name="text">
 | 
			
		||||
      <string>…</string>
 | 
			
		||||
     </property>
 | 
			
		||||
    </widget>
 | 
			
		||||
   </item>
 | 
			
		||||
   <item row="1" column="1">
 | 
			
		||||
    <layout class="QHBoxLayout" name="horizontalLayout_3">
 | 
			
		||||
     <item>
 | 
			
		||||
      <widget class="QgsDoubleSpinBox" name="spinOffset">
 | 
			
		||||
       <property name="sizePolicy">
 | 
			
		||||
        <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
 | 
			
		||||
         <horstretch>1</horstretch>
 | 
			
		||||
         <verstretch>0</verstretch>
 | 
			
		||||
        </sizepolicy>
 | 
			
		||||
       </property>
 | 
			
		||||
       <property name="styleSheet">
 | 
			
		||||
        <string notr="true"/>
 | 
			
		||||
       </property>
 | 
			
		||||
       <property name="decimals">
 | 
			
		||||
        <number>6</number>
 | 
			
		||||
       </property>
 | 
			
		||||
       <property name="minimum">
 | 
			
		||||
        <double>-100000.000000000000000</double>
 | 
			
		||||
       </property>
 | 
			
		||||
       <property name="maximum">
 | 
			
		||||
        <double>100000.000000000000000</double>
 | 
			
		||||
       </property>
 | 
			
		||||
       <property name="singleStep">
 | 
			
		||||
        <double>0.200000000000000</double>
 | 
			
		||||
       </property>
 | 
			
		||||
      </widget>
 | 
			
		||||
     </item>
 | 
			
		||||
     <item>
 | 
			
		||||
      <widget class="QgsUnitSelectionWidget" name="mOffsetUnitWidget" native="true">
 | 
			
		||||
       <property name="minimumSize">
 | 
			
		||||
        <size>
 | 
			
		||||
         <width>0</width>
 | 
			
		||||
         <height>0</height>
 | 
			
		||||
        </size>
 | 
			
		||||
       </property>
 | 
			
		||||
       <property name="focusPolicy">
 | 
			
		||||
        <enum>Qt::StrongFocus</enum>
 | 
			
		||||
       </property>
 | 
			
		||||
      </widget>
 | 
			
		||||
     </item>
 | 
			
		||||
    </layout>
 | 
			
		||||
   </item>
 | 
			
		||||
   <item row="2" column="0">
 | 
			
		||||
    <widget class="QLabel" name="label_5">
 | 
			
		||||
     <property name="text">
 | 
			
		||||
      <string>Join style</string>
 | 
			
		||||
     </property>
 | 
			
		||||
    </widget>
 | 
			
		||||
   </item>
 | 
			
		||||
   <item row="2" column="2">
 | 
			
		||||
    <widget class="QgsPropertyOverrideButton" name="mJoinStyleDDBtn">
 | 
			
		||||
     <property name="text">
 | 
			
		||||
      <string>…</string>
 | 
			
		||||
     </property>
 | 
			
		||||
    </widget>
 | 
			
		||||
   </item>
 | 
			
		||||
  </layout>
 | 
			
		||||
 </widget>
 | 
			
		||||
 <customwidgets>
 | 
			
		||||
  <customwidget>
 | 
			
		||||
   <class>QgsDoubleSpinBox</class>
 | 
			
		||||
   <extends>QDoubleSpinBox</extends>
 | 
			
		||||
   <header>qgsdoublespinbox.h</header>
 | 
			
		||||
  </customwidget>
 | 
			
		||||
  <customwidget>
 | 
			
		||||
   <class>QgsPropertyOverrideButton</class>
 | 
			
		||||
   <extends>QToolButton</extends>
 | 
			
		||||
   <header>qgspropertyoverridebutton.h</header>
 | 
			
		||||
  </customwidget>
 | 
			
		||||
  <customwidget>
 | 
			
		||||
   <class>QgsUnitSelectionWidget</class>
 | 
			
		||||
   <extends>QWidget</extends>
 | 
			
		||||
   <header>qgsunitselectionwidget.h</header>
 | 
			
		||||
   <container>1</container>
 | 
			
		||||
  </customwidget>
 | 
			
		||||
  <customwidget>
 | 
			
		||||
   <class>QgsPenJoinStyleComboBox</class>
 | 
			
		||||
   <extends>QComboBox</extends>
 | 
			
		||||
   <header>qgspenstylecombobox.h</header>
 | 
			
		||||
  </customwidget>
 | 
			
		||||
  <customwidget>
 | 
			
		||||
   <class>QgsPenCapStyleComboBox</class>
 | 
			
		||||
   <extends>QComboBox</extends>
 | 
			
		||||
   <header>qgspenstylecombobox.h</header>
 | 
			
		||||
  </customwidget>
 | 
			
		||||
 </customwidgets>
 | 
			
		||||
 <tabstops>
 | 
			
		||||
  <tabstop>spinWidth</tabstop>
 | 
			
		||||
  <tabstop>mPenWidthUnitWidget</tabstop>
 | 
			
		||||
  <tabstop>mPenWidthDDBtn</tabstop>
 | 
			
		||||
  <tabstop>spinOffset</tabstop>
 | 
			
		||||
  <tabstop>mOffsetUnitWidget</tabstop>
 | 
			
		||||
  <tabstop>mOffsetDDBtn</tabstop>
 | 
			
		||||
  <tabstop>cboJoinStyle</tabstop>
 | 
			
		||||
  <tabstop>mJoinStyleDDBtn</tabstop>
 | 
			
		||||
  <tabstop>cboCapStyle</tabstop>
 | 
			
		||||
  <tabstop>mCapStyleDDBtn</tabstop>
 | 
			
		||||
 </tabstops>
 | 
			
		||||
 <resources/>
 | 
			
		||||
 <connections/>
 | 
			
		||||
</ui>
 | 
			
		||||
@ -101,6 +101,7 @@ ADD_PYTHON_TEST(PyQgsFeatureSink test_qgsfeaturesink.py)
 | 
			
		||||
ADD_PYTHON_TEST(PyQgsFeatureSource test_qgsfeaturesource.py)
 | 
			
		||||
ADD_PYTHON_TEST(PyQgsFieldComboBoxTest test_qgsfieldcombobox.py)
 | 
			
		||||
ADD_PYTHON_TEST(PyQgsFieldFormattersTest test_qgsfieldformatters.py)
 | 
			
		||||
ADD_PYTHON_TEST(PyQgsFilledLineSymbolLayer test_qgsfilledlinesymbollayer.py)
 | 
			
		||||
ADD_PYTHON_TEST(PyQgsFillSymbolLayers test_qgsfillsymbollayers.py)
 | 
			
		||||
ADD_PYTHON_TEST(PyQgsFontManager test_qgsfontmanager.py)
 | 
			
		||||
ADD_PYTHON_TEST(PyQgsProject test_qgsproject.py)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										160
									
								
								tests/src/python/test_qgsfilledlinesymbollayer.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										160
									
								
								tests/src/python/test_qgsfilledlinesymbollayer.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,160 @@
 | 
			
		||||
"""
 | 
			
		||||
***************************************************************************
 | 
			
		||||
    test_qgsfilledlinesymbollayer.py
 | 
			
		||||
    ---------------------
 | 
			
		||||
    Date                 : November 2023
 | 
			
		||||
    Copyright            : (C) 2023 by Nyall Dawson
 | 
			
		||||
    Email                : nyall dot dawson at gmail dot com
 | 
			
		||||
***************************************************************************
 | 
			
		||||
*                                                                         *
 | 
			
		||||
*   This program is free software; you can redistribute it and/or modify  *
 | 
			
		||||
*   it under the terms of the GNU General Public License as published by  *
 | 
			
		||||
*   the Free Software Foundation; either version 2 of the License, or     *
 | 
			
		||||
*   (at your option) any later version.                                   *
 | 
			
		||||
*                                                                         *
 | 
			
		||||
***************************************************************************
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
__author__ = 'Nyall Dawson'
 | 
			
		||||
__date__ = 'October 2021'
 | 
			
		||||
__copyright__ = '(C) 2021, Nyall Dawson'
 | 
			
		||||
 | 
			
		||||
from typing import Optional
 | 
			
		||||
 | 
			
		||||
import qgis  # NOQA
 | 
			
		||||
from qgis.PyQt.QtCore import Qt
 | 
			
		||||
from qgis.PyQt.QtGui import QColor, QImage, QPainter
 | 
			
		||||
from qgis.core import (
 | 
			
		||||
    QgsFeature,
 | 
			
		||||
    QgsGeometry,
 | 
			
		||||
    QgsLineSymbol,
 | 
			
		||||
    QgsMapSettings,
 | 
			
		||||
    QgsRenderContext,
 | 
			
		||||
    QgsFilledLineSymbolLayer,
 | 
			
		||||
)
 | 
			
		||||
import unittest
 | 
			
		||||
from qgis.testing import start_app, QgisTestCase
 | 
			
		||||
 | 
			
		||||
from utilities import unitTestDataPath
 | 
			
		||||
 | 
			
		||||
start_app()
 | 
			
		||||
TEST_DATA_DIR = unitTestDataPath()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestQgsFilledLineSymbolLayer(QgisTestCase):
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def control_path_prefix(cls):
 | 
			
		||||
        return "symbol_filledline"
 | 
			
		||||
 | 
			
		||||
    def testRender(self):
 | 
			
		||||
        s = QgsLineSymbol()
 | 
			
		||||
        s.deleteSymbolLayer(0)
 | 
			
		||||
 | 
			
		||||
        line = QgsFilledLineSymbolLayer()
 | 
			
		||||
        # color should be passed to subsymbol
 | 
			
		||||
        line.setColor(QColor(255, 0, 0))
 | 
			
		||||
        line.setWidth(8)
 | 
			
		||||
 | 
			
		||||
        s.appendSymbolLayer(line.clone())
 | 
			
		||||
 | 
			
		||||
        g = QgsGeometry.fromWkt('LineString(0 0, 10 10, 10 0)')
 | 
			
		||||
        rendered_image = self.renderGeometry(s, g)
 | 
			
		||||
        self.assertTrue(self.image_check('render', 'render', rendered_image))
 | 
			
		||||
 | 
			
		||||
    def testRenderFlatCap(self):
 | 
			
		||||
        s = QgsLineSymbol()
 | 
			
		||||
        s.deleteSymbolLayer(0)
 | 
			
		||||
 | 
			
		||||
        line = QgsFilledLineSymbolLayer()
 | 
			
		||||
        line.setColor(QColor(255, 0, 0))
 | 
			
		||||
        line.setWidth(8)
 | 
			
		||||
        line.setPenCapStyle(Qt.FlatCap)
 | 
			
		||||
 | 
			
		||||
        s.appendSymbolLayer(line.clone())
 | 
			
		||||
 | 
			
		||||
        g = QgsGeometry.fromWkt('LineString(0 0, 10 10, 10 0)')
 | 
			
		||||
        rendered_image = self.renderGeometry(s, g)
 | 
			
		||||
        self.assertTrue(self.image_check('renderflatcap', 'renderflatcap', rendered_image))
 | 
			
		||||
 | 
			
		||||
    def testRenderMiterJoin(self):
 | 
			
		||||
        s = QgsLineSymbol()
 | 
			
		||||
        s.deleteSymbolLayer(0)
 | 
			
		||||
 | 
			
		||||
        line = QgsFilledLineSymbolLayer()
 | 
			
		||||
        line.setColor(QColor(255, 0, 0))
 | 
			
		||||
        line.setWidth(8)
 | 
			
		||||
        line.setPenJoinStyle(Qt.MiterJoin)
 | 
			
		||||
 | 
			
		||||
        s.appendSymbolLayer(line.clone())
 | 
			
		||||
 | 
			
		||||
        g = QgsGeometry.fromWkt('LineString(0 15, 0 0, 10 10, 10 0)')
 | 
			
		||||
        rendered_image = self.renderGeometry(s, g)
 | 
			
		||||
        self.assertTrue(self.image_check('render_miter', 'render_miter', rendered_image))
 | 
			
		||||
 | 
			
		||||
    def testRenderBevelJoin(self):
 | 
			
		||||
        s = QgsLineSymbol()
 | 
			
		||||
        s.deleteSymbolLayer(0)
 | 
			
		||||
 | 
			
		||||
        line = QgsFilledLineSymbolLayer()
 | 
			
		||||
        line.setColor(QColor(255, 0, 0))
 | 
			
		||||
        line.setWidth(8)
 | 
			
		||||
        line.setPenJoinStyle(Qt.BevelJoin)
 | 
			
		||||
 | 
			
		||||
        s.appendSymbolLayer(line.clone())
 | 
			
		||||
 | 
			
		||||
        g = QgsGeometry.fromWkt('LineString(2 2, 10 10, 10 0)')
 | 
			
		||||
        rendered_image = self.renderGeometry(s, g)
 | 
			
		||||
        self.assertTrue(self.image_check('render_bevel', 'render_bevel', rendered_image))
 | 
			
		||||
 | 
			
		||||
    def testLineOffset(self):
 | 
			
		||||
        s = QgsLineSymbol()
 | 
			
		||||
        s.deleteSymbolLayer(0)
 | 
			
		||||
 | 
			
		||||
        line = QgsFilledLineSymbolLayer()
 | 
			
		||||
        line.setColor(QColor(255, 0, 0))
 | 
			
		||||
        line.setWidth(5)
 | 
			
		||||
        line.setOffset(5)
 | 
			
		||||
 | 
			
		||||
        s.appendSymbolLayer(line.clone())
 | 
			
		||||
 | 
			
		||||
        g = QgsGeometry.fromWkt('LineString(2 2, 10 10, 10 0)')
 | 
			
		||||
        rendered_image = self.renderGeometry(s, g)
 | 
			
		||||
        self.assertTrue(self.image_check('render_offset', 'render_offset', rendered_image))
 | 
			
		||||
 | 
			
		||||
    def renderGeometry(self, symbol, geom, buffer=20):
 | 
			
		||||
        f = QgsFeature()
 | 
			
		||||
        f.setGeometry(geom)
 | 
			
		||||
 | 
			
		||||
        image = QImage(200, 200, QImage.Format_RGB32)
 | 
			
		||||
 | 
			
		||||
        painter = QPainter()
 | 
			
		||||
        ms = QgsMapSettings()
 | 
			
		||||
        extent = geom.get().boundingBox()
 | 
			
		||||
        # buffer extent by 10%
 | 
			
		||||
        if extent.width() > 0:
 | 
			
		||||
            extent = extent.buffered((extent.height() + extent.width()) / buffer)
 | 
			
		||||
        else:
 | 
			
		||||
            extent = extent.buffered(buffer / 2)
 | 
			
		||||
 | 
			
		||||
        ms.setExtent(extent)
 | 
			
		||||
        ms.setOutputSize(image.size())
 | 
			
		||||
        context = QgsRenderContext.fromMapSettings(ms)
 | 
			
		||||
        context.setPainter(painter)
 | 
			
		||||
        context.setScaleFactor(96 / 25.4)  # 96 DPI
 | 
			
		||||
        context.expressionContext().setFeature(f)
 | 
			
		||||
 | 
			
		||||
        painter.begin(image)
 | 
			
		||||
        try:
 | 
			
		||||
            image.fill(QColor(0, 0, 0))
 | 
			
		||||
            symbol.startRender(context)
 | 
			
		||||
            symbol.renderFeature(f, context)
 | 
			
		||||
            symbol.stopRender(context)
 | 
			
		||||
        finally:
 | 
			
		||||
            painter.end()
 | 
			
		||||
 | 
			
		||||
        return image
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
    unittest.main()
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								tests/testdata/control_images/symbol_filledline/expected_render/expected_render.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								tests/testdata/control_images/symbol_filledline/expected_render/expected_render.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 1.2 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								tests/testdata/control_images/symbol_filledline/expected_render_bevel/expected_render_bevel.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								tests/testdata/control_images/symbol_filledline/expected_render_bevel/expected_render_bevel.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 1.2 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								tests/testdata/control_images/symbol_filledline/expected_render_miter/expected_render_miter.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								tests/testdata/control_images/symbol_filledline/expected_render_miter/expected_render_miter.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 1.1 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								tests/testdata/control_images/symbol_filledline/expected_render_offset/expected_render_offset.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								tests/testdata/control_images/symbol_filledline/expected_render_offset/expected_render_offset.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 995 B  | 
							
								
								
									
										
											BIN
										
									
								
								tests/testdata/control_images/symbol_filledline/expected_renderflatcap/expected_renderflatcap.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								tests/testdata/control_images/symbol_filledline/expected_renderflatcap/expected_renderflatcap.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 1.2 KiB  | 
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user