diff --git a/.gitpod.dockerfile b/.gitpod.dockerfile index 6d6a4e895..53d336f81 100644 --- a/.gitpod.dockerfile +++ b/.gitpod.dockerfile @@ -1,9 +1,9 @@ -FROM gitpod/workspace-full +FROM gitpod/workspace-full-vnc RUN sudo apt-get update \ - && sudo apt-get install -y \ - doxygen \ - graphviz \ - ninja-build \ - && pip install cpplint \ - && sudo rm -rf /var/lib/apt/lists/* + && sudo apt-get install -y \ + doxygen \ + graphviz \ + ninja-build \ + && pip install cpplint \ + && sudo rm -rf /var/lib/apt/lists/* diff --git a/CMakeLists.txt b/CMakeLists.txt index 712c3db42..d7fa88559 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,7 +17,28 @@ if(MSVC) endif(MSVC) option(USE_OPENMP "flag to use OpenMP for multithreading" ON) - +if(USE_OPENMP) + find_package(OpenMP) + if (OpenMP_CXX_FOUND) + message(STATUS "Building with OpenMP Multithreading.") + else() + message(STATUS "No OpenMP found, no multithreading.") + endif() +endif() + +add_subdirectory(math) +add_subdirectory(others) +add_subdirectory(search) +add_subdirectory(ciphers) +add_subdirectory(strings) +add_subdirectory(sorting) +add_subdirectory(geometry) +add_subdirectory(graphics) +add_subdirectory(probability) +add_subdirectory(data_structures) +add_subdirectory(machine_learning) +add_subdirectory(numerical_methods) + cmake_policy(SET CMP0054 NEW) cmake_policy(SET CMP0057 NEW) find_package(Doxygen OPTIONAL_COMPONENTS dot dia) @@ -34,6 +55,7 @@ if(DOXYGEN_FOUND) set(DOXYGEN_STRIP_CODE_COMMENTS NO) set(DOXYGEN_EXT_LINKS_IN_WINDOW YES) set(DOXYGEN_BUILTIN_STL_SUPPORT YES) + set(DOXYGEN_ENABLE_PREPROCESSING YES) set(DOXYGEN_CLANG_ASSISTED_PARSING YES) set(DOXYGEN_FILE_PATTERNS *.cpp *.h *.hpp *.md) set(DOXYGEN_MATHJAX_EXTENSIONS TeX/AMSmath TeX/AMSsymbols) @@ -48,6 +70,12 @@ if(DOXYGEN_FOUND) set(DOXYGEN_INTERACTIVE_SVG YES) set(DOXYGEN_DOT_IMAGE_FORMAT "svg") endif() + if(OPENMP_FOUND) + set(DOXYGEN_PREDEFINED "_OPENMP=1") + endif() + if(GLUT_FOUND) + set(DOXYGEN_PREDEFINED ${DOXYGEN_PREDEFINED} "GLUT_FOUND=1") + endif() doxygen_add_docs( doc @@ -56,27 +84,6 @@ if(DOXYGEN_FOUND) ) endif() -if(USE_OPENMP) - find_package(OpenMP) - if (OpenMP_CXX_FOUND) - message(STATUS "Building with OpenMP Multithreading.") - else() - message(STATUS "No OpenMP found, no multithreading.") - endif() -endif() - -add_subdirectory(math) -add_subdirectory(others) -add_subdirectory(search) -add_subdirectory(ciphers) -add_subdirectory(strings) -add_subdirectory(sorting) -add_subdirectory(geometry) -add_subdirectory(probability) -add_subdirectory(data_structures) -add_subdirectory(machine_learning) -add_subdirectory(numerical_methods) - set(CPACK_PROJECT_NAME ${PROJECT_NAME}) set(CPACK_PROJECT_VERSION ${PROJECT_VERSION}) include(CPack) diff --git a/DIRECTORY.md b/DIRECTORY.md index f53d2f7ab..0f7e9a053 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -84,6 +84,9 @@ * [Topological Sort](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/topological_sort.cpp) * [Topological Sort By Kahns Algo](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graph/topological_sort_by_kahns_algo.cpp) +## Graphics + * [Spirograph](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/graphics/spirograph.cpp) + ## Greedy Algorithms * [Dijkstra](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/greedy_algorithms/dijkstra.cpp) * [Huffman](https://github.com/TheAlgorithms/C-Plus-Plus/blob/master/greedy_algorithms/huffman.cpp) diff --git a/graphics/CMakeLists.txt b/graphics/CMakeLists.txt new file mode 100644 index 000000000..2ea6ca230 --- /dev/null +++ b/graphics/CMakeLists.txt @@ -0,0 +1,83 @@ +find_package(OpenGL) +if(OpenGL_FOUND) + find_package(GLUT) + if(NOT GLUT_FOUND) + message("FreeGLUT library will be downloaded and built.") + include(ExternalProject) + ExternalProject_Add ( + FREEGLUT-PRJ + URL https://sourceforge.net/projects/freeglut/files/freeglut/3.2.1/freeglut-3.2.1.tar.gz + URL_MD5 cd5c670c1086358598a6d4a9d166949d + CMAKE_GENERATOR ${CMAKE_GENERATOR} + CMAKE_GENERATOR_TOOLSET ${CMAKE_GENERATOR_TOOLSET} + CMAKE_GENERATOR_PLATFORM ${CMAKE_GENERATOR_PLATFORM} + CMAKE_ARGS -DCMAKE_BUILD_TYPE=Release + -DFREEGLUT_BUILD_SHARED_LIBS=OFF + -DFREEGLUT_BUILD_STATIC_LIBS=ON + -DFREEGLUT_BUILD_DEMOS=OFF + PREFIX ${CMAKE_CURRENT_BINARY_DIR}/freeglut + # BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/freeglut-build + # BUILD_IN_SOURCE ON + # UPDATE_COMMAND "" + INSTALL_COMMAND "" + # CONFIGURE_COMMAND "" + # BUILD_COMMAND "" + ) + ExternalProject_Get_Property(FREEGLUT-PRJ SOURCE_DIR) + ExternalProject_Get_Property(FREEGLUT-PRJ BINARY_DIR) + set(FREEGLUT_BIN_DIR ${BINARY_DIR}) + set(FREEGLUT_SRC_DIR ${SOURCE_DIR}) + # add_library(libfreeglut STATIC IMPORTED) + # set_target_properties(libfreeglut PROPERTIES IMPORTED_LOCATION ${FREEGLUT_BIN_DIR}) + + # set(FREEGLUT_BUILD_DEMOS OFF CACHE BOOL "") + # set(FREEGLUT_BUILD_SHARED_LIBS OFF CACHE BOOL "") + # set(FREEGLUT_BUILD_STATIC_LIBS ON CACHE BOOL "") + # add_subdirectory(${FREEGLUT_SRC_DIR} ${FREEGLUT_BIN_DIR} EXCLUDE_FROM_ALL) + # add_subdirectory(${BINARY_DIR}) + # find_package(FreeGLUT) + endif(NOT GLUT_FOUND) +else(OpenGL_FOUND) + message(WARNING "OPENGL not found. Will not build graphical outputs.") +endif(OpenGL_FOUND) + +# If necessary, use the RELATIVE flag, otherwise each source file may be listed +# with full pathname. RELATIVE may makes it easier to extract an executable name +# automatically. +file( GLOB APP_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.cpp ) +# file( GLOB APP_SOURCES ${CMAKE_SOURCE_DIR}/*.c ) +# AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR} APP_SOURCES) +foreach( testsourcefile ${APP_SOURCES} ) + # I used a simple string replace, to cut off .cpp. + string( REPLACE ".cpp" "" testname ${testsourcefile} ) + add_executable( ${testname} ${testsourcefile} ) + + set_target_properties(${testname} PROPERTIES LINKER_LANGUAGE CXX) + if(OpenMP_CXX_FOUND) + target_link_libraries(${testname} PRIVATE OpenMP::OpenMP_CXX) + endif() + + if(OpenGL_FOUND) + if(NOT GLUT_FOUND) + add_dependencies(${testname} FREEGLUT-PRJ) + target_compile_definitions(${testname} PRIVATE FREEGLUT_STATIC) + target_include_directories(${testname} PRIVATE ${FREEGLUT_SRC_DIR}/include) + target_link_directories(${testname} PRIVATE ${FREEGLUT_BIN_DIR}/lib) + target_link_libraries(${testname} PRIVATE OpenGL::GL) + target_link_libraries(${testname} INTERFACE FREEGLUT-PRJ) + # target_include_directories(${testname} PRIVATE ${FREEGLUT_INCLUDE_DIRS}) + # target_link_libraries(${testname} INTERFACE freeglut_static) + else() + target_include_directories(${testname} PRIVATE ${GLUT_INCLUDE_DIRS}) + target_link_libraries(${testname} PRIVATE OpenGL::GL ${GLUT_LIBRARIES}) + endif() + target_compile_definitions(${testname} PRIVATE USE_GLUT) + endif(OpenGL_FOUND) + + if(APPLE) + target_compile_options(${testname} PRIVATE -Wno-deprecated) + endif(APPLE) + + install(TARGETS ${testname} DESTINATION "bin/graphics") + +endforeach( testsourcefile ${APP_SOURCES} ) diff --git a/graphics/spirograph.cpp b/graphics/spirograph.cpp new file mode 100644 index 000000000..84c17c4db --- /dev/null +++ b/graphics/spirograph.cpp @@ -0,0 +1,284 @@ +/** + * @file + * @author [Krishna Vedala](https://github.com/kvedala) + * @brief Implementation of + * [Spirograph](https://en.wikipedia.org/wiki/Spirograph) + * + * @details + * Implementation of the program is based on the geometry shown in the figure + * below: + * + * Spirograph geometry from Wikipedia + */ +#ifdef USE_GLUT +#ifdef __APPLE__ +#include // include path on Macs is different +#else +#include +#endif // __APPLE__ +#endif +#define _USE_MATH_DEFINES /**< required for MSVC compiler */ +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef _OPENMP +#include +#endif + +/** + * @namespace spirograph Functions related to spirograph.cpp + */ +namespace spirograph { +/** Generate spirograph curve into arrays `x` and `y` such that the i^th point + * in 2D is represented by `(x[i],y[i])`. The generating function is given by: + * \f{eqnarray*}{ + * x &=& R\left[ (1-k) \cos (t) + l\cdot k\cdot\cos \left(\frac{1-k}{k}t\right) + * \right]\\ + * y &=& R\left[ (1-k) \sin (t) - l\cdot k\cdot\sin \left(\frac{1-k}{k}t\right) + * \right] \f} + * where + * * \f$R\f$ is the scaling parameter that we will consider \f$=1\f$ + * * \f$l=\frac{\rho}{r}\f$ is the relative distance of marker from the centre + * of inner circle and \f$0\le l\le1\f$ + * * \f$\rho\f$ is physical distance of marker from centre of inner circle + * * \f$r\f$ is the radius of inner circle + * * \f$k=\frac{r}{R}\f$ is the ratio of radius of inner circle to outer circle + * and \f$0 +void spirograph(std::array, N> *points, double l, + double k, double rot) { + double dt = rot * 2.f * M_PI / N; + double R = 1.f; + const double k1 = 1.f - k; + int32_t step = 0; + +#ifdef _OPENMP +#pragma omp for +#endif + for (step = 0; step < N; step++) { + double t = dt * step; + double first = R * (k1 * std::cos(t) + l * k * std::cos(k1 * t / k)); + double second = R * (k1 * std::sin(t) - l * k * std::sin(k1 * t / k)); + points[0][step].first = first; + points[0][step].second = second; + } +} + +/** + * @brief Test function to save resulting points to a CSV file. + * + */ +void test() { + const size_t N = 500; + double l = 0.3, k = 0.75, rot = 10.; + std::stringstream fname; + fname << std::setw(3) << "spirograph_" << l << "_" << k << "_" << rot + << ".csv"; + std::ofstream fp(fname.str()); + if (!fp.is_open()) { + perror(fname.str().c_str()); + exit(EXIT_FAILURE); + } + + std::array, N> points; + + spirograph(&points, l, k, rot); + + for (size_t i = 0; i < N; i++) { + fp << points[i].first << "," << points[i].first; + if (i < N - 1) { + fp << '\n'; + } + } + + fp.close(); +} + +#ifdef USE_GLUT +static bool paused = 0; /**< flag to set pause/unpause animation */ +static const int animation_speed = 25; /**< animation delate in ms */ + +static const double step = 0.01; /**< animation step size */ +static double l_ratio = step * 10; /**< the l-ratio defined in docs */ +static double k_ratio = step; /**< the k-ratio defined in docs */ +static const double num_rot = 20.; /**< number of rotations to simulate */ + +/** A wrapper that is not available in all GLUT implementations. + */ +static inline void glutBitmapString(void *font, char *message) { + for (char *ch = message; *ch != '\0'; ch++) glutBitmapCharacter(font, *ch); +} + +/** + * @brief Function to graph (x,y) points on the OpenGL graphics window. + * + * @tparam N number of points = size of array + * @param [in] points Array of 2D points represented as std::pair + * @param l the relative distance of marker from the centre of + * inner circle and \f$0\le l\le1\f$ to display info + * @param k the ratio of radius of inner circle to outer circle and \f$0 +void display_graph(const std::array, N> &points, + double l, double k) { + glClearColor(1.0f, 1.0f, 1.0f, + 0.0f); // Set background color to white and opaque + glClear(GL_COLOR_BUFFER_BIT); // Clear the color buffer (background) + + glBegin(GL_LINES); // draw line segments + glColor3f(0.f, 0.f, 1.f); // blue + glPointSize(2.f); // point size in pixels + + for (size_t i = 1; i < N; i++) { + glVertex2f(points[i - 1].first, points[i - 1].second); // line from + glVertex2f(points[i].first, points[i].second); // line to + } + glEnd(); + + glColor3f(0.f, 0.f, 0.f); + std::stringstream buffer; + buffer << std::setw(3) << "l = " << l; + glRasterPos2f(-.85, .85); + glutBitmapString(GLUT_BITMAP_TIMES_ROMAN_24, + const_cast(buffer.str().c_str())); + buffer.str(""); + buffer.clear(); + buffer << std::setw(3) << "k = " << k; + glRasterPos2f(-.85, .70); + glutBitmapString(GLUT_BITMAP_TIMES_ROMAN_24, + const_cast(buffer.str().c_str())); + + glutSwapBuffers(); +} + +/** + * @brief Test function with animation + * + */ +void test2() { + const size_t N = 5000; // number of samples + + static bool direction1 = true; // increment if true, otherwise decrement + static bool direction2 = true; // increment if true, otherwise decrement + + std::array, N> points; + + spirograph(&points, l_ratio, k_ratio, num_rot); + display_graph(points, l_ratio, k_ratio); + + if (paused) + // if paused, do not update l_ratio and k_ratio + return; + + if (direction1) { // increment k_ratio + if (k_ratio >= (1.f - step)) // maximum limit + direction1 = false; // reverse direction of k_ratio + else + k_ratio += step; + } else { // decrement k_ratio + if (k_ratio <= step) { // minimum limit + direction1 = true; // reverse direction of k_ratio + + if (direction2) { // increment l_ratio + if (l_ratio >= (1.f - step)) // max limit of l_ratio + direction2 = false; // reverse direction of l_ratio + else + l_ratio += step; + } else { // decrement l_ratio + if (l_ratio <= step) // minimum limit of l_ratio + direction2 = true; // reverse direction of l_ratio + else + l_ratio -= step; + } + } else { // no min limit of k_ratio + k_ratio -= step; + } + } +} + +/** + * @brief GLUT timer callback function to add animation delay. + */ +void timer_cb(int t) { + glutTimerFunc(animation_speed, timer_cb, 0); + glutPostRedisplay(); +} + +/** + * @brief Keypress event call back function. + * + * @param key ID of the key pressed + * @param x mouse pointer position at event + * @param y mouse pointer position at event + */ +void keyboard_cb(unsigned char key, int x, int y) { + switch (key) { + case ' ': // spacebar toggles pause + paused = !paused; // toggle + break; + case GLUT_KEY_UP: + case '+': // up arrow key + k_ratio += step; + break; + case GLUT_KEY_DOWN: + case '_': // down arrow key + k_ratio -= step; + break; + case GLUT_KEY_RIGHT: + case '=': // left arrow key + l_ratio += step; + break; + case GLUT_KEY_LEFT: + case '-': // right arrow key + l_ratio -= step; + break; + case 0x1B: // escape key exits + exit(EXIT_SUCCESS); + default: + return; + } +} +#endif +} // namespace spirograph + +/** Main function */ +int main(int argc, char **argv) { + spirograph::test(); + +#ifdef USE_GLUT + glutInit(&argc, argv); + glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE); + glutCreateWindow("Spirograph"); + glutInitWindowSize(400, 400); + // glutIdleFunc(glutPostRedisplay); + glutTimerFunc(spirograph::animation_speed, spirograph::timer_cb, 0); + glutKeyboardFunc(spirograph::keyboard_cb); + glutDisplayFunc(spirograph::test2); + glutMainLoop(); +#endif + + return 0; +}