sine wave

closed account (zwA4jE8b)
I have the following code that draws a sine wave in my window...
1
2
3
4
5
6
7
8
9
10
void wavefunc(HWND hWnd, HDC hDC)
{
	static double _x;
	int _y;
	_y = sin(_x/_freq)*_amp + 300;
	SetPixel(hDC, 600, _y, blue);
	_x += 1;
	ScrollWindow(hWnd, -1, 0, NULL, NULL);
	Sleep(_sTime);
}


the problem is that _x just keeps increasing. I want to reset _x after one full period.

Can anyone tell me what my if() statement needs to look like to test for one period and reset _x to 0.

Thank you,
Mike

or suggest a better equation.

p.s. _freq and _amp get modified by keypresses, so the user can change the wave
the + 300 centers the value in the window.
Last edited on
On period is 2*pi radians

since you have sin(_x/_freq) that will repeat at:
_x / _freq = 2 * pi
... which is ...
_x = 2 * pi * _freq

Therefore:

1
2
3
double full = 2 * pi * _freq;
if(_x >= full)
  x -= full;
closed account (zwA4jE8b)
Awesome, thank you!
closed account (zwA4jE8b)
That works great!

for anyone that is interested here is my code for a windows program that draws a sine wave and takes user input to edit the amplitude and frequency, as well as speed.

It is not great but I am a novice at windows programming so there.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
#include <Windows.h>
#include <math.h>

#define screen_width 800
#define screen_height 600
#define pi 3.14159

COLORREF blue = RGB(0, 0, 255);

double _freq = 50;
int _sTime = 5, _amp = 100;

LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
void wavefunc(HWND, HDC);

int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine,
                   int nCmdShow)
{
	HWND hWnd;
	WNDCLASSEX wc;
	HDC hDC;

	ZeroMemory(&wc, sizeof(WNDCLASSEX));

	wc.cbSize = sizeof(WNDCLASSEX);
	wc.style = CS_HREDRAW | CS_VREDRAW;
	wc.lpfnWndProc = WindowProc;
	wc.hInstance = hInstance;
	wc.hCursor = LoadCursor(NULL, IDC_ARROW);
	wc.hIcon = (HICON)LoadImage(NULL, "C:\\Visual Studio 2010\\Icons\\Smiley.ico", IMAGE_ICON, 0, 0, LR_LOADFROMFILE);
	wc.lpszClassName = "SineWave";

	RegisterClassEx(&wc);

	hWnd = CreateWindowEx(NULL,
                          "SineWave", "Wave",
                          WS_SYSMENU,
                          0, 45,
                          screen_width, screen_height,
                          NULL, NULL,
                          hInstance,
                          NULL);

	ShowWindow(hWnd, nCmdShow);
	hDC = GetDC(hWnd);

	MSG msg;

	while(TRUE)
	{
		while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
		{DispatchMessage(&msg);}

		if(msg.message == WM_QUIT)
			break;
		
		wavefunc(hWnd, hDC);
	}

	return msg.wParam;
}

LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch(message)
	{
		case WM_DESTROY:
		{
			PostQuitMessage(0);
			return 0;
		} break;
		case WM_KEYDOWN:
		{
			switch (wParam)
			{
			case VK_NUMPAD4:
				_freq--;
				if(_freq < 1.0)
					_freq = 1;
				break;
			case VK_NUMPAD6:
				_freq++;
				break;
			case VK_NUMPAD8:
				_amp++;
				if(_amp > 200)
					_amp = 200;
				break;
			case VK_NUMPAD2:
				_amp--;
				if(_amp < 0)
					_amp = 0;
				break;
			case VK_SUBTRACT:
				_sTime++;
				if(_sTime > 50)
					_sTime = 50;
				break;
			case VK_ADD:
				_sTime--;
				if(_sTime < 0)
					_sTime = 0;
				break;
			}
			return 0;
		} break;
     }
     return DefWindowProc(hWnd, message, wParam, lParam);
 }

void wavefunc(HWND hWnd, HDC hDC)
{
	double full = 2 * pi * _freq;
	static double _x = 0;
	int _y = (sin(_x/_freq)*_amp) + 300;
	if (_x >= full)
		_x -= full;
	SetPixel(hDC, 600, _y, blue);
	ScrollWindow(hWnd, -1, 0, NULL, NULL);
	Sleep(_sTime);
	_x++;
}
Last edited on
I just compiled it in VS2008. It's fun! The way you decided to control the wave's amplitude, frequency and time (speed, really) is quite nice.

Room for improvements:

1. Whenever you set a high amplitude the curve just breaks into isolated pixels. It would be good exercise to find the solution for that one still using SetPixel(). Although a practical animation would just use Polyline() or PolylineTo(). SetPixel() tends to be much slower than these.
2. Show the 3 values that command the sine wave on screen (amplitude, frequency, speed). It would be nice to see them changing as one presses the keys.

3. Make a child window that acts as a color picker.

4. Add an up/down control that controls the width of the stroke (of course, this would require you to discontinue the use of SetPixel()).
closed account (zwA4jE8b)
Thank you.

I do want to make it a smooth curve. This is my first attempt at this stuff and SetPixel() was the easiest function I could do.

#2 I was thinking about showing the values of the variables. I know of the TextOut() function. Im guessing I need to use stringstream to convert the values from numbers to text though.

What do I need to include to use PolyLine()?
Last edited on
closed account (zwA4jE8b)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
void wavefunc(HWND hWnd, HDC hDC)
{
	std::string _amplitude, _frequency, _speed;
	double full = 2 * pi * _freq;
	static double _x = 0;
	int _y = (sin(_x/_freq)*_amp) + 300;
	if (_x >= full)
		_x -= full;

	_amplitude = "Amplitude : " + convstr(_amp * 2) + " pixels";
	_frequency = "Frequency : " + convstr(2 * pi * _freq) + " pixels";
	_speed = "Speed : " + convstr(_sTime);

	SetPixel(hDC, 600, _y, blue);
	ScrollWindow(hWnd, -1, 0, NULL, NULL);
	TextOut(hDC, 0, 0, _amplitude.c_str(), 22);
	TextOut(hDC, 0, 17, _frequency.c_str(), 22);
	TextOut(hDC, 0, 34, _speed.c_str(), 10);
	_x++;
	Sleep(_sTime);
}

std::string convstr(const int& t)
{
	std::stringstream itoa;
	itoa << t;
	return itoa.str();
}
Cool. Better. But drawing the values on screen every time wavefunc() is called is overkill and causes flickering in the text. After all, why redraw the values if they haven't changed? Split that functionality outside of wavefunc(). Only redraw the values when they change.

To make a smoother curve, draw lines instead of setting pixels. Simulate the curve using a series of points.

Since your current approach is based in "one pixel at a time", the simplest is to convert to "one tiny line at a time". Just remember the last point you calculate, use MoveToEx() (http://msdn.microsoft.com/en-us/library/dd145069(VS.85).aspx) to set the starting line there, then calculate the next point and call LineTo() (http://msdn.microsoft.com/en-us/library/dd145029(VS.85).aspx).
Topic archived. No new replies allowed.