【问题标题】:Get all points of a straight line in python在python中获取直线的所有点
【发布时间】:2020-10-28 22:43:29
【问题描述】:

非常简单,给定一个点 A(x,y) 和另一个点 B(m,n),我需要一个可以在任何可迭代对象中返回其间所有点的列表 [k,z] 的函数。

我只对整数点感兴趣,所以不需要浮点数。

我需要最好的 Pythonic 方式,因为这个“小”功能将被大量运行,并且是更大系统的关键支柱。

编辑:

@roippi,感谢指出有关整数的问题。从下面的代码中,您可以看到我尝试跨过 x 轴并获得相应的 y,然后对 y 执行相同的操作。我的点集不会有任何非离散坐标点,所以暂时我可以忽略这个小缺陷

import itertools
#Vars
origin = {'x':0, 'y':0}

def slope(origin, target):
    if target['x'] == origin['x']:
        return 0
    else:
        m = (target['y'] - origin['y']) / (target['x'] - origin['x'])
        return m

def line_eqn(origin, target):
    x = origin['x']
    y = origin['y']
    c = -(slope(origin, target)*x - y)
    c = y - (slope(origin, target)*x)
    #return 'y = ' + str(slope(target)) + 'x + ' + str(c)
    m = slope(origin, target)
    return {'m':m, 'c':c}

def get_y(x, slope, c):
    # y = mx + c    
    y = (slope*x) + c
    return y

def get_x(y, slope, c):     
    #x = (y-c)/m
    if slope == 0:
        c = 0   #vertical lines never intersect with y-axis
    if slope == 0:
        slope = 1   #Do NOT divide by zero
    x = (y - c)/slope
    return x

def get_points(origin, target):
    coord_list = []
    #Step along x-axis
    for i in range(origin['x'], target['x']+1):     
        eqn = line_eqn(origin, target)
        y = get_y(i, eqn['m'], eqn['c'])        
        coord_list.append([i, y])

    #Step along y-axis
    for i in range(origin['y'], target['y']+1):
        eqn = line_eqn(origin, target)
        x = get_x(i, eqn['m'], eqn['c'])
        coord_list.append([x, i])

    #return unique list     
    return list(k for k,_ in itertools.groupby(sorted(coord_list)))

origin = {'x':1, 'y':3}
target = {'x':1, 'y':6}

print get_points(origin, target)

【问题讨论】:

  • 到目前为止你尝试过什么?你知道如何求解直线方程吗?你不能在一个范围内生成点吗?你被困在哪里了?
  • 您必须计算直线的斜率,将其化简为不可约分数,并使用分子/分母作为 x 和 y 的增量。
  • er...很多段将很少/没有点,其中kz 都是精确整数。即使您确实有整数,某些段也只会有几个精确整数对,而另一些则有很多 - 使得稀疏性高度可变。我认为您还没有完全考虑过这个整数。
  • 你在找什么对我来说有点不清楚。您能否为一些简单的输入提供示例输出。例如,A = (0, 0)B = (17, 19) 的输出应该是什么?
  • (a) 做数学并找出你想要实现的算法。 (b) 尝试实施。 (c) 如果您在编程上有困难,请回到这里。

标签: python computational-geometry


【解决方案1】:
def get_line(x1, y1, x2, y2):
    points = []
    issteep = abs(y2-y1) > abs(x2-x1)
    if issteep:
        x1, y1 = y1, x1
        x2, y2 = y2, x2
    rev = False
    if x1 > x2:
        x1, x2 = x2, x1
        y1, y2 = y2, y1
        rev = True
    deltax = x2 - x1
    deltay = abs(y2-y1)
    error = int(deltax / 2)
    y = y1
    ystep = None
    if y1 < y2:
        ystep = 1
    else:
        ystep = -1
    for x in range(x1, x2 + 1):
        if issteep:
            points.append((y, x))
        else:
            points.append((x, y))
        error -= deltay
        if error < 0:
            y += ystep
            error += deltax
    # Reverse the list if the coordinates were reversed
    if rev:
        points.reverse()
    return points

【讨论】:

  • 对答案的简单解释会有所帮助,而不仅仅是代码。
【解决方案2】:

假设您知道如何计算直线方程,因此您有 m :你的渐变, c : 你的常数

您还有 2 个点:ab,a 的 x 值低于 b 的 x 值

for x in range(a[0], b[0]):
    y = m*x + c
    if isinstance(y, int) and (x,y) not in [a,b]:
        print (x, y)

【讨论】:

  • 虽然 m 可以很容易地得到,但建立 c 对人的思维来说可能是微不足道的,但它变得更加复杂,必须对显然永远不会与 y 相交的垂直线进行例外处理,因此 c = ∞ 和特殊情况对于 c=y 的水平线
  • 在这些情况下,您可以在 a[0] == b[0] 或 a[1] == b[1] 的地方添加异常,诚然这不是最好的处理方式,但是相当琐碎
【解决方案3】:

布雷森汉姆线段或其变体与参数方程有关

X = X0 + t.Dx
Y = Y0 + t.Dy,

其中 Dx=X1-X0 和 Dy=Y1-Y0,t 是 [0, 1] 中的参数。

事实证明,这个方程可以写成整数格,如

