-
Notifications
You must be signed in to change notification settings - Fork 1
/
main.cpp
290 lines (234 loc) · 9.01 KB
/
main.cpp
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
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
#include <windows.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/freeglut.h>
#include <cmath>
#include <stdio.h>
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#include <iostream>
#pragma comment(lib, "opengl32.lib")
#pragma comment(lib, "glu32.lib")
#pragma comment(lib, "freeglut.lib")
const float width = 10.0f; // The width of the image.
const float halfWidth = width / 2.0f; // Half the width
// Scale factor for positions (1 piksel = 1 pikometer)
const float SCALE = std::pow(10.0f, -12.0f);
// Charges are given in nano-Coulombs
float q1 = 1.0f; // nano-Coulombs
float q2 = -1.0f; // nano-Coulombs
// Positions are given in meters and scaled to pixels
// Initial positions are in the middle of the screen
float pos1 = 5; // pixels = pikometer
float pos2 = -5; // pixels = pikometer
// Coulomb constant (N*m^2/C^2)
const float k = 8.9875517923f * std::pow(10.0f, 9);
// Timestep (seconds)
float dt = 0.001f;
// Initial speeds are given in m/s and scaled to pixels/second
float speed1 = 0.01f * SCALE; // pixels/second
float speed2 = 0.01f * SCALE; // pixels/second
// Masses of particles are identical to an electron (kg)
float mass1 = 9.10938356f * std::pow(10.0f, -31); // kg
float mass2 = 9.10938356f * std::pow(10.0f, -31); // kg
// Friction coefficient
float friction = 1.0f; // N * dt / m
float force1;
float force2;
float force1exact;
float force2exact;
void updateForces() {
float r = std::abs(pos2 - pos1) / SCALE + 2 * halfWidth ;
float forceMagnitude = k * std::abs(q1 * q2) / (r * r);
float direction = (q1 * q2) < 0 ? -1.0f : 1.0f;
// Convert force to pikometer by multiplying by SCALE
force1 = direction * forceMagnitude * SCALE / mass1;
force2 = -direction * forceMagnitude * SCALE / mass2;
}
void updateVelocities() {
// Friction force is proportional to the velocity and in the opposite direction
float frictionForce1 = friction * std::abs(speed1) * -std::copysign(1.0f, speed1);
float frictionForce2 = friction * std::abs(speed2) * -std::copysign(1.0f, speed2);
speed1 += (force1 + frictionForce1) * dt;
speed2 += (force2 + frictionForce2) * dt;
pos1 += speed1 * dt;
pos2 += speed2 * dt;
}
GLuint texture[2];
//Function to load an image using the STB library
GLuint LoadTexture(const char* filename, int wrap)
{
int width, height, comp;
unsigned char* image = stbi_load(filename, &width, &height, &comp, STBI_rgb_alpha);
if (image == nullptr)
{
return 0;
}
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap ? GL_REPEAT : GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap ? GL_REPEAT : GL_CLAMP);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image);
stbi_image_free(image);
return texture;
}
//Function to draw images
void DrawImage(GLuint texture, float x, float y, float width, float height)
{
glBindTexture(GL_TEXTURE_2D, texture);
glEnable(GL_TEXTURE_2D);
glBegin(GL_QUADS);
glTexCoord2f(0, 0);
glVertex2f(x - width / 2, y - height / 2);
glTexCoord2f(1, 0);
glVertex2f(x + width / 2, y - height / 2);
glTexCoord2f(1, 1);
glVertex2f(x + width / 2, y + height / 2);
glTexCoord2f(0, 1);
glVertex2f(x - width / 2, y + height / 2);
glEnd();
glDisable(GL_TEXTURE_2D);
}
//Function to drawText
void drawText(const char* text, float x, float y) {
// Save current color before drawing text
float currentColor[4];
glGetFloatv(GL_CURRENT_COLOR, currentColor);
glColor3f(0.0f, 1.0f, 0.0f); // Set text color green
glRasterPos2f(x, y);
int len = strlen(text);
for (int i = 0; i < len; i++) {
glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18, text[i]);
}
// Restore the drawing color after rendering the text
glColor4fv(currentColor);
}
LRESULT CALLBACK WindowProcedure(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_CLOSE:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) {
const wchar_t CLASS_NAME[] = L"Sample Window Class";
WNDCLASS wc = { };
wc.lpfnWndProc = WindowProcedure;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
if (!RegisterClass(&wc)) {
MessageBox(NULL, L"Window Registration Failed!", L"Error!", MB_ICONEXCLAMATION | MB_OK);
return 0;
}
HWND hwnd = CreateWindow(CLASS_NAME, L"Sample Window", WS_MINIMIZEBOX | WS_SYSMENU, CW_USEDEFAULT, CW_USEDEFAULT, 800, 600, NULL, NULL, hInstance, NULL);
if (hwnd == NULL) {
MessageBox(NULL, L"Window Creation Failed!", L"Error!", MB_ICONEXCLAMATION | MB_OK);
return 0;
}
ShowWindow(hwnd, nShowCmd);
// Set up OpenGL context
HDC hdc = GetDC(hwnd);
PIXELFORMATDESCRIPTOR pfd;
ZeroMemory(&pfd, sizeof(pfd));
pfd.nSize = sizeof(pfd);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 24;
int iFormat = ChoosePixelFormat(hdc, &pfd);
SetPixelFormat(hdc, iFormat, &pfd);
HGLRC hglrc = wglCreateContext(hdc);
wglMakeCurrent(hdc, hglrc);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
int argc = 0; // GLUT requires the command line argument count...
char* argv[] = { NULL }; // ...and array, but they don't need to actually have any data
glutInit(&argc, argv);
// Set up the orthographic projection
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-100.0f, 100.0f, -100.0f, 100.0f, -1.0f, 1.0f);
glMatrixMode(GL_MODELVIEW);
MSG msg = { 0 };
bool running = true;
// High-resolution timer setup
LARGE_INTEGER frequency;
QueryPerformanceFrequency(&frequency);
double freq = static_cast<double>(frequency.QuadPart);
LARGE_INTEGER startCounter, endCounter;
QueryPerformanceCounter(&startCounter);
//Declare textures
if (q1 >= 0) {
texture[0] = LoadTexture("images\\electron.png", 1);
}
else {
texture[0] = LoadTexture("images\\proton.png", 1);
}
if (q2 >= 0) {
texture[1] = LoadTexture("images\\electron.png", 1);
}
else {
texture[1] = LoadTexture("images\\proton.png", 1);
}
GLuint background = LoadTexture("images\\space.png", 1);
while (running) {
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT) {
running = false;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
QueryPerformanceCounter(&endCounter);
double frameTime = (endCounter.QuadPart - startCounter.QuadPart) / freq;
if (frameTime > 1.0 / 120.0) { // limit to 60 FPS
// Update forces
updateForces();
updateVelocities();
// Clear the window
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// In the main loop where you draw the images, adjust the position by half the width
DrawImage(background, 0.0f, 0.0f, 800, 600); // assuming 800x600 window size
DrawImage(texture[0], pos1 + ((q1 > 0) ? halfWidth : -halfWidth), 0.0f, width, width);
DrawImage(texture[1], pos2 + ((q2 > 0) ? halfWidth : -halfWidth), 0.0f, width, width);
//Draw the text
char text[1024];
const char* formats[] = {
"Charge Q1: %.2f nC",
"Charge Q2: %.2f nC",
"Speed Q1: %.2e pm/s",
"Speed Q2: %.2e pm/s",
"Position Q1: %.2e pm",
"Position Q2: %.2e pm",
"Friction: %.2f N * dt / m",
"Distance between Q1 and Q2: %.4e pm",
"Force Applied to Q1: %.2e N/m",
"Force Applied to Q2: %.2e N/m"
};
float values[] = {
q1, q2, std::abs(speed1 / SCALE * dt), std::abs(speed2 / SCALE * dt), pos1 / SCALE, pos2 / SCALE, friction, abs(pos1 - pos2) / SCALE, force1 * SCALE, force1 * SCALE, force2 * SCALE
};
// Draw each line of text
for (int lineNumber = 0; lineNumber < 10; lineNumber++) {
char text[1024];
sprintf_s(text, sizeof(text), formats[lineNumber], values[lineNumber]);
// Render the text, setting the y position as a function of lineNumber
drawText(text, -100.0f, 95.0f - 8.0f * lineNumber);
}
// Swap buffers
SwapBuffers(hdc);
startCounter = endCounter;
}
}
wglMakeCurrent(NULL, NULL);
wglDeleteContext(hglrc);
ReleaseDC(hwnd, hdc);
return msg.wParam;
}