【问题标题】:Drawing an almost straight line画一条几乎直线
【发布时间】:2013-07-30 13:24:15
【问题描述】:

我得到了这些代表方格纸的数组,或者如果你愿意,也可以是像素。为了在这个线程中保持一致性,让“1”代表一个黑色单元格,“0”代表一个白色单元格。

现在,我想从 A 点到 B 点画一条(黑色)直线。我不能只使用原版 http://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm,因为如果它是一张纸,我需要将每个会被击中的单元格变黑一张方格纸,然后从正方形 A 的中心到正方形 B 的中心画一条线。

所以这些应该如下:

+—+—+—+    +—+—+—+
|A| | |    |█| | |
+—+—+—+    +—+—+—+
| | | |    |█|█| |
+—+—+—+    +—+—+—+
| | | | →  | |█|█|
+—+—+—+    +—+—+—+
| | |B|    | | |█|
+—+—+—+    +—+—+—+

+—+—+—+    +—+—+—+
|A| | |    |█| | |
+—+—+—+    +—+—+—+
| | | | →  | |█| |
+—+—+—+    +—+—+—+
| | |B|    | | |█|
+—+—+—+    +—+—+—+

+—+—+—+    +—+—+—+
|A| | | →  |█|█| |
+—+—+—+    +—+—+—+
| | |B|    | |█|█|
+—+—+—+    +—+—+—+

随意想象这些草图更像正方形,或者在一张方格纸上实际画出草图!它可能会解决问题。

和往常一样,随时发布任何可能有帮助的内容。谢谢!


(A) 解决方案

同样使用这个例子。并使用我的框架使用的索引,因此它看起来与答案中的索引略有不同。

 X 0 1 2      X 0 1 2
Y +—+—+—+ →  Y +—+—+—+
0 |A| | | →  0 |█| | |
  +—+—+—+ →    +—+—+—+
1 | | | | →  1 |█|█| |
  +—+—+—+ →    +—+—+—+
2 | | | | →  2 | |█|█|
  +—+—+—+ →    +—+—+—+
3 | | |B| →  3 | | |█|
  +—+—+—+ →    +—+—+—+

A 是 (0,0) =: (a1,a2), B 是 (2,3) =: (b1,b2),所以期望的点集是 {(0,0), (0, 1), (1,1), (1,2), (2,2), (2,3)}

首先,我们将在实际纸上绘制的直线形式化。

Therefore first define constant m which holds our slope:

    (b2 - a2)
m = —————————
    (b1 - a1)

For arbitrary (A, B), the straight is now defined by:
g: y = (x - a1) · m + a2

and inverted:
ǵ: x = (y - a2) / m + a1

Note that for m = 0, the whole thing does fail. But a straight-to-the-right line does not only sound like a straightforward thing.

在这个例子中,直道是

m = 3/2
g: y = x * 3/2
ǵ: x = y * 2/3

我们将在我们的边界框中为这些函数提供“线值”(正好在 2 个整数 x 和 x+1 之间,广泛称为“x 和一半”)。所以首先我们将在 a1 和 b1 之间(进给 g),然后在 a2 和 b2 之间(进给ǵ):

g(0.5) = 1/2 * 3/2 = 0.75 // 1 < g(0.5) + 0.5 < 2
g(1.5) = 3/2 * 3/2 = 2.25 // 2 < g(1.5) + 0.5 < 3

ǵ(0.5) = 1/2 * 2/3 = 0.33 // 0 < ǵ(0.5) + 0.5 < 1
ǵ(1.5) = 3/2 * 2/3 = 1    // 1 < ǵ(1.5) + 0.5 < 2
ǵ(2.5) = 5/2 * 2/3 = 1.66 // 2 < ǵ(2.5) + 0.5 < 3
  1. First 表示直线穿过第一条水平线上方(显示为“下方”)的第一条垂直线。变黑点:(0,1), (1,1)。
  2. Second 表示直线穿过第二条水平线上方的第二条垂直线。变黑点数:(1,2), (2,2)。
  3. Third 表示直线穿过第 0 条垂直线之后(显示为“右侧”)的第一条水平线。变黑点:(0,0), (0,1)。
  4. Fourth 表示直线穿过第一条垂直线之后的​​第二条水平线。黑点:(1,1), (1,2)。
  5. 第五表示直线在第二条垂直线之后穿过第三条水平线。变黑点数:(2,2), (2,3)。

所有点:{(0,1), (1,1), (1,2), (2,2), (0,0), (0,1), (1,1), (1,2), (2,2), (2,3)}

没有重复:{(0,0), (0,1), (1,1), (1,2), (2,2), (2,3)} (完全符合预期)

  • 因此,对于与第 x 条水平线上方的第 y 条垂直线相交的线,我们将 (x-1,y) 和 (x,y) 涂成黑色。
  • 对于在第 y 条垂直线之后穿过第 x 条水平线的线,我们绘制 (x,y-1) 和 (x,y)。
  • (这里从不正确)对于穿过第 x 个水平的线正好在第 y 个垂直的线,我们绘制 m > 0 (x-1,y-1)和 (x,y)。如果 m

