Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » sin & cos: small optimization

This thread is locked; no one can reply to it. rss feed Print
 1   2   3   4 
sin & cos: small optimization
Bruce Perry
Member #270
April 2000

A circle does have infinitely many axes of symmetry. But when drawing one, we make use of the vertical, horizontal and 45-degree diagonal axes. Thus our pixellated approximation will be symmetrical in these axes (and almost symmetrical in the other axes).

Goodbytes, the distance comparison is just Pythagoras's Theorem. Square the x and y offsets (the coordinates of the point minus the coordinates of the centre of the circle), add the squares together, and compare this with a squared version of the radius. You can then potentially optimise this by making use of the last value of x*x and y*y, e.g. when x is incremented you can take advantage of the following identity:

(x+1)*(x+1) = x*x + 2*x + 1

And thus you can modify 'x2', your squared x value, as follows:

x2 += 2*x + 1;

This may or may not be quicker than just recomputing x*x; it would depend on the processor architecture. However, taking advantage of old values of y² would almost certainly help because y will not always change.

--
Bruce "entheh" Perry [ Web site | DUMB | Set Up Us The Bomb !!! | Balls ]
Programming should be fun. That's why I hate C and C++.
The brxybrytl has you.

Mr. Xboxor
Member #3,763
August 2003
avatar

Yeah, but umm, the allegro circle routine just adds and subtracts stuff, ABSOLUTELEY no multiplaction or division, just addition and substraction. What I want to know is how it checks which way is the closest by just adding and subtracting.

"Java is high performance. By high performance we mean adequate. By adequate we mean slow." -Mr. Bunny

Bob
Free Market Evangelist
September 2000
avatar

Quote:

What I want to know is how it checks which way is the closest by just adding and subtracting.

