在python中获取直线的所有点

在python中获取直线的所有点,python,computational-geometry,Python,Computational Geometry,非常简单,给定一个点a(x,y)和另一个点B(m,n),我需要一个函数,它可以在任何一个iterable对象中返回中间所有点的列表[k,z] 我只对整型点感兴趣,所以不需要浮点数 我需要最好的pythonic方法,因为这个“小”函数将大量运行,并且是更大系统的关键支柱 编辑: @roippi,谢谢你指出关于整数的问题。从下面的代码中,您可以看到我尝试跨过x轴,得到相应的y,然后对y执行相同的操作。我的点集不会有任何非离散坐标点,因此目前我可以忽略这个小缺陷 import itertools #V

非常简单,给定一个点a(x,y)和另一个点B(m,n),我需要一个函数,它可以在任何一个iterable对象中返回中间所有点的列表[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)

我将此作为学习c语言的一个项目。直线的整数值遵循此模式。大数水平,一个横过一个向上重复n次,然后小数水平横过一个向上。副编号比主编号多或少一个。大数实际上是梯度,小数修正了舍入。

假设你知道如何计算直线方程,那么
m
:你的梯度,
c
:你的常数

您还有两点:
a
b
,其中x值a小于x值b

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)

Bresenham线段或其变体与参数方程有关

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,+)的符号组合的情况。

<代码> DEFGETYLIN(X1,Y1,X2,Y2): 点数=[] issteep=abs(y2-y1)>abs(x2-x1) 如果是issteep: x1,y1=y1,x1 x2,y2=y2,x2 rev=假 如果x1>x2: x1,x2=x2,x1 y1,y2=y2,y1 rev=真 deltax=x2-x1 deltay=abs(y2-y1) 错误=整数(deltax/2) y=y1 ystep=无 如果y1
def getLine(x1、y1、x2、y2):
如果x1==x2:##完全水平线,可以很容易地求解
返回[(x1,i)表示范围内的i(y1,y2,int(abs(y2-y1)/(y2-y1)))]
否则:##更大的问题是,可以使用比率
如果x1>x2:##如果线“向后”,翻转位置,向下“向前”。
x=x1
x1=x2
x2=x
y=y1
y1=y2
y2=y
斜率=(y2-y1)/(x2-x1)##计算直线的斜率
行=[]
i=0
当x1+i > < p>这里是一个C++等价的<代码> USE108839 < /Cord>的回答,对于任何感兴趣的人:

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;
}
向量bresenhamsLineGeneration(intx1,inty1,intx2,inty2){ std::向量点; bool-issteep=(abs(y2-y1)>abs(x2-x1)); 如果(issteep){ 标准:交换(x1,y1); 标准:交换(x2,y2); } bool rev=假; 如果(x1>x2){ 标准:交换(x1,x2); 标准:互换(y1,y2); rev=真; } int deltax=x2-x1; int deltay=abs(y2-y1); int error=int(deltax/2); int y=y1; int ystep; if(y1
到目前为止,您尝试了什么?你知道怎么解直线方程吗?不能在一个范围内生成点吗?你被卡在哪里了?你必须计算直线的斜率,把它减少到一个不可约的分数,并用分子/分母作为x和y的增量。。。很多段都有非常少/没有点,
k
z
都是精确整数。即使你真的有机会
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!

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;
}