22#include <QPainterPath> 
   25QLineF 
segment( 
int index, QRectF rect, 
double radius )
 
   27  const int yMultiplier = rect.height() < 0 ? -1 : 1;
 
   31      return QLineF( rect.left() + radius,
 
   33                     rect.right() - radius,
 
   36      return QLineF( rect.right(),
 
   37                     rect.top() + yMultiplier * radius,
 
   39                     rect.bottom() - yMultiplier * radius );
 
   41      return QLineF( rect.right() - radius,
 
   46      return QLineF( rect.left(),
 
   47                     rect.bottom() - yMultiplier * radius,
 
   49                     rect.top() + yMultiplier * radius );
 
   57  return createBalloon( origin, rect, wedgeWidth, 0 ).toFillPolygon();
 
   62  int balloonSegment = -1;
 
   63  QPointF balloonSegmentPoint1;
 
   64  QPointF balloonSegmentPoint2;
 
   66  const bool invertedY = rect.height() < 0;
 
   68  cornerRadius = std::min( cornerRadius, std::min( std::fabs( rect.height() ), rect.width() ) / 2.0 );
 
   71  if ( rect.contains( origin.
toQPointF() ) )
 
   78    QList<QLineF> segmentList;
 
   79    segmentList << 
segment( 0, rect, cornerRadius );
 
   80    segmentList << 
segment( 1, rect, cornerRadius );
 
   81    segmentList << 
segment( 2, rect, cornerRadius );
 
   82    segmentList << 
segment( 3, rect, cornerRadius );
 
   85    double minEdgeDist = std::numeric_limits<double>::max();
 
   86    int minEdgeIndex = -1;
 
   90    for ( 
int i = 0; i < 4; ++i )
 
   92      QLineF currentSegment = segmentList.at( i );
 
   94      double currentMinDist = origin.
sqrDistToSegment( currentSegment.x1(), currentSegment.y1(), currentSegment.x2(), currentSegment.y2(), currentMinDistPoint );
 
   95      bool isPreferredSegment = 
false;
 
   99        const double angle = fmod( origin.
azimuth( currentMinDistPoint ) + 360.0, 360.0 );
 
  100        if ( angle < 45 || angle > 315 )
 
  101          isPreferredSegment = i == 0;
 
  102        else if ( 
angle < 135 )
 
  103          isPreferredSegment = i == 3;
 
  104        else if ( 
angle < 225 )
 
  105          isPreferredSegment = i == 2;
 
  107          isPreferredSegment = i == 1;
 
  109      else if ( currentMinDist < minEdgeDist )
 
  110        isPreferredSegment = 
true;
 
  112      if ( isPreferredSegment )
 
  115        minEdgePoint = currentMinDistPoint;
 
  116        minEdgeDist = currentMinDist;
 
  117        minEdge = currentSegment;
 
  121    if ( minEdgeIndex >= 0 )
 
  123      balloonSegment = minEdgeIndex;
 
  124      QPointF minEdgeEnd = minEdge.p2();
 
  125      balloonSegmentPoint1 = QPointF( minEdgePoint.
x(), minEdgePoint.
y() );
 
  127      const double segmentLength = minEdge.length();
 
  128      const double clampedWedgeWidth = std::clamp( wedgeWidth, 0.0, segmentLength );
 
  129      if ( std::sqrt( minEdgePoint.
sqrDist( minEdgeEnd.x(), minEdgeEnd.y() ) ) < clampedWedgeWidth )
 
  134        balloonSegmentPoint1 = QPointF( x, y );
 
  141        balloonSegmentPoint2 = QPointF( x, y );
 
  149  for ( 
int i = 0; i < 4; ++i )
 
  151    QLineF currentSegment = 
segment( i, rect, cornerRadius );
 
  154      p0 = currentSegment.p1();
 
  155      path.moveTo( currentSegment.p1() );
 
  160        path.arcTo( std::min( p1.x(), currentSegment.p1().x() ),
 
  161                    std::min( p1.y(), currentSegment.p1().y() ),
 
  162                    cornerRadius, cornerRadius,
 
  163                    i == 0 ? -180 : ( i == 1 ? -90 : ( i == 2 ? 0 : 90 ) ),
 
  166        path.arcTo( std::min( p1.x(), currentSegment.p1().x() ),
 
  167                    std::min( p1.y(), currentSegment.p1().y() ),
 
  168                    cornerRadius, cornerRadius,
 
  169                    i == 0 ? 180 : ( i == 1 ? 90 : ( i == 2 ? 0 : -90 ) ),
 
  173    if ( i == balloonSegment )
 
  175      path.lineTo( balloonSegmentPoint1 );
 
  177      path.lineTo( balloonSegmentPoint2 );
 
  180    p1 = currentSegment.p2();
 
  185    path.arcTo( std::min( p1.x(), p0.x() ),
 
  186                std::min( p1.y(), p0.y() ),
 
  187                cornerRadius, cornerRadius,
 
  190    path.arcTo( std::min( p1.x(), p0.x() ),
 
  191                std::min( p1.y(), p0.y() ),
 
  192                cornerRadius, cornerRadius,
 
static QgsPoint pointOnLineWithDistance(const QgsPoint &startPoint, const QgsPoint &directionPoint, double distance) SIP_HOLDGIL
Returns a point a specified distance toward a second point.
 
A class to represent a 2D point.
 
double sqrDist(double x, double y) const SIP_HOLDGIL
Returns the squared distance between this point a specified x, y coordinate.
 
double azimuth(const QgsPointXY &other) const SIP_HOLDGIL
Calculates azimuth between this point and other one (clockwise in degree, starting from north)
 
QPointF toQPointF() const
Converts a point to a QPointF.
 
double sqrDistToSegment(double x1, double y1, double x2, double y2, QgsPointXY &minDistPoint, double epsilon=DEFAULT_SEGMENT_EPSILON) const SIP_HOLDGIL
Returns the minimum distance between this point and a segment.
 
static QPolygonF createBalloon(const QgsPointXY &origin, const QRectF &rect, double wedgeWidth)
Generates a "balloon"/"talking bubble" style shape (as a QPolygonF).
 
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
 
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
 
QLineF segment(int index, QRectF rect, double radius)