Issues trying to scale up sine wave in c - c

Hopefully somebody can point out why this isnt working or where i may be going wrong. Im producing a sine wave by way of for loops in c. The ultimate aim is to produce a .ppm file displaying this. Im working on a 1 to 1 pixel ratio. My box is 128H*256W. The sine wave is displaying but due to the answer being produced in rads the result is a very small two pixel high "wave". I assume this is due to the rad values being between 1 and -1. This is my code. I tried just simply timesing by a greater number to increase the size of the y values in the hopes it would plot correctly but this does little or worse causes the applicattion to stop running. Any ideas very welcome.
for (x = 0; x < H; x++)
{
y =(int) H/2+ sin(x*(2*PI));
y = y * 50;
image[y][x][1] = 0;
image[y][x][2] = 255;
image[y][x][3] = 0;
}
EDIT: This is what is being produced in the .ppm file when opened via infraview. Also im #defining PI 3.141592653589793. Again is this possibly an area of issue.
first sine wave .ppm

I conject that y is an int.
Your sin value will be truncated to an integer; 0 for most cases, but very occasionally -1 or +1.
The fix is simple: use a floating point for y, and cast once you want to use it as an array index.

As y is commented to be an int and H appears to be an int constant, perform calculations as double first, then convert to int.
Use round to avoid truncations effect of simply casting a double to int.
y = (int) round(50*(sin(x*(2*PI)) + H/2.0));
Original code also scaled H/2 by 50. I think code may only want to scale the sin() and not the offset.
#define XOffset 0
#define YOffset (H/2.0)
#define XScale (2*PI)
#define YScale 50
y = (int) round(YScale*sin(x*XScale + XOffset) + YOffset);
Defensive programming tip: since y is calculated, insure it is in the valid index range before using it as an index.
// Assuming image` is a fixed sized array
#define Y_MAX (sizeof image/sizeof image[0] - 1)
if (y >= 0 && y <= Y_MAX) {
image[y][x][1] = 0;
image[y][x][2] = 255;
image[y][x][3] = 0;
}

y = y * 50, where y = H/2 (+ or - 1) gives you y around 25*H, which is out of bounds.
A closer approximation is this:
y = (int) ( H/2 + H/2 * sin(x*2*PI) )
which gives the extremes H/2 - H/2 = 0 and H/2 + H/2 = H, which is one too high. So, we scale not by H/2 but by (H-1)/2:
y = (int) ( H/2 + (H-1)/2 * sin(x*2*PI) )
which gives us an y-range 0 to H-1.
To have a bit more control over the period of the sine wave, let's write it like this:
sin( x/W * 2*PI )
Here, we divide x by W so that x/W itself will range from 0 to 1.
It is then scaled by 2*PI to produce a range from 0 to 2π. This will plot one period of the sine wave across the entire width. If we introduce a frequency factor f:
sin( f * x/W * 2*PI )
we can now say how many periods to draw, even fractions. For f=1 it will draw one period, f=2 two periods, and f=1 half a period.
Here's a small JS demo showing three values for f: 0.5 is red, 1 is green and 2 is white:
var c = document.getElementById('c'),
W = c.width,
H = c.height,
ctx = c.getContext('2d'),
image = ctx.getImageData(0,0,W,H);
for ( var i = 0; i < image.data.length; i +=4) {
image.data[i+0]=0;
image.data[i+1]=0;
image.data[i+2]=0;
image.data[i+3]=255;
}
function render(image,colidx,f) {
for ( var x = 0; x < W; x++ )
{
var y = H/2 - Math.round( H/2 * Math.sin(f*x/W*Math.PI*2) );
if ( y >=0 && y < H ) {
if ( colidx & 1 ) image.data[ 4*( W*y + x ) + 0] = 255;
if ( colidx & 2 ) image.data[ 4*( W*y + x ) + 1] = 255;
if ( colidx & 4 ) image.data[ 4*( W*y + x ) + 2] = 255;
}
}
}
render(image,1,0.5);
render(image,2,1);
render(image,7,2);
ctx.putImageData(image, 0,0);
canvas{ border: 1px solid red;}
<canvas id='c' width='256' height='128'></canvas>
The code then becomes:
float f = 1;
for (x = 0; x < W; x++)
{
y = (int) ( (H-1)/2 + (H-1)/2 * sin(f * x/W * 2*PI) );
image[y][x][0] = 0;
image[y][x][1] = 255;
image[y][x][2] = 0;
}

Related

My Mandelbrot Set displays wrong outlines when plotting with few iterations