Google for "Bresenham circle algorithm", or "midpoint circle algorithm" (they're not quite the same, but they both reduce to just adds/subtracts/shifts).

--
- Bob
[ -- All my signature links are 404 -- ]

Mr. Xboxor
Member #3,763
August 2003
avatar

Quote:

Google for "Bresenham circle algorithm", or "midpoint circle algorithm" (they're not quite the same, but they both reduce to just adds/subtracts/shifts).

But that's what you're for Bob.:) And the allegro one doesn't use any shifts. Just addition and substraction, and not even a whole lot of that.

"Java is high performance. By high performance we mean adequate. By adequate we mean slow." -Mr. Bunny

Billybob
Member #3,136
January 2003

1void do_circle(BITMAP *bmp, int x, int y, int radius, int d, void (*proc)(BITMAP *, int, int, int))
2{
3 int cx = 0;
4 int cy = radius;
5 int df = 1 - radius;
6 int d_e = 3;
7 int d_se = -2 * radius + 5;
8 
9 do {
10 proc(bmp, x+cx, y+cy, d);
11 
12 if (cx)
13 proc(bmp, x-cx, y+cy, d);
14 
15 if (cy)
16 proc(bmp, x+cx, y-cy, d);
17 
18 if ((cx) && (cy))
19 proc(bmp, x-cx, y-cy, d);
20 
21 if (cx != cy) {
22 proc(bmp, x+cy, y+cx, d);
23 
24 if (cx)
25 proc(bmp, x+cy, y-cx, d);
26 
27 if (cy)
28 proc(bmp, x-cy, y+cx, d);
29 
30 if (cx && cy)
31 proc(bmp, x-cy, y-cx, d);
32 }
33 
34 if (df < 0) {
35 df += d_e;
36 d_e += 2;
37 d_se += 2;
38 }
39 else {
40 df += d_se;
41 d_e += 2;
42 d_se += 4;
43 cy--;
44 }
45 
46 cx++;
47 
48 } while (cx <= cy);
49}

most importantly:

if (df < 0)  {
   df += d_e;
   d_e += 2;
   d_se += 2;
      }
      else { 
   df += d_se;
   d_e += 2;
   d_se += 4;
   cy--;
      } 

      cx++;

insanely simple...yet so very very complex

Karadoc ~~
Member #2,749
September 2002
avatar

Quote:

karadoc, it's
sin(x) = cos(x - (pi)/2)
The difference is in the input not the output. Very important note there. There is probably also internal optimizations you can make to sin&cos tho to try and do something similar to your method(thus the creation of the sincos opcode(s)).

William, that is insulting to my intelligence...
honestly.
look again at the code:

double *sin = cos + (some number depending on your accuracy)

remember that we are talking about lookup tables.
notice the little asterisk next to sin.
This code will make your sin lookup table use the same memory as your cos lookup table, provided that your cos table goes a bit past where you'll be using it to.
See what I mean?

-----------

kikabo
Member #3,679
July 2003
avatar

just out of interest I found once that you can draw a circle using square root only instead of sin / cos (can't remember the situation - I think it was with atari basic!).

this is due to the fact that in the fig below the #'s are right angles, so if you know the diameter D and distant X you can find Y because 2 triangles are formed that share dimensions, so the result can be found using pythag.... can't remember the formula though but it's a nice little puzzle for those on their lunch breaks.

      **
    #*     **
   *| \      *
  *||  \      *
 *| |    \     *
 *| | Y   \     *
*|  |       \   *
*|  |         \ *
*---#---o-------*
  X     D

Edit: hah (I think)
y = sqrt( (x^2+(d-X)^2)/2)

Billybob
Member #3,136
January 2003

Standard Circle forumla:
r^2 = (x1 - x2)^2 + (y1 - y2)^2
center:(x1, y1)
Unit Circle:
y = (x + y)^2

:)
---
Karadoc: Yes, sorry, someone else pointed that out to me, I just wasn't paying attention to your entire code.

Mr. Xboxor
Member #3,763
August 2003
avatar

So do you guys think that you could use that circle drawing thing to approximate sin and cos values? I mean, if you can draw an approximated circle with sin and cos values, could you reverse the process, and instead use that circle to calculate sin and cos values? I'm writing a game with alot of rotation, and that would be sweet. Sure, I could use lookup tables, or a fixed point system, but that way would be cool too. As long as you don't have to add like a thousand things to get to the right point to calculate sin and cos.

"Java is high performance. By high performance we mean adequate. By adequate we mean slow." -Mr. Bunny

Billybob
Member #3,136
January 2003

Mr. Xboxor: I doubt it. The algorithim is designed to draw a circle, not calculate trig functions. sin/cos/tan are all designed to calculate trig functions. Use what they were designed for, is what I'm saying, because they would be more optimized in the intended usage.

EDIT: Also, I don't think the algorithim has a concept of angle, heh. It just draws points, there are no angles involved really...right?

Bruce Perry
Member #270
April 2000

The 'pixel walk' method used in drawing a circle will use points on the circle's circumference, but the rate of change of the angle will not be constant or predictable. Sorry, you can't replace sin and cos this way.

--
Bruce "entheh" Perry [ Web site | DUMB | Set Up Us The Bomb !!! | Balls ]
Programming should be fun. That's why I hate C and C++.
The brxybrytl has you.

Evert
Member #794
November 2000
avatar

Quote:

Degrees are cool, it's the thing we are most use to.

I never use degrees.

Quote:

It is also 0-360, not 0-6.282... Decimals are harder to deal with than integers, so degrees are better for that reason.

No. Using only integer values for the angles is far too inaccurate to do anything useful with (unless possibly if you do something like an spline interpolation, but then you probably loose any speed gain you might have had).

Quote:

I mean, if you can draw an approximated circle with sin and cos values, could you reverse the process, and instead use that circle to calculate sin and cos values?

Well, yeah, probably - using the laws for sines and cosines. However, it will be both less accurate and slower than using sin and cos directly.

Billybob
Member #3,136
January 2003

Evert, you do realize i'm talking about the human mind, right? and not a machine? It's harder for our minds to do decimal compared to integers, unless you've studied decimal math in your head extensively. Calculator and computer wise, duh of course decimals are fine, but I was talking about why we use degrees so much, cause we can do the calcs in our heads a lot.

i.e.
x - pi/2
is harder to calculate in our heads than
x - 90

And since we're the ones programming the computer, they have to follow suit :)

Anyway, the more you use radians, though, the better.

BTW, as for optimizing sin&cos: They actually aren't too bad. From Bob's exext.c code I came up with the Taylor Series(?) and this:
sin(x) = x - (x^3)/3! + (x^5)/5! - (x^7)/7! ... (x^b)/b!
where b is some odd number depending on your accuracy. Bob only went up to 7 and multipled the input by .98 to compensate for the inaccuracy. For calculator accuracy(10 digits) you need to go up to ~15, but 4 and the "fudging" should be accurate enough, so really the calculation is only: ~10 operations + the shift to the [-pi, pi] period. It really isn't as bad as you would think, especially sine trig functions are actually implemented on the CPU as a single OPCode(i.e. there is likely some more optimizations that can be done on the bit level). The problem really comes from the usage of floats(unavoidable, and yet floats are becoming faster as well) and calling the function excessively.

eDIT: Whoopsie

Bruce Perry
Member #270
April 2000

I remember this subject being discussed somewhere else, and someone said it gets a lot more cunning than that. I fully believe that there are other series that converge much more quickly than the above. In any case, it is done in hardware for you on the PC, so any attempt to use your own algorithm is likely to be slower.

--
Bruce "entheh" Perry [ Web site | DUMB | Set Up Us The Bomb !!! | Balls ]
Programming should be fun. That's why I hate C and C++.
The brxybrytl has you.

Oscar Giner
Member #2,207
April 2002
avatar

Not really ten operation.

For

sin(x) = x - (x^3)/3! + (x^5)/5! - (x^7)/7!

you need to write

sin(x) = x- (x*x*x*)/(3*2) + (x*x*x*x*x)/(5*4*3*2) + (x*x*x*x*x*x*x)/(7*6*5*4*3*2)

The good way is to calculate this in a loop, so you can reuse previous results.

Karadoc ~~
Member #2,749
September 2002
avatar

Quote:

i.e.
x - pi/2
is harder to calculate in our heads than
x - 180

I think you might have meant x - pi.
pi/2 = 90 degrees.

I prefer radians. They make sense. Degrees are just radians multiplied by a unitless constant. A waste of time.

The trick is to not work with decimal values when dealing with radians. pi can't be expressed as a decimal number anyway. Instead, just leave pi in all your numbers.
eg.
pi/2
pi/6
pi/12

-----------

kikabo
Member #3,679
July 2003
avatar

WH: :o doh!, I might of known it would simplify more than that, should have googled before posting, smacks head. If only I had google and an education in 1983!.
I'll bet there's a (a^2 - b^2) = (a + b)(a - b) simplification in the proof somewhere ... never trusted that since I learned that you can prove that a = -a with it (but that's a bit off topic for this thread I suppose ;) )

orz
Member #565
August 2000

You could, if you really wanted too, "optimize" the sin/cos table using simmetry - a single period of the sine wave has several symmetries to it:

//assuming 0 <= x < 256 (360 degrees == 256 arbitrary angle units)
if (!(x & ~63)) return sin_table[x];
if (!(x & ~127)) return sin_table[x^127];
if (!(x & 64)) return -sin_table[x&63];
return -sin_table[x^255];

In that way, the size of the sin+cos table could be cut down to 20% of its normal size. But those kinds of branches might be expensive on modern computers; I doubt it would be worth it for the average program. You can try various other optimizations if you want:

//assuming 0 <= x < 256 (360 degrees == 256 arbitrary angle units)
int a = (x << 24) >> 31;
int b = ((x << 25) >> 31) & 127;
x &= 127;
return (sin_table[x^b] ^ a) - a;

But I expect that the way that Allegro currently does it is about as good as it's possible to get for the average program.

(I probably screwed up one or both of the examples above...)

Bruce Perry
Member #270
April 2000

Quote:

never trusted that since I learned that you can prove that a = -a with it

The proof is invalid. To the best of my recollection, it uses a step like the following:

0*2 = 0*3
therefore 2 = 3

You are only proving that a = -a after having established that a = 0. It's something along those lines anyway. If you post the proof I'll comment specifically on how it's invalid.

--
Bruce "entheh" Perry [ Web site | DUMB | Set Up Us The Bomb !!! | Balls ]
Programming should be fun. That's why I hate C and C++.
The brxybrytl has you.

Evert
Member #794
November 2000
avatar

William Heatly said:

you do realize i'm talking about the human mind, right? and not a machine?

You do realize that I said I never use degrees?

Quote:

It's harder for our minds to do decimal compared to integers, unless you've studied decimal math in your head extensively.

rational numbers are not that much harder to deal with than integers. Real numbers is a different matter, because you cannot express them as a rational fraction. No big deal as long as you write sqrt(2) instead of 1.414..., pi instead of 3.1415... etc.
Besides, who ever said degrees are nescessarily integers?

Quote:

but I was talking about why we use degrees so much, cause we can do the calcs in our heads a lot.

As I said, I never use degrees. Bear in mind though - I do calculations that are outside the scope of game programming (calculating volume elements or areas). For these you need to express angles in radians because that is the natural way to express angles.

As a quick example, the circumference of a unit circle is the integral of 1 over all possible angles. The answer, of course, is 2pi. But if you naively integrate over 360 degrees instead, the answer would be 360 (yes, I know how the transformation should be done and that if you do it correctly you get 2pi as well - that's why I said naively).

Billybob
Member #3,136
January 2003

Quote:

do calculations that are outside the scope of game programming (calculating volume elements or areas).

Ahh yes, area calculations are much easier with radians, sure. But I'm pretty sure that for most of your childhood you used degrees, cause they really don't teach radians until pre-calc. Afterwards, yes, it is quite possible that you adapted to radians just fine and use them. :)

kikabo
Member #3,679
July 2003
avatar

Bruce, yep something like that

1// start off with a=b
2 
3a=b
4 
5// multiply by a
6 
7a^2=ab
8 
9// subtract b^2
10 
11a^2 - b^2 = ab - b^2
12 
13// expand a^2-b^2
14 
15(a+b)(a-b) = ab - b^2
16 
17// divide by (a-b)
18 
19a+b = ( ab - b^2 )/(a-b)
20 
21// subtract b
22 
23a = ( ab - b^2 )/(a-b) - b
24 
25// since a=b then
26 
27a = ( a^2 - a^2 )/(a-a) -a
28 
29a = 0/0 -a
30 
31a = -a

so when debugging, it's cause all you variables are negative! ::)

Marcello
Member #1,860
January 2002
avatar

Um, 0/0 isn't defined, is it?

Marcello

kikabo
Member #3,679
July 2003
avatar

could be infinity, could be 0, no wait I've just proved it's 2a !.. I'll stop deliberatly posting rubbish now and only do it by accident, stupidity or ignorance
---
if (posts_per_day<0.01) {worth_sigging=FALSE; }

Thomas Fjellstrom
Member #476
June 2000
avatar

It is defined. Its defined to throw an exception. (SIGFPE)

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

 1   2   3   4 


Go to: