Pygame画抗锯齿粗线

Pygame draw anti-aliased thick line

我曾经在 pygame 处画线(给定一些起点和终点),如下所示:pygame.draw.line(window, color_L1, X0, X1, 2),其中 2 定义线的粗细。

因为 .draw 不支持抗锯齿功能,所以我搬到了 .gfxdrawpygame.gfxdraw.line(window, X0[0], X0[1], X1[0], X1[1], color_L1)

但是,这不允许我定义线的粗细。我怎样才能同时拥有厚度和抗锯齿?

我建议使用实心矩形,如下所示:https://www.pygame.org/docs/ref/gfxdraw.html#pygame.gfxdraw.rectangle

您的代码将类似于:

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

然后记得填充表面。这是沿着:

thickLine.fill()

经过多次试验和错误,最佳方法如下:

  1. 首先,我们定义形状的中心点给定线的 X0_{x,y} 起点和 X1_{x,y} 终点:

    center_L1 = (X0+X1) / 2.
    
  2. 然后求直线的斜率(角度):

    length = 10  # Total length of line
    thickness = 2
    angle = math.atan2(X0[1] - X1[1], X0[0] - X1[0])
    
  3. 使用斜率和形状参数,您可以计算出方框末端的以下坐标:

    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. 使用计算出的坐标,我们绘制一个未填充的抗锯齿多边形(感谢@martineau),然后按照 pygame 的 documentation 中的建议填充它gfxdraw 用于绘制形状的模块。

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

您还可以通过在原始线周围 +/- 1-N 像素处绘制线的副本来使用 pygame.draw.aalines() 函数做一些修改(是的,这不是非常有效,但是它在紧要关头起作用)。例如,假设我们有一个要绘制的线段列表 (self._segments) 和宽度 (self._LINE_WIDTH):

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))

您的回答可以完成工作,但我认为这是一种 better/more 可读的方式。这是借用你的答案,但你值得信赖。

from math import atan2, cos, degrees, radians, sin

def Move(rotation, steps, position):
    """Return coordinate position of an amount of steps in a direction."""
    xPosition = cos(radians(rotation)) * steps + position[0]
    yPosition = sin(radians(rotation)) * steps + position[1]
    return (xPosition, yPosition)

def DrawThickLine(surface, point1, point2, thickness, color):
    angle = degrees(atan2(point1[1] - point2[1], point1[0] - point2[0]))

    vertices = list()
    vertices.append(Move(angle-90, thickness, point1))
    vertices.append(Move(angle+90, thickness, point1))
    vertices.append(Move(angle+90, thickness, point2))
    vertices.append(Move(angle-90, thickness, point2))

    pygame.gfxdraw.aapolygon(surface, vertices, color)
    pygame.gfxdraw.filled_polygon(surface, vertices, color)

请记住,这更多地将厚度视为半径而不是直径。如果你想让它更像直径,你可以将变量的每个实例除以 2。

所以无论如何,这会计算矩形的所有点并填充它。它通过转到每个点并通过旋转 90 度并向前移动来计算两个相邻点来完成此操作。

这是一段稍长的代码,但也许会对某些人有所帮助。 它使用矢量并在连接两点的直线的每一侧创建笔划。

def make_vector(pointA,pointB): #vector between two points
    x1,y1,x2,y2 = pointA[0],pointA[1],pointB[0],pointB[1]
    x,y = x2-x1,y2-y1
    return x,y

def normalize_vector(vector): #sel explanatory
    x, y = vector[0], vector[1]
    u = math.sqrt(x ** 2 + y ** 2)
    try:
        return x / u, y / u
    except:
        return 0,0

def perp_vectorCL(vector): #creates a vector perpendicular to the first clockwise
    x, y = vector[0], vector[1]
    return y, -x

def perp_vectorCC(vector): #creates a vector perpendicular to the first counterclockwise
    x, y = vector[0], vector[1]
    return -y, x

def add_thickness(point,vector,thickness): #offsets a point by the vector
    return point[0] + vector[0] * thickness, point[1] + vector[1] * thickness

def draw_line(surface,fill,thickness, start,end): #all draw instructions
    x,y = make_vector(start,end)
    x,y = normalize_vector((x,y))


    sx1,sy1 = add_thickness(start,perp_vectorCC((x,y)),thickness//2)
    ex1,ey1 = add_thickness(end,perp_vectorCC((x,y)),thickness//2)
    pygame.gfxdraw.aapolygon(surface,(start,end,(ex1,ey1),(sx1,sy1)),fill)
    pygame.gfxdraw.filled_polygon(surface, (start, end, (ex1, ey1), (sx1, sy1)), fill)

    sx2, sy2 = add_thickness(start, perp_vectorCL((x, y)), thickness // 2)
    ex2, ey2 = add_thickness(end, perp_vectorCL((x, y)), thickness//2)
    pygame.gfxdraw.aapolygon(surface, (start, end, (ex2, ey2), (sx2, sy2)), fill)
    pygame.gfxdraw.filled_polygon(surface, (start, end, (ex2, ey2), (sx2, sy2)), fill)