enable icon indicating copy to clipboard operation
enable copied to clipboard

Component bounds shrink with repeated changes to aspect ratio.

Open tonysyu opened this issue 11 years ago • 1 comments

Component._enforce_aspect_ratio rescales the component using the most recent component bounds as an upper bound. As a result, repeatedly changing between low and high aspect ratios will shrink the component bounds to a point.

For example, run chaco's aspect_ratio.py and click on the "Screen" (?) check box. Then, set the aspect ratio to 2, then 0.5, then 2, ... You should see a plot that shrinks to a point eventually.

Note that resizing the window (by dragging a corner of the window) will fix the plot bounds. This is because resizing resets the bounds. These "maximum bounds" are never saved because outer_bounds (a.k.a. outer_width and outer_height) gets overwritten when figuring out the plot layout.

This will probably be fixed automatically if the layout gets rewritten to use constraints-based layouts.

tonysyu avatar Mar 05 '13 15:03 tonysyu

I am running into the same issue with Chaco. As a temporary solution, I have monkey-patched the Component._enforce_aspect_ratio to not "use the current width and height as a bounding box and find the largest rectangle of the desired aspect ratio that will fit". It works, but I'd like to find a more "future-safe" fix.

Can anyone with more experience give me advice on what the best solution would be now in 2016? Are the constraint-based layouts a feature now and could I use them with Chaco?

Edit: If anyone runs into this in the future, my changes are the following, which you can compare to the original.

def _enforce_aspect_ratio_without_resize(self, notify=True):
    """ This method adjusts the width and/or height of the component so
    that the new width and height match the aspect ratio.  It uses the
    current width and height as a bounding box and finds the largest
    rectangle of the desired aspect ratio that will fit.
    If **notify** is True, then fires trait events for bounds and
    position changing.
    """
    ratio = self.aspect_ratio
    old_w, old_h = [self.container.width, self.container.height]
    if ratio is None:
        return
    elif ratio == 0:
        self.width = 0
        return
    elif old_h == 0:
        return
    elif int(old_w) == int(ratio * old_h):
        pass

    old_aspect = old_w / float(old_h)
    new_pos = None
    if ratio > old_aspect:
        # desired rectangle is wider than bounding box, so use the width
        # and compute a smaller height
        new_w = old_w
        new_h = new_w / ratio

    else:
        # desired rectangle is taller than bounding box, so use the height
        # and compute a smaller width
        new_h = old_h
        new_w = new_h * ratio

    if self.auto_center:
        new_pos = [(old_w-new_w)/2.0, (old_h-new_h)/2.0]

    self.set(bounds=[new_w, new_h], trait_change_notify=notify)
    if new_pos:
        self.set(position=new_pos, trait_change_notify=notify)
    return

To monkey-patch, you can simply:

import types
plot._enforce_aspect_ratio = types.MethodType(_enforce_aspect_ratio_without_resize, plot)

or subclass the plot if you prefer.

stevenengler avatar Jan 13 '16 18:01 stevenengler