рисование круга без вычисления с плавающей точкой - PullRequest
11 голосов
/ 31 мая 2010

Это распространенный вопрос интервью (согласно некоторым сайтам интервью), но я не могу найти нормальных ответов в Интернете - некоторые ошибочны, а некоторые указывают на сложную теорию, которая, как мне кажется, не требуется в интервью (например, алгоритм Брессенхема). 1001 *

Вопрос прост:

Круговое уравнение: x ^ 2 + y ^ 2 = R ^ 2. Учитывая R, нарисуйте 0,0-центрированный круг как можно лучше, не используя с плавающей точкой (без триго, квадратных корней и т. д., только целые числа)

Ответы [ 8 ]

8 голосов
/ 31 мая 2010

Подобные Брезенхаму алгоритмы, вероятно, являются ожидаемым ответом и могут быть получены без «сложной теории». Начните с точки (x,y) на окружности: (R,0) и сохраните значение d=x^2+y^2-R^2, изначально 0. D - квадрат расстояния от текущей точки до окружности. Мы увеличиваем Y и уменьшаем X по мере необходимости, чтобы D был минимальным:

// Discretize 1/8 circle:
x = R ; y = 0 ; d = 0
while x >= y
  print (x,y)
  // increment Y, D must be updated by (Y+1)^2 - Y^2 = 2*Y+1
  d += (2*y+1) ; y++
  // now if we decrement X, D will be updated by -2*X+1
  // do it only if it keeps D closer to 0
  if d >= 0
    d += (-2*x+1) ; x--
6 голосов
/ 31 мая 2010

Честно говоря, не достаточно алгоритм окружности средней точки ? Просто отразите это во всех секторах. И во что бы то ни стало нет - если только вы не пытаетесь устроиться на работу в качестве тестера оконных приложений, Алгоритм Брезенхема не является сложной теорией.

5 голосов
/ 31 мая 2010

Со второго метода на эта страница :

для каждого пикселя, оценить x 2 + y 2 и посмотрите, это в диапазоне от R 2 -R + 1 до R 2 + R включительно. Если это так, раскрасьте пиксель на экран, а если нет, то не надо.

Дальнейшие подробности и объяснения даны на вышеупомянутой странице, но суть в том, что вы ищете пиксели, которые находятся на расстоянии между R-0,5 и R + 0,5 от начала координат, поэтому квадрат расстояния равен x 2 + y 2 и пороговые расстояния в квадрате равны R 2 -R + 0,25 и R 2 + R + 0,25.

Для других методов Google "нарисует круг, используя только целочисленную арифметику".

4 голосов
/ 24 ноября 2017

Довольно старый вопрос, но я постараюсь предоставить конечному решению визуальные тесты на python как альтернативу алгоритму Брезенхэма - лучшее и самое короткое решение для этой задачи. Я думаю, что эта идея также может иметь место и, возможно, проще для понимания, но требует больше кода. Кто-то может также в конечном итоге с этим решением.

Идея основана на следующих фактах:

  1. Каждая точка на окружности находится на одинаковом расстоянии от центральной точки окружности
  2. Круг содержит 4 квадранта, который начинается и заканчивается в точках (r, 0), (2r, r), (r, 2r) и (0, r), если r - радиус, а центральная точка находится в (r, r ) точка.
  3. Круг - это непрерывная фигура, и каждая точка может иметь 8 соседних точек. Если двигаться по кругу в одном направлении, нам интересны только три точки - 3 лежат в противоположном направлении, а 2 слишком далеко от центра. Например, для точки (r, 0) с направлением на (2r, r) интересными точками будут (r + 1, 1), (r, 1) и (r + 1, 0)
import matplotlib.pyplot as plt
from itertools import chain

def get_distance(x1, y1, x2, y2):
    """
        Calculates squared distance between (x1, y1) and (x2, y2) points
    """
    return (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2);

