5

I am using pygame and the math module to try and draw a rectangle around two points. Here's what I mean by that:

Rectangle-controlled-by-two-points

The distance from the points to the ends of the rectangles is specified as an attribute of the class. I have tried a lot to get this to where it is, and the angle shown is probably the only working angle. Here is my class for the rectangle:

def Pol(x, y):
    """Converts rectangular coordinates into polar ones"""
    if x == 0: # This might be the source of my problems, but without it, it raises ZeroDivisionErrors at certain places
        if y >= 0:
            return [y, 90]
        else:
            return [-y, 270]
    r     = math.sqrt(x**2+y**2)
    angle = (math.degrees(math.atan((y/x))))%360
    return [r, angle]

class Path(pygame.sprite.Sprite):
    def __init__(self, start, end, color, width, **options):
        self.__dict__.update(options)
        self.start = start
        self.end = end
        self.color=color
        # Call the parent class (Sprite) constructor
        super().__init__()

        # Pass in the color of the blob, and its x and y position, width and height
        # Set the background color and make it transparent
        self.width = width

        # Draw the path
        self.redraw()

        # Fetch the rectangle object that has the dimensions of the image.
        self.rect = self.image.get_rect()
        self.rect.x, self.rect.y = (self.start[0]-self.width//2+self.ix,
                                    self.start[1]-self.width//2+self.iy)
        if self.inversed:
            self.rect.y-=(math.ceil(self.image.get_rect()[3]/2)-self.iy)

    def redraw(self):
        dis, angle = Pol(self.end[0]-self.start[0], self.end[1]-self.start[1])
        dis += self.width
        _image = pygame.Surface([dis, self.width])
        _image.fill([255, 255, 255])
        _image.set_colorkey([255, 255, 255])
        pygame.draw.rect(_image, self.color, pygame.Rect(0, 0, dis, self.width))
        nangle = (180-angle)%360
        self.inversed = nangle>=180
        self.image = pygame.transform.rotate(_image, nangle)
        i1 = _image.get_rect()
        i2 = self.image.get_rect()
        ix = i2[2]-i1[2]
        iy = i2[3]-i1[2]
        self.ix = ix//2
        self.iy = iy//2

So, I pass it some points, and it takes care of all the heavy lifting, and drawing. The points I passed it in the image were (100, 100) and (200, 200). I then tried it with (300, 300) and (200, 200), and it performed semi-successfully:

failed test

The code is above, you can try it out with other values, but you'll see that it quickly becomes problematic. To wrap it up, my question is, is there a robust way to draw a rectangle around given two points? I have tried:

  • with drawing thick lines, but pygame lines get broken up at certain angles, and have horizontal endings
  • testing and playing over and over again with the values, nothing I've tried works
  • changing the inverse if statement. According to what I think should happen, it's already backward
User 12692182
  • 927
  • 5
  • 16

1 Answers1

5

The issue is the function math.atan() which returns an angle in range [-pi/2, +pi/2].
Use math.atan2() (see math) which returns an angle in range [-pi,+pi]. Hence this function gives the correct angle at once, and you don't need any corrections:

angle = (math.degrees(math.atan((y/x))))%360

angle = math.degrees(math.atan2(y, x))

See also What is the difference between atan and atan2 in C++?

Rabbid76
  • 202,892
  • 27
  • 131
  • 174
  • 2
    [Some background reading](https://stackoverflow.com/questions/283406/what-is-the-difference-between-atan-and-atan2-in-c) (the Python functions mirror the C ones). – Karl Knechtel May 25 '20 at 18:53