Pygame draw anti-aliased thick line

2020-06-16 02:51发布

问题:

I used to draw lines (given some start and end points) at pygame like this: pygame.draw.line(window, color_L1, X0, X1, 2), where 2 was defining the thickness of the line.

As, anti-aliasing is not supported by .draw, so I moved to .gfxdraw and pygame.gfxdraw.line(window, X0[0], X0[1], X1[0], X1[1], color_L1).

However, this does not allow me to define the thickness of the line. How could I have thickness and anti-aliasing together?

回答1:

After many trials and errors, the optimal way to do it would be the following:

1) First, we define the center of the shape given the X0_{x,y} start and X1_{x,y} end points of the line.

center_L1 = (X0 + X1) / 2.

2) Then find the slope (angle) of the line.

length = 10 # Line size
thickness = 2
angle = math.atan2(X0[1] - X1[1], X0[0] - X1[0])

3) Using the slope and the shape parameters you can calculate the following coordinates of the box ends.

UL = (center_L1[0] + (length / 2.) * cos(angle) - (thickness / 2.) * sin(angle),
      center_L1[1] + (thickness / 2.) * cos(angle) + (length / 2.) * sin(angle))
UR = (center_L1[0] - (length / 2.) * cos(angle) - (thickness / 2.) * sin(angle),
      center_L1[1] + (thickness / 2.) * cos(angle) - (length / 2.) * sin(angle))
BL = (center_L1[0] + (length / 2.) * cos(angle) + (thickness / 2.) * sin(angle),
      center_L1[1] - (thickness / 2.) * cos(angle) + (length / 2.) * sin(angle))
BR = (center_L1[0] - (length / 2.) * cos(angle) + (thickness / 2.) * sin(angle),
      center_L1[1] - (thickness / 2.) * cos(angle) - (length / 2.) * sin(angle))

4) Using the computed coordinates we draw an anti-aliased polygon (thanks to @martineau) and then fill it as suggested on the gfxdraw website.

pygame.gfxdraw.aapolygon(window, (UL, UR, BR, BL), color_L1)
pygame.gfxdraw.filled_polygon(window, (UL, UR, BR, BL), color_L1)


回答2:

I would suggest a filled rectangle, as shown here: https://www.pygame.org/docs/ref/gfxdraw.html#pygame.gfxdraw.rectangle.

Your code would look something like:

thickLine = pygame.gfxdraw.rectangle(surface, rect, color)

and then remember to fill the surface. This is along the lines of:

thickLine.fill()


回答3:

You can also do a bit of a hack with the pygame.draw.aalines function by drawing copies of the line +/- 1-N pixels around the original line (yes, this isn't super efficient, but it works in a pinch). For example, assuming we have a list of line segments to draw:

for segment in self._segments:
  if len(segment) > 2:
    for i in xrange(self._LINE_WIDTH):
      pygame.draw.aalines(self._display, self._LINE_COLOR, False
                          ((x,y+i) for x,y in segment))
      pygame.draw.aalines(self._display, self._LINE_COLOR, False,
                          ((x,y-i) for x,y in segment))
      pygame.draw.aalines(self._display, self._LINE_COLOR, False
                          ((x+i,y) for x,y in segment))
      pygame.draw.aalines(self._display, self._LINE_COLOR, False,
                          ((x-i,y) for x,y in segment))