Source code for swinging_door

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""

swinging_door
=============

Implementation of the SwingingDoor algorithm in Python.

"""

from sys import version_info
from typing import TYPE_CHECKING

if TYPE_CHECKING:  # pragma: no cover
    from typing import Generator, Tuple

__author__ = "Aleksandr F. Mikhaylov (ChelAxe)"  # type: str
__version__ = "0.1.1"  # type: str
__license__ = "MIT"  # type: str


[docs]class Point: """ Point in rectangular coordinate system. """
[docs] def __init__(self, abscissa, ordinate): # type: (float, float) -> None """ Class constructor specifying the coordinates of the point. :param float abscissa: point abscissa; :param float ordinate: point ordinate. >>> Point(1.0, 2.0) Point(1.0, 2.0) """ self.abscissa = abscissa self.ordinate = ordinate
[docs] def __call__(self): # type: () -> Tuple[float, float] """ A call that returns the coordinates of a point. :rtype: Tuple[float, float] :return: Point coordinates. >>> Point(1.0, 2.0)() (1.0, 2.0) """ return self.abscissa, self.ordinate
[docs] def __repr__(self): # type: () -> str """ Unambiguous textual representation of an object. :rtype: str :return: Unambiguous textual representation of an object. >>> repr(Point(1.0, 2.0)) 'Point(1.0, 2.0)' """ return "Point({abscissa}, {ordinate})".format( abscissa=self.abscissa, ordinate=self.ordinate )
[docs] def __str__(self): # type: () -> str """ Natural textual representation of the object. :rtype: str :return: Natural textual representation of the object. >>> str(Point(1.0, 2.0)) '(1.0, 2.0)' """ return "({abscissa}, {ordinate})".format( abscissa=self.abscissa, ordinate=self.ordinate )
[docs]def swinging_door( data, # type: Generator[Tuple[float, float], None, None] deviation=0.1, # type: float ): # type: (...) -> Generator[Tuple[float, float], None, None] """ Implementation of the SwingingDoor algorithm. :param Generator[Tuple[float,float],None,None] data: data; :param float deviation: compression deflection. :rtype: Generator[Tuple[float, float], None, None] :return: Compressed data. >>> def data(values): ... x = 0.0 ... for y in values: ... yield x, y ... x += 1.0 >>> tuple(swinging_door(data([ ... 2.1, 3.1, 4.1, 4.6 ... ]), deviation=0.1)) ((0.0, 2.1), (2.444444444444445, 4.372222222222222)) >>> tuple(swinging_door(data([ ... 4.1, 3.1, 2.1, 4.6 ... ]), deviation=0.1)) ((0.0, 4.1), (2.088235294117647, 2.270588235294118)) """ entrance = current = Point( *( data.__next__() if version_info.major > 2 else data.next() # type: ignore ) ) # type: Point upper_pivot = Point( entrance.abscissa, entrance.ordinate + deviation ) # type: Point lower_pivot = Point( entrance.abscissa, entrance.ordinate - deviation ) # type: Point sloping_upper_max = sloping_lower_min = 0.0 # type: float yield entrance() while True: past = current # type: Point try: current = Point( *( data.__next__() if version_info.major > 2 else data.next() # type: ignore ) ) except StopIteration: break sloping_upper = (current.ordinate - upper_pivot.ordinate) / ( current.abscissa - upper_pivot.abscissa ) # type: float sloping_lower = (current.ordinate - lower_pivot.ordinate) / ( current.abscissa - lower_pivot.abscissa ) # type: float if not sloping_upper_max and not sloping_lower_min: sloping_upper_max = sloping_upper sloping_lower_min = sloping_lower continue if sloping_upper > sloping_upper_max: sloping_upper_max = sloping_upper if sloping_upper_max > sloping_lower_min: sloping_entrance = (current.ordinate - past.ordinate) / ( current.abscissa - past.abscissa ) # type: float entrance_upper = ( upper_pivot.ordinate - past.ordinate + sloping_entrance * past.abscissa - sloping_lower_min * upper_pivot.abscissa ) / ( sloping_entrance - sloping_lower_min ) # type: float entrance = Point( entrance_upper, upper_pivot.ordinate + sloping_lower_min * (entrance_upper - upper_pivot.abscissa) - deviation / 2, ) yield entrance() upper_pivot = Point( entrance.abscissa, entrance.ordinate + deviation ) lower_pivot = Point( entrance.abscissa, entrance.ordinate - deviation ) sloping_upper_max = sloping_upper = ( current.ordinate - upper_pivot.ordinate ) / (current.abscissa - upper_pivot.abscissa) sloping_lower_min = sloping_lower = ( current.ordinate - lower_pivot.ordinate ) / (current.abscissa - lower_pivot.abscissa) elif sloping_lower < sloping_lower_min: sloping_lower_min = sloping_lower if sloping_upper_max > sloping_lower_min: sloping_entrance = (current.ordinate - past.ordinate) / ( current.abscissa - past.abscissa ) entrance_lower = ( lower_pivot.ordinate - past.ordinate + sloping_entrance * past.abscissa - sloping_upper_max * lower_pivot.abscissa ) / (sloping_entrance - sloping_upper_max) entrance = Point( entrance_lower, lower_pivot.ordinate + sloping_upper_max * (entrance_lower - lower_pivot.abscissa) + deviation / 2, ) yield entrance() upper_pivot = Point( entrance.abscissa, entrance.ordinate + deviation ) lower_pivot = Point( entrance.abscissa, entrance.ordinate - deviation ) sloping_upper_max = sloping_upper = ( current.ordinate - upper_pivot.ordinate ) / (current.abscissa - upper_pivot.abscissa) sloping_lower_min = sloping_lower = ( current.ordinate - lower_pivot.ordinate ) / (current.abscissa - lower_pivot.abscissa)