diff --git a/CMakeLists.txt b/CMakeLists.txt index 6b32652..f56c93a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,8 +5,8 @@ cmake_minimum_required(VERSION 3.22) project(sortiva - LANGUAGES CXX - VERSION 1.0.0 + LANGUAGES CXX + VERSION 1.0.0 ) # ======================================================== @@ -18,7 +18,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) # Enforce C++20 standard set(CMAKE_CXX_EXTENSIONS OFF) # Disable compiler-specific extensions set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Generate compile commands (useful for IDEs) set(USE_FOLDERS ON) # Organize targets into folders (for IDEs) -set(BUILD_SHARED_LIBS OFF) # Build static libraries by default +set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE) # Build static libraries by default # ======================================================== # Helper Functions: @@ -30,35 +30,35 @@ set(BUILD_SHARED_LIBS OFF) # Build static libraries by default # It then sets the folder property for the target. function(put_targets_into_folder) - set(oneValueArgs FOLDER) - set(multiValueArgs TARGETS) - cmake_parse_arguments(ARGS "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + set(oneValueArgs FOLDER) + set(multiValueArgs TARGETS) + cmake_parse_arguments(ARGS "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - foreach(target ${ARGS_TARGETS}) - # Check if target exists - if (NOT TARGET ${target}) - message(FATAL_ERROR "${target} target not found") - endif() + foreach(target ${ARGS_TARGETS}) + # Check if target exists + if (NOT TARGET ${target}) + message(FATAL_ERROR "${target} target not found") + endif() - # Get the actual target (if it is aliased) - get_target_property(actual_target ${target} ALIASED_TARGET) - if (NOT actual_target) - set(actual_target ${target}) - endif() + # Get the actual target (if it is aliased) + get_target_property(actual_target ${target} ALIASED_TARGET) + if (NOT actual_target) + set(actual_target ${target}) + endif() - # Set the folder property for the target - set_target_properties(${actual_target} PROPERTIES FOLDER ${ARGS_FOLDER}) - endforeach() + # Set the folder property for the target + set_target_properties(${actual_target} PROPERTIES FOLDER ${ARGS_FOLDER}) + endforeach() endfunction() function(find_files var_name path) - set(sources) - foreach(ext ${ARGN}) - file(GLOB_RECURSE files "${path}/*.${ext}") - list(APPEND sources ${files}) - endforeach() - set(${var_name} ${${var_name}} ${sources} PARENT_SCOPE) + set(sources) + foreach(ext ${ARGN}) + file(GLOB_RECURSE files "${path}/*.${ext}") + list(APPEND sources ${files}) + endforeach() + set(${var_name} ${${var_name}} ${sources} PARENT_SCOPE) endfunction() # ======================================================== @@ -74,33 +74,27 @@ set(FETCHCONTENT_QUIET OFF) # Display download progress set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Generate compile_commands.json # ======================================================== -# Options +# External Dependencies (currently not needed) # ======================================================== -option(SVA_BUILD_TEST ON) # Option for test to be build as well - -# ======================================================== -# External Dependencies -# ======================================================== - -# --- Add Sol2 (Lua C++ Binding Library) -FetchContent_Declare( - sol2 - GIT_REPOSITORY https://github.com/ThePhD/sol2.git - GIT_TAG v3.3.0 -) -FetchContent_MakeAvailable(sol2) - -# --- Add Lua -FetchContent_Declare( - lua - GIT_REPOSITORY "https://github.com/marovira/lua" - GIT_TAG "5.4.4" -) - -FetchContent_MakeAvailable(lua) - -set(LUA_BUILD_INTERPRETER ON CACHE BOOL "Build the Lua interpreter" FORCE) +# # --- Add Sol2 (Lua C++ Binding Library) +# FetchContent_Declare( +# sol2 +# GIT_REPOSITORY https://github.com/ThePhD/sol2.git +# GIT_TAG v3.3.0 +# ) +# FetchContent_MakeAvailable(sol2) +# +# # --- Add Lua +# FetchContent_Declare( +# lua +# GIT_REPOSITORY "https://github.com/marovira/lua" +# GIT_TAG "5.4.4" +# ) +# FetchContent_MakeAvailable(lua) +# +# set(LUA_BUILD_INTERPRETER ON CACHE BOOL "Build the Lua interpreter" FORCE) +# set(SOL2_ENABLE_INSTALL OFF CACHE BOOL "" FORCE) # ======================================================== # Helper Function: Set Common Target Properties @@ -109,155 +103,96 @@ set(LUA_BUILD_INTERPRETER ON CACHE BOOL "Build the Lua interpreter" FORCE) # that are shared among all targets. function(set_common_properties target) - target_link_libraries(${target} PRIVATE sol2::sol2) # Link with Sol2 - target_compile_definitions(${target} PRIVATE SOL_ALL_SAFETIES_ON=1) - target_include_directories(${target} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include) # Include project headers - target_sources(${target} PRIVATE ${SVA_COMMON_FILES}) # Include common files - target_link_libraries(${target} PRIVATE lua::lua) + #target_link_libraries(${target} PRIVATE sol2::sol2) # Link with Sol2 + #target_compile_definitions(${target} PRIVATE SOL_ALL_SAFETIES_ON=1) + target_include_directories(${target} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include) # Include project headers + #target_sources(${target} PRIVATE ${SVA_COMMON_FILES}) # Include common files + #target_link_libraries(${target} PRIVATE lua::lua) endfunction() # ======================================================== -# GUI Setup +# Executable # ======================================================== set(GUI_TARGET_NAME "${PROJECT_NAME}") # --- Create GUI Executable find_files(SVA_GUI_FILES "./src/" hpp cpp h c hxx cxx) add_executable(${GUI_TARGET_NAME} - ${SVA_GUI_FILES} + ${SVA_GUI_FILES} ) +target_compile_definitions(${GUI_TARGET_NAME} PUBLIC "$<$:_DEBUG>") +source_group(TREE ${PROJECT_SOURCE_DIR}/src/ PREFIX "Source" FILES ${SVA_GUI_FILES}) # --------------- # RAYLIB # --------------- # Dependencies -set(RAYLIB_VERSION 5.0) +set(RAYLIB_VERSION 5.5) find_package(raylib ${RAYLIB_VERSION} QUIET) # QUIET or REQUIRED if (NOT raylib_FOUND) # If there's none, fetch and build raylib - include(FetchContent) - FetchContent_Declare( - raylib - DOWNLOAD_EXTRACT_TIMESTAMP OFF - URL https://github.com/raysan5/raylib/archive/refs/tags/${RAYLIB_VERSION}.tar.gz - ) - FetchContent_GetProperties(raylib) - if (NOT raylib_POPULATED) # Have we downloaded raylib yet? - set(FETCHCONTENT_QUIET NO) - FetchContent_MakeAvailable(raylib) - set(BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) # don't build the supplied examples - endif() + include(FetchContent) + FetchContent_Declare( + raylib + DOWNLOAD_EXTRACT_TIMESTAMP OFF + URL https://github.com/raysan5/raylib/archive/refs/tags/${RAYLIB_VERSION}.tar.gz + ) + FetchContent_GetProperties(raylib) + if (NOT raylib_POPULATED) # Have we downloaded raylib yet? + set(FETCHCONTENT_QUIET NO) + FetchContent_MakeAvailable(raylib) + set(BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) # don't build the supplied examples + endif() endif() -put_targets_into_folder(FOLDER "raylib" TARGETS raylib uninstall) - target_link_libraries(${GUI_TARGET_NAME} PRIVATE raylib) # Checks if OSX and links appropriate frameworks (Only required on MacOS) if (APPLE) - target_link_libraries(${PROJECT_NAME} "-framework IOKit") - target_link_libraries(${PROJECT_NAME} "-framework Cocoa") - target_link_libraries(${PROJECT_NAME} "-framework OpenGL") + target_link_libraries(${PROJECT_NAME} "-framework IOKit") + target_link_libraries(${PROJECT_NAME} "-framework Cocoa") + target_link_libraries(${PROJECT_NAME} "-framework OpenGL") endif() -# ------------------------------- -# SFML (Not used anymore) -# ------------------------------- -# -# --- Add SFML (Simple and Fast Multimedia Library) -# FetchContent_Declare(SFML -# GIT_REPOSITORY https://github.com/SFML/SFML.git -# GIT_TAG 2.6.x -# GIT_SHALLOW ON -# EXCLUDE_FROM_ALL -# SYSTEM -# ) -# FetchContent_MakeAvailable(SFML) -# -# target_link_libraries(${GUI_TARGET_NAME} PRIVATE sfml-graphics) # Link SFML graphics library -# -# --- Special handling for Windows: Copy OpenAL DLL (for sound support) -# if(WIN32) -# add_custom_command( -# TARGET ${GUI_TARGET_NAME} -# COMMENT "Copy OpenAL DLL" -# PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${SFML_SOURCE_DIR}/extlibs/bin/$,x64,x86>/openal32.dll $ -# VERBATIM -# ) -# endif() -# // SFML is not used anymore - set_common_properties(${GUI_TARGET_NAME}) - -# ======================================================== -# Test Setup (Only if SVA_BUILD_TEST is ON) -# ======================================================== -if(${SVA_BUILD_TEST}) - # --- Add Google Test - FetchContent_Declare( - googletest - GIT_REPOSITORY https://github.com/google/googletest.git - GIT_TAG release-1.12.1 - ) - set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) # Prevent shared CRT issues on Windows - FetchContent_MakeAvailable(googletest) - - # Enable testing support - enable_testing() - - # --- Create Test Executable - set(TEST_TARGET_NAME "${GUI_TARGET_NAME}_test") - add_executable( - ${TEST_TARGET_NAME} - tests/test_main.cpp # Test entry point - tests/test_sorting_functions.cpp - ) - - target_link_libraries( - ${TEST_TARGET_NAME} - PRIVATE GTest::gtest_main # Link Google Test framework - ) - set_common_properties(${TEST_TARGET_NAME}) - - # --- Enable Google Test's test discovery feature - include(GoogleTest) - gtest_discover_tests(${TEST_TARGET_NAME}) - - # put google test targets into a folder - put_targets_into_folder( - FOLDER "gtest" - TARGETS - GTest::gtest_main - GTest::gmock_main - GTest::gmock - GTest::gtest - ) -endif() + +put_targets_into_folder( + FOLDER "ThirdParty/raylib" + TARGETS + raylib uninstall +) + +#put_targets_into_folder( +# FOLDER "ThirdParty/lua" +# TARGETS +# lua lua_lib +#) + +put_targets_into_folder( + FOLDER "ThirdParty/glfw" + TARGETS + glfw update_mappings +) + + # Option to create an includes target set(SVA_CREATE_INCLUDES_TARGET ON CACHE BOOL "Create an includes target") if(${SVA_CREATE_INCLUDES_TARGET}) - set(INCLUDES_TARGET_NAME "${GUI_TARGET_NAME}_includes") - set(INCLUDE_FILES) - find_files( - INCLUDE_FILES - "include/" - hpp h hxx - ) - message(STATUS "Include files: ${INCLUDE_FILES}") - add_custom_target(${INCLUDES_TARGET_NAME} - SOURCES - ${INCLUDE_FILES} - ) - set_target_properties(${INCLUDES_TARGET_NAME} PROPERTIES FOLDER "sva") + set(INCLUDES_TARGET_NAME "includes") + set(INCLUDE_FILES) + find_files( + INCLUDE_FILES + "include/" + hpp h hxx + ) + message(STATUS "Include files: ${INCLUDE_FILES}") + add_custom_target(${INCLUDES_TARGET_NAME} + SOURCES + ${INCLUDE_FILES} + ) + source_group(TREE ${PROJECT_SOURCE_DIR}/include/ PREFIX "Source" FILES ${INCLUDE_FILES}) endif() - -put_targets_into_folder( - FOLDER "sva" - TARGETS - ${GUI_TARGET_NAME} - ${TEST_TARGET_NAME} -) diff --git a/LICENSE b/LICENSE index 261eeb9..232a1ab 100644 --- a/LICENSE +++ b/LICENSE @@ -1,201 +1,21 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ +MIT License - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION +Copyright (c) 2024 Jann Hoffmann - 1. Definitions. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/include/Profiling/Timer.hpp b/include/Profiling/Timer.hpp deleted file mode 100644 index 30094e9..0000000 --- a/include/Profiling/Timer.hpp +++ /dev/null @@ -1,106 +0,0 @@ -#pragma once -#include -#include -#include -#include - -struct timer_result -{ - double seconds = 0; - double difftime = 0; -}; - -struct Timer { - enum State : u8 - { - running = 0, - paused = 1 - }; - State state = paused; - - std::clock_t begin; - std::clock_t elapsed = 0; - std::optional out; - std::optional result; - - Timer() : begin(std::clock()) {} - Timer(std::ostream& o) : begin(std::clock()), out(&o) {} - Timer(timer_result& r) : begin(std::clock()), result(&r) {} - Timer(std::ostream& o, timer_result& r) : begin(std::clock()), out(&o), result(&r) {} - Timer(State initial_state) : state(initial_state), begin(std::clock()) - { - if (state == paused) - begin = 0; - } - - Timer(std::ostream& o, State initial_state) : state(initial_state), begin(std::clock()), out(&o) - { - if (state == paused) - begin = 0; - } - Timer(timer_result& r, State initial_state) : state(initial_state), begin(std::clock()), result(&r) - { - if (state == paused) - begin = 0; - } - - Timer(std::ostream& o, timer_result& r, State initial_state) : state(initial_state), begin(std::clock()), out(&o), result(&r) - { - if (state == paused) - begin = 0; - } - - void reset(State init_state) { - if (state == paused) - begin = 0; - else - begin = std::clock(); - elapsed = 0; - state = init_state; - lap(); - } - - void set_state(State new_state) - { - if (this->state == new_state) - return; - - if (new_state == running) - { - begin = std::clock(); - } - else // pausing - { - assert(begin != 0); - elapsed += std::clock() - begin; - begin = 0; - } - - lap(); - this->state = new_state; - } - - void lap() - { - std::clock_t total_time = elapsed; - if (state == running) - { - total_time += std::clock() - begin; - } - - if (result) - { - result.value()->seconds = static_cast(total_time) / CLOCKS_PER_SEC; - result.value()->difftime = static_cast(total_time) / CLOCKS_PER_SEC; - } - if (out) - { - *out.value() << static_cast(total_time) / CLOCKS_PER_SEC << " seconds.\n"; - *out.value() << "Total time = " << static_cast(total_time) / CLOCKS_PER_SEC << std::endl; - } - } - - ~Timer() { - lap(); - } -}; \ No newline at end of file diff --git a/include/TickSystem.hpp b/include/TickSystem.hpp new file mode 100644 index 0000000..69cbd32 --- /dev/null +++ b/include/TickSystem.hpp @@ -0,0 +1,25 @@ +#pragma once +#include +#include + +class TickSystem { +private: + std::chrono::duration timer{ 0 }; + std::chrono::duration tickRate; + +public: + TickSystem(std::chrono::duration tickRateSeconds) + : tickRate(tickRateSeconds) { + } + + bool update(std::chrono::duration deltaTime) { + timer += deltaTime; + + if (timer >= tickRate) { + timer -= tickRate; + return true; + } + + return false; + } +}; diff --git a/include/collections.hpp b/include/collections.hpp new file mode 100644 index 0000000..11e170e --- /dev/null +++ b/include/collections.hpp @@ -0,0 +1,137 @@ +#pragma once +#include + + +template +class tree +{ + size_t m_CountBranches = 0; + tree* branches = nullptr; +public: + T value; + + tree() = default; + + ~tree() + { + delete[] branches; + m_CountBranches = 0; + } + + bool operator==(const tree& rhs) const + { + return branches == rhs.branches; + } + + template + explicit tree(Args&&... args) : value(std::forward(args)...) {} + + tree& get(size_t idx) + { + if (m_CountBranches <= idx || !branches) throw std::out_of_range("Index is out of range!"); + return branches[idx]; + } + + void set(size_t idx, const tree* t) + { + if (m_CountBranches >= idx || !t || !branches) throw std::out_of_range("Index is out of range!"); + branches[idx] = *t; + } + + void add(const tree* t) + { + tree* tmp = new tree[m_CountBranches + 1]; + if (branches) { + memcpy(tmp, branches, sizeof(tree) * m_CountBranches); + delete[] branches; + } + branches = tmp; + tmp[m_CountBranches++] = *t; + } + + void set(size_t idx, const T& val) + { + if (m_CountBranches >= idx || !branches) throw std::out_of_range("Index is out of range!"); + branches[idx].value = value; + } + + template + void add(Args&& ...args) + { + tree* tmp = new tree[m_CountBranches + 1]; + if (branches) { + memcpy(tmp, branches, sizeof(tree) * m_CountBranches); + delete[] branches; + } + branches = tmp; + tmp[m_CountBranches++] = tree(std::forward(args)...); + } + + size_t depth() + { + if (branches) return 1; + size_t res = 0; + for (int i = 0; i < m_CountBranches; ++i) + { + res = max(res, branches[i].depth()); + } + return res + 1; + } + + size_t width() const + { + return m_CountBranches; + } + + tree& operator[](size_t idx) + { + return get(idx); + } +}; + +template +struct linked_list +{ + linked_list* next = nullptr; + linked_list* first = nullptr; + T value; + + linked_list() = default; + linked_list(T val) : value(val) {} + ~linked_list() + { + delete next; + } + + bool operator==(const linked_list& rhs) const + { + return + this->first == rhs.first && + this->next == rhs.next && + this->value == rhs.value; + } + + linked_list& put(const T& val) + { + if (!next) + { + next = new linked_list(val); + if (first) + next->first = first; + else + next->first = this; + return *next; + } + if (first) + next->first = first; + else + next->first = this; + return next->put(val); + } + + size_t depth() + { + if (!next) return 1; + return next->depth() + 1; + } +}; diff --git a/include/raylibs/modules/gui_window_file_dialog.hpp b/include/raylibs/modules/gui_window_file_dialog.hpp new file mode 100644 index 0000000..1641852 --- /dev/null +++ b/include/raylibs/modules/gui_window_file_dialog.hpp @@ -0,0 +1,626 @@ +#pragma once +/******************************************************************************************* +* +* Window File Dialog v1.2 - Modal file dialog to open/save files +* +* MODULE USAGE: +* #define GUI_WINDOW_FILE_DIALOG_IMPLEMENTATION +* #include "gui_window_file_dialog.h" +* +* INIT: GuiWindowFileDialogState state = GuiInitWindowFileDialog(); +* DRAW: GuiWindowFileDialog(&state); +* +* NOTE: This module depends on some raylib file system functions: +* - LoadDirectoryFiles() +* - UnloadDirectoryFiles() +* - GetWorkingDirectory() +* - DirectoryExists() +* - FileExists() +* +* LICENSE: zlib/libpng +* +* Copyright (c) 2019-2024 Ramon Santamaria (@raysan5) +* +* This software is provided "as-is", without any express or implied warranty. In no event +* will the authors be held liable for any damages arising from the use of this software. +* +* Permission is granted to anyone to use this software for any purpose, including commercial +* applications, and to alter it and redistribute it freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must not claim that you +* wrote the original software. If you use this software in a product, an acknowledgment +* in the product documentation would be appreciated but is not required. +* +* 2. Altered source versions must be plainly marked as such, and must not be misrepresented +* as being the original software. +* +* 3. This notice may not be removed or altered from any source distribution. +* +**********************************************************************************************/ + +#include + +#ifndef GUI_WINDOW_FILE_DIALOG_H +#define GUI_WINDOW_FILE_DIALOG_H + +// Gui file dialog context data +typedef struct { + + // Window management variables + bool windowActive; + Rectangle windowBounds; + Vector2 panOffset; + bool dragMode; + bool supportDrag; + + // UI variables + bool dirPathEditMode; + char dirPathText[1024]; + + int filesListScrollIndex; + bool filesListEditMode; + int filesListActive; + + bool fileNameEditMode; + char fileNameText[1024]; + bool SelectFilePressed; + bool CancelFilePressed; + int fileTypeActive; + int itemFocused; + + // Custom state variables + FilePathList dirFiles; + char filterExt[256]; + char dirPathTextCopy[1024]; + char fileNameTextCopy[1024]; + + int prevFilesListActive; + + bool saveFileMode; + +} GuiWindowFileDialogState; + +#ifdef __cplusplus +extern "C" { // Prevents name mangling of functions +#endif + + //---------------------------------------------------------------------------------- + // Defines and Macros + //---------------------------------------------------------------------------------- + //... + + //---------------------------------------------------------------------------------- + // Types and Structures Definition + //---------------------------------------------------------------------------------- + // ... + + //---------------------------------------------------------------------------------- + // Global Variables Definition + //---------------------------------------------------------------------------------- + //... + + //---------------------------------------------------------------------------------- + // Module Functions Declaration + //---------------------------------------------------------------------------------- + GuiWindowFileDialogState InitGuiWindowFileDialog(const char* initPath); + void GuiWindowFileDialog(GuiWindowFileDialogState* state); + +#ifdef __cplusplus +} +#endif + +#endif // GUI_WINDOW_FILE_DIALOG_H + +/*********************************************************************************** +* +* GUI_WINDOW_FILE_DIALOG IMPLEMENTATION +* +************************************************************************************/ +#if defined(GUI_WINDOW_FILE_DIALOG_IMPLEMENTATION) + +#include + +#include // Required for: strcpy() + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +#define MAX_DIRECTORY_FILES 2048 +#define MAX_ICON_PATH_LENGTH 512 +#ifdef _WIN32 +#define PATH_SEPERATOR "\\" +#else +#define PATH_SEPERATOR "/" +#endif + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +#if defined(USE_CUSTOM_LISTVIEW_FILEINFO) +// Detailed file info type +typedef struct FileInfo { + const char* name; + int size; + int modTime; + int type; + int icon; +} FileInfo; +#else +// Filename only +typedef char* FileInfo; // Files are just a path string +#endif + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +FileInfo* dirFilesIcon = NULL; // Path string + icon (for fancy drawing) + +//---------------------------------------------------------------------------------- +// Internal Module Functions Definition +//---------------------------------------------------------------------------------- +// Read files in new path +static void ReloadDirectoryFiles(GuiWindowFileDialogState* state); + +#if defined(USE_CUSTOM_LISTVIEW_FILEINFO) +// List View control for files info with extended parameters +static int GuiListViewFiles(Rectangle bounds, FileInfo* files, int count, int* focus, int* scrollIndex, int active); +#endif + +//---------------------------------------------------------------------------------- +// Module Functions Definition +//---------------------------------------------------------------------------------- +GuiWindowFileDialogState InitGuiWindowFileDialog(const char* initPath) +{ + GuiWindowFileDialogState state = { 0 }; + + // Init window data + state.windowBounds = { static_cast(GetScreenWidth()) / 2 - 440 / 2, static_cast(GetScreenHeight()) / 2 - 310 / 2, 440, 310 }; + state.windowActive = false; + state.supportDrag = true; + state.dragMode = false; + state.panOffset = { 0, 0 }; + + // Init path data + state.dirPathEditMode = false; + state.filesListActive = -1; + state.prevFilesListActive = state.filesListActive; + state.filesListScrollIndex = 0; + + state.fileNameEditMode = false; + + state.SelectFilePressed = false; + state.CancelFilePressed = false; + + state.fileTypeActive = 0; + + strcpy(state.fileNameText, "\0"); + + // Custom variables initialization + if (initPath && DirectoryExists(initPath)) + { + strcpy(state.dirPathText, initPath); + } + else if (initPath && FileExists(initPath)) + { + strcpy(state.dirPathText, GetDirectoryPath(initPath)); + strcpy(state.fileNameText, GetFileName(initPath)); + } + else strcpy(state.dirPathText, GetWorkingDirectory()); + + // TODO: Why we keep a copy? + strcpy(state.dirPathTextCopy, state.dirPathText); + strcpy(state.fileNameTextCopy, state.fileNameText); + + state.filterExt[0] = '\0'; + //strcpy(state.filterExt, "all"); + + state.dirFiles.count = 0; + + return state; +} + +// Update and draw file dialog +void GuiWindowFileDialog(GuiWindowFileDialogState* state) +{ + if (state->windowActive) + { + // Update window dragging + //---------------------------------------------------------------------------------------- + if (state->supportDrag) + { + Vector2 mousePosition = GetMousePosition(); + + if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) + { + // Window can be dragged from the top window bar + if (CheckCollisionPointRec(mousePosition, { state->windowBounds.x, state->windowBounds.y, (float)state->windowBounds.width, RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT })) + { + state->dragMode = true; + state->panOffset.x = mousePosition.x - state->windowBounds.x; + state->panOffset.y = mousePosition.y - state->windowBounds.y; + } + } + + if (state->dragMode) + { + state->windowBounds.x = (mousePosition.x - state->panOffset.x); + state->windowBounds.y = (mousePosition.y - state->panOffset.y); + + // Check screen limits to avoid moving out of screen + if (state->windowBounds.x < 0) state->windowBounds.x = 0; + else if (state->windowBounds.x > (GetScreenWidth() - state->windowBounds.width)) state->windowBounds.x = GetScreenWidth() - state->windowBounds.width; + + if (state->windowBounds.y < 0) state->windowBounds.y = 0; + else if (state->windowBounds.y > (GetScreenHeight() - state->windowBounds.height)) state->windowBounds.y = GetScreenHeight() - state->windowBounds.height; + + if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) state->dragMode = false; + } + } + //---------------------------------------------------------------------------------------- + + // Load dirFilesIcon and state->dirFiles lazily on windows open + // NOTE: They are automatically unloaded at fileDialog closing + //---------------------------------------------------------------------------------------- + if (dirFilesIcon == NULL) + { + dirFilesIcon = (FileInfo*)RL_CALLOC(MAX_DIRECTORY_FILES, sizeof(FileInfo)); // Max files to read + for (int i = 0; i < MAX_DIRECTORY_FILES; i++) dirFilesIcon[i] = (char*)RL_CALLOC(MAX_ICON_PATH_LENGTH, 1); // Max file name length + } + + // Load current directory files + if (state->dirFiles.paths == NULL) ReloadDirectoryFiles(state); + //---------------------------------------------------------------------------------------- + + // Draw window and controls + //---------------------------------------------------------------------------------------- + state->windowActive = !GuiWindowBox(state->windowBounds, "#198# Select File Dialog"); + + // Draw previous directory button + logic + if (GuiButton({ state->windowBounds.x + state->windowBounds.width - 48, state->windowBounds.y + 24 + 12, 40, 24 }, "< ..")) + { + // Move dir path one level up + strcpy(state->dirPathText, GetPrevDirectoryPath(state->dirPathText)); + + // Reload directory files (frees previous list) + ReloadDirectoryFiles(state); + + state->filesListActive = -1; + memset(state->fileNameText, 0, 1024); + memset(state->fileNameTextCopy, 0, 1024); + } + + // Draw current directory text box info + path editing logic + if (GuiTextBox({ state->windowBounds.x + 8, state->windowBounds.y + 24 + 12, state->windowBounds.width - 48 - 16, 24 }, state->dirPathText, 1024, state->dirPathEditMode)) + { + if (state->dirPathEditMode) + { + // Verify if a valid path has been introduced + if (DirectoryExists(state->dirPathText)) + { + // Reload directory files (frees previous list) + ReloadDirectoryFiles(state); + + strcpy(state->dirPathTextCopy, state->dirPathText); + } + else strcpy(state->dirPathText, state->dirPathTextCopy); + } + + state->dirPathEditMode = !state->dirPathEditMode; + } + + // List view elements are aligned left + int prevTextAlignment = GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT); + int prevElementsHeight = GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT); + GuiSetStyle(LISTVIEW, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT); + GuiSetStyle(LISTVIEW, LIST_ITEMS_HEIGHT, 24); +# if defined(USE_CUSTOM_LISTVIEW_FILEINFO) + state->filesListActive = GuiListViewFiles({ state->position.x + 8, state->position.y + 48 + 20, state->windowBounds.width - 16, state->windowBounds.height - 60 - 16 - 68 }, fileInfo, state->dirFiles.count, &state->itemFocused, &state->filesListScrollIndex, state->filesListActive); +# else + GuiListViewEx({ state->windowBounds.x + 8, state->windowBounds.y + 48 + 20, state->windowBounds.width - 16, state->windowBounds.height - 60 - 16 - 68 }, + (const char**)dirFilesIcon, state->dirFiles.count, &state->filesListScrollIndex, &state->filesListActive, &state->itemFocused); +# endif + GuiSetStyle(LISTVIEW, TEXT_ALIGNMENT, prevTextAlignment); + GuiSetStyle(LISTVIEW, LIST_ITEMS_HEIGHT, prevElementsHeight); + + // Check if a path has been selected, if it is a directory, move to that directory (and reload paths) + if ((state->filesListActive >= 0) && (state->filesListActive != state->prevFilesListActive)) + //&& (IsMouseButtonPressed(MOUSE_LEFT_BUTTON) || IsKeyPressed(KEY_ENTER) || IsKeyPressed(KEY_DPAD_A))) + { + strcpy(state->fileNameText, GetFileName(state->dirFiles.paths[state->filesListActive])); + + if (DirectoryExists(TextFormat("%s/%s", state->dirPathText, state->fileNameText))) + { + if (TextIsEqual(state->fileNameText, "..")) strcpy(state->dirPathText, GetPrevDirectoryPath(state->dirPathText)); + else strcpy(state->dirPathText, TextFormat("%s/%s", (strcmp(state->dirPathText, "/") == 0) ? "" : state->dirPathText, state->fileNameText)); + + strcpy(state->dirPathTextCopy, state->dirPathText); + + // Reload directory files (frees previous list) + ReloadDirectoryFiles(state); + + strcpy(state->dirPathTextCopy, state->dirPathText); + + state->filesListActive = -1; + strcpy(state->fileNameText, "\0"); + strcpy(state->fileNameTextCopy, state->fileNameText); + } + + state->prevFilesListActive = state->filesListActive; + } + + // Draw bottom controls + //-------------------------------------------------------------------------------------- + GuiLabel({ state->windowBounds.x + 8, state->windowBounds.y + state->windowBounds.height - 68, 60, 24 }, "File name:"); + if (GuiTextBox({ state->windowBounds.x + 72, state->windowBounds.y + state->windowBounds.height - 68, state->windowBounds.width - 184, 24 }, state->fileNameText, 128, state->fileNameEditMode)) + { + if (*state->fileNameText) + { + // Verify if a valid filename has been introduced + if (FileExists(TextFormat("%s/%s", state->dirPathText, state->fileNameText))) + { + // Select filename from list view + for (int i = 0; i < state->dirFiles.count; i++) + { + if (TextIsEqual(state->fileNameText, state->dirFiles.paths[i])) + { + state->filesListActive = i; + strcpy(state->fileNameTextCopy, state->fileNameText); + break; + } + } + } + else if (!state->saveFileMode) + { + strcpy(state->fileNameText, state->fileNameTextCopy); + } + } + + state->fileNameEditMode = !state->fileNameEditMode; + } + + GuiLabel({ state->windowBounds.x + 8, state->windowBounds.y + state->windowBounds.height - 24 - 12, 68, 24 }, "File filter:"); + GuiComboBox({ state->windowBounds.x + 72, state->windowBounds.y + state->windowBounds.height - 24 - 12, state->windowBounds.width - 184, 24 }, "All files", &state->fileTypeActive); + + state->SelectFilePressed = GuiButton({ state->windowBounds.x + state->windowBounds.width - 96 - 8, state->windowBounds.y + state->windowBounds.height - 68, 96, 24 }, "Select"); + + if (GuiButton({ state->windowBounds.x + state->windowBounds.width - 96 - 8, state->windowBounds.y + state->windowBounds.height - 24 - 12, 96, 24 }, "Cancel")) state->windowActive = false; + //-------------------------------------------------------------------------------------- + + // Exit on file selected + if (state->SelectFilePressed) state->windowActive = false; + + // File dialog has been closed, free all memory before exit + if (!state->windowActive) + { + // Free dirFilesIcon memory + for (int i = 0; i < MAX_DIRECTORY_FILES; i++) RL_FREE(dirFilesIcon[i]); + + RL_FREE(dirFilesIcon); + dirFilesIcon = NULL; + + // Unload directory file paths + UnloadDirectoryFiles(state->dirFiles); + + // Reset state variables + state->dirFiles.count = 0; + state->dirFiles.capacity = 0; + state->dirFiles.paths = NULL; + } + } +} + +// Compare two files from a directory +static inline int FileCompare(const char* d1, const char* d2, const char* dir) +{ + const bool b1 = DirectoryExists(TextFormat("%s/%s", dir, d1)); + const bool b2 = DirectoryExists(TextFormat("%s/%s", dir, d2)); + + if (b1 && !b2) return -1; + if (!b1 && b2) return 1; + + if (!FileExists(TextFormat("%s/%s", dir, d1))) return 1; + if (!FileExists(TextFormat("%s/%s", dir, d2))) return -1; + + return strcmp(d1, d2); +} + +// Read files in new path +static void ReloadDirectoryFiles(GuiWindowFileDialogState* state) +{ + UnloadDirectoryFiles(state->dirFiles); + + state->dirFiles = LoadDirectoryFilesEx(state->dirPathText, (state->filterExt[0] == '\0') ? NULL : state->filterExt, false); + state->itemFocused = 0; + + // Reset dirFilesIcon memory + for (int i = 0; i < MAX_DIRECTORY_FILES; i++) memset(dirFilesIcon[i], 0, MAX_ICON_PATH_LENGTH); + + // Copy paths as icon + fileNames into dirFilesIcon + for (int i = 0; i < state->dirFiles.count; i++) + { + if (IsPathFile(state->dirFiles.paths[i])) + { + // Path is a file, a file icon for convenience (for some recognized extensions) + if (IsFileExtension(state->dirFiles.paths[i], ".png;.bmp;.tga;.gif;.jpg;.jpeg;.psd;.hdr;.qoi;.dds;.pkm;.ktx;.pvr;.astc")) + { + strcpy(dirFilesIcon[i], TextFormat("#12#%s", GetFileName(state->dirFiles.paths[i]))); + } + else if (IsFileExtension(state->dirFiles.paths[i], ".wav;.mp3;.ogg;.flac;.xm;.mod;.it;.wma;.aiff")) + { + strcpy(dirFilesIcon[i], TextFormat("#11#%s", GetFileName(state->dirFiles.paths[i]))); + } + else if (IsFileExtension(state->dirFiles.paths[i], ".txt;.info;.md;.nfo;.xml;.json;.c;.cpp;.cs;.lua;.py;.glsl;.vs;.fs")) + { + strcpy(dirFilesIcon[i], TextFormat("#10#%s", GetFileName(state->dirFiles.paths[i]))); + } + else if (IsFileExtension(state->dirFiles.paths[i], ".exe;.bin;.raw;.msi")) + { + strcpy(dirFilesIcon[i], TextFormat("#200#%s", GetFileName(state->dirFiles.paths[i]))); + } + else strcpy(dirFilesIcon[i], TextFormat("#218#%s", GetFileName(state->dirFiles.paths[i]))); + } + else + { + // Path is a directory, add a directory icon + strcpy(dirFilesIcon[i], TextFormat("#1#%s", GetFileName(state->dirFiles.paths[i]))); + } + } +} + +#if defined(USE_CUSTOM_LISTVIEW_FILEINFO) +// List View control for files info with extended parameters +static int GuiListViewFiles(Rectangle bounds, FileInfo* files, int count, int* focus, int* scrollIndex, int* active) +{ + int result = 0; + GuiState state = guiState; + int itemFocused = (focus == NULL) ? -1 : *focus; + int itemSelected = *active; + + // Check if we need a scroll bar + bool useScrollBar = false; + if ((GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT) + GuiGetStyle(LISTVIEW, LIST_ITEMS_PADDING)) * count > bounds.height) useScrollBar = true; + + // Define base item rectangle [0] + Rectangle itemBounds = { 0 }; + itemBounds.x = bounds.x + GuiGetStyle(LISTVIEW, LIST_ITEMS_PADDING); + itemBounds.y = bounds.y + GuiGetStyle(LISTVIEW, LIST_ITEMS_PADDING) + GuiGetStyle(DEFAULT, BORDER_WIDTH); + itemBounds.width = bounds.width - 2 * GuiGetStyle(LISTVIEW, LIST_ITEMS_PADDING) - GuiGetStyle(DEFAULT, BORDER_WIDTH); + itemBounds.height = GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT); + if (useScrollBar) itemBounds.width -= GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH); + + // Get items on the list + int visibleItems = bounds.height / (GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT) + GuiGetStyle(LISTVIEW, LIST_ITEMS_PADDING)); + if (visibleItems > count) visibleItems = count; + + int startIndex = (scrollIndex == NULL) ? 0 : *scrollIndex; + if ((startIndex < 0) || (startIndex > (count - visibleItems))) startIndex = 0; + int endIndex = startIndex + visibleItems; + + // Update control + //-------------------------------------------------------------------- + if ((state != GUI_STATE_DISABLED) && !guiLocked) + { + Vector2 mousePoint = GetMousePosition(); + + // Check mouse inside list view + if (CheckCollisionPointRec(mousePoint, bounds)) + { + state = GUI_STATE_FOCUSED; + + // Check focused and selected item + for (int i = 0; i < visibleItems; i++) + { + if (CheckCollisionPointRec(mousePoint, itemBounds)) + { + itemFocused = startIndex + i; + if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) itemSelected = startIndex + i; + break; + } + + // Update item rectangle y position for next item + itemBounds.y += (GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT) + GuiGetStyle(LISTVIEW, LIST_ITEMS_PADDING)); + } + + if (useScrollBar) + { + int wheelMove = GetMouseWheelMove(); + startIndex -= wheelMove; + + if (startIndex < 0) startIndex = 0; + else if (startIndex > (count - visibleItems)) startIndex = count - visibleItems; + + endIndex = startIndex + visibleItems; + if (endIndex > count) endIndex = count; + } + } + else itemFocused = -1; + + // Reset item rectangle y to [0] + itemBounds.y = bounds.y + GuiGetStyle(LISTVIEW, LIST_ITEMS_PADDING) + GuiGetStyle(DEFAULT, BORDER_WIDTH); + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + DrawRectangleRec(bounds, GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR))); // Draw background + DrawRectangleLinesEx(bounds, GuiGetStyle(DEFAULT, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER + state * 3)), guiAlpha)); + + // TODO: Draw list view header with file sections: icon+name | size | type | modTime + + // Draw visible items + for (int i = 0; i < visibleItems; i++) + { + if (state == GUI_STATE_DISABLED) + { + if ((startIndex + i) == itemSelected) + { + DrawRectangleRec(itemBounds, Fade(GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_DISABLED)), guiAlpha)); + DrawRectangleLinesEx(itemBounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_DISABLED)), guiAlpha)); + } + + // TODO: Draw full file info line: icon+name | size | type | modTime + + GuiDrawText(files[startIndex + i].name, GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_DISABLED)), guiAlpha)); + } + else + { + if ((startIndex + i) == itemSelected) + { + // Draw item selected + DrawRectangleRec(itemBounds, Fade(GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_PRESSED)), guiAlpha)); + DrawRectangleLinesEx(itemBounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_PRESSED)), guiAlpha)); + + GuiDrawText(files[startIndex + i].name, GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_PRESSED)), guiAlpha)); + } + else if ((startIndex + i) == itemFocused) + { + // Draw item focused + DrawRectangleRec(itemBounds, Fade(GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_FOCUSED)), guiAlpha)); + DrawRectangleLinesEx(itemBounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_FOCUSED)), guiAlpha)); + + GuiDrawText(files[startIndex + i].name, GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_FOCUSED)), guiAlpha)); + } + else + { + // Draw item normal + GuiDrawText(files[startIndex + i].name, GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_NORMAL)), guiAlpha)); + } + } + + // Update item rectangle y position for next item + itemBounds.y += (GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT) + GuiGetStyle(LISTVIEW, LIST_ITEMS_PADDING)); + } + + if (useScrollBar) + { + Rectangle scrollBarBounds = { + bounds.x + bounds.width - GuiGetStyle(LISTVIEW, BORDER_WIDTH) - GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH), + bounds.y + GuiGetStyle(LISTVIEW, BORDER_WIDTH), (float)GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH), + bounds.height - 2 * GuiGetStyle(DEFAULT, BORDER_WIDTH) + }; + + // Calculate percentage of visible items and apply same percentage to scrollbar + float percentVisible = (float)(endIndex - startIndex) / count; + float sliderSize = bounds.height * percentVisible; + + int prevSliderSize = GuiGetStyle(SCROLLBAR, SLIDER_WIDTH); // Save default slider size + int prevScrollSpeed = GuiGetStyle(SCROLLBAR, SCROLL_SPEED); // Save default scroll speed + GuiSetStyle(SCROLLBAR, SLIDER_WIDTH, sliderSize); // Change slider size + GuiSetStyle(SCROLLBAR, SCROLL_SPEED, count - visibleItems); // Change scroll speed + + startIndex = GuiScrollBar(scrollBarBounds, startIndex, 0, count - visibleItems); + + GuiSetStyle(SCROLLBAR, SCROLL_SPEED, prevScrollSpeed); // Reset scroll speed to default + GuiSetStyle(SCROLLBAR, SLIDER_WIDTH, prevSliderSize); // Reset slider size to default + } + //-------------------------------------------------------------------- + + if (focus != NULL) *focus = itemFocused; + if (scrollIndex != NULL) *scrollIndex = startIndex; + + *active = itemSelected; + return result; +} +#endif // USE_CUSTOM_LISTVIEW_FILEINFO + +#endif // GUI_FILE_DIALOG_IMPLEMENTATION diff --git a/include/raylibs/raylib-cpp.hpp b/include/raylibs/raylib-cpp.hpp new file mode 100644 index 0000000..1ac27ba --- /dev/null +++ b/include/raylibs/raylib-cpp.hpp @@ -0,0 +1,80 @@ +/** + * [raylib-cpp](https://github.com/RobLoach/raylib-cpp) is a C++ wrapper library for raylib, a simple and easy-to-use library to enjoy videogames programming. This C++ header provides object-oriented wrappers around raylib's struct interfaces. + * + * @see raylib namespace for a list of all available classes. + * @mainpage raylib-cpp + * @include core_basic_window.cpp + * @author Rob Loach (RobLoach) + * @copyright zlib/libpng + * + * raylib-cpp is licensed under an unmodified zlib/libpng license, which is an OSI-certified, + * BSD-like license that allows static linking with closed source software: + * + * Copyright 2020 Rob Loach (RobLoach) + * + * This software is provided "as-is", without any express or implied warranty. In no event + * will the authors be held liable for any damages arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, including commercial + * applications, and to alter it and redistribute it freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not claim that you + * wrote the original software. If you use this software in a product, an acknowledgment + * in the product documentation would be appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not be misrepresented + * as being the original software. + * + * 3. This notice may not be removed or altered from any source distribution. + */ + +#ifndef RAYLIB_CPP_INCLUDE_RAYLIB_CPP_HPP_ +#define RAYLIB_CPP_INCLUDE_RAYLIB_CPP_HPP_ + +#include "./AudioDevice.hpp" +#include "./AudioStream.hpp" +#include "./AutomationEventList.hpp" +#include "./BoundingBox.hpp" +#include "./Camera2D.hpp" +#include "./Camera3D.hpp" +#include "./Color.hpp" +#include "./FileData.hpp" +#include "./FileText.hpp" +#include "./Font.hpp" +#include "./Functions.hpp" +#include "./Gamepad.hpp" +#include "./Image.hpp" +#include "./Keyboard.hpp" +#include "./Material.hpp" +#include "./Matrix.hpp" +#include "./Mesh.hpp" +#include "./Model.hpp" +#include "./ModelAnimation.hpp" +#include "./Mouse.hpp" +#include "./Music.hpp" +#include "./Ray.hpp" +#include "./RayCollision.hpp" +#include "./RaylibException.hpp" +#include "./Rectangle.hpp" +#include "./RenderTexture.hpp" +#include "./Shader.hpp" +#include "./Sound.hpp" +#include "./Text.hpp" +#include "./Texture.hpp" +#include "./TextureUnmanaged.hpp" +#include "./Touch.hpp" +#include "./Vector2.hpp" +#include "./Vector3.hpp" +#include "./Vector4.hpp" +#include "./VrStereoConfig.hpp" +#include "./Wave.hpp" +#include "./Window.hpp" + +/** + * All raylib-cpp classes and functions appear in the raylib namespace. + */ +namespace raylib { +// Nothing. +} // namespace raylib + +#endif // RAYLIB_CPP_INCLUDE_RAYLIB_CPP_HPP_ diff --git a/res/images/sva-logo.png b/res/images/sva-logo.png deleted file mode 100644 index dabd05d..0000000 Binary files a/res/images/sva-logo.png and /dev/null differ diff --git a/res/lua/bubble_sortiva.lua b/res/lua/bubble_sortiva.lua new file mode 100644 index 0000000..1ecb004 --- /dev/null +++ b/res/lua/bubble_sortiva.lua @@ -0,0 +1,25 @@ + +local bubble_sort = Future:new() + +function bubble_sort:poll(array) + local n = array:size() + self.state[n] = self.state[n] or n + if n == 0 then + return false + end + + local swapped = false + for i = 1, n - 1 do + if array.at(i-1) > array.at(i) then + array.swap(i-1, i) + swapped = true + end + end + self.state.n = self.state.n - 1 + + return not swapped +end + +function make_available(sorter_list) + table.insert(sorter_list, "bubble_sort") +end \ No newline at end of file diff --git a/res/lua/quick_sortiva.lua b/res/lua/quick_sortiva.lua new file mode 100644 index 0000000..654cf6f --- /dev/null +++ b/res/lua/quick_sortiva.lua @@ -0,0 +1,59 @@ +local quick_sort = Future:new() + +function quick_sort:sort(A, lo, hi, i) + if lo >= 0 and lo < hi then + lt, gt = partition(A, lo, hi) -- Multiple return values + + if #self.state["lt"] >= i and #self.state["gt"] >= i then + self:sort(A, lo, self.state["lt"][i] - 1, i+1) + self:sort(A, self.state["gt"][i] + 1, hi, i+1) + else + table.insert(self.state["lt"], lt) + table.insert(self.state["gt"], gt) + return false + end + end + return true +end + +function quick_sort:poll(array) + return self:sort(array, 0, array:size(),0) +end + + +-- Divides array into three partitions +function partition(A, lo, hi) + -- Pivot value + local pivot = A.at((lo + hi) / 2) -- Choose the middle element as the pivot (integer division) + + -- Lesser, equal and greater index + local lt = lo + local eq = lo + local gt = hi + + -- Iterate and compare all elements with the pivot + while eq <= gt do + if A[eq] < pivot then + -- Swap the elements at the equal and lesser indices + A:swap(eq, lt) + -- Increase lesser index + lt = lt + 1 + -- Increase equal index + eq = eq + 1 + elseif A[eq] > pivot then + -- Swap the elements at the equal and greater indices + A:swap(eq, gt) + -- Decrease greater index + gt = gt - 1 + else -- if A[eq] = pivot then + -- Increase equal index + eq = eq + 1 + end + end + -- Return lesser and greater indices + return lt, gt +end + +function make_available(sorter_list) + table.insert(sorter_list, "quick_sort") +end \ No newline at end of file diff --git a/src/LuaSortList.hpp b/src/LuaSortList.hpp new file mode 100644 index 0000000..5fc6255 --- /dev/null +++ b/src/LuaSortList.hpp @@ -0,0 +1,33 @@ +#pragma once +#include + +class LuaSortList +{ +public: + std::vector list; + + void swap(size_t i1, size_t i2) + { + std::swap(list[i1], list[i2]); + } + + size_t size() const + { + return list.size(); + } + + uint16_t at(size_t i) const + { + return list[i]; + } + + bool sorted() const + { + for (int i = 1; i < list.size(); ++i) + { + if (list[i - 1] > list[i]) return false; + } + return true; + } + +}; diff --git a/src/Renderer.cpp b/src/Renderer.cpp deleted file mode 100644 index ca38bab..0000000 --- a/src/Renderer.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "Renderer.h" diff --git a/src/Renderer.h b/src/Renderer.h deleted file mode 100644 index fa8c6d8..0000000 --- a/src/Renderer.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -class Renderer -{ -public: - -}; diff --git a/src/SortingTester.cpp b/src/SortingTester.cpp deleted file mode 100644 index aec1fec..0000000 --- a/src/SortingTester.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include "SortingTester.h" - - -SortingTester::SortingTester() : list(10) -{ - std::cout << "Loading lua" << std::endl; - - lua.lua.open_libraries(sol::lib::coroutine, sol::lib::table); - lua.lua.new_usertype("List", - "size", &SortingTester::list_size, - "swap", &SortingTester::list_swap, - "greater", &SortingTester::list_greater, - "less", &SortingTester::list_less, - "equal", &SortingTester::list_equal - ); - - lua.lua["list"] = this; -} - -void SortingTester::load(const std::string& script) -{ - if(lua.load(script) != safesol::LuaResultType::SUCCESS) - { - std::cerr << "Error loading file" << std::endl; - std::terminate(); - } -} - -#include "internal_script.hpp" - -void SortingTester::load_internal() -{ - if (lua.script(std_intern_sorting_script) != safesol::LuaResultType::SUCCESS) - { - std::cerr << "Error loading internal script" << std::endl; - std::terminate(); - } -} - -void SortingTester::populate(const populate_function& f) -{ - for (size_t i = 0; i < list.size(); i++) - { - f(i, list[i]); - } - std::cout << list << std::endl; -} diff --git a/src/SortingTester.h b/src/SortingTester.h deleted file mode 100644 index b823a8c..0000000 --- a/src/SortingTester.h +++ /dev/null @@ -1,143 +0,0 @@ -#pragma once -#define SVA_LIST_CALLBACKS -#include - -#include -#include "lua/safesol.h" -#include - - -class SortingTester -{ - List list; - - safesol lua; - - Timer timer = Timer(Timer::State::paused); - - using populate_function = std::function; - - template - sol::protected_function_result run_function(const sol::protected_function& function, Args&&... args) - { - timer.set_state(Timer::State::running); - sol::protected_function_result result = function(std::forward(args)...); - timer.set_state(Timer::State::paused); - return result; - } - - - bool list_greater(size_t index1, size_t index2) - { - if (active_result) - ++active_result->count_greater; - return list[index1] > list[index2]; - } - - bool list_less(size_t index1, size_t index2) - { - if (active_result) - ++active_result->count_less; - return list[index1] < list[index2]; - } - - bool list_equal(size_t index1, size_t index2) - { - if (active_result) - ++active_result->count_equal; - return list[index1] == list[index2]; - } - - void list_swap(size_t index1, size_t index2) - { - if (active_result) - ++active_result->count_swaps; - // list[index1] ^= list[index2]; - // list[index2] ^= list[index1]; - // list[index1] ^= list[index2]; - auto tmp = list[index1]; - list[index1] = list[index2]; - list[index2] = tmp; - } - - size_t list_size() const - { - return list.size(); - } - - bool is_sorted() - { - if (list.size() <= 1) - return true; - for (size_t i = 1; i < list.size(); i++) - { - if (list[i - 1] > list[i]) - return false; - } - return true; - } - - - -public: - struct run_result - { - timer_result timer; - // counts - u64 count_swaps = 0; - u64 count_greater = 0; - u64 count_less = 0; - u64 count_equal = 0; - // counts for comparisons - u64 count_comparisons = 0; - }; -private: - run_result* active_result = nullptr; - -public: - - SortingTester(); - - void load(const std::string& script); - void load_internal(); - - template - run_result run(const std::string& function_name, Args&&... args) - { - run_result result; - timer.result.emplace(&result.timer); - timer.reset(Timer::State::paused); - active_result = &result; - - // Create a bound member function using std::bind - auto bound_run_function = std::bind(&SortingTester::run_function, - this, - std::placeholders::_1, - std::forward(args)...); - - safesol::LuaResultType runstate = lua.run_on_caller(function_name, bound_run_function); - - if (runstate != safesol::LuaResultType::SUCCESS) - { - std::cerr << "Error running function \"" << function_name << "\": " << static_cast(runstate) << std::endl; - exit(static_cast(runstate)); - } - - if (!is_sorted()) - { - std::cerr << "[ERROR] The algorithm \"" << function_name << "\" did not sort the list" << std::endl; - std::cerr << list << std::endl; - std::flush(std::cerr); - std::terminate(); - } - - result.count_comparisons = result.count_equal + result.count_greater + result.count_less; - - active_result = nullptr; - return result; - } - - - - void populate(const populate_function& f); -}; diff --git a/src/gui/drawing_helper.cpp b/src/gui/drawing_helper.cpp deleted file mode 100644 index 3ec9b0b..0000000 --- a/src/gui/drawing_helper.cpp +++ /dev/null @@ -1,60 +0,0 @@ -#include "drawing_helper.hpp" - -namespace sva -{ - std::vector split(const std::string& str, char delim) - { - std::vector tokens; - std::string token; - std::istringstream tokenStream(str); - while (std::getline(tokenStream, token, delim)) - { - tokens.push_back(token); - } - return tokens; - } -} - - -Color sva::GetThemeColor(GuiControlProperty property) -{ - return GetColor(GuiGetStyle(DEFAULT, TEXT_COLOR_NORMAL)); -} - -void sva::DrawText(const std::string& text, vec2i pos, int size, Color color, TEXT_ALIGNMENT alignment) -{ - switch (alignment) - { - case TEXT_ALIGN_LEFT: - return DrawText(text.c_str(), pos.x, pos.y, size, color); - case TEXT_ALIGN_RIGHT: - if (text.find('\n') != std::string::npos) - { - std::vector lines = split(text, '\n'); - for (auto& line : lines) - { - pos.x -= MeasureText(line.c_str(), size); - DrawText(line.c_str(), pos.x, pos.y, size, color); - pos.y += size; - } - return; - } - pos.x -= MeasureText(text.c_str(), size); - return DrawText(text.c_str(), pos.x, pos.y, size, color); - - case TEXT_ALIGN_CENTER: - if (text.find('\n') != std::string::npos) - { - std::vector lines = sva::split(text, '\n'); - for (auto& line : lines) - { - pos.x -= MeasureText(line.c_str(), size) / 2; - DrawText(line.c_str(), pos.x, pos.y, size, color); - pos.y += size; - } - return; - } - pos.x -= MeasureText(text.c_str(), size) / 2; - DrawText(text.c_str(), pos.x, pos.y, size, color); - } -} diff --git a/src/gui/drawing_helper.hpp b/src/gui/drawing_helper.hpp deleted file mode 100644 index 92c74ed..0000000 --- a/src/gui/drawing_helper.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include - -namespace sva -{ - template - struct TVector2 - { - T x, y; - }; - typedef TVector2 vec2i; - typedef TVector2 vec2f; - - enum TEXT_ALIGNMENT - { - TEXT_ALIGN_LEFT, - TEXT_ALIGN_CENTER, - TEXT_ALIGN_RIGHT - }; - - - Color GetThemeColor(GuiControlProperty property); - - void DrawText(const std::string& text, vec2i pos, int size, Color color = GetColor(GuiGetStyle(DEFAULT, TEXT_COLOR_NORMAL)), TEXT_ALIGNMENT alignment = TEXT_ALIGN_LEFT); -} \ No newline at end of file diff --git a/src/gui/raygui.cpp b/src/gui/raygui.cpp deleted file mode 100644 index 3052e1a..0000000 --- a/src/gui/raygui.cpp +++ /dev/null @@ -1,2 +0,0 @@ -#define RAYGUI_IMPLEMENTATION -#include \ No newline at end of file diff --git a/src/gui/themes/dark.h b/src/gui/themes/dark.h deleted file mode 100644 index 6b1f0e0..0000000 --- a/src/gui/themes/dark.h +++ /dev/null @@ -1,42 +0,0 @@ -////////////////////////////////////////////////////////////////////////////////// -// // -// StyleAsCode exporter v2.0 - Style data exported as a values array // -// // -// USAGE: On init call: GuiLoadStyleDark(); // -// // -// more info and bugs-report: github.com/raysan5/raygui // -// feedback and support: ray[at]raylibtech.com // -// // -// Copyright (c) 2020-2023 raylib technologies (@raylibtech) // -// // -////////////////////////////////////////////////////////////////////////////////// - -#define DARK_STYLE_PROPS_COUNT 8 - -// Custom style name: dark -static const GuiStyleProp darkStyleProps[DARK_STYLE_PROPS_COUNT] = { - { 0, 0, 0x7b7b7bff }, // DEFAULT_BORDER_COLOR_NORMAL - { 0, 1, 0x595959ff }, // DEFAULT_BASE_COLOR_NORMAL - { 0, 2, 0xdededeff }, // DEFAULT_TEXT_COLOR_NORMAL - { 0, 9, 0x232323ff }, // DEFAULT_BORDER_COLOR_DISABLED - { 0, 10, 0x606060ff }, // DEFAULT_BASE_COLOR_DISABLED - { 0, 11, 0x9f9f9fff }, // DEFAULT_TEXT_COLOR_DISABLED - { 0, 18, 0x68cbd0ff }, // DEFAULT_LINE_COLOR - { 0, 19, 0x262626ff }, // DEFAULT_BACKGROUND_COLOR -}; - -// Style loading function: dark -static void GuiLoadStyleDark(void) -{ - // Load style properties provided - // NOTE: Default properties are propagated - for (int i = 0; i < DARK_STYLE_PROPS_COUNT; i++) - { - GuiSetStyle(darkStyleProps[i].controlId, darkStyleProps[i].propertyId, darkStyleProps[i].propertyValue); - } - - //----------------------------------------------------------------- - - // TODO: Custom user style setup: Set specific properties here (if required) - // i.e. Controls specific BORDER_WIDTH, TEXT_PADDING, TEXT_ALIGNMENT -} diff --git a/src/internal_script.hpp b/src/internal_script.hpp deleted file mode 100644 index 6055163..0000000 --- a/src/internal_script.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -inline const char* std_intern_sorting_script = R"( --- bubble sort -function bubble_sort() - local isSorted = false - while not isSorted do - local movedElements = 0 - for x = 0, list:size() - 2 do - if list:greater(x, x+1) then - movedElements = movedElements + 1 - list:swap(x, x+1) - end - end - if movedElements == 0 then - isSorted = true - end - end -end - -function do_nothing() -end - --- other functions - - -)"; \ No newline at end of file diff --git a/src/libimpl.cpp b/src/libimpl.cpp new file mode 100644 index 0000000..565a7a9 --- /dev/null +++ b/src/libimpl.cpp @@ -0,0 +1,7 @@ +#define RAYGUI_IMPLEMENTATION +#include + +#undef RAYGUI_IMPLEMENTATION +#define GUI_WINDOW_FILE_DIALOG_IMPLEMENTATION +#define RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT 24 +#include \ No newline at end of file diff --git a/src/lua/Future.lua b/src/lua/Future.lua new file mode 100644 index 0000000..61e9f81 --- /dev/null +++ b/src/lua/Future.lua @@ -0,0 +1,16 @@ +Future = { + state = {} +} + +function Future:new(state) + local f = {} + setmetatable(f, self) + self.__index = self + f["state"] = state or {} + + return f +end + +function Future:pull() + return true +end \ No newline at end of file diff --git a/src/lua/FutureLua.hpp b/src/lua/FutureLua.hpp new file mode 100644 index 0000000..1d2b902 --- /dev/null +++ b/src/lua/FutureLua.hpp @@ -0,0 +1,35 @@ +#pragma once + +/* +static inline const char lua_future_class[] = R"( +Future = { + state = {} +} + +function Future:new(state) + local f = {} + setmetatable(f, self) + self.__index = self + f["state"] = state or {} + + return f +end + +function Future:poll() + return true +end + +function Future.get(t, ...) + return t:poll(unpack(arg)) +end +)"; +*/ + +template +class Future +{ +public: + virtual ~Future() = default; + virtual bool poll() = 0; + virtual T& get() = 0; +}; \ No newline at end of file diff --git a/src/lua/safesol.cpp b/src/lua/safesol.cpp deleted file mode 100644 index fa9849b..0000000 --- a/src/lua/safesol.cpp +++ /dev/null @@ -1,61 +0,0 @@ -#include "safesol.h" - -safesol::LuaResultType safesol::script(const std::string& script) -{ - sol::load_result result = lua.load(script); - if (!result.valid()) { - sol::error err = result; - std::cout << "Error loading script: " << err.what() << std::endl; - return LuaResultType::LOAD_ERROR; - } - - sol::protected_function_result script_result = result(); - - if (!script_result.valid()) { - sol::error err = script_result; - std::cout << "Error running script: " << err.what() << std::endl; - return LuaResultType::SCRIPT_ERROR; - } - - return LuaResultType::SUCCESS; -} - -safesol::LuaResultType safesol::script(const char* script) -{ - sol::load_result result = lua.load(script); - if (!result.valid()) { - sol::error err = result; - std::cout << "Error loading script: " << err.what() << std::endl; - return LuaResultType::LOAD_ERROR; - } - - sol::protected_function_result script_result = result(); - - if (!script_result.valid()) { - sol::error err = script_result; - std::cout << "Error running script: " << err.what() << std::endl; - return LuaResultType::SCRIPT_ERROR; - } - - return LuaResultType::SUCCESS; -} - -safesol::LuaResultType safesol::load(const std::string& file) -{ - sol::load_result result = lua.load_file(file); - if (!result.valid()) { - sol::error err = result; - std::cout << "Error loading file: " << err.what() << std::endl; - return LuaResultType::LOAD_ERROR; - } - - sol::protected_function_result script_result = result(); - - if (!script_result.valid()) { - sol::error err = script_result; - std::cout << "Error running script: " << err.what() << std::endl; - return LuaResultType::SCRIPT_ERROR; - } - - return LuaResultType::SUCCESS; -} diff --git a/src/lua/safesol.h b/src/lua/safesol.h deleted file mode 100644 index 14f6dea..0000000 --- a/src/lua/safesol.h +++ /dev/null @@ -1,59 +0,0 @@ -#pragma once -#include -#include - -class safesol -{ -public: - enum class LuaResultType : u8 - { - SUCCESS = 0, - LOAD_ERROR = 1, - SCRIPT_ERROR, - RUN_ERROR, - UNKNOWN_ERROR - }; - - sol::state lua; - - - LuaResultType script(const std::string& script); - LuaResultType script(const char* script); - - LuaResultType load(const std::string& file); - - template - sol::protected_function_result default_caller(const sol::protected_function& function, Args&&... args) - { - return function(std::forward(args)...); - } - - template - LuaResultType run(const std::string& function_name, Args&&... args) - { - return run_on_caller(function_name, default_caller, std::forward(args)...); - } - - template> - LuaResultType run_on_caller(const std::string& function_name, const Caller& caller, Args&&... args) - { - std::cout << "Looking for function: " << function_name << std::endl; - - auto function = lua[function_name]; - if (!function.valid()) { - std::cout << "Error: function " << function_name << " not found" << std::endl; - return LuaResultType::RUN_ERROR; - } - - std::cout << "Found function, attempting to run" << std::endl; - - auto result = caller(function, std::forward(args)...); - if (!result.valid()) { - sol::error err = result; - std::cout << "Error running function " << function_name << ": " << err.what() << std::endl; - return LuaResultType::RUN_ERROR; - } - - return LuaResultType::SUCCESS; - } -}; diff --git a/src/main.cpp b/src/main.cpp index d5108ba..3fb7b8d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,213 +1,21 @@ -#include -#include -#include -#include "sva.hpp" - - -#include "gui/drawing_helper.hpp" - -#define RESOURCES_PATH "G:/School/Belegarbeit/sortiva/res/" - -#define SETTINGS_PATH "./.config.cfg" - -#define NO_GUI - - -#ifndef NO_GUI - -int screenWidth = 1280; -int screenHeight = 720; - -enum GAME_STATES -{ - SVA_STATE_TITLE, - SVA_STATE_GAMEPLAY, -}; - -GAME_STATES gameState = SVA_STATE_TITLE; - -void UpdateDrawFrame(); // Update and Draw one frame - -#include "gui/themes/dark.h" -#include "gui/Views/ViewManager.h" -#endif -#include - - -#ifdef NO_GUI -#include "SortingTester.h" -#endif - - -#include - -std::random_device rd; -std::mt19937 gen(rd()); -std::uniform_int_distribution dis(0, 10); - -void populate(size_t index, u64& value) -{ - value = dis(gen); -} - - +#include +#include "sortiva/sortiva.hpp" +//#undef _DEBUG int main(int argc, char** argv) { - sva_console_init(); - -#ifdef NO_GUI - - std::cout << "Sortiva - Sorting Algorithm Visualizer" << std::endl; - std::cout << "No GUI mode" << std::endl; - - SortingTester tester; - tester.populate( populate ); - - tester.load_internal(); - - SortingTester::run_result result = tester.run("bubble_sort"); - - std::cout << "Time: " << result.timer.seconds << " seconds" << std::endl; - std::cout << "Difftime: " << result.timer.difftime << std::endl; - - - std::cout << "Count swaps: " << result.count_swaps << std::endl; - std::cout << "Count comparisons: " << result.count_comparisons << std::endl; - std::cout << "\tCount greater: " << result.count_greater << std::endl; - std::cout << "\tCount less: " << result.count_less << std::endl; - std::cout << "\tCount equal: " << result.count_equal << std::endl; - - std::cout << "\n\n"; - - - std::cout << "\nPress any key to exit..." << std::endl; - // getchar(); - + printf("Darling, I'm Home!\n"); +#ifdef _DEBUG + try { #endif - -#ifndef NO_GUI - if(!DirectoryExists(RESOURCES_PATH)) - { - std::cerr << "Resources folder not found!" << std::endl; - return -1; + Sortiva app; + app.run(); +#ifdef _DEBUG } - - - InitWindow(screenWidth, screenHeight, "Sortiva - Sorting Algorithm Visualizer"); - - SetWindowIcon(LoadImage(RESOURCES_PATH "/images/sva-logo.png")); - - SetWindowMinSize(screenWidth, screenHeight); - SetWindowState(FLAG_WINDOW_RESIZABLE); - SetExitKey(0); - - SetTargetFPS(60); // Set our game to run at 60 frames-per-second - //-------------------------------------------------------------------------------------- - - GuiLoadStyleDark(); - GuiSetStyle(DEFAULT, TEXT_SIZE, 20); - - // Main game loop - while (!WindowShouldClose()) + catch (std::exception& e) { - if (IsWindowResized()) - { - screenWidth = GetScreenWidth(); - screenHeight = GetScreenHeight(); - } - UpdateDrawFrame(); + printf("\nError: %s\n\n", e.what()); } - CloseWindow(); #endif return 0; } - -#ifndef NO_GUI -//---------------------------------------------------------------------------------- -// Render Functions -//---------------------------------------------------------------------------------- - -void RenderGameplayState(); - - -Rectangle ButtonRects[] = { - {50, 150, 150, 40}, - {50, 200, 150, 40}, - {50, 300, 150, 40}, -}; - -void UpdateDrawFrame() -{ - BeginDrawing(); - - ClearBackground(GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR))); - - switch (gameState) - { - case SVA_STATE_TITLE: - sva::DrawText("Sortiva", {screenWidth/2, 20}, 100, sva::GetThemeColor(TEXT_COLOR_NORMAL), sva::TEXT_ALIGN_CENTER); - - if(GuiButton(ButtonRects[0], "Öffnen")) - { - gameState = SVA_STATE_GAMEPLAY; - } - if(GuiButton(ButtonRects[1], "Speichern")) - { - gameState = SVA_STATE_GAMEPLAY; - } - if(GuiButton(ButtonRects[2], "Schließen")) - { - CloseWindow(); - exit(0); - } - - break; - case SVA_STATE_GAMEPLAY: - if(IsKeyPressed(KEY_ESCAPE)) - { - gameState = SVA_STATE_TITLE; - } - RenderGameplayState(); - break; - } - EndDrawing(); -} -#include - -#define PI_2 std::numbers::pi_v / 2 - -#define sin3(x) pow((-cos(PI_2+ ##x)),3) - -#include - -Vector2 heart_position = Vector2{static_cast(screenWidth)/2, static_cast(screenHeight)/2.5f}; -Vector2 heart_function(float t, float scale) -{ - return Vector2{ - ((16 * static_cast(sin3(t))) * scale) + heart_position.x, - ((13 * cos(t) - 5 * cos(2*t) - 2 * cos(3*t) - cos(4*t)) * -scale) + heart_position.y - }; -} - -#define STEPS 1000 -#define STEP_SIZE 0.01f - -void RenderGameplayState() -{ - for (int i = 0; i < STEPS; i++) - { - Vector2 pos = heart_function(static_cast(i) * STEP_SIZE, 20); - Vector2 pos2 = heart_function(static_cast(i) * STEP_SIZE + STEP_SIZE, 20); - DrawLine( - pos.x, - pos.y, - pos2.x, - pos2.y, - GetColor(GuiGetStyle(DEFAULT, LINE_COLOR)) - ); - - } -} - -#endif \ No newline at end of file diff --git a/src/sortiva/GuiFileDialog.cpp b/src/sortiva/GuiFileDialog.cpp new file mode 100644 index 0000000..5dc3ce5 --- /dev/null +++ b/src/sortiva/GuiFileDialog.cpp @@ -0,0 +1,45 @@ +#include "GuiFileDialog.hpp" +#include +#include + + +Gui_WindowFileDialog::Gui_WindowFileDialog() +{ + fileDialogState = new GuiWindowFileDialogState(InitGuiWindowFileDialog(GetWorkingDirectory())); + fileDialogState->windowBounds = { 10, 10, fileDialogState->windowBounds.height, fileDialogState->windowBounds.height }; +} + +bool Gui_WindowFileDialog::update() +{ + bool file_opened = false; + if (fileDialogState->SelectFilePressed) + { + // Load image file (if supported extension) + if (IsFileExtension(fileDialogState->fileNameText, ".lua")) + { + strcpy(fileNameToLoad, TextFormat("%s/%s", fileDialogState->dirPathText, fileDialogState->fileNameText)); + openedFile = fileNameToLoad; + file_opened = true; + } + + fileDialogState->SelectFilePressed = false; + } + return file_opened; +} + +void Gui_WindowFileDialog::draw() +{ + // raygui: controls drawing + //---------------------------------------------------------------------------------- + if (fileDialogState->windowActive) GuiLock(); + + if (GuiButton({ 20, 20, 140, 30 }, GuiIconText(GuiIconName::ICON_FILE_OPEN, "Open Image"))) fileDialogState->windowActive = true; + + GuiUnlock(); + + // GUI: Dialog Window + //-------------------------------------------------------------------------------- + GuiWindowFileDialog(fileDialogState); + //-------------------------------------------------------------------------------- + +} \ No newline at end of file diff --git a/src/sortiva/GuiFileDialog.hpp b/src/sortiva/GuiFileDialog.hpp new file mode 100644 index 0000000..6f2162a --- /dev/null +++ b/src/sortiva/GuiFileDialog.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include +#include + + +class Gui_WindowFileDialog +{ + GuiWindowFileDialogState* fileDialogState; + char fileNameToLoad[512] = { 0 }; +public: + Gui_WindowFileDialog(); + + std::string openedFile; + + bool update(); + + void draw(); +}; diff --git a/src/sortiva/sortiva-draw.cpp b/src/sortiva/sortiva-draw.cpp new file mode 100644 index 0000000..28d621b --- /dev/null +++ b/src/sortiva/sortiva-draw.cpp @@ -0,0 +1,210 @@ +#include "sortiva.hpp" + +#include + +#include + + +constexpr Color sorter_colors[] = { + { 253, 249, 0, 255 }, // Yellow + { 255, 203, 0, 255 }, // Gold + { 255, 161, 0, 255 }, // Orange + { 255, 109, 194, 255 }, // Pink + { 230, 41, 55, 255 }, // Red + { 190, 33, 55, 255 }, // Maroon + { 0, 228, 48, 255 }, // Green + { 0, 158, 47, 255 }, // Lime + { 0, 117, 44, 255 }, // Dark Green + { 102, 191, 255, 255 }, // Sky Blue + { 0, 121, 241, 255 }, // Blue + { 0, 82, 172, 255 }, // Dark Blue + { 255, 0, 255, 255 }, // Magenta + { 200, 122, 255, 255 }, // Purple + { 135, 60, 190, 255 }, // Violet + { 112, 31, 126, 255 }, // Dark Purple + { 211, 176, 131, 255 }, // Beige + { 127, 106, 79, 255 }, // Brown + { 76, 63, 47, 255 }, // Dark Brown +}; + +constexpr int sorter_colors_size = sizeof(sorter_colors) / sizeof(Color); + +constexpr Color sorter_block_colors[] = { + { 200, 200, 200, 255 }, // Light Gray + { 80, 80, 80, 255 }, // Dark Gray +}; + +constexpr int sorter_block_colors_size = sizeof(sorter_block_colors) / sizeof(Color); + + +void Sortiva::draw() +{ + Vector2 margin = { + .x = m_Width * 0.01f, + .y = 70 + }; + DrawRectangleV(margin, { m_Width - 2 * margin.x, m_Height - 2 * margin.y }, { 60, 60, 60, 255 }); + + + margin = { + .x = m_Width * 0.02f, + .y = 80 + }; + + Rectangle plane = { + .x = margin.x, + .y = margin.y, + .width = m_Width - margin.x * 2, + .height = m_Height - margin.y * 2 + }; + + if (m_Steps->empty()) + { + static const char* msg = "Press \"Run\" to start a sorting"; + static int width = MeasureText(msg, 20); + DrawText(msg, plane.x + plane.width / 2 - width / 2, plane.y + plane.height / 2 - 10, 20, { 245, 245, 245, 255 }); + return; + } + + // +2 for start points and end points; will be removed later in the function + size_t cvals = m_Steps->size(); + + size_t steps = m_Steps->at(0).depth(); + for (size_t i = 1; i < cvals; ++i) + { + steps = std::min(steps, m_Steps->at(i).depth()); + } + + steps += 1; + + // ui distance values + int w = static_cast(plane.width / static_cast(steps)); + int g = plane.height / static_cast(cvals + 1); + int h = (plane.height - static_cast(cvals - 1) * g) / 2; + int e = w / 2; + float wf = static_cast(w); + + float pwhd = sqrtf(plane.width * plane.width + plane.height * plane.height); + float pw = plane.width / pwhd - 0.01f; + float ph = plane.height / pwhd; + float l = pw * ph * pwhd * 0.02f - static_cast(steps) / 2; // Make more fitting + + for (size_t i = 0; i < steps; ++i) + { + DrawRectangle( + static_cast(i) * w + static_cast(plane.x), + plane.y, + w, + plane.height, + sorter_block_colors[i % sorter_block_colors_size] + ); + } + steps -= 1; + + // colors to values - ratio ; adds high variance to the color selection + int colid = sorter_colors_size / cvals; + + // font settings + constexpr Color textColor = { 0, 0, 0, 255 }; + int l3 = static_cast(l * 3); + +#ifdef _DEBUG + DrawFPS(5, 5); +#endif + + for (int v = 0; v < cvals; ++v) + { + linked_list* list = &m_Steps->at(v); + uint16_t value = list->value; + Color col = sorter_colors[(v * colid) % sorter_colors_size]; + + + + + int s = 0; + for (; list->next; list = list->next) + { + float sf = static_cast(s); + + value = list->value; + uint16_t nvalue = list->next->value; + float y = plane.y + h + g * (value - 1); + float yt = plane.y + h + g * (nvalue - 1); + + + DrawSplineSegmentBezierCubic( + { (sf + 1.f) * wf + plane.x - 1, y }, + { (sf + 2.f) * wf + plane.x, y }, + { (sf + 1.f) * wf + plane.x, yt }, + { (sf + 2.f) * wf + plane.x, yt }, + l, + col + ); + +#ifdef _DEBUG + float font_size = l * 1.2f; + int yd = y - yt < 0 ? -font_size : +font_size; + yd *= 2; + DrawText( + TextFormat("%d -> %d", value, nvalue), + (sf + 1.f) * wf + plane.x + 10, + y + yd, + font_size, + col); +#endif + ++s; + } + + value = list->value; + + Vector2 pos = { wf * steps + e + plane.x, h + (value - 1) * g + plane.y }; + DrawCircleV(pos, l3, col); + + DrawLineEx( + { pos.x - e - 1, pos.y }, + pos, + l, + col + ); + + const char* strv = TextFormat("%d", v + 1); + DrawText( + strv, + static_cast(pos.x) + e / 2, + static_cast(pos.y) - l3 / 2, + l3, + textColor + ); + + if (list->first) + list = list->first; + value = list->value; + pos = { wf - e + plane.x, h + (value - 1) * g + plane.y }; + + DrawText( + strv, + static_cast(pos.x) - e / 2 - l3 / 2, + static_cast(pos.y) - l3 / 2, + l3, + textColor + ); + + DrawRing( + pos, + l * 2, + l3, + 20.f, + 340.f, + 10, + col + ); + + DrawLineEx( + pos, + { pos.x + e, pos.y }, + l, + col + ); + } + +} \ No newline at end of file diff --git a/src/sortiva/sortiva-draw_overlay.cpp b/src/sortiva/sortiva-draw_overlay.cpp new file mode 100644 index 0000000..345a529 --- /dev/null +++ b/src/sortiva/sortiva-draw_overlay.cpp @@ -0,0 +1,33 @@ +#include "sortiva.hpp" + +#include + + +void Sortiva::draw_overlay() +{ + //static bool edit_mode = false; + // + //GuiUnlock(); + // + //static int active = 0; + //if (GuiDropdownBox({ 250,20,200,30 }, m_Lua.SorterListStr.c_str(), &active, edit_mode)) edit_mode = !edit_mode; + // + //if (edit_mode) GuiLock(); + + if (GuiButton({ 20, 15, 100, 40 }, "Run")) + { + setup(); + m_SortingFinished = false; + } + if (GuiButton({ 140, 15, 100, 40 }, m_SortingFinished ? "Continue" : "Pause")) + { + if (!m_SortingFinished || !m_Steps->empty()) { + m_SortingFinished = !m_SortingFinished; + } + } + + if (GuiButton({ 20, static_cast(GetScreenHeight()) - 15 - 40, 100, 40 }, "Reset")) + { + m_Steps->clear(); + } +} diff --git a/src/sortiva/sortiva-update.cpp b/src/sortiva/sortiva-update.cpp new file mode 100644 index 0000000..f4c93c2 --- /dev/null +++ b/src/sortiva/sortiva-update.cpp @@ -0,0 +1,22 @@ +#include "sortiva.hpp" + + +void Sortiva::update(double dt) { + if (m_Ticker.update(std::chrono::duration(dt))) + { + if (!m_SortingFinished) { + if (m_Sorter.poll()) + { + m_SortingFinished = true; + } + else + { + LuaSortList& list = m_Sorter.get(); + for (uint16_t i = 0; i < list.size(); ++i) + { + m_Steps->at(list.at(i) - 1).put(i + 1); + } + } + } + } +} \ No newline at end of file diff --git a/src/sortiva/sortiva.cpp b/src/sortiva/sortiva.cpp new file mode 100644 index 0000000..98abe72 --- /dev/null +++ b/src/sortiva/sortiva.cpp @@ -0,0 +1,81 @@ +#include "sortiva.hpp" + +#include + +#include +#include + + +#include + + +Sortiva::Sortiva() : m_Ticker(std::chrono::seconds(1)) +{ + m_Sorter.set(m_List); + + m_Steps = std::make_unique(); + + InitWindow(1280, 720, "Sortiva"); + if (!IsWindowReady()) exit(2); + + m_Width = GetRenderWidth(); + m_Height = GetRenderHeight(); + SetWindowMinSize(static_cast(1280 * 0.25f), static_cast(720 * 0.25f)); + SetWindowState(ConfigFlags::FLAG_WINDOW_RESIZABLE | ConfigFlags::FLAG_VSYNC_HINT); + + SetTargetFPS(60); + + MaximizeWindow(); +} + +Sortiva::~Sortiva() +{ + CloseWindow(); +} + +void Sortiva::run() +{ + while (m_Running) + { + if (WindowShouldClose()) m_Running = false; + if (IsWindowResized()) + { + m_Width = GetScreenWidth(); + m_Height = GetScreenHeight(); + } + + ClearBackground({ 25, 25, 25, 255 }); + BeginDrawing(); + + update(GetFrameTime()); + draw(); + draw_overlay(); + + EndDrawing(); + } +} + +void Sortiva::setup() +{ + m_Steps->clear(); + m_List.list.clear(); + + uint16_t count = 5; + + + for (int i = 1; i <= count; ++i) // 1,2,3,4,5 + { + m_List.list.push_back(i); + m_Steps->emplace_back(); + } + + std::random_device dev; + std::mt19937 rng(dev()); + + std::ranges::shuffle(m_List.list, rng); // 2,3,1,5,4 + + for (int i = 1; i <= count; ++i) + { + m_Steps->at(m_List.at(i - 1) - 1).value = i; + } +} diff --git a/src/sortiva/sortiva.hpp b/src/sortiva/sortiva.hpp new file mode 100644 index 0000000..55e00dd --- /dev/null +++ b/src/sortiva/sortiva.hpp @@ -0,0 +1,82 @@ +#pragma once +#include +#include +#include +#include "../lua/FutureLua.hpp" +#include +#include "../LuaSortList.hpp" + +class Bubble_Sorter : Future +{ + LuaSortList* list = nullptr; + struct STATE + { + size_t n = 1; + } state; +public: + Bubble_Sorter() = default; + Bubble_Sorter(LuaSortList& l) : list(&l) {} + + bool poll() override + { + size_t n = list->size(); + if (n <= 1) { + return true; + } + + state.n = state.n + 1; + + if (state.n > n) + { + state.n = 2; + } + + if (list->at(state.n - 2) > list->at(state.n - 1)) { + list->swap(state.n - 2, state.n - 1); + return false; + } + return list->sorted(); + } + + LuaSortList& get() override + { + return *list; + } + + void set(LuaSortList& l) + { + list = &l; + } +}; + +class Sortiva final +{ +public: + Sortiva(); + ~Sortiva(); + + void run(); +private: + int m_Width; + int m_Height; + bool m_Running = true; + + void draw(); + void draw_overlay(); + void update(double); + + void setup(); + + bool m_SortingFinished = true; + TickSystem m_Ticker; + Bubble_Sorter m_Sorter; + LuaSortList m_List; + + + // std::unique_ptr + // -> tree + // -> linked_list + // -> uint16_t + using val_step_diag = std::vector>; + std::unique_ptr m_Steps; +}; diff --git a/src/sva.hpp b/src/sva.hpp deleted file mode 100644 index ba08f3f..0000000 --- a/src/sva.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once -#include -#include - -inline const char* ASCII_TITLE = R"( - __ - (_ _ ___|_ o _ - __)(_) | |_ | \_/(_| - - - -)"; - -inline void sva_console_init() -{ - std::cout << ASCII_TITLE; -} \ No newline at end of file diff --git a/tests/test_main.cpp b/tests/test_main.cpp deleted file mode 100644 index 093e784..0000000 --- a/tests/test_main.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include - -int main(int argc, char **argv) { - testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} \ No newline at end of file diff --git a/tests/test_sorting_functions.cpp b/tests/test_sorting_functions.cpp deleted file mode 100644 index 904223a..0000000 --- a/tests/test_sorting_functions.cpp +++ /dev/null @@ -1,54 +0,0 @@ -// tests/sorting_tests.cpp -#include -#include - -template -void swap(T& a, T& b) noexcept -{ - T temp = a; - a = b; - b = temp; -} - -template -void bubble_sort(std::vector& in) -{ - for (size_t i = 0; i < in.size(); ++i) - { - for (size_t j = 0; j < in.size() - 1; ++j) - { - if (in[j] > in[j + 1]) - { - swap(in[j], in[j + 1]); - } - } - } -} - - -TEST(BubbleSortTest, CorrectlySortsVector) { - std::vector input = {5, 2, 8, 1, 9}; - std::vector expected = {1, 2, 5, 8, 9}; - - bubble_sort(input); - - EXPECT_EQ(input, expected); -} - -TEST(BubbleSortTest, HandlesEmptyVector) { - std::vector input = {}; - std::vector expected = {}; - - bubble_sort(input); - - EXPECT_EQ(input, expected); -} - -TEST(BubbleSortTest, HandlesSingleElement) { - std::vector input = {1}; - std::vector expected = {1}; - - bubble_sort(input); - - EXPECT_EQ(input, expected); -} \ No newline at end of file