Animating a sine wave infinitely - c

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;
}

Related

Calculate sine curve in C

I want to get a periodic value that moves between 0 and a specified height (in my case that's 40) from the sine curve.
But I am messing something up, because my value goes all the way to 79 instead of the expected 40. What am I doing wrong?
This is my attempt:
#include <math.h>
#define degToRad(angleInDegrees) ((angleInDegrees)*M_PI / 180.0)
int main()
{
int height = 40;
int i = 0;
while (1) {
int value = height + sin(degToRad(i / 2 + 1)) * height;
printf("val = %i\n", value);
i++;
}
return 0;
}
A direct resolution is to divide the wave magnitude by 2 #Eric Postpischil
// int value = height + sin(degToRad(i / 2 + 1)) * height;
int value = height + sin(degToRad(i / 2 + 1)) * height)/2;
and use floating point math in the i/2 division. #bruno
I expect a more acceptable result using rounding rather than truncation (what OP's code does) going from floating point to int.
int value = height + lround(sin(degToRad(i / 2 + 1)) * height)/2);
The amplitude of the curve would then be height / 2 and not height; simply replace
int value = height + sin(degToRad(i / 2 + 1)) * height;
with
int value = height / 2 + sin(degToRad(i / 2 + 1)) * height / 2;
A good way to remember that is that sin x is always in the range [-1, 1].

Function to rotate bitmap in degrees works pretty fast, but repositions output image

I've built a good function for rotating bitmaps in degrees.
The problem that occurred after some optimizations is that the output image is getting slightly re-positioned during the rotation.
I've recorded a 360o rotating video for you to see what do I mean.
And the code as follows:
#define OFFSET_OF_ID (0x0)
#define OFFSET_OF_SIZE (0x2)
#define OFFSET_OF_PIXELS (0xA)
#define OFFSET_OF_NDIB (0xE)
#define OFFSET_OF_WIDTH (0x12)
#define OFFSET_OF_HEIGHT (0x16)
#define OFFSET_OF_BPP (0x1C)
#define OFFSET_OF_NRAW (0x22)
typedef unsigned char byte, pixel[3];
typedef unsigned short word;
typedef unsigned long dword;
typedef unsigned long long ddword;
byte*
bmp_rotate
(byte *buffer, float angle)
{
const dword src_width = *( (dword*)&buffer[OFFSET_OF_WIDTH]);
const dword src_height = *( (dword*)&buffer[OFFSET_OF_HEIGHT]);
const dword src_nraw = *( (dword*)&buffer[OFFSET_OF_NRAW]);
const dword src_pixels = *( (dword*)&buffer[OFFSET_OF_PIXELS]);
const dword src_bpp = *( (dword*)&buffer[OFFSET_OF_BPP]);
const dword single = src_bpp / 8;
const dword row = src_width * single;
dword rowsize = (row % 4) ? (row + 4 - row % 4) : (row);
byte *dest = calloc( src_pixels + src_nraw, sizeof(byte) );
double midX, midY;
int i, j;
double sin_angle = sin(angle);
double cos_angle = cos(angle);
midX = src_width / 2.0f;
midY = src_height / 2.0f;
memcpy(dest, buffer, src_pixels);
for(j = 0; j < src_height; j++)
{
dword dest_offset = src_pixels + j * rowsize;
double deltaY = j - midY;
double deltaX = 0 - midX;
double x_computation, y_computation;
x_computation = midX + deltaX * cos_angle + deltaY * sin_angle + 0.5f;
y_computation = midY - deltaX * sin_angle + deltaY * cos_angle + 0.5f;
for(i = 0; i < src_width; i++)
{
ddword rotX = x_computation;
ddword rotY = y_computation;
if(rotX >= 0 && rotX < src_width && rotY >= 0 && rotY < src_height)
{
ddword src_offset = src_pixels + rotY * rowsize + rotX * single;
memcpy(&dest[dest_offset], &buffer[src_offset], sizeof(pixel));
}
x_computation += cos_angle;
y_computation -= sin_angle;
dest_offset += single;
}
}
return dest;
}
What causes this indecent behavior?
I can't really see your problem in the video, but I assume that the full 360° rotation is achieved in various partial rotations.
If we look at a 180° rotation, the offset becomes clear: The sine is 0 and the cosine is −1. The rotated coordinates of the top left corner (0,0) is then:
x' = xm + (x - xm) * cosa + (y - ym) * sina = xm + xm = w
y' = ym - (x - xm) * sina + (y - ym) * cosa = ym + ym = h
The coordinates (w, h) are the exclusive right and bottom borders.
Your working coordinates are real numbers. The integer values describe the left and top coordinates. Converting the (positive) real coords to integers will truncate the fractional part and yield the zero-based pixel indices.
You add 0.5 to your working coordinates in the target space once to enforce proper index calculation. Instead, you should treat the real coordinates of all pixels as the middle of that pixel:
x(i) = i + 0.5
y(j) = j + 0.5
All your calculations remain correct, except:
double deltaY = j + 0.5 - midY;
double deltaX = 0 + 0.5 - midX;
x_computation = midX + deltaX * cos_angle + deltaY * sin_angle;
y_computation = midY - deltaX * sin_angle + deltaY * cos_angle;
The idea is to treat pixel coordinates as pixel centres in both source and target space. The correct representation in target space is achieved automatically by rounding towards zero when convering to integer.
It may be a rounding-related problem. I have also faced this kind of problem in my projects in the past. Please check the lines where "integer to other types" or "other types to integer" conversions occur.

