23   const QString skind = 
string.trimmed();
 
   25   if ( skind.compare( QLatin1String( 
"Square" ), Qt::CaseInsensitive ) == 0 )
 
   29   else if ( skind.compare( QLatin1String( 
"Ellipse" ), Qt::CaseInsensitive ) == 0 )
 
   33   else if ( skind.compare( QLatin1String( 
"Circle" ), Qt::CaseInsensitive ) == 0 )
 
   37   else if ( skind.compare( QLatin1String( 
"SVG" ), Qt::CaseInsensitive ) == 0 )
 
   41   else if ( skind.compare( QLatin1String( 
"marker" ), Qt::CaseInsensitive ) == 0 )
 
   50   const QString stype = 
string.trimmed();
 
   54   if ( stype.compare( QLatin1String( 
"Fixed" ), Qt::CaseInsensitive ) == 0 )
 
   63   const QString rotstr = 
string.trimmed();
 
   67   if ( rotstr.compare( QLatin1String( 
"Offset" ), Qt::CaseInsensitive ) == 0 )
 
   71   else if ( rotstr.compare( QLatin1String( 
"Fixed" ), Qt::CaseInsensitive ) == 0 )
 
   80   const QString 
str = 
string.trimmed();
 
   84   if ( 
str.compare( QLatin1String( 
"Text" ), Qt::CaseInsensitive ) == 0 )
 
   88   else if ( 
str.compare( QLatin1String( 
"Buffer" ), Qt::CaseInsensitive ) == 0 )
 
   92   else if ( 
str.compare( QLatin1String( 
"Background" ), Qt::CaseInsensitive ) == 0 )
 
  101   switch ( orientation )
 
  104       return QStringLiteral( 
"horizontal" );
 
  106       return QStringLiteral( 
"vertical" );
 
  108       return QStringLiteral( 
"rotation-based" );
 
  118   const QString cleaned = name.toLower().trimmed();
 
  120   if ( cleaned == QLatin1String( 
"horizontal" ) )
 
  122   else if ( cleaned == QLatin1String( 
"vertical" ) )
 
  124   else if ( cleaned == QLatin1String( 
"rotation-based" ) )
 
  148   const int r = layer->
customProperty( property + 
'R', QVariant( defaultColor.red() ) ).toInt();
 
  149   const int g = layer->
customProperty( property + 
'G', QVariant( defaultColor.green() ) ).toInt();
 
  150   const int b = layer->
customProperty( property + 
'B', QVariant( defaultColor.blue() ) ).toInt();
 
  151   const int a = withAlpha ? layer->
customProperty( property + 
'A', QVariant( defaultColor.alpha() ) ).toInt() : 255;
 
  152   return QColor( r, g, b, a );
 
  159   std::vector<double> pathDistances( numPoints );
 
  161   const double *x = line->
xData();
 
  162   const double *y = line->
yData();
 
  165   pathDistances[0] = 0;
 
  169   for ( 
int i = 1; i < numPoints; ++i )
 
  173     pathDistances[i] = std::sqrt( dx * dx + dy * dy );
 
  179   return generateCurvedTextPlacementPrivate( metrics, line->
xData(), line->
yData(), numPoints, pathDistances, offsetAlongLine, direction, maxConcaveAngle, maxConvexAngle, uprightOnly );
 
  185   return generateCurvedTextPlacementPrivate( metrics, x, y, numPoints, pathDistances, offsetAlongLine, direction, maxConcaveAngle, maxConvexAngle, uprightOnly );
 
  188 QgsTextRendererUtils::CurvePlacementProperties *QgsTextRendererUtils::generateCurvedTextPlacementPrivate( 
const QgsPrecalculatedTextMetrics &metrics, 
const double *x, 
const double *y, 
int numPoints, 
const std::vector<double> &pathDistances, 
double offsetAlongLine, LabelLineDirection direction, 
double maxConcaveAngle, 
double maxConvexAngle, 
bool uprightOnly, 
bool isSecondAttempt )
 
  190   std::unique_ptr< CurvePlacementProperties > output = std::make_unique< CurvePlacementProperties >();
 
  191   output->graphemePlacement.reserve( metrics.
count() );
 
  193   double offsetAlongSegment = offsetAlongLine;
 
  196   while ( index < numPoints && offsetAlongSegment > pathDistances[index] )
 
  198     offsetAlongSegment -= pathDistances[index];
 
  201   if ( index >= numPoints )
 
  203     return output.release();
 
  208   const double segmentLength = pathDistances[index];
 
  212     return output.release();
 
  215   const int characterCount = metrics.