X = X0 + (T.Dx) \ D
Y = Y0 + (T.Dy) \ D,

其中 \ 表示整数除法,D=Max(|Dx|, |Dy|) 并且 t 是 [0, D] 范围内的整数。

如您所见,根据 Dx 和 Dy 中的哪一个具有最大的绝对值以及它的符号,其中一个方程可以简化为 X = X0 + T(我们现在假设 Dx >= Dy >= 0)。

要实现这一点,您有三个选择:

  • 对 Y 方程使用浮点数,Y = Y0 + T.dy,其中 dy = Dy/D,最好将结果四舍五入以获得更好的对称性;当你增加 T 时,用 Y+= dy 更新;

  • 使用斜率的定点表示,选择 2 的幂进行缩放,设 2^B;设置 Y' = Y0 > B。

  • 使用纯整数算术。

在整数运算的情况下,可以通过计算 Y0 + (T.Dy + D/2) \ D 而不是 Y0 + (T.Dy \ D) 轻松获得舍入效果。事实上,当你除以 D 时,这相当于 Y0 + T.dy + 1/2。

除法是一个缓慢的操作。您可以通过一个简单的技巧将其换成比较:每当 T.Dy 增加 D 时,Y 就增加 1。您可以维护一个“剩余”变量,等于 (T.Dy) 模 D(或 T.Dy + D/2,用于四舍五入),每超过 D 就减少 D。

Y= Y0
R= 0
for X in range(X0, X1 + 1):
  # Pixel(X, Y)
  R+= Dy
  if R >= D:
    R-= D
    Y+= 1

对于一个优化好的版本,你应该分别考虑对应于Dx和Dy(-、0、+)符号组合的九种情况。

【讨论】:

  • 我知道直线的数学概念,是的,我可以编写伪代码来演示这个概念。但是正如问题所显示的那样,我需要一个工作功能代码。参考我上面的异常代码。
  • 你有那么懒惰吗?你甚至没有意识到我确实提供了 Python 代码。
【解决方案4】:
def getLine(x1,y1,x2,y2):
    if x1==x2: ## Perfectly horizontal line, can be solved easily
        return [(x1,i) for i in range(y1,y2,int(abs(y2-y1)/(y2-y1)))]
    else: ## More of a problem, ratios can be used instead
        if x1>x2: ## If the line goes "backwards", flip the positions, to go "forwards" down it.
            x=x1
            x1=x2
            x2=x
            y=y1
            y1=y2
            y2=y
        slope=(y2-y1)/(x2-x1) ## Calculate the slope of the line
        line=[]
        i=0
        while x1+i < x2: ## Keep iterating until the end of the line is reached
            i+=1
            line.append((x1+i,y1+slope*i)) ## Add the next point on the line
        return line ## Finally, return the line!

【讨论】:

    【解决方案5】:

    对于任何有兴趣的人来说,这里是 user1048839 的 C++ 等价物:

    std::vector<std::tuple<int, int>> bresenhamsLineGeneration(int x1, int y1, int x2, int y2) {
    std::vector<std::tuple<int, int>> points;
    bool                              issteep = (abs(y2 - y1) > abs(x2 - x1));
    if (issteep) {
        std::swap(x1, y1);
        std::swap(x2, y2);
    }
    bool rev = false;
    if (x1 > x2) {
        std::swap(x1, x2);
        std::swap(y1, y2);
        rev = true;
    }
    int deltax = x2 - x1;
    int deltay = abs(y2 - y1);
    int error  = int(deltax / 2);
    int y      = y1;
    int ystep;
    if (y1 < y2) {
        ystep = 1;
    } else {
        ystep = -1;
    }
    
    for (int x = x1; x < x2 + 1; ++x) {
        if (issteep) {
            std::tuple<int, int> pt = std::make_tuple(y, x);
            points.emplace_back(pt);
        } else {
            std::tuple<int, int> pt = std::make_tuple(x, y);
            points.emplace_back(pt);
        }
    
        error -= deltay;
        if (error < 0) {
            y += ystep;
            error += deltax;
        }
    }
    // Reverse the list if the coordinates were reversed
    if (rev) {
        std::reverse(points.begin(), points.end());
    }
    return points;
    }
    

    【讨论】:

      【解决方案6】:

      我将其视为一个学习 c 的项目。直线的整数值遵循此模式。主要数字水平,一对一向上重复 n 次,然后是次要数字水平一对一向上。次要编号比主要编号多或少一。主要数字实际上是梯度,次要数字纠正舍入。

      【讨论】:

      • 这可能是我见过的最糟糕的直线定义 - y=mx+c 有什么问题?
      • 好吧,除了我所描述的是如何在屏幕上绘制一条线,y=mx+c 所做的只是在几何上准确地描述它。
      • 你能用人类可读的形式重新解释你的解释吗?
      • 是的@Yves Daust 抱怨是对的。抱歉,他的回答是薄荷糖,我唯一的辩护是我试图描述他的答案中描述的数学实际看起来如何的可视化。
      猜你喜欢
      • 1970-01-01
      • 2010-10-22
      • 2018-08-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-01-30
      • 2014-03-11
      • 1970-01-01
      相关资源
      最近更新 更多