Моя реализация алгоритма Брезенхэма дает сбой для линий под определенными углами

Я написал реализацию алгоритма Брезенхэма на Python (следуя статье в Википедии), и работает корректно кроме линий под определенными углами. Все линии, которые должны простираться между 45 и 90 градусами или между 135 и 270 градусами, вместо этого будут проходить вдоль линии y = x.

Вот мой код:

def bresenham(origin, dest):
    # debug code
    print origin
    print dest
    # end debug code
    x0 = origin[0]; y0 = origin[1]
    x1 = dest[0]; y1 = dest[1]
    steep = abs(y1 - y0) > abs(x1 - x0)
    backward = x0 > x1

    if steep:
        x0, y0 = y0, x0
        x1, y1 = y1, x1
    if backward:
        x0, x1 = x1, x0
        y0, y1 = y1, y0

    dx = x1 - x0
    dy = abs(y1 - y0)
    error = dx / 2
    y = y0

    if y0 < y1: ystep = 1 
    else: ystep = -1

    result = []
    #if x0 > x1: xstep = -1
    #else: xstep = 1
    # debug code
    print "x0 = %d" % (x0)
    print "x1 = %d" % (x1)
    print "y0 = %d" % (y0)
    print "y1 = %d" % (y1)
    for x in range(x0, x1):
        if steep: result.append((y,x))
        else: result.append((x,y))
        error -= dy
        if error < 0:
            y += ystep
            error += dx 
    # ensure the line extends from the starting point to the destination
    # and not vice-versa
    if backward: result.reverse()
    print result
    return result

Кто-нибудь видит, что я лажаю?


РЕДАКТИРОВАТЬ:

Я добавил в функцию некоторый код печати.

(0,0) находится в верхнем левом углу дисплея.

Моя тестовая среда довольно проста. Это отдельная функция, поэтому я просто передаю ей две точки:

происхождение = (416, 384)
местонахождение = (440, 347)
брезенхэм(начало, место назначения)
(416, 384)
(440, 347)
x0 = 384
x1 = 347
y0 = 416
y1 = 440
[]


person Max    schedule 15.09.2010    source источник
comment
@aaa карп: Нет. Но спасибо за ответ.   -  person Max    schedule 15.09.2010
comment
Помимо того, что if x0 > x1: xstep = -1 не нужен, я не вижу ничего плохого в вашем коде, поэтому ваша проблема, вероятно, в другом месте. Вам нужно опубликовать свои фактические результаты, чтобы мы могли видеть, что происходит.   -  person Gabe    schedule 15.09.2010
comment
@Gabe: xstep необходим, потому что без него, если x0 > x1, цикл for немедленно завершится, так как шаг по умолчанию для цикла for в Python равен 1. Помимо этого, какие результаты вы хотите? Когда что-то идет не так, он возвращает список точек, где каждая точка на (1,1) выше или ниже того, что было до нее.   -  person Max    schedule 15.09.2010
comment
Макс: Пожалуйста, опубликуйте несколько примеров входных данных и неправильных выходных данных, которые они производят. Вам не нужно проверять if x0 > x1, потому что вы меняете местами x0 и x1 до запуска этой строки.   -  person Gabe    schedule 15.09.2010
comment
Глупый вопрос: можете ли вы показать свой тестовый фреймворк (мой Python-fu весь перекручен) и примеры вызовов, которые работают и не работают (это всего две строки кода). Я не думаю, что данные имеют решающее значение.   -  person Jonathan Leffler    schedule 15.09.2010
comment
Также - какая версия Python на какой платформе? Вы его собрали или скачали?   -  person Jonathan Leffler    schedule 15.09.2010
comment
@Gabe: я опубликовал несколько примеров входных и выходных данных в комментарии к ответу Блаэнка.   -  person Max    schedule 15.09.2010
comment
@Jonathan Leffler: я использую Python 2.6 в Ubuntu 9.10.   -  person Max    schedule 15.09.2010
comment
Я, кстати, выложил решение.   -  person Jorge Israel Peña    schedule 15.09.2010


Ответы (2)


Я не знаю, почему вы используете переменную xstep. Вам действительно не нужно это с алгоритмом, который вы используете.

@Gabe: xstep необходим, потому что без него, если x0 > x1, цикл for завершится немедленно, так как шаг по умолчанию для цикла for в Python равен 1.

