Little gotcha in Path.addPieSegment


#1

Hello, back from my travels and diagnosed an intermittent bug in my code that triggered a gotcha in Path.addPieSegment.

Path path; path.addPieSegment(bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight(), zeroAngle_, timeAngle_, 0); g.fillPath(path);

Due to division by “close to zero,” timeAngle_ was occasionally quite large so that this pair of calls would take an extremely long time to terminate, basically crashing the GUI of my program.

Now I’m not quite sure what to do here - because sometimes you do want to create paths that are greater than 360 degrees. Perhaps a warning on the documentation of the method that the time it takes to render is proportional to the difference between zeroAngle_ and timeAngle_?


#2

Hi Tom

Hmm, it wouldn’t hurt for it to add a limit on that value… I’ve not tried this, but maybe you can check that my maths is correct here:

[code]void Path::addCentredArc (const float centreX, const float centreY,
const float radiusX, const float radiusY,
const float rotationOfEllipse,
const float fromRadians,
float toRadians,
const bool startAsNewSubPath)
{
if (radiusX > 0.0f && radiusY > 0.0f)
{
const Point centre (centreX, centreY);
const AffineTransform rotation (AffineTransform::rotation (rotationOfEllipse, centreX, centreY));
float angle = fromRadians;

    if (startAsNewSubPath)
        startNewSubPath (centre.getPointOnCircumference (radiusX, radiusY, angle).transformedBy (rotation));

    if (fromRadians < toRadians)
    {
        if (startAsNewSubPath)
            angle += PathHelpers::ellipseAngularIncrement;

        if (toRadians > angle + 2.0f * float_Pi)
            toRadians = angle + 2.0f * float_Pi + std::fmod (toRadians - angle, 2.0f * float_Pi);
        
        while (angle < toRadians)
        {
            lineTo (centre.getPointOnCircumference (radiusX, radiusY, angle).transformedBy (rotation));
            angle += PathHelpers::ellipseAngularIncrement;
        }
    }
    else
    {
        if (startAsNewSubPath)
            angle -= PathHelpers::ellipseAngularIncrement;

        if (toRadians < angle - 2.0f * float_Pi)
            toRadians = angle - 2.0f * float_Pi - std::fmod (angle - toRadians, 2.0f * float_Pi);

        while (angle > toRadians)
        {
            lineTo (centre.getPointOnCircumference (radiusX, radiusY, angle).transformedBy (rotation));
            angle -= PathHelpers::ellipseAngularIncrement;
        }
    }

    lineTo (centre.getPointOnCircumference (radiusX, radiusY, toRadians).transformedBy (rotation));
}

}[/code]


#3

ooh, er.

Well, I don’t think the code is correct - in four different ways! :smiley: if (toRadians > angle + 2.0f * float_Pi) toRadians = angle + 2.0f * float_Pi + std::fmod (toRadians - angle, 2.0f * float_Pi);First, it’s fmodf if the arguments are float, not fmod. Second, the code only covers large positive angles, not large negative ones - you can just get rid of that test. Third, it’s off by 2pi!

The code you want would be: toRadians = angle + std::fmodf(toRadians - angle, 2.0f * float_Pi);

Fourth, even if you fixed the math, the new code doesn’t render the same as the old. You might want to render all the way around a circle, plus a little more, if you had a gradient and/or the color involved was partly transparent… in other words, adding a path of angular span 2*pi + theta should not be the same as adding a path of span theta, and it isn’t in the old code.


#4

…no - I’m not calling the C library ::fmod function, I’m calling the C++ version, std::fmod, which should be an overloaded/templated function that takes either float or double.

Are you sure that by “fixing” it you’ve not actually broken it? I deliberately added the 2pi so that if the arc goes around in a complete circle many times, then it’ll still go around more than 360 degrees, but should finish in the correct place… If you remove the 2pi, then it’ll certainly not be correct…(?)