LCOV - code coverage report
Current view: top level - core/textrenderer - qgstextrendererutils.cpp (source / functions) Hit Total Coverage
Test: coverage.info.cleaned Lines: 0 258 0.0 %
Date: 2021-04-10 08:29:14 Functions: 0 0 -
Branches: 0 0 -

           Branch data     Line data    Source code
       1                 :            : /***************************************************************************
       2                 :            :   qgstextrendererutils.h
       3                 :            :   -----------------
       4                 :            :    begin                : May 2020
       5                 :            :    copyright            : (C) Nyall Dawson
       6                 :            :    email                : nyall dot dawson at gmail dot com
       7                 :            :  ***************************************************************************
       8                 :            :  *                                                                         *
       9                 :            :  *   This program is free software; you can redistribute it and/or modify  *
      10                 :            :  *   it under the terms of the GNU General Public License as published by  *
      11                 :            :  *   the Free Software Foundation; either version 2 of the License, or     *
      12                 :            :  *   (at your option) any later version.                                   *
      13                 :            :  *                                                                         *
      14                 :            :  ***************************************************************************/
      15                 :            : 
      16                 :            : #include "qgstextrendererutils.h"
      17                 :            : #include "qgsvectorlayer.h"
      18                 :            : #include "qgslinestring.h"
      19                 :            : 
      20                 :          0 : QgsTextBackgroundSettings::ShapeType QgsTextRendererUtils::decodeShapeType( const QString &string )
      21                 :            : {
      22                 :          0 :   QgsTextBackgroundSettings::ShapeType shpkind = QgsTextBackgroundSettings::ShapeRectangle;
      23                 :          0 :   const QString skind = string.trimmed();
      24                 :            : 
      25                 :          0 :   if ( skind.compare( QLatin1String( "Square" ), Qt::CaseInsensitive ) == 0 )
      26                 :            :   {
      27                 :          0 :     shpkind = QgsTextBackgroundSettings::ShapeSquare;
      28                 :          0 :   }
      29                 :          0 :   else if ( skind.compare( QLatin1String( "Ellipse" ), Qt::CaseInsensitive ) == 0 )
      30                 :            :   {
      31                 :          0 :     shpkind = QgsTextBackgroundSettings::ShapeEllipse;
      32                 :          0 :   }
      33                 :          0 :   else if ( skind.compare( QLatin1String( "Circle" ), Qt::CaseInsensitive ) == 0 )
      34                 :            :   {
      35                 :          0 :     shpkind = QgsTextBackgroundSettings::ShapeCircle;
      36                 :          0 :   }
      37                 :          0 :   else if ( skind.compare( QLatin1String( "SVG" ), Qt::CaseInsensitive ) == 0 )
      38                 :            :   {
      39                 :          0 :     shpkind = QgsTextBackgroundSettings::ShapeSVG;
      40                 :          0 :   }
      41                 :          0 :   else if ( skind.compare( QLatin1String( "marker" ), Qt::CaseInsensitive ) == 0 )
      42                 :            :   {
      43                 :          0 :     shpkind = QgsTextBackgroundSettings::ShapeMarkerSymbol;
      44                 :          0 :   }
      45                 :          0 :   return shpkind;
      46                 :          0 : }
      47                 :            : 
      48                 :          0 : QgsTextBackgroundSettings::SizeType QgsTextRendererUtils::decodeBackgroundSizeType( const QString &string )
      49                 :            : {
      50                 :          0 :   const QString stype = string.trimmed();
      51                 :            :   // "Buffer"
      52                 :          0 :   QgsTextBackgroundSettings::SizeType sizType = QgsTextBackgroundSettings::SizeBuffer;
      53                 :            : 
      54                 :          0 :   if ( stype.compare( QLatin1String( "Fixed" ), Qt::CaseInsensitive ) == 0 )
      55                 :            :   {
      56                 :          0 :     sizType = QgsTextBackgroundSettings::SizeFixed;
      57                 :          0 :   }
      58                 :          0 :   return sizType;
      59                 :          0 : }
      60                 :            : 
      61                 :          0 : QgsTextBackgroundSettings::RotationType QgsTextRendererUtils::decodeBackgroundRotationType( const QString &string )
      62                 :            : {
      63                 :          0 :   const QString rotstr = string.trimmed();
      64                 :            :   // "Sync"
      65                 :          0 :   QgsTextBackgroundSettings::RotationType rottype = QgsTextBackgroundSettings::RotationSync;
      66                 :            : 
      67                 :          0 :   if ( rotstr.compare( QLatin1String( "Offset" ), Qt::CaseInsensitive ) == 0 )
      68                 :            :   {
      69                 :          0 :     rottype = QgsTextBackgroundSettings::RotationOffset;
      70                 :          0 :   }
      71                 :          0 :   else if ( rotstr.compare( QLatin1String( "Fixed" ), Qt::CaseInsensitive ) == 0 )
      72                 :            :   {
      73                 :          0 :     rottype = QgsTextBackgroundSettings::RotationFixed;
      74                 :          0 :   }
      75                 :          0 :   return rottype;
      76                 :          0 : }
      77                 :            : 
      78                 :          0 : QgsTextShadowSettings::ShadowPlacement QgsTextRendererUtils::decodeShadowPlacementType( const QString &string )
      79                 :            : {
      80                 :          0 :   const QString str = string.trimmed();
      81                 :            :   // "Lowest"
      82                 :          0 :   QgsTextShadowSettings::ShadowPlacement shdwtype = QgsTextShadowSettings::ShadowLowest;
      83                 :            : 
      84                 :          0 :   if ( str.compare( QLatin1String( "Text" ), Qt::CaseInsensitive ) == 0 )
      85                 :            :   {
      86                 :          0 :     shdwtype = QgsTextShadowSettings::ShadowText;
      87                 :          0 :   }
      88                 :          0 :   else if ( str.compare( QLatin1String( "Buffer" ), Qt::CaseInsensitive ) == 0 )
      89                 :            :   {
      90                 :          0 :     shdwtype = QgsTextShadowSettings::ShadowBuffer;
      91                 :          0 :   }
      92                 :          0 :   else if ( str.compare( QLatin1String( "Background" ), Qt::CaseInsensitive ) == 0 )
      93                 :            :   {
      94                 :          0 :     shdwtype = QgsTextShadowSettings::ShadowShape;
      95                 :          0 :   }
      96                 :          0 :   return shdwtype;
      97                 :          0 : }
      98                 :            : 
      99                 :          0 : QString QgsTextRendererUtils::encodeTextOrientation( QgsTextFormat::TextOrientation orientation )
     100                 :            : {
     101                 :          0 :   switch ( orientation )
     102                 :            :   {
     103                 :            :     case QgsTextFormat::HorizontalOrientation:
     104                 :          0 :       return QStringLiteral( "horizontal" );
     105                 :            :     case QgsTextFormat::VerticalOrientation:
     106                 :          0 :       return QStringLiteral( "vertical" );
     107                 :            :     case QgsTextFormat::RotationBasedOrientation:
     108                 :          0 :       return QStringLiteral( "rotation-based" );
     109                 :            :   }
     110                 :          0 :   return QString();
     111                 :          0 : }
     112                 :            : 
     113                 :          0 : QgsTextFormat::TextOrientation QgsTextRendererUtils::decodeTextOrientation( const QString &name, bool *ok )
     114                 :            : {
     115                 :          0 :   if ( ok )
     116                 :          0 :     *ok = true;
     117                 :            : 
     118                 :          0 :   QString cleaned = name.toLower().trimmed();
     119                 :            : 
     120                 :          0 :   if ( cleaned == QLatin1String( "horizontal" ) )
     121                 :          0 :     return QgsTextFormat::HorizontalOrientation;
     122                 :          0 :   else if ( cleaned == QLatin1String( "vertical" ) )
     123                 :          0 :     return QgsTextFormat::VerticalOrientation;
     124                 :          0 :   else if ( cleaned == QLatin1String( "rotation-based" ) )
     125                 :          0 :     return QgsTextFormat::RotationBasedOrientation;
     126                 :            : 
     127                 :          0 :   if ( ok )
     128                 :          0 :     *ok = false;
     129                 :          0 :   return QgsTextFormat::HorizontalOrientation;
     130                 :          0 : }
     131                 :            : 
     132                 :          0 : QgsUnitTypes::RenderUnit QgsTextRendererUtils::convertFromOldLabelUnit( int val )
     133                 :            : {
     134                 :          0 :   if ( val == 0 )
     135                 :          0 :     return QgsUnitTypes::RenderPoints;
     136                 :          0 :   else if ( val == 1 )
     137                 :          0 :     return QgsUnitTypes::RenderMillimeters;
     138                 :          0 :   else if ( val == 2 )
     139                 :          0 :     return QgsUnitTypes::RenderMapUnits;
     140                 :          0 :   else if ( val == 3 )
     141                 :          0 :     return QgsUnitTypes::RenderPercentage;
     142                 :            :   else
     143                 :          0 :     return QgsUnitTypes::RenderMillimeters;
     144                 :          0 : }
     145                 :            : 
     146                 :          0 : QColor QgsTextRendererUtils::readColor( QgsVectorLayer *layer, const QString &property, const QColor &defaultColor, bool withAlpha )
     147                 :            : {
     148                 :          0 :   int r = layer->customProperty( property + 'R', QVariant( defaultColor.red() ) ).toInt();
     149                 :          0 :   int g = layer->customProperty( property + 'G', QVariant( defaultColor.green() ) ).toInt();
     150                 :          0 :   int b = layer->customProperty( property + 'B', QVariant( defaultColor.blue() ) ).toInt();
     151                 :          0 :   int a = withAlpha ? layer->customProperty( property + 'A', QVariant( defaultColor.alpha() ) ).toInt() : 255;
     152                 :          0 :   return QColor( r, g, b, a );
     153                 :          0 : }
     154                 :            : 
     155                 :            : #if 0
     156                 :            : QgsTextRendererUtils::CurvePlacementProperties *QgsTextRendererUtils::generateCurvedTextPlacement( const QgsPrecalculatedTextMetrics &metrics, const QgsLineString *line, double offsetAlongLine, LabelLineDirection direction, double maxConcaveAngle, double maxConvexAngle, bool uprightOnly )
     157                 :            : {
     158                 :            :   const int numPoints = line->numPoints();
     159                 :            :   std::vector<double> pathDistances( numPoints );
     160                 :            : 
     161                 :            :   const double *x = line->xData();
     162                 :            :   const double *y = line->yData();
     163                 :            :   double dx, dy;
     164                 :            : 
     165                 :            :   pathDistances[0] = 0;
     166                 :            :   double prevX = *x++;
     167                 :            :   double prevY = *y++;
     168                 :            : 
     169                 :            :   for ( int i = 1; i < numPoints; ++i )
     170                 :            :   {
     171                 :            :     dx = *x - prevX;
     172                 :            :     dy = *y - prevY;
     173                 :            :     pathDistances[i] = std::sqrt( dx * dx + dy * dy );
     174                 :            : 
     175                 :            :     prevX = *x++;
     176                 :            :     prevY = *y++;
     177                 :            :   }
     178                 :            : 
     179                 :            :   return generateCurvedTextPlacementPrivate( metrics, line->xData(), line->yData(), numPoints, pathDistances, offsetAlongLine, direction, maxConcaveAngle, maxConvexAngle, uprightOnly );
     180                 :            : }
     181                 :            : #endif
     182                 :            : 
     183                 :          0 : QgsTextRendererUtils::CurvePlacementProperties *QgsTextRendererUtils::generateCurvedTextPlacement( 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 )
     184                 :            : {
     185                 :          0 :   return generateCurvedTextPlacementPrivate( metrics, x, y, numPoints, pathDistances, offsetAlongLine, direction, maxConcaveAngle, maxConvexAngle, uprightOnly );
     186                 :            : }
     187                 :            : 
     188                 :          0 : 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 )
     189                 :            : {
     190                 :          0 :   std::unique_ptr< CurvePlacementProperties > output = std::make_unique< CurvePlacementProperties >();
     191                 :          0 :   output->graphemePlacement.reserve( metrics.count() );
     192                 :            : 
     193                 :          0 :   double offsetAlongSegment = offsetAlongLine;
     194                 :          0 :   int index = 1;
     195                 :            :   // Find index of segment corresponding to starting offset
     196                 :          0 :   while ( index < numPoints && offsetAlongSegment > pathDistances[index] )
     197                 :            :   {
     198                 :          0 :     offsetAlongSegment -= pathDistances[index];
     199                 :          0 :     index += 1;
     200                 :            :   }
     201                 :          0 :   if ( index >= numPoints )
     202                 :            :   {
     203                 :          0 :     return output.release();
     204                 :            :   }
     205                 :            : 
     206                 :          0 :   const double characterHeight = metrics.characterHeight();
     207                 :            : 
     208                 :          0 :   const double segmentLength = pathDistances[index];
     209                 :          0 :   if ( qgsDoubleNear( segmentLength, 0.0 ) )
     210                 :            :   {
     211                 :            :     // Not allowed to place across on 0 length segments or discontinuities
     212                 :          0 :     return output.release();
     213                 :            :   }
     214                 :            : 
     215                 :          0 :   const int characterCount = metrics.count();
     216                 :            : 
     217                 :          0 :   if ( direction == RespectPainterOrientation && !isSecondAttempt )
     218                 :            :   {
     219                 :            :     // Calculate the orientation based on the angle of the path segment under consideration
     220                 :            : 
     221                 :          0 :     double distance = offsetAlongSegment;
     222                 :          0 :     int endindex = index;
     223                 :            : 
     224                 :          0 :     double startLabelX = 0;
     225                 :          0 :     double startLabelY = 0;
     226                 :          0 :     double endLabelX = 0;
     227                 :          0 :     double endLabelY = 0;
     228                 :          0 :     for ( int i = 0; i < characterCount; i++ )
     229                 :            :     {
     230                 :          0 :       const double characterWidth = metrics.characterWidth( i );
     231                 :            :       double characterStartX, characterStartY;
     232                 :          0 :       if ( !nextCharPosition( characterWidth, pathDistances[endindex], x, y, numPoints, endindex, distance, characterStartX, characterStartY, endLabelX, endLabelY ) )
     233                 :            :       {
     234                 :          0 :         return output.release();
     235                 :            :       }
     236                 :          0 :       if ( i == 0 )
     237                 :            :       {
     238                 :          0 :         startLabelX = characterStartX;
     239                 :          0 :         startLabelY = characterStartY;
     240                 :          0 :       }
     241                 :          0 :     }
     242                 :            : 
     243                 :            :     // Determine the angle of the path segment under consideration
     244                 :          0 :     double dx = endLabelX - startLabelX;
     245                 :          0 :     double dy = endLabelY - startLabelY;
     246                 :          0 :     const double lineAngle = std::atan2( -dy, dx ) * 180 / M_PI;
     247                 :            : 
     248                 :          0 :     if ( lineAngle > 90 || lineAngle < -90 )
     249                 :            :     {
     250                 :          0 :       output->labeledLineSegmentIsRightToLeft = true;
     251                 :          0 :     }
     252                 :          0 :   }
     253                 :            : 
     254                 :          0 :   if ( isSecondAttempt )
     255                 :            :   {
     256                 :            :     // we know that treating the segment as running from right to left gave too many upside down characters, so try again treating the
     257                 :            :     // segment as left to right
     258                 :          0 :     output->labeledLineSegmentIsRightToLeft = false;
     259                 :          0 :     output->flippedCharacterPlacementToGetUprightLabels = true;
     260                 :          0 :   }
     261                 :            : 
     262                 :          0 :   double dx = x[index] - x[index - 1];
     263                 :          0 :   double dy = y[index] - y[index - 1];
     264                 :            : 
     265                 :          0 :   double angle = std::atan2( -dy, dx );
     266                 :            : 
     267                 :          0 :   for ( int i = 0; i < characterCount; i++ )
     268                 :            :   {
     269                 :          0 :     double lastCharacterAngle = angle;
     270                 :            : 
     271                 :            :     // grab the next character according to the orientation
     272                 :          0 :     const double characterWidth = !output->flippedCharacterPlacementToGetUprightLabels ? metrics.characterWidth( i ) : metrics.characterWidth( characterCount - i - 1 );
     273                 :          0 :     if ( qgsDoubleNear( characterWidth, 0.0 ) )
     274                 :            :       // Certain scripts rely on zero-width character, skip those to prevent failure (see #15801)
     275                 :          0 :       continue;
     276                 :            : 
     277                 :          0 :     double characterStartX = 0;
     278                 :          0 :     double characterStartY = 0;
     279                 :          0 :     double characterEndX = 0;
     280                 :          0 :     double characterEndY = 0;
     281                 :          0 :     if ( !nextCharPosition( characterWidth, pathDistances[index], x, y, numPoints, index, offsetAlongSegment, characterStartX, characterStartY, characterEndX, characterEndY ) )
     282                 :            :     {
     283                 :          0 :       output->graphemePlacement.clear();
     284                 :          0 :       return output.release();
     285                 :            :     }
     286                 :            : 
     287                 :            :     // Calculate angle from the start of the character to the end based on start/end of character
     288                 :          0 :     angle = std::atan2( characterStartY - characterEndY, characterEndX - characterStartX );
     289                 :            : 
     290                 :          0 :     if ( maxConcaveAngle >= 0 || maxConvexAngle >= 0 )
     291                 :            :     {
     292                 :            :       // Test lastCharacterAngle vs angle
     293                 :            :       // since our rendering angle has changed then check against our
     294                 :            :       // max allowable angle change.
     295                 :          0 :       double angleDelta = lastCharacterAngle - angle;
     296                 :            :       // normalise between -180 and 180
     297                 :          0 :       while ( angleDelta > M_PI )
     298                 :          0 :         angleDelta -= 2 * M_PI;
     299                 :          0 :       while ( angleDelta < -M_PI )
     300                 :          0 :         angleDelta += 2 * M_PI;
     301                 :          0 :       if ( ( maxConcaveAngle >= 0 && angleDelta > 0 && angleDelta > maxConcaveAngle ) || ( maxConvexAngle >= 0 && angleDelta < 0 && angleDelta < -maxConvexAngle ) )
     302                 :            :       {
     303                 :          0 :         output->graphemePlacement.clear();
     304                 :          0 :         return output.release();
     305                 :            :       }
     306                 :          0 :     }
     307                 :            : 
     308                 :            :     // Shift the character downwards since the draw position is specified at the baseline
     309                 :            :     // and we're calculating the mean line here
     310                 :          0 :     double dist = 0.9 * metrics.characterHeight() / 2;
     311                 :          0 :     if ( output->flippedCharacterPlacementToGetUprightLabels )
     312                 :            :     {
     313                 :          0 :       dist = -dist;
     314                 :          0 :     }
     315                 :          0 :     characterStartX += dist * std::cos( angle + M_PI_2 );
     316                 :          0 :     characterStartY -= dist * std::sin( angle + M_PI_2 );
     317                 :            : 
     318                 :          0 :     double renderAngle = angle;
     319                 :          0 :     CurvedGraphemePlacement placement;
     320                 :          0 :     placement.graphemeIndex = !output->flippedCharacterPlacementToGetUprightLabels ? i : characterCount - i - 1;
     321                 :          0 :     placement.x = characterStartX;
     322                 :          0 :     placement.y = characterStartY;
     323                 :          0 :     placement.width = characterWidth;
     324                 :          0 :     placement.height = characterHeight;
     325                 :          0 :     if ( output->flippedCharacterPlacementToGetUprightLabels )
     326                 :            :     {
     327                 :            :       // rotate in place
     328                 :          0 :       placement.x += characterWidth * std::cos( renderAngle );
     329                 :          0 :       placement.y -= characterWidth * std::sin( renderAngle );
     330                 :          0 :       renderAngle += M_PI;
     331                 :          0 :     }
     332                 :          0 :     placement.angle = -renderAngle;
     333                 :          0 :     output->graphemePlacement.push_back( placement );
     334                 :            : 
     335                 :            :     // Normalise to 0 <= angle < 2PI
     336                 :          0 :     while ( renderAngle >= 2 * M_PI )
     337                 :          0 :       renderAngle -= 2 * M_PI;
     338                 :          0 :     while ( renderAngle < 0 )
     339                 :          0 :       renderAngle += 2 * M_PI;
     340                 :            : 
     341                 :          0 :     if ( renderAngle > M_PI_2 && renderAngle < 1.5 * M_PI )
     342                 :          0 :       output->upsideDownCharCount++;
     343                 :          0 :   }
     344                 :            : 
     345                 :          0 :   if ( !isSecondAttempt && uprightOnly && output->upsideDownCharCount >= characterCount / 2.0 )
     346                 :            :   {
     347                 :            :     // more of text is upside down then right side up...
     348                 :            :     // if text should be shown upright then retry with the opposite orientation
     349                 :          0 :     return generateCurvedTextPlacementPrivate( metrics, x, y, numPoints, pathDistances, offsetAlongLine, direction, maxConcaveAngle, maxConvexAngle, uprightOnly, true );
     350                 :            :   }
     351                 :            : 
     352                 :          0 :   return output.release();
     353                 :          0 : }
     354                 :            : 
     355                 :          0 : bool QgsTextRendererUtils::nextCharPosition( double charWidth, double segmentLength, const double *x, const double *y, int numPoints, int &index, double &currentDistanceAlongSegment, double &characterStartX, double &characterStartY, double &characterEndX, double &characterEndY )
     356                 :            : {
     357                 :            :   // Coordinates this character will start at
     358                 :          0 :   if ( qgsDoubleNear( segmentLength, 0.0 ) )
     359                 :            :   {
     360                 :            :     // Not allowed to place across on 0 length segments or discontinuities
     361                 :          0 :     return false;
     362                 :            :   }
     363                 :            : 
     364                 :          0 :   double segmentStartX = x[index - 1];
     365                 :          0 :   double segmentStartY = y[index - 1];
     366                 :            : 
     367                 :          0 :   double segmentEndX = x[index];
     368                 :          0 :   double segmentEndY = y[index];
     369                 :            : 
     370                 :          0 :   double segmentDx = segmentEndX - segmentStartX;
     371                 :          0 :   double segmentDy = segmentEndY - segmentStartY;
     372                 :            : 
     373                 :          0 :   characterStartX = segmentStartX + segmentDx * currentDistanceAlongSegment / segmentLength;
     374                 :          0 :   characterStartY = segmentStartY + segmentDy * currentDistanceAlongSegment / segmentLength;
     375                 :            : 
     376                 :            :   // Coordinates this character ends at, calculated below
     377                 :          0 :   characterEndX = 0;
     378                 :          0 :   characterEndY = 0;
     379                 :            : 
     380                 :          0 :   if ( segmentLength - currentDistanceAlongSegment >= charWidth )
     381                 :            :   {
     382                 :            :     // if the distance remaining in this segment is enough, we just go further along the segment
     383                 :          0 :     currentDistanceAlongSegment += charWidth;
     384                 :          0 :     characterEndX = segmentStartX + segmentDx * currentDistanceAlongSegment / segmentLength;
     385                 :          0 :     characterEndY = segmentStartY + segmentDy * currentDistanceAlongSegment / segmentLength;
     386                 :          0 :   }
     387                 :            :   else
     388                 :            :   {
     389                 :            :     // If there isn't enough distance left on this segment
     390                 :            :     // then we need to search until we find the line segment that ends further than ci.width away
     391                 :          0 :     do
     392                 :            :     {
     393                 :          0 :       segmentStartX = segmentEndX;
     394                 :          0 :       segmentStartY = segmentEndY;
     395                 :          0 :       index++;
     396                 :          0 :       if ( index >= numPoints ) // Bail out if we run off the end of the shape
     397                 :            :       {
     398                 :          0 :         return false;
     399                 :            :       }
     400                 :          0 :       segmentEndX = x[index];
     401                 :          0 :       segmentEndY = y[index];
     402                 :          0 :     }
     403                 :          0 :     while ( std::sqrt( std::pow( characterStartX - segmentEndX, 2 ) + std::pow( characterStartY - segmentEndY, 2 ) ) < charWidth ); // Distance from character start to end
     404                 :            : 
     405                 :            :     // Calculate the position to place the end of the character on
     406                 :          0 :     findLineCircleIntersection( characterStartX, characterStartY, charWidth, segmentStartX, segmentStartY, segmentEndX, segmentEndY, characterEndX, characterEndY );
     407                 :            : 
     408                 :            :     // Need to calculate distance on the new segment
     409                 :          0 :     currentDistanceAlongSegment = std::sqrt( std::pow( segmentStartX - characterEndX, 2 ) + std::pow( segmentStartY - characterEndY, 2 ) );
     410                 :            :   }
     411                 :          0 :   return true;
     412                 :          0 : }
     413                 :            : 
     414                 :          0 : void QgsTextRendererUtils::findLineCircleIntersection( double cx, double cy, double radius, double x1, double y1, double x2, double y2, double &xRes, double &yRes )
     415                 :            : {
     416                 :          0 :   double multiplier = 1;
     417                 :          0 :   if ( radius < 10 )
     418                 :            :   {
     419                 :            :     // these calculations get unstable for small coordinates differences, e.g. as a result of map labeling in a geographic
     420                 :            :     // CRS
     421                 :          0 :     multiplier = 10000;
     422                 :          0 :     x1 *= multiplier;
     423                 :          0 :     y1 *= multiplier;
     424                 :          0 :     x2 *= multiplier;
     425                 :          0 :     y2 *= multiplier;
     426                 :          0 :     cx *= multiplier;
     427                 :          0 :     cy *= multiplier;
     428                 :          0 :     radius *= multiplier;
     429                 :          0 :   }
     430                 :            : 
     431                 :          0 :   double dx = x2 - x1;
     432                 :          0 :   double dy = y2 - y1;
     433                 :            : 
     434                 :          0 :   double A = dx * dx + dy * dy;
     435                 :          0 :   double B = 2 * ( dx * ( x1 - cx ) + dy * ( y1 - cy ) );
     436                 :          0 :   double C = ( x1 - cx ) * ( x1 - cx ) + ( y1 - cy ) * ( y1 - cy ) - radius * radius;
     437                 :            : 
     438                 :          0 :   double det = B * B - 4 * A * C;
     439                 :          0 :   if ( A <= 0.000000000001 || det < 0 )
     440                 :            :     // Should never happen, No real solutions.
     441                 :          0 :     return;
     442                 :            : 
     443                 :          0 :   if ( qgsDoubleNear( det, 0.0 ) )
     444                 :            :   {
     445                 :            :     // Could potentially happen.... One solution.
     446                 :          0 :     double t = -B / ( 2 * A );
     447                 :          0 :     xRes = x1 + t * dx;
     448                 :          0 :     yRes = y1 + t * dy;
     449                 :          0 :   }
     450                 :            :   else
     451                 :            :   {
     452                 :            :     // Two solutions.
     453                 :            :     // Always use the 1st one
     454                 :            :     // We only really have one solution here, as we know the line segment will start in the circle and end outside
     455                 :          0 :     double t = ( -B + std::sqrt( det ) ) / ( 2 * A );
     456                 :          0 :     xRes = x1 + t * dx;
     457                 :          0 :     yRes = y1 + t * dy;
     458                 :            :   }
     459                 :            : 
     460                 :          0 :   if ( multiplier != 1 )
     461                 :            :   {
     462                 :          0 :     xRes /= multiplier;
     463                 :          0 :     yRes /= multiplier;
     464                 :          0 :   }
     465                 :          0 : }

Generated by: LCOV version 1.14