Results 1 to 9 of 9

Thread: Problems with QPainterPath::arcTo()

  1. #1
    Join Date
    Feb 2006
    Location
    US
    Posts
    173
    Qt products
    Qt4 Qt5
    Platforms
    Unix/X11 Windows Android
    Thanks
    16
    Thanked 5 Times in 5 Posts

    Default Problems with QPainterPath::arcTo()

    I’m hoping someone might shed some light as to why I’m having a problem with QPainterPath::arcTo() used to draw the inner pie boundary in the image below.
    The inner and outer arcs have the same start angle and sweep length, but they don't render that way. Why?
    pieIssue.jpg

    The pie shapes are drawn with:
    QPainterPath::arcTo(const QRectF & rectangle, qreal startAngle, qreal sweepLength)

    The method is giving unexpected results with the startAngle and sweepLength.

    Looking at the image, I expect the inner arc to begin (and end) coincident with the outer arc, but it doesn’t. The center, startAngle, and sweepLength are the same for both.

    Note, I computed the desired arc start/end point (shown in the red circle as a yellow square).

    Qt Code:
    1. void GraphicsPieFramed::createPaths()
    2. {
    3. // Clear the paths.
    4. mOutlinePath = QPainterPath();
    5. mFacePath = QPainterPath();
    6.  
    7. mMaxelSize = SCENE_SCALE*maxelDiameter(printHeadDiameter(), mVoltage);
    8.  
    9. double span = -normAngle360(mAngleEnd-mAngleBegin);
    10.  
    11. mOutlinePath.addRect(-0.5*mSize.width(), -0.5*mSize.height(), mSize.width(), mSize.height());
    12.  
    13. // outer pie
    14. QPainterPath outerPie;
    15. outerPie.moveTo(QPointF(0.0, 0.0));
    16. outerPie.arcTo(QRectF(-0.5*mSize.width(), -0.5*mSize.height(), mSize.width(), mSize.height()),
    17. -mAngleBegin, span);
    18. outerPie.closeSubpath();
    19. mFacePath.addPath(outerPie);
    20.  
    21. // inner pie
    22. QPainterPath innerPie;
    23. innerPie.moveTo(QPointF(0.0, 0.0));
    24. innerPie.arcTo(QRectF(-0.5*mSize.width()+mFrameWidth, -0.5*mSize.height()+mFrameHeight,
    25. mSize.width()-2.0*mFrameWidth, mSize.height()-2.0*mFrameHeight),
    26. -mAngleBegin, span);
    27. innerPie.closeSubpath();
    28. mFacePath.addPath(innerPie);
    29.  
    30. if(mFrameEnabled && mFrameWidth < 0.5*mSize.width() && mFrameHeight < 0.5*mSize.height())
    31. {
    32. mFacePath.addPath(innerPie);
    33. }
    34.  
    35. mFacePath.addEllipse(QPointF(0.0, 0.0), 3.0, 3.0); // Center location
    36.  
    37. createMaxelPaths();
    38. createResizeHandlePaths();
    39. createRotateHandlePath();
    40. }
    To copy to clipboard, switch view to plain text mode 

    Qt Code:
    1. void GraphicsPieFramed::createResizeHandlePaths()
    2. {
    3. // Clear the paths.
    4. mResizeLeftPath = QPainterPath();
    5. mResizeRightPath = QPainterPath();
    6. mResizeBottomPath = QPainterPath();
    7. mResizeTopPath = QPainterPath();
    8. mResizeBottomLeftPath = QPainterPath();
    9. mResizeBottomRightPath = QPainterPath();
    10. mResizeTopLeftPath = QPainterPath();
    11. mResizeTopRightPath = QPainterPath();
    12. mResizeAngleBeginPath = QPainterPath();
    13. mResizeAngleEndPath = QPainterPath();
    14. mResizeFrameWidthPath = QPainterPath();
    15. mResizeFrameHeightPath = QPainterPath();
    16.  
    17. double const sizeHandleHalf(0.5*SIZE_HANDLE);
    18. double const sizeHandleHalfDiag(sizeHandleHalf*sqrt(2.0));
    19. double const left(-0.5*mSize.width());
    20. double const right(0.5*mSize.width());
    21. double const bottom(-0.5*mSize.height());
    22. double const top(0.5*mSize.height());
    23.  
    24. // left
    25. mResizeLeftPath.addRect(left-sizeHandleHalf, -sizeHandleHalf, SIZE_HANDLE, SIZE_HANDLE);
    26. // right
    27. mResizeRightPath.addRect(right-sizeHandleHalf, -sizeHandleHalf, SIZE_HANDLE, SIZE_HANDLE);
    28. // bottom
    29. mResizeBottomPath.addRect(-sizeHandleHalf, bottom-sizeHandleHalf, SIZE_HANDLE, SIZE_HANDLE);
    30. // top
    31. mResizeTopPath.addRect(-sizeHandleHalf, top-sizeHandleHalf, SIZE_HANDLE, SIZE_HANDLE);
    32.  
    33. // bottom-left
    34. mResizeBottomLeftPath.addRect(left-sizeHandleHalf, bottom-sizeHandleHalf, SIZE_HANDLE, SIZE_HANDLE);
    35. // bottom-right
    36. mResizeBottomRightPath.addRect(right-sizeHandleHalf, bottom-sizeHandleHalf, SIZE_HANDLE, SIZE_HANDLE);
    37. // top-left
    38. mResizeTopLeftPath.addRect(left-sizeHandleHalf, top-sizeHandleHalf, SIZE_HANDLE, SIZE_HANDLE);
    39. // top-right
    40. mResizeTopRightPath.addRect(right-sizeHandleHalf, top-sizeHandleHalf, SIZE_HANDLE, SIZE_HANDLE);
    41.  
    42. // begin angle
    43. double xBegin = 0.5*mSize.width()*cos(DEG2RAD*mAngleBegin);
    44. double yBegin = 0.5*mSize.height()*sin(DEG2RAD*mAngleBegin);
    45. mResizeAngleBeginPath.addEllipse(xBegin-sizeHandleHalfDiag, yBegin-sizeHandleHalfDiag,
    46. 2.0*sizeHandleHalfDiag, 2.0*sizeHandleHalfDiag);
    47. // begin angle inner frame
    48. double lenBegin = sqrt(xBegin*xBegin + yBegin*yBegin);
    49. double xBeginPart = mFrameWidth*cos(DEG2RAD*mAngleBegin);
    50. double yBeginPart = mFrameHeight*sin(DEG2RAD*mAngleBegin);
    51. double lenBeginPart = sqrt(xBeginPart*xBeginPart + yBeginPart*yBeginPart);
    52. double x2Begin = interp(xBegin, 0.0, lenBeginPart/lenBegin);
    53. double y2Begin = interp(yBegin, 0.0, lenBeginPart/lenBegin);
    54. mResizeAngleBeginPath.addRect(x2Begin-sizeHandleHalf, y2Begin-sizeHandleHalf, 2.0*sizeHandleHalf, 2.0*sizeHandleHalf);
    55.  
    56. // end angle
    57. double xEnd = 0.5*mSize.width()*cos(DEG2RAD*mAngleEnd);
    58. double yEnd = 0.5*mSize.height()*sin(DEG2RAD*mAngleEnd);
    59. mResizeAngleEndPath.addEllipse(xEnd-sizeHandleHalfDiag, yEnd-sizeHandleHalfDiag,
    60. 2.0*sizeHandleHalfDiag, 2.0*sizeHandleHalfDiag);
    61. // end angle inner frame
    62. double lenEnd = sqrt(xEnd*xEnd + yEnd*yEnd);
    63. double xEndPart = mFrameWidth*cos(DEG2RAD*mAngleEnd);
    64. double yEndPart = mFrameHeight*sin(DEG2RAD*mAngleEnd);
    65. double lenEndPart = sqrt(xEndPart*xEndPart + yEndPart*yEndPart);
    66. double x2End = interp(xEnd, 0.0, lenEndPart/lenEnd);
    67. double y2End = interp(yEnd, 0.0, lenEndPart/lenEnd);
    68. mResizeAngleEndPath.addRect(x2End-sizeHandleHalf, y2End-sizeHandleHalf, 2.0*sizeHandleHalf, 2.0*sizeHandleHalf);
    69.  
    70. // frame width
    71. mResizeFrameWidthPath.addEllipse(left-sizeHandleHalfDiag+mFrameWidth, top-sizeHandleHalfDiag,
    72. 2.0*sizeHandleHalfDiag, 2.0*sizeHandleHalfDiag);
    73.  
    74. // frame height
    75. mResizeFrameHeightPath.addEllipse(left-sizeHandleHalfDiag, top-sizeHandleHalfDiag-mFrameHeight,
    76. 2.0*sizeHandleHalfDiag, 2.0*sizeHandleHalfDiag);
    77. }
    To copy to clipboard, switch view to plain text mode 

    Thanks!

  2. #2
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,376
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Thanks
    4
    Thanked 5,019 Times in 4,795 Posts
    Wiki edits
    10

    Default Re: Problems with QPainterPath::arcTo()

    I would guess the rectangle you pass to the inner arcTo() call is incorrect.
    Your biological and technological distinctiveness will be added to our own. Resistance is futile.

    Please ask Qt related questions on the forum and not using private messages or visitor messages.


  3. #3
    Join Date
    Feb 2006
    Location
    US
    Posts
    173
    Qt products
    Qt4 Qt5
    Platforms
    Unix/X11 Windows Android
    Thanks
    16
    Thanked 5 Times in 5 Posts

    Default Re: Problems with QPainterPath::arcTo()

    Shouldn't a smaller (same center) rectangle and same start/sweep angle just imply a smaller arc radii? The start angle should still be the same (and coincident), right?

    Thanks for the suggestion. I will look.

  4. #4
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,376
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Thanks
    4
    Thanked 5,019 Times in 4,795 Posts
    Wiki edits
    10

    Default Re: Problems with QPainterPath::arcTo()

    The rectangle serves as the bounding box for the arc thus it matters where the sides of the box are and not where the centre is. The arc will begin at one side of the rectangle and end at the other side. You might want to add drawing the box to your code to see how the arcs are drawn related to their corresponding boxes.
    Your biological and technological distinctiveness will be added to our own. Resistance is futile.

    Please ask Qt related questions on the forum and not using private messages or visitor messages.


  5. #5
    Join Date
    Feb 2006
    Location
    US
    Posts
    173
    Qt products
    Qt4 Qt5
    Platforms
    Unix/X11 Windows Android
    Thanks
    16
    Thanked 5 Times in 5 Posts

    Default Re: Problems with QPainterPath::arcTo()

    I drew the outer and inner boxes in green.
    From what I've read ...
    - The rectangle is used to define the maximum extents (and shape) of the arc.
    - The start/end angle depend upon the arcTo() specified start angle and sweep angle; the arc is not required to start at one side of the rectangle and end at the other side.
    Restated: The rectangle defines the ellipse; the start/sweep angle define the arc of the ellipse.

    From Qt Assistant:
    Creates an arc that occupies the given rectangle, beginning at the specified startAngle and extending sweepLength degrees counter-clockwise.
    Am I missing something?

    The start angle in the image below is off. The specified angle is the same as the outer pie shape.
    pieIssue2.jpg

    I drew the outer and inner boxes in green.
    From what I've read ...
    - The rectangle is used to define the maximum extents (and shape) of the arc.
    - The start/end angle depend upon the arcTo() specified start angle and sweep angle; the arc is not required to start at one side of the rectangle and end at the other side.
    Restated: The rectangle defines the ellipse; the start/sweep angle define the arc of the ellipse.

    From Qt Assistant:
    Creates an arc that occupies the given rectangle, beginning at the specified startAngle and extending sweepLength degrees counter-clockwise.
    Am I missing something?

    The start angle in the image below is off. The specified angle is the same as the outer pie shape.
    pieIssue2.jpg


    Added after 35 minutes:


    Here is a sequence of images that emphasize how the rectangle defines the ellipse (note inner/outer green rectangle) and the start/sweep angle define the arc of the ellipse.
    pieIssue3.jpg
    Last edited by brcain; 4th November 2014 at 19:55.

  6. #6
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,376
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Thanks
    4
    Thanked 5,019 Times in 4,795 Posts
    Wiki edits
    10

    Default Re: Problems with QPainterPath::arcTo()

    Is there a question here somewhere? You can clearly see on your images that the rectangle is not defined correctly. Your outer rectangle is a square(-ish), your inner one is not. Thus your outer arc lies on a circle and your inner does not.
    Your biological and technological distinctiveness will be added to our own. Resistance is futile.

    Please ask Qt related questions on the forum and not using private messages or visitor messages.


  7. #7
    Join Date
    Feb 2006
    Location
    US
    Posts
    173
    Qt products
    Qt4 Qt5
    Platforms
    Unix/X11 Windows Android
    Thanks
    16
    Thanked 5 Times in 5 Posts

    Default Re: Problems with QPainterPath::arcTo()

    The inner rectangle is not meant to be square-ish. Why did you infer that?
    My question regards the inner ellipse. An ellipse (by definition) is not restricted to being square-ish.

    My question: Does the arcTo() function work in such a way that the rectangle defines an ellipse and the start/sweep angle define an arc of the ellipse?

    The previous sequences of images supports the rectangle assertion, but the begin angle seems to be off.

    The images represent a pie shape that is allowed to have inner and outer ellipse fill boundaries.

    Here is an example of the outer boundary not being square-ish.
    pieIssue4.jpg
    You can see the fill algorithm is working perfectly. However, the arc start/sweep angles are not consistent -- despite the fact that the values specified for the start/sweep angles for the inner and outer ellipses are precisely the same. I even hand calculated the end and start angles which are shown on the inner ellipse as yellow squares. It appears there is a bug in the function ... or at a minimum, the documentation. However, I am willing to admit I may be missing something.
    Last edited by brcain; 4th November 2014 at 21:22.

  8. #8
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,376
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Thanks
    4
    Thanked 5,019 Times in 4,795 Posts
    Wiki edits
    10

    Default Re: Problems with QPainterPath::arcTo()

    Quote Originally Posted by brcain View Post
    The inner rectangle is not meant to be square-ish. Why did you infer that?
    That was my impression from your first post. But apparently I was wrong.

    My question: Does the arcTo() function work in such a way that the rectangle defines an ellipse and the start/sweep angle define an arc of the ellipse?
    QPainterPath::arcTo() docs show a visual hint how the arc is interpreted with regard to the rectangle. The rectangle defines an ellipse the arc sweeps over starting from startAngle and ending at startAngle+sweepLength. Angles are specified in degrees.

    Here is a simple testbed:

    Qt Code:
    1. #include <QtWidgets>
    2.  
    3. class Widget : public QWidget {
    4. Q_OBJECT
    5. Q_PROPERTY(int startAngle READ startAngle WRITE setStartAngle NOTIFY startAngleChanged)
    6. Q_PROPERTY(int sweepLength READ sweepLength WRITE setSweepLength NOTIFY sweepLengthChanged)
    7.  
    8. public:
    9. Widget(QWidget *parent = 0) : QWidget(parent) {
    10. m_startAngle = 0;
    11. m_sweepLength = 0;
    12. connect(this, SIGNAL(startAngleChanged(int)), SLOT(update()));
    13. connect(this, SIGNAL(sweepLengthChanged(int)), SLOT(update()));
    14. setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    15. }
    16.  
    17. int startAngle() const
    18. {
    19. return m_startAngle;
    20. }
    21. int sweepLength() const
    22. {
    23. return m_sweepLength;
    24. }
    25.  
    26. public slots:
    27. void setStartAngle(int arg)
    28. {
    29. if (m_startAngle == arg)
    30. return;
    31.  
    32. m_startAngle = arg;
    33. emit startAngleChanged(arg);
    34. }
    35. void setSweepLength(int arg)
    36. {
    37. if (m_sweepLength == arg)
    38. return;
    39.  
    40. m_sweepLength = arg;
    41. emit sweepLengthChanged(arg);
    42. }
    43.  
    44. protected:
    45. void paintEvent(QPaintEvent *pe) {
    46. QPainter painter(this);
    47. painter.setRenderHint(QPainter::Antialiasing);
    48. QLinearGradient myGradient(0, 0, width(), height());
    49. myGradient.setColorAt(0, Qt::yellow);
    50. myGradient.setColorAt(1, QColor("orange"));
    51.  
    52. QRect rectangle = rect().adjusted(10,10,-10,-10);
    53.  
    54. QPen myPen;
    55. myPen.setWidth(1);
    56. myPen.setColor(palette().color(QPalette::Dark));
    57.  
    58. QPointF center = rectangle.center();
    59.  
    60.  
    61. QPainterPath myPath;
    62. myPath.moveTo(center);
    63. myPath.arcTo(rectangle, m_startAngle,
    64. m_sweepLength);
    65. myPath.closeSubpath();
    66.  
    67. QPen dashPen;
    68. dashPen.setColor(Qt::red);
    69. dashPen.setStyle(Qt::DashLine);
    70. painter.setPen(dashPen);
    71. painter.setBrush(Qt::NoBrush);
    72. painter.drawRect(rectangle);
    73.  
    74. painter.setBrush(myGradient);
    75. painter.setPen(myPen);
    76. painter.drawPath(myPath);
    77. }
    78.  
    79. signals:
    80. void startAngleChanged(int arg);
    81. void sweepLengthChanged(int arg);
    82. private:
    83. int m_startAngle;
    84. int m_sweepLength;
    85. };
    86.  
    87. #include "main.moc"
    88.  
    89. int main(int argc, char **argv) {
    90. QApplication app(argc, argv);
    91.  
    92. QVBoxLayout *l = new QVBoxLayout(&w);
    93. Widget *arcWidget = new Widget;
    94. l->addWidget(arcWidget);
    95. QSlider *startSlider = new QSlider(Qt::Horizontal);
    96. QSlider *sweepSlider = new QSlider(Qt::Horizontal);
    97. startSlider->setRange(0, 360);
    98. sweepSlider->setRange(0, 360);
    99. l->addWidget(startSlider);
    100. l->addWidget(sweepSlider);
    101. QObject::connect(startSlider, SIGNAL(valueChanged(int)), arcWidget, SLOT(setStartAngle(int)));
    102. QObject::connect(sweepSlider, SIGNAL(valueChanged(int)), arcWidget, SLOT(setSweepLength(int)));
    103. sweepSlider->setValue(90);
    104. w.show();
    105. w.resize(800, 600);
    106.  
    107. return app.exec();
    108. }
    To copy to clipboard, switch view to plain text mode 
    Your biological and technological distinctiveness will be added to our own. Resistance is futile.

    Please ask Qt related questions on the forum and not using private messages or visitor messages.


  9. The following user says thank you to wysota for this useful post:

    brcain (4th November 2014)

  10. #9
    Join Date
    Feb 2006
    Location
    US
    Posts
    173
    Qt products
    Qt4 Qt5
    Platforms
    Unix/X11 Windows Android
    Thanks
    16
    Thanked 5 Times in 5 Posts

    Default Re: Problems with QPainterPath::arcTo()

    The example you wrote is producing the same effect.
    Note resizing the rectangle (keeping start/sweep angles the same) results in a different measurable start angle.
    The measurable start angle goes from around 15 degrees to around 40 degrees.
    pieIssue5.jpg
    I'm still somewhat puzzled what the start angle actually means with arcTo(); it appears to be ether the arc length or dependent upon the rectangle's aspect ratio.
    Perhaps I need to adjust my start angle based upon this aspect ratio.
    I'll run a desktop protractor app to measure the actual start/sweep angles to test my hypothesis.

    It appears the starting arc length (relative to x-axis) remains constant. Computing the actual arc length of an ellipse requires elliptic integrals. Nonetheless, I would have thought the starting arc length would vary, and the start angle would remain the same.
    Last edited by brcain; 5th November 2014 at 00:28.

Similar Threads

  1. QPainterPath
    By Karl123 in forum Newbie
    Replies: 2
    Last Post: 16th June 2013, 17:26
  2. Angles in QPainterPath::arcTo
    By hector1984 in forum Qt Programming
    Replies: 1
    Last Post: 27th January 2013, 06:28
  3. QPainterPath::quadTo(...) calls QPainterPath::cubicTo(...) ?
    By brucelamond in forum Qt Programming
    Replies: 0
    Last Post: 29th April 2011, 00:30
  4. QPainterPath::arcTo accuracy
    By robertson1 in forum Qt Programming
    Replies: 0
    Last Post: 18th July 2008, 13:13
  5. QPainterPath.arcTo
    By grellsworth in forum Qt Programming
    Replies: 1
    Last Post: 18th March 2008, 18:36

Tags for this Thread

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Qt is a trademark of The Qt Company.