22 #include <QPainterPath> 
   25 QLineF 
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)