I'm writing a program that plots the Mandelbrot set in C.
I've been able to display it and it looks fine however when I lower the number of iterations I get this effect that generates what I can only describe as "clouds":
And here's how it should look (I got it from a website) :
.
How can I make mine look like the above?
Here's the code that plots a single point:
double getFracPoint(double x,double y){
//scale x and y
x = x * ((plotEnd.x-plotStart.x) / SCREENWIDTH) + plotStart.x;
y = y * ((plotEnd.y-plotStart.y) / SCREENHEIGHT) + plotStart.y;
x/=zoom;
y/=zoom;
//instead of using the complex number library of the C standard
//I decided to use regular numbers as it turns out to be faster.
//The first number is the real part the second number is the imaginary
//part.
double z[2];
z[0] = z[1] = 0;
double c[2];
c[0] = x;
c[1] = y;
int n = 0;
for(int i = 0; i < ITERS; i++,n++){
//if it's out of boundaries we are sure it does not belong to the set.
if(z[0] > 4 || -4 > z[0] || -4 > z[1] || 4 < z[1])
break;
double t = z[1]; //store z[1]
//multiply z to itself
z[1] = (z[0] * z[1]) + (z[0] * z[1]);
z[0] = z[0] * z[0] + -(t*t);
//add C to Z
z[0] += c[0];
z[1] += c[1];
}
return (double)n/(double)ITERS;
}
What am I doing wrong here?
Your "out of bounds" test checks that z falls within a square of radius 4:
if(z[0] > 4 || -4 > z[0] || -4 > z[1] || 4 < z[1])
The typical test, however, is to check the Euclidean distance from origin (i.e. the complex number norm, i.e. check that it falls within a circle of radius 4):
if(z[0]*z[0] + z[1]*z[1] > 4*4)

Ray Tracing calculation in C

I'm new to ray tracing and trying to program one in C. But My program keep on showing a dot (around 1-3 pixel) of the sphere in the wrong places and now I'm confused. This feels like a very stupid question, but I'm confused about exactly how big is 1 radius of a sphere? What I mean by that is if the radius is 1, the circle is 2 pixels?
I know all the calculations and I triple checked if I had any errors in my codes. but just incase, here is part of my codes:
Directions:
//size: 1024x768, view point (512 384 1), screen (0 0 0) to (1024 768 0)
ray[0] = x - start_x;
ray[1] = y - start_y;
ray[2] = 0 - start_z;
//normalize
double length;
length = (sqrt((ray[0]*ray[0]) + (ray[1]*ray[1]) + (ray[2]*ray[2])));
ray[0] = ray[0]/length;
ray[1] = ray[1]/length;
ray[2] = ray[2]/length;
Intersection:
temp = top; //my struct with sphere data, _x, _y, _z, _r, _red, _green, _blue
//x and y is the current pixel value
while (temp != NULL) {
x_diff = start_x - temp->_x + 0.0;
y_diff = start_y - temp->_y + 0.0;
z_diff = start_z - temp->_z + 0.0;
//a = 1 because my direction is a normalized
b = 2.0 * ((rayVector[0] * x_diff) + (rayVector[1] * y_diff) + (rayVector[2] * z_diff));
c = (x_diff * x_diff * 1.0) + (y_diff * y_diff) + (z_diff * z_diff) - (temp->_r * temp->_r);
check = (b * b) - (4.0 * c);
if (check < 0) { //0
pixels[width][height][0] = 0.0;
pixels[width][height][1] = 0.0;
pixels[width][height][2] = 0.0;
}
else if (check == 0) { //1
r1 = (b * -1.0) /2.0;
if (r1 < nearest_z) {
nearest_z = r1;
pixels[width][height][0] = temp->_red;
pixels[width][height][1] = temp->_green;
pixels[width][height][2] = temp->_blue;
}
}
else { //2
r1 = ((b * -1.0) + sqrt(check))/2.0;
r2 = ((b * -1.0) - sqrt(check))/2.0;
if ((r1 < r2) && (r1 < nearest_z)) {
nearest_z = r1;
pixels[width][height][0] = 255.0;
pixels[width][height][1] = 0;
pixels[width][height][2] = 0;
}
else if ((r2 < r1) && (r2 < nearest_z)) {
nearest_z = r2;
pixels[width][height][0] = temp->_red;
pixels[width][height][1] = temp->_green;
pixels[width][height][2] = temp->_blue;
}
}
temp = temp->next;
}
I haven't done any lightings yet since the flat colouring it doesn't work. I'm new to openGL so expect me to miss some common functions in the codes. Thanks in advance.
Edit:
I only have one sphere currently, but my output looks like: img1
I was expecting a bigger circle? Also, I had a printf for each intersection (if there is) and when I manually plot in a paper, it is a 4x5 pixel square. But there are 4 dots in the output.
Edit 2: I change the size of the sphere to: x = 512 y = 384 z = -21 r = 30, it gave me this:
img2
Again, I only have one sphere and there are 4 in the image. Also, there are holds between the lines?
If I change the z value to -20, now my output is all white (colour of sphere).
I use glDrawPixels(1024,768,GL_RGB,GL_FLOAT,pixels); to draw
I had a RBG output file, everything seems to be in the right place. but when I draw on the program, it is off.

Animating a sine wave infinitely

I need a function to animate a sine wave infinitely over time. The sine wave moves to the left.
My sine wave is built using the following equation:
A * sin(B * x + C) + D
Now to animate the sine wave as if it is moving to the left, I simply increase C by 1 everytime I refresh the screen. Now this is all fine and dandy for a few minutes but I need to have that animation run for hours. I can't just have an integer build up 60 times a second forever. How does someone deal with this? Do I just try to find a point where the sine wave crosses 0 and then restart the animation from 0?
I just need to have the logic of something like this explained.
EDIT #1
I forgot to mention that there's a randomized component to my sine. The sine is not continuously the same. A and D are sinusoidal functions tied to that integer at the moment. The sine needs to look random with varying periods and amplitudes.
EDIT #2
Edited see Edit 3
EDIT #3
#Potatoswatter I tried implementing your technique but I don't think I'm getting it. Here's what I got:
static double i = 0;
i = i + (MPI / 2);
if ( i >= 800 * (MPI / 2) ) i -= 800 * (MPI / 2);
for (k = 0; k < 800; ++k)
{
double A1 = 145 * sin((rand1 * (k - 400) + i) / 300) + rand3; // Amplitude
double A2 = 100 * sin((rand2 * (k - 400) + i) / 300) + rand2; // Amplitude
double A3 = 168 * sin((rand3 * (k - 400) + i) / 300) + rand1; // Amplitude
double B1 = 3 + rand1 + (sin((rand3 * k) * i) / (500 * rand1)); // Period
double B2 = 3 + rand2 + (sin((rand2 * k) * i) / 500); // Period
double B3 = 3 + rand3 + (sin((rand1 * k) * i) / (500 * rand3)); // Period
double x = k; // Current x
double C1 = 10 * i; // X axis move
double C2 = 11 * i; // X axis move
double C3 = 12 * i; // X axis move
double D1 = rand1 + sin(rand1 * x / 600) * 4; // Y axis move
double D2 = rand2 + sin(rand2 * x / 500) * 4; // Y axis move
double D3 = rand3 + cos(rand3 * x / 400) * 4; // Y axis move
sine1[k] = (double)A1 * sin((B1 * x + C1) / 400) + D1;
sine2[k] = (double)A2 * sin((B2 * x + C2) / 300) + D2 + 100;
sine3[k] = (double)A3 * cos((B3 * x + C3) / 500) + D3 + 50;
}
How do I modify this to make it work?
Halp!
Sine has a period of 2 pi, meaning that sin(x) = sin(x + 2 * M_PI), for any x.
So, you could just increase C by, say, pi/n where n is any integer, as you refresh the screen, and after 2n refreshes, reset C (to 0, or whatever).
Edit for clarity: the integer n is not meant to change over time.
Instead, pick some n, for example, let's say n = 10. Now, every frame, increase x by pi / 10. After 20 frames, you have increased x by a total of 20 * pi / 10 = 2 * pi. Since sin(x + 2 * pi) = sin(x), you may as well just reset your sin(...) input to just x, and start the process over.
sin is periodic, with a period of 2π. Therefore, if the argument is greater than 2π, you can subtract 2 * M_PI from it and get the same answer.
Instead of using a single variable k to compute all waves of various speeds, use three variables double k1, k2, k3, and keep them bound in the range from 0 to 2π.
if ( k2 >= 2 * M_PI ) k2 -= 2 * M_PI;
They may be individually updated by adding some value each frame. If the increment may be more than 2π then subtracting a single 2π won't bring them back into range, but you can use fmod() instead.
I decided to change my course of action. I just drive i with the system's monotonic clock like so:
struct timespec spec;
int ms;
time_t s;
static unsigned long long etime = 0;
clock_gettime(CLOCK_MONOTONIC, &spec);
s = spec.tv_sec;
ms = spec.tv_nsec / 10000000;
etime = concatenate((long)s, ms);
Then I simply changed i to etime in my sine equations. Here's the concatenating function I used for this purpose:
unsigned concatenate(unsigned x, unsigned y) {
x = x * 100;
return x + y;
}

Optimize Bilinear Resize Algorithm in C

