[Interest] QPainterPathStroker's curve threshold

Elvis Stansvik elvstone at gmail.com
Thu Dec 8 19:20:01 CET 2016


Den 8 dec. 2016 1:09 em skrev "Ch'Gans" <chgans at gna.org>:
>
> On 8 December 2016 at 20:40, Elvis Stansvik <elvstone at gmail.com> wrote:
> > Den 8 dec. 2016 1:02 fm skrev "Ch'Gans" <chgans at gna.org>:
> >>
> >> Hi,
> >>
> >> The documentation of QPainterPathStroker's curve threshold says:
> >> ----------------------------------------------------------------
> >> Specifies the curve flattening threshold, controlling the granularity
> >> with which the generated outlines' curve is drawn.
> >>
> >> The default threshold is a well adjusted value (0.25), and normally
> >> you should not need to modify it. However, you can make the curve's
> >> appearance smoother by decreasing its value.
> >> ----------------------------------------------------------------
> >>
> >> However i have noticed that when used in a graphics scene with objects
> >> of small size, the default threshold value is obviously too big, eg a
> >> circle becomes a square!
> >>
> >> As the doc states that "normally you should not need to modify it", i
> >> was reluctant to change it, but then I realised that maybe this
> >> default value is actually well adjusted in the context of QWidget,
> >> where the unit of measure is "pixel". In a QGraphicsScene the unit is
> >> arbitrary.
> >> If I'm right, there could be potentially a problem as well if the
> >> scene object is too big, eg, a circle will be rendered as a regular
> >> polygon with thousands of edges. So it all boil down to find the
> >> correct threshold so that both small and big (in scene coordinates)
> >> will be rendered in the view (pixel coordinates), using say 26 edges
> >> polygon (icosikaihexagon [1][2] ;))
> >>
> >> Could anyone shed a bit of light on what "well adjusted" mean and how
> >> to actually adjust it to s specific use case? Is this threshold
> >> actually expressed in a pixel-related unit of measure? Is it a ratio
> >> of something (0.0 to 1.0) ?
> >
> > I've been wondering the same actually, though it was a long time ago.
> >
> > The QPainterPathStroker has no information about which scale you intend
to
> > paint the path, so I guess like you say, it must make some assumption
based
> > on pixel units and a regular screen size.
> >
> > For details of how it adjusts this value I think you'll have to look at
the
> > code. But maybe the docs should be extended a bit.
>
> Default value is indeed 0.25, but settings the stroke width will
> adjust as follow:
> threshold = strokeWidth > 4 ? 1.0/strokeWidth : 0.25
>
> Tracking the usage of curve threshold lead me to QStroker, QTransform
> and QBezier, but man, the code is hairy, even more than me! ;)
>
> The threshold seems to be adjusted dynamically too in some recursive
> algorithm...
>
> Anyway, there's this interesting comment in QBezier::toPolygon():
>
>     // flattening is done by splitting the bezier until we can replace
> the segment by a straight
>     // line. We split further until the control points are close
> enough to the line connecting the
>     // boundary points.
>     //
>     // the Distance of a point p from a line given by the points (a,b)
> is given by:
>     //
>     // d = abs( (bx - ax)(ay - py) - (by - ay)(ax - px) ) / line_length
>     //
>     // We can stop splitting if both control points are close enough
> to the line.
>     // To make the algorithm faster we use the manhattan length of the
line.
>
> [Note: AFAIK, any non-linear element in a QPainterPath is handled as a
> bezier curve, read: circular and ellipsoidal arcs are stored as bezier
> curves.]
>
> If i got it right, at the end of the day, this distance 'd' is
> compared to the 'bezier_flattening_threshold' (which is the
> curveThreshold from the stroker). Which means that the curve threshold
> is actually a ratio b/w 2 distances, so it is unit-less.
>
> I have observed this problem twice (circle becomes square, couldn't
> find the message in the archive).
> The first time was due to performing QPainterPath set operations
> (union, intersection) on small features (in the order of 10E-3), and
> set operations first flatten the path.
> Second time is quite similar, small features in a QGraphicsScene, no
> set operations, but the path is flatten by the path stroker...
>
> So if the curve threshold is really unitless, then the only
> explanation i can come with is that the flattening algorithm
> deteriorate as the input numbers get smaller, due to limitation of
> arithmetic on floating point numbers.
>
> Still, in the formula 'threshold = strokeWidth > 4 ? 1.0/strokeWidth :
> 0.25', 0.25 and 4 are obviously magic numbers that seem to consider
> the stroke width being expressed in pixels. And - if i'm not wrong -
> the documentation is lying: the default is 0.25, *if* the stroke with
> is greater than 4.
>
> To be continued...

Nice digging, I stand corrected. That looks like the normal algorithm for
flattening beziers indeed, so I'm also puzzled why it deteriorates for you
on small features.

I don't think limited machine precision should cause this, unless you're
working at a very small scale?

Someone who knows the code can surely answer better than me.

Elvis

>
> Chris
>
>
>
>
> >
> > Elvis
> >
> >>
> >> Thanks,
> >> Chris
> >>
> >> [1]
https://www.voltage.com/math-2/approximating-a-circle-with-a-polygon/
> >> [2] http://mathforum.org/dr.math/faq/faq.polygon.names.html
> >> _______________________________________________
> >> Interest mailing list
> >> Interest at qt-project.org
> >> http://lists.qt-project.org/mailman/listinfo/interest
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.qt-project.org/pipermail/interest/attachments/20161208/be1c9647/attachment.html>


More information about the Interest mailing list