【问题标题】:Python to Draw a Filled "Circle" on a GridPython 在网格上绘制填充的“圆”
【发布时间】:2014-02-24 11:48:35
【问题描述】:

您能否提供一种在任意位置和半径的网格中绘制圆形(ish)形状的有效算法?

. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . o O o . . . . . . . . . . . . . . . . . . . .
. . . . O O O O O . . . . . . . . . . . . . . . . . . .
. . . o O O O O O o . . . . . . . . . . . . . . . . . .
. . . O O O O O O O . . . . . . . . . . o O o . . . . .
. . . o O O O O O o . . . . . . . . . o O O O o . . . .
. . . . O O O O O . . . . . . . . . . O O O O O . . . .
. . . . . o O o . . . . . . . . . . . o O O O o . . . .
. . . . . . . . . . . . . . . . . . . . o O o . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .

我用它来寻路。它是更精细解析的图形字段的低分辨率抽象。这些形状用作要避免的块。

请记住,我希望能够使用它来快速索引块所在位置的二维数组。

score = self.map[x][y]

因此“绘制”圆类似于将值设置为被阻止:

self.map[x][y] = PATH_COST_PROX1

绘制字段如下所示:

def printme(self):
    """ Print the map to stdout in ASCII."""
    for y in reversed(range(self.ymax)):
        for x in range(self.xmax):
            if self.map[x][y] >= PATH_COST_PROX0:
                print 'O',
            elif self.map[x][y] >= PATH_COST_PROX1:
                print 'o',
            else:
                print '.',
        print ''

编辑:这是我最初的(可耻的)尝试。我在网格上手工制作了圆圈,并记下了每次增加半径时添加的点。这不是一个糟糕的主意,但公认的答案要优雅得多。

COVER_MAP = [
    [(0,0)],
    [(0,1),(1,0),(0,-1),(-1,0)],
    [(1,1),(1,-1),(-1,-1),(-1,1)],
    [(0,2),(2,0),(0,-2),(-2,0)],
    [(1,2),(2,1),(2,-1),(1,-2),(-1,-2),(-2,-1),(-2,1),(-1,2)],
    [(0,3),(2,2),(3,0),(2,-2),(0,-3),(-2,-2),(-3,0),(-2,2)],
    [(1,3),(3,1),(3,-1),(1,-3),(-1,-3),(-3,-1),(-3,1),(-1,3)]
]

def set_blocked(self, p, radius):
    """
    Set the blocked state of a coordinate. Takes an integer value that
    represents the cost of the block
    """
    #radius = radius * 2
    if radius > len(COVER_MAP)-1:
        radius=len(COVER_MAP)-1
    #print "point:",p," radius:",radius
    (cx,cy) = p
    for i in range(len(COVER_MAP)):
        for j in range(len(COVER_MAP[i])):
            (rx,ry) = COVER_MAP[i][j]
            x = cx + rx
            y = cy + ry
            if x >= 0 and x < self.xmax and y >= 0 and y < self.ymax:
                if i < radius:
                    self.map[x][y] = PATH_COST_PROX0
                elif i == radius:
                    self.map[x][y] = PATH_COST_PROX1
                elif i == radius + 1:
                    self.map[x][y] = PATH_COST_PROX2
                elif i == radius + 2:
                    self.map[x][y] = PATH_COST_PROX3
                elif i == radius + 3:
                    self.map[x][y] = PATH_COST_PROX4

我的确实具有能够在原始圆圈周围制作成本降低的模糊环的优势,这是下面的记忆算法所没有但可以适应提供的东西。

【问题讨论】:

标签: python plot geometry


【解决方案1】:

我怀疑最快的方法是使用memoization(不要与“记忆”混淆)。这是生成半径最大为 20 像素的光盘的示例。如果您想要圆形或空心圆盘而不是填充圆盘,则需要为它们指定宽度并在 if 语句中包含 x_sq + y_sq &gt;= (k_r_sq - width)