C: Improving performance of function with heavy sin() usage

I have a C function that computes the values of 4 sines based on time elapsed. Using gprof, I figured that this function uses 100% (100.7% to be exact lol) of the CPU time.
void
update_sines(void)
{
clock_gettime(CLOCK_MONOTONIC, &spec);
s = spec.tv_sec;
ms = spec.tv_nsec * 0.0000001;
etime = concatenate((long)s, ms);
int k;
for (k = 0; k < 799; ++k)
{
double A1 = 145 * sin((RAND1 * k + etime) * 0.00333) + RAND5; // Amplitude
double A2 = 100 * sin((RAND2 * k + etime) * 0.00333) + RAND4; // Amplitude
double A3 = 168 * sin((RAND3 * k + etime) * 0.00333) + RAND3; // Amplitude
double A4 = 136 * sin((RAND4 * k + etime) * 0.00333) + RAND2; // Amplitude
double B1 = 3 + RAND1 + (sin((RAND5 * k) * etime) * 0.00216); // Period
double B2 = 3 + RAND2 + (sin((RAND4 * k) * etime) * 0.002); // Period
double B3 = 3 + RAND3 + (sin((RAND3 * k) * etime) * 0.00245); // Period
double B4 = 3 + RAND4 + (sin((RAND2 * k) * etime) * 0.002); // Period
double x = k; // Current x
double C1 = 0.6 * etime; // X axis move
double C2 = 0.9 * etime; // X axis move
double C3 = 1.2 * etime; // X axis move
double C4 = 0.8 * etime + 200; // X axis move
double D1 = RAND1 + sin(RAND1 * x * 0.00166) * 4; // Y axis move
double D2 = RAND2 + sin(RAND2 * x * 0.002) * 4; // Y axis move
double D3 = RAND3 + cos(RAND3 * x * 0.0025) * 4; // Y axis move
double D4 = RAND4 + sin(RAND4 * x * 0.002) * 4; // Y axis move
sine1[k] = A1 * sin((B1 * x + C1) * 0.0025) + D1;
sine2[k] = A2 * sin((B2 * x + C2) * 0.00333) + D2 + 100;
sine3[k] = A3 * cos((B3 * x + C3) * 0.002) + D3 + 50;
sine4[k] = A4 * sin((B4 * x + C4) * 0.00333) + D4 + 100;
}
}
And this is the output from gprof:
Flat profile:
Each sample counts as 0.01 seconds.
% cumulative self self total
time seconds seconds calls Ts/call Ts/call name
100.07 0.04 0.04
I'm currently getting a frame rate of roughly 30-31 fps using this. Now I figure there as to be a more efficient way to do this.
As you noticed I already changed all the divisions to multiplications but that had very little effect on performance.
How could I increase the performance of this math heavy function?
Besides all the other advice given in other answers, here is a pure algorithmic optimization.
In most cases, you're computing something of the form sin(k * a + b), where a and b are constants, and k is a loop variable. If you were also to compute cos(k * a + b), then you could use a 2D rotation matrix to form a recurrence relationship (in matrix form):
|cos(k*a + b)| = |cos(a) -sin(a)| * |cos((k-1)*a + b)|
|sin(k*a + b)| |sin(a) cos(a)| |sin((k-1)*a + b)|
In other words, you can calculate the value for the current iteration in terms of the value from the previous iteration. Thus, you only need to to do the full trig calculation for k == 0, but the rest can be calculated via this recurrence (once you have calculated cos(a) and sin(a), which are constants). So you eliminate 75% of the trig function calls (it's not clear the same trick can be pulled for the final set of trig calls).
If you don't need all that precision, create a lookup for the sin() values you need, so if 1 degree is enough, use double sin_lookup[360], etc.. And possibly float sin_lookup[360] if float precision is sufficient.
Also, as noted in comments, at a certain point as per Keith, "You might also consider using linear interpolation between lookup values, which should give you substantially better accuracy (a reasonably continuous function rather than a step function) at a fairly small cost in performance"
EDIT: also consider changing the hardcoded A1,A2,A3,A4 pattern to arrays of size[4], and looping from 0 to 3 - should allow vectorization on many platforms and allow parrellism without needing to manage threads
EDIT2: some code and results
(Coded in C++ just to make comparisons easy between precisions, calcs are the same in C)
class simple_trig
{
public:
simple_trig(size_t prec) : precision(prec)
{
static const double PI=3.141592653589793;
const double dprec=(double)prec;
const double quotient=(2.0*PI)/dprec;
rev_quotient=dprec/(2.0*PI);
values.reserve(prec);
for (int i=0; i < precision; ++i)
{
values[i]=::sin(quotient*(double)i);
}
}
double sin(double x) const
{
double cvt=x*rev_quotient;
int index=(int)cvt;
double delta=cvt-(double)index;
int lookup1=index%precision;
int lookup2=(index+1)%precision;
return values[lookup1]*(1.0-delta)+values[lookup2]*delta;
}
double cos(double x) const
{
double cvt=x*rev_quotient;
int index=(int)cvt;
double delta=cvt-(double)index;
int lookup1=(index+precision/4)%precision;
int lookup2=(index+precision/4+1)%precision;
return values[lookup1]*(1.0-delta)+values[lookup2]*delta;
}
private:
const size_t precision;
double rev_quotient;
std::vector<double> values;
};
Examples Low is 100, Med is 1000 and High is 10,000
X=0 Sin=0 Sin Low=0 Sin Med=0 Sin High=0
X=0 Cos=1 Cos Low=1 Cos Med=1 Cos High=1
X=0.5 Sin=0.479426 Sin Low=0.479389 Sin Med=0.479423 Sin High=0.479426
X=0.5 Cos=0.877583 Cos Low=0.877512 Cos Med=0.877578 Cos High=0.877583
X=1.33333 Sin=0.971938 Sin Low=0.971607 Sin Med=0.971935 Sin High=0.971938
X=1.33333 Cos=0.235238 Cos Low=0.235162 Cos Med=0.235237 Cos High=0.235238
X=2.25 Sin=0.778073 Sin Low=0.777834 Sin Med=0.778072 Sin High=0.778073
X=2.25 Cos=-0.628174 Cos Low=-0.627986 Cos Med=-0.628173 Cos High=-0.628174
X=3.2 Sin=-0.0583741 Sin Low=-0.0583689 Sin Med=-0.0583739 Sin High=-0.0583741
X=3.2 Cos=-0.998295 Cos Low=-0.998166 Cos Med=-0.998291 Cos High=-0.998295
X=4.16667 Sin=-0.854753 Sin Low=-0.854387 Sin Med=-0.854751 Sin High=-0.854753
X=4.16667 Cos=-0.519036 Cos Low=-0.518818 Cos Med=-0.519034 Cos High=-0.519036
X=5.14286 Sin=-0.90877 Sin Low=-0.908542 Sin Med=-0.908766 Sin High=-0.90877
X=5.14286 Cos=0.417296 Cos Low=0.417195 Cos Med=0.417294 Cos High=0.417296
X=6.125 Sin=-0.157526 Sin Low=-0.157449 Sin Med=-0.157526 Sin High=-0.157526
X=6.125 Cos=0.987515 Cos Low=0.987028 Cos Med=0.987512 Cos High=0.987515
X=7.11111 Sin=0.73653 Sin Low=0.736316 Sin Med=0.736527 Sin High=0.73653
X=7.11111 Cos=0.676405 Cos Low=0.676213 Cos Med=0.676403 Cos High=0.676405
X=8.1 Sin=0.96989 Sin Low=0.969741 Sin Med=0.969887 Sin High=0.96989
X=8.1 Cos=-0.243544 Cos Low=-0.24351 Cos Med=-0.243544 Cos High=-0.243544
X=9.09091 Sin=0.327701 Sin Low=0.327558 Sin Med=0.3277 Sin High=0.327701
X=9.09091 Cos=-0.944782 Cos Low=-0.944381 Cos Med=-0.944779 Cos High=-0.944782
X=10.0833 Sin=-0.611975 Sin Low=-0.611673 Sin Med=-0.611973 Sin High=-0.611975
X=10.0833 Cos=-0.790877 Cos Low=-0.790488 Cos Med=-0.790875 Cos High=-0.790877
It seems to me that sine1, sine2, sine3 and sine4 arrays are completely independent from eachother. So you are basically running a single for loop for 4 different arrays which have no dependency.
Spawn 4 threads, 1 for each, so you have 4 for loops running at the same time. On multicore machine this should speed up your function dramatically. As a matter of fact, it should be a perfect 4x speedup (+- ...).
Actually combining the use of threads (consider this with OpenMP) and the use of a table for the sin is a good idea. If possible use float instead of double and, depending on the platform, you could also use simd instructions, but the later would make the use of threads unnecessary.
Cheers
Here is a C++ snippet to use the rotation matrix suggested in the accepted answer.
float a = 0.343;
float b = 2.3232;
float sina{};
float cosa{};
sincosf(a, &sina, &cosa);
float resSin{};
float resCos{};
for (int k = 0; k < 5; k++) {
if (k == 0) {
sincosf(b, &resSin, &resCos);
} else {
float newResCos, newResSin;
newResCos = cosa * resCos - sina * resSin;
newResSin = sina * resCos + cosa * resSin;
resCos = newResCos;
resSin = newResSin;
}
}

Numerical Evaluation of Pi

I would like to evaluate Pi approximately by running the following code which fits a regular polygon of n sides inside a circle with unit diameter and calculates its perimeter using the function in the code. However the output after the 34th term is 0 when long double variable type is used or it increases without bounds when double variable type is used. How can I remedy this situation? Any suggestion or help is appreciated and welcome.
Thanks
P.S: Operating system: Ubuntu 12.04 LTS 32-bit, Compiler: GCC 4.6.3
#include <stdio.h>
#include <math.h>
#include <limits.h>
#include <stdlib.h>
#define increment 0.25
int main()
{
int i = 0, k = 0, n[6] = {3, 6, 12, 24, 48, 96};
double per[61] = {0}, per2[6] = {0};
// Since the above algorithm is recursive we need to specify the perimeter for n = 3;
per[3] = 0.5 * 3 * sqrtl(3);
for(i = 3; i <= 60; i++)
{
per[i + 1] = powl(2, i) * sqrtl(2 * (1.0 - sqrtl(1.0 - (per[i] / powl(2, i)) * (per[i] / powl(2, i)))));
printf("%d %f \n", i, per[i]);
}
return 0;
for(k = 0; k < 6; k++)
{
//p[k] = k
}
}
Some ideas:
Use y = (1.0 - x)*( 1.0 + x) instead of y = 1.0 - x*x. This helps with 1 stage of "subtraction of nearly equal values", but I am still stuck on the next 1.0 - sqrtl(y) as y approaches 1.0.
// per[i + 1] = powl(2, i) * sqrtl(2 * (1.0 - sqrtl(1.0 - (per[i] / powl(2, i)) * (per[i] / powl(2, i)))));
long double p = powl(2, i);
// per[i + 1] = p * sqrtl(2 * (1.0 - sqrtl(1.0 - (per[i] / p) * (per[i] / p))));
long double x = per[i] / p;
// per[i + 1] = p * sqrtl(2 * (1.0 - sqrtl(1.0 - x * x)));
// per[i + 1] = p * sqrtl(2 * (1.0 - sqrtl((1.0 - x)*(1.0 + x)) ));
long double y = (1.0 - x)*( 1.0 + x);
per[i + 1] = p * sqrtl(2 * (1.0 - sqrtl(y) ));
Change array size or for()
double per[61+1] = { 0 }; // Add 1 here
...
for (i = 3; i <= 60; i++) {
...
per[i + 1] =
Following is a similar method for pi
unsigned n = 6;
double sine = 0.5;
double cosine = sqrt(0.75);
double pi = n*sine;
static const double mpi = 3.1415926535897932384626433832795;
do {
sine = sqrt((1 - cosine)/2);
cosine = sqrt((1 + cosine)/2);
n *= 2;
pi = n*sine;
printf("%6u s:%.17e c:%.17e pi:%.17e %%:%.6e\n", n, sine, cosine, pi, (pi-mpi)/mpi);
} while (n <500000);
Subtracting 1.0 from a nearly-1.0 number is leading to "catastrophic cancellation", where the relative error in a FP calculation skyrockets due to the loss of significant digits. Try evaluating pow(2, i) - (pow(2, i) - 1.0) for each i between 0 and 60 and you'll see what I mean.
The only real solution to this issue is reorganizing your equations to avoid subtracting nearly-equal nonzero quantities. For more details, see Acton, Real Computing Made Real, or Higham, Accuracy and Stability of Numerical Algorithms.

How to get the 0's of digits when iterating through int's in C

I am trying to iterate through some number as 02100021, this is a routing number which needs to get validated if it is a proper routing number. Therefore I am using the ABA routing number validation check as 3*(d1+d4+d7) + 7*(d2+d5+d8) + (d3+d6+d9) where mod 10 = 0
Since I have the number in integer type, my first question is how can I iterate through such number with 0's in it, or if there is some easier way of iterating through mode and multiplying it with such number.
Thanks
If I understand correctly, 7 * (d2, d5, d8) should be 7 * (d2 + d5 + d8).
Zeros modulo 10 do not count.
bool correct(long x) {
long a = x / 10 + x / 10000 + x / 10000000L;
long b = x / 100 + x / 100000L + x / 100000000L;
long c = x / 1000 + x / 1000000L + x / 1000000000L;
int aba_checksum = (int)((3 * a + 7 * b + c) % 10);
return aba_checksum == 0;
}
Corrected for d[i] = digit * 10 ^ (9 - i)
bool correct(long x) {
long a = x / 100000000L + x / 100000L + x / 100;
long b = x / 10000000L + x / 10000L + x / 10;
long c = x / 1000000L + x / 1000L + x;
int aba_checksum = (int)((3 * a + 7 * b + c) % 10);
return aba_checksum == 0;
}
Modulo arithmetic can be done at the end.
To prevent overflow (to negative numbers) one might do it earlier, above even 7 * b does not overflow.

Resources