localAreaToGlobal() fails when a Component has been transformed below integer resolution

I have a hierarchy of Components which are scaled with a simple AffineTransform. The inner Components are frequently very small. I want to use localAreaToGlobal() to find the absolute area of each Component, but this function fails when the inner Components shrink beyond the integer resolution.

Here is a simple example:

    Component outer, middle, inner;

    addAndMakeVisible(outer);
    outer.addAndMakeVisible(middle);
    middle.addAndMakeVisible(inner);

    outer. setSize(10, 10);
    middle.setSize(10, 10);
    inner. setSize(10, 10);

    auto scaleUp   = AffineTransform().scale(8.0f);
    auto scaleDown = AffineTransform().scale(0.125f);

    outer. setTransform(scaleUp);        // So outer's side should be 10 * 8         = 80
    middle.setTransform(scaleDown);      // middle should be          10 * 8 / 8     = 10
    inner. setTransform(scaleDown);      // inner should be           10 * 8 / 8 / 8 = 1.25

    auto outerArea  = outer. localAreaToGlobal(outer. getLocalBounds());
    auto middleArea = middle.localAreaToGlobal(middle.getLocalBounds());
    auto innerArea  = inner. localAreaToGlobal(inner. getLocalBounds());

    DBG(String("outer area: ")  + outerArea. toString());   // prints 80
    DBG(String("middle area: ") + middleArea.toString());   // prints 10
    DBG(String("inner area: ")  + innerArea. toString());   // prints 8 -- oh no!

In this example, the “outer” Component is scaled up by 8 times. I did this to illustrate how small rounding errors can be amplified, so that localAreaToGlobal() becomes completely inaccurate, rather than just a little bit.

It is quite obvious why this happens when you look at the source code for the Component class, but for a casual user it might not be. The Affine Transformations might have been set it completely different areas of the code, making the error difficult to track down.

As a temporary solution, I’m multiplying the area up before applying the function, and then dividing it back afterwards:

    auto correctInnerArea = inner.localAreaToGlobal(inner.getLocalBounds() * 128) / 128;
    DBG(String("correct inner area: ") + correctInnerArea.toString()); // prints 1 -- close enough!

This works in my case, but it is hardly a general solution.

Proposed solutions:

1: Fix the existing localAreaToGlobal() function so that it works internally with float precision. I’m not exactly sure how to do this, but it doesn’t look like it would be too complicated.

or 2: Add a new method in Component for Rectangle< float> localAreaToGlobal (Rectangle<float>). The existing Rectangle< int> localAreaToGlobal (Rectangle<int>) method is just accessing a template function ComponentHelpers::convertCoordinate() anyway, so I think this addition would be very trivial.

or 3: At the very least, add a disclaimer to the documentation, warning users that the function may not produce accurate results if the Component has been transformed below integer resolution.