根据 time.time()(如果你有 python 3.3 或更高版本,你可以使用 time.perf_counter()),加载每个磁盘的坐标集需要 3 微秒,但这没有考虑任何计算你可能想在那张光盘上做。

希望这会有所帮助。

import time
max_radius = 20    

i0 = time.time()
class DiscTemplate:
    def __init__(self, max_r):
        self.memos = []
        for k_r in range(1, max_r + 1):
            k_r_sq = k_r ** 2
            self.memos.append([])
            for x in range(-max_r, max_r + 1):
                x_sq = x ** 2
                for y in range(-max_r, max_r + 1):
                    y_sq = y ** 2
                    if x_sq + y_sq <= k_r_sq:
                        self.memos[k_r - 1].append((x,y))

        self.max_r = max_r

    def get_disc(self, r):
        return self.memos[r - 1]
test = DiscTemplate(20)
i1 = time.time()

print("time to make class:", i1 - i0)

t0 = time.time()
disc = test.get_disc(2)
t1 = time.time()

print("time to produce disc:", t1 - t0)
print("Disc coordinates: \n", disc)

【讨论】:

  • 如果我理解正确的话,你“预画”每个圆,记住每个半径的点列表,然后遍历点列表来渲染它们。这很棒。谢谢,@andreipmbcn。
  • 告诉我更多关于你用来填充圆圈的算法。或者更好的是,在您的代码中添加一些内联 cmets?
  • 我提出的预画圆的算法非常基本且缓慢 - 找到正方形中与圆心的距离在最小值和最大值之间的所有点,然后绘制它们。 Bresenham 圆算法对于预绘图要快得多。然而,无论您使用何种算法,记忆化本身都可以工作。我相信记忆本身比对每个新圈重复使用 Bresenham 算法要快,如果你愿意,我可以测试一下。
【解决方案2】:

我会尝试这样的:

  1. 找到你要画的圆的封闭矩形:

    top_left = (cx + radius*cos(3*pi/4), cy + radius*sin(3*pi/4)) # (cx, cy) center of the circle
    width, height = 2*radius, 2*radius
    
  2. 对于这个矩形的每个点,测试到中心的距离,如果这个距离小于半径,则设置相应的字符

编辑numpy

以下代码将为您提供圆圈中的点。这里没有 Python 中的任何循环,因此效率最高。

import numpy as np

#precomputed (depends on the dimensions of your grid)
x = np.arange(50) # x values
y = np.arange(50) # y values
xx, yy = np.meshgrid(x, y) # combine both vectors

# for this particular circle
cx, cy, r = 10, 10, 5

condition = (xx-cx)**2 + (yy-cy)**2 <= r**2
points = xx[condition] + 1j*yy[condition] # list of points in the circle (stored as complex)

【讨论】:

  • 为什么要重新发明轮子?绘制图元的算法有很多,大多数都比这要好。
  • 我的理解是,目标不是获得良好的抗锯齿绘图。此外,它可以通过 numpy 例程以非常有效的方式实现。 IHMOpremature optimization is the root of all evil
  • 它与过早优化无关,它是关于为目的选择正确的算法,这应该是设计应用程序的第一步。 OP要求an efficient algorithm for drawing a circle(ish) shape。你的算法会起作用,但它根本没有效率。
  • 所以我想我应该说中等效率。它将为每帧中的每个对象运行一次以计算寻路,但不是每帧多次。所以重要的是它的效率相当高,但不需要太快。也就是说,我不确定迭代封闭矩形中的所有空间是否合理有效。 O(n^2)
  • 虽然上面的记忆算法效率更高,但预先计算圆圈和使用记忆点列表的想法在实践中非常快速。像O(n)这样的东西。我还要注意,可以使用您的技术并记住结果以获得类似的结果。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-02
  • 1970-01-01
  • 2016-05-21
相关资源
最近更新 更多