Использование формулы стандартной декартовой окружности для рисования окружности в графическом режиме (C++)

Я хотел нарисовать круг с помощью Graphics.h в C++, но не напрямую с помощью функции circle(). Круг, который я хочу нарисовать, использует меньшие круги в качестве точек, т. е. меньшие круги составляют окружность большего круга. Поэтому я подумал, что если бы я сделал что-то подобное, это сработало бы:

    {
        int radius = 4;


        // Points at which smaller circles would be drawn
        int x, y;


        int maxx = getmaxx();
        int maxy = getmaxy();

        // Co-ordinates of center of the larger circle (centre of the screen)
        int h = maxx/2;
        int k = maxy/2;

        //Cartesian cirle formula >> (X-h)^2 + (Y-k)^2 = radius^2

        //Effectively, this nested loop goes through every single coordinate on the screen

        int gmode = DETECT;
        int gdriver;

        initgraph(&gmode, &gdriver, "");

        for(x = 0; x<maxx; x++)
        {
            for(y = 0; y<maxy; y++)
            {
             if((((x-h)*(x-h)) + ((y-k)*(y-k))) == (radius*radius))
             { 
                 circle(x, y, 5) //Draw smaller circle with radius 5 
             }                   //at points which satisfy circle equation only!
            }
        }
    getch();
    }

Это когда я использую graphics.h на Turbo C++, так как это компилятор, который мы изучаем в школе.

Я знаю, что он древний.

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

Однако, когда я запускаю программу, я получаю четыре гиперболы (все указывают на центр экрана), и когда я увеличиваю радиус, увеличивается заостренность (за неимением лучшего слова) гипербол. , пока, наконец, когда радиус не станет равным 256 или больше, две гиперболы вверху и внизу пересекаются, образуя на моем экране большой крест, например: "Все, пользователь, я сдаюсь!"

Я пришел к значению 256, так как заметил, что радиус был кратен 4, цифры выглядели ... лучше?

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

Какие-либо предложения???

РЕДАКТИРОВАТЬ >> Вот приблизительная диаграмма вывода, который я получил...

Это приблизительный вывод


person Zac    schedule 11.10.2016    source источник
comment
Можете ли вы добавить скриншот или два сломанного вывода?   -  person Jason C    schedule 11.10.2016
comment
Являются ли maxx и maxy границы экрана?   -  person Lajos Arpad    schedule 11.10.2016
comment
@JasonC В настоящее время я работаю на своем ноутбуке, на котором нет Turbo C++, поэтому я не могу сделать снимки экрана, а мой настольный ПК находится довольно далеко, поэтому я не могу. Прости. Вот почему я изо всех сил старался описать вывод...   -  person Zac    schedule 11.10.2016
comment
Зак, можешь нарисовать несколько картинок, похожих на то, что у тебя получилось?   -  person Lajos Arpad    schedule 11.10.2016
comment
@LajosArpad Да, maxx и maxy обозначают границы экрана.   -  person Zac    schedule 11.10.2016
comment
@LajosArpad, конечно, теперь в вопросе представлены фотографии ...   -  person Zac    schedule 11.10.2016
comment
Это настоящий код? Вы можете получить гиперболу с формулой ((x-h)*(x-h)) - ((y-k)*(y-k)))==..., а не с заданной   -  person MBo    schedule 11.10.2016


Ответы (3)


В вашем коде есть две проблемы:

Во-первых: вы действительно должны вызывать initgraph до вызова getmaxx и getmaxy, иначе они не обязательно вернут правильные размеры графического режима. Это может быть или не быть способствующим фактором в зависимости от вашей настройки.

Во-вторых, и это наиболее важно: В Turbo C++ int является 16-битным. Например, вот круг с радиусом 100 (после того, как предыдущая проблема с порядком initgraph была исправлена):

введите здесь описание изображения

Обратите внимание на беспорядочные круги в четырех углах. Если мы проведем небольшую отладку и добавим несколько распечаток (полезная стратегия, которую вы должны сохранить для дальнейшего использования):

if((((x-h)*(x-h)) + ((y-k)*(y-k))) == (radius*radius))
{
    printf(": (%d-%d)^2 + (%d-%d)^2 = %d^2\n", x, h, y, k, radius);
    circle(x, y, 5); //Draw smaller circle with radius 
}                   //at points which satisfy circle equation only!

Вы можете видеть, что происходит (первая строка — это maxx и maxy, не показанные в приведенном выше фрагменте):

введите здесь описание изображения

В частности, этот круг в (63, 139) является одним из углов. Если вы посчитаете, то увидите, что:

(63 - 319)2 + (139 - 239)2 = 75 536

И поскольку ваши целые числа 16-битные, 75536 по модулю 65536 = 10000 = вычисляемое значение = 1002 = круг, где его быть не должно.

