"Perfect" circles

Hello!

Somebody knows why this code does not make “perfect” circles? How I
can improve the precision? I have tested this same code in Delphi and
works perfectly. Can it be problem of the SDL_Rect? I’m using C++.

I have seen some programs done in SDL that make this perfectly!

Sorry, I know isn’t a specific SDL question, but I’m desperate.

Code:

float pre_cos[360];
float pre_sin[360];
bool is_pre_cos_sin_init = false;

const float PI = 3.14159265358979323846;

float RadToDeg(float rad) { return rad * 180 / PI; }
float DegToRad(float deg) { return deg * PI / 180; }

void init_pre_math()
{
for(int i = 0; i <= 360; i++)
{
pre_cos[i] = sin(DegToRad(i));
pre_sin[i] = cos(DegToRad(i));
}

is_pre_cos_sin_init = true;
}

SDL_Rect advance(SDL_Rect pos, float angle, int units)
{
if (!is_pre_cos_sin_init) init_pre_math();

int ang = static_cast(-angle);
ang = (ang < 0) ? (ang % 360) + 360: ang % 360;

pos.x = pos.x + static_cast(units * pre_cos[ang]);
pos.y = pos.y + static_cast(units * pre_sin[ang]);

return pos;
}

Hello xEsk,

Tuesday, October 31, 2006, 9:01:38 PM, you wrote:

Hello!

Somebody knows why this code does not make “perfect” circles? How I
can improve the precision? I have tested this same code in Delphi and
works perfectly. Can it be problem of the SDL_Rect? I’m using C++.

I have seen some programs done in SDL that make this perfectly!

Sorry, I know isn’t a specific SDL question, but I’m desperate.

Not really a solution to your problem, but don’t bother making sin/cos
tables. These days, there is no point.

Also, we can’t see the drawing code…–
Best regards,
Peter mailto:@Peter_Mulholland

Hello!

Somebody knows why this code does not make “perfect” circles? How I
can improve the precision? I have tested this same code in Delphi
and works perfectly. Can it be problem of the SDL_Rect? I’m using
C++.

What do you mean by “perfect”? (There are many definitions, some of
which are irrelevant in this context.)

[…]

pos.x = pos.x + static_cast(units * pre_cos[ang]);
pos.y = pos.y + static_cast(units * pre_sin[ang]);
[…]

Rounding before you center the circle is probably a bad idea, unless
you ensure that the rounding is done the exact way you intend. You’ll
get different results depending on whether rounding is done towards
minus infinity or towards zero.

//David Olofson - Programmer, Composer, Open Source Advocate

.------- http://olofson.net - Games, SDL examples -------.
| http://zeespace.net - 2.5D rendering engine |
| http://audiality.org - Music/audio engine |
| http://eel.olofson.net - Real time scripting |
’-- http://www.reologica.se - Rheology instrumentation --'On Tuesday 31 October 2006 22:01, xEsk PiV wrote:

Can you give some sample code that shows a “non-perfect” circle? When I
tried your routines in a small test program, they worked fine.

Regards, ChristophAm Dienstag, 31. Oktober 2006 22:01 schrieb xEsk PiV:

Somebody knows why this code does not make “perfect” circles? How I
can improve the precision? I have tested this same code in Delphi
and works perfectly. Can it be problem of the SDL_Rect? I’m using
C++.

I have seen some programs done in SDL that make this perfectly!


