####################################################################################################
#
# PyDvi - A Python Library to Process DVI Stream
# Copyright (C) 2014 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
####################################################################################################
####################################################################################################
#
# Audit
#
# - 13/05/2010 fabrice
#
####################################################################################################
####################################################################################################
__all__ = ['Interval', 'IntervalInt', 'Interval2D', 'IntervalInt2D']
####################################################################################################
from PyDvi.Tools.FuncTools import middle
####################################################################################################
empty_interval_string = '[empty]'
####################################################################################################
[docs]class Interval(object):
""" One-dimension Interval
"""
##############################################
# Fixme: better name than array ?
def __init__(self, *args):
""" Initialise an interval
* Interval(inf, sup)
* else args must support the __getitem__ interface, e.g.:
* Interval((inf, sup))
* Interval([inf, sup])
* Interval(interval_instance)
"""
array = self._check_arguments(args)
self.inf = array[0]
self.sup = array[1]
if self.inf > self.sup: # None > None = False
raise ValueError("inf <= sup condition is false [%u, %u]" % (self.inf, self.sup))
##############################################
[docs] def _check_arguments(self, args):
size = len(args)
if size == 1:
array = args[0]
elif size == 2:
array = args
else:
raise ValueError("Args size > 2")
return array
##############################################
[docs] def copy(self):
""" Return a clone of the interval
"""
return self.__class__(self.inf, self.sup)
##############################################
def __getitem__(self, index):
if isinstance(index, slice):
if index.start is None:
lower = 0
else:
lower = index.start
if index.stop is None:
upper = 1
else:
upper = index.stop -1
if lower == 0 and upper == 1:
return self.inf, self.sup
elif lower == 0 and upper == 0:
return self.inf
elif lower == 1 and upper == 1:
return self.sup
else:
raise IndexError("Wrong slice")
elif index == 0:
return self.inf
elif index == 1:
return self.sup
else:
raise IndexError("Index is out of range")
##############################################
def __repr__(self):
return str(self.__class__) + ' ' + str(self)
##############################################
def __str__(self):
if self.is_empty():
return empty_interval_string
else:
return '[%f, %f]' % (self.inf, self.sup)
##############################################
[docs] def print_object(self):
""" Print the interval
"""
print str(self)
##############################################
[docs] def is_empty(self):
return self.inf is None and self.sup is None
##############################################
@staticmethod
[docs] def _intersection(i1, i2):
if i1.intersect(i2):
return (max((i1.inf, i2.inf)),
min((i1.sup, i2.sup)))
else:
return None, None
def __and__(i1, i2):
""" Return the intersection of i1 and i2
"""
return i1.__class__(i1._intersection(i1, i2))
def __iand__(self, i2):
""" Update the interval with its intersection with i2
"""
self.inf, self.sup = self._intersection(self, i2)
return self
##############################################
@staticmethod
[docs] def _union(i1, i2):
return (min((i1.inf, i2.inf)),
max((i1.sup, i2.sup)))
def __or__(i1, i2):
""" Return the union of i1 and i2
"""
return i1.__class__(i1._union(i1, i2))
def __ior__(self, i2):
""" Update the interval with its union with i2
"""
self.inf, self.sup = self._union(self, i2)
return self
##############################################
def __eq__(i1, i2):
""" Equality test
"""
return i1.inf == i2.inf and i1.sup == i2.sup
##############################################
def __lt__(i1, i2):
""" Test if i1.sup < i2.inf
"""
return i1.sup < i2.inf
##############################################
def __gt__(i1, i2):
""" Test if i1.inf > i2.sup
"""
return i1.inf > i2.sup
##############################################
def __iadd__(self, dx):
""" Shift the interval of dx
"""
self.inf += dx
self.sup += dx
return self
##############################################
def __add__(self, dx):
""" Construct a new interval shifted of dx
"""
return self.__class__((self.inf + dx,
self.sup + dx))
##############################################
def __mul__(self, scale):
""" Construct a new interval scaled by scale
"""
return self.__class__((self.inf * scale,
self.sup * scale))
##############################################
def __isub__(self, dx):
""" Shift the interval of -dx
"""
self.inf -= dx
self.sup -= dx
return self
##############################################
def __sub__(self, dx):
""" Construct a new interval shifted of -dx
"""
return self.__class__((self.inf - dx,
self.sup - dx))
###############################################
[docs] def intersect(i1, i2):
""" Does the interval intersect with i2?
"""
return ((i1.inf <= i2.sup and i2.inf <= i1.sup) or
(i2.inf <= i1.sup and i1.inf <= i2.sup))
##############################################
def __contains__(self, x):
""" Is *x* in the interval?
"""
return self.inf <= x and x <= self.sup
##############################################
[docs] def is_included_in(i1, i2):
""" Is the interval included in i1?
"""
# print '(%f <= %f and %f <= %f)' % (i2.inf, i1.inf, i1.sup, i2.sup)
return i2.inf <= i1.inf and i1.sup <= i2.sup
##############################################
# Fixme: length -> size ?
[docs] def length(self):
""" Return sup - inf
"""
return self.sup - self.inf
##############################################
[docs] def zero_length(self):
""" Return sup == inf
"""
return self.sup == self.inf
##############################################
[docs] def middle(self):
""" Return interval middle
"""
return middle(self.inf, self.sup)
##############################################
[docs] def enlarge(self, dx):
""" Enlarge the interval of dx
"""
self.inf -= dx
self.sup += dx
####################################################################################################
[docs]class IntervalInt(Interval):
""" One-dimension Integer Interval
"""
##############################################
# Fixme: better name than array ?
def __init__(self, *args):
""" Initialise an interval
array must support the __getitem__ interface
"""
array = self._check_arguments(args)
if None not in array:
array = [int(x) for x in array[:2]] # Fixme: rint ?
# Fixme: else:
super(IntervalInt, self).__init__(array)
##############################################
def __str__(self):
if self.is_empty():
return empty_interval_string
else:
return '[%i, %i]' % (self.inf, self.sup)
##############################################
[docs] def length(self):
""" Return sup - inf +1
"""
return self.sup - self.inf +1
#################################################################################
[docs]class Interval2D(object):
""" Two-dimension Interval
"""
##############################################
def __init__(self, x, y):
""" Initialise a 2D interval
x and y must support the __getitem__ interface
"""
self.x = Interval(x)
self.y = Interval(y)
##############################################
[docs] def copy(self):
""" Return a clone of the interval
"""
return self.__class__(self.x, self.y)
##############################################
def __setitem__(self, index, interval):
if index == 0:
self.x = interval
elif index == 1:
self.y = interval
else:
raise IndexError("Index is out of range")
##############################################
def __getitem__(self, index):
if index == 0:
return self.x
elif index == 1:
return self.y
else:
raise IndexError("Index is out of range")
##############################################
def __str__(self):
return str(self.x) + '*' + str(self.y)
##############################################
def __repr__(self):
return str(self.__class__) + ' ' + str(self)
##############################################
[docs] def print_object(self):
""" Print the interval
"""
print str(self)
##############################################
[docs] def is_empty(self):
return self.x.is_empty() or self.y.is_empty()
##############################################
def __and__(i1, i2):
""" Return the intersection of i1 and i2
"""
return i1.__class__(i1.x & i2.x,
i1.y & i2.y)
def __iand__(self, i2):
""" Update the interval with its intersection with i2
"""
self.x &= i2.x
self.y &= i2.y
return self
##############################################
# Union
def __or__(i1, i2):
""" Return the union of i1 and i2
"""
return i1.__class__(i1.x | i2.x,
i1.y | i2.y)
def __ior__(self, i2):
""" Update the interval with its union with i2
"""
self.x |= i2.x
self.y |= i2.y
return self
##############################################
def __eq__(i1, i2):
""" Equality test
"""
return i1.x == i2.x and i1.y == i2.y
##############################################
def __iadd__(self, dxy):
""" Shift the interval of *dxy*.
"""
self.x += dx
self.y += dy
return self
##############################################
def __add__(self, dxy):
""" Construct a new interval shifted by *dxy*.
"""
return self.__class__(self.x + dxy[0], self.y + dxy[1])
##############################################
def __imul__(self, scale):
""" Scale the interval by *scale*.
"""
self.x += scale
self.y += scale
return self
##############################################
def __mul__(self, scale):
""" Construct a new interval scaled by scale
"""
return self.__class__(self.x * scale, self.y * scale)
##############################################
[docs] def intersect(self, i2):
""" Does the interval intersect with i2?
"""
return (self.x.intersect(i2.x) and
self.y.intersect(i2.y))
##############################################
[docs] def is_included_in(self, i2):
""" Is the interval included in i2?
"""
return (self.x.is_included_in(i2.x) and
self.y.is_included_in(i2.y))
##############################################
[docs] def size(self):
""" Return the horizontal and vertical size
"""
return self.x.length(), self.y.length()
##############################################
[docs] def area(self):
""" Return the area
"""
return self.x.length() * self.y.length()
##############################################
[docs] def bounding_box(self):
""" Return the corresponding bounding box (x.inf, y.inf, x.sup, y.sup)
"""
return (self.x.inf, self.y.inf,
self.x.sup, self.y.sup)
##############################################
[docs] def middle(self):
""" Return interval middle
"""
return self.x.middle(), self.y.middle()
##############################################
[docs] def enlarge(self, dx):
""" Enlarge the interval of dx
"""
self.x.enlarge(dx)
self.y.enlarge(dx)
####################################################################################################
[docs]class IntervalInt2D(Interval2D):
""" Two-dimension Integer Interval
"""
##############################################
def __init__(self, x, y):
""" Initialise a 2D Integer interval
x and y must support the __getitem__ interface
"""
self.x = IntervalInt(x)
self.y = IntervalInt(y)
####################################################################################################
#
# End
#
####################################################################################################