Самый чистый способ генерировать 8-соседние координаты

Я ищу способ генерировать следующую последовательность чисел (которые являются относительными координатами 8 соседей пикселя, начиная с северо-западного пикселя и заканчивая западным). Первое число - это координата y, а второе - координата x:

 y,  x
 _____

 1, -1   // N-W
 1,  0   // N
 1,  1   // N-E
 0,  1   // E
-1,  1   // S-E
-1,  0   // S
-1, -1   // S-W
 0, -1   // W

Я могу придумать несколько уродливых способов сделать это, например, просто поместить координаты в массив, но мне интересно, есть ли чистый и эффективный способ, о котором я не думал.

Редактировать: в связи с тем, как спроектирован алгоритм, который я пытаюсь реализовать, пиксели должны быть перебраны в определенном порядке (от NW до W).

2 ответа

Решение

Рассмотрим следующий метод генерации только Y-координат.

Начиная с NW мы хотим достичь {1, 1, 1, 0, -1, -1, -1, 0}. Это повторяющийся шаблон, заданный циклом:

for( int i = 0; i < 8; i++ )
{
    // You can combine into one ternary if you are adventurous
    int y = (i % 4 == 3) ? 0 : 1;
    y *= (i > 3) ? -1 : 1;
}

Так что это сгенерирует желаемую последовательность для значений y.

Теперь рассмотрим последовательность значений x, начиная с NE: {1, 1, 1, 0, -1, -1, -1, 0}. Вы можете видеть, что это та же самая последовательность.

Таким образом, мы можем создать желаемую последовательность, начиная с NW, используя смещение 2 к предыдущему циклу и добавление последней тройки, чтобы разместить перенос в конце последовательности:

for (int i = 2; i < 10; i++ )
{
    int x = (i % 4 == 3) ? 0 : 1;
    x *= (i % 8 > 3) ? 1 : -1;   
}

Теперь тривиально объединить два в один цикл:

for (int i = 0; i < 8; i++)
{
    int y = (i % 4 == 3) ? 0 : 1;
    y *= (i > 3) ? -1 : 1;

    int x = ( (i+2) % 4 == 3) ? 0 : 1;
    x *= ( (i+2) % 8 > 3) ? 1 : -1;
}

Другая удобочитаемая альтернатива - явно перечислить стороны следующим образом:

int x = -1;
int y = 1;
for (int side = 0; side < 4; side++)
{
    for (int steps = 0; steps < 2; steps++)
    {
        // use coordinates here
        printf("%d, %d\n", y, x);

        if (side == 0) { x++; }
        else if (side == 1) { y--; }
        else if (side == 2) { x--; }
        else /* if side == 3) */ { y++; }
    }
}

/*
result:
1, -1
1, 0
1, 1
0, 1
-1, 1
-1, 0
-1, -1
0, -1
*/

Это дает дополнительное преимущество, заключающееся в том, что окрестность любого размера может быть пройдена путем изменения начального угла и количества шагов вдоль стороны.

@louism: "Я могу придумать несколько уродливых способов сделать это, например, просто поместить координаты в массив" - я думаю, что это лучший способ, на самом деле. Он понятен и читаем (в отличие от метода арифметики модулей, описанного в @ose выше) и, возможно, самый быстрый.

@louism: не могли бы вы сравнить три различных метода (поиск в массивах, перечислить стороны, арифметику модуля) и опубликовать результаты? Я был бы весьма заинтересован в этом, так как это то, что я бы использовал в коде, который я пишу прямо сейчас.

Другие вопросы по тегам