Причина, по которой вам не нужна переменная xstep, заключается в том, что если она идет назад, координаты уже были переключены (в условном if backward: в начале), так что конечная точка теперь является начальной точкой и наоборот, например что теперь мы все еще идем слева направо.

Вам просто нужно это:

result = []

for x in range(x0, x1):
    if steep: result.append((y, x))
    else: result.append((x, y))
    error -= dy
    if error < 0:
        y += ystep
        error += dx

return result

Если вам нужен список координат в порядке от начальной до конечной точки, вы можете сделать проверку в конце:

if backward: return result.reverse()
else: return result

EDIT: проблема в том, что логическое значение backward оценивается прежде, чем это должно быть. Если условие steep выполняется, то значения меняются, но к тому времени ваше условие backward уже другое. Чтобы исправить это, вместо использования логического значения backward сделайте его явным выражением:

if x0 > x1:
    # swapping here

Опять же, поскольку позже вы используете логическое значение, вы можете просто определить его перед условным выражением:

backward = x0 > x1

if backward:
person Jorge Israel Peña    schedule 15.09.2010
comment
Первоначально у меня было так, как вы предлагали, но когда я попытался сделать линию в углах, упомянутых в моем вопросе, функция ничего не вернула, так как цикл for немедленно завершился. xstep был решением для этого. Возможно, это указывает на что-то еще не так с моей реализацией? - person Max; 15.09.2010
comment
Цикл не должен завершаться ошибкой, потому что помните, мы поменяли местами координаты так, чтобы они по-прежнему шли слева направо (например, x0, x1 = x1, x0). Возможно, вам следует распечатать x0 и x1 перед циклом для проверки. - person Jorge Israel Peña; 15.09.2010
comment
Я так и сделал, и x0 действительно больше, чем x1. - person Max; 15.09.2010
comment
@Max: ваш комментарий здесь предполагает, что диапазон установлен неправильно. Вы напечатали значения x0, x1, y0, y1, которые у вас получились? Весь смысл алгоритма в том, что он работает с увеличением значения x. Если x0 > x1, когда вы делаете диапазон, то вы получите пустой диапазон - и быстрый цикл. - person Jonathan Leffler; 15.09.2010
comment
Да. Подумай об этом. Условие вверху проверяет, является ли x0 > x1, и ЕСЛИ ЭТО ЕСТЬ, то меняет их местами. Таким образом, диапазон цикла ни в коем случае не должен учитывать, что x0 > x1, вы видите здесь разрыв? Возможно, ваш обмен не работает. Сделайте то, что сказал Джонатан, и распечатайте значения для проверки. - person Jorge Israel Peña; 15.09.2010
comment
Я понял, что вы были правы после того, как немного подумал об этом. Что-то должно быть более неправильно, чем я думал. Вот несколько распечаток алгоритма. origin: (416, 384) dest = (440, 347) x0 = 384, x1 = d47, y0 = 416, y1 = 440. Проливает ли это какой-то свет на предмет? - person Max; 15.09.2010
comment
Оказывается, это была довольно простая проблема, которую я упустил из виду. Мне потребовалась бумага для заметок и карандаш, чтобы пробежаться по голове, чтобы увидеть проблему xD - person Jorge Israel Peña; 15.09.2010
comment
Да, это было довольно очевидно, когда я посмотрел на тестовые данные. - person Gabe; 15.09.2010

Проблема в том, что вы вычисляете x0 > x1 до того, как поменяете местами x и y.

Вместо:

backward = x0 > x1 

if steep: 
    x0, y0 = y0, x0 
    x1, y1 = y1, x1 
if backward: 
    x0, x1 = x1, x0 
    y0, y1 = y1, y0 

Вы должны иметь:

if steep: 
    x0, y0 = y0, x0 
    x1, y1 = y1, x1 

backward = x0 > x1 
if backward: 
    x0, x1 = x1, x0 
    y0, y1 = y1, y0 
person Gabe    schedule 15.09.2010
comment
Думаю, я не заслуживаю даже ничтожного плюса за всю проделанную работу, кроме того факта, что я опубликовал решение до него ;_; Спасибо Гейбу за то, что он тоже это заметил :) - person Jorge Israel Peña; 15.09.2010
comment
@Блаенк. Извините, вы правы. Получил на целую минуту раньше. Исправлено и проголосовано за загрузку. Спасибо за вашу помощь. :) - person Max; 15.09.2010