Intersection and difference of two rectangles

前端 未结 3 1029
梦如初夏
梦如初夏 2020-12-06 14:49

Searching the internet has not given a satisfactory solution for the following problem. Given a class Rectangle defined as the following:

class          


        
3条回答
  •  醉话见心
    2020-12-06 15:32

    For the complete program the Rectangle class was used in and as a practical example of its usage:

    # The robots have requested your help setting up a new base on the
    # island. They need you to define the visibility of a building from the
    # southern edge of the base. To help you out, you have been given a map
    # of the buildings in the complex. The map is an orthogonal projection
    # of each of the buildings onto a horizontal plane. It is oriented on a
    # rectangular coordinate system so that the positive x-axis points east
    # and the positive y-axis points north. No two buildings in the map
    # overlap or touch. Each of the buildings have perfectly rectangular
    # sides and are aligned from north to south and from east to west. The
    # map is a list of buildings. Every building is presented as the list
    # with coordinate of south-west corner, coordinate of north-east corner
    # and height - [Xsw, Ysw, Xne, Yne, height]. We need to determinate how
    # many of the buildings are visible from the area just south of the base
    # (excluding the angle of vision, just using projection.) See the
    # illustration below.
    
    # Input: Building coordinates and heights as a list of lists. The
    # coordinates are integers. The heights are integers or floats.
    
    # Output:The quantity of visible buildings as an integer.
    
    # Example:
    # checkio([
    #     [1, 1, 4, 5, 3.5],
    #     [2, 6, 4, 8, 5],
    #     [5, 1, 9, 3, 6],
    #     [5, 5, 6, 6, 8],
    #     [7, 4, 10, 6, 4],
    #     [5, 7, 10, 8, 3]
    # ]) == 5 #"First"
    # checkio([
    #     [1, 1, 11, 2, 2],
    #     [2, 3, 10, 4, 1],
    #     [3, 5, 9, 6, 3],
    #     [4, 7, 8, 8, 2]
    # ]) == 2 #"Second"
    # assert checkio([
    #     [1, 1, 3, 3, 6],
    #     [5, 1, 7, 3, 6],
    #     [9, 1, 11, 3, 6],
    #     [1, 4, 3, 6, 6],
    #     [5, 4, 7, 6, 6],
    #     [9, 4, 11, 6, 6],
    #     [1, 7, 11, 8, 3.25]
    # ]) == 4 #"Third"
    
    # How it is used: This concept is useful for image recognition systems
    # and graphical systems. When rendering of 3D model you should determine
    # the visibility of the surfaces. This concept also can be applied in
    # architecture and city planning, allowing you to plan out which sides
    # of a building will receive sunlight, or if a building will block
    # natural light in another building.
    
    # Precondition: 0 < |buildings| < 10> 10
    # ∀ x ∈ coordinate : x is an integer; 0 ≤ x ≤10
    # ∀ h ∈ heights : x is an integer or a float; 0 < h ≤20
    
    ################################################################################
    
    from itertools import combinations, product, starmap, tee
    from pprint import pprint
    from random import randint
    
    ################################################################################
    
    TESTS = {
        "0. Basics": [
            #First
            {
                "input":
                    [
                        [1, 1, 4, 5, 3.5],
                        [2, 6, 4, 8, 5],
                        [5, 1, 9, 3, 6],
                        [5, 5, 6, 6, 8],
                        [7, 4, 10, 6, 4],
                        [5, 7, 10, 8, 3]
                    ],
                "answer": 5,
                "explanation": [5, 1, 3, 4, 0, 2]
            },
            #Second
            {
                "input":
                    [
                        [1, 1, 11, 2, 2],
                        [2, 3, 10, 4, 1],
                        [3, 5, 9, 6, 3],
                        [4, 7, 8, 8, 2]
                    ],
                "answer": 2
            },
            #Third
            {
                "input":
                    [
                        [1, 1, 3, 3, 6],
                        [5, 1, 7, 3, 6],
                        [9, 1, 11, 3, 6],
                        [1, 4, 3, 6, 6],
                        [5, 4, 7, 6, 6],
                        [9, 4, 11, 6, 6],
                        [1, 7, 11, 8, 3.25]
                    ],
                "answer": 4
            },
            #Alone
            {
                "input":
                    [
                        [0, 0, 1, 1, 10]
                    ],
                "answer": 1
            },
            #Shadow
            {
                "input":
                    [
                        [2, 2, 3, 3, 4],
                        [2, 5, 3, 6, 4]
                    ],
                "answer": 1
            },
        ],
        "1. Extra": [
            #H1
            {
                "input":
                    [
                        [1, 1, 3, 3, 20],
                        [3, 4, 5, 6, 10],
                        [5, 1, 7, 3, 20],
                        [1, 7, 7, 9, 20]
                    ],
                "answer": 4
            },
            #H2
            {
                "input":
                    [
                        [1, 1, 3, 3, 20],
                        [3, 4, 5, 6, 20],
                        [5, 1, 7, 3, 20],
                        [1, 7, 7, 9, 20]
    
                    ],
                "answer": 3
            },
            #H3
            {
                "input":
                    [
                        [0, 1, 1, 2, 2.5],
                        [0, 3, 1, 4, 3.5],
                        [0, 5, 1, 6, 1.5],
                        [3, 0, 4, 2, 30],
                        [5, 0, 6, 2, 2],
                        [7, 0, 8, 2, 2],
                        [4, 3, 8, 4, 2],
                        [4, 5, 5, 6, 1],
                        [7, 5, 8, 6, 3]
                    ],
                "answer": 7
            },
            #H4
            {
                "input":
                    [
                        [0, 0, 10, 1, 10],
                        [3, 3, 4, 4, 1],
                        [5, 5, 6, 6, 1],
                        [7, 7, 8, 8, 1]
                    ],
                "answer": 1
            },
        ],
        "2. Random": [
            #Half-Random
            {
                "input":
                    [
                        [0, 0, 10, 1, 10],
                        [3, 3, 4, 4, randint(1, 9)],
                        [5, 5, 6, 6, randint(1, 9)],
                    ],
                "answer": 1
            },
    
            #Half-Random
            {
                "input":
                    [
                        [1, 1, 2, 2, 1],
                        [randint(3, 5), randint(3, 5), randint(6, 8), randint(6, 8), 1]
                    ],
                "answer": 2
            },
        ]
    }
    
    ################################################################################
    
    def test():
        for category, tests in sorted(TESTS.items()):
            for test in tests:
                i, a = test['input'], test['answer']
                o = checkio(i)
                if o != a:
                    print('Category:', category)
                    print('  Input:')
                    pprint(i, indent=8)
                    print('  Output:', o)
                    print('  Answer:', a)
    
    def checkio(buildings):
        buildings = sorted(starmap(Building, buildings), key=lambda b: b.z)
        for a, b in combinations(buildings, 2):
            if a.seen:
                a.cover(b)
        return sum(b.seen for b in buildings)
    
    ################################################################################
    
    class Building:
    
        def __init__(self, x1, y1, x2, y2, height):
            self.rect = [Rectangle(x1, 0, x2, height)]
            self.z = min(y1, y2)
    
        def __str__(self):
            return 'Z = {}; {}'.format(self.z, self.rect)
    
        def cover(self, other):
            for s in self.rect:
                other.rect = list(flatten(o - s for o in other.rect))
    
        @property
        def seen(self):
            return bool(self.rect)
    
    def flatten(iterable):
        if isinstance(iterable, Rectangle):
            raise TypeError()
        for item in iterable:
            try:
                yield from flatten(item)
            except TypeError:
                yield item
    
    ################################################################################
    
    class Rectangle:
    
        __slots__ = '__x1', '__y1', '__x2', '__y2'
    
        def __init__(self, x1, y1, x2, y2):
            self.__setstate__((min(x1, x2), min(y1, y2), max(x1, x2), max(y1, y2)))
    
        def __repr__(self):
            return '{}({})'.format(type(self).__name__, ', '.join(map(repr, self)))
    
        def __eq__(self, other):
            return self.data == other.data
    
        def __ne__(self, other):
            return self.data != other.data
    
        def __hash__(self):
            return hash(self.data)
    
        def __len__(self):
            return 4
    
        def __getitem__(self, key):
            return self.data[key]
    
        def __iter__(self):
            return iter(self.data)
    
        def __and__(self, other):
            x1, y1, x2, y2 = max(self.x1, other.x1), max(self.y1, other.y1), \
                             min(self.x2, other.x2), min(self.y2, other.y2)
            if x1 < x2 and y1 < y2:
                return type(self)(x1, y1, x2, y2)
    
        def __sub__(self, other):
            intersection = self & other
            if intersection is None:
                yield self
            else:
                x, y = {self.x1, self.x2}, {self.y1, self.y2}
                if self.x1 < other.x1 < self.x2:
                    x.add(other.x1)
                if self.y1 < other.y1 < self.y2:
                    y.add(other.y1)
                if self.x1 < other.x2 < self.x2:
                    x.add(other.x2)
                if self.y1 < other.y2 < self.y2:
                    y.add(other.y2)
                for (x1, x2), (y1, y2) in product(pairwise(sorted(x)),
                                                  pairwise(sorted(y))):
                    instance = type(self)(x1, y1, x2, y2)
                    if instance != intersection:
                        yield instance
    
        def __getstate__(self):
            return self.x1, self.y1, self.x2, self.y2
    
        def __setstate__(self, state):
            self.__x1, self.__y1, self.__x2, self.__y2 = state
    
        @property
        def x1(self):
            return self.__x1
    
        @property
        def y1(self):
            return self.__y1
    
        @property
        def x2(self):
            return self.__x2
    
        @property
        def y2(self):
            return self.__y2
    
        @property
        def width(self):
            return self.x2 - self.x1
    
        @property
        def height(self):
            return self.y2 - self.y1
    
        intersection = __and__
    
        difference = __sub__
    
        data = property(__getstate__)
    
    def pairwise(iterable):
        "s -> (s0, s1), (s1, s2), (s2, s3), ..."
        a, b = tee(iterable)
        next(b, None)
        return zip(a, b)
    
    ################################################################################
    
    if __name__ == '__main__':
        test()
    

提交回复
热议问题