So I wrote a 2d rectangle class for use in a 2d pygame based game engine. It uses the vector class from this question, so if you want to test, you need that code too. I want to ask: Does this implementation has any huge downsides? Is the type hinting ok? Do I have any typos?

from numbers import Real
from typing import Sequence, Tuple

from vector import Vector, VectorType

RectType = Sequence[VectorType]

class Rect(RectType):
    def __len__(self) -> int:
        return 2

    def __getitem__(self, i: int) -> Vector:
        if i == 0:
            return self.pos1
            return self.pos2

    pos1: Vector
    pos2: Vector

    def __init__(self, pos1: VectorType, pos2: VectorType):
        x1, x2 = sorted((pos1[0], pos2[0]))
        y1, y2 = sorted((pos1[1], pos2[1]))
        self.pos1 = Vector(x1, y1)
        self.pos2 = Vector(x2, y2)

    def __repr__(self):
        return f"{self.__class__.__name__}{(self.pos1, self.pos2)}"

    def from_xywh(cls, xy: VectorType, wh: VectorType):
        return Rect(Vector(xy), Vector(xy) + Vector(wh))

    def scale_wh(self, scale: VectorType) -> Rect:
        return Rect((self.pos1.x * scale[0], self.pos1.y * scale[1]), (self.pos2.x * scale[0], self.pos2.y * scale[1]))

    def collide_point(self, pos: VectorType):
        return self.pos1.x <= pos[0] <= self.pos2[1] and self.pos1.y <= pos[1] <= self.pos2.y

    def collide_rect(self, other: Rect):
        return any(self.collide_point(p) for p in (other.top_left, other.top_right, other.bottom_left, other.bottom_right)) or \
               any(other.collide_point(p) for p in (self.top_left, self.top_right, self.bottom_left, self.bottom_right))

    def relative_rect(self, parent: Rect) -> Rect:
        p1 = self.pos1 - parent.pos1
        p1 = Vector(p1.x / parent.w, p1.y / parent.h)
        p2 = self.pos2 - parent.pos1
        p2 = Vector(p2.x / parent.w, p2.y / parent.h)
        return self.__class__(p1, p2)

    def relative_point(self, point: VectorType) -> Vector:
        return (point - self.pos1) / self.wh

    def xywh(self):
        return self.left, self.top, self.width, self.height

    def width(self):
        return abs(self.pos1.x - self.pos2.x)

    w = width

    def height(self):
        return abs(self.pos1.y - self.pos2.y)

    h = height

    def size(self):
        return Vector(self.w, self.h)

    wh: Vector = size

    def left(self) -> float:
        return self.pos1.x

    def left(self, value: float):
        self.pos1.x = value

    def right(self):
        return self.pos2.x

    def right(self, value):
        self.pos2.x = value

    def top(self):
        return self.pos1.y

    def top(self, value):
        self.pos1.y = value

    def bottom(self):
        return self.pos2.y

    def bottom(self, value):
        self.pos2.y = value

    def top_left(self) -> Vector:
        return self.pos1

    def top_left(self, value: Vector):
        self.pos1 = value

    xy: Vector = top_left

    def bottom_right(self) -> Vector:
        return self.pos2

    def bottom_right(self, value: Vector):
        self.pos2 = value

    def top_right(self) -> Vector:
        return Vector(self.pos2.x, self.pos1.y)

    def top_right(self, value: Vector):
        self.pos2.x, self.pos1.y = value

    def bottom_left(self) -> Vector:
        return Vector(self.pos1.x, self.pos2.y)

    def bottom_left(self, value: VectorType):
        self.pos1.x, self.pos2.y = value

    def center(self) -> Vector:
        return Vector(self.pos1.x + self.w / 2, self.pos1.y + self.h / 2)

    def normalized(self):
        x1, x2 = sorted((self.pos1.x, self.pos2.x))
        y1, y2 = sorted((self.pos1.y, self.pos2.y))
        return Rect((x1, y1), (x2, y2))

Here is the code on github (with a few additions)

  • \$\begingroup\$ Have you reviewed your code and tested it for typos yourself? \$\endgroup\$ – user34073 Apr 30 '18 at 20:44
  • 1
    \$\begingroup\$ @Hosch250 Yes, and I didn't find any. But maybe I just didn't look close enough. \$\endgroup\$ – MegaIng Apr 30 '18 at 20:46
return abs(self.pos1.x - self.pos2.x)

The abs is unnecessary since you've already sorted the coordinates.

I'm not sure using Vector for both absolute positions and relative sizes is the best practice. But in any event, you're missing the type hinting in

def size(self):

We also see a problem with:

    def left(self, value: float):
        self.pos1.x = value

in that it does not handle the case where the "left" is set to something greater than the right. It should either raise, or switch the coordinates. Similar with the other property setters.

| improve this answer | |
  • \$\begingroup\$ How would you go about solving the typehint problem for size? Create a Synonym for Vector, Size? \$\endgroup\$ – MegaIng May 1 '18 at 9:24
  • \$\begingroup\$ Thanks for the accept, but on this website it's customary to wait 24-48 hours before accepting. Accepting send a signal "I'm satisfied with this answer and I don't want any others." You really ought to give multiple people a chance to comment. \$\endgroup\$ – Snowbody May 1 '18 at 17:21
  • \$\begingroup\$ Ok. If you don't want. \$\endgroup\$ – MegaIng May 1 '18 at 17:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.