_ http://wormsofprey.org
__ __ __ __ __ __ | __ __ ___
((( () | ’ | ) ) ) () | |) | ’ (/_ (|
The new and free real-time Worms game | |

Maybe this doesn’t apply any more but I think that it does…someone correct
these statements if they are wrong please

Using the circle equation to draw circles doesn’t give a perfect circle, it
gives an ellipse. The reason is because the pixels in your monitor
themselves aren’t perfect squares (unless you are on a Macintosh I believe).

So what you need to do is use the ellipse function to reverse the effects of
the monitor being distorted.

Is this archaic information which is no longer true or is it still relevant?
I’m not sure so please someone else help me out :P> ----- Original Message -----

From: sdl-bounces+atrix2=cox.net@libsdl.org
[mailto:sdl-bounces+atrix2=cox.net at libsdl.org] On Behalf Of Christoph
Freundl
Sent: Wednesday, November 08, 2006 5:55 AM
To: sdl at libsdl.org
Subject: Re: [SDL] “Perfect” circles

Am Dienstag, 31. Oktober 2006 22:01 schrieb xEsk PiV:

Somebody knows why this code does not make “perfect” circles? How I
can improve the precision? I have tested this same code in Delphi
and works perfectly. Can it be problem of the SDL_Rect? I’m using
C++.

I have seen some programs done in SDL that make this perfectly!

Can you give some sample code that shows a “non-perfect” circle? When I
tried your routines in a small test program, they worked fine.

Regards, Christoph


_ http://wormsofprey.org
__ __ __ __ __ __ | __ __ ___
((( () | ’ | ) ) ) () | |) | ’ (/_ (|
The new and free real-time Worms game | |


SDL mailing list
SDL at libsdl.org
http://www.libsdl.org/mailman/listinfo/sdl

Hello !

Can you give some sample code that shows a “non-perfect” circle? When I
tried your routines in a small test program, they worked fine.

Maybe he means an aspect ratio correct circle ?

CU

[…]

Using the circle equation to draw circles doesn’t give a perfect
circle, it gives an ellipse. The reason is because the pixels in
your monitor themselves aren’t perfect squares (unless you are on a
Macintosh I believe).

This is true if you’re using 1280x1024 on a standard 4:3 aspect ratio
CRT monitor. In pretty much all other cases (including 1280x1280
TFTs, which are not 4:3!), pixels are perfect squares, within the
tolerances of linearity and whatnot on CRTs.

The bad news is that lots of people are using 1280x1024 instead of
1280x960 on 4:3 CRTs. Meanwhile, there are 5:4 monitors (even CRTs,
altough I think those are rare these days), so you cannot just assume
that 1280x1024 means pixels are non-square.

The only chance of getting it right (on properly configured systems)
would be to ask the system. I’m not sure there is a portable way of
doing this. Maybe something that SDL could handle?

//David Olofson - Programmer, Composer, Open Source Advocate

.------- http://olofson.net - Games, SDL examples -------.
| http://zeespace.net - 2.5D rendering engine |
| http://audiality.org - Music/audio engine |
| http://eel.olofson.net - Real time scripting |
’-- http://www.reologica.se - Rheology instrumentation --'On Wednesday 08 November 2006 15:26, Alan Wolfe wrote:

I think you’re thinking of the old-school 320x200 (256 color) mode,
which has an 8:5 aspect ratio. This doesn’t quite match up with your
monitor’s typical 4:3 aspect ratio. I think this video mode was
popular because of the fact that it fit in a single segment in memory
in those 16bit days (320 * 200 = 64000, and a segment could access
65536 bytes). Of course, the fact that your squares were rectangles
and circles were ellipses led to the infamous “Mode X”, which was
320x240 (4:3 ratio), but still 256 colors.

Nowadays if your monitor is 4:3 you’re probably running at 640x480,
800x600, 1024x768, 1600x1200, etc. - all of these modes are 4:3, so if
you try to draw a circle it should look like a circle (well, a
pixelated circle, but not elongated into an ellipse).

-MikeOn 11/8/06, Alan Wolfe wrote:

Maybe this doesn’t apply any more but I think that it does…someone correct
these statements if they are wrong please

Using the circle equation to draw circles doesn’t give a perfect circle, it
gives an ellipse. The reason is because the pixels in your monitor
themselves aren’t perfect squares (unless you are on a Macintosh I believe).

So what you need to do is use the ellipse function to reverse the effects of
the monitor being distorted.

Is this archaic information which is no longer true or is it still relevant?
I’m not sure so please someone else help me out :stuck_out_tongue:

2006/11/8, David Olofson :

The bad news is that lots of people are using 1280x1024 instead of
1280x960 on 4:3 CRTs. Meanwhile, there are 5:4 monitors (even CRTs,
altough I think those are rare these days), so you cannot just assume
that 1280x1024 means pixels are non-square.

The only chance of getting it right (on properly configured systems)
would be to ask the system. I’m not sure there is a portable way of
doing this. Maybe something that SDL could handle?

Under X there can be different DPI for vertical and horizontal resolution.
Also there is an option in X which sets physical size of monitor’s
visible area.
I doubt there is such way for Windows, but maybe for Mac OS X there is.–
Roman Kyrylych (??? ???)

Ahh… thanks, gonna update the old memory banks :P> ----- Original Message -----

From: sdl-bounces+atrix2=cox.net@libsdl.org
[mailto:sdl-bounces+atrix2=cox.net at libsdl.org] On Behalf Of Mike Shal
Sent: Wednesday, November 08, 2006 7:29 AM
To: A list for developers using the SDL library. (includes SDL-announce)
Subject: Re: [SDL] “Perfect” circles

On 11/8/06, Alan Wolfe wrote:

Maybe this doesn’t apply any more but I think that it does…someone
correct
these statements if they are wrong please

Using the circle equation to draw circles doesn’t give a perfect circle,
it
gives an ellipse. The reason is because the pixels in your monitor
themselves aren’t perfect squares (unless you are on a Macintosh I
believe).

So what you need to do is use the ellipse function to reverse the effects
of
the monitor being distorted.

Is this archaic information which is no longer true or is it still
relevant?
I’m not sure so please someone else help me out :stuck_out_tongue:

I think you’re thinking of the old-school 320x200 (256 color) mode,
which has an 8:5 aspect ratio. This doesn’t quite match up with your
monitor’s typical 4:3 aspect ratio. I think this video mode was
popular because of the fact that it fit in a single segment in memory
in those 16bit days (320 * 200 = 64000, and a segment could access
65536 bytes). Of course, the fact that your squares were rectangles
and circles were ellipses led to the infamous “Mode X”, which was
320x240 (4:3 ratio), but still 256 colors.

Nowadays if your monitor is 4:3 you’re probably running at 640x480,
800x600, 1024x768, 1600x1200, etc. - all of these modes are 4:3, so if
you try to draw a circle it should look like a circle (well, a
pixelated circle, but not elongated into an ellipse).

-Mike


SDL mailing list
SDL at libsdl.org
http://www.libsdl.org/mailman/listinfo/sdl

Peter Mulholland wrote:

Not really a solution to your problem, but don’t bother making sin/cos
tables. These days, there is no point.
Especialy considering he filled the cos table with sin and the sin table
with cos :smiley:

You need to look for circle rastering algorithms. The best one
according to me is Bresenham’s algorithm.

Googling gave me the following page about it:
http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/bresenham.html

It’s fast because it only uses integer calculations, and it gives a
very good result.

Kind regards,

Bernard Fran?ois

Depends on your target platform. :slight_smile:

-bill!On Wed, Nov 08, 2006 at 10:48:29AM +0000, Peter Mulholland wrote:

Not really a solution to your problem, but don’t bother making sin/cos
tables. These days, there is no point.

Hello Bill,

Wednesday, November 8, 2006, 10:20:54 PM, you wrote:

Depends on your target platform. :slight_smile:

Quite true, if it has no FPU :slight_smile:

Even then you only need a sine table, and enough to cover one quadrant
only. Takes me back to my Amiga days ;)–
Best regards,
Peter mailto:@Peter_Mulholland

Depends on your target platform. :slight_smile:

Quite true, if it has no FPU :slight_smile:

Precisely. Some I can think of: Sharp Zaurus, GamePark GP2X.

Even then you only need a sine table, and enough to cover one quadrant
only. Takes me back to my Amiga days :wink:

Indeed!On Wed, Nov 08, 2006 at 10:26:45PM +0000, Peter Mulholland wrote:


-bill!
bill at newbreedsoftware.com
http://www.newbreedsoftware.com/

Bill Kendrick wrote:> On Wed, Nov 08, 2006 at 10:26:45PM +0000, Peter Mulholland wrote:

Even then you only need a sine table, and enough to cover one quadrant
only. Takes me back to my Amiga days :wink:

Indeed!

If you’re only drawing circles, you don’t need trigonometric functions
at all. Just use Bresenham’s algorithm.


Rainer Deyke - rainerd at eldwood.com

Hello,

First, thank you to all for your answars! I’m so glad :slight_smile:

Second, I made this example to show you how I see the results (I build
it on Windows, my main computer where I work is broken…).

http://img73.imageshack.us/img73/2516/demo01lf8.png
http://img490.imageshack.us/img490/3441/demo02js5.png

Bye and have a nice day! :smiley:

xEsk.---------

Source code:

#include <math.h>
#include <SDL/SDL.h>

float pre_cos[360];
float pre_sin[360];
bool is_pre_cos_sin_init = false;

const float PI = 3.14159265358979323846;

float RadToDeg(float rad) { return rad * 180 / PI; }
float DegToRad(float deg) { return deg * PI / 180; }

void init_pre_math()
{
for(int i = 0; i <= 360; i++)
{
pre_cos[i] = cos(DegToRad(i));
pre_sin[i] = sin(DegToRad(i));
}

is_pre_cos_sin_init = true;
}

SDL_Rect advance(SDL_Rect pos, float angle, int units)
{
if (!is_pre_cos_sin_init) init_pre_math();

int ang = static_cast(-angle);
ang = (ang < 0) ? (ang % 360) + 360: ang % 360;

pos.x = pos.x + static_cast(units * pre_cos[ang]);
pos.y = pos.y + static_cast(units * pre_sin[ang]);

return pos;
}

/*
This function is a modification of
"int _putPixelAlpha(SDL_Surface * surface, Sint16 x, Sint16 y,
Uint32 color, Uint8 alpha);"

extracted from:
SDL_gfxPrimitives.h - Graphics primitives for SDL surfaces

LGPL (c) A. Schiffler

*/

/* Defines for pixel clipping tests (SDL_gfxPrimitives) */

#define clip_xmin(surface) surface->clip_rect.x
#define clip_xmax(surface) surface->clip_rect.x+surface->clip_rect.w-1
#define clip_ymin(surface) surface->clip_rect.y
#define clip_ymax(surface) surface->clip_rect.y+surface->clip_rect.h-1

bool putPixel(SDL_Surface* dst, Sint16 x, Sint16 y, Uint32 color,
const Uint8 alpha)
{
/* Lock the surface */
if (SDL_MUSTLOCK(dst))
if (SDL_LockSurface(dst) < 0)
return false;

Uint32 R, G, B, A = 0;
Uint32 Rmask = dst->format->Rmask,
Gmask = dst->format->Gmask,
Bmask = dst->format->Bmask,
Amask = dst->format->Amask;

/* First Check if this new pixel is in the surface /
if (x >= clip_xmin(dst) && x <= clip_xmax(dst) && y >=
clip_ymin(dst) && y <= clip_ymax(dst))
{
if (alpha == 255)
((Uint16 )dst->pixels + y * dst->pitch / 2 + x) = color;
else
{
Uint16
pixel = (Uint16
)dst->pixels + y
dst->pitch / 2 + x;
Uint32 dc = *pixel;

  R = ((dc & Rmask) + (((color & Rmask) - (dc & Rmask)) * alpha >>

8)) & Rmask;
G = ((dc & Gmask) + (((color & Gmask) - (dc & Gmask)) * alpha >>
8)) & Gmask;
B = ((dc & Bmask) + (((color & Bmask) - (dc & Bmask)) * alpha >>
8)) & Bmask;

  if (Amask)
    A = ((dc & Amask) + (((color & Amask) - (dc & Amask)) * alpha

8)) & Amask;

  *pixel = R | G | B | A;
}
return true;

}

/* Unlock the surface */
if (SDL_MUSTLOCK(dst))
SDL_UnlockSurface(dst);

return true;
}

int main (int argc, char argv[]){
/
Initialize SDL */
SDL_Init (SDL_INIT_VIDEO);
atexit (SDL_Quit);

/* Set 640x480 16-bits video mode */
SDL_Surface *screen = SDL_SetVideoMode (320, 250, 16,

SDL_SWSURFACE | SDL_DOUBLEBUF);
/* white screen */
SDL_FillRect(screen, NULL, SDL_MapRGB (screen->format, 255, 255, 255));

SDL_Rect position;
position.x = 160;
position.y = 240;

/* example: */
for (int n = 0; n < 360; n++)
{
  position = advance(position, n, 3);
  putPixel(screen, position.x, position.y, 0, 255);
}
/* Make sure everything is displayed on screen */
SDL_Flip(screen);

/* main loop */
int done = 0;
while (!done)
{
  SDL_Event event;
  /* Check for events */
  while (SDL_PollEvent (&event))
  {
    switch (event.type)
    {
      case SDL_KEYDOWN:
        break;
      case SDL_QUIT:
        done = 1;
        break;
      default:
        break;
    }
  }
}
return 0;

}

Ouch!!! is *answers no answars (sorry, my grammar sux xD)

Hello xEsk,

Thursday, November 9, 2006, 5:25:21 PM, you wrote:

Hello,

First, thank you to all for your answars! I’m so glad :slight_smile:

Second, I made this example to show you how I see the results (I build
it on Windows, my main computer where I work is broken…).

http://img73.imageshack.us/img73/2516/demo01lf8.png
http://img490.imageshack.us/img490/3441/demo02js5.png

That is undoubtedly caused by “units” being far too small. If you are
going to use a sin table in integer form, you should be using half of
the value for the floating point part of the number at least. Look up
"fixed point arithmetic" for more details.

Try “units” as 256.–
Best regards,
Peter mailto:@Peter_Mulholland

xEsk PiV wrote:

Hello,

First, thank you to all for your answars! I’m so glad :slight_smile:

Second, I made this example to show you how I see the results (I build
it on Windows, my main computer where I work is broken…).

http://img73.imageshack.us/img73/2516/demo01lf8.png
http://img490.imageshack.us/img490/3441/demo02js5.png

Bye and have a nice day! :smiley:

xEsk.


Source code:

#include <math.h>
#include <SDL/SDL.h>

float pre_cos[360];
float pre_sin[360];
bool is_pre_cos_sin_init = false;

const float PI = 3.14159265358979323846;

float RadToDeg(float rad) { return rad * 180 / PI; }
float DegToRad(float deg) { return deg * PI / 180; }

void init_pre_math()
{
for(int i = 0; i <= 360; i++)
{
pre_cos[i] = cos(DegToRad(i));
pre_sin[i] = sin(DegToRad(i));
}

is_pre_cos_sin_init = true;
}

SDL_Rect advance(SDL_Rect pos, float angle, int units)
{
if (!is_pre_cos_sin_init) init_pre_math();

int ang = static_cast(-angle);
ang = (ang < 0) ? (ang % 360) + 360: ang % 360;

pos.x = pos.x + static_cast(units * pre_cos[ang]);
pos.y = pos.y + static_cast(units * pre_sin[ang]);

return pos;
}

/*
This function is a modification of
"int _putPixelAlpha(SDL_Surface * surface, Sint16 x, Sint16 y,
Uint32 color, Uint8 alpha);"

extracted from:
SDL_gfxPrimitives.h - Graphics primitives for SDL surfaces

LGPL (c) A. Schiffler

*/

/* Defines for pixel clipping tests (SDL_gfxPrimitives) */

#define clip_xmin(surface) surface->clip_rect.x
#define clip_xmax(surface) surface->clip_rect.x+surface->clip_rect.w-1
#define clip_ymin(surface) surface->clip_rect.y
#define clip_ymax(surface) surface->clip_rect.y+surface->clip_rect.h-1

bool putPixel(SDL_Surface* dst, Sint16 x, Sint16 y, Uint32 color,
const Uint8 alpha)
{
/* Lock the surface */
if (SDL_MUSTLOCK(dst))
if (SDL_LockSurface(dst) < 0)
return false;

Uint32 R, G, B, A = 0;
Uint32 Rmask = dst->format->Rmask,
Gmask = dst->format->Gmask,
Bmask = dst->format->Bmask,
Amask = dst->format->Amask;

/* First Check if this new pixel is in the surface /
if (x >= clip_xmin(dst) && x <= clip_xmax(dst) && y >=
clip_ymin(dst) && y <= clip_ymax(dst))
{
if (alpha == 255)
((Uint16 )dst->pixels + y * dst->pitch / 2 + x) = color;
else
{
Uint16
pixel = (Uint16
)dst->pixels + y
dst->pitch / 2 + x;
Uint32 dc = *pixel;

  R = ((dc & Rmask) + (((color & Rmask) - (dc & Rmask)) * alpha >>

8)) & Rmask;
G = ((dc & Gmask) + (((color & Gmask) - (dc & Gmask)) * alpha >>
8)) & Gmask;
B = ((dc & Bmask) + (((color & Bmask) - (dc & Bmask)) * alpha >>
8)) & Bmask;

  if (Amask)
    A = ((dc & Amask) + (((color & Amask) - (dc & Amask)) * alpha

8)) & Amask;

  *pixel = R | G | B | A;
}
return true;

}

/* Unlock the surface */
if (SDL_MUSTLOCK(dst))
SDL_UnlockSurface(dst);

return true;
}

int main (int argc, char argv[]){
/
Initialize SDL */
SDL_Init (SDL_INIT_VIDEO);
atexit (SDL_Quit);

/* Set 640x480 16-bits video mode */
SDL_Surface *screen = SDL_SetVideoMode (320, 250, 16,

SDL_SWSURFACE | SDL_DOUBLEBUF);
/* white screen */
SDL_FillRect(screen, NULL, SDL_MapRGB (screen->format, 255, 255,
255));

SDL_Rect position;
position.x = 160;
position.y = 240;

/* example: */
for (int n = 0; n < 360; n++)
{
  position = advance(position, n, 3);
  putPixel(screen, position.x, position.y, 0, 255);
}
/* Make sure everything is displayed on screen */
SDL_Flip(screen);

/* main loop */
int done = 0;
while (!done)
{
  SDL_Event event;
  /* Check for events */
  while (SDL_PollEvent (&event))
  {
    switch (event.type)
    {
      case SDL_KEYDOWN:
        break;
      case SDL_QUIT:
        done = 1;
        break;
      default:
        break;
    }
  }
}
return 0;

}

It’s a miracle you even got something that looks somewhat like a circle to
draw :slight_smile:

Here :

pos.x = pos.x + static_cast(units * pre_cos[ang]);
pos.y = pos.y + static_cast(units * pre_sin[ang]);
You modify inplace the rectangle passed as an argument. In that situation,
returning pos is useless. But anyway, let’s admit that pos was initialised
with the center of the circle before the function call.

Here :

for (int n = 0; n < 360; n++)
{
  position = advance(position, n, 3);
  putPixel(screen, position.x, position.y, 0, 255);
}

You never reset position to the center of the circle between calls. And at
that point I’ve understood what you were trying to do. Now, I have to admit
that such algorithm will indeed be useful to display a circle, but it is a
strange way to do it. And since position stores the pixel coordinates in an
integer, you do not have enouth accuracy to make it work :slight_smile: I can only
suspect that your previous version stored the pixel coordinates in a float.

Much easier is to recompute the new pixel position from the center between
each call. Use that formula instead :

pos.x = center.x + pre_cos[ang] * radius
pos.y = center.y + pre_cos[ang] * radius

and loop with ang going from 0 to 360.

All that reminds me of the time I used Locomotive Basic on a CPC to draw
things :slight_smile: