sine wave drawing functionality

closed account (zwA4jE8b)
I am drawing a sine wave in a window and the problem is that when the frequency, and sometimes amplitude, change, the graph gets distorted. I am asking if there is a way to draw this wave so that when the frequency changes, the wave does not get distorted?

Now that I am typing this I am inclined to think that only allowing the shape to change when a full period has been reached or using maybe a while loop to gradualy fluctuate the wave is the correct way. Which raises the problem of creating a buffer for the changes made while the while loop is running.

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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
#include <Windows.h>
#include <math.h>
#include <string>
#include <sstream>

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

struct _points
{
	int _x;
	int _y;
};

struct _ycoords
{
	_points _one;
	_points _two;
} _yc;

COLORREF blue = RGB(0, 0, 255);
HDC hDC;
HWND hWnd;
std::string _amplitude, _frequency, _speed;

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

LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
void calcwave();
void drawwave();
std::string convstr(const int& t);

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

	ZeroMemory(&wc, sizeof(WNDCLASSEX));
	ZeroMemory(&_yc, sizeof(_yc));
	_yc._one._x = 600;
	_yc._two._x = 599;
	_yc._two._y = 300;

	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", "SineWave",
                          WS_SYSMENU,
                          0, 45,
                          screen_width, screen_height,
                          NULL, NULL,
                          hInstance,
                          NULL);

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

	HPEN _Pen = CreatePen(PS_SOLID, 1, blue);
	SelectObject(hDC, _Pen);

	MSG msg;

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

		if(msg.message == WM_QUIT)
			break;

		calcwave();
	}
	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.0;
				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 < 10)
					_sTime = 10;
				break;
			case 0x48:
				MessageBox(NULL, "NUMPAD + : Increase speed\n"
					 "NUMPAD - : Decrease speed\n"
					 "NUMPAD 8 : Increase amplitude\n"
					 "NUMPAD 2 : Decrease amplitude\n"
					 "NUMPAD 4 : Increase frequency\n"
					 "NUMPAD 6 : Decrease frequency\n"
					 "\nChanges are small, hold the button",
					 "Help", MB_OK);
				break;
			}
			return 0;
		} break;
     }
     return DefWindowProc(hWnd, message, wParam, lParam);
 }

void calcwave()
{
	double _full = 2 * pi * _freq;
	static double _x = 0;
	
	_yc._one._y = (sin(_x/_freq)*_amp) + 300;

	_x++;
	if (_x >= _full)
		_x -= _full;

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

	drawwave();
	TextOut(hDC, 0, 0, _amplitude.c_str(), _amplitude.size());
	TextOut(hDC, 0, 17, _frequency.c_str(), _frequency.size());
	TextOut(hDC, 0, 34, _speed.c_str(), _speed.size());

	_yc._two._y = _yc._one._y;
}

void drawwave()
{
	MoveToEx(hDC, _yc._two._x, _yc._two._y, NULL);
	LineTo(hDC, _yc._one._x, _yc._one._y);
	ScrollWindow(hWnd, -1, 0, NULL, NULL);
	Sleep(_sTime);
}

std::string convstr(const int& t)
{
	std::stringstream itoa;
	itoa << t;
	return itoa.str();
}


I am sure to some people this has an easy solution but I have not yet taken physics (that is next semester) but I am trying to learn more about waves

static double _x = 0; is named as such because originally this was the x value of the position, before I used scroll window. Perhaps now there is a better name for it. but my x value is now constant and I am just scrolling the window, so maybe x is still appropriate.
Last edited on
closed account (zwA4jE8b)
actually the amplitude is very smooth. its just the frequency adjustments that distort the graph
You're not being unicode friendly >=o

Anyway, as for your problem....

1
2
3
4
5
6
7
8
	double _full = 2 * pi * _freq;
	static double _x = 0;

	_yc._one._y = (sin(_x/_freq)*_amp) + 300;

	_x++;
	if (_x >= _full)
		_x -= _full;


Changing the freq changes where in the current wave you are. For example, if x==1, and freq==10... that would be a different y coord on the graph than x==1 and freq==11. And that's exactly what you're seeing. Whenever you change freq, the y coord has to "jump" to the new position, causing that distortion.


The better way to do this is to keep the freq constant (2*pi) and just increase x at different rates.

example:

1
2
3
4
5
6
7
8
    static const double _full = 2 * pi;  // keep the period constant
	static double _x = 0;
	
	_yc._one._y = (sin(_x)*_amp) + 300;  // don't / _freq here

	_x += 1.0 / _freq;  // instead, increment by / _freq
	if (_x >= _full)
		_x -= _full;


This way your current position doesn't jump around when the freq changes.
Last edited on
closed account (zwA4jE8b)
Thank you very much. That is beautiful. The wave keeps its shape and flows nicely.

I do not remember if it was your article or someone elses but I just read it yesterday and am going to try to make it unicode friendly. Any tips? I am very new to windows programming.

Code for anyone who is interested.
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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
#include <Windows.h>
#include <math.h>
#include <string>
#include <sstream>

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

struct _points
{
	int _x;
	int _y;
};

struct _ycoords
{
	_points _one;
	_points _two;
} _yc;

COLORREF blue = RGB(0, 0, 255);
HDC hDC;
HWND hWnd;
std::string _amplitude, _frequency, _speed;

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

LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
void calcwave();
void drawwave();
std::string convstr(const int& t);

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

	ZeroMemory(&wc, sizeof(WNDCLASSEX));
	ZeroMemory(&_yc, sizeof(_yc));
	_yc._one._x = 600;
	_yc._two._x = 599;
	_yc._two._y = 300;

	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", "SineWave",
                          WS_SYSMENU,
                          0, 45,
                          screen_width, screen_height,
                          NULL, NULL,
                          hInstance,
                          NULL);

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

	HPEN _Pen = CreatePen(PS_SOLID, 1, blue);
	SelectObject(hDC, _Pen);

	MSG msg;

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

		if(msg.message == WM_QUIT)
			break;

		calcwave();
	}
	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.0;
				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 < 10)
					_sTime = 10;
				break;
			case 0x48:
				MessageBox(NULL, "NUMPAD + : Increase speed\n"
					 "NUMPAD - : Decrease speed\n"
					 "NUMPAD 8 : Increase amplitude\n"
					 "NUMPAD 2 : Decrease amplitude\n"
					 "NUMPAD 4 : Increase frequency\n"
					 "NUMPAD 6 : Decrease frequency\n"
					 "\nChanges are small, hold the button",
					 "Help", MB_OK);
				break;
			}
			return 0;
		} break;
     }
     return DefWindowProc(hWnd, message, wParam, lParam);
 }

void calcwave()
{
	static const double _full = 2 * pi;
	static double _x = 0;
	
	_yc._one._y = (sin(_x)*_amp) + 300;

	_x += 1.0 / _freq;
	if (_x >= _full)
		_x -= _full;

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

	drawwave();
	TextOut(hDC, 0, 0, _amplitude.c_str(), _amplitude.size());
	TextOut(hDC, 0, 17, _frequency.c_str(), _frequency.size());
	TextOut(hDC, 0, 34, _speed.c_str(), _speed.size());

	_yc._two._y = _yc._one._y;
}

void drawwave()
{
	MoveToEx(hDC, _yc._two._x, _yc._two._y, NULL);
	LineTo(hDC, _yc._one._x, _yc._one._y);
	ScrollWindow(hWnd, -1, 0, NULL, NULL);
	Sleep(_sTime);
}

std::string convstr(const int& t)
{
	std::stringstream itoa;
	itoa << t;
	return itoa.str();
}
Well it doesn't matter so much for this kind of program, it's just a pet peeve of mine.

The thing is I got compiler errors when I tried to compile it because you're passing char strings to TCHAR WinAPI functions/structs:

MessageBox, TextOut, LoadImage, CreateWindowEx, WNDCLASSEX, RegisterClassEx

All of those should end in 'A' if you're passing a char string (ie: MessageBoxA)

Otherwise they should all take TCHARs and not chars.
closed account (zwA4jE8b)
I changed them to us 'A'

How do I make this a TCHAR*? _amplitude = "Amplitude : " + convstr(_amp * 2) + " pixels";

I looked at your article and see the defines. Does my convstr() function need to return a tchar?
To use TCHARs:

1) string literals go in _T() or TEXT() macros
2) Don't use string or stringstream, those both work with chars. You can typedef TCHAR versions:

1
2
typedef std::basic_string<TCHAR>         tstring;
typedef std::basic_stringstream<TCHAR>   tstringstream;



Although for this particular project, I don't really recommend going this route. You don't need TCHARs here, I would just stick with the A versions... it's fine.

Really the important thing is just that you shouldn't mix-and-match chars with TCHAR functions.
Last edited on
closed account (zwA4jE8b)
Well then I will stick with the 'A'.
What type of windows programs will require me to be unicode friendly?


My first dive into window programming looked like this. I just thought it would be fun. I learned a little bit about the 'A' and 'W' and TCHAR but only enough to make my program work.

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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
//Michael Ervin - Windows HWND and processID manipulator
//Must include Psapi.lib

#include <Windows.h>
#include <Psapi.h>
#include <iostream>
#include <vector>
#include <string>
using namespace std;

void enumproc(DWORD&);
void enumhwnd();
void gCnHwnd(vector<wstring>&);
void ghCn(vector<wstring>&);
void showhwnd();
void showcn(vector<wstring>&);
void showd(DWORD);
void setmsg(UINT&, wstring&);
void sendmsgA(UINT, wstring);
void sendmsgS(HWND, UINT, wstring);
void killproc(DWORD);
BOOL CALLBACK EnumChildProc(HWND hwnd, LPARAM lParam);

DWORD _dA[1024];
vector<HWND> _hV;

int main()
{
	vector<wstring> _cV;
	DWORD numproc = NULL;
	UINT _msg = NULL;
	wstring _Lparam;
	int _ch;

	do
	{
		std::cout << "main menu...\n" <<
			"1) enumerate processes\n" <<
			"2) enumerate handles\n" <<
			"3) get class name from handle (gCnHwnd)\n" <<
			"4) get handle from class name (ghCn)\n" <<
			"5) show proc ID\n" <<
			"6) show handles\n" <<
			"7) show class names\n" <<
			"8) set message to send\n" <<
			"9) send messages (all hwnd) (sendmsgA)\n"<<
			"10) send message (single) (sendmsgS)\n" <<
			"11) kill process\n" <<
			"89) reset all\n" <<
			"99) exit\n";
		cin >> _ch;
		switch (_ch)
		{
		case 1:
			enumproc(numproc);
			break;
		case 2:
			enumhwnd();
			break;
		case 3:
			if (!_hV.empty())
				gCnHwnd(_cV);
			else
				std::cout << "handle vector is empty.\n";
			break;
		case 4:
			if (!_cV.empty())
				ghCn(_cV);
			else
				std::cout << "class vector is empty.\n";
			break;
		case 5:
			if (_dA[1] != NULL)
				showd(numproc);
			else
				std::cout << "proc ID array is empty.\n";
			break;
		case 6:
			if (!_hV.empty())
				showhwnd();
			else
				std::cout << "handle vector is empty.\n";
			break;
		case 7:
			if (!_cV.empty())
				showcn(_cV);
			else
				std::cout << "class vector is empty.\n";
			break;
		case 8:
			setmsg(_msg, _Lparam);
			break;
		case 9:
			if (!_hV.empty() && _msg != NULL)
				sendmsgA(_msg, _Lparam);
			else
				std::cout << "handle vector is empty.\n";
			break;
		case 10:
			std::cout << "enter the class name (#)...";
			cin >> _ch;
			setmsg(_msg, _Lparam);
			sendmsgS(_hV[_ch], _msg, _Lparam);
			break;
		case 11:
			std::cout << "enter the process (#)...";
			cin >> _ch;
			killproc(_dA[_ch]);
			break;
		case 89:
			_hV.clear();
			_cV.clear();
			_msg = NULL;
			_Lparam = L"";
			for (int i = 0; i < 1024; i++)
				_dA[i] = NULL;
			std::cout << "RESET...\n";
			break;
		case 99:
			return 0;
			break;
		}
		std::cout <<"\n\n";
	}
	while (_ch != NULL);
	return 0;
}