Can anyone spot any way to improve the speed in the next Bilinear resizing Algorithm?
I need to improve Speed as this is critical, keeping good image quality. Is expected to be used in mobile devices with low speed CPUs.
The algorithm is used mainly for up-scale resizing. Any other faster Bilinear algorithm also would be appreciated. Thanks
void resize(int* input, int* output, int sourceWidth, int sourceHeight, int targetWidth, int targetHeight)
{
int a, b, c, d, x, y, index;
float x_ratio = ((float)(sourceWidth - 1)) / targetWidth;
float y_ratio = ((float)(sourceHeight - 1)) / targetHeight;
float x_diff, y_diff, blue, red, green ;
int offset = 0 ;
for (int i = 0; i < targetHeight; i++)
{
for (int j = 0; j < targetWidth; j++)
{
x = (int)(x_ratio * j) ;
y = (int)(y_ratio * i) ;
x_diff = (x_ratio * j) - x ;
y_diff = (y_ratio * i) - y ;
index = (y * sourceWidth + x) ;
a = input[index] ;
b = input[index + 1] ;
c = input[index + sourceWidth] ;
d = input[index + sourceWidth + 1] ;
// blue element
blue = (a&0xff)*(1-x_diff)*(1-y_diff) + (b&0xff)*(x_diff)*(1-y_diff) +
(c&0xff)*(y_diff)*(1-x_diff) + (d&0xff)*(x_diff*y_diff);
// green element
green = ((a>>8)&0xff)*(1-x_diff)*(1-y_diff) + ((b>>8)&0xff)*(x_diff)*(1-y_diff) +
((c>>8)&0xff)*(y_diff)*(1-x_diff) + ((d>>8)&0xff)*(x_diff*y_diff);
// red element
red = ((a>>16)&0xff)*(1-x_diff)*(1-y_diff) + ((b>>16)&0xff)*(x_diff)*(1-y_diff) +
((c>>16)&0xff)*(y_diff)*(1-x_diff) + ((d>>16)&0xff)*(x_diff*y_diff);
output [offset++] =
0x000000ff | // alpha
((((int)red) << 24)&0xff0000) |
((((int)green) << 16)&0xff00) |
((((int)blue) << 8)&0xff00);
}
}
}
Off the the top of my head:
Stop using floating-point, unless you're certain your target CPU has it in hardware with good performance.
Make sure memory accesses are cache-optimized, i.e. clumped together.
Use the fastest data types possible. Sometimes this means smallest, sometimes it means "most native, requiring least overhead".
Investigate if signed/unsigned for integer operations have performance costs on your platform.
Investigate if look-up tables rather than computations gain you anything (but these can blow the caches, so be careful).
And, of course, do lots of profiling and measurements.
In-Line Cache and Lookup Tables
Cache your computations in your algorithm.
Avoid duplicate computations (like (1-y_diff) or (x_ratio * j))
Go through all the lines of your algorithm, and try to identify patterns of repetitions. Extract these to local variables. And possibly extract to functions, if they are short enough to be inlined, to make things more readable.
Use a lookup-table
It's quite likely that, if you can spare some memory, you can implement a "store" for your RGB values and simply "fetch" them based on the inputs that produced them. Maybe you don't need to store all of them, but you could experiment and see if some come back often. Alternatively, you could "fudge" your colors and thus end up with less values to store for more lookup inputs.
If you know the boundaries for you inputs, you can calculate the complete domain space and figure out what makes sense to cache. For instance, if you can't cache the whole R, G, B values, maybe you can at least pre-compute the shiftings ((b>>16) and so forth...) that are most likely deterministic in your case).
Use the Right Data Types for Performance
If you can avoid double and float variables, use int. On most architectures, int would be test faster type for computations because of the memory model. You can still achieve decent precision by simply shifting your units (ie use 1026 as int instead of 1.026 as double or float). It's quite likely that this trick would be enough for you.
x = (int)(x_ratio * j) ;
y = (int)(y_ratio * i) ;
x_diff = (x_ratio * j) - x ;
y_diff = (y_ratio * i) - y ;
index = (y * sourceWidth + x) ;
Could surely use some optimization: you were using x_ration * j-1 just a few cycles earlier, so all you really need here is x+=x_ratio
My random guess (use a profiler instead of letting people guess!):
The compiler has to generate that works when input and output overlap which means it has to do generate loads of redundant stores and loads. Add restrict to the input and output parameters to remove that safety feature.
You could also try using a=b; and c=d; instead of loading them again.
here is my version, steal some ideas. My C-fu is quite weak, so some lines are pseudocodes, but you can fix them.
void resize(int* input, int* output,
int sourceWidth, int sourceHeight,
int targetWidth, int targetHeight
) {
// Let's create some lookup tables!
// you can move them into 2-dimensional arrays to
// group together values used at the same time to help processor cache
int sx[0..targetWidth ]; // target->source X lookup
int sy[0..targetHeight]; // target->source Y lookup
int mx[0..targetWidth ]; // left pixel's multiplier
int my[0..targetHeight]; // bottom pixel's multiplier
// we don't have to calc indexes every time, find out when
bool reloadPixels[0..targetWidth ];
bool shiftPixels[0..targetWidth ];
int shiftReloadPixels[0..targetWidth ]; // can be combined if necessary
int v; // temporary value
for (int j = 0; j < targetWidth; j++){
// (8bit + targetBits + sourceBits) should be < max int
v = 256 * j * (sourceWidth-1) / (targetWidth-1);
sx[j] = v / 256;
mx[j] = v % 256;
reloadPixels[j] = j ? ( sx[j-1] != sx[j] ? 1 : 0)
: 1; // always load first pixel
// if no reload -> then no shift too
shiftPixels[j] = j ? ( sx[j-1]+1 = sx[j] ? 2 : 0)
: 0; // nothing to shift at first pixel
shiftReloadPixels[j] = reloadPixels[i] | shiftPixels[j];
}
for (int i = 0; i < targetHeight; i++){
v = 256 * i * (sourceHeight-1) / (targetHeight-1);
sy[i] = v / 256;
my[i] = v % 256;
}
int shiftReload;
int srcIndex;
int srcRowIndex;
int offset = 0;
int lm, rm, tm, bm; // left / right / top / bottom multipliers
int a, b, c, d;
for (int i = 0; i < targetHeight; i++){
srcRowIndex = sy[ i ] * sourceWidth;
tm = my[i];
bm = 255 - tm;
for (int j = 0; j < targetWidth; j++){
// too much ifs can be too slow, measure.
// always true for first pixel in a row
if( shiftReload = shiftReloadPixels[ j ] ){
srcIndex = srcRowIndex + sx[j];
if( shiftReload & 2 ){
a = b;
c = d;
}else{
a = input[ srcIndex ];
c = input[ srcIndex + sourceWidth ];
}
b = input[ srcIndex + 1 ];
d = input[ srcIndex + 1 + sourceWidth ];
}
lm = mx[j];
rm = 255 - lm;
// WTF?
// Input AA RR GG BB
// Output RR GG BB AA
if( j ){
leftOutput = rightOutput ^ 0xFFFFFF00;
}else{
leftOutput =
// blue element
((( ( (a&0xFF)*tm
+ (c&0xFF)*bm )*lm
) & 0xFF0000 ) >> 8)
// green element
| ((( ( ((a>>8)&0xFF)*tm
+ ((c>>8)&0xFF)*bm )*lm
) & 0xFF0000 )) // no need to shift
// red element
| ((( ( ((a>>16)&0xFF)*tm
+ ((c>>16)&0xFF)*bm )*lm
) & 0xFF0000 ) << 8 )
;
}
rightOutput =
// blue element
((( ( (b&0xFF)*tm
+ (d&0xFF)*bm )*lm
) & 0xFF0000 ) >> 8)
// green element
| ((( ( ((b>>8)&0xFF)*tm
+ ((d>>8)&0xFF)*bm )*lm
) & 0xFF0000 )) // no need to shift
// red element
| ((( ( ((b>>16)&0xFF)*tm
+ ((d>>16)&0xFF)*bm )*lm
) & 0xFF0000 ) << 8 )
;
output[offset++] =
// alpha
0x000000ff
| leftOutput
| rightOutput
;
}
}
}

