2
votes

I wanted to draw a circle using graphics.h in C++, but not directly using the circle() function. The circle I want to draw uses smaller circles as it's points i.e. The smaller circles would constitute the circumference of the larger circle. So I thought, if I did something like this, it would work:

    {
        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();
    }

This is when I'm using graphics.h on Turbo C++ as this is the compiler we're learning with at school.

I know it's ancient.

So, theoretically, since the nested for loops check all the points on the screen, and draw a small circle at every point that satisfies the circle equation only, I thought I would get a large circle of radius as entered, whose circumference constitutes of the smaller circles I make in the for loop.

However, when I try the program, I get four hyperbolas (all pointing towards the center of the screen) and when I increase the radius, the pointiness (for lack of a better word) of the hyperbolas increase, until finally, when the radius is 256 or more, the two hyperbolas on the top and bottom intersect to make a large cross on my screen like : "That's it, user, I give up!"

I came to the value 256 as I noticed that of the radius was a multiple of 4 the figures looked ... better?

I looked around for a solution for quite some time, but couldn't get any answers, so here I am.

Any suggestions???

EDIT >> Here's a rough diagram of the output I got...

This is the rough output

3
Can you add a screenshot or two of the broken output?Jason C
Are maxx and maxy representing the borders of the screen?Lajos Arpad
@JasonC I am currently working on my laptop, which does not have Turbo C++, so I can't get the screenshots, and my Desktop PC is pretty far away, so I can't. Sorry. That's why I tried my best to describe the output...Zac
Zac, can you draw some pictures looking like the output you had?Lajos Arpad
Is it real code? You can get hyperbola with ((x-h)*(x-h)) - ((y-k)*(y-k)))==... formula, not with given oneMBo

3 Answers

4
votes

There are two issues in your code:

First: You should really call initgraph before you call getmaxx and getmaxy, otherwise they will not necessarily return the correct dimensions of the graphics mode. This may or may not be a contributing factor depending on your setup.

Second, and most importantly: In Turbo C++, int is 16-bit. For example, here is circle with radius 100 (after the previous initgraph order issue was fixed):

enter image description here

Note the stray circles in the four corners. If we do a little debugging and add some print-outs (a useful strategy that you should file away for future reference):

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!

You can see what's happening (first line is maxx and maxy, not shown in above snippet):

enter image description here

In particular that circle at (63, 139) is one of the corners. If you do the math, you see that:

(63 - 319)2 + (139 - 239)2 = 75536

And since your ints are 16-bit, 75536 modulo 65536 = 10000 = the value that ends up being calculated = 1002 = a circle where it shouldn't be.

An easy solution to this is to just change the relevant variables to long:

  • maxx, maxy
  • x, y
  • h, k

So:

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

And then you'll end up with correct output:

enter image description here

Note of course that like other answers point out, since you are using ints, you'll miss a lot of points. This may or may not be OK, but some values will produce noticeably poorer results (e.g. radius 256 only seems to have 4 integer solutions). You could introduce a tolerance if you want. You could also use a more direct approach but that might defeat the purpose of your exercise with the Cartesian circle formula. If you're into this sort of thing, here is a 24-page document containing a bunch of discussion, proofs, and properties about integers that are the sum of two squares.

I don't know enough about Turbo C++ to know if you can make it use 32-bit ints, I'll leave that as an exercise to you.

1
votes

First of all, maxx and maxy are integers, which you initialize using some functions representing the borders of the screen and then later you use them as functions. Just remove the paranthesis:

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

Then, you are checking for exact equality to check whether a point is on a circle. Since the screen is a grid of pixels, many of your points will be missed. You need to add a tolerance, a maximum distance between the point you check and the actual circle. So change this line:

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

to this:

if(abs(((x-h)*(x-h)) + ((y-k)*(y-k)) - radius*radius) < 2)
1
votes

Introduction of some level of tolerance will solve the problem.

But it is not wise to check all the points in graphical window. Would you change an approach? You can draw needed small circles without checks at all:

To fill all big circle circumference (with RBig radius), you need NCircles small circles with RSmall radius

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

Center of i-th small circle is at position

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

where mx, my - center of the big circle