count();
 
  221     double distance = offsetAlongSegment;
 
  222     int endindex = index;
 
  224     double startLabelX = 0;
 
  225     double startLabelY = 0;
 
  226     double endLabelX = 0;
 
  227     double endLabelY = 0;
 
  228     for ( 
int i = 0; i < characterCount; i++ )
 
  231       double characterStartX, characterStartY;
 
  232       if ( !nextCharPosition( characterWidth, pathDistances[endindex], x, y, numPoints, endindex, distance, characterStartX, characterStartY, endLabelX, endLabelY ) )
 
  234         return output.release();
 
  238         startLabelX = characterStartX;
 
  239         startLabelY = characterStartY;
 
  244     const double dx = endLabelX - startLabelX;
 
  245     const double dy = endLabelY - startLabelY;
 
  246     const double lineAngle = std::atan2( -dy, dx ) * 180 / M_PI;
 
  248     if ( lineAngle > 90 || lineAngle < -90 )
 
  250       output->labeledLineSegmentIsRightToLeft = 
true;
 
  254   if ( isSecondAttempt )
 
  258     output->labeledLineSegmentIsRightToLeft = 
false;
 
  259     output->flippedCharacterPlacementToGetUprightLabels = 
true;
 
  262   const double dx = x[index] - x[index - 1];
 
  263   const double dy = y[index] - y[index - 1];
 
  265   double angle = std::atan2( -dy, dx );
 
  267   for ( 
int i = 0; i < characterCount; i++ )
 
  269     const double lastCharacterAngle = 
angle;
 
  272     const double characterWidth = !output->flippedCharacterPlacementToGetUprightLabels ? metrics.
characterWidth( i ) : metrics.
characterWidth( characterCount - i - 1 );
 
  277     double characterStartX = 0;
 
  278     double characterStartY = 0;
 
  279     double characterEndX = 0;
 
  280     double characterEndY = 0;
 
  281     if ( !nextCharPosition( characterWidth, pathDistances[index], x, y, numPoints, index, offsetAlongSegment, characterStartX, characterStartY, characterEndX, characterEndY ) )
 
  283       output->graphemePlacement.clear();
 
  284       return output.release();
 
  288     angle = std::atan2( characterStartY - characterEndY, characterEndX - characterStartX );
 
  290     if ( maxConcaveAngle >= 0 || maxConvexAngle >= 0 )
 
  295       double angleDelta = lastCharacterAngle - 
angle;
 
  297       while ( angleDelta > M_PI )
 
  298         angleDelta -= 2 * M_PI;
 
  299       while ( angleDelta < -M_PI )
 
  300         angleDelta += 2 * M_PI;
 
  301       if ( ( maxConcaveAngle >= 0 && angleDelta > 0 && angleDelta > maxConcaveAngle ) || ( maxConvexAngle >= 0 && angleDelta < 0 && angleDelta < -maxConvexAngle ) )
 
  303         output->graphemePlacement.clear();
 
  304         return output.release();
 
  311     if ( output->flippedCharacterPlacementToGetUprightLabels )
 
  315     characterStartX += dist * std::cos( 
angle + M_PI_2 );
 
  316     characterStartY -= dist * std::sin( 
angle + M_PI_2 );
 
  318     double renderAngle = 