fast algorithm for drawing filled circles?

I am using Bresenham's circle algorithm for fast circle drawing. However, I also want to (at the request of the user) draw a filled circle.
Is there a fast and efficient way of doing this? Something along the same lines of Bresenham?
The language I am using is C.
Having read the Wikipedia page on Bresenham's (also 'Midpoint') circle algorithm, it would appear that the easiest thing to do would be to modify its actions, such that instead of
setPixel(x0 + x, y0 + y);
setPixel(x0 - x, y0 + y);
and similar, each time you instead do
lineFrom(x0 - x, y0 + y, x0 + x, y0 + y);
That is, for each pair of points (with the same y) that Bresenham would you have you plot, you instead connect with a line.
Just use brute force. This method iterates over a few too many pixels, but it only uses integer multiplications and additions. You completely avoid the complexity of Bresenham and the possible bottleneck of sqrt.
for(int y=-radius; y<=radius; y++)
for(int x=-radius; x<=radius; x++)
if(x*x+y*y <= radius*radius)
setpixel(origin.x+x, origin.y+y);
Here's a C# rough guide (shouldn't be that hard to get the right idea for C) - this is the "raw" form without using Bresenham to eliminate repeated square-roots.
Bitmap bmp = new Bitmap(200, 200);
int r = 50; // radius
int ox = 100, oy = 100; // origin
for (int x = -r; x < r ; x++)
{
int height = (int)Math.Sqrt(r * r - x * x);
for (int y = -height; y < height; y++)
bmp.SetPixel(x + ox, y + oy, Color.Red);
}
bmp.Save(#"c:\users\dearwicker\Desktop\circle.bmp");
You can use this:
void DrawFilledCircle(int x0, int y0, int radius)
{
int x = radius;
int y = 0;
int xChange = 1 - (radius << 1);
int yChange = 0;
int radiusError = 0;
while (x >= y)
{
for (int i = x0 - x; i <= x0 + x; i++)
{
SetPixel(i, y0 + y);
SetPixel(i, y0 - y);
}
for (int i = x0 - y; i <= x0 + y; i++)
{
SetPixel(i, y0 + x);
SetPixel(i, y0 - x);
}
y++;
radiusError += yChange;
yChange += 2;
if (((radiusError << 1) + xChange) > 0)
{
x--;
radiusError += xChange;
xChange += 2;
}
}
}
Great ideas here!
Since I'm at a project that requires many thousands of circles to be drawn, I have evaluated all suggestions here (and improved a few by precomputing the square of the radius):
http://quick-bench.com/mwTOodNOI81k1ddaTCGH_Cmn_Ag
The Rev variants just have x and y swapped because consecutive access along the y axis are faster with the way my grid/canvas structure works.
The clear winner is Daniel Earwicker's method ( DrawCircleBruteforcePrecalc ) that precomputes the Y value to avoid unnecessary radius checks. Somewhat surprisingly that negates the additional computation caused by the sqrt call.
Some comments suggest that kmillen's variant (DrawCircleSingleLoop) that works with a single loop should be very fast, but it's the slowest here. I assume that is because of all the divisions. But perhaps I have adapted it wrong to the global variables in that code. Would be great if someone takes a look.
EDIT: After looking for the first time since college years at some assembler code, I managed find that the final additions of the circle's origin are a culprit.
Precomputing those, I improved the fastest method by a factor of another 3.7-3.9 according to the bench!
http://quick-bench.com/7ZYitwJIUgF_OkDUgnyMJY4lGlA
Amazing.
This being my code:
for (int x = -radius; x < radius ; x++)
{
int hh = (int)std::sqrt(radius_sqr - x * x);
int rx = center_x + x;
int ph = center_y + hh;
for (int y = center_y-hh; y < ph; y++)
canvas[rx][y] = 1;
}
I like palm3D's answer. For being brute force, this is an amazingly fast solution. There are no square root or trigonometric functions to slow it down. Its one weakness is the nested loop.
Converting this to a single loop makes this function almost twice as fast.
int r2 = r * r;
int area = r2 << 2;
int rr = r << 1;
for (int i = 0; i < area; i++)
{
int tx = (i % rr) - r;
int ty = (i / rr) - r;
if (tx * tx + ty * ty <= r2)
SetPixel(x + tx, y + ty, c);
}
This single loop solution rivals the efficiency of a line drawing solution.
int r2 = r * r;
for (int cy = -r; cy <= r; cy++)
{
int cx = (int)(Math.Sqrt(r2 - cy * cy) + 0.5);
int cyy = cy + y;
lineDDA(x - cx, cyy, x + cx, cyy, c);
}
palm3D's brute-force algorithm I found to be a good starting point. This method uses the same premise, however it includes a couple of ways to skip checking most of the pixels.
First, here's the code:
int largestX = circle.radius;
for (int y = 0; y <= radius; ++y) {
for (int x = largestX; x >= 0; --x) {
if ((x * x) + (y * y) <= (circle.radius * circle.radius)) {
drawLine(circle.center.x - x, circle.center.x + x, circle.center.y + y);
drawLine(circle.center.x - x, circle.center.x + x, circle.center.y - y);
largestX = x;
break; // go to next y coordinate
}
}
}
Next, the explanation.
The first thing to note is that if you find the minimum x coordinate that is within the circle for a given horizontal line, you immediately know the maximum x coordinate.
This is due to the symmetry of the circle. If the minimum x coordinate is 10 pixels ahead of the left of the bounding box of the circle, then the maximum x is 10 pixels behind the right of the bounding box of the circle.
The reason to iterate from high x values to low x values, is that the minimum x value will be found with less iterations. This is because the minimum x value is closer to the left of the bounding box than the centre x coordinate of the circle for most lines, due to the circle being curved outwards, as seen on this image
The next thing to note is that since the circle is also symmetric vertically, each line you find gives you a free second line to draw, each time you find a line in the top half of the circle, you get one on the bottom half at the radius-y y coordinate. Therefore, when any line is found, two can be drawn and only the top half of the y values needs to be iterated over.
The last thing to note is that is that if you start from a y value that is at the centre of the circle and then move towards the top for y, then the minimum x value for each next line must be closer to the centre x coordinate of the circle than the last line. This is also due to the circle curving closer towards the centre x value as you go up the circle. Here is a visual on how that is the case.
In summary:
If you find the minimum x coordinate of a line, you get the maximum x coordinate for free.
Every line you find to draw on the top half of the circle gives you a line on the bottom half of the circle for free.
Every minimum x coordinate has to be closer to the centre of the circle than the previous x coordinate for each line when iterating from the centre y coordinate to the top.
You can also store the value of (radius * radius), and also (y * y) instead of calculating them
multiple times.
Here's how I'm doing it:
I'm using fixed point values with two bits precision (we have to manage half points and square values of half points)
As mentionned in a previous answer, I'm also using square values instead of square roots.
First, I'm detecting border limit of my circle in a 1/8th portion of the circle. I'm using symetric of these points to draw the 4 "borders" of the circle. Then I'm drawing the square inside the circle.
Unlike the midpoint circle algorith, this one will work with even diameters (and with real numbers diameters too, with some little changes).
Please forgive me if my explanations were not clear, I'm french ;)
void DrawFilledCircle(int circleDiameter, int circlePosX, int circlePosY)
{
const int FULL = (1 << 2);
const int HALF = (FULL >> 1);
int size = (circleDiameter << 2);// fixed point value for size
int ray = (size >> 1);
int dY2;
int ray2 = ray * ray;
int posmin,posmax;
int Y,X;
int x = ((circleDiameter&1)==1) ? ray : ray - HALF;
int y = HALF;
circlePosX -= (circleDiameter>>1);
circlePosY -= (circleDiameter>>1);
for (;; y+=FULL)
{
dY2 = (ray - y) * (ray - y);
for (;; x-=FULL)
{
if (dY2 + (ray - x) * (ray - x) <= ray2) continue;
if (x < y)
{
Y = (y >> 2);
posmin = Y;
posmax = circleDiameter - Y;
// Draw inside square and leave
while (Y < posmax)
{
for (X = posmin; X < posmax; X++)
setPixel(circlePosX+X, circlePosY+Y);
Y++;
}
// Just for a better understanding, the while loop does the same thing as:
// DrawSquare(circlePosX+Y, circlePosY+Y, circleDiameter - 2*Y);
return;
}
// Draw the 4 borders
X = (x >> 2) + 1;
Y = y >> 2;
posmax = circleDiameter - X;
int mirrorY = circleDiameter - Y - 1;
while (X < posmax)
{
setPixel(circlePosX+X, circlePosY+Y);
setPixel(circlePosX+X, circlePosY+mirrorY);
setPixel(circlePosX+Y, circlePosY+X);
setPixel(circlePosX+mirrorY, circlePosY+X);
X++;
}
// Just for a better understanding, the while loop does the same thing as:
// int lineSize = circleDiameter - X*2;
// Upper border:
// DrawHorizontalLine(circlePosX+X, circlePosY+Y, lineSize);
// Lower border:
// DrawHorizontalLine(circlePosX+X, circlePosY+mirrorY, lineSize);
// Left border:
// DrawVerticalLine(circlePosX+Y, circlePosY+X, lineSize);
// Right border:
// DrawVerticalLine(circlePosX+mirrorY, circlePosY+X, lineSize);
break;
}
}
}
void DrawSquare(int x, int y, int size)
{
for( int i=0 ; i<size ; i++ )
DrawHorizontalLine(x, y+i, size);
}
void DrawHorizontalLine(int x, int y, int width)
{
for(int i=0 ; i<width ; i++ )
SetPixel(x+i, y);
}
void DrawVerticalLine(int x, int y, int height)
{
for(int i=0 ; i<height ; i++ )
SetPixel(x, y+i);
}
To use non-integer diameter, you can increase precision of fixed point or use double values.
It should even be possible to make a sort of anti-alias depending on the difference between dY2 + (ray - x) * (ray - x) and ray2 (dx² + dy² and r²)
If you want a fast algorithm, consider drawing a polygon with N sides, the higher is N, the more precise will be the circle.
I would just generate a list of points and then use a polygon draw function for the rendering.
It may not be the algorithm yo are looking for and not the most performant one,
but I always do something like this:
void fillCircle(int x, int y, int radius){
// fill a circle
for(int rad = radius; rad >= 0; rad--){
// stroke a circle
for(double i = 0; i <= PI * 2; i+=0.01){
int pX = x + rad * cos(i);
int pY = y + rad * sin(i);
drawPoint(pX, pY);
}
}
}
The following two methods avoid the repeated square root calculation by drawing multiple parts of the circle at once and should therefore be quite fast:
void circleFill(const size_t centerX, const size_t centerY, const size_t radius, color fill) {
if (centerX < radius || centerY < radius || centerX + radius > width || centerY + radius > height)
return;
const size_t signedRadius = radius * radius;
for (size_t y = 0; y < radius; y++) {
const size_t up = (centerY - y) * width;
const size_t down = (centerY + y) * width;
const size_t halfWidth = roundf(sqrtf(signedRadius - y * y));
for (size_t x = 0; x < halfWidth; x++) {
const size_t left = centerX - x;
const size_t right = centerX + x;
pixels[left + up] = fill;
pixels[right + up] = fill;
pixels[left + down] = fill;
pixels[right + down] = fill;
}
}
}
void circleContour(const size_t centerX, const size_t centerY, const size_t radius, color stroke) {
if (centerX < radius || centerY < radius || centerX + radius > width || centerY + radius > height)
return;
const size_t signedRadius = radius * radius;
const size_t maxSlopePoint = ceilf(radius * 0.707106781f); //ceilf(radius * cosf(TWO_PI/8));
for (size_t i = 0; i < maxSlopePoint; i++) {
const size_t depth = roundf(sqrtf(signedRadius - i * i));
size_t left = centerX - depth;
size_t right = centerX + depth;
size_t up = (centerY - i) * width;
size_t down = (centerY + i) * width;
pixels[left + up] = stroke;
pixels[right + up] = stroke;
pixels[left + down] = stroke;
pixels[right + down] = stroke;
left = centerX - i;
right = centerX + i;
up = (centerY - depth) * width;
down = (centerY + depth) * width;
pixels[left + up] = stroke;
pixels[right + up] = stroke;
pixels[left + down] = stroke;
pixels[right + down] = stroke;
}
}
This was used in my new 3D printer Firmware, and it is proven the
fastest way for filled circle of a diameter from 1 to 43 pixel. If
larger is needed, the following memory block(or array) should be
extended following a structure I wont waste my time explaining...
If you have questions, or need larger diameter than 43, contact me, I
will help you drawing the fastest and perfect filled circles... or
Bresenham's circle drawing algorithm can be used above those
diameters, but having to fill the circle after, or incorporating the
fill into Bresenham's circle drawing algorithm, will only result in
slower fill circle than my code. I already benchmarked the different
codes, my solution is 4 to 5 times faster. As a test I have been
able to draw hundreds of filled circles of different size and colors
on a BigTreeTech tft24 1.1 running on a 1-core 72 Mhz cortex-m4
https://www.youtube.com/watch?v=7_Wp5yn3ADI
// this must be declared anywhere, as static or global
// as long as the function can access it !
uint8_t Rset[252]={
0,1,1,2,2,1,2,3,3,1,3,3,4,4,2,3,4,5,5,5,2,4,5,5,
6,6,6,2,4,5,6,6,7,7,7,2,4,5,6,7,7,8,8,8,2,5,6,7,
8,8,8,9,9,9,3,5,6,7,8,9,9,10,10,10,10,3,5,7,8,9,
9,10,10,11,11,11,11,3,5,7,8,9,10,10,11,11,12,12,
12,12,3,6,7,9,10,10,11,12,12,12,13,13,13,13,3,6,
8,9,10,11,12,12,13,13,13,14,14,14,14,3,6,8,9,10,
11,12,13,13,14,14,14,15,15,15,15,3,6,8,10,11,12,
13,13,14,14,15,15,15,16,16,16,16,4,7,8,10,11,12,
13,14,14,15,16,16,16,17,17,17,17,17,4,7,9,10,12,
13,14,14,15,16,16,17,17,17,18,18,18,18,18,4,7,9,
11,12,13,14,15,16,16,17,17,18,18,18,19,19,19,19,
19,7,9,11,12,13,15,15,16,17,18,18,19,19,20,20,20,
20,20,20,20,20,7,9,11,12,14,15,16,17,17,18,19,19
20,20,21,21,21,21,21,21,21,21};
// SOLUTION 1: (the fastest)
void FillCircle_v1(uint16_t x, uint16_t y, uint16_t r)
{
// all needed variables are created and set to their value...
uint16_t radius=(r<1) ? 1 : r ;
if (radius>21 ) {radius=21; }
uint16_t diam=(radius*2)+1;
uint16_t ymir=0, cur_y=0;
radius--; uint16_t target=(radius*radius+3*radius)/2; radius++;
// this part draws directly into the ILI94xx TFT buffer mem.
// using pointers..2 versions where you can draw
// pixels and lines with coordinates will follow
for (uint16_t yy=0; yy<diam; yy++)
{ ymir= (yy<=radius) ? yy+target : target+diam-(yy+1);
cur_y=y-radius+yy;
uint16_t *pixel=buffer_start_addr+x-Rset[ymir]+cur_y*buffer_width;
for (uint16_t xx= 0; xx<=(2*Rset[ymir]); xx++)
{ *pixel++ = CANVAS::draw_color; }}}
// SOLUTION 2: adaptable to any system that can
// add a pixel at a time: (drawpixel or add_pixel,etc_)
void FillCircle_v2(uint16_t x, uint16_t y, uint16_t r)
{
// all needed variables are created and set to their value...
uint16_t radius=(r<1) ? 1 : r ;
if (radius>21 ) {radius=21; }
uint16_t diam=(radius*2)+1;
uint16_t ymir=0, cur_y=0;
radius--; uint16_t target=(radius*radius+3*radius)/2; radius++;
for (uint16_t yy=0; yy<diam; yy++)
{ ymir= (yy<=radius) ? yy+target : target+diam-(yy+1);
cur_y=y-radius+yy;
uint16_t Pixel_x=x-Rset[ymir];
for (uint16_t xx= 0; xx<=(2*Rset[ymir]); xx++)
{ //use your add_pixel or draw_pixel here
// using those coordinates:
// X position will be... (Pixel_x+xx)
// Y position will be... (cur_y)
// and add those 3 brackets at the end
}}}
// SOLUTION 3: adaptable to any system that can draw fast
// horizontal lines
void FillCircle_v3(uint16_t x, uint16_t y, uint16_t r)
{
// all needed variables are created and set to their value...
uint16_t radius=(r<1) ? 1 : r ;
if (radius>21 ) {radius=21; }
uint16_t diam=(radius*2)+1;
uint16_t ymir=0, cur_y=0;
radius--; uint16_t target=(radius*radius+3*radius)/2; radius++;
for (uint16_t yy=0; yy<diam; yy++)
{ ymir= (yy<=radius) ? yy+target : target+diam-(yy+1);
cur_y=y-radius+yy;
uint16_t start_x=x-Rset[ymir];
uint16_t width_x=2*Rset[ymir];
// ... then use your best drawline function using those values:
// start_x: position X of the start of the line
// cur_y: position Y of the current line
// width_x: length of the line
// if you need a 2nd coordinate then :end_x=start_x+width_x
// and add those 2 brackets after !!!
}}
I did pretty much what AlegGeorge did but I changed three lines. I thought that this is faster but these are the results am I doing anything wrong? my function is called DrawBruteforcePrecalcV4. here's the code:
for (int x = 0; x < radius ; x++) // Instead of looping from -radius to radius I loop from 0 to radius
{
int hh = (int)std::sqrt(radius_sqr - x * x);
int rx = center_x + x;
int cmx = center_x - x;
int ph = center_y+hh;
for (int y = center_y-hh; y < ph; y++)
{
canvas[rx][y] = 1;
canvas[cmx][y] = 1;
}
}

Resources