感觉不错,这是我的想法。

有什么想法吗?请发表评论!

~LDer~

【问题讨论】:

    标签: python drawing geometry pixel


    【解决方案1】:

    初始化 给出“正方形”坐标的顶点(x,y)。对于每个正方形,v1 位于左上角,v2 位于右上方,v3 位于右下方,v4 位于左下方。计算你的线y = a*x + b的方程。

    算法

    For each square do
        for each vertex of the square do
            calculate valueOfVertex = y - a*x - b
        if two valueOfVertex have an opposite sign
        then cellColor == 1
        else cellcolor == 0
    

    输出应该是你所期望的。唯一的问题可能是一个valueOfVertex = 0 和所有其他的都是严格的正面或负面的。但这很容易处理。

    说明 如果直线穿过正方形(或像素),您可以找到两个不在直线同一侧的顶点。所以一个在正半平面,另一个在负半平面。

    改进一些简单的技巧会阻止您测试边界框的每个正方形。如果你发现一个完全在半平面上的正方形,你可以丢弃一些正方形。在您的示例中,如果测试的正方形在线上方,您可以丢弃所有上方和右侧的正方形。

    【讨论】:

    • 我想要更高效的东西,它不会强迫我查看每个顶点,而是(承诺)返回所有要绘制的正方形。如果我目前的想法做得不好,我可能会尝试这个...
    • 另一种更高效的可能性:从 A(你知道它是黑色的)开始并评估相邻的正方形。如果邻居方格是黑色的,则将其邻居添加到队列中。给定正方形的顶点,您还可以推断哪个相邻正方形可以是黑色或白色。一种改进算法的方法。
    • 当然,测试方块的顺序会有所不同;但我想先改进其他解决方案。没有冒犯:)
    【解决方案2】:

    假设我们正在处理这个例子:

    +—+—+—+    +—+—+—+
    |A| | |    |█| | |
    +—+—+—+    +—+—+—+
    | | | |    |█|█| |
    +—+—+—+    +—+—+—+
    | | | | →  | |█|█|
    +—+—+—+    +—+—+—+
    | | |B|    | | |█|
    +—+—+—+    +—+—+—+
    

    这是一个矩形3x4

    我们需要定义两个函数:
    y(x) = 4 * (1 - x/3)
    x(y) = 3 * (1 - y/4)

    现在让我们将矩形内的所有整数 xy 传递给它们。
    在每一步中,我们都用坐标绘制黑色像素
    [x; ceil(y(x)) - 1][ceil(x(y)) - 1; y - 1]

    y(0) == 4      # paint 0;3
    y(1) == 2.(6)  # paint 1;2
    y(2) == 1.(3)  # paint 2;1
    
    x(0) == 3      # paint 2;-1 (-1 is not a valid pixel coordinate, 
                   # so we may just throw away this, as there are no pixels below y=0)
    x(1) == 2.25   # paint 2;0
    x(2) == 1.5    # paint 1;1
    x(3) == 0.75   # paint 0;2
    

    x(y)y(x) 函数都为您提供对角线上点的第二个坐标,当我们将整数值传递给它们时,我们会找到对角线与网格的交点。
    y-函数将为我们提供所有垂直线右侧的像素和x-函数-低于每个水平线(这就是y - 1的原因)。唯一的问题是拐角交叉点,可以通过多一个条件来解决。

    【讨论】:

    • 你是如何从 [ceil(x(1)) - 1; 得到的1] 至 [2; 0]?我喜欢这个解决方案,现在有了我自己的可直接应用的想法,大致基于它:) 希望它运作良好。此外,如果你能澄清你的数字是如何编造出来的,那就太好了……
    • 所以 [ceil(x(2)) - 1; 2] 至 [1; 1] (应该是 [1; 2]) 是同样的神秘转换,以及 [ceil(x(3)) - 1; 3]到[0; 2](应该是 [0; 3]),只留下 [ (0, 3), (1, 2), (2, 1), (2, 0) ] 作为不同的值?等一下,这是bresenham'd! D:
    • @LDericher 嗯,你是对的,有问题。我会试着再一次将我的想法重现给自己。
    • @LDericher 是的,我明白了。等几分钟,我会试着在某个地方画出来。
    • 我也明白了,它是关于在 0.5 的步骤(“线”)评估您的功能,然后确定哪 2 个正方形恰好是交点的左/右(分别是上方/下方)点...
    猜你喜欢
    • 1970-01-01
    • 2014-05-22
    • 1970-01-01
    • 2021-01-02
    • 2012-05-30
    • 1970-01-01
    • 2021-10-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多