1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
|
"""
This module provides some routines for performing layout
calculations to organize rectangular windows in a larger canvas
"""
from __future__ import absolute_import, division, print_function
from collections import Counter
from glue.external import six
class Rectangle(object):
def __init__(self, x, y, w, h):
""" A rectangle (obviously).
:param x: Left edge
:param y: Bottom edge
:param w: Width
:param h: Height
"""
self.x = x
self.y = y
self.w = w
self.h = h
def __eq__(self, other):
return (self.x == other.x and
self.y == other.y and
self.w == other.w and
self.h == other.h)
# In Python 3, if __eq__ is defined, then __hash__ has to be re-defined
if six.PY3:
__hash__ = object.__hash__
def __str__(self):
return repr(self)
def __repr__(self):
return "Rectangle(%f, %f, %f, %f)" % (self.x, self.y, self.w, self.h)
def snap(self, xstep, ystep=None, padding=0.0):
"""
Snap the rectangle onto a grid, with optional padding.
:param xstep: The number of intervals to split the x=[0, 1] range into.
:param ystep: The number of intervals to split the y=[0, 1] range into.
:param padding: Uniform padding to add around the result. This shrinks
the result so that the edges + padding line up with the
grid.
:returns: A new Rectangle, obtained by snapping self onto the grid,
and applying padding
"""
if ystep is None:
ystep = xstep
return Rectangle(round(self.x * xstep) / xstep + padding,
round(self.y * ystep) / ystep + padding,
round(self.w * xstep) / xstep - 2 * padding,
round(self.h * ystep) / ystep - 2 * padding)
def _snap_size(rectangles):
x = Counter([round(1 / r.w) for r in rectangles])
y = Counter([round(1 / r.h) for r in rectangles])
return x.most_common()[0][0], y.most_common()[0][0]
def snap_to_grid(rectangles, padding=0.0):
"""
Snap a collection of rectangles onto a grid, in a sensible fashion
:param rectangles: List of Rectangle instances
:returns: A dictionary mapping each input rectangle to a snapped position
"""
result = {}
xs, ys = _snap_size(rectangles)
for r in rectangles:
result[r] = r.snap(xs, ys, padding=padding)
return result
|