├── LICENSE ├── LorenzAttractor.cpp ├── LorenzAttractor.h ├── README.md └── vectormath.h /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Orfeas Liossatos 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LorenzAttractor.cpp: -------------------------------------------------------------------------------- 1 | #include "LorenzAttractor.h" 2 | 3 | LorenzAttractor::LorenzAttractor() 4 | { 5 | // Text 6 | font.loadFromFile("FUTRFW.ttf"); 7 | text.setFont(font); 8 | text.setString(names[u]); 9 | text.setScale(0.1f, 0.1f); 10 | text.setPosition(sf::Vector2f(-85, -45)); 11 | 12 | // Re-center view 13 | view.setCenter(sf::Vector2f(0.0f, 0.0f)); 14 | view.setSize(sf::Vector2f(static_cast(g_screenWidth / 10), static_cast(g_screenHeight / 10))); 15 | 16 | // Set parameters 17 | params = 18 | { 19 | {10.0f, 30.0f, 8 / 3}, 20 | {1.24f, 1.1f, 4.4f, 3.21f}, 21 | {0.95f, 0.7f, 0.6f, 3.5f, 0.25f, 0.1f}, 22 | {0.3f, 1.0f}, 23 | {5.0f, -10.0f, -0.38f}, 24 | {1.4f}, 25 | {0.001f, 0.2f, 1.1f}, 26 | {0.4f, 0.175f}, 27 | {1.5f}, 28 | {0.2f} 29 | }; 30 | 31 | colours = 32 | { 33 | sf::Color::Color(115, 62, 101, 42), // 42 34 | sf::Color::Color(255, 126, 210, 127), 35 | sf::Color::Color(109, 193, 202, 179), 36 | sf::Color::Color(135, 216, 10, 203), 37 | sf::Color::Color(125, 26, 133, 99), 38 | sf::Color::Color(8, 161, 163, 57), 39 | sf::Color::Color(134, 184, 38, 183), 40 | sf::Color::Color(132, 159, 149, 188), 41 | sf::Color::Color(100, 195, 167, 160), 42 | sf::Color::Color(190, 242, 126, 252), 43 | }; 44 | 45 | trail_colours_params = 46 | { 47 | {61.3f, 62.1f, 33.9f}, 48 | {-65.5125f, -27.8521f, -97.0907f}, 49 | {-47.3382f, -49.5409f, -33.8347f}, 50 | {-12.9346f, -76.7609f, 70.7356f}, 51 | {-12.0673f, 51.6949f, 97.4566f}, 52 | {93.4094f, -16.196f, 40.8949f}, 53 | {-37.5288f, -31.3912f, -48.0061f}, 54 | {-5.6245f, -49.0192f, 19.271f}, 55 | {98.011f, -81.8857f, -80.084f}, 56 | {44.3435f, -93.5679f, -52.0752f}, 57 | 58 | }; 59 | std::cout << "\n\n\n"; 60 | for (unsigned c = 0; c < colours.size(); c++) 61 | { 62 | std::cout << static_cast(colours[c].r) << " " << 63 | static_cast(colours[c].g) << " " << 64 | static_cast(colours[c].b) << " " << 65 | static_cast(colours[c].a) << std::endl; 66 | std::cout << trail_colours_params[c][0] << " " << trail_colours_params[c][1] << " " << trail_colours_params[c][2] << std::endl; 67 | 68 | } 69 | 70 | circle.resize(num_points); 71 | point.resize(num_points); 72 | trail.resize(num_points); 73 | 74 | 75 | 76 | // Create trial trackers 77 | for (unsigned i = 0; i < num_points; i++) 78 | j.push_back(0); 79 | 80 | for (unsigned i = 0; i < num_points; i++) 81 | { 82 | // Create balls 83 | circle[i].setRadius(0.5f); 84 | circle[i].setOrigin(circle[i].getRadius(), circle[i].getRadius()); 85 | circle[i].setFillColor(colours[u]); 86 | 87 | // Set initial positions 88 | point[i] = { getRandomNumber(1.0f, 1.0f), getRandomNumber(1.0f, 1.0f), getRandomNumber(1.0f, 1.0f) }; 89 | 90 | // Create trails 91 | trail[i].resize(trail_length); 92 | for (auto &pos : trail[i]) 93 | pos = point[i]; 94 | 95 | } 96 | 97 | // Prepare colours 98 | 99 | 100 | // Create lineStrip object with two vertices. The first line should start with a first point. 101 | line.setPrimitiveType(sf::LinesStrip); 102 | line.append(sf::Vector2f(point[0].x, point[0].y)); 103 | line.append(sf::Vector2f(point[0].x, point[0].y)); 104 | 105 | // Set Camera 106 | switch (u) 107 | { 108 | case 0: {cam_position = { 0, 0, -50 }; cam_angle = { 0, 0, 0 }; break; } 109 | case 1: {cam_position = { 1.07676f, 0.3f, -0.447995f }; cam_angle = { 0.1f, 4.84f, 0.0f }; break; } 110 | case 2: {cam_position = { 2.25, 0, 0.75 }; cam_angle = { 0, -pi / 2, 0 }; break; } 111 | case 3: {cam_position = { -7.5, 5, -15 }; cam_angle = { 0, pi / 6, 0 }; break; } 112 | case 4: {cam_position = { 51.310f, -4.8f, 25.151f }; cam_angle = { -0.16f, -2.1f, 0.0f }; break; } 113 | case 5: {cam_position = { -23.357f, -16.4f, -20.731f }; cam_angle = { -0.5f, -5.48f, 0.0f }; break; } 114 | case 6: {cam_position = { 1.0216f, -5.7f, 6.1861f }; cam_angle = { -0.16f, 3.34f, 0.0f }; break; } 115 | case 7: {cam_position = { 0, 0, -0.5 }; cam_angle = { 0, 0, -0.134f }; break; } 116 | case 8: {cam_position = { -0.14397f, -8.4f, -1.3497f }; cam_angle = { -1.66f, -2.94f, 0 }; break; } 117 | case 9: {cam_position = { 7.1565f, 4.2f, 2.6844f }; cam_angle = { 0.48f, -1.92f, 0 }; break; } 118 | } 119 | } 120 | 121 | void LorenzAttractor::input(sf::RenderWindow &window) 122 | { 123 | while (window.pollEvent(event)) 124 | { 125 | // Window 126 | if (sf::Keyboard::isKeyPressed(sf::Keyboard::Escape)) 127 | endSubProgram = true; 128 | if (sf::Keyboard::isKeyPressed(sf::Keyboard::F)) 129 | { 130 | isFullscreen = !isFullscreen; 131 | window.create(sf::VideoMode(static_cast(g_screenWidth), static_cast(g_screenHeight)), "Coding Projects", (isFullscreen ? sf::Style::Fullscreen : sf::Style::Default), sf::ContextSettings()); 132 | window.setPosition(sf::Vector2i(0, 0)); 133 | window.setVerticalSyncEnabled(true); 134 | window.setFramerateLimit(60); 135 | } 136 | if ((sf::Keyboard::isKeyPressed(sf::Keyboard::H) || sf::Keyboard::isKeyPressed(sf::Keyboard::G)) && input_timer >= 0.1) 137 | { 138 | input_timer = 0; 139 | 140 | if (sf::Keyboard::isKeyPressed(sf::Keyboard::H)) 141 | { 142 | u++; 143 | if (u == params.size()) 144 | u--; 145 | } 146 | else 147 | { 148 | u--; 149 | if (u == -1) 150 | u++; 151 | } 152 | 153 | switch (u) 154 | { 155 | case 0: {cam_position = { 0, 0, -50 }; cam_angle = { 0, 0, 0 }; break; } 156 | case 1: {cam_position = { 1.07676f, 0.3f, -0.447995f }; cam_angle = { 0.1f, 4.84f, 0.0f }; break; } 157 | case 2: {cam_position = { 2.25, 0, 0.75 }; cam_angle = { 0, -pi / 2, 0 }; break; } 158 | case 3: {cam_position = { -7.5, 5, -15 }; cam_angle = { 0, pi / 6, 0 }; break; } 159 | case 4: {cam_position = { 51.310f, -4.8f, 25.151f }; cam_angle = { -0.16f, -2.1f, 0 }; break; } 160 | case 5: {cam_position = { -23.357f, -16.4f, -20.731f }; cam_angle = { -0.5f, -5.48f, 0.0f }; break; } 161 | case 6: {cam_position = { 1.0216f, -5.7f, 6.1861f }; cam_angle = { -0.16f, 3.34f, 0.0f }; break; } 162 | case 7: {cam_position = { 0, 0, -0.5 }; cam_angle = { 0, 0, 0 }; break; } 163 | case 8: {cam_position = { -0.1439f, -8.4f, -1.3497f }; cam_angle = { -1.66f, -2.94f, 0 }; break; } 164 | case 9: {cam_position = { 7.1565f, 4.2f, 2.6844f }; cam_angle = { 0.48f, -1.92f, 0 }; break; } 165 | } 166 | 167 | for (unsigned i = 0; i < num_points; i++) 168 | { 169 | circle[i].setFillColor(colours[u]); 170 | point[i] = { getRandomNumber(-0.001f, 0.001f), getRandomNumber(-0.001f, 0.001f), getRandomNumber(-0.001f, 0.001f) }; 171 | } 172 | for (unsigned i = 0; i < num_points; i++) 173 | { 174 | for (auto &pos : trail[i]) 175 | pos = point[i]; 176 | } 177 | 178 | text.setString(names[u]); 179 | } 180 | } 181 | } 182 | 183 | void LorenzAttractor::update() 184 | { 185 | /// Calculate timestep 186 | timestep = clock.getElapsedTime().asSeconds(); 187 | input_timer += timestep; 188 | clock.restart(); 189 | 190 | timestep *= speed; // Slow down or speed up time. 191 | 192 | // Update position according to chosen equation u 193 | std::vector &m = params[u]; 194 | switch (u) 195 | { 196 | case 0: 197 | { 198 | for (unsigned i = 0; i < num_points; i++) 199 | { 200 | point[i].x += static_cast(m[0] * (point[i].y - point[i].x) * timestep); 201 | point[i].y += static_cast((point[i].x * (m[1] - point[i].z) - point[i].y) * timestep); 202 | point[i].z += static_cast((point[i].x * point[i].y - m[2] * point[i].z) * timestep); 203 | } 204 | break; 205 | } 206 | case 1: 207 | { 208 | for (unsigned i = 0; i < num_points; i++) 209 | { 210 | float h1 = 0.5f * (abs(point[i].x + 1) - abs(point[i].x - 1)); 211 | float h2 = 0.5f * (abs(point[i].y + 1) - abs(point[i].y - 1)); 212 | float h3 = 0.5f * (abs(point[i].z + 1) - abs(point[i].z - 1)); 213 | 214 | point[i].x += static_cast((-point[i].x + m[0] * h1 - m[3] * h2 - m[3] * h3) * timestep); 215 | point[i].y += static_cast((-point[i].y - m[3] * h1 + m[1] * h2 - m[2] * h3) * timestep); 216 | point[i].z += static_cast((-point[i].z - m[3] * h1 + m[2] * h2 + h3) * timestep); 217 | } 218 | break; 219 | } 220 | case 2: 221 | { 222 | for (unsigned i = 0; i < num_points; i++) 223 | { 224 | point[i].x += static_cast(((point[i].z - m[1]) * point[i].x - m[3] * point[i].y) * timestep); 225 | point[i].y += static_cast((m[3] * point[i].x + (point[i].z - m[1]) * point[i].y) * timestep); 226 | point[i].z += static_cast((m[2] + m[0] * point[i].z - (point[i].z * point[i].z * point[i].z) / 3 - (point[i].x * point[i].x + point[i].y * point[i].y) * (1 + m[4] * point[i].z) + m[5] * point[i].z * point[i].x * point[i].x * point[i].x) * timestep); 227 | } 228 | break; 229 | } 230 | case 3: 231 | { 232 | for (unsigned i = 0; i < num_points; i++) 233 | { 234 | point[i].x += static_cast((point[i].x * (4 - point[i].y) + m[0] * point[i].z) * timestep); 235 | point[i].y += static_cast((-point[i].y * (1 - point[i].x * point[i].x)) * timestep); 236 | point[i].z += static_cast((-point[i].x * (1.5 - point[i].z * m[1]) - 0.05 * point[i].z) * timestep); 237 | } 238 | break; 239 | } 240 | case 4: 241 | { 242 | for (unsigned i = 0; i < num_points; i++) 243 | { 244 | point[i].x += static_cast((m[0] * point[i].x - point[i].y * point[i].z) * timestep * 0.25f); 245 | point[i].y += static_cast((m[1] * point[i].y + point[i].x * point[i].z) * timestep * 0.25f); 246 | point[i].z += static_cast((m[2] * point[i].z + point[i].x * point[i].y / 3) * timestep * 0.25f); 247 | } 248 | break; 249 | } 250 | case 5: 251 | { 252 | for (unsigned i = 0; i < num_points; i++) 253 | { 254 | point[i].x += static_cast((-m[0] * point[i].x - 4 * point[i].y - 4 * point[i].z - point[i].y * point[i].y) * timestep); 255 | point[i].y += static_cast((-m[0] * point[i].y - 4 * point[i].z - 4 * point[i].x - point[i].z * point[i].z) * timestep); 256 | point[i].z += static_cast((-m[0] * point[i].z - 4 * point[i].x - 4 * point[i].y - point[i].x * point[i].x) * timestep); 257 | } 258 | break; 259 | } 260 | case 6: 261 | { 262 | for (unsigned i = 0; i < num_points; i++) 263 | { 264 | point[i].x += static_cast(((1 / m[1] - m[0]) * point[i].x + point[i].z + point[i].x * point[i].y) * timestep); 265 | point[i].y += static_cast((-m[1] * point[i].y - point[i].x * point[i].x) * timestep); 266 | point[i].z += static_cast((-point[i].x - m[2] * point[i].z) * timestep); 267 | } 268 | break; 269 | } 270 | case 7: 271 | { 272 | for (unsigned i = 0; i < num_points; i++) 273 | { 274 | point[i].x += static_cast((-m[0] * point[i].x + point[i].y + 10.0f * point[i].y * point[i].z) * timestep); 275 | point[i].y += static_cast((-point[i].x - 0.4 * point[i].y + 5.0f * point[i].x * point[i].z) * timestep); 276 | point[i].z += static_cast((m[1] * point[i].z - 5.0f * point[i].x * point[i].y) * timestep); 277 | } 278 | break; 279 | } 280 | case 8: 281 | { 282 | for (unsigned i = 0; i < num_points; i++) 283 | { 284 | point[i].x += static_cast((point[i].y) * timestep); 285 | point[i].y += static_cast((-point[i].x + point[i].y * point[i].z) * timestep); 286 | point[i].z += static_cast((m[0] - point[i].y * point[i].y) * timestep); 287 | } 288 | break; 289 | } 290 | case 9: 291 | { 292 | for (unsigned i = 0; i < num_points; i++) 293 | { 294 | point[i].x += static_cast((-m[0] * point[i].x + sin(point[i].y)) * timestep); 295 | point[i].y += static_cast((-m[0] * point[i].y + sin(point[i].z)) * timestep); 296 | point[i].z += static_cast((-m[0] * point[i].z + sin(point[i].x)) * timestep); 297 | } 298 | break; 299 | } 300 | } 301 | 302 | /// Update Camera Position 303 | 304 | // Move Left and Right 305 | if (sf::Keyboard::isKeyPressed(sf::Keyboard::A)) 306 | { 307 | cam_position.x -= sin(cam_angle[1] + pi / 2.0f) * 0.25f; 308 | cam_position.z -= cos(cam_angle[1] + pi / 2.0f) * 0.25f; 309 | } 310 | if (sf::Keyboard::isKeyPressed(sf::Keyboard::D)) 311 | { 312 | cam_position.x += sin(cam_angle[1] + pi / 2.0f) * 0.25f; 313 | cam_position.z += cos(cam_angle[1] + pi / 2.0f) * 0.25f; 314 | } 315 | 316 | // Move Forwards and Backwards 317 | if (sf::Keyboard::isKeyPressed(sf::Keyboard::S)) 318 | { 319 | cam_position.z -= cos(cam_angle[1]) * 0.25f; 320 | cam_position.x -= sin(cam_angle[1]) * 0.25f; 321 | } 322 | if (sf::Keyboard::isKeyPressed(sf::Keyboard::W)) 323 | { 324 | cam_position.z += cos(cam_angle[1]) * 0.25f; 325 | cam_position.x += sin(cam_angle[1]) * 0.25f; 326 | } 327 | 328 | // Move Up and Down 329 | if (sf::Keyboard::isKeyPressed(sf::Keyboard::LShift)) 330 | cam_position.y += 0.1f; 331 | if (sf::Keyboard::isKeyPressed(sf::Keyboard::Space)) 332 | cam_position.y -= 0.1f; 333 | 334 | /// Update Camera Angle 335 | 336 | // Look Left and Right 337 | if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) 338 | cam_angle[1] -= 0.003f; 339 | if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) 340 | cam_angle[1] += 0.003f; 341 | 342 | 343 | // Look Up and Down 344 | if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up)) 345 | cam_angle[0] += 0.003f; 346 | if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down)) 347 | cam_angle[0] -= 0.003f; 348 | 349 | /// Compute Rotation Matrixes 350 | 351 | rotMatrixX = 352 | { 353 | {1, 0, 0}, 354 | {0, cos(cam_angle[0]), sin(cam_angle[0])}, 355 | {0, -sin(cam_angle[0]), cos(cam_angle[0])} 356 | }; 357 | rotMatrixY = 358 | { 359 | {cos(cam_angle[1]), 0, -sin(cam_angle[1])}, 360 | {0, 1, 0}, 361 | {sin(cam_angle[1]), 0, cos(cam_angle[1])} 362 | }; 363 | rotMatrixZ = 364 | { 365 | {cos(cam_angle[2]), sin(cam_angle[2]), 0}, 366 | {-sin(cam_angle[2]), cos(cam_angle[2]), 0}, 367 | {0, 0, 1} 368 | }; 369 | 370 | 371 | // Change speed 372 | if (sf::Keyboard::isKeyPressed(sf::Keyboard::PageUp)) 373 | speed += 0.025f; 374 | if (sf::Keyboard::isKeyPressed(sf::Keyboard::PageDown)) 375 | speed -= 0.025f; 376 | } 377 | 378 | void LorenzAttractor::draw(sf::RenderWindow &window) 379 | { 380 | // For every point 381 | for (unsigned g = 0; g < num_points; g++) 382 | { 383 | /// Draw circle 384 | 385 | // Projection maths 386 | sf::Vector3f d; 387 | d = point[g] - cam_position; 388 | d = rotMatrixX * (rotMatrixY * (rotMatrixZ * d)); 389 | 390 | // Only if the point is infront of the camera 391 | if (d.z >= 0) 392 | { 393 | projected_point = 394 | { 395 | display_position.z * d.x / d.z + display_position.x, 396 | display_position.z * d.y / d.z + display_position.y 397 | }; 398 | 399 | circle[g].setPosition(sf::Vector2f(projected_point.x, projected_point.y)); 400 | window.draw(circle[g]); 401 | } 402 | 403 | /// Draw trail 404 | 405 | /* 406 | The point of this algorithm is to use indexes in a clever manner in order to avoid shifting every 407 | previous position one across in "trail" every frame, which takes a lot of time. It flows like a 408 | "shifting" >for< loop. 409 | */ 410 | 411 | trail[g][j[g]] = point[g]; // Add most recent position to trail at index j[g] 412 | j[g]++; // j[g] incremented every frame, shifting the beginning of the for loop. 413 | if (j[g] == trail[g].size() - 1) // If j[g] gets to the final index, set it to 0 414 | { 415 | j[g] = 0; 416 | trail[g][trail[g].size() - 1] = trail[g][trail[g].size() - 2]; // This has to be done. 417 | } 418 | 419 | int k = 0; // Index that goes from 0 to num_points, used for colour 420 | 421 | // Starting from the index i right after j[g], then from 0 all the way to j[g]. 422 | int i = j[g] + 1; 423 | while (i != j[g]) 424 | { 425 | 426 | // Project the front end of the trail 427 | sf::Vector3f d1; 428 | d1 = trail[g][i] - cam_position; 429 | d1 = rotMatrixX * (rotMatrixY * (rotMatrixZ * d1)); 430 | 431 | sf::Vector2f proj1; 432 | proj1 = { display_position.z * d1.x / d1.z + display_position.x, 433 | display_position.z * d1.y / d1.z + display_position.y }; 434 | 435 | line[0].position = proj1; 436 | 437 | // Project the back end of the trail 438 | sf::Vector3f d2; 439 | if (i == 0) 440 | d2 = trail[g][trail[g].size() - 2] - cam_position; 441 | else 442 | d2 = trail[g][i - 1] - cam_position; 443 | d2 = rotMatrixX * (rotMatrixY * (rotMatrixZ * d2)); 444 | 445 | sf::Vector2f proj2; 446 | proj2 = { display_position.z * d2.x / d2.z + display_position.x, 447 | display_position.z * d2.y / d2.z + display_position.y }; 448 | 449 | line[1].position = proj2; 450 | 451 | // Calculate trail colours 452 | sf::Color fade; 453 | fade = sf::Color::Color( 454 | clamp(colours[u].r + trail_colours_params[u][0] * Magnitude(line[1].position - line[0].position)), 455 | clamp(colours[u].g + trail_colours_params[u][1] * Magnitude(line[1].position - line[0].position)), 456 | clamp(colours[u].b + trail_colours_params[u][2] * Magnitude(line[1].position - line[0].position)), 457 | 0 + static_cast((k * 255 / trail[g].size()))); 458 | 459 | line[0].color = fade; 460 | line[1].color = fade; 461 | 462 | // Draw if both the front and end of the trail are infront of the camera 463 | if (d1.z >= 0 && d2.z >= 0) 464 | window.draw(line); 465 | 466 | // Increment counters 467 | i++; 468 | if (i == trail[g].size()) // Set i to 0 once it gets to the end of "trail" 469 | i = 0; 470 | 471 | k++; 472 | } 473 | } 474 | 475 | // Text 476 | text.setString(names[u]); 477 | text.setScale(0.1f, 0.1f); 478 | text.setPosition(sf::Vector2f(-85, -45)); 479 | window.draw(text); 480 | 481 | /// UNCOMMENT FOR ON-SCREEN COORDINATES 482 | /* 483 | for (unsigned i = 0; i < num_points; i++) 484 | { 485 | std::string coordinate_string = "(" + std::to_string(static_cast(point[i].x)) + ", " + std::to_string(static_cast(point[i].y)) + ", " + std::to_string(static_cast(point[i].z)) + ")"; 486 | text.setString(coordinate_string); 487 | text.setPosition(circle[i].getPosition() + sf::Vector2f(1.0f, -5.0f)); 488 | window.draw(text); 489 | } 490 | */ 491 | 492 | // Display then clear the screen 493 | window.display(); 494 | window.clear(sf::Color::Color(0, 0, 0, 255)); 495 | } 496 | 497 | void LorenzAttractor::run(sf::RenderWindow &window) 498 | { 499 | window.setView(view); 500 | 501 | while (window.isOpen() && !endSubProgram) 502 | { 503 | this->input(window); // Get Input 504 | this->update(); // Update Graphics 505 | this->draw(window); // Draw Graphics 506 | } 507 | 508 | window.setView(sf::View(sf::Vector2f(static_cast(g_screenWidth / 2), static_cast(g_screenHeight / 2)), sf::Vector2f(static_cast(g_screenWidth), static_cast(g_screenHeight)))); 509 | } 510 | 511 | LorenzAttractor::~LorenzAttractor() 512 | { 513 | } 514 | -------------------------------------------------------------------------------- /LorenzAttractor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "vectormath.h" 3 | #include 4 | #include 5 | #include 6 | 7 | class LorenzAttractor 8 | { 9 | private: 10 | sf::Event event; 11 | sf::Clock clock; 12 | 13 | double input_timer = 0; 14 | double timestep; 15 | 16 | float g_screenWidth = 1920; 17 | float g_screenHeight = 1080; 18 | 19 | bool isFullscreen = true; 20 | bool endSubProgram = false; 21 | 22 | sf::View view; 23 | 24 | // Camera values 25 | sf::Vector3f cam_position = { 0, 0, -50 }; 26 | std::vector cam_angle = { 0, 0, 0 }; 27 | sf::Vector3f display_position = { 0, 0, 100 }; 28 | 29 | std::vector point; 30 | sf::Vector2f projected_point; 31 | 32 | // Attractor parameters 33 | unsigned u = 0; 34 | std::vector> params; 35 | 36 | // Rotation Matrices 37 | Matrix3 rotMatrixX; 38 | Matrix3 rotMatrixY; 39 | Matrix3 rotMatrixZ; 40 | 41 | // Visual assets 42 | std::vector> trail; // Contains "num_points" many "trail_length" previous coordinates 43 | std::vector j; // Indices of the last position drawn (algorithm for drawing trails without reallocating memory) 44 | sf::VertexArray line; // Line object to be drawn 45 | std::vector circle; 46 | std::vector colours; 47 | std::vector> trail_colours_params; 48 | 49 | // Visual parameters 50 | unsigned num_points = 2; 51 | unsigned trail_length = 50; 52 | float speed = 0.0f; 53 | 54 | // Constants 55 | float pi = 3.141f; 56 | 57 | // Names per attractor 58 | sf::Text text; 59 | sf::Font font; 60 | std::vector names = 61 | { 62 | "Lorenz Attractor", 63 | "3-Cells CNN Attractor", 64 | "Aizawa Attractor", 65 | "Bouali Attractor", 66 | "Chen-Lee Attractor", 67 | "Halvorsen Attractor", 68 | "Finance Attractor", 69 | "Newton-Leipnik Attractor", 70 | "Nose-Hoover Attractor", 71 | "Thomas Attractor" 72 | }; 73 | 74 | public: 75 | LorenzAttractor(); 76 | 77 | sf::Uint8 clamp(float x) 78 | { 79 | if (x <= 0) 80 | return 0; 81 | else if (x >= 255) 82 | return 255; 83 | else return static_cast(x); 84 | } 85 | 86 | float getRandomNumber(float MIN, float MAX) 87 | { 88 | std::random_device device; 89 | std::mt19937 generator(device()); 90 | std::uniform_real_distribution distribution(MIN, MAX); 91 | return distribution(generator); 92 | } 93 | 94 | int getRandomNumber(int MIN, int MAX) 95 | { 96 | std::random_device device; 97 | std::mt19937 generator(device()); 98 | std::uniform_int_distribution distribution(MIN, MAX); 99 | return distribution(generator); 100 | } 101 | 102 | void input(sf::RenderWindow &window); 103 | 104 | void update(); 105 | 106 | void draw(sf::RenderWindow &window); 107 | 108 | void run(sf::RenderWindow &window); 109 | 110 | ~LorenzAttractor(); 111 | }; 112 | 113 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Coding-Projects 2 | A collection of projects, graphical or otherwise. 3 | 4 | This project uses SFML 2.5.1 5 | 6 | To run any of the programs, create a main.cpp file, include "program.h" and write Program.run(window); 7 | 8 | example for Visual Studio 2017 to run LorenzAttractor: 9 | 10 | main.cpp 11 | ```cpp 12 | #include "stdafx.h" 13 | #include "LorenzAttractor.h" 14 | #include 15 | 16 | int main() 17 | { 18 | /// Create a window 19 | 20 | bool isFullscreen = true; 21 | sf::RenderWindow window; 22 | window.create(sf::VideoMode(1980, 1080), "Coding Projects", (isFullscreen ? sf::Style::Fullscreen : sf::Style::Default), sf::ContextSettings()); 23 | window.setPosition(sf::Vector2i(0, 0)); 24 | window.setVerticalSyncEnabled(true); 25 | window.setFramerateLimit(60); 26 | 27 | 28 | /// Important part is here 29 | 30 | LorenzAttractor.run(window); 31 | } 32 | ``` 33 | Controls : 34 | H to switch to the next attractor 35 | 36 | W/A/S/D for moving in the plane 37 | 38 | Space/Shift to move up and down 39 | 40 | Up/Down/Left/Right to turn the camera 41 | 42 | These will feel familiar to Minecraft players ;) 43 | -------------------------------------------------------------------------------- /vectormath.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | ///////////////////// 9 | // MATRIX CLASS // 10 | ///////////////////// 11 | 12 | template 13 | class Matrix3 14 | { 15 | private: 16 | T **m_data; 17 | public: 18 | Matrix3() 19 | { 20 | m_data = new T*[3]; 21 | for (int i = 0; i < 3; i++) 22 | m_data[i] = new T[3]; 23 | } 24 | 25 | Matrix3(const std::initializer_list> &list_ext) 26 | { 27 | assert(list_ext.size() == 3); 28 | 29 | m_data = new T*[3]; 30 | for (int i = 0; i < 3; i++) 31 | m_data[i] = new T[3]; 32 | 33 | int i = 0, j = 0; 34 | for (auto &list_int : list_ext) 35 | { 36 | assert(list_int.size() == 3); 37 | j = 0; 38 | for (auto &element : list_int) 39 | { 40 | m_data[i][j] = element; 41 | j++; 42 | } 43 | i++; 44 | } 45 | } 46 | 47 | void erase() 48 | { 49 | for (int i = 0; i < 3; i++) 50 | delete[] m_data[i]; 51 | delete[] m_data; 52 | 53 | m_data = nullptr; 54 | } 55 | 56 | const T* operator[](const int r) const 57 | { 58 | assert(r >= 0 && r < 3); 59 | return m_data[r]; 60 | } 61 | 62 | T* operator[](const int r) 63 | { 64 | assert(r >= 0 && r < 3); 65 | return m_data[r]; 66 | } 67 | 68 | Matrix3& operator=(const std::initializer_list> list_ext) 69 | { 70 | assert(list_ext.size() == 3); 71 | 72 | this->erase(); 73 | 74 | m_data = new T*[3]; 75 | for (int i = 0; i < 3; i++) 76 | m_data[i] = new T[3]; 77 | 78 | int i = 0, j = 0; 79 | for (auto &list_int : list_ext) 80 | { 81 | assert(list_int.size() == 3); 82 | j = 0; 83 | for (auto &element : list_int) 84 | { 85 | m_data[i][j] = element; 86 | j++; 87 | } 88 | i++; 89 | } 90 | 91 | return (*this); 92 | } 93 | 94 | Matrix3& operator=(const Matrix3 &m) 95 | { 96 | this->erase(); 97 | 98 | m_data = new T*[3]; 99 | for (int i = 0; i < 3; i++) 100 | m_data[i] = new T[3]; 101 | 102 | for (int i = 0; i < 3; i++) 103 | for (int j = 0; j < 3; j++) 104 | m_data[i][j] = m[i][j]; 105 | 106 | return (*this); 107 | } 108 | 109 | ~Matrix3() 110 | { 111 | delete[] m_data[0]; 112 | delete[] m_data[1]; 113 | delete[] m_data[2]; 114 | 115 | delete[] m_data; 116 | } 117 | }; 118 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 119 | template 120 | std::ostream& operator<<(std::ostream &out, Matrix3 &m) 121 | { 122 | for (int r = 0; r < 3; r++) 123 | { 124 | for (int c = 0; c < 3; c++) 125 | out << m[r][c] << " "; 126 | out << "\n"; 127 | } 128 | 129 | return out; 130 | } 131 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 132 | template 133 | bool operator==(Matrix3 m1, Matrix3 m2) 134 | { 135 | int s = 0; 136 | for (int r = 0; r < m1.getNumRows(); r++) 137 | for (int c = 0; c < m1.getNumCols(); c++) 138 | s += (m1[r][c] == m2[r][c]); 139 | 140 | if (s == m1.getNumRows() * m1.getNumCols()) 141 | return true; 142 | else return false; 143 | } 144 | 145 | template 146 | bool operator!=(Matrix3 m1, Matrix3 m2) 147 | { 148 | int s = 0; 149 | for (int r = 0; r < m1.getNumRows(); r++) 150 | for (int c = 0; c < m1.getNumCols(); c++) 151 | s += (m1[r][c] == m2[r][c]); 152 | 153 | if (s == m1.getNumRows() * m1.getNumCols()) 154 | return false; 155 | else return true; 156 | } 157 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 158 | template 159 | Matrix3 operator+(const Matrix3 &left, const Matrix3 &right) 160 | { 161 | return { {left[0][0] + right[0][0], left[0][1] + right[0][1], left[0][2] + right[0][2]}, 162 | {left[1][0] + right[1][0], left[1][1] + right[1][1], left[1][2] + right[1][2]}, 163 | {left[2][0] + right[2][0], left[2][1] + right[2][1], left[2][2] + right[2][2]} }; 164 | } 165 | 166 | template 167 | Matrix3& operator+=(Matrix3 &left, const Matrix3 &right) 168 | { 169 | for (int r = 0; r < 3; r++) 170 | for (int c = 0; c < 3; c++) 171 | left[r][c] += right[r][c]; 172 | 173 | return left; 174 | } 175 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 176 | template 177 | Matrix3 operator-(const Matrix3 &left, const Matrix3 &right) 178 | { 179 | return { {left[0][0] - right[0][0], left[0][1] - right[0][1], left[0][2] - right[0][2]}, 180 | {left[1][0] - right[1][0], left[1][1] - right[1][1], left[1][2] - right[1][2]}, 181 | {left[2][0] - right[2][0], left[2][1] - right[2][1], left[2][2] - right[2][2]} }; 182 | } 183 | 184 | template 185 | Matrix3& operator-=(Matrix3 &left, const Matrix3 &right) 186 | { 187 | for (int r = 0; r < 3; r++) 188 | for (int c = 0; c < 3; c++) 189 | left[r][c] -= right[r][c]; 190 | 191 | return left; 192 | } 193 | 194 | template 195 | Matrix3& operator-(Matrix3 &left) 196 | { 197 | for (int r = 0; r < 3; r++) 198 | for (int c = 0; c < 3; c++) 199 | left[r][c] = -left[r][c]; 200 | 201 | return left; 202 | } 203 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 204 | template 205 | Matrix3& operator*=(Matrix3 &left, const U &right) 206 | { 207 | for (int r = 0; r < 3; r++) 208 | for (int c = 0; c < 3; c++) 209 | left[r][c] *= right; 210 | return left; 211 | } 212 | 213 | template 214 | Matrix3 operator*(const Matrix3 &left, const U &right) 215 | { 216 | return { {left[0][0] * right, left[0][1] * right, left[0][2] * right}, 217 | {left[1][0] * right, left[1][1] * right, left[1][2] * right}, 218 | {left[2][0] * right, left[2][1] * right, left[2][2] * right} }; 219 | } 220 | 221 | template 222 | Matrix3 operator*(const U &left, const Matrix3 &right) 223 | { 224 | return { {left * right[0][0], left * right[0][1], left * right[0][2]}, 225 | {left * right[1][0], left * right[1][1], left * right[1][2]}, 226 | {left * right[2][0], left * right[2][1], left * right[2][2]} }; 227 | } 228 | 229 | template 230 | Matrix3 operator*(Matrix3 &left, Matrix3 &right) 231 | { 232 | T _00 = left[0][0] * right[0][0] + left[0][1] * right[1][0] + left[0][2] * right[2][0]; 233 | T _01 = left[0][0] * right[0][1] + left[0][1] * right[1][1] + left[0][2] * right[2][1]; 234 | T _02 = left[0][0] * right[0][2] + left[0][1] * right[1][2] + left[0][2] * right[2][2]; 235 | 236 | T _10 = left[1][0] * right[0][0] + left[1][1] * right[1][0] + left[1][2] * right[2][0]; 237 | T _11 = left[1][0] * right[0][1] + left[1][1] * right[1][1] + left[1][2] * right[2][1]; 238 | T _12 = left[1][0] * right[0][2] + left[1][1] * right[1][2] + left[1][2] * right[2][2]; 239 | 240 | T _20 = left[2][0] * right[0][0] + left[2][1] * right[1][0] + left[2][2] * right[2][0]; 241 | T _21 = left[2][0] * right[0][1] + left[2][1] * right[1][1] + left[2][2] * right[2][1]; 242 | T _22 = left[2][0] * right[0][2] + left[2][1] * right[1][2] + left[2][2] * right[2][2]; 243 | 244 | return { {_00,_01,_02},{_10,_11,_12},{_20,_21,_22} }; 245 | } 246 | 247 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 248 | template 249 | Matrix3& operator/=(Matrix3 &left, U right) 250 | { 251 | for (int r = 0; r < 3; r++) 252 | for (int c = 0; c < 3; c++) 253 | left[r][c] /= right; 254 | return left; 255 | } 256 | 257 | template 258 | Matrix3 operator/(Matrix3 left, U right) 259 | { 260 | return { {left[0][0] / right, left[0][1] / right, left[0][2] / right}, 261 | {left[1][0] / right, left[1][1] / right, left[1][2] / right}, 262 | {left[2][0] / right, left[2][1] / right, left[2][2] / right} }; 263 | } 264 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 265 | 266 | ///////////////////// 267 | // 2D VECTOR MATHS // 268 | ///////////////////// 269 | 270 | template 271 | float Magnitude(const V2 &v) 272 | { 273 | return sqrt(static_cast(v.x * v.x + v.y * v.y)); 274 | } 275 | 276 | template 277 | V2 UnitVector(const V2 &v) 278 | { 279 | return v / Magnitude(v); 280 | } 281 | 282 | template 283 | bool operator>(const V2 &v1,const V2 &v2) 284 | { 285 | if (Magnitude(v1) > Magnitude(v2)) 286 | return true; 287 | else return false; 288 | } 289 | 290 | template 291 | bool operator<(V2 &v1, V2 &v2) 292 | { 293 | if (Magnitude(v1) > Magnitude(v2)) 294 | return true; 295 | else return false; 296 | } 297 | 298 | ///////////////////// 299 | // 3D VECTOR MATHS // 300 | ///////////////////// 301 | 302 | template 303 | sf::Vector3 operator*(const Matrix3 &m, const sf::Vector3 &v) 304 | { 305 | return sf::Vector3( 306 | m[0][0] * v.x + m[0][1] * v.y + m[0][2] * v.z, 307 | m[1][0] * v.x + m[1][1] * v.y + m[1][2] * v.z, 308 | m[2][0] * v.x + m[2][1] * v.y + m[2][2] * v.z 309 | ); 310 | } --------------------------------------------------------------------------------