【问题标题】:Anti-aliased Arc Pygame抗锯齿弧 Pygame
【发布时间】:2016-11-03 17:33:16
【问题描述】:

我正在尝试使用 Pygame 在 Python 中编写一个简单的圆形计时器。 目前它看起来像这样:

如您所见,蓝线非常波浪形,其中有白点。我通过使用 pygame.draw.arc() 函数实现了这条蓝线,但它没有抗锯齿并且看起来很糟糕。我希望它是抗锯齿的,但是应该让我实现这一点的 gfxdraw 模块不支持弧宽选择。这是代码sn-p:

pygame.draw.arc(screen, blue, [center[0] - 120, center[1] - 120, 240, 240], pi/2, pi/2+pi*i*koef, 15)
pygame.gfxdraw.aacircle(screen, center[0], center[1], 105, black)
pygame.gfxdraw.aacircle(screen, center[0], center[1], 120, black)

【问题讨论】:

    标签: python pygame antialiasing


    【解决方案1】:

    我不知道任何可以解决此问题的 pygame 函数,这意味着您基本上必须自己编写解决方案(或使用 pygame 以外的其他东西),因为 draw 已损坏,正如您所指出的那样,gfxdraw不会给你厚度。

    一个非常难看但简单的解决方案是在弧段上绘制多次,总是稍微移动以“填充”缺失的间隙。这仍然会在计时器弧的最前面留下一些锯齿,但其余部分将被填充。

    import pygame
    from pygame.locals import *
    import pygame.gfxdraw
    import math
    
    # Screen size
    SCREEN_HEIGHT = 350
    SCREEN_WIDTH = 500
    
    # Colors
    BLACK = (0, 0, 0)
    WHITE = (255, 255, 255)
    GREY = (150, 150, 150)
    RED = (255,0,0)
    
    
     # initialisation
    pygame.init()
    screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
    done = False
    clock = pygame.time.Clock()
    
    # We need this if we want to be able to specify our
    #  arc in degrees instead of radians
    def degreesToRadians(deg):
        return deg/180.0 * math.pi
    
    # Draw an arc that is a portion of a circle.
    # We pass in screen and color,
    # followed by a tuple (x,y) that is the center of the circle, and the radius.
    # Next comes the start and ending angle on the "unit circle" (0 to 360)
    #  of the circle we want to draw, and finally the thickness in pixels
    def drawCircleArc(screen,color,center,radius,startDeg,endDeg,thickness):
        (x,y) = center
        rect = (x-radius,y-radius,radius*2,radius*2)
        startRad = degreesToRadians(startDeg)
        endRad = degreesToRadians(endDeg)
    
        pygame.draw.arc(screen,color,rect,startRad,endRad,thickness)
    
    
    # fill screen with background
    screen.fill(WHITE)
    center = [150, 200]
    pygame.gfxdraw.aacircle(screen, center[0], center[1], 105, BLACK)
    pygame.gfxdraw.aacircle(screen, center[0], center[1], 120, BLACK)
    
    pygame.display.update()
    
    step = 10
    maxdeg = 0
    
    while not done:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                done = True
    
        maxdeg = maxdeg + step
        for i in range(min(0,maxdeg-30),maxdeg):
            drawCircleArc(screen,RED,(150,200),119,i+90,max(i+10,maxdeg)+90,14)  
            #+90 will shift it from starting at the right to starting (roughly) at the top
        pygame.display.flip()
    
        clock.tick(2)  # ensures a maximum of 60 frames per second
    
    pygame.quit()
    

    请注意,我从https://www.cs.ucsb.edu/~pconrad/cs5nm/08F/ex/ex09/drawCircleArcExample.py 复制了degreesToRadiansdrawCircleArc

    我通常不推荐这种解决方案,但它可能会在紧要关头。

    【讨论】:

    • 这看起来好多了,但仍然不完美。我会尝试谷歌或考虑其他可能的解决方案
    • 如果您需要使用 pygame,更简单的方法是在图形程序中执行它并从 spritesheet 中对计数器步骤进行 blit。如果您只需要其中几个地方,那么手写 hack 可能不值得。
    • 为什么不绘制下一段然后应用泛洪填充?
    • @Jean-FrançoisFabre 你将如何进行洪水填充? Afaik pygame 没有内置函数。您可以使用pygame.gfxdraw.filled_polygon(),但这意味着要预先计算多边形弧段的点,这似乎很费力。
    • 在这种情况下,我将为洪水填充执行森林火灾算法,但这可能会消耗 cpu,除非它按照您之前的建议进行预先计算。
    【解决方案2】:

    好吧,这真的很老了,但为什么不尝试画馅饼呢。例如画一个馅饼,然后一个空心圆作为外环,然后一个实心圆作为内环,另一个空心圆作为内环。

    所以pie -> unfilled circle -> filled circle -> unfilled

    顺序有些随意,但如果您仍然遇到此问题,请尝试一下。 (顺便说一句,我还没有尝试过,但我认为它会起作用)

    【讨论】:

      【解决方案3】:

      我用多边形创建了弧线。

      def drawArc(surface, x, y, r, th, start, stop, color):
          points_outer = []
          points_inner = []
          n = round(r*abs(stop-start)/20)
          if n<2:
              n = 2
          for i in range(n):
              delta = i/(n-1)
              phi0 = start + (stop-start)*delta
              x0 = round(x+r*math.cos(phi0))
              y0 = round(y+r*math.sin(phi0))
              points_outer.append([x0,y0])
              phi1 = stop + (start-stop)*delta
              x1 = round(x+(r-th)*math.cos(phi1))
              y1 = round(y+(r-th)*math.sin(phi1))
              points_inner.append([x1,y1])
          points = points_outer + points_inner        
          pygame.gfxdraw.aapolygon(surface, points, color)
          pygame.gfxdraw.filled_polygon(surface, points, color)
      

      使用生成器当然可以更优雅地创建 for 循环,但我对 python 不是很熟练。

      arc 确实比 pygame.draw.arc 看起来更好,但是当我将它与我的 mac 上的屏幕渲染进行比较时,还有改进的余地。

      【讨论】:

        【解决方案4】:

        为了我自己的使用,我写了一个简单的包装函数,并且为了处理圆弧绘制的问题,我使用了一个丑陋的循环来多次绘制相同的弧。

        def DrawArc(surface, color, center, radius, startAngle, stopAngle, width=1):
            width -= 2
            for i in range(-2, 3):
                # (2pi rad) / (360 deg)
                deg2Rad = 0.01745329251
                rect = pygame.Rect(
                    center[0] - radius + i,
                    center[1] - radius,
                    radius * 2,
                    radius * 2
                )
        
                pygame.draw.arc(
                    surface,
                    color,
                    rect,
                    startAngle * deg2Rad,
                    stopAngle * deg2Rad,
                    width
                )
        

        我知道这不是一个很好的解决方案,但它适合我的使用。

        重要的一点是,我添加了“width -= 2”以希望至少更准确地保持弧的预期大小,但这会导致最小宽度增加 2。

        在您的情况下,您可能需要考虑采取更多措施来解决由此导致的问题。

        【讨论】:

          【解决方案5】:

          你说得对,一些 pygame 渲染功能确实很烂,所以你可以用PIL 来实现类似的功能。

          pie_size = (40, 40)  # defining constants
          pil_img = PIL.Image.new("RGBA", pie_size)  # PIL template image
          pil_draw = PIL.ImageDraw.Draw(pil_img)  # drawable image
          pil_draw.pieslice((0, 0, *[ps - 1 for ps in pie_size]), -90, 180, fill=(0, 0, 0))  # args: (x0, y0, x1, y1), start, end, fill
          

          这将创建一个 PIL 形状。现在我们可以将其转换为 pygame。

          data = pil_img.tobytes()
          size = pil_img.size
          mode = pil_img.mode
          pygame_img = pygame.image.fromstring(data, size, mode).convert_alpha()
          

          但不要忘记pip install pillow

          import PIL.Image
          import PIL.ImageDraw
          

          【讨论】:

            猜你喜欢
            • 2014-07-14
            • 2015-08-15
            • 1970-01-01
            • 1970-01-01
            • 2014-02-18
            • 1970-01-01
            • 2016-06-28
            • 2011-07-05
            • 2014-03-19
            相关资源
            最近更新 更多