angle;
 
  319     CurvedGraphemePlacement placement;
 
  320     placement.graphemeIndex = !output->flippedCharacterPlacementToGetUprightLabels ? i : characterCount - i - 1;
 
  321     placement.x = characterStartX;
 
  322     placement.y = characterStartY;
 
  323     placement.width = characterWidth;
 
  324     placement.height = characterHeight;
 
  325     if ( output->flippedCharacterPlacementToGetUprightLabels )
 
  328       placement.x += characterWidth * std::cos( renderAngle );
 
  329       placement.y -= characterWidth * std::sin( renderAngle );
 
  332     placement.angle = -renderAngle;
 
  333     output->graphemePlacement.push_back( placement );
 
  336     while ( renderAngle >= 2 * M_PI )
 
  337       renderAngle -= 2 * M_PI;
 
  338     while ( renderAngle < 0 )
 
  339       renderAngle += 2 * M_PI;
 
  341     if ( renderAngle > M_PI_2 && renderAngle < 1.5 * M_PI )
 
  342       output->upsideDownCharCount++;
 
  345   if ( !isSecondAttempt && uprightOnly && output->upsideDownCharCount >= characterCount / 2.0 )
 
  349     return generateCurvedTextPlacementPrivate( metrics, x, y, numPoints, pathDistances, offsetAlongLine, direction, maxConcaveAngle, maxConvexAngle, uprightOnly, 
true );
 
  352   return output.release();
 
  355 bool QgsTextRendererUtils::nextCharPosition( 
double charWidth, 
double segmentLength, 
const double *x, 
const double *y, 
int numPoints, 
int &index, 
double ¤tDistanceAlongSegment, 
double &characterStartX, 
double &characterStartY, 
double &characterEndX, 
double &characterEndY )
 
  364   double segmentStartX = x[index - 1];
 
  365   double segmentStartY = y[index - 1];
 
  367   double segmentEndX = x[index];
 
  368   double segmentEndY = y[index];
 
  370   const double segmentDx = segmentEndX - segmentStartX;
 
  371   const double segmentDy = segmentEndY - segmentStartY;
 
  373   characterStartX = segmentStartX + segmentDx * currentDistanceAlongSegment / segmentLength;
 
  374   characterStartY = segmentStartY + segmentDy * currentDistanceAlongSegment / segmentLength;
 
  380   if ( segmentLength - currentDistanceAlongSegment >= charWidth )
 
  383     currentDistanceAlongSegment += charWidth;
 
  384     characterEndX = segmentStartX + segmentDx * currentDistanceAlongSegment / segmentLength;
 
  385     characterEndY = segmentStartY + segmentDy * currentDistanceAlongSegment / segmentLength;
 
  393       segmentStartX = segmentEndX;
 
  394       segmentStartY = segmentEndY;
 
  396       if ( index >= numPoints ) 
 
  400       segmentEndX = x[index];
 
  401       segmentEndY = y[index];
 
  403     while ( std::sqrt( std::pow( characterStartX - segmentEndX, 2 ) + std::pow( characterStartY - segmentEndY, 2 ) ) < charWidth ); 
 
  406     findLineCircleIntersection( characterStartX, characterStartY, charWidth, segmentStartX, segmentStartY, segmentEndX, segmentEndY, characterEndX, characterEndY );
 
  409     currentDistanceAlongSegment = std::sqrt( std::pow( segmentStartX - characterEndX, 2 ) + std::pow( segmentStartY - characterEndY, 2 ) );
 
  414 void QgsTextRendererUtils::findLineCircleIntersection( 
double cx, 
double cy, 
double radius, 
double x1, 
double y1, 
double x2, 
double y2, 
double &xRes, 
double &yRes )
 
  416   double multiplier = 1;
 
  428     radius *= multiplier;
 
  431   const double dx = x2 - x1;
 
  432   const double dy = y2 - y1;
 
  434   const double A = dx * dx + dy * dy;
 
  435   const double B = 2 * ( dx * ( x1 - cx ) + dy * ( y1 - cy ) );
 
  436   const double C = ( x1 - cx ) * ( x1 - cx ) + ( y1 - cy ) * ( y1 - cy ) - radius * radius;
 
  438   const double det = B * B - 4 * A * C;
 
  439   if ( A <= 0.000000000001 || det < 0 )
 
  446     const double t = -B / ( 2 * A );
 
  455     const double t = ( -B + std::sqrt( det ) ) / ( 2 * A );
 
  460   if ( multiplier != 1 )