void enumproc(DWORD& numproc)
{
	DWORD cbneeded;
	EnumProcesses(_dA, sizeof(_dA), &cbneeded);
	numproc = cbneeded / sizeof(DWORD);
}

void enumhwnd()
{
	HWND hwnd;
	hwnd = GetDesktopWindow();
	std::cout << hwnd << endl;
	EnumChildWindows(hwnd, EnumChildProc, 0);
}

void gCnHwnd(vector<wstring>& _cV)
{
	TCHAR _tchWndText[128];
	for (int i = 0; i < _hV.size(); i++)
		if (GetClassNameW(_hV[i], _tchWndText, 128) != NULL)
			_cV.push_back(_tchWndText);
}

void ghCn(vector<wstring>& _cV)
{
	HWND hwnd;
	wstring _tempW;
	for (int i = 0; i < _cV.size(); i++)
		if ((hwnd = FindWindowW(_cV[i].c_str(), 0)) != NULL)
			_hV.push_back(hwnd);
}

void showd(DWORD numproc)
{
	std::cout << "\nCurrently stored proc ID's...\n";
	for (unsigned i = 0; i < numproc; i++)
		std::cout << i << ") " << _dA[i] << endl;
	std::cout << "\n\n";
}

void showhwnd()
{
	std::cout << "\nCurrently stored handles...\n";
	for (int i = 0; i < _hV.size(); i++)
		std::cout << _hV[i] << endl;
	std::cout << "\n\n";
}

void showcn(vector<wstring>& _cV)
{
	std::cout << "\nCurrently stored class names...\n";
	for (int i = 0; i < _cV.size(); i++)
		std::wcout << i << ") " << _cV[i] << endl;
	std::cout << "\n\n";
}

void setmsg(UINT& _msg, wstring& _Lparam)
{
	char ch;
	std::cout << "\nMessages:\n" <<
			"1) WM_CLOSE\n" <<
			"2) WM_SETTEXT\n";
	cin >> ch;
	switch (ch)
	{
	case '1':
		_msg = 0x0010;
		_Lparam = L"";
		break;
	case '2':
		_msg = 0x000C;
		std::cout << "enter the new text...\n";
		wcin >> _Lparam;
		cin.get();
		break;
	}
	std::cout << "\n\n";
}

void sendmsgA(UINT _msg, wstring _Lparam)
{
	for (int i = 0; i < _hV.size(); i++)
		::SendMessageW(_hV[i], _msg, 0, (LPARAM)_Lparam.c_str());
}

void sendmsgS(HWND hwnd, UINT _msg, wstring _Lparam)
{::SendMessageW(hwnd, _msg, 0, (LPARAM)_Lparam.c_str());}

void killproc(DWORD _procID)
{
	HANDLE _handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, _procID);
	if (_handle != NULL)
		::TerminateProcess(_handle, 0);
}

//function from http://www.dreamincode.net/forums/topic/70823-enumchildwindows/ - edited
BOOL CALLBACK EnumChildProc(HWND hwnd, LPARAM lParam) 
{_hV.push_back(hwnd);	return 1;}
Really interesting. I started work on a similar system yesterday and then found your code. I see that was written two weeks ago. Have you made important changes or additions since then???
I am using Dev-C++ and API.
Would be interested to see what more you have done.
Cheers
Paul
closed account (zwA4jE8b)
no, that is all I have. I have not worked on it in a while.
I just thought you might find this interesting. its a frequency analyzer program full with source code. thought you might think it would be fun to thumb through.

http://www.relisoft.com/freeware/freq.html

all my best.
closed account (zwA4jE8b)
thanks, that is really cool
Topic archived. No new replies allowed.