Results 1 to 10 of 10

Thread: QGraphicsItem with a cosmetic pen of a fixed width in pixels bigger 1

Hybrid View

Previous Post Previous Post   Next Post Next Post
  1. #1
    Join Date
    Jun 2012
    Posts
    4
    Qt products
    Qt4
    Platforms
    Windows

    Default Re: QGraphicsItem with a cosmetic pen of a fixed width in pixels bigger 1

    Ok, I got things to work with the messy solution I was trying to avoid. In the interest of the public good I'm posting below the core function which calculates the width in scene coordinates for each segment in a polyline so that the entire line displays with a constant user-defined width when the horizontal and vertical scaling factors (m11() and m22()) are different. The polyline object itself is derived from QGraphicsPathItem so this function calculates the QPainterPath that is used by the base class for drawing.
    If there's a better way to do this I'd dearly like to know, but I'm starting to think that the reason Qt doesn't implement scale-invariant pen widths > 1 is because of the performance issues obvious below:


    // If the horizontal and vertical scales of the graphics view are different the display width of a line segment will
    // depend on the angle the segment makes with the horizontal axis. If we think of the two scaling factors
    // as the major and minor axes of an ellipse the width scale will be the radius of the ellipse at an angle 90 degrees
    // from the angle of line segment. The equation for an ellipse in polar coordinates centered at the origin is
    // r = a b / sqrt(b^2 cos^2(theta) + a^2 sin^2(theta)) where a and b are the semi-major and semi-minor axes and theta
    // is (in our case) the angle perpendicular to the line segment. We can get the angle of the line segment (alpha) using
    // trigonometry, and since we really want the squares of the sine and cosine it's even easier:
    // sin^2(alpha) = deltax^2/(deltax^2+deltay^2) and cos^2(alpha) = deltay^2/(deltax^2+deltay^2).
    // (deltax and deltay are the x and y displacements of the segment)
    // Since alpha and theta are perpendicular we can replace sin^2(theta) in the
    // ellipse equation with cos^2(alpha) and cos^2(theta) with sin^2(alpha). This gives us the expression
    // r = width scale = xScale yScale / sqrt(yScale^2 sin^2(alpha) + xScale^2 cos^2(alpha))

    void MGraphicsLine::recalculateSegmentWidths(const QTransform &t)
    {
    double xScale = 1.0/t.m11();
    double yScale = fabs(1.0/t.m22());

    prepareGeometryChange(); // the bounding rect will change as a result of recalculating the widths
    // recalculate the path for the polyline using the new widths
    mShapePath = QPainterPath();

    for (int i=0; i<mLineSegments.size(); i++)
    {
    QPointF p1 = mLineSegments.at(i).first;
    QPointF p2 = mLineSegments.at(i).second;

    double widthScale;
    if (xScale == yScale)
    widthScale = xScale;
    else
    {
    double cosSq = 0, sinSq = 0;
    calculateSquaredSegmentAngles(p1, p2, cosSq, sinSq);
    widthScale = getEllipseRadius(xScale, yScale, sinSq, cosSq);
    }

    double width = mLineWidth*widthScale; // mLineWidth is the desired display width
    QPainterPath segPath;
    segPath.moveTo(p1);
    segPath.lineTo(p2);

    QPainterPathStroker stroker;
    stroker.setWidth(width);
    stroker.setDashPattern(mLineStyle);
    stroker.setCapStyle(Qt::FlatCap); // can't use a cap, to avoid issues with different scales
    QPainterPath strokePath = stroker.createStroke(segPath);
    mShapePath.addPath(strokePath);

    }

    setPath(mShapePath);
    }

    static bool calculateSquaredSegmentAngles(const QPointF &p1, const QPointF &p2, double &cosSq, double &sinSq)
    {
    cosSq = 0;
    sinSq = 0;

    double deltaX = p1.x()-p2.x();
    double deltaY = p1.y()-p2.y();
    double hypotSquared = deltaX*deltaX+deltaY*deltaY;
    if (hypotSquared)
    {
    cosSq = deltaX*deltaX/hypotSquared;
    sinSq = deltaY*deltaY/hypotSquared;
    return true;
    }
    return false;
    }

    static double getEllipseRadius(double a, double b, double squaredCos, double squaredSin)
    {
    double widthScale;
    if (a == b)
    return a;
    if (squaredCos == 0)
    return b;
    else if (squaredSin == 0)
    return a;

    return a*b/sqrt(a*a*squaredSin + b*b*squaredCos);
    }
    Last edited by AndyK; 4th June 2012 at 20:54.

  2. #2
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    5,330
    Thanks
    317
    Thanked 871 Times in 858 Posts
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: QGraphicsItem with a cosmetic pen of a fixed width in pixels bigger 1

    Thanks, but please learn about [CODE] tags when posting source code. Click the "Go Advanced" editing button next time you make a post, and you will see a "#" symbol in the editing buttons. This inserts [CODE] tags which properly formats your code for display and permits easy cut / paste, line numbering, etc.

  3. #3
    Join Date
    Jun 2012
    Posts
    4
    Qt products
    Qt4
    Platforms
    Windows

    Default Re: QGraphicsItem with a cosmetic pen of a fixed width in pixels bigger 1

    Sorry! I actually looked for a code tag under advanced before I posted but somehow missed it. I can't seem to edit my post now.


    Quote Originally Posted by d_stranz View Post
    Thanks, but please learn about [CODE] tags when posting source code. Click the "Go Advanced" editing button next time you make a post, and you will see a "#" symbol in the editing buttons. This inserts [CODE] tags which properly formats your code for display and permits easy cut / paste, line numbering, etc.

  4. #4
    Join Date
    Dec 2006
    Location
    Kędzierzyn-Koźle
    Posts
    16
    Thanks
    1
    Thanked 1 Time in 1 Post
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Default Re: QGraphicsItem with a cosmetic pen of a fixed width in pixels bigger 1

    Hi I had the same problem ...
    Anybody that get here - you might use QPen::setCosmetic - that will obey all transformations and draw line with given width of pixels.
    If you are going to use it with different devices (ex. printer and screen) you might recalculate thickness regardles to device ppi or other metrics.

    This thread was very helpfull about this:
    http://lists.trolltech.com/qt-intere...ad00163-0.html

  5. The following user says thank you to tzioboro for this useful post:

    montylee (16th November 2012)

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.