Простое решение — просто изменить соответствующие переменные на long:

  • макси, макси
  • x, y
  • h, k

So:

long x, y;
...
initgraph(...);
...
long maxx = getmaxx();
long maxy = getmaxy();
...
long h = maxx / 2;
long k = maxy / 2;

И тогда вы получите правильный вывод:

введите здесь описание изображения

Обратите внимание, конечно, что, как другие ответы указывают, поскольку вы используете целые числа, вы пропустите много точек. Это может быть или не быть в порядке, но некоторые значения будут давать заметно худшие результаты (например, радиус 256, кажется, имеет только 4 целочисленных решения). Вы можете ввести толерантность, если хотите. Вы также можете использовать более прямой подход, но это может нарушить цель вашего упражнения с формулой декартова круга. Если вам нравятся такие вещи, вот 24-страничный документ, содержащий кучу обсуждений, доказательств и свойств о целые числа, являющиеся суммой двух квадратов.

Я недостаточно знаю Turbo C++, чтобы знать, можете ли вы заставить его использовать 32-битные целые числа, я оставлю это вам в качестве упражнения.

person Jason C    schedule 11.10.2016
comment
Да, поскольку я написал код непосредственно в Stack Overflow, не проверяя исходный код, который я сделал, я сделал глупый вызов функции getmaxx, y перед переходом в графический режим. Ошибка, спасибо, что указали на это. И ВАУ, я знал о 16-битных значениях int, но никогда раньше не обращал внимания на эту деталь, и мне никогда не приходило в голову, что это может быть основной причиной! Код теперь работает нормально. Это было потрясающе. Большое спасибо! И спасибо за совет распечатать, я это запомню! - person Zac; 11.10.2016

Во-первых, maxx и maxy — это целые числа, которые вы инициализируете с помощью некоторых функций, представляющих границы экрана, а затем вы используете их как функции. Просто уберите скобки:

    // Co-ordinates of center of the larger circle (centre of the screen)
    int h = maxx/2;
    int k = maxy/2;

Затем вы проверяете точное равенство, чтобы проверить, находится ли точка на окружности. Поскольку экран представляет собой сетку пикселей, многие ваши моменты будут упущены. Вам нужно добавить допуск, максимальное расстояние между проверяемой точкой и фактическим кругом. Итак, измените эту строку:

if(((x-h)*(x-h)) + ((y-k)*(y-k)) == radius*radius)

к этому:

if(abs(((x-h)*(x-h)) + ((y-k)*(y-k)) - radius*radius) < 2)
person Lajos Arpad    schedule 11.10.2016
comment
На данный момент я не смогу проверить, так как не могу получить доступ к своему другому компьютеру, но не могли бы вы объяснить немного больше? Я предполагаю, что вы имеете в виду, поскольку экран представляет собой сетку пикселей, многие точки, которые должны удовлетворять уравнению, не будут соответствовать, потому что использование типа данных int недостаточно точно для координат? Или допуск используется для удаления кругов, образованных дополнительно (как часть других 3 гипербол) - person Zac; 11.10.2016
comment
@ Зак, это первое. Если круг проходит через квадрат, образованный четырьмя пикселями, но не пересекает ни один из них, то ваш алгоритм не отобразит там круг. Введение допуска решает эту проблему, так как вы проверяете небольшое расстояние вместо точного пересечения. - person Lajos Arpad; 11.10.2016
comment
Это не объясняет гиперболы. - person Jason C; 11.10.2016
comment
@JasonC, правда, но это явно ошибки. Если они будут исправлены, то мы увидим, где мы будем с проблемой. - person Lajos Arpad; 11.10.2016
comment
@LajosArpad Спасибо, что объяснили мне бит допуска. Буду иметь в виду, теперь проблема решена. - person Zac; 11.10.2016
comment
@Zac, я был рад внести свой вклад в решение, и здорово, что проблема решена. - person Lajos Arpad; 11.10.2016

Введение некоторого уровня толерантности решит проблему.

Но не стоит проверять все точки в графическом окне. Вы бы изменили подход? Вы можете рисовать нужные кружочки вообще без проверок:

Чтобы заполнить всю окружность большого круга (радиусом RBig), нужно NCircles маленьких кругов радиусом RSmall.

NCircles = round to integer (Pi / ArcSin(RSmall / RBig)); 

Центр i-го маленького круга находится в позиции

cx = mx + Round(RBig * Cos(i * 2 * Pi / N)); 
cy = my + Round(RBig * Sin(i * 2 * Pi / N));

где mx, my - центр большого круга

person MBo    schedule 11.10.2016
comment
Это определенно интересный подход (возможно, он станет ключом к моему следующему проекту на C++ ››› Заставить маленькие круги появляться и исчезать в порядке появления на окружности больших кругов), я рассмотрю его. Спасибо! - person Zac; 11.10.2016