def get_next_point(x, y, dx, dy, cx, cy, r):
    """
        Returns the next circle point base on base point (x, y), 
        direction (dx, dy), circle central point (cx, cy) and radius r
    """
    r2 = r * r

    # three possible points
    x1, y1 = x + dx, y + dy
    x2, y2 = x, y + dy
    x3, y3 = x + dx, y

    # calculate difference between possible point distances 
    # with central point and squared radius
    dif1 = abs(get_distance(x1, y1, cx, cy) - r2)
    dif2 = abs(get_distance(x2, y2, cx, cy) - r2)
    dif3 = abs(get_distance(x3, y3, cx, cy) - r2)

    # choosing the point with minimum distance difference
    diff_min = min(dif1, dif2, dif3)

    if diff_min == dif1:
        return x1, y1
    elif diff_min == dif2:
        return x2, y2
    else:
        return x3, y3

def get_quadrant(bx, by, dx, dy, cx, cy, r):
    """
        Returns circle quadrant starting from base point (bx, by), 
        direction (dx, dy), circle central point (cx, cy) and radius r
    """
    x = bx
    y = by

    # maximum or minimum quadrant point (x, y) values
    max_x = bx + dx * r
    max_y = by + dy * r

    # choosing only quadrant points
    while (dx * (x - max_x) <= 0) and (dy * (y - max_y) <= 0):
        x, y = get_next_point(x, y, dx, dy, cx, cy, r)
        yield x, y

def get_circle(r, cx, cy):
    """
        Returns circle points (list) with radius r and center point (cx, cy)
    """
     north_east_quadrant = get_quadrant(cx, cy - r, 1, 1, cx, cy, r)
     south_east_quadrant = get_quadrant(cx + r, cy, -1, 1, cx, cy, r)
     south_west_quadrant = get_quadrant(cx, cy + r, -1, -1, cx, cy, r)
     north_west_quadrant = get_quadrant(cy - r, cy, 1, -1, cx, cy, r)

    return chain(north_east_quadrant, south_east_quadrant,
                 south_west_quadrant, north_west_quadrant)

# testing

r = 500

circle_points = get_circle(r, r, r)

for x, y in circle_points:
    plt.plot([x], [y], marker='o', markersize=3, color="red")

plt.show()
3 голосов
/ 16 мая 2011

Я буду использовать алгоритм рисования круга Брессенхэма или алгоритм рисования средней точки круга. Оба выдают одинаковые координаты. А с симметрией между 8 октантами круга нам просто нужно сгенерировать один октант и просто отразить и скопировать его во все остальные позиции.

2 голосов
/ 13 октября 2011

Кто-нибудь считал, что он может искать боковой ответ, такой как «с компасом и карандашом» или «использовать внутреннюю часть рулонной ленты в качестве шаблона».

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

2 голосов
/ 31 мая 2010

Вы можете легко вычислить x в x ^ 2 = r ^ 2- y ^ 2, используя приближение Тейлора первого порядка

sqrt (u ^ 2 + a) = u + a / 2u

Это программа для этого в Mathematica (короткая, но, возможно, не красивая)

 rad=87; (* Example *)
 Calcy[r_,x_]:= ( 
     y2 = rad^2 - x^2;
     u = Ordering[Table[ Abs[n^2-y2], {n,1,y2}]] [[1]]; (* get the nearest perfect square*)
     Return[ u-(u^2-y2)/(2 u) ]; (* return Taylor approx *)
 )

 lista = Flatten[Table[{h Calcy[rad, x], j x}, {x, 0, rad}, {h, {-1, 1}}, {j, {-1, 1}}], 2];
 ListPlot[Union[lista, Map[Reverse, lista]], AspectRatio -> 1];

Это результат

альтернативный текст http://i47.tinypic.com/246nfxt.jpg

Не так уж плохо ИМХО ... Я ничего не знаю о графических алгоритмах ...

2 голосов
/ 31 мая 2010

Вот мой ответ на собеседование (нет исследований, это на месте) ...

Установите два вложенных цикла for, которые в совокупности зацикливаются на квадрате, определяемом {-R, -R, 2R, 2R}. Для каждого пикселя вычислите (i ^ 2 + j ^ 2), где i и j - переменные вашего цикла. Если это в пределах некоторого допуска к R ^ 2, то закрасьте этот пиксель черным, если нет, то оставьте этот пиксель в покое.

Мне лень определять, какой должна быть эта терпимость. Вам может потребоваться сохранить последнее вычисленное значение до нуля, в котором пиксель лучше всего представляет окружность ... Но этот базовый метод должен работать довольно хорошо.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...